summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--application/pom.xml7
-rw-r--r--application/src/test/java/com/yahoo/application/container/ContainerModelEvaluationTest.java3
-rw-r--r--build_settings.cmake31
-rw-r--r--bundle-plugin-test/src/test/java/com/yahoo/BundleIT.java5
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/bundle/AnalyzeBundle.java14
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/PackageTally.java18
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/Artifacts.java14
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/GenerateOsgiManifestMojo.java122
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/osgi/ImportPackages.java5
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/util/JdkPackages.java39
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/bundle/AnalyzeBundleTest.java9
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/PackageTallyTest.java24
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/osgi/ImportPackageTest.java5
-rw-r--r--clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/database/CasWriteFailed.java23
-rw-r--r--clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/database/Database.java16
-rw-r--r--clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/database/DatabaseHandler.java132
-rw-r--r--clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/database/ZooKeeperDatabase.java30
-rw-r--r--clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ZooKeeperDatabaseTest.java37
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java1
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/ConfigModelUtils.java49
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/MapConfigModelRegistry.java13
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/producer/UserConfigRepo.java11
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/provision/Host.java1
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/test/MockApplicationPackage.java4
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/test/MockRoot.java1
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java1
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/SearchBuilder.java1
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java2
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexInfo.java4
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexingScript.java1
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/derived/SummaryClass.java1
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/derived/SummaryMap.java9
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/derived/validation/Validator.java2
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/document/RankType.java5
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/document/SDField.java21
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/expressiontransforms/RankProfileTransformContext.java2
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/processing/AddAttributeTransformToSummaryOfImportedFields.java2
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/processing/AdjustPositionSummaryFields.java5
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/processing/IndexingInputs.java1
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/processing/multifieldresolver/StemmingResolver.java5
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/documentmodel/SearchDef.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/ConfigProducerRoot.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/Host.java3
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/HostPorts.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/HostResource.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/HostSystem.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/Admin.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainerCluster.java21
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/ZooKeepersConfigProvider.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerCluster.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainerCluster.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java12
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ContentClusterRemovalValidator.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomConfigPayloadBuilder.java43
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomV20ClientsBuilder.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/clients/Clients.java6
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java65
-rwxr-xr-xconfig-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java57
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/component/ContainerSubsystem.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/component/Servlet.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/docproc/ContainerDocproc.java18
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/docproc/DocprocChains.java14
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/docproc/DocumentProcessor.java3
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/Http.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/processing/Processor.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java18
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/QueryProfiles.java10
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/QueryProfilesBuilder.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/LocalProvider.java4
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/ContentNode.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java10
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/IndexedHierarchicDistributionValidator.java38
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/Redundancy.java48
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/ResourceLimits.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/StorageGroup.java84
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java32
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/cluster/RedundancyBuilder.java24
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/engines/ProtonProvider.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/storagecluster/IntegrityCheckerProducer.java4
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/ml/ConvertedModel.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/search/DocumentDatabase.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/search/IndexedSearchCluster.java3
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/search/IndexingDocprocChain.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/search/IndexingProcessor.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/search/NodeFlavorTuning.java9
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/search/SearchColumn.java27
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/search/SearchNode.java10
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/utils/Duration.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/utils/FileSender.java2
-rw-r--r--config-model/src/main/resources/schema/common.rnc2
-rw-r--r--config-model/src/test/cfg/admin/userconfigs/functiontest-defaultvalues.xml2
-rw-r--r--config-model/src/test/cfg/admin/userconfigs/whitespace-test.xml2
-rw-r--r--config-model/src/test/cfg/application/app_genericservices/services.xml8
-rw-r--r--config-model/src/test/java/com/yahoo/config/model/ApplicationPackageTester.java4
-rw-r--r--config-model/src/test/java/com/yahoo/config/model/MapConfigModelRegistryTest.java1
-rw-r--r--config-model/src/test/java/com/yahoo/config/model/QrserverAndGatewayPortAllocationTest.java5
-rw-r--r--config-model/src/test/java/com/yahoo/config/model/provision/HostsXmlProvisionerTest.java1
-rw-r--r--config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java19
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/FieldOfTypeDocumentTestCase.java9
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/IncorrectRankingExpressionFileRefTestCase.java1
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/IncorrectSummaryTypesTestCase.java4
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/NameFieldCheckTestCase.java5
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/OutsideTestCase.java1
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/RankingExpressionConstantsTestCase.java1
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/RankingExpressionInliningTestCase.java1
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/RankingExpressionValidationTestCase.java1
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/SearchDefinitionTestCase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/SearchDefinitionsParsingTestCase.java5
-rwxr-xr-xconfig-model/src/test/java/com/yahoo/searchdefinition/StructTestCase.java4
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/SummaryTestCase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/derived/AbstractExportingTestCase.java1
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/derived/DeriverTestCase.java11
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/derived/ExportingTestCase.java1
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/derived/InheritanceTestCase.java1
-rwxr-xr-xconfig-model/src/test/java/com/yahoo/searchdefinition/derived/StreamingStructTestCase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/derived/SummaryMapTestCase.java11
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/derived/TwoStreamingStructsTestCase.java1
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/AdjustPositionSummaryFieldsTestCase.java11
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/ImportedFieldsResolverTestCase.java11
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingValidationTestCase.java1
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingValuesTestCase.java1
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/PositionTestCase.java3
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionWithTensorFlowTestCase.java4
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/ReservedRankingExpressionFunctionNamesTestCase.java1
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/TensorTransformTestCase.java1
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/WeightedSetSummaryToTestCase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/HostPortsTest.java4
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/HostResourceTest.java1
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/InstanceResolverTest.java1
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/admin/ClusterControllerTestCase.java6
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/admin/DedicatedAdminV4Test.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyModelTester.java8
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidatorTestCase.java30
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ClusterSizeReductionValidatorTest.java15
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ConfigValueChangeValidatorTest.java1
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/GlobalDocumentChangeValidatorTest.java9
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/IndexingModeChangeValidatorTest.java4
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/AttributeChangeValidatorTest.java1
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentTypeChangeValidatorTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/builder/UserConfigBuilderTest.java37
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/Bug6068056Test.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/ContentBuilderTest.java1
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/DomConfigPayloadBuilderTest.java107
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/LegacyConfigModelBuilderTest.java11
-rwxr-xr-xconfig-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/VespaDomBuilderTest.java34
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/ContainerIncludeTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/configserver/TestOptions.java4
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/http/FilterBindingsTest.java5
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/jersey/xml/RestApiTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/MockSearchClusters.java1
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/SearchChainsTest.java3
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/SearchChainsTest2.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/SearchChainsTestBase.java1
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/SourceGroupTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/search/test/QueryProfilesTestCase.java6
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/search/test/empty.cfg1
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/search/test/explicit-reference-override.cfg3
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/search/test/newsbe-query-profiles-simple.cfg3
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/search/test/newsfe-query-profiles-simple.cfg3
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/search/test/query-profile-variants-configuration.cfg3
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/search/test/query-profile-variants2-configuration.cfg3
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/search/test/query-profiles.cfg3
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/search/test/variants-of-explicit-compound-with-reference.cfg3
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/search/test/variants-of-explicit-compound.cfg3
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessControlTest.java20
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessLogTest.java6
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTestBase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/xml/IdentityBuilderTest.java5
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/xml/JettyContainerModelBuilderTest.java10
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/xml/RoutingBuilderTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/xml/SearchBuilderTest.java8
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/DistributorTest.java1
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/GenericConfigTest.java10
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/IndexedHierarchicDistributionTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/IndexingAndDocprocRoutingTest.java8
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/MonitoringConfigSnoopTest.java4
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/RedundancyTest.java4
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/StorageClusterTest.java20
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/StorageContentTest.java16
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/StorageGroupTest.java4
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/cluster/ClusterTest.java16
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/utils/ContentClusterBuilder.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/generic/GenericServicesTest.java12
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/ml/ImportedModelTester.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/search/MultilevelDispatchTest.java5
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/search/NodeFlavorTuningTest.java37
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/search/test/DocumentDatabaseTestCase.java20
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/search/test/SearchClusterTest.java9
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/storage/test/StorageModelTestCase.java6
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/test/ModelAmendingTestCase.java4
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/test/ParentService.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/test/PortsMetaTestCase.java4
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/test/VespaModelTestCase.java29
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/test/utils/ApplicationPackageUtils.java31
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/test/utils/CommonVespaModelSetup.java34
-rw-r--r--config-provisioning/abi-spec.json19
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/RotationName.java57
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/exception/LoadBalancerServiceException.java (renamed from node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerServiceException.java)4
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/exception/package-info.java5
-rw-r--r--config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ConfigProxyRpcServer.java1
-rw-r--r--config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ConfigProxyStatistics.java104
-rw-r--r--config-proxy/src/main/java/com/yahoo/vespa/config/proxy/Mode.java5
-rw-r--r--config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ProxyServer.java55
-rw-r--r--config-proxy/src/main/java/com/yahoo/vespa/config/proxy/RpcConfigSourceClient.java6
-rw-r--r--config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ModeTest.java61
-rw-r--r--config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ProxyServerTest.java33
-rw-r--r--config-proxy/src/test/java/com/yahoo/vespa/config/proxy/RpcConfigSourceClientTest.java5
-rwxr-xr-xconfig/pom.xml43
-rw-r--r--config/src/apps/vespa-configproxy-cmd/proxycmd.h2
-rw-r--r--config/src/main/java/com/yahoo/config/subscription/CfgConfigPayloadBuilder.java2
-rwxr-xr-xconfig/src/main/java/com/yahoo/config/subscription/ConfigGetter.java4
-rw-r--r--config/src/main/java/com/yahoo/config/subscription/ConfigInstanceUtil.java6
-rw-r--r--config/src/main/java/com/yahoo/config/subscription/ConfigSet.java1
-rw-r--r--config/src/main/java/com/yahoo/config/subscription/impl/ConfigSetSubscription.java2
-rw-r--r--config/src/main/java/com/yahoo/config/subscription/impl/ConfigSubscription.java4
-rw-r--r--config/src/main/java/com/yahoo/config/subscription/impl/JRTConfigSubscription.java5
-rw-r--r--config/src/main/java/com/yahoo/config/subscription/impl/JarConfigSubscription.java3
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/ConfigDefinition.java169
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/ConfigDefinitionBuilder.java22
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/ConfigDefinitionSet.java64
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/ConfigFileFormat.java4
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/ConfigHelper.java51
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/ConfigPayloadApplier.java2
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/ConfigVerification.java42
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/GenericConfig.java9
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/JRTConnection.java6
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/TimingValues.java2
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/benchmark/StressTester.java1
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/buildergen/ConfigCompiler.java1
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/buildergen/ConfigDefinitionClass.java8
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/buildergen/LazyConfigCompiler.java3
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/protocol/SlimeConfigResponse.java3
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/protocol/SlimeResponseData.java2
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/protocol/Utf8SerializedString.java87
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/util/ConfigUtils.java176
-rw-r--r--config/src/test/java/com/yahoo/config/subscription/AppService.java34
-rw-r--r--config/src/test/java/com/yahoo/config/subscription/ConfigGetterTest.java4
-rw-r--r--config/src/test/java/com/yahoo/config/subscription/ConfigInstanceSerializerTest.java10
-rw-r--r--config/src/test/java/com/yahoo/config/subscription/ConfigInstanceTest.java10
-rw-r--r--config/src/test/java/com/yahoo/config/subscription/ConfigSetSubscriptionTest.java4
-rwxr-xr-xconfig/src/test/java/com/yahoo/config/subscription/ConfigSourceSetTest.java29
-rw-r--r--config/src/test/java/com/yahoo/config/subscription/ConfigSubscriptionTest.java5
-rw-r--r--config/src/test/java/com/yahoo/config/subscription/FunctionTest.java8
-rw-r--r--config/src/test/java/com/yahoo/vespa/config/ConfigBuilderMergeTest.java4
-rwxr-xr-xconfig/src/test/java/com/yahoo/vespa/config/ConfigCacheKeyTest.java10
-rw-r--r--config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionBuilderTest.java6
-rw-r--r--config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionKeyTest.java8
-rw-r--r--config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionSetTest.java55
-rwxr-xr-xconfig/src/test/java/com/yahoo/vespa/config/ConfigDefinitionTest.java31
-rw-r--r--config/src/test/java/com/yahoo/vespa/config/ConfigFileFormatterTest.java7
-rw-r--r--config/src/test/java/com/yahoo/vespa/config/ConfigHelperTest.java31
-rw-r--r--config/src/test/java/com/yahoo/vespa/config/ConfigKeyTest.java18
-rw-r--r--config/src/test/java/com/yahoo/vespa/config/ConfigPayloadTest.java77
-rw-r--r--config/src/test/java/com/yahoo/vespa/config/JRTConnectionPoolTest.java2
-rw-r--r--config/src/test/java/com/yahoo/vespa/config/RawConfigTest.java5
-rw-r--r--config/src/test/java/com/yahoo/vespa/config/protocol/PayloadTest.java21
-rw-r--r--config/src/test/java/com/yahoo/vespa/config/util/ConfigUtilsTest.java92
-rw-r--r--config/src/testrun/.gitignore10
-rw-r--r--config/src/vespa/config/subscription/confighandle.hpp2
-rwxr-xr-xconfiggen/bin/make-config.pl151
-rwxr-xr-xconfiggen/bin/make-configold.pl150
-rw-r--r--configgen/src/main/java/com/yahoo/config/codegen/CppClassBuilder.java17
-rw-r--r--configgen/src/main/java/com/yahoo/config/codegen/JavaClassBuilder.java2
-rw-r--r--configgen/src/main/java/com/yahoo/config/codegen/MakeConfig.java11
-rw-r--r--configgen/src/main/java/com/yahoo/config/codegen/MakeConfigProperties.java16
-rwxr-xr-xconfiggen/src/main/resources/make-config-preproc.pl952
-rw-r--r--configgen/src/test/java/com/yahoo/config/codegen/MakeConfigTest.java11
-rw-r--r--configserver-flags/pom.xml6
-rw-r--r--configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/DefinedFlag.java (renamed from configserver/src/main/java/com/yahoo/vespa/config/server/http/flags/DefinedFlag.java)6
-rw-r--r--configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/DefinedFlags.java (renamed from configserver/src/main/java/com/yahoo/vespa/config/server/http/flags/DefinedFlags.java)8
-rw-r--r--configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/ErrorResponse.java66
-rw-r--r--configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/FlagDataListResponse.java (renamed from configserver/src/main/java/com/yahoo/vespa/config/server/http/flags/FlagDataListResponse.java)5
-rw-r--r--configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/FlagDataResponse.java (renamed from configserver/src/main/java/com/yahoo/vespa/config/server/http/flags/FlagDataResponse.java)5
-rw-r--r--configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/FlagsHandler.java (renamed from configserver/src/main/java/com/yahoo/vespa/config/server/http/flags/FlagsHandler.java)68
-rw-r--r--configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/OKResponse.java (renamed from configserver/src/main/java/com/yahoo/vespa/config/server/http/flags/OKResponse.java)5
-rw-r--r--configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/SlimeJsonResponse.java38
-rw-r--r--configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/V1Response.java46
-rw-r--r--configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/package-info.java8
-rw-r--r--configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/package-info.java2
-rw-r--r--configserver-flags/src/test/java/com/yahoo/vespa/configserver/flags/ConfigServerFlagSourceTest.java2
-rw-r--r--configserver-flags/src/test/java/com/yahoo/vespa/configserver/flags/http/FlagsHandlerTest.java (renamed from configserver/src/test/java/com/yahoo/vespa/config/server/http/flags/FlagsHandlerTest.java)18
-rw-r--r--configserver/pom.xml6
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java64
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java17
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java8
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClient.java16
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpErrorResponse.java7
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpHandler.java3
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/flags/V1Response.java34
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java10
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/MetricsResponse.java56
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/metrics/ClusterInfo.java56
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/metrics/MetricsAggregator.java86
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/metrics/MetricsRetriever.java97
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/monitoring/ZKMetricUpdater.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSession.java5
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionRepo.java47
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionStateWatcher.java6
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionRepo.java20
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionStateWatcher.java10
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionFactoryImpl.java3
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepo.java100
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/Tenant.java11
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantBuilder.java16
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java3
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ConfigCurator.java14
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/serviceview/ConfigServerLocation.java18
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/serviceview/StateResource.java14
-rw-r--r--configserver/src/main/resources/configserver-app/services.xml5
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/InjectedGlobalComponentRegistryTest.java7
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/MemoryGenerationCounter.java4
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/MiscTestCase.java3
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/MockReloadHandler.java3
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/ModelFactoryRegistryTest.java4
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/PortRangeAllocator.java3
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/SuperModelControllerTest.java14
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/SuperModelRequestHandlerTest.java14
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/TimeoutBudgetTest.java12
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationMapperTest.java32
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationSetTest.java17
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationTest.java21
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/application/CompressedApplicationInputStreamTest.java11
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/application/ConfigConvergenceCheckerTest.java22
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/application/FileDistributionStatusTest.java2
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/application/MockModel.java1
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/application/OrchestratorMock.java1
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java4
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/configchange/ConfigChangeActionsBuilder.java7
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/configchange/MockConfigChangeAction.java2
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/configchange/RefeedActionsTest.java4
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/configchange/RestartActionsTest.java4
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployHandlerLoggerTest.java2
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java2
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/deploy/MockDeployer.java3
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/deploy/RedeployTest.java1
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClientTest.java16
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperDeployerTest.java4
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/host/HostRegistryTest.java23
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/ContentHandlerTestBase.java11
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpConfigRequestTest.java5
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpErrorResponseTest.java3
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpGetConfigHandlerTest.java12
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpHandlerTest.java2
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpListConfigsHandlerTest.java15
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/SessionHandlerTest.java37
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationContentHandlerTest.java8
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/HttpGetConfigHandlerTest.java4
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/HttpListConfigsHandlerTest.java16
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionActiveHandlerTest.java61
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionContentHandlerTest.java21
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java16
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareHandlerTest.java46
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/TenantHandlerTest.java3
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/maintenance/MaintainerTester.java4
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/metrics/MetricsRetrieverTest.java99
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/model/LbServicesProducerTest.java3
-rwxr-xr-xconfigserver/src/test/java/com/yahoo/vespa/config/server/model/RoutingProducerTest.java2
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/monitoring/ZKMetricUpdaterTest.java3
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/rpc/DelayedConfigResponseTest.java6
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/rpc/MockRpc.java9
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionRepoTest.java95
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionTest.java30
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/MockSessionZKClient.java6
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionRepoTest.java12
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionTest.java22
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionPreparerTest.java34
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionRepoTest.java3
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionZooKeeperClientTest.java10
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandlerTest.java31
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantTest.java1
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/zookeeper/InitializedCounterTest.java2
-rw-r--r--configserver/src/test/resources/configdefinitions/datastructures.def8
-rw-r--r--configserver/src/test/resources/configdefinitions/function-test.def53
-rw-r--r--configserver/src/test/resources/configdefinitions/unicode.def5
-rw-r--r--configserver/src/test/resources/metrics/container_metrics41
-rw-r--r--configserver/src/test/resources/metrics/content_metrics16
-rw-r--r--container-dependency-versions/pom.xml2
-rw-r--r--container-di/src/main/java/com/yahoo/container/di/componentgraph/core/ComponentGraph.java24
-rw-r--r--container-di/src/main/java/com/yahoo/container/di/componentgraph/cycle/CycleFinder.java98
-rw-r--r--container-di/src/main/java/com/yahoo/container/di/componentgraph/cycle/Graph.java43
-rw-r--r--container-di/src/test/java/com/yahoo/container/di/componentgraph/core/ComponentGraphTest.java1
-rw-r--r--container-di/src/test/java/com/yahoo/container/di/componentgraph/cycle/CycleFinderTest.java87
-rw-r--r--container-di/src/test/java/com/yahoo/container/di/componentgraph/cycle/GraphTest.java69
-rw-r--r--container-search/src/main/java/com/yahoo/search/handler/SearchHandler.java6
-rw-r--r--container-search/src/main/resources/configdefinitions/query-profiles.def4
-rw-r--r--controller-api/pom.xml6
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/ClusterMetrics.java40
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServer.java3
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServerException.java3
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/LoadBalancer.java11
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/metrics/ConfigServerMetricsService.java68
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/metrics/MetricsService.java (renamed from controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/MetricsService.java)2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/metrics/package-info.java5
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeHistory.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/Issue.java3
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java50
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshotConsumer.java8
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockResourceSnapshotConsumer.java16
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneRegistry.java5
-rw-r--r--controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/integration/metrics/ConfigServerMetricsServiceTest.java81
-rw-r--r--controller-server/pom.xml7
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java91
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/AssignedRotation.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java17
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointId.java13
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java1
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/RoutingId.java17
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/RoutingPolicy.java21
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzFacade.java5
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/AthenzDbMock.java13
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/ZmsClientMock.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java25
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Versions.java17
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterUtilizationMaintainer.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentExpirer.java1
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainer.java8
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java32
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RoutingPolicies.java55
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java41
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializer.java29
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/flags/AuditedFlagsHandler.java30
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VersionStatus.java3
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java3
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java29
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalDeploymentTester.java13
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java9
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java6
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/MetricsServiceMock.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java7
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainerTest.java25
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/RoutingPoliciesTest.java139
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java41
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java10
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerControllerTester.java4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerTester.java1
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java6
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java64
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java24
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/jobs-direct-deployment.json79
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/flags/AuditedFlagsApiTest.java57
-rw-r--r--defaults/src/main/java/com/yahoo/vespa/defaults/Defaults.java6
-rw-r--r--dist/vespa.spec1
-rw-r--r--document/abi-spec.json717
-rw-r--r--document/src/main/java/com/yahoo/document/select/Context.java6
-rw-r--r--document/src/main/java/com/yahoo/document/select/DocumentSelector.java2
-rw-r--r--document/src/main/java/com/yahoo/document/select/NowCheckVisitor.java14
-rw-r--r--document/src/main/java/com/yahoo/document/select/Result.java4
-rw-r--r--document/src/main/java/com/yahoo/document/select/ResultList.java22
-rw-r--r--document/src/main/java/com/yahoo/document/select/convert/NowQueryExpression.java2
-rw-r--r--document/src/main/java/com/yahoo/document/select/convert/NowQueryNode.java4
-rw-r--r--document/src/main/java/com/yahoo/document/select/convert/SelectionExpressionConverter.java21
-rw-r--r--document/src/main/java/com/yahoo/document/select/convert/package-info.java2
-rw-r--r--document/src/main/java/com/yahoo/document/select/parser/SelectParserUtils.java2
-rw-r--r--document/src/main/java/com/yahoo/document/select/parser/package-info.java2
-rw-r--r--document/src/main/java/com/yahoo/document/select/rule/ArithmeticNode.java21
-rw-r--r--document/src/main/java/com/yahoo/document/select/rule/AttributeNode.java29
-rw-r--r--document/src/main/java/com/yahoo/document/select/rule/ComparisonNode.java58
-rw-r--r--document/src/main/java/com/yahoo/document/select/rule/DocumentNode.java8
-rw-r--r--document/src/main/java/com/yahoo/document/select/rule/EmbracedNode.java5
-rw-r--r--document/src/main/java/com/yahoo/document/select/rule/ExpressionNode.java8
-rw-r--r--document/src/main/java/com/yahoo/document/select/rule/IdNode.java12
-rw-r--r--document/src/main/java/com/yahoo/document/select/rule/LogicNode.java94
-rw-r--r--document/src/main/java/com/yahoo/document/select/rule/NegationNode.java6
-rw-r--r--document/src/main/java/com/yahoo/document/select/rule/NowNode.java16
-rw-r--r--document/src/main/java/com/yahoo/document/select/rule/SearchColumnNode.java11
-rw-r--r--document/src/main/java/com/yahoo/document/select/rule/package-info.java2
-rw-r--r--document/src/main/java/com/yahoo/document/select/simple/IdSpecParser.java2
-rw-r--r--document/src/main/java/com/yahoo/document/select/simple/StringParser.java2
-rw-r--r--document/src/main/java/com/yahoo/document/select/simple/package-info.java2
-rw-r--r--document/src/test/java/com/yahoo/document/select/LogicalNodeTestCase.java126
-rw-r--r--document/src/tests/documenttestcase.cpp2
-rw-r--r--document/src/tests/gid_filter_test.cpp6
-rw-r--r--document/src/tests/repo/documenttyperepo_test.cpp2
-rw-r--r--document/src/vespa/document/repo/configbuilder.cpp4
-rw-r--r--document/src/vespa/document/repo/configbuilder.h10
-rw-r--r--document/src/vespa/document/repo/documenttyperepo.cpp14
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/SyncParameters.java3
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/SyncSession.java21
-rw-r--r--documentapi/src/main/java/com/yahoo/documentapi/messagebus/MessageBusAsyncSession.java10
-rwxr-xr-xdocumentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/PolicyTestCase.java36
-rw-r--r--eval/src/tests/eval/gbdt/gbdt_benchmark.cpp2
-rw-r--r--eval/src/tests/tensor/dense_add_dimension_optimizer/dense_add_dimension_optimizer_test.cpp16
-rw-r--r--eval/src/tests/tensor/dense_fast_rename_optimizer/dense_fast_rename_optimizer_test.cpp4
-rw-r--r--eval/src/tests/tensor/dense_inplace_join_function/dense_inplace_join_function_test.cpp13
-rw-r--r--eval/src/tests/tensor/dense_inplace_map_function/dense_inplace_map_function_test.cpp4
-rw-r--r--eval/src/tests/tensor/dense_remove_dimension_optimizer/dense_remove_dimension_optimizer_test.cpp4
-rw-r--r--eval/src/tests/tensor/dense_xw_product_function/dense_xw_product_function_test.cpp15
-rw-r--r--eval/src/vespa/eval/eval/value_type.cpp40
-rw-r--r--eval/src/vespa/eval/eval/value_type.h16
-rw-r--r--eval/src/vespa/eval/tensor/default_tensor_engine.cpp36
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_add_dimension_optimizer.cpp28
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_dot_product_function.cpp10
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_fast_rename_optimizer.cpp14
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_generic_join.hpp2
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_inplace_join_function.cpp71
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_inplace_map_function.cpp25
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_remove_dimension_optimizer.cpp12
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_tensor_view.cpp5
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_tensor_view.h7
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_xw_product_function.cpp116
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_xw_product_function.h3
-rw-r--r--eval/src/vespa/eval/tensor/dense/typed_cells.h29
-rw-r--r--fastlib/src/vespa/fastlib/io/bufferedfile.cpp10
-rw-r--r--fbench/README47
-rw-r--r--fbench/src/fbench/client.cpp6
-rw-r--r--fbench/src/fbench/fbench.cpp20
-rw-r--r--fbench/src/fbench/fbench.h3
-rw-r--r--filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReceiver.java24
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/Flags.java13
-rw-r--r--fnet/src/tests/frt/method_pt/method_pt.cpp18
-rw-r--r--functions.cmake2
-rw-r--r--http-utils/src/main/java/ai/vespa/util/http/VespaHttpClientBuilder.java66
-rw-r--r--http-utils/src/test/java/ai/vespa/util/http/VespaHttpClientBuilderTest.java40
-rw-r--r--jaxrs_client_utils/pom.xml9
-rw-r--r--jaxrs_client_utils/src/main/java/ai/vespa/util/http/VespaClientBuilderFactory.java72
-rw-r--r--jaxrs_client_utils/src/main/java/ai/vespa/util/http/package-info.java8
-rw-r--r--jaxrs_client_utils/src/main/java/com/yahoo/vespa/jaxrs/client/VespaJerseyJaxRsClientFactory.java55
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/routing/RoutingNode.java4
-rw-r--r--messagebus/src/vespa/messagebus/message.h6
-rw-r--r--metrics/src/vespa/metrics/metric.cpp1
-rw-r--r--metrics/src/vespa/metrics/xmlwriter.cpp6
-rw-r--r--model-integration/src/main/java/ai/vespa/rankingexpression/importer/DimensionRenamer.java4
-rw-r--r--model-integration/src/main/java/ai/vespa/rankingexpression/importer/operations/Const.java12
-rw-r--r--model-integration/src/main/java/ai/vespa/rankingexpression/importer/operations/IntermediateOperation.java28
-rw-r--r--model-integration/src/main/java/ai/vespa/rankingexpression/importer/tensorflow/GraphImporter.java1
-rw-r--r--model-integration/src/main/java/ai/vespa/rankingexpression/importer/tensorflow/TensorConverter.java64
-rw-r--r--model-integration/src/test/java/ai/vespa/rankingexpression/importer/tensorflow/DropoutImportTestCase.java2
-rw-r--r--model-integration/src/test/java/ai/vespa/rankingexpression/importer/tensorflow/RegressionTestCase.java79
-rw-r--r--model-integration/src/test/java/ai/vespa/rankingexpression/importer/tensorflow/TestableTensorFlowModel.java61
-rw-r--r--model-integration/src/test/models/tensorflow/regression/test1/saved_model.pbtxt6172
-rw-r--r--model-integration/src/test/models/tensorflow/regression/test1/variables/variables.data-00000-of-00001bin0 -> 86072 bytes
-rw-r--r--model-integration/src/test/models/tensorflow/regression/test1/variables/variables.indexbin0 -> 159 bytes
-rw-r--r--model-integration/src/test/models/tensorflow/regression/test2/saved_model.pbtxt389
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java10
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainer.java2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollector.java29
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContextImpl.java5
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImpl.java3
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/StoredInteger.java2
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollectorTest.java60
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java12
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityChecker.java526
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityReportMaintainer.java414
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRepositoryMaintenance.java6
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Agent.java2
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java2
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java33
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisioner.java2
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/HostCapacityResponse.java161
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/LoadBalancersResponse.java4
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesApiHandler.java1
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityCheckerTest.java (renamed from node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityReportMaintainerTest.java)54
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityCheckerTester.java (renamed from node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityReportMaintainerTester.java)13
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java23
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/capacity-hostremoval-impossible.json20
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/capacity-hostremoval-possible.json20
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/capacity-zone.json46
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/load-balancers-single.json4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/load-balancers.json8
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/RetryingClusterControllerClientFactory.java17
-rw-r--r--orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/RetryingClusterControllerClientFactoryTest.java4
-rw-r--r--persistence/src/vespa/persistence/conformancetest/conformancetest.cpp66
-rw-r--r--persistence/src/vespa/persistence/dummyimpl/dummypersistence.cpp24
-rw-r--r--persistence/src/vespa/persistence/spi/result.cpp7
-rw-r--r--persistence/src/vespa/persistence/spi/result.h8
-rw-r--r--searchcommon/src/vespa/searchcommon/common/schemaconfigurer.cpp10
-rw-r--r--searchcore/src/apps/proton/downpersistence.cpp2
-rw-r--r--searchcore/src/tests/proton/documentdb/documentdbconfigscout/documentdbconfigscout_test.cpp4
-rw-r--r--searchcore/src/tests/proton/documentdb/feedhandler/feedhandler_test.cpp10
-rw-r--r--searchcore/src/tests/proton/persistenceengine/persistenceengine_test.cpp40
-rw-r--r--searchcore/src/tests/proton/server/disk_mem_usage_filter/disk_mem_usage_filter_test.cpp2
-rw-r--r--searchcore/src/vespa/searchcore/fdispatch/program/fdispatch.cpp2
-rw-r--r--searchcore/src/vespa/searchcore/fdispatch/search/configdesc.h6
-rw-r--r--searchcore/src/vespa/searchcore/proton/common/CMakeLists.txt2
-rw-r--r--searchcore/src/vespa/searchcore/proton/common/hw_info_sampler.cpp6
-rw-r--r--searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp32
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/CMakeLists.txt2
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/buckethandler.cpp2
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/disk_mem_usage_filter.h4
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/disk_mem_usage_sampler.cpp4
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/disk_mem_usage_sampler.h2
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/documentdbconfigmanager.cpp4
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/feedhandler.cpp8
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/proton.cpp10
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/searchview.h6
-rw-r--r--searchlib/src/tests/attribute/attributemanager/attributemanager_test.cpp38
-rw-r--r--searchlib/src/tests/memoryindex/field_index_remover/field_index_remover_test.cpp2
-rw-r--r--searchlib/src/tests/queryeval/queryeval.cpp7
-rw-r--r--searchlib/src/vespa/searchlib/CMakeLists.txt2
-rw-r--r--searchlib/src/vespa/searchlib/attribute/configconverter.cpp34
-rw-r--r--searchlib/src/vespa/searchlib/attribute/enumattribute.hpp8
-rw-r--r--searchlib/src/vespa/searchlib/attribute/multi_value_mapping.hpp7
-rw-r--r--searchlib/src/vespa/searchlib/attribute/singleenumattribute.hpp3
-rw-r--r--searchlib/src/vespa/searchlib/bitcompression/compression.h4
-rw-r--r--searchlib/src/vespa/searchlib/common/tunefileinfo.h10
-rw-r--r--searchlib/src/vespa/searchlib/common/tunefileinfo.hpp18
-rw-r--r--searchlib/src/vespa/searchlib/expression/functionnodes.cpp7
-rw-r--r--searchlib/src/vespa/searchlib/features/attributefeature.cpp251
-rw-r--r--searchlib/src/vespa/searchlib/features/dotproductfeature.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/fef/featureexecutor.h9
-rw-r--r--searchlib/src/vespa/searchlib/predicate/predicate_index.h2
-rw-r--r--searchlib/src/vespa/searchlib/predicate/simple_index.h4
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/predicate_blueprint.h2
-rw-r--r--searchlib/src/vespa/searchlib/transactionlog/translogserverapp.cpp4
-rwxr-xr-xsecurity-tools/src/main/sh/vespa-curl-wrapper2
-rw-r--r--staging_vespalib/src/tests/xmlserializable/xmlserializabletest.cpp2
-rw-r--r--staging_vespalib/src/vespa/vespalib/util/xmlstream.cpp2
-rw-r--r--staging_vespalib/src/vespa/vespalib/util/xmlstream.h4
-rw-r--r--storage/CMakeLists.txt2
-rw-r--r--storage/src/tests/distributor/distributortest.cpp6
-rw-r--r--storage/src/tests/persistence/common/persistenceproviderwrapper.cpp4
-rw-r--r--storage/src/tests/persistence/common/persistenceproviderwrapper.h2
-rw-r--r--storage/src/tests/persistence/mergehandlertest.cpp12
-rw-r--r--storage/src/tests/persistence/provider_error_wrapper_test.cpp10
-rw-r--r--storage/src/vespa/storage/distributor/operations/idealstate/idealstateoperation.h2
-rw-r--r--storage/src/vespa/storage/persistence/bucketprocessor.cpp4
-rw-r--r--storage/src/vespa/storage/persistence/mergehandler.cpp12
-rw-r--r--storage/src/vespa/storage/persistence/persistenceutil.cpp12
-rw-r--r--storage/src/vespa/storage/persistence/processallhandler.cpp2
-rw-r--r--storage/src/vespa/storage/persistence/provider_error_wrapper.cpp4
-rw-r--r--storageapi/src/vespa/storageapi/messageapi/maintenancecommand.h4
-rw-r--r--storageserver/src/apps/storaged/storage.cpp4
-rw-r--r--storageserver/src/vespa/storageserver/app/distributorprocess.cpp2
-rw-r--r--streamingvisitors/src/vespa/searchvisitor/rankmanager.cpp2
-rw-r--r--vdslib/src/main/java/com/yahoo/vdslib/distribution/Group.java4
-rw-r--r--vdslib/src/test/java/com/yahoo/vdslib/distribution/GroupTestCase.java2
-rw-r--r--vespa-hadoop/pom.xml11
-rw-r--r--vespa-http-client/pom.xml27
-rw-r--r--vespa-http-client/src/main/java/com/yahoo/vespa/http/client/config/ConnectionParams.java47
-rw-r--r--vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/ApacheGatewayConnection.java31
-rw-r--r--vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/VespaTlsAwareClientBuilder.java26
-rw-r--r--vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/Vtag.java2
-rw-r--r--vespa-http-client/src/main/java/com/yahoo/vespa/http/client/runner/CommandLineArguments.java13
-rw-r--r--vespabase/src/vespa-configserver.service.in1
-rw-r--r--vespabase/src/vespa.service.in1
-rw-r--r--vespaclient-container-plugin/pom.xml4
-rw-r--r--vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandler.java4
-rw-r--r--vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandlerImpl.java13
-rw-r--r--vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/RestApi.java5
-rw-r--r--vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/OperationHandlerImplTest.java21
-rw-r--r--vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/MockedOperationHandler.java13
-rw-r--r--vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/RestApiTest.java9
-rw-r--r--vespaclient/CMakeLists.txt10
-rw-r--r--vespaclient/src/perl/lib/Yahoo/Vespa/Http.pm18
-rwxr-xr-xvespaclient/src/sh/vespa-get-cluster-state75
-rwxr-xr-xvespaclient/src/sh/vespa-get-node-state75
-rwxr-xr-xvespaclient/src/sh/vespa-set-node-state75
-rw-r--r--vespalib/src/tests/net/tls/openssl_impl/openssl_impl_test.cpp2
-rw-r--r--vespalib/src/vespa/vespalib/CMakeLists.txt2
-rw-r--r--vespalib/src/vespa/vespalib/btree/btreenode.h1
-rw-r--r--vespalib/src/vespa/vespalib/net/socket_address.cpp1
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/crypto_codec.cpp6
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/crypto_codec.h5
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.cpp30
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.h6
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.cpp23
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.h3
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.cpp2
-rw-r--r--vespalib/src/vespa/vespalib/stllike/hashtable.cpp1
-rw-r--r--vespamalloc/src/tests/doubledelete/doubledelete.cpp5
-rw-r--r--vespamalloc/src/tests/test1/testatomic.cpp4
-rw-r--r--vespamalloc/src/vespamalloc/malloc/common.h2
-rw-r--r--vespamalloc/src/vespamalloc/malloc/globalpool.h5
-rw-r--r--vespamalloc/src/vespamalloc/malloc/globalpool.hpp8
-rw-r--r--vespamalloc/src/vespamalloc/malloc/memblock.h4
-rw-r--r--vespamalloc/src/vespamalloc/malloc/memblock.hpp7
-rw-r--r--vespamalloc/src/vespamalloc/malloc/overload.h19
-rw-r--r--vespamalloc/src/vespamalloc/malloc/threadlist.h3
-rw-r--r--vespamalloc/src/vespamalloc/malloc/threadlist.hpp2
-rw-r--r--vsm/src/tests/searcher/searcher.cpp6
-rw-r--r--vsm/src/vespa/vsm/vsm/docsumfieldspec.cpp2
-rw-r--r--vsm/src/vespa/vsm/vsm/docsumfilter.cpp12
-rw-r--r--vsm/src/vespa/vsm/vsm/fieldsearchspec.cpp36
-rw-r--r--vsm/src/vespa/vsm/vsm/vsm-adapter.cpp2
681 files changed, 13546 insertions, 6980 deletions
diff --git a/application/pom.xml b/application/pom.xml
index f9f3e8394af..20b754fc236 100644
--- a/application/pom.xml
+++ b/application/pom.xml
@@ -134,7 +134,12 @@
<artifactId>httpclient</artifactId>
<scope>test</scope>
</dependency>
-
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>testutil</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
<!-- These dependencies are necessary in test classpath when using jdisc_http_filters -->
<dependency>
diff --git a/application/src/test/java/com/yahoo/application/container/ContainerModelEvaluationTest.java b/application/src/test/java/com/yahoo/application/container/ContainerModelEvaluationTest.java
index 66a7ae579fa..6a2b7945d73 100644
--- a/application/src/test/java/com/yahoo/application/container/ContainerModelEvaluationTest.java
+++ b/application/src/test/java/com/yahoo/application/container/ContainerModelEvaluationTest.java
@@ -7,6 +7,7 @@ import com.yahoo.application.container.handler.Request;
import com.yahoo.application.container.handler.Response;
import com.yahoo.tensor.Tensor;
import com.yahoo.tensor.TensorType;
+import com.yahoo.test.json.JsonTestHelper;
import org.junit.Ignore;
import org.junit.Test;
@@ -64,7 +65,7 @@ public class ContainerModelEvaluationTest {
private void assertResponse(String url, String expectedResponse, JDisc jdisc) {
try {
Response response = jdisc.handleRequest(new Request(url));
- assertEquals(expectedResponse, response.getBodyAsString());
+ JsonTestHelper.assertJsonEquals(expectedResponse, response.getBodyAsString());
assertEquals(200, response.getStatus());
}
catch (CharacterCodingException e) {
diff --git a/build_settings.cmake b/build_settings.cmake
index 7d8ba11f8a1..b1b0b065771 100644
--- a/build_settings.cmake
+++ b/build_settings.cmake
@@ -21,20 +21,45 @@ set(C_WARN_OPTS "-Winline -Wuninitialized -Werror -Wall -W -Wchar-subscripts -Wc
# Warnings that are specific to C++ compilation
# Note: this is not a union of C_WARN_OPTS, since CMAKE_CXX_FLAGS already includes CMAKE_C_FLAGS, which in turn includes C_WARN_OPTS transitively
-if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
set(CXX_SPECIFIC_WARN_OPTS "-Wnon-virtual-dtor -Wformat-security -Wno-overloaded-virtual")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-delete-null-pointer-checks")
+ if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
+ set(VESPA_ATOMIC_LIB "")
+ set(VESPA_GCC_LIB "")
+ set(VESPA_STDCXX_FS_LIB "")
+ else()
+ set(VESPA_ATOMIC_LIB "atomic")
+ if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.0)
+ set(VESPA_GCC_LIB "gcc")
+ set(VESPA_STDCXX_FS_LIB "stdc++fs")
+ else()
+ set(VESPA_GCC_LIB "")
+ set(VESPA_STDCXX_FS_LIB "")
+ endif()
+ endif()
else()
set(CXX_SPECIFIC_WARN_OPTS "-Wsuggest-override -Wnon-virtual-dtor -Wformat-security")
+ set(VESPA_ATOMIC_LIB "atomic")
+ set(VESPA_GCC_LIB "gcc")
+ set(VESPA_STDCXX_FS_LIB "stdc++fs")
endif()
# C and C++ compiler flags
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O3 -fno-omit-frame-pointer ${C_WARN_OPTS} -fPIC ${VESPA_CXX_ABI_FLAGS} -DBOOST_DISABLE_ASSERTS ${VESPA_CPU_ARCH_FLAGS} -mtune=intel ${EXTRA_C_FLAGS}")
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_C_FLAGS} ${CXX_SPECIFIC_WARN_OPTS} -std=c++1z -fvisibility-inlines-hidden -fdiagnostics-color=auto ${EXTRA_CXX_FLAGS}")
+if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_C_FLAGS} ${CXX_SPECIFIC_WARN_OPTS} -std=c++1z -fdiagnostics-color=auto ${EXTRA_CXX_FLAGS}")
+else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_C_FLAGS} ${CXX_SPECIFIC_WARN_OPTS} -std=c++1z -fvisibility-inlines-hidden -fdiagnostics-color=auto ${EXTRA_CXX_FLAGS}")
+endif()
# Linker flags
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
- set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -latomic -ldl")
+ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
+ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -ldl")
+ else()
+ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -latomic -ldl")
+ endif()
else()
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--build-id -latomic -ldl -Wl,-E")
endif()
diff --git a/bundle-plugin-test/src/test/java/com/yahoo/BundleIT.java b/bundle-plugin-test/src/test/java/com/yahoo/BundleIT.java
index 5b4fdda06f7..932b78a5c8d 100644
--- a/bundle-plugin-test/src/test/java/com/yahoo/BundleIT.java
+++ b/bundle-plugin-test/src/test/java/com/yahoo/BundleIT.java
@@ -18,6 +18,7 @@ import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
+import static com.yahoo.osgi.maven.ProjectBundleClassPaths.CLASSPATH_MAPPINGS_FILENAME;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.containsString;
@@ -143,9 +144,9 @@ public class BundleIT {
@SuppressWarnings("unchecked")
@Test
public void bundle_class_path_mappings_are_generated() throws URISyntaxException, IOException {
- URL mappingsUrl = getClass().getResource("/" + com.yahoo.osgi.maven.ProjectBundleClassPaths.CLASSPATH_MAPPINGS_FILENAME);
+ URL mappingsUrl = getClass().getResource("/" + CLASSPATH_MAPPINGS_FILENAME);
assertNotNull(
- "Could not find " + ProjectBundleClassPaths.CLASSPATH_MAPPINGS_FILENAME + " in the test output directory",
+ "Could not find " + CLASSPATH_MAPPINGS_FILENAME + " in the test output directory",
mappingsUrl);
ProjectBundleClassPaths bundleClassPaths = ProjectBundleClassPaths.load(Paths.get(mappingsUrl.toURI()));
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/bundle/AnalyzeBundle.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/bundle/AnalyzeBundle.java
index 798fea2644e..0626c786822 100644
--- a/bundle-plugin/src/main/java/com/yahoo/container/plugin/bundle/AnalyzeBundle.java
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/bundle/AnalyzeBundle.java
@@ -11,10 +11,14 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import java.util.jar.Manifest;
import java.util.stream.Collectors;
/**
+ * "Public package" in this context means a package that is either exported or declared as "Global-Package"
+ * in a bundle's manifest.
+ *
* @author Tony Vaagenes
* @author ollivir
*/
@@ -23,10 +27,18 @@ public class AnalyzeBundle {
public final List<Export> exports;
public final List<String> globals;
- public PublicPackages(List<Export> exports, List<String> globals) {
+ PublicPackages(List<Export> exports, List<String> globals) {
this.exports = exports;
this.globals = globals;
}
+
+ public Set<String> exportedPackageNames() {
+ return exports.stream()
+ .map(Export::getPackageNames)
+ .flatMap(Collection::stream)
+ .collect(Collectors.toSet());
+ }
+
}
public static PublicPackages publicPackagesAggregated(Collection<File> jarFiles) {
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/PackageTally.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/PackageTally.java
index 13bbc63192c..9f5fd2236e7 100644
--- a/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/PackageTally.java
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/PackageTally.java
@@ -1,6 +1,7 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.plugin.classanalysis;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Sets;
import com.yahoo.container.plugin.util.Maps;
@@ -10,6 +11,7 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* @author Tony Vaagenes
@@ -19,7 +21,8 @@ public class PackageTally {
private final Map<String, Optional<ExportPackageAnnotation>> definedPackagesMap;
private final Set<String> referencedPackagesUnfiltered;
- public PackageTally(Map<String, Optional<ExportPackageAnnotation>> definedPackagesMap, Set<String> referencedPackagesUnfiltered) {
+ @VisibleForTesting
+ PackageTally(Map<String, Optional<ExportPackageAnnotation>> definedPackagesMap, Set<String> referencedPackagesUnfiltered) {
this.definedPackagesMap = definedPackagesMap;
this.referencedPackagesUnfiltered = referencedPackagesUnfiltered;
}
@@ -41,6 +44,19 @@ public class PackageTally {
}
/**
+ * Returns the set of packages that is referenced from this tally, but not included in the given set of available packages.
+ *
+ * @param definedAndExportedPackages Set of available packages (usually all packages defined in the generated bundle's project + all exported packages of dependencies)
+ * @return The set of missing packages, that may cause problem when the bundle is deployed in an OSGi container runtime.
+ */
+ public Set<String> referencedPackagesMissingFrom(Set<String> definedAndExportedPackages) {
+ return Sets.difference(referencedPackages(), definedAndExportedPackages).stream()
+ .filter(pkg -> !pkg.startsWith("java."))
+ .filter(pkg -> !pkg.equals(com.yahoo.api.annotations.PublicApi.class.getPackageName()))
+ .collect(Collectors.toSet());
+ }
+
+ /**
* Represents the classes for two package tallies that are deployed as a single unit.
* <p>
* ExportPackageAnnotations from this has precedence over the other.
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/Artifacts.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/Artifacts.java
index fff88d413d0..a0d0e143724 100644
--- a/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/Artifacts.java
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/Artifacts.java
@@ -12,8 +12,8 @@ import java.util.List;
* @author Tony Vaagenes
* @author ollivir
*/
-public class Artifacts {
- public static class ArtifactSet {
+class Artifacts {
+ static class ArtifactSet {
private final List<Artifact> jarArtifactsToInclude;
private final List<Artifact> jarArtifactsProvided;
private final List<Artifact> nonJarArtifacts;
@@ -24,20 +24,20 @@ public class Artifacts {
this.nonJarArtifacts = nonJarArtifacts;
}
- public List<Artifact> getJarArtifactsToInclude() {
+ List<Artifact> getJarArtifactsToInclude() {
return jarArtifactsToInclude;
}
- public List<Artifact> getJarArtifactsProvided() {
+ List<Artifact> getJarArtifactsProvided() {
return jarArtifactsProvided;
}
- public List<Artifact> getNonJarArtifacts() {
+ List<Artifact> getNonJarArtifacts() {
return nonJarArtifacts;
}
}
- public static ArtifactSet getArtifacts(MavenProject project) {
+ static ArtifactSet getArtifacts(MavenProject project) {
List<Artifact> jarArtifactsToInclude = new ArrayList<>();
List<Artifact> jarArtifactsProvided = new ArrayList<>();
@@ -63,7 +63,7 @@ public class Artifacts {
return new ArtifactSet(jarArtifactsToInclude, jarArtifactsProvided, nonJarArtifactsToInclude);
}
- public static Collection<Artifact> getArtifactsToInclude(MavenProject project) {
+ static Collection<Artifact> getArtifactsToInclude(MavenProject project) {
return getArtifacts(project).getJarArtifactsToInclude();
}
}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/GenerateOsgiManifestMojo.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/GenerateOsgiManifestMojo.java
index 9c9957ae718..04f2428d284 100644
--- a/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/GenerateOsgiManifestMojo.java
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/GenerateOsgiManifestMojo.java
@@ -10,14 +10,12 @@ import com.yahoo.container.plugin.classanalysis.PackageTally;
import com.yahoo.container.plugin.osgi.ExportPackageParser;
import com.yahoo.container.plugin.osgi.ExportPackages;
import com.yahoo.container.plugin.osgi.ExportPackages.Export;
-import com.yahoo.container.plugin.osgi.ImportPackages;
import com.yahoo.container.plugin.osgi.ImportPackages.Import;
import com.yahoo.container.plugin.util.Strings;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
-import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
@@ -41,6 +39,9 @@ import java.util.jar.Manifest;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import static com.yahoo.container.plugin.bundle.AnalyzeBundle.publicPackagesAggregated;
+import static com.yahoo.container.plugin.osgi.ExportPackages.exportsByPackageName;
+import static com.yahoo.container.plugin.osgi.ImportPackages.calculateImports;
import static com.yahoo.container.plugin.util.Files.allDescendantFiles;
import static com.yahoo.container.plugin.util.IO.withFileOutputStream;
import static com.yahoo.container.plugin.util.JarFiles.withInputStream;
@@ -90,32 +91,43 @@ public class GenerateOsgiManifestMojo extends AbstractMojo {
@Parameter(alias = "X-Jersey-Binding")
private String jerseyBinding = null;
- public void execute() throws MojoExecutionException, MojoFailureException {
+ public void execute() throws MojoExecutionException {
try {
Artifacts.ArtifactSet artifactSet = Artifacts.getArtifacts(project);
warnOnUnsupportedArtifacts(artifactSet.getNonJarArtifacts());
- AnalyzeBundle.PublicPackages publicPackagesFromProvidedJars = AnalyzeBundle.publicPackagesAggregated(
+ // Packages from Export-Package and Global-Package headers in provided scoped jars
+ AnalyzeBundle.PublicPackages publicPackagesFromProvidedJars = publicPackagesAggregated(
artifactSet.getJarArtifactsProvided().stream().map(Artifact::getFile).collect(Collectors.toList()));
- PackageTally includedJarPackageTally = definedPackages(artifactSet.getJarArtifactsToInclude());
- PackageTally projectPackageTally = analyzeProjectClasses();
- PackageTally pluginPackageTally = projectPackageTally.combine(includedJarPackageTally);
+ // Packages from Export-Package headers in provided scoped jars
+ Set<String> exportedPackagesFromProvidedDeps = publicPackagesFromProvidedJars.exportedPackageNames();
- Set<String> definedPackages = new HashSet<>(projectPackageTally.definedPackages());
- definedPackages.addAll(includedJarPackageTally.definedPackages());
+ // Packaged defined in this project's code
+ PackageTally projectPackages = getProjectClassesTally();
- warnIfPackagesDefinedOverlapsGlobalPackages(definedPackages, publicPackagesFromProvidedJars.globals);
+ // Packages defined in compile scoped jars
+ PackageTally compileJarsPackages = definedPackages(artifactSet.getJarArtifactsToInclude());
- if (getLog().isDebugEnabled()) {
- getLog().debug("Referenced packages = " + pluginPackageTally.referencedPackages());
- getLog().debug("Defined packages = " + pluginPackageTally.definedPackages());
- getLog().debug("Exported packages of dependencies = " + publicPackagesFromProvidedJars.exports.stream()
- .map(e -> "(" + e.getPackageNames().toString() + ", " + e.version().orElse("")).collect(Collectors.joining(", ")));
+ // The union of packages in the project and compile scoped jars
+ PackageTally includedPackages = projectPackages.combine(compileJarsPackages);
+
+ warnIfPackagesDefinedOverlapsGlobalPackages(includedPackages.definedPackages(), publicPackagesFromProvidedJars.globals);
+ logDebugPackageSets(publicPackagesFromProvidedJars, includedPackages);
+
+ if (hasJdiscCoreProvided(artifactSet.getJarArtifactsProvided())) {
+ // jdisc_core being provided guarantees that log output does not contain its exported packages
+ logMissingPackages(exportedPackagesFromProvidedDeps, projectPackages, compileJarsPackages, includedPackages);
+ } else {
+ getLog().warn("This project does not have jdisc_core as provided dependency, so the " +
+ "generated 'Import-Package' OSGi header may be missing important packages.");
}
+ logOverlappingPackages(projectPackages, exportedPackagesFromProvidedDeps);
+ logUnnecessaryPackages(compileJarsPackages, exportedPackagesFromProvidedDeps);
- Map<String, Import> calculatedImports = ImportPackages.calculateImports(pluginPackageTally.referencedPackages(),
- pluginPackageTally.definedPackages(), ExportPackages.exportsByPackageName(publicPackagesFromProvidedJars.exports));
+ Map<String, Import> calculatedImports = calculateImports(includedPackages.referencedPackages(),
+ includedPackages.definedPackages(),
+ exportsByPackageName(publicPackagesFromProvidedJars.exports));
Map<String, Optional<String>> manualImports = emptyToNone(importPackage).map(GenerateOsgiManifestMojo::getManualImports)
.orElseGet(HashMap::new);
@@ -123,17 +135,74 @@ public class GenerateOsgiManifestMojo extends AbstractMojo {
calculatedImports.remove(packageName);
}
createManifestFile(new File(project.getBuild().getOutputDirectory()), manifestContent(project,
- artifactSet.getJarArtifactsToInclude(), manualImports, calculatedImports.values(), pluginPackageTally));
+ artifactSet.getJarArtifactsToInclude(), manualImports, calculatedImports.values(), includedPackages));
} catch (Exception e) {
- throw new MojoExecutionException("Failed generating osgi manifest.", e);
+ throw new MojoExecutionException("Failed generating osgi manifest", e);
+ }
+ }
+
+ private void logDebugPackageSets(AnalyzeBundle.PublicPackages publicPackagesFromProvidedJars, PackageTally includedPackages) {
+ if (getLog().isDebugEnabled()) {
+ getLog().debug("Referenced packages = " + includedPackages.referencedPackages());
+ getLog().debug("Defined packages = " + includedPackages.definedPackages());
+ getLog().debug("Exported packages of dependencies = " + publicPackagesFromProvidedJars.exports.stream()
+ .map(e -> "(" + e.getPackageNames().toString() + ", " + e.version().orElse("")).collect(Collectors.joining(", ")));
+ }
+ }
+
+ private boolean hasJdiscCoreProvided(List<Artifact> providedArtifacts) {
+ return providedArtifacts.stream().anyMatch(artifact -> artifact.getArtifactId().equals("jdisc_core"));
+ }
+
+ private void logMissingPackages(Set<String> exportedPackagesFromProvidedJars,
+ PackageTally projectPackages,
+ PackageTally compileJarPackages,
+ PackageTally includedPackages) {
+
+ Set<String> definedAndExportedPackages = Sets.union(includedPackages.definedPackages(), exportedPackagesFromProvidedJars);
+
+ Set<String> missingProjectPackages = projectPackages.referencedPackagesMissingFrom(definedAndExportedPackages);
+ if (! missingProjectPackages.isEmpty()) {
+ getLog().warn("Packages unavailable runtime are referenced from project classes " +
+ "(annotations can usually be ignored): " + missingProjectPackages);
+ }
+
+ Set<String> missingCompilePackages = compileJarPackages.referencedPackagesMissingFrom(definedAndExportedPackages);
+ if (! missingCompilePackages.isEmpty()) {
+ getLog().info("Packages unavailable runtime are referenced from compile scoped jars " +
+ "(annotations can usually be ignored): " + missingCompilePackages);
+ }
+ }
+
+ private void logOverlappingPackages(PackageTally projectPackages,
+ Set<String> exportedPackagesFromProvidedDeps) {
+ Set<String> overlappingProjectPackages = Sets.intersection(projectPackages.definedPackages(), exportedPackagesFromProvidedDeps);
+ if (! overlappingProjectPackages.isEmpty()) {
+ getLog().warn("Project classes use the following packages that are already defined in provided scoped dependencies: "
+ + overlappingProjectPackages);
+ }
+ }
+
+ /*
+ * This mostly detects packages re-exported via composite bundles like jdisc_core and container-disc.
+ * An artifact can only be represented once, either in compile or provided scope. So if the project
+ * adds an artifact in compile scope that we deploy as a pre-installed bundle, we won't see the same
+ * artifact as provided via container-dev and hence can't detect the duplicate packages.
+ */
+ private void logUnnecessaryPackages(PackageTally compileJarsPackages,
+ Set<String> exportedPackagesFromProvidedDeps) {
+ Set<String> unnecessaryPackages = Sets.intersection(compileJarsPackages.definedPackages(), exportedPackagesFromProvidedDeps);
+ if (! unnecessaryPackages.isEmpty()) {
+ getLog().info("Compile scoped jars contain the following packages that are most likely " +
+ "available from jdisc runtime: " + unnecessaryPackages);
}
}
private static void warnIfPackagesDefinedOverlapsGlobalPackages(Set<String> internalPackages, List<String> globalPackages)
throws MojoExecutionException {
Set<String> overlap = Sets.intersection(internalPackages, new HashSet<>(globalPackages));
- if (overlap.isEmpty() == false) {
+ if (! overlap.isEmpty()) {
throw new MojoExecutionException(
"The following packages are both global and included in the bundle:\n " + String.join("\n ", overlap));
}
@@ -173,7 +242,7 @@ public class GenerateOsgiManifestMojo extends AbstractMojo {
Pair.of("WebInfUrl", webInfUrl), //
Pair.of("Import-Package", importPackage), //
Pair.of("Export-Package", exportPackage))) {
- if (element.getValue() != null && element.getValue().isEmpty() == false) {
+ if (element.getValue() != null && ! element.getValue().isEmpty()) {
ret.put(element.getKey(), element.getValue());
}
}
@@ -232,7 +301,7 @@ public class GenerateOsgiManifestMojo extends AbstractMojo {
}
private void warnOnUnsupportedArtifacts(Collection<Artifact> nonJarArtifacts) {
- List<Artifact> unsupportedArtifacts = nonJarArtifacts.stream().filter(a -> "pom".equals(a.getType()) == false)
+ List<Artifact> unsupportedArtifacts = nonJarArtifacts.stream().filter(a -> ! a.getType().equals("pom"))
.collect(Collectors.toList());
unsupportedArtifacts.forEach(artifact -> getLog()
@@ -240,7 +309,7 @@ public class GenerateOsgiManifestMojo extends AbstractMojo {
artifact.getId(), artifact.getType())));
}
- private PackageTally analyzeProjectClasses() {
+ private PackageTally getProjectClassesTally() {
File outputDirectory = new File(project.getBuild().getOutputDirectory());
List<ClassFileMetaData> analyzedClasses = allDescendantFiles(outputDirectory).filter(file -> file.getName().endsWith(".class"))
@@ -258,7 +327,7 @@ public class GenerateOsgiManifestMojo extends AbstractMojo {
List<ClassFileMetaData> analyzedClasses = new ArrayList<>();
for (Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements();) {
JarEntry entry = entries.nextElement();
- if (entry.isDirectory() == false && entry.getName().endsWith(".class")) {
+ if (! entry.isDirectory() && entry.getName().endsWith(".class")) {
analyzedClasses.add(analyzeClass(jarFile, entry));
}
}
@@ -305,10 +374,7 @@ public class GenerateOsgiManifestMojo extends AbstractMojo {
}
private static Optional<String> emptyToNone(String str) {
- return Optional.ofNullable(str).map(String::trim).filter(s -> s.isEmpty() == false);
+ return Optional.ofNullable(str).map(String::trim).filter(s -> ! s.isEmpty());
}
- private static boolean isClassToAnalyze(String name) {
- return name.endsWith(".class") && name.endsWith("module-info.class") == false;
- }
}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/osgi/ImportPackages.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/osgi/ImportPackages.java
index b58248ec4a6..a3031cb7ad7 100644
--- a/bundle-plugin/src/main/java/com/yahoo/container/plugin/osgi/ImportPackages.java
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/osgi/ImportPackages.java
@@ -74,8 +74,9 @@ public class ImportPackages {
}
}
- public static Map<String, Import> calculateImports(Set<String> referencedPackages, Set<String> implementedPackages,
- Map<String, ExportPackages.Export> exportedPackages) {
+ public static Map<String, Import> calculateImports(Set<String> referencedPackages,
+ Set<String> implementedPackages,
+ Map<String, ExportPackages.Export> exportedPackages) {
Map<String, Import> ret = new HashMap<>();
for (String undefinedPackage : Sets.difference(referencedPackages, implementedPackages)) {
ExportPackages.Export export = exportedPackages.get(undefinedPackage);
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/util/JdkPackages.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/util/JdkPackages.java
new file mode 100644
index 00000000000..0786272bc70
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/util/JdkPackages.java
@@ -0,0 +1,39 @@
+package com.yahoo.container.plugin.util;
+
+import java.net.URL;
+
+/**
+ * @author gjoranv
+ */
+public class JdkPackages {
+
+ /**
+ * Returns a boolean indicating (via best effort) if the given package is part of the JDK.
+ */
+ public static boolean isJdkPackage(String pkg) {
+ return hasJdkExclusivePrefix(pkg)
+ || isResourceInPlatformClassLoader(pkg); // TODO: must be a class, not a package, due to module encapsulation
+ }
+
+ private static boolean isResourceInPlatformClassLoader(String klass) {
+ String klassAsPath = klass.replaceAll("\\.", "/") + ".class";
+ URL resource = getPlatformClassLoader().getResource(klassAsPath);
+ return !(resource == null);
+ }
+
+ private static ClassLoader getPlatformClassLoader() {
+ ClassLoader platform = JdkPackages.class.getClassLoader().getParent();
+
+ // Will fail upon changes in classloader hierarchy between JDK versions
+ assert (platform.getName().equals("platform"));
+
+ return platform;
+ }
+
+ private static boolean hasJdkExclusivePrefix(String pkg) {
+ return pkg.startsWith("java.")
+ || pkg.startsWith("sun.");
+ }
+
+}
+
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/bundle/AnalyzeBundleTest.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/bundle/AnalyzeBundleTest.java
index 8cccf0598ab..a09604b842b 100644
--- a/bundle-plugin/src/test/java/com/yahoo/container/plugin/bundle/AnalyzeBundleTest.java
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/bundle/AnalyzeBundleTest.java
@@ -11,8 +11,10 @@ import org.junit.rules.ExpectedException;
import java.io.File;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import static com.yahoo.container.plugin.classanalysis.TestUtilities.throwableMessage;
import static org.hamcrest.CoreMatchers.containsString;
@@ -28,6 +30,7 @@ import static org.junit.Assert.assertThat;
*/
public class AnalyzeBundleTest {
private final List<ExportPackages.Export> exports;
+ private final Set<String> exportedPackageNames;
private final Map<String, ExportPackages.Export> exportsByPackageName;
File jarDir = new File("src/test/resources/jar");
@@ -37,6 +40,7 @@ public class AnalyzeBundleTest {
File simple = new File(jarDir, "simple1.jar");
PublicPackages pp = AnalyzeBundle.publicPackagesAggregated(Arrays.asList(notOsgi, simple));
this.exports = pp.exports;
+ this.exportedPackageNames = pp.exportedPackageNames();
this.exportsByPackageName = ExportPackages.exportsByPackageName(exports);
}
@@ -55,6 +59,11 @@ public class AnalyzeBundleTest {
assertThat(exportsByPackageName.keySet(), hasItem("com.yahoo.sample.exported.package"));
}
+ @Test
+ public void exported_class_names_can_be_retrieved() {
+ assertThat(exportedPackageNames, is(new HashSet<>(exports.get(0).getPackageNames())));
+ }
+
@Rule
public ExpectedException exception = ExpectedException.none();
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/PackageTallyTest.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/PackageTallyTest.java
new file mode 100644
index 00000000000..e80562c2c13
--- /dev/null
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/PackageTallyTest.java
@@ -0,0 +1,24 @@
+// Copyright 2019 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.plugin.classanalysis;
+
+import org.junit.Test;
+
+import java.util.Set;
+
+import static java.util.Collections.emptyMap;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author gjoranv
+ */
+public class PackageTallyTest {
+
+ @Test
+ public void referenced_packages_missing_from_are_detected() {
+ PackageTally tally = new PackageTally(emptyMap(), Set.of("p1", "java.util", "com.yahoo.api.annotations", "missing"));
+ Set<String> missingPackages = tally.referencedPackagesMissingFrom(Set.of("p1"));
+ assertThat(missingPackages, is(Set.of("missing")));
+ }
+
+}
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/osgi/ImportPackageTest.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/osgi/ImportPackageTest.java
index 23960323a31..b76151c790e 100644
--- a/bundle-plugin/src/test/java/com/yahoo/container/plugin/osgi/ImportPackageTest.java
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/osgi/ImportPackageTest.java
@@ -109,8 +109,9 @@ public class ImportPackageTest {
is("com.yahoo.exported;version=\"[1.2.3,2)\""));
}
- private static Set<Import> calculateImports(Set<String> referencedPackages, Set<String> implementedPackages,
- Map<String, Export> exportedPackages) {
+ private static Set<Import> calculateImports(Set<String> referencedPackages,
+ Set<String> implementedPackages,
+ Map<String, Export> exportedPackages) {
return new HashSet<>(ImportPackages.calculateImports(referencedPackages, implementedPackages, exportedPackages).values());
}
diff --git a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/database/CasWriteFailed.java b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/database/CasWriteFailed.java
new file mode 100644
index 00000000000..c62d8d4bcd5
--- /dev/null
+++ b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/database/CasWriteFailed.java
@@ -0,0 +1,23 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.clustercontroller.core.database;
+
+/**
+ * Exception used to signal that a write intended to overwrite a value previously
+ * read encountered an underlying version conflict during an atomic compare-and-swap
+ * operation. This generally means that another node has written to the value since
+ * we last read it, and that the information we hold may be stale.
+ *
+ * Upon receiving such an exception, the caller should no longer assume it holds
+ * up-to-date information and should drop and roles that build on top of such an
+ * assumption (such as leadership sessions).
+ */
+public class CasWriteFailed extends RuntimeException {
+
+ public CasWriteFailed(String message) {
+ super(message);
+ }
+
+ public CasWriteFailed(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/database/Database.java b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/database/Database.java
index c98e362c2d2..4b0461200a4 100644
--- a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/database/Database.java
+++ b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/database/Database.java
@@ -46,7 +46,12 @@ public abstract class Database {
* store the version in the database, such that if another fleetcontroller takes over as master it will use a
* higher version system state.
*
+ * Precondition: retrieveLatestSystemStateVersion() MUST have been called at least once prior to calling
+ * this method.
+ *
* @return True if request succeeded. False if not.
+ * @throws CasWriteFailed if the expected version of the znode did not match what was actually stored in the DB.
+ * In this case, the write has NOT been applied.
*/
public abstract boolean storeLatestSystemStateVersion(int version) throws InterruptedException;
@@ -84,6 +89,17 @@ public abstract class Database {
*/
public abstract Map<Node, Long> retrieveStartTimestamps() throws InterruptedException;
+ /**
+ * Stores the last published cluster state bundle synchronously into ZooKeeper.
+ *
+ * Precondition: retrieveLastPublishedStateBundle() MUST have been called at least once prior to calling
+ * this method.
+ *
+ * @return true if the write is known to have been successful, false otherwise. If false is returned, the
+ * write may or may not have taken place.
+ * @throws CasWriteFailed if the expected version of the znode did not match what was actually stored in the DB.
+ * In this case, the write has NOT been applied.
+ */
public abstract boolean storeLastPublishedStateBundle(ClusterStateBundle stateBundle) throws InterruptedException;
public abstract ClusterStateBundle retrieveLastPublishedStateBundle() throws InterruptedException;
diff --git a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/database/DatabaseHandler.java b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/database/DatabaseHandler.java
index f30b86130c2..b9d02f16090 100644
--- a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/database/DatabaseHandler.java
+++ b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/database/DatabaseHandler.java
@@ -105,8 +105,7 @@ public class DatabaseHandler {
}
public void shutdown(FleetController fleetController) {
- reset();
- fleetController.lostDatabaseConnection();
+ relinquishDatabaseConnectivity(fleetController);
}
public boolean isClosed() { return database == null || database.isClosed(); }
@@ -232,68 +231,85 @@ public class DatabaseHandler {
didWork = true;
connect(context.getCluster(), currentTime);
}
- synchronized (databaseMonitor) {
- if (database == null || database.isClosed()) {
- return didWork;
- }
- if (pendingStore.masterVote != null) {
- didWork = true;
- log.log(LogLevel.DEBUG, "Fleetcontroller " + nodeIndex + ": Attempting to store master vote "
- + pendingStore.masterVote + " into zookeeper.");
- if (database.storeMasterVote(pendingStore.masterVote)) {
- log.log(LogLevel.DEBUG, "Fleetcontroller " + nodeIndex + ": Managed to store master vote "
- + pendingStore.masterVote + " into zookeeper.");
- currentlyStored.masterVote = pendingStore.masterVote;
- pendingStore.masterVote = null;
- } else {
- log.log(LogLevel.WARNING, "Fleetcontroller " + nodeIndex + ": Failed to store master vote");
+ try {
+ synchronized (databaseMonitor) {
+ if (database == null || database.isClosed()) {
return didWork;
}
+ didWork |= performZooKeeperWrites();
}
- if (pendingStore.lastSystemStateVersion != null) {
- didWork = true;
- log.log(LogLevel.DEBUG, "Fleetcontroller " + nodeIndex
- + ": Attempting to store last system state version " + pendingStore.lastSystemStateVersion
- + " into zookeeper.");
- // TODO guard version write with a CaS predicated on the version we last read/wrote.
- // TODO Drop leadership status if there is a mismatch, as it implies we're racing with another leader.
- if (database.storeLatestSystemStateVersion(pendingStore.lastSystemStateVersion)) {
- currentlyStored.lastSystemStateVersion = pendingStore.lastSystemStateVersion;
- pendingStore.lastSystemStateVersion = null;
- } else {
- return didWork;
- }
+ } catch (CasWriteFailed e) {
+ log.log(LogLevel.WARNING, String.format("CaS write to ZooKeeper failed, another controller " +
+ "has likely taken over ownership: %s", e.getMessage()));
+ // Clear DB and master election state. This shall trigger a full re-fetch of all
+ // version and election-related metadata.
+ relinquishDatabaseConnectivity(context.getFleetController());
+ }
+ return didWork;
+ }
+
+ private void relinquishDatabaseConnectivity(FleetController fleetController) {
+ reset();
+ fleetController.lostDatabaseConnection();
+ }
+
+ private boolean performZooKeeperWrites() throws InterruptedException {
+ boolean didWork = false;
+ if (pendingStore.masterVote != null) {
+ didWork = true;
+ log.log(LogLevel.DEBUG, "Fleetcontroller " + nodeIndex + ": Attempting to store master vote "
+ + pendingStore.masterVote + " into zookeeper.");
+ if (database.storeMasterVote(pendingStore.masterVote)) {
+ log.log(LogLevel.DEBUG, "Fleetcontroller " + nodeIndex + ": Managed to store master vote "
+ + pendingStore.masterVote + " into zookeeper.");
+ currentlyStored.masterVote = pendingStore.masterVote;
+ pendingStore.masterVote = null;
+ } else {
+ log.log(LogLevel.WARNING, "Fleetcontroller " + nodeIndex + ": Failed to store master vote");
+ return true;
}
- if (pendingStore.startTimestamps != null) {
- didWork = true;
- log.log(LogLevel.DEBUG, "Fleetcontroller " + nodeIndex + ": Attempting to store "
- + pendingStore.startTimestamps.size() + " start timestamps into zookeeper.");
- if (database.storeStartTimestamps(pendingStore.startTimestamps)) {
- currentlyStored.startTimestamps = pendingStore.startTimestamps;
- pendingStore.startTimestamps = null;
- } else {
- return didWork;
- }
+ }
+ if (pendingStore.lastSystemStateVersion != null) {
+ didWork = true;
+ log.log(LogLevel.DEBUG, "Fleetcontroller " + nodeIndex
+ + ": Attempting to store last system state version " + pendingStore.lastSystemStateVersion
+ + " into zookeeper.");
+ if (database.storeLatestSystemStateVersion(pendingStore.lastSystemStateVersion)) {
+ currentlyStored.lastSystemStateVersion = pendingStore.lastSystemStateVersion;
+ pendingStore.lastSystemStateVersion = null;
+ } else {
+ return true;
}
- if (pendingStore.wantedStates != null) {
- didWork = true;
- log.log(LogLevel.DEBUG, "Fleetcontroller " + nodeIndex + ": Attempting to store "
- + pendingStore.wantedStates.size() + " wanted states into zookeeper.");
- if (database.storeWantedStates(pendingStore.wantedStates)) {
- currentlyStored.wantedStates = pendingStore.wantedStates;
- pendingStore.wantedStates = null;
- } else {
- return didWork;
- }
+ }
+ if (pendingStore.startTimestamps != null) {
+ didWork = true;
+ log.log(LogLevel.DEBUG, "Fleetcontroller " + nodeIndex + ": Attempting to store "
+ + pendingStore.startTimestamps.size() + " start timestamps into zookeeper.");
+ if (database.storeStartTimestamps(pendingStore.startTimestamps)) {
+ currentlyStored.startTimestamps = pendingStore.startTimestamps;
+ pendingStore.startTimestamps = null;
+ } else {
+ return true;
}
- if (pendingStore.clusterStateBundle != null) {
- didWork = true;
- if (database.storeLastPublishedStateBundle(pendingStore.clusterStateBundle)) {
- currentlyStored.clusterStateBundle = pendingStore.clusterStateBundle;
- pendingStore.clusterStateBundle = null;
- } else {
- return true;
- }
+ }
+ if (pendingStore.wantedStates != null) {
+ didWork = true;
+ log.log(LogLevel.DEBUG, "Fleetcontroller " + nodeIndex + ": Attempting to store "
+ + pendingStore.wantedStates.size() + " wanted states into zookeeper.");
+ if (database.storeWantedStates(pendingStore.wantedStates)) {
+ currentlyStored.wantedStates = pendingStore.wantedStates;
+ pendingStore.wantedStates = null;
+ } else {
+ return true;
+ }
+ }
+ if (pendingStore.clusterStateBundle != null) {
+ didWork = true;
+ if (database.storeLastPublishedStateBundle(pendingStore.clusterStateBundle)) {
+ currentlyStored.clusterStateBundle = pendingStore.clusterStateBundle;
+ pendingStore.clusterStateBundle = null;
+ } else {
+ return true;
}
}
return didWork;
diff --git a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/database/ZooKeeperDatabase.java b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/database/ZooKeeperDatabase.java
index 77ca7e12711..880f2cdaf19 100644
--- a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/database/ZooKeeperDatabase.java
+++ b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/database/ZooKeeperDatabase.java
@@ -35,6 +35,10 @@ public class ZooKeeperDatabase extends Database {
private final int nodeIndex;
private final MasterDataGatherer masterDataGatherer;
private boolean reportErrors = true;
+ // Expected ZK znode versions. Note: these are _not_ -1 as that would match anything.
+ // We expect the caller to invoke the load methods prior to calling any store methods.
+ private int lastKnownStateBundleZNodeVersion = -2;
+ private int lastKnownStateVersionZNodeVersion = -2;
public void stopErrorReporting() {
reportErrors = false;
@@ -196,10 +200,14 @@ public class ZooKeeperDatabase extends Database {
byte data[] = Integer.toString(version).getBytes(utf8);
try{
log.log(LogLevel.INFO, String.format("Fleetcontroller %d: Storing new cluster state version in ZooKeeper: %d", nodeIndex, version));
- session.setData(zooKeeperRoot + "latestversion", data, -1);
+ var stat = session.setData(zooKeeperRoot + "latestversion", data, lastKnownStateVersionZNodeVersion);
+ lastKnownStateVersionZNodeVersion = stat.getVersion();
return true;
} catch (InterruptedException e) {
throw (InterruptedException) new InterruptedException("Interrupted").initCause(e);
+ } catch (KeeperException.BadVersionException e) {
+ throw new CasWriteFailed(String.format("version mismatch in cluster state version znode (expected %d): %s",
+ lastKnownStateVersionZNodeVersion, e.getMessage()), e);
} catch (Exception e) {
maybeLogExceptionWarning(e, "Failed to store latest system state version used " + version);
return false;
@@ -209,10 +217,13 @@ public class ZooKeeperDatabase extends Database {
public Integer retrieveLatestSystemStateVersion() throws InterruptedException {
Stat stat = new Stat();
try{
- log.log(LogLevel.DEBUG, "Fleetcontroller " + nodeIndex + ": Fetching latest cluster state at '" + zooKeeperRoot + "latestversion'");
+ log.log(LogLevel.DEBUG, () -> String.format("Fleetcontroller %d: Fetching latest cluster state at '%slatestversion'",
+ nodeIndex, zooKeeperRoot));
byte[] data = session.getData(zooKeeperRoot + "latestversion", false, stat);
+ lastKnownStateVersionZNodeVersion = stat.getVersion();
final Integer versionNumber = Integer.valueOf(new String(data, utf8));
- log.log(LogLevel.INFO, String.format("Fleetcontroller %d: Read cluster state version %d from ZooKeeper", nodeIndex, versionNumber));
+ log.log(LogLevel.INFO, String.format("Fleetcontroller %d: Read cluster state version %d from ZooKeeper " +
+ "(znode version %d)", nodeIndex, versionNumber, stat.getVersion()));
return versionNumber;
} catch (InterruptedException e) {
throw (InterruptedException) new InterruptedException("Interrupted").initCause(e);
@@ -336,12 +347,16 @@ public class ZooKeeperDatabase extends Database {
EnvelopedClusterStateBundleCodec envelopedBundleCodec = new SlimeClusterStateBundleCodec();
byte[] encodedBundle = envelopedBundleCodec.encodeWithEnvelope(stateBundle);
try{
- log.log(LogLevel.DEBUG, () -> String.format("Fleetcontroller %d: Storing published state bundle %s at '%spublished_state_bundle'",
- nodeIndex, stateBundle, zooKeeperRoot));
- // TODO CAS on expected zknode version
- session.setData(zooKeeperRoot + "published_state_bundle", encodedBundle, -1);
+ log.log(LogLevel.DEBUG, () -> String.format("Fleetcontroller %d: Storing published state bundle %s at " +
+ "'%spublished_state_bundle' with expected znode version %d",
+ nodeIndex, stateBundle, zooKeeperRoot, lastKnownStateBundleZNodeVersion));
+ var stat = session.setData(zooKeeperRoot + "published_state_bundle", encodedBundle, lastKnownStateBundleZNodeVersion);
+ lastKnownStateBundleZNodeVersion = stat.getVersion();
} catch (InterruptedException e) {
throw (InterruptedException) new InterruptedException("Interrupted").initCause(e);
+ } catch (KeeperException.BadVersionException e) {
+ throw new CasWriteFailed(String.format("version mismatch in cluster state bundle znode (expected %d): %s",
+ lastKnownStateBundleZNodeVersion, e.getMessage()), e);
} catch (Exception e) {
maybeLogExceptionWarning(e, "Failed to store last published cluster state bundle in ZooKeeper");
return false;
@@ -354,6 +369,7 @@ public class ZooKeeperDatabase extends Database {
Stat stat = new Stat();
try {
byte[] data = session.getData(zooKeeperRoot + "published_state_bundle", false, stat);
+ lastKnownStateBundleZNodeVersion = stat.getVersion();
if (data != null && data.length != 0) {
EnvelopedClusterStateBundleCodec envelopedBundleCodec = new SlimeClusterStateBundleCodec();
return envelopedBundleCodec.decodeWithEnvelope(data);
diff --git a/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ZooKeeperDatabaseTest.java b/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ZooKeeperDatabaseTest.java
index e5200b63a7e..6062f45c4de 100644
--- a/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ZooKeeperDatabaseTest.java
+++ b/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ZooKeeperDatabaseTest.java
@@ -1,9 +1,12 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.clustercontroller.core;
+import com.yahoo.vespa.clustercontroller.core.database.CasWriteFailed;
import com.yahoo.vespa.clustercontroller.core.database.Database;
import com.yahoo.vespa.clustercontroller.core.database.ZooKeeperDatabase;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
import java.io.IOException;
import java.time.Duration;
@@ -14,6 +17,9 @@ import static org.mockito.Mockito.mock;
public class ZooKeeperDatabaseTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
private static class Fixture implements AutoCloseable {
final ZooKeeperTestServer zkServer;
ClusterFixture clusterFixture;
@@ -53,9 +59,8 @@ public class ZooKeeperDatabaseTest {
public void can_store_and_load_cluster_state_bundle_from_database() throws Exception {
try (Fixture f = new Fixture()) {
f.createDatabase();
- ClusterStateBundle bundleToStore = ClusterStateBundleUtil.makeBundle("distributor:2 storage:2",
- StateMapping.of("default", "distributor:2 storage:2 .0.s:d"),
- StateMapping.of("upsidedown", "distributor:2 .0.s:d storage:2"));
+ f.db().retrieveLastPublishedStateBundle(); // Must be called once prior to prime last known znode version
+ ClusterStateBundle bundleToStore = dummyBundle();
f.db().storeLastPublishedStateBundle(bundleToStore);
ClusterStateBundle bundleReceived = f.db().retrieveLastPublishedStateBundle();
@@ -63,6 +68,32 @@ public class ZooKeeperDatabaseTest {
}
}
+ private static ClusterStateBundle dummyBundle() {
+ return ClusterStateBundleUtil.makeBundle("distributor:2 storage:2",
+ StateMapping.of("default", "distributor:2 storage:2 .0.s:d"),
+ StateMapping.of("upsidedown", "distributor:2 .0.s:d storage:2"));
+ }
+
+ @Test
+ public void storing_cluster_state_bundle_with_mismatching_expected_znode_version_throws_exception() throws Exception {
+ expectedException.expect(CasWriteFailed.class);
+ expectedException.expectMessage("version mismatch in cluster state bundle znode (expected -2)");
+ try (Fixture f = new Fixture()) {
+ f.createDatabase();
+ f.db().storeLastPublishedStateBundle(dummyBundle());
+ }
+ }
+
+ @Test
+ public void storing_cluster_state_version_with_mismatching_expected_znode_version_throws_exception() throws Exception {
+ expectedException.expect(CasWriteFailed.class);
+ expectedException.expectMessage("version mismatch in cluster state version znode (expected -2)");
+ try (Fixture f = new Fixture()) {
+ f.createDatabase();
+ f.db().storeLatestSystemStateVersion(12345);
+ }
+ }
+
@Test
public void empty_state_bundle_is_returned_if_no_bundle_already_stored_in_database() throws Exception {
try (Fixture f = new Fixture()) {
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java
index 3b4bc1c2c5c..4e47409a7a0 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java
@@ -59,6 +59,7 @@ public interface ModelContext {
boolean useAdaptiveDispatch();
// TODO: Remove temporary default implementation
default Optional<TlsSecrets> tlsSecrets() { return Optional.empty(); }
+ default boolean enableGroupingSessionCache() { return false; }
}
}
diff --git a/config-model/src/main/java/com/yahoo/config/model/ConfigModelUtils.java b/config-model/src/main/java/com/yahoo/config/model/ConfigModelUtils.java
deleted file mode 100644
index d80df441798..00000000000
--- a/config-model/src/main/java/com/yahoo/config/model/ConfigModelUtils.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.config.model;
-
-import java.io.Serializable;
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import static com.yahoo.text.Lowercase.toLowerCase;
-
-/**
- * Utilities for config models
- *
- * @author gjoranv
- */
-// TODO: Split this into appropriate classes, or move to ConfigModel superclass
-public class ConfigModelUtils implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- public static Pattern hourNmin = Pattern.compile("(\\d\\d):(\\d\\d)");
-
- public static Map<String, Integer> day2int;
- static {
- day2int = new HashMap<>();
- day2int.put("sunday", 0);
- day2int.put("monday", 1);
- day2int.put("tuesday", 2);
- day2int.put("wednesday", 3);
- day2int.put("thursday", 4);
- day2int.put("friday", 5);
- day2int.put("saturday", 6);
- }
-
- /** Parses a 24 hour clock that must be the five characters ##:## to an int stating minutes after midnight. */
- public static int getTimeOfDay(String time) {
- Matcher m = ConfigModelUtils.hourNmin.matcher(time);
- if (m.matches()) {
- return Integer.parseInt(m.group(1)) * 60 + Integer.parseInt(m.group(2));
- }
- throw new IllegalArgumentException("The string '" + time + "' is not in ##:## format.");
- }
-
- /** Parses a day of week name in english to an int, where 0 is sunday, 6 saturday. */
- public static int getDayOfWeek(String day) {
- return ConfigModelUtils.day2int.get(toLowerCase(day));
- }
-
-}
diff --git a/config-model/src/main/java/com/yahoo/config/model/MapConfigModelRegistry.java b/config-model/src/main/java/com/yahoo/config/model/MapConfigModelRegistry.java
index 9178e4beca9..d94a9c1580a 100644
--- a/config-model/src/main/java/com/yahoo/config/model/MapConfigModelRegistry.java
+++ b/config-model/src/main/java/com/yahoo/config/model/MapConfigModelRegistry.java
@@ -5,20 +5,19 @@ import com.google.inject.Inject;
import com.yahoo.component.provider.ComponentRegistry;
import com.yahoo.config.model.builder.xml.ConfigModelBuilder;
import com.yahoo.config.model.builder.xml.ConfigModelId;
-import com.yahoo.log.LogLevel;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.FrameworkUtil;
-import java.util.*;
-import java.util.logging.Logger;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
/**
* @author Ulf Lilleengen
- * @since 5.1
*/
public class MapConfigModelRegistry extends ConfigModelRegistry {
- private static final Logger log = Logger.getLogger(MapConfigModelRegistry.class.getPackage().getName());
private final List<ConfigModelBuilder> builders;
/**
diff --git a/config-model/src/main/java/com/yahoo/config/model/producer/UserConfigRepo.java b/config-model/src/main/java/com/yahoo/config/model/producer/UserConfigRepo.java
index 22dce91140d..9aeb5c40b28 100644
--- a/config-model/src/main/java/com/yahoo/config/model/producer/UserConfigRepo.java
+++ b/config-model/src/main/java/com/yahoo/config/model/producer/UserConfigRepo.java
@@ -1,9 +1,9 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.config.model.producer;
-import com.yahoo.config.application.api.DeployLogger;
-import com.yahoo.log.LogLevel;
-import com.yahoo.vespa.config.*;
+import com.yahoo.vespa.config.ConfigDefinitionKey;
+import com.yahoo.vespa.config.ConfigPayload;
+import com.yahoo.vespa.config.ConfigPayloadBuilder;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -14,7 +14,6 @@ import java.util.Set;
* how the user configs are stored, and defines the methods to retrieve user configs and merge the repo with others.
*
* @author Ulf Lilleengen
- * @since 5.1
*/
public class UserConfigRepo {
private final Map<ConfigDefinitionKey, ConfigPayloadBuilder> userConfigsMap;
@@ -46,10 +45,6 @@ public class UserConfigRepo {
this.userConfigsMap = map;
}
- public UserConfigRepo(UserConfigRepo userConfigRepo) {
- this.userConfigsMap = userConfigRepo.userConfigsMap;
- }
-
public ConfigPayloadBuilder get(ConfigDefinitionKey key) {
return userConfigsMap.get(key);
}
diff --git a/config-model/src/main/java/com/yahoo/config/model/provision/Host.java b/config-model/src/main/java/com/yahoo/config/model/provision/Host.java
index 8c8debbae43..c8d92220db3 100644
--- a/config-model/src/main/java/com/yahoo/config/model/provision/Host.java
+++ b/config-model/src/main/java/com/yahoo/config/model/provision/Host.java
@@ -5,7 +5,6 @@ import com.google.common.collect.ImmutableList;
import com.yahoo.component.Version;
import com.yahoo.config.provision.Flavor;
-import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
diff --git a/config-model/src/main/java/com/yahoo/config/model/test/MockApplicationPackage.java b/config-model/src/main/java/com/yahoo/config/model/test/MockApplicationPackage.java
index b381a5f28db..60435decb04 100644
--- a/config-model/src/main/java/com/yahoo/config/model/test/MockApplicationPackage.java
+++ b/config-model/src/main/java/com/yahoo/config/model/test/MockApplicationPackage.java
@@ -341,7 +341,7 @@ public class MockApplicationPackage implements ApplicationPackage {
}
@Override
- public Reader createReader() throws FileNotFoundException {
+ public Reader createReader() {
try {
if ( ! exists()) throw new FileNotFoundException("File '" + file + "' does not exist");
return IOUtils.createReader(file, "UTF-8");
@@ -352,7 +352,7 @@ public class MockApplicationPackage implements ApplicationPackage {
}
@Override
- public InputStream createInputStream() throws FileNotFoundException {
+ public InputStream createInputStream() {
try {
if ( ! exists()) throw new FileNotFoundException("File '" + file + "' does not exist");
return new BufferedInputStream(new FileInputStream(file));
diff --git a/config-model/src/main/java/com/yahoo/config/model/test/MockRoot.java b/config-model/src/main/java/com/yahoo/config/model/test/MockRoot.java
index 13d4064f55f..74ab4504136 100644
--- a/config-model/src/main/java/com/yahoo/config/model/test/MockRoot.java
+++ b/config-model/src/main/java/com/yahoo/config/model/test/MockRoot.java
@@ -7,7 +7,6 @@ import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.config.model.ConfigModelContext;
import com.yahoo.config.model.ConfigModelRepo;
import com.yahoo.config.model.api.HostProvisioner;
-import com.yahoo.config.model.application.provider.BaseDeployLogger;
import com.yahoo.config.model.builder.xml.XmlHelper;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.producer.AbstractConfigProducer;
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java b/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java
index d738929f721..7a34ff258b7 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java
@@ -20,7 +20,6 @@ import com.yahoo.searchlib.rankingexpression.evaluation.TensorValue;
import com.yahoo.searchlib.rankingexpression.evaluation.Value;
import com.yahoo.searchlib.rankingexpression.rule.ReferenceNode;
import com.yahoo.tensor.TensorType;
-import com.yahoo.tensor.evaluation.TypeContext;
import com.yahoo.vespa.model.VespaModel;
import java.io.File;
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/SearchBuilder.java b/config-model/src/main/java/com/yahoo/searchdefinition/SearchBuilder.java
index 6481d42446f..bf1939e2c3d 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/SearchBuilder.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/SearchBuilder.java
@@ -8,7 +8,6 @@ import com.yahoo.config.model.test.MockApplicationPackage;
import com.yahoo.document.DocumentTypeManager;
import com.yahoo.io.IOUtils;
import com.yahoo.io.reader.NamedReader;
-import com.yahoo.search.query.profile.QueryProfile;
import com.yahoo.search.query.profile.QueryProfileRegistry;
import com.yahoo.search.query.profile.config.QueryProfileXMLReader;
import com.yahoo.searchdefinition.derived.SearchOrderer;
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java b/config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java
index da1ea85f2ea..f25dd0be7b2 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java
@@ -78,7 +78,7 @@ public class DerivedConfiguration {
if ( ! search.isDocumentsOnly()) {
attributeFields = new AttributeFields(search);
summaries = new Summaries(search, deployLogger);
- summaryMap = new SummaryMap(search, summaries);
+ summaryMap = new SummaryMap(search);
juniperrc = new Juniperrc(search);
rankProfileList = new RankProfileList(search, search.rankingConstants(), attributeFields, rankProfileRegistry, queryProfiles, importedModels);
indexingScript = new IndexingScript(search);
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexInfo.java b/config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexInfo.java
index a807cc79d95..02b2a383318 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexInfo.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexInfo.java
@@ -10,8 +10,8 @@ import com.yahoo.searchdefinition.processing.NGramMatch;
import com.yahoo.vespa.documentmodel.SummaryField;
import com.yahoo.search.config.IndexInfoConfig;
-import java.util.*;
-import java.util.stream.Stream;
+import java.util.Map;
+import java.util.Set;
/**
* Per-index commands which should be applied to queries prior to searching
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexingScript.java b/config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexingScript.java
index dc6c17e425e..94d475cf519 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexingScript.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexingScript.java
@@ -4,7 +4,6 @@ package com.yahoo.searchdefinition.derived;
import com.yahoo.document.DataType;
import com.yahoo.document.PositionDataType;
import com.yahoo.searchdefinition.Search;
-import com.yahoo.searchdefinition.document.Attribute;
import com.yahoo.searchdefinition.document.ImmutableSDField;
import com.yahoo.vespa.configdefinition.IlscriptsConfig;
import com.yahoo.vespa.configdefinition.IlscriptsConfig.Ilscript.Builder;
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/derived/SummaryClass.java b/config-model/src/main/java/com/yahoo/searchdefinition/derived/SummaryClass.java
index f88da34428d..78400666c36 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/derived/SummaryClass.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/derived/SummaryClass.java
@@ -1,7 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.searchdefinition.derived;
-import com.yahoo.config.model.application.provider.BaseDeployLogger;
import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.document.DataType;
import com.yahoo.prelude.fastsearch.DocsumDefinitionSet;
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/derived/SummaryMap.java b/config-model/src/main/java/com/yahoo/searchdefinition/derived/SummaryMap.java
index 930d164ac4b..71bf05dbb6e 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/derived/SummaryMap.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/derived/SummaryMap.java
@@ -22,13 +22,12 @@ public class SummaryMap extends Derived implements SummarymapConfig.Producer {
private Map<String,FieldResultTransform> resultTransforms = new java.util.LinkedHashMap<>();
- /** Crateate a summary map from a search definition */
- public SummaryMap(Search search, Summaries summaries) {
- derive(search, summaries);
+ /** Creates a summary map from a search definition */
+ SummaryMap(Search search) {
+ derive(search);
}
- protected void derive(Search search, Summaries summaries) {
- // TODO: This should really derive from the 'summaries' argument. Bug?
+ protected void derive(Search search) {
for (DocumentSummary documentSummary : search.getSummaries().values()) {
derive(documentSummary);
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/derived/validation/Validator.java b/config-model/src/main/java/com/yahoo/searchdefinition/derived/validation/Validator.java
index b7b796d8976..e3bbf78ec21 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/derived/validation/Validator.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/derived/validation/Validator.java
@@ -4,8 +4,6 @@ package com.yahoo.searchdefinition.derived.validation;
import com.yahoo.searchdefinition.Search;
import com.yahoo.searchdefinition.derived.DerivedConfiguration;
-import java.util.logging.Logger;
-
/**
* @author mathiasm
*/
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/document/RankType.java b/config-model/src/main/java/com/yahoo/searchdefinition/document/RankType.java
index cae9b20b7b5..6df8a7998a6 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/document/RankType.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/document/RankType.java
@@ -1,11 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.searchdefinition.document;
-import java.io.Serializable;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Set;
-
/**
* The rank type of a field. For now this is just a container of a string name.
* This class is immutable.
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/document/SDField.java b/config-model/src/main/java/com/yahoo/searchdefinition/document/SDField.java
index c4e0f4cafef..8b523211471 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/document/SDField.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/document/SDField.java
@@ -463,33 +463,12 @@ public class SDField extends Field implements TypedKey, FieldOperationContainer,
}
}
}.visit(indexingScript);
- } else {
- if (!getDataType().equals(PositionDataType.INSTANCE) &&
- !getDataType().equals(DataType.getArray(PositionDataType.INSTANCE)) &&
- hasAttributeExpression(exp))
- {
- throw new IllegalArgumentException("For field '" + getName() + "': Setting attribute on a field that has struct or map sub-type(s) is not supported");
- }
}
for (SDField structField : getStructFields()) {
structField.setIndexingScript(exp);
}
}
- private static boolean hasAttributeExpression(ScriptExpression exp) {
- var visitor = new ExpressionVisitor() {
- boolean result = false;
- @Override
- protected void doVisit(Expression exp) {
- if (exp instanceof AttributeExpression) {
- result = true;
- }
- }
- };
- visitor.visit(exp);
- return visitor.result;
- }
-
@Override
public ScriptExpression getIndexingScript() {
return indexingScript;
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/expressiontransforms/RankProfileTransformContext.java b/config-model/src/main/java/com/yahoo/searchdefinition/expressiontransforms/RankProfileTransformContext.java
index 630c8644eb1..c76b8536ea0 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/expressiontransforms/RankProfileTransformContext.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/expressiontransforms/RankProfileTransformContext.java
@@ -3,9 +3,7 @@ package com.yahoo.searchdefinition.expressiontransforms;
import ai.vespa.rankingexpression.importer.configmodelview.ImportedMlModels;
import com.yahoo.search.query.profile.QueryProfileRegistry;
-import com.yahoo.searchdefinition.MapEvaluationTypeContext;
import com.yahoo.searchdefinition.RankProfile;
-import com.yahoo.searchlib.rankingexpression.Reference;
import com.yahoo.searchlib.rankingexpression.evaluation.Value;
import com.yahoo.searchlib.rankingexpression.transform.TransformContext;
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/processing/AddAttributeTransformToSummaryOfImportedFields.java b/config-model/src/main/java/com/yahoo/searchdefinition/processing/AddAttributeTransformToSummaryOfImportedFields.java
index 59dc4275e15..f0575f1f70f 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/processing/AddAttributeTransformToSummaryOfImportedFields.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/processing/AddAttributeTransformToSummaryOfImportedFields.java
@@ -6,11 +6,9 @@ import com.yahoo.searchdefinition.RankProfileRegistry;
import com.yahoo.searchdefinition.Search;
import com.yahoo.searchdefinition.document.ImmutableImportedComplexSDField;
import com.yahoo.searchdefinition.document.ImmutableSDField;
-import com.yahoo.searchdefinition.document.ImportedComplexField;
import com.yahoo.vespa.documentmodel.SummaryField;
import com.yahoo.vespa.documentmodel.SummaryTransform;
import com.yahoo.vespa.model.container.search.QueryProfiles;
-import com.yahoo.searchdefinition.document.ImmutableImportedSDField;
import java.util.stream.Stream;
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/processing/AdjustPositionSummaryFields.java b/config-model/src/main/java/com/yahoo/searchdefinition/processing/AdjustPositionSummaryFields.java
index 6e089d6155b..e8c958f9609 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/processing/AdjustPositionSummaryFields.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/processing/AdjustPositionSummaryFields.java
@@ -8,18 +8,13 @@ import com.yahoo.document.PositionDataType;
import com.yahoo.searchdefinition.RankProfileRegistry;
import com.yahoo.searchdefinition.Search;
import com.yahoo.searchdefinition.document.Attribute;
-import com.yahoo.searchdefinition.document.ImmutableImportedSDField;
import com.yahoo.searchdefinition.document.ImmutableSDField;
-import com.yahoo.searchdefinition.document.ImportedField;
import com.yahoo.vespa.documentmodel.DocumentSummary;
import com.yahoo.vespa.documentmodel.SummaryField;
import com.yahoo.vespa.documentmodel.SummaryField.Source;
import com.yahoo.vespa.documentmodel.SummaryTransform;
import com.yahoo.vespa.model.container.search.QueryProfiles;
-import java.util.LinkedList;
-import java.util.List;
-
/*
* Adjusts position summary fields by adding derived summary fields (.distance and .position) and setting summary
* transform and source.
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/processing/IndexingInputs.java b/config-model/src/main/java/com/yahoo/searchdefinition/processing/IndexingInputs.java
index b0129c0a836..fbe04881ce1 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/processing/IndexingInputs.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/processing/IndexingInputs.java
@@ -4,7 +4,6 @@ package com.yahoo.searchdefinition.processing;
import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.searchdefinition.RankProfileRegistry;
import com.yahoo.searchdefinition.Search;
-import com.yahoo.searchdefinition.document.SDDocumentType;
import com.yahoo.searchdefinition.document.SDField;
import com.yahoo.vespa.indexinglanguage.ExpressionConverter;
import com.yahoo.vespa.indexinglanguage.ExpressionVisitor;
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/processing/multifieldresolver/StemmingResolver.java b/config-model/src/main/java/com/yahoo/searchdefinition/processing/multifieldresolver/StemmingResolver.java
index cd75f1f8df7..483fe2105a3 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/processing/multifieldresolver/StemmingResolver.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/processing/multifieldresolver/StemmingResolver.java
@@ -2,14 +2,9 @@
package com.yahoo.searchdefinition.processing.multifieldresolver;
import com.yahoo.config.application.api.DeployLogger;
-import com.yahoo.language.Linguistics;
-import com.yahoo.searchdefinition.Index;
import com.yahoo.searchdefinition.Search;
import com.yahoo.searchdefinition.document.SDField;
import com.yahoo.searchdefinition.document.Stemming;
-import com.yahoo.searchdefinition.processing.BuiltInFieldSets;
-import com.yahoo.vespa.indexinglanguage.expressions.*;
-import com.yahoo.vespa.indexinglanguage.linguistics.AnnotatorConfig;
import java.util.List;
import java.util.logging.Level;
diff --git a/config-model/src/main/java/com/yahoo/vespa/documentmodel/SearchDef.java b/config-model/src/main/java/com/yahoo/vespa/documentmodel/SearchDef.java
index 5905ee3c3b5..1ff674da439 100644
--- a/config-model/src/main/java/com/yahoo/vespa/documentmodel/SearchDef.java
+++ b/config-model/src/main/java/com/yahoo/vespa/documentmodel/SearchDef.java
@@ -2,7 +2,6 @@
package com.yahoo.vespa.documentmodel;
import com.yahoo.document.DataType;
-import com.yahoo.document.DocumentType;
import com.yahoo.document.DocumentTypeManager;
import java.util.HashMap;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/ConfigProducerRoot.java b/config-model/src/main/java/com/yahoo/vespa/model/ConfigProducerRoot.java
index 2d4b8e9201f..b30aa5aeca6 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/ConfigProducerRoot.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/ConfigProducerRoot.java
@@ -2,7 +2,6 @@
package com.yahoo.vespa.model;
import com.yahoo.config.ConfigInstance;
-import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.producer.AbstractConfigProducer;
import com.yahoo.vespa.model.admin.Admin;
import com.yahoo.vespa.model.filedistribution.FileDistributor;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/Host.java b/config-model/src/main/java/com/yahoo/vespa/model/Host.java
index 1aff926a8c4..a952d63526b 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/Host.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/Host.java
@@ -2,7 +2,6 @@
package com.yahoo.vespa.model;
import java.io.File;
-import java.io.IOException;
import java.util.Objects;
import com.yahoo.cloud.config.SentinelConfig;
@@ -68,7 +67,7 @@ public final class Host extends AbstractConfigProducer<AbstractConfigProducer<?>
}
@Override
- public void writeFiles(File directory) throws IOException {
+ public void writeFiles(File directory) {
}
@Override
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/HostPorts.java b/config-model/src/main/java/com/yahoo/vespa/model/HostPorts.java
index 225d0bbcb11..69b692e3ad9 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/HostPorts.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/HostPorts.java
@@ -6,7 +6,6 @@ import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.config.provision.NetworkPorts;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/HostResource.java b/config-model/src/main/java/com/yahoo/vespa/model/HostResource.java
index 8b47c03f4a4..6da8df60161 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/HostResource.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/HostResource.java
@@ -6,7 +6,6 @@ import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.config.model.api.HostInfo;
import com.yahoo.config.provision.ClusterMembership;
import com.yahoo.config.provision.Flavor;
-import com.yahoo.config.provision.NetworkPorts;
import javax.annotation.Nullable;
import java.util.ArrayList;
@@ -18,7 +17,6 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
-import java.util.logging.Level;
import java.util.stream.Collectors;
/**
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/HostSystem.java b/config-model/src/main/java/com/yahoo/vespa/model/HostSystem.java
index 0dde5c99d4a..09e67ed96cb 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/HostSystem.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/HostSystem.java
@@ -8,7 +8,6 @@ import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterMembership;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.HostSpec;
-import com.yahoo.config.provision.NetworkPorts;
import com.yahoo.config.provision.ProvisionLogger;
import java.net.UnknownHostException;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/Admin.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/Admin.java
index 03c8055dd12..d31c411a39e 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/Admin.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/Admin.java
@@ -119,8 +119,6 @@ public class Admin extends AbstractConfigProducer implements Serializable {
return configservers;
}
- public void removeSlobroks() { slobroks.clear(); }
-
/** Returns an immutable list of the slobroks in this */
public List<Slobrok> getSlobroks() { return Collections.unmodifiableList(slobroks); }
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainerCluster.java
index f8f515cb609..458bd5903fc 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainerCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainerCluster.java
@@ -3,13 +3,15 @@ package com.yahoo.vespa.model.admin;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.producer.AbstractConfigProducer;
+import com.yahoo.container.handler.ThreadpoolConfig;
+import com.yahoo.search.config.QrStartConfig;
import com.yahoo.vespa.model.container.ContainerCluster;
import com.yahoo.vespa.model.container.component.Handler;
/**
- * @author gjoranv
+ * @author hmusum
*/
-public class LogserverContainerCluster extends ContainerCluster<LogserverContainer> {
+public class LogserverContainerCluster extends ContainerCluster<LogserverContainer> implements ThreadpoolConfig.Producer {
public LogserverContainerCluster(AbstractConfigProducer<?> parent, String name, DeployState deployState) {
super(parent, name, name, deployState);
@@ -21,6 +23,21 @@ public class LogserverContainerCluster extends ContainerCluster<LogserverContain
@Override
protected void doPrepare(DeployState deployState) { }
+ // Switch off verbose:gc, it's very noisy when Xms < Xmx
+ @Override
+ public void getConfig(QrStartConfig.Builder builder) {
+ super.getConfig(builder);
+ // This takes effect via vespa-start-container-daemon:configure_gcopts
+ builder.jvm.verbosegc(false);
+ }
+
+ @Override
+ public void getConfig(ThreadpoolConfig.Builder builder) {
+ builder.maxthreads(10);
+ }
+
+ protected boolean messageBusEnabled() { return false; }
+
private void addLogHandler() {
Handler<?> logHandler = Handler.fromClassName(ContainerCluster.LOG_HANDLER_CLASS);
logHandler.addServerBindings("*://*/logs");
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/ZooKeepersConfigProvider.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/ZooKeepersConfigProvider.java
index be3ec8a6a14..25dbd62e647 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/ZooKeepersConfigProvider.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/ZooKeepersConfigProvider.java
@@ -3,7 +3,6 @@ package com.yahoo.vespa.model.admin;
import com.yahoo.collections.CollectionUtil;
import com.yahoo.cloud.config.ZookeepersConfig;
-import com.yahoo.config.model.api.ConfigServerSpec;
import java.util.ArrayList;
import java.util.List;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerCluster.java
index 61a42d1c1a6..8ba85047057 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerCluster.java
@@ -8,7 +8,6 @@ import com.yahoo.config.model.producer.AbstractConfigProducer;
import com.yahoo.vespa.model.Service;
import com.yahoo.vespa.model.admin.Configserver;
import com.yahoo.vespa.model.container.Container;
-import com.yahoo.vespa.model.container.ContainerCluster;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainerCluster.java
index 277335fc3ea..2de4e5f5950 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainerCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainerCluster.java
@@ -27,4 +27,6 @@ public class ClusterControllerContainerCluster extends ContainerCluster<ClusterC
@Override
protected void doPrepare(DeployState deployState) { }
+ protected boolean messageBusEnabled() { return false; }
+
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java
index 34a7d3c16cf..091c5a3acb4 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java
@@ -21,6 +21,7 @@ import com.yahoo.config.model.producer.AbstractConfigProducer;
import com.yahoo.config.model.producer.AbstractConfigProducerRoot;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Zone;
+import com.yahoo.container.handler.ThreadpoolConfig;
import com.yahoo.osgi.provider.model.ComponentModel;
import com.yahoo.search.config.QrStartConfig;
import com.yahoo.vespa.model.VespaModel;
@@ -63,7 +64,8 @@ public class MetricsProxyContainerCluster extends ContainerCluster<MetricsProxyC
ApplicationDimensionsConfig.Producer,
ConsumersConfig.Producer,
MonitoringConfig.Producer,
- QrStartConfig.Producer
+ QrStartConfig.Producer,
+ ThreadpoolConfig.Producer
{
public static final Logger log = Logger.getLogger(MetricsProxyContainerCluster.class.getName());
@@ -91,7 +93,6 @@ public class MetricsProxyContainerCluster extends ContainerCluster<MetricsProxyC
this.parent = parent;
applicationId = deployState.getProperties().applicationId();
- setMessageBusEnabled(false);
setRpcServerEnabled(true);
addDefaultHandlersExceptStatus();
@@ -151,6 +152,13 @@ public class MetricsProxyContainerCluster extends ContainerCluster<MetricsProxyC
builder.jvm.verbosegc(false);
}
+ @Override
+ public void getConfig(ThreadpoolConfig.Builder builder) {
+ builder.maxthreads(10);
+ }
+
+ protected boolean messageBusEnabled() { return false; }
+
private MetricSet getAdditionalDefaultMetrics() {
return getAdmin()
.map(Admin::getAdditionalDefaultMetrics)
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ContentClusterRemovalValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ContentClusterRemovalValidator.java
index a2275b17233..866f647a351 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ContentClusterRemovalValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ContentClusterRemovalValidator.java
@@ -2,7 +2,6 @@
package com.yahoo.vespa.model.application.validation.change;
import com.yahoo.config.model.api.ConfigChangeAction;
-import com.yahoo.documentmodel.NewDocumentType;
import com.yahoo.vespa.model.VespaModel;
import com.yahoo.config.application.api.ValidationId;
import com.yahoo.config.application.api.ValidationOverrides;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomConfigPayloadBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomConfigPayloadBuilder.java
index e9abcfd87a9..d07f6c4e6fd 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomConfigPayloadBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomConfigPayloadBuilder.java
@@ -3,17 +3,16 @@ package com.yahoo.vespa.model.builder.xml.dom;
import com.yahoo.collections.Tuple2;
import com.yahoo.config.ConfigurationRuntimeException;
-import com.yahoo.config.codegen.CNode;
-import com.yahoo.log.LogLevel;
+import com.yahoo.vespa.config.ConfigDefinition;
+import com.yahoo.vespa.config.ConfigDefinitionKey;
+import com.yahoo.vespa.config.ConfigPayloadBuilder;
import com.yahoo.yolean.Exceptions;
import com.yahoo.text.XML;
-import com.yahoo.vespa.config.*;
import com.yahoo.vespa.config.util.ConfigUtils;
import org.w3c.dom.Element;
-import java.util.*;
-import java.util.logging.Logger;
+import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -27,8 +26,6 @@ import java.util.regex.Pattern;
*/
public class DomConfigPayloadBuilder {
- private static final Logger log = Logger.getLogger(DomConfigPayloadBuilder.class.getPackage().toString());
-
private static final Pattern namePattern = ConfigDefinition.namePattern;
private static final Pattern namespacePattern = ConfigDefinition.namespacePattern;
@@ -57,40 +54,30 @@ public class DomConfigPayloadBuilder {
public static ConfigDefinitionKey parseConfigName(Element configE) {
if (!configE.getNodeName().equals("config")) {
- throw new ConfigurationRuntimeException("The root element must be 'config', but was '"
- + configE.getNodeName() + "'.");
+ throw new ConfigurationRuntimeException("The root element must be 'config', but was '" + configE.getNodeName() + "'.");
}
+
if (!configE.hasAttribute("name")) {
throw new ConfigurationRuntimeException
("The 'config' element must have a 'name' attribute that matches the name of the config definition.");
}
- String xmlName = configE.getAttribute("name");
- final boolean xmlNamespaceAttributeExists = configE.hasAttribute("namespace");
-
- String xmlNamespace = null;
- // If name contains dots, rewrite to name and namespace
- if (xmlName.contains(".")) {
- Tuple2<String, String> t = ConfigUtils.getNameAndNamespaceFromString(xmlName);
- xmlName = t.first;
- xmlNamespace = t.second;
- } else {
- if (!xmlNamespaceAttributeExists) {
- log.log(LogLevel.WARNING, "No namespace in 'config name=" + xmlName + "', please specify one");
- }
+ String elementString = configE.getAttribute("name");
+ if (!elementString.contains(".")) {
+ throw new ConfigurationRuntimeException("The config name '" + elementString +
+ "' contains illegal characters. Only names with the pattern " +
+ namespacePattern.pattern() + "." + namePattern.pattern() + " are legal.");
}
+ Tuple2<String, String> t = ConfigUtils.getNameAndNamespaceFromString(elementString);
+ String xmlName = t.first;
+ String xmlNamespace = t.second;
+
if (!validName(xmlName)) {
throw new ConfigurationRuntimeException("The config name '" + xmlName +
"' contains illegal characters. Only names with the pattern " + namePattern.toString() + " are legal.");
}
- if (xmlNamespace == null) {
- xmlNamespace = configE.getAttribute("namespace");
- if (xmlNamespace == null || xmlNamespace.isEmpty()) {
- xmlNamespace = CNode.DEFAULT_NAMESPACE;
- }
- }
if (!validNamespace(xmlNamespace)) {
throw new ConfigurationRuntimeException("The config namespace '" + xmlNamespace +
"' contains illegal characters. Only namespaces with the pattern " + namespacePattern.toString() + " are legal.");
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomV20ClientsBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomV20ClientsBuilder.java
index 74e3ac581c4..63661104dae 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomV20ClientsBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomV20ClientsBuilder.java
@@ -1,7 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.builder.xml.dom;
-import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.text.XML;
import com.yahoo.vespa.model.clients.Clients;
import org.w3c.dom.Element;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/clients/Clients.java b/config-model/src/main/java/com/yahoo/vespa/model/clients/Clients.java
index 9b33568b61e..10979a21838 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/clients/Clients.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/clients/Clients.java
@@ -4,14 +4,8 @@ package com.yahoo.vespa.model.clients;
import com.yahoo.vespa.config.content.LoadTypeConfig;
import com.yahoo.config.model.ConfigModel;
import com.yahoo.config.model.ConfigModelContext;
-import com.yahoo.config.model.ConfigModelRepo;
-import com.yahoo.config.model.producer.AbstractConfigProducer;
import com.yahoo.documentapi.messagebus.loadtypes.LoadType;
import com.yahoo.documentapi.messagebus.loadtypes.LoadTypeSet;
-import com.yahoo.vespa.model.container.ContainerCluster;
-
-import java.util.LinkedList;
-import java.util.List;
/**
* This is the clients plugin for the Vespa model. It is responsible for creating
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java
index 73a401e6a2a..473971c5e7a 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java
@@ -2,13 +2,18 @@
package com.yahoo.vespa.model.container;
import com.yahoo.component.ComponentId;
+import com.yahoo.component.ComponentSpecification;
import com.yahoo.config.FileReference;
import com.yahoo.config.application.api.ComponentInfo;
import com.yahoo.config.model.api.TlsSecrets;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.producer.AbstractConfigProducer;
import com.yahoo.container.BundlesConfig;
+import com.yahoo.container.bundle.BundleInstantiationSpecification;
+import com.yahoo.container.jdisc.ContainerMbusConfig;
+import com.yahoo.container.jdisc.messagebus.MbusServerProvider;
import com.yahoo.jdisc.http.ServletPathsConfig;
+import com.yahoo.osgi.provider.model.ComponentModel;
import com.yahoo.vespa.config.search.RankProfilesConfig;
import com.yahoo.vespa.config.search.core.RankingConstantsConfig;
import com.yahoo.vespa.defaults.Defaults;
@@ -39,7 +44,8 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat
BundlesConfig.Producer,
RankProfilesConfig.Producer,
RankingConstantsConfig.Producer,
- ServletPathsConfig.Producer
+ ServletPathsConfig.Producer,
+ ContainerMbusConfig.Producer
{
private final Set<FileReference> applicationBundles = new LinkedHashSet<>();
@@ -50,11 +56,16 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat
private ContainerModelEvaluation modelEvaluation;
private Optional<TlsSecrets> tlsSecrets;
+ private final boolean enableGroupingSessionCache;
+
+ private MbusParams mbusParams;
+ private boolean messageBusEnabled = true;
public ApplicationContainerCluster(AbstractConfigProducer<?> parent, String subId, String name, DeployState deployState) {
super(parent, subId, name, deployState);
this.tlsSecrets = deployState.tlsSecrets();
+ this.enableGroupingSessionCache = deployState.getProperties().enableGroupingSessionCache();
restApiGroup = new ConfigProducerGroup<>(this, "rest-api");
servletGroup = new ConfigProducerGroup<>(this, "servlet");
@@ -154,8 +165,60 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat
if (modelEvaluation != null) modelEvaluation.getConfig(builder);
}
+ @Override
+ public void getConfig(ContainerMbusConfig.Builder builder) {
+ if (mbusParams != null) {
+ if (mbusParams.maxConcurrentFactor != null)
+ builder.maxConcurrentFactor(mbusParams.maxConcurrentFactor);
+ if (mbusParams.documentExpansionFactor != null)
+ builder.documentExpansionFactor(mbusParams.documentExpansionFactor);
+ if (mbusParams.containerCoreMemory != null)
+ builder.containerCoreMemory(mbusParams.containerCoreMemory);
+ }
+ if (getDocproc() != null)
+ getDocproc().getConfig(builder);
+ }
+
public Optional<TlsSecrets> getTlsSecrets() {
return tlsSecrets;
}
+ public boolean enableGroupingSessionCache() {
+ return enableGroupingSessionCache;
+ }
+
+ public void setMbusParams(MbusParams mbusParams) {
+ this.mbusParams = mbusParams;
+ }
+
+ public final void setMessageBusEnabled(boolean messageBusEnabled) { this.messageBusEnabled = messageBusEnabled; }
+
+ protected boolean messageBusEnabled() { return messageBusEnabled; }
+
+ public void addMbusServer(ComponentId chainId) {
+ ComponentId serviceId = chainId.nestInNamespace(ComponentId.fromString("MbusServer"));
+
+ addComponent(
+ new Component<>(new ComponentModel(new BundleInstantiationSpecification(
+ serviceId,
+ ComponentSpecification.fromString(MbusServerProvider.class.getName()),
+ null))));
+ }
+
+ public static class MbusParams {
+ // the amount of the maxpendingbytes to process concurrently, typically 0.2 (20%)
+ final Double maxConcurrentFactor;
+
+ // the amount that documents expand temporarily when processing them
+ final Double documentExpansionFactor;
+
+ // the space to reserve for container, docproc stuff (memory that cannot be used for processing documents), in MB
+ final Integer containerCoreMemory;
+
+ public MbusParams(Double maxConcurrentFactor, Double documentExpansionFactor, Integer containerCoreMemory) {
+ this.maxConcurrentFactor = maxConcurrentFactor;
+ this.documentExpansionFactor = documentExpansionFactor;
+ this.containerCoreMemory = containerCoreMemory;
+ }
+ }
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java
index 47adac637ee..779d0eb028d 100755
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java
@@ -5,7 +5,6 @@ import com.yahoo.cloud.config.ClusterInfoConfig;
import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.cloud.config.RoutingProviderConfig;
import com.yahoo.component.ComponentId;
-import com.yahoo.component.ComponentSpecification;
import com.yahoo.config.application.api.ApplicationMetaData;
import com.yahoo.config.docproc.DocprocConfig;
import com.yahoo.config.docproc.SchemamappingConfig;
@@ -20,10 +19,8 @@ import com.yahoo.container.bundle.BundleInstantiationSpecification;
import com.yahoo.container.core.ApplicationMetadataConfig;
import com.yahoo.container.core.document.ContainerDocumentConfig;
import com.yahoo.container.handler.ThreadPoolProvider;
-import com.yahoo.container.jdisc.ContainerMbusConfig;
import com.yahoo.container.jdisc.JdiscBindingsConfig;
import com.yahoo.container.jdisc.config.HealthMonitorConfig;
-import com.yahoo.container.jdisc.messagebus.MbusServerProvider;
import com.yahoo.container.jdisc.state.StateHandler;
import com.yahoo.container.logging.AccessLog;
import com.yahoo.container.usability.BindingsOverviewHandler;
@@ -89,7 +86,6 @@ public abstract class ContainerCluster<CONTAINER extends Container>
ComponentsConfig.Producer,
JdiscBindingsConfig.Producer,
DocumentmanagerConfig.Producer,
- ContainerMbusConfig.Producer,
ContainerDocumentConfig.Producer,
HealthMonitorConfig.Producer,
ApplicationMetadataConfig.Producer,
@@ -139,10 +135,6 @@ public abstract class ContainerCluster<CONTAINER extends Container>
private ContainerDocumentApi containerDocumentApi;
private SecretStore secretStore;
- // TODO: move all message-bus related fields/methods to ApplicationContainerCluster. No need for mbus for other clusters.
- private MbusParams mbusParams;
- private boolean messageBusEnabled = true;
-
private boolean rpcServerEnabled = true;
private boolean httpServerEnabled = true;
@@ -297,16 +289,6 @@ public abstract class ContainerCluster<CONTAINER extends Container>
protected abstract void doPrepare(DeployState deployState);
- public void addMbusServer(ComponentId chainId) {
- ComponentId serviceId = chainId.nestInNamespace(ComponentId.fromString("MbusServer"));
-
- addComponent(
- new Component<>(new ComponentModel(new BundleInstantiationSpecification(
- serviceId,
- ComponentSpecification.fromString(MbusServerProvider.class.getName()),
- null))));
- }
-
public String getName() {
return name;
}
@@ -551,24 +533,6 @@ public abstract class ContainerCluster<CONTAINER extends Container>
if (containerSearch != null) containerSearch.getConfig(builder);
}
- @Override
- public void getConfig(ContainerMbusConfig.Builder builder) {
- if (mbusParams != null) {
- if (mbusParams.maxConcurrentFactor != null)
- builder.maxConcurrentFactor(mbusParams.maxConcurrentFactor);
- if (mbusParams.documentExpansionFactor != null)
- builder.documentExpansionFactor(mbusParams.documentExpansionFactor);
- if (mbusParams.containerCoreMemory != null)
- builder.containerCoreMemory(mbusParams.containerCoreMemory);
- }
- if (containerDocproc != null)
- containerDocproc.getConfig(builder);
- }
-
- public void setMbusParams(MbusParams mbusParams) {
- this.mbusParams = mbusParams;
- }
-
public void initialize(Map<String, AbstractSearchCluster> clusterMap) {
if (containerSearch != null) containerSearch.connectSearchClusters(clusterMap);
}
@@ -660,10 +624,6 @@ public abstract class ContainerCluster<CONTAINER extends Container>
*/
public Optional<Integer> getMemoryPercentage() { return Optional.ofNullable(memoryPercentage); }
- public final void setMessageBusEnabled(boolean messageBusEnabled) { this.messageBusEnabled = messageBusEnabled; }
-
- boolean messageBusEnabled() { return messageBusEnabled; }
-
public final void setRpcServerEnabled(boolean rpcServerEnabled) { this.rpcServerEnabled = rpcServerEnabled; }
boolean rpcServerEnabled() { return rpcServerEnabled; }
@@ -677,21 +637,6 @@ public abstract class ContainerCluster<CONTAINER extends Container>
return "container cluster '" + getName() + "'";
}
- public static class MbusParams {
- // the amount of the maxpendingbytes to process concurrently, typically 0.2 (20%)
- final Double maxConcurrentFactor;
-
- // the amount that documents expand temporarily when processing them
- final Double documentExpansionFactor;
-
- // the space to reserve for container, docproc stuff (memory that cannot be used for processing documents), in MB
- final Integer containerCoreMemory;
-
- public MbusParams(Double maxConcurrentFactor, Double documentExpansionFactor, Integer containerCoreMemory) {
- this.maxConcurrentFactor = maxConcurrentFactor;
- this.documentExpansionFactor = documentExpansionFactor;
- this.containerCoreMemory = containerCoreMemory;
- }
- }
+ protected abstract boolean messageBusEnabled();
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/ContainerSubsystem.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/ContainerSubsystem.java
index 3b0bb959a7b..3fa4db60195 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/ContainerSubsystem.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/ContainerSubsystem.java
@@ -1,8 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.container.component;
-import com.yahoo.config.model.producer.AbstractConfigProducer;
-import com.yahoo.vespa.model.container.ContainerCluster;
import com.yahoo.vespa.model.container.component.chain.Chains;
import edu.umd.cs.findbugs.annotations.NonNull;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/Servlet.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/Servlet.java
index 011fcb7c3c7..e8d733f26a2 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/Servlet.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/Servlet.java
@@ -1,7 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.container.component;
-import com.yahoo.config.model.producer.AbstractConfigProducer;
import com.yahoo.jdisc.http.ServletPathsConfig;
import com.yahoo.osgi.provider.model.ComponentModel;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/ContainerDocproc.java b/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/ContainerDocproc.java
index 57d5c203d32..5a251194d75 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/ContainerDocproc.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/ContainerDocproc.java
@@ -3,13 +3,12 @@ package com.yahoo.vespa.model.container.docproc;
import com.yahoo.collections.Pair;
import com.yahoo.config.docproc.DocprocConfig;
-import com.yahoo.container.jdisc.config.SessionConfig;
-import com.yahoo.container.jdisc.ContainerMbusConfig;
import com.yahoo.config.docproc.SchemamappingConfig;
+import com.yahoo.container.jdisc.ContainerMbusConfig;
+import com.yahoo.container.jdisc.config.SessionConfig;
import com.yahoo.docproc.jdisc.messagebus.MbusRequestContext;
import com.yahoo.vespa.model.container.ContainerCluster;
import com.yahoo.vespa.model.container.component.ContainerSubsystem;
-import com.yahoo.vespa.model.container.component.chain.ProcessingHandler;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.util.HashMap;
@@ -18,7 +17,6 @@ import java.util.Map;
/**
* @author einarmr
* @author gjoranv
- * @since 5.1.9
*/
public class ContainerDocproc extends ContainerSubsystem<DocprocChains>
implements
@@ -70,18 +68,10 @@ public class ContainerDocproc extends ContainerSubsystem<DocprocChains>
return preferLocalNode;
}
- public void setPreferLocalNode(boolean preferLocalNode) {
- this.preferLocalNode = preferLocalNode;
- }
-
public int getNumNodesPerClient() {
return numNodesPerClient;
}
- public void setNumNodesPerClient(int numNodesPerClient) {
- this.numNodesPerClient = numNodesPerClient;
- }
-
@Override
public void getConfig(ContainerMbusConfig.Builder builder) {
builder.maxpendingcount(getMaxMessagesInQueue());
@@ -116,10 +106,6 @@ public class ContainerDocproc extends ContainerSubsystem<DocprocChains>
}
}
- public ProcessingHandler<DocprocChains> getDocprocHandler() {
- return getChains().getDocprocHandler();
- }
-
@Override
public void getConfig(SchemamappingConfig.Builder builder) {
Map<Pair<String, String>, String> allMappings = new HashMap<>();
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/DocprocChains.java b/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/DocprocChains.java
index cafc65019ce..5d08a0a6998 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/DocprocChains.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/DocprocChains.java
@@ -4,14 +4,14 @@ package com.yahoo.vespa.model.container.docproc;
import com.yahoo.component.ComponentId;
import com.yahoo.config.model.producer.AbstractConfigProducer;
import com.yahoo.container.jdisc.config.SessionConfig;
+import com.yahoo.vespa.model.container.ApplicationContainerCluster;
import com.yahoo.vespa.model.container.ContainerCluster;
import com.yahoo.vespa.model.container.component.Component;
import com.yahoo.vespa.model.container.component.chain.Chains;
import com.yahoo.vespa.model.container.component.chain.ProcessingHandler;
/**
- * @author einarmr
- * @since 5.1.9
+ * @author Einar M R Rosenvinge
*/
public class DocprocChains extends Chains<DocprocChain> {
private final ProcessingHandler<DocprocChains> docprocHandler;
@@ -22,10 +22,6 @@ public class DocprocChains extends Chains<DocprocChain> {
addComponent(docprocHandler);
}
- public ProcessingHandler<DocprocChains> getDocprocHandler() {
- return docprocHandler;
- }
-
private void addComponent(Component component) {
if (!(getParent() instanceof ContainerCluster)) {
return;
@@ -35,13 +31,13 @@ public class DocprocChains extends Chains<DocprocChain> {
public void addServersAndClientsForChains() {
- if (getParent() instanceof ContainerCluster) {
+ if (getParent() instanceof ApplicationContainerCluster) {
for (DocprocChain chain: getChainGroup().getComponents())
- addServerAndClientForChain((ContainerCluster) getParent(), chain);
+ addServerAndClientForChain((ApplicationContainerCluster) getParent(), chain);
}
}
- private void addServerAndClientForChain(ContainerCluster cluster, DocprocChain docprocChain) {
+ private void addServerAndClientForChain(ApplicationContainerCluster cluster, DocprocChain docprocChain) {
docprocHandler.addServerBindings("mbus://*/" + docprocChain.getSessionName());
cluster.addMbusServer(ComponentId.fromString(docprocChain.getSessionName()));
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/DocumentProcessor.java b/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/DocumentProcessor.java
index 92b636ed48b..62ea405179a 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/DocumentProcessor.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/DocumentProcessor.java
@@ -5,9 +5,6 @@ import com.yahoo.collections.Pair;
import com.yahoo.vespa.model.container.component.chain.ChainedComponent;
import com.yahoo.vespa.model.container.docproc.model.DocumentProcessorModel;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
import java.util.Map;
/**
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/Http.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/Http.java
index 9a67871e683..a03c5e1ec42 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/Http.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/Http.java
@@ -86,7 +86,7 @@ public class Http extends AbstractConfigProducer<AbstractConfigProducer<?>> impl
}
@Override
- public void validate() throws Exception {
+ public void validate() {
validate(bindings);
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/processing/Processor.java b/config-model/src/main/java/com/yahoo/vespa/model/container/processing/Processor.java
index 3ad6484aaec..efae7278b14 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/processing/Processor.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/processing/Processor.java
@@ -2,7 +2,6 @@
package com.yahoo.vespa.model.container.processing;
import com.yahoo.component.chain.model.ChainedComponentModel;
-import com.yahoo.config.model.producer.AbstractConfigProducer;
import com.yahoo.vespa.model.container.component.chain.ChainedComponent;
/**
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java
index d2f7c5e4549..d111c423ca1 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java
@@ -1,21 +1,20 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.container.search;
+import com.yahoo.container.QrSearchersConfig;
import com.yahoo.container.bundle.BundleInstantiationSpecification;
import com.yahoo.osgi.provider.model.ComponentModel;
import com.yahoo.prelude.fastsearch.FS4ResourcePool;
import com.yahoo.prelude.semantics.SemanticRulesConfig;
+import com.yahoo.search.config.IndexInfoConfig;
+import com.yahoo.search.pagetemplates.PageTemplatesConfig;
+import com.yahoo.search.query.profile.config.QueryProfilesConfig;
+import com.yahoo.vespa.configdefinition.IlscriptsConfig;
import com.yahoo.vespa.model.container.ApplicationContainerCluster;
-import com.yahoo.vespa.model.container.ContainerCluster;
import com.yahoo.vespa.model.container.component.Component;
import com.yahoo.vespa.model.container.component.ContainerSubsystem;
import com.yahoo.vespa.model.container.search.searchchain.LocalProvider;
import com.yahoo.vespa.model.container.search.searchchain.SearchChains;
-import com.yahoo.search.config.IndexInfoConfig;
-import com.yahoo.vespa.configdefinition.IlscriptsConfig;
-import com.yahoo.container.QrSearchersConfig;
-import com.yahoo.search.query.profile.config.QueryProfilesConfig;
-import com.yahoo.search.pagetemplates.PageTemplatesConfig;
import com.yahoo.vespa.model.search.AbstractSearchCluster;
import com.yahoo.vespa.model.search.Dispatch;
import com.yahoo.vespa.model.search.IndexedSearchCluster;
@@ -41,6 +40,7 @@ public class ContainerSearch extends ContainerSubsystem<SearchChains>
private final List<AbstractSearchCluster> systems = new LinkedList<>();
private final Options options;
+ private final boolean enableGroupingSessionCache;
private QueryProfiles queryProfiles;
private SemanticRules semanticRules;
@@ -52,6 +52,7 @@ public class ContainerSearch extends ContainerSubsystem<SearchChains>
// TODO: Should be added to container instead of cluster to get proper configId for qr config.
cluster.addComponent(getFS4ResourcePool());
+ this.enableGroupingSessionCache = cluster.enableGroupingSessionCache();
}
private static Component<?, ComponentModel> getFS4ResourcePool() {
@@ -98,7 +99,10 @@ public class ContainerSearch extends ContainerSubsystem<SearchChains>
@Override
public void getConfig(QueryProfilesConfig.Builder builder) {
- if (queryProfiles!=null) queryProfiles.getConfig(builder);
+ if (queryProfiles!=null) {
+ queryProfiles.getConfig(builder);
+ }
+ builder.enableGroupingSessionCache(enableGroupingSessionCache);
}
@Override
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/QueryProfiles.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/QueryProfiles.java
index c7114178ad6..0abb0803405 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/QueryProfiles.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/QueryProfiles.java
@@ -11,10 +11,16 @@ import com.yahoo.search.query.profile.types.QueryProfileType;
import com.yahoo.search.query.profile.config.QueryProfilesConfig;
import java.io.Serializable;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
import java.util.logging.Level;
-import java.util.stream.Collectors;
/**
* Owns the query profiles and query profile types to be handed to the qrs nodes.
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/QueryProfilesBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/QueryProfilesBuilder.java
index b832c1bbdcd..b653b0acf8d 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/QueryProfilesBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/QueryProfilesBuilder.java
@@ -6,7 +6,6 @@ import com.yahoo.io.reader.NamedReader;
import com.yahoo.search.query.profile.config.QueryProfileXMLReader;
import com.yahoo.config.application.api.ApplicationPackage;
-import java.util.Collections;
import java.util.List;
/**
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/LocalProvider.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/LocalProvider.java
index cb1d94717f6..aa820d1d898 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/LocalProvider.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/LocalProvider.java
@@ -85,7 +85,7 @@ public class LocalProvider extends Provider implements
}
}
- private void addProviderSearchers(LocalProviderSpec providerSpec) {
+ private void addProviderSearchers() {
for (ChainedComponentModel searcherModel : LocalProviderSpec.searcherModels) {
addInnerComponent(new Searcher<>(searcherModel));
}
@@ -130,7 +130,7 @@ public class LocalProvider extends Provider implements
FederationOptions federationOptions,
LocalProviderSpec providerSpec) {
super(specWithoutInnerSearchers, federationOptions);
- addProviderSearchers(providerSpec);
+ addProviderSearchers();
this.providerSpec = providerSpec;
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java
index 672ee0bb161..2bfb1da9dcb 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java
@@ -79,7 +79,6 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
-import java.util.logging.Level;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/ContentNode.java b/config-model/src/main/java/com/yahoo/vespa/model/content/ContentNode.java
index dc9372c463b..d25396c6a50 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/ContentNode.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/ContentNode.java
@@ -1,10 +1,8 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.content;
-import com.yahoo.config.provision.Flavor;
import com.yahoo.metrics.MetricsmanagerConfig;
import com.yahoo.vespa.config.content.LoadTypeConfig;
-import com.yahoo.vespa.config.content.StorFilestorConfig;
import com.yahoo.vespa.config.content.core.StorCommunicationmanagerConfig;
import com.yahoo.vespa.config.content.core.StorServerConfig;
import com.yahoo.vespa.config.content.core.StorStatusConfig;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java
index db7b4a88721..a6228af8991 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java
@@ -233,7 +233,7 @@ public class ContentSearchCluster extends AbstractConfigProducer implements Prot
Optional<Tuning> tuning = Optional.ofNullable(this.tuning);
if (element == null) {
snode = SearchNode.create(parent, "" + node.getDistributionKey(), node.getDistributionKey(), spec,
- clusterName, node, flushOnShutdown, tuning, parentGroup.getOwner().isHostedVespa());
+ clusterName, node, flushOnShutdown, tuning, parentGroup.isHosted());
snode.setHostResource(node.getHostResource());
snode.initService(deployState.getDeployLogger());
@@ -283,9 +283,13 @@ public class ContentSearchCluster extends AbstractConfigProducer implements Prot
if (usesHierarchicDistribution()) {
indexedCluster.setMaxNodesDownPerFixedRow((redundancy.effectiveFinalRedundancy() / groupToSpecMap.size()) - 1);
}
- indexedCluster.setSearchableCopies(redundancy.searchableCopies());
+ indexedCluster.setSearchableCopies(redundancy.readyCopies());
}
this.redundancy = redundancy;
+ for (SearchNode node : getSearchNodes()) {
+ node.setRedundancy(redundancy.finalRedundancy());
+ node.setSearchableCopies(redundancy.readyCopies());
+ }
}
private Optional<StreamingSearchCluster> findStreamingCluster(String docType) {
@@ -306,7 +310,7 @@ public class ContentSearchCluster extends AbstractConfigProducer implements Prot
@Override
public void getConfig(ProtonConfig.Builder builder) {
double visibilityDelay = hasIndexedCluster() ? getIndexed().getVisibilityDelay() : 0.0;
- builder.feeding.concurrency(0.25); // As if specified 0.5 in services.xml
+ builder.feeding.concurrency(0.30); // As if specified 0.6 in services.xml
boolean hasAnyNonIndexedCluster = false;
for (NewDocumentType type : TopologicalDocumentTypeSorter.sort(documentDefinitions.values())) {
ProtonConfig.Documentdb.Builder ddbB = new ProtonConfig.Documentdb.Builder();
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/IndexedHierarchicDistributionValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/content/IndexedHierarchicDistributionValidator.java
index c721d3d48bc..7ab5ee9fd80 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/IndexedHierarchicDistributionValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/IndexedHierarchicDistributionValidator.java
@@ -33,15 +33,15 @@ public class IndexedHierarchicDistributionValidator {
public void validate() throws Exception {
validateThatWeHaveOneGroupLevel();
validateThatLeafGroupsHasEqualNumberOfNodes();
- validateThatLeafGroupsCountIsAFactorOfRedundancy();
+ validateThatLeafGroupsCountIsAFactorOfRedundancy(clusterName, redundancy.effectiveFinalRedundancy(), rootGroup.getSubgroups().size());
validateThatRedundancyPerGroupIsEqual();
- validateThatReadyCopiesIsCompatibleWithRedundancy(rootGroup.getSubgroups().size());
+ validateThatReadyCopiesIsCompatibleWithRedundancy(clusterName, redundancy.effectiveFinalRedundancy(), redundancy.effectiveReadyCopies(), rootGroup.getSubgroups().size());
}
private void validateThatWeHaveOneGroupLevel() {
for (StorageGroup group : rootGroup.getSubgroups()) {
if (group.getSubgroups().size() > 0) {
- throw new IllegalArgumentException(getErrorMsgPrefix() + "Expected all groups under root group '" +
+ throw new IllegalArgumentException(getErrorMsgPrefix(clusterName) + "Expected all groups under root group '" +
rootGroup.getName() + "' to be leaf groups only containing nodes, but sub group '" + group.getName() + "' contains " +
group.getSubgroups().size() + " sub groups.");
}
@@ -59,18 +59,18 @@ public class IndexedHierarchicDistributionValidator {
}
if (group.getNodes().size() != previousGroup.getNodes().size())
- throw new IllegalArgumentException(getErrorMsgPrefix() + "Expected leaf groups to contain an equal number of nodes, but leaf group '" +
+ throw new IllegalArgumentException(getErrorMsgPrefix(clusterName) + "Expected leaf groups to contain an equal number of nodes, but leaf group '" +
previousGroup.getName() + "' contains " + previousGroup.getNodes().size() + " node(s) while leaf group '" +
group.getName() + "' contains " + group.getNodes().size() + " node(s).");
previousGroup = group;
}
}
- private void validateThatLeafGroupsCountIsAFactorOfRedundancy() {
- if (redundancy.effectiveFinalRedundancy() % rootGroup.getSubgroups().size() != 0) {
- throw new IllegalArgumentException(getErrorMsgPrefix() + "Expected number of leaf groups (" +
- rootGroup.getSubgroups().size() + ") to be a factor of redundancy (" +
- redundancy.effectiveFinalRedundancy() + "), but it is not.");
+ static public void validateThatLeafGroupsCountIsAFactorOfRedundancy(String clusterName, int totalRedundancy, int subGroups) {
+ if (totalRedundancy % subGroups != 0) {
+ throw new IllegalArgumentException(getErrorMsgPrefix(clusterName) + "Expected number of leaf groups (" +
+ subGroups + ") to be a factor of redundancy (" +
+ totalRedundancy + "), but it is not.");
}
}
@@ -78,7 +78,7 @@ public class IndexedHierarchicDistributionValidator {
int redundancyPerGroup = redundancy.effectiveFinalRedundancy() / rootGroup.getSubgroups().size();
String expPartitions = createDistributionPartitions(redundancyPerGroup, rootGroup.getSubgroups().size());
if (!rootGroup.getPartitions().get().equals(expPartitions)) {
- throw new IllegalArgumentException(getErrorMsgPrefix() + "Expected redundancy per leaf group to be " +
+ throw new IllegalArgumentException(getErrorMsgPrefix(clusterName) + "Expected redundancy per leaf group to be " +
redundancyPerGroup + ", but it is not according to distribution partitions '" +
rootGroup.getPartitions().get() + "'. Expected distribution partitions should be '" + expPartitions + "'.");
}
@@ -98,20 +98,20 @@ public class IndexedHierarchicDistributionValidator {
return sb.toString();
}
- private void validateThatReadyCopiesIsCompatibleWithRedundancy(int groupCount) throws Exception {
- if (redundancy.effectiveFinalRedundancy() % groupCount != 0) {
- throw new Exception(getErrorMsgPrefix() + "Expected equal redundancy per group.");
+ static public void validateThatReadyCopiesIsCompatibleWithRedundancy(String clusterName, int totalRedundancy, int totalReadyCopies, int groupCount) {
+ if (totalRedundancy % groupCount != 0) {
+ throw new IllegalArgumentException(getErrorMsgPrefix(clusterName) + "Expected equal redundancy per group.");
}
- if (redundancy.effectiveReadyCopies() % groupCount != 0) {
- throw new Exception(getErrorMsgPrefix() + "Expected equal amount of ready copies per group, but " +
- redundancy.effectiveReadyCopies() + " ready copies is specified with " + groupCount + " groups");
+ if (totalReadyCopies % groupCount != 0) {
+ throw new IllegalArgumentException(getErrorMsgPrefix(clusterName) + "Expected equal amount of ready copies per group, but " +
+ totalReadyCopies + " ready copies is specified with " + groupCount + " groups");
}
- if (redundancy.effectiveReadyCopies() == 0) {
- System.err.println(getErrorMsgPrefix() + "Warning. No ready copies configured. At least one is recommended.");
+ if (totalReadyCopies == 0) {
+ System.err.println(getErrorMsgPrefix(clusterName) + "Warning. No ready copies configured. At least one is recommended.");
}
}
- private String getErrorMsgPrefix() {
+ static private String getErrorMsgPrefix(String clusterName) {
return "In indexed content cluster '" + clusterName + "' using hierarchic distribution: ";
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/Redundancy.java b/config-model/src/main/java/com/yahoo/vespa/model/content/Redundancy.java
index b21a0da0d57..aa9b04d084d 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/Redundancy.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/Redundancy.java
@@ -11,54 +11,36 @@ import com.yahoo.vespa.config.search.core.ProtonConfig;
*/
public class Redundancy implements StorDistributionConfig.Producer, ProtonConfig.Producer {
+ // This numbers are all per group as wanted numbers.
private final int initialRedundancy ;
private final int finalRedundancy;
private final int readyCopies;
- private int implicitGroups = 1;
- private int explicitGroups = 1;
+ private final int groups;
/** The total number of nodes available in this cluster (assigned when this becomes known) */
- private int totalNodes = 0;
+ private final int totalNodes;
- public Redundancy(int initialRedundancy, int finalRedundancy, int readyCopies) {
+ public Redundancy(int initialRedundancy, int finalRedundancy, int readyCopies, int groups, int totalNodes) {
this.initialRedundancy = initialRedundancy;
this.finalRedundancy = finalRedundancy;
this.readyCopies = readyCopies;
+ this.groups = groups;
+ this.totalNodes = totalNodes;
}
- /**
- * Set the total number of nodes available in this cluster.
- * This impacts the effective redundancy in the case where there are fewer nodes available than
- * the requested redundancy.
- */
- public void setTotalNodes(int totalNodes) { this.totalNodes = totalNodes; }
+ public int finalRedundancy() { return effectiveFinalRedundancy()/groups; }
+ public int readyCopies() { return effectiveReadyCopies()/groups; }
+ public int groups() { return groups; }
+ public int totalNodes() { return totalNodes; }
- /**
- * Sets the number of groups resulting from implicit setup (groups attribute)
- * in this cluster. With implicit groups the redundancy settings are taken to be
- * <i>per group</i> and are multiplied by this number to get the effective <i>total</i>
- * values returned in the config.
- */
- public void setImplicitGroups(int implicitGroups) { this.implicitGroups = implicitGroups; }
-
- public void setExplicitGroups(int explicitGroups) { this.explicitGroups = explicitGroups; }
-
- public int initialRedundancy() { return initialRedundancy; }
- public int finalRedundancy() { return finalRedundancy; }
- public int readyCopies() { return readyCopies; }
- public int totalNodes() {
- return totalNodes;
- }
-
- public int effectiveInitialRedundancy() { return Math.min(totalNodes, initialRedundancy * implicitGroups); }
- public int effectiveFinalRedundancy() { return Math.min(totalNodes, finalRedundancy * implicitGroups); }
- public int effectiveReadyCopies() { return Math.min(totalNodes, readyCopies * implicitGroups); }
+ public int effectiveInitialRedundancy() { return Math.min(totalNodes, initialRedundancy * groups); }
+ public int effectiveFinalRedundancy() { return Math.min(totalNodes, finalRedundancy * groups); }
+ public int effectiveReadyCopies() { return Math.min(totalNodes, readyCopies * groups); }
public boolean isEffectivelyGloballyDistributed() {
return totalNodes == effectiveFinalRedundancy();
}
- public int searchableCopies() { return readyCopies/(explicitGroups*implicitGroups); }
@Override
public void getConfig(StorDistributionConfig.Builder builder) {
@@ -69,8 +51,8 @@ public class Redundancy implements StorDistributionConfig.Producer, ProtonConfig
@Override
public void getConfig(ProtonConfig.Builder builder) {
ProtonConfig.Distribution.Builder distBuilder = new ProtonConfig.Distribution.Builder();
- distBuilder.redundancy(finalRedundancy/explicitGroups);
- distBuilder.searchablecopies(searchableCopies());
+ distBuilder.redundancy(finalRedundancy());
+ distBuilder.searchablecopies(readyCopies());
builder.distribution(distBuilder);
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/ResourceLimits.java b/config-model/src/main/java/com/yahoo/vespa/model/content/ResourceLimits.java
index 67e5dcdd0ee..12ebde2276a 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/ResourceLimits.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/ResourceLimits.java
@@ -2,7 +2,6 @@
package com.yahoo.vespa.model.content;
import com.yahoo.vespa.config.search.core.ProtonConfig;
-import com.yahoo.vespa.defaults.Defaults;
import java.util.Optional;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/StorageGroup.java b/config-model/src/main/java/com/yahoo/vespa/model/content/StorageGroup.java
index 84132427427..4d1252a2618 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/StorageGroup.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/StorageGroup.java
@@ -15,6 +15,7 @@ import com.yahoo.vespa.model.builder.xml.dom.NodesSpecification;
import com.yahoo.vespa.model.builder.xml.dom.VespaDomBuilder;
import com.yahoo.vespa.model.container.Container;
import com.yahoo.vespa.model.content.cluster.ContentCluster;
+import com.yahoo.vespa.model.content.cluster.RedundancyBuilder;
import com.yahoo.vespa.model.content.engines.PersistenceEngine;
import java.util.ArrayList;
@@ -37,7 +38,7 @@ public class StorageGroup {
private final String index;
private Optional<String> partitions;
String name;
- private final ContentCluster owner;
+ private final boolean isHosted;
private final Optional<Long> mmapNoCoreLimit;
private final Optional<Boolean> coreOnOOM;
private final Optional<String> noVespaMalloc;
@@ -51,7 +52,7 @@ public class StorageGroup {
/**
* Creates a storage group
*
- * @param owner the cluster this group belongs to
+ * @param isHosted true if this is in a hosted setup
* @param name the name of this group
* @param index the distribution-key index og this group
* @param partitions the distribution strategy to use to distribute content to subgroups or empty
@@ -59,12 +60,12 @@ public class StorageGroup {
* (having nodes, not subgroups as children).
* @param useCpuSocketAffinity whether processes should be started with socket affinity
*/
- private StorageGroup(ContentCluster owner, String name, String index, Optional<String> partitions,
+ private StorageGroup(boolean isHosted, String name, String index, Optional<String> partitions,
boolean useCpuSocketAffinity, Optional<Long> mmapNoCoreLimit, Optional<Boolean> coreOnOOM,
Optional<String> noVespaMalloc, Optional<String> vespaMalloc,
Optional<String> vespaMallocDebug, Optional<String> vespaMallocDebugStackTrace)
{
- this.owner = owner;
+ this.isHosted = isHosted;
this.index = index;
this.name = name;
this.partitions = partitions;
@@ -76,8 +77,8 @@ public class StorageGroup {
this.vespaMallocDebug = vespaMallocDebug;
this.vespaMallocDebugStackTrace = vespaMallocDebugStackTrace;
}
- private StorageGroup(ContentCluster owner, String name, String index) {
- this(owner, name, index, Optional.empty(), false, Optional.empty(),Optional.empty(), Optional.empty(),
+ private StorageGroup(boolean isHosted, String name, String index) {
+ this(isHosted, name, index, Optional.empty(), false, Optional.empty(),Optional.empty(), Optional.empty(),
Optional.empty(), Optional.empty(), Optional.empty());
}
@@ -90,7 +91,7 @@ public class StorageGroup {
/** Returns the nodes of this, or an empty list of it is not a leaf group */
public List<StorageNode> getNodes() { return nodes; }
- public ContentCluster getOwner() { return owner; }
+ public boolean isHosted() { return isHosted; }
/** Returns the index of this group, or null if it is the root group */
public String getIndex() { return index; }
@@ -194,16 +195,14 @@ public class StorageGroup {
public static class Builder {
private final ModelElement clusterElement;
- private final ContentCluster owner;
private final ConfigModelContext context;
- public Builder(ModelElement clusterElement, ContentCluster owner, ConfigModelContext context) {
+ public Builder(ModelElement clusterElement, ConfigModelContext context) {
this.clusterElement = clusterElement;
- this.owner = owner;
this.context = context;
}
- public StorageGroup buildRootGroup(DeployState deployState) {
+ public StorageGroup buildRootGroup(DeployState deployState, RedundancyBuilder redundancyBuilder, ContentCluster owner) {
Optional<ModelElement> group = Optional.ofNullable(clusterElement.child("group"));
Optional<ModelElement> nodes = getNodes(clusterElement);
@@ -212,12 +211,28 @@ public class StorageGroup {
if (group.isPresent() && (group.get().stringAttribute("name") != null || group.get().integerAttribute("distribution-key") != null))
deployState.getDeployLogger().log(LogLevel.INFO, "'distribution-key' attribute on a content cluster's root group is ignored");
- GroupBuilder groupBuilder = collectGroup(group, nodes, null, null);
- if (owner.isHostedVespa()) {
- return groupBuilder.buildHosted(deployState, owner, Optional.empty());
- } else {
- return groupBuilder.buildNonHosted(deployState, owner, Optional.empty());
+ GroupBuilder groupBuilder = collectGroup(owner.isHosted(), group, nodes, null, null);
+ StorageGroup storageGroup = (owner.isHosted())
+ ? groupBuilder.buildHosted(deployState, owner, Optional.empty())
+ : groupBuilder.buildNonHosted(deployState, owner, Optional.empty());
+ Redundancy redundancy = redundancyBuilder.build(owner.getName(), owner.isHosted(), storageGroup.subgroups.size(), storageGroup.getNumberOfLeafGroups(), storageGroup.countNodes());
+ owner.setRedundancy(redundancy);
+ if (storageGroup.partitions.isEmpty() && (redundancy.groups() > 1)) {
+ storageGroup.partitions = Optional.of(computePartitions(redundancy.finalRedundancy(), redundancy.groups()));
}
+ return storageGroup;
+ }
+
+ /** This returns a partition string which specifies equal distribution between all groups */
+ // TODO: Make a partitions object
+ static private String computePartitions(int redundancyPerGroup, int numGroups) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < numGroups - 1; ++i) {
+ sb.append(redundancyPerGroup);
+ sb.append("|");
+ }
+ sb.append("*");
+ return sb.toString();
}
/**
@@ -262,9 +277,6 @@ public class StorageGroup {
if ( ! parent.isPresent() && subGroups.isEmpty() && nodeBuilders.isEmpty()) // no nodes or groups: create single node
storageGroup.nodes.add(buildSingleNode(deployState, owner));
-
- if ( ! parent.isPresent())
- owner.redundancy().setTotalNodes(storageGroup.countNodes());
return storageGroup;
}
@@ -297,19 +309,11 @@ public class StorageGroup {
if (hostGroups.size() > 1) {
if (parent.isPresent())
throw new IllegalArgumentException("Cannot specify groups using the groups attribute in nested content groups");
- owner.redundancy().setTotalNodes(hostMapping.size());
-
- // Switch redundancy settings to meaning "per group"
- owner.redundancy().setImplicitGroups(hostGroups.size());
-
- // Compute partitions expression
- int redundancyPerGroup = (int)Math.floor(owner.redundancy().effectiveFinalRedundancy() / hostGroups.size());
- storageGroup.partitions = Optional.of(computePartitions(redundancyPerGroup, hostGroups.size()));
// create subgroups as returned from allocation
for (Map.Entry<Optional<ClusterSpec.Group>, Map<HostResource, ClusterMembership>> hostGroup : hostGroups.entrySet()) {
String groupIndex = String.valueOf(hostGroup.getKey().get().index());
- StorageGroup subgroup = new StorageGroup(owner, groupIndex, groupIndex);
+ StorageGroup subgroup = new StorageGroup(true, groupIndex, groupIndex);
for (Map.Entry<HostResource, ClusterMembership> host : hostGroup.getValue().entrySet()) {
subgroup.nodes.add(createStorageNode(deployState, owner, host.getKey(), subgroup, host.getValue()));
}
@@ -323,24 +327,10 @@ public class StorageGroup {
for (GroupBuilder subGroup : subGroups) {
storageGroup.subgroups.add(subGroup.buildHosted(deployState, owner, Optional.of(this)));
}
- if ( ! parent.isPresent())
- owner.redundancy().setTotalNodes(storageGroup.countNodes());
}
return storageGroup;
}
- /** This returns a partition string which specifies equal distribution between all groups */
- // TODO: Make a partitions object
- private String computePartitions(int redundancyPerGroup, int numGroups) {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < numGroups - 1; ++i) {
- sb.append(redundancyPerGroup);
- sb.append("|");
- }
- sb.append("*");
- return sb.toString();
- }
-
/** Collect hosts per group */
private Map<Optional<ClusterSpec.Group>, Map<HostResource, ClusterMembership>> collectAllocatedSubgroups(Map<HostResource, ClusterMembership> hostMapping) {
Map<Optional<ClusterSpec.Group>, Map<HostResource, ClusterMembership>> hostsPerGroup = new LinkedHashMap<>();
@@ -387,9 +377,9 @@ public class StorageGroup {
* <li>Neither element is present: Create a single node.
* </ul>
*/
- private GroupBuilder collectGroup(Optional<ModelElement> groupElement, Optional<ModelElement> nodesElement, String name, String index) {
+ private GroupBuilder collectGroup(boolean isHosted, Optional<ModelElement> groupElement, Optional<ModelElement> nodesElement, String name, String index) {
StorageGroup group = new StorageGroup(
- owner, name, index,
+ isHosted, name, index,
childAsString(groupElement, "distribution.partitions"),
booleanAttributeOr(groupElement, VespaDomBuilder.CPU_SOCKET_AFFINITY_ATTRIB_NAME, false),
childAsLong(groupElement, VespaDomBuilder.MMAP_NOCORE_LIMIT),
@@ -399,7 +389,7 @@ public class StorageGroup {
childAsString(groupElement, VespaDomBuilder.VESPAMALLOC_DEBUG),
childAsString(groupElement, VespaDomBuilder.VESPAMALLOC_DEBUG_STACKTRACE));
- List<GroupBuilder> subGroups = groupElement.isPresent() ? collectSubGroups(group, groupElement.get()) : Collections.emptyList();
+ List<GroupBuilder> subGroups = groupElement.isPresent() ? collectSubGroups(isHosted, group, groupElement.get()) : Collections.emptyList();
List<XmlNodeBuilder> explicitNodes = new ArrayList<>();
explicitNodes.addAll(collectExplicitNodes(groupElement));
@@ -450,7 +440,7 @@ public class StorageGroup {
return nodes;
}
- private List<GroupBuilder> collectSubGroups(StorageGroup parentGroup, ModelElement parentGroupElement) {
+ private List<GroupBuilder> collectSubGroups(boolean isHosted, StorageGroup parentGroup, ModelElement parentGroupElement) {
List<ModelElement> subGroupElements = parentGroupElement.subElements("group");
if (subGroupElements.size() > 1 && ! parentGroup.getPartitions().isPresent())
throw new IllegalArgumentException("'distribution' attribute is required with multiple subgroups");
@@ -461,7 +451,7 @@ public class StorageGroup {
indexPrefix = parentGroup.index + ".";
}
for (ModelElement g : subGroupElements) {
- subGroups.add(collectGroup(Optional.of(g), Optional.ofNullable(g.child("nodes")), g.stringAttribute("name"),
+ subGroups.add(collectGroup(isHosted, Optional.of(g), Optional.ofNullable(g.child("nodes")), g.stringAttribute("name"),
indexPrefix + g.integerAttribute("distribution-key")));
}
return subGroups;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java
index 833afb67f58..48779657162 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java
@@ -80,9 +80,9 @@ public class ContentCluster extends AbstractConfigProducer implements
MessagetyperouteselectorpolicyConfig.Producer,
BucketspacesConfig.Producer {
- private String documentSelection;
+ private final String documentSelection;
private ContentSearchCluster search;
- private final boolean isHostedVespa;
+ private final boolean isHosted;
private final Map<String, NewDocumentType> documentDefinitions;
private final Set<NewDocumentType> globallyDistributedDocuments;
private com.yahoo.vespa.model.content.StorageGroup rootGroup;
@@ -91,9 +91,9 @@ public class ContentCluster extends AbstractConfigProducer implements
private Redundancy redundancy;
private ClusterControllerConfig clusterControllerConfig;
private PersistenceEngine.PersistenceFactory persistenceFactory;
- private String clusterName;
+ private final String clusterName;
private Integer maxNodesPerMerge;
- private Zone zone;
+ private final Zone zone;
/**
* If multitenant or a cluster controller was explicitly configured in this cluster:
@@ -124,21 +124,20 @@ public class ContentCluster extends AbstractConfigProducer implements
new SearchDefinitionBuilder().build(deployState.getDocumentModel().getDocumentManager(), documentsElement);
String routingSelection = new DocumentSelectionBuilder().build(documentsElement);
- Redundancy redundancy = new RedundancyBuilder().build(contentElement);
+ RedundancyBuilder redundancyBuilder = new RedundancyBuilder(contentElement);
Set<NewDocumentType> globallyDistributedDocuments = new GlobalDistributionBuilder(documentDefinitions).build(documentsElement);
ContentCluster c = new ContentCluster(context.getParentProducer(), getClusterName(contentElement), documentDefinitions,
- globallyDistributedDocuments, routingSelection, redundancy,
+ globallyDistributedDocuments, routingSelection,
deployState.zone(), deployState.isHosted());
c.clusterControllerConfig = new ClusterControllerConfig.Builder(getClusterName(contentElement), contentElement).build(deployState, c, contentElement.getXml());
c.search = new ContentSearchCluster.Builder(documentDefinitions, globallyDistributedDocuments).build(deployState, c, contentElement.getXml());
c.persistenceFactory = new EngineFactoryBuilder().build(contentElement, c);
c.storageNodes = new StorageCluster.Builder().build(deployState, c, w3cContentElement);
c.distributorNodes = new DistributorCluster.Builder(c).build(deployState, c, w3cContentElement);
- c.rootGroup = new StorageGroup.Builder(contentElement, c, context).buildRootGroup(deployState);
+ c.rootGroup = new StorageGroup.Builder(contentElement, context).buildRootGroup(deployState, redundancyBuilder, c);
validateThatGroupSiblingsAreUnique(c.clusterName, c.rootGroup);
- redundancy.setExplicitGroups(c.getRootGroup().getNumberOfLeafGroups());
- c.search.handleRedundancy(redundancy);
+ c.search.handleRedundancy(c.redundancy);
IndexedSearchCluster index = c.search.getIndexed();
if (index != null) {
@@ -492,14 +491,13 @@ public class ContentCluster extends AbstractConfigProducer implements
private ContentCluster(AbstractConfigProducer parent, String clusterName,
Map<String, NewDocumentType> documentDefinitions,
Set<NewDocumentType> globallyDistributedDocuments,
- String routingSelection, Redundancy redundancy, Zone zone, boolean isHostedVespa) {
+ String routingSelection, Zone zone, boolean isHosted) {
super(parent, clusterName);
- this.isHostedVespa = isHostedVespa;
+ this.isHosted = isHosted;
this.clusterName = clusterName;
this.documentDefinitions = documentDefinitions;
this.globallyDistributedDocuments = globallyDistributedDocuments;
this.documentSelection = routingSelection;
- this.redundancy = redundancy;
this.zone = zone;
}
@@ -553,6 +551,10 @@ public class ContentCluster extends AbstractConfigProducer implements
public final ContentSearchCluster getSearch() { return search; }
public Redundancy redundancy() { return redundancy; }
+ public ContentCluster setRedundancy(Redundancy redundancy) {
+ this.redundancy = redundancy;
+ return this;
+ }
@Override
public void getConfig(MessagetyperouteselectorpolicyConfig.Builder builder) {
@@ -639,14 +641,14 @@ public class ContentCluster extends AbstractConfigProducer implements
}
}
- public boolean isHostedVespa() {
- return isHostedVespa;
+ public boolean isHosted() {
+ return isHosted;
}
@Override
public void validate() throws Exception {
super.validate();
- if (search.usesHierarchicDistribution() && ! isHostedVespa) {
+ if (search.usesHierarchicDistribution() && !isHosted) {
// validate manually configured groups
new IndexedHierarchicDistributionValidator(search.getClusterName(), rootGroup, redundancy, search.getIndexed().getTuning().dispatch.policy).validate();
if (search.getIndexed().useMultilevelDispatchSetup()) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/RedundancyBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/RedundancyBuilder.java
index fe73fcc904b..bf579744efb 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/RedundancyBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/RedundancyBuilder.java
@@ -2,22 +2,22 @@
package com.yahoo.vespa.model.content.cluster;
import com.yahoo.vespa.model.builder.xml.dom.ModelElement;
+import com.yahoo.vespa.model.content.IndexedHierarchicDistributionValidator;
import com.yahoo.vespa.model.content.Redundancy;
/**
* Builds redundancy config for a content cluster.
*/
public class RedundancyBuilder {
+ Integer initialRedundancy = 2;
+ Integer finalRedundancy = 3;
+ Integer readyCopies = 2;
- Redundancy build(ModelElement clusterXml) {
- Integer initialRedundancy = 2;
- Integer finalRedundancy = 3;
- Integer readyCopies = 2;
-
+ RedundancyBuilder(ModelElement clusterXml) {
ModelElement redundancyElement = clusterXml.child("redundancy");
if (redundancyElement != null) {
initialRedundancy = redundancyElement.integerAttribute("reply-after");
- finalRedundancy = (int)redundancyElement.asLong();
+ finalRedundancy = (int) redundancyElement.asLong();
if (initialRedundancy == null) {
initialRedundancy = finalRedundancy;
@@ -35,8 +35,16 @@ public class RedundancyBuilder {
throw new IllegalArgumentException("Number of searchable copies can not be higher than final redundancy");
}
}
-
- return new Redundancy(initialRedundancy, finalRedundancy, readyCopies);
+ }
+ public Redundancy build(String clusterName, boolean isHosted, int subGroups, int leafGroups, int totalNodes) {
+ if (isHosted) {
+ return new Redundancy(initialRedundancy, finalRedundancy, readyCopies, leafGroups, totalNodes);
+ } else {
+ subGroups = Math.max(1, subGroups);
+ IndexedHierarchicDistributionValidator.validateThatLeafGroupsCountIsAFactorOfRedundancy(clusterName, finalRedundancy, subGroups);
+ IndexedHierarchicDistributionValidator.validateThatReadyCopiesIsCompatibleWithRedundancy(clusterName, finalRedundancy, readyCopies, subGroups);
+ return new Redundancy(initialRedundancy/subGroups, finalRedundancy/subGroups, readyCopies/subGroups, subGroups, totalNodes);
+ }
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/engines/ProtonProvider.java b/config-model/src/main/java/com/yahoo/vespa/model/content/engines/ProtonProvider.java
index ff3b4891146..c02f8b2b737 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/engines/ProtonProvider.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/engines/ProtonProvider.java
@@ -3,7 +3,6 @@ package com.yahoo.vespa.model.content.engines;
import com.yahoo.vespa.config.content.core.StorServerConfig;
import com.yahoo.vespa.model.content.StorageNode;
-import com.yahoo.vespa.model.search.SearchNode;
/**
* @author baldersheim
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/storagecluster/IntegrityCheckerProducer.java b/config-model/src/main/java/com/yahoo/vespa/model/content/storagecluster/IntegrityCheckerProducer.java
index 46d621981d4..4fa117ad9e7 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/storagecluster/IntegrityCheckerProducer.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/storagecluster/IntegrityCheckerProducer.java
@@ -2,7 +2,6 @@
package com.yahoo.vespa.model.content.storagecluster;
import com.yahoo.vespa.config.content.core.StorIntegritycheckerConfig;
-import com.yahoo.config.model.ConfigModelUtils;
import com.yahoo.vespa.model.builder.xml.dom.ModelElement;
import com.yahoo.vespa.model.content.cluster.ContentCluster;
@@ -21,9 +20,6 @@ public class IntegrityCheckerProducer implements StorIntegritycheckerConfig.Prod
private Integer stopTime;
private String weeklyCycle;
- IntegrityCheckerProducer() {
- }
-
IntegrityCheckerProducer(Integer startTime, Integer stopTime, String weeklyCycle) {
this.startTime = startTime;
this.stopTime = stopTime;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/ml/ConvertedModel.java b/config-model/src/main/java/com/yahoo/vespa/model/ml/ConvertedModel.java
index e12cc60b041..3e70ac4705e 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/ml/ConvertedModel.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/ml/ConvertedModel.java
@@ -31,7 +31,6 @@ import com.yahoo.searchlib.rankingexpression.rule.ReferenceNode;
import com.yahoo.searchlib.rankingexpression.rule.TensorFunctionNode;
import com.yahoo.tensor.Tensor;
import com.yahoo.tensor.TensorType;
-import com.yahoo.tensor.evaluation.TypeContext;
import com.yahoo.tensor.functions.Generate;
import com.yahoo.tensor.functions.Join;
import com.yahoo.tensor.functions.Reduce;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/search/DocumentDatabase.java b/config-model/src/main/java/com/yahoo/vespa/model/search/DocumentDatabase.java
index b29ed0fc25b..74b9e7309c8 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/search/DocumentDatabase.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/search/DocumentDatabase.java
@@ -3,7 +3,6 @@ package com.yahoo.vespa.model.search;
import com.yahoo.config.model.producer.AbstractConfigProducer;
import com.yahoo.search.config.IndexInfoConfig;
-import com.yahoo.searchdefinition.RankingConstant;
import com.yahoo.searchdefinition.derived.DerivedConfiguration;
import com.yahoo.vespa.config.search.AttributesConfig;
import com.yahoo.vespa.config.search.ImportedFieldsConfig;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/search/IndexedSearchCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/search/IndexedSearchCluster.java
index ee82d0cd719..1d804ba3b27 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/search/IndexedSearchCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/search/IndexedSearchCluster.java
@@ -378,13 +378,14 @@ public class IndexedSearchCluster extends SearchCluster
public void setMaxNodesDownPerFixedRow(int value) {
maxNodesDownPerFixedRow = value;
}
- int getSearchableCopies() {
+ public int getSearchableCopies() {
return searchableCopies;
}
public void setSearchableCopies(int searchableCopies) {
this.searchableCopies = searchableCopies;
}
+
public void setDispatchSpec(DispatchSpec dispatchSpec) {
if (dispatchSpec.getNumDispatchGroups() != null) {
this.dispatchSpec = new DispatchSpec.Builder().setGroups
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/search/IndexingDocprocChain.java b/config-model/src/main/java/com/yahoo/vespa/model/search/IndexingDocprocChain.java
index df5f2e844af..df44a429f9c 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/search/IndexingDocprocChain.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/search/IndexingDocprocChain.java
@@ -1,7 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.search;
-import com.yahoo.collections.Pair;
import com.yahoo.component.ComponentId;
import com.yahoo.component.ComponentSpecification;
import com.yahoo.component.chain.Phase;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/search/IndexingProcessor.java b/config-model/src/main/java/com/yahoo/vespa/model/search/IndexingProcessor.java
index a23191e7689..d3d2c8873eb 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/search/IndexingProcessor.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/search/IndexingProcessor.java
@@ -1,7 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.search;
-import com.yahoo.collections.Pair;
import com.yahoo.container.bundle.BundleInstantiationSpecification;
import com.yahoo.component.ComponentId;
import com.yahoo.component.ComponentSpecification;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/search/NodeFlavorTuning.java b/config-model/src/main/java/com/yahoo/vespa/model/search/NodeFlavorTuning.java
index 8ed18299b7b..3260cf3a680 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/search/NodeFlavorTuning.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/search/NodeFlavorTuning.java
@@ -16,9 +16,14 @@ public class NodeFlavorTuning implements ProtonConfig.Producer {
static long MB = 1024 * 1024;
static long GB = MB * 1024;
private final Flavor nodeFlavor;
+ private final int redundancy;
+ private final int searchableCopies;
- public NodeFlavorTuning(Flavor nodeFlavor) {
+
+ public NodeFlavorTuning(Flavor nodeFlavor, int redundancy, int searchableCopies) {
this.nodeFlavor = nodeFlavor;
+ this.redundancy = redundancy;
+ this.searchableCopies = searchableCopies;
}
@Override
@@ -41,7 +46,7 @@ public class NodeFlavorTuning implements ProtonConfig.Producer {
ProtonConfig.Documentdb dbCfg = builder.build();
if (dbCfg.mode() != ProtonConfig.Documentdb.Mode.Enum.INDEX) {
long numDocs = (long)nodeFlavor.getMinMainMemoryAvailableGb()*GB/64L;
- builder.allocation.initialnumdocs(numDocs);
+ builder.allocation.initialnumdocs(numDocs/Math.max(searchableCopies, redundancy));
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/search/SearchColumn.java b/config-model/src/main/java/com/yahoo/vespa/model/search/SearchColumn.java
deleted file mode 100644
index b45db0d1dc3..00000000000
--- a/config-model/src/main/java/com/yahoo/vespa/model/search/SearchColumn.java
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.model.search;
-
-import com.yahoo.config.model.producer.AbstractConfigProducer;
-
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * @author Simon Thoresen Hult
- */
-public class SearchColumn extends AbstractConfigProducer {
-
- // All search nodes contained in this column, these also exist as child config producers.
- private final List<SearchNode> nodes = new LinkedList<>();
-
- public SearchColumn(SearchCluster parent, String name, int index) {
- super(parent, name);
- }
-
- /** @return The number of rows in this column. */
- public int getNumRows() { return nodes.size(); }
-
- /** @return All search nodes contained in this column. */
- public List<SearchNode> getSearchNodes() { return nodes; }
-
-}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/search/SearchNode.java b/config-model/src/main/java/com/yahoo/vespa/model/search/SearchNode.java
index e255da5b487..21882ee640a 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/search/SearchNode.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/search/SearchNode.java
@@ -54,6 +54,8 @@ public class SearchNode extends AbstractService implements
private final boolean flushOnShutdown;
private NodeSpec nodeSpec;
private int distributionKey;
+ private int redundancy = 1;
+ private int searchableCopies = 1;
private final String clusterName;
private TransactionLogServer tls;
private AbstractService serviceLayerService;
@@ -140,6 +142,12 @@ public class SearchNode extends AbstractService implements
private String getBaseDir() {
return getDefaults().underVespaHome("var/db/vespa/search/cluster." + getClusterName()) + "/n" + distributionKey;
}
+ public void setSearchableCopies(int searchableCopies) {
+ this.searchableCopies = searchableCopies;
+ }
+ public void setRedundancy(int redundancy) {
+ this.redundancy = redundancy;
+ }
public void updatePartition(int partitionId) {
nodeSpec = new NodeSpec(nodeSpec.groupIndex(), partitionId);
@@ -267,7 +275,7 @@ public class SearchNode extends AbstractService implements
}
if (getHostResource() != null && getHostResource().getFlavor().isPresent()) {
Flavor nodeFlavor = getHostResource().getFlavor().get();
- NodeFlavorTuning nodeFlavorTuning = new NodeFlavorTuning(nodeFlavor);
+ NodeFlavorTuning nodeFlavorTuning = new NodeFlavorTuning(nodeFlavor, redundancy, searchableCopies);
nodeFlavorTuning.getConfig(builder);
if (tuning.isPresent()) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/utils/Duration.java b/config-model/src/main/java/com/yahoo/vespa/model/utils/Duration.java
index 0b1e23510fe..c55316186d2 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/utils/Duration.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/utils/Duration.java
@@ -3,7 +3,6 @@ package com.yahoo.vespa.model.utils;
import java.util.HashMap;
import java.util.Map;
-import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/utils/FileSender.java b/config-model/src/main/java/com/yahoo/vespa/model/utils/FileSender.java
index 8995fcbca99..46d41b1e0b0 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/utils/FileSender.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/utils/FileSender.java
@@ -20,8 +20,6 @@ import java.util.*;
*/
public class FileSender implements Serializable {
- public enum FileType {FILE, URI};
-
/**
* Send the given file to all given services.
*
diff --git a/config-model/src/main/resources/schema/common.rnc b/config-model/src/main/resources/schema/common.rnc
index 3e33227b064..c5690f9c915 100644
--- a/config-model/src/main/resources/schema/common.rnc
+++ b/config-model/src/main/resources/schema/common.rnc
@@ -41,7 +41,7 @@ OptionalDedicatedNodes = element nodes {
GenericConfig = element config {
attribute name { text },
- attribute namespace { text }?,
+ attribute namespace { text }?, # TODO: Remove in Vespa 8
attribute version { text }?,
anyElement +
}
diff --git a/config-model/src/test/cfg/admin/userconfigs/functiontest-defaultvalues.xml b/config-model/src/test/cfg/admin/userconfigs/functiontest-defaultvalues.xml
index 7da7d788ca1..298d6676354 100644
--- a/config-model/src/test/cfg/admin/userconfigs/functiontest-defaultvalues.xml
+++ b/config-model/src/test/cfg/admin/userconfigs/functiontest-defaultvalues.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
-<config name="function-test">
+<config name="test.function-test">
<bool_val>false</bool_val>
<int_val>5</int_val>
<long_val>1234567890123</long_val>
diff --git a/config-model/src/test/cfg/admin/userconfigs/whitespace-test.xml b/config-model/src/test/cfg/admin/userconfigs/whitespace-test.xml
index 68576786376..956cba1c9ff 100644
--- a/config-model/src/test/cfg/admin/userconfigs/whitespace-test.xml
+++ b/config-model/src/test/cfg/admin/userconfigs/whitespace-test.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
-<config name="function-test">
+<config name="test.function-test">
<stringVal> This is a string
that contains different kinds of whitespace </stringVal>
</config>
diff --git a/config-model/src/test/cfg/application/app_genericservices/services.xml b/config-model/src/test/cfg/application/app_genericservices/services.xml
index 6c7cad10e5d..9ae7f122e81 100644
--- a/config-model/src/test/cfg/application/app_genericservices/services.xml
+++ b/config-model/src/test/cfg/application/app_genericservices/services.xml
@@ -3,11 +3,11 @@
<services version="1.0">
<service version="1.0" name="myservice" command="mycmd1.sh">
- <config name="myconfig">
+ <config name="a.myconfig">
<mysetting>bar</mysetting>
</config>
<node hostalias="node1">
- <config name="myconfig">
+ <config name="a.myconfig">
<mysetting>baz</mysetting>
</config>
</node>
@@ -17,11 +17,11 @@
</service>
<service version="1.0" name="myotherservice" command="/home/vespa/bin/mycmd2.sh --ytest $FOO_BAR">
- <config name="myconfig">
+ <config name="a.myconfig">
<mysetting>bar2</mysetting>
</config>
<node hostalias="node3">
- <config name="myconfig">
+ <config name="a.myconfig">
<mysetting>baz2</mysetting>
</config>
</node>
diff --git a/config-model/src/test/java/com/yahoo/config/model/ApplicationPackageTester.java b/config-model/src/test/java/com/yahoo/config/model/ApplicationPackageTester.java
index fe64a69b311..ab321ac5835 100644
--- a/config-model/src/test/java/com/yahoo/config/model/ApplicationPackageTester.java
+++ b/config-model/src/test/java/com/yahoo/config/model/ApplicationPackageTester.java
@@ -48,8 +48,4 @@ public class ApplicationPackageTester {
return new ApplicationPackageTester(applicationPackageDir, true);
}
- public static ApplicationPackageTester createWithoutValidation(String applicationPackageDir) {
- return new ApplicationPackageTester(applicationPackageDir, false);
- }
-
}
diff --git a/config-model/src/test/java/com/yahoo/config/model/MapConfigModelRegistryTest.java b/config-model/src/test/java/com/yahoo/config/model/MapConfigModelRegistryTest.java
index 93d71bb3f43..9847ad09198 100644
--- a/config-model/src/test/java/com/yahoo/config/model/MapConfigModelRegistryTest.java
+++ b/config-model/src/test/java/com/yahoo/config/model/MapConfigModelRegistryTest.java
@@ -6,7 +6,6 @@ import com.yahoo.config.model.builder.xml.ConfigModelId;
import org.junit.Test;
import org.w3c.dom.Element;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
diff --git a/config-model/src/test/java/com/yahoo/config/model/QrserverAndGatewayPortAllocationTest.java b/config-model/src/test/java/com/yahoo/config/model/QrserverAndGatewayPortAllocationTest.java
index 8fa2d1e5e76..ac776d3cf78 100644
--- a/config-model/src/test/java/com/yahoo/config/model/QrserverAndGatewayPortAllocationTest.java
+++ b/config-model/src/test/java/com/yahoo/config/model/QrserverAndGatewayPortAllocationTest.java
@@ -6,14 +6,11 @@ import com.yahoo.vespa.model.container.Container;
import com.yahoo.vespa.model.container.ApplicationContainer;
import com.yahoo.vespa.model.test.utils.VespaModelCreatorWithFilePkg;
import org.junit.Test;
-import org.xml.sax.SAXException;
-import java.io.IOException;
import java.util.List;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
/**
* Tests that qrserver is assigned port Defaults.getDefaults().vespaWebServicePort() even if there is a HTTP gateway configured earlier in
@@ -24,7 +21,7 @@ import static org.junit.Assert.assertTrue;
public class QrserverAndGatewayPortAllocationTest {
@Test
- public void testPorts() throws IOException, SAXException {
+ public void testPorts() {
String appDir = "src/test/cfg/application/app_qrserverandgw/";
VespaModelCreatorWithFilePkg creator = new VespaModelCreatorWithFilePkg(appDir);
VespaModel vespaModel = creator.create();
diff --git a/config-model/src/test/java/com/yahoo/config/model/provision/HostsXmlProvisionerTest.java b/config-model/src/test/java/com/yahoo/config/model/provision/HostsXmlProvisionerTest.java
index 19d7def5787..1c7925b935a 100644
--- a/config-model/src/test/java/com/yahoo/config/model/provision/HostsXmlProvisionerTest.java
+++ b/config-model/src/test/java/com/yahoo/config/model/provision/HostsXmlProvisionerTest.java
@@ -127,7 +127,6 @@ public class HostsXmlProvisionerTest {
@Test
public void require_singlenode_HostAlias_is_used_if_hosts_xml() {
- String servicesXml = "<container id='default' version='1.0' />";
HostsXmlProvisioner hostProvisioner = createProvisioner(oneHost);
HostSpec hostSpec = hostProvisioner.allocateHost(Container.SINGLENODE_CONTAINER_SERVICESPEC);
assertThat(hostSpec.hostname(), is("test1.yahoo.com"));
diff --git a/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java b/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java
index dc0312aef8e..c7ee5188cd1 100644
--- a/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java
+++ b/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java
@@ -14,7 +14,7 @@ import com.yahoo.config.provision.Zone;
import com.yahoo.config.provisioning.FlavorsConfig;
import com.yahoo.container.core.ApplicationMetadataConfig;
import com.yahoo.search.config.QrStartConfig;
-import com.yahoo.searchdefinition.parser.ParseException;
+import com.yahoo.vespa.config.search.core.PartitionsConfig;
import com.yahoo.vespa.config.search.core.ProtonConfig;
import com.yahoo.vespa.model.HostResource;
import com.yahoo.vespa.model.HostSystem;
@@ -609,7 +609,7 @@ public class ModelProvisioningTest {
}
@Test
- public void testClusterControllersCanSupplementWithAllContainerClusters() throws ParseException {
+ public void testClusterControllersCanSupplementWithAllContainerClusters() {
String services =
"<?xml version='1.0' encoding='utf-8' ?>\n" +
"<services>" +
@@ -992,6 +992,10 @@ public class ModelProvisioningTest {
" <admin version='3.0'>" +
" <nodes count='3'/>" + // Ignored
" </admin>" +
+ " <container version='1.0' id='container'>" +
+ " <search/>" +
+ " <nodes count='2'/>" +
+ " </container>" +
" <content version='1.0' id='bar'>" +
" <redundancy reply-after='8'>12</redundancy>" +
" <documents>" +
@@ -1003,7 +1007,7 @@ public class ModelProvisioningTest {
" </content>" +
"</services>";
- int numberOfHosts = 4;
+ int numberOfHosts = 6;
VespaModelTester tester = new VespaModelTester();
tester.addHosts(numberOfHosts);
VespaModel model = tester.createModel(services, false);
@@ -1014,6 +1018,7 @@ public class ModelProvisioningTest {
assertEquals(4, cluster.redundancy().effectiveFinalRedundancy());
assertEquals(4, cluster.redundancy().effectiveReadyCopies());
assertEquals(4, cluster.getSearch().getIndexed().getDispatchSpec().getGroups().size());
+ assertEquals(4, cluster.getSearch().getIndexed().getSearchableCopies());
assertFalse(cluster.getRootGroup().getPartitions().isPresent());
assertEquals(4, cluster.getRootGroup().getNodes().size());
assertEquals(0, cluster.getRootGroup().getSubgroups().size());
@@ -1026,10 +1031,14 @@ public class ModelProvisioningTest {
assertThat(cluster.getRootGroup().getNodes().get(2).getConfigId(), is("bar/storage/2"));
assertThat(cluster.getRootGroup().getNodes().get(3).getDistributionKey(), is(3));
assertThat(cluster.getRootGroup().getNodes().get(3).getConfigId(), is("bar/storage/3"));
+ PartitionsConfig.Builder partBuilder = new PartitionsConfig.Builder();
+ cluster.getSearch().getIndexed().getTLDs().get(0).getConfig(partBuilder);
+ PartitionsConfig partCFg = partBuilder.build();
+ assertEquals(4, partCFg.dataset(0).searchablecopies());
}
@Test
- public void testUsingNodesAndGroupCountAttributesAndGettingJustOneNode() throws ParseException {
+ public void testUsingNodesAndGroupCountAttributesAndGettingJustOneNode() {
String services =
"<?xml version='1.0' encoding='utf-8' ?>\n" +
"<services>" +
@@ -1069,7 +1078,7 @@ public class ModelProvisioningTest {
}
@Test(expected = IllegalArgumentException.class)
- public void testRequiringMoreNodesThanAreAvailable() throws ParseException {
+ public void testRequiringMoreNodesThanAreAvailable() {
String services =
"<?xml version='1.0' encoding='utf-8' ?>\n" +
"<services>" +
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/FieldOfTypeDocumentTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/FieldOfTypeDocumentTestCase.java
index 9942b563297..be3bae05c5b 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/FieldOfTypeDocumentTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/FieldOfTypeDocumentTestCase.java
@@ -1,10 +1,13 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.searchdefinition;
-import com.yahoo.document.*;
+import com.yahoo.document.DataType;
+import com.yahoo.document.DocumentType;
+import com.yahoo.document.DocumentTypeManager;
+import com.yahoo.document.DocumentTypeManagerConfigurer;
+import com.yahoo.document.Field;
import com.yahoo.document.config.DocumentmanagerConfig;
import com.yahoo.searchdefinition.derived.Deriver;
-import com.yahoo.searchdefinition.parser.ParseException;
import org.junit.Test;
import java.io.IOException;
import java.util.ArrayList;
@@ -19,7 +22,7 @@ import static org.junit.Assert.assertSame;
public class FieldOfTypeDocumentTestCase extends SearchDefinitionTestCase {
@Test
- public void testDocument() throws IOException, ParseException {
+ public void testDocument() throws IOException {
List<String> sds = new ArrayList<>();
sds.add("src/test/examples/music.sd");
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/IncorrectRankingExpressionFileRefTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/IncorrectRankingExpressionFileRefTestCase.java
index 5b8b05e51da..16c6b070551 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/IncorrectRankingExpressionFileRefTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/IncorrectRankingExpressionFileRefTestCase.java
@@ -10,7 +10,6 @@ import org.junit.Test;
import java.io.IOException;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/IncorrectSummaryTypesTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/IncorrectSummaryTypesTestCase.java
index 627394bb6ea..c145c0e5634 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/IncorrectSummaryTypesTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/IncorrectSummaryTypesTestCase.java
@@ -4,8 +4,6 @@ package com.yahoo.searchdefinition;
import com.yahoo.searchdefinition.parser.ParseException;
import org.junit.Test;
-import java.io.IOException;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
@@ -15,7 +13,7 @@ import static org.junit.Assert.fail;
*/
public class IncorrectSummaryTypesTestCase extends SearchDefinitionTestCase {
@Test
- public void testImportingIncorrect() throws IOException, ParseException {
+ public void testImportingIncorrect() throws ParseException {
try {
SearchBuilder.createFromString(
"search incorrectsummarytypes {\n" +
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/NameFieldCheckTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/NameFieldCheckTestCase.java
index b539c65150d..d2360453976 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/NameFieldCheckTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/NameFieldCheckTestCase.java
@@ -1,11 +1,8 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.searchdefinition;
-import com.yahoo.searchdefinition.parser.ParseException;
import org.junit.Test;
-import java.io.IOException;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -20,7 +17,7 @@ import static org.junit.Assert.fail;
public class NameFieldCheckTestCase extends SearchDefinitionTestCase {
@Test
- public void testNameField() throws IOException, ParseException {
+ public void testNameField() {
try {
SearchBuilder.createFromString(
"search simple {\n" +
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/OutsideTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/OutsideTestCase.java
index ed4760e2432..5ac37bf0a3a 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/OutsideTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/OutsideTestCase.java
@@ -2,7 +2,6 @@
package com.yahoo.searchdefinition;
import com.yahoo.searchdefinition.parser.ParseException;
-import org.junit.Ignore;
import org.junit.Test;
import java.io.IOException;
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/RankingExpressionConstantsTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/RankingExpressionConstantsTestCase.java
index aa4515e4044..51508414205 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/RankingExpressionConstantsTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/RankingExpressionConstantsTestCase.java
@@ -13,7 +13,6 @@ import org.junit.Test;
import java.util.List;
import static org.junit.Assert.*;
-import static org.junit.Assert.assertEquals;
/**
* @author bratseth
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/RankingExpressionInliningTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/RankingExpressionInliningTestCase.java
index 55f0fdd19ac..c0a90b6aaa1 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/RankingExpressionInliningTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/RankingExpressionInliningTestCase.java
@@ -13,7 +13,6 @@ import java.util.Optional;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
/**
* @author bratseth
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/RankingExpressionValidationTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/RankingExpressionValidationTestCase.java
index 57d897498a5..0107331fe68 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/RankingExpressionValidationTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/RankingExpressionValidationTestCase.java
@@ -8,7 +8,6 @@ import ai.vespa.rankingexpression.importer.configmodelview.ImportedMlModels;
import com.yahoo.yolean.Exceptions;
import org.junit.Test;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/SearchDefinitionTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/SearchDefinitionTestCase.java
index 21c7362f793..a6c6939f830 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/SearchDefinitionTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/SearchDefinitionTestCase.java
@@ -5,12 +5,10 @@ import com.yahoo.io.IOUtils;
import java.io.BufferedReader;
import java.io.BufferedWriter;
-import java.io.File;
import java.io.IOException;
import static helpers.CompareConfigTestHelper.assertSerializedConfigEquals;
import static helpers.CompareConfigTestHelper.assertSerializedConfigFileEquals;
-import static org.junit.Assert.assertEquals;
public abstract class SearchDefinitionTestCase {
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/SearchDefinitionsParsingTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/SearchDefinitionsParsingTestCase.java
index a26154fc8da..fda15528eda 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/SearchDefinitionsParsingTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/SearchDefinitionsParsingTestCase.java
@@ -2,14 +2,9 @@
package com.yahoo.searchdefinition;
import java.io.IOException;
-import java.util.logging.Handler;
-import java.util.logging.Level;
-import java.util.logging.LogRecord;
-import java.util.logging.Logger;
import com.yahoo.searchdefinition.parser.ParseException;
-import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.*;
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/StructTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/StructTestCase.java
index 3d98ce46de7..001ad64e2da 100755
--- a/config-model/src/test/java/com/yahoo/searchdefinition/StructTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/StructTestCase.java
@@ -17,7 +17,7 @@ import static org.junit.Assert.fail;
public class StructTestCase extends SearchDefinitionTestCase {
@Test
- public void testStruct() throws IOException, ParseException {
+ public void testStruct() throws IOException {
assertConfigFile("src/test/examples/structresult.cfg",
new DocumentmanagerConfig(Deriver.getDocumentManagerConfig("src/test/examples/struct.sd")).toString() + "\n");
}
@@ -33,7 +33,7 @@ public class StructTestCase extends SearchDefinitionTestCase {
}
@Test
- public void testStructAndDocumentWithSameNames() throws IOException, ParseException {
+ public void testStructAndDocumentWithSameNames() {
try {
DocumenttypesConfig.Builder dt = Deriver.getDocumentTypesConfig("src/test/examples/structanddocumentwithsamenames.sd");
} catch (Exception e) {
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/SummaryTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/SummaryTestCase.java
index 7e6eaa0683a..f94ac1c285c 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/SummaryTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/SummaryTestCase.java
@@ -1,11 +1,9 @@
package com.yahoo.searchdefinition;
-import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.searchdefinition.parser.ParseException;
import com.yahoo.vespa.model.test.utils.DeployLoggerStub;
import org.junit.Test;
-import java.io.IOException;
import java.util.logging.Level;
import static org.junit.Assert.assertEquals;
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/AbstractExportingTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/AbstractExportingTestCase.java
index 7fbca88cb61..78382ccdad6 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/AbstractExportingTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/AbstractExportingTestCase.java
@@ -3,7 +3,6 @@ package com.yahoo.searchdefinition.derived;
import com.yahoo.document.DocumenttypesConfig;
import com.yahoo.document.config.DocumentmanagerConfig;
-import com.yahoo.search.query.profile.QueryProfileRegistry;
import com.yahoo.searchdefinition.Search;
import com.yahoo.searchdefinition.SearchBuilder;
import com.yahoo.searchdefinition.SearchDefinitionTestCase;
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/DeriverTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/DeriverTestCase.java
index 47915580017..cc27a0d6067 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/DeriverTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/DeriverTestCase.java
@@ -4,19 +4,12 @@ package com.yahoo.searchdefinition.derived;
import com.yahoo.document.DataType;
import com.yahoo.document.DocumentTypeManager;
import com.yahoo.document.config.DocumentmanagerConfig;
-import com.yahoo.io.IOUtils;
-import com.yahoo.searchdefinition.SearchBuilder;
import com.yahoo.searchdefinition.SearchDefinitionTestCase;
-import com.yahoo.searchdefinition.document.SDDocumentType;
import org.junit.Test;
-import java.io.File;
-import java.io.IOException;
+
import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Pattern;
+
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
/**
* Tests deriving using the Deriver facade
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/ExportingTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/ExportingTestCase.java
index 4600f6ae4c6..ebd2c752d5e 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/ExportingTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/ExportingTestCase.java
@@ -2,7 +2,6 @@
package com.yahoo.searchdefinition.derived;
import com.yahoo.searchdefinition.parser.ParseException;
-import org.junit.Ignore;
import org.junit.Test;
import java.io.IOException;
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/InheritanceTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/InheritanceTestCase.java
index 8ad6abbbb42..9b363bc5734 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/InheritanceTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/InheritanceTestCase.java
@@ -16,7 +16,6 @@ import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
-import java.util.LinkedList;
import java.util.List;
import org.junit.rules.TemporaryFolder;
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/StreamingStructTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/StreamingStructTestCase.java
index 1be9ee3f465..592fd6c45ed 100755
--- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/StreamingStructTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/StreamingStructTestCase.java
@@ -4,9 +4,7 @@ package com.yahoo.searchdefinition.derived;
import com.yahoo.searchdefinition.parser.ParseException;
import org.junit.Test;
-import java.io.File;
import java.io.IOException;
-import java.util.Arrays;
/**
* Tests VSM configuration deriving for structs
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/SummaryMapTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/SummaryMapTestCase.java
index 17c767f4029..b03db1d7f2e 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/SummaryMapTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/SummaryMapTestCase.java
@@ -17,6 +17,7 @@ import java.io.IOException;
import java.util.Iterator;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* Tests summary map extraction
@@ -27,7 +28,7 @@ public class SummaryMapTestCase extends SearchDefinitionTestCase {
@Test
public void testDeriving() throws IOException, ParseException {
Search search = SearchBuilder.buildFromFile("src/test/examples/simple.sd");
- SummaryMap summaryMap=new SummaryMap(search, new Summaries(search, new BaseDeployLogger()));
+ SummaryMap summaryMap=new SummaryMap(search);
Iterator transforms=summaryMap.resultTransformIterator();
FieldResultTransform transform = (FieldResultTransform)transforms.next();
@@ -66,7 +67,7 @@ public class SummaryMapTestCase extends SearchDefinitionTestCase {
assertEquals("access", transform.getFieldName());
assertEquals(SummaryTransform.ATTRIBUTE,transform.getTransform());
- assertTrue(!transforms.hasNext());
+ assertFalse(transforms.hasNext());
}
@Test
public void testPositionDeriving() {
@@ -77,7 +78,7 @@ public class SummaryMapTestCase extends SearchDefinitionTestCase {
SDField field = document.addField(fieldName, PositionDataType.INSTANCE);
field.parseIndexingScript("{ attribute | summary }");
new Processing().process(search, new BaseDeployLogger(), new RankProfileRegistry(), new QueryProfiles(), true, false);
- SummaryMap summaryMap = new SummaryMap(search, new Summaries(search, new BaseDeployLogger()));
+ SummaryMap summaryMap = new SummaryMap(search);
Iterator transforms = summaryMap.resultTransformIterator();
@@ -106,7 +107,7 @@ public class SummaryMapTestCase extends SearchDefinitionTestCase {
assertEquals("location_zcurve", transform.getFieldName());
assertEquals(SummaryTransform.ATTRIBUTE,transform.getTransform());
- assertTrue(!transforms.hasNext());
+ assertFalse(transforms.hasNext());
SummarymapConfig.Builder scb = new SummarymapConfig.Builder();
summaryMap.getConfig(scb);
@@ -143,7 +144,7 @@ public class SummaryMapTestCase extends SearchDefinitionTestCase {
@Test
public void testFailOnSummaryFieldSourceCollision() {
try {
- Search search = SearchBuilder.buildFromFile("src/test/examples/summaryfieldcollision.sd");
+ SearchBuilder.buildFromFile("src/test/examples/summaryfieldcollision.sd");
} catch (Exception e) {
assertTrue(e.getMessage().matches(".*equally named field.*"));
}
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/TwoStreamingStructsTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/TwoStreamingStructsTestCase.java
index 179aced3540..a70877a4f4f 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/TwoStreamingStructsTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/TwoStreamingStructsTestCase.java
@@ -3,7 +3,6 @@ package com.yahoo.searchdefinition.derived;
import com.yahoo.searchdefinition.SearchBuilder;
import com.yahoo.searchdefinition.parser.ParseException;
-import org.junit.Ignore;
import org.junit.Test;
import java.io.IOException;
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/AdjustPositionSummaryFieldsTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/AdjustPositionSummaryFieldsTestCase.java
index a385ed09809..e230840bcaa 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/AdjustPositionSummaryFieldsTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/AdjustPositionSummaryFieldsTestCase.java
@@ -1,20 +1,9 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.searchdefinition.processing;
-import com.google.common.collect.ImmutableMap;
-import com.yahoo.config.application.api.ApplicationPackage;
-import com.yahoo.config.model.test.MockApplicationPackage;
import com.yahoo.document.DataType;
import com.yahoo.document.PositionDataType;
-import com.yahoo.document.ReferenceDataType;
-import com.yahoo.document.TemporaryStructuredDataType;
-import com.yahoo.searchdefinition.DocumentReference;
-import com.yahoo.searchdefinition.DocumentReferences;
import com.yahoo.searchdefinition.Search;
-import com.yahoo.searchdefinition.document.SDDocumentType;
-import com.yahoo.searchdefinition.document.SDField;
-import com.yahoo.searchdefinition.document.TemporaryImportedField;
-import com.yahoo.searchdefinition.document.TemporarySDField;
import com.yahoo.vespa.documentmodel.DocumentSummary;
import com.yahoo.vespa.documentmodel.SummaryField;
import com.yahoo.vespa.documentmodel.SummaryTransform;
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/ImportedFieldsResolverTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/ImportedFieldsResolverTestCase.java
index 3e3cd932e55..c679bb7e61f 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/ImportedFieldsResolverTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/ImportedFieldsResolverTestCase.java
@@ -1,27 +1,16 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.searchdefinition.processing;
-import com.google.common.collect.ImmutableMap;
-import com.yahoo.config.application.api.ApplicationPackage;
-import com.yahoo.config.model.test.MockApplicationPackage;
import com.yahoo.document.DataType;
-import com.yahoo.document.ReferenceDataType;
-import com.yahoo.document.TemporaryStructuredDataType;
import com.yahoo.document.TensorDataType;
-import com.yahoo.searchdefinition.DocumentReference;
-import com.yahoo.searchdefinition.DocumentReferences;
import com.yahoo.searchdefinition.Search;
import com.yahoo.searchdefinition.document.ImmutableImportedSDField;
import com.yahoo.searchdefinition.document.ImmutableSDField;
import com.yahoo.searchdefinition.document.ImportedField;
import com.yahoo.searchdefinition.document.ImportedFields;
-import com.yahoo.searchdefinition.document.SDDocumentType;
import com.yahoo.searchdefinition.document.SDField;
-import com.yahoo.searchdefinition.document.TemporaryImportedField;
import com.yahoo.searchdefinition.document.TemporarySDField;
import com.yahoo.tensor.TensorType;
-import com.yahoo.vespa.documentmodel.DocumentSummary;
-import com.yahoo.vespa.documentmodel.SummaryField;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingValidationTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingValidationTestCase.java
index ad801ed50ab..3eeac1ee710 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingValidationTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingValidationTestCase.java
@@ -4,7 +4,6 @@ package com.yahoo.searchdefinition.processing;
import com.yahoo.searchdefinition.SearchBuilder;
import com.yahoo.searchdefinition.derived.AbstractExportingTestCase;
import com.yahoo.searchdefinition.parser.ParseException;
-import org.junit.Ignore;
import org.junit.Test;
import java.io.IOException;
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingValuesTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingValuesTestCase.java
index 8d3f1ba0020..a1c454da822 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingValuesTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingValuesTestCase.java
@@ -2,7 +2,6 @@
package com.yahoo.searchdefinition.processing;
import com.yahoo.searchdefinition.parser.ParseException;
-import org.junit.Ignore;
import org.junit.Test;
import java.io.IOException;
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/PositionTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/PositionTestCase.java
index 9cf555e2c9a..e5fadea6dd8 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/PositionTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/PositionTestCase.java
@@ -7,14 +7,11 @@ import com.yahoo.searchdefinition.Search;
import com.yahoo.searchdefinition.SearchBuilder;
import com.yahoo.searchdefinition.document.Attribute;
import com.yahoo.searchdefinition.document.FieldSet;
-import com.yahoo.searchdefinition.parser.ParseException;
import com.yahoo.vespa.documentmodel.SummaryField;
import com.yahoo.vespa.documentmodel.SummaryTransform;
-import org.junit.Ignore;
import org.junit.Test;
-import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionWithTensorFlowTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionWithTensorFlowTestCase.java
index cba931e81f0..38515c36690 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionWithTensorFlowTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionWithTensorFlowTestCase.java
@@ -319,7 +319,7 @@ public class RankingExpressionWithTensorFlowTestCase {
@Test
public void testFunctionGeneration() {
final String name = "mnist_saved";
- final String expression = "join(join(reduce(join(join(join(imported_ml_function_" + name + "_dnn_hidden2_add, reduce(constant(" + name + "_dnn_hidden2_Const), sum, d2), f(a,b)(a * b)), imported_ml_function_" + name + "_dnn_hidden2_add, f(a,b)(max(a,b))), constant(" + name + "_dnn_outputs_weights_read), f(a,b)(a * b)), sum, d2), constant(" + name + "_dnn_outputs_bias_read), f(a,b)(a + b)), tensor(d0[1])(1.0), f(a,b)(a * b))";
+ final String expression = "join(join(reduce(join(join(join(imported_ml_function_" + name + "_dnn_hidden2_add, reduce(rename(constant(" + name + "_dnn_hidden2_Const), d0, d2), sum, d2), f(a,b)(a * b)), imported_ml_function_" + name + "_dnn_hidden2_add, f(a,b)(max(a,b))), constant(" + name + "_dnn_outputs_weights_read), f(a,b)(a * b)), sum, d2), constant(" + name + "_dnn_outputs_bias_read), f(a,b)(a + b)), tensor(d0[1])(1.0), f(a,b)(a * b))";
final String functionExpression1 = "join(reduce(join(reduce(rename(input, (d0, d1), (d0, d4)), sum, d0), constant(" + name + "_dnn_hidden1_weights_read), f(a,b)(a * b)), sum, d4), constant(" + name + "_dnn_hidden1_bias_read), f(a,b)(a + b))";
final String functionExpression2 = "join(reduce(join(join(join(imported_ml_function_" + name + "_dnn_hidden1_add, 0.009999999776482582, f(a,b)(a * b)), imported_ml_function_" + name + "_dnn_hidden1_add, f(a,b)(max(a,b))), constant(" + name + "_dnn_hidden2_weights_read), f(a,b)(a * b)), sum, d3), constant(" + name + "_dnn_hidden2_bias_read), f(a,b)(a + b))";
@@ -349,7 +349,7 @@ public class RankingExpressionWithTensorFlowTestCase {
" rank-profile my_profile_child inherits my_profile {\n" +
" }";
- final String expression = "join(join(reduce(join(join(join(imported_ml_function_" + name + "_dnn_hidden2_add, reduce(constant(" + name + "_dnn_hidden2_Const), sum, d2), f(a,b)(a * b)), imported_ml_function_" + name + "_dnn_hidden2_add, f(a,b)(max(a,b))), constant(" + name + "_dnn_outputs_weights_read), f(a,b)(a * b)), sum, d2), constant(" + name + "_dnn_outputs_bias_read), f(a,b)(a + b)), tensor(d0[1])(1.0), f(a,b)(a * b))";
+ final String expression = "join(join(reduce(join(join(join(imported_ml_function_" + name + "_dnn_hidden2_add, reduce(rename(constant(" + name + "_dnn_hidden2_Const), d0, d2), sum, d2), f(a,b)(a * b)), imported_ml_function_" + name + "_dnn_hidden2_add, f(a,b)(max(a,b))), constant(" + name + "_dnn_outputs_weights_read), f(a,b)(a * b)), sum, d2), constant(" + name + "_dnn_outputs_bias_read), f(a,b)(a + b)), tensor(d0[1])(1.0), f(a,b)(a * b))";
final String functionExpression1 = "join(reduce(join(reduce(rename(input, (d0, d1), (d0, d4)), sum, d0), constant(" + name + "_dnn_hidden1_weights_read), f(a,b)(a * b)), sum, d4), constant(" + name + "_dnn_hidden1_bias_read), f(a,b)(a + b))";
final String functionExpression2 = "join(reduce(join(join(join(imported_ml_function_" + name + "_dnn_hidden1_add, 0.009999999776482582, f(a,b)(a * b)), imported_ml_function_" + name + "_dnn_hidden1_add, f(a,b)(max(a,b))), constant(" + name + "_dnn_hidden2_weights_read), f(a,b)(a * b)), sum, d3), constant(" + name + "_dnn_hidden2_bias_read), f(a,b)(a + b))";
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/ReservedRankingExpressionFunctionNamesTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/ReservedRankingExpressionFunctionNamesTestCase.java
index b39c48b67bf..eecab3c03d7 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/ReservedRankingExpressionFunctionNamesTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/ReservedRankingExpressionFunctionNamesTestCase.java
@@ -3,7 +3,6 @@ package com.yahoo.searchdefinition.processing;
import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.searchdefinition.RankProfileRegistry;
-import com.yahoo.searchdefinition.Search;
import com.yahoo.searchdefinition.SearchBuilder;
import com.yahoo.searchdefinition.parser.ParseException;
import org.junit.Test;
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/TensorTransformTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/TensorTransformTestCase.java
index 15c1d24ce33..f90320ad686 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/TensorTransformTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/TensorTransformTestCase.java
@@ -23,7 +23,6 @@ import org.junit.Test;
import java.util.List;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class TensorTransformTestCase extends SearchDefinitionTestCase {
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/WeightedSetSummaryToTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/WeightedSetSummaryToTestCase.java
index bee353692b8..ef6bc57223d 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/WeightedSetSummaryToTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/WeightedSetSummaryToTestCase.java
@@ -5,12 +5,10 @@ import com.yahoo.searchdefinition.Search;
import com.yahoo.searchdefinition.SearchBuilder;
import com.yahoo.searchdefinition.SearchDefinitionTestCase;
import com.yahoo.searchdefinition.parser.ParseException;
-import com.yahoo.vespa.documentmodel.DocumentSummary;
import org.junit.Test;
import java.io.IOException;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/** @author bratseth */
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/HostPortsTest.java b/config-model/src/test/java/com/yahoo/vespa/model/HostPortsTest.java
index e2834291c0d..1ded447993c 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/HostPortsTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/HostPortsTest.java
@@ -11,12 +11,8 @@ import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
/**
* @author arnej
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/HostResourceTest.java b/config-model/src/test/java/com/yahoo/vespa/model/HostResourceTest.java
index 3e9bb6d0615..02b77bdb375 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/HostResourceTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/HostResourceTest.java
@@ -14,7 +14,6 @@ import static com.yahoo.config.provision.ClusterSpec.Type.admin;
import static com.yahoo.config.provision.ClusterSpec.Type.container;
import static com.yahoo.config.provision.ClusterSpec.Type.content;
import static org.hamcrest.Matchers.endsWith;
-import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/InstanceResolverTest.java b/config-model/src/test/java/com/yahoo/vespa/model/InstanceResolverTest.java
index 0f58c5e3a2a..c96e28035e0 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/InstanceResolverTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/InstanceResolverTest.java
@@ -56,7 +56,6 @@ public class InstanceResolverTest {
/**
* Values unset on builder, trying to set them from def file, but type mismatches there
- * @throws Exception
*/
@Test
public void testApplyDefToBuilderMismatches() throws Exception {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/admin/ClusterControllerTestCase.java b/config-model/src/test/java/com/yahoo/vespa/model/admin/ClusterControllerTestCase.java
index 1c640a6e4d7..c88d91a3ede 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/admin/ClusterControllerTestCase.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/admin/ClusterControllerTestCase.java
@@ -95,7 +95,7 @@ public class ClusterControllerTestCase extends DomBuilderTest {
@Test(expected = IllegalArgumentException.class)
- public void testSeparateHostsRequired() throws Exception {
+ public void testSeparateHostsRequired() {
String xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" +
"<services>\n" +
"\n" +
@@ -122,7 +122,7 @@ public class ClusterControllerTestCase extends DomBuilderTest {
}
@Test(expected = IllegalArgumentException.class)
- public void testSeparateHostsFromConfigServerRequired() throws Exception {
+ public void testSeparateHostsFromConfigServerRequired() {
String xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" +
"<services>\n" +
"\n" +
@@ -150,7 +150,7 @@ public class ClusterControllerTestCase extends DomBuilderTest {
}
@Test
- public void testStandaloneZooKeeper() throws Exception {
+ public void testStandaloneZooKeeper() {
String xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" +
"<services>\n" +
"\n" +
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/admin/DedicatedAdminV4Test.java b/config-model/src/test/java/com/yahoo/vespa/model/admin/DedicatedAdminV4Test.java
index ac76783c2af..be7fc19a429 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/admin/DedicatedAdminV4Test.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/admin/DedicatedAdminV4Test.java
@@ -5,7 +5,6 @@ import com.yahoo.cloud.config.LogforwarderConfig;
import com.yahoo.cloud.config.SentinelConfig;
import com.yahoo.config.application.api.ApplicationPackage;
import com.yahoo.config.model.NullConfigModelRegistry;
-import com.yahoo.config.model.api.container.ContainerServiceType;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.deploy.TestProperties;
import com.yahoo.config.model.provision.Hosts;
@@ -27,7 +26,6 @@ import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
-import static com.yahoo.config.model.api.container.ContainerServiceType.CONTAINER;
import static com.yahoo.config.model.api.container.ContainerServiceType.LOGSERVER_CONTAINER;
import static com.yahoo.config.model.api.container.ContainerServiceType.METRICS_PROXY_CONTAINER;
import static com.yahoo.config.model.api.container.ContainerServiceType.QRSERVER;
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyModelTester.java b/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyModelTester.java
index ad6a7de935b..5fe62e6bd1b 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyModelTester.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyModelTester.java
@@ -9,8 +9,6 @@ import ai.vespa.metricsproxy.metric.dimensions.ApplicationDimensionsConfig;
import ai.vespa.metricsproxy.metric.dimensions.NodeDimensionsConfig;
import ai.vespa.metricsproxy.rpc.RpcConnectorConfig;
import ai.vespa.metricsproxy.service.VespaServicesConfig;
-import com.yahoo.config.provision.Flavor;
-import com.yahoo.config.provisioning.FlavorsConfig;
import com.yahoo.search.config.QrStartConfig;
import com.yahoo.vespa.model.VespaModel;
import com.yahoo.vespa.model.admin.monitoring.Metric;
@@ -20,7 +18,6 @@ import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyModelTester.T
import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyModelTester.TestMode.self_hosted;
import static com.yahoo.vespa.model.admin.monitoring.DefaultPublicConsumer.DEFAULT_PUBLIC_CONSUMER_ID;
import static com.yahoo.vespa.model.admin.monitoring.VespaMetricsConsumer.VESPA_CONSUMER_ID;
-import static org.junit.Assert.assertEquals;
/**
* @author gjoranv
@@ -96,9 +93,4 @@ class MetricsProxyModelTester {
return new RpcConnectorConfig((RpcConnectorConfig.Builder) model.getConfig(new RpcConnectorConfig.Builder(), CONTAINER_CONFIG_ID));
}
- private static Flavor flavorFromString(String name) {
- return new Flavor(new FlavorsConfig.Flavor(new FlavorsConfig.Flavor.Builder().
- name(name)));
- }
-
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidatorTestCase.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidatorTestCase.java
index a0c05193661..3ba3745f46e 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidatorTestCase.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidatorTestCase.java
@@ -49,36 +49,6 @@ public class ComplexAttributeFieldsValidatorTestCase {
}
@Test
- public void throws_when_attribute_is_set_on_a_field_with_struct_sub_type() throws IOException, SAXException {
- exceptionRule.expect(IllegalArgumentException.class);
- exceptionRule.expectMessage("For field 'struct_array.f2': Setting attribute on a field that has struct or map sub-type(s) is not supported");
- createModelAndValidate(joinLines(createSearchDefintionWithInvalidStructFieldAttribute("array<s1>")));
- }
-
- @Test
- public void throws_when_attribute_is_set_on_a_field_with_map_sub_type() throws IOException, SAXException {
- exceptionRule.expect(IllegalArgumentException.class);
- exceptionRule.expectMessage("For field 'struct_array.f2': Setting attribute on a field that has struct or map sub-type(s) is not supported");
- createModelAndValidate(joinLines(createSearchDefintionWithInvalidStructFieldAttribute("map<string, int>")));
- }
-
- private String createSearchDefintionWithInvalidStructFieldAttribute(String invalidFieldType) {
- return joinLines("search test {",
- " document test {",
- " struct s1 {",
- " field f1 type int {}",
- " }",
- " struct s2 {",
- " field f2 type " + invalidFieldType + " {}",
- " }",
- " field struct_array type array<s2> {",
- " struct-field f2 { indexing: attribute }",
- " }",
- " }",
- "}");
- }
-
- @Test
public void validation_passes_when_only_supported_struct_field_attributes_are_used() throws IOException, SAXException {
createModelAndValidate(joinLines("search test {",
" document test {",
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ClusterSizeReductionValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ClusterSizeReductionValidatorTest.java
index bce28dd9236..895aa4f6a36 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ClusterSizeReductionValidatorTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ClusterSizeReductionValidatorTest.java
@@ -3,22 +3,13 @@ package com.yahoo.vespa.model.application.validation.change;
import com.yahoo.config.application.api.ValidationId;
import com.yahoo.config.application.api.ValidationOverrides;
-import com.yahoo.config.model.api.ConfigChangeAction;
-import com.yahoo.config.model.api.ConfigChangeRefeedAction;
import com.yahoo.config.provision.Environment;
import com.yahoo.vespa.model.VespaModel;
import com.yahoo.vespa.model.application.validation.ValidationTester;
-import com.yahoo.vespa.model.search.AbstractSearchCluster;
import com.yahoo.yolean.Exceptions;
import org.junit.Test;
-import org.xml.sax.SAXException;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
@@ -27,7 +18,7 @@ import static org.junit.Assert.fail;
public class ClusterSizeReductionValidatorTest {
@Test
- public void testSizeReductionValidation() throws IOException, SAXException {
+ public void testSizeReductionValidation() {
ValidationTester tester = new ValidationTester(30);
VespaModel previous = tester.deploy(null, getServices(30), Environment.prod, null).getFirst();
@@ -43,7 +34,7 @@ public class ClusterSizeReductionValidatorTest {
}
@Test
- public void testSizeReductionValidationMinimalDecreaseIsAllowed() throws IOException, SAXException {
+ public void testSizeReductionValidationMinimalDecreaseIsAllowed() {
ValidationTester tester = new ValidationTester(30);
VespaModel previous = tester.deploy(null, getServices(3), Environment.prod, null).getFirst();
@@ -61,7 +52,7 @@ public class ClusterSizeReductionValidatorTest {
*/
@Test
- public void testOverridingSizereductionValidation() throws IOException, SAXException {
+ public void testOverridingSizereductionValidation() {
ValidationTester tester = new ValidationTester(30);
VespaModel previous = tester.deploy(null, getServices(30), Environment.prod, null).getFirst();
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ConfigValueChangeValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ConfigValueChangeValidatorTest.java
index eeeb5344e0b..9318130bb4f 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ConfigValueChangeValidatorTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ConfigValueChangeValidatorTest.java
@@ -22,7 +22,6 @@ import org.junit.Test;
import java.time.Instant;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/GlobalDocumentChangeValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/GlobalDocumentChangeValidatorTest.java
index 68b21222fc5..465b1a5d3ba 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/GlobalDocumentChangeValidatorTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/GlobalDocumentChangeValidatorTest.java
@@ -1,17 +1,10 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.application.validation.change;
-import com.yahoo.config.model.api.ConfigChangeAction;
-import com.yahoo.config.model.api.ConfigChangeRefeedAction;
import com.yahoo.config.provision.Environment;
import com.yahoo.vespa.model.VespaModel;
import com.yahoo.vespa.model.application.validation.ValidationTester;
import org.junit.Test;
-import org.xml.sax.SAXException;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -23,7 +16,7 @@ import static org.junit.Assert.assertFalse;
public class GlobalDocumentChangeValidatorTest {
@Test
- public void testChangGlobalAttribute() throws IOException, SAXException {
+ public void testChangGlobalAttribute() {
testChangeGlobalAttribute(true, false, false, null);
testChangeGlobalAttribute(true, true, true, null);
testChangeGlobalAttribute(false, false, true, null);
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/IndexingModeChangeValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/IndexingModeChangeValidatorTest.java
index cca112f3bd2..ab56178bee3 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/IndexingModeChangeValidatorTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/IndexingModeChangeValidatorTest.java
@@ -8,9 +8,7 @@ import com.yahoo.vespa.model.VespaModel;
import com.yahoo.vespa.model.application.validation.ValidationTester;
import com.yahoo.vespa.model.search.AbstractSearchCluster;
import org.junit.Test;
-import org.xml.sax.SAXException;
-import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
@@ -23,7 +21,7 @@ import static org.junit.Assert.assertTrue;
public class IndexingModeChangeValidatorTest {
@Test
- public void testChangingIndexMode() throws IOException, SAXException {
+ public void testChangingIndexMode() {
ValidationTester tester = new ValidationTester();
VespaModel oldModel =
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/AttributeChangeValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/AttributeChangeValidatorTest.java
index c8fe3c46c9a..038fe2c8675 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/AttributeChangeValidatorTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/AttributeChangeValidatorTest.java
@@ -10,7 +10,6 @@ import java.util.List;
import static com.yahoo.vespa.model.application.validation.change.ConfigChangeTestUtils.newRefeedAction;
import static com.yahoo.vespa.model.application.validation.change.ConfigChangeTestUtils.newRestartAction;
-import static org.junit.Assert.assertEquals;
public class AttributeChangeValidatorTest {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentTypeChangeValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentTypeChangeValidatorTest.java
index aeddd05209f..4064e53dfb7 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentTypeChangeValidatorTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentTypeChangeValidatorTest.java
@@ -180,7 +180,7 @@ public class DocumentTypeChangeValidatorTest {
}
@Test
- public void requireThatChangingTargetTypeOfReferenceFieldIsNotOK() throws Exception {
+ public void requireThatChangingTargetTypeOfReferenceFieldIsNotOK() {
DocumentTypeChangeValidator validator = new DocumentTypeChangeValidator(
createDocumentTypeWithReferenceField("oldDoc"),
createDocumentTypeWithReferenceField("newDoc"));
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/builder/UserConfigBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/builder/UserConfigBuilderTest.java
index d1ef1010b73..6bfd55ca5de 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/builder/UserConfigBuilderTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/builder/UserConfigBuilderTest.java
@@ -8,7 +8,6 @@ import com.yahoo.config.model.deploy.ConfigDefinitionStore;
import com.yahoo.test.SimpletypesConfig;
import com.yahoo.config.model.producer.UserConfigRepo;
import com.yahoo.config.model.builder.xml.XmlHelper;
-import com.yahoo.vespa.config.ConfigDefinition;
import com.yahoo.vespa.config.ConfigDefinitionKey;
import com.yahoo.vespa.config.ConfigPayload;
import com.yahoo.vespa.config.ConfigPayloadBuilder;
@@ -17,10 +16,7 @@ import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import javax.xml.parsers.ParserConfigurationException;
-import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.Optional;
@@ -30,47 +26,42 @@ import static org.junit.Assert.*;
/**
* @author Ulf Lilleengen
- * @since 5.1
*/
public class UserConfigBuilderTest {
- private final ConfigDefinitionStore configDefinitionStore = new ConfigDefinitionStore() {
- @Override
- public Optional<ConfigDefinition> getConfigDefinition(ConfigDefinitionKey defKey) { return Optional.empty(); }
- };
+ private final ConfigDefinitionStore configDefinitionStore = defKey -> Optional.empty();
@Test
- public void require_that_simple_config_is_resolved() throws ParserConfigurationException, IOException, SAXException {
- Element configRoot = getDocument("<config name=\"simpletypes\">" +
+ public void require_that_simple_config_is_resolved() {
+ Element configRoot = getDocument("<config name=\"test.simpletypes\">" +
" <intval>13</intval>" +
"</config>" +
- "<config name=\"simpletypes\" version=\"1\">" +
+ "<config name=\"test.simpletypes\" version=\"1\">" +
" <stringval>foolio</stringval>" +
"</config>");
UserConfigRepo map = UserConfigBuilder.build(configRoot, configDefinitionStore, new BaseDeployLogger());
assertFalse(map.isEmpty());
- ConfigDefinitionKey key = new ConfigDefinitionKey("simpletypes", "config");
+ ConfigDefinitionKey key = new ConfigDefinitionKey("simpletypes", "test");
assertNotNull(map.get(key));
SimpletypesConfig config = createConfig(SimpletypesConfig.class, map.get(key));
assertThat(config.intval(), is(13));
assertThat(config.stringval(), is("foolio"));
}
- public static <ConfigType extends ConfigInstance> ConfigType createConfig(Class<ConfigType> clazz, ConfigPayloadBuilder builder) {
+ private static <ConfigType extends ConfigInstance> ConfigType createConfig(Class<ConfigType> clazz, ConfigPayloadBuilder builder) {
return ConfigPayload.fromBuilder(builder).toInstance(clazz, "");
}
-
@Test
- public void require_that_arrays_config_is_resolved() throws ParserConfigurationException, IOException, SAXException {
- Element configRoot = getDocument("<config name=\"arraytypes\">" +
+ public void require_that_arrays_config_is_resolved() {
+ Element configRoot = getDocument("<config name=\"test.arraytypes\">" +
" <intarr operation=\"append\">13</intarr>" +
" <intarr operation=\"append\">10</intarr>" +
" <intarr operation=\"append\">1337</intarr>" +
"</config>");
UserConfigRepo map = UserConfigBuilder.build(configRoot, configDefinitionStore, new BaseDeployLogger());
assertFalse(map.isEmpty());
- ConfigDefinitionKey key = new ConfigDefinitionKey("arraytypes", "config");
+ ConfigDefinitionKey key = new ConfigDefinitionKey("arraytypes", "test");
assertNotNull(map.get(key));
ArraytypesConfig config = createConfig(ArraytypesConfig.class, map.get(key));
assertThat(config.intarr().size(), is(3));
@@ -80,7 +71,7 @@ public class UserConfigBuilderTest {
}
@Test
- public void require_that_arrays_of_structs_are_resolved() throws ParserConfigurationException, IOException, SAXException {
+ public void require_that_arrays_of_structs_are_resolved() {
Element configRoot = getDocument(
" <config name='vespa.configdefinition.specialtokens'>" +
" <tokenlist operation='append'>" +
@@ -107,16 +98,16 @@ public class UserConfigBuilderTest {
}
@Test
- public void no_exception_when_config_class_does_not_exist() throws ParserConfigurationException, IOException, SAXException {
- Element configRoot = getDocument("<config name=\"unknown\">" +
+ public void no_exception_when_config_class_does_not_exist() {
+ Element configRoot = getDocument("<config name=\"is.unknown\">" +
" <foo>1</foo>" +
"</config>");
UserConfigRepo repo = UserConfigBuilder.build(configRoot, configDefinitionStore, new BaseDeployLogger());
- ConfigPayloadBuilder builder = repo.get(new ConfigDefinitionKey("unknown", "config"));
+ ConfigPayloadBuilder builder = repo.get(new ConfigDefinitionKey("unknown", "is"));
assertNotNull(builder);
}
- private Element getDocument(String xml) throws ParserConfigurationException {
+ private Element getDocument(String xml) {
Reader xmlReader = new StringReader("<model>" + xml + "</model>");
Document doc;
try {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/Bug6068056Test.java b/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/Bug6068056Test.java
index b90c3173bec..6bc161a0212 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/Bug6068056Test.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/Bug6068056Test.java
@@ -44,7 +44,7 @@ public class Bug6068056Test {
"</services>";
@Test(expected = RuntimeException.class)
- public void testContainerClusterCalledDocproc() throws Exception {
+ public void testContainerClusterCalledDocproc() {
VespaModelCreatorWithMockPkg creator = new VespaModelCreatorWithMockPkg(HOSTS, SERVICES);
creator.create();
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/ContentBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/ContentBuilderTest.java
index c0dd894695e..567e54ed4c4 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/ContentBuilderTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/ContentBuilderTest.java
@@ -4,7 +4,6 @@ package com.yahoo.vespa.model.builder.xml.dom;
import com.yahoo.collections.CollectionUtil;
import com.yahoo.config.ConfigInstance;
import com.yahoo.config.application.api.ApplicationPackage;
-import com.yahoo.config.model.api.container.ContainerServiceType;
import com.yahoo.config.model.builder.xml.test.DomBuilderTest;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.deploy.TestProperties;
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/DomConfigPayloadBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/DomConfigPayloadBuilderTest.java
index 765d825bc2b..a62255c4c5c 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/DomConfigPayloadBuilderTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/DomConfigPayloadBuilderTest.java
@@ -16,9 +16,12 @@ import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
-import javax.xml.parsers.ParserConfigurationException;
-import java.io.*;
-import java.util.ArrayList;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.Reader;
+import java.io.StringReader;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
@@ -33,7 +36,7 @@ import static org.junit.Assert.fail;
public class DomConfigPayloadBuilderTest {
@Test
- public void testFunctionTest_DefaultValues() throws FileNotFoundException, ParserConfigurationException {
+ public void testFunctionTest_DefaultValues() throws FileNotFoundException {
Element configRoot = getDocument(new FileReader(new File("src/test/cfg/admin/userconfigs/functiontest-defaultvalues.xml")));
ConfigPayload config = ConfigPayload.fromBuilder(new DomConfigPayloadBuilder(null).build(configRoot));
String expected = ""
@@ -77,9 +80,9 @@ public class DomConfigPayloadBuilderTest {
}
@Test
- public void put_to_leaf_map() throws Exception {
+ public void put_to_leaf_map() {
Reader xmlConfig = new StringReader("<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
- "<config name=\"foobar\">" +
+ "<config name=\"test.foobar\">" +
" <intmap>" +
" <item key=\"bar\">1338</item>" +
" <item key=\"foo\">1337</item>" +
@@ -90,9 +93,9 @@ public class DomConfigPayloadBuilderTest {
}
@Test
- public void put_to_inner_map() throws Exception {
+ public void put_to_inner_map() {
Reader xmlConfig = new StringReader("<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
- "<config name=\"foobar\">" +
+ "<config name=\"test.foobar\">" +
" <innermap>" +
" <item key=\"bar\">" +
" <foo>baz</foo>" +
@@ -107,9 +110,9 @@ public class DomConfigPayloadBuilderTest {
}
@Test
- public void put_to_nested_map() throws Exception {
+ public void put_to_nested_map() {
Reader xmlConfig = new StringReader("<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
- "<config name=\"foobar\">" +
+ "<config name=\"test.foobar\">" +
" <nestedmap>" +
" <item key=\"bar\">" +
" <inner>" +
@@ -132,10 +135,10 @@ public class DomConfigPayloadBuilderTest {
}
@Test
- public void append_to_leaf_array() throws Exception {
+ public void append_to_leaf_array() {
// Simulate user config from vespa-services.xml
Reader xmlConfig = new StringReader("<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
- "<config name=\"function-test\">" +
+ "<config name=\"a.function-test\">" +
" <intarr operation=\"append\">1</intarr>" +
" <intarr operation=\"append\">2</intarr>" +
"</config> ");
@@ -144,9 +147,9 @@ public class DomConfigPayloadBuilderTest {
}
@Test
- public void camel_case_via_dashes() throws Exception {
+ public void camel_case_via_dashes() {
Reader xmlConfig = new StringReader("<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
- "<config name=\"function-test\">" +
+ "<config name=\"test.function-test\">" +
" <some-struct> <any-value>17</any-value> </some-struct>" +
"</config> ");
ConfigPayload userConfig = ConfigPayload.fromBuilder(new DomConfigPayloadBuilder(null).build(getDocument(xmlConfig)));
@@ -155,7 +158,7 @@ public class DomConfigPayloadBuilderTest {
// Verifies that an exception is thrown when the root element is not 'config'.
@Test
- public void testFailWrongTagName() throws FileNotFoundException, ParserConfigurationException {
+ public void testFailWrongTagName() {
Element configRoot = getDocument(new StringReader("<configs name=\"foo\"/>"));
try {
new DomConfigPayloadBuilder(null).build(configRoot);
@@ -168,7 +171,7 @@ public class DomConfigPayloadBuilderTest {
// Verifies that an exception is thrown when the root element is not 'config'.
@Test
- public void testFailNoNameAttribute() throws FileNotFoundException, ParserConfigurationException {
+ public void testFailNoNameAttribute() {
Element configRoot = getDocument(new StringReader("<config/>"));
try {
new DomConfigPayloadBuilder(null).build(configRoot);
@@ -180,53 +183,17 @@ public class DomConfigPayloadBuilderTest {
}
@Test
- public void testNamespace() throws FileNotFoundException, ParserConfigurationException {
- Element configRoot = getDocument(new StringReader("<config name=\"function-test\" namespace=\"config\">" +
- "<int_val>1</int_val> +" +
- "</config>"));
- ConfigPayload config = ConfigPayload.fromBuilder(new DomConfigPayloadBuilder(null).build(configRoot));
- assertPayload("{\"int_val\":\"1\"}", config);
-
- configRoot = getDocument(new StringReader("<config name=\"config.function-test\">" +
- "<int_val>1</int_val> +" +
- "</config>"));
- config = ConfigPayload.fromBuilder(new DomConfigPayloadBuilder(null).build(configRoot));
- assertPayload("{\"int_val\":\"1\"}", config);
-
- configRoot = getDocument(new StringReader("<config name=\"config.function_test\">" +
- "<int_val>1</int_val> +" +
- "</config>"));
- config = ConfigPayload.fromBuilder(new DomConfigPayloadBuilder(null).build(configRoot));
- assertPayload("{\"int_val\":\"1\"}", config);
- }
-
- @Test
- public void testNameParsing() throws FileNotFoundException, ParserConfigurationException {
- Element configRoot = getDocument(new StringReader("<config name=\"function-test\" version=\"1\" namespace=\"config\">" +
+ public void testNameParsing() {
+ Element configRoot = getDocument(new StringReader("<config name=\"test.function-test\" version=\"1\">" +
"<int_val>1</int_val> +" +
"</config>"));
ConfigDefinitionKey key = DomConfigPayloadBuilder.parseConfigName(configRoot);
assertThat(key.getName(), is("function-test"));
- assertThat(key.getNamespace(), is("config"));
-
- configRoot = getDocument(new StringReader("<config name=\"function_test\" version=\"1\">" +
- "<int_val>1</int_val> +" +
- "</config>"));
- key = DomConfigPayloadBuilder.parseConfigName(configRoot);
- assertThat(key.getName(), is("function_test"));
- assertThat(key.getNamespace(), is("config"));
-
- // Both namespace and name in name attribute
- configRoot = getDocument(new StringReader("<config name=\"config.function-test\" version=\"1\">" +
- "<int_val>1</int_val> +" +
- "</config>"));
- key = DomConfigPayloadBuilder.parseConfigName(configRoot);
- assertThat(key.getName(), is("function-test"));
- assertThat(key.getNamespace(), is("config"));
+ assertThat(key.getNamespace(), is("test"));
}
@Test(expected = ConfigurationRuntimeException.class)
- public void testNameParsingInvalidName() throws FileNotFoundException, ParserConfigurationException {
+ public void testNameParsingInvalidName() {
Element configRoot = getDocument(new StringReader("<config name=\" function-test\" version=\"1\">" +
"<int_val>1</int_val> +" +
"</config>"));
@@ -234,17 +201,17 @@ public class DomConfigPayloadBuilderTest {
}
@Test(expected = ConfigurationRuntimeException.class)
- public void testNameParsingInvalidNamespace() throws FileNotFoundException, ParserConfigurationException {
- Element configRoot = getDocument(new StringReader("<config name=\"function-test\" namespace=\"_foo\" version=\"1\">" +
+ public void testNameParsingInvalidNamespace() {
+ Element configRoot = getDocument(new StringReader("<config name=\"_foo.function-test\" version=\"1\">" +
"<int_val>1</int_val> +" +
"</config>"));
DomConfigPayloadBuilder.parseConfigName(configRoot);
}
@Test
- public void require_that_item_syntax_works_with_leaf() throws ParserConfigurationException {
+ public void require_that_item_syntax_works_with_leaf() {
Element configRoot = getDocument(
- "<config name=\"arraytypes\" version=\"1\">" +
+ "<config name=\"test.arraytypes\" version=\"1\">" +
" <intarr>" +
" <item>13</item>" +
" <item>10</item>" +
@@ -257,9 +224,9 @@ public class DomConfigPayloadBuilderTest {
}
@Test
- public void require_that_item_syntax_works_with_struct() throws ParserConfigurationException {
+ public void require_that_item_syntax_works_with_struct() {
Element configRoot = getDocument(
- "<config name=\"arraytypes\" version=\"1\">" +
+ "<config name=\"test.arraytypes\" version=\"1\">" +
" <lolarray>" +
" <item><foo>hei</foo><bar>hei2</bar></item>" +
" <item><foo>hoo</foo><bar>hoo2</bar></item>" +
@@ -273,9 +240,9 @@ public class DomConfigPayloadBuilderTest {
}
@Test
- public void require_that_item_syntax_works_with_struct_array() throws ParserConfigurationException {
+ public void require_that_item_syntax_works_with_struct_array() {
Element configRoot = getDocument(
- "<config name=\"arraytypes\" version=\"1\">" +
+ "<config name=\"test.arraytypes\" version=\"1\">" +
" <lolarray>" +
" <item><fooarray><item>13</item></fooarray></item>" +
" <item><fooarray><item>10</item></fooarray></item>" +
@@ -288,18 +255,18 @@ public class DomConfigPayloadBuilderTest {
}
@Test(expected = ConfigurationRuntimeException.class)
- public void require_that_item_is_reserved_in_root() throws ParserConfigurationException {
+ public void require_that_item_is_reserved_in_root() {
Element configRoot = getDocument(
- "<config name=\"arraytypes\" version=\"1\">" +
+ "<config name=\"test.arraytypes\" version=\"1\">" +
" <item>13</item>" +
"</config>");
new DomConfigPayloadBuilder(null).build(configRoot);
}
@Test(expected=ConfigurationRuntimeException.class)
- public void require_that_exceptions_are_issued() throws ParserConfigurationException, FileNotFoundException {
+ public void require_that_exceptions_are_issued() throws FileNotFoundException {
Element configRoot = getDocument(
- "<config name=\"simpletypes\">" +
+ "<config name=\"test.simpletypes\">" +
"<longval>invalid</longval>" +
"</config>");
DefParser defParser = new DefParser("simpletypes",
@@ -309,7 +276,7 @@ public class DomConfigPayloadBuilderTest {
//assertThat(builder.warnings().size(), is(1));
}
- private Element getDocument(Reader xmlReader) throws ParserConfigurationException {
+ private Element getDocument(Reader xmlReader) {
Document doc;
try {
doc = XmlHelper.getDocumentBuilder().parse(new InputSource(xmlReader));
@@ -319,7 +286,7 @@ public class DomConfigPayloadBuilderTest {
return doc.getDocumentElement();
}
- private Element getDocument(String xml) throws ParserConfigurationException {
+ private Element getDocument(String xml) {
Reader xmlReader = new StringReader(xml);
return getDocument(xmlReader);
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/LegacyConfigModelBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/LegacyConfigModelBuilderTest.java
index bef2b37d65d..fa4c4d313a4 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/LegacyConfigModelBuilderTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/LegacyConfigModelBuilderTest.java
@@ -3,15 +3,14 @@ package com.yahoo.vespa.model.builder.xml.dom;
import com.yahoo.config.model.ConfigModel;
import com.yahoo.config.model.ConfigModelContext;
-import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.builder.xml.ConfigModelId;
+import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.test.MockApplicationPackage;
import com.yahoo.config.model.test.MockRoot;
import com.yahoo.text.XML;
import org.junit.Test;
import org.w3c.dom.Element;
-import java.util.Arrays;
import java.util.List;
import static org.hamcrest.CoreMatchers.is;
@@ -19,12 +18,12 @@ import static org.junit.Assert.assertThat;
/**
* @author Ulf Lilleengen
- * @since 5.1
*/
public class LegacyConfigModelBuilderTest {
+
@Test
public void testThatProducerIsInserted() {
- String services = "<foo><config name=\"bar\"><key>value</key></config></foo>";
+ String services = "<foo><config name=\"bar.foo\"><key>value</key></config></foo>";
ModelBuilder builder = new ModelBuilder();
Model model = builder.build(DeployState.createTestState(new MockApplicationPackage.Builder().withServices(services).build()),
null, null, new MockRoot(), XML.getDocument(services).getDocumentElement());
@@ -51,7 +50,7 @@ public class LegacyConfigModelBuilderTest {
}
private static class ModelBuilder extends LegacyConfigModelBuilder<Model> {
- public ModelBuilder() {
+ ModelBuilder() {
super(Model.class);
}
@@ -61,7 +60,7 @@ public class LegacyConfigModelBuilderTest {
@Override
public List<ConfigModelId> handlesElements() {
- return Arrays.asList(ConfigModelId.fromName("foo"));
+ return List.of(ConfigModelId.fromName("foo"));
}
}
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/VespaDomBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/VespaDomBuilderTest.java
index dff21904c75..b8d9ad59ae0 100755
--- a/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/VespaDomBuilderTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/VespaDomBuilderTest.java
@@ -25,7 +25,7 @@ import static org.junit.Assert.assertThat;
*/
public class VespaDomBuilderTest {
- final static String hosts = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
+ private final static String hosts = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
"<hosts>" +
" <host name=\"localhost\">" +
" <alias>node1</alias>" +
@@ -33,9 +33,9 @@ public class VespaDomBuilderTest {
" </host>" +
"</hosts>";
- final static String services = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
+ private final static String services = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
"<services>" +
- " <config name=\"standard\">" +
+ " <config name=\"a.standard\">" +
" <basicStruct>" +
" <stringVal>default</stringVal>" +
" </basicStruct>" +
@@ -45,7 +45,7 @@ public class VespaDomBuilderTest {
" <adminserver hostalias=\"node1\" />" +
" </admin>" +
" <container version=\"1.0\">" +
- " <config name=\"standard\">" +
+ " <config name=\"a.standard\">" +
" <basicStruct>" +
" <stringVal>qrservers</stringVal>" +
" </basicStruct>" +
@@ -56,19 +56,7 @@ public class VespaDomBuilderTest {
" </container>\n" +
"</services>";
- final static String servicesWithNamespace = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
- "<services>" +
- " <config name=\"testnamespace\" namespace=\"foo\">" +
- " <basicStruct>" +
- " <stringVal>default</stringVal>" +
- " </basicStruct>" +
- " </config> " +
- " <admin version=\"2.0\">" +
- " <adminserver hostalias=\"node1\" />" +
- " </admin>" +
- "</services>";
-
- final static String servicesWithNamespace2 = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
+ private final static String servicesWithNamespace = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
"<services>" +
" <config name=\"foo.testnamespace\">" +
" <basicStruct>" +
@@ -82,7 +70,7 @@ public class VespaDomBuilderTest {
@Test
- public void testUserConfigsWithNamespace() throws Exception {
+ public void testUserConfigsWithNamespace() {
VespaModel model = createModel(hosts, servicesWithNamespace);
GenericConfig.GenericConfigBuilder builder =
@@ -93,16 +81,6 @@ public class VespaDomBuilderTest {
" \"stringVal\": \"default\"\n" +
" }\n" +
"}\n");
-
- model = createModel(hosts, servicesWithNamespace2);
-
- builder = new GenericConfig.GenericConfigBuilder(new ConfigDefinitionKey("testnamespace", "foo"), new ConfigPayloadBuilder());
- model.getConfig(builder, "admin");
- assertEquals(builder.getPayload().toString(), "{\n" +
- " \"basicStruct\": {\n" +
- " \"stringVal\": \"default\"\n" +
- " }\n" +
- "}\n");
}
@Test
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerIncludeTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerIncludeTest.java
index f40981916fa..62a36422dd8 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerIncludeTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerIncludeTest.java
@@ -117,7 +117,7 @@ public class ContainerIncludeTest {
}
@Test
- public void included_file_with_xml_schema_violation() throws Exception {
+ public void included_file_with_xml_schema_violation() {
try {
VespaModelCreatorWithFilePkg creator = new VespaModelCreatorWithFilePkg("src/test/cfg/container/data/include_xml_error/");
creator.create(true);
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/configserver/TestOptions.java b/config-model/src/test/java/com/yahoo/vespa/model/container/configserver/TestOptions.java
index ec01716d9c9..faa461e681b 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/configserver/TestOptions.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/configserver/TestOptions.java
@@ -15,10 +15,6 @@ public class TestOptions implements CloudConfigOptions {
private Optional<Integer> rpcPort = Optional.empty();
private Optional<String> environment = Optional.empty();
private Optional<String> region = Optional.empty();
- private Optional<String> defaultFlavor = Optional.empty();
- private Optional<String> defaultAdminFlavor = Optional.empty();
- private Optional<String> defaultContainerFlavor = Optional.empty();
- private Optional<String> defaultContentFlavor = Optional.empty();
private Optional<Boolean> useVespaVersionInRequest = Optional.empty();
private Optional<Boolean> hostedVespa = Optional.empty();
private Optional<Integer> numParallelTenantLoaders = Optional.empty();
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/http/FilterBindingsTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/http/FilterBindingsTest.java
index 9056a30eda2..6ec41df6a1f 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/http/FilterBindingsTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/http/FilterBindingsTest.java
@@ -11,9 +11,6 @@ import com.yahoo.vespa.model.container.xml.ContainerModelBuilder;
import com.yahoo.vespa.model.container.xml.ContainerModelBuilder.Networking;
import org.junit.Test;
import org.w3c.dom.Element;
-import org.xml.sax.SAXException;
-
-import java.io.IOException;
import static com.yahoo.collections.CollectionUtil.first;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -35,7 +32,7 @@ public class FilterBindingsTest extends DomBuilderTest {
}
- private void buildContainerCluster(Element containerElem) throws SAXException, IOException {
+ private void buildContainerCluster(Element containerElem) {
ContainerModel model = new ContainerModelBuilder(true, Networking.enable).build(DeployState.createTestState(), null, null, root, containerElem);
root.freezeModelTopology();
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/jersey/xml/RestApiTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/jersey/xml/RestApiTest.java
index e954e6343de..9ca508019b2 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/jersey/xml/RestApiTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/jersey/xml/RestApiTest.java
@@ -104,7 +104,7 @@ public class RestApiTest extends ContainerModelBuilderTestBase {
}
@Test
- public void all_non_restApi_components_are_injected_to_RestApiContext() throws Exception {
+ public void all_non_restApi_components_are_injected_to_RestApiContext() {
ComponentsConfig componentsConfig = root.getConfig(ComponentsConfig.class, CLUSTER_ID);
Set<ComponentId> clusterChildrenComponentIds = getContainerCluster(CLUSTER_ID).getAllComponents().stream()
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/MockSearchClusters.java b/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/MockSearchClusters.java
index 607bc484525..98bc4210602 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/MockSearchClusters.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/MockSearchClusters.java
@@ -3,7 +3,6 @@ package com.yahoo.vespa.model.container.search.searchchain;
import com.yahoo.vespa.config.search.AttributesConfig;
import com.yahoo.vespa.config.search.RankProfilesConfig;
-import com.yahoo.config.model.ConfigModelRepo;
import com.yahoo.config.model.producer.AbstractConfigProducerRoot;
import com.yahoo.prelude.fastsearch.DocumentdbInfoConfig;
import com.yahoo.search.config.IndexInfoConfig;
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/SearchChainsTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/SearchChainsTest.java
index d81ffedef7f..365a3ea44b5 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/SearchChainsTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/SearchChainsTest.java
@@ -11,9 +11,6 @@ import org.junit.Before;
import org.junit.Test;
import org.w3c.dom.Element;
-import java.util.List;
-
-import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/SearchChainsTest2.java b/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/SearchChainsTest2.java
index 9dd6f834e62..ef80683abd6 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/SearchChainsTest2.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/SearchChainsTest2.java
@@ -20,7 +20,7 @@ public class SearchChainsTest2 {
private MockRoot root;
@Before
- public void prepareTest() throws Exception {
+ public void prepareTest() {
root = new MockRoot("root");
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/SearchChainsTestBase.java b/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/SearchChainsTestBase.java
index 51a8333ef6e..8b49d1bff0d 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/SearchChainsTestBase.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/SearchChainsTestBase.java
@@ -1,7 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.container.search.searchchain;
-import com.yahoo.binaryprefix.BinaryScaledAmount;
import com.yahoo.config.model.builder.xml.test.DomBuilderTest;
import com.yahoo.vespa.model.builder.xml.dom.chains.search.DomSearchChainsBuilder;
import org.junit.Before;
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/SourceGroupTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/SourceGroupTest.java
index d14905ddab0..cc6478fa4a4 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/SourceGroupTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/SourceGroupTest.java
@@ -24,7 +24,7 @@ public class SourceGroupTest {
private SearchChains searchChains;
@Before
- public void setUp() throws Exception {
+ public void setUp() {
root = new MockRoot();
searchChains = new SearchChains(root, "searchchains");
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/QueryProfilesTestCase.java b/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/QueryProfilesTestCase.java
index 746e771667f..44181234d9a 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/QueryProfilesTestCase.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/QueryProfilesTestCase.java
@@ -1,26 +1,20 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.container.search.test;
-import com.yahoo.component.ComponentId;
import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.search.query.profile.QueryProfile;
import com.yahoo.search.query.profile.QueryProfileRegistry;
-import com.yahoo.search.query.profile.compiled.CompiledQueryProfileRegistry;
import com.yahoo.search.query.profile.config.QueryProfileConfigurer;
-import com.yahoo.search.query.profile.config.QueryProfileXMLReader;
import com.yahoo.search.query.profile.types.FieldDescription;
import com.yahoo.search.query.profile.types.FieldType;
import com.yahoo.search.query.profile.types.QueryProfileType;
import com.yahoo.search.query.profile.types.QueryProfileTypeRegistry;
-import com.yahoo.searchdefinition.SearchBuilder;
-import com.yahoo.searchdefinition.parser.ParseException;
import com.yahoo.vespa.model.container.search.QueryProfiles;
import com.yahoo.vespa.model.test.utils.DeployLoggerStub;
import org.junit.Test;
import java.io.IOException;
import java.util.logging.Level;
-import java.util.logging.Logger;
import static helpers.CompareConfigTestHelper.assertSerializedConfigFileEquals;
import static org.junit.Assert.assertEquals;
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/empty.cfg b/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/empty.cfg
index e69de29bb2d..13153de1644 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/empty.cfg
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/empty.cfg
@@ -0,0 +1 @@
+enableGroupingSessionCache false \ No newline at end of file
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/explicit-reference-override.cfg b/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/explicit-reference-override.cfg
index 99e2e3c5dcb..b62e00a2ad5 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/explicit-reference-override.cfg
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/explicit-reference-override.cfg
@@ -10,4 +10,5 @@ queryprofile[1].property[0].value "a.b"
queryprofile[1].property[0].overridable ""
queryprofile[1].reference[0].name "a"
queryprofile[1].reference[0].value "a1"
-queryprofile[1].reference[0].overridable "" \ No newline at end of file
+queryprofile[1].reference[0].overridable ""
+enableGroupingSessionCache false \ No newline at end of file
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/newsbe-query-profiles-simple.cfg b/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/newsbe-query-profiles-simple.cfg
index 196b6c3513a..65b4258f174 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/newsbe-query-profiles-simple.cfg
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/newsbe-query-profiles-simple.cfg
@@ -16,4 +16,5 @@ queryprofile[0].queryprofilevariant[0].fordimensionvalues[0] "yahoo"
queryprofile[0].queryprofilevariant[0].fordimensionvalues[1] "uk"
queryprofile[0].queryprofilevariant[0].fordimensionvalues[2] "sc"
queryprofile[0].queryprofilevariant[0].property[0].name "scthumbnail.sourcecountry"
-queryprofile[0].queryprofilevariant[0].property[0].value "uk" \ No newline at end of file
+queryprofile[0].queryprofilevariant[0].property[0].value "uk"
+enableGroupingSessionCache false \ No newline at end of file
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/newsfe-query-profiles-simple.cfg b/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/newsfe-query-profiles-simple.cfg
index 461f9b606c6..b416abf7c67 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/newsfe-query-profiles-simple.cfg
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/newsfe-query-profiles-simple.cfg
@@ -23,4 +23,5 @@ queryprofile[1].id "default"
queryprofile[1].type ""
queryprofile[1].reference[0].name "source.news"
queryprofile[1].reference[0].value "backend/news"
-queryprofile[1].reference[0].overridable "" \ No newline at end of file
+queryprofile[1].reference[0].overridable ""
+enableGroupingSessionCache false \ No newline at end of file
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/query-profile-variants-configuration.cfg b/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/query-profile-variants-configuration.cfg
index c86bba23286..352966d0d8b 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/query-profile-variants-configuration.cfg
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/query-profile-variants-configuration.cfg
@@ -38,4 +38,5 @@ queryprofile[2].id "wparent2"
queryprofile[2].type ""
queryprofile[2].property[0].name "a"
queryprofile[2].property[0].value "a1"
-queryprofile[2].property[0].overridable "" \ No newline at end of file
+queryprofile[2].property[0].overridable ""
+enableGroupingSessionCache false \ No newline at end of file
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/query-profile-variants2-configuration.cfg b/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/query-profile-variants2-configuration.cfg
index c915cd2efd0..0f644cbb9eb 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/query-profile-variants2-configuration.cfg
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/query-profile-variants2-configuration.cfg
@@ -58,4 +58,5 @@ queryprofile[3].property[1].overridable "false"
queryprofile[3].queryprofilevariant[0].fordimensionvalues[0] "love"
queryprofile[3].queryprofilevariant[0].fordimensionvalues[1] "default"
queryprofile[3].queryprofilevariant[0].property[0].name "defaultIndex"
-queryprofile[3].queryprofilevariant[0].property[0].value "default" \ No newline at end of file
+queryprofile[3].queryprofilevariant[0].property[0].value "default"
+enableGroupingSessionCache false \ No newline at end of file
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/query-profiles.cfg b/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/query-profiles.cfg
index 89a971adb15..4a9dabdc6ca 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/query-profiles.cfg
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/query-profiles.cfg
@@ -102,4 +102,5 @@ queryprofiletype[2].field[0].name "market"
queryprofiletype[2].field[0].type "string"
queryprofiletype[2].field[0].overridable false
queryprofiletype[2].field[0].mandatory false
-queryprofiletype[2].field[0].alias "" \ No newline at end of file
+queryprofiletype[2].field[0].alias ""
+enableGroupingSessionCache false \ No newline at end of file
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/variants-of-explicit-compound-with-reference.cfg b/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/variants-of-explicit-compound-with-reference.cfg
index e1cca7ed232..92605f504a0 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/variants-of-explicit-compound-with-reference.cfg
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/variants-of-explicit-compound-with-reference.cfg
@@ -23,4 +23,5 @@ queryprofile[2].queryprofilevariant[0].reference[0].name "a"
queryprofile[2].queryprofilevariant[0].reference[0].value "a2"
queryprofile[2].queryprofilevariant[1].fordimensionvalues[0] "x2"
queryprofile[2].queryprofilevariant[1].property[0].name "a.b"
-queryprofile[2].queryprofilevariant[1].property[0].value "a.b.x2" \ No newline at end of file
+queryprofile[2].queryprofilevariant[1].property[0].value "a.b.x2"
+enableGroupingSessionCache false \ No newline at end of file
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/variants-of-explicit-compound.cfg b/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/variants-of-explicit-compound.cfg
index d65b3fa5f92..1e7739d9962 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/variants-of-explicit-compound.cfg
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/search/test/variants-of-explicit-compound.cfg
@@ -14,4 +14,5 @@ queryprofile[1].queryprofilevariant[0].property[0].name "a.b"
queryprofile[1].queryprofilevariant[0].property[0].value "a.b.x1"
queryprofile[1].queryprofilevariant[1].fordimensionvalues[0] "x2"
queryprofile[1].queryprofilevariant[1].property[0].name "a.b"
-queryprofile[1].queryprofilevariant[1].property[0].value "a.b.x2" \ No newline at end of file
+queryprofile[1].queryprofilevariant[1].property[0].value "a.b.x2"
+enableGroupingSessionCache false \ No newline at end of file
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessControlTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessControlTest.java
index 3fea346f788..37a57fc59c9 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessControlTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessControlTest.java
@@ -13,9 +13,7 @@ import com.yahoo.vespa.model.container.http.xml.HttpBuilder;
import com.yahoo.vespa.model.container.jersey.Jersey2Servlet;
import org.junit.Test;
import org.w3c.dom.Element;
-import org.xml.sax.SAXException;
-import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
@@ -46,7 +44,7 @@ public class AccessControlTest extends ContainerModelBuilderTestBase {
ContainerCluster.ROOT_HANDLER_BINDING);
@Test
- public void access_control_filter_chain_is_set_up() throws Exception {
+ public void access_control_filter_chain_is_set_up() {
Element clusterElem = DomBuilderTest.parse(
" <http>",
" <filtering>",
@@ -61,7 +59,7 @@ public class AccessControlTest extends ContainerModelBuilderTestBase {
}
@Test
- public void properties_are_set_from_xml() throws Exception {
+ public void properties_are_set_from_xml() {
Element clusterElem = DomBuilderTest.parse(
" <http>",
" <filtering>",
@@ -82,7 +80,7 @@ public class AccessControlTest extends ContainerModelBuilderTestBase {
}
@Test
- public void read_is_disabled_and_write_is_enabled_by_default() throws Exception {
+ public void read_is_disabled_and_write_is_enabled_by_default() {
Element clusterElem = DomBuilderTest.parse(
" <http>",
" <filtering>",
@@ -98,7 +96,7 @@ public class AccessControlTest extends ContainerModelBuilderTestBase {
}
@Test
- public void read_and_write_can_be_overridden() throws Exception {
+ public void read_and_write_can_be_overridden() {
Element clusterElem = DomBuilderTest.parse(
" <http>",
" <filtering>",
@@ -114,7 +112,7 @@ public class AccessControlTest extends ContainerModelBuilderTestBase {
}
@Test
- public void access_control_filter_chain_has_correct_handler_bindings() throws Exception {
+ public void access_control_filter_chain_has_correct_handler_bindings() {
Element clusterElem = DomBuilderTest.parse(
"<container version='1.0'>",
" <search/>",
@@ -145,7 +143,7 @@ public class AccessControlTest extends ContainerModelBuilderTestBase {
}
@Test
- public void handler_can_be_excluded_by_excluding_one_of_its_bindings() throws Exception {
+ public void handler_can_be_excluded_by_excluding_one_of_its_bindings() {
final String notExcludedBinding = "http://*/custom-handler/*";
final String excludedBinding = "http://*/excluded/*";
Element clusterElem = DomBuilderTest.parse(
@@ -166,7 +164,7 @@ public class AccessControlTest extends ContainerModelBuilderTestBase {
}
@Test
- public void access_control_filter_chain_has_all_servlet_bindings() throws Exception {
+ public void access_control_filter_chain_has_all_servlet_bindings() {
final String servletPath = "servlet/path";
final String restApiPath = "api/v0";
final Set<String> requiredBindings = ImmutableSet.of(servletPath, restApiPath);
@@ -194,7 +192,7 @@ public class AccessControlTest extends ContainerModelBuilderTestBase {
}
@Test
- public void servlet_can_be_excluded_by_excluding_one_of_its_bindings() throws Exception {
+ public void servlet_can_be_excluded_by_excluding_one_of_its_bindings() {
final String servletPath = "servlet/path";
final String notExcludedBinding = "http://*:8081/" + servletPath;
final String excludedBinding = "http://*:8080/" + servletPath;
@@ -215,7 +213,7 @@ public class AccessControlTest extends ContainerModelBuilderTestBase {
}
@Test
- public void rest_api_can_be_excluded_by_excluding_one_of_its_bindings() throws Exception {
+ public void rest_api_can_be_excluded_by_excluding_one_of_its_bindings() {
final String restApiPath = "api/v0";
final String notExcludedBinding = "http://*:8081/" + restApiPath + Jersey2Servlet.BINDING_SUFFIX;;
final String excludedBinding = "http://*:8080/" + restApiPath + Jersey2Servlet.BINDING_SUFFIX;;
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessLogTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessLogTest.java
index 3876ce25d96..4ea10c9a1c1 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessLogTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessLogTest.java
@@ -23,7 +23,7 @@ import static org.junit.Assert.assertEquals;
public class AccessLogTest extends ContainerModelBuilderTestBase {
@Test
- public void default_access_log_is_only_added_when_search_is_present() throws Exception {
+ public void default_access_log_is_only_added_when_search_is_present() {
Element cluster1Elem = DomBuilderTest.parse(
"<container id='cluster1' version='1.0'>",
"<search />",
@@ -45,7 +45,7 @@ public class AccessLogTest extends ContainerModelBuilderTestBase {
}
@Test
- public void default_search_access_log_can_be_disabled() throws Exception {
+ public void default_search_access_log_can_be_disabled() {
final String jdiscClusterId = "jdisc-cluster";
Element clusterElem = DomBuilderTest.parse(
@@ -69,7 +69,7 @@ public class AccessLogTest extends ContainerModelBuilderTestBase {
}
@Test
- public void access_log_can_be_configured() throws Exception {
+ public void access_log_can_be_configured() {
Element clusterElem = DomBuilderTest.parse(
"<container id='default' version='1.0'>",
" <accesslog type='vespa' ",
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java
index 7b382a45730..8b92e1091ca 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java
@@ -396,7 +396,7 @@ public class ContainerModelBuilderTest extends ContainerModelBuilderTestBase {
}
@Test
- public void nested_components_are_injected_to_handlers() throws Exception {
+ public void nested_components_are_injected_to_handlers() {
Element clusterElem = DomBuilderTest.parse(
"<container id='default' version='1.0'>",
" <handler id='myHandler'>",
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTestBase.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTestBase.java
index 4fec94d4ab4..41997925666 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTestBase.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTestBase.java
@@ -71,7 +71,7 @@ public abstract class ContainerModelBuilderTestBase {
}
@Before
- public void prepareTest() throws Exception {
+ public void prepareTest() {
root = new MockRoot("root");
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/IdentityBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/IdentityBuilderTest.java
index 0695f7b30d7..9794062d83f 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/IdentityBuilderTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/IdentityBuilderTest.java
@@ -10,9 +10,6 @@ import com.yahoo.container.core.identity.IdentityConfig;
import com.yahoo.vespa.model.container.IdentityProvider;
import org.junit.Test;
import org.w3c.dom.Element;
-import org.xml.sax.SAXException;
-
-import java.io.IOException;
import static org.junit.Assert.assertEquals;
@@ -21,7 +18,7 @@ import static org.junit.Assert.assertEquals;
*/
public class IdentityBuilderTest extends ContainerModelBuilderTestBase {
@Test
- public void identity_config_produced_from_deployment_spec() throws IOException, SAXException {
+ public void identity_config_produced_from_deployment_spec() {
Element clusterElem = DomBuilderTest.parse(
"<container id='default' version='1.0'><search /></container>");
String deploymentXml = "<deployment version='1.0' athenz-domain='domain' athenz-service='service'>\n" +
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/JettyContainerModelBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/JettyContainerModelBuilderTest.java
index 4e119506104..f0fcb239521 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/JettyContainerModelBuilderTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/JettyContainerModelBuilderTest.java
@@ -35,7 +35,7 @@ import static org.junit.Assert.assertTrue;
public class JettyContainerModelBuilderTest extends ContainerModelBuilderTestBase {
@Test
- public void verify_that_overriding_connector_options_works() throws Exception {
+ public void verify_that_overriding_connector_options_works() {
Element clusterElem = DomBuilderTest.parse(
"<container id='default' version='1.0'>\n" +
" <http>\n" +
@@ -56,7 +56,7 @@ public class JettyContainerModelBuilderTest extends ContainerModelBuilderTestBas
}
@Test
- public void verify_that_enabling_jetty_works() throws Exception {
+ public void verify_that_enabling_jetty_works() {
Element clusterElem = DomBuilderTest.parse(
"<container id='default' version='1.0'>" +
nodesXml +
@@ -67,7 +67,7 @@ public class JettyContainerModelBuilderTest extends ContainerModelBuilderTestBas
}
@Test
- public void verify_that_enabling_jetty_works_for_custom_http_servers() throws Exception {
+ public void verify_that_enabling_jetty_works_for_custom_http_servers() {
Element clusterElem = DomBuilderTest.parse(
"<container id='default' version='1.0'>",
" <http>",
@@ -80,7 +80,7 @@ public class JettyContainerModelBuilderTest extends ContainerModelBuilderTestBas
}
@Test
- public void verifyThatJettyHttpServerHasFilterBindingsProvider() throws Exception {
+ public void verifyThatJettyHttpServerHasFilterBindingsProvider() {
final Element clusterElem = DomBuilderTest.parse(
"<container id='default' version='1.0'>",
nodesXml,
@@ -101,7 +101,7 @@ public class JettyContainerModelBuilderTest extends ContainerModelBuilderTestBas
}
@Test
- public void verifyThatJettyHttpServerHasFilterBindingsProviderForCustomHttpServers() throws Exception {
+ public void verifyThatJettyHttpServerHasFilterBindingsProviderForCustomHttpServers() {
final Element clusterElem = DomBuilderTest.parse(
"<container id='default' version='1.0'>",
" <http>",
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/RoutingBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/RoutingBuilderTest.java
index 7789b4b8a16..6ebd530ca8c 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/RoutingBuilderTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/RoutingBuilderTest.java
@@ -64,7 +64,7 @@ public class RoutingBuilderTest extends ContainerModelBuilderTestBase {
}
- private ApplicationContainer getContainer(ApplicationPackage applicationPackage, String region, Element clusterElem) throws IOException, SAXException {
+ private ApplicationContainer getContainer(ApplicationPackage applicationPackage, String region, Element clusterElem) {
DeployState deployState = new DeployState.Builder()
.applicationPackage(applicationPackage)
.zone(new Zone(Environment.prod, RegionName.from(region)))
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/SearchBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/SearchBuilderTest.java
index 0da3b8e1f5f..1f0b0188681 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/SearchBuilderTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/SearchBuilderTest.java
@@ -93,14 +93,14 @@ public class SearchBuilderTest extends ContainerModelBuilderTestBase {
// TODO: remove test when all containers are named 'container'
@Test
- public void cluster_with_only_search_gets_qrserver_as_service_name() throws Exception {
+ public void cluster_with_only_search_gets_qrserver_as_service_name() {
createClusterWithOnlyDefaultChains();
ApplicationContainerCluster cluster = (ApplicationContainerCluster)root.getChildren().get("default");
assertThat(cluster.getContainers().get(0).getServiceName(), is(QRSERVER.serviceName));
}
@Test
- public void empty_search_element_gives_default_chains() throws Exception {
+ public void empty_search_element_gives_default_chains() {
createClusterWithOnlyDefaultChains();
assertThat(chainsConfig().chains(), hasItemWithMethod("vespaPhases", "id"));
assertThat(chainsConfig().chains(), hasItemWithMethod("native", "id"));
@@ -137,7 +137,7 @@ public class SearchBuilderTest extends ContainerModelBuilderTestBase {
}
@Test
- public void cluster_is_connected_to_content_clusters() throws Exception {
+ public void cluster_is_connected_to_content_clusters() {
String hosts = hostsXml();
String services = "" +
@@ -163,7 +163,7 @@ public class SearchBuilderTest extends ContainerModelBuilderTestBase {
}
@Test
- public void cluster_is_connected_to_search_clusters() throws Exception {
+ public void cluster_is_connected_to_search_clusters() {
String hosts = hostsXml();
String services = "" +
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java
index c70e05c39c3..d98d1da9d2a 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java
@@ -695,7 +695,7 @@ public class ContentClusterTest extends ContentBaseTest {
}
@Test
- public void testConfiguredMetrics() throws Exception {
+ public void testConfiguredMetrics() {
String xml = "" +
"<services>" +
"<content version=\"1.0\" id=\"storage\">\n" +
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/DistributorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/DistributorTest.java
index 8ae68468374..f36ef6c3ba3 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/DistributorTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/DistributorTest.java
@@ -113,6 +113,7 @@ public class DistributorTest {
assertEquals(true, conf.inlinebucketsplitting());
cluster = parseCluster("<cluster id=\"storage\">\n" +
+ " <redundancy>2</redundancy>" +
" <documents/>" +
" <tuning>" +
" <distribution type=\"legacy\"/>" +
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/GenericConfigTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/GenericConfigTest.java
index a65c4e50521..992edf6b1bb 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/GenericConfigTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/GenericConfigTest.java
@@ -2,7 +2,6 @@
package com.yahoo.vespa.model.content;
import com.yahoo.vespa.config.content.StorFilestorConfig;
-import com.yahoo.searchdefinition.parser.ParseException;
import com.yahoo.vespa.model.VespaModel;
import com.yahoo.vespa.model.content.cluster.ContentCluster;
import com.yahoo.vespa.model.content.storagecluster.StorageCluster;
@@ -10,9 +9,6 @@ import com.yahoo.vespa.model.test.utils.ApplicationPackageUtils;
import com.yahoo.vespa.model.test.utils.VespaModelCreatorWithMockPkg;
import org.junit.Before;
import org.junit.Test;
-import org.xml.sax.SAXException;
-
-import java.io.IOException;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
@@ -53,12 +49,12 @@ public class GenericConfigTest {
}
@Before
- public void getVespaModel() throws IOException, SAXException, ParseException {
+ public void getVespaModel() {
model = (new VespaModelCreatorWithMockPkg(ContentBaseTest.getHosts(), servicesXml(), ApplicationPackageUtils.generateSearchDefinitions("type1"))).create();
}
@Test
- public void config_override_on_root_is_visible_on_storage_cluster() throws Exception {
+ public void config_override_on_root_is_visible_on_storage_cluster() {
StorageCluster cluster = model.getContentClusters().get("storage").getStorageNodes();
StorFilestorConfig config = model.getConfig(StorFilestorConfig.class, cluster.getConfigId());
@@ -66,7 +62,7 @@ public class GenericConfigTest {
}
@Test
- public void config_override_on_root_is_visible_on_content_cluster() throws Exception {
+ public void config_override_on_root_is_visible_on_content_cluster() {
ContentCluster cluster = model.getContentClusters().get("storage");
StorFilestorConfig config = model.getConfig(StorFilestorConfig.class, cluster.getConfigId());
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/IndexedHierarchicDistributionTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/IndexedHierarchicDistributionTest.java
index f402bae8fd9..afedbaea779 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/IndexedHierarchicDistributionTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/IndexedHierarchicDistributionTest.java
@@ -171,7 +171,7 @@ public class IndexedHierarchicDistributionTest {
return createCluster(createClusterXml(groupXml, 2, 2));
}
- private String getOddGroupsClusterXml() throws Exception {
+ private String getOddGroupsClusterXml() {
return joinLines(" <group>",
" <distribution partitions='2|*'/>",
" <group distribution-key='0' name='group0'>",
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/IndexingAndDocprocRoutingTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/IndexingAndDocprocRoutingTest.java
index e25bcea29f6..55d070d7247 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/IndexingAndDocprocRoutingTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/IndexingAndDocprocRoutingTest.java
@@ -149,7 +149,7 @@ public class IndexingAndDocprocRoutingTest extends ContentBaseTest {
}
@Test
- public void noContentClustersOneDocprocCluster() throws ParseException, IOException, SAXException {
+ public void noContentClustersOneDocprocCluster() {
String services =
"<?xml version='1.0' encoding='utf-8' ?>\n" +
"<services version='1.0'>\n" +
@@ -447,14 +447,12 @@ public class IndexingAndDocprocRoutingTest extends ContentBaseTest {
searchClusterPost, searchClusterPostPost, mainPost, searchClusterSpecs);
}
- private VespaModel getIndexedSearchVespaModel(String xml)
- throws ParseException, IOException, SAXException {
+ private VespaModel getIndexedSearchVespaModel(String xml) {
List<String> sds = ApplicationPackageUtils.generateSearchDefinitions("music", "album", "artist");
return new VespaModelCreatorWithMockPkg(getHosts(), xml, sds).create();
}
- private VespaModel getIndexedContentVespaModel(List<DocprocClusterSpec> docprocClusterSpecs, List<SearchClusterSpec> searchClusterSpecs)
- throws ParseException, IOException, SAXException {
+ private VespaModel getIndexedContentVespaModel(List<DocprocClusterSpec> docprocClusterSpecs, List<SearchClusterSpec> searchClusterSpecs) {
List<String> sds = new ArrayList<>();
for (SearchClusterSpec cluster : searchClusterSpecs) {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/MonitoringConfigSnoopTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/MonitoringConfigSnoopTest.java
index dea86616f06..d73140d39d5 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/MonitoringConfigSnoopTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/MonitoringConfigSnoopTest.java
@@ -17,7 +17,7 @@ public class MonitoringConfigSnoopTest {
private TestRoot root;
- public void initRoot(int interval) throws Exception {
+ public void initRoot(int interval) {
TestDriver tester = new TestDriver();
root = tester.buildModel(getAdminXml(interval) + getContent());
}
@@ -53,7 +53,7 @@ public class MonitoringConfigSnoopTest {
}
@Test
- public void correct_config_is_snooped_default_interval() throws Exception {
+ public void correct_config_is_snooped_default_interval() {
String getAdminXmlIntervalNotSpecified = "<admin version='2.0'>"
+ " <adminserver hostalias='mockhost' />"
+ "</admin>";
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/RedundancyTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/RedundancyTest.java
index 3b2a741a177..eef48120da8 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/RedundancyTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/RedundancyTest.java
@@ -22,9 +22,7 @@ public class RedundancyTest {
}
private static Redundancy createRedundancy(int redundancy, int implicitGroups, int totalNodes) {
- Redundancy r = new Redundancy(1, redundancy, 1);
- r.setImplicitGroups(implicitGroups);
- r.setTotalNodes(totalNodes);
+ Redundancy r = new Redundancy(1, redundancy, 1, implicitGroups, totalNodes);
return r;
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/StorageClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/StorageClusterTest.java
index 27f5af52b6c..21c384dfc69 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/StorageClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/StorageClusterTest.java
@@ -33,7 +33,7 @@ public class StorageClusterTest {
MockRoot root = new MockRoot();
return parse(xml, root);
}
- StorageCluster parse(String xml, MockRoot root) throws Exception {
+ StorageCluster parse(String xml, MockRoot root) {
root.getDeployState().getDocumentModel().getDocumentManager().add(
new NewDocumentType(new NewDocumentType.Name("music"))
);
@@ -190,7 +190,7 @@ public class StorageClusterTest {
}
@Test
- public void testCapacity() throws Exception {
+ public void testCapacity() {
String xml =
"<cluster id=\"storage\">\n" +
" <documents/>" +
@@ -214,7 +214,7 @@ public class StorageClusterTest {
}
@Test
- public void testRootFolder() throws Exception {
+ public void testRootFolder() {
String xml =
"<cluster id=\"storage\">\n" +
" <documents/>" +
@@ -245,7 +245,7 @@ public class StorageClusterTest {
}
@Test
- public void testGenericPersistenceTuning() throws Exception {
+ public void testGenericPersistenceTuning() {
String xml =
"<cluster id=\"storage\">\n" +
"<documents/>" +
@@ -271,7 +271,7 @@ public class StorageClusterTest {
}
@Test
- public void requireThatUserDoesNotSpecifyBothGroupAndNodes() throws Exception {
+ public void requireThatUserDoesNotSpecifyBothGroupAndNodes() {
String xml =
"<cluster id=\"storage\">\n" +
"<documents/>\n" +
@@ -302,10 +302,11 @@ public class StorageClusterTest {
}
@Test
- public void requireThatGroupNamesMustBeUniqueAmongstSiblings() throws Exception {
+ public void requireThatGroupNamesMustBeUniqueAmongstSiblings() {
String xml =
"<cluster id=\"storage\">\n" +
- "<documents/>\n" +
+ " <redundancy>2</redundancy>" +
+ " <documents/>\n" +
" <group>\n" +
" <distribution partitions=\"*\"/>\n" +
" <group distribution-key=\"0\" name=\"bar\">\n" +
@@ -327,9 +328,10 @@ public class StorageClusterTest {
}
@Test
- public void requireThatGroupNamesCanBeDuplicatedAcrossLevels() throws Exception {
+ public void requireThatGroupNamesCanBeDuplicatedAcrossLevels() {
String xml =
"<cluster id=\"storage\">\n" +
+ " <redundancy>2</redundancy>" +
"<documents/>\n" +
" <group>\n" +
" <distribution partitions=\"*\"/>\n" +
@@ -351,7 +353,7 @@ public class StorageClusterTest {
}
@Test
- public void requireThatNestedGroupsRequireDistribution() throws Exception {
+ public void requireThatNestedGroupsRequireDistribution() {
String xml =
"<cluster id=\"storage\">\n" +
"<documents/>\n" +
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/StorageContentTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/StorageContentTest.java
index 38f4b392f41..c0ddd49069d 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/StorageContentTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/StorageContentTest.java
@@ -4,19 +4,23 @@ package com.yahoo.vespa.model.content;
import com.yahoo.documentapi.messagebus.protocol.DocumentrouteselectorpolicyConfig;
import com.yahoo.messagebus.routing.RouteSpec;
import com.yahoo.messagebus.routing.RoutingTableSpec;
-import com.yahoo.searchdefinition.parser.ParseException;
import com.yahoo.vespa.model.VespaModel;
import com.yahoo.vespa.model.routing.DocumentProtocol;
import com.yahoo.vespa.model.routing.Routing;
import com.yahoo.vespa.model.test.utils.ApplicationPackageUtils;
import com.yahoo.vespa.model.test.utils.VespaModelCreatorWithMockPkg;
import org.junit.Test;
-import org.xml.sax.SAXException;
-import java.io.IOException;
-import java.util.*;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
public class StorageContentTest extends ContentBaseTest {
// TODO: Test with document-definitions
@@ -44,7 +48,7 @@ public class StorageContentTest extends ContentBaseTest {
"</services>";
}
- private VespaModel getStorageVespaModel(String cluster1docs, String cluster2docs) throws ParseException, IOException, SAXException {
+ private VespaModel getStorageVespaModel(String cluster1docs, String cluster2docs) {
List<String> sds = ApplicationPackageUtils.generateSearchDefinitions("type1", "type2", "type3");
return new VespaModelCreatorWithMockPkg(getHosts(), createStorageVespaServices(cluster1docs, cluster2docs), sds).create();
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/StorageGroupTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/StorageGroupTest.java
index 31c1e250183..cb457cabf6c 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/StorageGroupTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/StorageGroupTest.java
@@ -15,7 +15,7 @@ import static org.junit.Assert.assertTrue;
*/
public class StorageGroupTest {
- ContentCluster parse(String xml) throws Exception {
+ ContentCluster parse(String xml) {
return ContentClusterUtils.createCluster(xml, new MockRoot());
}
@@ -80,6 +80,7 @@ public class StorageGroupTest {
StorDistributionConfig.Builder builder = new StorDistributionConfig.Builder();
parse(
"<content version=\"1.0\" id=\"storage\">\n" +
+ " <redundancy>4</redundancy>" +
" <documents/>" +
" <group>\n" +
" <distribution partitions=\"1|*\"/>\n" +
@@ -134,6 +135,7 @@ public class StorageGroupTest {
StorDistributionConfig.Builder builder = new StorDistributionConfig.Builder();
parse(
"<content version=\"1.0\" id=\"storage\">\n" +
+ " <redundancy>2</redundancy>" +
" <documents/>" +
" <group>\n" +
" <distribution partitions=\"1|*\"/>\n" +
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/cluster/ClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/cluster/ClusterTest.java
index fb54f8f9241..4fadea74feb 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/cluster/ClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/cluster/ClusterTest.java
@@ -4,14 +4,11 @@ package com.yahoo.vespa.model.content.cluster;
import com.yahoo.config.application.api.ApplicationPackage;
import com.yahoo.config.model.test.MockApplicationPackage;
import com.yahoo.config.model.test.TestDriver;
-import com.yahoo.searchdefinition.SearchBuilder;
-import com.yahoo.searchdefinition.parser.ParseException;
import com.yahoo.vespa.config.search.core.PartitionsConfig;
import com.yahoo.vespa.config.search.core.ProtonConfig;
import com.yahoo.vespa.model.content.Content;
-import com.yahoo.vespa.model.search.IndexedSearchCluster;
-import com.yahoo.vespa.model.search.SearchDefinition;
import com.yahoo.vespa.model.search.Dispatch;
+import com.yahoo.vespa.model.search.IndexedSearchCluster;
import com.yahoo.vespa.model.test.utils.ApplicationPackageUtils;
import org.junit.Test;
@@ -42,7 +39,7 @@ public class ClusterTest {
}
@Test
- public void requireThatSearchCoverageIsApplied() throws ParseException {
+ public void requireThatSearchCoverageIsApplied() {
ContentCluster cluster = newContentCluster(joinLines("<search>",
" <coverage>",
" <minimum>0.11</minimum>",
@@ -127,6 +124,8 @@ public class ClusterTest {
" </engine>",
" <group>",
" <node hostalias='my_host' distribution-key='0' />",
+ " <node hostalias='my_host' distribution-key='1' />",
+ " <node hostalias='my_host' distribution-key='2' />",
" </group>",
contentSearchXml,
" </content>",
@@ -142,13 +141,6 @@ public class ClusterTest {
return "<document mode='index' type='my_document' " + (globalDocType ? "global='true' " : "") + "/>";
}
- private static SearchDefinition newSearchDefinition(String name) throws ParseException {
- SearchBuilder builder = new SearchBuilder();
- builder.importString("search " + name + " { document " + name + " { } }");
- builder.build();
- return new SearchDefinition(name, builder.getSearch(name));
- }
-
private static ProtonConfig getProtonConfig(ContentCluster cluster) {
ProtonConfig.Builder builder = new ProtonConfig.Builder();
cluster.getSearch().getConfig(builder);
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/utils/ContentClusterBuilder.java b/config-model/src/test/java/com/yahoo/vespa/model/content/utils/ContentClusterBuilder.java
index 95c57bb544c..866c03d82f0 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/utils/ContentClusterBuilder.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/utils/ContentClusterBuilder.java
@@ -77,7 +77,7 @@ public class ContentClusterBuilder {
return this;
}
- public ContentCluster build(MockRoot root) throws Exception {
+ public ContentCluster build(MockRoot root) {
return ContentClusterUtils.createCluster(getXml(), root);
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/generic/GenericServicesTest.java b/config-model/src/test/java/com/yahoo/vespa/model/generic/GenericServicesTest.java
index c6c08df64f4..0e7fce2d1ef 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/generic/GenericServicesTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/generic/GenericServicesTest.java
@@ -2,17 +2,9 @@
package com.yahoo.vespa.model.generic;
import com.yahoo.cloud.config.SentinelConfig;
-import com.yahoo.config.codegen.CNode;
import com.yahoo.config.application.api.ApplicationPackage;
import com.yahoo.config.model.application.provider.FilesApplicationPackage;
-import com.yahoo.vespa.config.ConfigDefinitionKey;
-import com.yahoo.vespa.config.ConfigPayload;
-import com.yahoo.vespa.config.ConfigPayloadBuilder;
-import com.yahoo.vespa.config.GenericConfig;
-import com.yahoo.vespa.defaults.Defaults;
import com.yahoo.vespa.model.VespaModel;
-import com.yahoo.vespa.model.generic.service.Service;
-import com.yahoo.vespa.model.generic.service.ServiceCluster;
import org.junit.BeforeClass;
import org.junit.Test;
import org.xml.sax.SAXException;
@@ -40,7 +32,7 @@ public class GenericServicesTest {
}
@Test
- public void testServicesSentinelConfig() throws IOException, SAXException {
+ public void testServicesSentinelConfig() {
String sentinelConfigId1="hosts/bogusname1/sentinel";
String sentinelConfigId2="hosts/bogusname2/sentinel";
String sentinelConfigId3="hosts/bogusname3/sentinel";
@@ -72,7 +64,7 @@ public class GenericServicesTest {
}
@Test
- public void testServicesModel() throws IOException, SAXException {
+ public void testServicesModel() {
// Testing that this model can be constructed only for now
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/ml/ImportedModelTester.java b/config-model/src/test/java/com/yahoo/vespa/model/ml/ImportedModelTester.java
index 41811738ea4..ce36ecc4a1c 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/ml/ImportedModelTester.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/ml/ImportedModelTester.java
@@ -9,12 +9,10 @@ import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.io.GrowableByteBuffer;
import com.yahoo.io.IOUtils;
import com.yahoo.path.Path;
-import com.yahoo.searchdefinition.RankProfile;
import com.yahoo.searchdefinition.RankingConstant;
import ai.vespa.rankingexpression.importer.onnx.OnnxImporter;
import ai.vespa.rankingexpression.importer.tensorflow.TensorFlowImporter;
import ai.vespa.rankingexpression.importer.xgboost.XGBoostImporter;
-import com.yahoo.searchdefinition.derived.RawRankProfile;
import com.yahoo.tensor.Tensor;
import com.yahoo.tensor.serialization.TypedBinaryFormat;
import com.yahoo.vespa.model.VespaModel;
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/search/MultilevelDispatchTest.java b/config-model/src/test/java/com/yahoo/vespa/model/search/MultilevelDispatchTest.java
index f50d41b9c95..3c5008cd5f6 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/search/MultilevelDispatchTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/search/MultilevelDispatchTest.java
@@ -169,7 +169,6 @@ public class MultilevelDispatchTest {
@Test
public void requireThatMaxHitsIsScaled() throws Exception {
ContentCluster cr = createCluster(getSimpleDispatchXml() + getMaxhitsTuning());
- IndexedSearchCluster ix = cr.getSearch().getIndexed();
Dispatch tld = cr.getSearch().getIndexed().getTLDs().get(0);
PartitionsConfig.Builder builder = new PartitionsConfig.Builder();
tld.getConfig(builder);
@@ -213,7 +212,7 @@ public class MultilevelDispatchTest {
}
@Test
- public void requireThatSearchCoverageIsSetInSingleLevelSetup() throws Exception {
+ public void requireThatSearchCoverageIsSetInSingleLevelSetup() {
TestRoot root = new TestDriver(true).buildModel(new MockApplicationPackage.Builder()
.withServices("<services version='1.0'>" +
"<content id='stateful' version='1.0'>" +
@@ -370,7 +369,7 @@ public class MultilevelDispatchTest {
}
@Test
- public void requireThatWeReferenceValidNodesWhenSettingUpDispatchGroups() throws Exception {
+ public void requireThatWeReferenceValidNodesWhenSettingUpDispatchGroups() {
try {
createIllegalSetupWithIllegalNodeReference();
assertFalse("Did not get expected Exception", true);
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/search/NodeFlavorTuningTest.java b/config-model/src/test/java/com/yahoo/vespa/model/search/NodeFlavorTuningTest.java
index a4fac3fc9c6..a4b414ba0da 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/search/NodeFlavorTuningTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/search/NodeFlavorTuningTest.java
@@ -33,7 +33,7 @@ public class NodeFlavorTuningTest {
assertEquals(24 * GB, cfg.hwinfo().memory().size());
}
- private ProtonConfig getProtonMemoryConfig(List<Pair<String, String>> sdAndMode, int gb) {
+ private ProtonConfig getProtonMemoryConfig(List<Pair<String, String>> sdAndMode, int gb, int redundancy, int searchableCopies) {
ProtonConfig.Builder builder = new ProtonConfig.Builder();
for (Pair<String, String> sdMode : sdAndMode) {
builder.documentdb.add(new ProtonConfig.Documentdb.Builder()
@@ -41,19 +41,26 @@ public class NodeFlavorTuningTest {
.configid("some/config/id/" + sdMode.getFirst())
.mode(ProtonConfig.Documentdb.Mode.Enum.valueOf(sdMode.getSecond())));
}
- return configFromMemorySetting(gb, builder);
+ return configFromMemorySetting(gb, builder, redundancy, searchableCopies);
}
- @Test
- public void require_that_initial_numdocs_is_dependent_of_mode() {
- ProtonConfig cfg = getProtonMemoryConfig(Arrays.asList(new Pair<>("a", "INDEX"), new Pair<>("b", "STREAMING"), new Pair<>("c", "STORE_ONLY")), 24);
+ private void verify_that_initial_numdocs_is_dependent_of_mode(int redundancy, int searchablecopies) {
+ int divisor = Math.max(redundancy, searchablecopies);
+ ProtonConfig cfg = getProtonMemoryConfig(Arrays.asList(new Pair<>("a", "INDEX"), new Pair<>("b", "STREAMING"), new Pair<>("c", "STORE_ONLY")), 24, redundancy, searchablecopies);
assertEquals(3, cfg.documentdb().size());
assertEquals(1024, cfg.documentdb(0).allocation().initialnumdocs());
assertEquals("a", cfg.documentdb(0).inputdoctypename());
- assertEquals(402653184, cfg.documentdb(1).allocation().initialnumdocs());
+ assertEquals(402653184/divisor, cfg.documentdb(1).allocation().initialnumdocs());
assertEquals("b", cfg.documentdb(1).inputdoctypename());
- assertEquals(402653184, cfg.documentdb(2).allocation().initialnumdocs());
+ assertEquals(402653184/divisor, cfg.documentdb(2).allocation().initialnumdocs());
assertEquals("c", cfg.documentdb(2).inputdoctypename());
}
+ @Test
+ public void require_that_initial_numdocs_is_dependent_of_mode_and_searchablecopies() {
+ verify_that_initial_numdocs_is_dependent_of_mode(2,0);
+ verify_that_initial_numdocs_is_dependent_of_mode(1,1);
+ verify_that_initial_numdocs_is_dependent_of_mode(3, 2);
+ verify_that_initial_numdocs_is_dependent_of_mode(3, 3);
+ }
@Test
public void require_that_hwinfo_cpu_cores_is_set() {
@@ -183,9 +190,9 @@ public class NodeFlavorTuningTest {
return getConfig(new FlavorsConfig.Flavor.Builder().
minMainMemoryAvailableGb(memoryGb));
}
- private static ProtonConfig configFromMemorySetting(int memoryGb, ProtonConfig.Builder builder) {
+ private static ProtonConfig configFromMemorySetting(int memoryGb, ProtonConfig.Builder builder, int redundancy, int searchableCopies) {
return getConfig(new FlavorsConfig.Flavor.Builder().
- minMainMemoryAvailableGb(memoryGb), builder);
+ minMainMemoryAvailableGb(memoryGb), builder, redundancy, searchableCopies);
}
private static ProtonConfig configFromNumCoresSetting(double numCores) {
@@ -198,16 +205,14 @@ public class NodeFlavorTuningTest {
}
private static ProtonConfig getConfig(FlavorsConfig.Flavor.Builder flavorBuilder) {
- getConfig(flavorBuilder, new ProtonConfig.Builder());
- flavorBuilder.name("my_flavor");
- NodeFlavorTuning tuning = new NodeFlavorTuning(new Flavor(new FlavorsConfig.Flavor(flavorBuilder)));
- ProtonConfig.Builder protonBuilder = new ProtonConfig.Builder();
- tuning.getConfig(protonBuilder);
- return new ProtonConfig(protonBuilder);
+ return getConfig(flavorBuilder, new ProtonConfig.Builder());
}
private static ProtonConfig getConfig(FlavorsConfig.Flavor.Builder flavorBuilder, ProtonConfig.Builder protonBuilder) {
+ return getConfig(flavorBuilder, protonBuilder, 1,1);
+ }
+ private static ProtonConfig getConfig(FlavorsConfig.Flavor.Builder flavorBuilder, ProtonConfig.Builder protonBuilder, int redundancy, int searchableCopies) {
flavorBuilder.name("my_flavor");
- NodeFlavorTuning tuning = new NodeFlavorTuning(new Flavor(new FlavorsConfig.Flavor(flavorBuilder)));
+ NodeFlavorTuning tuning = new NodeFlavorTuning(new Flavor(new FlavorsConfig.Flavor(flavorBuilder)), redundancy, searchableCopies);
tuning.getConfig(protonBuilder);
return new ProtonConfig(protonBuilder);
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/search/test/DocumentDatabaseTestCase.java b/config-model/src/test/java/com/yahoo/vespa/model/search/test/DocumentDatabaseTestCase.java
index c43787b37e0..b2c7da4b851 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/search/test/DocumentDatabaseTestCase.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/search/test/DocumentDatabaseTestCase.java
@@ -7,7 +7,6 @@ import com.yahoo.vespa.config.search.core.ProtonConfig;
import com.yahoo.vespa.config.search.RankProfilesConfig;
import com.yahoo.prelude.fastsearch.DocumentdbInfoConfig;
import com.yahoo.search.config.IndexInfoConfig;
-import com.yahoo.searchdefinition.parser.ParseException;
import com.yahoo.vespa.config.search.AttributesConfig;
import com.yahoo.vespa.configdefinition.IlscriptsConfig;
import com.yahoo.vespa.model.VespaModel;
@@ -17,8 +16,7 @@ import com.yahoo.vespa.model.search.IndexedSearchCluster;
import com.yahoo.vespa.model.test.utils.ApplicationPackageUtils;
import com.yahoo.vespa.model.test.utils.VespaModelCreatorWithMockPkg;
import org.junit.Test;
-import org.xml.sax.SAXException;
-import java.io.IOException;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Arrays;
@@ -103,7 +101,7 @@ public class DocumentDatabaseTestCase {
assertEquals(type1Id, proton.documentdb(0).configid());
}
@Test
- public void requireThatWeCanHaveOneSDForIndexedMode() throws IOException, SAXException, ParseException {
+ public void requireThatWeCanHaveOneSDForIndexedMode() {
assertSingleSD("index");
}
@@ -118,13 +116,13 @@ public class DocumentDatabaseTestCase {
@Test
public void requireThatConcurrencyIsReflectedCorrectlyForDefault() {
- verifyConcurrency("index", "", 0.25, 0.25);
- verifyConcurrency("streaming", "", 0.5, 0.0);
- verifyConcurrency("store-only", "", 0.5, 0.0);
+ verifyConcurrency("index", "", 0.30, 0.30);
+ verifyConcurrency("streaming", "", 0.6, 0.0);
+ verifyConcurrency("store-only", "", 0.6, 0.0);
}
@Test
public void requireThatMixedModeConcurrencyIsReflectedCorrectlyForDefault() {
- verifyConcurrency(Arrays.asList(DocType.create("a", "index"), DocType.create("b", "streaming")), "", 0.5, Arrays.asList(0.25, 0.0));
+ verifyConcurrency(Arrays.asList(DocType.create("a", "index"), DocType.create("b", "streaming")), "", 0.6, Arrays.asList(0.30, 0.0));
}
@Test
public void requireThatMixedModeConcurrencyIsReflected() {
@@ -213,7 +211,7 @@ public class DocumentDatabaseTestCase {
}
@Test
- public void requireThatWeCanHaveMultipleSearchDefinitions() throws IOException, SAXException, ParseException {
+ public void requireThatWeCanHaveMultipleSearchDefinitions() {
final List<String> sds = Arrays.asList("type1", "type2", "type3");
VespaModel model = new VespaModelCreatorWithMockPkg(vespaHosts, createVespaServices(sds, "index"),
ApplicationPackageUtils.generateSearchDefinitions(sds)).create();
@@ -263,7 +261,7 @@ public class DocumentDatabaseTestCase {
}
@Test
- public void requireThatRelevantConfigIsAvailableForClusterSearcher() throws ParseException, IOException, SAXException {
+ public void requireThatRelevantConfigIsAvailableForClusterSearcher() {
final List<String> sds = Arrays.asList("type1", "type2");
VespaModel model = new VespaModelCreatorWithMockPkg(vespaHosts, createVespaServices(sds, "index"),
ApplicationPackageUtils.generateSearchDefinitions(sds)).create();
@@ -336,7 +334,7 @@ public class DocumentDatabaseTestCase {
}
@Test
- public void requireThatDocumentDBConfigIsAvailableForStreaming() throws ParseException, IOException, SAXException {
+ public void requireThatDocumentDBConfigIsAvailableForStreaming() {
assertDocumentDBConfigAvailableForStreaming("streaming");
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/search/test/SearchClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/search/test/SearchClusterTest.java
index e39e0147dbf..007dc5d7266 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/search/test/SearchClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/search/test/SearchClusterTest.java
@@ -1,8 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.search.test;
-import com.yahoo.config.application.api.ApplicationPackage;
-import com.yahoo.config.model.test.MockApplicationPackage;
import com.yahoo.container.QrSearchersConfig;
import com.yahoo.document.DataType;
import com.yahoo.search.config.ClusterConfig;
@@ -19,9 +17,6 @@ import com.yahoo.vespa.model.container.ContainerCluster;
import com.yahoo.vespa.model.test.utils.ApplicationPackageUtils;
import com.yahoo.vespa.model.test.utils.VespaModelCreatorWithMockPkg;
import org.junit.Test;
-import org.xml.sax.SAXException;
-
-import java.io.IOException;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
@@ -49,7 +44,7 @@ public class SearchClusterTest {
"</hosts>";
@Test
- public void testSdConfigLogical() throws IOException, SAXException {
+ public void testSdConfigLogical() {
// sd1
SDDocumentType sdt1=new SDDocumentType("s1");
Search search1 = new Search("s1", null);
@@ -75,7 +70,7 @@ public class SearchClusterTest {
}
@Test
- public void search_model_is_connected_to_container_clusters_two_content_clusters() throws Exception {
+ public void search_model_is_connected_to_container_clusters_two_content_clusters() {
String services = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
"<services version=\"1.0\">" +
" <admin version='2.0'>" +
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/storage/test/StorageModelTestCase.java b/config-model/src/test/java/com/yahoo/vespa/model/storage/test/StorageModelTestCase.java
index 19d46ea8f11..e4dd8aee607 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/storage/test/StorageModelTestCase.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/storage/test/StorageModelTestCase.java
@@ -22,7 +22,7 @@ import static org.junit.Assert.assertThat;
public class StorageModelTestCase {
@Test(expected=RuntimeException.class)
- public void testTwoClustersSameName() throws Exception {
+ public void testTwoClustersSameName() {
createModel("src/test/cfg/storage/twoclusterssamename");
}
@@ -31,7 +31,7 @@ public class StorageModelTestCase {
}
@Test
- public void testIndexGreaterThanNumNodes() throws Exception {
+ public void testIndexGreaterThanNumNodes() {
VespaModel vespaModel = createModel("src/test/cfg/storage/app_index_higher_than_num_nodes");
// Test fleet controller config
@@ -43,7 +43,7 @@ public class StorageModelTestCase {
}
@Test
- public void testMetricsSnapshotIntervalYAMAS() throws Exception {
+ public void testMetricsSnapshotIntervalYAMAS() {
VespaModel vespaModel = createModel("src/test/cfg/storage/clustercontroller_advanced");
ContentCluster contentCluster = vespaModel.getContentClusters().values().iterator().next();
assertNotNull(contentCluster);
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/test/ModelAmendingTestCase.java b/config-model/src/test/java/com/yahoo/vespa/model/test/ModelAmendingTestCase.java
index 1ce584c2910..a8e946bcea1 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/test/ModelAmendingTestCase.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/test/ModelAmendingTestCase.java
@@ -20,9 +20,7 @@ import com.yahoo.vespa.model.container.xml.ContainerModelBuilder;
import com.yahoo.vespa.model.content.Content;
import org.junit.Test;
import org.w3c.dom.Element;
-import org.xml.sax.SAXException;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -42,7 +40,7 @@ import static org.junit.Assert.assertNotNull;
public class ModelAmendingTestCase {
@Test
- public void testModelAmending() throws IOException, SAXException {
+ public void testModelAmending() {
ConfigModelRegistry amendingModelRepo = MapConfigModelRegistry.createFromList(new AdminModelAmenderBuilder(),
new ContainerModelAmenderBuilder(),
new ContentModelAmenderBuilder());
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/test/ParentService.java b/config-model/src/test/java/com/yahoo/vespa/model/test/ParentService.java
index c7559c68592..b7887262ed5 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/test/ParentService.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/test/ParentService.java
@@ -13,8 +13,6 @@ import org.w3c.dom.NodeList;
*/
public class ParentService extends AbstractService implements com.yahoo.test.StandardConfig.Producer {
- public int childCnt = 0;
-
/**
* Creates a new ParentService instance
*
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/test/PortsMetaTestCase.java b/config-model/src/test/java/com/yahoo/vespa/model/test/PortsMetaTestCase.java
index 63c15bf2c13..7736bea3078 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/test/PortsMetaTestCase.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/test/PortsMetaTestCase.java
@@ -17,7 +17,7 @@ import static org.junit.Assert.assertTrue;
public class PortsMetaTestCase {
@Test
- public void testRegister() throws Exception {
+ public void testRegister() {
PortsMeta pm = new PortsMeta();
pm.on(0).tag("foo");
pm.on(1).tag("bar");
@@ -31,7 +31,7 @@ public class PortsMetaTestCase {
}
@Test
- public void testAdminStatusApi() throws Exception {
+ public void testAdminStatusApi() {
PortsMeta pm = new PortsMeta()
.on(0).tag("rpc").tag("nc").tag("admin").tag("status")
.on(1).tag("rpc").tag("rtx").tag("admin").tag("status")
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/test/VespaModelTestCase.java b/config-model/src/test/java/com/yahoo/vespa/model/test/VespaModelTestCase.java
index 3fa013d0089..ac81c1a7b7e 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/test/VespaModelTestCase.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/test/VespaModelTestCase.java
@@ -28,7 +28,7 @@ import com.yahoo.vespa.model.ConfigProducer;
import com.yahoo.vespa.model.VespaModel;
import com.yahoo.vespa.model.admin.Admin;
import com.yahoo.vespa.model.application.validation.Validation;
-import com.yahoo.vespa.model.test.utils.CommonVespaModelSetup;
+import com.yahoo.vespa.model.test.utils.ApplicationPackageUtils;
import com.yahoo.vespa.model.test.utils.VespaModelCreatorWithFilePkg;
import com.yahoo.vespa.model.test.utils.VespaModelCreatorWithMockPkg;
import org.junit.Ignore;
@@ -66,13 +66,9 @@ public class VespaModelTestCase {
"</host>" +
"</hosts>";
- public static VespaModel getVespaModel(String configPath) {
- return getVespaModel(configPath, true);
- }
-
- public static VespaModel getVespaModel(String configPath, boolean validateXml) {
+ private static VespaModel getVespaModel(String configPath) {
VespaModelCreatorWithFilePkg creator = new VespaModelCreatorWithFilePkg(configPath);
- return creator.create(validateXml);
+ return creator.create(true);
}
// Debugging
@@ -89,7 +85,7 @@ public class VespaModelTestCase {
// Verify that common config from plugins is delivered from the root node for any configId, using the Builder based API
@Test
- public void testCommonConfig() throws Exception {
+ public void testCommonConfig() {
VespaModel model = getVespaModel(TESTDIR + "app_nohosts/");
LogdConfig.Builder b = new LogdConfig.Builder();
b = (LogdConfig.Builder) model.getConfig(b, "");
@@ -135,7 +131,7 @@ public class VespaModelTestCase {
}
@Test
- public void testHostsOverrides() throws IOException, SAXException {
+ public void testHostsOverrides() {
VespaModel model = new VespaModelCreatorWithMockPkg(
simpleHosts,
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
@@ -171,8 +167,8 @@ public class VespaModelTestCase {
}
@Test
- public void testCreateFromReaders() throws SAXException, IOException {
- VespaModel model = CommonVespaModelSetup.createVespaModelWithMusic(
+ public void testCreateFromReaders() {
+ VespaModel model = new VespaModelCreatorWithMockPkg(
simpleHosts,
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
"<services version=\"1.0\">" +
@@ -195,7 +191,9 @@ public class VespaModelTestCase {
" <document type=\"music\" mode=\"index\"/>" +
" </documents>" +
"</content>" +
- "</services>");
+ "</services>",
+ ApplicationPackageUtils.generateSearchDefinition("music"))
+ .create();
MessagebusConfig.Builder mBusB = new MessagebusConfig.Builder();
model.getConfig(mBusB, "client");
MessagebusConfig mBus = new MessagebusConfig(mBusB);
@@ -230,7 +228,7 @@ public class VespaModelTestCase {
public void testDeployLogger() throws IOException, SAXException {
final String services = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
"<services version=\"1.0\">" +
- "<config name=\"unknsownfoo\">" +
+ "<config name=\"bar.unknsownfoo\">" +
"<logserver><host>foo</host></logserver>" +
"</config>" +
"<admin version=\"2.0\">" +
@@ -253,11 +251,12 @@ public class VespaModelTestCase {
@Test
public void testNoAdmin() {
- VespaModel model = CommonVespaModelSetup.createVespaModelWithMusic(
+ VespaModel model = new VespaModelCreatorWithMockPkg(
simpleHosts,
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
"<services version=\"1.0\">" +
- "</services>");
+ "</services>")
+ .create();
Admin admin = model.getAdmin();
assertThat(admin.getSlobroks().size(), is(1));
assertThat(admin.getConfigservers().size(), is(1));
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/test/utils/ApplicationPackageUtils.java b/config-model/src/test/java/com/yahoo/vespa/model/test/utils/ApplicationPackageUtils.java
index a58b6a2e372..ee6fc60ba46 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/test/utils/ApplicationPackageUtils.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/test/utils/ApplicationPackageUtils.java
@@ -1,11 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.test.utils;
-import com.yahoo.searchdefinition.Search;
-import com.yahoo.searchdefinition.SearchBuilder;
-import com.yahoo.searchdefinition.parser.ParseException;
-import com.yahoo.vespa.model.search.SearchDefinition;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -48,18 +43,6 @@ public class ApplicationPackageUtils {
"}";
}
- public static Search createSearch(String name, String field1, String field2) throws ParseException {
- SearchBuilder sb = new SearchBuilder();
- sb.importString(generateSearchDefinition(name, field1, field2));
- sb.build();
- return sb.getSearch();
- }
-
- public static SearchDefinition createSearchDefinition(String name, String field1, String field2) throws ParseException {
- com.yahoo.searchdefinition.Search type = ApplicationPackageUtils.createSearch(name, field1, field2);
- return new SearchDefinition(type.getName(), type);
- }
-
public static List<String> generateSearchDefinition(String name) {
return generateSearchDefinitions(name);
}
@@ -68,20 +51,6 @@ public class ApplicationPackageUtils {
return generateSearchDefinitions(Arrays.asList(sdNames));
}
- public static List<SearchDefinition> createSearchDefinition(String name) throws ParseException {
- return createSearchDefinitions(Arrays.asList(name));
- }
-
- public static List<SearchDefinition> createSearchDefinitions(List<String> sdNames) throws ParseException {
- List<SearchDefinition> sds = new ArrayList<>();
- int i = 0;
- for (String sdName : sdNames) {
- sds.add(createSearchDefinition(sdName, "f" + (i + 1), "f" + (i + 2)));
- i = i + 2;
- }
- return sds;
- }
-
public static List<String> generateSearchDefinitions(List<String> sdNames) {
List<String> sds = new ArrayList<>();
int i = 0;
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/test/utils/CommonVespaModelSetup.java b/config-model/src/test/java/com/yahoo/vespa/model/test/utils/CommonVespaModelSetup.java
deleted file mode 100644
index 3791331e40c..00000000000
--- a/config-model/src/test/java/com/yahoo/vespa/model/test/utils/CommonVespaModelSetup.java
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.model.test.utils;
-
-import com.yahoo.config.application.api.ApplicationPackage;
-import com.yahoo.config.model.test.MockApplicationPackage;
-import com.yahoo.vespa.model.VespaModel;
-
-import java.io.File;
-
-/**
- * @author Tony Vaagenes
- */
-//TODO Remove, use VespaModelCreatorWithMockPkg or VespaModelCreatorWithFilePkg instead
-public class CommonVespaModelSetup {
-
- public static VespaModel createVespaModelWithMusic(String path) {
- return createVespaModelWithMusic(new File(path));
- }
-
- public static VespaModel createVespaModelWithMusic(File dir) {
- VespaModelCreatorWithFilePkg modelCreator = new VespaModelCreatorWithFilePkg(dir);
- return modelCreator.create();
- }
-
- public static VespaModel createVespaModelWithMusic(String hosts, String services) {
- ApplicationPackage app = new MockApplicationPackage.Builder()
- .withHosts(hosts)
- .withServices(services)
- .withSearchDefinition(MockApplicationPackage.MUSIC_SEARCHDEFINITION)
- .build();
- VespaModelCreatorWithMockPkg modelCreator = new VespaModelCreatorWithMockPkg(app);
- return modelCreator.create();
- }
-}
diff --git a/config-provisioning/abi-spec.json b/config-provisioning/abi-spec.json
index 64114389751..33c02811318 100644
--- a/config-provisioning/abi-spec.json
+++ b/config-provisioning/abi-spec.json
@@ -737,25 +737,6 @@
],
"fields": []
},
- "com.yahoo.config.provision.RotationName": {
- "superClass": "java.lang.Object",
- "interfaces": [
- "java.lang.Comparable"
- ],
- "attributes": [
- "public"
- ],
- "methods": [
- "public java.lang.String value()",
- "public boolean equals(java.lang.Object)",
- "public int hashCode()",
- "public int compareTo(com.yahoo.config.provision.RotationName)",
- "public java.lang.String toString()",
- "public static com.yahoo.config.provision.RotationName from(java.lang.String)",
- "public bridge synthetic int compareTo(java.lang.Object)"
- ],
- "fields": []
- },
"com.yahoo.config.provision.SystemName": {
"superClass": "java.lang.Enum",
"interfaces": [],
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/RotationName.java b/config-provisioning/src/main/java/com/yahoo/config/provision/RotationName.java
deleted file mode 100644
index 5d9ac3699b3..00000000000
--- a/config-provisioning/src/main/java/com/yahoo/config/provision/RotationName.java
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.config.provision;
-
-import java.util.Objects;
-
-/**
- * Represents a rotation name for a container cluster. Typically created from the rotation element in services.xml.
- *
- * @author mpolden
- */
-public class RotationName implements Comparable<RotationName> {
-
- private final String name;
-
- private RotationName(String name) {
- this.name = requireNonBlank(name, "name must be non-empty");
- }
-
- public String value() {
- return name;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- RotationName that = (RotationName) o;
- return name.equals(that.name);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(name);
- }
-
- @Override
- public int compareTo(RotationName o) {
- return name.compareTo(o.name);
- }
-
- @Override
- public String toString() {
- return "rotation '" + name + "'";
- }
-
- public static RotationName from(String name) {
- return new RotationName(name);
- }
-
- private static String requireNonBlank(String s, String message) {
- if (s == null || s.isBlank()) {
- throw new IllegalArgumentException(message);
- }
- return s;
- }
-
-}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerServiceException.java b/config-provisioning/src/main/java/com/yahoo/config/provision/exception/LoadBalancerServiceException.java
index e5ab519ab94..41aa241621d 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerServiceException.java
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/exception/LoadBalancerServiceException.java
@@ -1,10 +1,10 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.provision.lb;
+package com.yahoo.config.provision.exception;
import com.yahoo.config.provision.TransientException;
/**
- * Transient exception thrown on behalf of a {@link LoadBalancerService}.
+ * Transient exception thrown on behalf of a load balancer service
*
* @author mpolden
*/
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/exception/package-info.java b/config-provisioning/src/main/java/com/yahoo/config/provision/exception/package-info.java
new file mode 100644
index 00000000000..5730f3fdb6b
--- /dev/null
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/exception/package-info.java
@@ -0,0 +1,5 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+@ExportPackage
+package com.yahoo.config.provision.exception;
+
+import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ConfigProxyRpcServer.java b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ConfigProxyRpcServer.java
index 5ffc7293742..e815fd9a5f9 100644
--- a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ConfigProxyRpcServer.java
+++ b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ConfigProxyRpcServer.java
@@ -133,7 +133,6 @@ public class ConfigProxyRpcServer implements Runnable, TargetWatcher, RpcServer
dispatchRpcRequest(req, () -> {
JRTServerConfigRequest request = JRTServerConfigRequestV3.createFromRequest(req);
if (isProtocolVersionSupported(request)) {
- proxyServer.getStatistics().incRpcRequests();
req.target().addWatcher(this);
getConfigImpl(request);
return;
diff --git a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ConfigProxyStatistics.java b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ConfigProxyStatistics.java
deleted file mode 100644
index 314a4b0cb11..00000000000
--- a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ConfigProxyStatistics.java
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.config.proxy;
-
-import com.yahoo.log.LogLevel;
-import com.yahoo.log.event.Event;
-
-/**
- * Statistics/metrics for config proxy.
- * //TODO Use metrics framework
- *
- * @author hmusum
- */
-class ConfigProxyStatistics implements Runnable {
- static final long defaultEventInterval = 5 * 60; // in seconds
-
- private final long eventInterval; // in seconds
- private boolean stopped;
- private long lastRun = System.currentTimeMillis();
-
- /* Number of RPC getConfig requests */
- private long rpcRequests = 0;
- private long processedRequests = 0;
- private long errors = 0;
- private long delayedResponses = 0;
-
- ConfigProxyStatistics() {
- this(defaultEventInterval);
- }
-
- ConfigProxyStatistics(long eventInterval) {
- this.eventInterval = eventInterval;
- }
-
- // Send events every eventInterval seconds
- public void run() {
- while (true) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- ProxyServer.log.log(LogLevel.WARNING, e.getMessage());
- }
- if (stopped) {
- return;
- }
- ProxyServer.log.log(LogLevel.SPAM, "Running ConfigProxyStatistics");
- // Only send events every eventInterval seconds
- if ((System.currentTimeMillis() - lastRun) > eventInterval * 1000) {
- lastRun = System.currentTimeMillis();
- sendEvents();
- }
- }
- }
-
- private void sendEvents() {
- Event.count("rpc_requests", rpcRequests());
- Event.count("processed_messages", processedRequests());
- Event.count("errors", errors());
- Event.value("delayed_responses", delayedResponses());
- }
-
- void stop() {
- stopped = true;
- }
-
- Long getEventInterval() {
- return eventInterval;
- }
-
- void incRpcRequests() {
- rpcRequests++;
- }
-
- void incProcessedRequests() {
- processedRequests++;
- }
-
- void incErrorCount() {
- errors++;
- }
-
- long processedRequests() {
- return processedRequests;
- }
-
- long rpcRequests() {
- return rpcRequests;
- }
-
- long errors() {
- return errors;
- }
-
- long delayedResponses() {
- return delayedResponses;
- }
-
- void delayedResponses(long count) {
- delayedResponses = count;
- }
-
- void decDelayedResponses() {
- delayedResponses--;
- }
-}
diff --git a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/Mode.java b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/Mode.java
index da6e7d975cb..fdc2b886701 100644
--- a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/Mode.java
+++ b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/Mode.java
@@ -31,9 +31,6 @@ class Mode {
Mode(String modeString) {
switch (modeString.toLowerCase()) {
- case "" :
- mode = ModeName.DEFAULT;
- break;
case "default" :
mode = ModeName.DEFAULT;
break;
@@ -41,7 +38,7 @@ class Mode {
mode = ModeName.MEMORYCACHE;
break;
default:
- throw new IllegalArgumentException("Unrecognized mode'" + modeString + "' supplied");
+ throw new IllegalArgumentException("Unrecognized mode '" + modeString + "' supplied");
}
}
diff --git a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ProxyServer.java b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ProxyServer.java
index 4bf3bd5a786..f4f2308f261 100644
--- a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ProxyServer.java
+++ b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ProxyServer.java
@@ -28,7 +28,7 @@ import static java.util.concurrent.TimeUnit.SECONDS;
/**
* A proxy server that handles RPC config requests. The proxy can run in two modes:
* 'default' and 'memorycache', where the last one will not get config from an upstream
- * config source, but will serve config only from memory cache.
+ * config source, but will serve config from memory cache only.
*
* @author hmusum
*/
@@ -38,7 +38,7 @@ public class ProxyServer implements Runnable {
private static final int JRT_TRANSPORT_THREADS = 4;
static final String DEFAULT_PROXY_CONFIG_SOURCES = "tcp/localhost:19070";
- final static Logger log = Logger.getLogger(ProxyServer.class.getName());
+ private final static Logger log = Logger.getLogger(ProxyServer.class.getName());
private final AtomicBoolean signalCaught = new AtomicBoolean(false);
// Scheduled executor that periodically checks for requests that have timed out and response should be returned to clients
@@ -52,7 +52,6 @@ public class ProxyServer implements Runnable {
private volatile ConfigSourceClient configClient;
- private final ConfigProxyStatistics statistics;
private final TimingValues timingValues;
private final MemoryCache memoryCache;
private static final double timingValuesRatio = 0.8;
@@ -72,33 +71,28 @@ public class ProxyServer implements Runnable {
defaultTimingValues = tv;
}
- private ProxyServer(Spec spec, DelayedResponses delayedResponses, ConfigSourceSet source,
- ConfigProxyStatistics statistics, TimingValues timingValues,
- boolean delayedResponseHandling, MemoryCache memoryCache,
- ConfigSourceClient configClient) {
- this.delayedResponses = delayedResponses;
+ private ProxyServer(Spec spec, ConfigSourceSet source, TimingValues timingValues,
+ boolean delayedResponseHandling, MemoryCache memoryCache, ConfigSourceClient configClient) {
+ this.delayedResponses = new DelayedResponses();
this.configSource = source;
log.log(LogLevel.DEBUG, "Using config source '" + source);
- this.statistics = statistics;
this.timingValues = timingValues;
this.delayedResponseHandling = delayedResponseHandling;
this.memoryCache = memoryCache;
this.rpcServer = createRpcServer(spec);
- this.configClient = createClient(rpcServer, statistics, delayedResponses, source, timingValues, memoryCache, configClient);
+ this.configClient = createClient(rpcServer, delayedResponses, source, timingValues, memoryCache, configClient);
this.fileDistributionAndUrlDownload = new FileDistributionAndUrlDownload(supervisor, source);
}
static ProxyServer createTestServer(ConfigSourceSet source) {
- return createTestServer(source, null, new MemoryCache(), new ConfigProxyStatistics());
+ return createTestServer(source, null, new MemoryCache());
}
static ProxyServer createTestServer(ConfigSourceSet source,
ConfigSourceClient configSourceClient,
- MemoryCache memoryCache,
- ConfigProxyStatistics statistics) {
+ MemoryCache memoryCache) {
final boolean delayedResponseHandling = false;
- return new ProxyServer(null, new DelayedResponses(),
- source, statistics, defaultTimingValues(), delayedResponseHandling,
+ return new ProxyServer(null, source, defaultTimingValues(), delayedResponseHandling,
memoryCache, configSourceClient);
}
@@ -120,7 +114,6 @@ public class ProxyServer implements Runnable {
}
RawConfig resolveConfig(JRTServerConfigRequest req) {
- statistics.incProcessedRequests();
// Calling getConfig() will either return with an answer immediately or
// create a background thread that retrieves config from the server and
// calls updateSubscribers when new config is returned from the config source.
@@ -155,12 +148,11 @@ public class ProxyServer implements Runnable {
}
}
- private ConfigSourceClient createClient(RpcServer rpcServer, ConfigProxyStatistics statistics,
- DelayedResponses delayedResponses,
+ private ConfigSourceClient createClient(RpcServer rpcServer, DelayedResponses delayedResponses,
ConfigSourceSet source, TimingValues timingValues,
MemoryCache memoryCache, ConfigSourceClient client) {
return (client == null)
- ? new RpcConfigSourceClient(rpcServer, source, statistics, memoryCache, timingValues, delayedResponses)
+ ? new RpcConfigSourceClient(rpcServer, source, memoryCache, timingValues, delayedResponses)
: client;
}
@@ -169,7 +161,7 @@ public class ProxyServer implements Runnable {
}
private RpcConfigSourceClient createRpcClient() {
- return new RpcConfigSourceClient(rpcServer, configSource, statistics, memoryCache, timingValues, delayedResponses);
+ return new RpcConfigSourceClient(rpcServer, configSource, memoryCache, timingValues, delayedResponses);
}
private void setupSignalHandler() {
@@ -202,15 +194,9 @@ public class ProxyServer implements Runnable {
port = Integer.parseInt(args[0]);
}
Event.started("configproxy");
- ConfigProxyStatistics statistics = new ConfigProxyStatistics(properties.eventInterval);
- Thread t = new Thread(statistics);
- t.setName("Metrics generator");
- t.setDaemon(true);
- t.start();
ConfigSourceSet configSources = new ConfigSourceSet(properties.configSources);
- DelayedResponses delayedResponses = new DelayedResponses();
- ProxyServer proxyServer = new ProxyServer(new Spec(null, port), delayedResponses, configSources, statistics,
+ ProxyServer proxyServer = new ProxyServer(new Spec(null, port), configSources,
defaultTimingValues(), true, new MemoryCache(), null);
// catch termination and interrupt signal
proxyServer.setupSignalHandler();
@@ -221,18 +207,14 @@ public class ProxyServer implements Runnable {
}
static Properties getSystemProperties() {
- // Read system properties
- long eventInterval = Long.getLong("eventinterval", ConfigProxyStatistics.defaultEventInterval);
final String[] inputConfigSources = System.getProperty("proxyconfigsources", DEFAULT_PROXY_CONFIG_SOURCES).split(",");
- return new Properties(eventInterval, inputConfigSources);
+ return new Properties(inputConfigSources);
}
static class Properties {
- final long eventInterval;
final String[] configSources;
- Properties(long eventInterval, String[] configSources) {
- this.eventInterval = eventInterval;
+ Properties(String[] configSources) {
this.configSources = configSources;
}
}
@@ -245,10 +227,6 @@ public class ProxyServer implements Runnable {
return timingValues;
}
- ConfigProxyStatistics getStatistics() {
- return statistics;
- }
-
// Cancels all config instances and flushes the cache. When this method returns,
// the cache will not be updated again before someone calls getConfig().
private synchronized void flush() {
@@ -261,9 +239,6 @@ public class ProxyServer implements Runnable {
if (rpcServer != null) rpcServer.shutdown();
if (delayedResponseScheduler != null) delayedResponseScheduler.cancel(true);
flush();
- if (statistics != null) {
- statistics.stop();
- }
fileDistributionAndUrlDownload.close();
}
diff --git a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/RpcConfigSourceClient.java b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/RpcConfigSourceClient.java
index c9f43ac48e2..d809a3c97ed 100644
--- a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/RpcConfigSourceClient.java
+++ b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/RpcConfigSourceClient.java
@@ -38,7 +38,6 @@ class RpcConfigSourceClient implements ConfigSourceClient {
private final HashMap<ConfigCacheKey, Subscriber> activeSubscribers = new HashMap<>();
private final Object activeSubscribersLock = new Object();
private final MemoryCache memoryCache;
- private final ConfigProxyStatistics statistics;
private final DelayedResponses delayedResponses;
private final TimingValues timingValues;
@@ -48,13 +47,11 @@ class RpcConfigSourceClient implements ConfigSourceClient {
RpcConfigSourceClient(RpcServer rpcServer,
ConfigSourceSet configSourceSet,
- ConfigProxyStatistics statistics,
MemoryCache memoryCache,
TimingValues timingValues,
DelayedResponses delayedResponses) {
this.rpcServer = rpcServer;
this.configSourceSet = configSourceSet;
- this.statistics = statistics;
this.memoryCache = memoryCache;
this.delayedResponses = delayedResponses;
this.timingValues = timingValues;
@@ -122,7 +119,6 @@ class RpcConfigSourceClient implements ConfigSourceClient {
// happens at the same time
DelayedResponse delayedResponse = new DelayedResponse(request);
delayedResponses.add(delayedResponse);
- statistics.delayedResponses(delayedResponses.size());
final ConfigCacheKey configCacheKey = new ConfigCacheKey(input.getKey(), input.getDefMd5());
RawConfig cachedConfig = memoryCache.get(configCacheKey);
@@ -139,7 +135,6 @@ class RpcConfigSourceClient implements ConfigSourceClient {
// unless another thread already did it
ret = cachedConfig;
}
- statistics.decDelayedResponses();
}
if (!cachedConfig.isError() && cachedConfig.getGeneration() > 0) {
needToGetConfig = false;
@@ -220,7 +215,6 @@ class RpcConfigSourceClient implements ConfigSourceClient {
*/
public void updateSubscribers(RawConfig config) {
log.log(LogLevel.DEBUG, () -> "Config updated for " + config.getKey() + "," + config.getGeneration());
- if (config.isError()) { statistics.incErrorCount(); }
DelayQueue<DelayedResponse> responseDelayQueue = delayedResponses.responses();
log.log(LogLevel.SPAM, () -> "Delayed response queue: " + responseDelayQueue);
if (responseDelayQueue.size() == 0) {
diff --git a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ModeTest.java b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ModeTest.java
deleted file mode 100644
index 1a6bbd11b59..00000000000
--- a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ModeTest.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.config.proxy;
-
-import org.junit.Test;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-/**
- * @author hmusum
- */
-public class ModeTest {
-
- @Test
- public void basic() {
- Mode mode = new Mode();
- assertModeName(Mode.ModeName.DEFAULT, mode);
- assertTrue(mode.isDefault());
-
- mode = new Mode("");
- assertModeName(Mode.ModeName.DEFAULT, mode);
- assertTrue(mode.isDefault());
-
- mode = new Mode(Mode.ModeName.DEFAULT.name());
- assertModeName(Mode.ModeName.DEFAULT, mode);
- assertTrue(mode.isDefault());
-
- mode = new Mode(Mode.ModeName.MEMORYCACHE.name());
- assertModeName(Mode.ModeName.MEMORYCACHE, mode);
- assertTrue(mode.isMemoryCache());
-
- assertTrue(new Mode(Mode.ModeName.DEFAULT.name()).requiresConfigSource());
-
- assertFalse(new Mode(Mode.ModeName.MEMORYCACHE.name()).requiresConfigSource());
-
- Set<String> modes = new HashSet<>();
- for (Mode.ModeName modeName : Mode.ModeName.values()) {
- modes.add(modeName.name().toLowerCase());
- }
-
- assertThat(Mode.modes(), is(modes));
-
- assertFalse(Mode.validModeName("foo"));
-
- assertThat(mode.toString(), is(Mode.ModeName.MEMORYCACHE.name().toLowerCase()));
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void failWhenInvalidMode() {
- new Mode("invalid_mode");
- }
-
- private void assertModeName(Mode.ModeName expected, Mode actual) {
- assertThat(actual.name(), is(expected.name().toLowerCase()));
- }
-}
diff --git a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ProxyServerTest.java b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ProxyServerTest.java
index 9d6d0ca2a39..1c64631d205 100644
--- a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ProxyServerTest.java
+++ b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ProxyServerTest.java
@@ -23,7 +23,6 @@ public class ProxyServerTest {
private final MemoryCache memoryCache = new MemoryCache();
private final MockConfigSource source = new MockConfigSource();
private MockConfigSourceClient client = new MockConfigSourceClient(source, memoryCache);
- private final ConfigProxyStatistics statistics = new ConfigProxyStatistics();
private ProxyServer proxy;
static final RawConfig fooConfig = ConfigTester.fooConfig;
@@ -42,7 +41,7 @@ public class ProxyServerTest {
source.clear();
source.put(fooConfig.getKey(), createConfigWithNextConfigGeneration(fooConfig, 0));
source.put(errorConfigKey, createConfigWithNextConfigGeneration(fooConfig, ErrorCode.UNKNOWN_DEFINITION));
- proxy = ProxyServer.createTestServer(source, client, memoryCache, statistics);
+ proxy = ProxyServer.createTestServer(source, client, memoryCache);
}
@After
@@ -64,27 +63,6 @@ public class ProxyServerTest {
assertThat(res.getPayload().toString(), is(ConfigTester.fooPayload.toString()));
assertEquals(1, memoryCache.size());
assertThat(memoryCache.get(new ConfigCacheKey(fooConfig.getKey(), fooConfig.getDefMd5())), is(res));
-
-
- assertEquals(1, statistics.processedRequests());
- assertEquals(0, statistics.rpcRequests());
- assertEquals(0, statistics.errors());
- assertEquals(0, statistics.delayedResponses());
-
- statistics.incProcessedRequests();
- statistics.incRpcRequests();
- statistics.incErrorCount();
- statistics.delayedResponses(1);
-
- assertEquals(2, statistics.processedRequests());
- assertEquals(1, statistics.rpcRequests());
- assertEquals(1, statistics.errors());
- assertEquals(1, statistics.delayedResponses());
-
- statistics.decDelayedResponses();
- assertEquals(0, statistics.delayedResponses());
-
- assertEquals(ConfigProxyStatistics.defaultEventInterval, statistics.getEventInterval().longValue());
}
/**
@@ -100,6 +78,14 @@ public class ProxyServerTest {
assertThat(proxy.getMode().name(), is(mode));
}
+ // Try setting an invalid mode
+ try {
+ proxy.setMode("invalid");
+ assert (false);
+ } catch (IllegalArgumentException e) {
+ assertEquals("Unrecognized mode 'invalid' supplied", e.getMessage());
+ }
+
// Also switch to DEFAULT mode, as that is not covered above
proxy.setMode("default");
assertTrue(proxy.getMode().isDefault());
@@ -228,7 +214,6 @@ public class ProxyServerTest {
@Test
public void testReadingSystemProperties() {
ProxyServer.Properties properties = ProxyServer.getSystemProperties();
- assertThat(properties.eventInterval, is(ConfigProxyStatistics.defaultEventInterval));
assertThat(properties.configSources.length, is(1));
assertThat(properties.configSources[0], is(ProxyServer.DEFAULT_PROXY_CONFIG_SOURCES));
}
diff --git a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/RpcConfigSourceClientTest.java b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/RpcConfigSourceClientTest.java
index 7f762955b92..35f1dd8fcd8 100644
--- a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/RpcConfigSourceClientTest.java
+++ b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/RpcConfigSourceClientTest.java
@@ -18,7 +18,6 @@ import static org.junit.Assert.assertEquals;
public class RpcConfigSourceClientTest {
private MockRpcServer rpcServer;
- private ConfigProxyStatistics statistics;
private DelayedResponses delayedResponses;
private RpcConfigSourceClient rpcConfigSourceClient;
@@ -29,10 +28,9 @@ public class RpcConfigSourceClientTest {
@Before
public void setup() {
rpcServer = new MockRpcServer();
- statistics = new ConfigProxyStatistics();
delayedResponses = new DelayedResponses();
rpcConfigSourceClient =
- new RpcConfigSourceClient(rpcServer, new MockConfigSource(), statistics,
+ new RpcConfigSourceClient(rpcServer, new MockConfigSource(),
new MemoryCache(), ProxyServer.defaultTimingValues(), delayedResponses);
}
@@ -57,7 +55,6 @@ public class RpcConfigSourceClientTest {
public void errorResponse() {
configUpdatedSendResponse(ProxyServerTest.errorConfig);
assertSentResponses(0);
- assertEquals(1, statistics.errors());
}
@Test
diff --git a/config/pom.xml b/config/pom.xml
index 40ad965a410..1be07b9b1b2 100755
--- a/config/pom.xml
+++ b/config/pom.xml
@@ -13,11 +13,14 @@
<packaging>container-plugin</packaging>
<version>7-SNAPSHOT</version>
<dependencies>
+ <!-- provided scope -->
<dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <scope>provided</scope>
</dependency>
+
+ <!-- compile scope -->
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>annotations</artifactId>
@@ -58,14 +61,30 @@
<version>${project.version}</version>
</dependency>
<dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest-core</artifactId>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>http-utils</artifactId>
+ <version>${project.version}</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>net.jpountz.lz4</groupId>
+ <artifactId>lz4</artifactId>
+ </dependency>
+
+ <!-- test scope -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- <scope>provided</scope>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-core</artifactId>
+ <scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
@@ -78,14 +97,6 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-core</artifactId>
- </dependency>
- <dependency>
- <groupId>net.jpountz.lz4</groupId>
- <artifactId>lz4</artifactId>
- </dependency>
</dependencies>
<profiles>
<profile>
diff --git a/config/src/apps/vespa-configproxy-cmd/proxycmd.h b/config/src/apps/vespa-configproxy-cmd/proxycmd.h
index 49be0fff885..ca5b5e6d119 100644
--- a/config/src/apps/vespa-configproxy-cmd/proxycmd.h
+++ b/config/src/apps/vespa-configproxy-cmd/proxycmd.h
@@ -4,7 +4,6 @@
#include <vespa/vespalib/stllike/string.h>
#include <vector>
-class FRT_Supervisor;
class FRT_Target;
class FRT_RPCRequest;
class FRT_Values;
@@ -26,7 +25,6 @@ class ProxyCmd
{
private:
std::unique_ptr<fnet::frt::StandaloneFRT> _server;
- FRT_Supervisor *_supervisor;
FRT_Target *_target;
FRT_RPCRequest *_req;
Flags _flags;
diff --git a/config/src/main/java/com/yahoo/config/subscription/CfgConfigPayloadBuilder.java b/config/src/main/java/com/yahoo/config/subscription/CfgConfigPayloadBuilder.java
index 90532344a58..80c55c7b558 100644
--- a/config/src/main/java/com/yahoo/config/subscription/CfgConfigPayloadBuilder.java
+++ b/config/src/main/java/com/yahoo/config/subscription/CfgConfigPayloadBuilder.java
@@ -14,7 +14,6 @@ import java.util.*;
* Deserializes config payload (cfg format) to a ConfigPayload.
*
* @author hmusum
- * @since 5.1.6
*/
public class CfgConfigPayloadBuilder {
private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(CfgConfigPayloadBuilder.class.getName());
@@ -29,6 +28,7 @@ public class CfgConfigPayloadBuilder {
return ConfigPayload.fromBuilder(deserializeToBuilder(lines));
}
+ @SuppressWarnings("WeakerAccess")
public ConfigPayloadBuilder deserializeToBuilder(List<String> lines) {
int lineNum = 1;
ConfigPayloadBuilder payloadBuilder = new ConfigPayloadBuilder();
diff --git a/config/src/main/java/com/yahoo/config/subscription/ConfigGetter.java b/config/src/main/java/com/yahoo/config/subscription/ConfigGetter.java
index 204d85015cb..e126b13388a 100755
--- a/config/src/main/java/com/yahoo/config/subscription/ConfigGetter.java
+++ b/config/src/main/java/com/yahoo/config/subscription/ConfigGetter.java
@@ -69,7 +69,7 @@ public class ConfigGetter<T extends ConfigInstance> {
* @return an instance of a config class
*/
public static <T extends ConfigInstance> T getConfig(Class<T> c, String configId) {
- ConfigGetter<T> getter = new ConfigGetter<T>(c);
+ ConfigGetter<T> getter = new ConfigGetter<>(c);
return getter.getConfig(configId);
}
@@ -82,7 +82,7 @@ public class ConfigGetter<T extends ConfigInstance> {
* @return an instance of a config class
*/
public static <T extends ConfigInstance> T getConfig(Class<T> c, String configId, ConfigSource source) {
- ConfigGetter<T> getter = new ConfigGetter<T>(source, c);
+ ConfigGetter<T> getter = new ConfigGetter<>(source, c);
return getter.getConfig(configId);
}
}
diff --git a/config/src/main/java/com/yahoo/config/subscription/ConfigInstanceUtil.java b/config/src/main/java/com/yahoo/config/subscription/ConfigInstanceUtil.java
index cce3ce0a0c5..fb2a3acbfdc 100644
--- a/config/src/main/java/com/yahoo/config/subscription/ConfigInstanceUtil.java
+++ b/config/src/main/java/com/yahoo/config/subscription/ConfigInstanceUtil.java
@@ -43,10 +43,10 @@ public class ConfigInstanceUtil {
ConfigPayload payload) {
T instance;
try {
- ConfigTransformer<?> transformer = new ConfigTransformer<T>(type);
- ConfigBuilder instanceBuilder = transformer.toConfigBuilder(payload);
+ ConfigTransformer<?> transformer = new ConfigTransformer<>(type);
+ ConfigInstance.Builder instanceBuilder = transformer.toConfigBuilder(payload);
Constructor<T> constructor = type.getConstructor(instanceBuilder.getClass());
- instance = constructor.newInstance((ConfigInstance.Builder) instanceBuilder);
+ instance = constructor.newInstance(instanceBuilder);
// Workaround for JDK7, where compilation fails due to fields being
// private and not accessible from T. Reference it as a
diff --git a/config/src/main/java/com/yahoo/config/subscription/ConfigSet.java b/config/src/main/java/com/yahoo/config/subscription/ConfigSet.java
index be4ab8f2116..57f4ad863f2 100644
--- a/config/src/main/java/com/yahoo/config/subscription/ConfigSet.java
+++ b/config/src/main/java/com/yahoo/config/subscription/ConfigSet.java
@@ -11,7 +11,6 @@ import com.yahoo.vespa.config.ConfigKey;
* Config source as a programmatically built set of {@link com.yahoo.config.ConfigInstance}s
*
* @author vegardh
- * @since 5.1
*/
public class ConfigSet implements ConfigSource {
private final Map<ConfigKey<?>, ConfigInstance.Builder> configs = new ConcurrentHashMap<>();
diff --git a/config/src/main/java/com/yahoo/config/subscription/impl/ConfigSetSubscription.java b/config/src/main/java/com/yahoo/config/subscription/impl/ConfigSetSubscription.java
index dcf18597046..aa80cc75ef0 100644
--- a/config/src/main/java/com/yahoo/config/subscription/impl/ConfigSetSubscription.java
+++ b/config/src/main/java/com/yahoo/config/subscription/impl/ConfigSetSubscription.java
@@ -23,7 +23,7 @@ public class ConfigSetSubscription<T extends ConfigInstance> extends ConfigSubsc
super(key, subscriber);
if (!(cset instanceof ConfigSet)) throw new IllegalArgumentException("Source is not a ConfigSet: "+cset);
this.set=(ConfigSet) cset;
- subKey = new ConfigKey<T>(configClass, key.getConfigId());
+ subKey = new ConfigKey<>(configClass, key.getConfigId());
if (!set.contains(subKey)) {
throw new IllegalArgumentException("The given ConfigSet "+set+" does not contain a config for "+subKey);
}
diff --git a/config/src/main/java/com/yahoo/config/subscription/impl/ConfigSubscription.java b/config/src/main/java/com/yahoo/config/subscription/impl/ConfigSubscription.java
index e0d4e6e6390..c02301a0c17 100644
--- a/config/src/main/java/com/yahoo/config/subscription/impl/ConfigSubscription.java
+++ b/config/src/main/java/com/yahoo/config/subscription/impl/ConfigSubscription.java
@@ -57,7 +57,7 @@ public abstract class ConfigSubscription<T extends ConfigInstance> {
this(false, 0L, false, false, null);
}
- private ConfigState<T> createUnchanged() { return new ConfigState<T>(generation, config); }
+ private ConfigState<T> createUnchanged() { return new ConfigState<>(generation, config); }
public boolean isConfigChanged() { return configChanged; }
public boolean isGenerationChanged() { return generationChanged; }
public Long getGeneration() { return generation; }
@@ -93,7 +93,7 @@ public abstract class ConfigSubscription<T extends ConfigInstance> {
this.key = key;
this.configClass = key.getConfigClass();
this.subscriber = subscriber;
- this.config.set(new ConfigState<T>());
+ this.config.set(new ConfigState<>());
}
diff --git a/config/src/main/java/com/yahoo/config/subscription/impl/JRTConfigSubscription.java b/config/src/main/java/com/yahoo/config/subscription/impl/JRTConfigSubscription.java
index 58f720b8cb6..d9366c28b9b 100644
--- a/config/src/main/java/com/yahoo/config/subscription/impl/JRTConfigSubscription.java
+++ b/config/src/main/java/com/yahoo/config/subscription/impl/JRTConfigSubscription.java
@@ -163,8 +163,9 @@ public class JRTConfigSubscription<T extends ConfigInstance> extends ConfigSubsc
@SuppressWarnings("serial")
public void close() {
super.close();
- reqQueue = new LinkedBlockingQueue<JRTClientConfigRequest>() {
- @Override public void put(JRTClientConfigRequest e) throws InterruptedException {
+ reqQueue = new LinkedBlockingQueue<>() {
+ @Override
+ public void put(JRTClientConfigRequest e) throws InterruptedException {
// When closed, throw away all requests that callbacks try to put
}
};
diff --git a/config/src/main/java/com/yahoo/config/subscription/impl/JarConfigSubscription.java b/config/src/main/java/com/yahoo/config/subscription/impl/JarConfigSubscription.java
index 6388b05e0cf..a96499482c5 100644
--- a/config/src/main/java/com/yahoo/config/subscription/impl/JarConfigSubscription.java
+++ b/config/src/main/java/com/yahoo/config/subscription/impl/JarConfigSubscription.java
@@ -4,6 +4,7 @@ package com.yahoo.config.subscription.impl;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
@@ -54,7 +55,7 @@ public class JarConfigSubscription<T extends ConfigInstance> extends ConfigSubsc
if (zipEntry==null) throw new IllegalArgumentException("Config '" + key.getName() + "' not found in '" + jarName + "!/" + path + "'.");
T config = null;
try {
- ConfigPayload payload = new CfgConfigPayloadBuilder().deserialize(Arrays.asList(IOUtils.readAll(new InputStreamReader(jarFile.getInputStream(zipEntry), "UTF-8")).split("\n")));
+ ConfigPayload payload = new CfgConfigPayloadBuilder().deserialize(Arrays.asList(IOUtils.readAll(new InputStreamReader(jarFile.getInputStream(zipEntry), StandardCharsets.UTF_8)).split("\n")));
config = payload.toInstance(configClass, key.getConfigId());
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException(e);
diff --git a/config/src/main/java/com/yahoo/vespa/config/ConfigDefinition.java b/config/src/main/java/com/yahoo/vespa/config/ConfigDefinition.java
index b969220ee05..0f04b4caf08 100644
--- a/config/src/main/java/com/yahoo/vespa/config/ConfigDefinition.java
+++ b/config/src/main/java/com/yahoo/vespa/config/ConfigDefinition.java
@@ -1,54 +1,58 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config;
-import com.yahoo.config.codegen.CNode;
import com.yahoo.yolean.Exceptions;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
import java.util.logging.Logger;
import java.util.regex.Pattern;
/**
* Represents one legal def file, or (internally) one array or inner array definition in a def file.
- * Definitions are comparable based on version.
* @author vegardh
*
*/
-public class ConfigDefinition implements Comparable<ConfigDefinition> {
+public class ConfigDefinition {
public static final Pattern namePattern = Pattern.compile("[a-zA-Z][a-zA-Z0-9-_]*");
- public static final Pattern namespacePattern = Pattern.compile("[a-zA-Z][a-zA-Z0-9-\\._]*");
+ public static final Pattern namespacePattern = Pattern.compile("[a-zA-Z][a-zA-Z0-9-._]*");
public static Logger log = Logger.getLogger(ConfigDefinition.class.getName());
private final String name;
private final String version;
private final String namespace;
- protected ConfigDefinition parent = null;
+ ConfigDefinition parent = null;
// TODO Strings without default are null, could be not OK.
- private Map<String, StringDef> stringDefs = new LinkedHashMap<String, StringDef>();
- private Map<String, BoolDef> boolDefs = new LinkedHashMap<String, BoolDef>();
- private Map<String, IntDef> intDefs = new LinkedHashMap<String, IntDef>();
- private Map<String, LongDef> longDefs = new LinkedHashMap<String, LongDef>();
- private Map<String, DoubleDef> doubleDefs = new LinkedHashMap<String, DoubleDef>();
- private Map<String, EnumDef> enumDefs = new LinkedHashMap<String, EnumDef>();
- private Map<String, RefDef> referenceDefs = new LinkedHashMap<String, RefDef>();
- private Map<String, FileDef> fileDefs = new LinkedHashMap<String, FileDef>();
+ private Map<String, StringDef> stringDefs = new LinkedHashMap<>();
+ private Map<String, BoolDef> boolDefs = new LinkedHashMap<>();
+ private Map<String, IntDef> intDefs = new LinkedHashMap<>();
+ private Map<String, LongDef> longDefs = new LinkedHashMap<>();
+ private Map<String, DoubleDef> doubleDefs = new LinkedHashMap<>();
+ private Map<String, EnumDef> enumDefs = new LinkedHashMap<>();
+ private Map<String, RefDef> referenceDefs = new LinkedHashMap<>();
+ private Map<String, FileDef> fileDefs = new LinkedHashMap<>();
private Map<String, PathDef> pathDefs = new LinkedHashMap<>();
private Map<String, UrlDef> urlDefs = new LinkedHashMap<>();
- private Map<String, StructDef> structDefs = new LinkedHashMap<String, StructDef>();
- private Map<String, InnerArrayDef> innerArrayDefs = new LinkedHashMap<String, InnerArrayDef>();
- private Map<String, ArrayDef> arrayDefs = new LinkedHashMap<String, ArrayDef>();
+ private Map<String, StructDef> structDefs = new LinkedHashMap<>();
+ private Map<String, InnerArrayDef> innerArrayDefs = new LinkedHashMap<>();
+ private Map<String, ArrayDef> arrayDefs = new LinkedHashMap<>();
private Map<String, LeafMapDef> leafMapDefs = new LinkedHashMap<>();
private Map<String, StructMapDef> structMapDefs = new LinkedHashMap<>();
- public static final Integer INT_MIN = -0x80000000;
- public static final Integer INT_MAX = 0x7fffffff;
+ static final Integer INT_MIN = -0x80000000;
+ static final Integer INT_MAX = 0x7fffffff;
- public static final Long LONG_MIN = -0x8000000000000000L;
- public static final Long LONG_MAX = 0x7fffffffffffffffL;
+ static final Long LONG_MIN = -0x8000000000000000L;
+ static final Long LONG_MAX = 0x7fffffffffffffffL;
- public static final Double DOUBLE_MIN = -1e308d;
- public static final Double DOUBLE_MAX = 1e308d;
+ private static final Double DOUBLE_MIN = -1e308d;
+ private static final Double DOUBLE_MAX = 1e308d;
public ConfigDefinition(String name, String version, String namespace) {
this.name = name;
@@ -56,29 +60,21 @@ public class ConfigDefinition implements Comparable<ConfigDefinition> {
this.namespace = namespace;
}
- public ConfigDefinition(String name, String version) {
- this(name, version, CNode.DEFAULT_NAMESPACE);
- }
-
public String getName() {
return name;
}
- public String getVersion() {
- return version;
- }
-
public String getNamespace() {
return namespace;
}
/** @return The parent ConfigDefinition, or null if this is the root. */
- public ConfigDefinition getParent() {
+ private ConfigDefinition getParent() {
return parent;
}
/** @return The root ConfigDefinition, might be this. */
- public ConfigDefinition getRoot() {
+ private ConfigDefinition getRoot() {
ConfigDefinition ancestor = this;
while (ancestor.getParent() != null) {
ancestor = ancestor.getParent();
@@ -205,7 +201,7 @@ public class ConfigDefinition implements Comparable<ConfigDefinition> {
if (commaSep==null) {
return null;
}
- List<String> in = new ArrayList<String>();
+ List<String> in = new ArrayList<>();
for (String val: commaSep.split(",")) {
in.add(val.trim());
}
@@ -233,7 +229,7 @@ public class ConfigDefinition implements Comparable<ConfigDefinition> {
return enumVals;
}
- public boolean checkValue(String id, String val, int index) {
+ boolean checkValue(String id, String val, int index) {
if ("int".equals(getType())) {
return checkInt(id, val, index);
} else if ("long".equals(getType())) {
@@ -342,8 +338,8 @@ public class ConfigDefinition implements Comparable<ConfigDefinition> {
* of as an inner array with only one element.
*/
public static class StructDef extends ConfigDefinition {
- public StructDef(String name, String version, ConfigDefinition parent) {
- super(name, version);
+ StructDef(String name, String version, ConfigDefinition parent) {
+ super(name, version, parent.getNamespace());
this.parent = parent;
}
}
@@ -354,8 +350,8 @@ public class ConfigDefinition implements Comparable<ConfigDefinition> {
*
*/
public static class InnerArrayDef extends ConfigDefinition {
- public InnerArrayDef(String name, String version, ConfigDefinition parent) {
- super(name, version);
+ InnerArrayDef(String name, String version, ConfigDefinition parent) {
+ super(name, version, parent.getNamespace());
this.parent = parent;
}
}
@@ -367,8 +363,8 @@ public class ConfigDefinition implements Comparable<ConfigDefinition> {
*/
public static class ArrayDef extends ConfigDefinition {
private TypeSpec typeSpec;
- public ArrayDef(String name, String version, ConfigDefinition parent) {
- super(name, version);
+ ArrayDef(String name, String version, ConfigDefinition parent) {
+ super(name, version, parent.getNamespace());
this.parent = parent;
}
public TypeSpec getTypeSpec() {
@@ -393,8 +389,8 @@ public class ConfigDefinition implements Comparable<ConfigDefinition> {
*/
public static class LeafMapDef extends ConfigDefinition {
private TypeSpec typeSpec;
- public LeafMapDef(String name, String version, ConfigDefinition parent) {
- super(name, version);
+ LeafMapDef(String name, String version, ConfigDefinition parent) {
+ super(name, version, parent.getNamespace());
this.parent = parent;
}
public TypeSpec getTypeSpec() {
@@ -411,8 +407,8 @@ public class ConfigDefinition implements Comparable<ConfigDefinition> {
*
*/
public static class StructMapDef extends ConfigDefinition {
- public StructMapDef(String name, String version, ConfigDefinition parent) {
- super(name, version);
+ StructMapDef(String name, String version, ConfigDefinition parent) {
+ super(name, version, parent.getNamespace());
this.parent = parent;
}
}
@@ -428,14 +424,14 @@ public class ConfigDefinition implements Comparable<ConfigDefinition> {
public static class EnumDef implements DefaultValued<String>{
private List<String> vals;
private String defVal;
- public EnumDef(List<String> vals, String defVal) {
+ EnumDef(List<String> vals, String defVal) {
if (defVal!=null && !vals.contains(defVal)) {
throw new IllegalArgumentException("Def val "+defVal+" is not in given vals "+vals);
}
this.vals = vals;
this.defVal = defVal;
}
- public List<String> getVals() {
+ List<String> getVals() {
return vals;
}
@@ -448,7 +444,7 @@ public class ConfigDefinition implements Comparable<ConfigDefinition> {
public static class StringDef implements DefaultValued<String> {
private String defVal;
- public StringDef(String def) {
+ StringDef(String def) {
this.defVal=def;
}
@@ -461,7 +457,7 @@ public class ConfigDefinition implements Comparable<ConfigDefinition> {
public static class BoolDef implements DefaultValued<Boolean> {
private Boolean defVal;
- public BoolDef(Boolean def) {
+ BoolDef(Boolean def) {
this.defVal=def;
}
@@ -478,29 +474,21 @@ public class ConfigDefinition implements Comparable<ConfigDefinition> {
private Double defVal;
private Double min;
private Double max;
- public DoubleDef(Double defVal, Double min, Double max) {
+ DoubleDef(Double defVal, Double min, Double max) {
super();
this.defVal = defVal;
- if (min == null) {
- this.min = DOUBLE_MIN;
- } else {
- this.min = min;
- }
- if (max == null){
- this.max = DOUBLE_MAX;
- } else {
- this.max = max;
- }
+ this.min = Objects.requireNonNullElse(min, DOUBLE_MIN);
+ this.max = Objects.requireNonNullElse(max, DOUBLE_MAX);
}
@Override
public Double getDefVal() {
return defVal;
}
- public Double getMin() {
+ Double getMin() {
return min;
}
- public Double getMax() {
+ Double getMax() {
return max;
}
}
@@ -509,19 +497,11 @@ public class ConfigDefinition implements Comparable<ConfigDefinition> {
private Integer defVal;
private Integer min;
private Integer max;
- public IntDef(Integer def, Integer min, Integer max) {
+ IntDef(Integer def, Integer min, Integer max) {
super();
this.defVal = def;
- if (min == null) {
- this.min = INT_MIN;
- } else {
- this.min = min;
- }
- if (max == null) {
- this.max = INT_MAX;
- } else {
- this.max = max;
- }
+ this.min = Objects.requireNonNullElse(min, INT_MIN);
+ this.max = Objects.requireNonNullElse(max, INT_MAX);
}
@Override
@@ -540,19 +520,11 @@ public class ConfigDefinition implements Comparable<ConfigDefinition> {
private Long defVal;
private Long min;
private Long max;
- public LongDef(Long def, Long min, Long max) {
+ LongDef(Long def, Long min, Long max) {
super();
this.defVal = def;
- if (min == null) {
- this.min = LONG_MIN;
- } else {
- this.min = min;
- }
- if (max == null) {
- this.max = LONG_MAX;
- } else {
- this.max = max;
- }
+ this.min = Objects.requireNonNullElse(min, LONG_MIN);
+ this.max = Objects.requireNonNullElse(max, LONG_MAX);
}
@Override
@@ -570,7 +542,7 @@ public class ConfigDefinition implements Comparable<ConfigDefinition> {
public static class RefDef implements DefaultValued<String>{
private String defVal;
- public RefDef(String defVal) {
+ RefDef(String defVal) {
super();
this.defVal = defVal;
}
@@ -584,7 +556,7 @@ public class ConfigDefinition implements Comparable<ConfigDefinition> {
public static class FileDef implements DefaultValued<String>{
private String defVal;
- public FileDef(String defVal) {
+ FileDef(String defVal) {
super();
this.defVal = defVal;
}
@@ -598,7 +570,7 @@ public class ConfigDefinition implements Comparable<ConfigDefinition> {
public static class PathDef implements DefaultValued<String>{
private String defVal;
- public PathDef(String defVal) {
+ PathDef(String defVal) {
this.defVal = defVal;
}
@@ -611,7 +583,7 @@ public class ConfigDefinition implements Comparable<ConfigDefinition> {
public static class UrlDef implements DefaultValued<String>{
private String defVal;
- public UrlDef(String defVal) {
+ UrlDef(String defVal) {
this.defVal = defVal;
}
@@ -634,7 +606,7 @@ public class ConfigDefinition implements Comparable<ConfigDefinition> {
}
public void addEnumDef(String id, List<String> vals, String defVal) {
- List<String> in = new ArrayList<String>();
+ List<String> in = new ArrayList<>();
for (String ins: vals) {
in.add(ins.trim());
}
@@ -940,15 +912,15 @@ public class ConfigDefinition implements Comparable<ConfigDefinition> {
return true;
}
- static void failTooSmall(Object val, Object min, String defName, String valKey) {
+ private static void failTooSmall(Object val, Object min, String defName, String valKey) {
defFail("Value \""+valKey+"\" outside range in definition \""+defName+"\": "+val+"<"+min);
}
- static void failTooBig(Object val, Object max, String defName, String valKey) {
+ private static void failTooBig(Object val, Object max, String defName, String valKey) {
defFail("Value \""+valKey+"\" outside range in definition \""+defName+"\": "+val+">"+max);
}
- static void failInvalidEnum(Object val, String defName, String defKey) {
+ private static void failInvalidEnum(Object val, String defName, String defKey) {
defFail("Invalid enum value \""+val+"\" for \""+defKey+"\" in definition \""+defName);
}
@@ -957,7 +929,7 @@ public class ConfigDefinition implements Comparable<ConfigDefinition> {
* @param msg failure message
* @return warnings list with msg added
*/
- static List<String> defFail(String msg) {
+ private static List<String> defFail(String msg) {
throw new IllegalArgumentException(msg);
}
@@ -1069,7 +1041,7 @@ public class ConfigDefinition implements Comparable<ConfigDefinition> {
*
* @return a string composed of the ancestors of this ConfigDefinition, not including the root.
*/
- public String getAncestorString() {
+ private String getAncestorString() {
StringBuilder ret = new StringBuilder();
ConfigDefinition ancestor = this;
while (ancestor.getParent() != null) {
@@ -1080,15 +1052,6 @@ public class ConfigDefinition implements Comparable<ConfigDefinition> {
}
@Override
- public int compareTo(ConfigDefinition other) {
- Objects.requireNonNull(other);
- if (!getName().equals(other.getName())) {
- throw new IllegalArgumentException("Different def names used to compare: "+getName()+"/"+other.getName());
- }
- return new VersionComparator().compare(getVersion(),other.getVersion());
- }
-
- @Override
public String toString() {
return getNamespace() + "." + getName();
}
diff --git a/config/src/main/java/com/yahoo/vespa/config/ConfigDefinitionBuilder.java b/config/src/main/java/com/yahoo/vespa/config/ConfigDefinitionBuilder.java
index 0121e47b9ae..b265b13a6b6 100644
--- a/config/src/main/java/com/yahoo/vespa/config/ConfigDefinitionBuilder.java
+++ b/config/src/main/java/com/yahoo/vespa/config/ConfigDefinitionBuilder.java
@@ -112,7 +112,7 @@ public class ConfigDefinitionBuilder {
}
- static void addNode(ConfigDefinition def, LeafCNode.IntegerLeaf leaf) {
+ private static void addNode(ConfigDefinition def, LeafCNode.IntegerLeaf leaf) {
if (leaf.getDefaultValue() != null) {
def.addIntDef(leaf.getName(), Integer.valueOf(leaf.getDefaultValue().getValue()));
} else {
@@ -120,7 +120,7 @@ public class ConfigDefinitionBuilder {
}
}
- static void addNode(ConfigDefinition def, LeafCNode.LongLeaf leaf) {
+ private static void addNode(ConfigDefinition def, LeafCNode.LongLeaf leaf) {
if (leaf.getDefaultValue() != null) {
def.addLongDef(leaf.getName(), Long.valueOf(leaf.getDefaultValue().getValue()));
} else {
@@ -128,7 +128,7 @@ public class ConfigDefinitionBuilder {
}
}
- static void addNode(ConfigDefinition def, LeafCNode.BooleanLeaf leaf) {
+ private static void addNode(ConfigDefinition def, LeafCNode.BooleanLeaf leaf) {
if (leaf.getDefaultValue() != null) {
def.addBoolDef(leaf.getName(), Boolean.valueOf(leaf.getDefaultValue().getValue()));
} else {
@@ -136,7 +136,7 @@ public class ConfigDefinitionBuilder {
}
}
- static void addNode(ConfigDefinition def, LeafCNode.DoubleLeaf leaf) {
+ private static void addNode(ConfigDefinition def, LeafCNode.DoubleLeaf leaf) {
if (leaf.getDefaultValue() != null) {
def.addDoubleDef(leaf.getName(), Double.valueOf(leaf.getDefaultValue().getValue()));
} else {
@@ -144,7 +144,7 @@ public class ConfigDefinitionBuilder {
}
}
- static void addNode(ConfigDefinition def, LeafCNode.StringLeaf leaf) {
+ private static void addNode(ConfigDefinition def, LeafCNode.StringLeaf leaf) {
if (leaf.getDefaultValue() != null) {
def.addStringDef(leaf.getName(), leaf.getDefaultValue().getValue());
} else {
@@ -152,7 +152,7 @@ public class ConfigDefinitionBuilder {
}
}
- static void addNode(ConfigDefinition def, LeafCNode.ReferenceLeaf leaf) {
+ private static void addNode(ConfigDefinition def, LeafCNode.ReferenceLeaf leaf) {
if (leaf.getDefaultValue() != null) {
def.addReferenceDef(leaf.getName(), leaf.getDefaultValue().getValue());
} else {
@@ -160,7 +160,7 @@ public class ConfigDefinitionBuilder {
}
}
- static void addNode(ConfigDefinition def, LeafCNode.FileLeaf leaf) {
+ private static void addNode(ConfigDefinition def, LeafCNode.FileLeaf leaf) {
if (leaf.getDefaultValue() != null) {
def.addFileDef(leaf.getName(), leaf.getDefaultValue().getValue());
} else {
@@ -168,7 +168,7 @@ public class ConfigDefinitionBuilder {
}
}
- static void addNode(ConfigDefinition def, LeafCNode.PathLeaf leaf) {
+ private static void addNode(ConfigDefinition def, LeafCNode.PathLeaf leaf) {
if (leaf.getDefaultValue() != null) {
def.addPathDef(leaf.getName(), leaf.getDefaultValue().getValue());
} else {
@@ -176,7 +176,7 @@ public class ConfigDefinitionBuilder {
}
}
- static void addNode(ConfigDefinition def, LeafCNode.UrlLeaf leaf) {
+ private static void addNode(ConfigDefinition def, LeafCNode.UrlLeaf leaf) {
if (leaf.getDefaultValue() != null) {
def.addUrlDef(leaf.getName(), leaf.getDefaultValue().getValue());
} else {
@@ -184,7 +184,7 @@ public class ConfigDefinitionBuilder {
}
}
- static void addNode(ConfigDefinition def, LeafCNode.EnumLeaf leaf) {
+ private static void addNode(ConfigDefinition def, LeafCNode.EnumLeaf leaf) {
if (leaf.getDefaultValue() != null) {
def.addEnumDef(leaf.getName(), Arrays.asList(leaf.getLegalValues()), leaf.getDefaultValue().getValue());
} else {
@@ -192,7 +192,7 @@ public class ConfigDefinitionBuilder {
}
}
- static String convertToEnumValueCommaSeparated(String[] enumValues) {
+ private static String convertToEnumValueCommaSeparated(String[] enumValues) {
StringBuilder sb = new StringBuilder();
for (String s : enumValues) {
sb.append(s);
diff --git a/config/src/main/java/com/yahoo/vespa/config/ConfigDefinitionSet.java b/config/src/main/java/com/yahoo/vespa/config/ConfigDefinitionSet.java
deleted file mode 100644
index c174ead815f..00000000000
--- a/config/src/main/java/com/yahoo/vespa/config/ConfigDefinitionSet.java
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.config;
-
-import com.yahoo.config.codegen.CNode;
-import com.yahoo.log.LogLevel;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Class to hold config definitions and resolving requests for the correct definition
- *
- * @author hmusum
- * @since 5.1
- */
-public class ConfigDefinitionSet {
- private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(ConfigDefinitionSet.class.getName());
-
- private final Map<ConfigDefinitionKey, ConfigDefinition> defs = new ConcurrentHashMap<ConfigDefinitionKey, ConfigDefinition>();
-
- public ConfigDefinitionSet() {
-
- }
-
- public void add(ConfigDefinitionKey key, ConfigDefinition def) {
- log.log(LogLevel.DEBUG, "Adding to set: " + key);
- defs.put(key, def);
- }
-
- /**
- * Returns a ConfigDefinition from the set matching the given <code>key</code>. If no ConfigDefinition
- * is found in the set, it will try to find a ConfigDefinition with same name in the default namespace.
- * @param key a {@link ConfigDefinitionKey}
- * @return a ConfigDefinition if found, else null
- */
- public ConfigDefinition get(ConfigDefinitionKey key) {
- log.log(LogLevel.DEBUG, "Getting from set " + defs + " for key " + key);
- ConfigDefinition ret = defs.get(key);
- if (ret == null) {
- // Return entry if we fallback to default namespace
- log.log(LogLevel.DEBUG, "Found no def for key " + key + ", trying to find def with same name in default namespace");
- for (Map.Entry<ConfigDefinitionKey, ConfigDefinition> entry : defs.entrySet()) {
- if (key.getName().equals(entry.getKey().getName()) && entry.getKey().getNamespace().equals(CNode.DEFAULT_NAMESPACE)) {
- return entry.getValue();
- }
- }
- }
- return ret;
- }
-
- public int size() {
- return defs.size();
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- for (ConfigDefinitionKey key : defs.keySet()) {
- sb.append(key.toString()).append("\n");
- }
- return sb.toString();
- }
-
-}
diff --git a/config/src/main/java/com/yahoo/vespa/config/ConfigFileFormat.java b/config/src/main/java/com/yahoo/vespa/config/ConfigFileFormat.java
index c478eaa18d3..1f955a7501f 100644
--- a/config/src/main/java/com/yahoo/vespa/config/ConfigFileFormat.java
+++ b/config/src/main/java/com/yahoo/vespa/config/ConfigFileFormat.java
@@ -184,7 +184,7 @@ public class ConfigFileFormat implements SlimeFormat, ObjectTraverser {
encode(os, slime.get());
}
- public void encode(OutputStream os, Inspector inspector) throws IOException {
+ private void encode(OutputStream os, Inspector inspector) throws IOException {
this.out = new DataOutputStream(os);
this.nodeStack = new Stack<>();
nodeStack.push(new Node(root));
@@ -226,7 +226,7 @@ public class ConfigFileFormat implements SlimeFormat, ObjectTraverser {
this.mapKey = mapKey;
}
- public Node(CNode node) {
+ Node(CNode node) {
this(node, -1, "");
}
}
diff --git a/config/src/main/java/com/yahoo/vespa/config/ConfigHelper.java b/config/src/main/java/com/yahoo/vespa/config/ConfigHelper.java
deleted file mode 100644
index 0f3b4b76aa8..00000000000
--- a/config/src/main/java/com/yahoo/vespa/config/ConfigHelper.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.config;
-
-import com.yahoo.config.subscription.ConfigSourceSet;
-
-/**
- * Helper class for config applications (currently ConfigManager and ConfigProxy).
- *
- * @author <a href="gv@yahoo-inc.com">G. Voldengen</a>
- */
-public class ConfigHelper {
- private final JRTConnectionPool jrtConnectionPool;
- private final TimingValues timingValues;
-
- /**
- * @param configSourceSet The set of config sources for this helper.
- */
- public ConfigHelper(ConfigSourceSet configSourceSet) {
- this(configSourceSet, new TimingValues());
- }
-
- /**
- * @param configSourceSet The set of config sources for this helper.
- * @param timingValues values for timeouts and delays, see {@link TimingValues}
- */
- public ConfigHelper(ConfigSourceSet configSourceSet, TimingValues timingValues) {
- jrtConnectionPool = new JRTConnectionPool(configSourceSet);
- this.timingValues = timingValues;
- }
-
- /**
- * @return the config sources (remote servers and/or proxies) in this helper's connection pool.
- */
- public ConfigSourceSet getConfigSourceSet() {
- return jrtConnectionPool.getSourceSet();
- }
-
- /**
- * @return the connection pool for this config helper.
- */
- public JRTConnectionPool getConnectionPool() {
- return jrtConnectionPool;
- }
-
- /**
- * @return the timing values for this config helper.
- */
- public TimingValues getTimingValues() {
- return timingValues;
- }
-}
diff --git a/config/src/main/java/com/yahoo/vespa/config/ConfigPayloadApplier.java b/config/src/main/java/com/yahoo/vespa/config/ConfigPayloadApplier.java
index 5655ff596f1..65106f158fc 100644
--- a/config/src/main/java/com/yahoo/vespa/config/ConfigPayloadApplier.java
+++ b/config/src/main/java/com/yahoo/vespa/config/ConfigPayloadApplier.java
@@ -433,7 +433,7 @@ public class ConfigPayloadApplier<T extends ConfigInstance.Builder> {
if (structBuilderClass == null)
throw new RuntimeException("Could not find builder class " + structBuilderName);
try {
- return structBuilderClass.getDeclaredConstructor(new Class<?>[]{});
+ return structBuilderClass.getDeclaredConstructor();
} catch (NoSuchMethodException e) {
throw new RuntimeException("Could not create class '" + "'" + structBuilderClass.getName() + "'");
}
diff --git a/config/src/main/java/com/yahoo/vespa/config/ConfigVerification.java b/config/src/main/java/com/yahoo/vespa/config/ConfigVerification.java
index a020c8c8c55..0888004ef02 100644
--- a/config/src/main/java/com/yahoo/vespa/config/ConfigVerification.java
+++ b/config/src/main/java/com/yahoo/vespa/config/ConfigVerification.java
@@ -1,18 +1,23 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config;
+import ai.vespa.util.http.VespaHttpClientBuilder;
import com.yahoo.slime.ArrayTraverser;
import com.yahoo.slime.Inspector;
import com.yahoo.slime.JsonDecoder;
import com.yahoo.slime.Slime;
import com.yahoo.text.Utf8;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.BasicResponseHandler;
+import org.apache.http.impl.client.CloseableHttpClient;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
/**
* Tool to verify that configs across multiple config servers are the same.
@@ -33,11 +38,13 @@ public class ConfigVerification {
for (String arg : args) {
configservers.add(prefix + arg + ":" + port + "/config/v2/tenant/" + tenant + "/application/" + appName + "/environment/" + environment + "/region/" + region + "/instance/" + instance + "/?recursive=true");
}
- System.exit(compareConfigs(listConfigs(configservers)));
+ try (CloseableHttpClient httpClient = VespaHttpClientBuilder.createWithBasicConnectionManager().build()) {
+ System.exit(compareConfigs(listConfigs(configservers, httpClient), httpClient));
+ }
}
- private static Map<String, Stack<String>> listConfigs(List<String> urls) throws IOException {
- Map<String, String> outputs = performRequests(urls);
+ private static Map<String, Stack<String>> listConfigs(List<String> urls, CloseableHttpClient httpClient) throws IOException {
+ Map<String, String> outputs = performRequests(urls, httpClient);
Map<String, Stack<String>> recurseMappings = new LinkedHashMap<>();
for (Map.Entry<String, String> entry : outputs.entrySet()) {
@@ -57,21 +64,21 @@ public class ConfigVerification {
return recurseMappings;
}
- private static Map<String, String> performRequests(List<String> urls) throws IOException {
+ private static Map<String, String> performRequests(List<String> urls, CloseableHttpClient httpClient) throws IOException {
Map<String, String> outputs = new LinkedHashMap<>();
for (String url : urls) {
- outputs.put(url, performRequest(url));
+ outputs.put(url, performRequest(url, httpClient));
}
return outputs;
}
- private static int compareConfigs(Map<String, Stack<String>> mappings) throws IOException {
+ private static int compareConfigs(Map<String, Stack<String>> mappings, CloseableHttpClient httpClient) throws IOException {
for (int n = 0; n < mappings.values().iterator().next().size(); n++) {
List<String> recurseUrls = new ArrayList<>();
for (Map.Entry<String, Stack<String>> entry : mappings.entrySet()) {
recurseUrls.add(entry.getValue().pop());
}
- int ret = compareOutputs(performRequests(recurseUrls));
+ int ret = compareOutputs(performRequests(recurseUrls, httpClient));
if (ret != 0) {
return ret;
}
@@ -90,14 +97,7 @@ public class ConfigVerification {
return 0;
}
- private static String performRequest(String url) throws IOException {
- URLConnection connection = new URL(url).openConnection();
- InputStream response = connection.getInputStream();
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- int ch;
- while ((ch = response.read()) > -1) {
- baos.write(ch);
- }
- return Utf8.toString(baos.toByteArray());
+ private static String performRequest(String url, CloseableHttpClient httpClient) throws IOException {
+ return httpClient.execute(new HttpGet(url), new BasicResponseHandler());
}
}
diff --git a/config/src/main/java/com/yahoo/vespa/config/GenericConfig.java b/config/src/main/java/com/yahoo/vespa/config/GenericConfig.java
index 8e8c767e393..2fbbda2aa38 100644
--- a/config/src/main/java/com/yahoo/vespa/config/GenericConfig.java
+++ b/config/src/main/java/com/yahoo/vespa/config/GenericConfig.java
@@ -11,19 +11,21 @@ import com.yahoo.config.ConfigInstance;
* when we don't have the schema.
*
* @author Ulf Lilleengen
- * @since 5.1
*/
public class GenericConfig {
public static class GenericConfigBuilder implements ConfigInstance.Builder {
+
private final ConfigPayloadBuilder payloadBuilder;
private final ConfigDefinitionKey defKey;
+
public GenericConfigBuilder(ConfigDefinitionKey defKey, ConfigPayloadBuilder payloadBuilder) {
this.defKey = defKey;
this.payloadBuilder = payloadBuilder;
}
+
+ @SuppressWarnings("unused") // Called by reflection
private ConfigBuilder override(GenericConfigBuilder superior) {
- ConfigPayloadBuilder superiorPayload = superior.payloadBuilder;
- payloadBuilder.override(superiorPayload);
+ payloadBuilder.override(superior.payloadBuilder);
return this;
}
@@ -49,4 +51,5 @@ public class GenericConfig {
return "";
}
}
+
}
diff --git a/config/src/main/java/com/yahoo/vespa/config/JRTConnection.java b/config/src/main/java/com/yahoo/vespa/config/JRTConnection.java
index 01da823b87b..cde46eb9de0 100644
--- a/config/src/main/java/com/yahoo/vespa/config/JRTConnection.java
+++ b/config/src/main/java/com/yahoo/vespa/config/JRTConnection.java
@@ -10,7 +10,7 @@ import java.util.logging.Logger;
/**
* A JRT connection to a config server or config proxy.
*
- * @author <a href="mailto:gunnarga@yahoo-inc.com">Gunnar Gauslaa Bergem</a>
+ * @author Gunnar Gauslaa Bergem
*/
public class JRTConnection implements Connection {
public final static Logger logger = Logger.getLogger(JRTConnection.class.getPackage().getName());
@@ -78,10 +78,6 @@ public class JRTConnection implements Connection {
lastSuccess = System.currentTimeMillis();
}
- public void setLastSuccess() {
- lastSuccess = System.currentTimeMillis();
- }
-
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Address: ");
diff --git a/config/src/main/java/com/yahoo/vespa/config/TimingValues.java b/config/src/main/java/com/yahoo/vespa/config/TimingValues.java
index 6a814f99ee3..113d561bf87 100644
--- a/config/src/main/java/com/yahoo/vespa/config/TimingValues.java
+++ b/config/src/main/java/com/yahoo/vespa/config/TimingValues.java
@@ -6,7 +6,7 @@ import java.util.Random;
/**
* Timeouts, delays and retries used in RPC config protocol.
*
- * @author <a href="mailto:gunnarga@yahoo-inc.com">Gunnar Gauslaa Bergem</a>
+ * @author Gunnar Gauslaa Bergem
*/
public class TimingValues {
public static final long defaultNextConfigTimeout = 1000;
diff --git a/config/src/main/java/com/yahoo/vespa/config/benchmark/StressTester.java b/config/src/main/java/com/yahoo/vespa/config/benchmark/StressTester.java
index 4234a6deff1..0152c0bc3ff 100644
--- a/config/src/main/java/com/yahoo/vespa/config/benchmark/StressTester.java
+++ b/config/src/main/java/com/yahoo/vespa/config/benchmark/StressTester.java
@@ -14,7 +14,6 @@ import java.util.*;
* with test classes that implement the {@link Tester} interface.
*
* @author hmusum
- * @since 5.1.5
*/
public class StressTester {
private static boolean debug = false;
diff --git a/config/src/main/java/com/yahoo/vespa/config/buildergen/ConfigCompiler.java b/config/src/main/java/com/yahoo/vespa/config/buildergen/ConfigCompiler.java
index 7565d198a7a..e4cedfafeea 100644
--- a/config/src/main/java/com/yahoo/vespa/config/buildergen/ConfigCompiler.java
+++ b/config/src/main/java/com/yahoo/vespa/config/buildergen/ConfigCompiler.java
@@ -5,7 +5,6 @@ package com.yahoo.vespa.config.buildergen;
* Interface towards compilers for compiling builders from a config class definition.
*
* @author Ulf Lilleengen
- * @since 5.2
*/
public interface ConfigCompiler {
CompiledBuilder compile(ConfigDefinitionClass defClass);
diff --git a/config/src/main/java/com/yahoo/vespa/config/buildergen/ConfigDefinitionClass.java b/config/src/main/java/com/yahoo/vespa/config/buildergen/ConfigDefinitionClass.java
index 6c3f3f90f40..8f222de3b55 100644
--- a/config/src/main/java/com/yahoo/vespa/config/buildergen/ConfigDefinitionClass.java
+++ b/config/src/main/java/com/yahoo/vespa/config/buildergen/ConfigDefinitionClass.java
@@ -4,7 +4,8 @@ package com.yahoo.vespa.config.buildergen;
/**
* @author Ulf Lilleengen
*/
-public class ConfigDefinitionClass {
+class ConfigDefinitionClass {
+
private final String name;
private final String pkg;
private final String definition;
@@ -19,11 +20,8 @@ public class ConfigDefinitionClass {
return definition;
}
- String getSimpleName() {
- return name;
- }
-
String getName() {
return pkg + "." + name;
}
+
}
diff --git a/config/src/main/java/com/yahoo/vespa/config/buildergen/LazyConfigCompiler.java b/config/src/main/java/com/yahoo/vespa/config/buildergen/LazyConfigCompiler.java
index 3d8e2d1c2b3..2fc3cd7aa19 100644
--- a/config/src/main/java/com/yahoo/vespa/config/buildergen/LazyConfigCompiler.java
+++ b/config/src/main/java/com/yahoo/vespa/config/buildergen/LazyConfigCompiler.java
@@ -5,7 +5,6 @@ import com.yahoo.config.ConfigInstance;
import javax.tools.*;
import java.io.File;
-import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
@@ -76,7 +75,7 @@ public class LazyConfigCompiler implements ConfigCompiler {
@SuppressWarnings("unchecked")
private <BUILDER extends ConfigInstance.Builder> BUILDER loadBuilder(String builderClassUrl) {
try {
- Class<BUILDER> clazz = (Class<BUILDER>) classLoader.<BUILDER>loadClass(builderClassUrl);
+ Class<BUILDER> clazz = (Class<BUILDER>) classLoader.loadClass(builderClassUrl);
return clazz.getDeclaredConstructor().newInstance();
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Error creating new instance of '" + builderClassUrl + "'", e);
diff --git a/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeConfigResponse.java b/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeConfigResponse.java
index 566e3597269..327acab53d3 100644
--- a/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeConfigResponse.java
+++ b/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeConfigResponse.java
@@ -9,6 +9,7 @@ import com.yahoo.vespa.config.ConfigPayload;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
@@ -60,7 +61,7 @@ public class SlimeConfigResponse implements ConfigResponse {
Payload v1payload = Payload.from(payload, compressionInfo).withCompression(CompressionType.UNCOMPRESSED);
try {
ConfigPayload.fromUtf8Array(v1payload.getData()).serialize(baos, format);
- return Arrays.asList(baos.toString("UTF-8").split("\\n"));
+ return Arrays.asList(baos.toString(StandardCharsets.UTF_8).split("\\n"));
} catch (IOException e) {
throw new RuntimeException(e);
}
diff --git a/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeResponseData.java b/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeResponseData.java
index 6add29074d1..40414c24c96 100644
--- a/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeResponseData.java
+++ b/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeResponseData.java
@@ -70,7 +70,7 @@ class SlimeResponseData {
boolean getResponseInternalRedeployment() {
Inspector inspector = getResponseField(RESPONSE_INTERNAL_REDEPLOY);
- return inspector.valid() ? inspector.asBool() : false;
+ return inspector.valid() && inspector.asBool();
}
}
diff --git a/config/src/main/java/com/yahoo/vespa/config/protocol/Utf8SerializedString.java b/config/src/main/java/com/yahoo/vespa/config/protocol/Utf8SerializedString.java
deleted file mode 100644
index 43c0f7f41e7..00000000000
--- a/config/src/main/java/com/yahoo/vespa/config/protocol/Utf8SerializedString.java
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.config.protocol;
-
-import com.fasterxml.jackson.core.SerializableString;
-import com.yahoo.text.Utf8Array;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-
-/**
- * Wraps utf8array as a {@link com.fasterxml.jackson.core.SerializableString} to avoid extra copy.
- *
- * @author Ulf Lilleengen
- * @since 5.17
- */
-public class Utf8SerializedString implements SerializableString {
- private final Utf8Array value;
- public Utf8SerializedString(Utf8Array value) {
- this.value = value;
- }
-
- @Override
- public String getValue() {
- return value.toString();
- }
-
- @Override
- public int charLength() {
- return value.getByteLength();
- }
-
- @Override
- public char[] asQuotedChars() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public byte[] asUnquotedUTF8() {
- return value.getBytes();
- }
-
- @Override
- public byte[] asQuotedUTF8() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int appendQuotedUTF8(byte[] buffer, int offset) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int appendQuoted(char[] buffer, int offset) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int appendUnquotedUTF8(byte[] buffer, int offset) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int appendUnquoted(char[] buffer, int offset) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int writeQuotedUTF8(OutputStream out) throws IOException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int writeUnquotedUTF8(OutputStream out) throws IOException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int putQuotedUTF8(ByteBuffer buffer) throws IOException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int putUnquotedUTF8(ByteBuffer out) throws IOException {
- throw new UnsupportedOperationException();
- }
-}
diff --git a/config/src/main/java/com/yahoo/vespa/config/util/ConfigUtils.java b/config/src/main/java/com/yahoo/vespa/config/util/ConfigUtils.java
index bd7aae3051a..56ede8897ed 100644
--- a/config/src/main/java/com/yahoo/vespa/config/util/ConfigUtils.java
+++ b/config/src/main/java/com/yahoo/vespa/config/util/ConfigUtils.java
@@ -9,33 +9,42 @@ import com.yahoo.net.HostName;
import com.yahoo.slime.JsonFormat;
import com.yahoo.text.Utf8;
import com.yahoo.text.Utf8Array;
-import com.yahoo.vespa.config.*;
-
-import java.io.*;
+import com.yahoo.vespa.config.ConfigDefinitionKey;
+import com.yahoo.vespa.config.ConfigPayload;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.LineNumberReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
- * Utilities for mangling config text, finding md5sums, version numbers in .def files etc.
+ * Utilities for mangling config text, finding md5sums, finding name and namespace in .def files etc.
*/
public class ConfigUtils {
/* Patterns used for finding ranges in config definitions */
private static final Pattern intPattern = Pattern.compile(".*int.*range.*");
private static final Pattern doublePattern = Pattern.compile(".*double.*range.*");
private static final Pattern spaceBeforeCommaPatter = Pattern.compile("\\s,");
- public static final String intFormattedMax = new DecimalFormat("#.#").format(0x7fffffff);
- public static final String intFormattedMin = new DecimalFormat("#.#", new DecimalFormatSymbols(Locale.ENGLISH)).format(-0x80000000);
- public static final String doubleFormattedMax = new DecimalFormat("#.#").format(1e308);
- public static final String doubleFormattedMin = new DecimalFormat("#.#", new DecimalFormatSymbols(Locale.ENGLISH)).format(-1e308);
+ private static final String intFormattedMax = new DecimalFormat("#.#").format(0x7fffffff);
+ private static final String intFormattedMin = new DecimalFormat("#.#", new DecimalFormatSymbols(Locale.ENGLISH)).format(-0x80000000);
+ private static final String doubleFormattedMax = new DecimalFormat("#.#").format(1e308);
+ private static final String doubleFormattedMin = new DecimalFormat("#.#", new DecimalFormatSymbols(Locale.ENGLISH)).format(-1e308);
/**
- * Computes Md5 hash of a list of strings. The only change to input lines before
- * computing md5 is to skip empty lines.
+ * Computes Md5 hash of a list of strings.
*
* @param payload a config payload
* @return the Md5 hash of the list, with lowercase letters
@@ -47,30 +56,7 @@ public class ConfigUtils {
} catch (IOException e) {
throw new RuntimeException(e);
}
- MessageDigest md5 = getMd5Instance();
- md5.update(baos.toByteArray());
- return HexDump.toHexString(md5.digest()).toLowerCase();
- }
-
- /**
- * Computes Md5 hash of a list of strings. The only change to input lines before
- * computing md5 is to skip empty lines.
- *
- * @param lines A list of lines
- * @return the Md5 hash of the list, with lowercase letters
- */
- public static String getMd5(List<String> lines) {
- StringBuilder sb = new StringBuilder();
- for (String line : lines) {
- // Remove empty lines
- line = line.trim();
- if (line.length() > 0) {
- sb.append(line).append("\n");
- }
- }
- MessageDigest md5 = getMd5Instance();
- md5.update(Utf8.toBytes(sb.toString()));
- return HexDump.toHexString(md5.digest()).toLowerCase();
+ return getMd5(baos.toByteArray());
}
/**
@@ -80,14 +66,16 @@ public class ConfigUtils {
* @return the Md5 hash of the input, with lowercase letters
*/
public static String getMd5(String input) {
- MessageDigest md5 = getMd5Instance();
- md5.update(IOUtils.utf8ByteBuffer(input));
- return HexDump.toHexString(md5.digest()).toLowerCase();
+ return getMd5(input.getBytes(StandardCharsets.UTF_8));
}
public static String getMd5(Utf8Array input) {
+ return getMd5(input.getBytes());
+ }
+
+ public static String getMd5(byte[] input) {
MessageDigest md5 = getMd5Instance();
- md5.update(input.getBytes());
+ md5.update(input);
return HexDump.toHexString(md5.digest()).toLowerCase();
}
@@ -95,7 +83,7 @@ public class ConfigUtils {
try {
return MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
- return null;
+ throw new RuntimeException("Could not get md5 instance");
}
}
@@ -105,7 +93,7 @@ public class ConfigUtils {
* @return String with spaces stripped
*/
public static String stripSpaces(String str) {
- StringBuilder ret = new StringBuilder("");
+ StringBuilder ret = new StringBuilder();
boolean inQuotes = false;
boolean inSpaceSequence = false;
for (char c : str.toCharArray()) {
@@ -161,12 +149,6 @@ public class ConfigUtils {
}
}
- MessageDigest md5;
- try {
- md5 = MessageDigest.getInstance("MD5");
- } catch (NoSuchAlgorithmException e) {
- return null;
- }
StringBuilder sb = new StringBuilder();
for (String line : linesCopy) {
// Normalize line, like it's done in make-config-preproc.pl
@@ -193,19 +175,7 @@ public class ConfigUtils {
sb.append(line).append("\n");
}
}
- md5.update(Utf8.toBytes(sb.toString()));
- return HexDump.toHexString(md5.digest()).toLowerCase();
- }
-
- /**
- * Finds the def version from a reader for a def-file. Returns "" (empty string)
- * if no version was found.
- *
- * @param in A reader to a def-file
- * @return version of the def-file, or "" (empty string) if no version was found
- */
- public static String getDefVersion(Reader in) {
- return getDefKeyword(in, "version");
+ return getMd5(sb.toString());
}
/**
@@ -223,17 +193,6 @@ public class ConfigUtils {
return getDefKeyword(defLines, "namespace");
}
- /**
- * Finds the value of the keyword in <code>keyword</code> from a reader for a def-file.
- * Returns "" (empty string) if no value for keyword was found.
- *
- * @param in A reader to a def-file
- * @return value of keyword, or "" (empty string) if no line matching keyword was found
- */
- public static String getDefKeyword(Reader in, String keyword) {
- return getDefKeyword(getDefLines(in), keyword);
- }
-
private static String getDefKeyword(List<String> defLines, String keyword) {
for (String line : defLines) {
if (line.startsWith(keyword)) {
@@ -271,16 +230,15 @@ public class ConfigUtils {
}
/**
- * Finds the name and version from a string with "name,version".
- * If no name is given, the first part of the tuple will be the empty string
- * If no version is given, the second part of the tuple will be the empty string
+ * Finds the name and namespace part from a string "name.namespace,version", which
+ * is how it is serialized in zookeeper (versions is always empty)
*
- * @param nameCommaVersion A string consisting of "name,version"
- * @return a Tuple2 with first item being name and second item being version
+ * @param nameCommaVersion A string consisting of "name.namespace,version" or "name.namespace,"
+ * @return a string with name.namespace
*/
- public static Tuple2<String, String> getNameAndVersionFromString(String nameCommaVersion) {
+ private static String getNameFromSerializedString(String nameCommaVersion) {
String[] av = nameCommaVersion.split(",");
- return new Tuple2<>(av[0], av.length >= 2 ? av[1] : "");
+ return av[0];
}
/**
@@ -302,72 +260,22 @@ public class ConfigUtils {
}
/**
- * Creates a ConfigDefinitionKey based on a string with namespace, name and version
- * (e.g. Vespa's own config definitions in $VESPA_HOME/share/vespa/configdefinitions)
- *
- * @param input A string consisting of "namespace.name.version"
- * @return a ConfigDefinitionKey
- */
- @SuppressWarnings("deprecation")
- public static ConfigDefinitionKey getConfigDefinitionKeyFromString(String input) {
- final String name;
- final String namespace;
- if (!input.contains(".")) {
- name = input;
- namespace = "";
- } else if (input.lastIndexOf(".") == input.indexOf(".")) {
- Tuple2<String, String> tuple = ConfigUtils.getNameAndNamespaceFromString(input);
- boolean containsVersion = false;
- for (int i=0; i < tuple.first.length(); i++) {
- if (Character.isDigit(tuple.first.charAt(i))) {
- containsVersion = true;
- break;
- }
- }
- if (containsVersion) {
- name = tuple.second;
- namespace = "";
- } else {
- name = tuple.first;
- namespace = tuple.second;
- }
- } else {
- Tuple2<String, String> tuple = ConfigUtils.getNameAndNamespaceFromString(input);
-
- String tempName = tuple.second;
- tuple = ConfigUtils.getNameAndNamespaceFromString(tempName);
- name = tuple.first;
- namespace = tuple.second;
- }
- return new ConfigDefinitionKey(name, namespace);
- }
-
- /**
* Creates a ConfigDefinitionKey from a string for the name of a node in ZooKeeper
* that holds a config definition
*
* @param nodeName name of a node in ZooKeeper that holds a config definition
* @return a ConfigDefinitionKey
*/
- @SuppressWarnings("deprecation")
public static ConfigDefinitionKey createConfigDefinitionKeyFromZKString(String nodeName) {
final String name;
final String namespace;
- if (nodeName.contains(".")) {
- Tuple2<String, String> tuple = ConfigUtils.getNameAndVersionFromString(nodeName);
- String tempName = tuple.first; // includes namespace
- tuple = ConfigUtils.getNameAndNamespaceFromString(tempName);
- name = tuple.first;
- namespace = tuple.second;
- } else {
- Tuple2<String, String> tuple = ConfigUtils.getNameAndVersionFromString(nodeName);
- name = tuple.first;
- namespace = "";
- }
+ String tempName = ConfigUtils.getNameFromSerializedString(nodeName); // includes namespace
+ Tuple2<String, String> tuple = ConfigUtils.getNameAndNamespaceFromString(tempName);
+ name = tuple.first;
+ namespace = tuple.second;
return new ConfigDefinitionKey(name, namespace);
}
-
/**
* Creates a ConfigDefinitionKey from a file by reading the file and parsing
* contents for namespace. Name and from filename, but the filename may be prefixed
@@ -392,8 +300,7 @@ public class ConfigUtils {
* @param content content of a config definition
* @return a ConfigDefinitionKey
*/
- @SuppressWarnings("deprecation")
- public static ConfigDefinitionKey createConfigDefinitionKeyFromDefContent(String name, byte[] content) {
+ static ConfigDefinitionKey createConfigDefinitionKeyFromDefContent(String name, byte[] content) {
String namespace = ConfigUtils.getDefNamespace(new StringReader(Utf8.toString(content)));
if (namespace.isEmpty()) {
namespace = CNode.DEFAULT_NAMESPACE;
@@ -401,7 +308,6 @@ public class ConfigUtils {
return new ConfigDefinitionKey(name, namespace);
}
-
/**
* Escapes a config value according to the cfg format.
* @param input the string to escape
diff --git a/config/src/test/java/com/yahoo/config/subscription/AppService.java b/config/src/test/java/com/yahoo/config/subscription/AppService.java
index 6c395b12129..0f8d93e6fe0 100644
--- a/config/src/test/java/com/yahoo/config/subscription/AppService.java
+++ b/config/src/test/java/com/yahoo/config/subscription/AppService.java
@@ -10,25 +10,22 @@ import com.yahoo.vespa.config.TimingValues;
* Application that subscribes to config defined in app.def and
* generated code in AppConfig.java.
*/
-public class AppService {
- protected int timesConfigured = 0;
+class AppService {
+ private int timesConfigured = 0;
- protected AppConfig config = null;
+ private AppConfig config = null;
private final ConfigSubscriber subscriber;
- protected final String configId;
- final Thread configThread;
- boolean stopThread = false;
+ private boolean stopThread = false;
- public AppService(String configId, ConfigSourceSet csource) {
+ AppService(String configId, ConfigSourceSet csource) {
this(configId, csource, null);
}
- public int timesConfigured() { return timesConfigured; }
+ int timesConfigured() { return timesConfigured; }
- public AppService(String configId, ConfigSourceSet csource, TimingValues timingValues) {
+ private AppService(String configId, ConfigSourceSet csource, TimingValues timingValues) {
if (csource == null) throw new IllegalArgumentException("Config source cannot be null");
- this.configId = configId;
subscriber = new ConfigSubscriber(csource);
ConfigHandle<AppConfig> temp;
if (timingValues == null) {
@@ -37,15 +34,12 @@ public class AppService {
temp = subscriber.subscribe(AppConfig.class, configId, csource, timingValues);
}
final ConfigHandle<AppConfig> handle = temp;
- configThread = new Thread(new Runnable() {
- @Override
- public void run() {
- while (!stopThread) {
- boolean changed = subscriber.nextConfig(500);
- if (changed) {
- configure(handle.getConfig());
- timesConfigured++;
- }
+ Thread configThread = new Thread(() -> {
+ while (!stopThread) {
+ boolean changed = subscriber.nextConfig(500);
+ if (changed) {
+ configure(handle.getConfig());
+ timesConfigured++;
}
}
});
@@ -56,7 +50,7 @@ public class AppService {
configThread.start();
}
- public void configure(AppConfig config) {
+ private void configure(AppConfig config) {
this.config = config;
}
diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigGetterTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigGetterTest.java
index 731a5f50816..07eeb78f0d9 100644
--- a/config/src/test/java/com/yahoo/config/subscription/ConfigGetterTest.java
+++ b/config/src/test/java/com/yahoo/config/subscription/ConfigGetterTest.java
@@ -37,6 +37,8 @@ public class ConfigGetterTest {
AppConfig serviceConfig = service.getConfig();
assertTrue(service.isConfigured());
assertThat(config, is(serviceConfig));
+
+ service.cancelSubscription();
}
@Test
@@ -107,5 +109,7 @@ public class ConfigGetterTest {
AppConfig serviceConfig = service.getConfig();
assertTrue(service.isConfigured());
assertThat(config, is(serviceConfig));
+
+ service.cancelSubscription();
}
}
diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceSerializerTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceSerializerTest.java
index 0c8af47dccb..ee8682efe3c 100644
--- a/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceSerializerTest.java
+++ b/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceSerializerTest.java
@@ -25,7 +25,7 @@ import static org.junit.Assert.fail;
*/
public class ConfigInstanceSerializerTest {
@Test
- public void test_that_leaf_types_are_serialized_to_json_types() throws IOException {
+ public void test_that_leaf_types_are_serialized_to_json_types() {
SimpletypesConfig.Builder builder = new SimpletypesConfig.Builder();
builder.boolval(false);
builder.stringval("foo");
@@ -50,7 +50,7 @@ public class ConfigInstanceSerializerTest {
}
@Test
- public void test_that_nested_structs_are_formatted_to_json() throws IOException {
+ public void test_that_nested_structs_are_formatted_to_json() {
StructtypesConfig.Builder builder = new StructtypesConfig.Builder();
StructtypesConfig.Nested.Builder nestedBuilder = new StructtypesConfig.Nested.Builder();
StructtypesConfig.Nested.Inner.Builder innerBuilder = new StructtypesConfig.Nested.Inner.Builder();
@@ -106,7 +106,7 @@ public class ConfigInstanceSerializerTest {
}
@Test
- public void test_that_arrays_are_formatted_to_json() throws IOException {
+ public void test_that_arrays_are_formatted_to_json() {
ArraytypesConfig.Builder builder = new ArraytypesConfig.Builder();
builder.boolarr(true);
builder.boolarr(false);
@@ -149,7 +149,7 @@ public class ConfigInstanceSerializerTest {
}
@Test
- public void test_that_maps_are_formatted_to_json() throws IOException {
+ public void test_that_maps_are_formatted_to_json() {
MaptypesConfig.Builder builder = new MaptypesConfig.Builder();
builder.boolmap("foo", true);
builder.intmap("bar", 3);
@@ -195,7 +195,7 @@ public class ConfigInstanceSerializerTest {
}
@Test
- public void test_that_non_standard_types_are_formatted_as_json_strings() throws IOException {
+ public void test_that_non_standard_types_are_formatted_as_json_strings() {
SpecialtypesConfig.Builder builder = new SpecialtypesConfig.Builder();
builder.myfile("thefilename");
builder.myref("thereference");
diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceTest.java
index e086202eca8..1da53e4c3b9 100644
--- a/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceTest.java
+++ b/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceTest.java
@@ -34,6 +34,9 @@ public class ConfigInstanceTest {
assertEquals(1, service1.timesConfigured());
assertEquals(1, service2.timesConfigured());
+
+ service1.cancelSubscription();
+ service2.cancelSubscription();
}
/**
@@ -107,11 +110,10 @@ public class ConfigInstanceTest {
}
private class TestNonstring {
- private final ConfigSubscriber subscriber;
- private final ConfigHandle<TestNonstringConfig> handle;
+
public TestNonstring(String configId) {
- subscriber = new ConfigSubscriber();
- handle = subscriber.subscribe(TestNonstringConfig.class, configId);
+ ConfigSubscriber subscriber = new ConfigSubscriber();
+ ConfigHandle<TestNonstringConfig> handle = subscriber.subscribe(TestNonstringConfig.class, configId);
subscriber.nextConfig();
handle.getConfig();
}
diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigSetSubscriptionTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigSetSubscriptionTest.java
index d17a2ff61f4..21cdfbe7d30 100644
--- a/config/src/test/java/com/yahoo/config/subscription/ConfigSetSubscriptionTest.java
+++ b/config/src/test/java/com/yahoo/config/subscription/ConfigSetSubscriptionTest.java
@@ -33,8 +33,8 @@ public class ConfigSetSubscriptionTest {
configSet,
new TimingValues());
- assertTrue(c1.equals(c1));
- assertFalse(c1.equals(c2));
+ assertEquals(c1, c1);
+ assertNotEquals(c1, c2);
}
@Test(expected = IllegalArgumentException.class)
diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigSourceSetTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigSourceSetTest.java
index b45f30d244d..38d4a6a4571 100755
--- a/config/src/test/java/com/yahoo/config/subscription/ConfigSourceSetTest.java
+++ b/config/src/test/java/com/yahoo/config/subscription/ConfigSourceSetTest.java
@@ -15,25 +15,24 @@ import static org.junit.Assert.*;
public class ConfigSourceSetTest {
@Test
public void testEquals() {
- assertTrue(new ConfigSourceSet().equals(new ConfigSourceSet()));
- assertFalse(new ConfigSourceSet().equals(new ConfigSourceSet(new String[]{"a"})));
+ assertEquals(new ConfigSourceSet(), new ConfigSourceSet());
+ assertNotEquals(new ConfigSourceSet(), new ConfigSourceSet(new String[]{"a"}));
- assertTrue(new ConfigSourceSet(new String[]{"a"}).equals(new ConfigSourceSet(new String[]{"a"})));
- assertTrue(new ConfigSourceSet(new String[]{"a"}).equals(new ConfigSourceSet(new String[]{" A "})));
- assertTrue(new ConfigSourceSet(new String[]{"a"}).equals(new ConfigSourceSet(new String[]{"A", "a"})));
- assertTrue(new ConfigSourceSet(new String[]{"A"}).equals(new ConfigSourceSet(new String[]{"a", " a "})));
+ assertEquals(new ConfigSourceSet(new String[]{"a"}), new ConfigSourceSet(new String[]{"a"}));
+ assertEquals(new ConfigSourceSet(new String[]{"a"}), new ConfigSourceSet(new String[]{" A "}));
+ assertEquals(new ConfigSourceSet(new String[]{"a"}), new ConfigSourceSet(new String[]{"A", "a"}));
+ assertEquals(new ConfigSourceSet(new String[]{"A"}), new ConfigSourceSet(new String[]{"a", " a "}));
- assertFalse(new ConfigSourceSet(new String[]{"a"}).equals(new ConfigSourceSet(new String[]{"b"})));
- assertFalse(new ConfigSourceSet(new String[]{"a"}).equals(new ConfigSourceSet(new String[]{"a", "b"})));
+ assertNotEquals(new ConfigSourceSet(new String[]{"a"}), new ConfigSourceSet(new String[]{"b"}));
+ assertNotEquals(new ConfigSourceSet(new String[]{"a"}), new ConfigSourceSet(new String[]{"a", "b"}));
- assertTrue(new ConfigSourceSet(new String[]{"a", "b"}).equals(new ConfigSourceSet(new String[]{"a", "b"})));
- assertTrue(new ConfigSourceSet(new String[]{"b", "a"}).equals(new ConfigSourceSet(new String[]{"a", "b"})));
- assertTrue(new ConfigSourceSet(new String[]{"A", " b"}).equals(new ConfigSourceSet(new String[]{"a ", "B"})));
- assertTrue(new ConfigSourceSet(new String[]{"b", "a", "c"})
- .equals(new ConfigSourceSet(new String[]{"a", "b", "c"})));
+ assertEquals(new ConfigSourceSet(new String[]{"a", "b"}), new ConfigSourceSet(new String[]{"a", "b"}));
+ assertEquals(new ConfigSourceSet(new String[]{"b", "a"}), new ConfigSourceSet(new String[]{"a", "b"}));
+ assertEquals(new ConfigSourceSet(new String[]{"A", " b"}), new ConfigSourceSet(new String[]{"a ", "B"}));
+ assertEquals(new ConfigSourceSet(new String[]{"b", "a", "c"}), new ConfigSourceSet(new String[]{"a", "b", "c"}));
- assertFalse(new ConfigSourceSet(new String[]{"a", "b"}).equals(new ConfigSourceSet(new String[]{"b", "c"})));
- assertFalse(new ConfigSourceSet().equals("foo"));
+ assertNotEquals(new ConfigSourceSet(new String[]{"a", "b"}), new ConfigSourceSet(new String[]{"b", "c"}));
+ assertNotEquals("foo", new ConfigSourceSet());
}
@Test
diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigSubscriptionTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigSubscriptionTest.java
index 3aa422eb116..933a9fd130a 100644
--- a/config/src/test/java/com/yahoo/config/subscription/ConfigSubscriptionTest.java
+++ b/config/src/test/java/com/yahoo/config/subscription/ConfigSubscriptionTest.java
@@ -3,7 +3,6 @@ package com.yahoo.config.subscription;
import com.yahoo.config.ConfigInstance;
import com.yahoo.config.ConfigurationRuntimeException;
-import com.yahoo.config.subscription.impl.GenericConfigHandle;
import com.yahoo.foo.SimpletypesConfig;
import com.yahoo.foo.AppConfig;
import com.yahoo.config.subscription.impl.ConfigSubscription;
@@ -59,8 +58,8 @@ public class ConfigSubscriptionTest {
configSet,
new TimingValues());
- assertTrue(c1.equals(c1));
- assertFalse(c1.equals(c2));
+ assertEquals(c1, c1);
+ assertNotEquals(c1, c2);
}
@Test
diff --git a/config/src/test/java/com/yahoo/config/subscription/FunctionTest.java b/config/src/test/java/com/yahoo/config/subscription/FunctionTest.java
index 5c535f6a5fa..b32cdcd8c16 100644
--- a/config/src/test/java/com/yahoo/config/subscription/FunctionTest.java
+++ b/config/src/test/java/com/yahoo/config/subscription/FunctionTest.java
@@ -178,7 +178,7 @@ public class FunctionTest {
}
//System.out.println("Config lacking " + param + "-> " + config + "\n");
try {
- ConfigGetter<FunctionTestConfig> getter = new ConfigGetter<FunctionTestConfig>(FunctionTestConfig.class);
+ ConfigGetter<FunctionTestConfig> getter = new ConfigGetter<>(FunctionTestConfig.class);
getter.getConfig("raw:\n" + config);
if (isArray) {
// Arrays are empty by default
@@ -210,7 +210,7 @@ public class FunctionTest {
assertEquals(1, config.boolarr().size());
assertEquals(1, config.boolarr().size()); // new api with accessor for a List of the original Java type
assertEquals(false, config.boolarr().get(0)); // new List api
- assertEquals(false, config.boolarr(0)); // short-hand
+ assertFalse(config.boolarr(0)); // short-hand
assertEquals(0, config.intarr().size());
assertEquals(2, config.longarr().size());
assertEquals(Long.MAX_VALUE, config.longarr(0));
@@ -239,9 +239,9 @@ public class FunctionTest {
assertEquals("inner1", config.rootStruct().inner1().name());
assertEquals(12, config.rootStruct().inner1().index());
assertEquals(2, config.rootStruct().innerArr().size());
- assertEquals(true, config.rootStruct().innerArr(0).boolVal());
+ assertTrue(config.rootStruct().innerArr(0).boolVal());
assertEquals("deep", config.rootStruct().innerArr(0).stringVal());
- assertEquals(false, config.rootStruct().innerArr(1).boolVal());
+ assertFalse(config.rootStruct().innerArr(1).boolVal());
assertEquals("blue a=\"escaped\"", config.rootStruct().innerArr(1).stringVal());
assertEquals(2, config.myarray().size()); // new List api
diff --git a/config/src/test/java/com/yahoo/vespa/config/ConfigBuilderMergeTest.java b/config/src/test/java/com/yahoo/vespa/config/ConfigBuilderMergeTest.java
index c1e5cfb8f0f..c77985c91d8 100644
--- a/config/src/test/java/com/yahoo/vespa/config/ConfigBuilderMergeTest.java
+++ b/config/src/test/java/com/yahoo/vespa/config/ConfigBuilderMergeTest.java
@@ -82,10 +82,10 @@ public class ConfigBuilderMergeTest {
public void require_that_struct_fields_are_overwritten() {
String name1 = "foo";
String gender1 = "MALE";
- String emails1[] = { "foo@bar", "bar@foo" };
+ String[] emails1 = {"foo@bar", "bar@foo"};
String name2 = "bar";
String gender2 = "FEMALE";
- String emails2[] = { "foo@bar", "bar@foo" };
+ String[] emails2 = {"foo@bar", "bar@foo"};
StructtypesConfig.Builder b1 = createSimpleStructBuilder(name1, gender1, emails1);
StructtypesConfig.Builder b2 = createSimpleStructBuilder(name2, gender2, emails2);
ConfigInstanceUtil.setValues(b1, b2);
diff --git a/config/src/test/java/com/yahoo/vespa/config/ConfigCacheKeyTest.java b/config/src/test/java/com/yahoo/vespa/config/ConfigCacheKeyTest.java
index bb4505b1250..bb65fdaa153 100755
--- a/config/src/test/java/com/yahoo/vespa/config/ConfigCacheKeyTest.java
+++ b/config/src/test/java/com/yahoo/vespa/config/ConfigCacheKeyTest.java
@@ -24,11 +24,11 @@ public class ConfigCacheKeyTest {
ConfigCacheKey k5 = new ConfigCacheKey("foo", "id", "ns_1", null); // test with null defMd5
final ConfigKey<?> configKey = new ConfigKey<>("foo", "id", "ns");
ConfigCacheKey k1_2 = new ConfigCacheKey(configKey, defMd5);
- assertTrue(k1.equals(k1));
- assertTrue(k1.equals(k1_2));
- assertTrue(k1.equals(k2));
- assertFalse(k3.equals(k2));
- assertFalse(k4.equals(k1));
+ assertEquals(k1, k1);
+ assertEquals(k1, k1_2);
+ assertEquals(k1, k2);
+ assertNotEquals(k3, k2);
+ assertNotEquals(k4, k1);
assertThat(k1.hashCode(), is(k2.hashCode()));
assertThat(k1.getDefMd5(), is(defMd5));
assertThat(k1.toString(), is(configKey.toString() + "," + defMd5));
diff --git a/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionBuilderTest.java b/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionBuilderTest.java
index f1220143b28..dba73223097 100644
--- a/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionBuilderTest.java
+++ b/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionBuilderTest.java
@@ -26,7 +26,7 @@ public class ConfigDefinitionBuilderTest {
@Test
// TODO Test ranges
- public void testCreateConfigDefinition() throws IOException, InterruptedException {
+ public void testCreateConfigDefinition() throws IOException {
File defFile = new File(DEF_NAME);
DefParser defParser = new DefParser(defFile.getName(), new FileReader(defFile));
CNode root = defParser.getTree();
@@ -122,8 +122,8 @@ public class ConfigDefinitionBuilderTest {
assertEquals(def.getLeafMapDefs().get("intMap").getTypeSpec().getType(), "int");
assertEquals(def.getLeafMapDefs().get("stringMap").getTypeSpec().getType(), "string");
assertEquals(def.getStructMapDefs().size(), 1);
- assertEquals(def.getStructMapDefs().get("myStructMap").getIntDefs().get("myInt").getDefVal(), null);
- assertEquals(def.getStructMapDefs().get("myStructMap").getStringDefs().get("myString").getDefVal(), null);
+ assertNull(def.getStructMapDefs().get("myStructMap").getIntDefs().get("myInt").getDefVal());
+ assertNull(def.getStructMapDefs().get("myStructMap").getStringDefs().get("myString").getDefVal());
assertEquals(def.getStructMapDefs().get("myStructMap").getIntDefs().get("myIntDef").getDefVal(), (Integer)56);
assertEquals(def.getStructMapDefs().get("myStructMap").getStringDefs().get("myStringDef").getDefVal(), "g");
diff --git a/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionKeyTest.java b/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionKeyTest.java
index c4024a73c97..4f5291c6a36 100644
--- a/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionKeyTest.java
+++ b/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionKeyTest.java
@@ -20,10 +20,10 @@ public class ConfigDefinitionKeyTest {
assertEquals("foo", def1.getName());
assertEquals("fuz", def1.getNamespace());
- assertTrue(def1.equals(def1));
- assertFalse(def1.equals(def2));
- assertFalse(def1.equals(new Object()));
- assertTrue(def2.equals(def2));
+ assertEquals(def1, def1);
+ assertNotEquals(def1, def2);
+ assertNotEquals(def1, new Object());
+ assertEquals(def2, def2);
}
@Test
diff --git a/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionSetTest.java b/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionSetTest.java
deleted file mode 100644
index 1dc70f3c1a5..00000000000
--- a/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionSetTest.java
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.config;
-
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-/**
- * Class to hold config definitions and resolving requests for the correct definition
- *
- * @author hmusum
- */
-public class ConfigDefinitionSetTest {
-
- @Test
- public void testBasic() {
- ConfigDefinitionSet configDefinitionSet = new ConfigDefinitionSet();
- ConfigDefinition def1 = new ConfigDefinition("foo", "1");
- ConfigDefinition def2 = new ConfigDefinition("foo", "1", "namespace1");
- ConfigDefinition def3 = new ConfigDefinition("foo", "1", "namespace2");
- final ConfigDefinitionKey key1 = new ConfigDefinitionKey(def1.getName(), def1.getNamespace());
- configDefinitionSet.add(key1, def1);
- ConfigDefinitionKey key2 = new ConfigDefinitionKey(def2.getName(), def2.getNamespace());
- configDefinitionSet.add(key2, def2);
- ConfigDefinitionKey key3 = new ConfigDefinitionKey(def3.getName(), def3.getNamespace());
- configDefinitionSet.add(key3, def3);
- assertEquals(3, configDefinitionSet.size());
- assertEquals(def1, configDefinitionSet.get(key1));
- assertEquals(def2, configDefinitionSet.get(key2));
- assertEquals(def3, configDefinitionSet.get(key3));
-
- String str = configDefinitionSet.toString();
- assertTrue(str.contains("namespace1.foo"));
- assertTrue(str.contains("namespace2.foo"));
- assertTrue(str.contains("config.foo"));
- }
-
- @Test
- public void testFallbackToDefaultNamespace() {
- ConfigDefinitionSet configDefinitionSet = new ConfigDefinitionSet();
- ConfigDefinition def1 = new ConfigDefinition("foo", "1");
- ConfigDefinition def2 = new ConfigDefinition("bar", "1", "namespace");
-
- configDefinitionSet.add(new ConfigDefinitionKey(def1.getName(), def1.getNamespace()), def1);
- configDefinitionSet.add(new ConfigDefinitionKey(def2.getName(), def2.getNamespace()), def2);
-
- // fallback to default namespace
- assertEquals(def1, configDefinitionSet.get(new ConfigDefinitionKey("foo", "namespace")));
- // Should not fallback to some other config with same name, but different namespace (not default namespace)
- assertNull(configDefinitionSet.get(new ConfigDefinitionKey("bar", "someothernamespace")));
- }
-
-}
diff --git a/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionTest.java b/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionTest.java
index 01bdf4e0ad8..88e7e766974 100755
--- a/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionTest.java
+++ b/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionTest.java
@@ -47,23 +47,8 @@ public class ConfigDefinitionTest {
}
@Test
- public void testDefNumberCompare() {
- ConfigDefinition df1 = new ConfigDefinition("d", "25");
- ConfigDefinition df2 = new ConfigDefinition("d", "5");
- ConfigDefinition df3 = new ConfigDefinition("d", "1-1");
- ConfigDefinition df4 = new ConfigDefinition("d", "0-2-3");
- ConfigDefinition df5 = new ConfigDefinition("d", "1");
- ConfigDefinition df6 = new ConfigDefinition("d", "1-0");
- assertTrue(df1.compareTo(df2) > 0);
- assertTrue(df2.compareTo(df4) > 0);
- assertEquals(1, df3.compareTo(df4));
- assertEquals(-1, df4.compareTo(df5));
- assertEquals(0, df5.compareTo(df6));
- }
-
- @Test
public void testIntDefaultValues() {
- ConfigDefinition def = new ConfigDefinition("foo", "1");
+ ConfigDefinition def = new ConfigDefinition("foo", "1", "namespace1");
def.addIntDef("foo");
def.addIntDef("bar", 0);
@@ -83,7 +68,7 @@ public class ConfigDefinitionTest {
@Test
public void testLongDefaultValues() {
- ConfigDefinition def = new ConfigDefinition("foo", "1");
+ ConfigDefinition def = new ConfigDefinition("foo", "1", "namespace1");
def.addLongDef("foo");
def.addLongDef("bar", 1234567890123L);
@@ -102,7 +87,7 @@ public class ConfigDefinitionTest {
@Test
@SuppressWarnings("serial")
public void testDefaultsPayloadMap() {
- ConfigDefinition def = new ConfigDefinition("foo", "1");
+ ConfigDefinition def = new ConfigDefinition("foo", "1", "namespace1");
def.addStringDef("mystring");
def.addStringDef("mystringdef", "foo");
def.addBoolDef("mybool");
@@ -113,8 +98,14 @@ public class ConfigDefinitionTest {
def.addLongDef("mylongdef", 11L);
def.addDoubleDef("mydouble");
def.addDoubleDef("mydoubledef", 2d);
- EnumDef ed = new EnumDef(new ArrayList<String>(){{add("a1"); add("a2");}}, null);
- EnumDef eddef = new EnumDef(new ArrayList<String>(){{add("a11"); add("a22");}}, "a22");
+ EnumDef ed = new EnumDef(new ArrayList<>() {{
+ add("a1");
+ add("a2");
+ }}, null);
+ EnumDef eddef = new EnumDef(new ArrayList<>() {{
+ add("a11");
+ add("a22");
+ }}, "a22");
def.addEnumDef("myenum", ed);
def.addEnumDef("myenumdef", eddef);
def.addReferenceDef("myref");
diff --git a/config/src/test/java/com/yahoo/vespa/config/ConfigFileFormatterTest.java b/config/src/test/java/com/yahoo/vespa/config/ConfigFileFormatterTest.java
index 27d907d279d..3cc030d944b 100644
--- a/config/src/test/java/com/yahoo/vespa/config/ConfigFileFormatterTest.java
+++ b/config/src/test/java/com/yahoo/vespa/config/ConfigFileFormatterTest.java
@@ -17,6 +17,7 @@ import org.junit.Ignore;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
@@ -137,7 +138,7 @@ public class ConfigFileFormatterTest {
InnerCNode def = new DefParser("simpletypes", new StringReader(StringUtilities.implode(SimpletypesConfig.CONFIG_DEF_SCHEMA, "\n"))).getTree();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
new ConfigFileFormat(def).encode(baos, slime);
- assertThat(baos.toString("UTF-8"), is("enumval null\nintval null\nlongval null\nboolval false\ndoubleval null\n"));
+ assertThat(baos.toString(StandardCharsets.UTF_8), is("enumval null\nintval null\nlongval null\nboolval false\ndoubleval null\n"));
}
// TODO: Reenable this when we can reenable typechecking.
@@ -160,7 +161,7 @@ public class ConfigFileFormatterTest {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InnerCNode def = new DefParser("simpletypes", new StringReader(StringUtilities.implode(SimpletypesConfig.CONFIG_DEF_SCHEMA, "\n"))).getTree();
new ConfigFileFormat(def).encode(baos, slime);
- assertThat(baos.toString("UTF-8"), is("stringval \"" + value + "\"\n"));
+ assertThat(baos.toString(StandardCharsets.UTF_8), is("stringval \"" + value + "\"\n"));
}
@Test
@@ -326,7 +327,7 @@ public class ConfigFileFormatterTest {
assertThat(Utf8.toString(baos.toByteArray()), is("stringval \"" + input + "\"\n"));
}
- public static String bytesToHexString(byte[] bytes){
+ private static String bytesToHexString(byte[] bytes){
StringBuilder sb = new StringBuilder();
for(byte b : bytes){
sb.append(String.format("%02x", b&0xff));
diff --git a/config/src/test/java/com/yahoo/vespa/config/ConfigHelperTest.java b/config/src/test/java/com/yahoo/vespa/config/ConfigHelperTest.java
deleted file mode 100644
index 040cb06d05f..00000000000
--- a/config/src/test/java/com/yahoo/vespa/config/ConfigHelperTest.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.config;
-
-import com.yahoo.config.subscription.ConfigSourceSet;
-import org.junit.Test;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-
-/**
- * @author hmusum
- * @since 5.1.9
- */
-public class ConfigHelperTest {
-
- @Test
- public void basic() {
- ConfigSourceSet configSourceSet = new ConfigSourceSet("host.com");
- ConfigHelper helper = new ConfigHelper(configSourceSet);
- assertThat(helper.getConfigSourceSet(), is(configSourceSet));
- assertThat(helper.getConnectionPool().getAllSourceAddresses(), is("host.com"));
- assertThat(helper.getTimingValues().getSubscribeTimeout(), is(new TimingValues().getSubscribeTimeout()));
-
- // Specify timing values
- TimingValues tv = new TimingValues();
- tv.setSubscribeTimeout(11L);
- helper = new ConfigHelper(configSourceSet, tv);
- assertThat(helper.getTimingValues().getSubscribeTimeout(), is(tv.getSubscribeTimeout()));
- }
-
-}
diff --git a/config/src/test/java/com/yahoo/vespa/config/ConfigKeyTest.java b/config/src/test/java/com/yahoo/vespa/config/ConfigKeyTest.java
index 0642d5733c9..427014316cf 100644
--- a/config/src/test/java/com/yahoo/vespa/config/ConfigKeyTest.java
+++ b/config/src/test/java/com/yahoo/vespa/config/ConfigKeyTest.java
@@ -27,16 +27,16 @@ public class ConfigKeyTest {
assertEquals(key1, key2);
ConfigKey<?> key3 = new ConfigKey<>("foo", "a/b/c/d", namespace);
- assertTrue(!key1.equals(key3));
assertFalse(key1.equals(key3));
+ assertNotEquals(key1, key3);
assertEquals("a/b/c", new ConfigKey<>("foo", "a/b/c", namespace).getConfigId());
assertEquals("a", new ConfigKey<>("foo", "a", namespace).getConfigId());
assertEquals("", new ConfigKey<>("foo", "", namespace).getConfigId());
- assertTrue(key1.equals(key1));
- assertFalse(key1.equals(key3));
- assertFalse(key1.equals(new Object()));
+ assertEquals(key1, key1);
+ assertNotEquals(key1, key3);
+ assertNotEquals(key1, new Object());
ConfigKey<?> key4 = new ConfigKey<>("myConfig", null, namespace);
assertEquals("", key4.getConfigId());
@@ -70,11 +70,11 @@ public class ConfigKeyTest {
ConfigKey<?> noNamespace = new ConfigKey<>("name", "id", null);
ConfigKey<?> namespaceFoo = new ConfigKey<>("name", "id", "foo");
ConfigKey<?> namespaceBar = new ConfigKey<>("name", "id", "bar");
- assertTrue(noNamespace.equals(noNamespace));
- assertTrue(namespaceFoo.equals(namespaceFoo));
- assertFalse(noNamespace.equals(namespaceFoo));
- assertFalse(namespaceFoo.equals(noNamespace));
- assertFalse(namespaceFoo.equals(namespaceBar));
+ assertEquals(noNamespace, noNamespace);
+ assertEquals(namespaceFoo, namespaceFoo);
+ assertNotEquals(noNamespace, namespaceFoo);
+ assertNotEquals(namespaceFoo, noNamespace);
+ assertNotEquals(namespaceFoo, namespaceBar);
assertEquals(noNamespace.getNamespace(), CNode.DEFAULT_NAMESPACE);
assertEquals(namespaceBar.getNamespace(), "bar");
}
diff --git a/config/src/test/java/com/yahoo/vespa/config/ConfigPayloadTest.java b/config/src/test/java/com/yahoo/vespa/config/ConfigPayloadTest.java
index 63a55d20edf..f1b0adc03e7 100644
--- a/config/src/test/java/com/yahoo/vespa/config/ConfigPayloadTest.java
+++ b/config/src/test/java/com/yahoo/vespa/config/ConfigPayloadTest.java
@@ -14,7 +14,6 @@ import com.yahoo.slime.Slime;
import com.yahoo.text.StringUtilities;
import org.junit.Test;
-import java.io.IOException;
import java.io.StringReader;
import static org.hamcrest.CoreMatchers.is;
@@ -31,13 +30,13 @@ import static org.junit.Assert.assertTrue;
public class ConfigPayloadTest {
@Test
- public void test_simple_builder() throws Exception {
+ public void test_simple_builder() {
SimpletypesConfig config = createSimpletypesConfig("stringval", "abcde");
assertThat(config.stringval(), is("abcde"));
}
@Test
- public void require_that_arrays_are_built() throws Exception {
+ public void require_that_arrays_are_built() {
AppConfig config = createAppConfig("foo", "4", new String[] { "bar", "baz", "bim" });
assertThat(config.message(), is("foo"));
assertThat(config.times(), is(4));
@@ -47,7 +46,7 @@ public class ConfigPayloadTest {
}
@Test
- public void test_int_leaf_legal() throws Exception {
+ public void test_int_leaf_legal() {
SimpletypesConfig config = createSimpletypesConfig("intval", "0");
assertThat(config.intval(), is(0));
config = createSimpletypesConfig("intval", String.valueOf(Integer.MIN_VALUE));
@@ -61,27 +60,27 @@ public class ConfigPayloadTest {
}
@Test (expected = RuntimeException.class)
- public void test_int_leaf_too_large() throws Exception {
- createSimpletypesConfig("intval", String.valueOf(Integer.MAX_VALUE) + "00");
+ public void test_int_leaf_too_large() {
+ createSimpletypesConfig("intval", Integer.MAX_VALUE + "00");
}
@Test (expected = RuntimeException.class)
- public void test_int_leaf_too_large_neg() throws Exception {
- createSimpletypesConfig("intval", String.valueOf(Integer.MIN_VALUE) + "00");
+ public void test_int_leaf_too_large_neg() {
+ createSimpletypesConfig("intval", Integer.MIN_VALUE + "00");
}
@Test(expected=RuntimeException.class)
- public void test_int_leaf_illegal_string() throws Exception {
+ public void test_int_leaf_illegal_string() {
createSimpletypesConfig("intval", "illegal");
}
@Test(expected=RuntimeException.class)
- public void test_int_leaf_illegal_string_suffix() throws Exception {
+ public void test_int_leaf_illegal_string_suffix() {
createSimpletypesConfig("intval", "123illegal");
}
@Test(expected=RuntimeException.class)
- public void test_int_leaf_illegal_string_prefix() throws Exception {
+ public void test_int_leaf_illegal_string_prefix() {
createSimpletypesConfig("intval", "illegal123");
}
@@ -95,7 +94,7 @@ public class ConfigPayloadTest {
@Test
- public void test_long_leaf() throws Exception {
+ public void test_long_leaf() {
SimpletypesConfig config = createSimpletypesConfig("longval", "0");
assertThat(config.longval(), is(0L));
config = createSimpletypesConfig("longval", String.valueOf(Long.MIN_VALUE));
@@ -109,22 +108,22 @@ public class ConfigPayloadTest {
}
@Test(expected = RuntimeException.class)
- public void test_long_leaf_illegal_string() throws Exception {
+ public void test_long_leaf_illegal_string() {
createSimpletypesConfig("longval", "illegal");
}
@Test (expected = RuntimeException.class)
- public void test_long_leaf_too_large() throws Exception {
- createSimpletypesConfig("longval", String.valueOf(Long.MAX_VALUE) + "00");
+ public void test_long_leaf_too_large() {
+ createSimpletypesConfig("longval", Long.MAX_VALUE + "00");
}
@Test (expected = RuntimeException.class)
- public void test_long_leaf_too_large_neg() throws Exception {
- createSimpletypesConfig("longval", String.valueOf(Long.MIN_VALUE) + "00");
+ public void test_long_leaf_too_large_neg() {
+ createSimpletypesConfig("longval", Long.MIN_VALUE + "00");
}
@Test
- public void test_double_leaf() throws Exception {
+ public void test_double_leaf() {
SimpletypesConfig config = createSimpletypesConfig("doubleval", "0");
assertEquals(0.0, config.doubleval(), 0.01);
assertEquals(133.3, createSimpletypesConfig("doubleval", "133.3").doubleval(), 0.001);
@@ -135,35 +134,35 @@ public class ConfigPayloadTest {
}
@Test
- public void test_serializer() throws IOException {
+ public void test_serializer() {
ConfigPayload payload = ConfigPayload.fromInstance(new SimpletypesConfig(new SimpletypesConfig.Builder()));
assertThat(payload.toString(true), is("{\"boolval\":false,\"doubleval\":0.0,\"enumval\":\"VAL1\",\"intval\":0,\"longval\":0,\"stringval\":\"s\"}"));
}
@Test(expected=RuntimeException.class)
- public void test_double_leaf_illegal_string() throws Exception {
+ public void test_double_leaf_illegal_string() {
createSimpletypesConfig("doubleval", "illegal");
}
@Test
- public void test_double_leaf_negative_infinity() throws Exception {
+ public void test_double_leaf_negative_infinity() {
assertThat(createSimpletypesConfig("doubleval", "-Infinity").doubleval(), is(Double.NEGATIVE_INFINITY));
assertThat(createSimpletypesConfig("doubleval", "Infinity").doubleval(), is(Double.POSITIVE_INFINITY));
}
@Test
- public void test_enum_leaf() throws Exception {
+ public void test_enum_leaf() {
assertThat(createSimpletypesConfig("enumval", "VAL1").enumval(), is(SimpletypesConfig.Enumval.Enum.VAL1));
assertThat(createSimpletypesConfig("enumval", "VAL2").enumval(), is(SimpletypesConfig.Enumval.Enum.VAL2));
}
@Test(expected=RuntimeException.class)
- public void test_enum_leaf_illegal_string() throws Exception {
+ public void test_enum_leaf_illegal_string() {
createSimpletypesConfig("enumval", "ILLEGAL");
}
@Test
- public void test_bool_leaf() throws Exception {
+ public void test_bool_leaf() {
SimpletypesConfig config = createSimpletypesConfig("boolval", "true");
assertThat(config.boolval(), is(true));
config = createSimpletypesConfig("boolval", "false");
@@ -175,18 +174,18 @@ public class ConfigPayloadTest {
}
@Test// FIXME: (expected = RuntimeException.class)
- public void test_bool_leaf_illegal() throws Exception {
+ public void test_bool_leaf_illegal() {
createSimpletypesConfig("boolval", "illegal");
}
@Test
- public void test_string_illegal_value() throws Exception {
+ public void test_string_illegal_value() {
// TODO: What do we consider illegal string values?
createSimpletypesConfig("stringval", "insert_illegal_value_please");
}
@Test
- public void test_int_array() throws Exception {
+ public void test_int_array() {
// Normal behavior
ArraytypesConfig config = createArraytypesConfig("intarr", new String[] { "2", "3", "1", "-2", "5"});
assertThat(config.intarr().size(), is(5));
@@ -210,12 +209,12 @@ public class ConfigPayloadTest {
}
@Test(expected = RuntimeException.class)
- public void test_int_array_illegal() throws Exception {
+ public void test_int_array_illegal() {
createArraytypesConfig("intarr", new String[] { "2", "3", "illegal", "-2", "5"});
}
@Test
- public void test_long_array() throws Exception {
+ public void test_long_array() {
// Normal behavior
ArraytypesConfig config = createArraytypesConfig("longarr", new String[] { "2", "3", "1", "-2", "5"});
assertThat(config.longarr().size(), is(5));
@@ -239,7 +238,7 @@ public class ConfigPayloadTest {
}
@Test
- public void test_double_array() throws Exception {
+ public void test_double_array() {
// Normal behavior
ArraytypesConfig config = createArraytypesConfig("doublearr", new String[] { "2.1", "3.3", "1.5", "-2.1", "Infinity"});
assertThat(config.doublearr().size(), is(5));
@@ -251,7 +250,7 @@ public class ConfigPayloadTest {
}
@Test
- public void test_enum_array() throws Exception {
+ public void test_enum_array() {
// Normal behavior
ArraytypesConfig config = createArraytypesConfig("enumarr", new String[] { "VAL1", "VAL2", "VAL1" });
assertThat(config.enumarr().size(), is(3));
@@ -261,7 +260,7 @@ public class ConfigPayloadTest {
}
@Test
- public void test_simple_struct() throws Exception {
+ public void test_simple_struct() {
Slime slime = new Slime();
addStructFields(slime.setObject().setObject("simple"), "foobar", "MALE", new String[] { "foo@bar", "bar@foo" });
StructtypesConfig config = new ConfigPayload(slime).toInstance(StructtypesConfig.class, "");
@@ -273,7 +272,7 @@ public class ConfigPayloadTest {
}
@Test
- public void test_simple_struct_arrays() throws Exception {
+ public void test_simple_struct_arrays() {
StructtypesConfig config = createStructtypesConfigArray(new String[] { "foo", "bar" },
new String[] { "MALE", "FEMALE" });
assertThat(config.simplearr(0).name(), is("foo"));
@@ -284,7 +283,7 @@ public class ConfigPayloadTest {
@Test
- public void test_nested_struct() throws Exception {
+ public void test_nested_struct() {
StructtypesConfig config = createStructtypesConfigNested("foo", "FEMALE");
assertThat(config.nested().inner().name(), is("foo"));
assertThat(config.nested().inner().gender(), is(StructtypesConfig.Nested.Inner.Gender.Enum.FEMALE));
@@ -293,7 +292,7 @@ public class ConfigPayloadTest {
@Test
- public void test_nested_struct_array() throws Exception {
+ public void test_nested_struct_array() {
String [] names = { "foo" ,"bar" };
String [] genders = { "FEMALE", "MALE" };
String [][] emails = {
@@ -314,7 +313,7 @@ public class ConfigPayloadTest {
@Test
- public void test_complex_struct_array() throws Exception {
+ public void test_complex_struct_array() {
String [][] names = {
{ "foo", "bar" },
{ "baz", "bim" }
@@ -454,19 +453,19 @@ public class ConfigPayloadTest {
}
@Test
- public void test_escaped_string() throws Exception {
+ public void test_escaped_string() {
SimpletypesConfig config = createSimpletypesConfig("stringval", "b=\"escaped\"");
assertThat(config.stringval(), is("b=\"escaped\""));
}
@Test
- public void test_unicode() throws Exception {
+ public void test_unicode() {
SimpletypesConfig config = createSimpletypesConfig("stringval", "Hei \u00E6\u00F8\u00E5 \uBC14\uB451 \u00C6\u00D8\u00C5 hallo");
assertThat(config.stringval(), is("Hei \u00E6\u00F8\u00E5 \uBC14\uB451 \u00C6\u00D8\u00C5 hallo"));
}
@Test
- public void test_empty_payload() throws Exception {
+ public void test_empty_payload() {
Slime slime = new Slime();
slime.setObject();
IntConfig config = new ConfigPayload(slime).toInstance(IntConfig.class, "");
diff --git a/config/src/test/java/com/yahoo/vespa/config/JRTConnectionPoolTest.java b/config/src/test/java/com/yahoo/vespa/config/JRTConnectionPoolTest.java
index faeedff2762..8a403b45003 100644
--- a/config/src/test/java/com/yahoo/vespa/config/JRTConnectionPoolTest.java
+++ b/config/src/test/java/com/yahoo/vespa/config/JRTConnectionPoolTest.java
@@ -13,7 +13,7 @@ import static org.junit.Assert.*;
/**
* Tests for the JRTConnectionPool class.
*
- * @author <a href="mailto:gunnarga@yahoo-inc.com">Gunnar Gauslaa Bergem</a>
+ * @author Gunnar Gauslaa Bergem
* @author hmusum
*/
public class JRTConnectionPoolTest {
diff --git a/config/src/test/java/com/yahoo/vespa/config/RawConfigTest.java b/config/src/test/java/com/yahoo/vespa/config/RawConfigTest.java
index b19da2c1689..a564fea8b2e 100644
--- a/config/src/test/java/com/yahoo/vespa/config/RawConfigTest.java
+++ b/config/src/test/java/com/yahoo/vespa/config/RawConfigTest.java
@@ -15,6 +15,7 @@ import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
@@ -75,10 +76,10 @@ public class RawConfigTest {
assertThat(config.getVespaVersion(), is(not(config3.getVespaVersion())));
// null config
- assertFalse(config.equals(null));
+ assertNotEquals(null, config);
// different type of object
- assertFalse(config.equals(key));
+ assertNotEquals(config, key);
// errors
RawConfig errorConfig1 = new RawConfig(key, defMd5, payload, configMd5, generation, false, 1, defContent, Optional.empty());
diff --git a/config/src/test/java/com/yahoo/vespa/config/protocol/PayloadTest.java b/config/src/test/java/com/yahoo/vespa/config/protocol/PayloadTest.java
index d4c63ae35cd..e5fc5190ad1 100644
--- a/config/src/test/java/com/yahoo/vespa/config/protocol/PayloadTest.java
+++ b/config/src/test/java/com/yahoo/vespa/config/protocol/PayloadTest.java
@@ -8,11 +8,10 @@ import com.yahoo.vespa.config.ConfigPayload;
import com.yahoo.vespa.config.LZ4PayloadCompressor;
import org.junit.Test;
-import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
/**
* @author Ulf Lilleengen
@@ -58,19 +57,15 @@ public class PayloadTest {
Payload h = null;
Payload i = null;
Payload j = null;
- try {
- g = Payload.from(new Utf8Array(foo1.getBytes("UTF-8")), CompressionInfo.uncompressed());
- h = Payload.from(new Utf8Array(foo1.getBytes("UTF-8")), CompressionInfo.uncompressed());
+ g = Payload.from(new Utf8Array(foo1.getBytes(StandardCharsets.UTF_8)), CompressionInfo.uncompressed());
+ h = Payload.from(new Utf8Array(foo1.getBytes(StandardCharsets.UTF_8)), CompressionInfo.uncompressed());
- LZ4PayloadCompressor compressor = new LZ4PayloadCompressor();
- CompressionInfo info = CompressionInfo.create(CompressionType.LZ4, foo2.length());
- Utf8Array compressed = new Utf8Array(compressor.compress(foo2.getBytes()));
+ LZ4PayloadCompressor compressor = new LZ4PayloadCompressor();
+ CompressionInfo info = CompressionInfo.create(CompressionType.LZ4, foo2.length());
+ Utf8Array compressed = new Utf8Array(compressor.compress(foo2.getBytes()));
- i = Payload.from(compressed, info);
- j = Payload.from(compressed, info);
- } catch (UnsupportedEncodingException e1) {
- fail();
- }
+ i = Payload.from(compressed, info);
+ j = Payload.from(compressed, info);
new EqualsTester()
.addEqualityGroup(a, b, g, h)
diff --git a/config/src/test/java/com/yahoo/vespa/config/util/ConfigUtilsTest.java b/config/src/test/java/com/yahoo/vespa/config/util/ConfigUtilsTest.java
index 0b615e59179..190c7479ee7 100644
--- a/config/src/test/java/com/yahoo/vespa/config/util/ConfigUtilsTest.java
+++ b/config/src/test/java/com/yahoo/vespa/config/util/ConfigUtilsTest.java
@@ -67,18 +67,18 @@ public class ConfigUtilsTest {
lines.add("foo=\"1#hello\"");
lines.add(""); //empty line should not affect md5sum
- assertThat(ConfigUtils.getMd5(lines), is(expectedMd5));
+ assertThat(getMd5(lines), is(expectedMd5));
lines.clear();
// Check that comment character in string leads to a different md5 than the original
lines.add("foo=\"1#hello and some more\"");
- String md5 = ConfigUtils.getMd5(lines);
+ String md5 = getMd5(lines);
assertThat(md5, is(not(expectedMd5)));
// Check that added characters aft comment character in string leads to a different md5 than above
lines.add("foo=\"1#hello and some more and even more\"");
- assertThat(ConfigUtils.getMd5(lines), is(not(md5)));
+ assertThat(getMd5(lines), is(not(md5)));
}
@Test
@@ -114,22 +114,6 @@ public class ConfigUtilsTest {
}
@Test
- public void testGetVersion() {
- StringReader reader = new StringReader("version=1\nint a default=0");
- assertThat(ConfigUtils.getDefVersion(reader), is("1"));
-
- // no version
- reader = new StringReader("int a default=0");
- assertThat(ConfigUtils.getDefVersion(reader), is(""));
-
- // namespace and version
- reader = new StringReader("version=1\nnamespace=foo\nint a default=0");
- assertThat(ConfigUtils.getDefVersion(reader), is("1"));
- reader = new StringReader("namespace=foo\nversion=1\nint a default=0");
- assertThat(ConfigUtils.getDefVersion(reader), is("1"));
- }
-
- @Test
public void testGetNamespace() {
StringReader reader = new StringReader("version=1\nnamespace=a\nint a default=0");
assertThat(ConfigUtils.getDefNamespace(reader), is("a"));
@@ -154,26 +138,6 @@ public class ConfigUtilsTest {
}
@Test
- public void testGetNameCommaVersion() {
- String nameCommaversion = "foo,1";
- Tuple2<String, String> tuple = ConfigUtils.getNameAndVersionFromString(nameCommaversion);
- assertThat(tuple.first, is("foo"));
- assertThat(tuple.second, is("1"));
-
- // no version
- nameCommaversion = "foo";
- tuple = ConfigUtils.getNameAndVersionFromString(nameCommaversion);
- assertThat(tuple.first, is("foo"));
- assertThat(tuple.second, is(""));
-
- // no name
- nameCommaversion = ",1";
- tuple = ConfigUtils.getNameAndVersionFromString(nameCommaversion);
- assertThat(tuple.first, is(""));
- assertThat(tuple.second, is("1"));
- }
-
- @Test
public void testNamespaceDotNames() {
String namespaceDotName = "foo.bar";
Tuple2<String, String> tuple = ConfigUtils.getNameAndNamespaceFromString(namespaceDotName);
@@ -205,29 +169,18 @@ public class ConfigUtilsTest {
}
@Test
- public void testGetConfigDefinitionKey() {
- String input = "foo.bar";
- ConfigDefinitionKey def = ConfigUtils.getConfigDefinitionKeyFromString(input);
- assertThat(def.getName(), is("bar"));
- assertThat(def.getNamespace(), is("foo"));
-
- input = "foo.bar.1";
- def = ConfigUtils.getConfigDefinitionKeyFromString(input);
- assertThat(def.getName(), is("bar"));
- assertThat(def.getNamespace(), is("foo"));
+ public void testCreateConfigDefinitionKeyFromZKString() {
+ ConfigDefinitionKey def1 = ConfigUtils.createConfigDefinitionKeyFromZKString("bar.foo,1");
+ assertThat(def1.getName(), is("foo"));
+ assertThat(def1.getNamespace(), is("bar"));
- input = "foo.bar.qux.2";
- def = ConfigUtils.getConfigDefinitionKeyFromString(input);
- assertThat(def.getName(), is("qux"));
- assertThat(def.getNamespace(), is("foo.bar"));
- }
+ ConfigDefinitionKey def2 = ConfigUtils.createConfigDefinitionKeyFromZKString("bar.foo,");
+ assertThat(def2.getName(), is("foo"));
+ assertThat(def2.getNamespace(), is("bar"));
- @Test
- public void testCreateConfigDefinitionKeyFromZKString() {
- String input = "bar.foo,1";
- ConfigDefinitionKey def = ConfigUtils.createConfigDefinitionKeyFromZKString(input);
- assertThat(def.getName(), is("foo"));
- assertThat(def.getNamespace(), is("bar"));
+ ConfigDefinitionKey def3 = ConfigUtils.createConfigDefinitionKeyFromZKString("bar.foo");
+ assertThat(def3.getName(), is("foo"));
+ assertThat(def3.getNamespace(), is("bar"));
}
@Test
@@ -270,4 +223,23 @@ public class ConfigUtilsTest {
assertThat(def.getNamespace(), is("mynamespace"));
}
+ /**
+ * Computes Md5 hash of a list of strings. The only change to input lines before
+ * computing md5 is to skip empty lines.
+ *
+ * @param lines A list of lines
+ * @return the Md5 hash of the list, with lowercase letters
+ */
+ private static String getMd5(List<String> lines) {
+ StringBuilder sb = new StringBuilder();
+ for (String line : lines) {
+ // Remove empty lines
+ line = line.trim();
+ if (line.length() > 0) {
+ sb.append(line).append("\n");
+ }
+ }
+ return ConfigUtils.getMd5(sb.toString());
+ }
+
}
diff --git a/config/src/testrun/.gitignore b/config/src/testrun/.gitignore
deleted file mode 100644
index faed45bc94a..00000000000
--- a/config/src/testrun/.gitignore
+++ /dev/null
@@ -1,10 +0,0 @@
-test-report.html
-test-report.html.*
-test.*.*.desc
-test.*.*.file.*
-test.*.*.files.html
-test.*.*.log
-tmp.*
-xsync.log
-/test.*.*.result
-Makefile
diff --git a/config/src/vespa/config/subscription/confighandle.hpp b/config/src/vespa/config/subscription/confighandle.hpp
index f79944f153a..df4f01d0b40 100644
--- a/config/src/vespa/config/subscription/confighandle.hpp
+++ b/config/src/vespa/config/subscription/confighandle.hpp
@@ -13,7 +13,7 @@ template <typename ConfigType>
std::unique_ptr<ConfigType>
ConfigHandle<ConfigType>::getConfig() const
{
- return _subscription->getConfig().newInstance<ConfigType>();
+ return _subscription->getConfig().template newInstance<ConfigType>();
}
template <typename ConfigType>
diff --git a/configgen/bin/make-config.pl b/configgen/bin/make-config.pl
deleted file mode 100755
index 13017a679e3..00000000000
--- a/configgen/bin/make-config.pl
+++ /dev/null
@@ -1,151 +0,0 @@
-#!/usr/bin/env perl
-# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#
-# This script transforms a .def file into a .h and .cpp file for config
-#
-# TODO: Remove this script and use the java code directly. BTW, why
-# does this script have the same limitations as the old make-config.pl
-# in that the .def-file must reside in the root directory? The java
-# code supports reading the .def-file from any directory. This script
-# should have the same input parameters as the java code, and just
-# map them directly to the java system properties
-
-# BEGIN perl environment bootstrap section
-# Do not edit between here and END as this section should stay identical in all scripts
-
-use File::Basename;
-use File::Path;
-
-sub findpath {
- my $myfullname = ${0};
- my($myname, $mypath) = fileparse($myfullname);
-
- return $mypath if ( $mypath && -d $mypath );
- $mypath=`pwd`;
-
- my $pwdfullname = $mypath . "/" . $myname;
- return $mypath if ( -f $pwdfullname );
- return 0;
-}
-
-# Returns the argument path if it seems to point to VESPA_HOME, 0 otherwise
-sub is_vespa_home {
- my($VESPA_HOME) = shift;
- my $COMMON_ENV="libexec/vespa/common-env.sh";
- if ( $VESPA_HOME && -d $VESPA_HOME ) {
- my $common_env = $VESPA_HOME . "/" . $COMMON_ENV;
- return $VESPA_HOME if -f $common_env;
- }
- return 0;
-}
-
-# Returns the home of Vespa, or dies if it cannot
-sub findhome {
- # Try the VESPA_HOME env variable
- return $ENV{'VESPA_HOME'} if is_vespa_home($ENV{'VESPA_HOME'});
- if ( $ENV{'VESPA_HOME'} ) { # was set, but not correctly
- die "FATAL: bad VESPA_HOME value '" . $ENV{'VESPA_HOME'} . "'\n";
- }
-
- # Try the ROOT env variable
- $ROOT = $ENV{'ROOT'};
- return $ROOT if is_vespa_home($ROOT);
-
- # Try the script location or current dir
- my $mypath = findpath();
- if ($mypath) {
- while ( $mypath =~ s|/[^/]*$|| ) {
- return $mypath if is_vespa_home($mypath);
- }
- }
- die "FATAL: Missing VESPA_HOME environment variable\n";
-}
-
-sub findhost {
- my $tmp = $ENV{'VESPA_HOSTNAME'};
- my $bin = $ENV{'VESPA_HOME'} . "/bin";
- if (!defined $tmp) {
- $tmp = `${bin}/vespa-detect-hostname || hostname -f || hostname || echo "localhost"`;
- chomp $tmp;
- }
- my $validate = "${bin}/vespa-validate-hostname";
- if (-f "${validate}") {
- system("${validate} $tmp");
- ( $? == 0 ) or die "Could not validate hostname\n";
- }
- return $tmp;
-}
-
-BEGIN {
- my $tmp = findhome();
- $ENV{'VESPA_HOME'} = $tmp;
- $tmp = findhost();
- $ENV{'VESPA_HOSTNAME'} = $tmp;
-}
-my $VESPA_HOME = $ENV{'VESPA_HOME'};
-
-# END perl environment bootstrap section
-
-use lib $ENV{'VESPA_HOME'} . '/lib/perl5/site_perl';
-use lib $ENV{'VESPA_HOME'} . '/lib64/perl5/site_perl';
-use Yahoo::Vespa::Defaults;
-readConfFile();
-
-require 5.006_001;
-use strict;
-use warnings;
-
-use Cwd 'abs_path';
-
-# Now this uses the new java codegen library. But the script still exist to
-# map be able to call java the right way, setting the necessary properties
-
-my ($root, $def) = @ARGV;
-
-if (!defined $root || !defined $def) {
- print "Usage make-config.pl <source root dir> <def file>\n";
- exit(1);
-}
-
-#print "Root: $root\n"
-# . "Def: $def\n";
-
-my $subdir = &getRelativePath($root, &getPath($def));
-
-my $cmd = "java"
- . " -Dconfig.spec=$def"
- . " -Dconfig.dest=$root"
- . " -Dconfig.lang=cppng"
- . " -Dconfig.requireNamespace=false"
- . " -Dconfig.subdir=$subdir"
- . " -Dconfig.dumpTree=false"
- . " -Xms64m -Xmx64m"
- . " -jar $VESPA_HOME/lib/jars/configgen.jar";
-
-print "Generating config: $cmd\n";
-exec($cmd);
-
-exit(0); # Will never be called due to exec above, but just to indicate end
-
-sub getRelativePath {
- my ($from, $to) = @_;
-
- $from = abs_path($from);
- $to = abs_path($to);
-
- # Escape $from so it can contain regex special characters in path
- $from =~ s/([\+\*\(\)\{\}\.\?\[\]\$\&])/\\$1/g;
-
- $to =~ /^$from\/(.*)$/
- or die "The def file must be contained within the root";
- return $1;
-}
-
-sub getPath {
- my $file = $_[0];
- if ($file =~ /^(.*)\/[^\/]*$/) {
- return $1;
- } else {
- return ".";
- }
-}
diff --git a/configgen/bin/make-configold.pl b/configgen/bin/make-configold.pl
deleted file mode 100755
index d299d09d3be..00000000000
--- a/configgen/bin/make-configold.pl
+++ /dev/null
@@ -1,150 +0,0 @@
-#!/usr/bin/env perl
-# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#
-# This script transforms a .def file into a .h and .cpp file for config
-#
-# TODO: Remove this script and use the java code directly. BTW, why
-# does this script have the same limitations as the old make-config.pl
-# in that the .def-file must reside in the root directory? The java
-# code supports reading the .def-file from any directory. This script
-# should have the same input parameters as the java code, and just
-# map them directly to the java system properties
-
-# BEGIN perl environment bootstrap section
-# Do not edit between here and END as this section should stay identical in all scripts
-
-use File::Basename;
-use File::Path;
-
-sub findpath {
- my $myfullname = ${0};
- my($myname, $mypath) = fileparse($myfullname);
-
- return $mypath if ( $mypath && -d $mypath );
- $mypath=`pwd`;
-
- my $pwdfullname = $mypath . "/" . $myname;
- return $mypath if ( -f $pwdfullname );
- return 0;
-}
-
-# Returns the argument path if it seems to point to VESPA_HOME, 0 otherwise
-sub is_vespa_home {
- my($VESPA_HOME) = shift;
- my $COMMON_ENV="libexec/vespa/common-env.sh";
- if ( $VESPA_HOME && -d $VESPA_HOME ) {
- my $common_env = $VESPA_HOME . "/" . $COMMON_ENV;
- return $VESPA_HOME if -f $common_env;
- }
- return 0;
-}
-
-# Returns the home of Vespa, or dies if it cannot
-sub findhome {
- # Try the VESPA_HOME env variable
- return $ENV{'VESPA_HOME'} if is_vespa_home($ENV{'VESPA_HOME'});
- if ( $ENV{'VESPA_HOME'} ) { # was set, but not correctly
- die "FATAL: bad VESPA_HOME value '" . $ENV{'VESPA_HOME'} . "'\n";
- }
-
- # Try the ROOT env variable
- $ROOT = $ENV{'ROOT'};
- return $ROOT if is_vespa_home($ROOT);
-
- # Try the script location or current dir
- my $mypath = findpath();
- if ($mypath) {
- while ( $mypath =~ s|/[^/]*$|| ) {
- return $mypath if is_vespa_home($mypath);
- }
- }
- die "FATAL: Missing VESPA_HOME environment variable\n";
-}
-
-sub findhost {
- my $tmp = $ENV{'VESPA_HOSTNAME'};
- my $bin = $ENV{'VESPA_HOME'} . "/bin";
- if (!defined $tmp) {
- $tmp = `${bin}/vespa-detect-hostname || hostname -f || hostname || echo "localhost"`;
- chomp $tmp;
- }
- my $validate = "${bin}/vespa-validate-hostname";
- if (-f "${validate}") {
- system("${validate} $tmp");
- ( $? == 0 ) or die "Could not validate hostname\n";
- }
- return $tmp;
-}
-
-BEGIN {
- my $tmp = findhome();
- $ENV{'VESPA_HOME'} = $tmp;
- $tmp = findhost();
- $ENV{'VESPA_HOSTNAME'} = $tmp;
-}
-my $VESPA_HOME = $ENV{'VESPA_HOME'};
-
-# END perl environment bootstrap section
-
-use lib $ENV{'VESPA_HOME'} . '/lib/perl5/site_perl';
-use lib $ENV{'VESPA_HOME'} . '/lib64/perl5/site_perl';
-use Yahoo::Vespa::Defaults;
-readConfFile();
-
-require 5.006_001;
-use strict;
-use warnings;
-
-use Cwd 'abs_path';
-
-# Now this uses the new java codegen library. But the script still exist to
-# map be able to call java the right way, setting the necessary properties
-
-my ($root, $def) = @ARGV;
-
-if (!defined $root || !defined $def) {
- print "Usage make-config.pl <source root dir> <def file>\n";
- exit(1);
-}
-
-#print "Root: $root\n"
-# . "Def: $def\n";
-
-my $subdir = &getRelativePath($root, &getPath($def));
-
-my $cmd = "java"
- . " -Dconfig.spec=$def"
- . " -Dconfig.dest=$root"
- . " -Dconfig.lang=cpp"
- . " -Dconfig.subdir=$subdir"
- . " -Dconfig.dumpTree=false"
- . " -Xms64m -Xmx64m"
- . " -jar $VESPA_HOME/lib/jars/configgen.jar";
-
-print "Generating config: $cmd\n";
-exec($cmd);
-
-exit(0); # Will never be called due to exec above, but just to indicate end
-
-sub getRelativePath {
- my ($from, $to) = @_;
-
- $from = abs_path($from);
- $to = abs_path($to);
-
- # Escape $from so it can contain regex special characters in path
- $from =~ s/([\+\*\(\)\{\}\.\?\[\]\$\&])/\\$1/g;
-
- $to =~ /^$from\/(.*)$/
- or die "The def file must be contained within the root";
- return $1;
-}
-
-sub getPath {
- my $file = $_[0];
- if ($file =~ /^(.*)\/[^\/]*$/) {
- return $1;
- } else {
- return ".";
- }
-}
diff --git a/configgen/src/main/java/com/yahoo/config/codegen/CppClassBuilder.java b/configgen/src/main/java/com/yahoo/config/codegen/CppClassBuilder.java
index 216a6b50981..bc8369677fc 100644
--- a/configgen/src/main/java/com/yahoo/config/codegen/CppClassBuilder.java
+++ b/configgen/src/main/java/com/yahoo/config/codegen/CppClassBuilder.java
@@ -272,7 +272,7 @@ public class CppClassBuilder implements ClassBuilder {
String typeName = getTypeName(child, false);
declaredTypes.add(child.getName());
if (child instanceof LeafCNode.EnumLeaf) {
- w.write(indent + "enum " + typeName + " { ");
+ w.write(indent + "enum class " + typeName + " { ");
LeafCNode.EnumLeaf leaf = (LeafCNode.EnumLeaf) child;
for (int i=0; i<leaf.getLegalValues().length; ++i) {
if (i != 0) {
@@ -313,7 +313,6 @@ public class CppClassBuilder implements ClassBuilder {
void writeHeaderFunctionDeclarations(Writer w, String className, CNode node, String indent) throws IOException {
w.write(""
+ indent + "const vespalib::string & defName() const override { return CONFIG_DEF_NAME; }\n"
- + indent + "const vespalib::string & defVersion() const { return CONFIG_DEF_VERSION; }\n"
+ indent + "const vespalib::string & defMd5() const override { return CONFIG_DEF_MD5; }\n"
+ indent + "const vespalib::string & defNamespace() const override { return CONFIG_DEF_NAMESPACE; }\n"
+ indent + "void serialize(::config::ConfigDataBuffer & __buffer) const override;\n");
@@ -624,7 +623,7 @@ public class CppClassBuilder implements ClassBuilder {
for (int i=0; i<leaf.getLegalValues().length; ++i) {
w.write(" " + (i != 0 ? "} else " : ""));
w.write("if (name == \"" + leaf.getLegalValues()[i] + "\") {\n"
- + " return " + leaf.getLegalValues()[i] + ";\n");
+ + " return " + typeName + "::" + leaf.getLegalValues()[i] + ";\n");
}
w.write(" } else {\n"
+ " throw InvalidConfigException(\"Illegal enum value '\" + name + \"'\");\n"
@@ -639,12 +638,12 @@ public class CppClassBuilder implements ClassBuilder {
+ " switch (t) {\n"
);
for (int i=0; i<leaf.getLegalValues().length; ++i) {
- w.write(" case " + leaf.getLegalValues()[i] + ": return \"" + leaf.getLegalValues()[i] + "\";\n");
+ w.write(" case " + typeName + "::" + leaf.getLegalValues()[i] + ": return \"" + leaf.getLegalValues()[i] + "\";\n");
}
w.write(" default:\n"
+ " {\n"
+ " vespalib::asciistream ost;\n"
- + " ost << \"UNKNOWN(\" << t << \")\";\n"
+ + " ost << \"UNKNOWN(\" << static_cast<int>(t) << \")\";\n"
+ " return ost.str();\n"
+ " }\n"
+ " }\n"
@@ -688,6 +687,9 @@ public class CppClassBuilder implements ClassBuilder {
} else if (child instanceof LeafCNode) { // If we have a default value, use that..
LeafCNode leaf = (LeafCNode) child;
if (leaf.getDefaultValue() != null) {
+ if (leaf.getType().equals("enum")) {
+ w.write(getTypeName(leaf, false) + "::");
+ }
w.write(getDefaultValue(leaf));
} else {
// Defines empty constructor defaults for primitives without default set
@@ -700,7 +702,7 @@ public class CppClassBuilder implements ClassBuilder {
} else if (leaf.getType().equals("string")) {
} else if (leaf.getType().equals("enum")) {
LeafCNode.EnumLeaf enumNode = (LeafCNode.EnumLeaf) leaf;
- w.write(enumNode.getLegalValues()[0]);
+ w.write(getTypeName(leaf, false) + "::" + enumNode.getLegalValues()[0]);
} else if (leaf.getType().equals("reference")) {
} else if (leaf.getType().equals("file")) {
}
@@ -1098,6 +1100,9 @@ public class CppClassBuilder implements ClassBuilder {
if (child instanceof LeafCNode && ((LeafCNode) child).getDefaultValue() != null) {
LeafCNode leaf = (LeafCNode) child;
String defaultValue = getDefaultValue(leaf);
+ if (leaf.getType().equals("enum")) {
+ defaultValue = getTypeName(leaf, false) + "::" + defaultValue;
+ }
w.write("()(" + childInspector + ", " + defaultValue + ");\n");
} else if (child instanceof InnerCNode) {
w.write("()(" + childInspector + ");\n");
diff --git a/configgen/src/main/java/com/yahoo/config/codegen/JavaClassBuilder.java b/configgen/src/main/java/com/yahoo/config/codegen/JavaClassBuilder.java
index 5c447191614..75149d7a50e 100644
--- a/configgen/src/main/java/com/yahoo/config/codegen/JavaClassBuilder.java
+++ b/configgen/src/main/java/com/yahoo/config/codegen/JavaClassBuilder.java
@@ -96,7 +96,7 @@ public class JavaClassBuilder implements ClassBuilder {
" public final static String CONFIG_DEF_MD5 = \"" + root.getMd5() + "\";\n" + //
" public final static String CONFIG_DEF_NAME = \"" + root.getName() + "\";\n" + //
" public final static String CONFIG_DEF_NAMESPACE = \"" + root.getNamespace() + "\";\n" + //
- " public final static String CONFIG_DEF_VERSION = \"" + root.getVersion() + "\";\n" + //
+ " public final static String CONFIG_DEF_VERSION = \"" + root.getVersion() + "\";\n" + // TODO: Remove on Vespa 8
" public final static String[] CONFIG_DEF_SCHEMA = {\n" + //
"" + indentCode(INDENTATION + INDENTATION, getDefSchema()) + "\n" + //
" };\n" + //
diff --git a/configgen/src/main/java/com/yahoo/config/codegen/MakeConfig.java b/configgen/src/main/java/com/yahoo/config/codegen/MakeConfig.java
index ac6bbea617e..9a7800988dc 100644
--- a/configgen/src/main/java/com/yahoo/config/codegen/MakeConfig.java
+++ b/configgen/src/main/java/com/yahoo/config/codegen/MakeConfig.java
@@ -18,13 +18,14 @@ public class MakeConfig {
classBuilder = createClassBuilder(root, nd, properties);
}
- public static ClassBuilder createClassBuilder(InnerCNode root, NormalizedDefinition nd, MakeConfigProperties properties) {
+ private static ClassBuilder createClassBuilder(InnerCNode root, NormalizedDefinition nd, MakeConfigProperties properties) {
if (isCpp(properties))
return new CppClassBuilder(root, nd, properties.destDir, properties.dirInRoot);
else
return new JavaClassBuilder(root, nd, properties.destDir, properties.javaPackagePrefix);
}
+ @SuppressWarnings("WeakerAccess") // Used by ConfigGenMojo
public static boolean makeConfig(MakeConfigProperties properties) throws FileNotFoundException {
for (File specFile : properties.specFiles) {
String name = specFile.getName();
@@ -49,7 +50,7 @@ public class MakeConfig {
/**
* Generates the code and print it to this.out.
*/
- void buildClasses() {
+ private void buildClasses() {
classBuilder.createConfigClasses();
}
@@ -58,7 +59,7 @@ public class MakeConfig {
out.println(" (default language for generated code is Java)");
}
- public static void main(String[] args) throws IOException, InterruptedException {
+ public static void main(String[] args) throws IOException {
try {
MakeConfigProperties props = new MakeConfigProperties();
boolean success = makeConfig(props);
@@ -81,7 +82,7 @@ public class MakeConfig {
}
private static boolean isCpp(MakeConfigProperties properties) {
- return (properties.language.equals("cppng") || properties.language.equals("cpp"));
+ return properties.language.equals("cpp");
}
// The Exceptions class below is copied from vespajlib/com.yahoo.protect.Exceptions
@@ -100,7 +101,7 @@ public class MakeConfig {
* <code>e.getMessage(): e.getCause().getMessage(): e.getCause().getCause().getMessage()...</code>
* In addition, some heuristics are used to clean up common cases where exception nesting causes bad messages.
*/
- public static String toMessageString(Throwable t) {
+ static String toMessageString(Throwable t) {
StringBuilder b = new StringBuilder();
String lastMessage = null;
String message;
diff --git a/configgen/src/main/java/com/yahoo/config/codegen/MakeConfigProperties.java b/configgen/src/main/java/com/yahoo/config/codegen/MakeConfigProperties.java
index 13807e63e53..da746bf3a01 100644
--- a/configgen/src/main/java/com/yahoo/config/codegen/MakeConfigProperties.java
+++ b/configgen/src/main/java/com/yahoo/config/codegen/MakeConfigProperties.java
@@ -11,9 +11,10 @@ import java.util.StringTokenizer;
*
* @author gjoranv
*/
+@SuppressWarnings("WeakerAccess") // Used by ConfigGenMojo
public class MakeConfigProperties {
- private static final List<String> legalLanguages = Arrays.asList("java", "cpp", "cppng" );
+ private static final List<String> legalLanguages = Arrays.asList("java", "cpp" );
final File destDir;
final File[] specFiles;
@@ -33,13 +34,14 @@ public class MakeConfigProperties {
System.getProperty("config.packagePrefix"));
}
+ @SuppressWarnings("WeakerAccess") // Used by ConfigGenMojo
public MakeConfigProperties(String destDir,
- String specFiles,
- String language,
- String dirInRoot,
- String dumpTree,
- String generateFrameworkCode,
- String javaPackagePrefix) throws PropertyException {
+ String specFiles,
+ String language,
+ String dirInRoot,
+ String dumpTree,
+ String generateFrameworkCode,
+ String javaPackagePrefix) throws PropertyException {
this.destDir = checkDestinationDir(destDir);
this.specFiles = checkSpecificationFiles(specFiles);
this.language = checkLanguage(language);
diff --git a/configgen/src/main/resources/make-config-preproc.pl b/configgen/src/main/resources/make-config-preproc.pl
deleted file mode 100755
index a3432957e04..00000000000
--- a/configgen/src/main/resources/make-config-preproc.pl
+++ /dev/null
@@ -1,952 +0,0 @@
-#!/usr/bin/perl
-# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-# This is the config pre-processor.
-# It handles import statements, and does syntax checking etc.
-# The idea is that it will be called directly from the script
-# that does the code generation.
-#
-# Errors and warnings are printed in "next-error" compatible ways
-# for emacs etc.
-#
-# Indented like this:
-# (cperl-set-style "Whitesmith")
-# (setq cperl-continued-brace-offset -4)
-
-require 5.006_001;
-use strict;
-use warnings;
-use Digest::MD5;
-
-use Math::BigInt;
-use Math::BigFloat;
-
-die "Usage: $0 <def-file>" unless $#ARGV == 0;
-
-my $defname = $ARGV[0];
-
-my $md5 = Digest::MD5->new;
-
-my @c_keywords =
- ("asm", "auto", "bool", "break", "case", "catch",
- "char", "class", "const", "const_cast", "continue", "default",
- "delete", "do", "double", "dynamic_cast", "else", "enum", "explicit",
- "export", "extern", "false", "float", "for", "friend", "goto", "if",
- "inline", "int", "long", "mutable", "namespace", "new", "operator",
- "private", "protected", "public", "register", "reinterpret_cast",
- "return", "short", "signed", "sizeof", "static", "static_cast",
- "struct", "switch", "template", "this", "throw", "true", "try",
- "typedef", "typeid", "typename", "union", "unsigned",
- "using", "virtual", "void", "volatile", "wchar_t", "while", "and", "bitor",
- "not", "or", "xor", "and_eq", "compl", "not_eq", "or_eq", "xor_eq",
- "bitand");
-
-
-my @java_keywords =
- ("abstract", "boolean", "break", "byte", "case",
- "catch", "char", "class","continue", "default", "do", "double",
- "else", "extends","false", "final", "finally", "float", "for",
- "if","implements", "import", "instanceof", "int", "interface",
- "long","native", "new", "null", "package", "private",
- "protected","public", "return", "short", "static",
- "strictfp","super","switch", "synchronized", "this",
- "throw","throws","transient", "true", "try", "void",
- "volatile","while", "byvalue", "cast", "const", "future",
- "generic","goto", "inner", "operator", "outer", "rest", "var");
-
-my %reserved_words;
-
-foreach my $word (@c_keywords) {
- $reserved_words{$word} = "C";
-}
-
-foreach my $word (@java_keywords) {
- my $x = $reserved_words{$word};
- if (defined($x)) {
- $x = "$x, Java";
- } else {
- $x = "Java";
- }
- $reserved_words{$word} = $x;
-}
-
-my $MIN_INT = -0x80000000;
-my $MAX_INT = 0x7fffffff;
-my $MIN_DOUBLE = -1e308;
-my $MAX_DOUBLE = 1e308;
-
-
-sub do_file {
- my ($file, $prefix, $strip) = @_;
-
- local *FH;
- open FH, "< $file" or die "Cannot open $file: $!\n";
-
- local *COPY;
- my $dir = $ENV{"VESPA_CONFIG_DEF_DIR"};
- my $copy;
- my $file_version;
- if (defined($dir)) {
- $copy = $file;
- $copy =~ s=.*/==;
- $copy = "$dir/$copy";
- open COPY, ">$copy.new" or die "Cannot open file $copy.new: $!\n";
- }
-
- # Read line by line.
- # 1. Strip away comments and trailing blanks
- # 2. Report any errors
- # 3. Handle import statements, disallow multi-level imports
- # 4. Print everyting to stdout
-
- my $linenr = 0;
- my $written_lines = 0;
- my $quoted_strip = quotemeta($strip);
- my $seen_version = 0;
-
- while (<FH>) {
- print COPY $_ if $copy;
- ++$linenr;
- my $line = $_;
- chomp $line;
-
- # Don't process comments or add them to md5 checksum, but print them
- # such that codegen can include comments
- if ($line =~ /^\s*#/) {
- print "$line\n";
- next;
- }
-
- # Strip away comments that are not at start of line
- $line = &strip_trailing_comment($line, $linenr)
- if ($line =~ m=[\\\#]=);
-
- if ($line eq "::error::") {
- return -1;
- }
-
- # Skip lines that are only whitespace
- next if $line =~ m=^\s*$=;
-
- # Get rid of trailing whitespace
- $line =~ s=\s+$==;
-
- if (!$seen_version) {
- if ($line =~ m!^version=([a-zA-Z0-9][-a-zA-Z0-9_/?]*)!) {
- $file_version = $1;
- $seen_version = 1;
- if ($prefix) {
- print "$prefix imported $file";
- print ":$strip" if $strip;
- print " ";
- }
- print "$line\n";
- next;
- } else {
- print STDERR "$file:$linenr: error: Definition file does not "
- . "start with a valid version= identifier!\n";
- return -1;
- }
- }
-
- if ($strip) {
- next unless $line =~ m=^${quoted_strip}[. \t]=;
- }
-
- if (&check_syntax($line, $linenr, $file) == -1) {
- return -1;
- }
-
- # Handle import statements
- my ($name, $type, $remains, $junk) = split(/\s+/, $line, 4);
- if ($type eq "import") {
- if ($strip || $prefix) {
- my $col = index($line, $type, length("$name "));
- print STDERR "$file:$linenr:$col: error: Multi-level "
- . "imports are disallowed.\n";
- return -1;
- }
- if ($junk) {
- my $col = index($line, $junk, length("$name $type $remains"))
- + 1;
- print STDERR "$file:$linenr:$col: error: Junk after import "
- . "target \"$remains\": \"$junk\"\n";
- return -1;
- }
- my ($impfile, $var) = split(/:/, $remains, 2);
- $var = "" unless $var; # Make it defined.
-
- # Make sure only arrays can include arrays:
- if ($name =~ m=\[\]$= && (!$var || $var !~ m=\[\]$=)) {
- print STDERR "$file:$linenr: error: Array cannot import "
- . "non-array in: $line\n";
- return -1;
- } elsif ($name !~ m=\[\]$= && ($var && $var =~ m=\[\]$=)) {
- print STDERR "$file:$linenr: error: Non-array cannot import "
- . "array in: $line\n";
- return -1;
- }
-
- local *X;
- unless (open(X, "< $impfile")) {
- my $col = index($line, $remains, length("$name $type")) + 1;
- print STDERR "$file:$linenr:$col: error: Cannot open "
- . "\"$impfile\": $!\n";
- return -1;
- }
- close X;
- my $imported_lines = &do_file("$impfile", "$name", "$var");
- if ($imported_lines == -1) {
- my $col = index($line, $remains, length("$name $type")) + 1;
- print STDERR "$file:$linenr:$col: error: Imported from here "
- . "as: $line\n";
- return -1;
- } elsif ($imported_lines == 0) {
- my $col = index($line, $remains, length("$name $type")) + 1;
- print STDERR "$file:$linenr:$col: error: Import target "
- . "\"$var\" not found in \"$impfile\"\n";
- return -1;
- }
- $written_lines += $imported_lines;
- } else {
- ++$written_lines;
- if ($strip) {
- $line =~ s=^${quoted_strip}=${prefix}=
- } elsif ($prefix) {
- $line = $prefix . "." . $line;
- }
-
- if (&check_name_sanity($line, $linenr, $file) == -1
- || &check_enum_sanity($line, $linenr, $file) == -1) {
- return -1;
- }
-
- $line = &normalize_line($line, $linenr);
- if ($line eq "::error::") {
- return -1;
- }
- print $line . "\n";
- }
- # Add this line to the md5 checksum
- $md5->add("$line\n") unless $prefix;
- }
-
- print "md5=" . $md5->hexdigest . "\n" unless $prefix;
- close FH;
- if ($copy) {
- close COPY;
- # We have made a copy. It needs a new name..
- my $new_name = $copy;
- $new_name =~ s=\.def==;
- $new_name .= ".${file_version}.def";
- if (-f $new_name) {
- system "cmp $copy.new $new_name 2>/dev/null" and die "$file:1: error: Definition file $file differs from ${new_name}!\n";
- unlink("$copy.new");
- } else {
- rename("$copy.new", "$new_name") or die "Rename $copy.new -> $new_name failed: $!\n";
- }
- }
- return $written_lines;
-}
-
-sub normalize_enum {
- my($x, $linenr, $colnr) = @_;
- my $len = length($x);
- my $char = '';
- my $output = '{ ';
- my $index;
- my %enum = ();
- my $current_variable = '';
- for ($index = $colnr + 1; $index < $len; ++$index) {
- $char = substr($x, $index, 1);
- if ($char eq '}') {
- if (length($current_variable) < 2) {
- print STDERR "$defname:$linenr:$index: error: ".
- " variable must be at least two characters: $x\n" ;
- return ('', 0);
- } elsif ($enum{$current_variable}) {
- print STDERR "$defname:$linenr:$index: error: ".
- " enum variable declared twice: $x\n" ;
- return ('', 0);
- } elsif (!%enum && !$current_variable) {
- print STDERR "$defname:$linenr:$index: error: ".
- " enum cannot be empty: $x\n" ;
- return ('', 0);
- }
- return ($output.$current_variable." } ", $index);
- } elsif ($char eq ',') {
- if (length($current_variable) < 2) {
- print STDERR "$defname:$linenr:$index: error: ".
- " variable must be at least two characters: $x\n" ;
- return ('', 0);
- } elsif ($enum{$current_variable}) {
- print STDERR "$defname:$linenr:$index: error: ".
- " enum variable declared twice: $x\n" ;
- return ('', 0);
- }
- $enum{$current_variable} = 1;
- $output .= "$current_variable, ";
- $current_variable = '';
- } elsif ($char =~ m=[A-Z]=) {
- $current_variable .= $char;
- } elsif ($char =~ m=[0-9_]= && $current_variable) {
- $current_variable .= $char;
- } elsif ($char =~ m=\s=) {
- if ($current_variable && !($x =~ /^.{$index}\s*[,\}]/)) {
- print STDERR "$defname:$linenr:$index: error: ".
- "expected ',' or '}': $x\n" ;
- return ("", 0);
- } else {
- # skip whitespace
- }
- } else {
- print STDERR "<$char> <$current_variable>\n";
-
- print STDERR "$defname:$linenr:$index: error: ".
- "Enum must match [A-Z][A-Z0-9_]+: $x\n";
- }
- }
- return ($output, $index);
-}
-
-{ package Range;
-
- $Range::DOUBLE_RANGE =
- new Range("a double range=[$MIN_DOUBLE,$MAX_DOUBLE] ",0,14);
- $Range::INT_RANGE = new Range("a int range=[$MIN_INT,$MAX_INT] ",0,11);
-
-
- sub in_range {
- my($self, $value) = @_;
-
- if ($value =~ s/KB$//) {
- $value *= 1024;
- } elsif ($value =~ s/MB$//) {
- $value *= (1024 * 1024);
- } elsif ($value =~ s/GB$//) {
- $value *= (1024*1024*1024);
- } elsif ($value =~ s/k$//) {
- $value *= 1000;
- } elsif ($value =~ s/M$//) {
- $value *= 1_000_000;
- } elsif ($value =~ s/G$//) {
- $value *= 1_000_000_000;
- } elsif ($value =~ m=^0[xX]=) {
- $value = hex($value);
- }
-
- if ($self->{start_bracket} eq '(' ) {
- return 0 if $value <= $self->{min};
- } elsif ($self->{start_bracket} eq '[' ) {
- return 0 if $value < $self->{min};
- } else {
- print STDERR "Illegal start_bracket '$self->{start_bracket}'\n";
- return undef;
- }
- if ($self->{end_bracket} eq ')' ) {
- return 0 if $value >= $self->{max};
- } elsif ($self->{end_bracket} eq ']' ) {
- return 0 if $value > $self->{max};
- } else {
- print STDERR "Illegal end_bracket '$self->{start_bracket}'\n";
- return undef;
- }
- return 1;
- }
-
-
- sub new {
- my($class, $x, $linenr, $colnr) = @_;
- my $len = length($x);
- my $self = {};
- bless($self, $class);
- $self->{min_value} = '';
- my $index;
- for ($index = $colnr + 1; $index < $len; ++$index) {
- my $char = substr($x, $index, 1);
- if (($char eq '(' || $char eq '[') && !$self->{start_bracket}) {
- $self->{start_bracket} = $char;
- } elsif (($char eq ')' || $char eq ']') && !$self->{end_bracket}) {
- $self->{end_bracket} = $char;
- last;
- } elsif ($char =~ m=\s=) {
- #ignore whitespace
- } elsif ($char eq ',' && !defined($self->{max_value})) {
- $self->{max_value} = '';
- } elsif ($char =~ m=[\d\.\+eE-]= ) {
- (defined($self->{max_value})
- ? $self->{max_value} : $self->{min_value}) .= $char;
- } else {
- print STDERR "$defname:$linenr:$index: error: ".
- " syntax error: $x\n" ;
- return undef;
- }
- }
- if ($self->{min_value} eq '' && $self->{max_value} eq '') {
- print STDERR "$defname:$linenr:$colnr: error: ".
- " range cannot be unbounded in both ends: $x\n" ;
- return undef;
- }
- unless ($self->{start_bracket} && $self->{end_bracket}) {
- print STDERR "$defname:$linenr:$colnr: error: ".
- " missing bracket: $x\n" ;
- return undef;
- }
-
-
- my @arr = split(/\s+/, $x, 3);
- if ($arr[1] eq 'int') {
- $self->{min} = Math::BigInt->new
- ($self->{min_value} eq '' ? $MIN_INT : $self->{min_value});
- unless (defined($self->{min}) && $self->{min} ne 'NaN') {
- print STDERR "$defname:$linenr:$colnr: error: ".
- " parse error $self->{min_value}: $x\n" ;
- return undef;
- }
- my $min_val =
- $self->{min} + ($self->{start_bracket} eq '('? 1 : 0);
-
- $self->{max} = Math::BigInt->new
- ($self->{max_value} eq '' ? $MAX_INT : $self->{max_value});
- unless (defined($self->{max}) && $self->{max} ne 'NaN') {
- print STDERR "$defname:$linenr:$colnr: error: ".
- " parse error $self->{max_value}: $x\n" ;
- return undef;
- }
- my $max_val =
- $self->{max} - ($self->{end_bracket} eq ')'? 1 : 0);
-
- if ($min_val < $MIN_INT ) {
- print STDERR "$defname:$linenr:$colnr: error: ".
- " start of interval less than MIN_INT: $x\n" ;
- return undef;
- }
- if ($max_val > $MAX_INT) {
- print STDERR "$self->{max} - 1 > $MAX_INT\n";
- print STDERR "$defname:$linenr:$colnr: error: ".
- " end of interval greater than MAX_INT: $x\n" ;
- return undef;
- }
- if ($max_val < $min_val) {
- print STDERR "$defname:$linenr:$colnr: error: ".
- " illegal range: $x\n" ;
- return undef;
- }
- $self->{string} =
- "$self->{start_bracket}$self->{min},$self->{max}$self->{end_bracket}";
- $self->{string} =~ s/\+//g;
- $self->{index} = $index;
- return $self;
- } elsif ($arr[1] eq 'double') {
- $self->{min} = Math::BigFloat->new
- ($self->{min_value} eq '' ? $MIN_DOUBLE : $self->{min_value});
- unless (defined($self->{min}) && $self->{min} ne 'NaN') {
- print STDERR "$defname:$linenr:$colnr: error: ".
- " parse error $self->{min_value}: $x\n" ;
- return undef;
- }
- $self->{max} = Math::BigFloat->new
- ($self->{max_value} eq '' ? $MAX_DOUBLE : $self->{max_value});
- unless (defined($self->{max}) && $self->{max} ne 'NaN') {
- print STDERR "$defname:$linenr:$colnr: error: ".
- " parse error $self->{max_value}: $x\n" ;
- return undef;
- }
- if ($self->{min} < $MIN_DOUBLE) {
- print STDERR "$defname:$linenr:$colnr: error: ".
- " start of interval less than MIN_DOUBLE: $x\n" ;
- return undef;
- }
- if ($self->{max} > $MAX_DOUBLE) {
- print STDERR "$defname:$linenr:$colnr: error: ".
- " start of interval greater than MAX_DOUBLE: $x\n" ;
- return undef;
- }
- if ($self->{max} < $self->{min}) {
- print STDERR "$defname:$linenr:$colnr: error: ".
- " illegal range: $x\n" ;
- return undef;
- }
- if (($self->{start_bracket} eq '(' || $self->{end_bracket} eq ')')
- && ($self->{min_value} + $self->{min_value}
- >= $self->{min_value} + $self->{max_value})
- && ($self->{max_value} + $self->{max_value}
- <= $self->{min_value} + $self->{max_value})) {
- print STDERR "$defname:$linenr:$colnr: error: ".
- " illegal range: $x\n" ;
- return undef;
- }
- $self->{string} = $self->{start_bracket}.$self->{min}->fnorm.
- ','.$self->{max}->fnorm.$self->{end_bracket};
- $self->{string} =~ s/\+//g;
- $self->{index} = $index;
- return $self;
- } else {
- print STDERR "$defname:$linenr:$colnr: error: ".
- " range-option works only for type 'int' and 'double': $x\n" ;
- return undef;
- }
- print STDERR "$defname:$linenr:$colnr: error: ".
- " script error: $x\n" ;
- return undef;
-
- }
-
-}
-
-
-
-
-sub strip_trailing_comment {
- my ($x, $linenr) = @_;
-
- my $index = 0;
- my $len = length($x);
- my $in_quotes = 0;
-
- # ### Support both " and ' quotes maybe?
-
- for ($index = 0; $index < $len; ++$index) {
- if (substr($x, $index, 1) eq "\\") {
- ++$index;
- next;
- }
- if (substr($x, $index, 1) eq "\"") {
- $in_quotes ^= 1;
- }
- if ($in_quotes == 0 && substr($x, $index, 1) eq "#") {
- if (!(substr($x, $index - 1, 1) =~ m=\s=)) {
- my $col = $index + 1;
- print STDERR "$defname:$linenr:$col: warning: No whitespace "
- . "before comment in line: $x\n";
- }
- print substr($x, $index). "\n";
- $x = substr($x, 0, $index);
- last;
- }
- }
- if ($index > $len) {
- print STDERR "$defname:$linenr:$len: error: syntax error, line "
- . "ends with \\: \"$x\"\n";
- return "::error::";
- }
-
- return $x;
-}
-
-sub normalize_line {
- my ($x, $linenr) = @_;
-
- my $index = 0;
- my $len = length($x);
- my $in_quotes = 0;
- my $char = '';
- my $output = '';
- my %hash = ();
-
- my @arr = split(/\s+/, $x, 3);
- $hash{type} = $arr[1];
-
- for ($index = 0; $index < length($x); ++$index) {
- $char = substr($x, $index, 1);
- if ($char eq "\\") {
- $output .= substr($x, $index, 2);
- ++$index;
- next;
- }
- if ($char eq "\"") {
- $in_quotes ^= 1;
- $output .= $char;
- next;
- }
- my $ends_with_whitespace = ($output =~ m= $=);
-
- if ($in_quotes == 0) {
- if ($char =~ m=\s=) {
- #delete multiple spaces
- if (!$ends_with_whitespace) { # && ($output =~ !m=\=$=)) {
- $output .= ' ';
- }
- } elsif ($char eq '{') {
- my($enum, $i) = &normalize_enum($x, $linenr, $index);
- return "::error::" unless $i;
- $index = $i;
- $output .= ($ends_with_whitespace) ? $enum : " $enum ";
- } elsif ($char eq ',') {
- chop $output if ($ends_with_whitespace);
- $output .= ',';
- } elsif ($char eq '=') {
- chop $output if ($ends_with_whitespace);
- $output .= '=';
- if ($output =~ /range=$/) {
- $hash{range} =
- new Range($x, $linenr, $index);
- return "::error::" unless $hash{range};
- $index = $hash{range}->{index};
- $output .= $hash{range}->{string}." ";
- }
- if ($output =~ /default=$/
- && ($hash{type} eq 'int' || $hash{type} eq 'double')) {
- $x =~ /^.{$index}=\s*(\S+)/;
- $hash{default} = $1;
- if ($hash{type} eq 'int' &&
- !$Range::INT_RANGE->in_range($hash{default})) {
- print STDERR "$defname:$linenr:$index: error: ".
- "Default not in range: $x\n";
- return "::error::";
- }
- if ($hash{type} eq 'double' &&
- !$Range::DOUBLE_RANGE->in_range($hash{default})) {
- print STDERR "$defname:$linenr:$index: error: ".
- "Default not in range: $x\n";
- return "::error::";
- }
- }
- if (defined($hash{default}) && $hash{range}) {
- unless ($hash{range}->in_range($hash{default})) {
- print STDERR "$defname:$linenr:$index: error: ".
- "Default not in range: $x\n";
- return "::error::";
- }
- }
- } else {
- $output .= $char;
- }
- } else {
- $output .= $char;
- }
- }
- if ($index > $len) {
- print STDERR "$defname:$linenr:$len: error: syntax error, line "
- . "ends with \\: \"$x\"\n";
- return "::error::";
- }
- chop $output if $output =~ m/ $/;
- return $output;
-}
-
-my %used_enum;
-sub check_enum_sanity {
- my ($line, $linenr, $file) = @_;
-
- my ($name, $type, $rest) = split(/\s+/, $line, 3);
- return 0 unless ($type eq "enum");
-
- $name =~ /(.*)\./;
- my $prefix = $1;
- $prefix = "" unless defined $prefix; # Make top level prefix
- $used_enum{"$prefix"} = $used_enum{"$prefix"} || {};
- $rest = "" unless defined $rest;
- $rest =~ /\{\s*(.*?)\}/;
- my @values = split(/[,\s]+/, $1);
- foreach my $value (@values) {
- if ($used_enum{"$prefix"}->{$value}) {
- print STDERR
- "$file:$linenr: error: Name \"$value\" is already defined\n";
- my $prevdef = $used_enum{"$prefix"}->{$value};
- print STDERR "$prevdef: error: At this point\n";
- return -1;
- } else {
- $used_enum{"$prefix"}->{$value} = "$file:$linenr";
- }
- }
- return 0;
-}
-
-
-my %used_name;
-my %used_component;
-my %banned_prefixes;
-my $cns_prev_name;
-sub check_name_sanity {
- my ($line, $linenr, $file) = @_;
- my ($name, $junk) = split(/\s+/, $line, 2);
-
- my $plain_name = $name;
- $plain_name =~ s=\[\]$==;
-
- # See if the name is already used.
- if ($used_name{"$plain_name"}) {
- print STDERR
- "$file:$linenr: error: Name \"$name\" is already defined\n";
- my $prevdef = $used_name{$name};
- print STDERR "$prevdef: error: At this point\n";
- return -1;
- } else {
- $used_name{$name} = "$file:$linenr";
- }
-
- # Test for bans
- my $banned = "${name}.";
- do {
- my $err = $banned_prefixes{$banned};
- if (defined($err)) {
- print STDERR "$file:$linenr: error: The prefix \"$banned\" is illegal here\n";
- print STDERR "$err\n";
- return -1;
- }
- } while (($banned =~ s=[.][^.]+[.]$=.=));
-
- # Add any new bans generated by this line
- $banned_prefixes{"${name}."} = "$file:$linenr: error: \"${name}\" cannot "
- . "be both a struct and a non-struct!";
- if ($cns_prev_name) {
- my $prev = $cns_prev_name;
- my $oldprev = $prev;
- while (($prev =~ s=[.][^.]+[.]?$=.=)) {
- if (substr($name, 0, length($prev)) eq $prev) {
- $banned_prefixes{"$oldprev"} = "$file:" . ($linenr - 1)
- . ": error: Last possible line is after this";
- last;
- }
- $oldprev = $prev;
- }
- }
- $cns_prev_name = $name;
-
- # See if any of the components previously have a different "arrayness"
- my $part_name = $name;
- while (($part_name =~ s=[.][^.]+$==)) {
- my $clashing_name = $part_name;
- if ($part_name =~ m=\[\]$=) {
- $clashing_name =~ s=\[\]$==;
- } else {
- $clashing_name .= "[]";
- }
- my $clashline = $used_component{"$clashing_name"};
- if (defined $clashline) {
- print STDERR "$file:$linenr: error: \"$clashing_name\" cannot be both array and non-array\n";
- print STDERR "$clashline: error: Previously defined here\n";
- return -1;
- } elsif (!$used_component{"$part_name"}) {
- $used_component{"$part_name"} = "$file:$linenr";
- }
- }
- return 0;
-}
-
-# These are all the allowed types/commands
-my %types = ( "int" => \&check_int,
- "double" => \&check_double,
- "string" => \&check_string,
- "reference" => \&check_reference,
- "enum" => \&check_enum,
- "bool" => \&check_bool,
- "properties" => \&check_properties,
- "import" => \&check_import );
-
-sub check_syntax {
- my ($line, $linenr, $file) = @_;
-
- my $col = 0;
- my $llen = length($line);
-
- # Step 1. Sanity check the name.
- my $atstart = 1;
- my $array_ok = 1;
-
- for ($col = 0; $col < $llen; ++$col) {
- my $c = substr($line, $col, 1);
- if ($atstart) {
- if ($c !~ m=[a-zA-Z]=) {
- print STDERR "$file:$linenr:$col: error: Non-alphabetic start "
- . "of variable name in $line\n";
- return -1;
- }
- $atstart = 0;
- } else {
- if ($c =~ m=[a-zA-Z0-9_]=) {
- 0; # Do nothing
- } elsif ($c eq ".") {
- $atstart = 1;
- $array_ok = 1;
- } elsif ($c eq "[") {
- if (!$array_ok) {
- ++$col;
- print STDERR "$file:$linenr:$col: error: Arrays cannot be "
- . "multidimensional in $line\n";
- return -1;
- }
- ++$col;
- $array_ok = 0;
- $c = substr($line, $col, 1);
- if ($c ne "]") {
- ++$col;
- print STDERR "$file:$linenr:$col: error: Expected ] to "
- . "terminate array definition in $line\n";
- return -1;
- }
- } elsif ($c =~ m=\s=) {
- last;
- } else {
- ++$col;
- print STDERR "$file:$linenr:$col: error: Syntax error, "
- . "unexpected character in $line\n";
- return -1;
- }
- }
- }
-
- my $name = substr($line, 0, $col);
- $name =~ s=.*[.]==;
- $name =~ s=[[]]$==;
-
- my $clash = $reserved_words{$name};
- if ($clash) {
- $col -= (3 + length($name));
- $col = index($line, $name, $col) + 1;
- print STDERR "$file:$linenr:$col: error: $name is a reserved word in: "
- . "${clash}\n";
- return -1;
- }
-
- while (substr($line, $col, 1) =~ m=\s=) {
- ++$col;
- }
-
- # At this point the name is sane. Next, check the type.
- my ($type) = split(/\s/, substr($line, $col));
-
- unless (defined $types{$type}) {
- ++$col;
- print STDERR "$file:$linenr:$col: error: Unknown type/command "
- . "\"$type\"\n";
- return -1;
- }
- $col += length($type);
- while (substr($line, $col, 1) =~ m=\s=) {
- ++$col;
- }
- return $types{$type}($col, $line, $linenr, $file);
-}
-
-sub reg_words_check {
- my ($col, $line, $linenr, $file, $reg) = @_;
- my $remainder = substr($line, $col);
- my @options = split(/\s+/, $remainder);
-
- foreach my $option (@options) {
- # Keep track of where we are for error reporting
- $col = index($line, $option, $col) + 1;
- unless ($option =~ m!${reg}!) {
- print STDERR "$file:$linenr:$col: error: Bad option \"$option\" no match for m!${reg}!\n";
- return -1;
- }
- }
- return 0;
-}
-
-sub check_int {
- my ($col, $line, $linenr, $file) = @_;
- my $num = "(-?\\d+(KB|MB|GB|k|M|G)?|0x[0-9a-fA-F]+)"; # All legal numbers
- my $optnum = "(${num})?"; # All legal optional numbers
- return &reg_words_check($col, $line, $linenr, $file,
- "^("
- . "default=${num}"
- . "|range=[[(]${optnum},${optnum}"."[])]"
- . "|restart"
- . ")\$");
-}
-
-sub check_double {
- my ($col, $line, $linenr, $file) = @_;
- my $num = "-?(\\d+(\\.\\d*)?|\\.\\d+)([eE][+-]?\\d+)?"; # All legal doubles
- my $optnum = "(${num})?"; # Optional doubles
- return &reg_words_check($col, $line, $linenr, $file,
- "^("
- . "default=${num}"
- . "|range=[[(]${optnum},${optnum}"."[])]"
- . "|restart"
- . ")\$");
-}
-
-sub check_string {
- my ($col, $line, $linenr, $file) = @_;
- my $opts = substr($line, $col);
-
- # not entirely correct either for something like \\"
- my $def = "default=((\"(\\\"|[^\"])*\")|null)";
-
- my $res = "restart";
- my $reg = "^(${def}\\s+${res}|(${def})?|${res}|${res}\\s+${def})\$";
-
- unless ($opts =~ m!${reg}!) {
- print STDERR "$file:$linenr:$col: error: Bad options \"$opts\", no match for m!${reg}!\n";
- return -1;
- }
- return 0;
-}
-
-sub check_reference {
- my ($col, $line, $linenr, $file) = @_;
- my $opts = substr($line, $col);
- my $def = "default=((\"(\\\"|[^\"])*\")|null)";
-
- unless ($opts eq "" || $opts =~m!${def}!) {
- print STDERR "$file:$linenr:$col: error: reference can only "
- . "take the 'default' option\n";
- return -1;
- }
- return 0;
-}
-
-
-sub check_enum {
- my ($col, $line, $linenr, $file) = @_;
- my $ret = &reg_words_check($col, $line, $linenr, $file,
- "^("
- . "[{},]"
- . "|[A-Z][A-Z0-9_]+,?"
- . "|default=[A-Z][A-Z0-9_]+"
- . "|restart"
- . ")\$");
- return -1 if $ret;
- $col = index($line, '}', $col) + 1; #move $col to end of enum --> }
- while (substr($line, $col, 1) =~ m=[\s\{]=) {
- ++$col;
- }
- return 0 if $col >= length($line);
-
-
- return &reg_words_check($col, $line, $linenr, $file,
- "^("
- . "default=[A-Z][A-Z0-9_]+"
- . "|restart"
- . ")\$");
-}
-
-sub check_bool {
- my ($col, $line, $linenr, $file) = @_;
- return &reg_words_check($col, $line, $linenr, $file,
- "^("
- . "default=(true|false)"
- . "|restart"
- . ")\$");
-}
-
-sub check_properties {
- my ($col, $line, $linenr, $file) = @_;
- return &reg_words_check($col, $line, $linenr, $file, "^restart\$");
-}
-
-sub check_import {
- my ($col, $line, $linenr, $file) = @_;
- my $word = "[a-zA-Z][_a-zA-Z0-9]*";
- my $fnam = "${word}(\\.${word})*";
- my $var = "${word}((\\[\\])?\.${word})*(\\[\\])?";
- return &reg_words_check($col, $line, $linenr, $file,
- "^${fnam}\\.def:(${var})?\$");
- return 0;
-}
-
-
-my $lines = &do_file($defname, "", "");
-
-if ($lines == -1) {
- die "There were irrecoverable errors in \"$defname\"!\n";
-}
-if ($lines == 0) {
- die "$defname:1: error: Resulting definition is empty!\n";
-}
-
-exit 0;
diff --git a/configgen/src/test/java/com/yahoo/config/codegen/MakeConfigTest.java b/configgen/src/test/java/com/yahoo/config/codegen/MakeConfigTest.java
index 501d7778fd7..7486d464e43 100644
--- a/configgen/src/test/java/com/yahoo/config/codegen/MakeConfigTest.java
+++ b/configgen/src/test/java/com/yahoo/config/codegen/MakeConfigTest.java
@@ -12,7 +12,7 @@ import org.junit.Test;
public class MakeConfigTest {
- File dest;
+ private File dest;
@Before
public void setUp() {
@@ -29,8 +29,9 @@ public class MakeConfigTest {
if (dir.isDirectory()) {
String[] children = dir.list();
- for (int i = 0; i < children.length; i++) {
- boolean success = recursiveDeleteDir(new File(dir, children[i]));
+ assert children != null;
+ for (String child : children) {
+ boolean success = recursiveDeleteDir(new File(dir, child));
if (!success) return false;
}
@@ -42,10 +43,8 @@ public class MakeConfigTest {
@Test
public void testProps() throws PropertyException {
- long ts = System.currentTimeMillis();
System.setProperty("config.dumpTree", "true");
System.setProperty("config.useFramework", "true");
- System.setProperty("config.requireNamespace", "true");
System.setProperty("config.dest", dest.getAbsolutePath());
System.setProperty("config.spec", "src/test/resources/allfeatures.def");
MakeConfigProperties p = new MakeConfigProperties();
@@ -57,7 +56,6 @@ public class MakeConfigTest {
System.setProperty("config.dumpTree", "false");
System.setProperty("config.useFramework", "false");
- System.setProperty("config.requireNamespace", "false");
System.setProperty("config.dest", dest.getAbsolutePath());
System.setProperty("config.spec", "src/test/resources/allfeatures.def,src/test/resources/bar.foo.def");
p = new MakeConfigProperties();
@@ -71,7 +69,6 @@ public class MakeConfigTest {
public void testMake() throws IOException, InterruptedException {
System.setProperty("config.dumpTree", "true");
System.setProperty("config.useFramework", "true");
- System.setProperty("config.requireNamespace", "true");
System.setProperty("config.dest", dest.getAbsolutePath());
System.setProperty("config.spec", "src/test/resources/allfeatures.def");
MakeConfig.main(new String[]{});
diff --git a/configserver-flags/pom.xml b/configserver-flags/pom.xml
index 8c96512c4c0..11ef9b6c950 100644
--- a/configserver-flags/pom.xml
+++ b/configserver-flags/pom.xml
@@ -20,6 +20,12 @@
<!-- provided -->
<dependency>
<groupId>com.yahoo.vespa</groupId>
+ <artifactId>container-dev</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
<artifactId>zkfacade</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/flags/DefinedFlag.java b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/DefinedFlag.java
index 92397fc84a7..c706a2b1e51 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/flags/DefinedFlag.java
+++ b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/DefinedFlag.java
@@ -1,12 +1,11 @@
// Copyright 2019 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.config.server.http.flags;
+package com.yahoo.vespa.configserver.flags.http;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.jdisc.Response;
-import com.yahoo.vespa.config.server.http.HttpConfigResponse;
import com.yahoo.vespa.flags.FlagDefinition;
import com.yahoo.vespa.flags.json.DimensionHelper;
@@ -42,6 +41,7 @@ public class DefinedFlag extends HttpResponse {
@Override
public String getContentType() {
- return HttpConfigResponse.JSON_CONTENT_TYPE;
+ return "application/json";
}
+
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/flags/DefinedFlags.java b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/DefinedFlags.java
index 9604c51ee4b..26d590593c0 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/flags/DefinedFlags.java
+++ b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/DefinedFlags.java
@@ -1,11 +1,10 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.config.server.http.flags;
+package com.yahoo.vespa.configserver.flags.http;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.jdisc.Response;
-import com.yahoo.vespa.config.server.http.HttpConfigResponse;
import com.yahoo.vespa.flags.FlagDefinition;
import java.io.IOException;
@@ -18,8 +17,7 @@ import java.util.List;
*/
public class DefinedFlags extends HttpResponse {
private static ObjectMapper mapper = new ObjectMapper();
- private static final Comparator<FlagDefinition> sortByFlagId =
- (left, right) -> left.getUnboundFlag().id().compareTo(right.getUnboundFlag().id());
+ private static final Comparator<FlagDefinition> sortByFlagId = Comparator.comparing(flagDefinition -> flagDefinition.getUnboundFlag().id());
private final List<FlagDefinition> flags;
@@ -40,6 +38,6 @@ public class DefinedFlags extends HttpResponse {
@Override
public String getContentType() {
- return HttpConfigResponse.JSON_CONTENT_TYPE;
+ return "application/json";
}
}
diff --git a/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/ErrorResponse.java b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/ErrorResponse.java
new file mode 100644
index 00000000000..b9e5c75fe22
--- /dev/null
+++ b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/ErrorResponse.java
@@ -0,0 +1,66 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.configserver.flags.http;
+
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.Slime;
+
+import static com.yahoo.jdisc.Response.Status.BAD_REQUEST;
+import static com.yahoo.jdisc.Response.Status.FORBIDDEN;
+import static com.yahoo.jdisc.Response.Status.INTERNAL_SERVER_ERROR;
+import static com.yahoo.jdisc.Response.Status.METHOD_NOT_ALLOWED;
+import static com.yahoo.jdisc.Response.Status.NOT_FOUND;
+import static com.yahoo.jdisc.Response.Status.UNAUTHORIZED;
+
+/**
+ * A HTTP JSON response containing an error code and a message
+ *
+ * @author bratseth
+ */
+public class ErrorResponse extends SlimeJsonResponse {
+
+ public enum errorCodes {
+ NOT_FOUND,
+ BAD_REQUEST,
+ FORBIDDEN,
+ METHOD_NOT_ALLOWED,
+ INTERNAL_SERVER_ERROR,
+ UNAUTHORIZED
+ }
+
+ public ErrorResponse(int statusCode, String errorType, String message) {
+ super(statusCode, asSlimeMessage(errorType, message));
+ }
+
+ private static Slime asSlimeMessage(String errorType, String message) {
+ Slime slime = new Slime();
+ Cursor root = slime.setObject();
+ root.setString("error-code", errorType);
+ root.setString("message", message);
+ return slime;
+ }
+
+ public static ErrorResponse notFoundError(String message) {
+ return new ErrorResponse(NOT_FOUND, errorCodes.NOT_FOUND.name(), message);
+ }
+
+ public static ErrorResponse internalServerError(String message) {
+ return new ErrorResponse(INTERNAL_SERVER_ERROR, errorCodes.INTERNAL_SERVER_ERROR.name(), message);
+ }
+
+ public static ErrorResponse badRequest(String message) {
+ return new ErrorResponse(BAD_REQUEST, errorCodes.BAD_REQUEST.name(), message);
+ }
+
+ public static ErrorResponse forbidden(String message) {
+ return new ErrorResponse(FORBIDDEN, errorCodes.FORBIDDEN.name(), message);
+ }
+
+ public static ErrorResponse unauthorized(String message) {
+ return new ErrorResponse(UNAUTHORIZED, errorCodes.UNAUTHORIZED.name(), message);
+ }
+
+ public static ErrorResponse methodNotAllowed(String message) {
+ return new ErrorResponse(METHOD_NOT_ALLOWED, errorCodes.METHOD_NOT_ALLOWED.name(), message);
+ }
+
+}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/flags/FlagDataListResponse.java b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/FlagDataListResponse.java
index b33fc7c2b04..efc78cb7930 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/flags/FlagDataListResponse.java
+++ b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/FlagDataListResponse.java
@@ -1,12 +1,11 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.config.server.http.flags;
+package com.yahoo.vespa.configserver.flags.http;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.jdisc.Response;
-import com.yahoo.vespa.config.server.http.HttpConfigResponse;
import com.yahoo.vespa.flags.FlagId;
import com.yahoo.vespa.flags.json.FlagData;
import com.yahoo.vespa.flags.json.wire.WireFlagDataList;
@@ -54,6 +53,6 @@ public class FlagDataListResponse extends HttpResponse {
@Override
public String getContentType() {
- return HttpConfigResponse.JSON_CONTENT_TYPE;
+ return "application/json";
}
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/flags/FlagDataResponse.java b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/FlagDataResponse.java
index 054b218ff2d..8ff4085df8d 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/flags/FlagDataResponse.java
+++ b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/FlagDataResponse.java
@@ -1,9 +1,8 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.config.server.http.flags;
+package com.yahoo.vespa.configserver.flags.http;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.jdisc.Response;
-import com.yahoo.vespa.config.server.http.HttpConfigResponse;
import com.yahoo.vespa.flags.json.FlagData;
import java.io.OutputStream;
@@ -26,6 +25,6 @@ public class FlagDataResponse extends HttpResponse {
@Override
public String getContentType() {
- return HttpConfigResponse.JSON_CONTENT_TYPE;
+ return "application/json";
}
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/flags/FlagsHandler.java b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/FlagsHandler.java
index 00f3d457d3d..40bb69111e0 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/flags/FlagsHandler.java
+++ b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/FlagsHandler.java
@@ -1,14 +1,12 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.config.server.http.flags;
+package com.yahoo.vespa.configserver.flags.http;
import com.google.inject.Inject;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.container.jdisc.LoggingRequestHandler;
+import com.yahoo.log.LogLevel;
import com.yahoo.restapi.Path;
-import com.yahoo.vespa.config.server.http.HttpErrorResponse;
-import com.yahoo.vespa.config.server.http.HttpHandler;
-import com.yahoo.vespa.config.server.http.NotFoundException;
import com.yahoo.vespa.configserver.flags.FlagsDb;
import com.yahoo.vespa.flags.FlagDefinition;
import com.yahoo.vespa.flags.FlagId;
@@ -25,7 +23,8 @@ import java.util.Objects;
*
* @author hakonhall
*/
-public class FlagsHandler extends HttpHandler {
+public class FlagsHandler extends LoggingRequestHandler {
+
private final FlagsDb flagsDb;
@Inject
@@ -35,28 +34,44 @@ public class FlagsHandler extends HttpHandler {
}
@Override
- protected HttpResponse handleGET(HttpRequest request) {
+ public HttpResponse handle(HttpRequest request) {
+ try {
+ switch (request.getMethod()) {
+ case GET: return handleGET(request);
+ case DELETE: return handleDELETE(request);
+ case PUT: return handlePUT(request);
+ default: return ErrorResponse.methodNotAllowed("Method '" + request.getMethod() + "' is not supported");
+ }
+ }
+ catch (IllegalArgumentException e) {
+ return ErrorResponse.badRequest(Exceptions.toMessageString(e));
+ }
+ catch (RuntimeException e) {
+ log.log(LogLevel.WARNING, "Unexpected error handling '" + request.getUri() + "'", e);
+ return ErrorResponse.internalServerError(Exceptions.toMessageString(e));
+ }
+ }
+
+ private HttpResponse handleGET(HttpRequest request) {
Path path = new Path(request.getUri());
if (path.matches("/flags/v1")) return new V1Response(flagsV1Uri(request), "data", "defined");
if (path.matches("/flags/v1/data")) return getFlagDataList(request);
if (path.matches("/flags/v1/data/{flagId}")) return getFlagData(findFlagId(request, path));
if (path.matches("/flags/v1/defined")) return new DefinedFlags(Flags.getAllFlags());
if (path.matches("/flags/v1/defined/{flagId}")) return getDefinedFlag(findFlagId(request, path));
- throw new NotFoundException("Nothing at path '" + path + "'");
+ return ErrorResponse.notFoundError("Nothing at path '" + path + "'");
}
- @Override
- protected HttpResponse handlePUT(HttpRequest request) {
+ private HttpResponse handlePUT(HttpRequest request) {
Path path = new Path(request.getUri());
if (path.matches("/flags/v1/data/{flagId}")) return putFlagData(request, findFlagId(request, path));
- throw new NotFoundException("Nothing at path '" + path + "'");
+ return ErrorResponse.notFoundError("Nothing at path '" + path + "'");
}
- @Override
- protected HttpResponse handleDELETE(HttpRequest request) {
+ private HttpResponse handleDELETE(HttpRequest request) {
Path path = new Path(request.getUri());
if (path.matches("/flags/v1/data/{flagId}")) return deleteFlagData(findFlagId(request, path));
- throw new NotFoundException("Nothing at path '" + path + "'");
+ return ErrorResponse.notFoundError("Nothing at path '" + path + "'");
}
private String flagsV1Uri(HttpRequest request) {
@@ -66,19 +81,24 @@ public class FlagsHandler extends HttpHandler {
}
private HttpResponse getDefinedFlag(FlagId flagId) {
- FlagDefinition definition = Flags.getFlag(flagId)
- .orElseThrow(() -> new NotFoundException("Flag " + flagId + " not defined"));
- return new DefinedFlag(definition);
+ var definedFlag = Flags.getFlag(flagId).map(DefinedFlag::new);
+ if (definedFlag.isPresent()) {
+ return definedFlag.get();
+ }
+ return ErrorResponse.notFoundError("Flag " + flagId + " not defined");
}
private HttpResponse getFlagDataList(HttpRequest request) {
return new FlagDataListResponse(flagsV1Uri(request), flagsDb.getAllFlags(),
- Objects.equals(request.getProperty("recursive"), "true"));
+ Objects.equals(request.getProperty("recursive"), "true"));
}
private HttpResponse getFlagData(FlagId flagId) {
- FlagData data = flagsDb.getValue(flagId).orElseThrow(() -> new NotFoundException("Flag " + flagId + " not set"));
- return new FlagDataResponse(data);
+ var data = flagsDb.getValue(flagId).map(FlagDataResponse::new);
+ if (data.isPresent()) {
+ return data.get();
+ }
+ return ErrorResponse.notFoundError("Flag " + flagId + " not set");
}
private HttpResponse putFlagData(HttpRequest request, FlagId flagId) {
@@ -86,7 +106,7 @@ public class FlagsHandler extends HttpHandler {
try {
data = FlagData.deserialize(request.getData());
} catch (UncheckedIOException e) {
- return HttpErrorResponse.badRequest("Failed to deserialize request data: " + Exceptions.toMessageString(e));
+ return ErrorResponse.badRequest("Failed to deserialize request data: " + Exceptions.toMessageString(e));
}
if (!isForce(request)) {
@@ -105,16 +125,14 @@ public class FlagsHandler extends HttpHandler {
private FlagId findFlagId(HttpRequest request, Path path) {
FlagId flagId = new FlagId(path.get("flagId"));
-
- if (!isForce(request)) {
- Flags.getFlag(flagId).orElseThrow(() ->
- new NotFoundException("There is no flag '" + flagId + "' (use ?force=true to override)"));
+ if (!isForce(request) && Flags.getFlag(flagId).isEmpty()) {
+ throw new IllegalArgumentException("There is no flag '" + flagId + "' (use ?force=true to override)");
}
-
return flagId;
}
private boolean isForce(HttpRequest request) {
return Objects.equals(request.getProperty("force"), "true");
}
+
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/flags/OKResponse.java b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/OKResponse.java
index 87c02ae56f1..f41940f692b 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/flags/OKResponse.java
+++ b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/OKResponse.java
@@ -1,9 +1,8 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.config.server.http.flags;
+package com.yahoo.vespa.configserver.flags.http;
import com.yahoo.container.jdisc.EmptyResponse;
import com.yahoo.jdisc.Response;
-import com.yahoo.vespa.config.server.http.HttpConfigResponse;
/**
* @author hakonhall
@@ -15,6 +14,6 @@ public class OKResponse extends EmptyResponse {
@Override
public String getContentType() {
- return HttpConfigResponse.JSON_CONTENT_TYPE;
+ return "application/json";
}
}
diff --git a/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/SlimeJsonResponse.java b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/SlimeJsonResponse.java
new file mode 100644
index 00000000000..e5568514894
--- /dev/null
+++ b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/SlimeJsonResponse.java
@@ -0,0 +1,38 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.configserver.flags.http;
+
+import com.yahoo.container.jdisc.HttpResponse;
+import com.yahoo.slime.JsonFormat;
+import com.yahoo.slime.Slime;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A generic Json response using Slime for JSON encoding
+ *
+ * @author bratseth
+ */
+public class SlimeJsonResponse extends HttpResponse {
+
+ private final Slime slime;
+
+ public SlimeJsonResponse(Slime slime) {
+ super(200);
+ this.slime = slime;
+ }
+
+ public SlimeJsonResponse(int statusCode, Slime slime) {
+ super(statusCode);
+ this.slime = slime;
+ }
+
+ @Override
+ public void render(OutputStream stream) throws IOException {
+ new JsonFormat(true).encode(stream, slime);
+ }
+
+ @Override
+ public String getContentType() { return "application/json"; }
+
+}
diff --git a/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/V1Response.java b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/V1Response.java
new file mode 100644
index 00000000000..ac1e9514700
--- /dev/null
+++ b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/V1Response.java
@@ -0,0 +1,46 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.configserver.flags.http;
+
+import com.yahoo.container.jdisc.HttpResponse;
+import com.yahoo.jdisc.Response;
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.JsonFormat;
+import com.yahoo.slime.Slime;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+
+/**
+ * @author hakonhall
+ */
+public class V1Response extends HttpResponse {
+
+ private final Slime slime;
+
+ public V1Response(String flagsV1Uri, String... names) {
+ super(Response.Status.OK);
+ this.slime = generateBody(flagsV1Uri, List.of(names));
+ }
+
+ @Override
+ public void render(OutputStream stream) throws IOException {
+ new JsonFormat(true).encode(stream, slime);
+ }
+
+ @Override
+ public String getContentType() {
+ return "application/json";
+ }
+
+ private static Slime generateBody(String flagsV1Uri, List<String> names) {
+ Slime slime = new Slime();
+ Cursor root = slime.setObject();
+ names.forEach(name -> {
+ Cursor data = root.setObject(name);
+ data.setString("url", flagsV1Uri + "/" + name);
+ });
+ return slime;
+ }
+
+}
diff --git a/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/package-info.java b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/package-info.java
new file mode 100644
index 00000000000..87b63114b73
--- /dev/null
+++ b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/package-info.java
@@ -0,0 +1,8 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+/**
+ * @author mpolden
+ */
+@ExportPackage
+package com.yahoo.vespa.configserver.flags.http;
+
+import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/package-info.java b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/package-info.java
index 97e66d95715..d6f078326a3 100644
--- a/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/package-info.java
+++ b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/package-info.java
@@ -3,5 +3,3 @@
package com.yahoo.vespa.configserver.flags;
import com.yahoo.osgi.annotation.ExportPackage;
-
-/** The node repository controls and allocates the nodes available in a hosted Vespa zone */
diff --git a/configserver-flags/src/test/java/com/yahoo/vespa/configserver/flags/ConfigServerFlagSourceTest.java b/configserver-flags/src/test/java/com/yahoo/vespa/configserver/flags/ConfigServerFlagSourceTest.java
index d0d1d61628c..c46677bfc10 100644
--- a/configserver-flags/src/test/java/com/yahoo/vespa/configserver/flags/ConfigServerFlagSourceTest.java
+++ b/configserver-flags/src/test/java/com/yahoo/vespa/configserver/flags/ConfigServerFlagSourceTest.java
@@ -103,4 +103,4 @@ public class ConfigServerFlagSourceTest {
assertFalse(rawFlag2.isPresent());
verify(flagsDb, times(1)).getValue(flagId2);
}
-} \ No newline at end of file
+}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/flags/FlagsHandlerTest.java b/configserver-flags/src/test/java/com/yahoo/vespa/configserver/flags/http/FlagsHandlerTest.java
index 5ae6ce9820b..cbd37c8a5cf 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/flags/FlagsHandlerTest.java
+++ b/configserver-flags/src/test/java/com/yahoo/vespa/configserver/flags/http/FlagsHandlerTest.java
@@ -1,25 +1,27 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.config.server.http.flags;
+package com.yahoo.vespa.configserver.flags.http;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.jdisc.http.HttpRequest.Method;
import com.yahoo.text.Utf8;
-import com.yahoo.vespa.config.server.http.SessionHandlerTest;
+import com.yahoo.vespa.configserver.flags.FlagsDb;
import com.yahoo.vespa.configserver.flags.db.FlagsDbImpl;
import com.yahoo.vespa.curator.mock.MockCurator;
import com.yahoo.vespa.flags.FetchVector;
import com.yahoo.vespa.flags.FlagId;
import com.yahoo.vespa.flags.Flags;
import com.yahoo.vespa.flags.UnboundBooleanFlag;
+import com.yahoo.yolean.Exceptions;
import org.junit.Test;
import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import static com.yahoo.yolean.Exceptions.uncheck;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
@@ -37,7 +39,7 @@ public class FlagsHandlerTest {
private static final String FLAGS_V1_URL = "https://foo.com:4443/flags/v1";
- private final FlagsDbImpl flagsDb = new FlagsDbImpl(new MockCurator());
+ private final FlagsDb flagsDb = new FlagsDbImpl(new MockCurator());
private final FlagsHandler handler = new FlagsHandler(FlagsHandler.testOnlyContext(), flagsDb);
@Test
@@ -161,7 +163,7 @@ public class FlagsHandlerTest {
@Test
public void testForcing() {
- assertThat(handle(Method.PUT, "/data/" + new FlagId("undef"), "", 404),
+ assertThat(handle(Method.PUT, "/data/" + new FlagId("undef"), "", 400),
containsString("There is no flag 'undef'"));
assertThat(handle(Method.PUT, "/data/" + new FlagId("undef") + "?force=true", "", 400),
@@ -191,10 +193,12 @@ public class FlagsHandlerTest {
HttpResponse response = handler.handle(request);
assertEquals(expectedStatus, response.getStatus());
assertEquals("application/json", response.getContentType());
- return uncheck(() -> SessionHandlerTest.getRenderedString(response));
+ var outputStream = new ByteArrayOutputStream();
+ Exceptions.uncheck(() -> response.render(outputStream));
+ return outputStream.toString(StandardCharsets.UTF_8);
}
private InputStream makeInputStream(String content) {
return new ByteArrayInputStream(Utf8.toBytes(content));
}
-} \ No newline at end of file
+}
diff --git a/configserver/pom.xml b/configserver/pom.xml
index f346cde63a3..fd33950a546 100644
--- a/configserver/pom.xml
+++ b/configserver/pom.xml
@@ -185,6 +185,12 @@
<scope>compile</scope>
</dependency>
<dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>jaxrs_client_utils</artifactId>
+ <version>${project.version}</version>
+ <scope>compile</scope> <!-- TODO Should ideally be provided, but this bundle is not installed as part of configserver. Orchestrator bundle also includes jaxrs_client_utils in compile scope -->
+ </dependency>
+ <dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>provided</scope>
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
index 192488c11ab..62fef0ad79a 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
@@ -42,7 +42,11 @@ import com.yahoo.vespa.config.server.deploy.Deployment;
import com.yahoo.vespa.config.server.deploy.InfraDeployerProvider;
import com.yahoo.vespa.config.server.http.LogRetriever;
import com.yahoo.vespa.config.server.http.SimpleHttpFetcher;
+import com.yahoo.vespa.config.server.http.v2.MetricsResponse;
import com.yahoo.vespa.config.server.http.v2.PrepareResult;
+import com.yahoo.vespa.config.server.metrics.ClusterInfo;
+import com.yahoo.vespa.config.server.metrics.MetricsAggregator;
+import com.yahoo.vespa.config.server.metrics.MetricsRetriever;
import com.yahoo.vespa.config.server.provision.HostProvisionerProvider;
import com.yahoo.vespa.config.server.session.LocalSession;
import com.yahoo.vespa.config.server.session.LocalSessionRepo;
@@ -69,8 +73,11 @@ import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collection;
+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;
import java.util.logging.Level;
@@ -80,6 +87,7 @@ import java.util.stream.Collectors;
import static com.yahoo.config.model.api.container.ContainerServiceType.CLUSTERCONTROLLER_CONTAINER;
import static com.yahoo.config.model.api.container.ContainerServiceType.CONTAINER;
import static com.yahoo.config.model.api.container.ContainerServiceType.LOGSERVER_CONTAINER;
+import static com.yahoo.config.model.api.container.ContainerServiceType.METRICS_PROXY_CONTAINER;
import static com.yahoo.vespa.config.server.tenant.TenantRepository.HOSTED_VESPA_TENANT;
import static java.nio.file.Files.readAttributes;
@@ -460,7 +468,7 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
Tenant tenant = tenantRepository.getTenant(applicationId.tenant());
if (tenant == null) throw new NotFoundException("Tenant '" + applicationId.tenant() + "' not found");
long sessionId = getSessionIdForApplication(tenant, applicationId);
- RemoteSession session = tenant.getRemoteSessionRepo().getSession(sessionId, 0);
+ RemoteSession session = tenant.getRemoteSessionRepo().getSession(sessionId);
return session.ensureApplicationLoaded().getForVersionOrLatest(version, clock.instant());
} catch (NotFoundException e) {
log.log(LogLevel.WARNING, "Failed getting application for '" + applicationId + "': " + e.getMessage());
@@ -633,6 +641,21 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
return tenantRepository.getTenant(tenantName).getApplicationRepo().activeApplications();
}
+ // ---------------- Metrics ------------------------------------------------------------------------
+
+ public MetricsResponse getMetrics(ApplicationId applicationId) {
+ var metricsRetriever = new MetricsRetriever();
+ var clusters = getClustersOfApplication(applicationId);
+ var clusterMetrics = new LinkedHashMap<ClusterInfo, MetricsAggregator>();
+
+ clusters.forEach(cluster -> {
+ var metrics = metricsRetriever.requestMetricsForCluster(cluster);
+ clusterMetrics.put(cluster, metrics);
+ });
+
+ return new MetricsResponse(200, applicationId, clusterMetrics);
+ }
+
// ---------------- Misc operations ----------------------------------------------------------------
public ApplicationMetaData getMetadataFromSession(Tenant tenant, long sessionId) {
@@ -750,16 +773,47 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
.anyMatch(serviceInfo -> serviceInfo.getServiceType().equalsIgnoreCase("logserver")))
.findFirst().orElseThrow(() -> new IllegalArgumentException("Could not find HostInfo for LogServer"));
- ServiceInfo containerServiceInfo = logServerHostInfo.getServices().stream()
- .filter(service -> List.of(LOGSERVER_CONTAINER.serviceName, CONTAINER.serviceName).contains(service.getServiceType()))
+ ServiceInfo serviceInfo = logServerHostInfo.getServices().stream().filter(service -> List.of(LOGSERVER_CONTAINER.serviceName, CONTAINER.serviceName).contains(service.getServiceType()))
.findFirst().orElseThrow(() -> new IllegalArgumentException("No container running on logserver host"));
+ int port = servicePort(serviceInfo);
+ return "http://" + logServerHostInfo.getHostname() + ":" + port + "/logs";
+ }
- int port = containerServiceInfo.getPorts().stream()
+ private int servicePort(ServiceInfo serviceInfo) {
+ int port = serviceInfo.getPorts().stream()
.filter(portInfo -> portInfo.getTags().stream().anyMatch(tag -> tag.equalsIgnoreCase("http")))
.findFirst().orElseThrow(() -> new IllegalArgumentException("Could not find HTTP port"))
.getPort();
+ return port;
+ }
- return "http://" + logServerHostInfo.getHostname() + ":" + port + "/logs";
+ /** Finds the hosts of an application, grouped by cluster name */
+ private Collection<ClusterInfo> getClustersOfApplication(ApplicationId applicationId) {
+ Application application = getApplication(applicationId);
+ Map<String, ClusterInfo> clusters = new HashMap<>();
+ application.getModel().getHosts().stream()
+ .filter(host -> host.getServices().stream().noneMatch(serviceInfo -> serviceInfo.getServiceType().equalsIgnoreCase("logserver")))
+ .forEach(hostInfo -> {
+ ServiceInfo metricsService = getServiceInfoByType(hostInfo, METRICS_PROXY_CONTAINER.serviceName);
+ ServiceInfo clusterServiceInfo = getServiceInfoByType(hostInfo, "container", "searchnode");
+ ClusterInfo clusterInfo = createClusterInfo(clusterServiceInfo);
+ URI host = URI.create("http://" + hostInfo.getHostname() + ":" + servicePort(metricsService) + "/metrics/v1/values?consumer=Vespa");
+ clusters.computeIfAbsent(clusterInfo.getClusterId(), c -> clusterInfo).addHost(host);
+ }
+ );
+ return clusters.values();
+
+ }
+
+ private ServiceInfo getServiceInfoByType(HostInfo hostInfo, String... types) {
+ List<String> type = List.of(types);
+ return hostInfo.getServices().stream().filter(serviceInfo -> type.contains(serviceInfo.getServiceType())).findFirst().orElseThrow();
+ }
+
+ private ClusterInfo createClusterInfo(ServiceInfo serviceInfo) {
+ String clusterName = serviceInfo.getServiceName();
+ ClusterInfo.ClusterType clusterType = serviceInfo.getServiceType().equals("searchnode") ? ClusterInfo.ClusterType.content : ClusterInfo.ClusterType.container;
+ return new ClusterInfo(clusterName, clusterType);
}
/** Returns version to use when deploying application in given environment */
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java
index d55e07540d6..d0f8005ace1 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server.application;
+import ai.vespa.util.http.VespaClientBuilderFactory;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.inject.Inject;
import com.yahoo.component.AbstractComponent;
@@ -17,8 +18,9 @@ import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.client.Client;
-import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.HttpHeaders;
import java.net.URI;
import java.time.Duration;
import java.util.ArrayList;
@@ -55,6 +57,7 @@ public class ConfigConvergenceChecker extends AbstractComponent {
);
private final StateApiFactory stateApiFactory;
+ private final VespaClientBuilderFactory clientBuilderFactory = new VespaClientBuilderFactory();
@Inject
public ConfigConvergenceChecker() {
@@ -97,6 +100,11 @@ public class ConfigConvergenceChecker extends AbstractComponent {
}
}
+ @Override
+ public void deconstruct() {
+ clientBuilderFactory.close();
+ }
+
@Path(statePath)
public interface StateApi {
@Path(configSubPath)
@@ -152,8 +160,11 @@ public class ConfigConvergenceChecker extends AbstractComponent {
return false;
}
- private static Client createClient(Duration timeout) {
- return ClientBuilder.newBuilder()
+ private Client createClient(Duration timeout) {
+ return clientBuilderFactory.newBuilder()
+ .register(
+ (ClientRequestFilter) ctx ->
+ ctx.getHeaders().put(HttpHeaders.USER_AGENT, List.of("config-convergence-checker")))
.property(ClientProperties.CONNECT_TIMEOUT, (int) timeout.toMillis())
.property(ClientProperties.READ_TIMEOUT, (int) timeout.toMillis())
.build();
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
index d875385d14d..64148ba5de4 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
@@ -136,6 +136,7 @@ public class ModelContextImpl implements ModelContext {
private final boolean useAdaptiveDispatch;
private final boolean dispatchWithProtobuf;
private final Optional<TlsSecrets> tlsSecrets;
+ private final boolean enableGroupingSessionCache;
public Properties(ApplicationId applicationId,
boolean multitenantFromConfig,
@@ -172,6 +173,8 @@ public class ModelContextImpl implements ModelContext {
this.useAdaptiveDispatch = Flags.USE_ADAPTIVE_DISPATCH.bindTo(flagSource)
.with(FetchVector.Dimension.APPLICATION_ID, applicationId.serializedForm()).value();
this.tlsSecrets = tlsSecrets;
+ this.enableGroupingSessionCache = Flags.ENABLE_GROUPING_SESSION_CACHE.bindTo(flagSource)
+ .with(FetchVector.Dimension.APPLICATION_ID, applicationId.serializedForm()).value();
}
@Override
@@ -228,6 +231,11 @@ public class ModelContextImpl implements ModelContext {
@Override
public Optional<TlsSecrets> tlsSecrets() { return tlsSecrets; }
+
+ @Override
+ public boolean enableGroupingSessionCache() {
+ return enableGroupingSessionCache;
+ }
}
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClient.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClient.java
index 110e73bcdf9..484124991d9 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClient.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClient.java
@@ -288,22 +288,14 @@ public class ZooKeeperClient {
for (Map.Entry<ConfigDefinitionKey, UnparsedConfigDefinition> entry : configDefs.entrySet()) {
ConfigDefinitionKey key = entry.getKey();
String contents = entry.getValue().getUnparsedContent();
- write(key.getName(), key.getNamespace(), getZooKeeperAppPath(ConfigCurator.USER_DEFCONFIGS_ZK_SUBPATH).getAbsolute(), contents);
- write(key.getName(), key.getNamespace(), getZooKeeperAppPath(ConfigCurator.DEFCONFIGS_ZK_SUBPATH).getAbsolute(), contents);
+ writeConfigDefinition(key.getName(), key.getNamespace(), getZooKeeperAppPath(ConfigCurator.USER_DEFCONFIGS_ZK_SUBPATH).getAbsolute(), contents);
+ writeConfigDefinition(key.getName(), key.getNamespace(), getZooKeeperAppPath(ConfigCurator.DEFCONFIGS_ZK_SUBPATH).getAbsolute(), contents);
}
logger.log(LogLevel.FINE, configDefs.size() + " user config definitions");
}
- private void write(String name, String namespace, String path, String data) {
- write(name, namespace, "", path, com.yahoo.text.Utf8.toBytes(data));
- }
-
- private void write(String name, String namespace, String version, String path, byte[] data) {
- configCurator.putDefData(
- ("".equals(namespace)) ? name : (namespace + "." + name),
- version,
- path,
- data);
+ private void writeConfigDefinition(String name, String namespace, String path, String data) {
+ configCurator.putDefData(namespace + "." + name, path, com.yahoo.text.Utf8.toBytes(data));
}
private void write(Version vespaVersion, FileRegistry fileRegistry) {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpErrorResponse.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpErrorResponse.java
index 3d2ecd4a2ca..6559292645c 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpErrorResponse.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpErrorResponse.java
@@ -49,7 +49,8 @@ public class HttpErrorResponse extends HttpResponse {
REQUEST_TIMEOUT,
UNKNOWN_VESPA_VERSION,
PARENT_HOST_NOT_READY,
- CERTIFICATE_NOT_READY
+ CERTIFICATE_NOT_READY,
+ LOAD_BALANCER_NOT_READY
}
public static HttpErrorResponse notFoundError(String msg) {
@@ -100,6 +101,10 @@ public class HttpErrorResponse extends HttpResponse {
return new HttpErrorResponse(CONFLICT, errorCodes.CERTIFICATE_NOT_READY.name(), msg);
}
+ public static HttpErrorResponse loadBalancerNotReady(String msg) {
+ return new HttpErrorResponse(CONFLICT, errorCodes.LOAD_BALANCER_NOT_READY.name(), msg);
+ }
+
@Override
public void render(OutputStream stream) throws IOException {
new JsonFormat(true).encode(stream, slime);
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpHandler.java
index 20ee77be9fe..7cdb8d3a60c 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpHandler.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpHandler.java
@@ -4,6 +4,7 @@ package com.yahoo.vespa.config.server.http;
import com.yahoo.config.provision.ApplicationLockException;
import com.yahoo.config.provision.CertificateNotReadyException;
import com.yahoo.config.provision.ParentHostUnavailableException;
+import com.yahoo.config.provision.exception.LoadBalancerServiceException;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.container.jdisc.LoggingRequestHandler;
@@ -67,6 +68,8 @@ public class HttpHandler extends LoggingRequestHandler {
return HttpErrorResponse.parentHostNotReady(getMessage(e, request));
} catch (CertificateNotReadyException e) {
return HttpErrorResponse.certificateNotReady(getMessage(e, request));
+ } catch (LoadBalancerServiceException e) {
+ return HttpErrorResponse.loadBalancerNotReady(getMessage(e, request));
} catch (Exception e) {
log.log(LogLevel.WARNING, "Unexpected exception handling a config server request", e);
return HttpErrorResponse.internalServerError(getMessage(e, request));
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/flags/V1Response.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/flags/V1Response.java
deleted file mode 100644
index 3594c801ca8..00000000000
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/flags/V1Response.java
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.config.server.http.flags;
-
-import com.yahoo.jdisc.Response;
-import com.yahoo.slime.Cursor;
-import com.yahoo.slime.Slime;
-import com.yahoo.text.Utf8;
-import com.yahoo.vespa.config.SlimeUtils;
-import com.yahoo.vespa.config.server.http.HttpConfigResponse;
-import com.yahoo.vespa.config.server.http.StaticResponse;
-
-import java.util.Arrays;
-import java.util.List;
-
-import static com.yahoo.yolean.Exceptions.uncheck;
-
-/**
- * @author hakonhall
- */
-public class V1Response extends StaticResponse {
- public V1Response(String flagsV1Uri, String... names) {
- super(Response.Status.OK, HttpConfigResponse.JSON_CONTENT_TYPE, generateBody(flagsV1Uri, Arrays.asList(names)));
- }
-
- private static String generateBody(String flagsV1Uri, List<String> names) {
- Slime slime = new Slime();
- Cursor root = slime.setObject();
- names.forEach(name -> {
- Cursor data = root.setObject(name);
- data.setString("url", flagsV1Uri + "/" + name);
- });
- return Utf8.toString(uncheck(() -> SlimeUtils.toJsonBytes(slime)));
- }
-}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java
index 865805d1258..e18c6ad6c56 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java
@@ -104,6 +104,10 @@ public class ApplicationHandler extends HttpHandler {
return applicationRepository.getLogs(applicationId, hostname, apiParams);
}
+ if (isMetricsRequest(request)) {
+ return applicationRepository.getMetrics(applicationId);
+ }
+
if (isIsSuspendedRequest(request)) {
return new ApplicationSuspendedResponse(applicationRepository.isSuspended(applicationId));
}
@@ -144,6 +148,7 @@ public class ApplicationHandler extends HttpHandler {
"http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/serviceconverge",
"http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/serviceconverge/*",
"http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/clustercontroller/*/status/*",
+ "http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/metrics",
"http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*",
"http://*/application/v2/tenant/*/application/*/logs",
"http://*/application/v2/tenant/*/application/*");
@@ -154,6 +159,11 @@ public class ApplicationHandler extends HttpHandler {
request.getUri().getPath().endsWith("/suspended");
}
+ private static boolean isMetricsRequest(HttpRequest request) {
+ return getBindingMatch(request).groupCount() == 7 &&
+ request.getUri().getPath().endsWith("/metrics");
+ }
+
private static boolean isLogRequest(HttpRequest request) {
return getBindingMatch(request).groupCount() == 4 &&
request.getUri().getPath().endsWith("/logs");
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/MetricsResponse.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/MetricsResponse.java
new file mode 100644
index 00000000000..0abc59f54b2
--- /dev/null
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/MetricsResponse.java
@@ -0,0 +1,56 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.config.server.http.v2;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.container.jdisc.HttpResponse;
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.JsonFormat;
+import com.yahoo.slime.Slime;
+import com.yahoo.vespa.config.server.http.HttpConfigResponse;
+import com.yahoo.vespa.config.server.metrics.ClusterInfo;
+import com.yahoo.vespa.config.server.metrics.MetricsAggregator;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Map;
+
+/**
+ * @author olaa
+ */
+public class MetricsResponse extends HttpResponse {
+
+ private final Slime slime = new Slime();
+
+ public MetricsResponse(int status, ApplicationId applicationId, Map<ClusterInfo, MetricsAggregator> aggregatedMetrics) {
+ super(status);
+
+ Cursor application = slime.setObject();
+ application.setString("applicationId", applicationId.serializedForm());
+
+ Cursor clusters = application.setArray("clusters");
+
+ for (var entry : aggregatedMetrics.entrySet()) {
+ Cursor cluster = clusters.addObject();
+ cluster.setString("clusterId", entry.getKey().getClusterId());
+ cluster.setString("clusterType", entry.getKey().getClusterType().name());
+
+ MetricsAggregator aggregator = entry.getValue();
+ Cursor metrics = cluster.setObject("metrics");
+ aggregator.aggregateQueryRate().ifPresent(queryRate -> metrics.setDouble("queriesPerSecond", queryRate));
+ aggregator.aggregateFeedRate().ifPresent(feedRate -> metrics.setDouble("feedPerSecond", feedRate));
+ aggregator.aggregateDocumentCount().ifPresent(documentCount -> metrics.setDouble("documentCount", documentCount));
+ aggregator.aggregateQueryLatency().ifPresent(queryLatency -> metrics.setDouble("queryLatency",queryLatency));
+ aggregator.aggregateFeedLatency().ifPresent(feedLatency -> metrics.setDouble("feedLatency", feedLatency));
+ }
+ }
+
+ @Override
+ public void render(OutputStream outputStream) throws IOException {
+ new JsonFormat(false).encode(outputStream, slime);
+ }
+
+ @Override
+ public String getContentType() {
+ return HttpConfigResponse.JSON_CONTENT_TYPE;
+ }
+}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/metrics/ClusterInfo.java b/configserver/src/main/java/com/yahoo/vespa/config/server/metrics/ClusterInfo.java
new file mode 100644
index 00000000000..7507b5c4c2c
--- /dev/null
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/metrics/ClusterInfo.java
@@ -0,0 +1,56 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.config.server.metrics;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author olaa
+ */
+public class ClusterInfo {
+
+ private final String clusterId;
+ private final ClusterType clusterType;
+ private final List<URI> hostnames;
+
+ public ClusterInfo(String clusterId, ClusterType clusterType) {
+ this(clusterId, clusterType, new ArrayList<>());
+ }
+
+ public ClusterInfo(String clusterId, ClusterType clusterType, List<URI> hostnames) {
+ this.clusterId = clusterId;
+ this.clusterType = clusterType;
+ this.hostnames = hostnames;
+ }
+
+ public String getClusterId() {
+ return clusterId;
+ }
+
+ public ClusterType getClusterType() {
+ return clusterType;
+ }
+
+ public List<URI> getHostnames() {
+ return hostnames;
+ }
+
+ public void addHost(URI host) {
+ hostnames.add(host);
+ }
+
+ public enum ClusterType {
+ content,
+ container;
+
+ public static boolean isValidType(String enumString) {
+ try {
+ valueOf(enumString);
+ return true;
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ }
+ };
+}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/metrics/MetricsAggregator.java b/configserver/src/main/java/com/yahoo/vespa/config/server/metrics/MetricsAggregator.java
new file mode 100644
index 00000000000..8fa08275ad5
--- /dev/null
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/metrics/MetricsAggregator.java
@@ -0,0 +1,86 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.config.server.metrics;
+
+import java.time.Instant;
+import java.util.Optional;
+
+/**
+ * @author olaa
+ * @author ogronnesby
+ */
+public class MetricsAggregator {
+
+ private LatencyMetrics feed;
+ private LatencyMetrics qr;
+ private LatencyMetrics container;
+ private Double documentCount;
+ private Instant timestamp;
+
+ public MetricsAggregator addFeedLatency(double sum, double count) {
+ this.feed = combineLatency(this.feed, sum, count);
+ return this;
+ }
+
+ public MetricsAggregator addQrLatency(double sum, double count) {
+ this.qr = combineLatency(this.qr, sum, count);
+ return this;
+ }
+
+ public MetricsAggregator addContainerLatency(double sum, double count) {
+ this.container = combineLatency(this.container, sum, count);
+ return this;
+ }
+
+ public MetricsAggregator addDocumentCount(double count) {
+ this.documentCount = (this.documentCount == null ? 0.0 : this.documentCount) + count;
+ return this;
+ }
+
+ public MetricsAggregator setTimestamp(Instant timestamp) {
+ this.timestamp = timestamp;
+ return this;
+ }
+
+ public Optional<Double> aggregateFeedLatency() {
+ return Optional.ofNullable(feed).map(m -> m.latencySum / m.latencyCount).filter(num -> !num.isNaN());
+
+ }
+
+ public Optional<Double> aggregateFeedRate() {
+ return Optional.ofNullable(feed).map(m -> m.latencyCount / 60);
+ }
+
+ public Optional<Double> aggregateQueryLatency() {
+ if (container == null && qr == null) return Optional.empty();
+ var c = Optional.ofNullable(container).orElseGet(LatencyMetrics::new);
+ var q = Optional.ofNullable(qr).orElseGet(LatencyMetrics::new);
+ return Optional.of((c.latencySum + q.latencySum) / (c.latencyCount + q.latencyCount)).filter(num -> !num.isNaN());
+ }
+
+ public Optional<Double> aggregateQueryRate() {
+ if (container == null && qr == null) return Optional.empty();
+ var c = Optional.ofNullable(container).orElseGet(LatencyMetrics::new);
+ var q = Optional.ofNullable(qr).orElseGet(LatencyMetrics::new);
+ return Optional.of((c.latencyCount + q.latencyCount) / 60);
+ }
+
+ public Optional<Double> aggregateDocumentCount() {
+ return Optional.ofNullable(documentCount);
+ }
+
+ public Instant getTimestamp() {
+ return timestamp;
+ }
+
+ private LatencyMetrics combineLatency(LatencyMetrics metricsOrNull, double sum, double count) {
+ var metrics = Optional.ofNullable(metricsOrNull).orElseGet(LatencyMetrics::new);
+ metrics.latencyCount += count;
+ metrics.latencySum += sum;
+ return metrics;
+ }
+
+ private static class LatencyMetrics {
+ double latencySum;
+ double latencyCount;
+ }
+}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/metrics/MetricsRetriever.java b/configserver/src/main/java/com/yahoo/vespa/config/server/metrics/MetricsRetriever.java
new file mode 100644
index 00000000000..4fbeae11758
--- /dev/null
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/metrics/MetricsRetriever.java
@@ -0,0 +1,97 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.config.server.metrics;
+
+import com.yahoo.slime.ArrayTraverser;
+import com.yahoo.slime.Inspector;
+import com.yahoo.slime.Slime;
+import com.yahoo.vespa.config.SlimeUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.HttpClientBuilder;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.net.URI;
+import java.time.Instant;
+import java.util.logging.Logger;
+
+
+/**
+ * Client for reaching out to nodes in an application instance and get their
+ * metrics.
+ *
+ * @author olaa
+ * @author ogronnesby
+ */
+public class MetricsRetriever {
+ private static final Logger log = Logger.getLogger(MetricsRetriever.class.getName());
+ private final HttpClient httpClient = HttpClientBuilder.create().build();
+
+ /**
+ * Call the metrics API on each host in the cluster and aggregate the metrics
+ * into a single value.
+ */
+ public MetricsAggregator requestMetricsForCluster(ClusterInfo clusterInfo) {
+ var aggregator = new MetricsAggregator();
+ clusterInfo.getHostnames().forEach(host -> getHostMetrics(host, aggregator));
+ return aggregator;
+ }
+
+ private void getHostMetrics(URI hostURI, MetricsAggregator metrics) {
+ Slime responseBody = doMetricsRequest(hostURI);
+ var parseError = responseBody.get().field("error_message");
+
+ if (parseError.valid()) {
+ log.info("Failed to retrieve metrics from " + hostURI + ": " + parseError.asString());
+ }
+
+ Inspector services = responseBody.get().field("services");
+ services.traverse((ArrayTraverser) (i, servicesInspector) -> {
+ parseService(servicesInspector, metrics);
+ });
+ }
+
+ private Slime doMetricsRequest(URI hostURI) {
+ HttpGet get = new HttpGet(hostURI);
+ try {
+ HttpResponse response = httpClient.execute(get);
+ InputStream is = response.getEntity().getContent();
+ Slime slime = SlimeUtils.jsonToSlime(is.readAllBytes());
+ is.close();
+ return slime;
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private void parseService(Inspector service, MetricsAggregator metrics) {
+ String serviceName = service.field("name").asString();
+ Instant timestamp = Instant.ofEpochSecond(service.field("timestamp").asLong());
+ metrics.setTimestamp(timestamp);
+ service.field("metrics").traverse((ArrayTraverser) (i, m) -> {
+ Inspector values = m.field("values");
+ switch (serviceName) {
+ case "vespa.container":
+ metrics.addContainerLatency(
+ values.field("query_latency.sum").asDouble(),
+ values.field("query_latency.count").asDouble());
+ metrics.addFeedLatency(
+ values.field("feed_latency.sum").asDouble(),
+ values.field("feed_latency.count").asDouble());
+ break;
+ case "vespa.qrserver":
+ metrics.addQrLatency(
+ values.field("query_latency.sum").asDouble(),
+ values.field("query_latency.count").asDouble());
+ break;
+ case "vespa.distributor":
+ metrics.addDocumentCount(values.field("vds.distributor.docsstored.average").asDouble());
+ break;
+ }
+ });
+
+ }
+
+}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/monitoring/ZKMetricUpdater.java b/configserver/src/main/java/com/yahoo/vespa/config/server/monitoring/ZKMetricUpdater.java
index 408bf44e733..b2813be5456 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/monitoring/ZKMetricUpdater.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/monitoring/ZKMetricUpdater.java
@@ -97,7 +97,7 @@ public class ZKMetricUpdater extends TimerTask {
buffer.clear();
} while (nread >= 0);
- return Optional.of(baos.toString("UTF-8"));
+ return Optional.of(baos.toString(StandardCharsets.UTF_8));
} catch (IOException | InterruptedException | ExecutionException | TimeoutException e) {
log.warning("Failure in retrieving monitoring data: (" + e.getClass().getName() + ") " + e.getMessage());
return Optional.empty();
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSession.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSession.java
index 7fd29368ab3..2c6d7de8b0c 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSession.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSession.java
@@ -36,7 +36,7 @@ import java.util.Optional;
// TODO: Separate the "application store" and "session" aspects - the latter belongs in the HTTP layer -bratseth
public class LocalSession extends Session implements Comparable<LocalSession> {
- private final ApplicationPackage applicationPackage;
+ protected final ApplicationPackage applicationPackage;
private final TenantApplications applicationRepo;
private final SessionPreparer sessionPreparer;
private final SessionContext sessionContext;
@@ -47,7 +47,6 @@ public class LocalSession extends Session implements Comparable<LocalSession> {
*
* @param sessionId The session id for this session.
*/
- // TODO tenant in SessionContext?
public LocalSession(TenantName tenant, long sessionId, SessionPreparer sessionPreparer, SessionContext sessionContext) {
super(tenant, sessionId, sessionContext.getSessionZooKeeperClient());
this.serverDB = sessionContext.getServerDBSessionDir();
@@ -211,7 +210,7 @@ public class LocalSession extends Session implements Comparable<LocalSession> {
private final String pathToDelete;
- public DeleteOperation(String pathToDelete) {
+ DeleteOperation(String pathToDelete) {
this.pathToDelete = pathToDelete;
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionRepo.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionRepo.java
index f6d73f33504..ad7a5116ac4 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionRepo.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionRepo.java
@@ -1,8 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server.session;
-import com.yahoo.concurrent.InThreadExecutorService;
-import com.yahoo.concurrent.StripedExecutor;
import com.yahoo.config.provision.TenantName;
import com.yahoo.log.LogLevel;
import com.yahoo.path.Path;
@@ -16,7 +14,6 @@ import com.yahoo.vespa.curator.Curator;
import java.io.File;
import java.io.FilenameFilter;
import java.time.Clock;
-import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -40,23 +37,20 @@ public class LocalSessionRepo extends SessionRepo<LocalSession> {
private final Clock clock;
private final Curator curator;
private final Executor zkWatcherExecutor;
+ private final TenantFileSystemDirs tenantFileSystemDirs;
- public LocalSessionRepo(TenantName tenantName, GlobalComponentRegistry registry, TenantFileSystemDirs tenantFileSystemDirs, LocalSessionLoader loader) {
- this(registry.getClock(), registry.getCurator(), registry.getConfigserverConfig().sessionLifetime(),
- command -> registry.getZkWatcherExecutor().execute(tenantName, command));
- loadSessions(tenantFileSystemDirs.sessionsPath(), loader);
+ public LocalSessionRepo(TenantName tenantName, GlobalComponentRegistry componentRegistry, LocalSessionLoader loader) {
+ this(tenantName, componentRegistry);
+ loadSessions(loader);
}
// Constructor public only for testing
- public LocalSessionRepo(Clock clock, Curator curator) {
- this(clock, curator, Duration.ofDays(1).toMillis(), Runnable::run);
- }
-
- private LocalSessionRepo(Clock clock, Curator curator, long sessionLifetime, Executor zkWatcherExecutor) {
- this.clock = clock;
- this.curator = curator;
- this.sessionLifetime = sessionLifetime;
- this.zkWatcherExecutor = zkWatcherExecutor;
+ public LocalSessionRepo(TenantName tenantName, GlobalComponentRegistry componentRegistry) {
+ this.clock = componentRegistry.getClock();
+ this.curator = componentRegistry.getCurator();
+ this.sessionLifetime = componentRegistry.getConfigserverConfig().sessionLifetime();
+ this.zkWatcherExecutor = command -> componentRegistry.getZkWatcherExecutor().execute(tenantName, command);
+ this.tenantFileSystemDirs = new TenantFileSystemDirs(componentRegistry.getConfigServerDB(), tenantName);
}
@Override
@@ -68,17 +62,17 @@ public class LocalSessionRepo extends SessionRepo<LocalSession> {
sessionStateWatchers.put(sessionId, new LocalSessionStateWatcher(fileCache, session, this, zkWatcherExecutor));
}
- private void loadSessions(File applicationsDir, LocalSessionLoader loader) {
- File[] applications = applicationsDir.listFiles(sessionApplicationsFilter);
- if (applications == null) {
+ private void loadSessions(LocalSessionLoader loader) {
+ File[] sessions = tenantFileSystemDirs.sessionsPath().listFiles(sessionApplicationsFilter);
+ if (sessions == null) {
return;
}
- for (File application : applications) {
+ for (File session : sessions) {
try {
- addSession(loader.loadSession(Long.parseLong(application.getName())));
+ addSession(loader.loadSession(Long.parseLong(session.getName())));
} catch (IllegalArgumentException e) {
- log.log(LogLevel.WARNING, "Could not load application '" +
- application.getAbsolutePath() + "':" + e.getMessage() + ", skipping it.");
+ log.log(LogLevel.WARNING, "Could not load session '" +
+ session.getAbsolutePath() + "':" + e.getMessage() + ", skipping it.");
}
}
}
@@ -118,7 +112,12 @@ public class LocalSessionRepo extends SessionRepo<LocalSession> {
transaction.commit();
}
- public void deleteAllSessions() {
+ public void close() {
+ deleteAllSessions();
+ tenantFileSystemDirs.delete();
+ }
+
+ private void deleteAllSessions() {
List<LocalSession> sessions = new ArrayList<>(listSessions());
for (LocalSession session : sessions) {
deleteSession(session);
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionStateWatcher.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionStateWatcher.java
index 53a472c2b67..d5a87b3c45e 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionStateWatcher.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionStateWatcher.java
@@ -63,9 +63,9 @@ public class LocalSessionStateWatcher {
public void nodeChanged() {
zkWatcherExecutor.execute(() -> {
try {
- ChildData data = fileCache.getCurrentData();
- if (data != null) {
- sessionChanged(Session.Status.parse(Utf8.toString(fileCache.getCurrentData().getData())));
+ ChildData node = fileCache.getCurrentData();
+ if (node != null) {
+ sessionChanged(Session.Status.parse(Utf8.toString(node.getData())));
}
} catch (Exception e) {
log.log(LogLevel.WARNING, session.logPre() + "Error handling session changed for session " + getSessionId(), e);
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionRepo.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionRepo.java
index e0727effeda..c37f4aa8401 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionRepo.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionRepo.java
@@ -3,9 +3,7 @@ package com.yahoo.vespa.config.server.session;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
-import com.yahoo.concurrent.InThreadExecutorService;
import com.yahoo.concurrent.StripedExecutor;
-import com.yahoo.concurrent.ThreadFactoryFactory;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.TenantName;
import com.yahoo.log.LogLevel;
@@ -20,7 +18,6 @@ import com.yahoo.vespa.config.server.zookeeper.ConfigCurator;
import com.yahoo.vespa.curator.Curator;
import com.yahoo.vespa.flags.FlagSource;
import com.yahoo.vespa.flags.Flags;
-import com.yahoo.vespa.flags.InMemoryFlagSource;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
@@ -33,8 +30,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
@@ -68,7 +63,6 @@ public class RemoteSessionRepo extends SessionRepo<RemoteSession> {
ReloadHandler reloadHandler,
TenantName tenantName,
TenantApplications applicationRepo) {
-
this.curator = registry.getCurator();
this.sessionsPath = TenantRepository.getSessionsPath(tenantName);
this.applicationRepo = applicationRepo;
@@ -85,20 +79,6 @@ public class RemoteSessionRepo extends SessionRepo<RemoteSession> {
this.directoryCache.start();
}
- // For testing only
- public RemoteSessionRepo(TenantName tenantName) {
- this.curator = null;
- this.remoteSessionFactory = null;
- this.reloadHandler = null;
- this.tenantName = tenantName;
- this.sessionsPath = TenantRepository.getSessionsPath(tenantName);
- this.metrics = null;
- this.directoryCache = null;
- this.applicationRepo = null;
- this.flagSource = new InMemoryFlagSource();
- this.zkWatcherExecutor = Runnable::run;
- }
-
public List<Long> getSessions() {
return getSessionList(curator.getChildren(sessionsPath));
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionStateWatcher.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionStateWatcher.java
index 653a2616cbe..a3fad3d7322 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionStateWatcher.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionStateWatcher.java
@@ -1,8 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server.session;
-import com.yahoo.concurrent.StripedExecutor;
-import com.yahoo.config.provision.TenantName;
import com.yahoo.log.LogLevel;
import com.yahoo.text.Utf8;
import com.yahoo.vespa.config.server.ReloadHandler;
@@ -72,12 +70,12 @@ public class RemoteSessionStateWatcher {
}
}
- public void nodeChanged() {
+ private void nodeChanged() {
zkWatcherExecutor.execute(() -> {
try {
- ChildData data = fileCache.getCurrentData();
- if (data != null) {
- sessionChanged(Session.Status.parse(Utf8.toString(fileCache.getCurrentData().getData())));
+ ChildData node = fileCache.getCurrentData();
+ if (node != null) {
+ sessionChanged(Session.Status.parse(Utf8.toString(node.getData())));
}
} catch (Exception e) {
log.log(LogLevel.WARNING, session.logPre() + "Error handling session changed for session " + getSessionId(), e);
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionFactoryImpl.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionFactoryImpl.java
index cc46a157b34..fad5685d6fa 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionFactoryImpl.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionFactoryImpl.java
@@ -54,7 +54,6 @@ public class SessionFactoryImpl implements SessionFactory, LocalSessionLoader {
public SessionFactoryImpl(GlobalComponentRegistry globalComponentRegistry,
TenantApplications applicationRepo,
- TenantFileSystemDirs tenantFileSystemDirs,
HostValidator<ApplicationId> hostRegistry,
TenantName tenant) {
this.hostRegistry = hostRegistry;
@@ -65,7 +64,7 @@ public class SessionFactoryImpl implements SessionFactory, LocalSessionLoader {
this.sessionCounter = new SessionCounter(globalComponentRegistry.getConfigCurator(), tenant);
this.sessionsPath = TenantRepository.getSessionsPath(tenant);
this.applicationRepo = applicationRepo;
- this.tenantFileSystemDirs = tenantFileSystemDirs;
+ this.tenantFileSystemDirs = new TenantFileSystemDirs(globalComponentRegistry.getConfigServerDB(), tenant);
this.serverId = globalComponentRegistry.getConfigserverConfig().serverId();
this.nodeFlavors = globalComponentRegistry.getZone().nodeFlavors();
this.clock = globalComponentRegistry.getClock();
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepo.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepo.java
index 415ff268309..3400504fb58 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepo.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepo.java
@@ -1,14 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server.session;
-import com.yahoo.transaction.AbstractTransaction;
-import com.yahoo.transaction.NestedTransaction;
-import com.yahoo.transaction.Transaction;
-import com.yahoo.vespa.config.server.TimeoutBudget;
-import com.yahoo.vespa.config.server.NotFoundException;
-
-import java.time.Clock;
-import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -29,16 +21,10 @@ public class SessionRepo<SESSIONTYPE extends Session> {
sessions.put(session.getSessionId(), session);
}
- public synchronized SESSIONTYPE removeSession(long id) {
+ synchronized void removeSession(long id) {
if ( ! sessions.containsKey(id))
throw new IllegalArgumentException("No session with id '" + id + "' exists");
- return sessions.remove(id);
- }
-
- public void removeSession(long id, NestedTransaction nestedTransaction) {
- SessionRepoTransaction transaction = new SessionRepoTransaction();
- transaction.addRemoveOperation(id);
- nestedTransaction.add(transaction);
+ sessions.remove(id);
}
/**
@@ -51,90 +37,8 @@ public class SessionRepo<SESSIONTYPE extends Session> {
return sessions.get(id);
}
- /**
- * Gets a Session with a timeout
- *
- * @param id session id
- * @param timeoutInMillis timeout for getting session (loops and wait for session to show up if not found)
- * @return a session belonging to the id supplied, or null if no session with the id was found
- */
- public synchronized SESSIONTYPE getSession(long id, long timeoutInMillis) {
- try {
- return internalGetSession(id, timeoutInMillis);
- } catch (InterruptedException e) {
- throw new RuntimeException("Interrupted while retrieving session with id " + id);
- }
- }
-
- private synchronized SESSIONTYPE internalGetSession(long id, long timeoutInMillis) throws InterruptedException {
- TimeoutBudget timeoutBudget = new TimeoutBudget(Clock.systemUTC(), Duration.ofMillis(timeoutInMillis));
- do {
- SESSIONTYPE session = getSession(id);
- if (session != null) {
- return session;
- }
- wait(100);
- } while (timeoutBudget.hasTimeLeft());
- throw new NotFoundException("Unable to retrieve session with id " + id + " before timeout was reached");
- }
-
public synchronized Collection<SESSIONTYPE> listSessions() {
return new ArrayList<>(sessions.values());
}
- public class SessionRepoTransaction extends AbstractTransaction {
-
- void addRemoveOperation(long sessionIdToRemove) {
- add(new RemoveOperation(sessionIdToRemove));
- }
-
- @Override
- public void prepare() { }
-
- @Override
- @SuppressWarnings("unchecked")
- public void commit() {
- for (Operation operation : operations())
- ((SessionOperation)operation).commit();
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public void rollbackOrLog() {
- for (Operation operation : operations())
- ((SessionOperation)operation).rollback();
- }
-
- abstract class SessionOperation implements Transaction.Operation {
-
- abstract void commit();
-
- abstract void rollback();
-
- }
-
- public class RemoveOperation extends SessionOperation {
-
- private final long sessionIdToRemove;
- private SESSIONTYPE removed = null;
-
- RemoveOperation(long sessionIdToRemove) {
- this.sessionIdToRemove = sessionIdToRemove;
- }
-
- @Override
- public void commit() {
- removed = removeSession(sessionIdToRemove);
- }
-
- @Override
- public void rollback() {
- if (removed != null)
- addSession(removed);
- }
-
- }
-
- }
-
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/Tenant.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/Tenant.java
index 0aed3977625..bbd3ae55f10 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/Tenant.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/Tenant.java
@@ -6,7 +6,6 @@ import com.yahoo.path.Path;
import com.yahoo.vespa.config.server.ReloadHandler;
import com.yahoo.vespa.config.server.RequestHandler;
import com.yahoo.vespa.config.server.application.TenantApplications;
-import com.yahoo.vespa.config.server.deploy.TenantFileSystemDirs;
import com.yahoo.vespa.config.server.session.LocalSessionRepo;
import com.yahoo.vespa.config.server.session.RemoteSessionRepo;
import com.yahoo.vespa.config.server.session.SessionFactory;
@@ -22,13 +21,11 @@ import java.util.Optional;
*
* @author vegardh
* @author Ulf Lilleengen
- * @since 5.1.26
*/
public class Tenant implements TenantHandlerProvider {
static final String SESSIONS = "sessions";
static final String APPLICATIONS = "applications";
- static final String LOCKS = "locks";
private final TenantName name;
private final RemoteSessionRepo remoteSessionRepo;
@@ -38,7 +35,6 @@ public class Tenant implements TenantHandlerProvider {
private final TenantApplications applicationRepo;
private final RequestHandler requestHandler;
private final ReloadHandler reloadHandler;
- private final TenantFileSystemDirs tenantFileSystemDirs;
private final Curator curator;
Tenant(TenantName name,
@@ -49,8 +45,7 @@ public class Tenant implements TenantHandlerProvider {
RequestHandler requestHandler,
ReloadHandler reloadHandler,
TenantApplications applicationRepo,
- Curator curator,
- TenantFileSystemDirs tenantFileSystemDirs) {
+ Curator curator) {
this.name = name;
this.path = path;
this.requestHandler = requestHandler;
@@ -59,7 +54,6 @@ public class Tenant implements TenantHandlerProvider {
this.sessionFactory = sessionFactory;
this.localSessionRepo = localSessionRepo;
this.applicationRepo = applicationRepo;
- this.tenantFileSystemDirs = tenantFileSystemDirs;
this.curator = curator;
}
@@ -147,10 +141,9 @@ public class Tenant implements TenantHandlerProvider {
* Called by watchers as a reaction to {@link #delete()}.
*/
void close() {
- tenantFileSystemDirs.delete(); // Deletes all local files.
remoteSessionRepo.close(); // Closes watchers and clears memory.
applicationRepo.close(); // Closes watchers.
- localSessionRepo.deleteAllSessions(); // Closes watchers, clears memory, and deletes some local files and ZK session state.
+ localSessionRepo.close(); // Closes watchers, clears memory, and deletes local files and ZK session state.
}
/** Deletes the tenant tree from ZooKeeper (application and session status for the tenant) and triggers {@link #close()}. */
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantBuilder.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantBuilder.java
index 861b2f91c8f..23a6abb2c6c 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantBuilder.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantBuilder.java
@@ -33,7 +33,6 @@ public class TenantBuilder {
private TenantRequestHandler reloadHandler;
private RequestHandler requestHandler;
private RemoteSessionFactory remoteSessionFactory;
- private TenantFileSystemDirs tenantFileSystemDirs;
private HostValidator<ApplicationId> hostValidator;
private TenantBuilder(GlobalComponentRegistry componentRegistry, TenantName tenant) {
@@ -81,7 +80,6 @@ public class TenantBuilder {
createApplicationRepo();
createRemoteSessionFactory();
createRemoteSessionRepo();
- createServerDbDirs();
createSessionFactory();
createLocalSessionRepo();
return new Tenant(tenant,
@@ -92,20 +90,18 @@ public class TenantBuilder {
requestHandler,
reloadHandler,
applicationRepo,
- componentRegistry.getCurator(),
- tenantFileSystemDirs);
+ componentRegistry.getCurator());
}
private void createLocalSessionRepo() {
if (localSessionRepo == null) {
- localSessionRepo = new LocalSessionRepo(tenant, componentRegistry, tenantFileSystemDirs, localSessionLoader);
+ localSessionRepo = new LocalSessionRepo(tenant, componentRegistry, localSessionLoader);
}
}
private void createSessionFactory() {
if (sessionFactory == null || localSessionLoader == null) {
- SessionFactoryImpl impl = new SessionFactoryImpl(componentRegistry, applicationRepo,
- tenantFileSystemDirs, hostValidator, tenant);
+ SessionFactoryImpl impl = new SessionFactoryImpl(componentRegistry, applicationRepo, hostValidator, tenant);
if (sessionFactory == null) {
sessionFactory = impl;
}
@@ -155,11 +151,5 @@ public class TenantBuilder {
}
}
- private void createServerDbDirs() {
- if (tenantFileSystemDirs == null) {
- tenantFileSystemDirs = new TenantFileSystemDirs(componentRegistry.getConfigServerDB(), tenant);
- }
- }
-
public TenantName getTenantName() { return tenant; }
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java
index ad2472add89..2e09d830783 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java
@@ -5,7 +5,6 @@ import com.google.common.collect.ImmutableSet;
import com.google.inject.Inject;
import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.concurrent.StripedExecutor;
-import com.yahoo.concurrent.ThreadFactoryFactory;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.TenantName;
import com.yahoo.log.LogLevel;
@@ -285,7 +284,7 @@ public class TenantRepository {
tenants.get(name).delete();
}
- public synchronized void closeTenant(TenantName name) {
+ private synchronized void closeTenant(TenantName name) {
Tenant tenant = tenants.remove(name);
if (tenant == null)
throw new IllegalArgumentException("Closing '" + name + "' failed, tenant does not exist");
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ConfigCurator.java b/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ConfigCurator.java
index 92d7589ea43..56709225c1d 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ConfigCurator.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ConfigCurator.java
@@ -284,23 +284,11 @@ public class ConfigCurator {
* Puts config definition data and metadata into ZK.
*
* @param name The config definition name (including namespace)
- * @param version The config definition version
* @param path /zoopath
* @param data The contents to write to ZK (as a byte array)
*/
- public void putDefData(String name, String version, String path, byte[] data) {
- if (version == null) {
+ public void putDefData(String name, String path, byte[] data) {
putData(path, name, data);
- } else {
- String fullPath = createFullPath(path, name + "," + version);
- if (exists(fullPath)) {
- // TODO This should not happen when all the compatibility hacks in 5.1 have been removed
- log.log(LogLevel.INFO, "There already exists a config definition '" + name + "', skipping feeding this one to ZooKeeper");
- }
- else {
- putData(fullPath, data);
- }
- }
}
/**
diff --git a/configserver/src/main/java/com/yahoo/vespa/serviceview/ConfigServerLocation.java b/configserver/src/main/java/com/yahoo/vespa/serviceview/ConfigServerLocation.java
index 5dcdfcdaf37..cc452421d2d 100644
--- a/configserver/src/main/java/com/yahoo/vespa/serviceview/ConfigServerLocation.java
+++ b/configserver/src/main/java/com/yahoo/vespa/serviceview/ConfigServerLocation.java
@@ -1,20 +1,28 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.serviceview;
+import ai.vespa.util.http.VespaClientBuilderFactory;
+import com.google.inject.Inject;
import com.yahoo.cloud.config.ConfigserverConfig;
+import com.yahoo.component.AbstractComponent;
/**
* Wrapper for settings from the cloud.config.configserver config.
*
- * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ * @author Steinar Knutsen
*/
-public class ConfigServerLocation {
- public final int restApiPort;
+public class ConfigServerLocation extends AbstractComponent {
+ final int restApiPort;
+ // The client factory must be owned by a component as StateResource is instantiated per request
+ final VespaClientBuilderFactory clientBuilderFactory = new VespaClientBuilderFactory();
+
+ @Inject
public ConfigServerLocation(ConfigserverConfig configServer) {
restApiPort = configServer.httpport();
}
+
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
@@ -22,4 +30,8 @@ public class ConfigServerLocation {
return builder.toString();
}
+ @Override
+ public void deconstruct() {
+ clientBuilderFactory.close();
+ }
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/serviceview/StateResource.java b/configserver/src/main/java/com/yahoo/vespa/serviceview/StateResource.java
index a6d4c229500..c58f3659ca5 100644
--- a/configserver/src/main/java/com/yahoo/vespa/serviceview/StateResource.java
+++ b/configserver/src/main/java/com/yahoo/vespa/serviceview/StateResource.java
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.serviceview;
+import ai.vespa.util.http.VespaClientBuilderFactory;
import com.yahoo.container.jaxrs.annotation.Component;
import com.yahoo.vespa.serviceview.bindings.ApplicationView;
import com.yahoo.vespa.serviceview.bindings.ConfigClient;
@@ -14,7 +15,6 @@ import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Client;
-import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Context;
@@ -28,8 +28,6 @@ import java.util.List;
import java.util.ListIterator;
import java.util.Map;
-import static java.util.Collections.singletonList;
-
/**
* A web service to discover and proxy Vespa service state info.
@@ -42,6 +40,7 @@ public class StateResource implements StateClient {
private static final String USER_AGENT = "service-view-config-server-client";
private static final String SINGLE_API_LINK = "url";
+ private final VespaClientBuilderFactory clientBuilderFactory;
private final int restApiPort;
private final String host;
private final UriInfo uriInfo;
@@ -58,6 +57,7 @@ public class StateResource implements StateClient {
}
public StateResource(@Component ConfigServerLocation configServer, @Context UriInfo ui) {
+ this.clientBuilderFactory = configServer.clientBuilderFactory;
this.restApiPort = configServer.restApiPort;
this.host = "localhost";
this.uriInfo = ui;
@@ -278,11 +278,9 @@ public class StateResource implements StateClient {
newUri.append(link.getRawPath());
}
- private static Client client() {
- return ClientBuilder.newBuilder()
- .register((ClientRequestFilter) ctx -> ctx.getHeaders().put(HttpHeaders.USER_AGENT,
- singletonList(USER_AGENT)))
+ private Client client() {
+ return clientBuilderFactory.newBuilder()
+ .register((ClientRequestFilter) ctx -> ctx.getHeaders().put(HttpHeaders.USER_AGENT, List.of(USER_AGENT)))
.build();
}
-
}
diff --git a/configserver/src/main/resources/configserver-app/services.xml b/configserver/src/main/resources/configserver-app/services.xml
index cfacd6ff8c9..97b2156e8ca 100644
--- a/configserver/src/main/resources/configserver-app/services.xml
+++ b/configserver/src/main/resources/configserver-app/services.xml
@@ -92,10 +92,6 @@
<handler id='com.yahoo.vespa.config.server.http.status.StatusHandler' bundle='configserver'>
<binding>http://*/status</binding>
</handler>
- <handler id='com.yahoo.vespa.config.server.http.flags.FlagsHandler' bundle='configserver'>
- <binding>http://*/flags/v1</binding>
- <binding>http://*/flags/v1/*</binding>
- </handler>
<handler id='com.yahoo.vespa.config.server.http.v2.TenantHandler' bundle='configserver'>
<binding>http://*/application/v2/tenant/</binding>
<binding>http://*/application/v2/tenant/*</binding>
@@ -129,6 +125,7 @@
<binding>http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/serviceconverge</binding>
<binding>http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/serviceconverge/*</binding>
<binding>http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/clustercontroller/*/status/*</binding>
+ <binding>http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/metrics</binding>
<binding>http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*</binding>
<binding>http://*/application/v2/tenant/*/application/*</binding>
<binding>http://*/application/v2/tenant/*/application/*/logs</binding>
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/InjectedGlobalComponentRegistryTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/InjectedGlobalComponentRegistryTest.java
index e4ff8702ff1..6c144fe2f43 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/InjectedGlobalComponentRegistryTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/InjectedGlobalComponentRegistryTest.java
@@ -45,7 +45,6 @@ public class InjectedGlobalComponentRegistryTest {
private SessionPreparer sessionPreparer;
private ConfigserverConfig configserverConfig;
private RpcServer rpcServer;
- private SuperModelGenerationCounter generationCounter;
private ConfigDefinitionRepo defRepo;
private PermanentApplicationPackage permanentApplicationPackage;
private HostRegistries hostRegistries;
@@ -68,8 +67,10 @@ public class InjectedGlobalComponentRegistryTest {
.configDefinitionsDir(temporaryFolder.newFolder("configdefinitions").getAbsolutePath()));
sessionPreparer = new SessionTest.MockSessionPreparer();
rpcServer = new RpcServer(configserverConfig, null, Metrics.createTestMetrics(),
- new HostRegistries(), new ConfigRequestHostLivenessTracker(), new FileServer(temporaryFolder.newFolder("filereferences")), new NoopRpcAuthorizer(), new RpcRequestHandlerProvider());
- generationCounter = new SuperModelGenerationCounter(curator);
+ new HostRegistries(), new ConfigRequestHostLivenessTracker(),
+ new FileServer(temporaryFolder.newFolder("filereferences")),
+ new NoopRpcAuthorizer(), new RpcRequestHandlerProvider());
+ SuperModelGenerationCounter generationCounter = new SuperModelGenerationCounter(curator);
defRepo = new StaticConfigDefinitionRepo();
permanentApplicationPackage = new PermanentApplicationPackage(configserverConfig);
hostRegistries = new HostRegistries();
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/MemoryGenerationCounter.java b/configserver/src/test/java/com/yahoo/vespa/config/server/MemoryGenerationCounter.java
index b83a46cb066..2df28f81e9e 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/MemoryGenerationCounter.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/MemoryGenerationCounter.java
@@ -5,10 +5,10 @@ import com.yahoo.vespa.config.GenerationCounter;
/**
* @author Ulf Lilleengen
- * @since 5.
*/
public class MemoryGenerationCounter implements GenerationCounter {
- long value;
+ private long value;
+
@Override
public long increment() {
return ++value;
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/MiscTestCase.java b/configserver/src/test/java/com/yahoo/vespa/config/server/MiscTestCase.java
index 88e602eed7c..f8777f4c477 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/MiscTestCase.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/MiscTestCase.java
@@ -3,6 +3,7 @@ package com.yahoo.vespa.config.server;
import static org.junit.Assert.*;
import java.io.*;
+import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.ArrayList;
import org.junit.Test;
@@ -32,7 +33,7 @@ public class MiscTestCase {
private static List<String> file2lines(File file) throws IOException {
List<String> lines = new ArrayList<>();
- LineNumberReader in = new LineNumberReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
+ LineNumberReader in = new LineNumberReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8));
String line;
while ((line = in.readLine()) != null) {
lines.add(line);
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/MockReloadHandler.java b/configserver/src/test/java/com/yahoo/vespa/config/server/MockReloadHandler.java
index 99d0c6ea4da..6e9299c1a61 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/MockReloadHandler.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/MockReloadHandler.java
@@ -8,16 +8,13 @@ import java.util.Set;
/**
* @author Ulf Lilleengen
- * @since 5.1.24
*/
public class MockReloadHandler implements ReloadHandler {
- public ApplicationSet current = null;
public volatile ApplicationId lastRemoved = null;
@Override
public void reloadConfig(ApplicationSet application) {
- this.current = application;
}
@Override
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/ModelFactoryRegistryTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/ModelFactoryRegistryTest.java
index 986e9f5603d..2fd87161fd0 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/ModelFactoryRegistryTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/ModelFactoryRegistryTest.java
@@ -67,7 +67,7 @@ public class ModelFactoryRegistryTest {
@Test(expected = UnknownVespaVersionException.class)
public void testThatUnknownVersionGivesError() {
- ModelFactoryRegistry registry = new ModelFactoryRegistry(Arrays.asList(new TestFactory(new Version(1, 2, 3))));
+ ModelFactoryRegistry registry = new ModelFactoryRegistry(List.of(new TestFactory(new Version(1, 2, 3))));
registry.getFactory(new Version(3, 2, 1));
}
@@ -75,7 +75,7 @@ public class ModelFactoryRegistryTest {
private final Version version;
- public TestFactory(Version version) {
+ TestFactory(Version version) {
this.version = version;
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/PortRangeAllocator.java b/configserver/src/test/java/com/yahoo/vespa/config/server/PortRangeAllocator.java
index 8424dccbedc..359c8562869 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/PortRangeAllocator.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/PortRangeAllocator.java
@@ -13,7 +13,6 @@ import java.util.Stack;
* Allocates port ranges for all configserver tests.
*
* @author Ulf Lilleengen
- * @since 5.1.26
*/
public class PortRangeAllocator {
private final static PortRange portRange = new PortRange();
@@ -33,7 +32,7 @@ public class PortRangeAllocator {
private static final int first = 18651;
private static final int last = 18899; // see: factory/doc/port-ranges
- public PortRange() {
+ PortRange() {
freePorts.addAll(ContiguousSet.create(Range.closed(first, last), DiscreteDomain.integers()));
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/SuperModelControllerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/SuperModelControllerTest.java
index c6f6be5fbab..b7486dc7951 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/SuperModelControllerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/SuperModelControllerTest.java
@@ -54,7 +54,7 @@ public class SuperModelControllerTest {
File testApp = new File("src/test/resources/deploy/app");
ApplicationId app = ApplicationId.from(TenantName.from("a"),
ApplicationName.from("foo"), InstanceName.defaultName());
- models.put(app, new ApplicationInfo(app, 4l, new VespaModel(FilesApplicationPackage.fromFile(testApp))));
+ models.put(app, new ApplicationInfo(app, 4L, new VespaModel(FilesApplicationPackage.fromFile(testApp))));
SuperModel superModel = new SuperModel(models);
handler = new SuperModelController(new SuperModelConfigProvider(superModel, Zone.defaultZone(), new InMemoryFlagSource()), new TestConfigDefinitionRepo(), 2, new UncompressedConfigResponseFactory());
}
@@ -94,9 +94,9 @@ public class SuperModelControllerTest {
ApplicationId simple = applicationId("mysimpleapp", t1);
ApplicationId advanced = applicationId("myadvancedapp", t1);
ApplicationId tooAdvanced = applicationId("minetooadvancedapp", t2);
- models.put(simple, createApplicationInfo(testApp1, simple, 4l));
- models.put(advanced, createApplicationInfo(testApp2, advanced, 4l));
- models.put(tooAdvanced, createApplicationInfo(testApp3, tooAdvanced, 4l));
+ models.put(simple, createApplicationInfo(testApp1, simple, 4L));
+ models.put(advanced, createApplicationInfo(testApp2, advanced, 4L));
+ models.put(tooAdvanced, createApplicationInfo(testApp3, tooAdvanced, 4L));
SuperModel superModel = new SuperModel(models);
SuperModelController han = new SuperModelController(new SuperModelConfigProvider(superModel, Zone.defaultZone(), new InMemoryFlagSource()), new TestConfigDefinitionRepo(), 2, new UncompressedConfigResponseFactory());
@@ -122,9 +122,9 @@ public class SuperModelControllerTest {
ApplicationId simple = applicationId("mysimpleapp", t1);
ApplicationId advanced = applicationId("myadvancedapp", t1);
ApplicationId tooAdvanced = applicationId("minetooadvancedapp", t2);
- models.put(simple, createApplicationInfo(testApp1, simple, 4l));
- models.put(advanced, createApplicationInfo(testApp2, advanced, 4l));
- models.put(tooAdvanced, createApplicationInfo(testApp3, tooAdvanced, 4l));
+ models.put(simple, createApplicationInfo(testApp1, simple, 4L));
+ models.put(advanced, createApplicationInfo(testApp2, advanced, 4L));
+ models.put(tooAdvanced, createApplicationInfo(testApp3, tooAdvanced, 4L));
SuperModel superModel = new SuperModel(models);
SuperModelController han = new SuperModelController(new SuperModelConfigProvider(superModel, Zone.defaultZone(), new InMemoryFlagSource()), new TestConfigDefinitionRepo(), 2, new UncompressedConfigResponseFactory());
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/SuperModelRequestHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/SuperModelRequestHandlerTest.java
index f7b900c8f02..dd2c3e07131 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/SuperModelRequestHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/SuperModelRequestHandlerTest.java
@@ -57,14 +57,14 @@ public class SuperModelRequestHandlerTest {
assertNotNull(controller.getHandler());
long gen = counter.get();
- controller.reloadConfig(createApp(foo, 3l));
+ controller.reloadConfig(createApp(foo, 3L));
assertNotNull(controller.getHandler());
assertThat(controller.getHandler().getGeneration(), is(gen + 1));
- controller.reloadConfig(createApp(foo, 4l));
+ controller.reloadConfig(createApp(foo, 4L));
assertThat(controller.getHandler().getGeneration(), is(gen + 2));
// Test that a new app is used when there already exist an application with the same id
- assertThat(controller.getHandler().getSuperModel().applicationModels().get(foo).getGeneration(), is(4l));
- controller.reloadConfig(createApp(bar, 2l));
+ assertThat(controller.getHandler().getSuperModel().applicationModels().get(foo).getGeneration(), is(4L));
+ controller.reloadConfig(createApp(bar, 2L));
assertThat(controller.getHandler().getGeneration(), is(gen + 3));
}
@@ -75,9 +75,9 @@ public class SuperModelRequestHandlerTest {
ApplicationId baz = applicationId("b", "baz");
long gen = counter.get();
- controller.reloadConfig(createApp(foo, 3l));
- controller.reloadConfig(createApp(bar, 30l));
- controller.reloadConfig(createApp(baz, 9l));
+ controller.reloadConfig(createApp(foo, 3L));
+ controller.reloadConfig(createApp(bar, 30L));
+ controller.reloadConfig(createApp(baz, 9L));
assertThat(controller.getHandler().getGeneration(), is(gen + 3));
assertThat(controller.getHandler().getSuperModel().applicationModels().size(), is(3));
assertEquals(Arrays.asList(foo, bar, baz), new ArrayList<>(controller.getHandler().getSuperModel().applicationModels().keySet()));
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/TimeoutBudgetTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/TimeoutBudgetTest.java
index 070201a4369..db9906b9e02 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/TimeoutBudgetTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/TimeoutBudgetTest.java
@@ -24,16 +24,16 @@ public class TimeoutBudgetTest {
ManualClock clock = new ManualClock();
TimeoutBudget budget = new TimeoutBudget(clock, Duration.ofMillis(7));
- assertThat(budget.timeLeft().toMillis(), is(7l));
+ assertThat(budget.timeLeft().toMillis(), is(7L));
clock.advance(Duration.ofMillis(1));
- assertThat(budget.timeLeft().toMillis(), is(6l));
+ assertThat(budget.timeLeft().toMillis(), is(6L));
clock.advance(Duration.ofMillis(5));
- assertThat(budget.timeLeft().toMillis(), is(1l));
- assertThat(budget.timeLeft().toMillis(), is(1l));
+ assertThat(budget.timeLeft().toMillis(), is(1L));
+ assertThat(budget.timeLeft().toMillis(), is(1L));
clock.advance(Duration.ofMillis(1));
- assertThat(budget.timeLeft().toMillis(), is(0l));
+ assertThat(budget.timeLeft().toMillis(), is(0L));
clock.advance(Duration.ofMillis(5));
- assertThat(budget.timeLeft().toMillis(), is(0l));
+ assertThat(budget.timeLeft().toMillis(), is(0L));
clock.advance(Duration.ofMillis(1));
assertThat(budget.timesUsed(), is("[0 ms, 1 ms, 5 ms, 0 ms, 1 ms, 5 ms, total: 13 ms]"));
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationMapperTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationMapperTest.java
index 284bb716a6e..a562f89c7ec 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationMapperTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationMapperTest.java
@@ -4,6 +4,7 @@ package com.yahoo.vespa.config.server.application;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Optional;
import com.yahoo.config.provision.ApplicationId;
@@ -18,22 +19,21 @@ import static org.junit.Assert.assertEquals;
public class ApplicationMapperTest {
- ApplicationId appId;
- ApplicationMapper applicationMapper;
- ArrayList<Version> vespaVersions = new ArrayList<>();
- ArrayList<Application> applications = new ArrayList<>();
+ private ApplicationId appId;
+ private ApplicationMapper applicationMapper;
+ private ArrayList<Version> vespaVersions = new ArrayList<>();
+ private ArrayList<Application> applications = new ArrayList<>();
@Before
public void setUp() {
applicationMapper = new ApplicationMapper();
appId = new ApplicationId.Builder()
- .tenant("test").applicationName("test").instanceName("test").build();
- vespaVersions.add(Version.fromString("1.2.3"));
- vespaVersions.add(Version.fromString("1.2.4"));
- vespaVersions.add(Version.fromString("1.2.5"));
- applications.add(new Application(new ModelStub(), null, 0, false, vespaVersions.get(0), MetricUpdater.createTestUpdater(), ApplicationId.defaultId()));
- applications.add(new Application(new ModelStub(), null, 0, false, vespaVersions.get(1), MetricUpdater.createTestUpdater(), ApplicationId.defaultId()));
- applications.add(new Application(new ModelStub(), null, 0, false, vespaVersions.get(2), MetricUpdater.createTestUpdater(), ApplicationId.defaultId()));
+ .tenant("test")
+ .applicationName("test")
+ .instanceName("test")
+ .build();
+ vespaVersions.addAll(List.of(Version.fromString("1.2.3"), Version.fromString("1.2.4"), Version.fromString("1.2.5")));
+ applications.addAll(List.of(createApplication(vespaVersions.get(0)), createApplication(vespaVersions.get(1)), createApplication(vespaVersions.get(2))));
}
@Test
@@ -53,7 +53,6 @@ public class ApplicationMapperTest {
@Test (expected = VersionDoesNotExistException.class)
public void testGetForVersionThrows() {
applicationMapper.register(appId, ApplicationSet.fromList(Arrays.asList(applications.get(0), applications.get(2))));
-
applicationMapper.getForVersion(appId, Optional.of(vespaVersions.get(1)), Instant.now());
}
@@ -62,9 +61,16 @@ public class ApplicationMapperTest {
applicationMapper.register(appId, ApplicationSet.fromSingle(applications.get(0)));
applicationMapper.getForVersion(new ApplicationId.Builder()
- .tenant("different").applicationName("different").instanceName("different").build(),
+ .tenant("different")
+ .applicationName("different")
+ .instanceName("different")
+ .build(),
Optional.of(vespaVersions.get(1)),
Instant.now());
}
+ private Application createApplication(Version version) {
+ return new Application(new ModelStub(), null, 0, false, version, MetricUpdater.createTestUpdater(), ApplicationId.defaultId());
+ }
+
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationSetTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationSetTest.java
index 8ee6a82bd1d..cf1e00674cb 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationSetTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationSetTest.java
@@ -20,18 +20,14 @@ import static org.junit.Assert.assertEquals;
*/
public class ApplicationSetTest {
- ApplicationSet applicationSet;
- List<Version> vespaVersions = new ArrayList<>();
- List<Application> applications = new ArrayList<>();
+ private ApplicationSet applicationSet;
+ private List<Version> vespaVersions = new ArrayList<>();
+ private List<Application> applications = new ArrayList<>();
@Before
public void setUp() {
- vespaVersions.add(Version.fromString("1.2.3"));
- vespaVersions.add(Version.fromString("1.2.4"));
- vespaVersions.add(Version.fromString("1.2.5"));
- applications.add(new Application(new ModelStub(), null, 0, false, vespaVersions.get(0), MetricUpdater.createTestUpdater(), ApplicationId.defaultId()));
- applications.add(new Application(new ModelStub(), null, 0, false, vespaVersions.get(1), MetricUpdater.createTestUpdater(), ApplicationId.defaultId()));
- applications.add(new Application(new ModelStub(), null, 0, false, vespaVersions.get(2), MetricUpdater.createTestUpdater(), ApplicationId.defaultId()));
+ vespaVersions.addAll(List.of(Version.fromString("1.2.3"), Version.fromString("1.2.4"), Version.fromString("1.2.5")));
+ applications.addAll(List.of(createApplication(vespaVersions.get(0)), createApplication(vespaVersions.get(1)), createApplication(vespaVersions.get(2))));
}
@Test
@@ -54,4 +50,7 @@ public class ApplicationSetTest {
applicationSet.getForVersionOrLatest(Optional.of(vespaVersions.get(1)), Instant.now());
}
+ private Application createApplication(Version version) {
+ return new Application(new ModelStub(), null, 0, false, version, MetricUpdater.createTestUpdater(), ApplicationId.defaultId());
+ }
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationTest.java
index cc30ef09878..405fff3e190 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationTest.java
@@ -55,7 +55,7 @@ public class ApplicationTest {
ServerCache cache = new ServerCache();
Version vespaVersion = new Version(1, 2, 3);
Application app = new Application(new ModelStub(), cache, 1337L, false, vespaVersion, MetricUpdater.createTestUpdater(), appId);
- assertThat(app.getApplicationGeneration(), is(1337l));
+ assertThat(app.getApplicationGeneration(), is(1337L));
assertNotNull(app.getModel());
assertThat(app.getCache(), is(cache));
assertThat(app.getId().application().value(), is("foobar"));
@@ -107,7 +107,7 @@ public class ApplicationTest {
@Test
public void require_that_known_config_defs_are_found() {
- handler.resolveConfig(createSimpleConfigRequest(emptySchema));
+ handler.resolveConfig(createSimpleConfigRequest());
}
@Test
@@ -119,7 +119,7 @@ public class ApplicationTest {
@Test
public void require_that_non_existent_fields_in_schema_is_skipped() {
// Ask for config without schema and check that we get correct default value back
- List<String> payload = handler.resolveConfig(createSimpleConfigRequest(emptySchema)).getLegacyPayload();
+ List<String> payload = handler.resolveConfig(createSimpleConfigRequest()).getLegacyPayload();
assertThat(payload.get(0), is("boolval false"));
// Ask for config with wrong schema
String[] schema = new String[1];
@@ -138,19 +138,18 @@ public class ApplicationTest {
assertTrue(response == cached_response);
}
- private static GetConfigRequest createRequest(String name, String namespace, String defMd5, String[] schema, String configId) {
+ private static GetConfigRequest createRequest(String name, String namespace, String defMd5, String[] schema) {
Request request = JRTClientConfigRequestV3.
- createWithParams(new ConfigKey<>(name, configId, namespace, defMd5, null), DefContent.fromArray(schema),
+ createWithParams(new ConfigKey<>(name, "admin/model", namespace, defMd5, null), DefContent.fromArray(schema),
"fromHost", "", 0, 100, Trace.createDummy(), CompressionType.UNCOMPRESSED,
Optional.empty()).getRequest();
return JRTServerConfigRequestV3.createFromRequest(request);
}
- private static GetConfigRequest createRequest(String name, String namespace, String defMd5, String[] schema) {
- return createRequest(name, namespace, defMd5, schema, "admin/model");
- }
-
- private static GetConfigRequest createSimpleConfigRequest(String[] schema) {
- return createRequest(SimpletypesConfig.CONFIG_DEF_NAME, SimpletypesConfig.CONFIG_DEF_NAMESPACE, SimpletypesConfig.CONFIG_DEF_MD5, schema);
+ private static GetConfigRequest createSimpleConfigRequest() {
+ return createRequest(SimpletypesConfig.CONFIG_DEF_NAME,
+ SimpletypesConfig.CONFIG_DEF_NAMESPACE,
+ SimpletypesConfig.CONFIG_DEF_MD5,
+ ApplicationTest.emptySchema);
}
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/application/CompressedApplicationInputStreamTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/CompressedApplicationInputStreamTest.java
index 496da2cf809..016192e3281 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/application/CompressedApplicationInputStreamTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/CompressedApplicationInputStreamTest.java
@@ -3,7 +3,6 @@ package com.yahoo.vespa.config.server.application;
import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams;
-import com.yahoo.vespa.config.server.application.CompressedApplicationInputStream;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
@@ -22,6 +21,7 @@ import java.util.zip.GZIPOutputStream;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@@ -30,13 +30,13 @@ import static org.junit.Assert.assertTrue;
*/
public class CompressedApplicationInputStreamTest {
- static void writeFileToTar(ArchiveOutputStream taos, File file) throws IOException {
+ private static void writeFileToTar(ArchiveOutputStream taos, File file) throws IOException {
taos.putArchiveEntry(taos.createArchiveEntry(file, file.getName()));
ByteStreams.copy(new FileInputStream(file), taos);
taos.closeArchiveEntry();
}
- public static File createArchiveFile(ArchiveOutputStream taos, File outFile) throws IOException {
+ private static File createArchiveFile(ArchiveOutputStream taos, File outFile) throws IOException {
File app = new File("src/test/resources/deploy/validapp");
writeFileToTar(taos, new File(app, "services.xml"));
writeFileToTar(taos, new File(app, "hosts.xml"));
@@ -51,14 +51,15 @@ public class CompressedApplicationInputStreamTest {
return createArchiveFile(archiveOutputStream, outFile);
}
- public static File createZipFile() throws IOException {
+ private static File createZipFile() throws IOException {
File outFile = File.createTempFile("testapp", ".tar.gz");
ArchiveOutputStream archiveOutputStream = new ZipArchiveOutputStream(new FileOutputStream(outFile));
return createArchiveFile(archiveOutputStream, outFile);
}
- void assertTestApp(File outApp) {
+ private void assertTestApp(File outApp) {
String [] files = outApp.list();
+ assertNotNull(files);
assertThat(files.length, is(3));
assertThat(Arrays.asList(files), containsInAnyOrder(ImmutableList.of(is("hosts.xml"), is("services.xml"), is("deployment.xml"))));
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/application/ConfigConvergenceCheckerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/ConfigConvergenceCheckerTest.java
index 871182d75d9..71ae6955e56 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/application/ConfigConvergenceCheckerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/ConfigConvergenceCheckerTest.java
@@ -40,6 +40,7 @@ import static org.junit.Assert.assertTrue;
*/
public class ConfigConvergenceCheckerTest {
+ private static final Duration clientTimeout = Duration.ofSeconds(10);
private final TenantName tenant = TenantName.from("mytenant");
private final ApplicationId appId = ApplicationId.from(tenant, ApplicationName.from("myapp"), InstanceName.from("myinstance"));
@@ -76,7 +77,7 @@ public class ConfigConvergenceCheckerTest {
String serviceName = hostAndPort(this.service);
URI requestUrl = testServer().resolve("/serviceconverge/" + serviceName);
wireMock.stubFor(get(urlEqualTo("/state/v1/config")).willReturn(okJson("{\"config\":{\"generation\":3}}")));
- HttpResponse serviceResponse = checker.checkService(application, hostAndPort(this.service), requestUrl, Duration.ofSeconds(5));
+ HttpResponse serviceResponse = checker.checkService(application, hostAndPort(this.service), requestUrl, clientTimeout);
assertResponse("{\n" +
" \"url\": \"" + requestUrl.toString() + "\",\n" +
" \"host\": \"" + hostAndPort(this.service) + "\",\n" +
@@ -91,8 +92,7 @@ public class ConfigConvergenceCheckerTest {
{ // Missing service
String serviceName = "notPresent:1337";
URI requestUrl = testServer().resolve("/serviceconverge/" + serviceName);
- HttpResponse response = checker.checkService(application, "notPresent:1337", requestUrl,
- Duration.ofSeconds(5));
+ HttpResponse response = checker.checkService(application, "notPresent:1337", requestUrl,clientTimeout);
assertResponse("{\n" +
" \"url\": \"" + requestUrl.toString() + "\",\n" +
" \"host\": \"" + serviceName + "\",\n" +
@@ -111,7 +111,7 @@ public class ConfigConvergenceCheckerTest {
URI requestUrl = testServer().resolve("/serviceconverge");
URI serviceUrl = testServer().resolve("/serviceconverge/" + serviceName);
wireMock.stubFor(get(urlEqualTo("/state/v1/config")).willReturn(okJson("{\"config\":{\"generation\":3}}")));
- HttpResponse response = checker.servicesToCheck(application, requestUrl, Duration.ofSeconds(5));
+ HttpResponse response = checker.servicesToCheck(application, requestUrl, clientTimeout);
assertResponse("{\n" +
" \"services\": [\n" +
" {\n" +
@@ -148,7 +148,7 @@ public class ConfigConvergenceCheckerTest {
URI requestUrl = testServer().resolve("/serviceconverge");
URI serviceUrl = testServer().resolve("/serviceconverge/" + hostAndPort(service));
URI serviceUrl2 = testServer().resolve("/serviceconverge/" + hostAndPort(service2));
- HttpResponse response = checker.servicesToCheck(application, requestUrl, Duration.ofSeconds(5));
+ HttpResponse response = checker.servicesToCheck(application, requestUrl, clientTimeout);
assertResponse("{\n" +
" \"services\": [\n" +
" {\n" +
@@ -180,16 +180,14 @@ public class ConfigConvergenceCheckerTest {
public void service_convergence_timeout() {
URI requestUrl = testServer().resolve("/serviceconverge");
wireMock.stubFor(get(urlEqualTo("/state/v1/config")).willReturn(aResponse()
- .withFixedDelay((int) Duration.ofSeconds(10).toMillis())
+ .withFixedDelay((int) clientTimeout.plus(Duration.ofSeconds(1)).toMillis())
.withBody("response too slow")));
HttpResponse response = checker.checkService(application, hostAndPort(service), requestUrl, Duration.ofMillis(1));
// Message contained in a SocketTimeoutException may differ across platforms, so we do a partial match of the response here
- assertResponse((responseBody) -> {
- assertTrue("Response matches", responseBody.startsWith(
- "{\"url\":\"" + requestUrl.toString() + "\",\"host\":\"" + hostAndPort(requestUrl) +
- "\",\"wantedGeneration\":3,\"error\":\"java.net.SocketTimeoutException") &&
- responseBody.endsWith("\"}"));
- }, 404, response);
+ assertResponse((responseBody) -> assertTrue("Response matches", responseBody.startsWith(
+ "{\"url\":\"" + requestUrl.toString() + "\",\"host\":\"" + hostAndPort(requestUrl) +
+ "\",\"wantedGeneration\":3,\"error\":\"java.net.SocketTimeoutException") &&
+ responseBody.endsWith("\"}")), 404, response);
}
private URI testServer() {
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/application/FileDistributionStatusTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/FileDistributionStatusTest.java
index 0c27066dd6b..89392d799ba 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/application/FileDistributionStatusTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/FileDistributionStatusTest.java
@@ -170,7 +170,7 @@ public class FileDistributionStatusTest {
appId);
}
- HttpResponse getStatus(FileDistributionStatus fileDistributionStatus, Application application) {
+ private HttpResponse getStatus(FileDistributionStatus fileDistributionStatus, Application application) {
return fileDistributionStatus.status(application, timeout);
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/application/MockModel.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/MockModel.java
index 433a63f4ad2..0da96f9f01d 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/application/MockModel.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/MockModel.java
@@ -45,6 +45,7 @@ public class MockModel implements Model {
return new HostInfo(hostname, Arrays.asList(container, serviceNoStatePort));
}
+ // TODO: Move to caller
static MockModel createClusterController(String hostname, int statePort) {
ServiceInfo container = createServiceInfo(
hostname,
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/application/OrchestratorMock.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/OrchestratorMock.java
index aa157366a60..66d113afbe6 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/application/OrchestratorMock.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/OrchestratorMock.java
@@ -4,7 +4,6 @@ import com.yahoo.config.provision.ApplicationId;
import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.orchestrator.Host;
import com.yahoo.vespa.orchestrator.Orchestrator;
-import com.yahoo.vespa.orchestrator.model.NodeGroup;
import com.yahoo.vespa.orchestrator.status.ApplicationInstanceStatus;
import com.yahoo.vespa.orchestrator.status.HostStatus;
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java
index e3335dded4c..33932a678b7 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java
@@ -69,11 +69,11 @@ public class TenantApplicationsTest {
TenantApplications repo = createZKAppRepo();
ApplicationId myapp = createApplicationId("myapp");
repo.createApplication(myapp);
- repo.createPutTransaction(myapp, 3l).commit();
+ repo.createPutTransaction(myapp, 3).commit();
String path = TenantRepository.getApplicationsPath(tenantName).append(myapp.serializedForm()).getAbsolute();
assertNotNull(curatorFramework.checkExists().forPath(path));
assertThat(Utf8.toString(curatorFramework.getData().forPath(path)), is("3"));
- repo.createPutTransaction(myapp, 5l).commit();
+ repo.createPutTransaction(myapp, 5).commit();
assertNotNull(curatorFramework.checkExists().forPath(path));
assertThat(Utf8.toString(curatorFramework.getData().forPath(path)), is("5"));
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/ConfigChangeActionsBuilder.java b/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/ConfigChangeActionsBuilder.java
index 4a23d76b76e..2566b1029a8 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/ConfigChangeActionsBuilder.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/ConfigChangeActionsBuilder.java
@@ -11,7 +11,6 @@ import java.util.List;
/**
* @author geirst
- * @since 5.44
*/
public class ConfigChangeActionsBuilder {
@@ -25,15 +24,15 @@ public class ConfigChangeActionsBuilder {
public ConfigChangeActionsBuilder restart(String message, String clusterName, String clusterType, String serviceType, String serviceName) {
actions.add(new MockRestartAction(message,
- Arrays.asList(createService(clusterName, clusterType, serviceType, serviceName))));
+ List.of(createService(clusterName, clusterType, serviceType, serviceName))));
return this;
}
- public ConfigChangeActionsBuilder refeed(String name, boolean allowed, String message, String documentType, String clusterName, String serviceName) {
+ ConfigChangeActionsBuilder refeed(String name, boolean allowed, String message, String documentType, String clusterName, String serviceName) {
actions.add(new MockRefeedAction(name,
allowed,
message,
- Arrays.asList(createService(clusterName, "myclustertype", "myservicetype", serviceName)), documentType));
+ List.of(createService(clusterName, "myclustertype", "myservicetype", serviceName)), documentType));
return this;
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/MockConfigChangeAction.java b/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/MockConfigChangeAction.java
index 64e3aceee8d..639e4a6fee3 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/MockConfigChangeAction.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/MockConfigChangeAction.java
@@ -15,7 +15,7 @@ public abstract class MockConfigChangeAction implements ConfigChangeAction {
private final String message;
private final List<ServiceInfo> services;
- protected MockConfigChangeAction(String message, List<ServiceInfo> services) {
+ MockConfigChangeAction(String message, List<ServiceInfo> services) {
this.message = message;
this.services = services;
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/RefeedActionsTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/RefeedActionsTest.java
index 6f21682981a..7235b8905c5 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/RefeedActionsTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/RefeedActionsTest.java
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server.configchange;
+import com.yahoo.config.model.api.ServiceInfo;
import org.junit.Test;
import java.util.List;
@@ -13,7 +14,6 @@ import static com.yahoo.vespa.config.server.configchange.Utils.*;
/**
* @author geirst
- * @since 5.44
*/
public class RefeedActionsTest {
@@ -21,7 +21,7 @@ public class RefeedActionsTest {
StringBuilder builder = new StringBuilder();
builder.append(entry.getDocumentType() + "." + entry.getClusterName() + ":");
builder.append(entry.getServices().stream().
- map(service -> service.getServiceName()).
+ map(ServiceInfo::getServiceName).
sorted().
collect(Collectors.joining(",", "[", "]")));
builder.append(entry.getMessages().stream().
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/RestartActionsTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/RestartActionsTest.java
index 4c937061733..ee0180802af 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/RestartActionsTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/RestartActionsTest.java
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server.configchange;
+import com.yahoo.config.model.api.ServiceInfo;
import org.junit.Test;
import java.util.List;
@@ -13,7 +14,6 @@ import static com.yahoo.vespa.config.server.configchange.Utils.*;
/**
* @author geirst
- * @since 5.44
*/
public class RestartActionsTest {
@@ -21,7 +21,7 @@ public class RestartActionsTest {
StringBuilder builder = new StringBuilder();
builder.append(entry.getClusterType() + "." + entry.getClusterName() + "." + entry.getServiceType() + ":");
builder.append(entry.getServices().stream().
- map(service -> service.getServiceName()).
+ map(ServiceInfo::getServiceName).
sorted().
collect(Collectors.joining(",", "[", "]")));
builder.append(entry.getMessages().stream().
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployHandlerLoggerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployHandlerLoggerTest.java
index d5092adb90b..d321edefe67 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployHandlerLoggerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployHandlerLoggerTest.java
@@ -8,7 +8,6 @@ import com.yahoo.slime.Cursor;
import com.yahoo.slime.JsonFormat;
import com.yahoo.slime.Slime;
-import com.yahoo.vespa.config.server.deploy.DeployHandlerLogger;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
@@ -19,7 +18,6 @@ import static org.junit.Assert.assertTrue;
/**
* @author Ulf Lilleengen
- * @since 5.1
*/
public class DeployHandlerLoggerTest {
@Test
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java
index b363c749212..74d3fd4ec74 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java
@@ -366,7 +366,7 @@ public class HostedDeployTest {
@Override
public ModelCreateResult createAndValidateModel(ModelContext modelContext, ValidationParameters validationParameters) {
ModelCreateResult result = super.createAndValidateModel(modelContext, validationParameters);
- return new ModelCreateResult(result.getModel(), Arrays.asList(action));
+ return new ModelCreateResult(result.getModel(), List.of(action));
}
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/MockDeployer.java b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/MockDeployer.java
index da387eb569a..967e2321b95 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/MockDeployer.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/MockDeployer.java
@@ -13,8 +13,6 @@ import java.util.Optional;
*/
public class MockDeployer implements com.yahoo.config.provision.Deployer {
- public ApplicationId lastDeployed;
-
@Override
public Optional<Deployment> deployFromLocalActive(ApplicationId application) {
return deployFromLocalActive(application, Duration.ofSeconds(60));
@@ -27,7 +25,6 @@ public class MockDeployer implements com.yahoo.config.provision.Deployer {
@Override
public Optional<Deployment> deployFromLocalActive(ApplicationId application, Duration timeout) {
- lastDeployed = application;
return Optional.empty();
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/RedeployTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/RedeployTest.java
index adf3ba19fe3..49327f984b7 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/RedeployTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/RedeployTest.java
@@ -9,7 +9,6 @@ import com.yahoo.component.Version;
import org.junit.Test;
import java.time.Clock;
-import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClientTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClientTest.java
index 918670d71f2..d01ebad8c26 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClientTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClientTest.java
@@ -80,7 +80,7 @@ public class ZooKeeperClientTest {
}
@Test
- public void testInitZooKeeper() throws IOException {
+ public void testInitZooKeeper() {
ConfigCurator zk = ConfigCurator.create(new MockCurator());
BaseDeployLogger logger = new BaseDeployLogger();
long generation = 1L;
@@ -88,8 +88,8 @@ public class ZooKeeperClientTest {
zooKeeperClient.setupZooKeeper();
String appPath = "/";
assertThat(zk.getChildren(appPath).size(), is(1));
- assertTrue(zk.exists("/" + String.valueOf(generation)));
- String currentAppPath = appPath + String.valueOf(generation);
+ assertTrue(zk.exists("/" + generation));
+ String currentAppPath = appPath + generation;
assertTrue(zk.exists(currentAppPath, ConfigCurator.DEFCONFIGS_ZK_SUBPATH.replaceFirst("/", "")));
assertThat(zk.getChildren(currentAppPath).size(), is(4));
}
@@ -101,14 +101,14 @@ public class ZooKeeperClientTest {
List<String> children = zk.getChildren(defsPath);
assertEquals(defsPath + " children", 2, children.size());
Collections.sort(children);
- assertThat(children.get(0), is("a.b.test2,"));
+ assertThat(children.get(0), is("a.b.test2"));
assertTrue(zk.exists(appPath, ConfigCurator.USER_DEFCONFIGS_ZK_SUBPATH.replaceFirst("/", "")));
String userDefsPath = appPath + ConfigCurator.USER_DEFCONFIGS_ZK_SUBPATH;
children = zk.getChildren(userDefsPath);
assertThat(children.size(), is(2));
Collections.sort(children);
- assertThat(children.get(0), is("a.b.test2,"));
+ assertThat(children.get(0), is("a.b.test2"));
}
// TODO: Evaluate if we want this or not
@@ -140,9 +140,9 @@ public class ZooKeeperClientTest {
assertTrue(metaData.isInternalRedeploy());
assertThat(metaData.getDeployedByUser(), is("foo"));
assertThat(metaData.getDeployPath(), is("/bar/baz"));
- assertThat(metaData.getDeployTimestamp(), is(1345l));
- assertThat(metaData.getGeneration(), is(3l));
- assertThat(metaData.getPreviousActiveGeneration(), is(2l));
+ assertThat(metaData.getDeployTimestamp(), is(1345L));
+ assertThat(metaData.getGeneration(), is(3L));
+ assertThat(metaData.getPreviousActiveGeneration(), is(2L));
}
@Test
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperDeployerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperDeployerTest.java
index d00c0b8dd32..4825ccc1328 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperDeployerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperDeployerTest.java
@@ -8,7 +8,6 @@ import com.yahoo.config.provision.AllocatedHosts;
import com.yahoo.component.Version;
import com.yahoo.io.IOUtils;
import com.yahoo.path.Path;
-import com.yahoo.prelude.semantics.parser.ParseException;
import com.yahoo.vespa.curator.mock.MockCurator;
import com.yahoo.vespa.config.server.zookeeper.ConfigCurator;
import org.junit.Rule;
@@ -25,7 +24,6 @@ import static org.junit.Assert.fail;
/**
* @author Ulf Lilleengen
- * @since 5.1
*/
public class ZooKeeperDeployerTest {
@@ -34,7 +32,7 @@ public class ZooKeeperDeployerTest {
private static final String defFile = "test2.def";
@Test
- public void require_that_deployer_is_initialized() throws IOException, ParseException {
+ public void require_that_deployer_is_initialized() throws IOException {
ConfigCurator zkfacade = ConfigCurator.create(new MockCurator());
File serverdbDir = folder.newFolder("serverdb");
File defsDir = new File(serverdbDir, "serverdefs");
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/host/HostRegistryTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/host/HostRegistryTest.java
index b2335165105..63dfb1d01bd 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/host/HostRegistryTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/host/HostRegistryTest.java
@@ -2,7 +2,6 @@
package com.yahoo.vespa.config.server.host;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@@ -20,12 +19,12 @@ public class HostRegistryTest {
public void old_hosts_are_removed() {
HostRegistry<String> reg = new HostRegistry<>();
assertNull(reg.getKeyForHost("foo.com"));
- reg.update("fookey", Arrays.asList("foo.com", "bar.com", "baz.com"));
+ reg.update("fookey", List.of("foo.com", "bar.com", "baz.com"));
assertGetKey(reg, "foo.com", "fookey");
assertGetKey(reg, "bar.com", "fookey");
assertGetKey(reg, "baz.com", "fookey");
assertThat(reg.getAllHosts().size(), is(3));
- reg.update("fookey", Arrays.asList("bar.com", "baz.com"));
+ reg.update("fookey", List.of("bar.com", "baz.com"));
assertNull(reg.getKeyForHost("foo.com"));
assertGetKey(reg, "bar.com", "fookey");
assertGetKey(reg, "baz.com", "fookey");
@@ -41,8 +40,8 @@ public class HostRegistryTest {
@Test
public void multiple_keys_are_handled() {
HostRegistry<String> reg = new HostRegistry<>();
- reg.update("fookey", Arrays.asList("foo.com", "bar.com"));
- reg.update("barkey", Arrays.asList("baz.com", "quux.com"));
+ reg.update("fookey", List.of("foo.com", "bar.com"));
+ reg.update("barkey", List.of("baz.com", "quux.com"));
assertGetKey(reg, "foo.com", "fookey");
assertGetKey(reg, "bar.com", "fookey");
assertGetKey(reg, "baz.com", "barkey");
@@ -52,22 +51,22 @@ public class HostRegistryTest {
@Test(expected = IllegalArgumentException.class)
public void keys_cannot_overlap() {
HostRegistry<String> reg = new HostRegistry<>();
- reg.update("fookey", Arrays.asList("foo.com", "bar.com"));
- reg.update("barkey", Arrays.asList("bar.com", "baz.com"));
+ reg.update("fookey", List.of("foo.com", "bar.com"));
+ reg.update("barkey", List.of("bar.com", "baz.com"));
}
@Test
public void all_hosts_are_returned() {
HostRegistry<String> reg = new HostRegistry<>();
- reg.update("fookey", Arrays.asList("foo.com", "bar.com"));
- reg.update("barkey", Arrays.asList("baz.com", "quux.com"));
+ reg.update("fookey", List.of("foo.com", "bar.com"));
+ reg.update("barkey", List.of("baz.com", "quux.com"));
assertThat(reg.getAllHosts().size(), is(4));
}
@Test
public void ensure_that_collection_is_copied() {
HostRegistry<String> reg = new HostRegistry<>();
- List<String> hosts = new ArrayList<>(Arrays.asList("foo.com", "bar.com", "baz.com"));
+ List<String> hosts = new ArrayList<>(List.of("foo.com", "bar.com", "baz.com"));
reg.update("fookey", hosts);
assertThat(reg.getHostsForKey("fookey").size(), is(3));
hosts.remove(2);
@@ -77,10 +76,10 @@ public class HostRegistryTest {
@Test
public void ensure_that_underlying_hosts_do_not_change() {
HostRegistry<String> reg = new HostRegistry<>();
- reg.update("fookey", new ArrayList<>(Arrays.asList("foo.com", "bar.com", "baz.com")));
+ reg.update("fookey", List.of("foo.com", "bar.com", "baz.com"));
Collection<String> hosts = reg.getAllHosts();
assertThat(hosts.size(), is(3));
- reg.update("fookey", new ArrayList<>(Arrays.asList("foo.com")));
+ reg.update("fookey", List.of("foo.com"));
assertThat(hosts.size(), is(3));
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/ContentHandlerTestBase.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/ContentHandlerTestBase.java
index 3415facd714..20e52263350 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/ContentHandlerTestBase.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/ContentHandlerTestBase.java
@@ -11,10 +11,8 @@ import static org.junit.Assert.assertThat;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
-import javax.annotation.Nullable;
import org.junit.Test;
-import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.Collections2;
import com.yahoo.container.jdisc.HttpResponse;
@@ -37,7 +35,7 @@ public abstract class ContentHandlerTestBase extends SessionHandlerTest {
}
@Test
- public void require_that_nonexistant_file_returns_not_found() throws IOException {
+ public void require_that_nonexistant_file_returns_not_found() {
HttpResponse response = doRequest(HttpRequest.Method.GET, "/test2.txt");
assertNotNull(response);
assertThat(response.getStatus(), is(NOT_FOUND));
@@ -88,12 +86,7 @@ public abstract class ContentHandlerTestBase extends SessionHandlerTest {
protected abstract HttpResponse doRequest(HttpRequest.Method method, String path);
private String generateResultArray(String... files) {
- Collection<String> output = Collections2.transform(Arrays.asList(files), new Function<String, String>() {
- @Override
- public String apply(@Nullable String input) {
- return "\"" + baseUrl + input + "\"";
- }
- });
+ Collection<String> output = Collections2.transform(Arrays.asList(files), input -> "\"" + baseUrl + input + "\"");
StringBuilder sb = new StringBuilder();
sb.append("[");
sb.append(Joiner.on(",").join(output));
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpConfigRequestTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpConfigRequestTest.java
index 62ec451107d..d28aa804c6f 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpConfigRequestTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpConfigRequestTest.java
@@ -1,8 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server.http;
-import java.io.IOException;
-
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.vespa.config.ConfigKey;
@@ -17,7 +15,6 @@ import static org.junit.Assert.assertTrue;
/**
* @author Ulf Lilleengen
- * @since 5.1
*/
public class HttpConfigRequestTest {
@Test
@@ -39,7 +36,7 @@ public class HttpConfigRequestTest {
}
@Test
- public void require_that_request_can_be_created_with_advanced_uri() throws IOException {
+ public void require_that_request_can_be_created_with_advanced_uri() {
HttpConfigRequest.createFromRequestV1(HttpRequest.createTestRequest(
"http://example.yahoo.com:19071/config/v1/vespa.config.cloud.sentinel/host-01.example.yahoo.com", GET));
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpErrorResponseTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpErrorResponseTest.java
index 54a3db3d94d..56391127b62 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpErrorResponseTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpErrorResponseTest.java
@@ -12,7 +12,6 @@ import static com.yahoo.jdisc.http.HttpResponse.Status.*;
/**
* @author Ulf Lilleengen
- * @since 5.1
*/
public class HttpErrorResponseTest {
@Test
@@ -29,7 +28,7 @@ public class HttpErrorResponseTest {
}
@Test
- public void testThatHttpErrorResponseHasJsonContentType() throws IOException {
+ public void testThatHttpErrorResponseHasJsonContentType() {
HttpErrorResponse response = HttpErrorResponse.badRequest("Error doing something");
assertThat(response.getContentType(), is("application/json"));
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpGetConfigHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpGetConfigHandlerTest.java
index a1fbbb57ce2..3ae98c1b8f2 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpGetConfigHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpGetConfigHandlerTest.java
@@ -6,7 +6,6 @@ import com.yahoo.config.codegen.DefParser;
import com.yahoo.config.codegen.InnerCNode;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
-import com.yahoo.container.logging.AccessLog;
import com.yahoo.text.StringUtilities;
import com.yahoo.vespa.config.ConfigKey;
import com.yahoo.vespa.config.ConfigPayload;
@@ -20,17 +19,14 @@ import java.io.IOException;
import java.io.StringReader;
import java.util.Collections;
import java.util.HashSet;
-import java.util.concurrent.Executor;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.*;
import static com.yahoo.jdisc.http.HttpRequest.Method.GET;
import static com.yahoo.jdisc.http.HttpResponse.Status.*;
-
/**
* @author Ulf Lilleengen
- * @since 5.1
*/
public class HttpGetConfigHandlerTest {
private static final String configUri = "http://yahoo.com:8080/config/v1/foo.bar/myid";
@@ -41,12 +37,10 @@ public class HttpGetConfigHandlerTest {
@Before
public void setUp() {
mockRequestHandler = new MockRequestHandler();
- mockRequestHandler.setAllConfigs(new HashSet<ConfigKey<?>>() {{
+ mockRequestHandler.setAllConfigs(new HashSet<>() {{
add(new ConfigKey<>("bar", "myid", "foo"));
- }} );
- handler = new HttpGetConfigHandler(
- HttpGetConfigHandler.testOnlyContext(),
- mockRequestHandler);
+ }} );
+ handler = new HttpGetConfigHandler(HttpGetConfigHandler.testOnlyContext(), mockRequestHandler);
}
@Test
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpHandlerTest.java
index e8d4373842d..e2bd8a120e0 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpHandlerTest.java
@@ -46,7 +46,7 @@ public class HttpHandlerTest {
private static class HttpTestHandler extends HttpHandler {
private RuntimeException exception;
- public HttpTestHandler(RuntimeException exception) {
+ HttpTestHandler(RuntimeException exception) {
super(HttpHandler.testOnlyContext());
this.exception = exception;
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpListConfigsHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpListConfigsHandlerTest.java
index 25b9e66ceb3..9113978d58b 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpListConfigsHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpListConfigsHandlerTest.java
@@ -3,7 +3,6 @@ package com.yahoo.vespa.config.server.http;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
-import com.yahoo.container.logging.AccessLog;
import com.yahoo.vespa.config.ConfigKey;
import com.yahoo.vespa.config.server.rpc.MockRequestHandler;
import com.yahoo.vespa.config.server.http.HttpListConfigsHandler.ListConfigsResponse;
@@ -13,7 +12,6 @@ import org.junit.Test;
import java.io.IOException;
import java.util.*;
-import java.util.concurrent.Executor;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.*;
@@ -23,7 +21,6 @@ import static com.yahoo.jdisc.http.HttpRequest.Method.GET;
/**
* @author Ulf Lilleengen
- * @since 5.1
*/
public class HttpListConfigsHandlerTest {
@@ -34,9 +31,9 @@ public class HttpListConfigsHandlerTest {
@Before
public void setUp() {
mockRequestHandler = new MockRequestHandler();
- mockRequestHandler.setAllConfigs(new HashSet<ConfigKey<?>>() {{
+ mockRequestHandler.setAllConfigs(new HashSet<>() {{
add(new ConfigKey<>("bar", "conf/id/", "foo"));
- }} );
+ }} );
HttpListConfigsHandler.Context ctx = HttpListConfigsHandler.testOnlyContext();
handler = new HttpListConfigsHandler(ctx, mockRequestHandler);
namedHandler = new HttpListNamedConfigsHandler(ctx, mockRequestHandler);
@@ -51,14 +48,14 @@ public class HttpListConfigsHandlerTest {
@Test
public void require_that_named_handler_can_be_created() throws IOException {
HttpRequest req = HttpRequest.createTestRequest("http://foo.com:8080/config/v1/foo.bar/conf/id/", GET);
- req.getJDiscRequest().parameters().put("http.path", Arrays.asList("foo.bar"));
+ req.getJDiscRequest().parameters().put("http.path", List.of("foo.bar"));
HttpResponse response = namedHandler.handle(req);
assertThat(SessionHandlerTest.getRenderedString(response), is("{\"children\":[],\"configs\":[]}"));
}
@Test
public void require_child_listings_correct() {
- Set<ConfigKey<?>> keys = new LinkedHashSet<ConfigKey<?>>() {{
+ Set<ConfigKey<?>> keys = new LinkedHashSet<>() {{
add(new ConfigKey<>("name1", "id/1", "ns1"));
add(new ConfigKey<>("name1", "id/1", "ns1"));
add(new ConfigKey<>("name1", "id/2", "ns1"));
@@ -74,7 +71,7 @@ public class HttpListConfigsHandlerTest {
@Test
public void require_url_building_and_mimetype_correct() {
- HttpListConfigsHandler.ListConfigsResponse resp = new ListConfigsResponse(new HashSet<ConfigKey<?>>(), null, "http://foo.com/config/v1/", true);
+ HttpListConfigsHandler.ListConfigsResponse resp = new ListConfigsResponse(new HashSet<>(), null, "http://foo.com/config/v1/", true);
assertEquals(resp.toUrl(new ConfigKey<>("myconfig", "my/id", "mynamespace"), true), "http://foo.com/config/v1/mynamespace.myconfig/my/id");
assertEquals(resp.toUrl(new ConfigKey<>("myconfig", "my/id", "mynamespace"), false), "http://foo.com/config/v1/mynamespace.myconfig/my/id/");
assertEquals(resp.getContentType(), "application/json");
@@ -96,7 +93,7 @@ public class HttpListConfigsHandlerTest {
@Test
public void require_correct_error_response_on_no_model() throws IOException {
- mockRequestHandler.setAllConfigs(new HashSet<ConfigKey<?>>());
+ mockRequestHandler.setAllConfigs(new HashSet<>());
HttpResponse response = namedHandler.handle(HttpRequest.createTestRequest("http://yahoo.com:8080/config/v1/foo.bar/myid/", GET));
HandlerTest.assertHttpStatusCodeErrorCodeAndMessage(response, NOT_FOUND,
HttpErrorResponse.errorCodes.NOT_FOUND,
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/SessionHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/SessionHandlerTest.java
index 0381af57cc3..9a326a18dd5 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/SessionHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/SessionHandlerTest.java
@@ -6,7 +6,6 @@ import com.yahoo.config.application.api.ApplicationFile;
import com.yahoo.config.application.api.ApplicationPackage;
import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.config.model.application.provider.FilesApplicationPackage;
-import com.yahoo.config.model.test.MockApplicationPackage;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterSpec;
@@ -33,14 +32,13 @@ import com.yahoo.vespa.config.server.session.PrepareParams;
import com.yahoo.vespa.config.server.session.Session;
import com.yahoo.vespa.config.server.session.SessionContext;
import com.yahoo.vespa.config.server.session.SessionFactory;
-import com.yahoo.vespa.config.server.session.SessionPreparer;
-import com.yahoo.vespa.config.server.session.SessionTest;
import com.yahoo.vespa.flags.InMemoryFlagSource;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Collection;
import java.util.List;
@@ -80,16 +78,13 @@ public class SessionHandlerTest {
public static String getRenderedString(HttpResponse response) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
response.render(baos);
- return baos.toString("UTF-8");
+ return baos.toString(StandardCharsets.UTF_8);
}
public static class MockSession extends LocalSession {
- private final InMemoryFlagSource flagSource;
public boolean doVerboseLogging = false;
public Session.Status status;
- private final SessionPreparer preparer;
- private final ApplicationPackage app;
private ConfigChangeActions actions = new ConfigChangeActions();
private long createTime = System.currentTimeMillis() / 1000;
private ApplicationId applicationId;
@@ -99,10 +94,7 @@ public class SessionHandlerTest {
}
private MockSession(long id, ApplicationPackage app, InMemoryFlagSource flagSource) {
- super(TenantName.defaultName(), id, null, new SessionContext(null, new MockSessionZKClient(MockApplicationPackage.createEmpty()), null, null, new HostRegistry<>(), flagSource));
- this.app = app;
- this.preparer = new SessionTest.MockSessionPreparer();
- this.flagSource = flagSource;
+ super(TenantName.defaultName(), id, null, new SessionContext(app, new MockSessionZKClient(app), null, null, new HostRegistry<>(), flagSource));
}
public MockSession(long sessionId, ApplicationPackage applicationPackage, long createTime) {
@@ -140,31 +132,17 @@ public class SessionHandlerTest {
@Override
public Transaction createDeactivateTransaction() {
- return new DummyTransaction().add((DummyTransaction.RunnableOperation) () -> {
- status = Status.DEACTIVATE;
- });
+ return new DummyTransaction().add((DummyTransaction.RunnableOperation) () -> status = Status.DEACTIVATE);
}
@Override
public Transaction createActivateTransaction() {
- return new DummyTransaction().add((DummyTransaction.RunnableOperation) () -> {
- status = Status.ACTIVATE;
- });
+ return new DummyTransaction().add((DummyTransaction.RunnableOperation) () -> status = Status.ACTIVATE);
}
@Override
public ApplicationFile getApplicationFile(Path relativePath, Mode mode) {
- if (mode == Mode.WRITE) {
- status = Status.NEW;
- }
- if (preparer == null) {
- return null;
- }
- ApplicationPackage pkg = app;
- if (pkg == null) {
- return null;
- }
- return pkg.getFile(relativePath);
+ return this.applicationPackage.getFile(relativePath);
}
@Override
@@ -205,8 +183,7 @@ public class SessionHandlerTest {
public File applicationPackage;
@Override
- public LocalSession createSession(File applicationDirectory, ApplicationId applicationId,
- TimeoutBudget timeoutBudget) {
+ public LocalSession createSession(File applicationDirectory, ApplicationId applicationId, TimeoutBudget timeoutBudget) {
createCalled = true;
if (doThrow) {
throw new RuntimeException("foo");
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationContentHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationContentHandlerTest.java
index c6a8e1f2f9d..b7f55aa0670 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationContentHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationContentHandlerTest.java
@@ -53,17 +53,17 @@ public class ApplicationContentHandlerTest extends ContentHandlerTestBase {
tenantRepository.addTenant(TenantBuilder.create(componentRegistry, tenantName1));
tenantRepository.addTenant(TenantBuilder.create(componentRegistry, tenantName2));
- session2 = new MockSession(2l, FilesApplicationPackage.fromFile(new File("src/test/apps/content")));
+ session2 = new MockSession(2, FilesApplicationPackage.fromFile(new File("src/test/apps/content")));
Tenant tenant1 = tenantRepository.getTenant(tenantName1);
tenant1.getLocalSessionRepo().addSession(session2);
tenant1.getApplicationRepo().createApplication(idTenant1);
- tenant1.getApplicationRepo().createPutTransaction(idTenant1, 2l).commit();
+ tenant1.getApplicationRepo().createPutTransaction(idTenant1, 2).commit();
- MockSession session3 = new MockSession(3l, FilesApplicationPackage.fromFile(new File("src/test/apps/content2")));
+ MockSession session3 = new MockSession(3, FilesApplicationPackage.fromFile(new File("src/test/apps/content2")));
Tenant tenant2 = tenantRepository.getTenant(tenantName2);
tenant2.getLocalSessionRepo().addSession(session3);
tenant2.getApplicationRepo().createApplication(idTenant2);
- tenant2.getApplicationRepo().createPutTransaction(idTenant2, 3l).commit();
+ tenant2.getApplicationRepo().createPutTransaction(idTenant2, 3).commit();
handler = new ApplicationHandler(ApplicationHandler.testOnlyContext(),
Zone.defaultZone(),
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/HttpGetConfigHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/HttpGetConfigHandlerTest.java
index bc583c64206..fb09aa99039 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/HttpGetConfigHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/HttpGetConfigHandlerTest.java
@@ -44,9 +44,9 @@ public class HttpGetConfigHandlerTest {
@Before
public void setUp() {
mockRequestHandler = new MockRequestHandler();
- mockRequestHandler.setAllConfigs(new HashSet<ConfigKey<?>>() {{
+ mockRequestHandler.setAllConfigs(new HashSet<>() {{
add(new ConfigKey<>("bar", "myid", "foo"));
- }} );
+ }} );
TestComponentRegistry componentRegistry = new TestComponentRegistry.Builder().build();
TenantRepository tenantRepository = new TenantRepository(componentRegistry, false);
tenantRepository.addTenant(TenantBuilder.create(componentRegistry, tenant).withRequestHandler(mockRequestHandler));
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/HttpListConfigsHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/HttpListConfigsHandlerTest.java
index 750ad1c9fc0..7789c5d88db 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/HttpListConfigsHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/HttpListConfigsHandlerTest.java
@@ -41,9 +41,9 @@ public class HttpListConfigsHandlerTest {
@Before
public void setUp() {
mockRequestHandler = new MockRequestHandler();
- mockRequestHandler.setAllConfigs(new HashSet<ConfigKey<?>>() {{
+ mockRequestHandler.setAllConfigs(new HashSet<>() {{
add(new ConfigKey<>("bar", "conf/id", "foo"));
- }} );
+ }} );
TenantName tenantName = TenantName.from("mytenant");
TenantRepository tenantRepository = new TenantRepository(componentRegistry, false);
TenantBuilder tenantBuilder = TenantBuilder.create(componentRegistry, tenantName)
@@ -73,7 +73,7 @@ public class HttpListConfigsHandlerTest {
@Test
public void require_that_named_handler_can_be_created() throws IOException {
HttpRequest req = HttpRequest.createTestRequest("http://foo.com:8080/config/v2/tenant/mytenant/application/myapplication/foo.bar/conf/id/", GET);
- req.getJDiscRequest().parameters().put("http.path", Arrays.asList("foo.bar"));
+ req.getJDiscRequest().parameters().put("http.path", List.of("foo.bar"));
HttpResponse response = namedHandler.handle(req);
assertThat(SessionHandlerTest.getRenderedString(response), is("{\"children\":[],\"configs\":[]}"));
}
@@ -81,14 +81,14 @@ public class HttpListConfigsHandlerTest {
@Test
public void require_that_named_handler_can_be_created_from_full_appid() throws IOException {
HttpRequest req = HttpRequest.createTestRequest("http://foo.com:8080/config/v2/tenant/mytenant/application/myapplication/environment/prod/region/myregion/instance/myinstance/foo.bar/conf/id/", GET);
- req.getJDiscRequest().parameters().put("http.path", Arrays.asList("foo.bar"));
+ req.getJDiscRequest().parameters().put("http.path", List.of("foo.bar"));
HttpResponse response = namedHandler.handle(req);
assertThat(SessionHandlerTest.getRenderedString(response), is("{\"children\":[],\"configs\":[]}"));
}
@Test
public void require_child_listings_correct() {
- Set<ConfigKey<?>> keys = new LinkedHashSet<ConfigKey<?>>() {{
+ Set<ConfigKey<?>> keys = new LinkedHashSet<>() {{
add(new ConfigKey<>("name1", "id/1", "ns1"));
add(new ConfigKey<>("name1", "id/1", "ns1"));
add(new ConfigKey<>("name1", "id/2", "ns1"));
@@ -104,7 +104,7 @@ public class HttpListConfigsHandlerTest {
@Test
public void require_url_building_and_mimetype_correct() {
- ListConfigsResponse resp = new ListConfigsResponse(new HashSet<ConfigKey<?>>(), null, "http://foo.com/config/v2/tenant/mytenant/application/mya/", true);
+ ListConfigsResponse resp = new ListConfigsResponse(new HashSet<>(), null, "http://foo.com/config/v2/tenant/mytenant/application/mya/", true);
assertEquals(resp.toUrl(new ConfigKey<>("myconfig", "my/id", "mynamespace"), true), "http://foo.com/config/v2/tenant/mytenant/application/mya/mynamespace.myconfig/my/id");
assertEquals(resp.toUrl(new ConfigKey<>("myconfig", "my/id", "mynamespace"), false), "http://foo.com/config/v2/tenant/mytenant/application/mya/mynamespace.myconfig/my/id/");
assertEquals(resp.toUrl(new ConfigKey<>("myconfig", "", "mynamespace"), false), "http://foo.com/config/v2/tenant/mytenant/application/mya/mynamespace.myconfig");
@@ -128,7 +128,7 @@ public class HttpListConfigsHandlerTest {
@Test
public void require_correct_error_response_on_no_model() throws IOException {
- mockRequestHandler.setAllConfigs(new HashSet<ConfigKey<?>>());
+ mockRequestHandler.setAllConfigs(new HashSet<>());
HttpResponse response = namedHandler.handle(HttpRequest.createTestRequest("http://yahoo.com:8080/config/v2/tenant/mytenant/application/myapplication/foo.bar/myid/", GET));
HandlerTest.assertHttpStatusCodeErrorCodeAndMessage(response, NOT_FOUND,
HttpErrorResponse.errorCodes.NOT_FOUND,
@@ -137,7 +137,7 @@ public class HttpListConfigsHandlerTest {
@Test
public void require_correct_configid_parent() {
- assertEquals(ListConfigsResponse.parentConfigId(null), null);
+ assertNull(ListConfigsResponse.parentConfigId(null));
assertEquals(ListConfigsResponse.parentConfigId("foo"), "");
assertEquals(ListConfigsResponse.parentConfigId(""), "");
assertEquals(ListConfigsResponse.parentConfigId("/"), "");
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionActiveHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionActiveHandlerTest.java
index d94194e58d9..52ecc5e2bae 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionActiveHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionActiveHandlerTest.java
@@ -21,7 +21,6 @@ import com.yahoo.jdisc.http.HttpRequest;
import com.yahoo.slime.JsonFormat;
import com.yahoo.vespa.config.server.ApplicationRepository;
import com.yahoo.vespa.config.server.MockReloadHandler;
-import com.yahoo.vespa.config.server.SuperModelGenerationCounter;
import com.yahoo.vespa.config.server.TestComponentRegistry;
import com.yahoo.vespa.config.server.application.OrchestratorMock;
import com.yahoo.vespa.config.server.application.TenantApplications;
@@ -37,7 +36,6 @@ import com.yahoo.vespa.config.server.session.LocalSession;
import com.yahoo.vespa.config.server.session.LocalSessionRepo;
import com.yahoo.vespa.config.server.session.MockSessionZKClient;
import com.yahoo.vespa.config.server.session.RemoteSession;
-import com.yahoo.vespa.config.server.session.RemoteSessionRepo;
import com.yahoo.vespa.config.server.session.Session;
import com.yahoo.vespa.config.server.session.SessionContext;
import com.yahoo.vespa.config.server.session.SessionTest;
@@ -58,7 +56,6 @@ import org.junit.rules.TemporaryFolder;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
-import java.time.Clock;
import java.util.Collections;
import java.util.Optional;
@@ -81,9 +78,7 @@ public class SessionActiveHandlerTest extends SessionHandlerTest {
private static final String activatedMessage = " for tenant '" + tenantName + "' activated.";
private final InMemoryFlagSource flagSource = new InMemoryFlagSource();
- private final Clock clock = Clock.systemUTC();
private Curator curator;
- private RemoteSessionRepo remoteSessionRepo;
private LocalSessionRepo localRepo;
private TenantApplications applicationRepo;
private MockProvisioner hostProvisioner;
@@ -97,7 +92,6 @@ public class SessionActiveHandlerTest extends SessionHandlerTest {
@Before
public void setup() {
- remoteSessionRepo = new RemoteSessionRepo(tenantName);
curator = new MockCurator();
modelFactory = new VespaModelFactory(new NullConfigModelRegistry());
componentRegistry = new TestComponentRegistry.Builder()
@@ -106,13 +100,12 @@ public class SessionActiveHandlerTest extends SessionHandlerTest {
.build();
tenantRepository = new TenantRepository(componentRegistry, false);
applicationRepo = TenantApplications.create(componentRegistry, new MockReloadHandler(), tenantName);
- localRepo = new LocalSessionRepo(clock, curator);
+ localRepo = new LocalSessionRepo(tenantName, componentRegistry);
pathPrefix = "/application/v2/tenant/" + tenantName + "/session/";
hostProvisioner = new MockProvisioner();
TenantBuilder tenantBuilder = TenantBuilder.create(componentRegistry, tenantName)
.withSessionFactory(new MockSessionFactory())
.withLocalSessionRepo(localRepo)
- .withRemoteSessionRepo(remoteSessionRepo)
.withApplicationRepo(applicationRepo);
tenantRepository.addTenant(tenantBuilder);
handler = createHandler();
@@ -120,15 +113,15 @@ public class SessionActiveHandlerTest extends SessionHandlerTest {
@Test
public void testThatPreviousSessionIsDeactivated() throws Exception {
- RemoteSession firstSession = activateAndAssertOK(90l, 0l);
- activateAndAssertOK(91l, 90l);
+ RemoteSession firstSession = activateAndAssertOK(90, 0);
+ activateAndAssertOK(91, 90);
assertThat(firstSession.getStatus(), Is.is(Session.Status.DEACTIVATE));
}
@Test
public void testForceActivationWithActivationInBetween() throws Exception {
- activateAndAssertOK(90l, 0l);
- activateAndAssertOK(92l, 89l, "?force=true");
+ activateAndAssertOK(90, 0);
+ activateAndAssertOK(92, 89, "?force=true");
}
@Test
@@ -140,9 +133,9 @@ public class SessionActiveHandlerTest extends SessionHandlerTest {
@Test
public void testActivationWithBarrierTimeout() throws Exception {
// Needed so we can test that previous active session is still active after a failed activation
- activateAndAssertOK(90l, 0l);
+ activateAndAssertOK(90, 0);
((MockCurator) curator).timeoutBarrierOnEnter(true);
- ActivateRequest activateRequest = new ActivateRequest(91l, 90l, "", Clock.systemUTC()).invoke();
+ ActivateRequest activateRequest = new ActivateRequest(91, 90, "").invoke();
HttpResponse actResponse = activateRequest.getActResponse();
assertThat(actResponse.getStatus(), Is.is(INTERNAL_SERVER_ERROR));
}
@@ -155,7 +148,7 @@ public class SessionActiveHandlerTest extends SessionHandlerTest {
activateAndAssertOK(sessionId, 1);
sessionId++;
- ActivateRequest activateRequest = new ActivateRequest(sessionId, 1, "", clock).invoke();
+ ActivateRequest activateRequest = new ActivateRequest(sessionId, 1, "").invoke();
HttpResponse actResponse = activateRequest.getActResponse();
String message = getRenderedString(actResponse);
assertThat(message, actResponse.getStatus(), Is.is(CONFLICT));
@@ -166,7 +159,7 @@ public class SessionActiveHandlerTest extends SessionHandlerTest {
@Test
public void testAlreadyActivatedSession() throws Exception {
activateAndAssertOK(1, 0);
- HttpResponse response = handler.handle(SessionHandlerTest.createTestRequest(pathPrefix, HttpRequest.Method.PUT, Cmd.ACTIVE, 1l));
+ HttpResponse response = handler.handle(SessionHandlerTest.createTestRequest(pathPrefix, HttpRequest.Method.PUT, Cmd.ACTIVE, 1L));
String message = getRenderedString(response);
assertThat(message, response.getStatus(), Is.is(BAD_REQUEST));
assertThat(message, containsString("Session 1 is already active"));
@@ -179,8 +172,8 @@ public class SessionActiveHandlerTest extends SessionHandlerTest {
@Test
public void testActivationWithActivationInBetween() throws Exception {
- activateAndAssertOK(90l, 0l);
- activateAndAssertError(92l, 89l, clock,
+ activateAndAssertOK(90, 0);
+ activateAndAssertError(92, 89,
Response.Status.CONFLICT, HttpErrorResponse.errorCodes.ACTIVATION_CONFLICT,
"tenant:" + tenantName + " app:default:default Cannot activate session 92 because the currently active session (90) has changed since session 92 was created (was 89 at creation time)");
}
@@ -188,9 +181,9 @@ public class SessionActiveHandlerTest extends SessionHandlerTest {
@Test
public void testActivationOfUnpreparedSession() throws Exception {
// Needed so we can test that previous active session is still active after a failed activation
- RemoteSession firstSession = activateAndAssertOK(90l, 0l);
+ RemoteSession firstSession = activateAndAssertOK(90, 0);
long sessionId = 91L;
- ActivateRequest activateRequest = new ActivateRequest(sessionId, 0l, Session.Status.NEW, "", clock).invoke();
+ ActivateRequest activateRequest = new ActivateRequest(sessionId, 0, Session.Status.NEW, "").invoke();
HttpResponse actResponse = activateRequest.getActResponse();
RemoteSession session = activateRequest.getSession();
assertThat(actResponse.getStatus(), is(Response.Status.BAD_REQUEST));
@@ -211,7 +204,7 @@ public class SessionActiveHandlerTest extends SessionHandlerTest {
public void require_that_handler_gives_error_when_provisioner_activated_fails() throws Exception {
hostProvisioner = new FailingMockProvisioner();
hostProvisioner.activated = false;
- activateAndAssertError(1, 0, clock, BAD_REQUEST, HttpErrorResponse.errorCodes.BAD_REQUEST, "Cannot activate application");
+ activateAndAssertError(1, 0, BAD_REQUEST, HttpErrorResponse.errorCodes.BAD_REQUEST, "Cannot activate application");
assertFalse(hostProvisioner.activated);
}
@@ -221,9 +214,7 @@ public class SessionActiveHandlerTest extends SessionHandlerTest {
TenantRepository.getSessionsPath(tenantName).append(String.valueOf(sessionId)));
zkC.write(Collections.singletonMap(modelFactory.version(), new MockFileRegistry()));
zkC.write(AllocatedHosts.withHosts(Collections.emptySet()));
- RemoteSession session = new RemoteSession(tenantName, sessionId, componentRegistry, zkClient);
- remoteSessionRepo.addSession(session);
- return session;
+ return new RemoteSession(tenantName, sessionId, componentRegistry, zkClient);
}
private void addLocalSession(long sessionId, DeployData deployData, SessionZooKeeperClient zkc) throws IOException {
@@ -237,7 +228,7 @@ public class SessionActiveHandlerTest extends SessionHandlerTest {
}
private ActivateRequest activateAndAssertOKPut(long sessionId, long previousSessionId, String subPath) throws Exception {
- ActivateRequest activateRequest = new ActivateRequest(sessionId, previousSessionId, subPath, clock);
+ ActivateRequest activateRequest = new ActivateRequest(sessionId, previousSessionId, subPath);
activateRequest.invoke();
HttpResponse actResponse = activateRequest.getActResponse();
String message = getRenderedString(actResponse);
@@ -248,9 +239,9 @@ public class SessionActiveHandlerTest extends SessionHandlerTest {
return activateRequest;
}
- private void activateAndAssertErrorPut(long sessionId, long previousSessionId, Clock clock,
+ private void activateAndAssertErrorPut(long sessionId, long previousSessionId,
int statusCode, HttpErrorResponse.errorCodes errorCode, String expectedError) throws Exception {
- ActivateRequest activateRequest = new ActivateRequest(sessionId, previousSessionId, "", clock);
+ ActivateRequest activateRequest = new ActivateRequest(sessionId, previousSessionId, "");
activateRequest.invoke();
HttpResponse actResponse = activateRequest.getActResponse();
RemoteSession session = activateRequest.getSession();
@@ -277,24 +268,22 @@ public class SessionActiveHandlerTest extends SessionHandlerTest {
private DeployData deployData;
private ApplicationMetaData metaData;
private String subPath;
- private Clock clock;
- ActivateRequest(long sessionId, long previousSessionId, String subPath, Clock clock) {
- this(sessionId, previousSessionId, Session.Status.PREPARE, subPath, clock);
+ ActivateRequest(long sessionId, long previousSessionId, String subPath) {
+ this(sessionId, previousSessionId, Session.Status.PREPARE, subPath);
}
- ActivateRequest(long sessionId, long previousSessionId, Session.Status initialStatus, String subPath, Clock clock) {
+ ActivateRequest(long sessionId, long previousSessionId, Session.Status initialStatus, String subPath) {
this.sessionId = sessionId;
this.initialStatus = initialStatus;
this.deployData = new DeployData("foo",
"bar",
appName,
- 0l,
+ 0L,
false,
sessionId,
previousSessionId);
this.subPath = subPath;
- this.clock = clock;
}
public RemoteSession getSession() {
@@ -354,9 +343,9 @@ public class SessionActiveHandlerTest extends SessionHandlerTest {
assertThat(hostProvisioner.lastHosts.size(), is(1));
}
- private void activateAndAssertError(long sessionId, long previousSessionId, Clock clock, int statusCode, HttpErrorResponse.errorCodes errorCode, String expectedError) throws Exception {
+ private void activateAndAssertError(long sessionId, long previousSessionId, int statusCode, HttpErrorResponse.errorCodes errorCode, String expectedError) throws Exception {
hostProvisioner.activated = false;
- activateAndAssertErrorPut(sessionId, previousSessionId, clock, statusCode, errorCode, expectedError);
+ activateAndAssertErrorPut(sessionId, previousSessionId, statusCode, errorCode, expectedError);
assertFalse(hostProvisioner.activated);
}
@@ -370,7 +359,7 @@ public class SessionActiveHandlerTest extends SessionHandlerTest {
new ApplicationRepository(tenantRepository,
hostProvisioner,
new OrchestratorMock(),
- clock),
+ componentRegistry.getClock()),
tenantRepository,
Zone.defaultZone());
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionContentHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionContentHandlerTest.java
index 42b3fadc0de..d4dd17ca280 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionContentHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionContentHandlerTest.java
@@ -24,7 +24,6 @@ import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-import java.time.Clock;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertNotNull;
@@ -37,7 +36,6 @@ public class SessionContentHandlerTest extends ContentHandlerTestBase {
private static final TenantName tenant = TenantName.from("contenttest");
private final TestComponentRegistry componentRegistry = new TestComponentRegistry.Builder().build();
- private final Clock clock = componentRegistry.getClock();
private TenantRepository tenantRepository;
private SessionContentHandler handler = null;
@@ -59,10 +57,11 @@ public class SessionContentHandlerTest extends ContentHandlerTestBase {
assertMkdir("/bar/brask/");
assertMkdir("/bar/brask/bram/");
assertMkdir("/brask/og/bram/");
- }// TODO: Enable when we have a predictable way of checking request body existence.
+ }
@Test
@Ignore
+ // TODO: Enable when we have a predictable way of checking request body existence.
public void require_that_mkdir_with_body_is_illegal(){
HttpResponse response = put("/foobio/", "foo");
assertNotNull(response);
@@ -70,15 +69,15 @@ public class SessionContentHandlerTest extends ContentHandlerTestBase {
}
@Test
- public void require_that_nonexistant_session_returns_not_found() {
- HttpResponse response = doRequest(HttpRequest.Method.GET, "/test.txt", 2l);
+ public void require_that_nonexistent_session_returns_not_found() {
+ HttpResponse response = doRequest(HttpRequest.Method.GET, "/test.txt", 2);
assertNotNull(response);
assertThat(response.getStatus(), is(Response.Status.NOT_FOUND));
}
protected HttpResponse put(String path, String content) {
ByteArrayInputStream data = new ByteArrayInputStream(Utf8.toBytes(content));
- return doRequest(HttpRequest.Method.PUT, path, data);
+ return doRequest(HttpRequest.Method.PUT, path, 1, data);
}
@Test
@@ -95,7 +94,7 @@ public class SessionContentHandlerTest extends ContentHandlerTestBase {
}
@Test
- public void require_that_nonexistant_file_returs_not_found_when_deleted() throws IOException {
+ public void require_that_nonexistent_file_returns_not_found_when_deleted() throws IOException {
assertDeleteFile(Response.Status.NOT_FOUND, "/test2.txt", "{\"error-code\":\"NOT_FOUND\",\"message\":\"Session 1 does not contain a file 'test2.txt'\"}");
}
@@ -152,17 +151,13 @@ public class SessionContentHandlerTest extends ContentHandlerTestBase {
}
protected HttpResponse doRequest(HttpRequest.Method method, String path) {
- return doRequest(method, path, 1l);
+ return doRequest(method, path, 1);
}
private HttpResponse doRequest(HttpRequest.Method method, String path, long sessionId) {
return handler.handle(SessionHandlerTest.createTestRequest(pathPrefix, method, Cmd.CONTENT, sessionId, path));
}
- private HttpResponse doRequest(HttpRequest.Method method, String path, InputStream data) {
- return doRequest(method, path, 1l, data);
- }
-
private HttpResponse doRequest(HttpRequest.Method method, String path, long sessionId, InputStream data) {
return handler.handle(SessionHandlerTest.createTestRequest(pathPrefix, method, Cmd.CONTENT, sessionId, path, data));
}
@@ -173,7 +168,7 @@ public class SessionContentHandlerTest extends ContentHandlerTestBase {
new ApplicationRepository(tenantRepository,
new SessionHandlerTest.MockProvisioner(),
new OrchestratorMock(),
- clock),
+ componentRegistry.getClock()),
tenantRepository);
}
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java
index ac4b4ea9005..6d2c2d35ab9 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java
@@ -18,8 +18,6 @@ import com.yahoo.vespa.config.server.http.SessionHandlerTest;
import com.yahoo.vespa.config.server.session.LocalSessionRepo;
import com.yahoo.vespa.config.server.tenant.TenantBuilder;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
-import com.yahoo.vespa.curator.Curator;
-import com.yahoo.vespa.curator.mock.MockCurator;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
@@ -28,7 +26,6 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.time.Clock;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -53,7 +50,6 @@ public class SessionCreateHandlerTest extends SessionHandlerTest {
private static final HashMap<String, String> postHeaders = new HashMap<>();
private final TestComponentRegistry componentRegistry = new TestComponentRegistry.Builder().build();
- private final Clock clock = componentRegistry.getClock();
private String pathPrefix = "/application/v2/session/";
private String createdMessage = " created.\"";
@@ -71,9 +67,8 @@ public class SessionCreateHandlerTest extends SessionHandlerTest {
@Before
public void setupRepo() {
- Curator curator = new MockCurator();
applicationRepo = TenantApplications.create(componentRegistry, new MockReloadHandler(), tenant);
- localSessionRepo = new LocalSessionRepo(Clock.systemUTC(), curator);
+ localSessionRepo = new LocalSessionRepo(tenant, componentRegistry);
tenantRepository = new TenantRepository(componentRegistry, false);
sessionFactory = new MockSessionFactory();
TenantBuilder tenantBuilder = TenantBuilder.create(componentRegistry, tenant)
@@ -182,13 +177,12 @@ public class SessionCreateHandlerTest extends SessionHandlerTest {
public void require_that_session_is_stored_in_repo() throws IOException {
File outFile = CompressedApplicationInputStreamTest.createTarFile();
createHandler().handle(post(outFile));
- assertNotNull(localSessionRepo.getSession(0l));
+ assertNotNull(localSessionRepo.getSession(0));
}
-
@Test
public void require_that_application_urls_can_be_given_as_from_parameter() throws Exception {
- localSessionRepo.addSession(new SessionHandlerTest.MockSession(2l, FilesApplicationPackage.fromFile(testApp)));
+ localSessionRepo.addSession(new SessionHandlerTest.MockSession(2, FilesApplicationPackage.fromFile(testApp)));
ApplicationId fooId = new ApplicationId.Builder()
.tenant(tenant)
.applicationName("foo")
@@ -197,7 +191,7 @@ public class SessionCreateHandlerTest extends SessionHandlerTest {
applicationRepo.createApplication(fooId);
applicationRepo.createPutTransaction(fooId, 2).commit();
assertFromParameter("3", "http://myhost:40555/application/v2/tenant/" + tenant + "/application/foo/environment/test/region/baz/instance/quux");
- localSessionRepo.addSession(new SessionHandlerTest.MockSession(5l, FilesApplicationPackage.fromFile(testApp)));
+ localSessionRepo.addSession(new SessionHandlerTest.MockSession(5, FilesApplicationPackage.fromFile(testApp)));
ApplicationId bioId = new ApplicationId.Builder()
.tenant(tenant)
.applicationName("foobio")
@@ -224,7 +218,7 @@ public class SessionCreateHandlerTest extends SessionHandlerTest {
new ApplicationRepository(tenantRepository,
new SessionHandlerTest.MockProvisioner(),
new OrchestratorMock(),
- clock),
+ componentRegistry.getClock()),
tenantRepository,
componentRegistry.getConfigserverConfig());
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareHandlerTest.java
index b6c3de8a1b1..e201ce107bd 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareHandlerTest.java
@@ -46,9 +46,10 @@ import java.util.Collections;
import java.util.List;
import java.util.Optional;
-import static com.yahoo.jdisc.Response.Status.*;
import static com.yahoo.jdisc.Response.Status.BAD_REQUEST;
+import static com.yahoo.jdisc.Response.Status.METHOD_NOT_ALLOWED;
import static com.yahoo.jdisc.Response.Status.NOT_FOUND;
+import static com.yahoo.jdisc.Response.Status.OK;
import static com.yahoo.vespa.config.server.http.HandlerTest.assertHttpStatusCodeErrorCodeAndMessage;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
@@ -63,30 +64,25 @@ import static org.junit.Assert.assertThat;
public class SessionPrepareHandlerTest extends SessionHandlerTest {
private static final TenantName tenant = TenantName.from("test");
- private final TestComponentRegistry componentRegistry = new TestComponentRegistry.Builder().build();
+ private Curator curator = new MockCurator();
+ private final TestComponentRegistry componentRegistry = new TestComponentRegistry.Builder().curator(curator).build();
private final Clock clock = componentRegistry.getClock();
-
- private Curator curator;
private LocalSessionRepo localRepo;
private String preparedMessage = " prepared.\"}";
private String tenantMessage = "";
- private RemoteSessionRepo remoteSessionRepo;
private TenantRepository tenantRepository;
@Before
public void setupRepo() {
- curator = new MockCurator();
- localRepo = new LocalSessionRepo(clock, curator);
+ localRepo = new LocalSessionRepo(tenant, componentRegistry);
pathPrefix = "/application/v2/tenant/" + tenant + "/session/";
preparedMessage = " for tenant '" + tenant + "' prepared.\"";
tenantMessage = ",\"tenant\":\"" + tenant + "\"";
tenantRepository = new TenantRepository(componentRegistry, false);
- remoteSessionRepo = new RemoteSessionRepo(tenant);
TenantBuilder tenantBuilder = TenantBuilder.create(componentRegistry, tenant)
.withSessionFactory(new MockSessionFactory())
.withLocalSessionRepo(localRepo)
- .withRemoteSessionRepo(remoteSessionRepo)
.withApplicationRepo(TenantApplications.create(componentRegistry, new MockReloadHandler(), tenant));
tenantRepository.addTenant(tenantBuilder);
}
@@ -151,21 +147,8 @@ public class SessionPrepareHandlerTest extends SessionHandlerTest {
assertThat(SessionHandlerTest.getRenderedString(response), containsString("debuglog"));
}
- /**
- * A mock remote session repo based on contents of local repo. Only works when there is just one session in local repo
- */
- // TODO: Fix this mess
- private SessionZooKeeperClient fromLocalSessionRepo(LocalSessionRepo localRepo) {
- SessionZooKeeperClient zooKeeperClient = null;
- for (LocalSession ls : localRepo.listSessions()) {
- zooKeeperClient = new MockSessionZKClient(curator, tenant, ls.getSessionId());
- if (ls.getStatus()!=null) zooKeeperClient.writeStatus(ls.getStatus());
- RemoteSession remSess = new RemoteSession(tenant, ls.getSessionId(),
- new TestComponentRegistry.Builder().curator(curator).build(),
- zooKeeperClient);
- remoteSessionRepo.addSession(remSess);
- }
- return zooKeeperClient;
+ private SessionZooKeeperClient createSessionZooKeeperClient(LocalSession session) {
+ return new MockSessionZKClient(curator, tenant, session.getSessionId());
}
@Test
@@ -175,7 +158,7 @@ public class SessionPrepareHandlerTest extends SessionHandlerTest {
SessionHandler sessHandler = createHandler();
sessHandler.handle(SessionHandlerTest.createTestRequest(pathPrefix, HttpRequest.Method.PUT, Cmd.PREPARED, 1L));
session.setStatus(Session.Status.PREPARE);
- SessionZooKeeperClient zooKeeperClient = fromLocalSessionRepo(localRepo);
+ SessionZooKeeperClient zooKeeperClient = createSessionZooKeeperClient(session);
zooKeeperClient.writeStatus(Session.Status.PREPARE);
HttpResponse getResponse = sessHandler.handle(
SessionHandlerTest.createTestRequest(pathPrefix, HttpRequest.Method.GET, Cmd.PREPARED, 1L));
@@ -189,7 +172,7 @@ public class SessionPrepareHandlerTest extends SessionHandlerTest {
localRepo.addSession(session);
SessionHandler sessHandler = createHandler();
session.setStatus(Session.Status.NEW);
- SessionZooKeeperClient zooKeeperClient = fromLocalSessionRepo(localRepo);
+ SessionZooKeeperClient zooKeeperClient = createSessionZooKeeperClient(session);
zooKeeperClient.writeStatus(Session.Status.NEW);
HttpResponse getResponse = sessHandler.handle(
SessionHandlerTest.createTestRequest(pathPrefix, HttpRequest.Method.GET, Cmd.PREPARED, 1L));
@@ -244,12 +227,11 @@ public class SessionPrepareHandlerTest extends SessionHandlerTest {
@Test
public void require_that_preparing_with_multiple_tenants_work() throws Exception {
- // Need different repo for 'test2' tenant
- LocalSessionRepo localRepoDefault = new LocalSessionRepo(clock, curator);
final TenantName defaultTenant = TenantName.from("test2");
+ // Need different repo for 'test2' tenant
+ LocalSessionRepo localRepoDefault = new LocalSessionRepo(defaultTenant, componentRegistry);
TenantBuilder defaultTenantBuilder = TenantBuilder.create(componentRegistry, defaultTenant)
.withLocalSessionRepo(localRepoDefault)
- .withRemoteSessionRepo(new RemoteSessionRepo(defaultTenant))
.withSessionFactory(new MockSessionFactory());
tenantRepository.addTenant(defaultTenantBuilder);
final SessionHandler handler = createHandler();
@@ -326,7 +308,7 @@ public class SessionPrepareHandlerTest extends SessionHandlerTest {
}
@Test
- public void test_out_of_capacity_response() throws InterruptedException, IOException {
+ public void test_out_of_capacity_response() throws IOException {
String message = "Internal error";
SessionThrowingException session = new SessionThrowingException(new OutOfCapacityException(message));
localRepo.addSession(session);
@@ -339,7 +321,7 @@ public class SessionPrepareHandlerTest extends SessionHandlerTest {
}
@Test
- public void test_that_nullpointerexception_gives_internal_server_error() throws InterruptedException, IOException {
+ public void test_that_nullpointerexception_gives_internal_server_error() throws IOException {
String message = "No nodes available";
SessionThrowingException session = new SessionThrowingException(new NullPointerException(message));
localRepo.addSession(session);
@@ -352,7 +334,7 @@ public class SessionPrepareHandlerTest extends SessionHandlerTest {
}
@Test
- public void test_application_lock_failure() throws InterruptedException, IOException {
+ public void test_application_lock_failure() throws IOException {
String message = "Timed out after waiting PT1M to acquire lock '/provision/v1/locks/foo/bar/default'";
SessionThrowingException session =
new SessionThrowingException(new ApplicationLockException(new UncheckedTimeoutException(message)));
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/TenantHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/TenantHandlerTest.java
index 6effa3359b1..9d843a1f2c6 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/TenantHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/TenantHandlerTest.java
@@ -6,6 +6,7 @@ import static org.junit.Assert.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.time.Clock;
import com.yahoo.config.provision.ApplicationId;
@@ -139,7 +140,7 @@ public class TenantHandlerTest {
private void assertResponseEquals(SessionResponse response, String payload) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
response.render(baos);
- assertEquals(baos.toString("UTF-8"), payload);
+ assertEquals(baos.toString(StandardCharsets.UTF_8), payload);
}
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/maintenance/MaintainerTester.java b/configserver/src/test/java/com/yahoo/vespa/config/server/maintenance/MaintainerTester.java
index 659baf5a184..c2489a1d3e9 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/maintenance/MaintainerTester.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/maintenance/MaintainerTester.java
@@ -10,8 +10,6 @@ import com.yahoo.vespa.config.server.tenant.TenantRepository;
import com.yahoo.vespa.curator.Curator;
import com.yahoo.vespa.curator.mock.MockCurator;
-import java.time.Clock;
-
class MaintainerTester {
private final Curator curator;
@@ -25,7 +23,7 @@ class MaintainerTester {
applicationRepository = new ApplicationRepository(tenantRepository,
new SessionHandlerTest.MockProvisioner(),
new OrchestratorMock(),
- Clock.systemUTC());
+ componentRegistry.getClock());
}
Curator curator() { return curator; }
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/metrics/MetricsRetrieverTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/metrics/MetricsRetrieverTest.java
new file mode 100644
index 00000000000..1b878a432c9
--- /dev/null
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/metrics/MetricsRetrieverTest.java
@@ -0,0 +1,99 @@
+package com.yahoo.vespa.config.server.metrics;
+
+import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import junit.framework.AssertionFailedError;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.BiConsumer;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
+import static org.junit.Assert.*;
+
+
+/**
+ * @author olaa
+ */
+public class MetricsRetrieverTest {
+
+ @Rule
+ public final WireMockRule wireMock = new WireMockRule(options().port(8080), true);
+
+ @Test
+ public void testMetricAggregation() throws IOException {
+ var metricsRetriever = new MetricsRetriever();
+
+ var clusters = List.of(
+ new ClusterInfo("cluster1", ClusterInfo.ClusterType.content, List.of(URI.create("http://localhost:8080/1"), URI.create("http://localhost:8080/2"))),
+ new ClusterInfo("cluster2", ClusterInfo.ClusterType.container, List.of(URI.create("http://localhost:8080/3")))
+ );
+
+ stubFor(get(urlEqualTo("/1"))
+ .willReturn(aResponse()
+ .withStatus(200)
+ .withBody(contentMetrics())));
+
+ stubFor(get(urlEqualTo("/2"))
+ .willReturn(aResponse()
+ .withStatus(200)
+ .withBody(contentMetrics())));
+
+ stubFor(get(urlEqualTo("/3"))
+ .willReturn(aResponse()
+ .withStatus(200)
+ .withBody(containerMetrics())));
+
+ compareAggregators(
+ new MetricsAggregator().addDocumentCount(6000.0),
+ metricsRetriever.requestMetricsForCluster(clusters.get(0))
+ );
+
+ compareAggregators(
+ new MetricsAggregator()
+ .addContainerLatency(3000, 43)
+ .addContainerLatency(2000, 0)
+ .addQrLatency(3000, 43)
+ .addFeedLatency(3000, 43),
+ metricsRetriever.requestMetricsForCluster(clusters.get(1))
+ );
+
+ wireMock.stop();
+ }
+
+ private String containerMetrics() throws IOException {
+ return Files.readString(Path.of("src/test/resources/metrics/container_metrics"));
+ }
+
+ private String contentMetrics() throws IOException {
+ return Files.readString(Path.of("src/test/resources/metrics/content_metrics"));
+ }
+
+ // Same tolerance value as used internally in MetricsAggregator.isZero
+ private static final double metricsTolerance = 0.001;
+
+ private void compareAggregators(MetricsAggregator expected, MetricsAggregator actual) {
+ BiConsumer<Double, Double> assertDoubles = (a, b) -> assertEquals(a.doubleValue(), b.doubleValue(), metricsTolerance);
+
+ compareOptionals(expected.aggregateDocumentCount(), actual.aggregateDocumentCount(), assertDoubles);
+ compareOptionals(expected.aggregateQueryRate(), actual.aggregateQueryRate(), assertDoubles);
+ compareOptionals(expected.aggregateFeedRate(), actual.aggregateFeedRate(), assertDoubles);
+ compareOptionals(expected.aggregateQueryLatency(), actual.aggregateQueryLatency(), assertDoubles);
+ compareOptionals(expected.aggregateFeedLatency(), actual.aggregateFeedLatency(), assertDoubles);
+ }
+
+ @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
+ private static <T> void compareOptionals(Optional<T> a, Optional<T> b, BiConsumer<T, T> comparer) {
+ if (a.isPresent() != b.isPresent()) throw new AssertionFailedError("Both optionals are not present: " + a + ", " + b);
+ a.ifPresent(x -> b.ifPresent(y -> comparer.accept(x, y)));
+ }
+} \ No newline at end of file
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/model/LbServicesProducerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/model/LbServicesProducerTest.java
index 34a4074c7cf..9b678b9b37f 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/model/LbServicesProducerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/model/LbServicesProducerTest.java
@@ -28,7 +28,6 @@ import org.xml.sax.SAXException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
@@ -212,7 +211,7 @@ public class LbServicesProducerTest {
private ApplicationInfo createApplication(ApplicationId appId, DeployState.Builder deploystateBuilder) throws IOException, SAXException {
return new ApplicationInfo(
appId,
- 3l,
+ 3,
createVespaModel(createApplicationPackage(
appId.tenant() + "." + appId.application() + ".yahoo.com", appId.tenant().value() + "." + appId.application().value() + "2.yahoo.com"),
deploystateBuilder));
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/model/RoutingProducerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/model/RoutingProducerTest.java
index 7ce2c39c6a4..5a34dd4c912 100755
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/model/RoutingProducerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/model/RoutingProducerTest.java
@@ -66,7 +66,7 @@ public class RoutingProducerTest {
private ApplicationInfo createApplication(ApplicationId appId, DeployState.Builder deploystateBuilder) throws IOException, SAXException {
return new ApplicationInfo(
appId,
- 3l,
+ 3L,
createVespaModel(
createApplicationPackage(
appId.tenant() + "." + appId.application() + ".yahoo.com",
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/monitoring/ZKMetricUpdaterTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/monitoring/ZKMetricUpdaterTest.java
index 28c6dddd5a7..ed089109759 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/monitoring/ZKMetricUpdaterTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/monitoring/ZKMetricUpdaterTest.java
@@ -62,8 +62,7 @@ public class ZKMetricUpdaterTest {
private ZKMetricUpdater buildUpdater() {
ZookeeperServerConfig zkServerConfig = new ZookeeperServerConfig(
new ZookeeperServerConfig.Builder().clientPort(serverPort).myid(12345));
- ZKMetricUpdater updater = new ZKMetricUpdater(zkServerConfig, 0, -1);
- return updater;
+ return new ZKMetricUpdater(zkServerConfig, 0, -1);
}
private void setupTcpServer(Supplier<String> reportProvider) throws IOException {
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/DelayedConfigResponseTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/DelayedConfigResponseTest.java
index 819078d296b..87459228d0d 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/DelayedConfigResponseTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/DelayedConfigResponseTest.java
@@ -17,11 +17,13 @@ import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.IOException;
+import java.time.Duration;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@@ -52,7 +54,7 @@ public class DelayedConfigResponseTest {
assertThat(responses.size(), is(2));
assertTrue(req.isDelayedResponse());
List<DelayedConfigResponses.DelayedConfigResponse> it = responses.allDelayedResponses();
- assertTrue(!it.isEmpty());
+ assertFalse(it.isEmpty());
}
@Test
@@ -74,7 +76,7 @@ public class DelayedConfigResponseTest {
assertThat(responses.toString(), is("DelayedConfigResponses. Average Size=0"));
JRTServerConfigRequest req = createRequest("foo", "md5", "myid", "mymd5", 3, 100, "bar");
responses.delayResponse(req, GetConfigContext.testContext(ApplicationId.defaultId()));
- rpc.waitUntilSet(5000);
+ rpc.waitUntilSet(Duration.ofSeconds(5));
assertThat(rpc.latestRequest, is(req));
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/MockRpc.java b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/MockRpc.java
index 5fa51e1c404..07f6e9cf222 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/MockRpc.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/MockRpc.java
@@ -15,6 +15,8 @@ import com.yahoo.vespa.config.server.rpc.security.NoopRpcAuthorizer;
import com.yahoo.vespa.config.server.tenant.MockTenantProvider;
import java.io.File;
+import java.time.Duration;
+import java.time.Instant;
import java.util.Optional;
import java.util.concurrent.CompletionService;
@@ -67,10 +69,9 @@ public class MockRpc extends RpcServer {
return new ConfigserverConfig(b);
}
- public boolean waitUntilSet(int timeout) {
- long start = System.currentTimeMillis();
- long end = start + timeout;
- while (start < end) {
+ boolean waitUntilSet(Duration timeout) {
+ Instant end = Instant.now().plus(timeout);
+ while (Instant.now().isBefore(end)) {
if (latestRequest != null)
return true;
try {
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionRepoTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionRepoTest.java
index d3f364e30ac..9232109a89b 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionRepoTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionRepoTest.java
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server.session;
+import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.config.model.application.provider.FilesApplicationPackage;
import com.yahoo.test.ManualClock;
import com.yahoo.config.provision.TenantName;
@@ -8,7 +9,6 @@ import com.yahoo.vespa.config.server.GlobalComponentRegistry;
import com.yahoo.vespa.config.server.MockReloadHandler;
import com.yahoo.vespa.config.server.TestComponentRegistry;
import com.yahoo.vespa.config.server.application.TenantApplications;
-import com.yahoo.vespa.config.server.deploy.TenantFileSystemDirs;
import com.yahoo.io.IOUtils;
import com.yahoo.vespa.config.server.host.HostRegistry;
import com.yahoo.vespa.config.server.http.SessionHandlerTest;
@@ -20,6 +20,8 @@ import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.time.Duration;
import java.time.Instant;
@@ -46,61 +48,68 @@ public class LocalSessionRepoTest {
}
private void setupSessions(TenantName tenantName, boolean createInitialSessions) throws Exception {
- TenantFileSystemDirs tenantFileSystemDirs = new TenantFileSystemDirs(temporaryFolder.newFolder(), tenantName);
+ File configserverDbDir = temporaryFolder.newFolder().getAbsoluteFile();
if (createInitialSessions) {
- IOUtils.copyDirectory(testApp, new File(tenantFileSystemDirs.sessionsPath(), "1"));
- IOUtils.copyDirectory(testApp, new File(tenantFileSystemDirs.sessionsPath(), "2"));
- IOUtils.copyDirectory(testApp, new File(tenantFileSystemDirs.sessionsPath(), "3"));
+ Path sessionsPath = Paths.get(configserverDbDir.getAbsolutePath(), "tenants", tenantName.value(), "sessions");
+ IOUtils.copyDirectory(testApp, sessionsPath.resolve("1").toFile());
+ IOUtils.copyDirectory(testApp, sessionsPath.resolve("2").toFile());
+ IOUtils.copyDirectory(testApp, sessionsPath.resolve("3").toFile());
}
clock = new ManualClock(Instant.ofEpochSecond(1));
- GlobalComponentRegistry globalComponentRegistry = new TestComponentRegistry.Builder().curator(new MockCurator())
- .clock(clock)
- .build();
+ GlobalComponentRegistry globalComponentRegistry = new TestComponentRegistry.Builder()
+ .curator(new MockCurator())
+ .clock(clock)
+ .configServerConfig(new ConfigserverConfig.Builder()
+ .configServerDBDir(configserverDbDir.getAbsolutePath())
+ .configDefinitionsDir(temporaryFolder.newFolder().getAbsolutePath())
+ .sessionLifetime(5)
+ .build())
+ .build();
LocalSessionLoader loader = new SessionFactoryImpl(globalComponentRegistry,
TenantApplications.create(globalComponentRegistry, new MockReloadHandler(), tenantName),
- tenantFileSystemDirs, new HostRegistry<>(),
+ new HostRegistry<>(),
tenantName);
- repo = new LocalSessionRepo(tenantName, globalComponentRegistry, tenantFileSystemDirs, loader);
+ repo = new LocalSessionRepo(tenantName, globalComponentRegistry, loader);
}
@Test
public void require_that_sessions_can_be_loaded_from_disk() {
- assertNotNull(repo.getSession(1l));
- assertNotNull(repo.getSession(2l));
- assertNotNull(repo.getSession(3l));
- assertNull(repo.getSession(4l));
+ assertNotNull(repo.getSession(1L));
+ assertNotNull(repo.getSession(2L));
+ assertNotNull(repo.getSession(3L));
+ assertNull(repo.getSession(4L));
}
@Test
public void require_that_old_sessions_are_purged() {
clock.advance(Duration.ofSeconds(1));
- assertNotNull(repo.getSession(1l));
- assertNotNull(repo.getSession(2l));
- assertNotNull(repo.getSession(3l));
+ assertNotNull(repo.getSession(1L));
+ assertNotNull(repo.getSession(2L));
+ assertNotNull(repo.getSession(3L));
clock.advance(Duration.ofSeconds(1));
- assertNotNull(repo.getSession(1l));
- assertNotNull(repo.getSession(2l));
- assertNotNull(repo.getSession(3l));
+ assertNotNull(repo.getSession(1L));
+ assertNotNull(repo.getSession(2L));
+ assertNotNull(repo.getSession(3L));
clock.advance(Duration.ofSeconds(1));
- addSession(4l, 6);
- assertNotNull(repo.getSession(1l));
- assertNotNull(repo.getSession(2l));
- assertNotNull(repo.getSession(3l));
- assertNotNull(repo.getSession(4l));
+ addSession(4L, 6);
+ assertNotNull(repo.getSession(1L));
+ assertNotNull(repo.getSession(2L));
+ assertNotNull(repo.getSession(3L));
+ assertNotNull(repo.getSession(4L));
clock.advance(Duration.ofSeconds(1));
- addSession(5l, 10);
+ addSession(5L, 10);
repo.purgeOldSessions();
- assertNull(repo.getSession(1l));
- assertNull(repo.getSession(2l));
- assertNull(repo.getSession(3l));
+ assertNull(repo.getSession(1L));
+ assertNull(repo.getSession(2L));
+ assertNull(repo.getSession(3L));
}
@Test
public void require_that_all_sessions_are_deleted() {
- repo.deleteAllSessions();
- assertNull(repo.getSession(1l));
- assertNull(repo.getSession(2l));
- assertNull(repo.getSession(3l));
+ repo.close();
+ assertNull(repo.getSession(1L));
+ assertNull(repo.getSession(2L));
+ assertNull(repo.getSession(3L));
}
private void addSession(long sessionId, long createTime) {
@@ -110,10 +119,10 @@ public class LocalSessionRepoTest {
@Test
public void require_that_sessions_belong_to_a_tenant() {
// tenant is "default"
- assertNotNull(repo.getSession(1l));
- assertNotNull(repo.getSession(2l));
- assertNotNull(repo.getSession(3l));
- assertNull(repo.getSession(4l));
+ assertNotNull(repo.getSession(1L));
+ assertNotNull(repo.getSession(2L));
+ assertNotNull(repo.getSession(3L));
+ assertNull(repo.getSession(4L));
// tenant is "newTenant"
try {
@@ -121,12 +130,12 @@ public class LocalSessionRepoTest {
} catch (Exception e) {
fail();
}
- assertNull(repo.getSession(1l));
+ assertNull(repo.getSession(1L));
- repo.addSession(new SessionHandlerTest.MockSession(1l, FilesApplicationPackage.fromFile(testApp)));
- repo.addSession(new SessionHandlerTest.MockSession(2l, FilesApplicationPackage.fromFile(testApp)));
- assertNotNull(repo.getSession(1l));
- assertNotNull(repo.getSession(2l));
- assertNull(repo.getSession(3l));
+ repo.addSession(new SessionHandlerTest.MockSession(1L, FilesApplicationPackage.fromFile(testApp)));
+ repo.addSession(new SessionHandlerTest.MockSession(2L, FilesApplicationPackage.fromFile(testApp)));
+ assertNotNull(repo.getSession(1L));
+ assertNotNull(repo.getSession(2L));
+ assertNull(repo.getSession(3L));
}
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionTest.java
index 96caff9b3a7..b357f712004 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionTest.java
@@ -65,17 +65,17 @@ public class LocalSessionTest {
@Test
public void require_that_session_is_initialized() throws Exception {
LocalSession session = createSession(TenantName.defaultName(), 2);
- assertThat(session.getSessionId(), is(2l));
+ assertThat(session.getSessionId(), is(2L));
session = createSession(TenantName.defaultName(), Long.MAX_VALUE);
assertThat(session.getSessionId(), is(Long.MAX_VALUE));
- assertThat(session.getActiveSessionAtCreate(), is(0l));
+ assertThat(session.getActiveSessionAtCreate(), is(0L));
}
@Test
public void require_that_session_status_is_updated() throws Exception {
LocalSession session = createSession(TenantName.defaultName(), 3);
assertThat(session.getStatus(), is(Session.Status.NEW));
- doPrepare(session, Instant.now());
+ doPrepare(session);
assertThat(session.getStatus(), is(Session.Status.PREPARE));
session.createActivateTransaction().commit();
assertThat(session.getStatus(), is(Session.Status.ACTIVATE));
@@ -84,7 +84,7 @@ public class LocalSessionTest {
@Test
public void require_that_marking_session_modified_changes_status_to_new() throws Exception {
LocalSession session = createSession(TenantName.defaultName(), 3);
- doPrepare(session, Instant.now());
+ doPrepare(session);
assertThat(session.getStatus(), is(Session.Status.PREPARE));
session.getApplicationFile(Path.createRoot(), LocalSession.Mode.READ);
assertThat(session.getStatus(), is(Session.Status.PREPARE));
@@ -97,7 +97,7 @@ public class LocalSessionTest {
SessionTest.MockSessionPreparer preparer = new SessionTest.MockSessionPreparer();
LocalSession session = createSession(TenantName.defaultName(), 3, preparer);
assertFalse(preparer.isPrepared);
- doPrepare(session, Instant.now());
+ doPrepare(session);
assertTrue(preparer.isPrepared);
assertThat(session.getStatus(), is(Session.Status.PREPARE));
}
@@ -148,7 +148,7 @@ public class LocalSessionTest {
NetworkPorts ports = new NetworkPorts(list);
AllocatedHosts input = AllocatedHosts.withHosts(Collections.singleton(
- new HostSpec("myhost", Collections.<String>emptyList(),
+ new HostSpec("myhost", Collections.emptyList(),
Optional.empty(), Optional.empty(), Optional.empty(),
Optional.of(ports))));
@@ -156,7 +156,7 @@ public class LocalSessionTest {
ApplicationId origId = new ApplicationId.Builder()
.tenant("tenant")
.applicationName("foo").instanceName("quux").build();
- doPrepare(session, new PrepareParams.Builder().applicationId(origId).build(), Instant.now());
+ doPrepare(session, new PrepareParams.Builder().applicationId(origId).build());
AllocatedHosts info = session.getAllocatedHosts();
assertNotNull(info);
assertThat(info.getHosts().size(), is(1));
@@ -169,7 +169,7 @@ public class LocalSessionTest {
@Test
public void require_that_application_metadata_is_correct() throws Exception {
LocalSession session = createSession(TenantName.defaultName(), 3);
- doPrepare(session, new PrepareParams.Builder().build(), Instant.now());
+ doPrepare(session, new PrepareParams.Builder().build());
assertThat(session.getMetaData().toString(), is("n/a, n/a, 0, 0, , 0"));
}
@@ -179,7 +179,7 @@ public class LocalSessionTest {
}
private LocalSession createSession(TenantName tenant, long sessionId, SessionTest.MockSessionPreparer preparer) throws Exception {
- return createSession(tenant, sessionId, preparer, Optional.<AllocatedHosts>empty());
+ return createSession(tenant, sessionId, preparer, Optional.empty());
}
private LocalSession createSession(TenantName tenant, long sessionId, SessionTest.MockSessionPreparer preparer, Optional<AllocatedHosts> allocatedHosts) throws Exception {
@@ -204,16 +204,16 @@ public class LocalSessionTest {
flagSource));
}
- private void doPrepare(LocalSession session, Instant now) {
- doPrepare(session, new PrepareParams.Builder().build(), now);
+ private void doPrepare(LocalSession session) {
+ doPrepare(session, new PrepareParams.Builder().build());
}
- private void doPrepare(LocalSession session, PrepareParams params, Instant now) {
- session.prepare(getLogger(false), params, Optional.empty(), tenantPath, now);
+ private void doPrepare(LocalSession session, PrepareParams params) {
+ session.prepare(getLogger(), params, Optional.empty(), tenantPath, Instant.now());
}
- DeployHandlerLogger getLogger(boolean verbose) {
- return new DeployHandlerLogger(new Slime().get(), verbose,
+ private DeployHandlerLogger getLogger() {
+ return new DeployHandlerLogger(new Slime().get(), false,
new ApplicationId.Builder().tenant("testtenant").applicationName("testapp").build());
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/MockSessionZKClient.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/MockSessionZKClient.java
index e22185ae69b..b0e184398e5 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/MockSessionZKClient.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/MockSessionZKClient.java
@@ -5,9 +5,6 @@ import com.yahoo.config.application.api.ApplicationPackage;
import com.yahoo.config.model.test.MockApplicationPackage;
import com.yahoo.config.provision.AllocatedHosts;
import com.yahoo.config.provision.TenantName;
-import com.yahoo.transaction.Transaction;
-import com.yahoo.path.Path;
-import com.yahoo.vespa.config.server.session.Session.Status;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
import com.yahoo.vespa.curator.Curator;
import com.yahoo.vespa.curator.mock.MockCurator;
@@ -23,7 +20,6 @@ public class MockSessionZKClient extends SessionZooKeeperClient {
private ApplicationPackage app;
private Optional<AllocatedHosts> info = Optional.empty();
- private Status sessionStatus;
public MockSessionZKClient(Curator curator, TenantName tenantName, long sessionId) {
this(curator, tenantName, sessionId, (ApplicationPackage) null);
@@ -34,7 +30,7 @@ public class MockSessionZKClient extends SessionZooKeeperClient {
this.info = allocatedHosts;
}
- public MockSessionZKClient(Curator curator, TenantName tenantName, long sessionId, ApplicationPackage application) {
+ MockSessionZKClient(Curator curator, TenantName tenantName, long sessionId, ApplicationPackage application) {
super(curator, TenantRepository.getSessionsPath(tenantName).append(String.valueOf(sessionId)));
this.app = application;
curator.create(TenantRepository.getSessionsPath(tenantName).append(String.valueOf(sessionId)));
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionRepoTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionRepoTest.java
index 83183a27666..9110671b494 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionRepoTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionRepoTest.java
@@ -49,8 +49,8 @@ public class RemoteSessionRepoTest {
this.remoteSessionRepo = tenant.getRemoteSessionRepo();
curator.create(TenantRepository.getTenantPath(tenantName).append("/applications"));
curator.create(TenantRepository.getSessionsPath(tenantName));
- createSession(1l, false);
- createSession(2l, false);
+ createSession(1L, false);
+ createSession(2L, false);
}
private void createSession(long sessionId, boolean wait) {
@@ -69,14 +69,14 @@ public class RemoteSessionRepoTest {
@Test
public void testInitialize() {
- assertSessionExists(1l);
- assertSessionExists(2l);
+ assertSessionExists(1L);
+ assertSessionExists(2L);
}
@Test
public void testCreateSession() {
- createSession(3l, true);
- assertSessionExists(3l);
+ createSession(3L, true);
+ assertSessionExists(3L);
}
@Test
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionTest.java
index c89c2f23873..99ef1831744 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionTest.java
@@ -62,7 +62,7 @@ public class RemoteSessionTest {
public void require_that_session_is_initialized() {
Clock clock = Clock.systemUTC();
Session session = createSession(2, clock);
- assertThat(session.getSessionId(), is(2l));
+ assertThat(session.getSessionId(), is(2L));
session = createSession(Long.MAX_VALUE, clock);
assertThat(session.getSessionId(), is(Long.MAX_VALUE));
}
@@ -73,14 +73,14 @@ public class RemoteSessionTest {
session.loadPrepared();
ApplicationSet applicationSet = session.ensureApplicationLoaded();
assertNotNull(applicationSet);
- assertThat(applicationSet.getApplicationGeneration(), is(3l));
+ assertThat(applicationSet.getApplicationGeneration(), is(3L));
assertThat(applicationSet.getForVersionOrLatest(Optional.empty(), Instant.now()).getId().application().value(), is("foo"));
assertNotNull(applicationSet.getForVersionOrLatest(Optional.empty(), Instant.now()).getModel());
session.deactivate();
applicationSet = session.ensureApplicationLoaded();
assertNotNull(applicationSet);
- assertThat(applicationSet.getApplicationGeneration(), is(3l));
+ assertThat(applicationSet.getApplicationGeneration(), is(3L));
assertThat(applicationSet.getForVersionOrLatest(Optional.empty(), Instant.now()).getId().application().value(), is("foo"));
assertNotNull(applicationSet.getForVersionOrLatest(Optional.empty(), Instant.now()).getModel());
}
@@ -224,9 +224,7 @@ public class RemoteSessionTest {
.curator(curator)
.clock(clock)
.modelFactoryRegistry(new ModelFactoryRegistry(modelFactories));
- if (permanentApplicationPackage.isPresent())
- registryBuilder.permanentApplicationPackage(permanentApplicationPackage.get());
-
+ permanentApplicationPackage.ifPresent(registryBuilder::permanentApplicationPackage);
return new RemoteSession(tenantName, sessionId, registryBuilder.build(), zkc);
}
@@ -234,12 +232,12 @@ public class RemoteSessionTest {
private class MockModelFactory implements ModelFactory {
/** Throw a RuntimeException on load - this is handled gracefully during model building */
- public boolean throwOnLoad = false;
+ boolean throwOnLoad = false;
/** Throw an Error on load - this is useful to propagate this condition all the way to the test */
- public boolean throwErrorOnLoad = false;
+ boolean throwErrorOnLoad = false;
- public ModelContext modelContext;
+ ModelContext modelContext;
public Version vespaVersion = new Version(1, 2, 3);
/** The validation overrides of this, or null if none */
@@ -247,9 +245,9 @@ public class RemoteSessionTest {
private Clock clock = Clock.fixed(LocalDate.parse("2000-01-01", DateTimeFormatter.ISO_DATE).atStartOfDay().atZone(ZoneOffset.UTC).toInstant(), ZoneOffset.UTC);
- public MockModelFactory() { this(null); }
+ MockModelFactory() { this(null); }
- public MockModelFactory(String validationOverrides) {
+ MockModelFactory(String validationOverrides) {
this.validationOverrides = validationOverrides;
}
@@ -271,7 +269,7 @@ public class RemoteSessionTest {
return loadModel();
}
- public Model loadModel() {
+ Model loadModel() {
try {
ApplicationPackage application = new MockApplicationPackage.Builder().withEmptyHosts().withEmptyServices().withValidationOverrides(validationOverrides).build();
DeployState deployState = new DeployState.Builder().applicationPackage(application).now(clock.instant()).build();
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionPreparerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionPreparerTest.java
index 651dde375ee..e219c516f95 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionPreparerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionPreparerTest.java
@@ -9,14 +9,22 @@ import com.yahoo.config.model.application.provider.BaseDeployLogger;
import com.yahoo.config.model.application.provider.FilesApplicationPackage;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ApplicationName;
+import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.CertificateNotReadyException;
+import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.HostFilter;
+import com.yahoo.config.provision.HostSpec;
import com.yahoo.config.provision.InstanceName;
+import com.yahoo.config.provision.ProvisionLogger;
+import com.yahoo.config.provision.Provisioner;
import com.yahoo.config.provision.Rotation;
import com.yahoo.config.provision.TenantName;
+import com.yahoo.config.provision.exception.LoadBalancerServiceException;
import com.yahoo.io.IOUtils;
import com.yahoo.log.LogLevel;
import com.yahoo.path.Path;
import com.yahoo.slime.Slime;
+import com.yahoo.transaction.NestedTransaction;
import com.yahoo.vespa.config.server.MockReloadHandler;
import com.yahoo.vespa.config.server.MockSecretStore;
import com.yahoo.vespa.config.server.TestComponentRegistry;
@@ -45,6 +53,7 @@ import java.io.File;
import java.io.IOException;
import java.time.Instant;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@@ -294,6 +303,13 @@ public class SessionPreparerTest {
prepare(new File("src/test/resources/deploy/hosted-app"), params);
}
+ @Test(expected = LoadBalancerServiceException.class)
+ public void require_that_conflict_is_returned_when_creating_load_balancer_fails() throws IOException {
+ preparer = createPreparer(HostProvisionerProvider.withProvisioner(new FailWithTransientExceptionProvisioner()));
+ var params = new PrepareParams.Builder().applicationId(applicationId("test")).build();
+ prepare(new File("src/test/resources/deploy/hosted-app"), params);
+ }
+
private void prepare(File app) throws IOException {
prepare(app, new PrepareParams.Builder().build());
}
@@ -327,4 +343,22 @@ public class SessionPreparerTest {
ApplicationName.from(applicationName), InstanceName.defaultName());
}
+ private static class FailWithTransientExceptionProvisioner implements Provisioner {
+
+ @Override
+ public List<HostSpec> prepare(ApplicationId applicationId, ClusterSpec cluster, Capacity capacity, int groups, ProvisionLogger logger) {
+ throw new LoadBalancerServiceException("Unable to create load balancer", new Exception("some internal exception"));
+ }
+
+ @Override
+ public void activate(NestedTransaction transaction, ApplicationId application, Collection<HostSpec> hosts) { }
+
+ @Override
+ public void remove(NestedTransaction transaction, ApplicationId application) { }
+
+ @Override
+ public void restart(ApplicationId application, HostFilter filter) { }
+
+ }
+
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionRepoTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionRepoTest.java
index 01cb90721f3..b8a3d0bc401 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionRepoTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionRepoTest.java
@@ -12,7 +12,6 @@ import static org.junit.Assert.assertThat;
/**
* @author hmusum
- * @since 5.1.14
*/
public class SessionRepoTest {
@Test
@@ -20,7 +19,7 @@ public class SessionRepoTest {
SessionRepo<TestSession> sessionRepo = new SessionRepo<>();
assertNull(sessionRepo.getSession(1L));
sessionRepo.addSession(new TestSession(1));
- assertThat(sessionRepo.getSession(1L).getSessionId(), is(1l));
+ assertThat(sessionRepo.getSession(1L).getSessionId(), is(1L));
}
@Test(expected = IllegalArgumentException.class)
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionZooKeeperClientTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionZooKeeperClientTest.java
index 522a21a47b3..27e04bd7422 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionZooKeeperClientTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionZooKeeperClientTest.java
@@ -92,18 +92,18 @@ public class SessionZooKeeperClientTest {
public void require_that_create_time_can_be_written_and_read() {
SessionZooKeeperClient zkc = createSessionZKClient("3");
curator.delete(Path.fromString("3"));
- assertThat(zkc.readCreateTime(), is(0l));
- zkc.createNewSession(123456l, TimeUnit.SECONDS);
- assertThat(zkc.readCreateTime(), is(123456l));
+ assertThat(zkc.readCreateTime(), is(0L));
+ zkc.createNewSession(123456L, TimeUnit.SECONDS);
+ assertThat(zkc.readCreateTime(), is(123456L));
}
@Test
public void require_that_create_time_has_correct_unit() {
SessionZooKeeperClient zkc = createSessionZKClient("3");
curator.delete(Path.fromString("3"));
- assertThat(zkc.readCreateTime(), is(0l));
+ assertThat(zkc.readCreateTime(), is(0L));
zkc.createNewSession(60, TimeUnit.MINUTES);
- assertThat(zkc.readCreateTime(), is(3600l));
+ assertThat(zkc.readCreateTime(), is(3600L));
}
private void assertApplicationIdParse(String sessionId, String idString, String expectedIdString) {
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandlerTest.java
index 36bb7a926b5..ff3d9449448 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandlerTest.java
@@ -27,7 +27,6 @@ import com.yahoo.vespa.config.server.TestComponentRegistry;
import com.yahoo.vespa.config.server.application.Application;
import com.yahoo.vespa.config.server.application.ApplicationSet;
import com.yahoo.vespa.config.server.deploy.ZooKeeperDeployer;
-import com.yahoo.vespa.config.server.host.HostRegistries;
import com.yahoo.vespa.config.server.model.TestModelFactory;
import com.yahoo.vespa.config.server.modelfactory.ModelFactoryRegistry;
import com.yahoo.vespa.config.server.monitoring.MetricUpdater;
@@ -135,24 +134,24 @@ public class TenantRequestHandlerTest {
new TestModelFactory(new Version(3, 2, 1))));
}
- public <T extends ConfigInstance> T resolve(Class<T> clazz,
- TenantRequestHandler tenantRequestHandler,
- ApplicationId appId,
- Version vespaVersion,
- String configId) {
+ private <T extends ConfigInstance> T resolve(Class<T> clazz,
+ TenantRequestHandler tenantRequestHandler,
+ ApplicationId appId,
+ Version vespaVersion,
+ String configId) {
ConfigResponse response = getConfigResponse(clazz, tenantRequestHandler, appId, vespaVersion, configId);
return ConfigPayload.fromUtf8Array(response.getPayload()).toInstance(clazz, configId);
}
- public <T extends ConfigInstance> ConfigResponse getConfigResponse(Class<T> clazz,
- TenantRequestHandler tenantRequestHandler,
- ApplicationId appId,
- Version vespaVersion,
- String configId) {
+ private <T extends ConfigInstance> ConfigResponse getConfigResponse(Class<T> clazz,
+ TenantRequestHandler tenantRequestHandler,
+ ApplicationId appId,
+ Version vespaVersion,
+ String configId) {
return tenantRequestHandler.resolveConfig(appId, new GetConfigRequest() {
@Override
public ConfigKey<T> getConfigKey() {
- return new ConfigKey<T>(clazz, configId);
+ return new ConfigKey<>(clazz, configId);
}
@Override
@@ -183,7 +182,7 @@ public class TenantRequestHandlerTest {
// Using only payload list for this simple test
SimpletypesConfig config = resolve(SimpletypesConfig.class, server, defaultApp(), vespaVersion, "");
assertThat(config.intval(), is(1337));
- assertThat(server.getApplicationGeneration(applicationId, Optional.of(vespaVersion)), is(1l));
+ assertThat(server.getApplicationGeneration(applicationId, Optional.of(vespaVersion)), is(1L));
server.reloadConfig(reloadConfig(1L));
ConfigResponse configResponse = getConfigResponse(SimpletypesConfig.class, server, defaultApp(), vespaVersion, "");
@@ -191,7 +190,7 @@ public class TenantRequestHandlerTest {
config = resolve(SimpletypesConfig.class, server, defaultApp(), vespaVersion, "");
assertThat(config.intval(), is(1337));
assertThat(listener.reloaded.get(), is(2));
- assertThat(server.getApplicationGeneration(applicationId, Optional.of(vespaVersion)), is(1l));
+ assertThat(server.getApplicationGeneration(applicationId, Optional.of(vespaVersion)), is(1L));
assertThat(listener.tenantHosts.size(), is(1));
assertThat(server.resolveApplicationId("mytesthost"), is(applicationId));
@@ -204,7 +203,7 @@ public class TenantRequestHandlerTest {
config = resolve(SimpletypesConfig.class, server, defaultApp(), vespaVersion,"");
assertThat(config.intval(), is(1330));
assertThat(listener.reloaded.get(), is(1));
- assertThat(server.getApplicationGeneration(applicationId, Optional.of(vespaVersion)), is(2l));
+ assertThat(server.getApplicationGeneration(applicationId, Optional.of(vespaVersion)), is(2L));
}
@Test
@@ -284,7 +283,7 @@ public class TenantRequestHandlerTest {
}
public static class MockReloadListener implements ReloadListener {
- public AtomicInteger reloaded = new AtomicInteger(0);
+ AtomicInteger reloaded = new AtomicInteger(0);
AtomicInteger removed = new AtomicInteger(0);
Map<String, Collection<String>> tenantHosts = new LinkedHashMap<>();
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantTest.java
index e140dae3650..5b8f5299c01 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantTest.java
@@ -6,7 +6,6 @@ import com.yahoo.config.provision.TenantName;
import com.yahoo.vespa.config.server.MockReloadHandler;
import com.yahoo.vespa.config.server.TestComponentRegistry;
import com.yahoo.vespa.config.server.application.TenantApplications;
-import com.yahoo.vespa.curator.mock.MockCurator;
import org.junit.Before;
import org.junit.Test;
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/zookeeper/InitializedCounterTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/zookeeper/InitializedCounterTest.java
index 888cbb7a68b..f745e023126 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/zookeeper/InitializedCounterTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/zookeeper/InitializedCounterTest.java
@@ -20,7 +20,7 @@ public class InitializedCounterTest {
configCurator.createNode("/sessions/2");
InitializedCounter counter = new InitializedCounter(configCurator, "/counter", "/sessions");
- assertThat(counter.counter.get(), is(2l));
+ assertThat(counter.counter.get(), is(2L));
}
}
diff --git a/configserver/src/test/resources/configdefinitions/datastructures.def b/configserver/src/test/resources/configdefinitions/datastructures.def
deleted file mode 100644
index 79b3a8e77a0..00000000000
--- a/configserver/src/test/resources/configdefinitions/datastructures.def
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-namespace=config
-
-date[] string
-
-stock[].ticker string
-stock[].type enum { COMMON, ETF, ETC } default=COMMON
-stock[].volume[] int
diff --git a/configserver/src/test/resources/configdefinitions/function-test.def b/configserver/src/test/resources/configdefinitions/function-test.def
deleted file mode 100644
index b2a27c42285..00000000000
--- a/configserver/src/test/resources/configdefinitions/function-test.def
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#
-# This def file should test most aspects of def files that makes a difference
-# for the autogenerated config classes. The goal is to trigger all blocks of
-# code in the code generators. This includes:
-#
-# - Use all legal special characters in the def file name, to ensure that those
-# that needs to be replaced in type names are actually replaced.
-# - Use the same enum type twice to verify that we dont declare or define it
-# twice.
-# - Use the same struct type twice for the same reason.
-# - Include arrays of primitives and structs.
-# - Include enum primitives and array of enums. Arrays of enums must be handled
-# specially by the C++ code.
-# - Include enums both with and without default values.
-# - Include primitive string, numbers & doubles both with and without default
-# values.
-# - Have an array within a struct, to verify that we correctly recurse.
-# - Reuse type name further within to ensure that this works.
-
-namespace=config
-
-# Some random bool without a default value. These comments exist to check
- # that comment parsing works.
-bool_val bool
- ## A bool with a default value set.
-bool_with_def bool default=false
-int_val int
-int_with_def int default=-545
-double_val double
-double_with_def double default=-6.43
-# Another comment
-string_val string
-stringwithdef string default="foobar"
-enum_val enum { FOO, BAR, FOOBAR }
-enumwithdef enum { FOO2, BAR2, FOOBAR2 } default=BAR2
-refval reference
-refwithdef reference default=":parent:"
-
-boolarr[] bool
-intarr[] int
-doublearr[] double
-stringarr[] string
-enumarr[] enum { ARRAY, VALUES }
-refarr[] reference
-
-
-myarray[].intval int default=14
-myarray[].stringval[] string
-myarray[].enumval enum { INNER, ENUM, TYPE } default=TYPE
-myarray[].refval reference # Value in array without default
-myarray[].anotherarray[].foo int default=-4
-
diff --git a/configserver/src/test/resources/configdefinitions/unicode.def b/configserver/src/test/resources/configdefinitions/unicode.def
deleted file mode 100644
index e3b93671e99..00000000000
--- a/configserver/src/test/resources/configdefinitions/unicode.def
+++ /dev/null
@@ -1,5 +0,0 @@
-# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-namespace=config
-
-unicodestring1 string
-unicodestring2 string default="abc æøå 囲碁 ÆØÅ ABC"
diff --git a/configserver/src/test/resources/metrics/container_metrics b/configserver/src/test/resources/metrics/container_metrics
new file mode 100644
index 00000000000..a6a5828934c
--- /dev/null
+++ b/configserver/src/test/resources/metrics/container_metrics
@@ -0,0 +1,41 @@
+{
+ "services": [
+ {
+ "name":"vespa.container",
+ "timestamp": 1557306075,
+ "metrics": [
+ {
+ "values": {
+ "queries.rate": 23.0,
+ "query_latency.sum": 2000,
+ "document.count": 300000,
+ "feed.rate": 23.0,
+ "write_latency.sum": 2000
+ }
+ },
+ {
+ "values": {
+ "query_latency.count": 43.0,
+ "query_latency.sum": 3000,
+ "feed_latency.count": 43.0,
+ "feed_latency.sum": 3000
+
+ }
+ }
+ ]
+ },
+
+ {
+ "name":"vespa.qrserver",
+ "timestamp": 1557306075,
+ "metrics": [
+ {
+ "values": {
+ "query_latency.count": 43.0,
+ "query_latency.sum": 3000
+ }
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/configserver/src/test/resources/metrics/content_metrics b/configserver/src/test/resources/metrics/content_metrics
new file mode 100644
index 00000000000..c881e574f8b
--- /dev/null
+++ b/configserver/src/test/resources/metrics/content_metrics
@@ -0,0 +1,16 @@
+{
+ "services": [
+ {
+ "name":"vespa.distributor",
+ "timestamp": 1557306075,
+ "metrics": [
+ {
+ "values": {
+ "vds.distributor.docsstored.average": 3000
+
+ }
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/container-dependency-versions/pom.xml b/container-dependency-versions/pom.xml
index 0c27d074871..d18e6eeb13e 100644
--- a/container-dependency-versions/pom.xml
+++ b/container-dependency-versions/pom.xml
@@ -447,7 +447,7 @@
<properties>
<bouncycastle.version>1.58</bouncycastle.version>
- <felix.version>6.0.2</felix.version>
+ <felix.version>6.0.3</felix.version>
<findbugs.version>1.3.9</findbugs.version>
<guava.version>20.0</guava.version>
<guice.version>3.0</guice.version>
diff --git a/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/ComponentGraph.java b/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/ComponentGraph.java
index 4a0598d3436..cdd886b4672 100644
--- a/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/ComponentGraph.java
+++ b/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/ComponentGraph.java
@@ -13,6 +13,8 @@ import com.yahoo.component.ComponentId;
import com.yahoo.component.provider.ComponentRegistry;
import com.yahoo.config.ConfigInstance;
import com.yahoo.container.di.componentgraph.Provider;
+import com.yahoo.container.di.componentgraph.cycle.CycleFinder;
+import com.yahoo.container.di.componentgraph.cycle.Graph;
import com.yahoo.log.LogLevel;
import com.yahoo.vespa.config.ConfigKey;
import net.jcip.annotations.NotThreadSafe;
@@ -372,13 +374,19 @@ public class ComponentGraph {
/**
* The returned list is the nodes from the graph bottom-up.
*
+ * For each iteration, the algorithm finds the components that are not "wanted by" any other component,
+ * and prepends those components into the resulting 'sorted' list. Hence, the first element in the returned
+ * list is the component that is directly or indirectly wanted by "most" other components.
+ *
* @return A list where a earlier than b in the list implies that there is no path from a to b
*/
private static List<Node> topologicalSort(Collection<Node> nodes) {
Map<ComponentId, Integer> numIncoming = new HashMap<>();
nodes.forEach(
- node -> node.usedComponents().forEach(injectedNode -> numIncoming.merge(injectedNode.componentId(), 1, (a, b) -> a + b)));
+ node -> node.usedComponents().forEach(
+ injectedNode -> numIncoming.merge(injectedNode.componentId(), 1, (a, b) -> a + b)));
+
LinkedList<Node> sorted = new LinkedList<>();
List<Node> unsorted = new ArrayList<>(nodes);
@@ -394,7 +402,7 @@ public class ComponentGraph {
});
if (ready.isEmpty()) {
- throw new IllegalStateException("There is a cycle in the component injection graph.");
+ throw new IllegalStateException("There is a cycle in the component injection graph: " + findCycle(notReady));
}
ready.forEach(node -> node.usedComponents()
@@ -404,4 +412,16 @@ public class ComponentGraph {
}
return sorted;
}
+
+ private static List<String> findCycle(List<Node> nodes) {
+ var cyclicGraph = new Graph<String>();
+ for (var node : nodes) {
+ for (var adjacent : node.usedComponents()) {
+ cyclicGraph.edge(node.componentId().stringValue(),
+ adjacent.componentId().stringValue());
+ }
+ }
+ return new CycleFinder<>(cyclicGraph).findCycle();
+ }
+
}
diff --git a/container-di/src/main/java/com/yahoo/container/di/componentgraph/cycle/CycleFinder.java b/container-di/src/main/java/com/yahoo/container/di/componentgraph/cycle/CycleFinder.java
new file mode 100644
index 00000000000..3b29fa0a04f
--- /dev/null
+++ b/container-di/src/main/java/com/yahoo/container/di/componentgraph/cycle/CycleFinder.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+ */
+
+package com.yahoo.container.di.componentgraph.cycle;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+import static com.yahoo.container.di.componentgraph.cycle.CycleFinder.State.BLACK;
+import static com.yahoo.container.di.componentgraph.cycle.CycleFinder.State.GRAY;
+import static com.yahoo.container.di.componentgraph.cycle.CycleFinder.State.WHITE;
+import static com.yahoo.log.LogLevel.DEBUG;
+import static java.util.Collections.singletonList;
+
+
+/**
+ * <p>Applies the
+ * <a href="https://www.geeksforgeeks.org/detect-cycle-direct-graph-using-colors/"> three-color algorithm</a>
+ * to detect a cycle in a directed graph. If there are multiple cycles, this implementation only detects one
+ * of them and does not guarantee that the shortest cycle is found.
+ * </p>
+ *
+ * @author gjoranv
+ */
+public class CycleFinder<T> {
+ private static final Logger log = Logger.getLogger(CycleFinder.class.getName());
+
+ enum State {
+ WHITE, GRAY, BLACK;
+ }
+
+ private final Graph<T> graph;
+
+ private Map<T, State> colors;
+
+ private List<T> cycle;
+
+ public CycleFinder(Graph<T> graph) {
+ this.graph = graph;
+ }
+
+ private void resetState() {
+ cycle = null;
+ colors = new LinkedHashMap<>();
+ graph.getVertices().forEach(v -> colors.put(v, WHITE));
+ }
+
+ /**
+ * Returns a list of vertices constituting a cycle in the graph, or an empty
+ * list if no cycle was found. Only the first encountered cycle is returned.
+ */
+ public List<T> findCycle() {
+ resetState();
+ for (T vertex : graph.getVertices()) {
+ if (colors.get(vertex) == WHITE) {
+ if (visitDepthFirst(vertex, new ArrayList<>(singletonList(vertex)))) {
+ if (cycle == null) throw new IllegalStateException("Null cycle - this should never happen");
+ if (cycle.isEmpty()) throw new IllegalStateException("Empty cycle - this should never happen");
+ log.log(DEBUG, "Cycle detected: " + cycle);
+ return cycle;
+ }
+ }
+ }
+ return new ArrayList<>();
+ }
+
+ private boolean visitDepthFirst(T vertex, List<T> path) {
+ colors.put(vertex, GRAY);
+ log.log(DEBUG, "Vertex start " + vertex + " - colors: " + colors + " - path: " + path);
+ for (T adjacent : graph.getAdjacent(vertex)) {
+ path.add(adjacent);
+ if (colors.get(adjacent) == GRAY) {
+ cycle = removePathIntoCycle(path);
+ return true;
+ }
+ if (colors.get(adjacent) == WHITE && visitDepthFirst(adjacent, path)) {
+ return true;
+ }
+ path.remove(adjacent);
+ }
+ colors.put(vertex, BLACK);
+ log.log(DEBUG, "Vertex end " + vertex + " - colors: " + colors + " - path: " + path);
+ return false;
+ }
+
+ private List<T> removePathIntoCycle(List<T> pathWithCycle) {
+ T cycleStart = pathWithCycle.get(pathWithCycle.size() - 1);
+ return pathWithCycle.stream()
+ .dropWhile(vertex -> ! vertex.equals(cycleStart))
+ .collect(Collectors.toList());
+ }
+
+}
diff --git a/container-di/src/main/java/com/yahoo/container/di/componentgraph/cycle/Graph.java b/container-di/src/main/java/com/yahoo/container/di/componentgraph/cycle/Graph.java
new file mode 100644
index 00000000000..e1b110d51ee
--- /dev/null
+++ b/container-di/src/main/java/com/yahoo/container/di/componentgraph/cycle/Graph.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+ */
+
+package com.yahoo.container.di.componentgraph.cycle;
+
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Class representing a directed graph.
+ *
+ * @author gjoranv
+ */
+public class Graph<T> {
+
+ private final Map<T, LinkedHashSet<T>> adjMap = new LinkedHashMap<>();
+
+ public void edge(T from, T to) {
+ if (from == null || to == null)
+ throw new IllegalArgumentException("Null vertices are not allowed, edge: " + from + "->" + to);
+
+ adjMap.computeIfAbsent(from, k -> new LinkedHashSet<>()).add(to);
+ adjMap.computeIfAbsent(to, k -> new LinkedHashSet<>());
+ }
+
+ Set<T> getVertices() {
+ return adjMap.keySet();
+ }
+
+ /**
+ * Returns the outgoing edges of the given vertex.
+ */
+ Set<T> getAdjacent(T vertex) {
+ return adjMap.get(vertex);
+ }
+
+ private void throwIfMissingVertex(T vertex) {
+ if (! adjMap.containsKey(vertex)) throw new IllegalArgumentException("No such vertex in the graph: " + vertex);
+ }
+}
diff --git a/container-di/src/test/java/com/yahoo/container/di/componentgraph/core/ComponentGraphTest.java b/container-di/src/test/java/com/yahoo/container/di/componentgraph/core/ComponentGraphTest.java
index a5934bcf098..8d323233ef5 100644
--- a/container-di/src/test/java/com/yahoo/container/di/componentgraph/core/ComponentGraphTest.java
+++ b/container-di/src/test/java/com/yahoo/container/di/componentgraph/core/ComponentGraphTest.java
@@ -323,6 +323,7 @@ public class ComponentGraphTest {
fail("Cycle exception expected.");
} catch (Throwable e) {
assertThat(e.getMessage(), containsString("cycle"));
+ assertThat(e.getMessage(), containsString("ComponentCausingCycle"));
}
}
diff --git a/container-di/src/test/java/com/yahoo/container/di/componentgraph/cycle/CycleFinderTest.java b/container-di/src/test/java/com/yahoo/container/di/componentgraph/cycle/CycleFinderTest.java
new file mode 100644
index 00000000000..219aa6b5e8b
--- /dev/null
+++ b/container-di/src/test/java/com/yahoo/container/di/componentgraph/cycle/CycleFinderTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+ */
+
+package com.yahoo.container.di.componentgraph.cycle;
+
+import org.junit.Test;
+
+import static com.yahoo.container.di.componentgraph.cycle.CycleFinderTest.Vertices.A;
+import static com.yahoo.container.di.componentgraph.cycle.CycleFinderTest.Vertices.B;
+import static com.yahoo.container.di.componentgraph.cycle.CycleFinderTest.Vertices.C;
+import static com.yahoo.container.di.componentgraph.cycle.CycleFinderTest.Vertices.D;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.empty;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author gjoranv
+ */
+public class CycleFinderTest {
+
+ enum Vertices {A, B, C, D}
+
+ @Test
+ public void graph_without_cycles_returns_no_cycle() {
+ var graph = new Graph<Vertices>();
+ graph.edge(A, B);
+ graph.edge(B, C);
+ graph.edge(A, C);
+ graph.edge(D, A);
+
+ var cycleFinder = new CycleFinder<>(graph);
+ assertThat(cycleFinder.findCycle(), empty());
+ }
+
+ @Test
+ public void graph_with_cycle_returns_cycle() {
+ var graph = new Graph<Vertices>();
+ graph.edge(A, B);
+ graph.edge(B, C);
+ graph.edge(C, A);
+
+ var cycleFinder = new CycleFinder<>(graph);
+ assertThat(cycleFinder.findCycle(), contains(A, B, C, A));
+ }
+
+ @Test
+ public void graph_with_self_referencing_vertex_returns_cycle() {
+ var graph = new Graph<Vertices>();
+ graph.edge(A, A);
+
+ var cycleFinder = new CycleFinder<>(graph);
+ assertThat(cycleFinder.findCycle(), contains(A, A));
+ }
+
+ @Test
+ public void leading_nodes_are_stripped_from_cycle() {
+ var graph = new Graph<Vertices>();
+ graph.edge(A, B);
+ graph.edge(B, C);
+ graph.edge(C, B);
+
+ var cycleFinder = new CycleFinder<>(graph);
+ assertThat(cycleFinder.findCycle(), contains(B, C, B));
+ }
+
+ @Test
+ public void findCycle_is_idempotent_with_cycle() {
+ var graph = new Graph<Vertices>();
+ graph.edge(A, A);
+
+ var cycleFinder = new CycleFinder<>(graph);
+ assertThat(cycleFinder.findCycle(), contains(A, A));
+ assertThat(cycleFinder.findCycle(), contains(A, A));
+ }
+
+ @Test
+ public void findCycle_is_idempotent_without_cycle() {
+ var graph = new Graph<Vertices>();
+ graph.edge(A, B);
+
+ var cycleFinder = new CycleFinder<>(graph);
+ assertThat(cycleFinder.findCycle(), empty());
+ assertThat(cycleFinder.findCycle(), empty());
+ }
+
+}
diff --git a/container-di/src/test/java/com/yahoo/container/di/componentgraph/cycle/GraphTest.java b/container-di/src/test/java/com/yahoo/container/di/componentgraph/cycle/GraphTest.java
new file mode 100644
index 00000000000..588c1e30ffe
--- /dev/null
+++ b/container-di/src/test/java/com/yahoo/container/di/componentgraph/cycle/GraphTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+ */
+
+package com.yahoo.container.di.componentgraph.cycle;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static com.yahoo.container.di.componentgraph.cycle.GraphTest.Vertices.A;
+import static com.yahoo.container.di.componentgraph.cycle.GraphTest.Vertices.B;
+import static com.yahoo.container.di.componentgraph.cycle.GraphTest.Vertices.C;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.empty;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author gjoranv
+ */
+public class GraphTest {
+
+ enum Vertices {A, B, C}
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void vertices_and_edges_are_added_and_can_be_retrieved() {
+ var graph = new Graph<Vertices>();
+ graph.edge(A, B);
+ graph.edge(B, C);
+ graph.edge(A, C);
+
+ assertThat(graph.getVertices().size(), is(3));
+ assertThat(graph.getAdjacent(A), containsInAnyOrder(B, C));
+ assertThat(graph.getAdjacent(B), containsInAnyOrder(C));
+ assertThat(graph.getAdjacent(C), empty());
+ }
+
+ @Test
+ public void null_vertices_are_not_allowed() {
+ var graph = new Graph<Vertices>();
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Null vertices are not allowed");
+ graph.edge(A, null);
+ }
+
+ @Test
+ public void duplicate_edges_are_ignored() {
+ var graph = new Graph<Vertices>();
+ graph.edge(A, B);
+ graph.edge(A, B);
+
+ assertThat(graph.getAdjacent(A).size(), is(1));
+ }
+
+ @Test
+ public void self_edges_are_allowed() {
+ var graph = new Graph<Vertices>();
+ graph.edge(A, A);
+
+ assertThat(graph.getAdjacent(A), contains(A));
+ }
+
+}
diff --git a/container-search/src/main/java/com/yahoo/search/handler/SearchHandler.java b/container-search/src/main/java/com/yahoo/search/handler/SearchHandler.java
index 8e654bf34b8..45303a3c646 100644
--- a/container-search/src/main/java/com/yahoo/search/handler/SearchHandler.java
+++ b/container-search/src/main/java/com/yahoo/search/handler/SearchHandler.java
@@ -99,6 +99,8 @@ public class SearchHandler extends LoggingRequestHandler {
private final ExecutionFactory executionFactory;
+ private final boolean enableGroupingSessionCache;
+
private final class MeanConnections implements Callback {
@Override
@@ -122,6 +124,7 @@ public class SearchHandler extends LoggingRequestHandler {
ExecutionFactory executionFactory) {
super(executor, accessLog, metric, true);
log.log(LogLevel.DEBUG, "SearchHandler.init " + System.identityHashCode(this));
+ this.enableGroupingSessionCache = queryProfileConfig.enableGroupingSessionCache();
QueryProfileRegistry queryProfileRegistry = QueryProfileConfigurer.createFromConfig(queryProfileConfig);
this.queryProfileRegistry = queryProfileRegistry.compile();
this.executionFactory = executionFactory;
@@ -232,6 +235,9 @@ public class SearchHandler extends LoggingRequestHandler {
CompiledQueryProfile queryProfile = queryProfileRegistry.findQueryProfile(queryProfileName);
Query query = new Query(request, requestMap, queryProfile);
+ if (enableGroupingSessionCache) {
+ query.setGroupingSessionCache(true);
+ }
boolean benchmarking = VespaHeaders.benchmarkOutput(request);
boolean benchmarkCoverage = VespaHeaders.benchmarkCoverage(benchmarking, request.getJDiscRequest().headers());
diff --git a/container-search/src/main/resources/configdefinitions/query-profiles.def b/container-search/src/main/resources/configdefinitions/query-profiles.def
index 20fcbda0d72..11966cae8ce 100644
--- a/container-search/src/main/resources/configdefinitions/query-profiles.def
+++ b/container-search/src/main/resources/configdefinitions/query-profiles.def
@@ -86,7 +86,9 @@ queryprofiletype[].field[].mandatory bool default=false
# A space-separated list of aliases of this field name. Aliases are case insensitive
queryprofiletype[].field[].alias string default=""
-
+# Temporary feature flag for enabling grouping session cache by default
+# TODO Remove me once grouping session cache rollout is complete and cache is enabled by default
+enableGroupingSessionCache bool default=false
diff --git a/controller-api/pom.xml b/controller-api/pom.xml
index 15098a04787..eeb6425bc25 100644
--- a/controller-api/pom.xml
+++ b/controller-api/pom.xml
@@ -70,6 +70,12 @@
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+
</dependencies>
<build>
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/ClusterMetrics.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/ClusterMetrics.java
new file mode 100644
index 00000000000..1377a333335
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/ClusterMetrics.java
@@ -0,0 +1,40 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.application.v4.model;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author olaa
+ */
+public class ClusterMetrics {
+
+ private final String clusterId;
+ private final ClusterType clusterType;
+ private final Map<String, Double> metrics;
+
+ public ClusterMetrics(String clusterId, ClusterType clusterType) {
+ this.clusterId = clusterId;
+ this.clusterType = clusterType;
+ this.metrics = new HashMap<>();
+ }
+
+ public String getClusterId() {
+ return clusterId;
+ }
+
+ public ClusterType getClusterType() {
+ return clusterType;
+ }
+
+ public Map<String, Double> getMetrics() {
+ return Collections.unmodifiableMap(metrics);
+ }
+
+ public void addMetric(String name, double value) {
+ metrics.put(name, value);
+ }
+
+ public enum ClusterType {content, container};
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServer.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServer.java
index 9eae2965c45..688cf275892 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServer.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServer.java
@@ -6,6 +6,7 @@ import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeployOptions;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.EndpointStatus;
+import com.yahoo.vespa.hosted.controller.api.application.v4.model.ClusterMetrics;
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import com.yahoo.vespa.hosted.controller.api.identifiers.Hostname;
import com.yahoo.vespa.hosted.controller.api.integration.certificates.ApplicationCertificate;
@@ -44,6 +45,8 @@ public interface ConfigServer {
InputStream getLogs(DeploymentId deployment, Map<String, String> queryParameters);
+ List<ClusterMetrics> getMetrics(DeploymentId deployment);
+
List<String> getContentClusters(DeploymentId deployment);
/**
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServerException.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServerException.java
index 20599e92aa9..780969206a2 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServerException.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServerException.java
@@ -38,7 +38,8 @@ public class ConfigServerException extends RuntimeException {
REQUEST_TIMEOUT,
UNKNOWN_VESPA_VERSION,
PARENT_HOST_NOT_READY,
- CERTIFICATE_NOT_READY
+ CERTIFICATE_NOT_READY,
+ LOAD_BALANCER_NOT_READY
}
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/LoadBalancer.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/LoadBalancer.java
index 9f686570da1..0ad15e9cabe 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/LoadBalancer.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/LoadBalancer.java
@@ -1,15 +1,12 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.api.integration.configserver;
-import com.google.common.collect.ImmutableSortedSet;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.HostName;
-import com.yahoo.config.provision.RotationName;
import java.util.Objects;
import java.util.Optional;
-import java.util.Set;
/**
* Represents an exclusive load balancer, assigned to an application's cluster.
@@ -23,16 +20,14 @@ public class LoadBalancer {
private final ClusterSpec.Id cluster;
private final HostName hostname;
private final Optional<String> dnsZone;
- private final Set<RotationName> rotations;
public LoadBalancer(String id, ApplicationId application, ClusterSpec.Id cluster, HostName hostname,
- Optional<String> dnsZone, Set<RotationName> rotations) {
+ Optional<String> dnsZone) {
this.id = Objects.requireNonNull(id, "id must be non-null");
this.application = Objects.requireNonNull(application, "application must be non-null");
this.cluster = Objects.requireNonNull(cluster, "cluster must be non-null");
this.hostname = Objects.requireNonNull(hostname, "hostname must be non-null");
this.dnsZone = Objects.requireNonNull(dnsZone, "dnsZone must be non-null");
- this.rotations = ImmutableSortedSet.copyOf(Objects.requireNonNull(rotations, "rotations must be non-null"));
}
public String id() {
@@ -55,8 +50,4 @@ public class LoadBalancer {
return dnsZone;
}
- public Set<RotationName> rotations() {
- return rotations;
- }
-
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/metrics/ConfigServerMetricsService.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/metrics/ConfigServerMetricsService.java
new file mode 100644
index 00000000000..a47e2165291
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/metrics/ConfigServerMetricsService.java
@@ -0,0 +1,68 @@
+package com.yahoo.vespa.hosted.controller.api.integration.metrics;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.HostName;
+import com.yahoo.config.provision.zone.ZoneId;
+import com.yahoo.vespa.hosted.controller.api.application.v4.model.ClusterMetrics;
+import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServer;
+import com.yahoo.vespa.hosted.controller.api.integration.routing.RotationStatus;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+
+/**
+ * Retrieves metrics from the configuration server.
+ *
+ * @author ogronnesby
+ */
+public class ConfigServerMetricsService implements MetricsService {
+ private final ConfigServer configServerClient;
+
+ public ConfigServerMetricsService(ConfigServer configServerClient) {
+ this.configServerClient = configServerClient;
+ }
+
+ @Override
+ public ApplicationMetrics getApplicationMetrics(ApplicationId application) {
+ // TODO(ogronnesby): How to produce these values in Public context?
+ return new ApplicationMetrics(0.0, 0.0);
+ }
+
+ @Override
+ public DeploymentMetrics getDeploymentMetrics(ApplicationId application, ZoneId zone) {
+ var deploymentId = new DeploymentId(application, zone);
+ var metrics = configServerClient.getMetrics(deploymentId);
+
+ // TODO(ogronnesby): We probably want something more intelligent than just using .sum(), but it's better to
+ // TODO(ogronnesby): get some values populated and then fix the formula later.
+
+ // The field names here come from the MetricsResponse class.
+
+ return new DeploymentMetrics(
+ doubleStream(metrics, "queriesPerSecond").mapToDouble(Double::doubleValue).sum(),
+ doubleStream(metrics, "feedPerSecond").mapToDouble(Double::doubleValue).sum(),
+ doubleStream(metrics, "documentCount").mapToLong(Double::longValue).sum(),
+ doubleStream(metrics, "queryLatency").mapToDouble(Double::doubleValue).sum(),
+ doubleStream(metrics, "feedLatency").mapToDouble(Double::doubleValue).sum()
+ );
+ }
+
+ @Override
+ public Map<HostName, RotationStatus> getRotationStatus(String rotationName) {
+ // TODO(ogronnesby): getRotationStatus doesn't really belong in this interface, and global
+ // TODO(ogronnesby): endpoints does not work in public yet.
+ return Map.of();
+ }
+
+ @Override
+ public Map<String, SystemMetrics> getSystemMetrics(ApplicationId application, ZoneId zone) {
+ // TODO(ogronnesby): Need a backing source for this data
+ return Map.of();
+ }
+
+ private Stream<Double> doubleStream(List<ClusterMetrics> metrics, String name) {
+ return metrics.stream().map(m -> m.getMetrics().getOrDefault(name, 0.0));
+ }
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/MetricsService.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/metrics/MetricsService.java
index 32e94625c5a..a6d7c5d0688 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/MetricsService.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/metrics/MetricsService.java
@@ -1,5 +1,5 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.api.integration;
+package com.yahoo.vespa.hosted.controller.api.integration.metrics;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.HostName;
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/metrics/package-info.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/metrics/package-info.java
new file mode 100644
index 00000000000..d14f2e79882
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/metrics/package-info.java
@@ -0,0 +1,5 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+@ExportPackage
+package com.yahoo.vespa.hosted.controller.api.integration.metrics;
+
+import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeHistory.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeHistory.java
index 42e2c97e515..a4691001adc 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeHistory.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeHistory.java
@@ -32,6 +32,6 @@ public class NodeHistory {
return event;
}
- public enum Agent { system, application, operator, NodeRetirer, NodeFailer }
+ public enum Agent { system, application, operator, NodeFailer }
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/Issue.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/Issue.java
index 2731ccc9597..08c913e9085 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/Issue.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/Issue.java
@@ -105,7 +105,8 @@ public class Issue {
public enum Type {
defect, // A defect which needs fixing.
- task // A task the humans must perform.
+ task, // A task the humans must perform.
+ operationalTask // SRE and operational tasks.
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java
index 7f7a6b758d5..e3a2781142a 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java
@@ -1,23 +1,63 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.api.integration.resource;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryNode;
+
import java.time.Instant;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
/**
* @author olaa
*/
public class ResourceSnapshot {
- private final ResourceAllocation resourceAllocation;
+ private final ApplicationId applicationId;
+ private final double cpuCores;
+ private final double memoryGb;
+ private final double diskGb;
private final Instant timestamp;
- public ResourceSnapshot(ResourceAllocation resourceAllocation, Instant timestamp) {
- this.resourceAllocation = resourceAllocation;
+ public ResourceSnapshot(ApplicationId applicationId, double cpuCores, double memoryGb, double diskGb, Instant timestamp) {
+ this.applicationId = applicationId;
+ this.cpuCores = cpuCores;
+ this.memoryGb = memoryGb;
+ this.diskGb = diskGb;
this.timestamp = timestamp;
}
- public ResourceAllocation getResourceAllocation() {
- return resourceAllocation;
+ public static ResourceSnapshot from(List<NodeRepositoryNode> nodes, Instant timestamp) {
+ Set<ApplicationId> applicationIds = nodes.stream()
+ .map(n -> ApplicationId.from(n.getOwner().tenant, n.getOwner().application, n.getOwner().instance))
+ .collect(Collectors.toSet());
+
+ if (applicationIds.size() != 1) throw new IllegalArgumentException("List of nodes can only represent one application");
+
+ return new ResourceSnapshot(
+ applicationIds.iterator().next(),
+ nodes.stream().mapToDouble(NodeRepositoryNode::getMinCpuCores).sum(),
+ nodes.stream().mapToDouble(NodeRepositoryNode::getMinMainMemoryAvailableGb).sum(),
+ nodes.stream().mapToDouble(NodeRepositoryNode::getMinDiskAvailableGb).sum(),
+ timestamp
+ );
+ }
+
+ public ApplicationId getApplicationId() {
+ return applicationId;
+ }
+
+ public double getCpuCores() {
+ return cpuCores;
+ }
+
+ public double getMemoryGb() {
+ return memoryGb;
+ }
+
+ public double getDiskGb() {
+ return diskGb;
}
public Instant getTimestamp() {
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshotConsumer.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshotConsumer.java
index f7f3eddb482..bb6830770e2 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshotConsumer.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshotConsumer.java
@@ -1,9 +1,7 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.api.integration.resource;
-import com.yahoo.config.provision.ApplicationId;
-
-import java.util.Map;
+import java.util.List;
/**
* Consumes a snapshot of resourses allocated/used per application.
@@ -12,5 +10,7 @@ import java.util.Map;
*/
public interface ResourceSnapshotConsumer {
- public void consume(Map<ApplicationId, ResourceSnapshot> resources);
+ public void consume(List<ResourceSnapshot> resources);
+
+ public List<ResourceSnapshot> getResourceSnapshots(String tenantName, String applicationName);
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockResourceSnapshotConsumer.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockResourceSnapshotConsumer.java
index d5d7b63e933..2cd3aef1903 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockResourceSnapshotConsumer.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockResourceSnapshotConsumer.java
@@ -1,25 +1,29 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.api.integration.stubs;
-import com.yahoo.config.provision.ApplicationId;
import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshot;
import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshotConsumer;
-import java.util.Map;
+import java.util.List;
/**
* @author olaa
*/
public class MockResourceSnapshotConsumer implements ResourceSnapshotConsumer {
- private Map<ApplicationId, ResourceSnapshot> resources;
+ private List<ResourceSnapshot> resources;
@Override
- public void consume(Map<ApplicationId, ResourceSnapshot> resources){
+ public void consume(List<ResourceSnapshot> resources){
this.resources = resources;
}
- public Map<ApplicationId, ResourceSnapshot> consumedResources() {
- return resources;
+ @Override
+ public List<ResourceSnapshot> getResourceSnapshots(String tenantName, String applicationName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public List<ResourceSnapshot> consumedResources() {
+ return this.resources;
}
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneRegistry.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneRegistry.java
index db9291cd651..475671181e3 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneRegistry.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneRegistry.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.controller.api.integration.zone;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.AthenzDomain;
import com.yahoo.config.provision.CloudName;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.RegionName;
@@ -10,7 +11,6 @@ import com.yahoo.config.provision.zone.UpgradePolicy;
import com.yahoo.config.provision.zone.ZoneFilter;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.athenz.api.AthenzIdentity;
-import com.yahoo.vespa.athenz.api.AthenzService;
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
@@ -56,6 +56,9 @@ public interface ZoneRegistry {
/** Return the configserver's Athenz service identity */
AthenzIdentity getConfigServerAthenzIdentity(ZoneId zoneId);
+ /** Return the system Athenz domain */
+ AthenzDomain accessControlDomain();
+
/** Returns the Vespa upgrade policy to use for zones in this registry */
UpgradePolicy upgradePolicy();
diff --git a/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/integration/metrics/ConfigServerMetricsServiceTest.java b/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/integration/metrics/ConfigServerMetricsServiceTest.java
new file mode 100644
index 00000000000..9c14bc641c7
--- /dev/null
+++ b/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/integration/metrics/ConfigServerMetricsServiceTest.java
@@ -0,0 +1,81 @@
+package com.yahoo.vespa.hosted.controller.api.integration.metrics;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.zone.ZoneId;
+import com.yahoo.vespa.hosted.controller.api.application.v4.model.ClusterMetrics;
+import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServer;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class ConfigServerMetricsServiceTest {
+
+ private final ApplicationId applicationId = new ApplicationId.Builder()
+ .tenant("foo")
+ .applicationName("bar")
+ .instanceName("default")
+ .build();
+
+ private final ZoneId zoneId = ZoneId.from("prod", "us-west-1");
+
+ private ConfigServer configServer;
+ private ConfigServerMetricsService service;
+
+ @Before
+ public void before() {
+ configServer = Mockito.mock(ConfigServer.class);
+ service = new ConfigServerMetricsService(configServer);
+ }
+
+ @Test
+ public void test_returning_metrics() {
+ //
+ // Wire up the test
+ //
+ var deploymentId = new DeploymentId(applicationId, zoneId);
+
+ var clusterMetrics1 = new ClusterMetrics("niceCluster", ClusterMetrics.ClusterType.container) {{
+ addMetric("queriesPerSecond", 23.0);
+ addMetric("queryLatency", 1337.0);
+ }};
+
+ var clusterMetrics2 = new ClusterMetrics("alsoNiceCluster", ClusterMetrics.ClusterType.container) {{
+ addMetric("queriesPerSecond", 11.0);
+ addMetric("queryLatency", 12.0);
+ }};
+
+ var response = List.of(clusterMetrics1, clusterMetrics2);
+
+ Mockito.when(configServer.getMetrics(deploymentId)).thenReturn(response);
+
+ //
+ // Now we can actually test stuff :(
+ //
+ var deploymentMetrics = service.getDeploymentMetrics(applicationId, zoneId);
+
+ assertEquals(23.0 + 11.0, deploymentMetrics.queriesPerSecond(), 0.001);
+ assertEquals(1337.0 + 12.0, deploymentMetrics.queryLatencyMillis(), 0.001); // TODO: again, this definition of combined latency makes no sense
+ assertEquals(0, deploymentMetrics.documentCount());
+ assertEquals(0.0, deploymentMetrics.writeLatencyMillis(), 0.001);
+ assertEquals(0.0, deploymentMetrics.writesPerSecond(), 0.001);
+ }
+
+ @Test
+ public void test_not_implemented_application_metrics() {
+ var applicationMetrics = service.getApplicationMetrics(applicationId);
+ assertEquals(0.0, applicationMetrics.queryServiceQuality(), 0.001);
+ assertEquals(0.0, applicationMetrics.writeServiceQuality(), 0.001);
+ }
+
+ @Test
+ public void test_not_implemented_metrics() {
+ assertTrue(service.getRotationStatus("foo").isEmpty());
+ assertTrue(service.getSystemMetrics(applicationId, zoneId).isEmpty());
+ }
+}
diff --git a/controller-server/pom.xml b/controller-server/pom.xml
index c6c6acafe15..ae756eae1fb 100644
--- a/controller-server/pom.xml
+++ b/controller-server/pom.xml
@@ -107,6 +107,13 @@
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>configserver-flags</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
<!-- compile -->
<dependency>
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java
index d8b56502fc3..ad29c4b2a0e 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java
@@ -10,7 +10,7 @@ import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.ZoneId;
-import com.yahoo.vespa.hosted.controller.api.integration.MetricsService.ApplicationMetrics;
+import com.yahoo.vespa.hosted.controller.api.integration.metrics.MetricsService.ApplicationMetrics;
import com.yahoo.vespa.hosted.controller.api.integration.certificates.ApplicationCertificate;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion;
import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
index 60fd095eb04..677e9e960e8 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
@@ -13,7 +13,9 @@ import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.athenz.api.AthenzDomain;
+import com.yahoo.vespa.athenz.api.AthenzIdentity;
import com.yahoo.vespa.athenz.api.AthenzPrincipal;
+import com.yahoo.vespa.athenz.api.AthenzService;
import com.yahoo.vespa.athenz.api.AthenzUser;
import com.yahoo.vespa.curator.Lock;
import com.yahoo.vespa.flags.BooleanFlag;
@@ -279,7 +281,7 @@ public class ApplicationController {
/** Deploys an application. If the application does not exist it is created. */
// TODO: Get rid of the options arg
- // TODO jvenstad: Split this, and choose between deployDirectly and deploy in handler, excluding internally built from the latter.
+ // TODO(jvenstad): Split this, and choose between deployDirectly and deploy in handler, excluding internally built from the latter.
public ActivateResult deploy(ApplicationId applicationId, ZoneId zone,
Optional<ApplicationPackage> applicationPackageFromDeployer,
Optional<ApplicationVersion> applicationVersionFromDeployer,
@@ -333,11 +335,12 @@ public class ApplicationController {
validateRun(application.get(), zone, platformVersion, applicationVersion);
}
- // TODO: Remove this when all packages are validated upon submission, as in ApplicationApiHandler.submit(...).
+ // TODO(jvenstad): Remove this when all packages are validated upon submission, as in ApplicationApiHandler.submit(...).
verifyApplicationIdentityConfiguration(applicationId.tenant(), applicationPackage, deployingIdentity);
-
// Assign global rotation
+ // TODO(ogronnesby): Remove feature flag and replace calls to withRotationLegacy with withRotation
+ // TODO(mpolden): Remove all handling of legacy endpoints once withRotationLegacy disappears
if (useMultipleEndpoints.with(FetchVector.Dimension.APPLICATION_ID, application.get().id().serializedForm()).value()) {
application = withRotation(application, zone);
@@ -373,7 +376,7 @@ public class ApplicationController {
if ( ! preferOldestVersion
&& ! application.get().deploymentJobs().deployedInternally()
&& ! zone.environment().isManuallyDeployed())
- // TODO jvenstad: Store only on submissions
+ // TODO(jvenstad): Store only on submissions
storeWithUpdatedConfig(application, applicationPackage);
} // Release application lock while doing the deployment, which is a lengthy task.
@@ -469,7 +472,7 @@ public class ApplicationController {
} finally {
// Even if prepare fails, a load balancer may have been provisioned. Always refresh routing policies so that
// any DNS updates can be propagated as early as possible.
- routingPolicies.refresh(application, zone);
+ routingPolicies.refresh(application, applicationPackage.deploymentSpec(), zone);
}
}
@@ -495,21 +498,16 @@ public class ApplicationController {
}
private List<AssignedRotation> createDefaultGlobalIdRotation(Application application, Rotation rotation) {
- // This is guaranteed by .withRotationLegacy, but add this to make inspections accept the use of .get() below
- assert application.deploymentSpec().globalServiceId().isPresent();
-
- final Set<RegionName> regions = application.deploymentSpec().zones().stream()
- .filter(zone -> zone.environment().isProduction())
- .flatMap(zone -> zone.region().stream())
- .collect(Collectors.toSet());
-
- final var assignment = new AssignedRotation(
+ Set<RegionName> regions = application.deploymentSpec().zones().stream()
+ .filter(zone -> zone.environment().isProduction())
+ .flatMap(zone -> zone.region().stream())
+ .collect(Collectors.toSet());
+ var assignment = new AssignedRotation(
ClusterSpec.Id.from(application.deploymentSpec().globalServiceId().get()),
EndpointId.default_(),
rotation.id(),
regions
);
-
return List.of(assignment);
}
@@ -517,7 +515,7 @@ public class ApplicationController {
private LockedApplication withRotation(LockedApplication application, ZoneId zone) {
if (zone.environment() == Environment.prod) {
try (RotationLock rotationLock = rotationRepository.lock()) {
- final var rotations = rotationRepository.getOrAssignRotations(application.get(), rotationLock);
+ var rotations = rotationRepository.getOrAssignRotations(application.get(), rotationLock);
application = application.with(rotations);
store(application); // store assigned rotation even if deployment fails
registerAssignedRotationCnames(application.get());
@@ -528,16 +526,12 @@ public class ApplicationController {
private void registerAssignedRotationCnames(Application application) {
application.assignedRotations().forEach(assignedRotation -> {
- final var endpoints = application
- .endpointsIn(controller.system(), assignedRotation.endpointId())
- .scope(Endpoint.Scope.global);
-
- final var maybeRotation = rotationRepository.getRotation(assignedRotation.rotationId());
-
+ var endpoints = application.endpointsIn(controller.system(), assignedRotation.endpointId())
+ .scope(Endpoint.Scope.global);
+ var maybeRotation = rotationRepository.getRotation(assignedRotation.rotationId());
maybeRotation.ifPresent(rotation -> {
- endpoints.main().ifPresent(mainEndpoint -> {
- registerCname(mainEndpoint.dnsName(), rotation.name());
- });
+ // For rotations assigned using <endpoints/> syntax, we only register the non-legacy name in DNS.
+ endpoints.main().ifPresent(mainEndpoint -> registerCname(mainEndpoint.dnsName(), rotation.name()));
});
});
}
@@ -545,7 +539,7 @@ public class ApplicationController {
private LockedApplication withApplicationCertificate(LockedApplication application) {
ApplicationId applicationId = application.get().id();
- // TODO: Verify that the application is deploying to a zone where certificate provisioning is enabled
+ // TODO(tokle): Verify that the application is deploying to a zone where certificate provisioning is enabled
boolean provisionCertificate = provisionApplicationCertificate.with(FetchVector.Dimension.APPLICATION_ID, applicationId.serializedForm()).value();
if (provisionCertificate) {
application = application.withApplicationCertificate(
@@ -627,8 +621,7 @@ public class ApplicationController {
.iterator());
}
catch (RuntimeException e) {
- log.log(Level.WARNING, "Failed to get endpoint information for " + deploymentId + ": "
- + Exceptions.toMessageString(e));
+ log.log(Level.WARNING, "Failed to get endpoint information for " + deploymentId, e);
return Collections.emptyList();
}
}
@@ -640,14 +633,14 @@ public class ApplicationController {
.orElse(id.applicationId().instance().isTester()))
throw new NotExistsException("Deployment", id.toString());
- // TODO jvenstad: Swap to use routingPolicies first, when this is ready.
+ // TODO(jvenstad): Swap to use routingPolicies first, when this is ready.
try {
var endpoints = routingGenerator.clusterEndpoints(id);
if ( ! endpoints.isEmpty())
return endpoints;
}
catch (RuntimeException e) {
- log.log(Level.WARNING, "Failed to get endpoint information for " + id + ": " + Exceptions.toMessageString(e));
+ log.log(Level.WARNING, "Failed to get endpoint information for " + id, e);
}
return routingPolicies.get(id).stream()
.filter(policy -> policy.endpointIn(controller.system()).scope() == Endpoint.Scope.zone)
@@ -699,12 +692,12 @@ public class ApplicationController {
applicationStore.removeAll(TesterId.of(id));
application.get().assignedRotations().forEach(assignedRotation -> {
- final var endpoints = application.get().endpointsIn(controller.system(), assignedRotation.endpointId());
+ var endpoints = application.get().endpointsIn(controller.system(), assignedRotation.endpointId());
endpoints.asList().stream()
- .map(Endpoint::dnsName)
- .forEach(name -> {
- controller.nameServiceForwarder().removeRecords(Record.Type.CNAME, RecordName.from(name), Priority.normal);
- });
+ .map(Endpoint::dnsName)
+ .forEach(name -> {
+ controller.nameServiceForwarder().removeRecords(Record.Type.CNAME, RecordName.from(name), Priority.normal);
+ });
});
log.info("Deleted " + application);
@@ -789,7 +782,7 @@ public class ApplicationController {
} catch (NotFoundException ignored) {
// ok; already gone
} finally {
- routingPolicies.refresh(application.get().id(), zone);
+ routingPolicies.refresh(application.get().id(), application.get().deploymentSpec(), zone);
}
return application.withoutDeploymentIn(zone);
}
@@ -861,14 +854,17 @@ public class ApplicationController {
/**
* Verifies that the application can be deployed to the tenant, following these rules:
*
- * 1. If the principal is given, verify that the principal is tenant admin or admin of the tenant domain
- * 2. If the principal is not given, verify that the Athenz domain of the tenant equals Athenz domain given in deployment.xml
+ * 1. Verify that the Athenz service can be launched by the config server
+ * 2. If the principal is given, verify that the principal is tenant admin or admin of the tenant domain
+ * 3. If the principal is not given, verify that the Athenz domain of the tenant equals Athenz domain given in deployment.xml
*
* @param tenantName Tenant where application should be deployed
* @param applicationPackage Application package
* @param deployer Principal initiating the deployment, possibly empty
*/
public void verifyApplicationIdentityConfiguration(TenantName tenantName, ApplicationPackage applicationPackage, Optional<Principal> deployer) {
+ verifyAllowedLaunchAthenzService(applicationPackage.deploymentSpec());
+
applicationPackage.deploymentSpec().athenzDomain().ifPresent(identityDomain -> {
Tenant tenant = controller.tenants().require(tenantName);
deployer.filter(AthenzPrincipal.class::isInstance)
@@ -894,6 +890,25 @@ public class ApplicationController {
});
}
+ /*
+ * Verifies that the configured athenz service (if any) can be launched.
+ */
+ private void verifyAllowedLaunchAthenzService(DeploymentSpec deploymentSpec) {
+ deploymentSpec.athenzDomain().ifPresent(athenzDomain -> {
+ controller.zoneRegistry().zones().reachable().ids()
+ .forEach(zone -> {
+ AthenzIdentity configServerAthenzIdentity = controller.zoneRegistry().getConfigServerAthenzIdentity(zone);
+ deploymentSpec.athenzService(zone.environment(), zone.region())
+ .map(service -> new AthenzService(athenzDomain.value(), service.value()))
+ .ifPresent(service -> {
+ boolean allowedToLaunch = ((AthenzFacade) accessControl).canLaunch(configServerAthenzIdentity, service);
+ if (!allowedToLaunch)
+ throw new IllegalArgumentException("Not allowed to launch Athenz service " + service.getFullName());
+ });
+ });
+ });
+ }
+
/** Returns the latest known version within the given major. */
private Optional<Version> lastCompatibleVersion(int targetMajorVersion) {
return controller.versionStatus().versions().stream()
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java
index 08c95d1ecab..ff27a92de9a 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java
@@ -12,7 +12,7 @@ import com.yahoo.config.provision.zone.ZoneApi;
import com.yahoo.vespa.curator.Lock;
import com.yahoo.vespa.flags.FlagSource;
import com.yahoo.vespa.hosted.controller.api.integration.BuildService;
-import com.yahoo.vespa.hosted.controller.api.integration.MetricsService;
+import com.yahoo.vespa.hosted.controller.api.integration.metrics.MetricsService;
import com.yahoo.vespa.hosted.controller.api.integration.RunDataStore;
import com.yahoo.vespa.hosted.controller.api.integration.certificates.ApplicationCertificateProvider;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServer;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java
index 294dc10d0bd..c7607a7a422 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java
@@ -8,8 +8,8 @@ import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.HostName;
import com.yahoo.vespa.curator.Lock;
-import com.yahoo.vespa.hosted.controller.api.integration.MetricsService;
-import com.yahoo.vespa.hosted.controller.api.integration.MetricsService.ApplicationMetrics;
+import com.yahoo.vespa.hosted.controller.api.integration.metrics.MetricsService;
+import com.yahoo.vespa.hosted.controller.api.integration.metrics.MetricsService.ApplicationMetrics;
import com.yahoo.vespa.hosted.controller.api.integration.certificates.ApplicationCertificate;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
@@ -25,7 +25,6 @@ import com.yahoo.vespa.hosted.controller.application.DeploymentJobs;
import com.yahoo.vespa.hosted.controller.application.DeploymentMetrics;
import com.yahoo.vespa.hosted.controller.application.JobStatus;
import com.yahoo.vespa.hosted.controller.application.RotationStatus;
-import com.yahoo.vespa.hosted.controller.rotation.RotationId;
import java.time.Instant;
import java.util.LinkedHashMap;
@@ -279,7 +278,6 @@ public class LockedApplication {
metrics, pemDeployKey, rotations, rotationStatus, applicationCertificate);
}
-
/** Don't expose non-leaf sub-objects. */
private LockedApplication with(Deployment deployment) {
Map<ZoneId, Deployment> deployments = new LinkedHashMap<>(this.deployments);
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/AssignedRotation.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/AssignedRotation.java
index ec13066d069..e23230b8503 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/AssignedRotation.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/AssignedRotation.java
@@ -72,7 +72,7 @@ public class AssignedRotation {
public static AssignedRotation fromStrings(String clusterId, String endpointId, String rotationId, Collection<String> regions) {
return new AssignedRotation(
new ClusterSpec.Id(clusterId),
- new EndpointId(endpointId),
+ EndpointId.of(endpointId),
new RotationId(rotationId),
regions.stream().map(RegionName::from).collect(Collectors.toSet())
);
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
index 5dccd5c8120..4041c955cc4 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
@@ -3,7 +3,6 @@ package com.yahoo.vespa.hosted.controller.application;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
-import com.yahoo.config.provision.RotationName;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.ZoneId;
@@ -216,7 +215,6 @@ public class Endpoint {
private ZoneId zone;
private ClusterSpec.Id cluster;
- private RotationName rotation;
private EndpointId endpointId;
private Port port;
private boolean legacy = false;
@@ -228,7 +226,7 @@ public class Endpoint {
/** Sets the cluster and zone target of this */
public EndpointBuilder target(ClusterSpec.Id cluster, ZoneId zone) {
- if (rotation != null || endpointId != null) {
+ if (endpointId != null) {
throw new IllegalArgumentException("Cannot set multiple target types");
}
this.cluster = cluster;
@@ -236,18 +234,9 @@ public class Endpoint {
return this;
}
- /** Sets the rotation target of this */
- public EndpointBuilder target(RotationName rotation) {
- if ((cluster != null && zone != null) || endpointId != null) {
- throw new IllegalArgumentException("Cannot set multiple target types");
- }
- this.rotation = rotation;
- return this;
- }
-
/** Sets the endpoint ID as defines in deployments.xml */
public EndpointBuilder named(EndpointId endpointId) {
- if (rotation != null || cluster != null || zone != null) {
+ if (cluster != null || zone != null) {
throw new IllegalArgumentException("Cannot set multiple target types");
}
this.endpointId = endpointId;
@@ -277,8 +266,6 @@ public class Endpoint {
String name;
if (cluster != null && zone != null) {
name = cluster.value();
- } else if (rotation != null) {
- name = rotation.value();
} else if (endpointId != null) {
name = endpointId.id();
} else {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointId.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointId.java
index 13c242c7b5f..7c88b94a2ae 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointId.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointId.java
@@ -1,5 +1,7 @@
package com.yahoo.vespa.hosted.controller.application;
+import org.jetbrains.annotations.NotNull;
+
import java.util.Objects;
/**
@@ -8,12 +10,13 @@ import java.util.Objects;
*
* @author ogronnesby
*/
-public class EndpointId {
+public class EndpointId implements Comparable<EndpointId> {
+
private static final EndpointId DEFAULT = new EndpointId("default");
private final String id;
- public EndpointId(String id) {
+ private EndpointId(String id) {
this.id = requireNotEmpty(id);
}
@@ -50,4 +53,10 @@ public class EndpointId {
public static EndpointId default_() { return DEFAULT; }
public static EndpointId of(String id) { return new EndpointId(id); }
+
+ @Override
+ public int compareTo(@NotNull EndpointId o) {
+ return id.compareTo(o.id);
+ }
+
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java
index d9aea783880..c4613db27d1 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java
@@ -2,7 +2,6 @@
package com.yahoo.vespa.hosted.controller.application;
import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.RotationName;
import com.yahoo.config.provision.SystemName;
import com.yahoo.vespa.hosted.controller.application.Endpoint.Port;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/RoutingId.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/RoutingId.java
index c9378e27b61..7b0ec3d27ba 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/RoutingId.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/RoutingId.java
@@ -2,31 +2,30 @@
package com.yahoo.vespa.hosted.controller.application;
import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.RotationName;
import java.util.Objects;
/**
- * Unique identifier for a global routing table entry (application x rotation name).
+ * Unique identifier for a global routing table entry (application x endpoint ID).
*
* @author mpolden
*/
public class RoutingId {
private final ApplicationId application;
- private final RotationName rotation;
+ private final EndpointId endpointId;
- public RoutingId(ApplicationId application, RotationName rotation) {
+ public RoutingId(ApplicationId application, EndpointId endpointId) {
this.application = Objects.requireNonNull(application, "application must be non-null");
- this.rotation = Objects.requireNonNull(rotation, "rotation must be non-null");
+ this.endpointId = Objects.requireNonNull(endpointId, "endpointId must be non-null");
}
public ApplicationId application() {
return application;
}
- public RotationName rotation() {
- return rotation;
+ public EndpointId endpointId() {
+ return endpointId;
}
@Override
@@ -35,12 +34,12 @@ public class RoutingId {
if (o == null || getClass() != o.getClass()) return false;
RoutingId that = (RoutingId) o;
return application.equals(that.application) &&
- rotation.equals(that.rotation);
+ endpointId.equals(that.endpointId);
}
@Override
public int hashCode() {
- return Objects.hash(application, rotation);
+ return Objects.hash(application, endpointId);
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/RoutingPolicy.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/RoutingPolicy.java
index e0145e6b94c..a86bbaa317e 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/RoutingPolicy.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/RoutingPolicy.java
@@ -5,7 +5,6 @@ import com.google.common.collect.ImmutableSortedSet;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.HostName;
-import com.yahoo.config.provision.RotationName;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.application.Endpoint.Port;
@@ -27,17 +26,17 @@ public class RoutingPolicy {
private final ZoneId zone;
private final HostName canonicalName;
private final Optional<String> dnsZone;
- private final Set<RotationName> rotations;
+ private final Set<EndpointId> endpoints;
/** DO NOT USE. Public for serialization purposes */
public RoutingPolicy(ApplicationId owner, ClusterSpec.Id cluster, ZoneId zone, HostName canonicalName,
- Optional<String> dnsZone, Set<RotationName> rotations) {
+ Optional<String> dnsZone, Set<EndpointId> endpoints) {
this.owner = Objects.requireNonNull(owner, "owner must be non-null");
this.cluster = Objects.requireNonNull(cluster, "cluster must be non-null");
this.zone = Objects.requireNonNull(zone, "zone must be non-null");
this.canonicalName = Objects.requireNonNull(canonicalName, "canonicalName must be non-null");
this.dnsZone = Objects.requireNonNull(dnsZone, "dnsZone must be non-null");
- this.rotations = ImmutableSortedSet.copyOf(Objects.requireNonNull(rotations, "rotations must be non-null"));
+ this.endpoints = ImmutableSortedSet.copyOf(Objects.requireNonNull(endpoints, "endpoints must be non-null"));
}
/** The application owning this */
@@ -65,9 +64,9 @@ public class RoutingPolicy {
return dnsZone;
}
- /** The rotations in this policy */
- public Set<RotationName> rotations() {
- return rotations;
+ /** The endpoints of this policy */
+ public Set<EndpointId> endpoints() {
+ return endpoints;
}
/** Returns the endpoint of this */
@@ -77,7 +76,7 @@ public class RoutingPolicy {
/** Returns rotation endpoints of this */
public EndpointList rotationEndpointsIn(SystemName system) {
- return EndpointList.of(rotations.stream().map(rotation -> endpointOf(owner, rotation, system)));
+ return EndpointList.of(endpoints.stream().map(endpointId -> endpointOf(owner, endpointId, system)));
}
@Override
@@ -95,14 +94,14 @@ public class RoutingPolicy {
@Override
public String toString() {
- return String.format("%s [rotations: %s%s], %s owned by %s, in %s", canonicalName, rotations,
+ return String.format("%s [rotations: %s%s], %s owned by %s, in %s", canonicalName, endpoints,
dnsZone.map(z -> ", DNS zone: " + z).orElse(""), cluster, owner.toShortString(),
zone.value());
}
/** Returns the endpoint of given rotation */
- public static Endpoint endpointOf(ApplicationId application, RotationName rotation, SystemName system) {
- return Endpoint.of(application).target(rotation).on(Port.tls()).directRouting().in(system);
+ public static Endpoint endpointOf(ApplicationId application, EndpointId endpointId, SystemName system) {
+ return Endpoint.of(application).named(endpointId).on(Port.tls()).directRouting().in(system);
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzFacade.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzFacade.java
index 75b7e137998..9257855eb6c 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzFacade.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzFacade.java
@@ -11,6 +11,7 @@ import com.yahoo.vespa.athenz.api.AthenzIdentity;
import com.yahoo.vespa.athenz.api.AthenzPrincipal;
import com.yahoo.vespa.athenz.api.AthenzResourceName;
import com.yahoo.vespa.athenz.api.AthenzRole;
+import com.yahoo.vespa.athenz.api.AthenzService;
import com.yahoo.vespa.athenz.api.OktaAccessToken;
import com.yahoo.vespa.athenz.client.zms.RoleAction;
import com.yahoo.vespa.athenz.client.zms.ZmsClient;
@@ -192,6 +193,10 @@ public class AthenzFacade implements AccessControl {
return hasAccess("modify", service.getDomain().getName() + ":hosted-vespa", identity);
}
+ public boolean canLaunch(AthenzIdentity principal, AthenzService service) {
+ return hasAccess("launch", service.getDomain().getName() + ":service."+service.getName(), principal);
+ }
+
/**
* Used when creating tenancies. As there are no tenancy policies at this point,
* we cannot use {@link #hasTenantAdminAccess(AthenzIdentity, AthenzDomain)}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/AthenzDbMock.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/AthenzDbMock.java
index a11426b9a23..4d9296ea18d 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/AthenzDbMock.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/AthenzDbMock.java
@@ -26,6 +26,10 @@ public class AthenzDbMock {
return this;
}
+ public Domain getOrCreateDomain(AthenzDomain domain) {
+ return domains.computeIfAbsent(domain, Domain::new);
+ }
+
public AthenzDbMock addHostedOperator(AthenzIdentity athenzIdentity) {
hostedOperators.add(athenzIdentity);
return this;
@@ -37,6 +41,7 @@ public class AthenzDbMock {
public final Set<AthenzIdentity> admins = new HashSet<>();
public final Set<AthenzIdentity> tenantAdmins = new HashSet<>();
public final Map<ApplicationId, Application> applications = new HashMap<>();
+ public final Map<String, Service> services = new HashMap<>();
public boolean isVespaTenant = false;
public Domain(AthenzDomain name) {
@@ -78,4 +83,12 @@ public class AthenzDbMock {
}
}
+ public static class Service {
+
+ public final boolean allowLaunch;
+
+ public Service(boolean allowLaunch) {
+ this.allowLaunch = allowLaunch;
+ }
+ }
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/ZmsClientMock.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/ZmsClientMock.java
index 37926d944b7..01f77795c4b 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/ZmsClientMock.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/ZmsClientMock.java
@@ -107,6 +107,12 @@ public class ZmsClientMock implements ZmsClient {
return false;
}
return false;
+ } else if ("launch".equals(action)){
+ AthenzDbMock.Domain domain = getDomainOrThrow(resource.getDomain(), false);
+ String serviceName = resource.getEntityName().replace("service.","");
+ if(!domain.services.containsKey(serviceName)) return false;
+ AthenzDbMock.Service service = domain.services.get(serviceName);
+ return service.allowLaunch;
}
return false;
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java
index ce0e7c0dbab..7ee834844af 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java
@@ -12,7 +12,6 @@ import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.AthenzDomain;
import com.yahoo.config.provision.AthenzService;
import com.yahoo.config.provision.ClusterSpec;
-import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.io.IOUtils;
import com.yahoo.log.LogLevel;
@@ -63,6 +62,7 @@ import static com.yahoo.vespa.hosted.controller.api.integration.configserver.Con
import static com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException.ErrorCode.BAD_REQUEST;
import static com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException.ErrorCode.CERTIFICATE_NOT_READY;
import static com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException.ErrorCode.INVALID_APPLICATION_PACKAGE;
+import static com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException.ErrorCode.LOAD_BALANCER_NOT_READY;
import static com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException.ErrorCode.OUT_OF_CAPACITY;
import static com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException.ErrorCode.PARENT_HOST_NOT_READY;
import static com.yahoo.vespa.hosted.controller.api.integration.configserver.Node.State.active;
@@ -125,7 +125,7 @@ public class InternalStepRunner implements StepRunner {
}
}
catch (UncheckedIOException e) {
- logger.log(INFO, "IO exception running " + id + ": " + Exceptions.toMessageString(e));
+ logger.logWithInternalException(INFO, "IO exception running " + id + ": " + Exceptions.toMessageString(e), e);
return Optional.empty();
}
catch (RuntimeException e) {
@@ -233,7 +233,8 @@ public class InternalStepRunner implements StepRunner {
|| e.getErrorCode() == ACTIVATION_CONFLICT
|| e.getErrorCode() == APPLICATION_LOCK_FAILURE
|| e.getErrorCode() == PARENT_HOST_NOT_READY
- || e.getErrorCode() == CERTIFICATE_NOT_READY) {
+ || e.getErrorCode() == CERTIFICATE_NOT_READY
+ || e.getErrorCode() == LOAD_BALANCER_NOT_READY) {
logger.log("Will retry, because of '" + e.getErrorCode() + "' deploying:\n" + e.getMessage());
return Optional.empty();
}
@@ -382,7 +383,7 @@ public class InternalStepRunner implements StepRunner {
private Optional<RunStatus> startTests(RunId id, DualLogger logger) {
Optional<Deployment> deployment = deployment(id.application(), id.type());
- if ( ! deployment.isPresent()) {
+ if (deployment.isEmpty()) {
logger.log(INFO, "Deployment expired before tests could start.");
return Optional.of(aborted);
}
@@ -587,7 +588,7 @@ public class InternalStepRunner implements StepRunner {
ApplicationVersion version = controller.jobController().run(id).get().versions().targetApplication();
DeploymentSpec spec = controller.applications().require(id.application()).deploymentSpec();
- byte[] servicesXml = servicesXml(controller.system(), testerFlavorFor(id, spec));
+ byte[] servicesXml = servicesXml(controller.zoneRegistry().accessControlDomain(), testerFlavorFor(id, spec));
byte[] testPackage = controller.applications().applicationStore().get(id.tester(), version);
ZoneId zone = id.type().zone(controller.system());
@@ -619,9 +620,7 @@ public class InternalStepRunner implements StepRunner {
}
/** Returns the generated services.xml content for the tester application. */
- static byte[] servicesXml(SystemName systemName, Optional<String> testerFlavor) {
- String domain = systemName == SystemName.main ? "vespa.vespa" : "vespa.vespa.cd";
-
+ static byte[] servicesXml(AthenzDomain domain, Optional<String> testerFlavor) {
String flavor = testerFlavor.orElse("d-1-4-50");
int memoryGb = Integer.parseInt(flavor.split("-")[2]); // Memory available in tester container.
int jdiscMemoryPercentage = (int) Math.ceil(200.0 / memoryGb); // 2Gb memory for tester application (excessive?).
@@ -646,7 +645,7 @@ public class InternalStepRunner implements StepRunner {
" <http>\n" +
" <server id='default' port='4080'/>\n" +
" <filtering>\n" +
- " <access-control domain='" + domain + "'>\n" + // Set up dummy access control to pass validation :/
+ " <access-control domain='" + domain.value() + "'>\n" + // Set up dummy access control to pass validation :/
" <exclude>\n" +
" <binding>http://*/tester/v1/*</binding>\n" +
" </exclude>\n" +
@@ -659,7 +658,7 @@ public class InternalStepRunner implements StepRunner {
" </config>\n" +
" <component id=\"com.yahoo.jdisc.http.filter.security.athenz.StaticRequestResourceMapper\" bundle=\"jdisc-security-filters\">\n" +
" <config name=\"jdisc.http.filter.security.athenz.static-request-resource-mapper\">\n" +
- " <resourceName>" + domain + ":tester-application</resourceName>\n" +
+ " <resourceName>" + domain.value() + ":tester-application</resourceName>\n" +
" <action>deploy</action>\n" +
" </config>\n" +
" </component>\n" +
@@ -711,6 +710,12 @@ public class InternalStepRunner implements StepRunner {
log(level, message, null);
}
+ // Print stack trace in our logs, but don't expose it to end users
+ private void logWithInternalException(Level level, String message, Throwable thrown) {
+ logger.log(level, id + " at " + step + ": " + message, thrown);
+ controller.jobController().log(id, step, level, message);
+ }
+
private void log(Level level, String message, Throwable thrown) {
logger.log(level, id + " at " + step + ": " + message, thrown);
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Versions.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Versions.java
index bef61dda875..068a41ed92c 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Versions.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Versions.java
@@ -131,18 +131,21 @@ public class Versions {
return change.platform().get();
return max(change.platform(), deployment.map(Deployment::version))
- .orElse(application.oldestDeployedPlatform()
- .orElse(defaultVersion));
+ .orElseGet(() -> application.oldestDeployedPlatform().orElse(defaultVersion));
}
private static ApplicationVersion targetApplication(Application application, Change change,
Optional<Deployment> deployment) {
return max(change.application(), deployment.map(Deployment::applicationVersion))
- .orElse(application.oldestDeployedApplication()
- .orElse(application.deploymentJobs().jobStatus().get(JobType.component)
- .lastSuccess()
- .get()
- .application()));
+ .orElseGet(() -> defaultApplicationVersion(application));
+ }
+
+ private static ApplicationVersion defaultApplicationVersion(Application application) {
+ return application.oldestDeployedApplication()
+ .orElseGet(() -> Optional.ofNullable(application.deploymentJobs().jobStatus().get(JobType.component))
+ .flatMap(JobStatus::lastSuccess)
+ .map(JobStatus.JobRun::application)
+ .orElse(ApplicationVersion.unknown));
}
private static <T extends Comparable<T>> Optional<T> max(Optional<T> o1, Optional<T> o2) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterUtilizationMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterUtilizationMaintainer.java
index 7cf710623ea..865084064a2 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterUtilizationMaintainer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterUtilizationMaintainer.java
@@ -7,7 +7,7 @@ import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.Controller;
-import com.yahoo.vespa.hosted.controller.api.integration.MetricsService;
+import com.yahoo.vespa.hosted.controller.api.integration.metrics.MetricsService;
import com.yahoo.vespa.hosted.controller.application.ClusterUtilization;
import com.yahoo.vespa.hosted.controller.application.Deployment;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentExpirer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentExpirer.java
index cd3341ed3a6..d396bfda669 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentExpirer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentExpirer.java
@@ -28,6 +28,7 @@ public class DeploymentExpirer extends Maintainer {
if (!isExpired(deployment)) continue;
try {
+ log.log(Level.INFO, "Expiring deployment of " + application.id() + " in " + deployment.zone());
controller().applications().deactivate(application.id(), deployment.zone());
} catch (Exception e) {
log.log(Level.WARNING, "Could not expire " + deployment + " of " + application +
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainer.java
index 4ad5940f8f2..40c16cbb8c9 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainer.java
@@ -6,7 +6,7 @@ import com.yahoo.config.provision.SystemName;
import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.ApplicationController;
import com.yahoo.vespa.hosted.controller.Controller;
-import com.yahoo.vespa.hosted.controller.api.integration.MetricsService;
+import com.yahoo.vespa.hosted.controller.api.integration.metrics.MetricsService;
import com.yahoo.vespa.hosted.controller.application.Deployment;
import com.yahoo.vespa.hosted.controller.application.DeploymentMetrics;
import com.yahoo.vespa.hosted.controller.application.RotationStatus;
@@ -21,7 +21,6 @@ import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -41,8 +40,7 @@ public class DeploymentMetricsMaintainer extends Maintainer {
private final ApplicationController applications;
public DeploymentMetricsMaintainer(Controller controller, Duration duration, JobControl jobControl) {
- super(controller, duration, jobControl, DeploymentMetricsMaintainer.class.getSimpleName(),
- SystemName.allOf(Predicate.not(SystemName::isPublic)));
+ super(controller, duration, jobControl, DeploymentMetricsMaintainer.class.getSimpleName(), SystemName.all());
this.applications = controller.applications();
}
@@ -79,6 +77,7 @@ public class DeploymentMetricsMaintainer extends Maintainer {
.at(now);
applications.store(locked.with(existingDeployment.zone(), newMetrics)
.recordActivityAt(now, existingDeployment.zone()));
+
});
}
} catch (Exception e) {
@@ -122,5 +121,4 @@ public class DeploymentMetricsMaintainer extends Maintainer {
default: throw new IllegalArgumentException("Unknown API value for rotation status: " + status);
}
}
-
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java
index 5e2ba48da3f..41460cd9fdc 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java
@@ -10,22 +10,17 @@ import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeReposi
import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeOwner;
import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryNode;
import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeState;
-import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceAllocation;
import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshot;
import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshotConsumer;
import java.time.Clock;
import java.time.Duration;
-import java.time.Instant;
import java.util.Collections;
import java.util.List;
-import java.util.Map;
-import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* Creates a ResourceSnapshot per application, which is then passed on to a ResourceSnapshotConsumer
- * TODO: Write JSON blob of node repo somewhere
*
* @author olaa
*/
@@ -36,8 +31,8 @@ public class ResourceMeterMaintainer extends Maintainer {
private final NodeRepository nodeRepository;
private final ResourceSnapshotConsumer resourceSnapshotConsumer;
- private static final String metering_last_reported = "metering_last_reported";
- private static final String metering_total_reported = "metering_total_reported";
+ private static final String METERING_LAST_REPORTED = "metering_last_reported";
+ private static final String METERING_TOTAL_REPORTED = "metering_total_reported";
@SuppressWarnings("WeakerAccess")
public ResourceMeterMaintainer(Controller controller,
@@ -47,7 +42,7 @@ public class ResourceMeterMaintainer extends Maintainer {
Clock clock,
Metric metric,
ResourceSnapshotConsumer resourceSnapshotConsumer) {
- super(controller, interval, jobControl, null, SystemName.allOf(Predicate.not(SystemName::isPublic)));
+ super(controller, interval, jobControl, null, SystemName.all());
this.clock = clock;
this.nodeRepository = nodeRepository;
this.metric = metric;
@@ -57,22 +52,12 @@ public class ResourceMeterMaintainer extends Maintainer {
@Override
protected void maintain() {
List<NodeRepositoryNode> nodes = getNodes();
- Map<ApplicationId, ResourceAllocation> resourceAllocationByApplication = getResourceAllocationByApplication(nodes);
-
- // For now, we're only interested in resource allocation
- Instant timeStamp = clock.instant();
- Map<ApplicationId, ResourceSnapshot> resourceSnapshots = resourceAllocationByApplication.entrySet().stream()
- .collect(Collectors.toMap(
- e -> e.getKey(),
- e -> new ResourceSnapshot(e.getValue(), timeStamp))
- );
-
+ List<ResourceSnapshot> resourceSnapshots = getResourceSnapshots(nodes);
resourceSnapshotConsumer.consume(resourceSnapshots);
- metric.set(metering_last_reported, clock.millis() / 1000, metric.createContext(Collections.emptyMap()));
- metric.set(metering_total_reported, resourceSnapshots.values().stream()
- .map(ResourceSnapshot::getResourceAllocation)
+ metric.set(METERING_LAST_REPORTED, clock.millis() / 1000, metric.createContext(Collections.emptyMap()));
+ metric.set(METERING_TOTAL_REPORTED, resourceSnapshots.stream()
.mapToDouble(r -> r.getCpuCores() + r.getMemoryGb() + r.getDiskGb()) // total metered resource usage, for alerting on drastic changes
.sum()
, metric.createContext(Collections.emptyMap()));
@@ -88,11 +73,12 @@ public class ResourceMeterMaintainer extends Maintainer {
.collect(Collectors.toList());
}
- private Map<ApplicationId, ResourceAllocation> getResourceAllocationByApplication(List<NodeRepositoryNode> nodes) {
+ private List<ResourceSnapshot> getResourceSnapshots(List<NodeRepositoryNode> nodes) {
return nodes.stream()
.collect(Collectors.groupingBy(
node -> applicationIdFromNodeOwner(node.getOwner()),
- Collectors.collectingAndThen(Collectors.toList(), ResourceAllocation::from)));
+ Collectors.collectingAndThen(Collectors.toList(), nodeList -> ResourceSnapshot.from(nodeList, clock.instant()))
+ )).values().stream().collect(Collectors.toList());
}
private ApplicationId applicationIdFromNodeOwner(NodeOwner owner) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RoutingPolicies.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RoutingPolicies.java
index 4a98cb49227..020203c6548 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RoutingPolicies.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RoutingPolicies.java
@@ -1,6 +1,7 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.maintenance;
+import com.yahoo.config.application.api.DeploymentSpec;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.curator.Lock;
@@ -12,6 +13,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.dns.Record;
import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordData;
import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordName;
import com.yahoo.vespa.hosted.controller.application.Endpoint;
+import com.yahoo.vespa.hosted.controller.application.EndpointId;
import com.yahoo.vespa.hosted.controller.application.RoutingId;
import com.yahoo.vespa.hosted.controller.application.RoutingPolicy;
import com.yahoo.vespa.hosted.controller.dns.NameServiceQueue.Priority;
@@ -70,14 +72,13 @@ public class RoutingPolicies {
* Refresh routing policies for application in given zone. This is idempotent and changes will only be performed if
* load balancers for given application have changed.
*/
- public void refresh(ApplicationId application, ZoneId zone) {
- // TODO: Use this to decide how apply routing policies for shared routing layer
+ public void refresh(ApplicationId application, DeploymentSpec deploymentSpec, ZoneId zone) {
if (!controller.zoneRegistry().zones().directlyRouted().ids().contains(zone)) return;
var lbs = new LoadBalancers(application, zone, controller.applications().configServer()
.getLoadBalancers(application, zone));
try (var lock = db.lockRoutingPolicies()) {
- removeObsoleteEndpointsFromDns(lbs, lock);
- storePoliciesOf(lbs, lock);
+ removeObsoleteEndpointsFromDns(lbs, deploymentSpec, lock);
+ storePoliciesOf(lbs, deploymentSpec, lock);
removeObsoletePolicies(lbs, lock);
registerEndpointsInDns(lbs, lock);
}
@@ -89,7 +90,7 @@ public class RoutingPolicies {
// Create DNS record for each routing ID
for (Map.Entry<RoutingId, List<RoutingPolicy>> routeEntry : routingTable.entrySet()) {
- Endpoint endpoint = RoutingPolicy.endpointOf(routeEntry.getKey().application(), routeEntry.getKey().rotation(),
+ Endpoint endpoint = RoutingPolicy.endpointOf(routeEntry.getKey().application(), routeEntry.getKey().endpointId(),
controller.system());
Set<AliasTarget> targets = routeEntry.getValue()
.stream()
@@ -103,10 +104,10 @@ public class RoutingPolicies {
}
/** Store routing policies for given route */
- private void storePoliciesOf(LoadBalancers loadBalancers, @SuppressWarnings("unused") Lock lock) {
+ private void storePoliciesOf(LoadBalancers loadBalancers, DeploymentSpec spec, @SuppressWarnings("unused") Lock lock) {
Set<RoutingPolicy> policies = new LinkedHashSet<>(get(loadBalancers.application));
for (LoadBalancer loadBalancer : loadBalancers.list) {
- RoutingPolicy policy = createPolicy(loadBalancers.application, loadBalancers.zone, loadBalancer);
+ RoutingPolicy policy = createPolicy(loadBalancers.application, spec, loadBalancers.zone, loadBalancer);
if (!policies.add(policy)) {
policies.remove(policy);
policies.add(policy);
@@ -116,12 +117,14 @@ public class RoutingPolicies {
}
/** Create a policy for given load balancer and register a CNAME for it */
- private RoutingPolicy createPolicy(ApplicationId application, ZoneId zone, LoadBalancer loadBalancer) {
- RoutingPolicy routingPolicy = new RoutingPolicy(application, loadBalancer.cluster(), zone,
- loadBalancer.hostname(), loadBalancer.dnsZone(),
- loadBalancer.rotations());
- RecordName name = RecordName.from(routingPolicy.endpointIn(controller.system()).dnsName());
- RecordData data = RecordData.fqdn(loadBalancer.hostname().value());
+ private RoutingPolicy createPolicy(ApplicationId application, DeploymentSpec deploymentSpec, ZoneId zone,
+ LoadBalancer loadBalancer) {
+ var endpoints = endpointIdsOf(loadBalancer, zone, deploymentSpec);
+ var routingPolicy = new RoutingPolicy(application, loadBalancer.cluster(), zone,
+ loadBalancer.hostname(), loadBalancer.dnsZone(),
+ endpoints);
+ var name = RecordName.from(routingPolicy.endpointIn(controller.system()).dnsName());
+ var data = RecordData.fqdn(loadBalancer.hostname().value());
controller.nameServiceForwarder().createCname(name, data, Priority.normal);
return routingPolicy;
}
@@ -145,23 +148,23 @@ public class RoutingPolicies {
}
/** Remove unreferenced global endpoints for given route from DNS */
- private void removeObsoleteEndpointsFromDns(LoadBalancers loadBalancers, @SuppressWarnings("unused") Lock lock) {
+ private void removeObsoleteEndpointsFromDns(LoadBalancers loadBalancers, DeploymentSpec deploymentSpec, @SuppressWarnings("unused") Lock lock) {
var zonePolicies = get(loadBalancers.application, loadBalancers.zone);
var removalCandidates = routingTableFrom(zonePolicies).keySet();
- var activeRoutingIds = routingIdsFrom(loadBalancers.list);
+ var activeRoutingIds = routingIdsFrom(loadBalancers, deploymentSpec);
removalCandidates.removeAll(activeRoutingIds);
for (var id : removalCandidates) {
- Endpoint endpoint = RoutingPolicy.endpointOf(id.application(), id.rotation(), controller.system());
+ Endpoint endpoint = RoutingPolicy.endpointOf(id.application(), id.endpointId(), controller.system());
controller.nameServiceForwarder().removeRecords(Record.Type.ALIAS, RecordName.from(endpoint.dnsName()), Priority.normal);
}
}
/** Compute routing IDs from given load balancers */
- private static Set<RoutingId> routingIdsFrom(List<LoadBalancer> loadBalancers) {
+ private static Set<RoutingId> routingIdsFrom(LoadBalancers loadBalancers, DeploymentSpec spec) {
Set<RoutingId> routingIds = new LinkedHashSet<>();
- for (var loadBalancer : loadBalancers) {
- for (var rotation : loadBalancer.rotations()) {
- routingIds.add(new RoutingId(loadBalancer.application(), rotation));
+ for (var loadBalancer : loadBalancers.list) {
+ for (var endpointId : endpointIdsOf(loadBalancer, loadBalancers.zone, spec)) {
+ routingIds.add(new RoutingId(loadBalancer.application(), endpointId));
}
}
return Collections.unmodifiableSet(routingIds);
@@ -171,7 +174,7 @@ public class RoutingPolicies {
private static Map<RoutingId, List<RoutingPolicy>> routingTableFrom(Set<RoutingPolicy> routingPolicies) {
var routingTable = new LinkedHashMap<RoutingId, List<RoutingPolicy>>();
for (var policy : routingPolicies) {
- for (var rotation : policy.rotations()) {
+ for (var rotation : policy.endpoints()) {
var id = new RoutingId(policy.owner(), rotation);
routingTable.putIfAbsent(id, new ArrayList<>());
routingTable.get(id).add(policy);
@@ -180,6 +183,16 @@ public class RoutingPolicies {
return routingTable;
}
+ /** Compute all endpoint IDs of given load balancer */
+ private static Set<EndpointId> endpointIdsOf(LoadBalancer loadBalancer, ZoneId zone, DeploymentSpec spec) {
+ return spec.endpoints().stream()
+ .filter(endpoint -> endpoint.containerId().equals(loadBalancer.cluster().value()))
+ .filter(endpoint -> endpoint.regions().contains(zone.region()))
+ .map(com.yahoo.config.application.api.Endpoint::endpointId)
+ .map(EndpointId::of)
+ .collect(Collectors.toSet());
+ }
+
/** Load balancers for a particular deployment */
private static class LoadBalancers {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java
index 0c045eb7253..b9581d63583 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java
@@ -14,7 +14,7 @@ import com.yahoo.slime.ObjectTraverser;
import com.yahoo.slime.Slime;
import com.yahoo.vespa.config.SlimeUtils;
import com.yahoo.vespa.hosted.controller.Application;
-import com.yahoo.vespa.hosted.controller.api.integration.MetricsService.ApplicationMetrics;
+import com.yahoo.vespa.hosted.controller.api.integration.metrics.MetricsService.ApplicationMetrics;
import com.yahoo.vespa.hosted.controller.api.integration.certificates.ApplicationCertificate;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
@@ -42,7 +42,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -110,6 +109,7 @@ public class ApplicationSerializer {
private final String lastWrittenField = "lastWritten";
private final String lastQueriesPerSecondField = "lastQueriesPerSecond";
private final String lastWritesPerSecondField = "lastWritesPerSecond";
+ private final String clusterMetricsField = "clusterMetrics";
// DeploymentJobs fields
private final String projectIdField = "projectId";
@@ -555,27 +555,6 @@ public class ApplicationSerializer {
private List<AssignedRotation> assignedRotationsFromSlime(DeploymentSpec deploymentSpec, Inspector root) {
final var assignedRotations = new LinkedHashMap<EndpointId, AssignedRotation>();
- // Add the legacy rotation field to the set - this needs to be first
- // TODO: Remove when we retire the rotations field
- final var legacyRotation = legacyRotationFromSlime(root.field(deprecatedRotationField));
- if (legacyRotation.isPresent() && deploymentSpec.globalServiceId().isPresent()) {
- final var clusterId = new ClusterSpec.Id(deploymentSpec.globalServiceId().get());
- final var regions = deploymentSpec.zones().stream().flatMap(zone -> zone.region().stream()).collect(Collectors.toSet());
- assignedRotations.putIfAbsent(EndpointId.default_(), new AssignedRotation(clusterId, EndpointId.default_(), legacyRotation.get(), regions));
- }
-
- // Now add the same entries from "stupid" list of rotations
- // TODO: Remove when we retire the rotations field
- final var rotations = rotationListFromSlime(root.field(rotationsField));
- for (var rotation : rotations) {
- final var regions = deploymentSpec.zones().stream().flatMap(zone -> zone.region().stream()).collect(Collectors.toSet());
- if (deploymentSpec.globalServiceId().isPresent()) {
- final var clusterId = new ClusterSpec.Id(deploymentSpec.globalServiceId().get());
- assignedRotations.putIfAbsent(EndpointId.default_(), new AssignedRotation(clusterId, EndpointId.default_(), rotation, regions));
- }
- }
-
- // Last - add the actual entries we want. Do _not_ remove this during clean-up
root.field(assignedRotationsField).traverse((ArrayTraverser) (idx, inspector) -> {
final var clusterId = new ClusterSpec.Id(inspector.field(assignedRotationClusterField).asString());
final var endpointId = EndpointId.of(inspector.field(assignedRotationEndpointField).asString());
@@ -590,22 +569,6 @@ public class ApplicationSerializer {
return List.copyOf(assignedRotations.values());
}
- private List<RotationId> rotationListFromSlime(Inspector field) {
- final var rotations = new ArrayList<RotationId>();
-
- field.traverse((ArrayTraverser) (idx, inspector) -> {
- final var rotation = new RotationId(inspector.asString());
- rotations.add(rotation);
- });
-
- return rotations;
- }
-
- // TODO: Remove after June 2019 once the 'rotation' field is gone from storage
- private Optional<RotationId> legacyRotationFromSlime(Inspector field) {
- return field.valid() ? optionalString(field).map(RotationId::new) : Optional.empty();
- }
-
private OptionalLong optionalLong(Inspector field) {
return field.valid() ? OptionalLong.of(field.asLong()) : OptionalLong.empty();
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java
index d704d701cf0..b7b64b9cda2 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java
@@ -106,10 +106,6 @@ public class CuratorDb {
CuratorDb(Curator curator, Duration tryLockTimeout) {
this.curator = curator;
this.tryLockTimeout = tryLockTimeout;
-
- // TODO: Remove after 7.60
- curator.delete(root.append("openStackServerPool"));
- curator.delete(root.append("vespaServerPool"));
}
/** Returns all hosts configured to be part of this ZooKeeper cluster */
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializer.java
index 9cfce8dc16a..80858e713c2 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializer.java
@@ -4,11 +4,10 @@ package com.yahoo.vespa.hosted.controller.persistence;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.HostName;
-import com.yahoo.config.provision.RotationName;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.slime.ArrayTraverser;
-import com.yahoo.slime.Cursor;
import com.yahoo.slime.Slime;
+import com.yahoo.vespa.hosted.controller.application.EndpointId;
import com.yahoo.vespa.hosted.controller.application.RoutingPolicy;
import java.util.Collections;
@@ -39,36 +38,36 @@ public class RoutingPolicySerializer {
private static final String rotationsField = "rotations";
public Slime toSlime(Set<RoutingPolicy> routingPolicies) {
- Slime slime = new Slime();
- Cursor root = slime.setObject();
- Cursor policyArray = root.setArray(routingPoliciesField);
+ var slime = new Slime();
+ var root = slime.setObject();
+ var policyArray = root.setArray(routingPoliciesField);
routingPolicies.forEach(policy -> {
- Cursor policyObject = policyArray.addObject();
+ var policyObject = policyArray.addObject();
policyObject.setString(clusterField, policy.cluster().value());
policyObject.setString(zoneField, policy.zone().value());
policyObject.setString(canonicalNameField, policy.canonicalName().value());
policy.dnsZone().ifPresent(dnsZone -> policyObject.setString(dnsZoneField, dnsZone));
- Cursor rotationArray = policyObject.setArray(rotationsField);
- policy.rotations().forEach(rotation -> {
- rotationArray.addString(rotation.value());
+ var rotationArray = policyObject.setArray(rotationsField);
+ policy.endpoints().forEach(endpointId -> {
+ rotationArray.addString(endpointId.id());
});
});
return slime;
}
public Set<RoutingPolicy> fromSlime(ApplicationId owner, Slime slime) {
- Set<RoutingPolicy> policies = new LinkedHashSet<>();
- Cursor root = slime.get();
- Cursor field = root.field(routingPoliciesField);
+ var policies = new LinkedHashSet<RoutingPolicy>();
+ var root = slime.get();
+ var field = root.field(routingPoliciesField);
field.traverse((ArrayTraverser) (i, inspect) -> {
- Set<RotationName> rotations = new LinkedHashSet<>();
- inspect.field(rotationsField).traverse((ArrayTraverser) (j, rotation) -> rotations.add(RotationName.from(rotation.asString())));
+ var endpointIds = new LinkedHashSet<EndpointId>();
+ inspect.field(rotationsField).traverse((ArrayTraverser) (j, endpointId) -> endpointIds.add(EndpointId.of(endpointId.asString())));
policies.add(new RoutingPolicy(owner,
ClusterSpec.Id.from(inspect.field(clusterField).asString()),
ZoneId.from(inspect.field(zoneField).asString()),
HostName.from(inspect.field(canonicalNameField).asString()),
Serializers.optionalField(inspect.field(dnsZoneField), Function.identity()),
- rotations));
+ endpointIds));
});
return Collections.unmodifiableSet(policies);
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/flags/AuditedFlagsHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/flags/AuditedFlagsHandler.java
new file mode 100644
index 00000000000..31058a71816
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/flags/AuditedFlagsHandler.java
@@ -0,0 +1,30 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.restapi.flags;
+
+import com.yahoo.container.jdisc.HttpRequest;
+import com.yahoo.container.jdisc.HttpResponse;
+import com.yahoo.vespa.configserver.flags.FlagsDb;
+import com.yahoo.vespa.configserver.flags.http.FlagsHandler;
+import com.yahoo.vespa.hosted.controller.Controller;
+import com.yahoo.vespa.hosted.controller.auditlog.AuditLogger;
+
+/**
+ * An extension of {@link FlagsHandler} which logs requests to the audit log.
+ *
+ * @author mpolden
+ */
+public class AuditedFlagsHandler extends FlagsHandler {
+
+ private final AuditLogger auditLogger;
+
+ public AuditedFlagsHandler(Context context, Controller controller, FlagsDb flagsDb) {
+ super(context, flagsDb);
+ auditLogger = controller.auditLogger();
+ }
+
+ @Override
+ public HttpResponse handle(HttpRequest request) {
+ return super.handle(auditLogger.log(request));
+ }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VersionStatus.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VersionStatus.java
index ab5fd2714e5..82838c8de32 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VersionStatus.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VersionStatus.java
@@ -7,7 +7,6 @@ import com.yahoo.component.Version;
import com.yahoo.component.Vtag;
import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.zone.ZoneApi;
-import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.log.LogLevel;
import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.Controller;
@@ -168,7 +167,7 @@ public class VersionStatus {
boolean configConverged = application.configConvergedIn(zone.getId(), controller, Optional.empty());
if (!configConverged) {
- log.log(LogLevel.WARNING, "Config for " + application.id() + " in " + zone + " has not converged");
+ log.log(LogLevel.WARNING, "Config for " + application.id() + " in " + zone.getId() + " has not converged");
}
for (Node node : eligibleForUpgradeApplicationNodes) {
// Only use current node version if config has converged
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java
index d5935c752d9..40936dd8a68 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java
@@ -248,8 +248,7 @@ public final class ControllerTester {
public AthenzDomain createDomainWithAdmin(String domainName, AthenzUser user) {
AthenzDomain domain = new AthenzDomain(domainName);
- athenzDb.addDomain(new AthenzDbMock.Domain(domain));
- athenzDb.domains.get(domain).admin(user);
+ athenzDb.getOrCreateDomain(domain).admin(user);
return domain;
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
index f5047a82e2f..bf798d2f004 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
@@ -3,7 +3,6 @@ package com.yahoo.vespa.hosted.controller.application;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
-import com.yahoo.config.provision.RotationName;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.application.Endpoint.Port;
@@ -23,51 +22,51 @@ public class EndpointTest {
@Test
public void test_global_endpoints() {
- RotationName rotation = RotationName.from("default"); // Always default for non-direct routing
+ EndpointId endpointId = EndpointId.default_();
Map<String, Endpoint> tests = Map.of(
// Legacy endpoint
"http://a1.t1.global.vespa.yahooapis.com:4080/",
- Endpoint.of(app1).target(rotation).on(Port.plain(4080)).legacy().in(SystemName.main),
+ Endpoint.of(app1).named(endpointId).on(Port.plain(4080)).legacy().in(SystemName.main),
// Legacy endpoint with TLS
"https://a1--t1.global.vespa.yahooapis.com:4443/",
- Endpoint.of(app1).target(rotation).on(Port.tls(4443)).legacy().in(SystemName.main),
+ Endpoint.of(app1).named(endpointId).on(Port.tls(4443)).legacy().in(SystemName.main),
// Main endpoint
"https://a1--t1.global.vespa.oath.cloud:4443/",
- Endpoint.of(app1).target(rotation).on(Port.tls(4443)).in(SystemName.main),
+ Endpoint.of(app1).named(endpointId).on(Port.tls(4443)).in(SystemName.main),
// Main endpoint in CD
"https://cd--a1--t1.global.vespa.oath.cloud:4443/",
- Endpoint.of(app1).target(rotation).on(Port.tls(4443)).in(SystemName.cd),
+ Endpoint.of(app1).named(endpointId).on(Port.tls(4443)).in(SystemName.cd),
// Main endpoint with direct routing and default TLS port
"https://a1.t1.global.vespa.oath.cloud/",
- Endpoint.of(app1).target(rotation).on(Port.tls()).directRouting().in(SystemName.main),
+ Endpoint.of(app1).named(endpointId).on(Port.tls()).directRouting().in(SystemName.main),
// Main endpoint with custom rotation name
"https://r1.a1.t1.global.vespa.oath.cloud/",
- Endpoint.of(app1).target(RotationName.from("r1")).on(Port.tls()).directRouting().in(SystemName.main),
+ Endpoint.of(app1).named(EndpointId.of("r1")).on(Port.tls()).directRouting().in(SystemName.main),
// Main endpoint for custom instance in default rotation
"https://a2.t2.global.vespa.oath.cloud/",
- Endpoint.of(app2).target(rotation).on(Port.tls()).directRouting().in(SystemName.main),
+ Endpoint.of(app2).named(endpointId).on(Port.tls()).directRouting().in(SystemName.main),
// Main endpoint for custom instance with custom rotation name
"https://r2.a2.t2.global.vespa.oath.cloud/",
- Endpoint.of(app2).target(RotationName.from("r2")).on(Port.tls()).directRouting().in(SystemName.main),
+ Endpoint.of(app2).named(EndpointId.of("r2")).on(Port.tls()).directRouting().in(SystemName.main),
// Main endpoint in public system
"https://a1.t1.global.public.vespa.oath.cloud/",
- Endpoint.of(app1).target(rotation).on(Port.tls()).directRouting().in(SystemName.Public)
+ Endpoint.of(app1).named(endpointId).on(Port.tls()).directRouting().in(SystemName.Public)
);
tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString()));
}
@Test
public void test_global_endpoints_with_endpoint_id() {
- final var endpointId = EndpointId.default_();
+ var endpointId = EndpointId.default_();
Map<String, Endpoint> tests = Map.of(
// Legacy endpoint
@@ -111,9 +110,9 @@ public class EndpointTest {
@Test
public void test_zone_endpoints() {
- ClusterSpec.Id cluster = ClusterSpec.Id.from("default"); // Always default for non-direct routing
- ZoneId prodZone = ZoneId.from("prod", "us-north-1");
- ZoneId testZone = ZoneId.from("test", "us-north-2");
+ var cluster = ClusterSpec.Id.from("default"); // Always default for non-direct routing
+ var prodZone = ZoneId.from("prod", "us-north-1");
+ var testZone = ZoneId.from("test", "us-north-2");
Map<String, Endpoint> tests = Map.of(
// Legacy endpoint (always contains environment)
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalDeploymentTester.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalDeploymentTester.java
index a992ce1e3de..46e991a135a 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalDeploymentTester.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalDeploymentTester.java
@@ -19,6 +19,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockTesterCloud;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion;
+import com.yahoo.vespa.hosted.controller.athenz.mock.AthenzDbMock;
import com.yahoo.vespa.hosted.controller.integration.ConfigServerMock;
import com.yahoo.vespa.hosted.controller.integration.RoutingGeneratorMock;
import com.yahoo.vespa.hosted.controller.maintenance.JobControl;
@@ -34,12 +35,16 @@ import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.aborted;
import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.unfinished;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
public class InternalDeploymentTester {
+ private static final String ATHENZ_DOMAIN = "domain";
+ private static final String ATHENZ_SERVICE = "service";
+
public static final ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
- .athenzIdentity(AthenzDomain.from("domain"), AthenzService.from("service"))
+ .athenzIdentity(AthenzDomain.from(ATHENZ_DOMAIN), AthenzService.from(ATHENZ_SERVICE))
.upgradePolicy("default")
.region("us-central-1")
.parallel("us-west-1", "us-east-3")
@@ -83,6 +88,10 @@ public class InternalDeploymentTester {
Logger.getLogger(InternalStepRunner.class.getName()).setLevel(LogLevel.DEBUG);
Logger.getLogger("").setLevel(LogLevel.DEBUG);
tester.controllerTester().configureDefaultLogHandler(handler -> handler.setLevel(LogLevel.DEBUG));
+
+ // Mock Athenz domain to allow launch of service
+ AthenzDbMock.Domain domain = tester.controllerTester().athenzDb().getOrCreateDomain(new com.yahoo.vespa.athenz.api.AthenzDomain(ATHENZ_DOMAIN));
+ domain.services.put(ATHENZ_SERVICE, new AthenzDbMock.Service(true));
}
/**
@@ -171,7 +180,7 @@ public class InternalDeploymentTester {
.findAny()
.orElseThrow(() -> new AssertionError(type + " is not among the active: " + jobs.active()));
assertFalse(run.hasFailed());
- assertFalse(run.status() == aborted);
+ assertNotSame(aborted, run.status());
ZoneId zone = type.zone(tester.controller().system());
DeploymentId deployment = new DeploymentId(appId, zone);
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java
index 095651df033..f8157680cfd 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java
@@ -3,15 +3,12 @@ package com.yahoo.vespa.hosted.controller.deployment;
import com.yahoo.component.Version;
import com.yahoo.config.application.api.DeploymentSpec;
-import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.AthenzDomain;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.HostName;
-import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.slime.ArrayTraverser;
import com.yahoo.slime.Inspector;
-import com.yahoo.slime.JsonFormat;
-import com.yahoo.slime.ObjectTraverser;
import com.yahoo.vespa.config.SlimeUtils;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.configserverbindings.ConfigChangeActions;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.configserverbindings.RefeedAction;
@@ -28,7 +25,6 @@ import com.yahoo.vespa.hosted.controller.application.RoutingPolicy;
import org.junit.Before;
import org.junit.Test;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
@@ -38,7 +34,6 @@ import java.nio.file.Paths;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Executors;
@@ -404,7 +399,7 @@ public class InternalStepRunnerTest {
@Test
public void generates_correct_services_xml_test() {
- assertFile("test_runner_services.xml-cd", new String(InternalStepRunner.servicesXml(SystemName.cd, Optional.of("d-2-12-75"))));
+ assertFile("test_runner_services.xml-cd", new String(InternalStepRunner.servicesXml(AthenzDomain.from("vespa.vespa.cd"), Optional.of("d-2-12-75"))));
}
private void assertFile(String resourceName, String actualContent) {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java
index a89c5988396..fbc7bf20a24 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java
@@ -11,6 +11,7 @@ import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeployOptions;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.EndpointStatus;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.configserverbindings.ConfigChangeActions;
+import com.yahoo.vespa.hosted.controller.api.application.v4.model.ClusterMetrics;
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import com.yahoo.vespa.hosted.controller.api.identifiers.Hostname;
import com.yahoo.vespa.hosted.controller.api.identifiers.Identifier;
@@ -330,6 +331,11 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer
return applicationView;
}
+ @Override
+ public List<ClusterMetrics> getMetrics(DeploymentId deployment) {
+ return List.of();
+ }
+
// Returns a canned example response
@Override
public Map<?,?> getServiceApiResponse(String tenantName, String applicationName, String instanceName,
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/MetricsServiceMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/MetricsServiceMock.java
index ce2f74e3389..d925f7a33db 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/MetricsServiceMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/MetricsServiceMock.java
@@ -4,7 +4,7 @@ package com.yahoo.vespa.hosted.controller.integration;
import com.yahoo.component.AbstractComponent;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.HostName;
-import com.yahoo.vespa.hosted.controller.api.integration.MetricsService;
+import com.yahoo.vespa.hosted.controller.api.integration.metrics.MetricsService;
import com.yahoo.vespa.hosted.controller.api.integration.routing.RotationStatus;
import com.yahoo.config.provision.zone.ZoneId;
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java
index f802a6af549..cff0f8da463 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java
@@ -6,6 +6,7 @@ import com.google.inject.Inject;
import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.component.AbstractComponent;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.AthenzDomain;
import com.yahoo.config.provision.CloudName;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.RegionName;
@@ -27,7 +28,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.stream.Collectors;
/**
* @author mpolden
@@ -108,6 +108,11 @@ public class ZoneRegistryMock extends AbstractComponent implements ZoneRegistry
}
@Override
+ public AthenzDomain accessControlDomain() {
+ return AthenzDomain.from("vespadomain");
+ }
+
+ @Override
public UpgradePolicy upgradePolicy() {
return upgradePolicy;
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainerTest.java
index 540efcba3b8..ad4511e7f11 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainerTest.java
@@ -1,10 +1,8 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.maintenance;
-import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.ControllerTester;
import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryNode;
import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshot;
import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockResourceSnapshotConsumer;
import com.yahoo.vespa.hosted.controller.integration.NodeRepositoryClientMock;
@@ -13,10 +11,7 @@ import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock;
import org.junit.Test;
import java.time.Duration;
-import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
-import java.util.Map;
import static org.junit.Assert.*;
@@ -41,20 +36,20 @@ public class ResourceMeterMaintainerTest {
ResourceMeterMaintainer resourceMeterMaintainer = new ResourceMeterMaintainer(tester.controller(), Duration.ofMinutes(5), new JobControl(tester.curator()), nodeRepository, tester.clock(), metrics, snapshotConsumer);
resourceMeterMaintainer.maintain();
- Map<ApplicationId, ResourceSnapshot> consumedResources = snapshotConsumer.consumedResources();
+ List<ResourceSnapshot> consumedResources = snapshotConsumer.consumedResources();
+ // The mocked repository contains two applications, so we should also consume two ResourceSnapshots
assertEquals(2, consumedResources.size());
+ ResourceSnapshot app1 = consumedResources.stream().filter(snapshot -> snapshot.getApplicationId().equals(ApplicationId.from("tenant1", "app1", "default"))).findFirst().orElseThrow();
+ ResourceSnapshot app2 = consumedResources.stream().filter(snapshot -> snapshot.getApplicationId().equals(ApplicationId.from("tenant2", "app2", "default"))).findFirst().orElseThrow();
- ResourceSnapshot app1 = consumedResources.get(ApplicationId.from("tenant1", "app1", "default"));
- ResourceSnapshot app2 = consumedResources.get(ApplicationId.from("tenant2", "app2", "default"));
+ assertEquals(24, app1.getCpuCores(), DELTA);
+ assertEquals(24, app1.getMemoryGb(), DELTA);
+ assertEquals(500, app1.getDiskGb(), DELTA);
- assertEquals(24, app1.getResourceAllocation().getCpuCores(), DELTA);
- assertEquals(24, app1.getResourceAllocation().getMemoryGb(), DELTA);
- assertEquals(500, app1.getResourceAllocation().getDiskGb(), DELTA);
-
- assertEquals(40, app2.getResourceAllocation().getCpuCores(), DELTA);
- assertEquals(24, app2.getResourceAllocation().getMemoryGb(), DELTA);
- assertEquals(500, app2.getResourceAllocation().getDiskGb(), DELTA);
+ assertEquals(40, app2.getCpuCores(), DELTA);
+ assertEquals(24, app2.getMemoryGb(), DELTA);
+ assertEquals(500, app2.getDiskGb(), DELTA);
assertEquals(tester.clock().millis()/1000, metrics.getMetric("metering_last_reported"));
assertEquals(1112.0d, (Double) metrics.getMetric("metering_total_reported"), DELTA);
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/RoutingPoliciesTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/RoutingPoliciesTest.java
index f0344cb8d12..e6387ee3e0c 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/RoutingPoliciesTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/RoutingPoliciesTest.java
@@ -3,19 +3,18 @@ package com.yahoo.vespa.hosted.controller.maintenance;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
-import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.HostName;
-import com.yahoo.config.provision.RotationName;
-import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.LoadBalancer;
import com.yahoo.vespa.hosted.controller.api.integration.dns.Record;
+import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordData;
import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordName;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.RoutingPolicy;
import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
+import com.yahoo.vespa.hosted.controller.deployment.BuildJob;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester;
import org.junit.Test;
@@ -26,7 +25,6 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
-import java.util.function.Supplier;
import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
@@ -54,59 +52,88 @@ public class RoutingPoliciesTest {
@Test
public void maintains_global_routing_policies() {
- int buildNumber = 42;
+ long buildNumber = BuildJob.defaultBuildNumber;
int clustersPerZone = 2;
- // Cluster 0 is member of 2 global rotations
- Map<Integer, Set<RotationName>> rotations = Map.of(0, Set.of(RotationName.from("r0"), RotationName.from("r1")));
- provisionLoadBalancers(clustersPerZone, rotations, app1.id(), zone1, zone2);
+ int numberOfDeployments = 2;
+ var applicationPackage = new ApplicationPackageBuilder()
+ .region(zone1.region())
+ .region(zone2.region())
+ .endpoint("r0", "c0")
+ .endpoint("r1", "c0", "us-west-1")
+ .endpoint("r2", "c1")
+ .build();
+ provisionLoadBalancers(clustersPerZone, app1.id(), zone1, zone2);
- // Creates alias records for cluster0
+ // Creates alias records
tester.deployCompletely(app1, applicationPackage, ++buildNumber);
- Supplier<List<Record>> records1 = () -> tester.controllerTester().nameService().findRecords(Record.Type.ALIAS, RecordName.from("r0.app1.tenant1.global.vespa.oath.cloud"));
- Supplier<List<Record>> records2 = () -> tester.controllerTester().nameService().findRecords(Record.Type.ALIAS, RecordName.from("r1.app1.tenant1.global.vespa.oath.cloud"));
- assertEquals(2, records1.get().size());
- assertEquals(records1.get().size(), records2.get().size());
- assertEquals("lb-0--tenant1:app1:default--prod.us-central-1/dns-zone-1/prod.us-central-1", records1.get().get(0).data().asString());
- assertEquals("lb-0--tenant1:app1:default--prod.us-west-1/dns-zone-1/prod.us-west-1", records1.get().get(1).data().asString());
- assertEquals("lb-0--tenant1:app1:default--prod.us-central-1/dns-zone-1/prod.us-central-1", records2.get().get(0).data().asString());
- assertEquals("lb-0--tenant1:app1:default--prod.us-west-1/dns-zone-1/prod.us-west-1", records2.get().get(1).data().asString());
- assertEquals(2, tester.controller().applications().routingPolicies().get(app1.id()).iterator().next()
- .rotationEndpointsIn(SystemName.main).asList().size());
+ var endpoint1 = "r0.app1.tenant1.global.vespa.oath.cloud";
+ var endpoint2 = "r1.app1.tenant1.global.vespa.oath.cloud";
+ var endpoint3 = "r2.app1.tenant1.global.vespa.oath.cloud";
+
+ assertEquals(endpoint1 + " points to c0 in all regions",
+ List.of("lb-0--tenant1:app1:default--prod.us-central-1/dns-zone-1/prod.us-central-1",
+ "lb-0--tenant1:app1:default--prod.us-west-1/dns-zone-1/prod.us-west-1"),
+ aliasDataOf(endpoint1));
+ assertEquals(endpoint2 + " points to c0 us-west-1",
+ List.of("lb-0--tenant1:app1:default--prod.us-west-1/dns-zone-1/prod.us-west-1"),
+ aliasDataOf(endpoint2));
+ assertEquals(endpoint3 + " points to c1 in all regions",
+ List.of("lb-1--tenant1:app1:default--prod.us-central-1/dns-zone-1/prod.us-central-1",
+ "lb-1--tenant1:app1:default--prod.us-west-1/dns-zone-1/prod.us-west-1"),
+ aliasDataOf(endpoint3));
+ assertEquals("Routing policy count is equal to cluster count",
+ numberOfDeployments * clustersPerZone,
+ tester.controller().applications().routingPolicies().get(app1.id()).size());
// Applications gains a new deployment
- ApplicationPackage updatedApplicationPackage = new ApplicationPackageBuilder()
- .environment(Environment.prod)
+ ApplicationPackage applicationPackage2 = new ApplicationPackageBuilder()
+ .region(zone1.region())
+ .region(zone2.region())
+ .region(zone3.region())
+ .endpoint("r0", "c0")
+ .endpoint("r1", "c0", "us-west-1")
+ .endpoint("r2", "c1")
+ .build();
+ numberOfDeployments++;
+ provisionLoadBalancers(clustersPerZone, app1.id(), zone3);
+ tester.deployCompletely(app1, applicationPackage2, ++buildNumber);
+
+ // Endpoint is updated to contain cluster in new deployment
+ assertEquals(endpoint1 + " points to c0 in all regions",
+ List.of("lb-0--tenant1:app1:default--prod.us-central-1/dns-zone-1/prod.us-central-1",
+ "lb-0--tenant1:app1:default--prod.us-east-3/dns-zone-1/prod.us-east-3",
+ "lb-0--tenant1:app1:default--prod.us-west-1/dns-zone-1/prod.us-west-1"),
+ aliasDataOf(endpoint1));
+
+ // Another application is deployed with a single cluster and global endpoint
+ var endpoint4 = "r0.app2.tenant1.global.vespa.oath.cloud";
+ provisionLoadBalancers(1, app2.id(), zone1, zone2);
+ var applicationPackage3 = new ApplicationPackageBuilder()
+ .region(zone1.region())
+ .region(zone2.region())
+ .endpoint("r0", "c0")
+ .build();
+ tester.deployCompletely(app2, applicationPackage3);
+ assertEquals(endpoint4 + " points to c0 in all regions",
+ List.of("lb-0--tenant1:app2:default--prod.us-central-1/dns-zone-1/prod.us-central-1",
+ "lb-0--tenant1:app2:default--prod.us-west-1/dns-zone-1/prod.us-west-1"),
+ aliasDataOf(endpoint4));
+
+ // All endpoints for app1 are removed
+ ApplicationPackage applicationPackage4 = new ApplicationPackageBuilder()
.region(zone1.region())
.region(zone2.region())
.region(zone3.region())
.build();
- int numberOfDeployments = 3;
- provisionLoadBalancers(clustersPerZone, rotations, app1.id(), zone3);
- tester.deployCompletely(app1, updatedApplicationPackage, ++buildNumber);
-
- // Cluster in new deployment is added to the rotation
- assertEquals(numberOfDeployments, records1.get().size());
- assertEquals("lb-0--tenant1:app1:default--prod.us-central-1/dns-zone-1/prod.us-central-1", records1.get().get(0).data().asString());
- assertEquals("lb-0--tenant1:app1:default--prod.us-east-3/dns-zone-1/prod.us-east-3", records1.get().get(1).data().asString());
- assertEquals("lb-0--tenant1:app1:default--prod.us-west-1/dns-zone-1/prod.us-west-1", records1.get().get(2).data().asString());
-
- // Another application is deployed
- Supplier<List<Record>> records3 = () -> tester.controllerTester().nameService().findRecords(Record.Type.ALIAS, RecordName.from("r0.app2.tenant1.global.vespa.oath.cloud"));
- provisionLoadBalancers(1, Map.of(0, Set.of(RotationName.from("r0"))), app2.id(), zone1, zone2);
- tester.deployCompletely(app2, applicationPackage);
- assertEquals(2, records3.get().size());
- assertEquals("lb-0--tenant1:app2:default--prod.us-central-1/dns-zone-1/prod.us-central-1", records3.get().get(0).data().asString());
- assertEquals("lb-0--tenant1:app2:default--prod.us-west-1/dns-zone-1/prod.us-west-1", records3.get().get(1).data().asString());
-
- // All rotations for app1 are removed
- provisionLoadBalancers(clustersPerZone, Map.of(), app1.id(), zone1, zone2, zone3);
- tester.deployCompletely(app1, updatedApplicationPackage, ++buildNumber);
- assertEquals(List.of(), records1.get());
+ tester.deployCompletely(app1, applicationPackage4, ++buildNumber);
+ assertEquals("DNS records are removed", List.of(), aliasDataOf(endpoint1));
+ assertEquals("DNS records are removed", List.of(), aliasDataOf(endpoint2));
+ assertEquals("DNS records are removed", List.of(), aliasDataOf(endpoint3));
Set<RoutingPolicy> policies = tester.controller().curator().readRoutingPolicies(app1.id());
assertEquals(clustersPerZone * numberOfDeployments, policies.size());
assertTrue("Rotation membership is removed from all policies",
- policies.stream().allMatch(policy -> policy.rotations().isEmpty()));
- assertEquals("Rotations for " + app2 + " are not removed", 2, records3.get().size());
+ policies.stream().allMatch(policy -> policy.endpoints().isEmpty()));
+ assertEquals("Rotations for " + app2 + " are not removed", 2, aliasDataOf(endpoint4).size());
}
@Test
@@ -222,30 +249,30 @@ public class RoutingPoliciesTest {
.collect(Collectors.toSet());
}
- private void provisionLoadBalancers(int clustersPerZone, Map<Integer, Set<RotationName>> clusterRotations, ApplicationId application, ZoneId... zones) {
- for (ZoneId zone : zones) {
- tester.configServer().removeLoadBalancers(application, zone);
- tester.configServer().addLoadBalancers(zone, createLoadBalancers(zone, application, clustersPerZone, clusterRotations));
- }
+ private List<String> aliasDataOf(String name) {
+ return tester.controllerTester().nameService().findRecords(Record.Type.ALIAS, RecordName.from(name)).stream()
+ .map(Record::data)
+ .map(RecordData::asString)
+ .collect(Collectors.toList());
}
private void provisionLoadBalancers(int clustersPerZone, ApplicationId application, ZoneId... zones) {
- provisionLoadBalancers(clustersPerZone, Map.of(), application, zones);
+ for (ZoneId zone : zones) {
+ tester.configServer().removeLoadBalancers(application, zone);
+ tester.configServer().addLoadBalancers(zone, createLoadBalancers(zone, application, clustersPerZone));
+ }
}
- private static List<LoadBalancer> createLoadBalancers(ZoneId zone, ApplicationId application, int count,
- Map<Integer, Set<RotationName>> clusterRotations) {
+ private static List<LoadBalancer> createLoadBalancers(ZoneId zone, ApplicationId application, int count) {
List<LoadBalancer> loadBalancers = new ArrayList<>();
for (int i = 0; i < count; i++) {
- Set<RotationName> rotations = clusterRotations.getOrDefault(i, Collections.emptySet());
loadBalancers.add(
new LoadBalancer("LB-" + i + "-Z-" + zone.value(),
application,
ClusterSpec.Id.from("c" + i),
HostName.from("lb-" + i + "--" + application.serializedForm() +
"--" + zone.value()),
- Optional.of("dns-zone-1"),
- rotations));
+ Optional.of("dns-zone-1")));
}
return loadBalancers;
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java
index 7b39b0d53a4..fb701746d77 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java
@@ -10,7 +10,7 @@ import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.RegionName;
import com.yahoo.vespa.config.SlimeUtils;
import com.yahoo.vespa.hosted.controller.Application;
-import com.yahoo.vespa.hosted.controller.api.integration.MetricsService;
+import com.yahoo.vespa.hosted.controller.api.integration.metrics.MetricsService;
import com.yahoo.vespa.hosted.controller.api.integration.certificates.ApplicationCertificate;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
@@ -253,9 +253,8 @@ public class ApplicationSerializerTest {
// ok if no error
}
- /** TODO: Test can be removed after June 2019 - once legacy field for single rotation is retired */
@Test
- public void testParsingLegacyRotationElement() throws IOException {
+ public void testParsingAssignedRotations() throws IOException {
// Use the 'complete-application.json' as a baseline
final var applicationJson = Files.readAllBytes(testData.resolve("complete-application.json"));
final var slime = SlimeUtils.jsonToSlime(applicationJson);
@@ -267,12 +266,6 @@ public class ApplicationSerializerTest {
// Add the necessary fields to the Slime representation of the application
final var cursor = slime.get();
- cursor.setString("rotation", "single-rotation");
-
- final var rotations = cursor.setArray("endpoints");
- rotations.addString("multiple-rotation-1");
- rotations.addString("multiple-rotation-2");
-
final var assignedRotations = cursor.setArray("assignedRotations");
final var assignedRotation = assignedRotations.addObject();
assignedRotation.setString("clusterId", "foobar");
@@ -282,51 +275,23 @@ public class ApplicationSerializerTest {
// Parse and test the output from parsing contains both legacy rotation and multiple rotations
final var application = applicationSerializer.fromSlime(slime);
- // Since only one AssignedEndpoint can be "default", we make sure that we are ignoring the
- // multiple-rotation entries as the globalServiceId will override them
assertEquals(
List.of(
- new RotationId("single-rotation"),
new RotationId("assigned-rotation")
),
application.rotations()
);
assertEquals(
- Optional.of(new RotationId("single-rotation")), application.legacyRotation()
+ Optional.of(new RotationId("assigned-rotation")), application.legacyRotation()
);
- // The same goes here for AssignedRotations with "default" EndpointId as in the .rotations() test above.
- // Note that we are only using Set.of() on "assigned-rotation" because in this test we do not have access
- // to a deployment.xml that describes the zones a rotation should map to.
assertEquals(
List.of(
- new AssignedRotation(new ClusterSpec.Id("foo"), EndpointId.of("default"), new RotationId("single-rotation"), regions),
new AssignedRotation(new ClusterSpec.Id("foobar"), EndpointId.of("nice-endpoint"), new RotationId("assigned-rotation"), Set.of())
),
application.assignedRotations()
);
}
- @Test
- public void testParsingOnlyLegacyRotationElement() throws IOException {
- // Use the 'complete-application.json' as a baseline
- final var applicationJson = Files.readAllBytes(testData.resolve("complete-application.json"));
- final var slime = SlimeUtils.jsonToSlime(applicationJson);
-
- // Add the necessary fields to the Slime representation of the application
- final var cursor = slime.get();
-
- cursor.setString("rotation", "single-rotation");
-
- // Parse and test the output from parsing contains both legacy rotation and multiple rotations
- final var application = applicationSerializer.fromSlime(slime);
-
- assertEquals(
- List.of(
- new RotationId("single-rotation")
- ),
- application.rotations()
- );
- }
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java
index a0e95bd0393..e99cc302ffe 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java
@@ -5,8 +5,8 @@ import com.google.common.collect.ImmutableSet;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.HostName;
-import com.yahoo.config.provision.RotationName;
import com.yahoo.config.provision.zone.ZoneId;
+import com.yahoo.vespa.hosted.controller.application.EndpointId;
import com.yahoo.vespa.hosted.controller.application.RoutingPolicy;
import org.junit.Test;
@@ -26,19 +26,19 @@ public class RoutingPolicySerializerTest {
@Test
public void test_serialization() {
var owner = ApplicationId.defaultId();
- var rotations = Set.of(RotationName.from("r1"), RotationName.from("r2"));
+ var endpoints = Set.of(EndpointId.of("r1"), EndpointId.of("r2"));
var policies = ImmutableSet.of(new RoutingPolicy(owner,
ClusterSpec.Id.from("my-cluster1"),
ZoneId.from("prod", "us-north-1"),
HostName.from("long-and-ugly-name"),
Optional.of("zone1"),
- rotations),
+ endpoints),
new RoutingPolicy(owner,
ClusterSpec.Id.from("my-cluster2"),
ZoneId.from("prod", "us-north-2"),
HostName.from("long-and-ugly-name-2"),
Optional.empty(),
- rotations));
+ endpoints));
var serialized = serializer.fromSlime(owner, serializer.toSlime(policies));
assertEquals(policies.size(), serialized.size());
for (Iterator<RoutingPolicy> it1 = policies.iterator(), it2 = serialized.iterator(); it1.hasNext();) {
@@ -49,7 +49,7 @@ public class RoutingPolicySerializerTest {
assertEquals(expected.zone(), actual.zone());
assertEquals(expected.canonicalName(), actual.canonicalName());
assertEquals(expected.dnsZone(), actual.dnsZone());
- assertEquals(expected.rotations(), actual.rotations());
+ assertEquals(expected.endpoints(), actual.endpoints());
}
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerControllerTester.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerControllerTester.java
index 427428a3a94..54fe575a365 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerControllerTester.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerControllerTester.java
@@ -31,7 +31,6 @@ import com.yahoo.vespa.hosted.controller.integration.ArtifactRepositoryMock;
import com.yahoo.vespa.hosted.controller.maintenance.JobControl;
import com.yahoo.vespa.hosted.controller.maintenance.Upgrader;
import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
-import com.yahoo.vespa.hosted.controller.persistence.MockCuratorDb;
import com.yahoo.vespa.hosted.controller.security.AthenzCredentials;
import com.yahoo.vespa.hosted.controller.security.AthenzTenantSpec;
@@ -159,10 +158,9 @@ public class ContainerControllerTester {
AthenzClientFactoryMock mock = (AthenzClientFactoryMock) containerTester.container().components()
.getComponent(AthenzClientFactoryMock.class.getName());
AthenzDomain athensDomain = new AthenzDomain(domainName);
- AthenzDbMock.Domain domain = new AthenzDbMock.Domain(athensDomain);
+ AthenzDbMock.Domain domain = mock.getSetup().getOrCreateDomain(athensDomain);
domain.markAsVespaTenant();
domain.admin(new AthenzUser(userName));
- mock.getSetup().addDomain(domain);
return athensDomain;
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerTester.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerTester.java
index c7be543dd00..b32cbbcb926 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerTester.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerTester.java
@@ -7,7 +7,6 @@ import com.yahoo.application.container.handler.Response;
import com.yahoo.component.ComponentSpecification;
import com.yahoo.component.Version;
import com.yahoo.config.provision.zone.ZoneApi;
-import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.container.http.filter.FilterChainRepository;
import com.yahoo.jdisc.http.filter.SecurityRequestFilter;
import com.yahoo.jdisc.http.filter.SecurityRequestFilterChain;
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java
index 11aa132b478..83a43287880 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java
@@ -60,6 +60,8 @@ public class ControllerContainerTest {
" </rotations>\n" +
" </config>\n" +
" <component id='com.yahoo.vespa.flags.InMemoryFlagSource'/>\n" +
+ " <component id='com.yahoo.vespa.configserver.flags.db.FlagsDbImpl'/>\n" +
+ " <component id='com.yahoo.vespa.curator.mock.MockCurator'/>\n" +
" <component id='com.yahoo.vespa.hosted.controller.persistence.MockCuratorDb'/>\n" +
" <component id='com.yahoo.vespa.hosted.controller.athenz.mock.AthenzClientFactoryMock'/>\n" +
" <component id='com.yahoo.vespa.hosted.controller.api.integration.dns.MemoryNameService'/>\n" +
@@ -112,6 +114,10 @@ public class ControllerContainerTest {
" <binding>http://*/zone/v2</binding>\n" +
" <binding>http://*/zone/v2/*</binding>\n" +
" </handler>\n" +
+ " <handler id='com.yahoo.vespa.hosted.controller.restapi.flags.AuditedFlagsHandler'>\n" +
+ " <binding>http://*/flags/v1</binding>\n" +
+ " <binding>http://*/flags/v1/*</binding>\n" +
+ " </handler>\n" +
variablePartXml() +
"</container>";
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
index 577b8491bd2..8db820f6c83 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
@@ -11,7 +11,6 @@ import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.RegionName;
-import com.yahoo.config.provision.RotationName;
import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.slime.Cursor;
@@ -29,10 +28,10 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.Property;
import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId;
import com.yahoo.vespa.hosted.controller.api.identifiers.ScrewdriverId;
import com.yahoo.vespa.hosted.controller.api.identifiers.UserId;
-import com.yahoo.vespa.hosted.controller.api.integration.MetricsService.ApplicationMetrics;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
+import com.yahoo.vespa.hosted.controller.api.integration.metrics.MetricsService.ApplicationMetrics;
import com.yahoo.vespa.hosted.controller.api.integration.organization.Contact;
import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId;
import com.yahoo.vespa.hosted.controller.api.integration.organization.MockContactRetriever;
@@ -44,6 +43,7 @@ import com.yahoo.vespa.hosted.controller.application.ClusterUtilization;
import com.yahoo.vespa.hosted.controller.application.Deployment;
import com.yahoo.vespa.hosted.controller.application.DeploymentJobs;
import com.yahoo.vespa.hosted.controller.application.DeploymentMetrics;
+import com.yahoo.vespa.hosted.controller.application.EndpointId;
import com.yahoo.vespa.hosted.controller.application.JobStatus;
import com.yahoo.vespa.hosted.controller.application.RotationStatus;
import com.yahoo.vespa.hosted.controller.application.RoutingPolicy;
@@ -562,6 +562,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
.athenzIdentity(com.yahoo.config.provision.AthenzDomain.from(ATHENZ_TENANT_DOMAIN_2.getName()), AthenzService.from("service"))
.region("us-west-1")
.build();
+ configureAthenzIdentity(new com.yahoo.vespa.athenz.api.AthenzService(ATHENZ_TENANT_DOMAIN_2, "service"), true);
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/submit", POST)
.screwdriverIdentity(SCREWDRIVER_ID)
.data(createApplicationSubmissionData(packageWithServiceForWrongDomain)),
@@ -573,6 +574,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
.athenzIdentity(com.yahoo.config.provision.AthenzDomain.from(ATHENZ_TENANT_DOMAIN.getName()), AthenzService.from("service"))
.region("us-west-1")
.build();
+ configureAthenzIdentity(new com.yahoo.vespa.athenz.api.AthenzService(ATHENZ_TENANT_DOMAIN, "service"), true);
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/submit", POST)
.screwdriverIdentity(SCREWDRIVER_ID)
.data(createApplicationSubmissionData(packageWithService)),
@@ -1127,12 +1129,13 @@ public class ApplicationApiTest extends ControllerContainerTest {
public void deployment_fails_on_illegal_domain_in_deployment_spec() {
ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
.upgradePolicy("default")
- .athenzIdentity(com.yahoo.config.provision.AthenzDomain.from("invalid.domain"), com.yahoo.config.provision.AthenzService.from("service"))
+ .athenzIdentity(com.yahoo.config.provision.AthenzDomain.from("another.domain"), com.yahoo.config.provision.AthenzService.from("service"))
.environment(Environment.prod)
.region("us-west-1")
.build();
long screwdriverProjectId = 123;
createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN, USER_ID);
+ configureAthenzIdentity(new com.yahoo.vespa.athenz.api.AthenzService(new AthenzDomain("another.domain"), "service"), true);
Application application = controllerTester.createApplication(ATHENZ_TENANT_DOMAIN.getName(), "tenant1", "application1", "default");
ScrewdriverId screwdriverId = new ScrewdriverId(Long.toString(screwdriverProjectId));
@@ -1147,7 +1150,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/test/region/us-east-1/instance/default/", POST)
.data(createApplicationDeployData(applicationPackage, false))
.screwdriverIdentity(screwdriverId),
- "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Athenz domain in deployment.xml: [invalid.domain] must match tenant domain: [domain1]\"}",
+ "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Athenz domain in deployment.xml: [another.domain] must match tenant domain: [domain1]\"}",
400);
}
@@ -1164,6 +1167,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
ScrewdriverId screwdriverId = new ScrewdriverId(Long.toString(screwdriverProjectId));
createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN, USER_ID);
+ configureAthenzIdentity(new com.yahoo.vespa.athenz.api.AthenzService(ATHENZ_TENANT_DOMAIN, "service"), true);
Application application = controllerTester.createApplication(ATHENZ_TENANT_DOMAIN.getName(), "tenant1", "application1", "default");
controllerTester.authorize(ATHENZ_TENANT_DOMAIN, screwdriverId, ApplicationAction.deploy, application);
@@ -1188,6 +1192,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
UserId tenantAdmin = new UserId("tenant-admin");
UserId userId = new UserId("new-user");
createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN, tenantAdmin);
+ configureAthenzIdentity(new com.yahoo.vespa.athenz.api.AthenzService(ATHENZ_TENANT_DOMAIN, "service"), true);
// Create tenant
// PUT (create) the authenticated user
@@ -1222,6 +1227,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.computeVersionStatus();
UserId tenantAdmin = new UserId("new_user");
createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN, tenantAdmin);
+ configureAthenzIdentity(new com.yahoo.vespa.athenz.api.AthenzService(ATHENZ_TENANT_DOMAIN, "service"), true);
// Create tenant
// PUT (create) the authenticated user
@@ -1247,6 +1253,39 @@ public class ApplicationApiTest extends ControllerContainerTest {
}
@Test
+ public void deployment_fails_when_athenz_service_cannot_be_launched() {
+ ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
+ .upgradePolicy("default")
+ .athenzIdentity(com.yahoo.config.provision.AthenzDomain.from("domain1"), com.yahoo.config.provision.AthenzService.from("service"))
+ .environment(Environment.prod)
+ .region("us-west-1")
+ .build();
+ long screwdriverProjectId = 123;
+ ScrewdriverId screwdriverId = new ScrewdriverId(Long.toString(screwdriverProjectId));
+
+ createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN, USER_ID);
+ configureAthenzIdentity(new com.yahoo.vespa.athenz.api.AthenzService(ATHENZ_TENANT_DOMAIN, "service"), false);
+
+ Application application = controllerTester.createApplication(ATHENZ_TENANT_DOMAIN.getName(), "tenant1", "application1", "default");
+ controllerTester.authorize(ATHENZ_TENANT_DOMAIN, screwdriverId, ApplicationAction.deploy, application);
+
+ // Allow systemtest to succeed by notifying completion of system test
+ controllerTester.jobCompletion(JobType.component)
+ .application(application.id())
+ .projectId(screwdriverProjectId)
+ .uploadArtifact(applicationPackage)
+ .submit();
+
+ String expectedResult="{\"error-code\":\"BAD_REQUEST\",\"message\":\"Not allowed to launch Athenz service domain1.service\"}";
+ tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/test/region/us-east-1/instance/default/", POST)
+ .data(createApplicationDeployData(applicationPackage, false))
+ .screwdriverIdentity(screwdriverId),
+ expectedResult,
+ 400);
+
+ }
+
+ @Test
public void redeployment_succeeds_when_not_specifying_versions_or_application_package() {
// Setup
addUserToHostedOperatorRole(HostedAthenzIdentities.from(HOSTED_VESPA_OPERATOR));
@@ -1262,6 +1301,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
ScrewdriverId screwdriverId = new ScrewdriverId(Long.toString(screwdriverProjectId));
createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN, USER_ID);
+ configureAthenzIdentity(new com.yahoo.vespa.athenz.api.AthenzService(ATHENZ_TENANT_DOMAIN, "service"), true);
Application application = controllerTester.createApplication(ATHENZ_TENANT_DOMAIN.getName(), "tenant1", "application1", "default");
controllerTester.authorize(ATHENZ_TENANT_DOMAIN, screwdriverId, ApplicationAction.deploy, application);
@@ -1380,7 +1420,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
ClusterSpec.Id.from("default"),
ZoneId.from(Environment.prod, RegionName.from("us-west-1")),
HostName.from("lb-0-canonical-name"),
- Optional.of("dns-zone-1"), Set.of(RotationName.from("c0")));
+ Optional.of("dns-zone-1"), Set.of(EndpointId.of("c0")));
tester.controller().curator().writeRoutingPolicies(app.id(), Set.of(policy));
// GET application
@@ -1476,13 +1516,23 @@ public class ApplicationApiTest extends ControllerContainerTest {
private void createAthenzDomainWithAdmin(AthenzDomain domain, UserId userId) {
AthenzClientFactoryMock mock = (AthenzClientFactoryMock) container.components()
.getComponent(AthenzClientFactoryMock.class.getName());
- AthenzDbMock.Domain domainMock = new AthenzDbMock.Domain(domain);
+ AthenzDbMock.Domain domainMock = mock.getSetup().getOrCreateDomain(domain);
domainMock.markAsVespaTenant();
domainMock.admin(AthenzUser.fromUserId(userId.id()));
- mock.getSetup().addDomain(domainMock);
}
/**
+ * Mock athenz service identity configuration. Simulates that configserver is allowed to launch a service
+ */
+ private void configureAthenzIdentity(com.yahoo.vespa.athenz.api.AthenzService service, boolean allowLaunch) {
+ AthenzClientFactoryMock mock = (AthenzClientFactoryMock) container.components()
+ .getComponent(AthenzClientFactoryMock.class.getName());
+ AthenzDbMock.Domain domainMock = mock.getSetup().domains.computeIfAbsent(service.getDomain(), AthenzDbMock.Domain::new);
+ domainMock.services.put(service.getName(), new AthenzDbMock.Service(allowLaunch));
+ }
+
+
+ /**
* In production this happens outside hosted Vespa, so there is no API for it and we need to reach down into the
* mock setup to replicate the action.
*/
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java
index 616db640132..614df953ca9 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java
@@ -2,11 +2,13 @@
package com.yahoo.vespa.hosted.controller.restapi.application;
import com.yahoo.component.Version;
+import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.container.jdisc.HttpResponse;
+import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeployOptions;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException;
-import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
+import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
import com.yahoo.vespa.hosted.controller.deployment.InternalDeploymentTester;
import org.json.JSONException;
import org.json.JSONObject;
@@ -134,12 +136,24 @@ public class JobControllerApiHandlerHelperTest {
assertResponse(JobControllerApiHandlerHelper.jobTypeResponse(tester.tester().controller(), appId, URI.create("https://some.url:43/root")), "dev-overview.json");
}
+ @Test
+ public void testResponsesWithDirectDeployment() {
+ var tester = new InternalDeploymentTester();
+ tester.clock().setInstant(Instant.EPOCH);
+ var region = "us-west-1";
+ var applicationPackage = new ApplicationPackageBuilder().region(region).build();
+ // Deploy directly to production zone, like integration tests.
+ tester.tester().controller().applications().deploy(tester.app().id(), ZoneId.from("prod", region),
+ Optional.of(applicationPackage),
+ new DeployOptions(true, Optional.empty(),
+ false, false));
+ assertResponse(JobControllerApiHandlerHelper.jobTypeResponse(tester.tester().controller(), appId, URI.create("https://some.url:43/root/")),
+ "jobs-direct-deployment.json");
+ }
+
private void compare(HttpResponse response, String expected) throws JSONException, IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
response.render(baos);
-
- System.err.println(baos);
-
JSONObject actualJSON = new JSONObject(new String(baos.toByteArray()));
JSONObject expectedJSON = new JSONObject(expected);
assertEquals(expectedJSON.toString(), actualJSON.toString());
@@ -148,7 +162,7 @@ public class JobControllerApiHandlerHelperTest {
private void assertResponse(HttpResponse response, String fileName) {
try {
Path path = Paths.get("src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/").resolve(fileName);
- String expected = new String(Files.readAllBytes(path));
+ String expected = Files.readString(path);
compare(response, expected);
} catch (Exception e) {
throw new RuntimeException(e);
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/jobs-direct-deployment.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/jobs-direct-deployment.json
new file mode 100644
index 00000000000..5535e286dcd
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/jobs-direct-deployment.json
@@ -0,0 +1,79 @@
+{
+ "devJobs": {},
+ "deployments": [
+ {
+ "us-west-1": {
+ "at": 0,
+ "application": {
+ "hash": "unknown"
+ },
+ "verified": false,
+ "platform": "6.1"
+ }
+ }
+ ],
+ "lastVersions": {},
+ "deploying": {},
+ "jobs": {
+ "staging-test": {
+ "runs": [
+ {
+ "reason": "Testing for productionUsWest1",
+ "wantedPlatform": "6.1",
+ "currentPlatform": "6.1",
+ "wantedApplication": {
+ "hash": "unknown"
+ },
+ "currentApplication": {
+ "hash": "unknown"
+ },
+ "tasks": {
+ "capacity": "running"
+ },
+ "status": "pending"
+ }
+ ],
+ "url": "https://some.url:43/root/staging-test"
+ },
+ "system-test": {
+ "runs": [
+ {
+ "reason": "Testing for productionUsWest1",
+ "wantedPlatform": "6.1",
+ "currentPlatform": "6.1",
+ "wantedApplication": {
+ "hash": "unknown"
+ },
+ "currentApplication": {
+ "hash": "unknown"
+ },
+ "tasks": {
+ "capacity": "running"
+ },
+ "status": "pending"
+ }
+ ],
+ "url": "https://some.url:43/root/system-test"
+ },
+ "us-west-1": {
+ "runs": [
+ {
+ "wantedPlatform": "6.1",
+ "currentPlatform": "6.1",
+ "wantedApplication": {
+ "hash": "unknown"
+ },
+ "currentApplication": {
+ "hash": "unknown"
+ },
+ "tasks": {
+ "staging-test": "pending",
+ "system-test": "pending"
+ },
+ "status": "pending"
+ }
+ ],
+ "url": "https://some.url:43/root/production-us-west-1"
+ }
+ }
+}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/flags/AuditedFlagsApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/flags/AuditedFlagsApiTest.java
new file mode 100644
index 00000000000..b4ef98cc7f6
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/flags/AuditedFlagsApiTest.java
@@ -0,0 +1,57 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.restapi.flags;
+
+import com.yahoo.application.container.handler.Request;
+import com.yahoo.vespa.athenz.api.AthenzIdentity;
+import com.yahoo.vespa.athenz.api.AthenzUser;
+import com.yahoo.vespa.hosted.controller.auditlog.AuditLog;
+import com.yahoo.vespa.hosted.controller.restapi.ContainerControllerTester;
+import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerTest;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author mpolden
+ */
+public class AuditedFlagsApiTest extends ControllerContainerTest {
+
+ private static final String responses = "src/test/java/com/yahoo/vespa/hosted/controller/restapi/flags/responses/";
+ private static final AthenzIdentity operator = AthenzUser.fromUserId("operatorUser");
+
+ private ContainerControllerTester tester;
+
+ @Before
+ public void before() {
+ addUserToHostedOperatorRole(operator);
+ tester = new ContainerControllerTester(container, responses);
+ }
+
+ @Test
+ public void test_audit_logging() {
+ var body = "{\n" +
+ " \"id\": \"id1\",\n" +
+ " \"rules\": [\n" +
+ " {\n" +
+ " \"value\": true\n" +
+ " }\n" +
+ " ]\n" +
+ "}";
+ assertResponse(new Request("http://localhost:8080/flags/v1/data/id1?force=true", body, Request.Method.PUT),
+ "", 200);
+ var log = tester.controller().auditLogger().readLog();
+ assertEquals(1, log.entries().size());
+ var entry = log.entries().get(0);
+ assertEquals(operator.getFullName(), entry.principal());
+ assertEquals(AuditLog.Entry.Method.PUT, entry.method());
+ assertEquals("/flags/v1/data/id1?force=true", entry.resource());
+ assertEquals(body, log.entries().get(0).data().get());
+ }
+
+ private void assertResponse(Request request, String body, int statusCode) {
+ addIdentityToRequest(request, operator);
+ tester.assertResponse(request, body, statusCode);
+ }
+
+}
diff --git a/defaults/src/main/java/com/yahoo/vespa/defaults/Defaults.java b/defaults/src/main/java/com/yahoo/vespa/defaults/Defaults.java
index f1b7e38986f..6fb6e4f0860 100644
--- a/defaults/src/main/java/com/yahoo/vespa/defaults/Defaults.java
+++ b/defaults/src/main/java/com/yahoo/vespa/defaults/Defaults.java
@@ -114,11 +114,11 @@ public class Defaults {
public String vespaHostname() { return vespaHost; }
/**
- * Returns the path where a Vespa application can store arbitrary files. This should only be used for temporary
- * files as there are no availability guarantees for files stored here. The application must be able to recreate
+ * Returns the path where a Vespa application can store arbitrary files on the node. This path
+ * is persistent during the lifetime of this node. The application must be able to recreate
* required files on its own (e.g. by downloading them from a remote source) if missing.
*
- * @return the temporary storage path
+ * @return the local application storage path
*/
public String temporaryApplicationStorage() { return temporaryApplicationStorage; }
diff --git a/dist/vespa.spec b/dist/vespa.spec
index 3426eff459d..ba1dbc32831 100644
--- a/dist/vespa.spec
+++ b/dist/vespa.spec
@@ -90,6 +90,7 @@ Requires: perl-Getopt-Long
Requires: perl-IO-Socket-IP
Requires: perl-JSON
Requires: perl-libwww-perl
+Requires: perl-LWP-Protocol-https
Requires: perl-Net-INET6Glue
Requires: perl-Pod-Usage
Requires: perl-URI
diff --git a/document/abi-spec.json b/document/abi-spec.json
index 81cf5509a57..134200d96ec 100644
--- a/document/abi-spec.json
+++ b/document/abi-spec.json
@@ -3601,723 +3601,6 @@
],
"fields": []
},
- "com.yahoo.document.select.convert.NowQueryExpression": {
- "superClass": "java.lang.Object",
- "interfaces": [],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>(com.yahoo.document.select.rule.AttributeNode, com.yahoo.document.select.rule.ComparisonNode, com.yahoo.document.select.rule.ArithmeticNode)",
- "public java.lang.String getDocumentType()",
- "public java.lang.String toString()"
- ],
- "fields": []
- },
- "com.yahoo.document.select.convert.NowQueryNode": {
- "superClass": "java.lang.Object",
- "interfaces": [],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>(long)",
- "public void <init>(com.yahoo.document.select.rule.ArithmeticNode)",
- "public java.lang.String toString()"
- ],
- "fields": []
- },
- "com.yahoo.document.select.convert.SelectionExpressionConverter": {
- "superClass": "java.lang.Object",
- "interfaces": [
- "com.yahoo.document.select.Visitor"
- ],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>()",
- "public java.util.Map getQueryMap()",
- "public void visit(com.yahoo.document.select.rule.ArithmeticNode)",
- "public void visit(com.yahoo.document.select.rule.AttributeNode)",
- "public void visit(com.yahoo.document.select.rule.ComparisonNode)",
- "public void visit(com.yahoo.document.select.rule.DocumentNode)",
- "public void visit(com.yahoo.document.select.rule.EmbracedNode)",
- "public void visit(com.yahoo.document.select.rule.IdNode)",
- "public void visit(com.yahoo.document.select.rule.LiteralNode)",
- "public void visit(com.yahoo.document.select.rule.LogicNode)",
- "public void visit(com.yahoo.document.select.rule.NegationNode)",
- "public void visit(com.yahoo.document.select.rule.NowNode)",
- "public void visit(com.yahoo.document.select.rule.SearchColumnNode)",
- "public void visit(com.yahoo.document.select.rule.VariableNode)"
- ],
- "fields": []
- },
- "com.yahoo.document.select.parser.CharStream": {
- "superClass": "java.lang.Object",
- "interfaces": [],
- "attributes": [
- "public",
- "interface",
- "abstract"
- ],
- "methods": [
- "public abstract char readChar()",
- "public abstract int getBeginColumn()",
- "public abstract int getBeginLine()",
- "public abstract int getEndColumn()",
- "public abstract int getEndLine()",
- "public abstract void backup(int)",
- "public abstract char beginToken()",
- "public abstract java.lang.String getImage()",
- "public abstract char[] getSuffix(int)",
- "public abstract void done()",
- "public abstract void setTabSize(int)",
- "public abstract int getTabSize()",
- "public abstract void setTrackLineColumn(boolean)",
- "public abstract boolean isTrackLineColumn()"
- ],
- "fields": []
- },
- "com.yahoo.document.select.parser.ParseException": {
- "superClass": "java.lang.Exception",
- "interfaces": [],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>(com.yahoo.document.select.parser.Token, int[][], java.lang.String[])",
- "public void <init>()",
- "public void <init>(java.lang.String)"
- ],
- "fields": [
- "protected static final java.lang.String EOL",
- "public com.yahoo.document.select.parser.Token currentToken",
- "public int[][] expectedTokenSequences",
- "public java.lang.String[] tokenImage"
- ]
- },
- "com.yahoo.document.select.parser.SelectInput": {
- "superClass": "com.yahoo.javacc.FastCharStream",
- "interfaces": [
- "com.yahoo.document.select.parser.CharStream"
- ],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>(java.lang.String)"
- ],
- "fields": []
- },
- "com.yahoo.document.select.parser.SelectParser": {
- "superClass": "java.lang.Object",
- "interfaces": [
- "com.yahoo.document.select.parser.SelectParserConstants"
- ],
- "attributes": [
- "public"
- ],
- "methods": [
- "public final com.yahoo.document.select.rule.ExpressionNode expression()",
- "public final com.yahoo.document.select.rule.ExpressionNode logic()",
- "public final com.yahoo.document.select.rule.ExpressionNode negation()",
- "public final com.yahoo.document.select.rule.NowNode now()",
- "public final com.yahoo.document.select.rule.ExpressionNode relational()",
- "public final com.yahoo.document.select.rule.ExpressionNode arithmetic()",
- "public final com.yahoo.document.select.rule.VariableNode variable()",
- "public final com.yahoo.document.select.rule.ExpressionNode attribute()",
- "public final com.yahoo.document.select.rule.ExpressionNode value()",
- "public final com.yahoo.document.select.rule.DocumentNode document()",
- "public final void identifier()",
- "public final com.yahoo.document.select.rule.IdNode id()",
- "public final com.yahoo.document.select.rule.SearchColumnNode searchColumn()",
- "public final com.yahoo.document.select.rule.LiteralNode literal()",
- "public void <init>(com.yahoo.document.select.parser.CharStream)",
- "public void ReInit(com.yahoo.document.select.parser.CharStream)",
- "public void <init>(com.yahoo.document.select.parser.SelectParserTokenManager)",
- "public void ReInit(com.yahoo.document.select.parser.SelectParserTokenManager)",
- "public final com.yahoo.document.select.parser.Token getNextToken()",
- "public final com.yahoo.document.select.parser.Token getToken(int)",
- "public com.yahoo.document.select.parser.ParseException generateParseException()",
- "public final boolean trace_enabled()",
- "public final void enable_tracing()",
- "public final void disable_tracing()"
- ],
- "fields": [
- "public com.yahoo.document.select.parser.SelectParserTokenManager token_source",
- "public com.yahoo.document.select.parser.Token token",
- "public com.yahoo.document.select.parser.Token jj_nt"
- ]
- },
- "com.yahoo.document.select.parser.SelectParserConstants": {
- "superClass": "java.lang.Object",
- "interfaces": [],
- "attributes": [
- "public",
- "interface",
- "abstract"
- ],
- "methods": [],
- "fields": [
- "public static final int EOF",
- "public static final int INTEGER",
- "public static final int DECIMAL",
- "public static final int HEX",
- "public static final int OCTAL",
- "public static final int FLOAT",
- "public static final int EXPONENT",
- "public static final int LBRACE",
- "public static final int RBRACE",
- "public static final int ADD",
- "public static final int SUB",
- "public static final int MUL",
- "public static final int DIV",
- "public static final int MOD",
- "public static final int DOT",
- "public static final int COMMA",
- "public static final int NE",
- "public static final int GE",
- "public static final int LE",
- "public static final int REGEX",
- "public static final int EQ",
- "public static final int GLOB",
- "public static final int GT",
- "public static final int LT",
- "public static final int DOLLAR",
- "public static final int STRING",
- "public static final int ID",
- "public static final int SEARCHCOLUMN",
- "public static final int ID_SCHEME",
- "public static final int ID_TYPE",
- "public static final int ID_NAMESPACE",
- "public static final int ID_SPECIFIC",
- "public static final int ID_USER",
- "public static final int ID_GROUP",
- "public static final int ID_ORDER",
- "public static final int ID_BUCKET",
- "public static final int NULL",
- "public static final int NOW",
- "public static final int TRUE",
- "public static final int FALSE",
- "public static final int AND",
- "public static final int OR",
- "public static final int NOT",
- "public static final int IDENTIFIER",
- "public static final int DEFAULT",
- "public static final java.lang.String[] tokenImage"
- ]
- },
- "com.yahoo.document.select.parser.SelectParserTokenManager": {
- "superClass": "java.lang.Object",
- "interfaces": [
- "com.yahoo.document.select.parser.SelectParserConstants"
- ],
- "attributes": [
- "public"
- ],
- "methods": [
- "protected com.yahoo.document.select.parser.Token jjFillToken()",
- "public com.yahoo.document.select.parser.Token getNextToken()",
- "public void <init>(com.yahoo.document.select.parser.CharStream)",
- "public void <init>(com.yahoo.document.select.parser.CharStream, int)",
- "public void ReInit(com.yahoo.document.select.parser.CharStream)",
- "public void ReInit(com.yahoo.document.select.parser.CharStream, int)",
- "public void SwitchTo(int)"
- ],
- "fields": [
- "public static final java.lang.String[] jjstrLiteralImages",
- "public static final java.lang.String[] lexStateNames",
- "public static final int[] jjnewLexState",
- "protected com.yahoo.document.select.parser.CharStream input_stream",
- "protected int curChar"
- ]
- },
- "com.yahoo.document.select.parser.SelectParserUtils": {
- "superClass": "java.lang.Object",
- "interfaces": [],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>()",
- "public static long decodeLong(java.lang.String)",
- "public static java.lang.String quote(java.lang.String, char)",
- "public static java.lang.String unquote(java.lang.String)"
- ],
- "fields": []
- },
- "com.yahoo.document.select.parser.Token": {
- "superClass": "java.lang.Object",
- "interfaces": [
- "java.io.Serializable"
- ],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>()",
- "public void <init>(int)",
- "public void <init>(int, java.lang.String)",
- "public java.lang.Object getValue()",
- "public java.lang.String toString()",
- "public static com.yahoo.document.select.parser.Token newToken(int, java.lang.String)",
- "public static com.yahoo.document.select.parser.Token newToken(int)"
- ],
- "fields": [
- "public int kind",
- "public int beginLine",
- "public int beginColumn",
- "public int endLine",
- "public int endColumn",
- "public java.lang.String image",
- "public com.yahoo.document.select.parser.Token next",
- "public com.yahoo.document.select.parser.Token specialToken"
- ]
- },
- "com.yahoo.document.select.parser.TokenMgrException": {
- "superClass": "java.lang.RuntimeException",
- "interfaces": [],
- "attributes": [
- "public"
- ],
- "methods": [
- "protected static final java.lang.String addEscapes(java.lang.String)",
- "protected static java.lang.String LexicalErr(boolean, int, int, int, java.lang.String, int)",
- "public java.lang.String getMessage()",
- "public void <init>()",
- "public void <init>(java.lang.String, int)",
- "public void <init>(boolean, int, int, int, java.lang.String, int, int)"
- ],
- "fields": [
- "public static final int LEXICAL_ERROR",
- "public static final int STATIC_LEXER_ERROR",
- "public static final int INVALID_LEXICAL_STATE",
- "public static final int LOOP_DETECTED"
- ]
- },
- "com.yahoo.document.select.rule.ArithmeticNode$NodeItem": {
- "superClass": "java.lang.Object",
- "interfaces": [],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>(int, com.yahoo.document.select.rule.ExpressionNode)",
- "public int getOperator()",
- "public com.yahoo.document.select.rule.ExpressionNode getNode()"
- ],
- "fields": []
- },
- "com.yahoo.document.select.rule.ArithmeticNode": {
- "superClass": "java.lang.Object",
- "interfaces": [
- "com.yahoo.document.select.rule.ExpressionNode"
- ],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>()",
- "public com.yahoo.document.select.rule.ArithmeticNode add(java.lang.String, com.yahoo.document.select.rule.ExpressionNode)",
- "public java.util.List getItems()",
- "public com.yahoo.document.select.BucketSet getBucketSet(com.yahoo.document.BucketIdFactory)",
- "public java.lang.Object evaluate(com.yahoo.document.select.Context)",
- "public java.lang.String toString()",
- "public java.lang.String operatorToString(int)",
- "public com.yahoo.document.select.OrderingSpecification getOrdering(int)",
- "public void accept(com.yahoo.document.select.Visitor)"
- ],
- "fields": [
- "public static final int NOP",
- "public static final int ADD",
- "public static final int SUB",
- "public static final int MOD",
- "public static final int DIV",
- "public static final int MUL"
- ]
- },
- "com.yahoo.document.select.rule.AttributeNode$Item": {
- "superClass": "java.lang.Object",
- "interfaces": [],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>(java.lang.String)",
- "public java.lang.String getName()",
- "public com.yahoo.document.select.rule.AttributeNode$Item setName(java.lang.String)",
- "public int getType()",
- "public com.yahoo.document.select.rule.AttributeNode$Item setType(int)",
- "public java.lang.String toString()"
- ],
- "fields": [
- "public static final int ATTRIBUTE",
- "public static final int FUNCTION"
- ]
- },
- "com.yahoo.document.select.rule.AttributeNode$VariableValueList": {
- "superClass": "java.util.ArrayList",
- "interfaces": [],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>()"
- ],
- "fields": []
- },
- "com.yahoo.document.select.rule.AttributeNode": {
- "superClass": "java.lang.Object",
- "interfaces": [
- "com.yahoo.document.select.rule.ExpressionNode"
- ],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>(com.yahoo.document.select.rule.ExpressionNode, java.util.List)",
- "public com.yahoo.document.select.rule.ExpressionNode getValue()",
- "public com.yahoo.document.select.rule.AttributeNode setValue(com.yahoo.document.select.rule.ExpressionNode)",
- "public java.util.List getItems()",
- "public com.yahoo.document.select.BucketSet getBucketSet(com.yahoo.document.BucketIdFactory)",
- "public java.lang.Object evaluate(com.yahoo.document.select.Context)",
- "public void accept(com.yahoo.document.select.Visitor)",
- "public java.lang.String toString()",
- "public com.yahoo.document.select.OrderingSpecification getOrdering(int)"
- ],
- "fields": []
- },
- "com.yahoo.document.select.rule.ComparisonNode": {
- "superClass": "java.lang.Object",
- "interfaces": [
- "com.yahoo.document.select.rule.ExpressionNode"
- ],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>(com.yahoo.document.select.rule.ExpressionNode, java.lang.String, com.yahoo.document.select.rule.ExpressionNode)",
- "public com.yahoo.document.select.rule.ExpressionNode getLHS()",
- "public com.yahoo.document.select.rule.ComparisonNode setLHS(com.yahoo.document.select.rule.ExpressionNode)",
- "public java.lang.String getOperator()",
- "public com.yahoo.document.select.rule.ComparisonNode setOperator(java.lang.String)",
- "public com.yahoo.document.select.rule.ExpressionNode getRHS()",
- "public com.yahoo.document.select.rule.ComparisonNode setRHS(com.yahoo.document.select.rule.ExpressionNode)",
- "public com.yahoo.document.select.OrderingSpecification getOrdering(com.yahoo.document.select.rule.IdNode, com.yahoo.document.select.rule.LiteralNode, java.lang.String, int)",
- "public com.yahoo.document.select.OrderingSpecification getOrdering(int)",
- "public com.yahoo.document.select.BucketSet getBucketSet(com.yahoo.document.BucketIdFactory)",
- "public java.lang.Object evaluate(com.yahoo.document.select.Context)",
- "public com.yahoo.document.select.ResultList evaluateListsTrue(com.yahoo.document.select.rule.AttributeNode$VariableValueList, com.yahoo.document.select.rule.AttributeNode$VariableValueList)",
- "public com.yahoo.document.select.ResultList evaluateListsFalse(com.yahoo.document.select.rule.AttributeNode$VariableValueList, com.yahoo.document.select.rule.AttributeNode$VariableValueList)",
- "public com.yahoo.document.select.ResultList evaluateListAndSingle(com.yahoo.document.select.rule.AttributeNode$VariableValueList, java.lang.Object)",
- "public void accept(com.yahoo.document.select.Visitor)",
- "public java.lang.String toString()"
- ],
- "fields": []
- },
- "com.yahoo.document.select.rule.DocumentNode": {
- "superClass": "java.lang.Object",
- "interfaces": [
- "com.yahoo.document.select.rule.ExpressionNode"
- ],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>(java.lang.String)",
- "public java.lang.String getType()",
- "public com.yahoo.document.select.rule.DocumentNode setType(java.lang.String)",
- "public com.yahoo.document.select.BucketSet getBucketSet(com.yahoo.document.BucketIdFactory)",
- "public java.lang.Object evaluate(com.yahoo.document.select.Context)",
- "public java.lang.Object evaluate(com.yahoo.document.DocumentOperation)",
- "public void accept(com.yahoo.document.select.Visitor)",
- "public java.lang.String toString()",
- "public com.yahoo.document.select.OrderingSpecification getOrdering(int)"
- ],
- "fields": []
- },
- "com.yahoo.document.select.rule.EmbracedNode": {
- "superClass": "java.lang.Object",
- "interfaces": [
- "com.yahoo.document.select.rule.ExpressionNode"
- ],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>(com.yahoo.document.select.rule.ExpressionNode)",
- "public com.yahoo.document.select.rule.ExpressionNode getNode()",
- "public com.yahoo.document.select.rule.EmbracedNode setNode(com.yahoo.document.select.rule.ExpressionNode)",
- "public com.yahoo.document.select.BucketSet getBucketSet(com.yahoo.document.BucketIdFactory)",
- "public java.lang.Object evaluate(com.yahoo.document.select.Context)",
- "public java.lang.String toString()",
- "public void accept(com.yahoo.document.select.Visitor)",
- "public com.yahoo.document.select.OrderingSpecification getOrdering(int)"
- ],
- "fields": []
- },
- "com.yahoo.document.select.rule.ExpressionNode": {
- "superClass": "java.lang.Object",
- "interfaces": [],
- "attributes": [
- "public",
- "interface",
- "abstract"
- ],
- "methods": [
- "public abstract java.lang.Object evaluate(com.yahoo.document.select.Context)",
- "public abstract com.yahoo.document.select.BucketSet getBucketSet(com.yahoo.document.BucketIdFactory)",
- "public abstract com.yahoo.document.select.OrderingSpecification getOrdering(int)",
- "public abstract void accept(com.yahoo.document.select.Visitor)"
- ],
- "fields": []
- },
- "com.yahoo.document.select.rule.IdNode": {
- "superClass": "java.lang.Object",
- "interfaces": [
- "com.yahoo.document.select.rule.ExpressionNode"
- ],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>()",
- "public java.lang.String getField()",
- "public com.yahoo.document.select.rule.IdNode setField(java.lang.String)",
- "public com.yahoo.document.select.rule.IdNode setWidthBits(short)",
- "public com.yahoo.document.select.rule.IdNode setDivisionBits(short)",
- "public short getWidthBits()",
- "public short getDivisionBits()",
- "public com.yahoo.document.select.BucketSet getBucketSet(com.yahoo.document.BucketIdFactory)",
- "public com.yahoo.document.select.OrderingSpecification getOrdering(int)",
- "public java.lang.Object evaluate(com.yahoo.document.select.Context)",
- "public void accept(com.yahoo.document.select.Visitor)",
- "public java.lang.String toString()"
- ],
- "fields": []
- },
- "com.yahoo.document.select.rule.LiteralNode": {
- "superClass": "java.lang.Object",
- "interfaces": [
- "com.yahoo.document.select.rule.ExpressionNode"
- ],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>(java.lang.Object)",
- "public java.lang.Object getValue()",
- "public com.yahoo.document.select.rule.LiteralNode setValue(java.lang.Object)",
- "public com.yahoo.document.select.BucketSet getBucketSet(com.yahoo.document.BucketIdFactory)",
- "public java.lang.Object evaluate(com.yahoo.document.select.Context)",
- "public void accept(com.yahoo.document.select.Visitor)",
- "public java.lang.String toString()",
- "public com.yahoo.document.select.OrderingSpecification getOrdering(int)"
- ],
- "fields": []
- },
- "com.yahoo.document.select.rule.LogicNode$NodeItem": {
- "superClass": "java.lang.Object",
- "interfaces": [],
- "attributes": [
- "public",
- "final"
- ],
- "methods": [
- "public void <init>(com.yahoo.document.select.rule.LogicNode, int, com.yahoo.document.select.rule.ExpressionNode)",
- "public int getOperator()",
- "public com.yahoo.document.select.rule.ExpressionNode getNode()"
- ],
- "fields": []
- },
- "com.yahoo.document.select.rule.LogicNode": {
- "superClass": "java.lang.Object",
- "interfaces": [
- "com.yahoo.document.select.rule.ExpressionNode"
- ],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>()",
- "public java.util.List getItems()",
- "public com.yahoo.document.select.rule.LogicNode add(java.lang.String, com.yahoo.document.select.rule.ExpressionNode)",
- "public com.yahoo.document.select.BucketSet getBucketSet(com.yahoo.document.BucketIdFactory)",
- "public com.yahoo.document.select.OrderingSpecification getOrdering(int)",
- "public java.lang.Object evaluate(com.yahoo.document.select.Context)",
- "public void accept(com.yahoo.document.select.Visitor)",
- "public java.lang.String toString()",
- "public java.lang.String operatorToString(int)"
- ],
- "fields": [
- "public static final int NOP",
- "public static final int OR",
- "public static final int AND"
- ]
- },
- "com.yahoo.document.select.rule.NegationNode": {
- "superClass": "java.lang.Object",
- "interfaces": [
- "com.yahoo.document.select.rule.ExpressionNode"
- ],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>(com.yahoo.document.select.rule.ExpressionNode)",
- "public com.yahoo.document.select.rule.ExpressionNode getNode()",
- "public com.yahoo.document.select.rule.NegationNode setNode(com.yahoo.document.select.rule.ExpressionNode)",
- "public com.yahoo.document.select.BucketSet getBucketSet(com.yahoo.document.BucketIdFactory)",
- "public java.lang.Object evaluate(com.yahoo.document.select.Context)",
- "public void accept(com.yahoo.document.select.Visitor)",
- "public java.lang.String toString()",
- "public com.yahoo.document.select.OrderingSpecification getOrdering(int)"
- ],
- "fields": []
- },
- "com.yahoo.document.select.rule.NowNode": {
- "superClass": "java.lang.Object",
- "interfaces": [
- "com.yahoo.document.select.rule.ExpressionNode"
- ],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>()",
- "public com.yahoo.document.select.BucketSet getBucketSet(com.yahoo.document.BucketIdFactory)",
- "public java.lang.Object evaluate(com.yahoo.document.select.Context)",
- "public java.lang.String toString()",
- "public com.yahoo.document.select.OrderingSpecification getOrdering(int)",
- "public void accept(com.yahoo.document.select.Visitor)"
- ],
- "fields": []
- },
- "com.yahoo.document.select.rule.SearchColumnNode": {
- "superClass": "java.lang.Object",
- "interfaces": [
- "com.yahoo.document.select.rule.ExpressionNode"
- ],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>()",
- "public int getField()",
- "public com.yahoo.document.select.rule.SearchColumnNode setField(int)",
- "public com.yahoo.document.BucketDistribution getDistribution()",
- "public com.yahoo.document.select.BucketSet getBucketSet(com.yahoo.document.BucketIdFactory)",
- "public java.lang.Object evaluate(com.yahoo.document.select.Context)",
- "public void accept(com.yahoo.document.select.Visitor)",
- "public java.lang.String toString()",
- "public com.yahoo.document.select.OrderingSpecification getOrdering(int)"
- ],
- "fields": []
- },
- "com.yahoo.document.select.rule.VariableNode": {
- "superClass": "java.lang.Object",
- "interfaces": [
- "com.yahoo.document.select.rule.ExpressionNode"
- ],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>(java.lang.String)",
- "public java.lang.Object getValue()",
- "public com.yahoo.document.select.rule.VariableNode setValue(java.lang.String)",
- "public com.yahoo.document.select.BucketSet getBucketSet(com.yahoo.document.BucketIdFactory)",
- "public java.lang.Object evaluate(com.yahoo.document.select.Context)",
- "public void accept(com.yahoo.document.select.Visitor)",
- "public java.lang.String toString()",
- "public com.yahoo.document.select.OrderingSpecification getOrdering(int)"
- ],
- "fields": []
- },
- "com.yahoo.document.select.simple.IdSpecParser": {
- "superClass": "com.yahoo.document.select.simple.Parser",
- "interfaces": [],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>()",
- "public com.yahoo.document.select.rule.IdNode getId()",
- "public boolean isUserSpec()",
- "public boolean parse(java.lang.CharSequence)"
- ],
- "fields": []
- },
- "com.yahoo.document.select.simple.IntegerParser": {
- "superClass": "com.yahoo.document.select.simple.Parser",
- "interfaces": [],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>()",
- "public com.yahoo.document.select.rule.LiteralNode getValue()",
- "public boolean parse(java.lang.CharSequence)"
- ],
- "fields": []
- },
- "com.yahoo.document.select.simple.OperatorParser": {
- "superClass": "com.yahoo.document.select.simple.Parser",
- "interfaces": [],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>()",
- "public java.lang.String getOperator()",
- "public boolean parse(java.lang.CharSequence)"
- ],
- "fields": []
- },
- "com.yahoo.document.select.simple.Parser": {
- "superClass": "java.lang.Object",
- "interfaces": [],
- "attributes": [
- "public",
- "abstract"
- ],
- "methods": [
- "public void <init>()",
- "public abstract boolean parse(java.lang.CharSequence)",
- "public java.lang.CharSequence getRemaining()",
- "protected void setRemaining(java.lang.CharSequence)",
- "protected int eatWhite(java.lang.CharSequence)",
- "protected boolean icmp(char, char)"
- ],
- "fields": []
- },
- "com.yahoo.document.select.simple.SelectionParser": {
- "superClass": "com.yahoo.document.select.simple.Parser",
- "interfaces": [],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>()",
- "public com.yahoo.document.select.rule.ExpressionNode getNode()",
- "public boolean parse(java.lang.CharSequence)"
- ],
- "fields": []
- },
- "com.yahoo.document.select.simple.StringParser": {
- "superClass": "com.yahoo.document.select.simple.Parser",
- "interfaces": [],
- "attributes": [
- "public"
- ],
- "methods": [
- "public void <init>()",
- "public com.yahoo.document.select.rule.LiteralNode getValue()",
- "public boolean parse(java.lang.CharSequence)"
- ],
- "fields": []
- },
"com.yahoo.document.serialization.AnnotationReader": {
"superClass": "java.lang.Object",
"interfaces": [],
diff --git a/document/src/main/java/com/yahoo/document/select/Context.java b/document/src/main/java/com/yahoo/document/select/Context.java
index ac2bb6b9f0d..481131b4776 100644
--- a/document/src/main/java/com/yahoo/document/select/Context.java
+++ b/document/src/main/java/com/yahoo/document/select/Context.java
@@ -7,7 +7,8 @@ import java.util.HashMap;
import java.util.Map;
public class Context {
- private DocumentOperation documentOperation = null;
+ private DocumentOperation documentOperation;
+ private Map<String, Object> variables = new HashMap<>();
public Context(DocumentOperation documentOperation) {
this.documentOperation = documentOperation;
@@ -28,7 +29,4 @@ public class Context {
public void setVariables(Map<String, Object> variables) {
this.variables = variables;
}
-
- private Map<String, Object> variables = new HashMap<String, Object>();
-
}
diff --git a/document/src/main/java/com/yahoo/document/select/DocumentSelector.java b/document/src/main/java/com/yahoo/document/select/DocumentSelector.java
index 7f5b92ea233..1ee6ff45c99 100644
--- a/document/src/main/java/com/yahoo/document/select/DocumentSelector.java
+++ b/document/src/main/java/com/yahoo/document/select/DocumentSelector.java
@@ -84,7 +84,7 @@ public class DocumentSelector {
* @return True if the document is accepted.
* @throws RuntimeException if the evaluation enters an illegal state
*/
- public ResultList getMatchingResultList(Context context) {
+ private ResultList getMatchingResultList(Context context) {
return ResultList.toResultList(expression.evaluate(context));
}
diff --git a/document/src/main/java/com/yahoo/document/select/NowCheckVisitor.java b/document/src/main/java/com/yahoo/document/select/NowCheckVisitor.java
index 5efcdec848a..fd799e36a70 100644
--- a/document/src/main/java/com/yahoo/document/select/NowCheckVisitor.java
+++ b/document/src/main/java/com/yahoo/document/select/NowCheckVisitor.java
@@ -1,8 +1,18 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.document.select;
-import com.yahoo.document.select.Visitor;
-import com.yahoo.document.select.rule.*;
+import com.yahoo.document.select.rule.ArithmeticNode;
+import com.yahoo.document.select.rule.AttributeNode;
+import com.yahoo.document.select.rule.ComparisonNode;
+import com.yahoo.document.select.rule.DocumentNode;
+import com.yahoo.document.select.rule.EmbracedNode;
+import com.yahoo.document.select.rule.IdNode;
+import com.yahoo.document.select.rule.LiteralNode;
+import com.yahoo.document.select.rule.LogicNode;
+import com.yahoo.document.select.rule.NegationNode;
+import com.yahoo.document.select.rule.NowNode;
+import com.yahoo.document.select.rule.SearchColumnNode;
+import com.yahoo.document.select.rule.VariableNode;
/**
* Traverse and check if there exists any now() function in the expression tree.
diff --git a/document/src/main/java/com/yahoo/document/select/Result.java b/document/src/main/java/com/yahoo/document/select/Result.java
index 784b7fdc7e7..5f057da900c 100644
--- a/document/src/main/java/com/yahoo/document/select/Result.java
+++ b/document/src/main/java/com/yahoo/document/select/Result.java
@@ -15,7 +15,7 @@ public enum Result {
FALSE,
INVALID;
- // Inherit doc from Object.
+ @Override
public String toString() {
return name().toLowerCase();
}
@@ -38,7 +38,7 @@ public enum Result {
*/
public static Result toResult(Object value) {
if (value == null || value == Result.FALSE || value == Boolean.FALSE ||
- (Number.class.isInstance(value) && ((Number)value).doubleValue() == 0)) {
+ (value instanceof Number && ((Number)value).doubleValue() == 0)) {
return Result.FALSE;
} else if (value == INVALID) {
return Result.INVALID;
diff --git a/document/src/main/java/com/yahoo/document/select/ResultList.java b/document/src/main/java/com/yahoo/document/select/ResultList.java
index 8d73c475981..edf05eb044e 100644
--- a/document/src/main/java/com/yahoo/document/select/ResultList.java
+++ b/document/src/main/java/com/yahoo/document/select/ResultList.java
@@ -43,7 +43,7 @@ public class ResultList {
}
}
- List<ResultPair> results = new ArrayList<ResultPair>();
+ private List<ResultPair> results = new ArrayList<>();
public ResultList() {
}
@@ -86,7 +86,7 @@ public class ResultList {
}
}
- boolean combineVariables(FieldPathIteratorHandler.VariableMap output, FieldPathIteratorHandler.VariableMap input) {
+ private boolean combineVariables(FieldPathIteratorHandler.VariableMap output, FieldPathIteratorHandler.VariableMap input) {
// First, verify that all variables are overlapping
for (Map.Entry<String, FieldPathIteratorHandler.IndexValue> entry : output.entrySet()) {
FieldPathIteratorHandler.IndexValue found = input.get(entry.getKey());
@@ -116,13 +116,19 @@ public class ResultList {
return true;
}
- public ResultList combineAND(ResultList other)
+ public interface LazyResultList {
+ ResultList getResult();
+ }
+
+ public ResultList combineAND(LazyResultList other)
{
+ if (Result.FALSE == toResult()) return ResultList.toResultList(false);
+
ResultList result = new ResultList();
// TODO: optimize
for (ResultPair pair : results) {
- for (ResultPair otherPair : other.results) {
+ for (ResultPair otherPair : other.getResult().results) {
FieldPathIteratorHandler.VariableMap varMap = (FieldPathIteratorHandler.VariableMap)pair.variables.clone();
if (combineVariables(varMap, otherPair.variables)) {
@@ -144,13 +150,15 @@ public class ResultList {
return Result.INVALID;
}
- public ResultList combineOR(ResultList other)
+ public ResultList combineOR(LazyResultList other)
{
+ if (Result.TRUE == toResult()) return ResultList.toResultList(true);
+
ResultList result = new ResultList();
// TODO: optimize
for (ResultPair pair : results) {
- for (ResultPair otherPair : other.results) {
+ for (ResultPair otherPair : other.getResult().results) {
FieldPathIteratorHandler.VariableMap varMap = (FieldPathIteratorHandler.VariableMap)pair.variables.clone();
if (combineVariables(varMap, otherPair.variables)) {
@@ -188,7 +196,7 @@ public class ResultList {
}
return retVal;
} else if (value == null || value == Result.FALSE || value == Boolean.FALSE ||
- (Number.class.isInstance(value) && ((Number)value).doubleValue() == 0)) {
+ (value instanceof Number && ((Number)value).doubleValue() == 0)) {
return new ResultList(Result.FALSE);
} else if (value == Result.INVALID) {
return new ResultList(Result.INVALID);
diff --git a/document/src/main/java/com/yahoo/document/select/convert/NowQueryExpression.java b/document/src/main/java/com/yahoo/document/select/convert/NowQueryExpression.java
index d379a73aec1..a4ce6afb099 100644
--- a/document/src/main/java/com/yahoo/document/select/convert/NowQueryExpression.java
+++ b/document/src/main/java/com/yahoo/document/select/convert/NowQueryExpression.java
@@ -16,7 +16,7 @@ public class NowQueryExpression {
private final ComparisonNode comparison;
private final NowQueryNode now;
- public NowQueryExpression(AttributeNode attribute, ComparisonNode comparison, ArithmeticNode arithmetic) {
+ NowQueryExpression(AttributeNode attribute, ComparisonNode comparison, ArithmeticNode arithmetic) {
this.attribute = attribute;
this.comparison = comparison;
this.now = (arithmetic != null ? new NowQueryNode(arithmetic) : new NowQueryNode(0));
diff --git a/document/src/main/java/com/yahoo/document/select/convert/NowQueryNode.java b/document/src/main/java/com/yahoo/document/select/convert/NowQueryNode.java
index 121fa0a45eb..0bd94a3854c 100644
--- a/document/src/main/java/com/yahoo/document/select/convert/NowQueryNode.java
+++ b/document/src/main/java/com/yahoo/document/select/convert/NowQueryNode.java
@@ -10,10 +10,10 @@ import com.yahoo.document.select.rule.ArithmeticNode;
*/
public class NowQueryNode {
private final long value;
- public NowQueryNode(long value) {
+ NowQueryNode(long value) {
this.value = value;
}
- public NowQueryNode(ArithmeticNode node) {
+ NowQueryNode(ArithmeticNode node) {
// Assumes that the structure is checked and verified earlier
this.value = Long.parseLong(node.getItems().get(1).getNode().toString());
}
diff --git a/document/src/main/java/com/yahoo/document/select/convert/SelectionExpressionConverter.java b/document/src/main/java/com/yahoo/document/select/convert/SelectionExpressionConverter.java
index 46721254b9f..a5708871529 100644
--- a/document/src/main/java/com/yahoo/document/select/convert/SelectionExpressionConverter.java
+++ b/document/src/main/java/com/yahoo/document/select/convert/SelectionExpressionConverter.java
@@ -3,7 +3,20 @@ package com.yahoo.document.select.convert;
import com.yahoo.document.select.NowCheckVisitor;
import com.yahoo.document.select.Visitor;
-import com.yahoo.document.select.rule.*;
+import com.yahoo.document.select.rule.ArithmeticNode;
+import com.yahoo.document.select.rule.AttributeNode;
+import com.yahoo.document.select.rule.ComparisonNode;
+import com.yahoo.document.select.rule.DocumentNode;
+import com.yahoo.document.select.rule.EmbracedNode;
+import com.yahoo.document.select.rule.ExpressionNode;
+import com.yahoo.document.select.rule.IdNode;
+import com.yahoo.document.select.rule.LiteralNode;
+import com.yahoo.document.select.rule.LogicNode;
+import com.yahoo.document.select.rule.NegationNode;
+import com.yahoo.document.select.rule.NowNode;
+import com.yahoo.document.select.rule.SearchColumnNode;
+import com.yahoo.document.select.rule.VariableNode;
+
import java.util.HashMap;
import java.util.Map;
@@ -15,14 +28,13 @@ import java.util.Map;
*/
public class SelectionExpressionConverter implements Visitor {
- private Map<String, NowQueryExpression> expressionMap = new HashMap<String, NowQueryExpression>();
+ private Map<String, NowQueryExpression> expressionMap = new HashMap<>();
private class BuildState {
public AttributeNode attribute;
public ComparisonNode comparison;
public ArithmeticNode arithmetic;
public NowNode now;
- public boolean hasNow() { return now != null; }
}
private BuildState state;
@@ -38,7 +50,7 @@ public class SelectionExpressionConverter implements Visitor {
}
public Map<String, String> getQueryMap() {
- Map<String, String> ret = new HashMap<String, String>();
+ Map<String, String> ret = new HashMap<>();
for (NowQueryExpression expression : expressionMap.values()) {
ret.put(expression.getDocumentType(), expression.toString());
}
@@ -92,7 +104,6 @@ public class SelectionExpressionConverter implements Visitor {
}
state.comparison = node;
if (state.attribute != null &&
- state.comparison != null &&
(state.arithmetic != null || state.now != null)) {
NowQueryExpression expression = new NowQueryExpression(state.attribute, state.comparison, state.arithmetic);
expressionMap.put(expression.getDocumentType(), expression);
diff --git a/document/src/main/java/com/yahoo/document/select/convert/package-info.java b/document/src/main/java/com/yahoo/document/select/convert/package-info.java
index a3cd3c7c55b..aa7276099b0 100644
--- a/document/src/main/java/com/yahoo/document/select/convert/package-info.java
+++ b/document/src/main/java/com/yahoo/document/select/convert/package-info.java
@@ -1,7 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
@ExportPackage
-@PublicApi
package com.yahoo.document.select.convert;
-import com.yahoo.api.annotations.PublicApi;
import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/document/src/main/java/com/yahoo/document/select/parser/SelectParserUtils.java b/document/src/main/java/com/yahoo/document/select/parser/SelectParserUtils.java
index 6537eefaa7a..2bfed803ea7 100644
--- a/document/src/main/java/com/yahoo/document/select/parser/SelectParserUtils.java
+++ b/document/src/main/java/com/yahoo/document/select/parser/SelectParserUtils.java
@@ -10,7 +10,7 @@ import java.math.BigInteger;
*/
public class SelectParserUtils {
- public static long decodeLong(String str) {
+ static long decodeLong(String str) {
if (str.startsWith("0x") || str.startsWith("0X")) {
str = Long.toString(new BigInteger(str.substring(2), 16).longValue());
}
diff --git a/document/src/main/java/com/yahoo/document/select/parser/package-info.java b/document/src/main/java/com/yahoo/document/select/parser/package-info.java
index 11cda77e3e4..476f0409c6f 100644
--- a/document/src/main/java/com/yahoo/document/select/parser/package-info.java
+++ b/document/src/main/java/com/yahoo/document/select/parser/package-info.java
@@ -1,7 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
@ExportPackage
-@PublicApi
package com.yahoo.document.select.parser;
-import com.yahoo.api.annotations.PublicApi;
import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/document/src/main/java/com/yahoo/document/select/rule/ArithmeticNode.java b/document/src/main/java/com/yahoo/document/select/rule/ArithmeticNode.java
index 813c3b27612..c39bd7e668d 100644
--- a/document/src/main/java/com/yahoo/document/select/rule/ArithmeticNode.java
+++ b/document/src/main/java/com/yahoo/document/select/rule/ArithmeticNode.java
@@ -3,7 +3,10 @@ package com.yahoo.document.select.rule;
import com.yahoo.document.BucketIdFactory;
import com.yahoo.document.datatypes.NumericFieldValue;
-import com.yahoo.document.select.*;
+import com.yahoo.document.select.BucketSet;
+import com.yahoo.document.select.Context;
+import com.yahoo.document.select.OrderingSpecification;
+import com.yahoo.document.select.Visitor;
import java.util.List;
import java.util.ArrayList;
@@ -21,7 +24,7 @@ public class ArithmeticNode implements ExpressionNode {
public static final int DIV = 4;
public static final int MUL = 5;
- private final List<NodeItem> items = new ArrayList<NodeItem>();
+ private final List<NodeItem> items = new ArrayList<>();
public ArithmeticNode() {
// empty
@@ -36,15 +39,15 @@ public class ArithmeticNode implements ExpressionNode {
return items;
}
- // Inherit doc from ExpressionNode.
+ @Override
public BucketSet getBucketSet(BucketIdFactory factory) {
return null;
}
- // Inherit doc from ExpressionNode.
+ @Override
public Object evaluate(Context context) {
StringBuilder ret = null;
- Stack<ValueItem> buf = new Stack<ValueItem>();
+ Stack<ValueItem> buf = new Stack<>();
for (int i = 0; i < items.size(); ++i) {
NodeItem item = items.get(i);
Object val = item.node.evaluate(context);
@@ -77,7 +80,7 @@ public class ArithmeticNode implements ExpressionNode {
ret.append(val);
continue;
}
- } else if (Number.class.isInstance(val)) {
+ } else if (val instanceof Number) {
if (!buf.isEmpty()) {
while (buf.peek().operator > item.operator) {
popOffTheTop(buf);
@@ -171,10 +174,12 @@ public class ArithmeticNode implements ExpressionNode {
}
}
+ @Override
public OrderingSpecification getOrdering(int order) {
return null;
}
+ @Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
@@ -183,7 +188,7 @@ public class ArithmeticNode implements ExpressionNode {
public int operator;
public Number value;
- public ValueItem(int operator, Number value) {
+ ValueItem(int operator, Number value) {
this.operator = operator;
this.value = value;
}
@@ -193,7 +198,7 @@ public class ArithmeticNode implements ExpressionNode {
private int operator;
private ExpressionNode node;
- public NodeItem(int operator, ExpressionNode node) {
+ NodeItem(int operator, ExpressionNode node) {
this.operator = operator;
this.node = node;
}
diff --git a/document/src/main/java/com/yahoo/document/select/rule/AttributeNode.java b/document/src/main/java/com/yahoo/document/select/rule/AttributeNode.java
index b6d75acd0ea..3e03709d0f1 100644
--- a/document/src/main/java/com/yahoo/document/select/rule/AttributeNode.java
+++ b/document/src/main/java/com/yahoo/document/select/rule/AttributeNode.java
@@ -2,10 +2,21 @@
package com.yahoo.document.select.rule;
import com.yahoo.collections.BobHash;
-import com.yahoo.document.*;
+import com.yahoo.document.BucketIdFactory;
+import com.yahoo.document.Document;
+import com.yahoo.document.DocumentGet;
+import com.yahoo.document.DocumentPut;
+import com.yahoo.document.DocumentRemove;
+import com.yahoo.document.DocumentUpdate;
+import com.yahoo.document.FieldPath;
import com.yahoo.document.datatypes.FieldPathIteratorHandler;
import com.yahoo.document.datatypes.FieldValue;
-import com.yahoo.document.select.*;
+import com.yahoo.document.select.BucketSet;
+import com.yahoo.document.select.Context;
+import com.yahoo.document.select.OrderingSpecification;
+import com.yahoo.document.select.Result;
+import com.yahoo.document.select.ResultList;
+import com.yahoo.document.select.Visitor;
import java.util.ArrayList;
import java.util.List;
@@ -16,7 +27,7 @@ import java.util.List;
public class AttributeNode implements ExpressionNode {
private ExpressionNode value;
- private final List<Item> items = new ArrayList<Item>();
+ private final List<Item> items = new ArrayList<>();
public AttributeNode(ExpressionNode value, List items) {
this.value = value;
@@ -43,14 +54,14 @@ public class AttributeNode implements ExpressionNode {
return items;
}
- // Inherit doc from ExpressionNode.
+ @Override
public BucketSet getBucketSet(BucketIdFactory factory) {
return null;
}
- // Inherit doc from ExpressionNode.
+ @Override
public Object evaluate(Context context) {
- String pos = value.toString();
+ StringBuilder pos = new StringBuilder(value.toString());
Object obj = value.evaluate(context);
StringBuilder builder = new StringBuilder();
@@ -74,7 +85,7 @@ public class AttributeNode implements ExpressionNode {
obj = evaluateFunction(item.getName(), obj);
}
- pos = pos + "." + item;
+ pos.append(".").append(item);
}
if (builder.length() > 0) {
@@ -98,7 +109,7 @@ public class AttributeNode implements ExpressionNode {
private static Object applyFunction(String function, Object value) {
if (function.equalsIgnoreCase("abs")) {
- if (Number.class.isInstance(value)) {
+ if (value instanceof Number) {
Number nValue = (Number)value;
if (value instanceof Double) {
return nValue.doubleValue() * (nValue.doubleValue() < 0 ? -1 : 1);
@@ -157,6 +168,7 @@ public class AttributeNode implements ExpressionNode {
return applyFunction(function, value);
}
+ @Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
@@ -171,6 +183,7 @@ public class AttributeNode implements ExpressionNode {
return ret.toString();
}
+ @Override
public OrderingSpecification getOrdering(int order) {
return null;
}
diff --git a/document/src/main/java/com/yahoo/document/select/rule/ComparisonNode.java b/document/src/main/java/com/yahoo/document/select/rule/ComparisonNode.java
index 13a990566e3..a7ab3d62c58 100644
--- a/document/src/main/java/com/yahoo/document/select/rule/ComparisonNode.java
+++ b/document/src/main/java/com/yahoo/document/select/rule/ComparisonNode.java
@@ -7,9 +7,13 @@ import com.yahoo.document.DocumentId;
import com.yahoo.document.datatypes.FieldPathIteratorHandler;
import com.yahoo.document.datatypes.NumericFieldValue;
import com.yahoo.document.idstring.GroupDocIdString;
-import com.yahoo.document.select.*;
+import com.yahoo.document.select.BucketSet;
+import com.yahoo.document.select.Context;
+import com.yahoo.document.select.OrderingSpecification;
+import com.yahoo.document.select.Result;
+import com.yahoo.document.select.ResultList;
+import com.yahoo.document.select.Visitor;
-import java.util.List;
import java.util.regex.Pattern;
/**
@@ -46,17 +50,6 @@ public class ComparisonNode implements ExpressionNode {
}
/**
- * Sets the left hand side of this comparison.
- *
- * @param lhs The new left hand side.
- * @return This, to allow chaining.
- */
- public ComparisonNode setLHS(ExpressionNode lhs) {
- this.lhs = lhs;
- return this;
- }
-
- /**
* Returns the comparison operator of this.
*
* @return The operator.
@@ -85,17 +78,6 @@ public class ComparisonNode implements ExpressionNode {
return rhs;
}
- /**
- * Sets the right hand side of this comparison.
- *
- * @param rhs The new right hand side.
- * @return This, to allow chaining.
- */
- public ComparisonNode setRHS(ExpressionNode rhs) {
- this.rhs = rhs;
- return this;
- }
-
public OrderingSpecification getOrdering(IdNode lhs, LiteralNode rhs, String operator, int order) {
if (lhs.getWidthBits() == -1 || lhs.getDivisionBits() == -1 || !(rhs.getValue() instanceof Long)) {
return null;
@@ -126,17 +108,18 @@ public class ComparisonNode implements ExpressionNode {
return null;
}
+ @Override
public OrderingSpecification getOrdering(int order) {
if (lhs instanceof IdNode && rhs instanceof LiteralNode) {
return getOrdering((IdNode)lhs, (LiteralNode)rhs, operator, order);
} else if (rhs instanceof IdNode && lhs instanceof LiteralNode) {
- return getOrdering((IdNode)rhs, (LiteralNode)rhs, operator, order);
+ return getOrdering((IdNode)rhs, (LiteralNode)lhs, operator, order);
}
return null;
}
- // Inherit doc from ExpressionNode.
+ @Override
public BucketSet getBucketSet(BucketIdFactory factory) {
if (operator.equals("==") || operator.equals("=")) {
if (lhs instanceof IdNode && rhs instanceof LiteralNode) {
@@ -144,9 +127,9 @@ public class ComparisonNode implements ExpressionNode {
} else if (rhs instanceof IdNode && lhs instanceof LiteralNode) {
return compare(factory, (IdNode)rhs, (LiteralNode)lhs, operator);
} else if (lhs instanceof SearchColumnNode && rhs instanceof LiteralNode) {
- return compare(factory, (SearchColumnNode)lhs, (LiteralNode)rhs);
+ return compare((SearchColumnNode)lhs, (LiteralNode)rhs);
} else if (rhs instanceof SearchColumnNode && lhs instanceof LiteralNode) {
- return compare(factory, (SearchColumnNode)rhs, (LiteralNode)lhs);
+ return compare((SearchColumnNode)rhs, (LiteralNode)lhs);
}
}
return null;
@@ -155,12 +138,11 @@ public class ComparisonNode implements ExpressionNode {
/**
* Compares a search column node with a literal node.
*
- * @param factory The bucket id factory used.
* @param node The search column node.
* @param literal The literal node to compare to.
* @return The bucket set containing the buckets covered.
*/
- private BucketSet compare(BucketIdFactory factory, SearchColumnNode node, LiteralNode literal) {
+ private BucketSet compare(SearchColumnNode node, LiteralNode literal) {
Object value = literal.getValue();
int bucketCount = (int) Math.pow(2, 16);
if (value instanceof Long) {
@@ -211,7 +193,7 @@ public class ComparisonNode implements ExpressionNode {
return null;
}
- // Inherit doc from Node.
+ @Override
public Object evaluate(Context context) {
Object oLeft = lhs.evaluate(context);
Object oRight = rhs.evaluate(context);
@@ -254,7 +236,7 @@ public class ComparisonNode implements ExpressionNode {
}
}
- public ResultList evaluateListsTrue(AttributeNode.VariableValueList lhs, AttributeNode.VariableValueList rhs) {
+ private ResultList evaluateListsTrue(AttributeNode.VariableValueList lhs, AttributeNode.VariableValueList rhs) {
if (lhs.size() != rhs.size()) {
return new ResultList(Result.FALSE);
}
@@ -272,7 +254,7 @@ public class ComparisonNode implements ExpressionNode {
return new ResultList(Result.TRUE);
}
- public ResultList evaluateListsFalse(AttributeNode.VariableValueList lhs, AttributeNode.VariableValueList rhs) {
+ private ResultList evaluateListsFalse(AttributeNode.VariableValueList lhs, AttributeNode.VariableValueList rhs) {
ResultList lst = evaluateListsTrue(lhs, rhs);
if (lst.toResult() == Result.TRUE) {
return new ResultList(Result.FALSE);
@@ -283,7 +265,7 @@ public class ComparisonNode implements ExpressionNode {
}
}
- public ResultList evaluateListAndSingle(AttributeNode.VariableValueList lhs, Object rhs) {
+ private ResultList evaluateListAndSingle(AttributeNode.VariableValueList lhs, Object rhs) {
if (rhs == null && lhs == null) {
return new ResultList(Result.TRUE);
}
@@ -293,9 +275,9 @@ public class ComparisonNode implements ExpressionNode {
}
ResultList retVal = new ResultList();
- for (int i = 0; i < lhs.size(); i++) {
- Result result = evaluateBool(lhs.get(i).getValue(), rhs);
- retVal.add((FieldPathIteratorHandler.VariableMap)lhs.get(i).getVariables().clone(), result);
+ for (ResultList.VariableValue value : lhs) {
+ Result result = evaluateBool(value.getValue(), rhs);
+ retVal.add((FieldPathIteratorHandler.VariableMap)value.getVariables().clone(), result);
}
return retVal;
@@ -440,11 +422,11 @@ public class ComparisonNode implements ExpressionNode {
}
}
+ @Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
- // Inherit doc from Object.
@Override
public String toString() {
return lhs + " " + operator + " " + rhs;
diff --git a/document/src/main/java/com/yahoo/document/select/rule/DocumentNode.java b/document/src/main/java/com/yahoo/document/select/rule/DocumentNode.java
index c0907693dab..9b766a56eb3 100644
--- a/document/src/main/java/com/yahoo/document/select/rule/DocumentNode.java
+++ b/document/src/main/java/com/yahoo/document/select/rule/DocumentNode.java
@@ -1,7 +1,13 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.document.select.rule;
-import com.yahoo.document.*;
+import com.yahoo.document.BucketIdFactory;
+import com.yahoo.document.DocumentGet;
+import com.yahoo.document.DocumentOperation;
+import com.yahoo.document.DocumentPut;
+import com.yahoo.document.DocumentRemove;
+import com.yahoo.document.DocumentType;
+import com.yahoo.document.DocumentUpdate;
import com.yahoo.document.select.BucketSet;
import com.yahoo.document.select.Context;
import com.yahoo.document.select.OrderingSpecification;
diff --git a/document/src/main/java/com/yahoo/document/select/rule/EmbracedNode.java b/document/src/main/java/com/yahoo/document/select/rule/EmbracedNode.java
index e1bd4b2d53f..7bf3413bafb 100644
--- a/document/src/main/java/com/yahoo/document/select/rule/EmbracedNode.java
+++ b/document/src/main/java/com/yahoo/document/select/rule/EmbracedNode.java
@@ -27,11 +27,12 @@ public class EmbracedNode implements ExpressionNode {
return this;
}
- // Inherit doc from ExpressionNode.
+ @Override
public BucketSet getBucketSet(BucketIdFactory factory) {
return node.getBucketSet(factory);
}
+ @Override
public Object evaluate(Context context) {
return node.evaluate(context);
}
@@ -41,10 +42,12 @@ public class EmbracedNode implements ExpressionNode {
return "(" + node + ")";
}
+ @Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
+ @Override
public OrderingSpecification getOrdering(int order) {
return null;
}
diff --git a/document/src/main/java/com/yahoo/document/select/rule/ExpressionNode.java b/document/src/main/java/com/yahoo/document/select/rule/ExpressionNode.java
index 29f49c4459c..9350b8ac1c5 100644
--- a/document/src/main/java/com/yahoo/document/select/rule/ExpressionNode.java
+++ b/document/src/main/java/com/yahoo/document/select/rule/ExpressionNode.java
@@ -21,14 +21,14 @@ public interface ExpressionNode {
* @param doc The document to evaluate over.
* @return The value of this.
*/
- public Object evaluate(Context doc);
+ Object evaluate(Context doc);
/**
* Returns the set of bucket ids covered by this node.
*
* @param factory The factory used by the current application.
*/
- public BucketSet getBucketSet(BucketIdFactory factory);
+ BucketSet getBucketSet(BucketIdFactory factory);
/**
* If this document selection implies a specific ordering (using the orderdoc scheme),
@@ -36,13 +36,13 @@ public interface ExpressionNode {
*
* @param order The order in which we are looking to traverse the ordering (ASCENDING or DESCENDING)
*/
- public OrderingSpecification getOrdering(int order);
+ OrderingSpecification getOrdering(int order);
/**
* Perform visitation of this node.
*
* @param visitor The visitor that wishes to visit the node.
*/
- public void accept(Visitor visitor);
+ void accept(Visitor visitor);
}
diff --git a/document/src/main/java/com/yahoo/document/select/rule/IdNode.java b/document/src/main/java/com/yahoo/document/select/rule/IdNode.java
index 3c15a2866cc..3ca53c1d07d 100644
--- a/document/src/main/java/com/yahoo/document/select/rule/IdNode.java
+++ b/document/src/main/java/com/yahoo/document/select/rule/IdNode.java
@@ -3,8 +3,11 @@ package com.yahoo.document.select.rule;
import com.yahoo.document.DocumentId;
import com.yahoo.document.BucketIdFactory;
-import com.yahoo.document.select.*;
-import com.yahoo.document.idstring.*;
+import com.yahoo.document.idstring.OrderDocIdString;
+import com.yahoo.document.select.BucketSet;
+import com.yahoo.document.select.Context;
+import com.yahoo.document.select.OrderingSpecification;
+import com.yahoo.document.select.Visitor;
/**
* @author Simon Thoresen Hult
@@ -46,7 +49,7 @@ public class IdNode implements ExpressionNode {
return divisionBits;
}
- // Inherit doc from ExpressionNode.
+ @Override
public BucketSet getBucketSet(BucketIdFactory factory) {
return null;
}
@@ -55,7 +58,7 @@ public class IdNode implements ExpressionNode {
return null;
}
- // Inherit doc from ExpressionNode.
+ @Override
public Object evaluate(Context context) {
DocumentId id = context.getDocumentOperation().getId();
if (id == null) {
@@ -97,6 +100,7 @@ public class IdNode implements ExpressionNode {
return null;
}
+ @Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
diff --git a/document/src/main/java/com/yahoo/document/select/rule/LogicNode.java b/document/src/main/java/com/yahoo/document/select/rule/LogicNode.java
index a7b112fac70..1ae397398b3 100644
--- a/document/src/main/java/com/yahoo/document/select/rule/LogicNode.java
+++ b/document/src/main/java/com/yahoo/document/select/rule/LogicNode.java
@@ -30,7 +30,7 @@ public class LogicNode implements ExpressionNode {
public static final int AND = 2;
// The items contained in this.
- private final List<NodeItem> items = new ArrayList<NodeItem>();
+ private final List<NodeItem> items = new ArrayList<>();
/**
* Construct an empty logic expression.
@@ -55,7 +55,7 @@ public class LogicNode implements ExpressionNode {
return this;
}
- // Inherit doc from ExpressionNode.
+ @Override
public BucketSet getBucketSet(BucketIdFactory factory) {
Stack<BucketItem> buf = new Stack<>();
for (NodeItem item : items) {
@@ -72,6 +72,7 @@ public class LogicNode implements ExpressionNode {
return buf.pop().buckets;
}
+ @Override
public OrderingSpecification getOrdering(int order) {
Stack<OrderingItem> buf = new Stack<>();
for (NodeItem item : items) {
@@ -159,23 +160,21 @@ public class LogicNode implements ExpressionNode {
buf.push(lhs);
}
- // Inherit doc from ExpressionNode.
@Override
public Object evaluate(Context context) {
Stack<ValueItem> buf = new Stack<>();
for (NodeItem item : items) {
- if ( ! buf.isEmpty()) {
- while (buf.peek().operator > item.operator) {
+ if ( buf.size() > 1) {
+ while ((buf.peek().getOperator() >= item.operator)) {
combineValues(buf);
}
}
-
- buf.push(new ValueItem(item.operator, ResultList.toResultList(item.node.evaluate(context))));
+ buf.push(new LazyValueItem(item, context));
}
while (buf.size() > 1) {
combineValues(buf);
}
- return buf.pop().value;
+ return buf.pop().getResult();
}
/**
@@ -186,24 +185,14 @@ public class LogicNode implements ExpressionNode {
private void combineValues(Stack<ValueItem> buf) {
ValueItem rhs = buf.pop();
ValueItem lhs = buf.pop();
-
- switch (rhs.operator) {
- case AND:
- buf.push(new ValueItem(lhs.operator, lhs.value.combineAND(rhs.value)));
- break;
- case OR:
- buf.push(new ValueItem(lhs.operator, lhs.value.combineOR(rhs.value)));
- break;
- default:
- throw new IllegalStateException("Arithmetic operator " + rhs.operator + " not supported.");
- }
+ buf.push(new LazyCombinedItem(lhs, rhs));
}
+ @Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
- // Inherit doc from Object.
@Override
public String toString() {
StringBuilder ret = new StringBuilder();
@@ -222,7 +211,7 @@ public class LogicNode implements ExpressionNode {
* @param operator The operator index to convert.
* @return The string representation.
*/
- public String operatorToString(int operator) {
+ private String operatorToString(int operator) {
switch (operator) {
case NOP:
return null;
@@ -257,13 +246,58 @@ public class LogicNode implements ExpressionNode {
/**
* Private class to store results in a stack.
*/
- private final class ValueItem {
- private int operator;
- private ResultList value;
-
- public ValueItem(int operator, ResultList value) {
+ private abstract class ValueItem implements ResultList.LazyResultList {
+ private final int operator;
+ ValueItem(int operator) {
this.operator = operator;
- this.value = value;
+ }
+ int getOperator() { return operator; }
+ }
+
+ private final class LazyValueItem extends ValueItem {
+ private final NodeItem item;
+ private final Context context;
+ private ResultList lazyResult = null;
+
+ LazyValueItem(NodeItem item, Context context) {
+ super(item.operator);
+ this.item = item;
+ this.context = context;
+ }
+ @Override
+ public ResultList getResult() {
+ if (lazyResult == null) {
+ lazyResult = ResultList.toResultList(item.node.evaluate(context));
+ }
+ return lazyResult;
+ }
+ }
+
+ private final class LazyCombinedItem extends ValueItem {
+ private final ValueItem lhs;
+ private final ValueItem rhs;
+ private ResultList lazyResult = null;
+
+ LazyCombinedItem(ValueItem lhs, ValueItem rhs) {
+ super(lhs.getOperator());
+ this.lhs = lhs;
+ this.rhs = rhs;
+ }
+ @Override
+ public ResultList getResult() {
+ if (lazyResult == null) {
+ switch (rhs.getOperator()) {
+ case AND:
+ lazyResult = lhs.getResult().combineAND(rhs);
+ break;
+ case OR:
+ lazyResult = lhs.getResult().combineOR(rhs);
+ break;
+ default:
+ throw new IllegalStateException("Logical operator " + rhs.getOperator() + " not supported.");
+ }
+ }
+ return lazyResult;
}
}
@@ -274,7 +308,7 @@ public class LogicNode implements ExpressionNode {
private int operator;
private BucketSet buckets;
- public BucketItem(int operator, BucketSet buckets) {
+ BucketItem(int operator, BucketSet buckets) {
this.operator = operator;
this.buckets = buckets;
}
@@ -287,7 +321,7 @@ public class LogicNode implements ExpressionNode {
private int operator;
private OrderingSpecification ordering;
- public OrderingItem(int operator, OrderingSpecification orderSpec) {
+ OrderingItem(int operator, OrderingSpecification orderSpec) {
this.operator = operator;
this.ordering = orderSpec;
}
@@ -300,7 +334,7 @@ public class LogicNode implements ExpressionNode {
private int operator;
private ExpressionNode node;
- public NodeItem(int operator, ExpressionNode node) {
+ NodeItem(int operator, ExpressionNode node) {
this.operator = operator;
this.node = node;
}
diff --git a/document/src/main/java/com/yahoo/document/select/rule/NegationNode.java b/document/src/main/java/com/yahoo/document/select/rule/NegationNode.java
index c89759bbf07..ba75b13747d 100644
--- a/document/src/main/java/com/yahoo/document/select/rule/NegationNode.java
+++ b/document/src/main/java/com/yahoo/document/select/rule/NegationNode.java
@@ -28,16 +28,17 @@ public class NegationNode implements ExpressionNode {
return this;
}
- // Inherit doc from ExpressionNode.
+ @Override
public BucketSet getBucketSet(BucketIdFactory factory) {
return null;
}
- // Inherit doc from ExpressionNode.
+ @Override
public Object evaluate(Context context) {
return Result.invert(Result.toResult(node.evaluate(context)));
}
+ @Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
@@ -47,6 +48,7 @@ public class NegationNode implements ExpressionNode {
return "not " + node;
}
+ @Override
public OrderingSpecification getOrdering(int order) {
return null;
}
diff --git a/document/src/main/java/com/yahoo/document/select/rule/NowNode.java b/document/src/main/java/com/yahoo/document/select/rule/NowNode.java
index 900423addff..567d9ddcb1f 100644
--- a/document/src/main/java/com/yahoo/document/select/rule/NowNode.java
+++ b/document/src/main/java/com/yahoo/document/select/rule/NowNode.java
@@ -1,22 +1,25 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.document.select.rule;
+
import com.yahoo.document.BucketIdFactory;
-import com.yahoo.document.select.*;
+import com.yahoo.document.select.BucketSet;
+import com.yahoo.document.select.Context;
+import com.yahoo.document.select.OrderingSpecification;
+import com.yahoo.document.select.Visitor;
/**
* @author Ulf Lilleengen
*/
public class NowNode implements ExpressionNode {
- // Inherit doc from ExpressionNode.
+ @Override
public BucketSet getBucketSet(BucketIdFactory factory) {
return null;
}
- // Inherit doc from ExpressionNode.
+ @Override
public Object evaluate(Context context) {
- Object ret = System.currentTimeMillis() / 1000;
- return ret;
+ return System.currentTimeMillis() / 1000;
}
@Override
@@ -24,10 +27,11 @@ public class NowNode implements ExpressionNode {
return "now()";
}
+ @Override
public OrderingSpecification getOrdering(int order) {
return null;
}
-
+ @Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
diff --git a/document/src/main/java/com/yahoo/document/select/rule/SearchColumnNode.java b/document/src/main/java/com/yahoo/document/select/rule/SearchColumnNode.java
index ffcf576dcfc..071e51c192f 100644
--- a/document/src/main/java/com/yahoo/document/select/rule/SearchColumnNode.java
+++ b/document/src/main/java/com/yahoo/document/select/rule/SearchColumnNode.java
@@ -3,7 +3,10 @@ package com.yahoo.document.select.rule;
import com.yahoo.document.BucketDistribution;
import com.yahoo.document.BucketIdFactory;
-import com.yahoo.document.select.*;
+import com.yahoo.document.select.BucketSet;
+import com.yahoo.document.select.Context;
+import com.yahoo.document.select.OrderingSpecification;
+import com.yahoo.document.select.Visitor;
/**
* @author Simon Thoresen Hult
@@ -31,16 +34,17 @@ public class SearchColumnNode implements ExpressionNode {
return distribution;
}
- // Inherit doc from ExpressionNode.
+ @Override
public BucketSet getBucketSet(BucketIdFactory factory) {
return null;
}
- // Inherit doc from ExpressionNode.
+ @Override
public Object evaluate(Context context) {
return distribution.getColumn(factory.getBucketId(context.getDocumentOperation().getId()));
}
+ @Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
@@ -50,6 +54,7 @@ public class SearchColumnNode implements ExpressionNode {
return "searchcolumn." + field;
}
+ @Override
public OrderingSpecification getOrdering(int order) {
return null;
}
diff --git a/document/src/main/java/com/yahoo/document/select/rule/package-info.java b/document/src/main/java/com/yahoo/document/select/rule/package-info.java
index 0b80dd5f25d..6c643397264 100644
--- a/document/src/main/java/com/yahoo/document/select/rule/package-info.java
+++ b/document/src/main/java/com/yahoo/document/select/rule/package-info.java
@@ -1,7 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
@ExportPackage
-@PublicApi
package com.yahoo.document.select.rule;
-import com.yahoo.api.annotations.PublicApi;
import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/document/src/main/java/com/yahoo/document/select/simple/IdSpecParser.java b/document/src/main/java/com/yahoo/document/select/simple/IdSpecParser.java
index f792c4b8c0f..db4d46e871b 100644
--- a/document/src/main/java/com/yahoo/document/select/simple/IdSpecParser.java
+++ b/document/src/main/java/com/yahoo/document/select/simple/IdSpecParser.java
@@ -9,7 +9,7 @@ import com.yahoo.document.select.rule.IdNode;
public class IdSpecParser extends Parser {
private IdNode id;
public IdNode getId() { return id; }
- public boolean isUserSpec() { return "user".equals(id.getField()); }
+ boolean isUserSpec() { return "user".equals(id.getField()); }
public boolean parse(CharSequence s) {
boolean retval = false;
int pos = eatWhite(s);
diff --git a/document/src/main/java/com/yahoo/document/select/simple/StringParser.java b/document/src/main/java/com/yahoo/document/select/simple/StringParser.java
index d43c57aee03..50d4915e360 100644
--- a/document/src/main/java/com/yahoo/document/select/simple/StringParser.java
+++ b/document/src/main/java/com/yahoo/document/select/simple/StringParser.java
@@ -14,7 +14,7 @@ public class StringParser extends Parser {
int pos = eatWhite(s);
if (pos + 1 < s.length()) {
if (s.charAt(pos++) == '"') {
- StringBuffer str = new StringBuffer("");
+ StringBuilder str = new StringBuilder();
for(; (pos < s.length()) && (s.charAt(pos) != '"');pos++) {
if ((pos < s.length()) && (s.charAt(pos) == '\\')) {
pos++;
diff --git a/document/src/main/java/com/yahoo/document/select/simple/package-info.java b/document/src/main/java/com/yahoo/document/select/simple/package-info.java
index e6885d54fd4..936d63a7697 100644
--- a/document/src/main/java/com/yahoo/document/select/simple/package-info.java
+++ b/document/src/main/java/com/yahoo/document/select/simple/package-info.java
@@ -1,7 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
@ExportPackage
-@PublicApi
package com.yahoo.document.select.simple;
-import com.yahoo.api.annotations.PublicApi;
import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/document/src/test/java/com/yahoo/document/select/LogicalNodeTestCase.java b/document/src/test/java/com/yahoo/document/select/LogicalNodeTestCase.java
new file mode 100644
index 00000000000..25aca22b108
--- /dev/null
+++ b/document/src/test/java/com/yahoo/document/select/LogicalNodeTestCase.java
@@ -0,0 +1,126 @@
+package com.yahoo.document.select;
+
+import com.yahoo.document.BucketIdFactory;
+import com.yahoo.document.select.rule.ExpressionNode;
+import com.yahoo.document.select.rule.LiteralNode;
+import com.yahoo.document.select.rule.LogicNode;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class LogicalNodeTestCase {
+ private static class TracedNode implements ExpressionNode {
+ private final AtomicInteger evalOrder;
+ private final ExpressionNode node;
+ private int evaluatedAs = -1;
+
+ TracedNode(AtomicInteger evalOrder, ExpressionNode node) {
+ this.evalOrder = evalOrder;
+ this.node = node;
+ }
+ @Override
+ public Object evaluate(Context doc) {
+ evaluatedAs = evalOrder.getAndIncrement();
+ return node.evaluate(doc);
+ }
+
+ @Override
+ public BucketSet getBucketSet(BucketIdFactory factory) {
+ return node.getBucketSet(factory);
+ }
+
+ @Override
+ public OrderingSpecification getOrdering(int order) {
+ return node.getOrdering(order);
+ }
+
+ @Override
+ public void accept(Visitor visitor) {
+ node.accept(visitor);
+ }
+ boolean isEvaluated() { return evaluatedAs >= 0; }
+ int getEvalOrder() { return evaluatedAs; }
+ }
+ private static Result evaluate(ExpressionNode node) {
+ return ((ResultList)node.evaluate(new Context(null))).toResult();
+ }
+
+ private static TracedNode createTraced(AtomicInteger evalOrder, char node) {
+ return new TracedNode(evalOrder, new LiteralNode(node == 'T'));
+ }
+
+ private static void addOperator(LogicNode logical, char operator, ExpressionNode node) {
+ if (operator == '&') {
+ logical.add("and", node);
+ } else if (operator == '|') {
+ logical.add("or", node);
+ } else {
+ throw new IllegalArgumentException("Bad operator '" + operator + "'");
+ }
+ }
+
+ static private void verifyEvaluationOrder(String expr, boolean expectedResult, List<Integer> expectedEvaluationOrder ) {
+ assertEquals(1, expr.length()%2);
+ assertEquals(expectedEvaluationOrder.size()*2 - 1, expr.length());
+ TracedNode [] traced = new TracedNode[expectedEvaluationOrder.size()];
+ AtomicInteger evalOrder = new AtomicInteger(0);
+ for (int i=0; i < traced.length; i++) {
+ traced[i] = createTraced(evalOrder, expr.charAt(i*2));
+ }
+ LogicNode logical = new LogicNode().add(null, traced[0]);
+ for (int i=1; i < traced.length; i++) {
+ addOperator(logical, expr.charAt(i*2-1), traced[i]);
+ }
+ for (TracedNode node : traced) {
+ assertFalse(node.isEvaluated());
+ }
+ assertEquals(Result.toResult(expectedResult), evaluate(logical));
+ for (int i = 0; i < traced.length; i++) {
+ assertEquals(expectedEvaluationOrder.get(i).intValue(), traced[i].getEvalOrder());
+ }
+ }
+ @Test
+ public void testFullyExhaustedAND() {
+ verifyEvaluationOrder("T&T", true, List.of(0,1));
+
+ }
+ @Test
+ public void testShortCircuitAND() {
+ verifyEvaluationOrder("F&T", false, List.of(0,-1));
+ }
+
+ @Test
+ public void testFullyExhaustedOR() {
+ verifyEvaluationOrder("F|T", true, List.of(0,1));
+ }
+
+ @Test
+ public void testShortCircuitOR() {
+ verifyEvaluationOrder("T|F", true, List.of(0,-1));
+ }
+
+ @Test
+ public void testLeft2Right() {
+ verifyEvaluationOrder("T&T&T&T&T", true, List.of(0,1,2,3,4));
+ verifyEvaluationOrder("T&T&F&T&F", false, List.of(0,1,2,-1,-1));
+
+ verifyEvaluationOrder("F|F|F|F|T", true, List.of(0,1,2,3,4));
+ verifyEvaluationOrder("F|F|F|F|F", false, List.of(0,1,2,3,4));
+ verifyEvaluationOrder("F|F|T|F|T", true, List.of(0,1,2,-1,-1));
+ }
+
+ @Test
+ public void testLeft2RightWithPriority() {
+ verifyEvaluationOrder("T&F|T", true, List.of(0,1,2));
+ verifyEvaluationOrder("F&T|T", true, List.of(0,-1,1));
+
+ verifyEvaluationOrder("T|F&T", true, List.of(0,-1,-1));
+ verifyEvaluationOrder("F|F&T", false, List.of(0,1,-1));
+ verifyEvaluationOrder("F|T&T", true, List.of(0,1,2));
+ }
+}
diff --git a/document/src/tests/documenttestcase.cpp b/document/src/tests/documenttestcase.cpp
index bb07f63a83b..5521fac58e8 100644
--- a/document/src/tests/documenttestcase.cpp
+++ b/document/src/tests/documenttestcase.cpp
@@ -1161,7 +1161,7 @@ TEST(DocumentTest, testCompressionConfigured)
Struct("serializetest.body").setId(45)
.addField("stringfield", DataType::T_STRING)
.setCompression(DocumenttypesConfig::Documenttype::
- Datatype::Sstruct::Compression::LZ4,
+ Datatype::Sstruct::Compression::Type::LZ4,
9, 99, 0));
DocumentTypeRepo repo2(builder2.config());
diff --git a/document/src/tests/gid_filter_test.cpp b/document/src/tests/gid_filter_test.cpp
index dcfafb56b99..fec6581b8f7 100644
--- a/document/src/tests/gid_filter_test.cpp
+++ b/document/src/tests/gid_filter_test.cpp
@@ -19,8 +19,10 @@ protected:
Fixture(vespalib::stringref selection);
~Fixture();
- Fixture(Fixture&&) = default;
- Fixture& operator=(Fixture&&) = default;
+ Fixture(const Fixture&) = delete;
+ Fixture(Fixture&&) = delete;
+ Fixture& operator=(const Fixture&) = delete;
+ Fixture& operator=(Fixture&&) = delete;
static Fixture for_selection(vespalib::stringref s) {
return Fixture(s);
diff --git a/document/src/tests/repo/documenttyperepo_test.cpp b/document/src/tests/repo/documenttyperepo_test.cpp
index 64900d2ad65..b263ad75930 100644
--- a/document/src/tests/repo/documenttyperepo_test.cpp
+++ b/document/src/tests/repo/documenttyperepo_test.cpp
@@ -86,7 +86,7 @@ TEST("requireThatStructsCanConfigureCompression") {
builder.document(doc_type_id, type_name,
Struct(header_name),
Struct(body_name).setCompression(
- Sstruct::Compression::LZ4,
+ Sstruct::Compression::Type::LZ4,
comp_level, comp_minres, comp_minsize));
DocumentTypeRepo repo(builder.config());
diff --git a/document/src/vespa/document/repo/configbuilder.cpp b/document/src/vespa/document/repo/configbuilder.cpp
index 9610697b84f..7d9b607facd 100644
--- a/document/src/vespa/document/repo/configbuilder.cpp
+++ b/document/src/vespa/document/repo/configbuilder.cpp
@@ -62,8 +62,8 @@ DocTypeRep
DocumenttypesConfigBuilderHelper::document(int32_t id, const vespalib::string &name,
const DatatypeConfig &header,
const DatatypeConfig &body) {
- assert(header.type == DatatypeConfig::STRUCT);
- assert(body.type == DatatypeConfig::STRUCT);
+ assert(header.type == DatatypeConfig::Type::STRUCT);
+ assert(body.type == DatatypeConfig::Type::STRUCT);
_config.documenttype.resize(_config.documenttype.size() + 1);
_config.documenttype.back().id = id;
_config.documenttype.back().name = name;
diff --git a/document/src/vespa/document/repo/configbuilder.h b/document/src/vespa/document/repo/configbuilder.h
index 5f6f5548ae2..15ee0da0c79 100644
--- a/document/src/vespa/document/repo/configbuilder.h
+++ b/document/src/vespa/document/repo/configbuilder.h
@@ -39,7 +39,7 @@ struct TypeOrId {
struct Struct : DatatypeConfig {
Struct(const vespalib::string &name) {
- type = STRUCT;
+ type = Type::STRUCT;
sstruct.name = name;
}
Struct &setCompression(Sstruct::Compression::Type t, int32_t level,
@@ -65,7 +65,7 @@ struct Struct : DatatypeConfig {
struct Array : DatatypeConfig {
Array(TypeOrId nested_type) {
addNestedType(nested_type);
- type = ARRAY;
+ type = Type::ARRAY;
array.element.id = nested_type.id;
}
};
@@ -73,7 +73,7 @@ struct Array : DatatypeConfig {
struct Wset : DatatypeConfig {
Wset(TypeOrId nested_type) {
addNestedType(nested_type);
- type = WSET;
+ type = Type::WSET;
wset.key.id = nested_type.id;
}
Wset &removeIfZero() { wset.removeifzero = true; return *this; }
@@ -87,7 +87,7 @@ struct Map : DatatypeConfig {
Map(TypeOrId key_type, TypeOrId value_type) {
addNestedType(key_type);
addNestedType(value_type);
- type = MAP;
+ type = Type::MAP;
map.key.id = key_type.id;
map.value.id = value_type.id;
}
@@ -95,7 +95,7 @@ struct Map : DatatypeConfig {
struct AnnotationRef : DatatypeConfig {
AnnotationRef(int32_t annotation_type_id) {
- type = ANNOTATIONREF;
+ type = Type::ANNOTATIONREF;
annotationref.annotation.id = annotation_type_id;
}
};
diff --git a/document/src/vespa/document/repo/documenttyperepo.cpp b/document/src/vespa/document/repo/documenttyperepo.cpp
index bdecd521f44..da59f527115 100644
--- a/document/src/vespa/document/repo/documenttyperepo.cpp
+++ b/document/src/vespa/document/repo/documenttyperepo.cpp
@@ -310,7 +310,7 @@ void addStruct(int32_t id, const Datatype::Sstruct &s, Repo &repo) {
}
CompressionConfig::Type type = CompressionConfig::NONE;
- if (s.compression.type == Datatype::Sstruct::Compression::LZ4) {
+ if (s.compression.type == Datatype::Sstruct::Compression::Type::LZ4) {
type = CompressionConfig::LZ4;
}
@@ -348,18 +348,18 @@ void addAnnotationRef(int32_t id, const Datatype::Annotationref &a, Repo &r, con
void addDataType(const Datatype &type, Repo &repo, const AnnotationTypeRepo &a_repo) {
switch (type.type) {
- case Datatype::STRUCT:
+ case Datatype::Type::STRUCT:
return addStruct(type.id, type.sstruct, repo);
- case Datatype::ARRAY:
+ case Datatype::Type::ARRAY:
return addArray(type.id, type.array, repo);
- case Datatype::WSET:
+ case Datatype::Type::WSET:
return addWset(type.id, type.wset, repo);
- case Datatype::MAP:
+ case Datatype::Type::MAP:
return addMap(type.id, type.map, repo);
- case Datatype::ANNOTATIONREF:
+ case Datatype::Type::ANNOTATIONREF:
return addAnnotationRef(type.id, type.annotationref, repo, a_repo);
default:
- throw IllegalArgumentException(make_string("Unknown datatype type %d for id %d", type.type, type.id));
+ throw IllegalArgumentException(make_string("Unknown datatype type %d for id %d", static_cast<int>(type.type), type.id));
}
}
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/SyncParameters.java b/documentapi/src/main/java/com/yahoo/documentapi/SyncParameters.java
index acf027dd0aa..29d04c85a05 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/SyncParameters.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/SyncParameters.java
@@ -11,6 +11,7 @@ import java.util.Optional;
* @author Simon Thoresen Hult
*/
public class SyncParameters extends Parameters {
+
private final Duration defaultTimeout;
private SyncParameters() {
@@ -26,6 +27,7 @@ public class SyncParameters extends Parameters {
}
public static class Builder {
+
private Duration defaultTimeout;
/**
@@ -39,4 +41,5 @@ public class SyncParameters extends Parameters {
return new SyncParameters(defaultTimeout);
}
}
+
}
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/SyncSession.java b/documentapi/src/main/java/com/yahoo/documentapi/SyncSession.java
index 418c0374193..6c4306b683c 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/SyncSession.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/SyncSession.java
@@ -51,13 +51,11 @@ public interface SyncSession extends Session {
/**
* Gets a document with an unspecified timeout
*
- * @param id The id of the document to get.
- * @param fieldSet A comma-separated list of fields to retrieve
- * @param priority The priority with which to perform this operation.
- * @return The known document having this id, or null if there is no
- * document having this id.
- * @throws UnsupportedOperationException Thrown if this access does not
- * support retrieving.
+ * @param id the id of the document to get
+ * @param fieldSet a comma-separated list of fields to retrieve
+ * @param priority the priority with which to perform this operation
+ * @return the document with this id, or null if there is none
+ * @throws UnsupportedOperationException thrown if this does not support retrieving
*/
default Document get(DocumentId id, String fieldSet, DocumentProtocol.Priority priority) {
return get(id, fieldSet, priority, null);
@@ -66,11 +64,10 @@ public interface SyncSession extends Session {
/**
* Gets a document with timeout.
*
- * @param id The id of the document to get.
- * @param timeout Timeout. If timeout is null, an unspecified default will be used.
- * @return The known document having this id, or null if there is no
- * document having this id.
- * @throws UnsupportedOperationException Thrown if this access does not support retrieving.
+ * @param id The id of the document to get
+ * @param timeout Timeout. If timeout is null, an unspecified default will be used
+ * @return the document with this id, or null if there is none
+ * @throws UnsupportedOperationException thrown if this access does not support retrieving
* @throws DocumentAccessException on any messagebus error, including timeout ({@link com.yahoo.messagebus.ErrorCode#TIMEOUT}).
*/
Document get(DocumentId id, Duration timeout);
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/MessageBusAsyncSession.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/MessageBusAsyncSession.java
index a2bad9c84e1..33286d227bf 100644
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/MessageBusAsyncSession.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/MessageBusAsyncSession.java
@@ -143,6 +143,14 @@ public class MessageBusAsyncSession implements MessageBusSession, AsyncSession {
return send(msg);
}
+ private boolean mayOverrideWithGetOnlyRoute(Message msg) {
+ // Only allow implicitly overriding the default Get route if the message is attempted sent
+ // with the default route originally. Otherwise it's reasonable to assume that the caller
+ // has some explicit idea of why the regular route is set to the value it is.
+ return ((msg.getType() == DocumentProtocol.MESSAGE_GETDOCUMENT)
+ && ("default".equals(route) || "route:default".equals(route)));
+ }
+
/**
* A convenience method for assigning the internal trace level and route string to a message before sending it
* through the internal mbus session object.
@@ -155,7 +163,7 @@ public class MessageBusAsyncSession implements MessageBusSession, AsyncSession {
long reqId = requestId.incrementAndGet();
msg.setContext(reqId);
msg.getTrace().setLevel(traceLevel);
- String toRoute = (msg.getType() == DocumentProtocol.MESSAGE_GETDOCUMENT ? routeForGet : route);
+ String toRoute = (mayOverrideWithGetOnlyRoute(msg) ? routeForGet : route);
if (toRoute != null) {
return toResult(reqId, session.send(msg, toRoute, true));
} else {
diff --git a/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/PolicyTestCase.java b/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/PolicyTestCase.java
index fd5f43c23c1..cb32ce75e03 100755
--- a/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/PolicyTestCase.java
+++ b/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/PolicyTestCase.java
@@ -47,6 +47,7 @@ import com.yahoo.messagebus.routing.RoutingSpec;
import com.yahoo.messagebus.routing.RoutingTableSpec;
import com.yahoo.messagebus.test.Receptor;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import java.util.ArrayList;
@@ -60,7 +61,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -68,7 +69,6 @@ import static org.junit.Assert.assertTrue;
/**
* @author Simon Thoresen Hult
*/
-@SuppressWarnings("deprecation")
public class PolicyTestCase {
private static final int TIMEOUT = 300;
@@ -313,7 +313,7 @@ public class PolicyTestCase {
for (int i = 0; i < 10; ++i) {
RoutingNode leaf = frame.select(1).get(0);
String recipient = leaf.getRoute().toString();
- assertTrue(recipient.equals("docproc/cluster.default/*/chain.default"));
+ assertEquals(recipient, "docproc/cluster.default/*/chain.default");
lst.add(recipient);
leaf.handleReply(new EmptyReply());
@@ -496,7 +496,7 @@ public class PolicyTestCase {
if (prev == null) {
assertNotNull(next);
} else {
- assertFalse(prev.equals(next));
+ assertNotEquals(prev, next);
}
prev = next;
leaf.handleReply(new EmptyReply());
@@ -612,6 +612,32 @@ public class PolicyTestCase {
frame.destroy();
}
+ @Test
+ public void testDocumentSelectorDualCluster() {
+ PolicyTestFrame frame = new PolicyTestFrame(manager);
+ frame.setHop(new HopSpec("test", "[DocumentRouteSelector:raw:" +
+ "route[2]\n" +
+ "route[0].name \"foo\"\n" +
+ "route[0].selector \"(testdoc AND (testdoc.intfield / 1000 > 0))\"\n" +
+ "route[0].feed \"myfeed\"\n" +
+ "route[1].name \"bar\"\n" +
+ "route[1].selector \"(other AND (other.intfield / 1000 > 0))\"\n" +
+ "route[1].feed \"myfeed\"\n]").addRecipient("foo").addRecipient("bar"));
+
+ frame.setMessage(new GetDocumentMessage(new DocumentId("doc:scheme:"), "fieldSet"));
+ frame.assertSelect(Arrays.asList("bar", "foo"));
+
+ Document doc = new Document(manager.getDocumentType("testdoc"), new DocumentId("doc:scheme:"));
+ doc.setFieldValue("intfield", 3000);
+ Message put = new PutDocumentMessage(new DocumentPut(doc));
+ frame.setMessage(put);
+ frame.assertSelect(Arrays.asList("foo"));
+
+ frame.setMessage(put);
+ frame.assertMergeOneReply("foo");
+
+ frame.destroy();
+ }
@Test
public void testDocumentRouteSelectorIgnore() {
@@ -676,7 +702,7 @@ public class PolicyTestCase {
assertSelect(frame, 32, Arrays.asList("docproc/cluster.default/9/chain.default"));
frame.getNetwork().unregisterSession("9/chain.default");
assertTrue(frame.waitSlobrok("docproc/cluster.default/*/chain.default", 7));
- assertSelect(frame, 32, new ArrayList<String>());
+ assertSelect(frame, 32, new ArrayList<>());
// Test merge behavior.
frame.setHop(new HopSpec("test", "[RoundRobin]").addRecipient("docproc/cluster.default/0/chain.default"));
diff --git a/eval/src/tests/eval/gbdt/gbdt_benchmark.cpp b/eval/src/tests/eval/gbdt/gbdt_benchmark.cpp
index 230f1ed251e..20e04c9593e 100644
--- a/eval/src/tests/eval/gbdt/gbdt_benchmark.cpp
+++ b/eval/src/tests/eval/gbdt/gbdt_benchmark.cpp
@@ -95,7 +95,7 @@ std::vector<Option> all_options({{0, none},{1, vm_forest}});
struct Result {
double us;
size_t opt_idx;
- bool operator<(const Result &rhs) {
+ bool operator<(const Result &rhs) const {
return (us < rhs.us);
}
};
diff --git a/eval/src/tests/tensor/dense_add_dimension_optimizer/dense_add_dimension_optimizer_test.cpp b/eval/src/tests/tensor/dense_add_dimension_optimizer/dense_add_dimension_optimizer_test.cpp
index eaf4623afea..274117ea693 100644
--- a/eval/src/tests/tensor/dense_add_dimension_optimizer/dense_add_dimension_optimizer_test.cpp
+++ b/eval/src/tests/tensor/dense_add_dimension_optimizer/dense_add_dimension_optimizer_test.cpp
@@ -25,6 +25,7 @@ const TensorEngine &prod_engine = DefaultTensorEngine::ref();
EvalFixture::ParamRepo make_params() {
return EvalFixture::ParamRepo()
.add("x5", spec({x(5)}, N()))
+ .add("x5f", spec(float_cells({x(5)}), N()))
.add("x5y1", spec({x(5),y(1)}, N()))
.add("y1z1", spec({y(1),z(1)}, N()))
.add("x_m", spec({x({"a"})}, N()));
@@ -78,9 +79,9 @@ TEST("require that non-canonical dimension addition is not optimized") {
TEST_DO(verify_not_optimized("tensor(y[1])(1)/x5"));
}
-TEST("require that dimension addition with overlapping dimensions is not optimized") {
- TEST_DO(verify_not_optimized("x5y1*tensor(y[1],z[1])(1)"));
- TEST_DO(verify_not_optimized("tensor(y[1],z[1])(1)*x5y1"));
+TEST("require that dimension addition with overlapping dimensions is optimized") {
+ TEST_DO(verify_optimized("x5y1*tensor(y[1],z[1])(1)"));
+ TEST_DO(verify_optimized("tensor(y[1],z[1])(1)*x5y1"));
}
TEST("require that dimension addition with inappropriate dimensions is not optimized") {
@@ -99,8 +100,13 @@ TEST("require that dimension addition optimization requires unit constant tensor
TEST_DO(verify_not_optimized("tensor(x[2])(1)*tensor(y[2])(1)"));
}
-TEST("require that optimization is disabled for tensors with non-double cells") {
- TEST_DO(verify_not_optimized("x5*tensor<float>(a[1],b[1],c[1])(1)"));
+TEST("require that optimization also works for float cells") {
+ TEST_DO(verify_optimized("x5*tensor<float>(a[1],b[1],c[1])(1)"));
+ TEST_DO(verify_optimized("x5f*tensor<float>(a[1],b[1],c[1])(1)"));
+}
+
+TEST("require that optimization is disabled if unit vector would promote tensor cell types") {
+ TEST_DO(verify_not_optimized("x5f*tensor(a[1],b[1],c[1])(1)"));
}
TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/eval/src/tests/tensor/dense_fast_rename_optimizer/dense_fast_rename_optimizer_test.cpp b/eval/src/tests/tensor/dense_fast_rename_optimizer/dense_fast_rename_optimizer_test.cpp
index 4995ea89735..55a9414f82b 100644
--- a/eval/src/tests/tensor/dense_fast_rename_optimizer/dense_fast_rename_optimizer_test.cpp
+++ b/eval/src/tests/tensor/dense_fast_rename_optimizer/dense_fast_rename_optimizer_test.cpp
@@ -72,8 +72,8 @@ TEST("require that chained optimized renames are compacted into a single operati
TEST_DO(verify_optimized("rename(rename(x5,x,y),y,z)"));
}
-TEST("require that optimization is disabled for tensors with non-double cells") {
- TEST_DO(verify_not_optimized("rename(x5f,x,y)"));
+TEST("require that optimization works for float cells") {
+ TEST_DO(verify_optimized("rename(x5f,x,y)"));
}
TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/eval/src/tests/tensor/dense_inplace_join_function/dense_inplace_join_function_test.cpp b/eval/src/tests/tensor/dense_inplace_join_function/dense_inplace_join_function_test.cpp
index 083ed1c7071..80321ac3d22 100644
--- a/eval/src/tests/tensor/dense_inplace_join_function/dense_inplace_join_function_test.cpp
+++ b/eval/src/tests/tensor/dense_inplace_join_function/dense_inplace_join_function_test.cpp
@@ -144,10 +144,15 @@ TEST("require that inplace join can be debug dumped") {
fprintf(stderr, "%s\n", info[0]->as_string().c_str());
}
-TEST("require that optimization is disabled for tensors with non-double cells") {
- TEST_DO(verify_not_optimized("mut_x5_A-mut_x5f_D"));
- TEST_DO(verify_not_optimized("mut_x5f_D-mut_x5_A"));
- TEST_DO(verify_not_optimized("mut_x5f_D-mut_x5f_E"));
+TEST("require that optimization works with float cells") {
+ TEST_DO(verify_p0_optimized("mut_x5f_D-mut_x5f_E", 1));
+}
+
+TEST("require that overwritten value must have same cell type as result") {
+ TEST_DO(verify_p0_optimized("mut_x5_A-mut_x5f_D", 1));
+ TEST_DO(verify_p1_optimized("mut_x5f_D-mut_x5_A", 1));
+ TEST_DO(verify_not_optimized("con_x5_A-mut_x5f_D"));
+ TEST_DO(verify_not_optimized("mut_x5f_D-con_x5_A"));
}
TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/eval/src/tests/tensor/dense_inplace_map_function/dense_inplace_map_function_test.cpp b/eval/src/tests/tensor/dense_inplace_map_function/dense_inplace_map_function_test.cpp
index 314d3a6186c..f85742b4e0f 100644
--- a/eval/src/tests/tensor/dense_inplace_map_function/dense_inplace_map_function_test.cpp
+++ b/eval/src/tests/tensor/dense_inplace_map_function/dense_inplace_map_function_test.cpp
@@ -72,8 +72,8 @@ TEST("require that mapped tensors are not optimized") {
TEST_DO(verify_not_optimized("map(_x_m,f(x)(x+10))"));
}
-TEST("require that optimization is disabled for tensors with non-double cells") {
- TEST_DO(verify_not_optimized("map(_x5f,f(x)(x+10))"));
+TEST("require that optimization works for float cells") {
+ TEST_DO(verify_optimized("map(_x5f,f(x)(x+10))", 1));
}
TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/eval/src/tests/tensor/dense_remove_dimension_optimizer/dense_remove_dimension_optimizer_test.cpp b/eval/src/tests/tensor/dense_remove_dimension_optimizer/dense_remove_dimension_optimizer_test.cpp
index 7856775ae30..179fdd3eff4 100644
--- a/eval/src/tests/tensor/dense_remove_dimension_optimizer/dense_remove_dimension_optimizer_test.cpp
+++ b/eval/src/tests/tensor/dense_remove_dimension_optimizer/dense_remove_dimension_optimizer_test.cpp
@@ -78,8 +78,8 @@ TEST("require that inappropriate tensor types cannot be optimized") {
TEST_DO(verify_not_optimized("reduce(x1y5z_m,sum,z)"));
}
-TEST("require that optimization is disabled for tensors with non-double cells") {
- TEST_DO(verify_not_optimized("reduce(x1y5z1f,avg,x)"));
+TEST("require that optimization works for float cells") {
+ TEST_DO(verify_optimized("reduce(x1y5z1f,avg,x)"));
}
TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/eval/src/tests/tensor/dense_xw_product_function/dense_xw_product_function_test.cpp b/eval/src/tests/tensor/dense_xw_product_function/dense_xw_product_function_test.cpp
index 335aa4791a4..426281686d7 100644
--- a/eval/src/tests/tensor/dense_xw_product_function/dense_xw_product_function_test.cpp
+++ b/eval/src/tests/tensor/dense_xw_product_function/dense_xw_product_function_test.cpp
@@ -45,6 +45,7 @@ EvalFixture::ParamRepo make_params() {
.add("y1z1", spec({y(1),z(1)}, MyMatSeq()))
.add("x2y3", spec({x(2),y(3)}, MyMatSeq()))
.add("x2y3f", spec(float_cells({x(2),y(3)}), MyMatSeq()))
+ .add("y3z2f", spec(float_cells({y(3),z(2)}), MyMatSeq()))
.add("x2z3", spec({x(2),z(3)}, MyMatSeq()))
.add("y3z2", spec({y(3),z(2)}, MyMatSeq()))
.add("x8y5", spec({x(8),y(5)}, MyMatSeq()))
@@ -118,10 +119,16 @@ TEST("require that xw product can be debug dumped") {
fprintf(stderr, "%s\n", info[0]->as_string().c_str());
}
-TEST("require that optimization is disabled for tensors with non-double cells") {
- TEST_DO(verify_not_optimized("reduce(y3f*x2y3,sum,y)"));
- TEST_DO(verify_not_optimized("reduce(y3*x2y3f,sum,y)"));
- TEST_DO(verify_not_optimized("reduce(y3f*x2y3f,sum,y)"));
+TEST("require that optimization works for float cells") {
+ TEST_DO(verify_optimized("reduce(y3f*x2y3,sum,y)", 3, 2, true));
+ TEST_DO(verify_optimized("reduce(y3*x2y3f,sum,y)", 3, 2, true));
+ TEST_DO(verify_optimized("reduce(y3f*x2y3f,sum,y)", 3, 2, true));
+}
+
+TEST("require that optimization works for float cells with inconvenient dimension nesting") {
+ TEST_DO(verify_optimized("reduce(y3f*y3z2,sum,y)", 3, 2, false));
+ TEST_DO(verify_optimized("reduce(y3*y3z2f,sum,y)", 3, 2, false));
+ TEST_DO(verify_optimized("reduce(y3f*y3z2f,sum,y)", 3, 2, false));
}
TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/eval/src/vespa/eval/eval/value_type.cpp b/eval/src/vespa/eval/eval/value_type.cpp
index fc0f3cc5414..d6ba8e83855 100644
--- a/eval/src/vespa/eval/eval/value_type.cpp
+++ b/eval/src/vespa/eval/eval/value_type.cpp
@@ -12,21 +12,27 @@ using CellType = ValueType::CellType;
using Dimension = ValueType::Dimension;
using DimensionList = std::vector<Dimension>;
-CellType unify(CellType a, CellType b) {
- if (a == b) {
- return a;
- } else {
- return CellType::DOUBLE;
+template <typename A, typename B>
+CellType unify() {
+ using type = typename UnifyCellTypes<A,B>::type;
+ return get_cell_type<type>();
+}
+
+template <typename A>
+CellType unify(CellType b) {
+ switch (b) {
+ case CellType::DOUBLE: return unify<A,double>();
+ case CellType::FLOAT: return unify<A,float>();
}
+ abort();
}
-CellType unify_cell_type(const ValueType &a, const ValueType &b) {
- if (a.is_double()) {
- return b.cell_type();
- } else if (b.is_double()) {
- return a.cell_type();
+CellType unify(CellType a, CellType b) {
+ switch (a) {
+ case CellType::DOUBLE: return unify<double>(b);
+ case CellType::FLOAT: return unify<float>(b);
}
- return unify(a.cell_type(), b.cell_type());
+ abort();
}
size_t my_dimension_index(const std::vector<Dimension> &list, const vespalib::string &name) {
@@ -265,6 +271,16 @@ ValueType::join(const ValueType &lhs, const ValueType &rhs)
return tensor_type(std::move(result.dimensions), unify(lhs._cell_type, rhs._cell_type));
}
+CellType
+ValueType::unify_cell_types(const ValueType &a, const ValueType &b) {
+ if (a.is_double()) {
+ return b.cell_type();
+ } else if (b.is_double()) {
+ return a.cell_type();
+ }
+ return unify(a.cell_type(), b.cell_type());
+}
+
ValueType
ValueType::concat(const ValueType &lhs, const ValueType &rhs, const vespalib::string &dimension)
{
@@ -278,7 +294,7 @@ ValueType::concat(const ValueType &lhs, const ValueType &rhs, const vespalib::st
if (!find_dimension(result.dimensions, dimension)) {
result.dimensions.emplace_back(dimension, 2);
}
- return tensor_type(std::move(result.dimensions), unify_cell_type(lhs, rhs));
+ return tensor_type(std::move(result.dimensions), unify_cell_types(lhs, rhs));
}
ValueType
diff --git a/eval/src/vespa/eval/eval/value_type.h b/eval/src/vespa/eval/eval/value_type.h
index 0eb3e1ca28e..64003e2636e 100644
--- a/eval/src/vespa/eval/eval/value_type.h
+++ b/eval/src/vespa/eval/eval/value_type.h
@@ -78,15 +78,27 @@ public:
static ValueType from_spec(const vespalib::string &spec);
vespalib::string to_spec() const;
static ValueType join(const ValueType &lhs, const ValueType &rhs);
+ static CellType unify_cell_types(const ValueType &a, const ValueType &b);
static ValueType concat(const ValueType &lhs, const ValueType &rhs, const vespalib::string &dimension);
static ValueType either(const ValueType &one, const ValueType &other);
};
std::ostream &operator<<(std::ostream &os, const ValueType &type);
-// utility template
-template <typename T> inline bool check_cell_type(ValueType::CellType type);
+// utility templates
+
+template <typename CT> inline bool check_cell_type(ValueType::CellType type);
template <> inline bool check_cell_type<double>(ValueType::CellType type) { return (type == ValueType::CellType::DOUBLE); }
template <> inline bool check_cell_type<float>(ValueType::CellType type) { return (type == ValueType::CellType::FLOAT); }
+template <typename LCT, typename RCT> struct UnifyCellTypes{};
+template <> struct UnifyCellTypes<double, double> { using type = double; };
+template <> struct UnifyCellTypes<double, float> { using type = double; };
+template <> struct UnifyCellTypes<float, double> { using type = double; };
+template <> struct UnifyCellTypes<float, float> { using type = float; };
+
+template <typename CT> inline ValueType::CellType get_cell_type();
+template <> inline ValueType::CellType get_cell_type<double>() { return ValueType::CellType::DOUBLE; }
+template <> inline ValueType::CellType get_cell_type<float>() { return ValueType::CellType::FLOAT; }
+
} // namespace
diff --git a/eval/src/vespa/eval/tensor/default_tensor_engine.cpp b/eval/src/vespa/eval/tensor/default_tensor_engine.cpp
index 58db90f5557..f1eb9ff1523 100644
--- a/eval/src/vespa/eval/tensor/default_tensor_engine.cpp
+++ b/eval/src/vespa/eval/tensor/default_tensor_engine.cpp
@@ -37,6 +37,7 @@ using eval::TensorFunction;
using eval::TensorSpec;
using eval::Value;
using eval::ValueType;
+using CellType = eval::ValueType::CellType;
using vespalib::IllegalArgumentException;
using vespalib::make_string;
@@ -355,8 +356,7 @@ DefaultTensorEngine::reduce(const Value &a, Aggr aggr, const std::vector<vespali
size_t vector_size(const ValueType &type, const vespalib::string &dimension) {
if (type.is_double()) {
return 1;
- } else if ((type.cell_type() == ValueType::CellType::DOUBLE) &&
- (type.dimensions().size() == 1) &&
+ } else if ((type.dimensions().size() == 1) &&
(type.dimensions()[0].is_indexed()) &&
(type.dimensions()[0].name == dimension))
{
@@ -366,40 +366,50 @@ size_t vector_size(const ValueType &type, const vespalib::string &dimension) {
}
}
+template <typename OCT>
struct CallAppendVector {
template <typename CT>
- static void call(const ConstArrayRef<CT> &arr, double *&pos) {
- for (CT cell : arr) { *pos++ = cell; }
+ static void call(const ConstArrayRef<CT> &arr, OCT *&pos) {
+ for (CT cell: arr) { *pos++ = cell; }
}
};
-void append_vector(double *&pos, const Value &value) {
+template <typename OCT>
+void append_vector(OCT *&pos, const Value &value) {
if (auto tensor = value.as_tensor()) {
const DenseTensorView *view = static_cast<const DenseTensorView *>(tensor);
- TypedCells cellsRef = view->cellsRef();
- dispatch_1<CallAppendVector>(cellsRef, pos);
+ dispatch_1<CallAppendVector<OCT> >(view->cellsRef(), pos);
} else {
*pos++ = value.as_double();
}
}
+template <typename OCT>
const Value &concat_vectors(const Value &a, const Value &b, const vespalib::string &dimension, size_t vector_size, Stash &stash) {
- ArrayRef<double> cells = stash.create_array<double>(vector_size);
- double *pos = cells.begin();
- append_vector(pos, a);
- append_vector(pos, b);
+ ArrayRef<OCT> cells = stash.create_array<OCT>(vector_size);
+ OCT *pos = cells.begin();
+ append_vector<OCT>(pos, a);
+ append_vector<OCT>(pos, b);
assert(pos == cells.end());
- const ValueType &type = stash.create<ValueType>(ValueType::tensor_type({ValueType::Dimension(dimension, vector_size)}));
+ const ValueType &type = stash.create<ValueType>(ValueType::tensor_type({ValueType::Dimension(dimension, vector_size)}, ValueType::unify_cell_types(a.type(), b.type())));
return stash.create<DenseTensorView>(type, TypedCells(cells));
}
+struct CallConcatVectors {
+ template <typename OCT>
+ static const Value &call(const Value &a, const Value &b, const vespalib::string &dimension, size_t vector_size, Stash &stash) {
+ return concat_vectors<OCT>(a, b, dimension, vector_size, stash);
+ }
+};
+
const Value &
DefaultTensorEngine::concat(const Value &a, const Value &b, const vespalib::string &dimension, Stash &stash) const
{
size_t a_size = vector_size(a.type(), dimension);
size_t b_size = vector_size(b.type(), dimension);
if ((a_size > 0) && (b_size > 0)) {
- return concat_vectors(a, b, dimension, a_size + b_size, stash);
+ CellType result_cell_type = ValueType::unify_cell_types(a.type(), b.type());
+ return dispatch_0<CallConcatVectors>(result_cell_type, a, b, dimension, (a_size + b_size), stash);
}
return to_default(simple_engine().concat(to_simple(a, stash), to_simple(b, stash), dimension, stash), stash);
}
diff --git a/eval/src/vespa/eval/tensor/dense/dense_add_dimension_optimizer.cpp b/eval/src/vespa/eval/tensor/dense/dense_add_dimension_optimizer.cpp
index 842e064de43..a4331b6b251 100644
--- a/eval/src/vespa/eval/tensor/dense/dense_add_dimension_optimizer.cpp
+++ b/eval/src/vespa/eval/tensor/dense/dense_add_dimension_optimizer.cpp
@@ -19,21 +19,8 @@ using namespace eval::operation;
namespace {
-bool is_concrete_dense_tensor(const ValueType &type) {
- if (type.cell_type() != ValueType::CellType::DOUBLE) {
- return false; // non-double cell types not supported
- }
- return type.is_dense();
-}
-
-bool not_overlapping(const ValueType &a, const ValueType &b) {
- size_t npos = ValueType::Dimension::npos;
- for (const auto &dim: b.dimensions()) {
- if (a.dimension_index(dim.name) != npos) {
- return false;
- }
- }
- return true;
+bool same_cell_type(const TensorFunction &a, const TensorFunction &b) {
+ return (a.result_type().cell_type() == b.result_type().cell_type());
}
bool is_unit_constant(const TensorFunction &node) {
@@ -57,15 +44,14 @@ DenseAddDimensionOptimizer::optimize(const eval::TensorFunction &expr, Stash &st
const TensorFunction &lhs = join->lhs();
const TensorFunction &rhs = join->rhs();
if ((join->function() == Mul::f) &&
- is_concrete_dense_tensor(lhs.result_type()) &&
- is_concrete_dense_tensor(rhs.result_type()) &&
- not_overlapping(lhs.result_type(), rhs.result_type()))
+ lhs.result_type().is_dense() &&
+ rhs.result_type().is_dense())
{
- if (is_unit_constant(lhs)) {
+ if (is_unit_constant(lhs) && same_cell_type(rhs, expr)) {
return DenseReplaceTypeFunction::create_compact(expr.result_type(), rhs, stash);
}
- if (is_unit_constant(rhs)) {
- return DenseReplaceTypeFunction::create_compact(expr.result_type(), lhs, stash);
+ if (is_unit_constant(rhs) && same_cell_type(lhs, expr)) {
+ return DenseReplaceTypeFunction::create_compact(expr.result_type(), lhs, stash);
}
}
}
diff --git a/eval/src/vespa/eval/tensor/dense/dense_dot_product_function.cpp b/eval/src/vespa/eval/tensor/dense/dense_dot_product_function.cpp
index 9b839e1b12f..8bcaddba3b4 100644
--- a/eval/src/vespa/eval/tensor/dense/dense_dot_product_function.cpp
+++ b/eval/src/vespa/eval/tensor/dense/dense_dot_product_function.cpp
@@ -18,12 +18,6 @@ using namespace eval::operation;
namespace {
-template <typename T>
-ConstArrayRef<T> getCellsRef(const eval::Value &value) {
- const DenseTensorView &denseTensor = static_cast<const DenseTensorView &>(value);
- return denseTensor.cellsRef().typify<T>();
-}
-
template <typename LCT, typename RCT>
struct HWSupport {
static double call(hwaccelrated::IAccelrated *, const ConstArrayRef<LCT> &lhs, const ConstArrayRef<RCT> &rhs) {
@@ -48,8 +42,8 @@ template <> struct HWSupport<double, double> {
template <typename LCT, typename RCT>
void my_dot_product_op(eval::InterpretedFunction::State &state, uint64_t param) {
auto *hw = (hwaccelrated::IAccelrated *)(param);
- auto lhs = getCellsRef<LCT>(state.peek(1));
- auto rhs = getCellsRef<RCT>(state.peek(0));
+ auto lhs = DenseTensorView::typify_cells<LCT>(state.peek(1));
+ auto rhs = DenseTensorView::typify_cells<RCT>(state.peek(0));
double result = HWSupport<LCT,RCT>::call(hw, lhs, rhs);
state.pop_pop_push(state.stash.create<eval::DoubleValue>(result));
}
diff --git a/eval/src/vespa/eval/tensor/dense/dense_fast_rename_optimizer.cpp b/eval/src/vespa/eval/tensor/dense/dense_fast_rename_optimizer.cpp
index d8e1876ac64..ac8442477e4 100644
--- a/eval/src/vespa/eval/tensor/dense/dense_fast_rename_optimizer.cpp
+++ b/eval/src/vespa/eval/tensor/dense/dense_fast_rename_optimizer.cpp
@@ -17,15 +17,10 @@ using namespace eval::tensor_function;
namespace {
-bool is_concrete_dense_stable_rename(const ValueType &from_type, const ValueType &to_type,
- const std::vector<vespalib::string> &from,
- const std::vector<vespalib::string> &to)
+bool is_dense_stable_rename(const ValueType &from_type, const ValueType &to_type,
+ const std::vector<vespalib::string> &from,
+ const std::vector<vespalib::string> &to)
{
- if (from_type.cell_type() != ValueType::CellType::DOUBLE ||
- to_type.cell_type() != ValueType::CellType::DOUBLE)
- {
- return false; // non-double cell types not supported
- }
if (!from_type.is_dense() ||
!to_type.is_dense() ||
(from.size() != to.size()))
@@ -51,7 +46,8 @@ DenseFastRenameOptimizer::optimize(const eval::TensorFunction &expr, Stash &stas
if (auto rename = as<Rename>(expr)) {
const ValueType &from_type = rename->child().result_type();
const ValueType &to_type = expr.result_type();
- if (is_concrete_dense_stable_rename(from_type, to_type, rename->from(), rename->to())) {
+ if (is_dense_stable_rename(from_type, to_type, rename->from(), rename->to())) {
+ assert(to_type.cell_type() == from_type.cell_type());
return DenseReplaceTypeFunction::create_compact(to_type, rename->child(), stash);
}
}
diff --git a/eval/src/vespa/eval/tensor/dense/dense_generic_join.hpp b/eval/src/vespa/eval/tensor/dense/dense_generic_join.hpp
index aa08e6982bb..cdc89b30fff 100644
--- a/eval/src/vespa/eval/tensor/dense/dense_generic_join.hpp
+++ b/eval/src/vespa/eval/tensor/dense/dense_generic_join.hpp
@@ -43,7 +43,7 @@ struct CallGenericJoin {
DenseDimensionCombiner & combiner,
Function &&func)
{
- using OCT = typename OutputCellType<LCT, RCT>::output_type;
+ using OCT = typename eval::UnifyCellTypes<LCT, RCT>::type;
TypedDenseTensorBuilder<OCT> builder(combiner.result_type);
return generic_join(combiner, builder, lhsArr, rhsArr, std::move(func));
}
diff --git a/eval/src/vespa/eval/tensor/dense/dense_inplace_join_function.cpp b/eval/src/vespa/eval/tensor/dense/dense_inplace_join_function.cpp
index 5fdfdbc4e9f..0b5bba88d37 100644
--- a/eval/src/vespa/eval/tensor/dense/dense_inplace_join_function.cpp
+++ b/eval/src/vespa/eval/tensor/dense/dense_inplace_join_function.cpp
@@ -17,35 +17,45 @@ using namespace eval::tensor_function;
namespace {
-TypedCells getCellsRef(const eval::Value &value) {
- const DenseTensorView &denseTensor = static_cast<const DenseTensorView &>(value);
- return denseTensor.cellsRef();
+template <typename LCT, typename RCT>
+void my_inplace_join_left_op(eval::InterpretedFunction::State &state, uint64_t param) {
+ join_fun_t function = (join_fun_t)param;
+ auto lhs_cells = unconstify(DenseTensorView::typify_cells<LCT>(state.peek(1)));
+ auto rhs_cells = DenseTensorView::typify_cells<RCT>(state.peek(0));
+ for (size_t i = 0; i < lhs_cells.size(); ++i) {
+ lhs_cells[i] = function(lhs_cells[i], rhs_cells[i]);
+ }
+ state.stack.pop_back();
}
-template <bool write_left>
-void my_inplace_join_op(eval::InterpretedFunction::State &state, uint64_t param) {
+template <typename LCT, typename RCT>
+void my_inplace_join_right_op(eval::InterpretedFunction::State &state, uint64_t param) {
join_fun_t function = (join_fun_t)param;
- ConstArrayRef<double> lhs_cells = getCellsRef(state.peek(1)).typify<double>();
- ConstArrayRef<double> rhs_cells = getCellsRef(state.peek(0)).typify<double>();
- auto dst_cells = unconstify(write_left ? lhs_cells : rhs_cells);
- for (size_t i = 0; i < dst_cells.size(); ++i) {
- dst_cells[i] = function(lhs_cells[i], rhs_cells[i]);
- }
- if (write_left) {
- state.stack.pop_back();
- } else {
- const Value &result = state.stack.back();
- state.pop_pop_push(result);
+ auto lhs_cells = DenseTensorView::typify_cells<LCT>(state.peek(1));
+ auto rhs_cells = unconstify(DenseTensorView::typify_cells<RCT>(state.peek(0)));
+ for (size_t i = 0; i < rhs_cells.size(); ++i) {
+ rhs_cells[i] = function(lhs_cells[i], rhs_cells[i]);
}
+ const Value &result = state.stack.back();
+ state.pop_pop_push(result);
}
-bool sameShapeConcreteDenseTensors(const ValueType &a, const ValueType &b) {
- if (a.cell_type() != ValueType::CellType::DOUBLE ||
- b.cell_type() != ValueType::CellType::DOUBLE)
- {
- return false; // non-double cell types not supported
+struct MyInplaceJoinLeftOp {
+ template <typename LCT, typename RCT>
+ static auto get_fun() { return my_inplace_join_left_op<LCT,RCT>; }
+};
+
+struct MyInplaceJoinRightOp {
+ template <typename LCT, typename RCT>
+ static auto get_fun() { return my_inplace_join_right_op<LCT,RCT>; }
+};
+
+eval::InterpretedFunction::op_function my_select(CellType lct, CellType rct, bool write_left) {
+ if (write_left) {
+ return select_2<MyInplaceJoinLeftOp>(lct, rct);
+ } else {
+ return select_2<MyInplaceJoinRightOp>(lct, rct);
}
- return (a.is_dense() && (a == b));
}
} // namespace vespalib::tensor::<unnamed>
@@ -68,7 +78,8 @@ DenseInplaceJoinFunction::~DenseInplaceJoinFunction()
eval::InterpretedFunction::Instruction
DenseInplaceJoinFunction::compile_self(Stash &) const
{
- auto op = _write_left ? my_inplace_join_op<true> : my_inplace_join_op<false>;
+ auto op = my_select(lhs().result_type().cell_type(),
+ rhs().result_type().cell_type(), _write_left);
return eval::InterpretedFunction::Instruction(op, (uint64_t)function());
}
@@ -85,11 +96,17 @@ DenseInplaceJoinFunction::optimize(const eval::TensorFunction &expr, Stash &stas
if (auto join = as<Join>(expr)) {
const TensorFunction &lhs = join->lhs();
const TensorFunction &rhs = join->rhs();
- if ((lhs.result_is_mutable() || rhs.result_is_mutable()) &&
- sameShapeConcreteDenseTensors(lhs.result_type(), rhs.result_type()))
+ if (lhs.result_type().is_dense() &&
+ (lhs.result_type().dimensions() == rhs.result_type().dimensions()))
{
- return stash.create<DenseInplaceJoinFunction>(join->result_type(), lhs, rhs,
- join->function(), lhs.result_is_mutable());
+ if (lhs.result_is_mutable() && (lhs.result_type() == expr.result_type())) {
+ return stash.create<DenseInplaceJoinFunction>(join->result_type(), lhs, rhs,
+ join->function(), /* write left: */ true);
+ }
+ if (rhs.result_is_mutable() && (rhs.result_type() == expr.result_type())) {
+ return stash.create<DenseInplaceJoinFunction>(join->result_type(), lhs, rhs,
+ join->function(), /* write left: */ false);
+ }
}
}
return expr;
diff --git a/eval/src/vespa/eval/tensor/dense/dense_inplace_map_function.cpp b/eval/src/vespa/eval/tensor/dense/dense_inplace_map_function.cpp
index b38a6b175dc..c82cda34a28 100644
--- a/eval/src/vespa/eval/tensor/dense/dense_inplace_map_function.cpp
+++ b/eval/src/vespa/eval/tensor/dense/dense_inplace_map_function.cpp
@@ -16,24 +16,19 @@ using namespace eval::tensor_function;
namespace {
-ArrayRef<double> getMutableCells(const eval::Value &value) {
- const DenseTensorView &denseTensor = static_cast<const DenseTensorView &>(value);
- return unconstify(denseTensor.cellsRef().typify<double>());
-}
-
+template <typename CT>
void my_inplace_map_op(eval::InterpretedFunction::State &state, uint64_t param) {
map_fun_t function = (map_fun_t)param;
- for (double &cell: getMutableCells(state.peek(0))) {
+ ArrayRef<CT> cells = unconstify(DenseTensorView::typify_cells<CT>(state.peek(0)));
+ for (CT &cell: cells) {
cell = function(cell);
}
}
-bool isConcreteDenseTensor(const ValueType &type) {
- if (type.cell_type() != ValueType::CellType::DOUBLE) {
- return false; // non-double cell types not supported
- }
- return type.is_dense();
-}
+struct MyInplaceMapOp {
+ template <typename CT>
+ static auto get_fun() { return my_inplace_map_op<CT>; }
+};
} // namespace vespalib::tensor::<unnamed>
@@ -51,14 +46,16 @@ DenseInplaceMapFunction::~DenseInplaceMapFunction()
eval::InterpretedFunction::Instruction
DenseInplaceMapFunction::compile_self(Stash &) const
{
- return eval::InterpretedFunction::Instruction(my_inplace_map_op, (uint64_t)function());
+ auto op = select_1<MyInplaceMapOp>(result_type().cell_type());
+ return eval::InterpretedFunction::Instruction(op, (uint64_t)function());
}
const TensorFunction &
DenseInplaceMapFunction::optimize(const eval::TensorFunction &expr, Stash &stash)
{
if (auto map = as<Map>(expr)) {
- if (map->child().result_is_mutable() && isConcreteDenseTensor(map->result_type())) {
+ if (map->child().result_is_mutable() && map->result_type().is_dense()) {
+ assert(map->result_type().cell_type() == map->child().result_type().cell_type());
return stash.create<DenseInplaceMapFunction>(map->result_type(), map->child(), map->function());
}
}
diff --git a/eval/src/vespa/eval/tensor/dense/dense_remove_dimension_optimizer.cpp b/eval/src/vespa/eval/tensor/dense/dense_remove_dimension_optimizer.cpp
index 3c58320a6e6..a64d5edbb37 100644
--- a/eval/src/vespa/eval/tensor/dense/dense_remove_dimension_optimizer.cpp
+++ b/eval/src/vespa/eval/tensor/dense/dense_remove_dimension_optimizer.cpp
@@ -14,13 +14,6 @@ using namespace eval::tensor_function;
namespace {
-bool is_concrete_dense_tensor(const ValueType &type) {
- if (type.cell_type() != ValueType::CellType::DOUBLE) {
- return false; // non-double cell types not supported
- }
- return type.is_dense();
-}
-
bool is_ident_aggr(Aggr aggr) {
return ((aggr == Aggr::AVG) ||
(aggr == Aggr::PROD) ||
@@ -47,11 +40,12 @@ DenseRemoveDimensionOptimizer::optimize(const eval::TensorFunction &expr, Stash
{
if (auto reduce = as<Reduce>(expr)) {
const TensorFunction &child = reduce->child();
- if (is_concrete_dense_tensor(expr.result_type()) &&
- is_concrete_dense_tensor(child.result_type()) &&
+ if (expr.result_type().is_dense() &&
+ child.result_type().is_dense() &&
is_ident_aggr(reduce->aggr()) &&
is_trivial_dim_list(child.result_type(), reduce->dimensions()))
{
+ assert(expr.result_type().cell_type() == child.result_type().cell_type());
return DenseReplaceTypeFunction::create_compact(expr.result_type(), child, stash);
}
}
diff --git a/eval/src/vespa/eval/tensor/dense/dense_tensor_view.cpp b/eval/src/vespa/eval/tensor/dense/dense_tensor_view.cpp
index d98cf52d279..3fed84323ca 100644
--- a/eval/src/vespa/eval/tensor/dense/dense_tensor_view.cpp
+++ b/eval/src/vespa/eval/tensor/dense/dense_tensor_view.cpp
@@ -95,8 +95,7 @@ sameShapeJoin(const ConstArrayRef<LCT> &lhs, const ConstArrayRef<RCT> &rhs,
{
size_t sz = lhs.size();
assert(sz == rhs.size());
- using OutputSelector = OutputCellType<LCT, RCT>;
- using OCT = typename OutputSelector::output_type;
+ using OCT = typename eval::UnifyCellTypes<LCT,RCT>::type;
std::vector<OCT> newCells;
newCells.reserve(sz);
auto rhsCellItr = rhs.cbegin();
@@ -107,7 +106,7 @@ sameShapeJoin(const ConstArrayRef<LCT> &lhs, const ConstArrayRef<RCT> &rhs,
}
assert(rhsCellItr == rhs.cend());
assert(newCells.size() == sz);
- auto newType = eval::ValueType::tensor_type(lhs_type.dimensions(), OutputSelector::output_cell_type());
+ auto newType = eval::ValueType::tensor_type(lhs_type.dimensions(), eval::get_cell_type<OCT>());
return std::make_unique<DenseTensor<OCT>>(std::move(newType), std::move(newCells));
}
diff --git a/eval/src/vespa/eval/tensor/dense/dense_tensor_view.h b/eval/src/vespa/eval/tensor/dense/dense_tensor_view.h
index 1ec4daf40fd..778f2aa2871 100644
--- a/eval/src/vespa/eval/tensor/dense/dense_tensor_view.h
+++ b/eval/src/vespa/eval/tensor/dense/dense_tensor_view.h
@@ -42,6 +42,13 @@ public:
Tensor::UP clone() const override;
eval::TensorSpec toSpec() const override;
void accept(TensorVisitor &visitor) const override;
+
+ template <typename T> static ConstArrayRef<T> typify_cells(const eval::Value &self) {
+ return static_cast<const DenseTensorView &>(self).cellsRef().typify<T>();
+ }
+ template <typename T> static ConstArrayRef<T> unsafe_typify_cells(const eval::Value &self) {
+ return static_cast<const DenseTensorView &>(self).cellsRef().unsafe_typify<T>();
+ }
protected:
explicit DenseTensorView(const eval::ValueType &type_in)
: _typeRef(type_in),
diff --git a/eval/src/vespa/eval/tensor/dense/dense_xw_product_function.cpp b/eval/src/vespa/eval/tensor/dense/dense_xw_product_function.cpp
index b6ac87ce012..2db5b4e8f92 100644
--- a/eval/src/vespa/eval/tensor/dense/dense_xw_product_function.cpp
+++ b/eval/src/vespa/eval/tensor/dense/dense_xw_product_function.cpp
@@ -21,21 +21,36 @@ using namespace eval::operation;
namespace {
-XWInput getCellsRef(const eval::Value &value) {
- const DenseTensorView &denseTensor = static_cast<const DenseTensorView &>(value);
- TypedCells ref = denseTensor.cellsRef();
- assert(ref.type == CellType::DOUBLE);
- return ref.typify<double>();
-}
+template <typename LCT, typename RCT>
+struct HWSupport {
+ static double call(hwaccelrated::IAccelrated *, const LCT *lhs, const RCT *rhs, size_t len) {
+ double result = 0.0;
+ for (size_t i = 0; i < len; ++i) {
+ result += (lhs[i] * rhs[i]);
+ }
+ return result;
+ }
+};
+template <> struct HWSupport<float, float> {
+ static double call(hwaccelrated::IAccelrated *hw, const float *lhs, const float *rhs, size_t len) {
+ return hw->dotProduct(lhs, rhs, len);
+ }
+};
+template <> struct HWSupport<double, double> {
+ static double call(hwaccelrated::IAccelrated *hw, const double *lhs, const double *rhs, size_t len) {
+ return hw->dotProduct(lhs, rhs, len);
+ }
+};
+template <typename LCT, typename RCT, typename OCT>
void multiDotProduct(const DenseXWProductFunction::Self &self,
- const XWInput &vectorCells, const XWInput &matrixCells, XWOutput &result)
+ const ConstArrayRef<LCT> &vectorCells, const ConstArrayRef<RCT> &matrixCells, ArrayRef<OCT> &result)
{
- double *out = result.begin();
- const double *matrixP = matrixCells.cbegin();
- const double * const vectorP = vectorCells.cbegin();
+ OCT *out = result.begin();
+ const RCT *matrixP = matrixCells.cbegin();
+ const LCT * const vectorP = vectorCells.cbegin();
for (size_t row = 0; row < self._resultSize; ++row) {
- double cell = self._hwAccelerator->dotProduct(vectorP, matrixP, self._vectorSize);
+ double cell = HWSupport<LCT,RCT>::call(self._hwAccelerator.get(), vectorP, matrixP, self._vectorSize);
*out++ = cell;
matrixP += self._vectorSize;
}
@@ -43,12 +58,13 @@ void multiDotProduct(const DenseXWProductFunction::Self &self,
assert(matrixP == matrixCells.cend());
}
+template <typename LCT, typename RCT, typename OCT>
void transposedProduct(const DenseXWProductFunction::Self &self,
- const XWInput &vectorCells, const XWInput &matrixCells, XWOutput &result)
+ const ConstArrayRef<LCT> &vectorCells, const ConstArrayRef<RCT> &matrixCells, ArrayRef<OCT> &result)
{
- double *out = result.begin();
- const double * const matrixP = matrixCells.cbegin();
- const double * const vectorP = vectorCells.cbegin();
+ OCT *out = result.begin();
+ const RCT * const matrixP = matrixCells.cbegin();
+ const LCT * const vectorP = vectorCells.cbegin();
for (size_t row = 0; row < self._resultSize; ++row) {
double cell = 0;
for (size_t col = 0; col < self._vectorSize; ++col) {
@@ -59,41 +75,54 @@ void transposedProduct(const DenseXWProductFunction::Self &self,
assert(out == result.end());
}
-template <bool commonDimensionInnermost>
+template <typename LCT, typename RCT, bool commonDimensionInnermost>
void my_xw_product_op(eval::InterpretedFunction::State &state, uint64_t param) {
DenseXWProductFunction::Self *self = (DenseXWProductFunction::Self *)(param);
- XWInput vectorCells = getCellsRef(state.peek(1));
- XWInput matrixCells = getCellsRef(state.peek(0));
-
- ArrayRef<double> outputCells = state.stash.create_array<double>(self->_resultSize);
+ using OCT = typename eval::UnifyCellTypes<LCT,RCT>::type;
+ auto vectorCells = DenseTensorView::typify_cells<LCT>(state.peek(1));
+ auto matrixCells = DenseTensorView::typify_cells<RCT>(state.peek(0));
+ auto outputCells = state.stash.create_array<OCT>(self->_resultSize);
if (commonDimensionInnermost) {
multiDotProduct(*self, vectorCells, matrixCells, outputCells);
} else {
transposedProduct(*self, vectorCells, matrixCells, outputCells);
}
+
state.pop_pop_push(state.stash.create<DenseTensorView>(self->_resultType, TypedCells(outputCells)));
}
-bool isConcreteDenseTensor(const ValueType &type, size_t d) {
- if (type.cell_type() != ValueType::CellType::DOUBLE) {
- return false; // non-double cell types not supported
+template <bool common_inner>
+struct MyXWProductOp {
+ template <typename LCT, typename RCT>
+ static auto get_fun() { return my_xw_product_op<LCT,RCT,common_inner>; }
+};
+
+eval::InterpretedFunction::op_function my_select(CellType lct, CellType rct, bool common_innermost) {
+ if (common_innermost) {
+ return select_2<MyXWProductOp<true> >(lct, rct);
+ } else {
+ return select_2<MyXWProductOp<false> >(lct, rct);
}
+}
+
+bool isDenseTensor(const ValueType &type, size_t d) {
return (type.is_dense() && (type.dimensions().size() == d));
}
bool isDenseXWProduct(const ValueType &res, const ValueType &vec, const ValueType &mat) {
- if (isConcreteDenseTensor(res, 1) &&
- isConcreteDenseTensor(vec, 1) &&
- isConcreteDenseTensor(mat, 2))
+ if (isDenseTensor(res, 1) &&
+ isDenseTensor(vec, 1) &&
+ isDenseTensor(mat, 2))
{
size_t res_idx = mat.dimension_index(res.dimensions()[0].name);
size_t vec_idx = mat.dimension_index(vec.dimensions()[0].name);
size_t npos = ValueType::Dimension::npos;
if ((res_idx != npos) && (vec_idx != npos) && (res_idx != vec_idx)) {
- return ((mat.dimensions()[res_idx].size == res.dimensions()[0].size) &&
- (mat.dimensions()[vec_idx].size == vec.dimensions()[0].size));
+ assert(mat.dimensions()[res_idx].size == res.dimensions()[0].size);
+ assert(mat.dimensions()[vec_idx].size == vec.dimensions()[0].size);
+ return true;
}
}
return false;
@@ -134,7 +163,8 @@ eval::InterpretedFunction::Instruction
DenseXWProductFunction::compile_self(Stash &stash) const
{
Self &self = stash.create<Self>(result_type(), _vectorSize, _resultSize);
- auto op = _commonDimensionInnermost ? my_xw_product_op<true> : my_xw_product_op<false>;
+ auto op = my_select(lhs().result_type().cell_type(),
+ rhs().result_type().cell_type(), _commonDimensionInnermost);
return eval::InterpretedFunction::Instruction(op, (uint64_t)(&self));
}
@@ -150,22 +180,22 @@ DenseXWProductFunction::visit_self(vespalib::ObjectVisitor &visitor) const
const TensorFunction &
DenseXWProductFunction::optimize(const eval::TensorFunction &expr, Stash &stash)
{
- const Reduce *reduce = as<Reduce>(expr);
- if (reduce && (reduce->aggr() == Aggr::SUM)) {
- const ValueType &result_type = reduce->result_type();
- const Join *join = as<Join>(reduce->child());
- if (join && (join->function() == Mul::f)) {
- const TensorFunction &lhs = join->lhs();
- const TensorFunction &rhs = join->rhs();
- if (isDenseXWProduct(result_type, lhs.result_type(), rhs.result_type())) {
- return createDenseXWProduct(result_type, lhs, rhs, stash);
- }
- if (isDenseXWProduct(result_type, rhs.result_type(), lhs.result_type())) {
- return createDenseXWProduct(result_type, rhs, lhs, stash);
- }
+ const Reduce *reduce = as<Reduce>(expr);
+ if (reduce && (reduce->aggr() == Aggr::SUM)) {
+ const ValueType &result_type = reduce->result_type();
+ const Join *join = as<Join>(reduce->child());
+ if (join && (join->function() == Mul::f)) {
+ const TensorFunction &lhs = join->lhs();
+ const TensorFunction &rhs = join->rhs();
+ if (isDenseXWProduct(result_type, lhs.result_type(), rhs.result_type())) {
+ return createDenseXWProduct(result_type, lhs, rhs, stash);
+ }
+ if (isDenseXWProduct(result_type, rhs.result_type(), lhs.result_type())) {
+ return createDenseXWProduct(result_type, rhs, lhs, stash);
}
}
- return expr;
+ }
+ return expr;
}
} // namespace vespalib::tensor
diff --git a/eval/src/vespa/eval/tensor/dense/dense_xw_product_function.h b/eval/src/vespa/eval/tensor/dense/dense_xw_product_function.h
index 9f1bc12b110..f2f4d67c0f0 100644
--- a/eval/src/vespa/eval/tensor/dense/dense_xw_product_function.h
+++ b/eval/src/vespa/eval/tensor/dense/dense_xw_product_function.h
@@ -8,9 +8,6 @@
namespace vespalib::tensor {
-using XWInput = ConstArrayRef<double>;
-using XWOutput = ArrayRef<double>;
-
/**
* Tensor function for product of one 1-dimensional and one 2-dimensional dense tensor.
*/
diff --git a/eval/src/vespa/eval/tensor/dense/typed_cells.h b/eval/src/vespa/eval/tensor/dense/typed_cells.h
index 98f95d54d9b..0f22c85735e 100644
--- a/eval/src/vespa/eval/tensor/dense/typed_cells.h
+++ b/eval/src/vespa/eval/tensor/dense/typed_cells.h
@@ -12,25 +12,6 @@ namespace vespalib::tensor {
using CellType = vespalib::eval::ValueType::CellType;
-
-template<typename LCT, typename RCT> struct OutputCellType;
-template<> struct OutputCellType<double, double> {
- typedef double output_type;
- static constexpr CellType output_cell_type() { return CellType::DOUBLE; };
-};
-template<> struct OutputCellType<float, double> {
- typedef double output_type;
- static constexpr CellType output_cell_type() { return CellType::DOUBLE; };
-};
-template<> struct OutputCellType<double, float> {
- typedef double output_type;
- static constexpr CellType output_cell_type() { return CellType::DOUBLE; };
-};
-template<> struct OutputCellType<float, float> {
- typedef float output_type;
- static constexpr CellType output_cell_type() { return CellType::FLOAT; };
-};
-
struct TypedCells {
const void *data;
CellType type;
@@ -67,7 +48,7 @@ struct TypedCells {
};
template <typename TGT, typename... Args>
-auto dispatch_0(CellType ct, Args &&...args) {
+decltype(auto) dispatch_0(CellType ct, Args &&...args) {
switch (ct) {
case CellType::DOUBLE: return TGT::template call<double>(std::forward<Args>(args)...);
case CellType::FLOAT: return TGT::template call<float>(std::forward<Args>(args)...);
@@ -76,7 +57,7 @@ auto dispatch_0(CellType ct, Args &&...args) {
}
template <typename TGT, typename... Args>
-auto dispatch_1(const TypedCells &a, Args &&...args) {
+decltype(auto) dispatch_1(const TypedCells &a, Args &&...args) {
switch (a.type) {
case CellType::DOUBLE: return TGT::call(a.unsafe_typify<double>(), std::forward<Args>(args)...);
case CellType::FLOAT: return TGT::call(a.unsafe_typify<float>(), std::forward<Args>(args)...);
@@ -85,7 +66,7 @@ auto dispatch_1(const TypedCells &a, Args &&...args) {
}
template <typename TGT, typename A1, typename... Args>
-auto dispatch_2(A1 &&a, const TypedCells &b, Args &&...args) {
+decltype(auto) dispatch_2(A1 &&a, const TypedCells &b, Args &&...args) {
switch (b.type) {
case CellType::DOUBLE: return dispatch_1<TGT>(std::forward<A1>(a), b.unsafe_typify<double>(), std::forward<Args>(args)...);
case CellType::FLOAT: return dispatch_1<TGT>(std::forward<A1>(a), b.unsafe_typify<float>(), std::forward<Args>(args)...);
@@ -94,7 +75,7 @@ auto dispatch_2(A1 &&a, const TypedCells &b, Args &&...args) {
}
template <typename T, typename... Args>
-auto select_1(CellType a_type) {
+decltype(auto) select_1(CellType a_type) {
switch(a_type) {
case CellType::DOUBLE: return T::template get_fun<double, Args...>();
case CellType::FLOAT: return T::template get_fun<float, Args...>();
@@ -103,7 +84,7 @@ auto select_1(CellType a_type) {
}
template <typename T>
-auto select_2(CellType a_type, CellType b_type) {
+decltype(auto) select_2(CellType a_type, CellType b_type) {
switch(b_type) {
case CellType::DOUBLE: return select_1<T, double>(a_type);
case CellType::FLOAT: return select_1<T, float>(a_type);
diff --git a/fastlib/src/vespa/fastlib/io/bufferedfile.cpp b/fastlib/src/vespa/fastlib/io/bufferedfile.cpp
index 9af2a73a31b..bd1dd729e39 100644
--- a/fastlib/src/vespa/fastlib/io/bufferedfile.cpp
+++ b/fastlib/src/vespa/fastlib/io/bufferedfile.cpp
@@ -171,7 +171,7 @@ Fast_BufferedFile::SetPosition(const int64_t s)
if ((diff <= 0l) || (diff > (_bufe - buf()))) {
const int64_t newPos(s & ~(_buf.size() - 1l) );
if ((s - newPos) >= static_cast<int64_t>(_buf.size())) {
- *static_cast<int *>(0) = 1;
+ abort();
}
int64_t oldPos(_filepos);
int64_t oldLeft(_fileleft);
@@ -181,19 +181,19 @@ Fast_BufferedFile::SetPosition(const int64_t s)
fillReadBuf();
if ((oldLeft == _fileleft) && (_fileleft != 0l)) {
- *static_cast<int *>(0) = 2;
+ abort();
}
if ((_filepos == oldPos) && (_fileleft != 0l)) {
- *static_cast<int *>(0) = 3;
+ abort();
}
if ((_filepos < s) || ((_filepos == s) && (_fileleft != 0))) {
- *static_cast<int *>(0) = 4;
+ abort();
}
diff = _filepos - s;
if ( !(((diff > 0l) || ((diff == 0l) && (_fileleft == 0l))) && (diff <= static_cast<int64_t>(_buf.size())))) {
char tmp[8196];
sprintf(tmp, "diff %" PRId64 " _fileleft=%" PRId64 " _buflen=%zu", diff, _fileleft, _buf.size());
- *static_cast<int *>(0) = 5;
+ abort();
}
}
_bufi = _bufe - diff;
diff --git a/fbench/README b/fbench/README
index 6627786edfb..5d9ee714a32 100644
--- a/fbench/README
+++ b/fbench/README
@@ -5,24 +5,35 @@ vespa-fbench - fastserver benchmarking program
1 Installing vespa-fbench
-------------------------
-The preferred way of running vespa-fbench is to create your own test
-directory where you place all fbench executables and prepare test
-files. If you have access to the fbench source, you may consult the
-'INSTALL' file for information on how to install fbench. If you have a
-pre-compiled distribution of fbench, simply extract the archive. The
-fbench install directory should contain the following set of files:
-
- README
- bin/vespa-fbench
- bin/vespa-fbench-filter-file
- bin/vespa-fbench-geturl
- bin/plot.pl
- bin/pretest.sh
- bin/vespa-fbench-result-filter.pl
- bin/runtests.sh
- bin/separate.pl
- bin/vespa-fbench-split-file
-
+vespa-fbench is distributed together with Vespa in the published RPM
+and Docker image. Using the pre-built vespa-fbench is preferred, but
+if you have to compile it yourself consult the README.md in
+https://github.com/vespa-engine/vespa.
+
+Installing on Vespa CentOS / RHEL 7:
+ yum-config-manager --add-repo \
+ https://copr.fedorainfracloud.org/coprs/g/vespa/vespa/repo/epel-7/group_vespa-vespa-epel-7.repo
+ yum -y install epel-release centos-release-scl
+ yum -y install vespa
+
+The above installation provides the follwing vespa-fbench executables:
+ /opt/vespa/bin/vespa-fbench
+ /opt/vespa/bin/vespa-fbench-filter-file
+ /opt/vespa/bin/vespa-fbench-geturl
+ /opt/vespa/bin/vespa-fbench-result-filter.pl
+ /opt/vespa/bin/vespa-fbench-split-file
+
+Additional utilities referenced in this document can be fetched from
+https://github.com/vespa-engine/vespa/tree/master/fbench/util:
+ plot.pl
+ pretest.sh
+ runtests.sh
+ separate.pl
+
+It is also possible to use Docker to directly execute vespa-fbench by
+using the pre-built Vespa docker image:
+ docker run --entrypoint /opt/vespa/bin/vespa-fbench \
+ docker.io/vespaengine/vespa <your fbench options>
2 Benchmark overview
--------------------
diff --git a/fbench/src/fbench/client.cpp b/fbench/src/fbench/client.cpp
index 754fc809511..7ac52ff6c7c 100644
--- a/fbench/src/fbench/client.cpp
+++ b/fbench/src/fbench/client.cpp
@@ -182,7 +182,7 @@ Client::run()
}
}
if (_output)
- _output->write(FBENCH_DELIMITER + 1, strlen(FBENCH_DELIMITER) - 1);
+ _output->write(&FBENCH_DELIMITER[1], strlen(FBENCH_DELIMITER) - 1);
if (_args->_ignoreCount == 0)
_masterTimer->Start();
@@ -230,12 +230,12 @@ Client::run()
if (!fetch_status.Ok()) {
_output->write("\nFBENCH: URL FETCH FAILED!\n",
strlen("\nFBENCH: URL FETCH FAILED!\n"));
- _output->write(FBENCH_DELIMITER + 1, strlen(FBENCH_DELIMITER) - 1);
+ _output->write(&FBENCH_DELIMITER[1], strlen(FBENCH_DELIMITER) - 1);
} else {
sprintf(timestr, "\nTIME USED: %0.4f s\n",
_reqTimer->GetTimespan() / 1000.0);
_output->write(timestr, strlen(timestr));
- _output->write(FBENCH_DELIMITER + 1, strlen(FBENCH_DELIMITER) - 1);
+ _output->write(&FBENCH_DELIMITER[1], strlen(FBENCH_DELIMITER) - 1);
}
}
if (fetch_status.ResultSize() >= _args->_byteLimit) {
diff --git a/fbench/src/fbench/fbench.cpp b/fbench/src/fbench/fbench.cpp
index 205dc867950..723980cd1c7 100644
--- a/fbench/src/fbench/fbench.cpp
+++ b/fbench/src/fbench/fbench.cpp
@@ -63,13 +63,18 @@ FBench::~FBench()
bool
FBench::init_crypto_engine(const std::string &ca_certs_file_name,
const std::string &cert_chain_file_name,
- const std::string &private_key_file_name)
+ const std::string &private_key_file_name,
+ bool allow_default_tls)
{
if (ca_certs_file_name.empty() &&
cert_chain_file_name.empty() &&
private_key_file_name.empty())
{
- _crypto_engine = std::make_shared<vespalib::NullCryptoEngine>();
+ if (allow_default_tls) {
+ _crypto_engine = vespalib::CryptoEngine::get_default();
+ } else {
+ _crypto_engine = std::make_shared<vespalib::NullCryptoEngine>();
+ }
return true;
}
if (ca_certs_file_name.empty()) {
@@ -297,7 +302,8 @@ FBench::Usage()
printf(" -z : use single query file to be distributed between clients.\n");
printf(" -T <str> : CA certificate file to verify peer against.\n");
printf(" -C <str> : client certificate file name.\n");
- printf(" -K <str> : client private key file name.\n\n");
+ printf(" -K <str> : client private key file name.\n");
+ printf(" -D : use TLS configuration from environment if T/C/K is not used\n\n");
printf(" <hostname> : the host you want to benchmark.\n");
printf(" <port> : the port to use when contacting the host.\n\n");
printf("Several hostnames and ports can be listed\n");
@@ -332,6 +338,7 @@ FBench::Main(int argc, char *argv[])
std::string ca_certs_file_name; // -T
std::string cert_chain_file_name; // -C
std::string private_key_file_name; // -K
+ bool allow_default_tls = false; // -D
int restartLimit = -1;
bool keepAlive = true;
@@ -351,7 +358,7 @@ FBench::Main(int argc, char *argv[])
idx = 1;
optError = false;
- while((opt = GetOpt(argc, argv, "H:A:T:C:K:a:n:c:l:i:s:q:o:r:m:p:kxyzP", arg, idx)) != -1) {
+ while((opt = GetOpt(argc, argv, "H:A:T:C:K:Da:n:c:l:i:s:q:o:r:m:p:kxyzP", arg, idx)) != -1) {
switch(opt) {
case 'A':
authority = arg;
@@ -372,6 +379,9 @@ FBench::Main(int argc, char *argv[])
case 'K':
private_key_file_name = std::string(arg);
break;
+ case 'D':
+ allow_default_tls = true;
+ break;
case 'a':
queryStringToAppend = std::string(arg);
break;
@@ -443,7 +453,7 @@ FBench::Main(int argc, char *argv[])
return -1;
}
- if (!init_crypto_engine(ca_certs_file_name, cert_chain_file_name, private_key_file_name)) {
+ if (!init_crypto_engine(ca_certs_file_name, cert_chain_file_name, private_key_file_name, allow_default_tls)) {
fprintf(stderr, "failed to initialize crypto engine\n");
return -1;
}
diff --git a/fbench/src/fbench/fbench.h b/fbench/src/fbench/fbench.h
index 8cbab2e6d6c..e4a8e4e0b27 100644
--- a/fbench/src/fbench/fbench.h
+++ b/fbench/src/fbench/fbench.h
@@ -35,7 +35,8 @@ private:
bool init_crypto_engine(const std::string &ca_certs_file_name,
const std::string &cert_chain_file_name,
- const std::string &private_key_file_name);
+ const std::string &private_key_file_name,
+ bool allow_default_tls);
void InitBenchmark(int numClients, int ignoreCount, int cycle,
const char *filenamePattern, const char *outputPattern,
diff --git a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReceiver.java b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReceiver.java
index 60a5e25b3e0..c4487252e27 100644
--- a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReceiver.java
+++ b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReceiver.java
@@ -193,15 +193,9 @@ public class FileReceiver {
} catch (FileAlreadyExistsException e) {
// Don't fail if it already exists (we might get the file from several config servers when retrying, servers are down etc.
// so it might be written already). Delete temp file/dir in that case, to avoid filling the disk.
- log.log(LogLevel.DEBUG, () -> "File '" + destination.getAbsolutePath() + "' already exists, continuing: " + e.getMessage());
- try {
- if (tempFile.isDirectory())
- IOUtils.recursiveDeleteDir(tempFile);
- else
- Files.delete(tempFile.toPath());
- } catch (IOException ioe) {
- log.log(LogLevel.WARNING, "Failed deleting file/dir " + tempFile);
- }
+ log.log(LogLevel.INFO, "Failed moving file '" + tempFile.getAbsolutePath() + "' to '" + destination.getAbsolutePath() +
+ "', '" + destination.getAbsolutePath() + "' already exists");
+ deleteFileOrDirectory(tempFile);
} catch (IOException e) {
String message = "Failed moving file '" + tempFile.getAbsolutePath() + "' to '" + destination.getAbsolutePath() + "'";
log.log(LogLevel.ERROR, message, e);
@@ -209,6 +203,18 @@ public class FileReceiver {
}
}
+ private static void deleteFileOrDirectory(File path) {
+ if ( ! path.exists()) return;
+ try {
+ if (path.isDirectory())
+ IOUtils.recursiveDeleteDir(path);
+ else
+ Files.delete(path.toPath());
+ } catch (IOException ioe) {
+ log.log(LogLevel.WARNING, "Failed deleting file/dir " + path);
+ }
+ }
+
private void receiveFileMeta(Request req) {
log.log(LogLevel.DEBUG, () -> "Received method call '" + req.methodName() + "' with parameters : " + req.parameters());
FileReference reference = new FileReference(req.parameters().get(0).asString());
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
index 0981d7b84e2..3fdd6d8dc48 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
@@ -135,7 +135,7 @@ public class Flags {
HOSTNAME);
public static final UnboundStringFlag CONFIGSERVER_RPC_AUTHORIZER = defineStringFlag(
- "configserver-rpc-authorizer", "log-only",
+ "configserver-rpc-authorizer", "enforce",
"Configserver RPC authorizer. Allowed values: ['disable', 'log-only', 'enforce']",
"Takes effect on restart of configserver");
@@ -151,12 +151,11 @@ public class Flags {
"Takes effect on deployment through controller",
APPLICATION_ID);
- public static final UnboundBooleanFlag DISABLE_CHEF = defineFeatureFlag(
- "disable-chef", false,
- "Stops and disables chef-client",
- "Takes effect on next host-admin tick",
- HOSTNAME, NODE_TYPE);
-
+ public static final UnboundBooleanFlag ENABLE_GROUPING_SESSION_CACHE = defineFeatureFlag(
+ "enable-grouping-session-cache", false,
+ "Enable grouping session cache",
+ "Takes effect at redeployment",
+ APPLICATION_ID);
/** WARNING: public for testing: All flags should be defined in {@link Flags}. */
public static UnboundBooleanFlag defineFeatureFlag(String flagId, boolean defaultValue, String description,
diff --git a/fnet/src/tests/frt/method_pt/method_pt.cpp b/fnet/src/tests/frt/method_pt/method_pt.cpp
index 608a435bd1d..53960d73466 100644
--- a/fnet/src/tests/frt/method_pt/method_pt.cpp
+++ b/fnet/src/tests/frt/method_pt/method_pt.cpp
@@ -61,12 +61,18 @@ public:
//-------------------------------------------------------------
+#ifdef __clang__
+#define UNUSED_MEMBER [[maybe_unused]]
+#else
+#define UNUSED_MEMBER
+#endif
+
class ComplexA
{
private:
- uint32_t _fill1;
- uint32_t _fill2;
- uint32_t _fill3;
+ UNUSED_MEMBER uint32_t _fill1;
+ UNUSED_MEMBER uint32_t _fill2;
+ UNUSED_MEMBER uint32_t _fill3;
public:
@@ -83,9 +89,9 @@ public:
class ComplexB
{
private:
- uint32_t _fill1;
- uint32_t _fill2;
- uint32_t _fill3;
+ UNUSED_MEMBER uint32_t _fill1;
+ UNUSED_MEMBER uint32_t _fill2;
+ UNUSED_MEMBER uint32_t _fill3;
public:
diff --git a/functions.cmake b/functions.cmake
index eb29a9ffe22..a049276d845 100644
--- a/functions.cmake
+++ b/functions.cmake
@@ -132,7 +132,7 @@ function(vespa_generate_config TARGET RELATIVE_CONFIG_DEF_PATH)
add_custom_command(
OUTPUT ${CONFIG_H_PATH} ${CONFIG_CPP_PATH}
- COMMAND java -Dconfig.spec=${CONFIG_DEF_PATH} -Dconfig.dest=${CONFIG_DEST_PARENT_DIR} -Dconfig.lang=cppng -Dconfig.requireNamespace=false -Dconfig.subdir=${CONFIG_DEST_DIRNAME} -Dconfig.dumpTree=false -Xms64m -Xmx64m -jar ${PROJECT_SOURCE_DIR}/configgen/target/configgen.jar
+ COMMAND java -Dconfig.spec=${CONFIG_DEF_PATH} -Dconfig.dest=${CONFIG_DEST_PARENT_DIR} -Dconfig.lang=cpp -Dconfig.subdir=${CONFIG_DEST_DIRNAME} -Dconfig.dumpTree=false -Xms64m -Xmx64m -jar ${PROJECT_SOURCE_DIR}/configgen/target/configgen.jar
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/..
MAIN_DEPENDENCY ${CONFIG_DEF_PATH}
)
diff --git a/http-utils/src/main/java/ai/vespa/util/http/VespaHttpClientBuilder.java b/http-utils/src/main/java/ai/vespa/util/http/VespaHttpClientBuilder.java
index 5e7a9441fc8..529cfdc2aff 100644
--- a/http-utils/src/main/java/ai/vespa/util/http/VespaHttpClientBuilder.java
+++ b/http-utils/src/main/java/ai/vespa/util/http/VespaHttpClientBuilder.java
@@ -4,24 +4,28 @@ package ai.vespa.util.http;
import com.yahoo.security.tls.MixedMode;
import com.yahoo.security.tls.TlsContext;
import com.yahoo.security.tls.TransportSecurityUtils;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
-import org.apache.http.HttpRequestInterceptor;
-import org.apache.http.client.methods.HttpRequestBase;
-import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
+import org.apache.http.conn.UnsupportedSchemeException;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.routing.HttpRoutePlanner;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
+import org.apache.http.impl.conn.DefaultSchemePortResolver;
import org.apache.http.protocol.HttpContext;
import javax.net.ssl.SSLParameters;
-import java.net.URI;
-import java.net.URISyntaxException;
+import java.net.InetAddress;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -69,7 +73,7 @@ public class VespaHttpClientBuilder {
private static HttpClientBuilder createBuilder(ConnectionManagerFactory connectionManagerFactory) {
var builder = HttpClientBuilder.create();
addSslSocketFactory(builder, connectionManagerFactory);
- addTlsAwareRequestInterceptor(builder);
+ addHttpsRewritingRoutePlanner(builder);
return builder;
}
@@ -83,14 +87,17 @@ public class VespaHttpClientBuilder {
} else {
builder.setSSLSocketFactory(socketFactory);
}
+ // Workaround that allows re-using https connections, see https://stackoverflow.com/a/42112034/1615280 for details.
+ // Proper solution would be to add a request interceptor that adds a x500 principal as user token,
+ // but certificate subject CN is not accessible through the TlsContext currently.
+ builder.setUserTokenHandler(context -> null);
});
}
- private static void addTlsAwareRequestInterceptor(HttpClientBuilder builder) {
+ private static void addHttpsRewritingRoutePlanner(HttpClientBuilder builder) {
if (TransportSecurityUtils.isTransportSecurityEnabled()
&& TransportSecurityUtils.getInsecureMixedMode() != MixedMode.PLAINTEXT_CLIENT_MIXED_SERVER) {
- log.log(Level.FINE, "Adding request interceptor to client");
- builder.addInterceptorFirst(new HttpToHttpsRewritingRequestInterceptor());
+ builder.setRoutePlanner(new HttpToHttpsRoutePlanner());
}
}
@@ -106,29 +113,32 @@ public class VespaHttpClientBuilder {
.build();
}
- static class HttpToHttpsRewritingRequestInterceptor implements HttpRequestInterceptor {
+
+ /**
+ * Reroutes requests using 'http' to 'https'.
+ * Implementation inspired by {@link org.apache.http.impl.conn.DefaultRoutePlanner}, but without proxy support.
+ */
+ static class HttpToHttpsRoutePlanner implements HttpRoutePlanner {
+
@Override
- public void process(HttpRequest request, HttpContext context) {
- if (request instanceof HttpRequestBase) {
- HttpRequestBase httpUriRequest = (HttpRequestBase) request;
- httpUriRequest.setURI(rewriteUri(httpUriRequest.getURI()));
- } else {
- log.log(Level.FINE, () -> "Not a HttpRequestBase - skipping URI rewriting: " + request.getClass().getName());
- }
+ public HttpRoute determineRoute(HttpHost host, HttpRequest request, HttpContext context) throws HttpException {
+ HttpClientContext clientContext = HttpClientContext.adapt(context);
+ RequestConfig config = clientContext.getRequestConfig();
+ InetAddress local = config.getLocalAddress();
+
+ HttpHost target = resolveTarget(host);
+ boolean secure = target.getSchemeName().equalsIgnoreCase("https");
+ return new HttpRoute(target, local, secure);
}
- private static URI rewriteUri(URI originalUri) {
- if (!originalUri.getScheme().equals("http")) {
- return originalUri;
- }
- int port = originalUri.getPort();
- int rewrittenPort = port != -1 ? port : 80;
+ private HttpHost resolveTarget(HttpHost host) throws HttpException {
try {
- URI rewrittenUri = new URIBuilder(originalUri).setScheme("https").setPort(rewrittenPort).build();
- log.log(Level.FINE, () -> String.format("Uri rewritten from '%s' to '%s'", originalUri, rewrittenUri));
- return rewrittenUri;
- } catch (URISyntaxException e) {
- throw new RuntimeException(e);
+ String originalScheme = host.getSchemeName();
+ String scheme = originalScheme.equalsIgnoreCase("http") ? "https" : originalScheme;
+ int port = DefaultSchemePortResolver.INSTANCE.resolve(host);
+ return new HttpHost(host.getHostName(), port, scheme);
+ } catch (UnsupportedSchemeException e) {
+ throw new HttpException(e.getMessage(), e);
}
}
}
diff --git a/http-utils/src/test/java/ai/vespa/util/http/VespaHttpClientBuilderTest.java b/http-utils/src/test/java/ai/vespa/util/http/VespaHttpClientBuilderTest.java
index 7ffd0e459b0..85ee0913c58 100644
--- a/http-utils/src/test/java/ai/vespa/util/http/VespaHttpClientBuilderTest.java
+++ b/http-utils/src/test/java/ai/vespa/util/http/VespaHttpClientBuilderTest.java
@@ -1,14 +1,15 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package ai.vespa.util.http;
-import ai.vespa.util.http.VespaHttpClientBuilder.HttpToHttpsRewritingRequestInterceptor;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.conn.routing.HttpRoute;
import org.junit.Test;
-import java.net.URI;
-
-import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
/**
* @author bjorncs
@@ -16,24 +17,25 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
public class VespaHttpClientBuilderTest {
@Test
- public void request_interceptor_modifies_scheme_of_requests() {
- verifyProcessedUriMatchesExpectedOutput("http://dummyhostname:8080/a/path/to/resource?query=value",
- "https://dummyhostname:8080/a/path/to/resource?query=value");
+ public void route_planner_modifies_scheme_of_requests() throws HttpException {
+ verifyProcessedUriMatchesExpectedOutput("http://dummyhostname:8080", "https://dummyhostname:8080");
+ }
+
+ @Test
+ public void route_planer_handles_implicit_http_port() throws HttpException {
+ verifyProcessedUriMatchesExpectedOutput("http://dummyhostname", "https://dummyhostname:80");
}
@Test
- public void request_interceptor_add_handles_implicit_http_port() {
- verifyProcessedUriMatchesExpectedOutput("http://dummyhostname/a/path/to/resource?query=value",
- "https://dummyhostname:80/a/path/to/resource?query=value");
+ public void route_planer_handles_https_port() throws HttpException {
+ verifyProcessedUriMatchesExpectedOutput("http://dummyhostname:443", "https://dummyhostname:443");
}
- private static void verifyProcessedUriMatchesExpectedOutput(String inputUri, String expectedOutputUri) {
- var interceptor = new HttpToHttpsRewritingRequestInterceptor();
- HttpGet request = new HttpGet(inputUri);
- interceptor.process(request, new BasicHttpContext());
- URI modifiedUri = request.getURI();
- URI expectedUri = URI.create(expectedOutputUri);
- assertThat(modifiedUri).isEqualTo(expectedUri);
+ private static void verifyProcessedUriMatchesExpectedOutput(String inputHostString, String expectedHostString) throws HttpException {
+ var routePlanner = new VespaHttpClientBuilder.HttpToHttpsRoutePlanner();
+ HttpRoute newRoute = routePlanner.determineRoute(HttpHost.create(inputHostString), mock(HttpRequest.class), new HttpClientContext());
+ HttpHost target = newRoute.getTargetHost();
+ assertEquals(expectedHostString, target.toURI());
}
} \ No newline at end of file
diff --git a/jaxrs_client_utils/pom.xml b/jaxrs_client_utils/pom.xml
index 636fbab7bb0..d32d4c5eccc 100644
--- a/jaxrs_client_utils/pom.xml
+++ b/jaxrs_client_utils/pom.xml
@@ -16,6 +16,7 @@
<packaging>container-plugin</packaging>
<name>${project.artifactId}</name>
<dependencies>
+ <!-- provided -->
<dependency>
<groupId>com.yahoo.vespa</groupId>
<artifactId>vespajlib</artifactId>
@@ -29,6 +30,12 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>security-utils</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0</version>
@@ -44,6 +51,8 @@
<artifactId>jersey-proxy-client</artifactId>
<scope>provided</scope>
</dependency>
+
+ <!-- test -->
<dependency>
<groupId>com.yahoo.vespa</groupId>
<artifactId>jaxrs_utils</artifactId>
diff --git a/jaxrs_client_utils/src/main/java/ai/vespa/util/http/VespaClientBuilderFactory.java b/jaxrs_client_utils/src/main/java/ai/vespa/util/http/VespaClientBuilderFactory.java
new file mode 100644
index 00000000000..d55128069c4
--- /dev/null
+++ b/jaxrs_client_utils/src/main/java/ai/vespa/util/http/VespaClientBuilderFactory.java
@@ -0,0 +1,72 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.util.http;
+
+import com.yahoo.security.tls.MixedMode;
+import com.yahoo.security.tls.TlsContext;
+import com.yahoo.security.tls.TransportSecurityUtils;
+
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.core.UriBuilder;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Factory for JAX-RS http client builder for internal Vespa communications over http/https.
+ *
+ * Notes:
+ * - hostname verification is not enabled - CN/SAN verification is assumed to be handled by the underlying x509 trust manager.
+ * - ssl context or hostname verifier must not be overriden by the caller
+ *
+ * @author bjorncs
+ */
+public class VespaClientBuilderFactory implements AutoCloseable {
+
+ private static final Logger log = Logger.getLogger(VespaClientBuilderFactory.class.getName());
+
+ private final TlsContext tlsContext = TransportSecurityUtils.createTlsContext().orElse(null);
+ private final MixedMode mixedMode = TransportSecurityUtils.getInsecureMixedMode();
+
+ public ClientBuilder newBuilder() {
+ ClientBuilder builder = ClientBuilder.newBuilder();
+ setSslConfiguration(builder);
+ return builder;
+ }
+
+ private void setSslConfiguration(ClientBuilder builder) {
+ if (tlsContext != null) {
+ builder.sslContext(tlsContext.context());
+ builder.hostnameVerifier((hostname, sslSession) -> true); // disable hostname verification
+ if (mixedMode != MixedMode.PLAINTEXT_CLIENT_MIXED_SERVER) {
+ builder.register(new UriRewritingRequestFilter());
+ }
+ }
+ }
+
+ @Override
+ public void close() {
+ if (tlsContext != null) {
+ tlsContext.close();
+ }
+ }
+
+ static class UriRewritingRequestFilter implements ClientRequestFilter {
+ @Override
+ public void filter(ClientRequestContext requestContext) {
+ requestContext.setUri(rewriteUri(requestContext.getUri()));
+ }
+
+ private static URI rewriteUri(URI originalUri) {
+ if (!originalUri.getScheme().equals("http")) {
+ return originalUri;
+ }
+ int port = originalUri.getPort();
+ int rewrittenPort = port != -1 ? port : 80;
+ URI rewrittenUri = UriBuilder.fromUri(originalUri).scheme("https").port(rewrittenPort).build();
+ log.log(Level.FINE, () -> String.format("Uri rewritten from '%s' to '%s'", originalUri, rewrittenUri));
+ return rewrittenUri;
+ }
+ }
+}
diff --git a/jaxrs_client_utils/src/main/java/ai/vespa/util/http/package-info.java b/jaxrs_client_utils/src/main/java/ai/vespa/util/http/package-info.java
new file mode 100644
index 00000000000..8ee304d6de8
--- /dev/null
+++ b/jaxrs_client_utils/src/main/java/ai/vespa/util/http/package-info.java
@@ -0,0 +1,8 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+/**
+ * @author bjorncs
+ */
+@ExportPackage
+package ai.vespa.util.http;
+
+import com.yahoo.osgi.annotation.ExportPackage; \ No newline at end of file
diff --git a/jaxrs_client_utils/src/main/java/com/yahoo/vespa/jaxrs/client/VespaJerseyJaxRsClientFactory.java b/jaxrs_client_utils/src/main/java/com/yahoo/vespa/jaxrs/client/VespaJerseyJaxRsClientFactory.java
new file mode 100644
index 00000000000..bdc89d737d4
--- /dev/null
+++ b/jaxrs_client_utils/src/main/java/com/yahoo/vespa/jaxrs/client/VespaJerseyJaxRsClientFactory.java
@@ -0,0 +1,55 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.jaxrs.client;
+
+import ai.vespa.util.http.VespaClientBuilderFactory;
+import com.yahoo.vespa.applicationmodel.HostName;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.HttpUrlConnectorProvider;
+import org.glassfish.jersey.client.proxy.WebResourceFactory;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.UriBuilder;
+import java.util.List;
+
+/**
+ * Factory for creating Jersey based Vespa clients from a JAX-RS resource interface.
+ *
+ * @author bjorncs
+ */
+public class VespaJerseyJaxRsClientFactory implements JaxRsClientFactory, AutoCloseable {
+
+ private final VespaClientBuilderFactory clientBuilder = new VespaClientBuilderFactory();
+ // Client is a heavy-weight object with a finalizer so we create only one and re-use it
+ private final Client client;
+
+ public VespaJerseyJaxRsClientFactory(String userAgent) {
+ this.client = clientBuilder.newBuilder()
+ .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true) // Allow empty PUT
+ .property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true) // Allow e.g. PATCH method.
+ .property(ClientProperties.FOLLOW_REDIRECTS, true)
+ .register((ClientRequestFilter) context -> context.getHeaders().put(HttpHeaders.USER_AGENT, List.of(userAgent)))
+ .build();
+ }
+
+ @Override
+ public <T> T createClient(Params<T> params) {
+ WebTarget target = client.target(params.uri());
+ target.property(ClientProperties.CONNECT_TIMEOUT, (int) params.connectTimeout().toMillis());
+ target.property(ClientProperties.READ_TIMEOUT, (int) params.readTimeout().toMillis());
+ return WebResourceFactory.newResource(params.apiClass(), target);
+ }
+
+ @Override
+ public <T> T createClient(Class<T> apiClass, HostName hostName, int port, String pathPrefix, String scheme) {
+ UriBuilder uriBuilder = UriBuilder.fromPath(pathPrefix).host(hostName.s()).port(port).scheme(scheme);
+ return createClient(new Params<>(apiClass, uriBuilder.build()));
+ }
+
+ @Override
+ public void close() {
+ clientBuilder.close();
+ }
+}
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingNode.java b/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingNode.java
index e28809c2928..d4a62bb179f 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingNode.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingNode.java
@@ -244,7 +244,7 @@ public class RoutingNode implements ReplyHandler {
policy.merge(routingContext);
} catch (RuntimeException e) {
setError(ErrorCode.POLICY_ERROR,
- "Policy '" + dir.getName() + "' threw an exception during merge; " + exceptionMessageWithTrace(e));
+ "Policy '" + dir.getName() + "' and route '" + route + "' threw an exception during merge; " + exceptionMessageWithTrace(e));
}
if (reply == null) {
setError(ErrorCode.APP_FATAL_ERROR,
@@ -539,7 +539,7 @@ public class RoutingNode implements ReplyHandler {
policy.select(routingContext);
} catch (RuntimeException e) {
setError(ErrorCode.POLICY_ERROR,
- "Policy '" + dir.getName() + "' threw an exception during select; " + exceptionMessageWithTrace(e));
+ "Policy '" + dir.getName() + "' and route '" +route + "' threw an exception during select; " + exceptionMessageWithTrace(e));
return false;
}
if (children.isEmpty()) {
diff --git a/messagebus/src/vespa/messagebus/message.h b/messagebus/src/vespa/messagebus/message.h
index e7e7d74033e..539720374d0 100644
--- a/messagebus/src/vespa/messagebus/message.h
+++ b/messagebus/src/vespa/messagebus/message.h
@@ -29,8 +29,10 @@ public:
* Constructs a new instance of this class.
*/
Message();
- Message(Message &&) = default;
- Message & operator = (Message &&) = default;
+ Message(const Message &) = delete;
+ Message(Message &&) = delete;
+ Message & operator = (const Message &) = delete;
+ Message & operator = (Message &&) = delete;
/**
* If a message is deleted with elements on the callstack, this destructor
diff --git a/metrics/src/vespa/metrics/metric.cpp b/metrics/src/vespa/metrics/metric.cpp
index 579e3bdfbe3..20083300271 100644
--- a/metrics/src/vespa/metrics/metric.cpp
+++ b/metrics/src/vespa/metrics/metric.cpp
@@ -12,6 +12,7 @@
#include <iterator>
#include <cassert>
#include <algorithm>
+#include <ostream>
namespace metrics {
diff --git a/metrics/src/vespa/metrics/xmlwriter.cpp b/metrics/src/vespa/metrics/xmlwriter.cpp
index 25bc4e23d96..70e4f72d761 100644
--- a/metrics/src/vespa/metrics/xmlwriter.cpp
+++ b/metrics/src/vespa/metrics/xmlwriter.cpp
@@ -37,7 +37,7 @@ XmlWriter::visitMetricSet(const MetricSet& set, bool)
{
using namespace vespalib::xml;
if (set.used() || _verbosity >= 2) {
- _xos << XmlTag(set.getName(), CONVERT_ILLEGAL_CHARACTERS);
+ _xos << XmlTag(set.getName(), XmlTagFlags::CONVERT_ILLEGAL_CHARACTERS);
printCommonXmlParts(set);
return true;
}
@@ -56,7 +56,7 @@ XmlWriter::visitCountMetric(const AbstractCountMetric& metric, bool)
if (!metric.inUse(*values) && _verbosity < 2) return true;
using namespace vespalib::xml;
std::ostringstream ost;
- _xos << XmlTag(metric.getName(), CONVERT_ILLEGAL_CHARACTERS)
+ _xos << XmlTag(metric.getName(), XmlTagFlags::CONVERT_ILLEGAL_CHARACTERS)
<< XmlAttribute(metric.sumOnAdd()
? "count" : "value", values->toString("count"));
printCommonXmlParts(metric);
@@ -70,7 +70,7 @@ XmlWriter::visitValueMetric(const AbstractValueMetric& metric, bool)
MetricValueClass::UP values(metric.getValues());
if (!metric.inUse(*values) && _verbosity < 2) return true;
using namespace vespalib::xml;
- _xos << XmlTag(metric.getName(), CONVERT_ILLEGAL_CHARACTERS)
+ _xos << XmlTag(metric.getName(), XmlTagFlags::CONVERT_ILLEGAL_CHARACTERS)
<< XmlAttribute("average", values->getLongValue("count") == 0
? 0 : values->getDoubleValue("total")
/ values->getDoubleValue("count"))
diff --git a/model-integration/src/main/java/ai/vespa/rankingexpression/importer/DimensionRenamer.java b/model-integration/src/main/java/ai/vespa/rankingexpression/importer/DimensionRenamer.java
index 0f563a75b11..e9be35b6f84 100644
--- a/model-integration/src/main/java/ai/vespa/rankingexpression/importer/DimensionRenamer.java
+++ b/model-integration/src/main/java/ai/vespa/rankingexpression/importer/DimensionRenamer.java
@@ -75,7 +75,6 @@ public class DimensionRenamer {
if (solution != null) return solution;
for (RenameTarget target : prioritizedRenameTargets()) {
- System.out.println("Trying rename " + target);
target.insertRename(this);
solution = solveWithOrWithoutSoftConstraints(maxIterations);
if (solution != null) return solution;
@@ -90,8 +89,9 @@ public class DimensionRenamer {
if ( solution == null) {
ListMap<Arc, Constraint> hardConstraints = new ListMap<>();
boolean anyRemoved = copyHard(constraints, hardConstraints);
- if (anyRemoved)
+ if (anyRemoved) {
solution = NamingConstraintSolver.solve(dimensions, hardConstraints, maxIterations);
+ }
}
return solution;
}
diff --git a/model-integration/src/main/java/ai/vespa/rankingexpression/importer/operations/Const.java b/model-integration/src/main/java/ai/vespa/rankingexpression/importer/operations/Const.java
index d13c1ad5f3c..fc59ad35ef8 100644
--- a/model-integration/src/main/java/ai/vespa/rankingexpression/importer/operations/Const.java
+++ b/model-integration/src/main/java/ai/vespa/rankingexpression/importer/operations/Const.java
@@ -10,6 +10,8 @@ import com.yahoo.searchlib.rankingexpression.rule.ExpressionNode;
import com.yahoo.searchlib.rankingexpression.rule.ReferenceNode;
import com.yahoo.searchlib.rankingexpression.rule.TensorFunctionNode;
import com.yahoo.tensor.TensorType;
+import com.yahoo.tensor.evaluation.VariableTensor;
+import com.yahoo.tensor.functions.Rename;
import com.yahoo.tensor.functions.TensorFunction;
import java.util.List;
@@ -18,6 +20,7 @@ import java.util.Optional;
public class Const extends IntermediateOperation {
private final AttributeMap attributeMap;
+ private OrderedTensorType standardNamingType; // using standard naming convention: d0, d1, ...
public Const(String modelName,
String nodeName,
@@ -27,6 +30,7 @@ public class Const extends IntermediateOperation {
super(modelName, nodeName, inputs);
this.attributeMap = attributeMap;
this.type = type.rename(vespaName() + "_");
+ standardNamingType = OrderedTensorType.standardType(type);
setConstantValue(value());
}
@@ -51,7 +55,13 @@ public class Const extends IntermediateOperation {
} else {
expressionNode = new ReferenceNode(Reference.simple("constant", vespaName()));
}
- return new TensorFunctionNode.TensorFunctionExpressionNode(expressionNode);
+ TensorFunction output = new TensorFunctionNode.TensorFunctionExpressionNode(expressionNode);
+ if ( ! standardNamingType.equals(type)) {
+ List<String> renameFrom = standardNamingType.dimensionNames();
+ List<String> renameTo = type.dimensionNames();
+ output = new Rename(output, renameFrom, renameTo);
+ }
+ return output;
}
/** Constant names are prefixed by "modelName_" to avoid name conflicts between models */
diff --git a/model-integration/src/main/java/ai/vespa/rankingexpression/importer/operations/IntermediateOperation.java b/model-integration/src/main/java/ai/vespa/rankingexpression/importer/operations/IntermediateOperation.java
index c3980b8fe93..9c9fed89585 100644
--- a/model-integration/src/main/java/ai/vespa/rankingexpression/importer/operations/IntermediateOperation.java
+++ b/model-integration/src/main/java/ai/vespa/rankingexpression/importer/operations/IntermediateOperation.java
@@ -102,12 +102,12 @@ public abstract class IntermediateOperation {
/** Add dimension name constraints for this operation */
public void addDimensionNameConstraints(DimensionRenamer renamer) { }
- /** Conveinence method to adds dimensions and constraints of the given tensor type */
+ /** Convenience method to adds dimensions and constraints of the given tensor type */
protected void addConstraintsFrom(OrderedTensorType type, DimensionRenamer renamer) {
for (int i = 0; i < type.dimensions().size(); i++) {
renamer.addDimension(type.dimensions().get(i).name());
- // Each dimension is distinct:
+ // Each dimension is distinct and ordered correctly:
for (int j = i + 1; j < type.dimensions().size(); j++) {
renamer.addConstraint(type.dimensions().get(i).name(), type.dimensions().get(j).name(),
DimensionRenamer.Constraint.notEqual(false),
@@ -216,29 +216,29 @@ public abstract class IntermediateOperation {
return i < 0 ? 0 : Integer.parseInt(name.substring(i + 1));
}
- /**
- * An interface mapping operation attributes to Vespa Values.
- * Adapter for differences in different model types.
- */
- public interface AttributeMap {
- Optional<Value> get(String key);
- Optional<Value> get(String key, OrderedTensorType type);
- Optional<List<Value>> getList(String key);
- }
-
public abstract String operationName();
@Override
public String toString() {
- return operationName() +
+ return operationName() + "(" +
inputs().stream().map(input -> asString(input.type())).collect(Collectors.joining(", ")) +
")";
}
public String toFullString() {
- return "\t" + lazyGetType() + ":\t" + operationName() +
+ return "\t" + lazyGetType() + ":\t" + operationName() + "(" +
inputs().stream().map(input -> input.toFullString()).collect(Collectors.joining(", ")) +
")";
}
+ /**
+ * An interface mapping operation attributes to Vespa Values.
+ * Adapter for differences in different model types.
+ */
+ public interface AttributeMap {
+ Optional<Value> get(String key);
+ Optional<Value> get(String key, OrderedTensorType type);
+ Optional<List<Value>> getList(String key);
+ }
+
}
diff --git a/model-integration/src/main/java/ai/vespa/rankingexpression/importer/tensorflow/GraphImporter.java b/model-integration/src/main/java/ai/vespa/rankingexpression/importer/tensorflow/GraphImporter.java
index 357794faee2..0e9c98b2b56 100644
--- a/model-integration/src/main/java/ai/vespa/rankingexpression/importer/tensorflow/GraphImporter.java
+++ b/model-integration/src/main/java/ai/vespa/rankingexpression/importer/tensorflow/GraphImporter.java
@@ -34,6 +34,7 @@ import org.tensorflow.framework.MetaGraphDef;
import org.tensorflow.framework.NodeDef;
import org.tensorflow.framework.SignatureDef;
import org.tensorflow.framework.TensorInfo;
+import org.tensorflow.op.core.DecodeRaw;
import java.io.IOException;
import java.util.List;
diff --git a/model-integration/src/main/java/ai/vespa/rankingexpression/importer/tensorflow/TensorConverter.java b/model-integration/src/main/java/ai/vespa/rankingexpression/importer/tensorflow/TensorConverter.java
index 9cba388d00e..6ab7a69e469 100644
--- a/model-integration/src/main/java/ai/vespa/rankingexpression/importer/tensorflow/TensorConverter.java
+++ b/model-integration/src/main/java/ai/vespa/rankingexpression/importer/tensorflow/TensorConverter.java
@@ -5,15 +5,16 @@ import ai.vespa.rankingexpression.importer.OrderedTensorType;
import com.yahoo.tensor.IndexedTensor;
import com.yahoo.tensor.Tensor;
import com.yahoo.tensor.TensorType;
-import org.tensorflow.DataType;
+import org.tensorflow.framework.DataType;
import org.tensorflow.framework.TensorProto;
+import org.tensorflow.framework.TensorShapeProto;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
-
+import java.util.List;
/**
* Converts TensorFlow tensors into Vespa tensors.
@@ -48,9 +49,11 @@ public class TensorConverter {
static Tensor toVespaTensor(TensorProto tensorProto, TensorType type) {
IndexedTensor.BoundBuilder builder = (IndexedTensor.BoundBuilder)Tensor.Builder.of(type);
Values values = readValuesOf(tensorProto);
- for (int i = 0; i < values.size(); ++i) {
+ if (values.size() == 0) // Might be stored as "tensor_content" instead
+ return toVespaTensor(readTensorContentOf(tensorProto));
+
+ for (int i = 0; i < values.size(); ++i)
builder.cellByDirectIndex(i, values.get(i));
- }
return builder.build();
}
@@ -74,28 +77,47 @@ public class TensorConverter {
case UINT8: return new IntValues(tfTensor);
case INT32: return new IntValues(tfTensor);
case INT64: return new LongValues(tfTensor);
+ default: throw new IllegalArgumentException("Cannot convert a tensor with elements of type " +
+ tfTensor.dataType() + " to a Vespa tensor");
}
- throw new IllegalArgumentException("Cannot convert a tensor with elements of type " +
- tfTensor.dataType() + " to a Vespa tensor");
}
private static Values readValuesOf(TensorProto tensorProto) {
switch (tensorProto.getDtype()) {
- case DT_BOOL:
- return new ProtoBoolValues(tensorProto);
- case DT_HALF:
- return new ProtoHalfValues(tensorProto);
- case DT_INT16:
- case DT_INT32:
- return new ProtoIntValues(tensorProto);
- case DT_INT64:
- return new ProtoInt64Values(tensorProto);
- case DT_FLOAT:
- return new ProtoFloatValues(tensorProto);
- case DT_DOUBLE:
- return new ProtoDoubleValues(tensorProto);
- }
- throw new IllegalArgumentException("Unsupported data type in attribute tensor import");
+ case DT_BOOL: return new ProtoBoolValues(tensorProto);
+ case DT_HALF: return new ProtoHalfValues(tensorProto);
+ case DT_INT16: case DT_INT32: return new ProtoIntValues(tensorProto);
+ case DT_INT64: return new ProtoInt64Values(tensorProto);
+ case DT_FLOAT: return new ProtoFloatValues(tensorProto);
+ case DT_DOUBLE: return new ProtoDoubleValues(tensorProto);
+ default: throw new IllegalArgumentException("Unsupported data type in attribute tensor import");
+ }
+ }
+
+ private static Class dataTypeToClass(DataType dataType) {
+ switch (dataType) {
+ case DT_BOOL: return Boolean.class;
+ case DT_INT16: return Short.class;
+ case DT_INT32: return Integer.class;
+ case DT_INT64: return Long.class;
+ case DT_HALF: return Float.class;
+ case DT_FLOAT: return Float.class;
+ case DT_DOUBLE: return Double.class;
+ default: throw new IllegalArgumentException("Unsupported data type in attribute tensor import");
+ }
+ }
+
+ private static org.tensorflow.Tensor readTensorContentOf(TensorProto tensorProto) {
+ return org.tensorflow.Tensor.create(dataTypeToClass(tensorProto.getDtype()),
+ asSizeArray(tensorProto.getTensorShape().getDimList()),
+ tensorProto.getTensorContent().asReadOnlyByteBuffer());
+ }
+
+ private static long[] asSizeArray(List<TensorShapeProto.Dim> dimensions) {
+ long[] sizes = new long[dimensions.size()];
+ for (int i = 0; i < dimensions.size(); i++)
+ sizes[i] = dimensions.get(i).getSize();
+ return sizes;
}
/** Allows reading values from buffers of various numeric types as bytes */
diff --git a/model-integration/src/test/java/ai/vespa/rankingexpression/importer/tensorflow/DropoutImportTestCase.java b/model-integration/src/test/java/ai/vespa/rankingexpression/importer/tensorflow/DropoutImportTestCase.java
index b9d767774be..f38403bfbd4 100644
--- a/model-integration/src/test/java/ai/vespa/rankingexpression/importer/tensorflow/DropoutImportTestCase.java
+++ b/model-integration/src/test/java/ai/vespa/rankingexpression/importer/tensorflow/DropoutImportTestCase.java
@@ -34,7 +34,7 @@ public class DropoutImportTestCase {
ImportedMlFunction function = signature.outputFunction("y", "y");
assertNotNull(function);
- assertEquals("join(join(imported_ml_function_test_outputs_BiasAdd, reduce(constant(test_outputs_Const), sum, d1), f(a,b)(a * b)), imported_ml_function_test_outputs_BiasAdd, f(a,b)(max(a,b)))",
+ assertEquals("join(join(imported_ml_function_test_outputs_BiasAdd, reduce(rename(constant(test_outputs_Const), d0, d1), sum, d1), f(a,b)(a * b)), imported_ml_function_test_outputs_BiasAdd, f(a,b)(max(a,b)))",
function.expression());
model.assertEqualResult("X", "outputs/Maximum");
assertEquals("{X=tensor(d0[],d1[784])}", function.argumentTypes().toString());
diff --git a/model-integration/src/test/java/ai/vespa/rankingexpression/importer/tensorflow/RegressionTestCase.java b/model-integration/src/test/java/ai/vespa/rankingexpression/importer/tensorflow/RegressionTestCase.java
new file mode 100644
index 00000000000..46ced6f42ad
--- /dev/null
+++ b/model-integration/src/test/java/ai/vespa/rankingexpression/importer/tensorflow/RegressionTestCase.java
@@ -0,0 +1,79 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.rankingexpression.importer.tensorflow;
+
+import ai.vespa.rankingexpression.importer.ImportedModel;
+import com.yahoo.tensor.Tensor;
+import com.yahoo.tensor.TensorType;
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * @author bratseth
+ */
+public class RegressionTestCase {
+
+ @Test
+ public void testRegressionModel1() {
+ TestableTensorFlowModel model = new TestableTensorFlowModel("test",
+ "src/test/models/tensorflow/regression/test1",
+ 14,
+ 1536);
+
+ // Check constants
+ Assert.assertEquals(2, model.get().largeConstants().size());
+
+ Tensor constant0 = Tensor.from(model.get().largeConstants().get("test_Variable_read"));
+ assertNotNull(constant0);
+ assertEquals(new TensorType.Builder().indexed("d2", 1536).indexed("d1", 14).build(),
+ constant0.type());
+ assertEquals(21504, constant0.size());
+
+ Tensor constant1 = Tensor.from(model.get().largeConstants().get("test_Variable_1_read"));
+ assertNotNull(constant1);
+ assertEquals(new TensorType.Builder().indexed("d1", 14).build(), constant1.type());
+ assertEquals(14, constant1.size());
+
+ // Check (provided) functions
+ Assert.assertEquals(0, model.get().functions().size());
+
+ // Check signatures
+ Assert.assertEquals(1, model.get().signatures().size());
+ ImportedModel.Signature signature = model.get().signatures().get("serving_default");
+ assertNotNull(signature);
+
+ // Test execution
+ model.assertEqualResult("input", "MatMul");
+ model.assertEqualResult("input", "logits");
+ model.assertEqualResult("input", "Sigmoid");
+ model.assertEqualResult("input", "add");
+ }
+
+ @Test
+ public void testRegressionModel2() {
+ TestableTensorFlowModel model = new TestableTensorFlowModel("test",
+ "src/test/models/tensorflow/regression/test2",
+ 14,
+ 1536,
+ false);
+
+ // Check constants
+ Assert.assertEquals(2, model.get().largeConstants().size());
+
+ // Check (provided) functions
+ Assert.assertEquals(0, model.get().functions().size());
+
+ // Check signatures
+ Assert.assertEquals(1, model.get().signatures().size());
+ ImportedModel.Signature signature = model.get().signatures().get("serving_default");
+ assertNotNull(signature);
+
+ // Test execution
+ model.assertEqualResult("input", "MatMul");
+ model.assertEqualResult("input", "add");
+ model.assertEqualResult("input", "predict");
+ }
+
+}
diff --git a/model-integration/src/test/java/ai/vespa/rankingexpression/importer/tensorflow/TestableTensorFlowModel.java b/model-integration/src/test/java/ai/vespa/rankingexpression/importer/tensorflow/TestableTensorFlowModel.java
index 75fa2ed7933..41f343dbdaa 100644
--- a/model-integration/src/test/java/ai/vespa/rankingexpression/importer/tensorflow/TestableTensorFlowModel.java
+++ b/model-integration/src/test/java/ai/vespa/rankingexpression/importer/tensorflow/TestableTensorFlowModel.java
@@ -16,6 +16,7 @@ import com.yahoo.tensor.TensorType;
import org.tensorflow.SavedModelBundle;
import org.tensorflow.Session;
+import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.util.List;
@@ -23,7 +24,6 @@ import static org.junit.Assert.assertEquals;
/**
* Helper for TensorFlow import tests: Imports a model and provides asserts on it.
- * This currently assumes the TensorFlow model takes a single input of type tensor(d0[1],d1[784])
*
* @author bratseth
*/
@@ -32,19 +32,26 @@ public class TestableTensorFlowModel {
private SavedModelBundle tensorFlowModel;
private ImportedModel model;
- // Sizes of the input vector
- private int d0Size = 1;
- private int d1Size = 784;
+ // Spec of the input vector
+ private final boolean floatInput; // false: double
+ private final int d0Size;
+ private final int d1Size;
+
public TestableTensorFlowModel(String modelName, String modelDir) {
- tensorFlowModel = SavedModelBundle.load(modelDir, "serve");
- model = new TensorFlowImporter().importModel(modelName, modelDir, tensorFlowModel);
+ this(modelName, modelDir, 1, 784);
}
public TestableTensorFlowModel(String modelName, String modelDir, int d0Size, int d1Size) {
- this(modelName, modelDir);
+ this(modelName, modelDir, d0Size, d1Size, true);
+ }
+
+ public TestableTensorFlowModel(String modelName, String modelDir, int d0Size, int d1Size, boolean floatInput) {
+ tensorFlowModel = SavedModelBundle.load(modelDir, "serve");
+ model = new TensorFlowImporter().importModel(modelName, modelDir, tensorFlowModel);
this.d0Size = d0Size;
this.d1Size = d1Size;
+ this.floatInput = floatInput;
}
public ImportedModel get() { return model; }
@@ -53,7 +60,7 @@ public class TestableTensorFlowModel {
public void assertEqualResultSum(String inputName, String operationName, double delta) {
Tensor tfResult = tensorFlowExecute(tensorFlowModel, inputName, operationName);
Context context = contextFrom(model);
- Tensor placeholder = placeholderArgument();
+ Tensor placeholder = vespaInputArgument();
context.put(inputName, new TensorValue(placeholder));
model.functions().forEach((k, v) -> evaluateFunction(context, model, k));
@@ -71,8 +78,8 @@ public class TestableTensorFlowModel {
public void assertEqualResult(String inputName, String operationName) {
Tensor tfResult = tensorFlowExecute(tensorFlowModel, inputName, operationName);
Context context = contextFrom(model);
- Tensor placeholder = placeholderArgument();
- context.put(inputName, new TensorValue(placeholder));
+ Tensor inputValue = vespaInputArgument();
+ context.put(inputName, new TensorValue(inputValue));
model.functions().forEach((k, v) -> evaluateFunction(context, model, k));
@@ -81,17 +88,14 @@ public class TestableTensorFlowModel {
optimizer.optimize(expression, (ContextIndex)context);
Tensor vespaResult = expression.evaluate(context).asTensor();
- assertEquals("Operation '" + operationName + "' produces equal results", tfResult, vespaResult);
+ assertEquals("Operation '" + operationName + "': Actual value from Vespa equals expected value from TensorFlow",
+ tfResult, vespaResult);
}
private Tensor tensorFlowExecute(SavedModelBundle model, String inputName, String operationName) {
Session.Runner runner = model.session().runner();
- FloatBuffer fb = FloatBuffer.allocate(d0Size * d1Size);
- for (int i = 0; i < d1Size; ++i) {
- fb.put(i, (float)(i * 1.0 / d1Size));
- }
- org.tensorflow.Tensor<?> placeholder = org.tensorflow.Tensor.create(new long[]{ d0Size, d1Size }, fb);
- runner.feed(inputName, placeholder);
+ org.tensorflow.Tensor<?> input = floatInput ? tensorFlowFloatInputArgument() : tensorFlowDoubleInputArgument();
+ runner.feed(inputName, input);
List<org.tensorflow.Tensor<?>> results = runner.fetch(operationName).run();
assertEquals(1, results.size());
return TensorConverter.toVespaTensor(results.get(0));
@@ -104,7 +108,28 @@ public class TestableTensorFlowModel {
return context;
}
- private Tensor placeholderArgument() {
+ /** Must be the same as vespaInputArgument() */
+ private org.tensorflow.Tensor<?> tensorFlowDoubleInputArgument() {
+ DoubleBuffer fb = DoubleBuffer.allocate(d0Size * d1Size);
+ int i = 0;
+ for (int d0 = 0; d0 < d0Size; d0++)
+ for (int d1 = 0; d1 < d1Size; ++d1)
+ fb.put(i++, (d1 * 1.0 / d1Size));
+ return org.tensorflow.Tensor.create(new long[]{ d0Size, d1Size }, fb);
+ }
+
+ /** Must be the same as vespaInputArgument() */
+ private org.tensorflow.Tensor<?> tensorFlowFloatInputArgument() {
+ FloatBuffer fb = FloatBuffer.allocate(d0Size * d1Size);
+ int i = 0;
+ for (int d0 = 0; d0 < d0Size; d0++)
+ for (int d1 = 0; d1 < d1Size; ++d1)
+ fb.put(i++, (float)(d1 * 1.0 / d1Size));
+ return org.tensorflow.Tensor.create(new long[]{ d0Size, d1Size }, fb);
+ }
+
+ /** Must be the same as tensorFlowFloatInputArgument() */
+ private Tensor vespaInputArgument() {
Tensor.Builder b = Tensor.Builder.of(new TensorType.Builder().indexed("d0", d0Size).indexed("d1", d1Size).build());
for (int d0 = 0; d0 < d0Size; d0++)
for (int d1 = 0; d1 < d1Size; d1++)
diff --git a/model-integration/src/test/models/tensorflow/regression/test1/saved_model.pbtxt b/model-integration/src/test/models/tensorflow/regression/test1/saved_model.pbtxt
new file mode 100644
index 00000000000..678fced00d2
--- /dev/null
+++ b/model-integration/src/test/models/tensorflow/regression/test1/saved_model.pbtxt
@@ -0,0 +1,6172 @@
+saved_model_schema_version: 1
+meta_graphs {
+ meta_info_def {
+ stripped_op_list {
+ op {
+ name: "Add"
+ input_arg {
+ name: "x"
+ type_attr: "T"
+ }
+ input_arg {
+ name: "y"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "z"
+ type_attr: "T"
+ }
+ attr {
+ name: "T"
+ type: "type"
+ allowed_values {
+ list {
+ type: DT_BFLOAT16
+ type: DT_HALF
+ type: DT_FLOAT
+ type: DT_DOUBLE
+ type: DT_UINT8
+ type: DT_INT8
+ type: DT_INT16
+ type: DT_INT32
+ type: DT_INT64
+ type: DT_COMPLEX64
+ type: DT_COMPLEX128
+ type: DT_STRING
+ }
+ }
+ }
+ }
+ op {
+ name: "AddN"
+ input_arg {
+ name: "inputs"
+ type_attr: "T"
+ number_attr: "N"
+ }
+ output_arg {
+ name: "sum"
+ type_attr: "T"
+ }
+ attr {
+ name: "N"
+ type: "int"
+ has_minimum: true
+ minimum: 1
+ }
+ attr {
+ name: "T"
+ type: "type"
+ allowed_values {
+ list {
+ type: DT_FLOAT
+ type: DT_DOUBLE
+ type: DT_INT32
+ type: DT_UINT8
+ type: DT_INT16
+ type: DT_INT8
+ type: DT_COMPLEX64
+ type: DT_INT64
+ type: DT_QINT8
+ type: DT_QUINT8
+ type: DT_QINT32
+ type: DT_BFLOAT16
+ type: DT_UINT16
+ type: DT_COMPLEX128
+ type: DT_HALF
+ type: DT_UINT32
+ type: DT_UINT64
+ type: DT_VARIANT
+ }
+ }
+ }
+ is_aggregate: true
+ is_commutative: true
+ }
+ op {
+ name: "ApplyGradientDescent"
+ input_arg {
+ name: "var"
+ type_attr: "T"
+ is_ref: true
+ }
+ input_arg {
+ name: "alpha"
+ type_attr: "T"
+ }
+ input_arg {
+ name: "delta"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "out"
+ type_attr: "T"
+ is_ref: true
+ }
+ attr {
+ name: "T"
+ type: "type"
+ allowed_values {
+ list {
+ type: DT_FLOAT
+ type: DT_DOUBLE
+ type: DT_INT32
+ type: DT_UINT8
+ type: DT_INT16
+ type: DT_INT8
+ type: DT_COMPLEX64
+ type: DT_INT64
+ type: DT_QINT8
+ type: DT_QUINT8
+ type: DT_QINT32
+ type: DT_BFLOAT16
+ type: DT_UINT16
+ type: DT_COMPLEX128
+ type: DT_HALF
+ type: DT_UINT32
+ type: DT_UINT64
+ }
+ }
+ }
+ attr {
+ name: "use_locking"
+ type: "bool"
+ default_value {
+ b: false
+ }
+ }
+ }
+ op {
+ name: "Assign"
+ input_arg {
+ name: "ref"
+ type_attr: "T"
+ is_ref: true
+ }
+ input_arg {
+ name: "value"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "output_ref"
+ type_attr: "T"
+ is_ref: true
+ }
+ attr {
+ name: "T"
+ type: "type"
+ }
+ attr {
+ name: "validate_shape"
+ type: "bool"
+ default_value {
+ b: true
+ }
+ }
+ attr {
+ name: "use_locking"
+ type: "bool"
+ default_value {
+ b: true
+ }
+ }
+ allows_uninitialized_input: true
+ }
+ op {
+ name: "BroadcastGradientArgs"
+ input_arg {
+ name: "s0"
+ type_attr: "T"
+ }
+ input_arg {
+ name: "s1"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "r0"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "r1"
+ type_attr: "T"
+ }
+ attr {
+ name: "T"
+ type: "type"
+ default_value {
+ type: DT_INT32
+ }
+ allowed_values {
+ list {
+ type: DT_INT32
+ type: DT_INT64
+ }
+ }
+ }
+ }
+ op {
+ name: "Cast"
+ input_arg {
+ name: "x"
+ type_attr: "SrcT"
+ }
+ output_arg {
+ name: "y"
+ type_attr: "DstT"
+ }
+ attr {
+ name: "SrcT"
+ type: "type"
+ }
+ attr {
+ name: "DstT"
+ type: "type"
+ }
+ attr {
+ name: "Truncate"
+ type: "bool"
+ default_value {
+ b: false
+ }
+ }
+ }
+ op {
+ name: "Const"
+ output_arg {
+ name: "output"
+ type_attr: "dtype"
+ }
+ attr {
+ name: "value"
+ type: "tensor"
+ }
+ attr {
+ name: "dtype"
+ type: "type"
+ }
+ }
+ op {
+ name: "Exp"
+ input_arg {
+ name: "x"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "y"
+ type_attr: "T"
+ }
+ attr {
+ name: "T"
+ type: "type"
+ allowed_values {
+ list {
+ type: DT_BFLOAT16
+ type: DT_HALF
+ type: DT_FLOAT
+ type: DT_DOUBLE
+ type: DT_COMPLEX64
+ type: DT_COMPLEX128
+ }
+ }
+ }
+ }
+ op {
+ name: "Fill"
+ input_arg {
+ name: "dims"
+ type_attr: "index_type"
+ }
+ input_arg {
+ name: "value"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "output"
+ type_attr: "T"
+ }
+ attr {
+ name: "T"
+ type: "type"
+ }
+ attr {
+ name: "index_type"
+ type: "type"
+ default_value {
+ type: DT_INT32
+ }
+ allowed_values {
+ list {
+ type: DT_INT32
+ type: DT_INT64
+ }
+ }
+ }
+ }
+ op {
+ name: "FloorDiv"
+ input_arg {
+ name: "x"
+ type_attr: "T"
+ }
+ input_arg {
+ name: "y"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "z"
+ type_attr: "T"
+ }
+ attr {
+ name: "T"
+ type: "type"
+ allowed_values {
+ list {
+ type: DT_BFLOAT16
+ type: DT_HALF
+ type: DT_FLOAT
+ type: DT_DOUBLE
+ type: DT_UINT8
+ type: DT_INT8
+ type: DT_UINT16
+ type: DT_INT16
+ type: DT_INT32
+ type: DT_INT64
+ type: DT_COMPLEX64
+ type: DT_COMPLEX128
+ }
+ }
+ }
+ }
+ op {
+ name: "GreaterEqual"
+ input_arg {
+ name: "x"
+ type_attr: "T"
+ }
+ input_arg {
+ name: "y"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "z"
+ type: DT_BOOL
+ }
+ attr {
+ name: "T"
+ type: "type"
+ allowed_values {
+ list {
+ type: DT_FLOAT
+ type: DT_DOUBLE
+ type: DT_INT32
+ type: DT_UINT8
+ type: DT_INT16
+ type: DT_INT8
+ type: DT_INT64
+ type: DT_BFLOAT16
+ type: DT_UINT16
+ type: DT_HALF
+ type: DT_UINT32
+ type: DT_UINT64
+ }
+ }
+ }
+ }
+ op {
+ name: "Identity"
+ input_arg {
+ name: "input"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "output"
+ type_attr: "T"
+ }
+ attr {
+ name: "T"
+ type: "type"
+ }
+ }
+ op {
+ name: "L2Loss"
+ input_arg {
+ name: "t"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "output"
+ type_attr: "T"
+ }
+ attr {
+ name: "T"
+ type: "type"
+ allowed_values {
+ list {
+ type: DT_HALF
+ type: DT_BFLOAT16
+ type: DT_FLOAT
+ type: DT_DOUBLE
+ }
+ }
+ }
+ }
+ op {
+ name: "Log1p"
+ input_arg {
+ name: "x"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "y"
+ type_attr: "T"
+ }
+ attr {
+ name: "T"
+ type: "type"
+ allowed_values {
+ list {
+ type: DT_BFLOAT16
+ type: DT_HALF
+ type: DT_FLOAT
+ type: DT_DOUBLE
+ type: DT_COMPLEX64
+ type: DT_COMPLEX128
+ }
+ }
+ }
+ }
+ op {
+ name: "MatMul"
+ input_arg {
+ name: "a"
+ type_attr: "T"
+ }
+ input_arg {
+ name: "b"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "product"
+ type_attr: "T"
+ }
+ attr {
+ name: "transpose_a"
+ type: "bool"
+ default_value {
+ b: false
+ }
+ }
+ attr {
+ name: "transpose_b"
+ type: "bool"
+ default_value {
+ b: false
+ }
+ }
+ attr {
+ name: "T"
+ type: "type"
+ allowed_values {
+ list {
+ type: DT_BFLOAT16
+ type: DT_HALF
+ type: DT_FLOAT
+ type: DT_DOUBLE
+ type: DT_INT32
+ type: DT_INT64
+ type: DT_COMPLEX64
+ type: DT_COMPLEX128
+ }
+ }
+ }
+ }
+ op {
+ name: "Maximum"
+ input_arg {
+ name: "x"
+ type_attr: "T"
+ }
+ input_arg {
+ name: "y"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "z"
+ type_attr: "T"
+ }
+ attr {
+ name: "T"
+ type: "type"
+ allowed_values {
+ list {
+ type: DT_BFLOAT16
+ type: DT_HALF
+ type: DT_FLOAT
+ type: DT_DOUBLE
+ type: DT_INT32
+ type: DT_INT64
+ }
+ }
+ }
+ is_commutative: true
+ }
+ op {
+ name: "Mean"
+ input_arg {
+ name: "input"
+ type_attr: "T"
+ }
+ input_arg {
+ name: "reduction_indices"
+ type_attr: "Tidx"
+ }
+ output_arg {
+ name: "output"
+ type_attr: "T"
+ }
+ attr {
+ name: "keep_dims"
+ type: "bool"
+ default_value {
+ b: false
+ }
+ }
+ attr {
+ name: "T"
+ type: "type"
+ allowed_values {
+ list {
+ type: DT_FLOAT
+ type: DT_DOUBLE
+ type: DT_INT32
+ type: DT_UINT8
+ type: DT_INT16
+ type: DT_INT8
+ type: DT_COMPLEX64
+ type: DT_INT64
+ type: DT_QINT8
+ type: DT_QUINT8
+ type: DT_QINT32
+ type: DT_BFLOAT16
+ type: DT_UINT16
+ type: DT_COMPLEX128
+ type: DT_HALF
+ type: DT_UINT32
+ type: DT_UINT64
+ }
+ }
+ }
+ attr {
+ name: "Tidx"
+ type: "type"
+ default_value {
+ type: DT_INT32
+ }
+ allowed_values {
+ list {
+ type: DT_INT32
+ type: DT_INT64
+ }
+ }
+ }
+ }
+ op {
+ name: "MergeV2Checkpoints"
+ input_arg {
+ name: "checkpoint_prefixes"
+ type: DT_STRING
+ }
+ input_arg {
+ name: "destination_prefix"
+ type: DT_STRING
+ }
+ attr {
+ name: "delete_old_dirs"
+ type: "bool"
+ default_value {
+ b: true
+ }
+ }
+ is_stateful: true
+ }
+ op {
+ name: "Mul"
+ input_arg {
+ name: "x"
+ type_attr: "T"
+ }
+ input_arg {
+ name: "y"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "z"
+ type_attr: "T"
+ }
+ attr {
+ name: "T"
+ type: "type"
+ allowed_values {
+ list {
+ type: DT_BFLOAT16
+ type: DT_HALF
+ type: DT_FLOAT
+ type: DT_DOUBLE
+ type: DT_UINT8
+ type: DT_INT8
+ type: DT_UINT16
+ type: DT_INT16
+ type: DT_INT32
+ type: DT_INT64
+ type: DT_COMPLEX64
+ type: DT_COMPLEX128
+ }
+ }
+ }
+ is_commutative: true
+ }
+ op {
+ name: "Neg"
+ input_arg {
+ name: "x"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "y"
+ type_attr: "T"
+ }
+ attr {
+ name: "T"
+ type: "type"
+ allowed_values {
+ list {
+ type: DT_BFLOAT16
+ type: DT_HALF
+ type: DT_FLOAT
+ type: DT_DOUBLE
+ type: DT_INT32
+ type: DT_INT64
+ type: DT_COMPLEX64
+ type: DT_COMPLEX128
+ }
+ }
+ }
+ }
+ op {
+ name: "NoOp"
+ }
+ op {
+ name: "Pack"
+ input_arg {
+ name: "values"
+ type_attr: "T"
+ number_attr: "N"
+ }
+ output_arg {
+ name: "output"
+ type_attr: "T"
+ }
+ attr {
+ name: "N"
+ type: "int"
+ has_minimum: true
+ minimum: 1
+ }
+ attr {
+ name: "T"
+ type: "type"
+ }
+ attr {
+ name: "axis"
+ type: "int"
+ default_value {
+ i: 0
+ }
+ }
+ }
+ op {
+ name: "Placeholder"
+ output_arg {
+ name: "output"
+ type_attr: "dtype"
+ }
+ attr {
+ name: "dtype"
+ type: "type"
+ }
+ attr {
+ name: "shape"
+ type: "shape"
+ default_value {
+ shape {
+ unknown_rank: true
+ }
+ }
+ }
+ }
+ op {
+ name: "PlaceholderWithDefault"
+ input_arg {
+ name: "input"
+ type_attr: "dtype"
+ }
+ output_arg {
+ name: "output"
+ type_attr: "dtype"
+ }
+ attr {
+ name: "dtype"
+ type: "type"
+ }
+ attr {
+ name: "shape"
+ type: "shape"
+ }
+ }
+ op {
+ name: "Prod"
+ input_arg {
+ name: "input"
+ type_attr: "T"
+ }
+ input_arg {
+ name: "reduction_indices"
+ type_attr: "Tidx"
+ }
+ output_arg {
+ name: "output"
+ type_attr: "T"
+ }
+ attr {
+ name: "keep_dims"
+ type: "bool"
+ default_value {
+ b: false
+ }
+ }
+ attr {
+ name: "T"
+ type: "type"
+ allowed_values {
+ list {
+ type: DT_FLOAT
+ type: DT_DOUBLE
+ type: DT_INT32
+ type: DT_UINT8
+ type: DT_INT16
+ type: DT_INT8
+ type: DT_COMPLEX64
+ type: DT_INT64
+ type: DT_QINT8
+ type: DT_QUINT8
+ type: DT_QINT32
+ type: DT_BFLOAT16
+ type: DT_UINT16
+ type: DT_COMPLEX128
+ type: DT_HALF
+ type: DT_UINT32
+ type: DT_UINT64
+ }
+ }
+ }
+ attr {
+ name: "Tidx"
+ type: "type"
+ default_value {
+ type: DT_INT32
+ }
+ allowed_values {
+ list {
+ type: DT_INT32
+ type: DT_INT64
+ }
+ }
+ }
+ }
+ op {
+ name: "RealDiv"
+ input_arg {
+ name: "x"
+ type_attr: "T"
+ }
+ input_arg {
+ name: "y"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "z"
+ type_attr: "T"
+ }
+ attr {
+ name: "T"
+ type: "type"
+ allowed_values {
+ list {
+ type: DT_BFLOAT16
+ type: DT_HALF
+ type: DT_FLOAT
+ type: DT_DOUBLE
+ type: DT_UINT8
+ type: DT_INT8
+ type: DT_UINT16
+ type: DT_INT16
+ type: DT_INT32
+ type: DT_INT64
+ type: DT_COMPLEX64
+ type: DT_COMPLEX128
+ }
+ }
+ }
+ }
+ op {
+ name: "Reciprocal"
+ input_arg {
+ name: "x"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "y"
+ type_attr: "T"
+ }
+ attr {
+ name: "T"
+ type: "type"
+ allowed_values {
+ list {
+ type: DT_BFLOAT16
+ type: DT_HALF
+ type: DT_FLOAT
+ type: DT_DOUBLE
+ type: DT_INT32
+ type: DT_INT64
+ type: DT_COMPLEX64
+ type: DT_COMPLEX128
+ }
+ }
+ }
+ }
+ op {
+ name: "Reshape"
+ input_arg {
+ name: "tensor"
+ type_attr: "T"
+ }
+ input_arg {
+ name: "shape"
+ type_attr: "Tshape"
+ }
+ output_arg {
+ name: "output"
+ type_attr: "T"
+ }
+ attr {
+ name: "T"
+ type: "type"
+ }
+ attr {
+ name: "Tshape"
+ type: "type"
+ default_value {
+ type: DT_INT32
+ }
+ allowed_values {
+ list {
+ type: DT_INT32
+ type: DT_INT64
+ }
+ }
+ }
+ }
+ op {
+ name: "RestoreV2"
+ input_arg {
+ name: "prefix"
+ type: DT_STRING
+ }
+ input_arg {
+ name: "tensor_names"
+ type: DT_STRING
+ }
+ input_arg {
+ name: "shape_and_slices"
+ type: DT_STRING
+ }
+ output_arg {
+ name: "tensors"
+ type_list_attr: "dtypes"
+ }
+ attr {
+ name: "dtypes"
+ type: "list(type)"
+ has_minimum: true
+ minimum: 1
+ }
+ is_stateful: true
+ }
+ op {
+ name: "SaveV2"
+ input_arg {
+ name: "prefix"
+ type: DT_STRING
+ }
+ input_arg {
+ name: "tensor_names"
+ type: DT_STRING
+ }
+ input_arg {
+ name: "shape_and_slices"
+ type: DT_STRING
+ }
+ input_arg {
+ name: "tensors"
+ type_list_attr: "dtypes"
+ }
+ attr {
+ name: "dtypes"
+ type: "list(type)"
+ has_minimum: true
+ minimum: 1
+ }
+ is_stateful: true
+ }
+ op {
+ name: "Select"
+ input_arg {
+ name: "condition"
+ type: DT_BOOL
+ }
+ input_arg {
+ name: "t"
+ type_attr: "T"
+ }
+ input_arg {
+ name: "e"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "output"
+ type_attr: "T"
+ }
+ attr {
+ name: "T"
+ type: "type"
+ }
+ }
+ op {
+ name: "Shape"
+ input_arg {
+ name: "input"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "output"
+ type_attr: "out_type"
+ }
+ attr {
+ name: "T"
+ type: "type"
+ }
+ attr {
+ name: "out_type"
+ type: "type"
+ default_value {
+ type: DT_INT32
+ }
+ allowed_values {
+ list {
+ type: DT_INT32
+ type: DT_INT64
+ }
+ }
+ }
+ }
+ op {
+ name: "ShardedFilename"
+ input_arg {
+ name: "basename"
+ type: DT_STRING
+ }
+ input_arg {
+ name: "shard"
+ type: DT_INT32
+ }
+ input_arg {
+ name: "num_shards"
+ type: DT_INT32
+ }
+ output_arg {
+ name: "filename"
+ type: DT_STRING
+ }
+ }
+ op {
+ name: "Sigmoid"
+ input_arg {
+ name: "x"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "y"
+ type_attr: "T"
+ }
+ attr {
+ name: "T"
+ type: "type"
+ allowed_values {
+ list {
+ type: DT_BFLOAT16
+ type: DT_HALF
+ type: DT_FLOAT
+ type: DT_DOUBLE
+ type: DT_COMPLEX64
+ type: DT_COMPLEX128
+ }
+ }
+ }
+ }
+ op {
+ name: "StringJoin"
+ input_arg {
+ name: "inputs"
+ type: DT_STRING
+ number_attr: "N"
+ }
+ output_arg {
+ name: "output"
+ type: DT_STRING
+ }
+ attr {
+ name: "N"
+ type: "int"
+ has_minimum: true
+ minimum: 1
+ }
+ attr {
+ name: "separator"
+ type: "string"
+ default_value {
+ s: ""
+ }
+ }
+ }
+ op {
+ name: "Sub"
+ input_arg {
+ name: "x"
+ type_attr: "T"
+ }
+ input_arg {
+ name: "y"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "z"
+ type_attr: "T"
+ }
+ attr {
+ name: "T"
+ type: "type"
+ allowed_values {
+ list {
+ type: DT_BFLOAT16
+ type: DT_HALF
+ type: DT_FLOAT
+ type: DT_DOUBLE
+ type: DT_UINT8
+ type: DT_INT8
+ type: DT_UINT16
+ type: DT_INT16
+ type: DT_INT32
+ type: DT_INT64
+ type: DT_COMPLEX64
+ type: DT_COMPLEX128
+ }
+ }
+ }
+ }
+ op {
+ name: "Sum"
+ input_arg {
+ name: "input"
+ type_attr: "T"
+ }
+ input_arg {
+ name: "reduction_indices"
+ type_attr: "Tidx"
+ }
+ output_arg {
+ name: "output"
+ type_attr: "T"
+ }
+ attr {
+ name: "keep_dims"
+ type: "bool"
+ default_value {
+ b: false
+ }
+ }
+ attr {
+ name: "T"
+ type: "type"
+ allowed_values {
+ list {
+ type: DT_FLOAT
+ type: DT_DOUBLE
+ type: DT_INT32
+ type: DT_UINT8
+ type: DT_INT16
+ type: DT_INT8
+ type: DT_COMPLEX64
+ type: DT_INT64
+ type: DT_QINT8
+ type: DT_QUINT8
+ type: DT_QINT32
+ type: DT_BFLOAT16
+ type: DT_UINT16
+ type: DT_COMPLEX128
+ type: DT_HALF
+ type: DT_UINT32
+ type: DT_UINT64
+ }
+ }
+ }
+ attr {
+ name: "Tidx"
+ type: "type"
+ default_value {
+ type: DT_INT32
+ }
+ allowed_values {
+ list {
+ type: DT_INT32
+ type: DT_INT64
+ }
+ }
+ }
+ }
+ op {
+ name: "Tile"
+ input_arg {
+ name: "input"
+ type_attr: "T"
+ }
+ input_arg {
+ name: "multiples"
+ type_attr: "Tmultiples"
+ }
+ output_arg {
+ name: "output"
+ type_attr: "T"
+ }
+ attr {
+ name: "T"
+ type: "type"
+ }
+ attr {
+ name: "Tmultiples"
+ type: "type"
+ default_value {
+ type: DT_INT32
+ }
+ allowed_values {
+ list {
+ type: DT_INT32
+ type: DT_INT64
+ }
+ }
+ }
+ }
+ op {
+ name: "TruncatedNormal"
+ input_arg {
+ name: "shape"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "output"
+ type_attr: "dtype"
+ }
+ attr {
+ name: "seed"
+ type: "int"
+ default_value {
+ i: 0
+ }
+ }
+ attr {
+ name: "seed2"
+ type: "int"
+ default_value {
+ i: 0
+ }
+ }
+ attr {
+ name: "dtype"
+ type: "type"
+ allowed_values {
+ list {
+ type: DT_HALF
+ type: DT_BFLOAT16
+ type: DT_FLOAT
+ type: DT_DOUBLE
+ }
+ }
+ }
+ attr {
+ name: "T"
+ type: "type"
+ allowed_values {
+ list {
+ type: DT_INT32
+ type: DT_INT64
+ }
+ }
+ }
+ is_stateful: true
+ }
+ op {
+ name: "VariableV2"
+ output_arg {
+ name: "ref"
+ type_attr: "dtype"
+ is_ref: true
+ }
+ attr {
+ name: "shape"
+ type: "shape"
+ }
+ attr {
+ name: "dtype"
+ type: "type"
+ }
+ attr {
+ name: "container"
+ type: "string"
+ default_value {
+ s: ""
+ }
+ }
+ attr {
+ name: "shared_name"
+ type: "string"
+ default_value {
+ s: ""
+ }
+ }
+ is_stateful: true
+ }
+ op {
+ name: "ZerosLike"
+ input_arg {
+ name: "x"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "y"
+ type_attr: "T"
+ }
+ attr {
+ name: "T"
+ type: "type"
+ }
+ }
+ }
+ tags: "serve"
+ tensorflow_version: "1.13.1"
+ tensorflow_git_version: "b\'unknown\'"
+ }
+ graph_def {
+ node {
+ name: "input"
+ op: "Placeholder"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 1536
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "shape"
+ value {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 1536
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "Placeholder"
+ op: "Placeholder"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "shape"
+ value {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "truncated_normal/shape"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 2
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ dim {
+ size: 2
+ }
+ }
+ tensor_content: "\000\006\000\000\016\000\000\000"
+ }
+ }
+ }
+ }
+ node {
+ name: "truncated_normal/mean"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ }
+ float_val: 0.0
+ }
+ }
+ }
+ }
+ node {
+ name: "truncated_normal/stddev"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ }
+ float_val: 1.0
+ }
+ }
+ }
+ }
+ node {
+ name: "truncated_normal/TruncatedNormal"
+ op: "TruncatedNormal"
+ input: "truncated_normal/shape"
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 1536
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "seed"
+ value {
+ i: 0
+ }
+ }
+ attr {
+ key: "seed2"
+ value {
+ i: 0
+ }
+ }
+ }
+ node {
+ name: "truncated_normal/mul"
+ op: "Mul"
+ input: "truncated_normal/TruncatedNormal"
+ input: "truncated_normal/stddev"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 1536
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "truncated_normal"
+ op: "Add"
+ input: "truncated_normal/mul"
+ input: "truncated_normal/mean"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 1536
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "Variable"
+ op: "VariableV2"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 1536
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "container"
+ value {
+ s: ""
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "shape"
+ value {
+ shape {
+ dim {
+ size: 1536
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ attr {
+ key: "shared_name"
+ value {
+ s: ""
+ }
+ }
+ }
+ node {
+ name: "Variable/Assign"
+ op: "Assign"
+ input: "Variable"
+ input: "truncated_normal"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@Variable"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 1536
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "use_locking"
+ value {
+ b: true
+ }
+ }
+ attr {
+ key: "validate_shape"
+ value {
+ b: true
+ }
+ }
+ }
+ node {
+ name: "Variable/read"
+ op: "Identity"
+ input: "Variable"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@Variable"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 1536
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "zeros"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ dim {
+ size: 14
+ }
+ }
+ float_val: 0.0
+ }
+ }
+ }
+ }
+ node {
+ name: "Variable_1"
+ op: "VariableV2"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "container"
+ value {
+ s: ""
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "shape"
+ value {
+ shape {
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ attr {
+ key: "shared_name"
+ value {
+ s: ""
+ }
+ }
+ }
+ node {
+ name: "Variable_1/Assign"
+ op: "Assign"
+ input: "Variable_1"
+ input: "zeros"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@Variable_1"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "use_locking"
+ value {
+ b: true
+ }
+ }
+ attr {
+ key: "validate_shape"
+ value {
+ b: true
+ }
+ }
+ }
+ node {
+ name: "Variable_1/read"
+ op: "Identity"
+ input: "Variable_1"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@Variable_1"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "MatMul"
+ op: "MatMul"
+ input: "input"
+ input: "Variable/read"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "transpose_a"
+ value {
+ b: false
+ }
+ }
+ attr {
+ key: "transpose_b"
+ value {
+ b: false
+ }
+ }
+ }
+ node {
+ name: "add"
+ op: "Add"
+ input: "MatMul"
+ input: "Variable_1/read"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "logits"
+ op: "Identity"
+ input: "add"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "logistic_loss/zeros_like"
+ op: "ZerosLike"
+ input: "logits"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "logistic_loss/GreaterEqual"
+ op: "GreaterEqual"
+ input: "logits"
+ input: "logistic_loss/zeros_like"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "logistic_loss/Select"
+ op: "Select"
+ input: "logistic_loss/GreaterEqual"
+ input: "logits"
+ input: "logistic_loss/zeros_like"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "logistic_loss/Neg"
+ op: "Neg"
+ input: "logits"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "logistic_loss/Select_1"
+ op: "Select"
+ input: "logistic_loss/GreaterEqual"
+ input: "logistic_loss/Neg"
+ input: "logits"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "logistic_loss/mul"
+ op: "Mul"
+ input: "logits"
+ input: "Placeholder"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "logistic_loss/sub"
+ op: "Sub"
+ input: "logistic_loss/Select"
+ input: "logistic_loss/mul"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "logistic_loss/Exp"
+ op: "Exp"
+ input: "logistic_loss/Select_1"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "logistic_loss/Log1p"
+ op: "Log1p"
+ input: "logistic_loss/Exp"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "logistic_loss"
+ op: "Add"
+ input: "logistic_loss/sub"
+ input: "logistic_loss/Log1p"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "Const"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 2
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ dim {
+ size: 2
+ }
+ }
+ tensor_content: "\000\000\000\000\001\000\000\000"
+ }
+ }
+ }
+ }
+ node {
+ name: "Mean"
+ op: "Mean"
+ input: "logistic_loss"
+ input: "Const"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tidx"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ attr {
+ key: "keep_dims"
+ value {
+ b: false
+ }
+ }
+ }
+ node {
+ name: "L2Loss"
+ op: "L2Loss"
+ input: "Variable/read"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "mul/x"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ }
+ float_val: 9.999999747378752e-05
+ }
+ }
+ }
+ }
+ node {
+ name: "mul"
+ op: "Mul"
+ input: "mul/x"
+ input: "L2Loss"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "add_1"
+ op: "Add"
+ input: "Mean"
+ input: "mul"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "Const_1"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ dim {
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "Mean_1"
+ op: "Mean"
+ input: "add_1"
+ input: "Const_1"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tidx"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ attr {
+ key: "keep_dims"
+ value {
+ b: false
+ }
+ }
+ }
+ node {
+ name: "gradients/Shape"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ dim {
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/grad_ys_0"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ }
+ float_val: 1.0
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/Fill"
+ op: "Fill"
+ input: "gradients/Shape"
+ input: "gradients/grad_ys_0"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ attr {
+ key: "index_type"
+ value {
+ type: DT_INT32
+ }
+ }
+ }
+ node {
+ name: "gradients/Mean_1_grad/Reshape/shape"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ dim {
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/Mean_1_grad/Reshape"
+ op: "Reshape"
+ input: "gradients/Fill"
+ input: "gradients/Mean_1_grad/Reshape/shape"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tshape"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/Mean_1_grad/Const"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ dim {
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/Mean_1_grad/Tile"
+ op: "Tile"
+ input: "gradients/Mean_1_grad/Reshape"
+ input: "gradients/Mean_1_grad/Const"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tmultiples"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/Mean_1_grad/Const_1"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ }
+ float_val: 1.0
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/Mean_1_grad/truediv"
+ op: "RealDiv"
+ input: "gradients/Mean_1_grad/Tile"
+ input: "gradients/Mean_1_grad/Const_1"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/add_1_grad/tuple/group_deps"
+ op: "NoOp"
+ input: "^gradients/Mean_1_grad/truediv"
+ }
+ node {
+ name: "gradients/add_1_grad/tuple/control_dependency"
+ op: "Identity"
+ input: "gradients/Mean_1_grad/truediv"
+ input: "^gradients/add_1_grad/tuple/group_deps"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@gradients/Mean_1_grad/truediv"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/add_1_grad/tuple/control_dependency_1"
+ op: "Identity"
+ input: "gradients/Mean_1_grad/truediv"
+ input: "^gradients/add_1_grad/tuple/group_deps"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@gradients/Mean_1_grad/truediv"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/Mean_grad/Reshape/shape"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 2
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ dim {
+ size: 2
+ }
+ }
+ tensor_content: "\001\000\000\000\001\000\000\000"
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/Mean_grad/Reshape"
+ op: "Reshape"
+ input: "gradients/add_1_grad/tuple/control_dependency"
+ input: "gradients/Mean_grad/Reshape/shape"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tshape"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 1
+ }
+ dim {
+ size: 1
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/Mean_grad/Shape"
+ op: "Shape"
+ input: "logistic_loss"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 2
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "out_type"
+ value {
+ type: DT_INT32
+ }
+ }
+ }
+ node {
+ name: "gradients/Mean_grad/Tile"
+ op: "Tile"
+ input: "gradients/Mean_grad/Reshape"
+ input: "gradients/Mean_grad/Shape"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tmultiples"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/Mean_grad/Shape_1"
+ op: "Shape"
+ input: "logistic_loss"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 2
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "out_type"
+ value {
+ type: DT_INT32
+ }
+ }
+ }
+ node {
+ name: "gradients/Mean_grad/Shape_2"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ dim {
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/Mean_grad/Const"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 1
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ dim {
+ size: 1
+ }
+ }
+ int_val: 0
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/Mean_grad/Prod"
+ op: "Prod"
+ input: "gradients/Mean_grad/Shape_1"
+ input: "gradients/Mean_grad/Const"
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "Tidx"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ attr {
+ key: "keep_dims"
+ value {
+ b: false
+ }
+ }
+ }
+ node {
+ name: "gradients/Mean_grad/Const_1"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 1
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ dim {
+ size: 1
+ }
+ }
+ int_val: 0
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/Mean_grad/Prod_1"
+ op: "Prod"
+ input: "gradients/Mean_grad/Shape_2"
+ input: "gradients/Mean_grad/Const_1"
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "Tidx"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ attr {
+ key: "keep_dims"
+ value {
+ b: false
+ }
+ }
+ }
+ node {
+ name: "gradients/Mean_grad/Maximum/y"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ }
+ int_val: 1
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/Mean_grad/Maximum"
+ op: "Maximum"
+ input: "gradients/Mean_grad/Prod_1"
+ input: "gradients/Mean_grad/Maximum/y"
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/Mean_grad/floordiv"
+ op: "FloorDiv"
+ input: "gradients/Mean_grad/Prod"
+ input: "gradients/Mean_grad/Maximum"
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/Mean_grad/Cast"
+ op: "Cast"
+ input: "gradients/Mean_grad/floordiv"
+ attr {
+ key: "DstT"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "SrcT"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "Truncate"
+ value {
+ b: false
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/Mean_grad/truediv"
+ op: "RealDiv"
+ input: "gradients/Mean_grad/Tile"
+ input: "gradients/Mean_grad/Cast"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/mul_grad/Mul"
+ op: "Mul"
+ input: "gradients/add_1_grad/tuple/control_dependency_1"
+ input: "L2Loss"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/mul_grad/Mul_1"
+ op: "Mul"
+ input: "gradients/add_1_grad/tuple/control_dependency_1"
+ input: "mul/x"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/mul_grad/tuple/group_deps"
+ op: "NoOp"
+ input: "^gradients/mul_grad/Mul"
+ input: "^gradients/mul_grad/Mul_1"
+ }
+ node {
+ name: "gradients/mul_grad/tuple/control_dependency"
+ op: "Identity"
+ input: "gradients/mul_grad/Mul"
+ input: "^gradients/mul_grad/tuple/group_deps"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@gradients/mul_grad/Mul"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/mul_grad/tuple/control_dependency_1"
+ op: "Identity"
+ input: "gradients/mul_grad/Mul_1"
+ input: "^gradients/mul_grad/tuple/group_deps"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@gradients/mul_grad/Mul_1"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss_grad/Shape"
+ op: "Shape"
+ input: "logistic_loss/sub"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 2
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "out_type"
+ value {
+ type: DT_INT32
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss_grad/Shape_1"
+ op: "Shape"
+ input: "logistic_loss/Log1p"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 2
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "out_type"
+ value {
+ type: DT_INT32
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss_grad/BroadcastGradientArgs"
+ op: "BroadcastGradientArgs"
+ input: "gradients/logistic_loss_grad/Shape"
+ input: "gradients/logistic_loss_grad/Shape_1"
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ }
+ shape {
+ dim {
+ size: -1
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss_grad/Sum"
+ op: "Sum"
+ input: "gradients/Mean_grad/truediv"
+ input: "gradients/logistic_loss_grad/BroadcastGradientArgs"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tidx"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ unknown_rank: true
+ }
+ }
+ }
+ }
+ attr {
+ key: "keep_dims"
+ value {
+ b: false
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss_grad/Reshape"
+ op: "Reshape"
+ input: "gradients/logistic_loss_grad/Sum"
+ input: "gradients/logistic_loss_grad/Shape"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tshape"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss_grad/Sum_1"
+ op: "Sum"
+ input: "gradients/Mean_grad/truediv"
+ input: "gradients/logistic_loss_grad/BroadcastGradientArgs:1"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tidx"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ unknown_rank: true
+ }
+ }
+ }
+ }
+ attr {
+ key: "keep_dims"
+ value {
+ b: false
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss_grad/Reshape_1"
+ op: "Reshape"
+ input: "gradients/logistic_loss_grad/Sum_1"
+ input: "gradients/logistic_loss_grad/Shape_1"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tshape"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss_grad/tuple/group_deps"
+ op: "NoOp"
+ input: "^gradients/logistic_loss_grad/Reshape"
+ input: "^gradients/logistic_loss_grad/Reshape_1"
+ }
+ node {
+ name: "gradients/logistic_loss_grad/tuple/control_dependency"
+ op: "Identity"
+ input: "gradients/logistic_loss_grad/Reshape"
+ input: "^gradients/logistic_loss_grad/tuple/group_deps"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@gradients/logistic_loss_grad/Reshape"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss_grad/tuple/control_dependency_1"
+ op: "Identity"
+ input: "gradients/logistic_loss_grad/Reshape_1"
+ input: "^gradients/logistic_loss_grad/tuple/group_deps"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@gradients/logistic_loss_grad/Reshape_1"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/L2Loss_grad/mul"
+ op: "Mul"
+ input: "Variable/read"
+ input: "gradients/mul_grad/tuple/control_dependency_1"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 1536
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/sub_grad/Shape"
+ op: "Shape"
+ input: "logistic_loss/Select"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 2
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "out_type"
+ value {
+ type: DT_INT32
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/sub_grad/Shape_1"
+ op: "Shape"
+ input: "logistic_loss/mul"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 2
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "out_type"
+ value {
+ type: DT_INT32
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/sub_grad/BroadcastGradientArgs"
+ op: "BroadcastGradientArgs"
+ input: "gradients/logistic_loss/sub_grad/Shape"
+ input: "gradients/logistic_loss/sub_grad/Shape_1"
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ }
+ shape {
+ dim {
+ size: -1
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/sub_grad/Sum"
+ op: "Sum"
+ input: "gradients/logistic_loss_grad/tuple/control_dependency"
+ input: "gradients/logistic_loss/sub_grad/BroadcastGradientArgs"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tidx"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ unknown_rank: true
+ }
+ }
+ }
+ }
+ attr {
+ key: "keep_dims"
+ value {
+ b: false
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/sub_grad/Reshape"
+ op: "Reshape"
+ input: "gradients/logistic_loss/sub_grad/Sum"
+ input: "gradients/logistic_loss/sub_grad/Shape"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tshape"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/sub_grad/Sum_1"
+ op: "Sum"
+ input: "gradients/logistic_loss_grad/tuple/control_dependency"
+ input: "gradients/logistic_loss/sub_grad/BroadcastGradientArgs:1"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tidx"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ unknown_rank: true
+ }
+ }
+ }
+ }
+ attr {
+ key: "keep_dims"
+ value {
+ b: false
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/sub_grad/Neg"
+ op: "Neg"
+ input: "gradients/logistic_loss/sub_grad/Sum_1"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ unknown_rank: true
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/sub_grad/Reshape_1"
+ op: "Reshape"
+ input: "gradients/logistic_loss/sub_grad/Neg"
+ input: "gradients/logistic_loss/sub_grad/Shape_1"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tshape"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/sub_grad/tuple/group_deps"
+ op: "NoOp"
+ input: "^gradients/logistic_loss/sub_grad/Reshape"
+ input: "^gradients/logistic_loss/sub_grad/Reshape_1"
+ }
+ node {
+ name: "gradients/logistic_loss/sub_grad/tuple/control_dependency"
+ op: "Identity"
+ input: "gradients/logistic_loss/sub_grad/Reshape"
+ input: "^gradients/logistic_loss/sub_grad/tuple/group_deps"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@gradients/logistic_loss/sub_grad/Reshape"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/sub_grad/tuple/control_dependency_1"
+ op: "Identity"
+ input: "gradients/logistic_loss/sub_grad/Reshape_1"
+ input: "^gradients/logistic_loss/sub_grad/tuple/group_deps"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@gradients/logistic_loss/sub_grad/Reshape_1"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/Log1p_grad/add/x"
+ op: "Const"
+ input: "^gradients/logistic_loss_grad/tuple/control_dependency_1"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ }
+ float_val: 1.0
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/Log1p_grad/add"
+ op: "Add"
+ input: "gradients/logistic_loss/Log1p_grad/add/x"
+ input: "logistic_loss/Exp"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/Log1p_grad/Reciprocal"
+ op: "Reciprocal"
+ input: "gradients/logistic_loss/Log1p_grad/add"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/Log1p_grad/mul"
+ op: "Mul"
+ input: "gradients/logistic_loss_grad/tuple/control_dependency_1"
+ input: "gradients/logistic_loss/Log1p_grad/Reciprocal"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/Select_grad/zeros_like"
+ op: "ZerosLike"
+ input: "logits"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/Select_grad/Select"
+ op: "Select"
+ input: "logistic_loss/GreaterEqual"
+ input: "gradients/logistic_loss/sub_grad/tuple/control_dependency"
+ input: "gradients/logistic_loss/Select_grad/zeros_like"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/Select_grad/Select_1"
+ op: "Select"
+ input: "logistic_loss/GreaterEqual"
+ input: "gradients/logistic_loss/Select_grad/zeros_like"
+ input: "gradients/logistic_loss/sub_grad/tuple/control_dependency"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/Select_grad/tuple/group_deps"
+ op: "NoOp"
+ input: "^gradients/logistic_loss/Select_grad/Select"
+ input: "^gradients/logistic_loss/Select_grad/Select_1"
+ }
+ node {
+ name: "gradients/logistic_loss/Select_grad/tuple/control_dependency"
+ op: "Identity"
+ input: "gradients/logistic_loss/Select_grad/Select"
+ input: "^gradients/logistic_loss/Select_grad/tuple/group_deps"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@gradients/logistic_loss/Select_grad/Select"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/Select_grad/tuple/control_dependency_1"
+ op: "Identity"
+ input: "gradients/logistic_loss/Select_grad/Select_1"
+ input: "^gradients/logistic_loss/Select_grad/tuple/group_deps"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@gradients/logistic_loss/Select_grad/Select_1"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/mul_grad/Shape"
+ op: "Shape"
+ input: "logits"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 2
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "out_type"
+ value {
+ type: DT_INT32
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/mul_grad/Shape_1"
+ op: "Shape"
+ input: "Placeholder"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 2
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "out_type"
+ value {
+ type: DT_INT32
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/mul_grad/BroadcastGradientArgs"
+ op: "BroadcastGradientArgs"
+ input: "gradients/logistic_loss/mul_grad/Shape"
+ input: "gradients/logistic_loss/mul_grad/Shape_1"
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ }
+ shape {
+ dim {
+ size: -1
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/mul_grad/Mul"
+ op: "Mul"
+ input: "gradients/logistic_loss/sub_grad/tuple/control_dependency_1"
+ input: "Placeholder"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/mul_grad/Sum"
+ op: "Sum"
+ input: "gradients/logistic_loss/mul_grad/Mul"
+ input: "gradients/logistic_loss/mul_grad/BroadcastGradientArgs"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tidx"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ unknown_rank: true
+ }
+ }
+ }
+ }
+ attr {
+ key: "keep_dims"
+ value {
+ b: false
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/mul_grad/Reshape"
+ op: "Reshape"
+ input: "gradients/logistic_loss/mul_grad/Sum"
+ input: "gradients/logistic_loss/mul_grad/Shape"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tshape"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/mul_grad/Mul_1"
+ op: "Mul"
+ input: "logits"
+ input: "gradients/logistic_loss/sub_grad/tuple/control_dependency_1"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/mul_grad/Sum_1"
+ op: "Sum"
+ input: "gradients/logistic_loss/mul_grad/Mul_1"
+ input: "gradients/logistic_loss/mul_grad/BroadcastGradientArgs:1"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tidx"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ unknown_rank: true
+ }
+ }
+ }
+ }
+ attr {
+ key: "keep_dims"
+ value {
+ b: false
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/mul_grad/Reshape_1"
+ op: "Reshape"
+ input: "gradients/logistic_loss/mul_grad/Sum_1"
+ input: "gradients/logistic_loss/mul_grad/Shape_1"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tshape"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/mul_grad/tuple/group_deps"
+ op: "NoOp"
+ input: "^gradients/logistic_loss/mul_grad/Reshape"
+ input: "^gradients/logistic_loss/mul_grad/Reshape_1"
+ }
+ node {
+ name: "gradients/logistic_loss/mul_grad/tuple/control_dependency"
+ op: "Identity"
+ input: "gradients/logistic_loss/mul_grad/Reshape"
+ input: "^gradients/logistic_loss/mul_grad/tuple/group_deps"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@gradients/logistic_loss/mul_grad/Reshape"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/mul_grad/tuple/control_dependency_1"
+ op: "Identity"
+ input: "gradients/logistic_loss/mul_grad/Reshape_1"
+ input: "^gradients/logistic_loss/mul_grad/tuple/group_deps"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@gradients/logistic_loss/mul_grad/Reshape_1"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/Exp_grad/mul"
+ op: "Mul"
+ input: "gradients/logistic_loss/Log1p_grad/mul"
+ input: "logistic_loss/Exp"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/Select_1_grad/zeros_like"
+ op: "ZerosLike"
+ input: "logistic_loss/Neg"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/Select_1_grad/Select"
+ op: "Select"
+ input: "logistic_loss/GreaterEqual"
+ input: "gradients/logistic_loss/Exp_grad/mul"
+ input: "gradients/logistic_loss/Select_1_grad/zeros_like"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/Select_1_grad/Select_1"
+ op: "Select"
+ input: "logistic_loss/GreaterEqual"
+ input: "gradients/logistic_loss/Select_1_grad/zeros_like"
+ input: "gradients/logistic_loss/Exp_grad/mul"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/Select_1_grad/tuple/group_deps"
+ op: "NoOp"
+ input: "^gradients/logistic_loss/Select_1_grad/Select"
+ input: "^gradients/logistic_loss/Select_1_grad/Select_1"
+ }
+ node {
+ name: "gradients/logistic_loss/Select_1_grad/tuple/control_dependency"
+ op: "Identity"
+ input: "gradients/logistic_loss/Select_1_grad/Select"
+ input: "^gradients/logistic_loss/Select_1_grad/tuple/group_deps"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@gradients/logistic_loss/Select_1_grad/Select"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/Select_1_grad/tuple/control_dependency_1"
+ op: "Identity"
+ input: "gradients/logistic_loss/Select_1_grad/Select_1"
+ input: "^gradients/logistic_loss/Select_1_grad/tuple/group_deps"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@gradients/logistic_loss/Select_1_grad/Select_1"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/logistic_loss/Neg_grad/Neg"
+ op: "Neg"
+ input: "gradients/logistic_loss/Select_1_grad/tuple/control_dependency"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/AddN"
+ op: "AddN"
+ input: "gradients/logistic_loss/Select_grad/tuple/control_dependency"
+ input: "gradients/logistic_loss/mul_grad/tuple/control_dependency"
+ input: "gradients/logistic_loss/Select_1_grad/tuple/control_dependency_1"
+ input: "gradients/logistic_loss/Neg_grad/Neg"
+ attr {
+ key: "N"
+ value {
+ i: 4
+ }
+ }
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@gradients/logistic_loss/Select_grad/Select"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/add_grad/Shape"
+ op: "Shape"
+ input: "MatMul"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 2
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "out_type"
+ value {
+ type: DT_INT32
+ }
+ }
+ }
+ node {
+ name: "gradients/add_grad/Shape_1"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 1
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ dim {
+ size: 1
+ }
+ }
+ int_val: 14
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/add_grad/BroadcastGradientArgs"
+ op: "BroadcastGradientArgs"
+ input: "gradients/add_grad/Shape"
+ input: "gradients/add_grad/Shape_1"
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ }
+ shape {
+ dim {
+ size: -1
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/add_grad/Sum"
+ op: "Sum"
+ input: "gradients/AddN"
+ input: "gradients/add_grad/BroadcastGradientArgs"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tidx"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ unknown_rank: true
+ }
+ }
+ }
+ }
+ attr {
+ key: "keep_dims"
+ value {
+ b: false
+ }
+ }
+ }
+ node {
+ name: "gradients/add_grad/Reshape"
+ op: "Reshape"
+ input: "gradients/add_grad/Sum"
+ input: "gradients/add_grad/Shape"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tshape"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/add_grad/Sum_1"
+ op: "Sum"
+ input: "gradients/AddN"
+ input: "gradients/add_grad/BroadcastGradientArgs:1"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tidx"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ unknown_rank: true
+ }
+ }
+ }
+ }
+ attr {
+ key: "keep_dims"
+ value {
+ b: false
+ }
+ }
+ }
+ node {
+ name: "gradients/add_grad/Reshape_1"
+ op: "Reshape"
+ input: "gradients/add_grad/Sum_1"
+ input: "gradients/add_grad/Shape_1"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tshape"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/add_grad/tuple/group_deps"
+ op: "NoOp"
+ input: "^gradients/add_grad/Reshape"
+ input: "^gradients/add_grad/Reshape_1"
+ }
+ node {
+ name: "gradients/add_grad/tuple/control_dependency"
+ op: "Identity"
+ input: "gradients/add_grad/Reshape"
+ input: "^gradients/add_grad/tuple/group_deps"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@gradients/add_grad/Reshape"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/add_grad/tuple/control_dependency_1"
+ op: "Identity"
+ input: "gradients/add_grad/Reshape_1"
+ input: "^gradients/add_grad/tuple/group_deps"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@gradients/add_grad/Reshape_1"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/MatMul_grad/MatMul"
+ op: "MatMul"
+ input: "gradients/add_grad/tuple/control_dependency"
+ input: "Variable/read"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 1536
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "transpose_a"
+ value {
+ b: false
+ }
+ }
+ attr {
+ key: "transpose_b"
+ value {
+ b: true
+ }
+ }
+ }
+ node {
+ name: "gradients/MatMul_grad/MatMul_1"
+ op: "MatMul"
+ input: "input"
+ input: "gradients/add_grad/tuple/control_dependency"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 1536
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "transpose_a"
+ value {
+ b: true
+ }
+ }
+ attr {
+ key: "transpose_b"
+ value {
+ b: false
+ }
+ }
+ }
+ node {
+ name: "gradients/MatMul_grad/tuple/group_deps"
+ op: "NoOp"
+ input: "^gradients/MatMul_grad/MatMul"
+ input: "^gradients/MatMul_grad/MatMul_1"
+ }
+ node {
+ name: "gradients/MatMul_grad/tuple/control_dependency"
+ op: "Identity"
+ input: "gradients/MatMul_grad/MatMul"
+ input: "^gradients/MatMul_grad/tuple/group_deps"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@gradients/MatMul_grad/MatMul"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 1536
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/MatMul_grad/tuple/control_dependency_1"
+ op: "Identity"
+ input: "gradients/MatMul_grad/MatMul_1"
+ input: "^gradients/MatMul_grad/tuple/group_deps"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@gradients/MatMul_grad/MatMul_1"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 1536
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "gradients/AddN_1"
+ op: "AddN"
+ input: "gradients/L2Loss_grad/mul"
+ input: "gradients/MatMul_grad/tuple/control_dependency_1"
+ attr {
+ key: "N"
+ value {
+ i: 2
+ }
+ }
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@gradients/L2Loss_grad/mul"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 1536
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "GradientDescent/learning_rate"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ }
+ float_val: 0.5
+ }
+ }
+ }
+ }
+ node {
+ name: "GradientDescent/update_Variable/ApplyGradientDescent"
+ op: "ApplyGradientDescent"
+ input: "Variable"
+ input: "GradientDescent/learning_rate"
+ input: "gradients/AddN_1"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@Variable"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 1536
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "use_locking"
+ value {
+ b: false
+ }
+ }
+ }
+ node {
+ name: "GradientDescent/update_Variable_1/ApplyGradientDescent"
+ op: "ApplyGradientDescent"
+ input: "Variable_1"
+ input: "GradientDescent/learning_rate"
+ input: "gradients/add_grad/tuple/control_dependency_1"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@Variable_1"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "use_locking"
+ value {
+ b: false
+ }
+ }
+ }
+ node {
+ name: "GradientDescent"
+ op: "NoOp"
+ input: "^GradientDescent/update_Variable/ApplyGradientDescent"
+ input: "^GradientDescent/update_Variable_1/ApplyGradientDescent"
+ }
+ node {
+ name: "Sigmoid"
+ op: "Sigmoid"
+ input: "logits"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "init"
+ op: "NoOp"
+ input: "^Variable/Assign"
+ input: "^Variable_1/Assign"
+ }
+ node {
+ name: "save/filename/input"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_STRING
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_STRING
+ tensor_shape {
+ }
+ string_val: "model"
+ }
+ }
+ }
+ }
+ node {
+ name: "save/filename"
+ op: "PlaceholderWithDefault"
+ input: "save/filename/input"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_STRING
+ }
+ }
+ attr {
+ key: "shape"
+ value {
+ shape {
+ }
+ }
+ }
+ }
+ node {
+ name: "save/Const"
+ op: "PlaceholderWithDefault"
+ input: "save/filename"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_STRING
+ }
+ }
+ attr {
+ key: "shape"
+ value {
+ shape {
+ }
+ }
+ }
+ }
+ node {
+ name: "save/StringJoin/inputs_1"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_STRING
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_STRING
+ tensor_shape {
+ }
+ string_val: "_temp_171a1c01c56c46198d395a032e5b295b/part"
+ }
+ }
+ }
+ }
+ node {
+ name: "save/StringJoin"
+ op: "StringJoin"
+ input: "save/Const"
+ input: "save/StringJoin/inputs_1"
+ attr {
+ key: "N"
+ value {
+ i: 2
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ attr {
+ key: "separator"
+ value {
+ s: ""
+ }
+ }
+ }
+ node {
+ name: "save/num_shards"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ }
+ int_val: 1
+ }
+ }
+ }
+ }
+ node {
+ name: "save/ShardedFilename/shard"
+ op: "Const"
+ device: "/device:CPU:0"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ }
+ int_val: 0
+ }
+ }
+ }
+ }
+ node {
+ name: "save/ShardedFilename"
+ op: "ShardedFilename"
+ input: "save/StringJoin"
+ input: "save/ShardedFilename/shard"
+ input: "save/num_shards"
+ device: "/device:CPU:0"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "save/SaveV2/tensor_names"
+ op: "Const"
+ device: "/device:CPU:0"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 2
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_STRING
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_STRING
+ tensor_shape {
+ dim {
+ size: 2
+ }
+ }
+ string_val: "Variable"
+ string_val: "Variable_1"
+ }
+ }
+ }
+ }
+ node {
+ name: "save/SaveV2/shape_and_slices"
+ op: "Const"
+ device: "/device:CPU:0"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 2
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_STRING
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_STRING
+ tensor_shape {
+ dim {
+ size: 2
+ }
+ }
+ string_val: ""
+ string_val: ""
+ }
+ }
+ }
+ }
+ node {
+ name: "save/SaveV2"
+ op: "SaveV2"
+ input: "save/ShardedFilename"
+ input: "save/SaveV2/tensor_names"
+ input: "save/SaveV2/shape_and_slices"
+ input: "Variable"
+ input: "Variable_1"
+ device: "/device:CPU:0"
+ attr {
+ key: "dtypes"
+ value {
+ list {
+ type: DT_FLOAT
+ type: DT_FLOAT
+ }
+ }
+ }
+ }
+ node {
+ name: "save/control_dependency"
+ op: "Identity"
+ input: "save/ShardedFilename"
+ input: "^save/SaveV2"
+ device: "/device:CPU:0"
+ attr {
+ key: "T"
+ value {
+ type: DT_STRING
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@save/ShardedFilename"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "save/MergeV2Checkpoints/checkpoint_prefixes"
+ op: "Pack"
+ input: "save/ShardedFilename"
+ input: "^save/control_dependency"
+ device: "/device:CPU:0"
+ attr {
+ key: "N"
+ value {
+ i: 1
+ }
+ }
+ attr {
+ key: "T"
+ value {
+ type: DT_STRING
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 1
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "axis"
+ value {
+ i: 0
+ }
+ }
+ }
+ node {
+ name: "save/MergeV2Checkpoints"
+ op: "MergeV2Checkpoints"
+ input: "save/MergeV2Checkpoints/checkpoint_prefixes"
+ input: "save/Const"
+ device: "/device:CPU:0"
+ attr {
+ key: "delete_old_dirs"
+ value {
+ b: true
+ }
+ }
+ }
+ node {
+ name: "save/Identity"
+ op: "Identity"
+ input: "save/Const"
+ input: "^save/MergeV2Checkpoints"
+ input: "^save/control_dependency"
+ device: "/device:CPU:0"
+ attr {
+ key: "T"
+ value {
+ type: DT_STRING
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "save/RestoreV2/tensor_names"
+ op: "Const"
+ device: "/device:CPU:0"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 2
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_STRING
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_STRING
+ tensor_shape {
+ dim {
+ size: 2
+ }
+ }
+ string_val: "Variable"
+ string_val: "Variable_1"
+ }
+ }
+ }
+ }
+ node {
+ name: "save/RestoreV2/shape_and_slices"
+ op: "Const"
+ device: "/device:CPU:0"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 2
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_STRING
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_STRING
+ tensor_shape {
+ dim {
+ size: 2
+ }
+ }
+ string_val: ""
+ string_val: ""
+ }
+ }
+ }
+ }
+ node {
+ name: "save/RestoreV2"
+ op: "RestoreV2"
+ input: "save/Const"
+ input: "save/RestoreV2/tensor_names"
+ input: "save/RestoreV2/shape_and_slices"
+ device: "/device:CPU:0"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ unknown_rank: true
+ }
+ shape {
+ unknown_rank: true
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtypes"
+ value {
+ list {
+ type: DT_FLOAT
+ type: DT_FLOAT
+ }
+ }
+ }
+ }
+ node {
+ name: "save/Assign"
+ op: "Assign"
+ input: "Variable"
+ input: "save/RestoreV2"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@Variable"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 1536
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "use_locking"
+ value {
+ b: true
+ }
+ }
+ attr {
+ key: "validate_shape"
+ value {
+ b: true
+ }
+ }
+ }
+ node {
+ name: "save/Assign_1"
+ op: "Assign"
+ input: "Variable_1"
+ input: "save/RestoreV2:1"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "_class"
+ value {
+ list {
+ s: "loc:@Variable_1"
+ }
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "use_locking"
+ value {
+ b: true
+ }
+ }
+ attr {
+ key: "validate_shape"
+ value {
+ b: true
+ }
+ }
+ }
+ node {
+ name: "save/restore_shard"
+ op: "NoOp"
+ input: "^save/Assign"
+ input: "^save/Assign_1"
+ }
+ node {
+ name: "save/restore_all"
+ op: "NoOp"
+ input: "^save/restore_shard"
+ }
+ versions {
+ producer: 27
+ }
+ }
+ saver_def {
+ filename_tensor_name: "save/Const:0"
+ save_tensor_name: "save/Identity:0"
+ restore_op_name: "save/restore_all"
+ max_to_keep: 5
+ sharded: true
+ keep_checkpoint_every_n_hours: 10000.0
+ version: V2
+ }
+ collection_def {
+ key: "train_op"
+ value {
+ node_list {
+ value: "GradientDescent"
+ }
+ }
+ }
+ collection_def {
+ key: "trainable_variables"
+ value {
+ bytes_list {
+ value: "\n\nVariable:0\022\017Variable/Assign\032\017Variable/read:02\022truncated_normal:08\001"
+ value: "\n\014Variable_1:0\022\021Variable_1/Assign\032\021Variable_1/read:02\007zeros:08\001"
+ }
+ }
+ }
+ collection_def {
+ key: "variables"
+ value {
+ bytes_list {
+ value: "\n\nVariable:0\022\017Variable/Assign\032\017Variable/read:02\022truncated_normal:08\001"
+ value: "\n\014Variable_1:0\022\021Variable_1/Assign\032\021Variable_1/read:02\007zeros:08\001"
+ }
+ }
+ }
+ signature_def {
+ key: "serving_default"
+ value {
+ inputs {
+ key: "input"
+ value {
+ name: "input:0"
+ dtype: DT_FLOAT
+ tensor_shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 1536
+ }
+ }
+ }
+ }
+ outputs {
+ key: "output"
+ value {
+ name: "Sigmoid:0"
+ dtype: DT_FLOAT
+ tensor_shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ method_name: "tensorflow/serving/predict"
+ }
+ }
+}
diff --git a/model-integration/src/test/models/tensorflow/regression/test1/variables/variables.data-00000-of-00001 b/model-integration/src/test/models/tensorflow/regression/test1/variables/variables.data-00000-of-00001
new file mode 100644
index 00000000000..083abe4a917
--- /dev/null
+++ b/model-integration/src/test/models/tensorflow/regression/test1/variables/variables.data-00000-of-00001
Binary files differ
diff --git a/model-integration/src/test/models/tensorflow/regression/test1/variables/variables.index b/model-integration/src/test/models/tensorflow/regression/test1/variables/variables.index
new file mode 100644
index 00000000000..43ceede272c
--- /dev/null
+++ b/model-integration/src/test/models/tensorflow/regression/test1/variables/variables.index
Binary files differ
diff --git a/model-integration/src/test/models/tensorflow/regression/test2/saved_model.pbtxt b/model-integration/src/test/models/tensorflow/regression/test2/saved_model.pbtxt
new file mode 100644
index 00000000000..112f9a28cb7
--- /dev/null
+++ b/model-integration/src/test/models/tensorflow/regression/test2/saved_model.pbtxt
@@ -0,0 +1,389 @@
+saved_model_schema_version: 1
+meta_graphs {
+ meta_info_def {
+ stripped_op_list {
+ op {
+ name: "Add"
+ input_arg {
+ name: "x"
+ type_attr: "T"
+ }
+ input_arg {
+ name: "y"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "z"
+ type_attr: "T"
+ }
+ attr {
+ name: "T"
+ type: "type"
+ allowed_values {
+ list {
+ type: DT_BFLOAT16
+ type: DT_HALF
+ type: DT_FLOAT
+ type: DT_DOUBLE
+ type: DT_UINT8
+ type: DT_INT8
+ type: DT_INT16
+ type: DT_INT32
+ type: DT_INT64
+ type: DT_COMPLEX64
+ type: DT_COMPLEX128
+ type: DT_STRING
+ }
+ }
+ }
+ }
+ op {
+ name: "Const"
+ output_arg {
+ name: "output"
+ type_attr: "dtype"
+ }
+ attr {
+ name: "value"
+ type: "tensor"
+ }
+ attr {
+ name: "dtype"
+ type: "type"
+ }
+ }
+ op {
+ name: "Identity"
+ input_arg {
+ name: "input"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "output"
+ type_attr: "T"
+ }
+ attr {
+ name: "T"
+ type: "type"
+ }
+ }
+ op {
+ name: "MatMul"
+ input_arg {
+ name: "a"
+ type_attr: "T"
+ }
+ input_arg {
+ name: "b"
+ type_attr: "T"
+ }
+ output_arg {
+ name: "product"
+ type_attr: "T"
+ }
+ attr {
+ name: "transpose_a"
+ type: "bool"
+ default_value {
+ b: false
+ }
+ }
+ attr {
+ name: "transpose_b"
+ type: "bool"
+ default_value {
+ b: false
+ }
+ }
+ attr {
+ name: "T"
+ type: "type"
+ allowed_values {
+ list {
+ type: DT_BFLOAT16
+ type: DT_HALF
+ type: DT_FLOAT
+ type: DT_DOUBLE
+ type: DT_INT32
+ type: DT_INT64
+ type: DT_COMPLEX64
+ type: DT_COMPLEX128
+ }
+ }
+ }
+ }
+ op {
+ name: "Placeholder"
+ output_arg {
+ name: "output"
+ type_attr: "dtype"
+ }
+ attr {
+ name: "dtype"
+ type: "type"
+ }
+ attr {
+ name: "shape"
+ type: "shape"
+ default_value {
+ shape {
+ unknown_rank: true
+ }
+ }
+ }
+ }
+ }
+ tags: "serve"
+ tensorflow_version: "1.13.1"
+ tensorflow_git_version: "b\'unknown\'"
+ }
+ graph_def {
+ node {
+ name: "input"
+ op: "Placeholder"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 1536
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_DOUBLE
+ }
+ }
+ attr {
+ key: "shape"
+ value {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 1536
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "Const"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 1536
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_DOUBLE
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_DOUBLE
+ tensor_shape {
+ dim {
+ size: 1536
+ }
+ dim {
+ size: 14
+ }
+ }
+ tensor_content: "\306C\326o\340\326\223?\020\277\341\231\265\306\270?\316P\000\311KI\207\277\220\227\253 \371\377\301\277v\017\032|E<\276\277\341Z\0000\306X\235\277\022\234<\027\361~p?D\223\252\246^~\305?;\245\032\230\276\206\301\277\332\273f\313o\255\275\277\225\371\337T\205q\257?\344l\364\354\365iM?\333p\307\334E\010\304\277Aj6X\256?\275\277\236\217V\305\021|\300??\022\307j\253\030\301?n)\022\372Kf\246?\023\010\201\026\212\243\204\277\244\017\025\200I*\243\277\332k\317\331\036\003\267?\300\334pk\254A\301\277\235y\006\310jR\300?\001 \331\267\276\315\220?\350Y\221w\262K\200\277\024\256_\t\361\311\301?\326H\352h\226\203\260?\0237\325z\351\353\266\277.W\003\312\016g\220\277\327_\265\225\226\353\243\277N\006\376V\262\260\304?^Z\265\276\206\211\253\277\234/\315\345qM\300\277!\r\332-\263\364\221?0x\361O\221\020\261\277\343S\030|\376&\211?\272v\000{\002S\317\277\243\026:\022_\247\273?\3632&\364\360\210\224?\r\027\212\022x\224\246?((UB\235\331\223\277y\374\253\333\277\226\221?\003\004\363(\2270K?\303\370\363\270\302u\263\277&L^.\260\201\261\277\275,\250\263\003\261\251\277i\271I\261LX\261?*\347\232\245\205F\307?,\273}\203\033\251\214\277\2743\371l\037\324\272?\240\022\305\372l\252\247?\366 \300}\247a\312?\2277\227\332\340\004\247?E\327\353\221\215`\274\277\036\235\215K\247C\261\277\361\233\302H\\W\226?\375\270\022\021n\211\316\277\300\273\314!\021;\300\277a\231\255\356\312\375\310?e\306\323\212\213\375\207?\243@\212S\307\226\262\277\201\007r\200x\266\243\2776\355\264\200\224\332\226\277HVA\002\213\372\312?\203EZ3\367\337\266?\036\322\374\370\320\324\306\277D\231?\257b^\177?c\363\365z\363ZX\277\373\242TTS#\303\277\037/r\217\224\242\271\277\301\017\356\343\022\363\263\277\315%\261\213\317\301|\277\005\357\013\260M\010\320?\257^A\360\257V\262?\350f\367!\366?\275?\004\237\311\022\023F\303\277r\305^\300\243\361\300?\217\017*Q\276\201\267?\025x\212\225\343\265\304\277ey\271\324\353\033|?\246wO\244$)\240?\204\335\343\264L\242\257\277\315>y\350\346\314\305?\277\256\335%57\254?\235\024\335+FA\237\277*\244\0329\030\200l\2779\251,w\035\006\302\277\274\010\271\303\233:\236?\'9 3\346-\177?\275\320\177\351xr\302?\037<mds\021\272?ua\353~\371\275\276\277\346@\326\353T\335\227?\235*\031\227\244\006\216\277\353\177k\221\374\361}?\n\233\372Y\255L\301?e\276\364/\354\336\260\277a+U\223\216\202\245?\214\3049\306\377\361\204\277.w\252Q\223\036]\277)G\363%/\212\241?\375\252\342\027E\361\266\277]\200\252\034\302\346p?\3516\266\030\177w\312?|\014\363%\3342\226\277`6So\034\020\245?\212\'\305\377Q\344\272\277K\242]\362\014\335\253?\256\215\274\016\200\307\220?H4\370:\276\332\270?PC@dEW\206\277\237\202n\202m.\240\277\344\204w\3204]\300\277\256\353ZQ\325P\271?\311\210\262p\3524\264\2775#\025\031\262\256\240\277ku\311<\006\315\240\277\311\346\267\260\007/\252\277\t\215B\001\276.\301?\255\020\311\276\026Ay\277\372\t7b2\355\217\277\232\344\303\240F\245\307\277Gs\366_\263\220\253?\333\nA\" lv\277Q\264|\022f\232\245?\037Q\306z\232\324\271\2777\225\343p\230]\244?\371t\226F\035Zb?\267e6\331\306\032\303?\340\205\254\324\313\215\256\277\014U\r\366A\212\317\277\223\'.l\372K\270\277\226\215\020\236\233\017A?n|,W\234B\241?\330\253\022VW&j?\233\200k\303[\323\245\277L{3}J\033\237\277\353\333\214_\010R\225?\025Q\273\364\261S\211?\205b\006\200b\020\260\277tl\350]\342\007\300?Z\244f*\341\025\203?\334+\220#\226\377\271?\305\036\332,\362J\234\277\3632\232dZ[\227\277\0202E\313\230\322\274\277\205\036\235E\306\350\217?!B\354\364\325\301\303?L1\004\252\275\215\237\277\030?5\341\254\373\301\277\'\031\256\240\026\223~\277\217\002\2600\216\232\224?{\336\270\215_\371\272\277?\267\330V\1775\306\277f\200\021F\321\206\225?\303\341\377e/h\232\277\003\\>G\377\\\252?!\307\205\342,\222\255\277!m6\000~\266\223?<(\207\362\004\352\311\277\245\307\010>\257\343\264?\022\361\262\362\261Xs?<\200\362\"\013\223\300?z\2050\350C\260\304?0y\265\r\273\260\225?00\221ICa\271\277Q\3722RL.\252\277\371\036y\034\374\346\272\277\344\320\273\002\374a\225?[\022!>r\250\240\277\213\232&*x\254\237\277\332:\023\321\250W\270?\262S\367\252y\321\266?wQ/\256#\222\247\277\337+\257\226\355R\266?\217\216YI\317\203\241\277G%\322f>c\264\277M]\377\330O\026\300\2771\332\222\345-\304\265?\370\343\333a\346\225\304?o\023\303/O\330\276?\016c\265\225W\211\312?\303\250\230\361p\337|\277\306\315\360\310\372\365\267?\357\252\315\"\016\'\306?\272#[\364\016\251\271\277\013\241\007\023\317\372\273?8! \022(\362\304?n\210\2722\375L\230\277\346#\" \350\004\264\277\203\334Q\343\2455\311\277[\317\t\316\222)\314?p}\206\205\250\242\253\277\362\230\320\224d\347\305\277\205\325\211:zq\274?\322\220\367y\223\356\262\277g\201\021\233\356H\203\277\210{n\310:\034\241\277\356:\024}v\026\262\277\273\221|(\241\232\223\277*z\236m\276\377\203?\033\375a\312\334o\260?FXb\030j\021\306\277\027\260\367>\016\002\320?\246\327\215\255\301\256\244?\002\3201\264}\324\315\277\240\241\326\362\177-\300\277\2610\224#\305i\240\277\350ou\362\304\246\265\277\257l7\'u\326\300\277\234\344\232\233!\230\247\277\001/qDg\317\240?\212D\252\024\375&\272?\220\320_.|\333\262\277\346(pe\'\251\300\277E\300\255X\347s\243\277\377\034\345E\247\237\261?[\204\030\211\216\301\303?\n08\357@\222\250\277\217\267d\213\303x\311\277\225\017\320\352\212\r\227?N1M\233I\244\314?I\242\340N\";\262\277\356\327\034\317o\332\273?\026\016n\342X%\267\277h\211\002\262\336\236\223\277\245!n\033x\222\314?\013\006\353\370\274\025\255\277\000\215\237\300W`\226?V\204\252\256\034]}\277\240\353\232~\314\327&?e\010\205\222\rB\301\277paq\376c\003\305?5aA\254\225\031t\277\242\230\327z\341\017\220\277@e\367\332\302\314\265\277\362\376\313\351\327q\241\277FU\376*\361\"\300\277@\271,\363~\331\244\277\242l\357\202bF\307?x\r\377\301F\377\254?@;;\250&K\263?\235|\243\022J\035q?\'\240\005\020\272!\253\2776\334@\032h\357\245?N_\02423\343\302\277\222u\370ug\240\270?\007\222\224+\216\246\223?\332\253Ns\274b\212\277\261\211\320\310\003\343\202\277]\251\240\016\261\323\276?\236\215\212\n\270\271\300?Q\304\223\246\036\363\300?Wo\331;\227\200\257?\247\375\257\203\002X\255\277\330\310!\006\376B\222?hb\262\231\377\356\272?W\204rt\0378\320\277\201\256>V\336\231\213\277mHi\241V\236\246\277\250>@u\314\266\260\277s\317\251H;\257\270?\335\256i\004y\374\266?\001\262>\373\326\311t\277\301p\002\2409O\313\277\315\300W\253\265\310\264?K\347\234\\\321\006\263?\350\304l(c\021\264\277\031\334R\333\010l\307?\244\364\030\330N\217\225\277\262\312s\267\316\023\271\277\024G%\257\224\306\213?\255\232\215^\374\371\244\277bu!\301\354\361\321\277t\243\2336D\345\260?\303\212\026c\312\330\266??\216\2433)]\237?k\316\014n`J\306?rOS\241\373@\262\277\217\264{n\323\345\323?\354Q\337vA\263\260?\253\374\264\325A\000\254\277\003\002\256\266\304\\\304\277}\232&\'\302\300\270\277\256t\332z\344\362\277?:\210vy\025\223\246?\327\3678>\370!\302\277\202\245\210\036\372\304\271\277\\i\346~\355\327\207\277\"v9\361Nr\263?\205\323c\026\'6\265?E\nG5\034`\257?aX\266JLe\307?\324\217.\223\323f\272\277\234\366\301^\007\001\224?64d\205\3402\220?^\376\022=\233\224\315\277.<}\034e\321\246\277m\323\354\000\335\002\272\277t7\016\265m\346\240?\nFB\307\2644\257\277\013\2667W\023\210i\2770a\310\231A\205\224?\336V\364\372\244x\244\277\266\347\023!I\230\243?#B\3774<\344\250\277\261\347\300Y\371\033\251?\3005/T\370\250\255\277fe\031&lQ\267?\201\033(?\330\335\242?\206\220\372\264\324\257\274?\036X\017Qw\245~?H\301\177\014\320/\305\277\000Y\361\233P\255\263?_\240\022\341Q\000\217\277\262|\206\222\234\025\267?K\256I\276\230\n\310\277S\3749\351\252\220\227?\033 \377\224\227\247\211\277 \214\036\344u\201\307\277H\027\345x\262\300\244\277\220\026\224#n\364\303?\\Q\323\233\375\224\245?\037]!@\376\273w?\024D\2725s(\255?\223\364\307\305\025_\253\277a\352\275`\266z\227?\344,\304\371\357E\305?\373\266cR/N\272\277m\255\213q8sK?r\216\350H\307\242\311?\215S`\'\000\205\227\277\027\210\326\016\236\262\253\277sM<\260rtt?%\220Mt}Q\264?)\252\340\034\2716\247?p\272\"%\334\367\250\2775S\003o\313\240\250?w\324B\360\2212\301\277\374\215\221\332H\222s\277#\264W\r\317\305u\277\322\255\257\314\213\352\265\277Og\201ev\330\227\277\262\306\230\226u~\272?\337\273\177@\034\314\257?F\215U\353\357\037\272\277\323\307V\360T<\266?\0350\304\255o\002\321\277\334\352\301\334P\317\265?\353\333\t\271I\325\264\277\026\016\370\242\243\337n?\350%\033\302\327\310\231?\177\272,<W6\252\277\375}\267\234\246\373\202?b2\265u\375I\235?\356\335\251\010\275\332\322\277$1\332\311gn\301?\257m\327p\220{\264\277\020\251;&ZK\300?\247\177,\340S\036\271?>\224\233\247\222J\244?F\345 \260\243\252\276?\306\010\3153\211\024\261\277\2671\254S\376$\321?\006\244\230o\250\300\300\277D9\305\335l|\276?H{6\n\361\227\261\277\021K\001\225u\323\223\277\247\000S\237I\313\264\277\225m\277\016\264M\225\277\366\262Z\177\244N-\277\356\275 \342\310\304\222\277m\221<\312P\263\264\277S\250\005K\3408\311\277M\350#\014\016c\211\277\t\317}_M9y\277\352\261|a}\022\227\277\243T0\021\026\322\262\277&z\213>\205\365\267?T\273\032\344~9\300?vM\276\207\276Q\255\277\241\250\347\371H\343\244\277\2420\230\233nf\266?+\365(\214,g\311\277\344\303#\304\202\010\263?/TK\311\227]\226?\215u\337\311\330\025\311\277Zp\374\344E^\266?\3075\230\260\302>\245?\342\024s\001U\224\271?W\303\225p3O\251?\0311\033\312+\357\270\277\202\010\337\333\0106\340>\317\321r\312\354\310\273\277\207\336\226=\337O\276\277r\234\266t\253#\247\277\240q\007\342\275K\310\277+\311\247RR\366\265?\t\335\236\216+\034\302?6r\217_\003\237\300?\207\216$#\1775\245\277,&k\202\241\265\316?\244\274$\241\367^\246\277\204\327\305fHj\306?\304\256\223\371t\226\303?\023d\301\351\375^\264\277\256i#\020S\243\237?\205\035\2226\361B\224?6]\237\330\241\220\267\277\265\311O\334d:\251\277\306\366\\\037/\332\266?z\t\036\315b\371\234\277F\317A?\254\322\264\277\254g\310q?\212\276?\332~\025\000\\\345\230\277\236\347\221`|\322\277\277a\320t\211\030\311\233\2777|X\336\356\306\243\277h*\200r\321$\242?\3343{\277\277\205\311?\313\016\345\0361q\300?O\226\245\310\245\343\271?[\213\332p\3248X\277\027\006\361\307Z\225\265?\004\007TA\373!\223\277\3263\\\241G\020\236\277\3578\372\310\303\010\246\277\350\016\345`\022\035\222\277\321S7\3055Z\257\277\373F\326sjg\242?\177\314\220\212\317G\247?\204\t\256\037wo\225\277osg\363wC\267\277\376\250{\246@\330\222?\324\014}\374\r\225\242\277%\331!n3\334\246?\240p\316\005\306U|\277s\027J(\307\370\256?=\274\300=\014\024\222?; l\324\306\264\264\277q\200\035*\216]\243?\027\016G\301\375\\\224?\226\033\270QWm\302?Dt\331\302u1\303?\255\311\025\215\244\300\216?o\211\264\350\272\345S?X`\231:\224YS\277$\226\336}\027 \225\277l\203\265\375\367\306\314\277:2\307\365\014\'\246\277DU\353\224\371[y?\337\3533\232\223\274\251?:\272d\256\364\326\260\277B\326\211S\255\022\207\277=\313\3234<\247\276?+\236\310\263S\'\246?T\030|\305\206\023\304?3T\223H\001\300\325\277\322\216\350\312L\356\233?\362r\247\201\231\251\243\277b\361\177\235\262\244\260?77\326\266\222\027\322?\275\322\356\3358\312\234\277\\J\017\2700\271\264?\036\355\t\0143\010\247\277\264\340\331\300u\230\307?\315\2665)\036x\275\277&\377\275\243\304{\274?4\314B\342\026\010U?5\232j\006\240\230\245\277\270d\367\007\272\020\274?\227\221xzR\316\275\277\027)\361\3176\274\240?X\205\336\261F?\217?\273I\226\227\355\r\301\277y\361tPpz\200?\267f\326\242>\244\277\277\211\214Yinu\270\277\227\324\241\210\362\350\301?\317H\356b\371\347\250\277\327\376\232\325W\345\177?\233U\240\305\025\306\262?;\327\222\353\272 \202\277\200\234\020\226v\271\300?\245,\010\227\267\017\313\277[\320\010m\r\341\204?\214\216\256@\322\026\224\277\255\326t2\001\225\216\277K\223^\234\357j\221?:\354\267\3534\235\243??\001c\245B\331\252\277W\325\350n\034H\274\277\035\224`\003\260+\312\277\372\356S\265\246\377\207?\305\230\231\256\333\203\272\277\316k\035\202\261\037\303?\245\023\241\243\025\315\247?\276\216H{\272\223\301\277gb\355\227\205\034\314?\026N\t\274\303/\300\277\302\275]\353W\301\223?\026\254h\026C4\273\277\261\305\342D\372v\260\277~-\347\301\274h\320?\nj\255\261\027\027\213?\021Vn\025y\201\266\277\326\210e\325\266\373\271\277\254\001d\2455\326\243\277}ut\221G\262\303\277Z^\334Z(}\274?\262\345\177\231\276\357\260\277\0214\324\252\314Y\271?\360\007\337>LL\320\277E\210\016\037u\177\314?+\200\250\342\343i\261\277\261\201\263\3617b\301?\351<\212E\234E\304?\027\325\3337\n\325\312?\014\2363f$\237\250\277\247\327ziu\341\264\277\231N\010$|\263\232\277\355\315\271\200uq\233\277\201R=\004s\200\250\277\001V\334\346\240I\246?\306\021\314\376\034T{\277\243\034\320\"}b\243?%\21470\374+\262\277\277\316\273\005H\217\241?\026_\221\314\212\362\221\277pp\337i/e\304?a\007\374\245g0\241?c\235*]\342h\304\277\"\256\002\216\314\275\301\277L\360u\247\257/\276\277\340 \321Q\262\254\323?\237WwE\034r\221\277\300\274\0138\372\014\262?w\006\222\315\030.\324\277FMI\347\213\263\251?\272l\3424\217\335\304?\37179Rb\200\304\277\010,\313\367\337<\251?;rW\2172\202\300?\000T\245\376ca\230?(r\003\2559P\302\277#|\253F\3666\254?\213j\377n\007\364\264?\304I$\354\211\256\233?\352\232\333\302\271\341\256\277\272b\301\257U\201\313\277\220L\r\315H\250\306\277\007M\313KR\206\267?\352r\256)\017\017\227?LT\300\022\376S\230?):%\332\245\210\315\277\260\202\363\235\236\241\267\277\274\035`\342\304\221\223?G\330\270W;\261\276?\2154\310\374\206T\272?\017\020\220IE\334\302\277g\301\202\260\246\344\271?X+\"\253\\\002\312\277\323{\353\357o+\267?&\257\363\306\032\225z?\335\375\336\352Jj\253?\005}?\023\331\200\262\277\207\213>x\231l\263?\013\235\016mu\030\302\277\367\237u\255\240\177\242?\265\357P\020f\216\315?\365\010\333x6U\263?v\020p\321\201\242\240?)\343@\256\273\240\317\277|_E\315\225R\313?,RE\214\013\272\243\277T\337\244\314\327n`\277&\017W\211\022\377\271\277\354\361\241\035]\341\251?\327\017%\340\n,\241?y\014\246\234?\246\301?(\273\240!\317\002\236?\300k3\330\034\330\246\277rD7\002(N\322?X\030\241\251\357\313\302?\036\363g\034\007\345\264?s@\310tz\334\251?\314\347\374\300Po\306\277\346\250hC7\023\316\277\315\017\222\202;\373\254\277\244\355\237\006\351\334\244?\354\017\007^}}\270?\367\255\330\036\200P\266\277\007\020\001_I\352\243\277|\311_\340/\215\266?\216@g\024\272\311\241?\362W07\274\003\254?\035`\"*\261T\277\277-S\271\241\250e\331\277\224%q\003\344@\240\277\027j\250\2601\341\254\277-\266\232\253\220\312\313?\222At\0179\253\321?\207\247\353\354\260\356\231?7$8\312z\246\220\277\206\010\241\343\260\006\311\277\003\225|\245\014\332\253\277\347\371\364a\330ER\2776\267\206*O`\200?\250\031.4a\243\216?\026\340\365\026\343\233\216?\026\014J\026\344}\203\277\024\200C\n%\331\206?r\324;<\377\321\201?\277\022Im\317}\312?v\031<g.\230i\277\202\353\314Q\032\007\271\277\373\007L\366;\037\267?S\336\305\322\311\265\205?\"\363\314\n\005H\305?\26223\to\023\177\277\222\"T0C\243\305\277\205\341Z\3541G\265?\305\037N{+\331\253\277E\250\326\267\361\030\274?F$\360bn;\265\277\207Z\273\370Q\267\252\277\362c\255\224\347\\\242\277\275\366\373\226\007K\266\277#\346\335\354\355Q\320?\206*\304\226 \014\311\277\032\0200\205\034\372\307\277I\200\016\242\032\306\243?C;\322\244\351W\305?\231h\252\234\312\013\235?\'\246\311\t\227\251\241\277>p\353\270v\346\274?\370_\005\202\223 \250\277\244W$6\310\033\240\277t\034\343>x\034\257?\332piG\325f\270?\251Q\334\'\214\300\234?\240@U\204\335\372\276\277wR\206g\013m\260\277Mt\311\230\0357\225\277\354\022\312;\263c\247\277\346\204%\220\220v\234?*tP\333\331\267\265\277\315\177w\277\315}\266?\367\007\323WP\373\263\277\000\034\n*Z\366\324\277D\272\326\210\326\332{?\235\335Y\010L\215\266?\337\325\024tA\\\302\277\201\320\353C=\252\300?\236\203\" \354\032\265?g}\346\021g\220\210\277x7\234\"\2470\243\277|}\025\201s\007\273\277\212\310\007\263N7\223\277C\220\350\n]B\213\277c\003\351\211m\321\241\277j4r\257\331\227\245?\231\363\206\323\262\025\220?\035G(\333\006\370\273?\234\016V\242\347&\243\277ji\022h\000\266\254\277\367\211\2161\235L\266?}\n\220\376\371\263\272?\253\031\215H\006_\222\2770`\244\367\033\323\253?j5\266@\377\254\270\277r\201)\240\313\213\241\277n\2507\366jB\263? \204C\3534\007\264?\036\221\210\322\202\260\300\277\r:\200\251\003\303\211?\"\245\254\034\317\252\272\277\207tT\000\234P\211?\205\007\230,\031\346\242\277>\2727\303\313\230\243?\016\200\361\343+=\330?\203\234;\307\271Z\311\277\376\r\262D\002U\223?\317\237s\236\372\357\303\277\347\250\200w\307h\260\277\010\025\030\306f\225\303?V\345r\2650;\240\277i\331\363a/\331\202?\363\360\265\312\010\274\300\277\331\332\226\265\353\010\224?\265\332q\034\333n\265?\352~\214\261\206\020\261\277\nC\213D;^\264?\240\272\270\304\037\\\307?\276\344]\270\035\245\320\277\216\204\271\240P\371\325\277\331\221&S2\310\244\277\362g\035,\230\024\270?\314\021i|\017\356\322?\242\346iCcT\231\277WI}B\223W\267?\264e!\272.8\204\277h0\204\337\362c\267\277\250\007sFuw\246\277?f\323>sQ\254?\334mw\014\365\324\210\277\304\230\254A\206\013\236\277!\276\331O\010S\265?\224sihm\340\251\277\250YE\3144>\272\277h\355/\362\317T\251\277L\327\342[\325\027\223\277^\325\ng\331@\270\277iJ\232\004\037H\216\277\324\370$T\2426\265?T!-\266\226*\271\277\251\372I\267,\235\321??!\375PG:\310\277\027\340\t\277\275\022\237\277\316s\'V\353\n\263\277\306Hk1,\227i?\316M\270\342\t\220\263\277\352\020\314\227\305s\220\277\356\257\017>\341M\324\277ig\335}\0047\222\277\255Y\214*m\217\245?\376+\2279\353\202\316\277\305\022\'N\311\240\305\277%ItUF\004\324\277\276X\003l\313\353\266\2771<bK5\"\313\277\017g\203D<t\245\277\\\341\000\337\366\177\272\277quv\036\326\355\251?i\354\352\223\2016\270\277(-\200\004\351\211\277?\251\223\240\346#w\235?\210\222\005-\311$\261\277\360w\343Ak\341\303\277\270\265u\276\367\326\301\277\020x\014\212[;\261\277N\243\312\224;(\256\277\026\236[n$\211\205\277\3716c\214x\013\302\277\324\300\326\033Y\271\315?Xw\254\025\337\006\232\277O\306\357L\264%\260?d\204\022J\006.\326?\371\317l\013o\334\300?\300$\200\30106\251\277\373\315\177\312\226\245\246?TU8S?K\262\277\346X\336\340\370\232\223\277O\276\211\\\2768\301\277\377\010\320\344q\377\276\277|{jh_\307\255\277\021\276?\360EY\207?\260\3722\026o\254\260?F\215#\252\252v\262?\331/\341#\034\217\207?\362\356\250\032\223\320\266\277\237Wf \353\010\312?3\242F\022%\334\302?\213\36621\252G\316\277N\224\366\253\235m\251?\314\215[\323\351lv\277\036\350*\352\253Vw\277^\336\247\317\273$\303\277\304Fm<\343\353\322? -]O\200A\262?M\037\365/\344\235x\277P\267\323\205\3572\275\277\036\227\364\351ds\277?\237E\225\007\224\202\245?\212\246\334\201\346d\256\277\006\320\303\224\333\253\312\277\367\317\354-\331\321\273\277z\372\022\352\212\320[\277Miw\304Ww\271?\201\364\355\313\253\241\277\277\204\022\321f.ue?\354Q\021\221\007\323\277?\267\207\202UX\320\320?\302\303\365\034\240\032\321\277mz\3625\344\023\262?bX\316\265\203e\267?\302\023\r\336\304\274\247?j\240\314\200\360\217\264\277\215\307jN\345f\202?\355(\255\311\272\377\324?\355\005\356\372\337\272\305\277?j;\025z\021\276\277\264B\377\0055\205\310\277MZ\177)\337h\265\277\373\223\377\363\340\357\246?\014\265m\006 \256\254?8&1\375N\252\302?\003\265\244A,W\244?<|/\273\256\027\310\2770\235O\034\252\260\244\277\365\322\237\027c\346\301?R\335d[\333\315\235?\302\204\t\343V\300\273\277\035\354tv\325\204\314\277\272!T\223q\316\263?\017\020\010\341\215\t\272\277\017\213\206Q\016c\274?&t\304\335A!\272?\217P\352\335i%p?\334\351\201y\023J\232\277@\344\374\177J/\242\277/,Q\017\361\"\222\277\036T}\316\203\031\251?\245\337\"U\223\010\225\277\243d\235I\276\033\306\277\237\336\231\240\267l\246\277\024\300\244\232\024p\262\277\314:y\2678\333\254?\2677\370\261\256T\271\277q\034\353\235\373\003\264?9Sd\303\325w\272\277\360+\237uw\006\312?^\005\r\277\264/x\277\235l\242\211\311h\203\277ld[M\232a\201?\227\342/TF\307\243\277o\216\332\300\237\374\246\277\320\014I\206\225(\217?\024\277\344G\314\235\301?MI\2503\234\334\215\277\251x\337\335\330\370\270\277\241\200r\376K\241\312?\335>\206\271<\273\311\277c\005w\321\232Ar\277.n\255lY/\264\277\031d\203_\370\375\302\277\016Y\332=I\215\250?\344\202z\225&\004\306\277\t\203\001 \236\217\261?\220\207\250\224\t\267\274?\270.\017\352\240g\277?\320\350<\313\250x\243\277\320ZNae\241m?\023\037\032\274\374\363\257\277\r\313\t\027\211h\204?\014\3027\254\036Y\307\277\006\227\377HL\274\230\277\027\235@\241\216\277\261\277\250\032yI\017%\245\277M\232)\355\331z\301\277\353_\240\216\031p\264\277P\350r\231\337\227\223\277\362Jn\253\030\223\241?\311\352\002a\004\020\273?JT\363\026Jm\261?\036\016\300\363;\'\312\277\365\331\302\357V\216\321\277W\372\365\320\324<\231?%\242\276\366\202\266\270?H\370\205\310b\034\207\277\333\236\244\230d\030\233?__Oj\342\325\252\277/\252y\214\262:\267?y4\370\204GU\263?\217\350\270f\335\231\246?h\374\315\332A1\271?\300\332\354\352\311\325\275?\025\030d\375#\221\313?\021\354\262\364\210\242\233?G\356!(\317H\257\277\014Ap?\327\310\302?\205\n-\\6\332j\277~JY));\264\277\020\032Cv\024\337\276\277z|~4;v\247\277\016\354L\376C\023\263?\277g5\325\356\'\320?\312\315\260q,\231\262\277\325e\344\2725\212\301?\3312\002\355u\315\247\277\217\251\251f!\017\277\277?\031\303$A\216\227\277r9,\026U\305\301\277\273\230\344\334\207X\215\277t3\312-{N\242?\271\261n\3427\350\236?#+\316\373\360W\262?\270e\372\214\315l\263\277\345\245\267\3652\256\260?\301\022$\366\023\r\265?\"\262\"\254%\336\255\277\253B,\334\377{\304\277\356!G\221(\214\251\277\227Io\270k\201\232?gM\177\215\343\300\302?\234\247~^[&\251?\370\317/\334\023\303s?\371\353\016\202u\352\302?G=K\0014f\262\277>\234 \232<t\216?\261\027YwM\261\251?{\r\316\360\205\244\322?\317L\371T\377)\243\277\222\003\\\374\244\266\277\277\325\261\377\035a\036\305?\315XX\371\332\004\323\277\023\245\371l\355\310\262?F\266^\375\315\204\250?)\263tk\372\352\302\277\n\334\017(\020\000\242\277\331\275rH\255\'\262\277\321\337\317J\314\017\230?i:b\274d\300\306?\201b\234\230{\367\270?y \257l6K\252\277\202\025\256\260~\350\274?\200\337x\021\354q\302?0\305\035L\351\331\240?\256~\366b\353>\261?\327\213\251\21441\330\277\325\234\010ESL\241\277\025T|C\231\033\276?JQ\206\307\340\232\320\277\034\307\227Y\377\024\202?\312\200L\010\217N\273\277R\313\327\010\355\211\253\277\";\001@\351\271\254?D\314\274\003\260\342\202\277\3736\216\200\330n\264?\223{5\004k\210\303\277\302\322!h\232d\270?\346\242\363\"f\367\250\277\317\000*2L\356\274?!Vl:\335\324\277\277\250kBl\003f\244?\256H|\365\303[\205?\274\026$?\227\257q\277y\303_\322\365\337\253?\365b\303,U\247\264\277\004\tB\014\300\273\270\277!b\214\244\367\032\317\277niA\220\375\243\250\277\001\243\367\214\340\333\244\277\267\266\311\232\025b\303?\203R\227\272u^x?\343\361\311\212Dh\216\277\315\372\027\344u\242\304?\235Vg\214\347\356\305?\251f\300e\025\'\254?%\271\033R\006\325\254\277\351/\207\002\212c\321?\347\336_\\6\307\254?\360\343\374\023\007\305w?\210\016\036\351\200\373\273?\364\212\372<\274\255\323?&#\257\216g\314\222?l\351\245_\361e\300?Vl\331\367!\272\254?IM\217]\332\036\227?\306+\247\351e\307\302\277F\312\235.:\272\220\277\0133\020T\233q\227\277\224P\246\222\315\266u?\252beE\262\233\245?{\006\242\261\030\316\311\277x>{f\324T\230?\272\360\\\270p\223\257?#\313;\327\343\246\220\277\317\"\364\212\341\366\324?(%\3039\007\245\243?+/\254J\244\036\306?[\363\344\031\035\277\300\277\241=+\257\214\306@\277\337\352;\300\254B\310\277\257E f\240t\302?\363\205\027\233n\232\244\277\204\237\375\215F2\250\277\334D\331\266~\345\274?p\004\352\276\022\003\306?G\006\223,Wm\250?%\235\376n\206-\264?3\356~\026\372\031\255\277\250\333\231x\031\316\304\277_B\3432[@\232?\300Q3-\235?\242\277\211\326\326\016b\251\254\277>\274\031[\014`\251?z\324\250\220\335g\275?v\206g\023\260\246z?M\275bd\036$\275\277]\333\031\020\215 \203?r7buX\225\242?F&\364\344\"\010\255?\257\310E\301\362z\271?\315\021%~\'\n\251\277\356m\023\331\327\353\210\277=\316\177\233\213\241\277?\307 ;4ku\231\277\320\345\313\373qQ\245\2777D\001F\310:_\277X\373\250-\222&\212\277\371\026\230\233}-\302?\0130$\323\277y\321\277\273\017u\356\225Z\253?\023\242\252\034D+\255\277\352\251\\n\342\033\316\277\020o+8\354Vu?O\264\007\254a9\272?xf\037d\363K\277\277\355\222\021V\"\324\230?\340/\275\220\013f\307\277:\3754Bh\274\251\277\002xP\335\r\035\277\277\253\242Wh.\013\200\277\2540\200\333\336\335\274?\344\025m\255 \314\241?m\244$\177\005`\307?\342U!k\200w\320?|F\277\"\320\030\273\277\001E[M\320a\247\277\253\332)\361\030\272\244\277%{\264B\020^\301\277\342\300\023Za\017\222?o\266H\340[i\243?`\362\310\357\247C\232?\027\356\222\035\245\325\223\277#h\353\0339\322\246?\325Oi\321X\003\312\277l\251\267\035\240\301Q\277\354;\233\205\346\351\267?\203(`\33240\245?\035\261\016lWE\275\277O\017l\225\023\034\210?\2143#\246DD\222\277\306\351\366\261\266\024\272?DH\355\317\230\031\266?D\275 \341\361\256\235\277\266\346\322\217\306\274\303?V@\021f\347\001\244\277\'_\324f\350\335\222\277\353\3323V\007 \276\277k/\013\026\302\356\250?\313\372Le\024\354g\277\346\351\220\204ac\256\277\320+\004\013\001\266\254?\r)&\276\303\256\264?B\366h\"r9\253?\353\274\017\020E\267\270?;\270_\"!t\275\277\222\016H\330\376(\260?A\020\237\301\252\311d?\335\264\375}\217W\242?2*dJ\016\221\242\277\241\352\203M\367\301\241\277\305\376G&\275\244\310\277y\2340^\017R\203?\347\217\t\t\254\300\254?D\214\036\322\212\350\214?)\310`\"\021L\246?\247\305a\242E6\305\277ZeP\223\202\353\272?r\266\326\212D\330\237\277\366\334\200\260%\303\261?\321\221\3001\265oi\277\200I\346\200\360\200\262\277\275\204\341E\351\377G?y\302\300\360\202\215\303\277n\212\215!cy\261?\352}\030\377\242.\307?x\214\363&Wb\311?\t\263\003\336d\336\241?bFe \337!\306\277\337S\227\313\200\376\263\277\310\267A\335\230\330\267?\365oe%J1\302?\273\322\327\275\036.\242?Vq\324\201\247\214\314?+%\207\300\304\231\203?X\206CI\267\007\260?\363\354\227\t\033\267w\277 3c5\235\211\256?\313`\245\'\206n\204\277\363\2574\371\320m\221\277\330xD$\036\2776\277}\326(\330\3569\256\277\234\324c\326\277Q\310\277p\276\303\225\274\002\233\2775\215\256\314\212o\320?\020\355#>\006\207s?Ez\3270\343\262\254\2776b\332j\276\367c?\336\317\331\313V\373\251\277`\300\304\347\236;\241?\263\343:\244\237\365\252?\344\242\'\007o%\302?\224\364\244\3576\337\254?\251\221\317%\230\215\220?i\032\307\357\316\007\273?\276\270}\260\0206\276?\233\025N\331P\271\215\277\354\207[\266\352\275\264\277\275\013Q\310\016\233\260?\254D]s\255\300\273\277\326\253\212\2754\240\306\277Z\350^)\374\210\217\277\001\326\020\363c\331\224\277\201Z\0237r\351\266\277\307\321|xq\214\256?\201\t#\343\351B\263\277\266\034\300\231j\347\311?\225:\364i\346\342\244?\037\204W\354\017\233\260\277\310\373\321T[\267\216?\2777\261\177\\\221\300\277f\237\037\212\362d\264\277&\3714\352Ry\302\277\204\232+o\332\365\270?\207\306\315MM\217\271?\346J\023a\005\277\277?_\306\020E\351 \274?{\332\350\251\212\234\275?z\267\234\202\033\246\274\277\332\313\321\231\333\274\202\277\355\006\nB\240\300\206\277r\351o\314 \320\250?\237\272\022)\217\371\323?Q2\274\324i\356\221?\346t\177\274\3278\274\277\271\0253\234\276:\310?&i\226\020\305\241\242?,?\254\313\206\214\241?\223\252\241\362F8\374>6\314}X\235D\304\277\033\371\234\313\231`\254?0\236m}\'\255\264\277\271\177O\026Z\352\300\277\"uAZ\376zr\277\001\364\354\2229\235\213\277s\227\303{v \265?\212\023\033\372~\034\305?\310R\336j\304\215\214?#}QS\014\001\244?\033\222V\205\330\373\312?\334\342\225M\272\n\277\277\270\247?\265t\331\256?!T\242Vb\036\224\277\374\014P/\004\236\303\2770\336)2^P\276?M\264$\352o\217\247\277\201\261\345\212\275\201\252\277\256\267 z\225\333\254\277\026\316\256c\313}\222?S\322\'\325\332\372\213\277\343\216}\001\273W\261\277\376\226B\3463\231\257?\014bP<\376\020\263?\214\331\017Nl\215\312\277*\223\021\324\371\317\317\277\224\333\325O>~\220?\031;\340\242\305^\264?\277\365\311\341)u\226\2779ra\010\354c}\277\010\272\232\260\0178\253\277\366L\212\365\266\247\303?\346u-W\264?\223?;\2206\312&\201\205\277 VwA1\201\235?8\201\256\377\002]\305?\313\000x.Bug?\216\360\230\301gA\260\277)7\262B9\324r?\372\207N\242f\003\321\277\212\334\350\315;\234\266\277\210\031\241\235\246\270\311?\240\340T/\337\267\304\277^T1\256\224\323\243?\257\347&<\364a|?\332\277?@\370\026\270?\010\273\332\304\n2\274?}d\007\334b\316\261\277\347\361\r\307\353\211\271?\321\201W\322\227\270\237?D\276\373\240\231\315g?F\307\200\307\0311\236\277%h\245.\327\003\277\277\335\233 \275\216\361d?\377\355\376\202[c\252\277\325\036\203\204\205\340\232?\350\311\335\253\243\222\270?\305,\310\323Uz\321\2773|\022w\313%\215\277\370\002\202\001\276p\240?\361\211}(X\357\277\277K\204\363\207\"Y\272\277]9Y\367\262\020\204\277F\324\224-\322\001\302\277L3*E\314\352\267\277\365\201k\233\271\245\205?3\"\251\243\tk\306\277\233\313]\232\265T\320?\200\214\221\031\332\022\261\277\374\375\247\304\376\331\266?\363v\323k\227S\240\277\331\317\001E\'\204\236?j\373\213\261MT\261\277\016\270\310\330\232\253\221\277\312o\215\216<\321\242\277\371A\226\352\'5\243?\177[\'\275\307\211\306\277\231\241\215\003\347\002\302\277\373\267\336\300ag\304\277QC_\3306\363\254?\303\265@B\312\206\264?\"h\202\334\004\224\302?\022|\323&\303\275\271?\363X\034?\371\222\321\277\312\311\377Nho\301\277\010u>\221\270\250\262?\240\032b\035\231\303\261?h:\273P\237\322\225\277\000S\274:\345>\247\277\377\243\206\216\371\030\260?r/\203\312C\277\262?T?\346\265P\332\235?\030\302j\340W\270\245\277\r\311\201cv1\243\277\002\343a\237EX\313?\264?\351\271\215\207\241\277V\256\243\240\306\261\302?@\032\233\316>\307\277\277b)$\277:/\320?\347\211\022\203\316\251\302\277\"\206\2242\266\017\263\277\227/\334E \002\273\277A~7=\013\320\210?\373\213\306\372\364\327\271?\250\242c\2100\227\312\277\331\263o\354_\235o\277\030l\324;\037\r\264?\000\341.\244\337\365\273?8-\004\0231\022\217?\243s\254\037\234\211\301\277xBV\013\023\301\262?y\270\203\251\223\236\270\277\356\252\217\362\320\301\261\277n\030*\016,\362\312?\341J\023F\026\310\267?\245\207_\361\004\273\264\277\254\371k\013\263\242}\277\216\234\330O\347\337\271?\001\241\200\276\372\307\261?\225\212\357\372\312\020\266?K6\300\252\2607\264?\240Gc\364\375\004\274\277\014\264\253\315\231\035\312\277?\177\272\245a\344\304?\027(;\031\260\357\237\277\342:\376vt\243\303?/\025D\335\242\262\322\277\022\005$y\0309\267?\213#\205Z \\\227\277y\276\237\017\035\177\246\2771q\315\036\037\220\257\2777\024\323\024Q$x\277\232\243\202\321d`\232\277\274\007\366W|\020\304?\235F:\241\014\364\253\277p\207Qn\t,\302\277\030U\353{\320\243\252?2bp\321\022\270\317?5a\366\344@\207\301?\346!\035\336\251\024\246?\032\350\034\210;\265x?\215P\301(\352\020\242?GC(\345\035|\270?\346\252\277r*#\250?q\216\206\014\264\032\312\277\374\315\330\241\\\311\276\277\313\304\020\020\206X\277?\004\365\252f \000\253\277\005\025\350\360\301\360\247\277\32621\206\312\276\275?\227\273b\263n/\320?\333\307X\026 \023\320\277\257\032\021\222m_{\277\202\341/\247+\246\217\277\351\237]\022q\203\264\277t\364 :;\035\307?\204\371\346\217d\221\177\277\270_\365\223\2222\301?*\rh\374dq\313\277\374\217\004^Ps\237\277\316\354\225\276\350\252\240\277\362\222\203\3657i\300\277\3613\355J\215X\245\2771\224\256&x\237\262\277\027C\216y\272{\267?\266\363}\343(\332\300?\267\035\335\323\251Q\251\277G\315\243W\352\371i?\266}\001k\352S\246\277\327\33459\3649\302\277C\312SpB\350\257?\227a\016\177\204\000\273\277\255(m\242I\314\310\277\224\241\023g\315\305\266?[\331I\r\037\242\300?\016/\302>\034\244\272\277\367\222uW\3228\206?\"\023\354\324Y\331\303\277p\342\331l(2\315?\224\230&\267\215\364\325?\270\304\236\271\201\305\252?\370\350,\313\360\266\303\277\317\225\351M(@\307\2777\273\260\003\364\\\231\277\001\217\250I\204\300\247\277\375\344\246\323\336\345^\277\216\253\205(\0037\305\277j\221\260Q\347\314\254?I\214\347[\235\356\324\277\254W:\315=G\271\277\006\233\315\250l#\301?\033\325\340\203\215\246\272?\005\235E\305 \366\263?}=\347g\013\340\302\277\265\223@\316\004\032\242\277\360R\302t^\227\271?\343%\3749\374\033\201\277\316.T\364S\032\302?+\'\266gY\345\267?\2178,\025\301\364\231\277\302\342l\202\347\004\241\277@\\\373\366D\014\262?h\263\202\241>\223\226?\317L\tXe\305\312\277\327\345n,Z#\227?\\\035\'\376A\253\253?o\351\331\270\334\"\316\277&%\266\023K\'\347?J\251\026\347T\010\240?u\211Q\014\212_\311\277\250l\"v\273b\250\277G\222\352o#\377\206\277\204d\361\0004%\236\277\254\252L\034\212\323\304?\237f\240\032\342s\241?C+\022\226c\372\204?]V\377\034\217\3336\277;J\014\231\312\016\317\277\037\273\273\347\263\252\300?\266y\2709\033\361\243?3D\004\335\353D\275\277\202+\216\027\310\t\317?mbB\027\214\014\213?\257\225N\301\227\332\213?yw\216\270\250\362\304\277\261\264\273\352\t\354\260\277\2057{\246\307Y\263?\220!\334\252\010?\302\277\267\26712\372\251\310?\2551\005\007\261$\214\277\276\205\323\005F\257\227\277\240\223\377\243<*\266\277:\363\322\2074\203\244?\261\022\333\327r\326\261\277\345\010\335\261\002\000\265?\353\254\357P\324\376\242?\323\225\205\023\365z\223\277\211@zw\035\362\321\277Qh>6\243,\305?\n\r.\034L`\257\277\016#\3259\014F\203?\0103\2258v\020\210?_\302\253\244J\220\235\277x\225\376\255\247\266\255\277\275\361\004\374\336}\203\277\370\235\205\237\351J\233\277\301\224\240\306yX\204\277\300a=2\002\272\251?6v\014\310\263\336\226\277\307?\005r\212 \320?\007u\342\362K\032\261\277\273\\xD\035=\231?\377\rx\026\260P\271\2774\355X\301\265\300\272?o\257\230\017\356c\303\277m\344\225\245\016\234\267?\022\363\307H\276\253\224\277<\034\247\302%\373\221?po\0109\203\223\303?h\006\014y\203\261\311\277\234U\371/\367\320t?\032\021\345\225\373\007x?EC\350fq\265\310\277\tv\255W\032L\320?\010\025\374\375pvs\277\260\313}\020X7\307?\032\275\031\374rq\244\277Vk\263\366\311\276\311?\220\'\237z\030\223\311\277\r\377\275\"\320\347\252\277\033iI\017\252\276\272?`j\261\232\226P\263??\3211\206(\235\303\277\360\351\345\370z\321\235\277-\025\014\265\347E\234?\033\002\267\243\226>\266\277\213\322\313xI\364\260\277\024\226\260h\020&\327?\330\366\243\r\023\342\205?\252\227\242\r\221\036\241?i\247\300E\210\232\301\277\301\013\207\364Z\333\237?T\260\034\364Q\255\300?\310\244N\200\205*\242?\ra\255\357-\000\266\277D\252\307\354B\345&\277<[CB\323y\303?\257\373\004\362\247>\267\277\r4\246\320\265\273\260?\375\244s]\026\346\206\277_\302\327\265\324\201y?:\304\302P\\O\254\277;f\237\275\244\355\231?h\321O fB\303\277\236[\272\237Z\204\303?\363\005(\215\t)\234\277w\375\227\014\361\367\253?A\032\244pO\307\257?\014\201y\316\014j\304\277c\361\212;Zm\272\277L\353\316\306\337\005\231?\301\001r\302\377\005\303\277\335\343\304\373\007\217\272?\010,\225D\227\345\225?\037\320\237\031h*\264\277\002\221#\3166/\317?J-Z\267iW\240?i\376t=X\271\272\277\027\216\272\201\n\020\225?\312\271)\247\254O\226\277\2633\2106\377r\245\277\021\352\364\216\245\252m\277\367\2617\370C\255\272?\354\225\214\034\014l\256?\223\023R\245\246\324\267?v8\274s\261t\313\277\227\263\375\031\321\375\304\277p\021\010\335\301\016\213?B\335\355\351\017\036\267\277\240V\314\313\322m\261?\035\025F\352\226|p?\333_\255q.\316\221?\034\350\364RO\266\240\277^\365SQ\022\334\301?W\367~\372i\336\220?\215\327l\214\325X\307\277&n=\276\376\204\222\277D\231\020p\375\235\303?\312\031e\356\243)\266?\207r\007\211r\277\245\277L\230\350?\025\270\272?\005\331\331\345\034[\237?x`\242\325\374y\276?\203^\376\354T`\305?\305+\336@\326\nN?s\370\237 /\353\224?C\267\032\2113\307\304?q\003/\217g4\253\277\231\245\222\250|\254\262?v?dg\010\274\223?\"\036\032\017t@\306?w\024,x\036 \234\277jf\014\222\001\343\260?\364\021\264E\227\304\263\277T\017\205M\371\356\273?\200\033d\215\032\205\211\277\373\276\263\355#\272\240\277\371\215\024y\306_\307\277\264\303\032\246\241\365\242?b\331z%\275\325\255?\257~\211V\030\257\304\277\324\255\271&\324\215\260\277;\247c\236\373]\241\277B\245ziM\303\221?@7$\372t\361\321\277]2\003b\324\007\255?\325\341\336\013VP\265\277\373L\241\217\336\377\263?\204\225\\\310\356\203\313?\005\\\3044$\254\264?\200D\302\2654\272W\277z\273\252\027&\307\226\277a\223\307\372\311\213\251\277\211\357**\300\325\247?\2200\035(\337\"\241?K\224\274?\032\020\275\277\315\216\245e^Y\210?\'Y\027\374\256\323\311?j\344\013X\330\266\263\277\357\271\210\253\320\230\232\277\204\254\024\264\301\331r?3X,&P\265\263\277\345<\371\252D1\236\277\350\331\"\t\225g\272\277x\222=\336#\345g\277s\001\221bpo\302?j\331\r\0340-\233\277\353_\001\374\200@\300?\310r\204\0371;\317?z\356\025\235\351.\260?vX\273\373\357\026\235\277\014\r|\017\277c\257\277\263\346G\rQ\016\274?\007\356\033Q\235\346\246?\006\001\254\324\036\032\221?\343\354\362%\257\264\226\277\236f\000\330\000\211\234?\311(&\341\001\277\226\277Y\216H\210O\321\236\277T9\023\"\356\305\301\277\346\251/\322\0370\226\277kJ\330\371\301\243\220?T\227d\2452\237\245?L\213O\271\246\375\260?\014\001\360}w\231O\277\364\224P\272\227H\206\277G\326RK\002Y\302?\222\237)\261\007\246\250?\225I\265\205\366\275\307?\270\271\234\273p\354\274\277<Hl~\2652\270?\020\215n\202\335\345\311\277\023mz\331/\220\265?$\375\016e\261$\321\277\313\210X\231|\340\225?\221\365o\307GJ\277?\367\250j\r\016\002\230\277}xj:5j\300?\030\373m\335Nd\250?\311\022s\262\016\033\232\277\"E\205\342\222\371\322?\000\316o\010\255\303\250?\322*\300F\207)\224?\353\267\240\313\"1\224\277:\347\377\311r?\300?9\022_Q\013)\250\277\231j\321\375\366d\301?\331\327\322\"\020\341\257?\333\3733x7\014\256\277\'\0331O\376*\206\2777\030\233p}\257\253?KEr\010\206U\267?\217\020\006K\333\353\251?),X\177#Z\277?.r\301S\323\216\267\277\033\027c\005\333\343\237?bj\213\305.\010\223\277\274\221\020\370(\243\311?\\SuA2\013\301\2778\010\345\033\220\357\201?\013hp\337\343\344\313?\023V/\217\272k\336\277B\327\020\032=\242\271\277@}j\271\247\362\263\277f\346T\203\375\333\304?\3523*\335\366\r\263?\241\374\301\363\314\016\244?^\214\222\307\370\375\217\277!O\237\014\036\216\275\277\331\335\257\332\337w\247\277/pl\257A\342\251\277 -&\365\257;\321\277\303\312\203\t%B\270\277`\317\335\362\320\344\262\277\022\303\306n&-\277?\016*]\014\365\177\303?\232\351\312\200\207\203\225\277\333+\006\224\207Z\321?\342\010\274\377DI\301?\235\237\203\333U\310\245\277\242\344@M\017\331\225?\030\324\216b\243\207\302\277\246q\255D@\217\300\277W\211s\313\364\201\244?\227\371|2\325\203\302?\000\217E2\240D\274?R\252\177\223\200\275\224?*V\215\203$\254\247?\345\253N\334\250^\306\277\275<\316\263\301f\254?f\035\342\311M\017\210??\026\343\256J\036\257\277Z\314yO\037\275\310\277\263\367\023E\221\256\301?Bf~y\215r\262\277\215\203\025\000\314\362h\277\326\260\332\260\244\'\265\277\331V0\262A\037\207?G\337\316\271(\013\256?\247q\016\216\324\013\252?\302Q\n\n+\300\252?\247\000\303K\201\325\231?\271\3372\256h\242\252\277\237\336|\'@l8?\271\305\262\231Z=S\2773\312\341\221\313=\310?_>A\215\223S\306\277Vg\345t\330\360\254?\352\244\243\216S\277\232\277`\302\275*\336\232\244\277\373;F\204\177H\311\277k\375gA\323T\245\277,\036HzR\374\261?i\036#\377\036H\264?d\254\321\274\0335\267?\250\266\231h\230\255q\277\250\366L\225ne[\277\007\260\000x\244M\263?\270\254]\2135F\235\277u\213c\234\260\335\221?\265\2413\303\311\n\272?%\354(\014\000\313\213\277/\026\253j\001\254\260\277/@n\212\372\227\256?\037Z\323\006|G\326?\037\2436\340\034\324\254\277\376\207\241js\202\271\277\245\374\254\305-\224\321?\016\256\215!\311\321\307\277#\224|\n\272\305\304\277\377\355\212\017\232\332\304\2778)l\222\335W\235\277\264\263\267\342\367|\250\277\210\346\2363Li\262\277\320J\312\227I\246\270?\342\263\031^\005C\263?\030nI\n_\231\302?\240P~T\033^\276\277&S-\3572\304\260\277\033\254c\342|\211\242?Y\235\205\274\344\213\252?t\336\326e\274\226\266\277\300\347\315\227\240%\227?\345\370\267N\023\360\223?\337\347K\225#\210\321?E\251\177\307\330|\262?\321\247\330nz\337\260?\276\330,sx\177y?\374[\215\330\276\244\300\277\2407\223\322\316\355\312\277\262*\t9T\230\253?[\024\314 #\365\236\277\313\242\334\272\r\236~?\373HG)c^\234\277\323\204\002\216\326\225\243?\375\026W\370H\000\257?e\353\324\317Jy\254?j\300\360\254)k\260?=jh\246-\006\215\277\312\376\001?}\345\240?h\363\035\372\027\\\221?8\221\340R\327\270\267?\320\372\371\'#\027\234\277/\335\373j\200\033\241?\265\366\nhr\033\245\277\020\300\315T\367;\245\277\014\337z\377\021\307\317?\314\273\016\032y\361\251?\307\221\252\231\330T\272?~R\342\313.~\311\277\374\347\353\232\247G\231\277{UYj\247\234\271?\016\232\337\n`\031\265\277\n\345bl\rY\251? Z\357\372\207\027\273\277\352\234,\302\3034\250\277\345\345\245\020\252\003\251?,^\336\364)2v\277\216\266I\301\251I\246?\347:\341\336\'3\236?\267\335Ou\306[\230?\276\343\341\275\275\256\204\277\201\334\305\007\024m\245\277\2038\302]\r\231\266?y\000#\311\355\365\243?\013\220\366j>\010\260\277\313\'\303\327W\361\310?\024\r\005\177a\021g\277\206\'Ns\371x\275\277\226\307a\312;n\262\277\036($(\213 \315?\023\224fr\251\353\274\277\323\224k\342\035&\302?\213\2138\337\327)\300?i\005\3145\023i\257\277\"6P\r\372^\262\277e\017.\003\004N\303\277\262\367\021S{\026\250?\2436\214\n=D\226?\203:\016\225\232\247\251?\003g\246\344g\304\240\277\035uX[w\351\250\277j)\2217\231Y\246\277\014].Lyjr\277\007W\231\234\200@\310\277\242\220v\375$*\257?\222E\025\3122\310\253?\014zw~\2376\256\277v\035\231\273\373D\306\277k\360\335\340\300\323\246\277c\346\351\304\025\303\245\277\032\025|\316\310u\307?\245~\223\025\323A\224\277m|\355\227\355Q\303\277s\340\272On\200\271?\2105\341\023r_\265?\255]M\310)t\261?\205\027\335p\367q\212\277Y\031\003]\234j\315?\205\022N\262\002\254\264\277\246tz\271\242\227\276\277\307%\310\227\241?\263\2776\324\002h\270\212s\277^\254\231w\207\230\252?*\336\025\177\220\004\275\277\213]\263\276\3422\310\277J,\'?f8\263\277_\212\313V\350\267\271?\177m\377\313FH\313?a\362\013\203~\333\326\277\377\310\036\3116\376\254?\230\203\n\246\375x\267\277\205W\\\230j/\252?\256\361\337\275\246\200m?\314X\220\232\304\243\260?+\304\247s\264\333\272\277F>$\327\272\006\221\277\373\266\2750V\023}\277\363\307\244Y%\215\305\277\317\363\t\311`/\311\277!\204\370O\233\245\230?\211\346\275MZ[\240\277k\312\317\253~#\335?\305\341\002X\266\263\272?\354y\212\370\332\300k\277eP1\033\262c\313\277\215\347\206\261o\374\272?\242\363\252\013\365_\201?>\377\365\273TU\227?\024#G\215\310\271\261?\302\341Q\"\2413\227\277\020\005\"\221\260\266\366\276Y\006- \023\353\252?dh?//B\241?\336W\333@N\266\276?8\324\336 \321G\273?]3\302\235PL\271\277\277\032\321\313\367\267\324\277B\260\263]\275\334\222?-h/uM\005\301\277\2119i\260\325\206\260\277\302\341\332\333\356\374\263?\302Py\310\321&\234\277\224\026IOv\261\265?\353_3\270S\336\241\277O\3610\236\334\014\261?\264\0332\360!\260\272?\230\263\036wd\026{\277\234s\027\366\356\373y?F9\200Q\207=\242?r*+i:R\323?\300\304\255\335\226u\321\277\021K$c?\222\246\277\271h\320Fq\315\241\277\240\375\364\245-I\241?\025\300O\343A\375\267\277\013\317\013\350\303\205\260?\336\263Q\037\317|\276?V~\035\230\332\000\256?\214\340\212\204i\004\260\277\232\013Azt\207\266?\227\'\031\177\263f\260\277v\264\021\253=o\222\277\265,\210\031\226\221\231?\n\004F\370\201,\277?q\366-(\267`\203?-\216R\326\277\222)\277\355\346s\302G\371\255\277\371!\022r_\t\302\277W!\026\007\314@\270\277\220\250.I\343\202\302?\354\304\310\013|\247\254?g\004\'\367r\252\256?\260\271\342\333\340\022\302\277\331\321#LZ<\301?\\\211j)\210\027\276?zW\366P\021\307\271\277)\2701k\376\235\272\277\2667\377\314]u\311?\367G\"Y\344\220\250?\357@l\2512Q\274?\206\024\263\215\242N\226\277\375\273\3604\320T\224\277\327\254F_\221\272\313\277S\235;\275n\271\240\277\206w/\303\t^\236\277e\342\366\324\236A\262?\313\023\371\r\031\273\255\277avt\000J\260\237?k\024\254\031j\271\302\277\254\314\312\351\373\250\255?\r\272\353Z\'z\271?\373\364\240\237\214\023\323\277\022\037J\013\236T\274?\003R\303\252}\273h\277s\256:{\240o\261?[\033\350\213|\307\277?\273\312\32238%\243\277\267X\025\365\351hq?\037)O\355\213\'\261\277 \223\311\212\227\025R?0\003O\271\214\370\253?@\226\035\212T\272\267?\210\217\350\336\016\343\261?\307\204\035\214qY\250\277\333\342\002\341\265=u\277\252h\2437\017\333\240\277m9\355\307\003C\250?`l\364\360\251\323\253\277\326\t\241\013<\354\250?Vf\235\316b\261\254\277\023\210\370\024uf\277\277\000\356\274\025\217\267\242\277\227\213\242S\377\277i\277\013\234\363\367H\200\301\277\177\014\271\023\251\241\227?\353\343\207Z\010\367\222?9\303ku\373\277\321\277\336\342\033p\036\233\207?\352)\366zw\366\271?\345\305k\215\177J\277\277\203\245\206\333U\230\263?\246>yM\374C\230\277\034\216\352\236\n\326\266?\222pQ\2523\275\226?_\211\357\251\332}\273?LV\237\334\004j\315\277\356<4\216I,\303\277x*\337S\325|\264\277\233G6=\002\250\226\277\306\347<\220\204\326\260\277\357u+\235\305\236t\277\317\273\2752x\323\305?4\033\341\341\223\307\255?<x+~\220N\235?}#\306\225\342|\243\277q\351?\255\355Q\273?\312q\021\204\014\017\326?1j\3758h\353\222\277M\271E\202\366\026\306\277\232d\216\306\345\014\255?\343\252\222\227\227\361\253\277\032\355I\333\240\211\212\277\375\235\205H\363\027p\277/\305X@\337r\242?;[N{\250\220\271\277&\214Rz\245\271\262?|Ar\033\273\301\304\277\020Q\022\350\033\351\306?\320\351\227\342\306v\236?\245y\017\304\312\263\215?f\021\000\257(\010\225\277E_I\341_f\243\277\006\210\322a\246\312\302?5\345\034D\206T\262?\017\013\345\016\337[\306\277\022\302:k<R\220\277\\I\033\334\016\321\263?\376CH\215\243C\301\277_\344s\344\220&\273?\246\244\362\021#\016\251\277a\266\306\007\017;\263?_\032\335\324M0\330?Lh{Ay\035\315\277cB\\k\026\t\230?1\031\227z\341\016\252?8\205\216\014\260\337\302?\354\325%&\353 \316\277d\306\360\235\007\305\244\277\'\267\331\235\231\240:?X\3053*\370X\253\277\"W\037\034q\360\245\277\257\033|\232^\367\246?\323t_\335b%\246?\247\024!7\364t\246?O/\211W\354N\227?\006^-\270\244\223\257?\350\016u\257\310\253\263?\3641\247D\013e\222\277Tx\232\373\0351\272\277\n#\313\350\377o\253\277i\231;\242\036\326\236?\267\245G[\023\226\227\277\3238?h)m\243\277\360\301K\315;s\320\277\266;:|V\225\244?\374d\252\233x\245\243?\323\304#^&\242d\277\353\343!A\254\350\250?\356\014G6\364?\253\277j\345\033cu\'\272\277\325\344\366zY\375\264\277\225\324\265\357D\216\262?n\272\037dZ\300\203\277!\030\200\023Lt\256?\234\223\273\334\320s\274?\334I\206K@\336\272\277/\202\036\201|\266\230\277\220x\007\221\257S\323?\344b\352p\'k$?\345\234\277\027g\207\227\277\215\322Ex*d\234\277\345i-\243i\301\274\277\231\232\233\206\311\013\223?B\342\226r\207\353\312\277L_6h\253\005\265\2778E\317\201\335\364\252\277\317\006\\:\230\020\302?JH\263\274\347\321\206\277\221\037\331U\213;\310?\221B\361\330u\356\244\2773e\212\027\376\200\301?~\002*\323\'\374\321\277\026\347I\305\230\275{?\004\016w\313V\324\266\277U+\2420\030\177\233?\325\207\217\272,\201\252\277\237U\2157\'\214\302?\337?\271X[\350\224?\'-\204\254\323\027\277\277\252\031\267\276\033\177\266?x\334\001\270\325L\265\277\360\213J\354\032T\242?\021\316\355o\3147\300\277\267d\324\037\n,\223?\343\267\2774\027\030\263?\272\345\347c\023i\307?;W\367o\202\347\252?\330\0021C\002\355\274?7X\332^\002\200\254?\277\352\343\222+\337\256?Bc\350\022\344\274\252? *\\A>J\264\277k\236\272\344\200\034\307\277w2\240\306\220\263\306\277NW\202\273i\256\241?\200\237\271 \337\324\310?\245F\253{g\262\301\277\213f:^\365d\240?_\24406u\353\225\277\205x!\301\002>\266?R\354C\357\257n\242?\224\022v\267\r\361\300\277I\270\313\200N\026\317\2775\340\225\334\r\025\237\277b2\242\277\r\215\265?,\036y\215\343\270\316?z\023\324lJ5\265?B`\026\262\364\243\252?\341^/Mq\035\265?\211\0142\260\241(\303\277}\232\232\200e\025\315?c\r\tS$k\313\277U\301\343?\245K\255\277\024\014\205\2167\230\271\277!\375\305j\261j\264?\323\302\3555\361!\242\277\016\227T\232w/\307?\tE\315\030T\241\251?\\\014kU\177b\214?VI\001\231\212\362\304\277\231\227Y\310l\321]? \353\2451\305\033\240?\027\002\374\036\013Q\251?\233\035`\240\276v\211?\010\333\251\366\241\252x\277HM\206\025a\271\253\277\0144\315\035N`\264\277>7xakE\300\277=\365\323\211a\353\300?]\235\356\266B\005\226?\366e\021\325\245\376\274?\323\216\364\202\351>\266\277y \216\270\t\232\260?\005\203vg\230[\303?\242,}\260!\003\273\277\231NR\235\030y\272?\037\242NU.\236\310\277!\332\003\365\270\326\232?\271\365t\215pu\262\277\203\026\367\215\260\250\304?0\341T~\214d\240\277I\200b\377\270\255\267\277VG\241\272\224\005\243\277\261\274\320\\o$\246\277\377;Sj\302\301\300\277\334\221\245\352\264i\252?\331\367\250DH\334s\277\021-\251\nb\230\246?\277\203\335\370\030\237\250?#Yv\351E\276\211?&\310f\340\037\363\311\277C\022\033\020n\276\261?\367\257\337\227\242n\261?\003H{\200\256}\257?\250\361\224\026\242\246\303\277\034n\357\'\260\320\260\277\371<\\G\211(\252\277|M\307\241\202\260\267\277\221\036*3\257\357\266?8\032\014\205\215\\\251?|\366\t\007\227\024\246?H]\257}\3532\305\277\236e4\256\205\327\274?\242#0\242\231@\250?\300h\034\331?\336\271\277iT\225\330\223\242\312?\036\3232M(\301\307\277\255\031\265\rQI\261?\213\013\2568\351\241\220?\245\344\341\006\2530\227\277\nGl\250\326\220\243?\300\017!\000\027\013\300?\232\216\005Y\025\256\314\277\002\307\326)\270\377\244\2775\245\353\003\230}\274\277\206zLA6\260\307?\014r)\225\211\365\226\277\305,&\376B\274\210\277\'\343\340A\351\256\317?\306\304D\276\211%\245?7\363\014\216\301\203\267?e\032\255\262\347\'\211?_\314\266?\022\021\275?\364\270\370}\324\374\302?l,WZw\276\264\277\370a\215\333\367k\263\277\220Iz\005\265\320\243?\331\313!\251\351\210\252?PA\276\216\214?\263?c\"\324}\034f\325\277\264\223\315\307\260\023\275?\253=\264\002\213\025\260\277S\210\223\024Z\366\326\277\363}}\\i\376\260\277!\022\262w)l\241\277\215\020\330\247R\201\263?}\023\265\002\'g\257?\224\037k\200\013\013\177\277Q\345Q\257\207\363\214\277*e\222\304\244K\227?\263\021t/\215S\320\2772\365z\247\263\025\271?d\3530n\'\026\266?\324\360\301\366\353\215\260\277\006*\217\020\213{\304\277\364\247\366c\033\255\230?\351\2518\035\0244\263?\337\037C1\376,\271?\235:\332\333L\344\275\277\240\352-\031\214\353~?\321;d\244\216r\310?g\360z\336\032\206\312\277 \360\030\000t\236\261?\327nf*\032L\207\277[\025\277h=U\242\277\273\303\027\263\345\311_\277=GY\256\032\225\224\277\314\010\317\013\257$\326?\347kp\263\205I\310\277\2719T/d\351\232?\306\224\017\0027\030\224\277\301\332\325\357\223\010\265\277C\306#K0\233\260?\327\322\223n\r\364\260\277\3556]\002%\034N\277\314\023\326f\265\374\310?\374\354K\214\002\303\272\277\177P\251\335\327\254\271?\010\215#\2000\363\310\277\246`\241A9\337\267\277*\263x\220\r)\304?Z\005\365\277<X\302?\360\017\223\252\357l\341\277r\006\221\330\336\177\302\277\251\373\301\0366\360\222\277\003D\361\006XS\265\277]\261c\240\n\225\251\277\323/\rv\265\031\266\277x\262w\3433\224\260\277S\332r|7\007\310?\334\275\n\017\304{\257?fk\3759\224\346\275?\001\245\202\3711\317\262\277\311&\304(at\200\277\212\376\272\007\300\214\266\277\007\033 `q\020\303?\335\363\317\352C\203\306?N\237:\262FQ\224\277\2144\3470\341o\220?\215ST\337\3442\225\277\203\0255\nk\220\240\277\230\004\205U\372\266\262\2771)\251\001\327\364\315\277m\371E(\024\237\275\277S\307\324\246\177.\251?\321\035-vTu\266\277\214\327\025zD\227\301?\027\312S\271\302\315\262\277E\033\216\224\025\231\260?\030l\007\010\243Z\276?\005\n\274\350\327\263\312?\002\027\232\317\013D\251?\374t\247\226U\247\302?\362\316ZKw\227\263\277<R]O\232E\302\277 %\177<w\021\262\277\224\247\024\276\377:\265?\005\374eA\203\033\306\277\037\277\342\305\2719\272\277wS`<8M]?\275\321\255R\347\002\311?\251E\035\275\360\300\223?\331P\217\021\232\244\244\277\255j\330X\231\315\200\277\313\223\377\367\353\027\243\277\337\217\306\230\370\277\304\277\213\224\334\250\322\303\260\277\207L\241\nB\264\301?\224\321\242\255\375#\206?\317D\204\0246N\260?#\235\271(\362M\300\277\024w#\303\365:\201?\020_\231\357a\237\265?!/C+\025\305\251\277\"\250\354\200\004\252\274\277\366\326;}7`\260\277#\210\356\006\002\222\252\277\210\356.\355e|\267?\006\014\342\240 \265\306?2\217\264\034\306\201\256?\333r\276W\275>\304\2779\256b\350\255\021\224\277h>H\305\333\001\274?\374.\261\324\322 \306\277\352\352?\372\274\256\213\277j\251\0017Vt\305\277w\343=\222\\\360p?^\037\032`Y0\244\277\202~<\n\303\314\313\277\010X9vk\271\263\277\207\025\272\367_1\260\277\232\236\225\342\265\224\306?,\271\021-\332\236\310?\t\232o\376\363\021\215\277\367L\177\275\230\000\256?.k`\003|\323\272?\357T\250]2>\316?\274&\372\362\215\243\250?\013\313\'W\177\305\307?\253\302\022\223rI\233?\333ux;O\204\242?\336\331\253\312=\205\312\277g\231k\207d\026\236\277\320d\336\307x\023\241\277\302\\\277m\267\241\266?\027\224 \201R\177\246?=\237%K\3472\306\277q\301\257\352\272a\200\277\\1)o\227M\305?\277K\313\367mv\322?\326i%j\311\327\266\277\236<$7S\352\307\277b\245x0\273\363\235\277\304t\372\352X\272\300\277\323\342\255\021\021\020\227\277m\217\377\005\375\376\304?e Ym\307(\262?2\025\212;cb\205\277\250sp\203\331x\265\277\\\245?W{\343\323?\204\n\345\247\022\026W\277\327\027\003\013N\217\246\277@\266>$\3127\245?A\305S#\333n\275\277\016Nz\373j\325\302?\273\204\3765}Z\304?<\320XjF\320\270?\317\312\n\263\317o\303?\3161s\023\217\364\254?\305\261\316\260\253\364\241?\214\203\277\337\363\006\276?~\220{2/\276\241?)Y\027\212\020\302\210\2772(\342\343E\350\300\277\222\250|\270\201 \300\277\376\335\274JG\266\243?\372\n\371\310d\365\272\277\034&D\t\030S\303\277\\\n}\276kt\275\277\225\365D\001\303\221\230\277\020\371(\227mB\274?\374X]3\t\254\221\277\334\224\200\242\200Y\245\277h\266\307\025\006:\264?\020\026\323\332\370\210\250\277>k\177\355\301C\213?\354X\345\244\201\374\257\277h/\237\037\034\223\313?\302\342\206k\206;\254\277$Y\331\323\220\326\271\277\330q^\226\321\302\301?\265\223\333\262\016$t?~\322\r\274\2053r\277i\203\224sW\306\261\277\365\317#\301\265\000\177?EM\25526\270\240?\344\314\301L,\362\246\277e-\354\206V\261W\277 \241\335\204\r\352\250\277\241f/\224\251\177\247?\034\204\254\013\260\017\220?e|\275\337!\224\300\277\301\300\234+d\260\267?\367\201\217\337\000\310\223?\255\350\361\037\th\247?\200\033+n\263f\262?\227N\356\372L\332t\277s\3409\0137\325\273\277\003\336%\376{$\307?8x\325M\020\246\302?\351\356\336>/A\254?\035\330\245<\310\263\230?\352)\t\213\330\022\275\277/\036f\315\353\240\253\277\031P\214S\221U\227\277\225\306\340p\245\000\233\277%\261,9\230\367\307?\336\352\227\241~\226\220\277\371\267*\305\203\352\303\277]\020=$\327\237\245\277H\314Uf>\354\272\277\275r\330}\317>\240?/\235\311)\321\033\234\277D,\353l\324K\321\277B\007\206\366\\\232\265?`\232O\354\001\323\300?U\226K\367\320\211\312\277J\322Y\353R\334\300\277.\234\024\244t\254\245?\232/\211fd\360V?9\240\205I\304X\320\277\312\t@\004\341\244v\277\016F\340\2504\021\272?\177\311B\214:\002\272?19s\332\376\363\304\277\274\026\375\2000\252\271?m\207?*\240\275\310\277\'\303\315\310>3\315?r\343ea\257$\305?\347\257\317\036\n\213\262\277\247\363\231Q\016\322\261?\340\276\330UC\220\310?\006\351\252N\246\224\300\277\'\276\313_\300\372q?v\341XFM\000\312\277\371\251R\031\372\315\237\277-\315\246\304\213N\220\277 \210\336\226\234\371\304\277\357\321b\241\2475\243?UEU\327\273X\272\277\t\277\233\202\212\233\242? \032\305\332E\317\251\277\244\355\032\262\202\245\214\277h\035\3553\262\213\265?\357\007s\031E\005\310?9\321\020\2454m\231?2\206;)\217\036\257\277\374\343A\202h\203\201?\370\312\035V\353\001\325?tae\232x\277\245\277\317\323\r\247:u\257\277\'\261\317\275\215\356\313\277`n-\224\343v\320\277vx\356\025\351\026\276?&\213\264=\231i\313\277\351\354\272\351n\034\303?\264\315\n\344 \n\233?\256\304\031PCh\216\277\357\360\2608\337\333\311\277k\3608bQ\231\256?\323\205\3160\r\246\251?^\3413\206\033f\244?\217N\245\317\0023\273\277\"\333\250~Z\263\260\277|m0\210/@\302?\241\337\231s\203\345\315?K\221{np\241\261\277\230<q\032\224q\270\277P\325\025\324\300d\200?,w4t\033\236\260\277\260\006\307\245\371Y\264\2775\344j2u\001\242?\305\224\212[-\205\321?\335\354 \255\315\031\225\277\r\342!\311\304\264\250\277\312B\017V?\204\243\277\274=\330\252\210\252\270\277[\211\227\306Az\301\277}Jt\304d\350\246?\224\2474!\004\226\307?>81o6\374\201\277+\360\326E1\006\311?x\272\025\216]\002\263?\345[\227?V\201\250\277\203]@(!\264\227\277Ic\216\373\302b\270\277\220\371z\017Z\241\254?\210^\254\307\332\372k?C\354b0L\226G\277\231H\252\245\365M\301\277S\352\321\"\323\277o\277\226\010\207\220\213\247\242?W8\211B\215\331\177\277\221\'\202Z\255\247\314\277\224\243\361\224\202\023\304?X\014\3276L\216\246\277TU\263\221\340\303\252?\356\311\317\360\275\364\300\277J\237\324&\353Un\277@\255\217\274\262\336\257\277B\346\226;\252\357\255?\240\371\346\005\023:\310?\353\001\n\337I\021\202?)\261h\010B\245\301?D\337\252\272\363V\270\277\t\352\201\200\007\'\255?]q\232\2570\377\232\277\032\2234W\000\323\244\277$\034\276\207\232\343\244\277M\032%\267 \303\241\277\236\363\357\250\312q\246?\325K\202\024\2415\327\277\0066\225\243;\024\225?\255\302\342\213E\277z\277\224\233-0.\367\253\277\264W\365\343\320\267\306?#\212\320\3131-\271\2777%\202p\025}\261?u\222e\362\332)\310?L\234\213\3033\305\246\277\212\320\270\005)\324\207\277\254d\322\363\373}\276?\305\020\020\204\241\210\305?\262\267\004\037\325\223\270\277A?\2668\354\235\226\277]\221\254>\361v\326\277Yi\274d\352%\270\277\322M,v4\304\272\277-D\332\313x\375\246\277z\375\244\232BV\177\277\274\022\354\203\257C\305?\272\242\006$5)\240?\301\346\364\340%\335\221?\212\304\341*\217s\252?\221\236\251\324w\350\210?-\331\226\320\265o\205\277\026b|?\256a\262\277\001\272\240\311\271\023\265\277X\032\2726\363\376\251\277\353K\3144\312\254\314?\006\267\005\322\005A\250\277<|\320\344\327\202\261?\271\003\232\033{\004\231?\206}3\344?\326\234?\204F|o\331vv\277\221\t\300\266\020\276\235?\217W/\230^\341\253\277~\200\265\374q&\221\277v\222\256\246\210\307\274\277\273^|\376C\355\245?\223\277\233\356\021\341\303\277\022\252o\301\251$\241?_U\260F\007R\240?\270[oi\215\371\301?:?\365a1\201\246?\275\233Q]\004\320\300?\001\203T\327?\316\252?\205} \263$)\255\277\230\330T\354{\213\177\277\203h/\315\252\212\323\277/\003\021\261\026\215\276?z\2628q\247$\236?\251\231Z\234\374\217\256\277,\233#`!\005\311?d\223n0\177\035\264\277q\334\240\205Qd\250\277\r\311Ao\236\021\262\277\373VR\267\312\313\261\277\244\242\023\225\316,\206\277\032i\003\317\304\rZ\277\031:\001\026\337\255\323\277\370v\237\253\375A\253\277RX\352\364Gv\304?\2147/,\261\365t?l_\240,\336\357\320?\234\304+\215\272\212\270\277\277/4\215\353\235\250?\267\346\273\302;\241\301\277\201\240\322\003\330\024p\277(\356\242Gz\370\265\277^\234\246\214\347\001\252\277\245L\247\021\301\005\221?\007Od\377E\202u?\242\010X\200C\275\262?\271(\007\371\010\244\242\277\310k\237\327=G\262\277\203m9\306\331\333\300\277\251\321\261a\234\024\227\277\266\327R\365\201x\202\277\213\250\233\3446\372\271?\315A\354^cS\260?\2470\243\356\377\252\260\277\315\231tR\211m\266?\3353\326t\205\233\306?\350\200\363\023\221f\303?\252<_\035XY\325\277\276W\301\016\250\270\265?\036\307\277\025\000\032\315\277\364\014\207\317\214\013\263\277\255\353+\3077\315\261?\316\217\016\307\017\302n?\260#8w\362Y\205?/K\224\237\315\332\255\277\036@\213f\377nM?4KH\022\017^\302?\203\257\2610@\320\220\277.\366\316\351\210\261\236\2778\306\226\315\021\304\231\277\236ah\030\030|\257?l\270\210oO\375\275?\220\247\226\342(\267\275?\227\006\035\004\032\n\231?\213F\276\351\240\362\234\277\333\336\313\321\267\270\255?!\0051\206\253\341\242\277\350\007|\352\\\356\310?5\327\213\200n\273\241?\261?\004\025!\254%\277\332\201\002\270\266\\\242?\276OZf\371\023\223\277\242\023\275.S\336\300?%\351\003\327@u\256?\220\337\345\276\010\244\301?\355\227\372f\000\013\271\277:\242\242\366V1k?\306\016\346\362\301T\276\277\374\316\277\004mh\273?A\347\346\247\007O\263?\"3\216n,\263\254\277\323\230\316\345t\307\264?Q\265.\230\346E\321\277\371\214\252T@\324\262\277xwS\252\234cp?;\nH\336\326\005\276?}\367L\303\375\203\256?L\226{7^>\275?\005\002\202\264\216\375\305\277\2765\037\265B\274\222?\021\206\305\035\377>\263\277\207\261\234\213\377\245\225\277KY\347(\357\222\267?\255\202\013\021\n\220\266\277\'\377\245\306\331\351\244\277\241\327!\3446HY?\312\265e\356%d\301?7iu\227\311\252\245?\334\205bSO\335\234?t ma\036\026\306\277\255s^\277W\236\300?w\327\254\306BN\217?7\343|\371aN\225\277\342\024T\204u\247\202\277k\240\013\372;\375\246?\231s\033FDFk\277\344\320k\211\222Y\304?R\226\303\001\370\216\301?gN\204(wA\254\2772dR\335\207\243\255\277|>\n1\212o\271?\204\231\213\020\3353\257?P\224]\363\3105\263\277\337\224Y\242\320\207\267?o\224\262u\343r\321\277cq\333\223(\302\255\277\r\342hc\033\226\300?\317\024\265\240f\307\261\277\252-\210^\212\217\247\277Y\327\251\257\2556\274?oS\013\232\331\025\272?\267\027\206\002K\200\246?\225\030\t\205\211 \220?[:\344,\255\036\234\277@UU\200#\234\266\277\317\3157\200i\243\261\277W\2770\026\224-\267\277\211\007J%\371\207\233?\212R\245\275\320A\251?\235w\032\322\315u^\277\221e\241a)\231\247\277\314\346\254\337Ee\315?\222WF.dG\256\277\217\353\341\266\300\344\272?\341\373\211:7N\220?\0378t\327n\331\273\277~\\\022Z64\266\2779\231\271\376\365\307\274?d\361\206\350\367\243\234?\2124^T\005\354\273?\367\215\310\221\267\276\300?\212\326\251U\260\021\272\277\306\230D\225\020{\267?Gc\210\207\224\342\261?\371\037b\005X\274\270?.\224K\253\014:\307\277\326]\354\314Rb\177\277T\310@.\232-\267?\r\363\253ejn:\277r\366&\212\240\177\275\277\377r\354K\314\220\275?9\366\361u\222Z\245?\273\351\203\241\326\233\300\277\326b`U\366\336\247?v\337M\024hP\221?\306+\216(\330C\271\277z|\234A\325\347\242?\323\3622vy\345\222\277)M\030\203\"\267\242?\262\300\250\004\204\371\266\2777\213qT\261}\273?\241\263\326\375\"2\245\277\377S\301:\3010\323\277\367\005\352M\031\335\304?\324\363nZZ\305\226?\350\252Z\237v\350\311?N\254\035\3507\345\323?\356\005il\336\026\303\277\262\372\353t\261Y\274?\213\'e^F\277\254?\277\016y\004>E\243?\277\223\334\307\210Y$?d\351\303\'y\177\254?\257\240\365\270\257\261\306\277\307\260\351\214\275\206\264\277\213\256\017\354\205+\265?Pn\025 \032\360\321\277li\350\016\'\353\271\277\267\217<J4\025\274?y\335?\006Ay\226?>\266q\373\237U\246?\\\235\027\006:\033\226\2776<C\\\020_\246?(@\017\206\213\240\274?Br\240\377\003/\271\277?(L@)[\262?\322>W\342\';\313?\\\0177\230\366\341\303\277\242P,\025\0035\213?n\234ob\305\350\225\277\261\301s\310\002\225\265\277{\270\374A\014ip\277\377o\251j>%\242?\036\031t\214(\371\312?\2455Qu\2026\315\277\021\317{E\234\243\217?~-\237\036;\232\244\277\255\243\203\266\353k\264?\254\022+\306\356\201\231?e\033\026i\031\031\221\277\006\206\021\345\305\304\244\277u\036\313\206;r\256\277q\"\377\335\t-\243?\031\214\210\256\221\225\265?\372\276\240\354w\225\272?=\205Z\326\242n\241?)\326c9\347\032S\277k\337\347\223\357\004\312?\037\230\334\344\223\r?\277\244\013\300\204\204\000\260?\t\272\242\326Kv\315\277\311\373\272\323\360e\230?\005\317\210N\232S\276?X\023n\242\222l\256?\237\267]\221\243\311\304?\004\275\027\276\276\221\273\277\343\276\377\361\027E\257?]f\231\376\353;\264\2776\221\233\r\'\335\207\277\235\032x\336\221\320\241?\23071\252US\260\277\177\312\031\277\327\333\303\277\2777\222\2125\032\301?.\316\227)\336q\232?\367\206p\014\"P\223\277\177\"\344\256\017}\220?\307\271\333\275ka\252?\274\222\235I6c\266\277\234Y\007\372\252H\255?M\0224w)e\253?\201\256\n\200\"\222\255?@%\333\\l;\272\277\377zp\206w\353\320\277#\361M\316\217\321\216\277!\017+\032\355\257\306? \005\001\3272\225\275?\2114\346<\263\216\267\277\276\037z\331JB\250\277\002\217\263:\204\305\256?\014\003Z0)\251\235?\270oc`\007\347\274\277\332\322\273\027\252O\270?\210I\366\263\2212n\277Q\330\261\310\331\244\303\277p\030\332]\035\235`?\200\231\306\355\302\210\245\277!\202;\032Q\252\315?o\226\315\242\202f\261?\2657\224m\'\222\270\277\210S\230.\300\253\262?R\375\245=l\033\317\277yltB2h\252\277\226\364\025\200~\000\274?aJ\\\3371L\270?\334\210y\225m\256\226?\274\004\200V)\010\247?_)`\220\354\031\251?=\240\316\334\235\027\260?\360\372\3478\024\224\265\277\221X6Y\334\343\302\277\000\237\242\021ih\243?\245\343\013\202\210\322\232?\350\342X\'\230\276\271?\310\235\002dy\231\212?\235\034R\215Ud\321\277\257%\3442/\203y\277R\025V_\023z\277?\233\2215Y_\360\276\277\326\014\336;\244\216\237?2#\200\035z\370\270\277\275\034*\204\220O\231?}\207\327\371\n\034\306\277\375\241\245-&\254\241?4\215Rh8)\263?x\260\251\th\\\303?\006\267=XZ\007\240\277-\313D\273\310\373\303\277cG\224\303@\263\254\277\363(\373\351\014\314\237?[\321AwnW\260?\307\267\227v\037?\247?<\367E\n\353b\304?H\317Gn\030>\272?\r\214\357\0311\242\303?H\005.\341\032\003\302\277\232\304\355\220B\005\276?h\257\nhs@\250?\213\202_\312K\'\230?d\2202O\200\202w?\344\357\254E\206\254\263\277c\221?\353\rp\233\2779_B\351I$\310\277\005\251\276\371\231c\272?&\226n\327\250\265\240\2772\005\203\001\366\320\304?\300M\030q\3174\277?\357\237\372\231\232(\237?\340\0362\364\342\003\226\277i;\327\264\215\334\272?\263|\276\234\370\211\320?T\3042f\210\371\305?\330\233b\\W\253\232\277\n\342\247\343\374F|?6\274G@\250\373\245\277l?\030\305#\202\305\277\352@\242\241\2407\270?N\314\355A\237\214\212\277\370p\347HH\377\262?\373\242\330\0368,\266?7y\003\303+\206\264?\360A\233\354#,\303?\201VM\007\264\334\275?\200\017\370Q\337\211\265?\251\363\005\3708V\303?\205\002\004\204\244\374\234?\245\312\316nQ@\265?)\004\303\227\033 \322\277\211\241\342A\377L\266\277s\356\315Y\372\245\267?\2433\027\207\371,\201\277\273\311!{\212\"\316\277\267g\032\315(`\234\277\303\313\273\177b:~?kW8\324\237\267|\277/H.\321Fx\252?\003\304W\225\007\335P?\3163\026F\315\333\270\277\355\245\252\2662\003\302\277.r~\000\254\017\261\277;\371\314s#\010\271\277\362\275\204\342T\307\333\277\'\247\366\224o{\301\277q\002 \255($\267?\265\265P?*\'\260\277\377\302jS\032\n\322?\251\223R\2659\232\226\2770\307\341\027\002\347y\277\320\tgy\\\311\220\277;7\212-\215I\274\277e\315\305P\353\336\230\277F\304\365z\277\362\262\277U\252\252\233\203\336\252\277\025F,\014\274\177\264?\312Rd1\251\236\242\277\r\215r\333\350\313\321?9\231\366\263\030\306\262\277\325(2\356\013\227\206\277\266J\340wP\023\262\2778pC\0245R\206?\254\362\241\371\263\354\253?\230UB\232\311\347\267\277I\024\337\300\021x\273\277X]0c?\271\312\277=\357\234\334\221\020\213?\337w&\301\324\006\261\2779\242\026\355]\000\203\277\252TC\034\014\305\256\277\272[\344\352\273\340\300\277\200\332\024\232\332\031\234\277\007\266\272\374\244\242\253?\360\026\020\213\371J\242\277\023\212\007%\\\326\331\277^\322\355\275\3056\260?\273\325{)\361u\242?F\351<,\025\021\265?x\305K\246\326\213\262\277\225\333\272\277lA\216?\225F\215\222\340\244\255\277k3\275\034\037\351\245?\233\320\031d\205\246\217?\272\234Y@\345\302\246?\274j\200\033,\315\260\277l1\202[Sg\307?\240\207\320fHf\255?\242\256IW\307\357\260?%\306\325\177\023\275\305? \355_W\245\335\277\277O\025\334\'\226\242\263\277D;\226\244M\003\270?\237\333\377\010L\325\271?\217\010\265\225\351\212\244\277\327\353k\2209\346\275\277,&e<\266^\211\277\263\000\341\353d\231\263\277A\322\373\375\026l\256?\242}\250t\345\265\235?\274\023\021\340_\360\270?oQ\315\346w\247\264\277=\231-\n\267?\272?7\323\305\217\022\177\254?\177\333\312{\302\277\311\277\275\226\227\304\362\252\240\277\370U\356_\363E\250\277\335\036G0\2465\207?k\354\351\tam\254\277u\220v\257\313\250\217\277\017\0016k,\264\213\277\220F\257\304ft\325?5\254\014\204\302z\261?\352|\273\341\224y\305?\306\225c\3656\027\304?\364M\324\222<\357\263\277\025H\366~\020O\314\277i\367\257n\275%\325\277O\256\204Md\363\307?(\0231\256;U\266?\302S\354`\3778\301\277,\237\271\217\361\342\261\277\323\000L\216W\005\303?\230\034G\252\346\235\235?\247M\223HI`d?\350\301\024\363#y\327?D\223Cj!\004\262\277\031\306\265\013\210\261\252?\202\330|\341\221\260\256?\352T\245bEo\254?}.Ihx\377\205?G\331\304\272)I\322?\023/I\327(\003\256\277z\024\352;|16?\2357([\310\324\315\277[\300\346/\2630\257\277\231\366\'d\305\002\310\277\211\246\'i\337RR?v+n/\316\243X\277\031\213\375s\261\357\272?\204\256p\373[)\242\277Q_\241\332\245c\267\277\347\257\321p\350\247\300\277\255\267\271\300HLa??pK\024sI\226\277\251\354\346\274\237\366\311?\230\223s\332\340\351\210\277; \036\021\352\021\242?j\215;4\377\326\261?k\030\035\036\302*\245\277\321\373t\305\266)\215?,2\033_\222\005\271?L\236DB~\257\216\277\341\237\234\342N\337\273?\216\314a\214\213\\\235?z~\347\201\020\315\260\277\022cS\323\342\273\311?\177\374\361\025\304\340\215?\324o\0270.3\257\277\370\252\360\250\227\013\276\277\367\220S\316\021t\302?yG\022@s!\236?\374\375\244\3125\'\300\277\215\036\305\375M\362\266\277\321? \033\320\327\212\2770\231\251v\031i\261\277\355I\356\223\262q\252?_\2361\246\216\200\251\277S\363G\357\'\005\246?\267#\204$\211\333\224\277\347p\033wA\337\304\277H\026\230Q\217\017\242\277\n\312i^\375j\304?M.A\344>\006\242?\327\270\265\364\325\023\321\277\342A\341\243N\200\232\277\034\001!y\002q\247?V\244\253\305\003\340\321\277yAM\0273[\273?d\204\236fw\304\300\2774\242u\261\306\237\242?\013Kg\304{\360\251?l\251\334\024\323U\226?\236Xt+h\233\276\277\306:\322\014`\340\262\2774\220.b\272\000\264\277\217\312Z\276\257L\262?\207\220S\020\320g\312?\262\230\036\237\010H\245?\006M\027`\377\352\271\277[\2450t3\026\263\277\247\374[+\351\251\310\277N\350\2279\003\343\261?\341\345\331\353\024k\254\277\222`U\024\370\025\243?tF*\2267\360\276\277W>\363\256\027\240\266\277\220=Rg\316,\260\277\276`xmj\335\223?P\250N\247\2468\202?L\263i\240\216\345\270?\346e\242\275ba\256\277[\210JY/\005\270\277\'\262d\351\030T\216?r\026D\025u\341\265\277\201\256\371Cx\250\226\277\220\216\0039\023k\250\277f\262\261\227.\211\225\277F\270\300\371\325q\263\277\203\206\277B\201\n\304\277\021\005P>aT\271?)\355P\346\370\246\207\277V\020\356!\363\225\243?\003\225\337m\262xY\277\216w3T\020\350\227?\267\237\354\t@\254\231?\350\031f\240if\277?Al~\276\347h\250?\315M\013L\031\'\315\277\305\t\222\212\2220\274\277w\331E\336\336\254\241?z\203\330\204\000\202\265\277w\017\272\303V\241\272\277m\226D\346\3727Y?,\3566\354&\010\240\277\010\216\337\252\245\\\252?G\3038\216\224\364\224?\367\221#\307\307\275\274\277\234\251\212\366~\240\267?\250\364\251Hi\213\263?\030\221\375\013\374\234\310?]\213#\314>o\257?&,\232\222\2617\225?\273huH(nk\277\335\202\337(\3040\264\277\314\367e\264\\ \301?!\025\321\016\331U\242\277\201\025\335\0215K\304?i\371\001\256u;\260\277,\201`\250x\016\250?\316D\224--\346\216\277^\336\r%MS\300\277H\3479\366\265\301\250?\265\317w\274\3059\267\277WhGr\372\255\244?\372\217U\224\370\361\276?B\310I\\\210\233\215\277\201\332\2748--\263\277y\006\362\227\216\230\234\277@:\005`\252\355\246?B\t\0238Wa\264?\334\021]\273\241\243\261?f\212\264O\311u\234?\237\243\203M;\361\262\277\224d\273iz\351\242?K\374%\365v\334\220\277\372\037\345U\222G\276\277\376\350d\024\177y\250\277O+\224J[\361\265\277\370G|\275\350\352\227?\220\335y\010\363\321\301\277g}\033\006bt\242\277E\016\032\243\357\030\300?\312\037\235\301U\034\257\277?\324\033\325z\253\260?\251\375\302M\212\317\312\277\220\340jR\241\314\241?\364)tZ\t\234\302?eI\270\373\204\316\305?=\202\030\263\016L\267?\373\2757\025\204\345\260?\202\346\330\376j\005\320\277?\377\023\007/\245r\277\332x\307s|\264q\277\266\251\364\254\204\242\257?k\036K\326\361D\247?\"0L \310\346\277\277e\257\020b\367vn?\231\375v?\026T\205\277g\226\203HL\306\254?\266\371.?\027\202l\277*;;\020\311\350\212\277\230:\230\331_\030\311?\356\271\032[\0103\240?\r*B\337\354a\267\277\243\2142N-f\257\277{\3711\312\206\235\301?QI\367l\212\214\234\277/\023\006\256.#\300?s\305\271\306\234\233\241\277p\263w\363\336|\241\277\335\266O\330\306\003\236\277z\353\375G\016\214\232\277\200p\274\323l{\304?\017\317\262\241\364m\260?\002\257\252{\004\227\275?X\017\220)\320\033\302?\026QU|\214\005\261?\361\314\362\214\314\235y?0\237\263\300\024\"\234?\327\260L+/\035\323?\247\001\206\364hZ\274\2771M\213\320\306\372\261?\342\312\024V\032v\304\277\340\226\r\246d\357\202?\367U\313(\300l\230\277\263\330\332\206\345\377\243?\344W-F\025\246\221\277\200\036\241g\234f\244\277\307$\026w{\030\264?<\250jW\3616\231\277\250\3414\315\"\276\272\277\256\336R\373\317\367\305?\304k@U\2543\270?\316\236W\001\372W\212\277\246\026\005\022\2341\215?h\275F\034\307\255\316\277g\357q\312\021\252b\277OV1\274\320\276}?\034\260\375b\335:\221?\023\273*!2\266\270?\013\264\214\234\251Mg\277\373\016\354\340\217\321\217?\'\034\377e\027\027\267\277CX\021kU\013\322\277\230U\200\001\254\267\301?\337\337|\275\226\212\254\277\004\327$\265FS\311?\216\212\305?\305\222\251?D/\335\264g\033\233\277xK\371{\272\205\267\277\231\252O^\366p\240?\235\177\025\360\375\250\200\277h\261*\016]\231\245\277\207\356\255\206b\014\250?\004:\363\266\351x\321?\243\340D\341LQ\272?\025V\n\024h\325\262\277\243\'E\310\017]\303?2`_\014\236H\274\277\230tS\262\3504\243\277S*\002\202\253K\223?[.|\023\010\021\250\277\310\220\005\204\214\274\274\277%=\013Jz\013\241\277\260\217\353\356o\234\274?mR\256\233\nT\274\277\267\314\376h\214~\227\277\3306\363\300\345\234\225\277t\300P\273\305\017\325?\023\263\210\312\221\000\265\277rQ\334\027sy\217?)\r\354/\225M\267\277QG\367P\312=\306?\216\301\235\357\242p\265?\257\t\342\277\214\354\262\277),)\237<g\251?\232-}\017\256\233\244\277\303\306\"\363/\237\254\277\227~\312\200s \262\277\370\3119x\033\242\251\277\333\300k\242i\367\217?>\\2X8X\275\277\310\371^\270\241s\304?\353\327.\337\0209\251?\231\320<=\006o0?\034P\354M\217\376\301\277\371\270\301\230\032\320\267?\3531|\236\031n\230\277\003\227\020\363\202Z\300?\204\2751\321\032\243\320?\306\213$vNc\253?\3515\007Tm~s?\023G\212\031\216V\274\277b\003t\tu\022\320\277<\034q#\210{\260\277\253\313\303En\037\246\277\252\010-\265\024\'\277?\242\352\202\307\221+\254\277\212\350m\304U\263\266\277\262\324b MM\312?\304\374\301\310\323\214\226?\311~\2669\242\362\262\277\373\345\032\036\240\205\256\277\213s\311_\316\014\260?\016\312\311\037sz\255\277\263\205\316\211\222\212\301\277%\236\210\n\351/\266\277\347A\235\3416\243n\277\202\332&\214St\274?j\031K\372z<\240\277<\304[\231\262z\255\277\265\003v~\243\356\201\277`.)\000\273\244\221\277\\\350\366\227Y\212q?$?\323\301\322\236\244\277/s\016t\346\037\276?D\304{`;\250\257?\375\2467\346\212\000\272\277\230p\376j\036bv\277nD\314\231\271\256\306\277\310#\036`\"$\217?[sD\273\230.u\277\211\034\332d\233a\252\277\246\207\027I\254f\302?\376\260C\212\255-\321\277M\246P\215\017\370\214?wE\320\217\364\303\263\277u\247\357\310\203\332\246\277\235\252[\340\305\030u?\202\247z\373\256\203\266\277\325\026\335P\266.\265\277|\264\334r\240ow?J\351\346\343\370\201\237?\nKPj/[w?\344\311\263\322\323\303\270?-\031fA3\300\252?\241w\356\021\365w\222?_\"\014\240\237\\\257?b\241\323\207d\007\265\277\267\"=\224\361\316\303?\r\007\233i\233=\254?\356\267\013\372/\373\310?\214\320\024C\343\365\224?\227I\377\373\002\265\261\277vU\rr#\226\303\277\372\341O\376\344!\257?(\250\314\332\305\356\240?\230\001\207\214i\323\267\277;\345u6#\216\270?J*\235\0165\004\252\277x\355\344,#\016\305\2778\325\200t\321\203\230\277\217\\\026\365\377\305\274\277\214\253U\372m\253\270\277_\223\223F\032\326\224?\257C\273LQ\201\221?W8\244j4\324\263?$\344v\324h\326\216?\221\200O\223\204\235\243\277\375\357~2\247e\304\277\3239B\350\031\354\277\277\376\261f\032\000}\263?Ss\367\307Pz\274\277\230\274<_\223d\256?2\232\273\341\030:\260?w\241L\035H\256\265\277\206q\247\235\014\256\310\277\3545\305\211\334#\255\277\237\216<~t:\260\277+X\231\305\213\024\313\277\"\321\313\032K\364\224\277\014\020\240>\3055\301?*%\271\000\213E\214\277Wj\357\264\014\020\314?F\200>M\210\375\254?\246X\020\333\3707\227\277\304\253\202\212Uw\267\277\025Mn<e/\265?X\265XN\377H\205?c\3258\260\235n\261?\030\023J\212u@\276\277\rCc\337g8\272\277\243F\334\246\323\271\243\277\242\037\314\335\201\030y\277+0\353}\n\373\275?\205(\221eK|\261\277\250/\213\016\004\260\217?3lc\302\207\375\250\277?\255|\237\330\300\222\277K\374\276\026\022oj?~\r\202\274\t6\302\277\005\343\030\n\014O\244?\343?\373\253\342\376\252?i\037\365\245zQ\243?\362A\302\211\214\377\246\277\325\246g~\332\373\231\277\267C\347%\307D\302?\311\341Q\307\215(\315?\257\'\341\271\326\030\251?v\366Z\177WS}?\\f\316\244\237\361\266\277\034-EB\344\335\246\277\236\002\302\223Y\311\211\277\347\341\361R/\274\266?\225?\230\366/\377\235\277\031N\002\207\256}\215\277\375\t\271\256\034\365\254?\320\205kzs\036\246\277\265\276\r\263\373\267\245?\n>\020\237z!\260\277\261\351\300\212MB\276\277\2205d\333\213Y\251\277qC\310\205\003\r\204?%\322\355O\234\300\301?\007\276\311\251\010\201\323\277\254yQ@dj\221?~\027=\200\355\207\256\277VH:\345@\331\237?\010\317\006\204U&\205?\274\203zJ\233\201\255\277W\303%\342=q\262?\355o\222\276\t;\262?\2205+\3600\037\273\277\347\225\344\037B#\201\277\235\264\231\003F\336\263?\000AD6D\353\317\277=y\223\215\340\nd\277\037~nhb}\245\277\013\277\210\023\357\235\245\277\272\001\235I\331\330\272?\224]\003\315\312Y\223\2774!\231\2453\375\265\277xi\202\202}\322\303?\314bDLA\264\240?=\310AK\233\241\250?\266\017\026M\005!\252\277\277\210\014\220\355`\270?:\357\320%2\336\262?R\372\302[D\347\263\277\341\022\257uO\226\264?.\246v\321P%\234\2772\222U\343\314\324\223?+\262_{\030\007\252\277\341\014^W\345\326\303\277\347f\225\007xl\260\277~\305\355\266s\206\316\2774\204\340\331i)\233\277(m\335\264\006=t\277\235#D\026\270\356\263?wj\013$%\322\232?z\200\240\255SF\271?E/\317\210\213|\215?\266(u5K\363\261\277,\003Tb\2237\262?\230\r\030\304Hw\261\277\311fj\034\362\212\242?\302K\354\305-KV?\'\2063\322\367\026\240?\232\0137V0\022\214\277\230z\250\230\213\213\262\277\265\211\302\253#p\312\277U\365\027\262\241u\275\277\336\343Lb\302\205p?\031\')us\010\261?\260s\301I\340)\300?\\\250\032\310U~\223\277\317\304T\300Z\316\260?r\303\342\236\3217H?\203s\005\300\235\005\260\2773c+):\007\242\277\005\314\362\302w9\222?C\350\202\324\262\025\201?\016\235$\324\244\272\261\277\322}\014J?\314\302?`\321\203\203\t\204\310?2\244\243\370\324+\250\277\347\342\320a\364\341\221\277\230\023\305\033\321\337\264?$Y\263g~\363\314?\265\251\010f\231`\251?\200M\234\267h\033\250?\260{V1(\346\302? _g\024\005\216\254\277\272B\373o\364\274\251\277\217\016\n\033k\376\300\277\200+d\027\024\260\262?\354~\261\267\340\361=\277c\036\023yA\200\236\2770\244{\202\250\r\273\277\347.\014\r{\232\253\277\013\204\313m\301\250\311?\004\366\342k\311\005\200?\313xY.\346n\263\277hp\277\035p\000\262?\335\201\367\210\211\312\250?\342\370i\243\331T\256?\356\234\t\023\360\246\255?9\220\315tM.\231\277jpNA\372k\306?\025\331\206\347kW\272\277U\314\337\250\225 \261\277w\305m\213h\201\260?\374\312\307\2049\242w?|\357\310\252\177\373\272\277\263q\010\245\266\236\244?\214\252\234\000\223\322u?m\274\036\216\354\376~\277 \324\270%\037\340\257\277\265\363\245T\357\004\257\277V\375\345\223I\305\266?9X\324\372\306Y\277\277M\322gpya\237?\313\272t\365\366J\322\277\027j\366T\224\250\202\277\354U\255\307\217\r\235\277\316\232\305\026\nw\303?\331\026\204Q\241\031\251?TR(5\354\353\277?\210b\354\320\266J\256\277c\334\332\377\340\231\266?t/\033\267\023R\275\277\021\013!\314\221W\275?\345\254\311\212\024\013\260?\304\362\363\220B%\265\277P`\322r\263\242\277? yX\373xn\314\277S\177Q\325>;\310?\253^y\006\203\263\300\277\342GX\032|\306\214?/9\026)5\001\266\277Jc\210\203-\303\220\277[\3137\207K7\274?H\207L\306\005\024\226\277\263\335\211\200\354\203\214?\273!=\366\346r\257\277\202y\207\355\347^\250\277\246\227\\\362A\023\300\277EX\036\036<\371\251\277\303\356\306\321\010\215]\277\221\365\331\342+\262\262?H\220\016t@f\220\277,%\316\346\017\345\260?\027\217\252J\330\271\264\277\221\350a\300\"-\276?3\226\347B:\035\321\277U\2540y[\345\252\277&[\226}w\304\271\277\266$\275\225\225\001k?\300\n\364\327\335\304\306?an\030\'\351f\260?le\020\324\2000\222?\232e\024j\307\263\302\277g~\322\326\240J\264\277e\t\205-S\311\250\277FG\227\353ch\272?\224\265_y(R\306?.=\236\220\213\217\177?\231\314\376\273\377N\221\277\261\275\220\273\3247\262?\351\216p\0211\035\263?\r\271.K\020\232\237?x.\221\2332k\266?\367\335g\252\362\330\224?c\262{\006VV\254\277\247\177\261C[q\267\277\261O\000\241S\304\246?\033aQ\334\334\006\270?\"0]Npno?\323\376\304\264\336\022\242?\367>.\177\203z\305\277\237r\214\276\2416\243?\360,{\340\371\351\231\277\211%\005t\017\024\305\277\251\004L\375\326!\233?\275\".\231W\030\262?c\270=\355\256F\253?!\241d\335\217\231\241\277u]d\261\300\237\267\277\261@`\270\027\007\247\277\360S6\233z\243\217?\ni\215o\232\215\254\277\006s{\n\3034\272?\254\315\231\010N\"\276\277\375\\>t\033\316\311?F\260\3313\355+\221\277\306\352\026\357\251\234\260\277Ej\263\242@\204\245\277\273\210\332#\203\010\222?U;\362\002\327\t\271\277\026\343\036\225T\237\263?\315x\237\232\201\341\311\277\372\327\254\370\344\026\256?\2749\341\231\323\264\257?\007\256\321)!\027\262?\022\265\331\343\032\033\274\277\025\355%]\346\374\261\277s\030\303\243\304\014\301?\017\002\270\346_\211\271\277\227D\306\263\312j\264\277,4G\264u\247\256\277H\230\252\334=B\253?\314\345\001\024\023i\230?x\360\374\323/p\212?\003W,]\313\270\270\277w\024!e\3327\262?v)\217\323\326\352\302\277%4Vo\220\342\245\277U\323\222F\211\254\317?\361W\344\214\324K\232?y\224\216^c\351\270?,c\013\271\3127\271\277\366\263\034\356bv\275\277\000L\272\t\277\315\227\277\353\302\\\'8\226\271?1\342\004\r1b\323\277 \226\270<\"\231\217?Y Q\007\375\235\222\277\326\\\014\'\033\202\272?\341f\374\236\275\202j\2776\246%\214\355<J\277\354\001\266 \371\334\276\2772\3529\306\225~\306?\315\004;\007\005I{\277\262\225\326\276\225\005\273\277\312\033\301\327\007\307\260?\000\325\026C\252\014\277\277SC\3420\324\233\243\277\323\304Z&\373\357\303?\274~E\307\364-y\277\'\276\341\306\3647\320\277\325\315\260\224{\260\233\277\310\0144\271Q#\213\277\270\177\352%\002\266\242\277\370\206g\204lI\250\277\027\024\272X\334h\251\277]\267`\037(\233\321\277\300O1\000%\037\260\277\327l\204\320F\357\260?(\001?\261t\266\322?\272\027F\331\372\234S\277\274\325\374G\333f\271\277\227(\350/\347\303\262\277C\307L@$\336\302\277Y\300x\307,\355\301?\364e\010\377)\033.?\026\373\360\271\215\323j?\225\375\327\301`|\320?\253\362\377\221\367E\251?\201r{\344\232\225\300?\227@\357\243s\014\322\277E\344\365\306\376S\303?D\224\360\202YJ\261\277\256\363\265\0322\257\257\277\344\347\235\2553\306\233\277\215\311\2412\206h\212\277\243O\244\275\355h\244?\221\260\367I\220\363\313?\276\030\252\246U\241d?\271\224\032\377\334r\203\277+\214\332\366\200b\273?\032\000\240\211a\256Y?\010\303\246*\224E\253\277L\207\256Y\225\231\220?-\017\371?\361\013z\277>Go\300\003\377\262\277\245\016#\253\037R\303\277\025\300\377\210\237\222\276?N\305\321\r&\361\212?$\267\246K\255\035\242?9\017\323\256\265\243\272?\232\3452\021\361\014\260\277A\304f\227\213\367w\277q\247\014\356\031\204\300?~\324\307\351\215&z?\025\224r\230\341\307\321\277\310\0329s\231\273\244\277Ox\357\216\030\267\237?F\342\244\345T\271\267?oN\3519\223\\\227\277\000\003%\036\0065\273?\342_=YaN\300\277\214l-\210\016\214\320?\032OE\260\320\222\272\277\216\322\210\241\371q\302?\007$\207n\354<\307?\211\017\262\rDa\263\277\016\253\037\230;\307\212\277U\366C\n\273\332\246?A\224_\364\274\360\233?\240\254A\313\261\360\256\277\323\233T\357\004\216\251?\251~\233\241\377\205\310?z\323\221\3223\335\263?\266K\245\014\367\033\270\277\301zr\265ki\265?\177\210\220\214\371\376\207\277\307\031_l\253\200\266?\335\'t\037\032\374\247\277\311\350\375?\001\275\243\277!:\234\252\207\300\241?\020\255\370\266?\206\244?\222b\267u.\022\271?\016\376<_H\231\250?4C\242\035\363T\262\277V\351\232Z\211\364\244\277:\254Y2\322\201\243?:\374\026\250*w\272?\236X<@\000a\270\277\310VH \356\014\302?<\346\353\177\206/\312\277\"\033\373?^\263\221\277\200\360\311\262\240p\304?\335\0071\257\227\216\261?-\260\314\271S\253\324\277\214\372\006\313\217C\232\277\030\326\232\3520\305\240?7\274\213\225\306A\257?fRis\201,\242?\306\030\356\2501\304\263\277`\342\027\024\314\307\322\277l`\2420\261\030\271??<\341\300\203\261R?\306\332l\034\324\244\272\277\310@\364C\034\266\274?\316\336\305\363+\001\261\277\276\016At\036$\246?\026c(\025\n\373\260\2770\322<C\222\352\234?\014\021\235-k\343\254?\220\017\010\340\272z\274\277\365\324\323\252\255\021\263\277a\'m\310<o\235\277\327\027\267\315\236%\210?\236\306\267\266\214&\302?\005\274\344\361f\212\247\277^H\000\315^\305\264?$\003\237\030\2769\237\277\037\024\003lgi\276\277q\255\354\235\016*\247?\250\205Cm\333\274\225\277\247\202t\275,Q\247\277\357@\020\247\220<\245\277\377hT\020l\307\211?%\006\222|\025d\230?\334\224\343g\016*\310\277\273\223<Q\262t\274\277\255\333\211I\374\350\226?!\231\003!\327w\306\277\227\231\355\225\274\216\270\277\270\014v\336\324\031\224?0\321\2524v\206m?h_H\314\232\217\310?\356\216\210pa\260\201\277;\212\177\262\264\242\262\277\272\265\251\277O\304\302?\242\2204\316Fd\302?\200\255\244\261}q\243\277=v\256\377\261*\206?+\342`\177\010v\331\277g\203<\347y6\253\277\343!\024|\236\330\216?\317\256\341-\374\013\201\277\021\\\350I\332v\247?\000\215Xe|\026\261?\241C\230u\006\006\227\277t\236a\005\r\223\313?\347f2\231\352\234\264\277\350\226\261\3157\325\264?\256\023\355\031\2423\253\277\216\275\203G\232\210\260\277\225<\341\335\2712\270\277\361\265a\303H\353\275?(i\350\010L\362\301\277\273k\324q\253\335\263\277WB^\305\016\200\246\277\222?\030\322=\216\260?\213$\323\r\342\026\236?\250R_\271\254.o?\251\304\230W=(\312\2772\022\273\346\335\204\300?.\006N\032\'\232\271\277\274R\360\306\031@\270?\357\371J\204%\255\030\277uNL\004\367\322\260?\263\307\316\276\007s\200\277\301\326X\374}#\235\277._\266%\034\334\316\277#7\356\261\351_p\277\0239\177\233\314\311\272\277\306\336?a\340n\254\277\304A\203lB\265\276\277\t\266\004\214G\301\276?zRs\233A\'\322?\374\367IB\265\344Q?\261\374+\367E\262\206?\255F\217\312D\210\215?~\235\002\353\031C\222?<\270\334A/k\244?Gy\201\203\036\377\245\277L)[<\323\302U\277G>Y\336!%\310\277\264\222\327\0363\r\266\277*\257\024c\363\351\320?\224N\332>\362\243\267\2774\243\'\210I\225\263\277\315P\032\211\313\310{\277\247\202\235\031\364\327\242\277\224\315j\274\3628\222\277\340Q \014\206\327\215\277\'=\3638\325\272\320?\272\010t\\\206\"\311?\371\356\231\3072\"\223\277\001\320\205N}O\255?\000\017\376\025h\327\265\277G\265\t\266\2216h?\343\312\260\372\245\304\272?\356#\014\002\264\006\250?\255M@Y\243\206\322\277\253#\324 \303m\260?m\024\005\375\033\200\241\277\350+\201\277H\207\266?(\243\314\346v\356\276?\333\2141R4)\267?\336\347\353\220\266v\301?gC[\222\224d\273?Q\304\241\210\n|\264?\243\316L~\271\034\302\277\264\212\304\253\274\256\242?F\311F\177XY\305?\204\307\262\\\205S\233\277J\243\001+H\326\261\277\244s\340+\367\370\272\277q\0133\322\006\314\260?\334\263t\364\317\324\301?\022\265\214dp\316\225\277\364z\336\325\244\303\252?\'\030\315\272n\"\260?Z\276\236\000\365_\266?\3732L\352A\214\317\277,\235M3\301h\265\277$\030\264Q\336~\250\277\347W\277-\213\242\237\277\033\312\3502\235\023\320?F\23147\347\025\251?\272u\311\376\216\256\214?\342/\374\207\254Q\300\277\220\274\307\217\326\327\222?\310\274A\346\2259\242?\357#+\032\202\006\245?:\023l\2721\333\255?#\005B\247=-\247\277~W\177\350\253\263\220?\260\231\223\217\230<\255\277\037\213\264\312>\336\304?Mn\376\213J9\263?\367{~M51\302?\353\247D\243\235\306\326\277\252\363\225\251\320\n\237\277\373\024\245t\372\204\254?\333\376\2555\366Xs\277\233\267\200+\217+\210?\265\263(/L\027\240?\213q\236\221\014S\242?\233\000\200\016\\\220\243\277\271(\004}$\026s\277\230Y\235/\356\014\243?\021m\030\307\213\340\275\277=\037\215\307\357\201\216?\036\272\207\327\344\036\265\277\244\272\275\315\007\206\220?\3449f\264&\256\315\277\360\2315\316\361\007\272?\371\375\366\211Q\177s?n\007\271ib\005\272?\225\323\006H{k\205\277# \017[\240\341\251\277\202\"b$\311\020\263?\302$\352>\343V\307\277\362\027\376\n\033)\242?\351\212q\225)Z\271\277ct.\006k;\303\277E\345\030\254F\020\272?\312E\014\323\263f\213\277\006G\333\030\274\320\260\277\341\346\025\013GS\245?[W\247\013?v\273\277;\016\217\010\227I\273\277\016UCx,\262\310\277\024\222\330\351\313:\307?@ju\227\322T\271?\022K\013\0348\371\300\277\262\0045\326\203\311\220\277jb\251\342R\353\241\277\353\243\262\250\374]\266?\255|\210g=9\230\277**2\313\234\317\300\277\311\312\205\222\206v\272?A\\\361\024\003\005\217?\271y\260[\211\266\255?\212EVd5\322\305?\000\257{\370W.\251?nB\255\304\221\265\266?@`Q\202g\260w?T\246\357\010\304\034p\277\016\342\202;\275\377\301\277\31517\273L\200\251?I\274yN\3727}?\252\272\333\'\342\215\242\277\254X\234\312ly\270\277\201\300ZL\354\320\273\277*\355,w@Ai?`\202\371\033J\323\243\277cs>\355\253\311\310\277\010\263}sBm\266\277\272N\r\335/2\307?\200\251\343!\274\311\312?\313\215w#|K\231\277\342\213aV4\326\260\277jBEYj\005\302\277\032Pb1\201\226\304?\247M7\000\014W\210\277\034\305\376qc\317p\277S\312\360\031\010\370\300\277\262\206>#~\277\276?\370{\264\205\335\261\263\277\347\306G\r\202C\250?\222A\354\346\317\006\306\277\276\216$\253?.\210?1\242\367^\257Z\305\277\372\016\313\027M\221\272\277\325h\331\266\2762\220?\024e\354\315\267E\271?\327\370&3\2153\277\277\257%\314\330g\007\307\277\027\350\371\276 n\230\277\273\340\277\240A4\315\277j\240\361\233\234\264\300?\360*x\204\361:\270\277\274\303ms\361\306|\277\370\222\324\037\310\255\321?`T\017\244\225\270\256?\321s\301\337.\222\262?\026<[\301\243\276\261\277\306\037\326\272\3452\303\277\205\267bD\203~\225?\013\224T\214g_\245?\023\013\246\326Mo\302?\316k\223\324\032>\251?C\333\346\351Q\025\264\277\257K\n\203\\A\303\277c,\345\363\326I\267?1\017\200a\246\346v?O\260\236A\357P\241?5\243\321+(\255\302?}\246\235%\002\327\211\277\314\373\321z\207\206\256?!\033nt\242\326\242?\226\005\307\272V&\276?\220[\261|\247\375\263\277\352\234N:\'\036\307\277~\221\230\346l\r\322?\324\200%`C\341\320\277\200~\263\322\256\250\272?\"]\215Cv.\220?\010\372\275\177\003\344\304?\342\200\244*,,\265?\356\001\305\220\257\223\256\277\273\322X\366t\331\316\277\360c\313\352\356_\263?\265\275{\032\304\334\272\277\346\027\036\353\320\033\301\277\323xC\3624\334\320?\234\004\026[,G\272\277R\214\224\032\010O\300?\337M`)\361\373\273\277{~\325\226A\014\305?\233\316\357PxV\263\277\266\335\n\211J&\321\277\256\226\3528\246?\303?}g\273]2\264\314?\372\376k}\2037K\277@\201\213\321\323\025S\277r\325Y\363\016\037\275?\025^Q-~\271\267\277\031V\214\372\227\000\312?\002\027\251\267\005\205\266?\022J\276\272M\'t\277\035f\016\004\203c\232?\355\225T_)D\317\277y%\2258;\231\264\2774\242\377\001[*\202\277&y\364\323\256K\253\277\213\215qm\331\345\301\277.\363\357\000\336\033\241?\361\3632\263\342{\301?\003\252ZG\275O\233\277\'xf}\3141\273\277\2221\234\331\002\010\302\277\312^\230\006\252a\261\277i\340\203:\320k\325?\301%\367\362\324\215\230?\256-\t2\267\334\237\277=e20\326y\307\277\365\016Aq\231\230\317\277E+s\367\370\254\271?\246\372\331~\204\325\266?\"\224\256I\343\333\306?[0\320\337\026@|\277\315\335\204bl\326\272?\210\241\216c\002\216\200?W\316\273\203\361\335\237\277L\347:\314h \273\277\321s+\030\326\n\275\277\256\240\\R\324\347\321?\330\345\277\204\267\364\300\277\n\262h\323!W\262?\222\257\223%\345R\254?\001C\010\250\260Z\265?\241y)\364\357P\262?\024\3027\251\343>\205\277\241^\363\217*\320r\277\273W\241\310\336P\264?\250\013m \260\243\271\277\342\266\200n\004\'\260?\3637\327\360[1\222?\334\213\211I4\035\234?7\247!\302R\006\310\277\004\222\004\352\003\345\301?\317\241f~\261%\263? \323\010\0265\031\311\277\350*\333Z\215\236\310?\304Q\0333\266\356\261\277\223\203@\003\3324\261?\254\344\356\000x\205\245\277\217\213\367\361\225\333\203?\275\021q\231$>\202\2772\226\233\r\336\346\271\277Q\007\0349\213t\242?E*\307\367\002\254Z?_C\253\377%\024s?\264\2320\276\276\265\257\277\001\346\377\307\243\032n\277~\347\016\350R*\270?\260\254\370\024u1\305\277\343:\n\213TQ\307\277N\002\n\335\321\037\260?\320]&/<t\252?P\355J_\034\337\257?\332|\240\004\211\277\337?\216\270\213K\240N]?\347\226\r\274\200\253\263\277O;\301\300\320N\223?+ \352\342W1\264\2778\350\330q\310>\227?qC\213\254\351\311\271\277\204\231K}\246\262\301?\204-$\333,\257\214\277\032\225\2161\377\212\305\277\r+\275\'x\315\321?N\201Y\223n\267\305?\211\337\037\016\326\027\265?\002\320\352l\344\261\263\277|H\324\304\224R\245\277\223??\267OU\312\277\n(8\035\026 \223\277-\213\"\275\031\027\251\277\262k.9\025\260\304?\206\250,\314\023\212\257\277\202\371Le\254\\\242?\245\266\373\351X\343\323\277\247\267F\320\325V\254?\200\372\332v\206\272\204?;\330\036\203\340\027\273?\375\022\356\254\0132\277?G\362\317Ls\255\213?H\376\373n\377\267\266?dC\024\265\3013\302\277\250U\255\025\327\372\206\277\013\014L\030n\231\247?\242\245rO\226&\206??\354;<\337;\303?\252\267\363\230\031%\242\2775E\3143\314\332\263\277\352\353\266&iB\270?\033\220,Z\300\\\222\277\213\340\3715\225K\303\277\200\216\356>\207\t\265\277\337\020\267\360\275\364\304?\347<AP\315p\272?\301\313\360L\005G\265\277[\204\t\352\302\240\252\277`%w\210yG\260?\335\342]\272\243\250\240?\244\005\023@\262\027\246?\375\346\273\324\'\220\300\277\304\336\247\020\257\262\266?\272\215\216\253\204\241\260?2]\314\213wg\267?X\367l\0302\332\272\277\220N\257\374\244\265\235\277\361\311_\244\273}v?h\330\002\305\205\327\276?\211F\363\322\345C\220?\327\220J\215w\353\260?\3023\347\"\242n\301?k\233\253}@\027\303?\335\255\353;\263;\276\277\007\336w\371i;\265?g\301\000\275b\220\300\277c=\355\366\354#\262?\031\337\225\220\231\322\266\277\361&)\353\354_\263\277Z0\0168\377\t\247?_\010s\214\221\246\300?\\\3668\0136?\253?\315\245\215\252\202\327\302\277\237)\036Z\014d\200\277\235\006\324\322\370\323\220\277\261\356\321\202\240\241\265\277\277E6\343\247\251\231\277\263\367}ky\354\275\277\216yJw#\305\217?\2479>p\3410\313\277\322f\233`\251\254{?\355y\320*\304Z\247?\264\000\333\342\324c\256?\'9-Erd\261?L\\\r\371?\244\266?\214\256K\271\037y\271\277\030\343\2014@\023|\277\202wPRMe\244?\010\343\373\355>\310\261\277\027\326N\031\301\222\234?\337>\3630\203\030\261\277RU\265\3431\247\266\277\333\246\025\346\354\310\273\277\266P\201\307\226\027\261?\232a\001\351\027\202\261?\314B\000\321\'\352\303\277\3262>\241k\267\271?Z\243A\2160\330\302?\"\354S=o\220\263?R\275Z\220D\352\273\277\016\330i\322\032\231\245?xu?\364/\032\200?$\240><\350|\234\277\006\016H\360=\307\301\277H\177\276\217\262\240\245\27791\305\'p\'\213?\322d\227\345eV\261?;\340:\255\332\313\230?\324=x\305\010\367\250\277\301`~\275\034\251\263\277P\335F\002X\376v?\336\360\353M\251\331\277?\3355q\263%\366\265\277\3471\307\031*\025\312?\351+>\300Si\264\277x\377\370\365\270\203\244\277e\345j\214.\023\275\277\221}\320\036o\345\262\277xs\000x\220\031\314?J\274{E\277+\266\277$\010\313\356\363p\277?\352\317\010\201\001\330\305?\303\"\303\311\035Z\262?Ts\220\347\350\013\257\277\031R\216\"\007\353\275\277\206=\313+\365\313\254?\2277\031\276\023\024\263?+\2445b\242\372\301\277\261\301\002\000\270\001\254\277\310\250}\241\261@\254\277\324{\214\213\025c_\277>o\261\3403\367\252\277\324\2226\230\341E\255?M\363q\033\211\353\243\277\034\233\362b \225\251\277\333\311\302\253\237\270\316?_\275\026=\336]\216\277\027\364\034\372\277\256\260?\256\035\235R\213Yn\277/\0175\363\266\204\216?\311\271\304OCQ\273\277!\033+\tU\311\237?\034|\032F\351\234\322\277^\364\373\204R\370\265?\364\026\212\033y\353\272\277\314\034\221\232Cu\202\277D\n\230\\\327\302\273?\343\neD`V\262\277\330\200\263Y4-\205?\250\366Fi\3360\307?\345\361\030\324\220S\300\277\366Tm\227\na\273\277,\323F\341*\204\320?\0012`[\2656\262?\274\242Wl\"\371\242\277\375f\017|{\374\300\277\\6\235\361\274y\252?\374X\3272\333\022\212?\322|rvV8\262\277\366/\324\377\235\022\273?\300\213\017}\244\036\303\277\203\371\342k~\251\261\2776\013\374\302\361\254\243?`\273\367\346\324\312\315\277K\325\353\347\364\020\244?]\224#\267\313Q\221\277\"\3676\240\255\377\324?Yx\255\261\347\205\276\277$.$\366\341\244\201\277\003P\343\327\212\377\243\277\357\376\275(*\240\311?1?\263\203\326\245\241?\237\222\351\261x\310\275\277\007\301]4\264\200\254\277-\375H8\372\026\302\277R\3341\215\003\222\254\277M\363k\t\365\327\273\277\217\002\213\231^\033\275?\232\305H\340\307\226\263?X\002.yF\235\235\277\017\0024\317\253\000\304\277\246_KZ\253\006Z\277o\276\346\205\277\232\300?\235\303\305ce\177\263\277\327T\342\344\263\006\255\277\313\031*\270$\214{?\237\200\202\023\037X\300?2@Pu\312%\272\277o\202pb\253\216\276\277U1R7\324=\274?\311\227\360>`\020\241?97\330\337RX\251?\310N\215\367\016q\244\277\265\271,\312~-\252\277o)\332\007\204T\264\277_6\302wd\336\264\277\376l\233J\347\367\265\277\205wS\030\211\331z\277c\217\214\225\225\235\335?\213_g\026\256\365\256?\275\034c\230\276\375\211\277\303\255\225\224$\354\265\277\230\370i1N\247\272\277x\327\330\0042\343\306\277y\247 6\257\370\246?\234\367\350\3111\320\240\277\307\357?\021\3012\234?\232\376\316\266\203\275\223?\247F\005\252\246T\317?q\\\013lN;\261\277I\022\370\364\030\035u\2770M0\252\350\311\273\277\020c(](\260\303?%D\365\325\014\221\217?aj-\355\2236\225\277\323l\265\224\023\373\265\277\t\206\225\261v\245r?{\0067\021\340*\300\277\215\304v\006\016j\216?\377\214\n\307\332\231\310\277\002\332\247\327\262\207\272?\311t\225=$U\301\277\337\016m\221H0\321\277SN\231+x.\301\277\223\265\206~dA\316?U\342\217~\201\250\333?\237\314\231\230i\364\321\277\251\374\327\313\271n\263\277\007\001H\200T\341\272\277!7\340>_\351\265\277\253\013\210\333\037\265\313\277~{;\210\232m\246?\022\356\213\013\001E\265\277\361\224\242\222\010b\307?E\204\265z\006K\247?\252F0o\377\362\255\277\223 \010 U#\207?Y\267,\037Gy\304\277\351t\275\315}\351\257?\035\235\224\376\300\311\252\277U\211jq\250\320\245?\351Y\221\332h\007\205?\277&\350\223\3479\307\277\327\210\001\350\273=d\277\351_\375\346\266r\241\277\177\314\302b2\005\263?\356s\351\007A\373\273\277\253\311\362\300\250A\222?\306\367\312Ie\216\260?\373\2220\316\355\211\222?\205SL\"\362\320\276?\\{u\215r\370\204?\225yr\306\026\343\254?\3232M\307\322\214\266\277\356\320\216\037\312\257h?|\324\272\254\210iw\277\032\355\344\203 Pf\277\356\374\302\252\022l\267\277\347\\\337\213\233\265\300\277\3713%\200\300\325\261?\037\014\330v\225`\301\277kR\342r^\027\261\277\347\236\204\340\227\225\254\277+\273\250\242\250\\\271\277}0M\245\370lS\277?t[\207\363\031\244?\201l\322\206k\037\231\277K}z_8\002s??]\022\303C\265\304?\344\357\326Nx\206\241?\323\006`\302j\214\302\277\350\367\247\344Eb\315\277\022\243><\266\350\234?\234\327\263\341\213-\271?\305r\216\221\220\305\250?\361\"\346U\220]\245?}H\344\2026\353\243?79\242\036-\017\313\277?\217\302\"r\231\275?\266\217\261#\332\213\256?j\320\305\224\345\225\314\2778b\240\304c\020x?\254\376\001\211o>\323?HQr+\372\335a?\021\370\227 \244N\303\277li\244\310\343\216\263\277\210\303\240\263s\257\251\277\307\217\334qr\205\244?Z\302\205\334\031\277\263?\345\242\263\270w\302w?\023\342\002\352\314\246\216\277\367\255\347\001_\257\214?\r0bd\372\023\216\277U\310\265\013\227V\251?.\334$\353\205K\266?0\204\263\253f\253\220\277\024#\270m6\214\310\277\316\316\266\224&1e?QZ\307B_\213\266?\034^\243\315o\322\206?\nln\227\364\026\233\277h-\374\274\203\364\273?\230f\357\244\326\213\314?\000 \017\006\314\301\245\277\177P\350Y\210f\250?@\361\202\274\214a\244?\334_m\327 \031\251\277\361\205 \314MHm?\007M\232`-s\301?\363\311?\365\233\364\307\277/\240\350\213\017A\327\277\335\311X\272.c\264\277\340\302,\203j\272\231? \003\360x\301\n\305\277\236\276\347\304a\r\271\277M\227\241M0\304\271?\206\232\327+\361\216\322\277 \320\224\036\354f\300\277\0263,\026\362\207\306\277=\215\303\365\301\325\243\277\024\374\376m\3510\275?T\317\226\373W}\310\277\016\'\007\013}|\266?]*C\266\344\336\255\277\312\233l\306\025\201\334\277\253G\344M\333\377\300\277@\022siD\331\304\277?QO7v\313\260\277\216\252\301\0003\320\260\277\030\202\323+[w\230?\357j\003\\c\014\252?\332\306\000\n\315\303\262?\306\000R\007F\365\265\277\201P\000\n\202\037\302\277\305\026\2325\2276\232\277\236U\230\2078\024\256?\227E6\204\327\271\201?\3772\352\227\354\226\250\277\201\321I4\242\210\320?\0323(\341\'\\\261?)\001\200\245\311\010\245?5rlm\303\231\310\277\357\373\367s\272\372\271\277Pl\3003\236\242\266\277\205F\030\025\225\023\221?\342\030\311\314\320\344\320?N\264\267\220\323\376\254?&n\3740t\241\263?\350\301{d`y\252?\225\177\030\344\244\017\263\277\322/4\3635W\261\277_\270S\257K\256\331\277Q3\310\260\377\304\256?\000\277\307\277v\213\260\277\233OT\205v\347\262?\321\332\303\212\3647\263\277\334ED\216\223z\251\277?\303\336\0133\266\237\277`\314\223>z\022\230\277\r\211\274o7U\256?|\203t\312\377|\261\277*4S\3660J\243\277\337\002AL\027\016\313\277\260(\250=\260&\240?x\337;\255\027\027\241?@\243o\030\305\331\225\277=\275\344Z\366>\305?j\257\302\355\260gx?Xb\356^L9\262?\306\323\034\003\234\n\273\277L\326\370\001\031\376\275?;\014\033\242\377\252\252?5\n\265\312\270\006\221?\273\345d \214\"\236??B\246\255fB\306\277)\310:\rT\225\247?n\342X\270O\272\210?r\354u\272\254\241\301?\206\272u\255_X\263?\006\266T\361\371\224\313\277Guv\321_\272\322\277\313PY:\255\'\242?\333Xt]B\237\244\277\205\321h*Y\r\222\277\325p=\270[\340\227\277\347\256\277\031\212\351\267\277\204\352\207pu\025\246\277\3153\222~;\255\242\277k&!\307\207\037v?\000\025\327o4\277\242?\253\315h\227f\312\313?\302\311\314\312P\333\325\277$\014\326\377\246\005\264?\336\007\375O\277V\266\277\023m\356\346\354\343\274\277Q)sD\3729\211\277>\255\346Z\337A\242?\266\020\331\334Ze\260?H\010\2323\026X\242?\274\205\325\021]Y\237?\275m\307\014j\332\246?*\213h\261e\332\304\277\215H,\024\356\352\271\277\367\211\036\265\022m\266\277m\3453\220/\310\271?F\253\230\2019\236\265\277U0\225%x\356\256\277\260dN4\237\005\313?\335K\373\302\212\215\320\277|\226R\2330\372\232?aW\223E\342\202\251?>1]Ey\377\247?\266\216\370\034\263\361\272?\270\311\237(\036\320\272?\352\366\266\227\343F\247\2777\363k\255s\361\301?J\271(\210?\212\271?D\224A\3661u\260?\007lw\301I\257\255\277\352V+\013X|\306?m.\203U\231\r\314\277u%[\303\254P\307?\013\031t:\364S\304\277hwfB\021\263\261?-N\340\022\276>\301?&\201wJy+\273?\347\033\222\253\261\267\237?T\275\252y\274\351\307?\306\251$\221\370\003\256\277\350}G\235\271\207\304?\374F\3759D@u?;\363\257\227\232$\257\2774[\305B\315\367\274?|$;\363\261\364\252\277\3152\227\265f\005\271?1[\313\365Q\322\253\277\'d\334\230\331m\315\277\235\27289\347\264\276?\2101\255\332\223X\250?\261\002\331\0030a\206?\320\257\336\364\263\r\257?\013!\260\335\352\234\213?\306$\204\236\037\361\311\277{<\362Y\257-\214?\007\222\362\306\310\356\277\277\277\367\314a\331\302\301?g\317\021o\220b\234\277\240\227\237~\274\232\223\277\003f\373\275p\362\222\277/X\2113\230\254\305\277\302\304\030~W\326\301\277\324\250A\262\231\223\253?\271\302{\010\372\345\301\277\231\013q\262\247!\301?\256\264+\247\264\252\224?.\014M\002\2176j\277\363\223\277\350\273\327\274?\331o4F\362l\266?\213\212\007j\306}\212\277H\237\023\rY\225\235?\253\376\311-\367\344G\277\037\200\325\256\202\257\235\277\027:\336x6\r\235??\002\205Y\310K\271?\t\302~vk\035\225?\372W\'U\314}\244?\243N\224;\nQ\311?\024\316\016\031\212#\261?\330\330\314`\267*\241\2779\\X\245\351-\221\277\030\333\017\301`%E\277\017k\301Fx\275\265?\225\351R&)\324\245?M\nq\361\022u\273\277\035\026\342bI\331\303\277\010T\370 %-\262\277]Y\240\216j7G\277f\351\371\235=q\217\277Kk[i(\004\327?\355\333\204\320\335|\274\277Z\225\350\224L\017r\277b\023\302y\0101\232?\"\371\212\276\230l\310\277H\370\310+\247-\272\277\002\232\360$L\234\255\277\254\201\256\024\004F\301?\"v\301\314\225c\301\277\303j\230\331\216,\270?\331E\244vG\026\252?T\370\247\202\003<\300\277\246Q\361\300\210\200\250\277&\362Qk\310\220\306\277\320gx\021\345m\303\277D?\007V\"\347\250\277\324>\370a5\356\276?|nOZ\332\216\246?\257\264\366\260\371?\244\277F\232\366tC>s\277\304N\204k\204u\242?\n\350\327\0212.\305\277\356\324-$#`\273?,\264\034\344\036\206\276\277\306{$U)R\327\277\274\273\314n\334\261\222?\373H\304\003\027\366\260?\342Lsy)\014\274?\363\2651\341\356\212\331?\311\334r\234\213=\274\277\341\254\352\273\3272\245?\342\302\030M\376\300\253?z@\344I\232\372\234\277T\0324\350>D\245?\037!6Qz\005\223\277\222\321\323\006\206\252\267?\262\2437\354\300e\252?\256\276\321\220N\307\212?\n\001O\023\366{\252\277\316\021p\024\314\035k?q\222\353\310\215R\300?%\233%;d\302\240\277(\261W\320|\371j\277\273z\304~/6\242?V\'\023\206U\350\275\277[\022\207[\265\202\270?\316\216<\0228U\270\277\315\367\321\355d>\273\277n\tF]/J\241\277\017\323e\r\261\203\323\277o\002o\352\2562\264?\340\"\204t\\\340\271?\237o=\307i\324\277\277\301\322\346mc#\273?\021\2106\213\257\210\274\277UT\343]\301 \316\277\234\024!.j\356\315?>\202\230\3058\003\276?\355\265\316\301\242(\264?\n\357\250\272\300\246\310?*\311\342}x\022\263?}\304\027\0226/\321?\221\252\373D\277m\257?0~\253v\211d\311\277\r\037\326}\322\tg?\302\272A\203\360$\316\277\373I\212\243\323<\320?\222\033N\036\326\267\243?\356\220\177y)\206\256\277\210v\204\354\205\237\311?\247\215SJ\324\357\254?\254\345\273\023ph\210\277\206K\264\022\332\334\255\277^\221]_\340\224\235\277\177Q\001_\327.b\277!/\216\021<\251\257\277\014S\223\326\003F\215?\n\313\204\021\241\237\266?&\262\"\246\222L\255?,;!\262ax\257?E2\324\270\362\013\311\277&\304\272\251\202\363\261\277\361vQ=\337\310\261\277~P-\352\375\203\261?7\257T6\240\221\250\277I\337\367@\270D\256?\242Y\270)\247\216\272?>=R$\364wJ\277\321u\242\341\206>\313?\017\277\366\177F\214\266?\265\266\244\203\013\034\255\277Cb\013\233O\302\252\277\354\250!\240\036v\222?@Fe\352\3669\266?\271\002\202\262\004\005\311?#\3150\036nC\222\277M\215{\000:\260\303\277P\342\212\362<\200\304\277t\327<\272\2241\304?X\217dA\221\257\253?\023\022k\320\200u\234\277\353\333\230Lk\241\270\277t^-\344(\265\304?0\007\202\025\367\034\261?\023\"\264\362\365\253\244\277\230\277\227\201\250\273\256?P\032\372n\377\224\247?\021\3409\377\262\343\251\277\230\375\357\312\030K\254?\242\316,U;\273x?70c\2344\263h?\035O\006r\033\026\313?\271Ce\330\203\021\301?\375\312\226\231\335\364\235\277F\343PDf\016\261\277\311k\227\245U\\\267\277\335\320\234=\271\"\315\2774\032\006\226\337\204\224?\3574\224\305?\277\264\277\246;^\016\004\217\261\277\320\270\351\266D\202\264?\021\240\315\361FW\223?\237h~\020\027\330\275?L\261\362h\334\274\241?\270\217&\024\272\235\220?\231\2325.\352\204\251?<x\212;( \256?\"\321\202K\375\2650\277V\\o\310\306\211\264\277\206\322\215\336U\032\262?Xi\024\266\277g\316?\303\007\326CY\\\302?\023\000\254d\306\031\227?\314\322\221\363\307\227\241?\207e.X\330i\260?\344\260%\3109\375\264\277n\025QI\233\342\263?\351\361imPB\264\277\3745\355W$\253\265\277\207\311\204\005\306\320\237\277\"r\377\335y\035\252\277,\332\323\371\205\355\275\277M\231\212\006\235-\315?\275~\222\023 \221\225?\343\354\027kSV\310\277\200\204\341q{\227\243?\036\321\362<ov\262\277\252\210\340\235R\n\300?\321h\226\220\344B\240?\320K\243\245\024\262\250?\007\34125\355%\260\277\2445V\303\331\333\310\277\003\323\267\263gH\260?\330S\341\366\302\355\317\277R\td\274\305\357\243\277\242\334\354\2412t\227?G\254[s\323\350\247\277\005~\310\355\274\033\231\277\023#}\320Wj\320?C\255-3\'\377\213\277\313\334iJ\357\206\272?+#r\2502\330\275\277x\253\r\335\267L\244\277\376\224V\220=Q\301\277L\302\037jz\316\305?\346\314\024\234\276\247\226\277\005\032-q\252Y\240?\257`d\257\033\032\310\277\032\301>\001(x\300\2772\364\376\274G\355\221\277$\t\240\224\267\244\312?\341\023\301N\275\277\251\277\236s\273f,\n\317?Z\234\372H\271\225\244?\216\203l\335\354\271\265?*\322\216P[9\307?\'\370\335o:`\301?Mw\326*\036z\262\277\266\303p*\375\342\325?\261P|\377BE\274\277\364\302P\367iT\301\277\006\367\306\013\207r\274\277\355\350]\037\363\237\311\277=\202\300\250a\237\200?\226i\0261\327\255\300\277\265+\034}\0162\255?\022N<l\023+\254?&\256\250\350\231[\246?\210\n\037Rk\333\220?m\"n\347N\346\305\277\364\242Iq \337\243\277e\307\320\246\201G\261\277 \2473\317\236\347\225\277\271\310\232\215\276f\262\277y\276\324]=V\300?\2540\035DR\347\305\277\256B\026y\316\316\320\277\321m\315\334\352\354\231\277b\nJ\254rk\265\277p\276j$7D\302?\036p\201u~C\303\277\2116\037\226\211\316\264?\245/\362\227\345\221\230\277\221A\350\340L\277\272?\002\037\333\216\360\335\254?\001\217\375l\253\267|?\001^o\004^\271\213?\001\236\0333+\347\210\277\221\373\312,\3420\265\277SK\352+\346\343\305?\327\216[\003#\362\273\277\020\247\0059\350\014\300\277\277\313<:\343R\204?r.\216*\217q\264\277\023\371|:\320\304\250\277X]\226\212}\342y?\242\376\215D\002\027\271?\236\246\257\354\034\254\301?\246Ww\314#\030\267\277lx@\375\374\244\266?&\257g(\340)\267\277\324@\'\253=\245\262?\360V\r\255\371\010\217\277\300\0200\302\035\025\263\277:u\031\000.\224\243\277>\037\312\367J;\256\277\352+q\242\225\337\232?\210\372V\023D\023\263\277\343;\344;\343\324\257?\347O$\376\033B\301?1T\035\312\024\361\256?<\312\252b\022m\301\277NT\326\007<O\245\277@\227>\272H\301\303?\250\221P\243\223o\246?\275\365\007\315\366\223\245\277\004A\217\313w\300\303\277\017\tx\377\013\354\225?\t\234\357\331l\217\323?nLE\346s\376z\277\345\370\000+p\362\231\277\301\230\230rc\376\305?2!~|[\271\224?\212Sk\264q\327\230?\220\0041\267;@\231?,\262\235%h\222\236?/\317\353\003\241\300e?\214V\221\r\217b\271?\374\313\\\370S\022\305\277\027\357\237\023Oh\260\277\016-\\\313-\372\260\277\210?\256\031\211\313\305?\233K\334\365\301H\304\277\304\310a_vg\244\277E\337\364\237\323\312\254?n7}c\244\343\256?\327\306\230\253\330\323\235\277O\000\260m\357J\257\277\222T\032\202\244\030\264?Te\327\215\315\247\265\277\264\037e*1\002\221\277\370 -\362U\275\302\277Q\032\032\005\241\267w?;\004m\006\3166h\277{E8\233h\220#\277|\242\300\013\"\344\321\277\210\375\226k\264\267\304\277\252\274L\326\254\320\257?I!\010\360\3172\262\277\353\343\252\2455Y\263?\335\364\211\334\227f\232\277\"\311\302\323S\023f?\250\246\325\356\213/\235?\365\272l\357\3109\302\277\303S\005\025&\004\306\277^\211\210\371\\R\245?D78eQ\210\300?\200(\260a\020\014\220\277b\256\220\367(\177T?\332\253\311\274(X\302?,\253\0311\245k\306?t\347\2148D\371\251\277\346e2\305XD\245?\000w\252\007B \251\277\266Y\'\023\211\010\255\277\274\332\002v\205\252\240?Go\"\274I\363\245?I\253\201\342\322\313\277?\214V\013\222\373Q\271?}\222@\t\251T\270\277$TW\032\316|\305\277\363\271\255\234\223Yy\277\211^qOt\332\277?\311\2242j\207\361\243\277\203C\006~\320\026\260\2774g\230{\225r\265?\274\033\342\023\361\277\322\277\321\345\030~X\261\301?O\344+\252\262\226\301?\237y\227\3250;\271\277\302\370\312\020\207\225w?\016\357\311\3564+\242\277\215\2438\225\315\361\246\277\342\001UI\301\225\252?\353~\254\326\313\027\206?\004?\021\266<\t\212?\376C\331\303\320\252\265\277I\021\230\302A\330\226\277\324h\375\225\222\343i\277\363\225\217\034\370\360M?\025\225\266H\316N\247\277\255\335\230\377\\\037\270?\320/\305Qc\'\270?Ue\006Hr\026\262?\021\345!\250\331\234\232\277#\344Z>\341\257\301\277\225;\342\356\3250\237\277UA+D!\235\233\277\004\376\324\220\304d\305?\010\250\t\371%\010\263\277\004\271Fb\342\227\244\277\200\241&!L\335\320?\363c\037\245\365t\270?a\215\267\302\353\036\014\277a*U,\342C\272?S\362\3071\205\224\307\277k&A9\302\201\263?\037k3\030Wl\226?\336\230#\243\303\307t?]\004\005\220\000\217\200?\323`\360Lv\010\247\277}D\332\212\354\356\311?D \240\234s\236\257\277V\264\303\366\243\365\200\277\316\304\032x\305\213\230\277\000W)\031\230\022\327?\301\372]>n\205\217?[\224Vm\221g\222\277O\311\224\376\224w\301\277`[_Ya\203\245\277\327\267\022\3700\014\302?b\324\253\000\320\243\234?G\345h\234\374\013\257?FY\r\252\337\330\321?6\327\034\304\302\254\217?\035\023\374\264\357\013\274?\266\351\311~@\347\315\277\223&-9\001d\206?\204\013\020m\'\320\254\277\365\371VZ\265\277{\277M\\jlV\237\217\277\343UXrs\363k\277\377g\371\260|\177\226?\304\203\243\237\330\200\301\277\000\320I\276\"\346\271\277f\007!\223Y\373\260?\202\313u\234R\310\253?\275f\365\311\001e\261?Ph\340\005z\217\301?\016eo8\376\000\245\277\233\346\374a\307\347\277\277b\350\2263kQ\233?\321\301\353\350\220\357\251\277\274At\023Q\256\214\277T< \260]\037\200?z=\236\346s\010\265\277\310}\355:\306\260\233?\324\320\350\240\023*\245\277\030\305m\007+w\300\2775jB\017A\251r\277\310\365hZ\204Sd\277\344\3126\337\033\311\266\277\217\021\245\326\0071\224?\240/v;T\256\263\277\246\245\231\237\376\343\226\277\236\330\237\\\001;\255?\033\371\221_^\232\270?\361d\032\016\206y\304?X\033\216\336c\373\260\277d\317\020q\210\250\201?JP\014v\373\366\273\277x\025\244H\250\216\266?\225\270\330&cQ\260\277\014e:\305\262Q\257?\262\336\332I\244-\267?YcM%\312\243\317\277\354eF\000\014\203\227\277\333\\1\275\036\026\246\277\026[\321\206\301\226\271?\0250\014eb\345\210?\364\316\323\206~\254\224\277I\265\250=&\017\250\277\364\352\016%\334\324\204\277\221\330\315>N\206\273\277>\365\375W\375\330\266\277\004\006B\261\007\242\270?\357P\354\271\234\275\252?5\207\377z\355=z?\332&8\366\216L\232?\346\243\230$\244\007\244\277I\222\264<\321F\255?9\230\354\230\301\024\255?\034\360\265\314\177g\245?\212&v\264\225\332\266\277\351\303\257\030E\243\251?D;\264\243\"\254\304\277\356\270G~\371\336\216?\177S\325[\220\340\220?o\277B\301\342\200\232?\230\243\233`\275f\217\277HMEm\345\240\221?\344\014\324#\005\361\273?\341r\221\370\336\256\262\277\224\275R\347\374\233\270\277\321A\262v\201o\251\2771\365:\215e>\275?\36532-\001\237\275?\372B\266F\0346\304?\3754\326a\201\262\212?K\027\320\351\004\272\267?4\235\316\334\310\t\276\277Af\272\321\245{\257\277\025hi\357w,\253???\210c@\026\265\277\227\337,\357\020\300\251?\356\374\305t\245\250\301\277\352\263\004L\313\002\204\277\027\237\3574?\216\240?>\265\014\213P\333\222\277B\rP\213B#\242? \324\027\301\n\314\300?\321\364&\004\254\233\225?s4l\354~@\261?\020\321\306\005\033+\310\2777k-,\304p\262?\214\177v\336\235\277\265?\244\230\255\377j\357\244\277^\377hO\320A\242?`\251I\366L2\272\277Z\241vew{\241?Yo\001L\207#\266\277)\370V\264d[\305?z\342@m\311\026\221\277\3163\240\021\343\253\260?\374\240\362e\332\026\244\277\036R\252b\245+\220\277\262:V\224\021\n\236\277[x`X\334Z\314?j\266\372\267\373\324\311\277\035\\\013`^\354\256\277\036^\024Q\301\376\n?\365\207\216\230\025\030\305?A\273H\201\225ea\277\203\354*>\364\247\226\277\271.z8\342~\263?\305\234\020\336t;\306\2776\301\212!\005d\251\277\353\371\233\250XA\267\277\230\370\231I\242\374\266\277\352\330\332\026\340\334\265?\3147\037\036m\257\301?)\302\260J\345\034\277?\311\034b\355\017\027\271\277iq\017\0379C\223\277\221G\002\307\243\035\222?Z\302\361\247\244\030\305\277\225pm|h\036j?\01758\300\271\024\253?\302-XJ\016\241\300\277A5=\271\376\314\220?\333\356\260\000<n\230\277\365\230\246H\307R\273?Z\274\032\307\323\317\312?<3`\323\354w\310\277*r\345XM\035\277\277\032\256Nc\355\304\267\277u\367\245@\333g\262\277\n\275\303\026I\344\273?\247\364\226\342\377\325\246\277cZ\212\334g\307\314?\217\201\367,[m\243\277H\234>S\216\204\274?f\322\342!\232d\236\277\243\216\313\377\322\177\250\277b5\234-IH\321\277\352\324\365\317W\257\300\277FkA\0358+\301?\240\214\353\273\243\274\305?2Mb\004f}\234?\034UKP\\L\314?\217\207~\255\224\306\312\277\251\361\201 \013\340\240\277\276\273\323;\344\353\303\277\262\2042\305\251\303\230?H\267!\225L\326\320\277\310\230~\310_\313\273\277\333\233)Uj\202\303\277\371(\206N\022_\324\277\005(\271\300\217\262\255\277\326\245\254\240\275\005\301\277\371\263\006}C-\205?\211\333\255X\324\246\302?\207Q~\310@W\263\277\236\312m\253\0072\233?\372O\005\356\263b\340?KvMdr\272\270?\'\227\312\035 \272\271\277\005\332\261\027W\\\221?d\304\037\376\371\260\301?\3146\305\313\031\'P\277\004N\340dK\004\246?\016\010\340\372\327\003\302?\364g\362\373$\024\265?\255\356\242M2\345\276?\345\233\001\264\362p\321\277\214\323\303a\257\300\220?\327\223CzH\342\220\277c\'\370\351\267\253\266\277\207\302\025\025\241:\264?\336\370\024\375\277\326\301\277y\347\245\3665\317\247?\274M\326Pm\327\234?\263\321d\214\232\317\236\277\372\315\017\321\256*\206?&^\234\364\343\340\265\277.\036r\307\3426\277\277U9\026\202UE\261\277_\tu\356\336K\267\277~\226YQ\316p\325\277\277\371%\312\020l\211\277\377S\354[\213\006\260?\000\242\267\005\204F\253?\336\372\327\310\334\357\302?\242Aq\272\'\214\212?\374\027\207\360\200q\310? \325\242\'o\367\270\277\010n\004\265W1\274?\207\233Ce\336\273=?]^\365Z\210U\261?\210H\376\270\252s\310\277\317bdq\375\007\205?\037\331(\213\327\356\302?\373\327E\010\000\314a?W\010\002/\230\305\250\277\177a>l\364\201\244\277\034\222*\350\222:\275?\2502\354e\032\246W\277>\203\273\243\304\304w?\306\353\347\303\t\214\265?zN\034/C\271\276?\346\264\341\230\264j\247?=\031;\213,\037\304?\367k\200\277\343\356\250\277\333\216e\273\261\201\303?\220/&\221\217\220\275\277K\232\017X\313|\244?x\377 dr\222\306?X\006\226\243>\322\265?8\215\325xfs\303\277\203\307ecQQ\322\277h\214\315U\231\206\244?\342\235\0350\221w\234\277I*\222\247 \230\311?\tY\0033\035\213\317?\362\304&y+\346\242\277a\220\255}\304\013\302?\276Bn\005g\312\300?e\273\222qhI\274\277\321\354m\000\337\217\267\277x\021\"\316}\316\220?/\236\336\373\372\212\333?c\222\203!SbB\277\353\007#\311\343\254\234\277\220\373\004\225\243\006\264?y@N\300\340\355\330\277\355\005\365_\2625\267\277\352\n]\026\2402\266?\255@\274\027\235{\260?EH|\364\017{\304?\371!*Y\265i\244?\325\2113-\215\245\271\277\317\034v\360\034\231\276\277H@\357\274:\017\027?\350O\\^U\270\277\277\\\266\265\305\330\007\314\277?\315\215J~\360\277?\302\322\317\324\242\"\275?9\014\263,\3200\303\277lqM\363{\301\325?\202\2211\036\364/\235?<m<\206~S\247?\314\021\021\216\024<\273?VS \277\001\274\301?\261a\255?rQ\241\277Y[\352X\375\371\252?\"s\"\346\037-\253\277&1\010\373`\217\257\277\016\005\247K(\241\260?k\330\265x\321Y\234?2\n\013\244I\211\264?\313\355p\325\317\350\246?\036t\374\273\247T\232\277\001\206\303UL\207\300\277\177\253\365\3360\213\265?\374b\007L\t\264\311? \347] K\346\220\277\222<N\270I\004\317\277\274\263\210u`K\300?hz\201\322!\360\245?^\261\023#\335}\303?1\364v\177\233\037\211\277Y\0278\347\034/\235\277\265\007)*\226\n\313?\004\257\352oR\252\261?r\323\246\341\331]\310\277Y\313,\252\366S\256?,\300\253\342\206.\250?[\226\316\225i\357\236\277E;$\246\023.\240?\211y\345\003\376\342\307?\232\000\252\000a/\266\277\020\357buM|\273?q\037\300\257\tB\245\277-\221\027\t\263\010\312\277\313\275\033\3628\316\221\277ih\'\246\212,\275\277\004\022\273e\021w\254?\367\344\367\205f\r\216?\225\036$\354MY\257\277c+mp\227\363\270\277s\275\361\027u\240\213?\025{\263\310\341\202\256\277%\032(\353\274\363\245?\347%\321h\224\210\263?XL\333(\031/o?\340p\310\2616,\265?(K\307\366$3\272?\023 b\035\360>g?\023\371\021\032\377\316\221?Q\021\276@\004\337\273\277\333X\234\303B\260\253\277y_y\356\235\262\270?\336\265\373\246\211Z\242\277\364\023D\034^3\253\277\177V\374\307\"W\304?$\256\265~\251\307\261?\246\247>f\244\333\244\277\022\371Md\220\275\306?\330X\210\217\262\002\250\277\210?#W\337\251\301\277\254q\342\2155D\265\277H\204\324\313\310\270\255?#U\365\337w_\231?\357\203\241\002\202\245\234?\332\325@5\026+\213?>wBv\032\326\262?\273E#N\224\360\303?^\240\216\353\313\341\240\277\317\375-i\317\034\305\277\302\341\325}\236<\263\277\355SL\261\3647\255?\000\242d\010J0\227? 7O\\Yy\261?\005\346\2272\030\360\217?;S{f|8\307?\025\315\326\314\216h\302?\322<\325\3676\277\240?\021&\314\003\322\322\254?<X\364\241\035\343\302?\305(\254\305Lz\275\277\345\244\207\313\262y\303\277\235\377\222^\371\343\273\2777\375\320\274\'[\340?\033.%\254?\221\242?\245?\221$GX\276\277&\013n\332\335h\276?\223b\234\347h\022\221\277\271\305t\377\\\n\265\277\024\225\024\t\342\346\206\277\034\255\203P\364\345\302\277\3727\214\342v\312\253?\244\017\312X#\361t?Me\260\337\324\354\310\277\237\003\330=\212\030\242?S\317\010\327\014$\217?\353\344\266\r\314\036\220?\211\004L\321\014\351\264\277\311\020 \314\304x\254\277\0366\245\036\004\234\243?.\212\342\024\325\227\233\277\372\310\030=8\214\241?\224M<\316__\224?Dsd\026s\302\177?G;\241o\261*\277?\252a\325 \364\257\220?5\037\276\222)\315\230?\325\302\305\002/?\235?_\245\200A\343\021\230\277_\310\013\317\367\205\213\277}\277\265h\235\313U?6krS\216\371\301\277\271\333\022\365!\r\250?\273\t*\230\320\315\257?D\336\365xX\263\257\277\3707\037\035\002\273\305\277\257\211~\324]\356\266?\316R\372~\217\243\232\277k\342}\027\3434w?\204\351\377Khw\254\277F\210\212:2\204\265?w\013F\366X\006\253?\007\020\345\270I\330\277\277\237\255\025\217\353\317\261?3\216\025\236\375\206\272\277\035\325j\274\022\337\331?f\335,\265\344\251\244?\222S\374\371\373\214\305\277\321K\275\226\325\037\321\277&\243\3544c\224\253\277\337\024D\014\206l\242?U\304\361\215\261\310\301\277e\257\331i\010\235\240?4\235\363\262\221:\272\277\367\246\251H\0071\256?\036\355#8\336\350\305\277\246&\'\335\007\257\270?H\360\373\357\273i\227\277p\327\350Y\222r\315\277\346F\241\325%\'\236\277CyH~\321\004\216?f\224\003M?\302\272?_\201-( ?\307\277\326W\017\375\216\361\305?uB\3059tV\246?\212N\235W\235h\275?<\273l\034\336\206\275?B*x\025zv\251\277\300\216<\303 \264\232\277i\210\343\365\366K\327\277\212\251\341jD\220\261\277\310\276\307\245\265q\276?\356P\344\335w?\223\277x\357\037\304U\032\301\277\224G_~\311\275\272?\245\221+\316\366z\256\277&\371p\204\245\023\220?\370\335]\334\206\335\246?L\021\273\260\347\357\302\2779H\275\027\353\014\247?\315z\343\333\274#\256\277E\232\265\037\304\256\262\277\247\021\233[l\311\266?=\262\362\002\220\367\266?2\257\033\nYS\275\277\342\266B\275\325\341\232\277\321\246\336\264.\234\300?\252\361\234\3306\311\300?6\035\247\037\224\010\231\277\330\275y\034t \264?{\362\270\346\275\211\322\277Qj\307\023n`\273??Y\027\206\211]\264\277\327\372\344p\333\232\260\277l\372\351r\233\274\275?\0075\263V\371\317\220?\333{.\346M\370\246?\216:\253\252\212\267\275\277\177C\345~\006\335k\277j\202\314v\205\316\231?\207\261\005\367\366\264\270\277n\177\026\264$\310\227\277\365n\232\243\241T\254\277Pu3\276R1\264?_\017\2452\305\324u\277\\\240n\377\010\257\311\277y\372d\316\031\337\301?0\"x\265\201Bt? \275lc$\341\302\277\345_O\240\320:\300?\031\371\355\322\026\222\236?3\002\325\005\371\335\217\277\363\241Cb\230\200\302\277\314\027\224\371\205\224\227\277\236jG\025\337l\306\277\206R\177\345\340^u?C\205\nd\257\353\207?\025\245DN\270\'\242?\356E\302/7%\244\277i\027\034T\302\016\272?\336\021mLC\343\202\277\'Y\213\227\r\247\304?6\017\221_\236\363p\2776Y\013>\313\203\207?\215M\367b\265\226f?\006E\227?\006\031\321\277\360Y\362\001We\265\277\336\031,\311\203\335x?\270c\331\"\222\037\273?tW\361M\035.\320?\033&\\\310$\370\241\277!f\002[\217\272\315?\251m\321\350A\021\272?$\001\016s7\202\261\277?\376_T\325\210\256?\335W\263\001\017c\257?\000:\351\313\0310\256?\304\256`\207\357\004\270?\207\273\324\347lP\261?5\016\264\031\365\n\314?gt)@6*|?K\215+\256\251H\275\277\275\2472\323c\317\306\277\324\271\251\314\'\336\314?\222\346\240\'I{\262?n\025\326\351\205+\240\277R\003\314\206z|\263\277\032\241\337i\025\346\271?i\305\003\3026\003\264\277\253\233\326y\270\211\257?\026\214\311\374\000\010\277\277\266\026EYO\246\240?\363o\2265a\370\320\277Qu\177\220\344@\251\277^\013\361\371U\261\315?\206\321o\336N\320\264?6\030\007\033<\310\307?($\227\177\r\215V?X\253Ce\313?\251\277\316\335\245\336\364C\261\277\370%\224\372\276\377\210?\020\205\021w\241\356\300?\230\320\377uT\304\302\277\232<\017\343\275\006\260\277\326\206\001p\205\325\271\277\331\203\234\212D-\307\277\345\321D\314\017\035`\277\221o\315\211\341\223\300?\342\303\201%\177\334\242?\250\241\313tW\307\274?6\245r\3018\210\273?\242\022\304\373\353}\313\277\257\341r\352\310j\250\277\254\026\246|\017\212\222\277\032\000\tk\311\352\312?ZOE\250W\005\315\277^\\\362Nb;\221?\trc\206\201\n\240\277\221\373_^Zs\222\277Mw\'O\320I\220?\032=)@E\016\242?\021\352\223\245\000y\320?\370\357\344\n\204\256\244?\200\026\356>\362\313\252?\\Ls}\262\250\241\277y\247\255H I\306\277\211\200Z$@I\221\277\257\310\204\335\307\240\270\277\246D\315\226\327\025\301?+s\374\312\2608\306\277c\031\\\313\020\355\257?W\234\002\022\230\222\271?\354\347\374:\343 \307\277\016\263\337\325\370\216\250\277\030\377\250\r-\327\300\277\331\222\241\230h\010z?\023\331}\306\304\200\261\277\265.0\210cK\305?\251us\351L{\305\277\215\231\014F\323\326\325?/a\251M\243\213\233?\3657p$Uz\307\277?\267\013\265\371\244\254\277\341C\016\034,N\300?\214\366\336@o\260\260\277e6^\354\362Q\267\277?\300\224\307\353\325\243\277\246\361\254\303\352a\226\277ZW\235\001d\004\273\277\023\375\240\202\024\252\211\277\242m\277\261\260)\304?%\313\321\032\3022Z\277\207Ud\367E!\311?/`\232$\005\301\321?\201k+43[\235?w\302Ot(\351\263?\355\312t\343U?\301\277Z\367\253lo\357j\277\003q\032\3656`\250\277E\327\207\220\353\370\265\277\352\307\357\006\232\366\305?\354\374\017{F6\267?\260\216\306\274\247\274\230?!o#\335aW\270?\032\371\\\262\\\355\246\277\313Da\026I\350\212?\255\254\241\332\261|\244?\355\013\302\304]\340\310\277\344f\310-i\303\225?\357\374\351\316\301\334\245\277\037C\366\004\314\314\311?\003\027\371n\243v\261?B\265\223*\014z\300\277\302=\375\357GM\263?\344\261\027P:\013\312?1\212w`_\312\307\277v\246\272\276\250\177l?\210O\372C\317\264\225\277\016Gv\331_\"\262\277\n\033\327\361\206\320\243?F\335|\026r\345\312\277\013\3218\241\251\262\220?\035a\211\270n\003\253\277os\376\2325\276\267\277\344;FclC\277?!\022t\235\004\001\300\277:\022\331\233T\312\270\277\211F\272G\274\023\271?`\007\244\334\023\332\240?_+E\220r\025\243\277\262[\330\223\013B\265?\347\265`\001K\230\224\277\3562\005\247\355x\240?\306\366\330\027\230\212\243\277\257}e\237\3330\266\277\246\010s\'g#\302\277\362Ib\273\2577\232\277\357\376\340\234\217\242\312?d&}\343Ou\320?Z\036\210\036\341\222\256\277\177vU;\314?\226?\271\306J\255\207\251\263\277\311+8\010\207m\300?\340E\215\251\223\030\240?F+\351?\377\214\307?\036\272\004\032t\303\301\277+\271;]\234\032\256\277\035Z\210\227\244iv\277\232\271\006k\240 \315\277\306\371\037\235X\321\327?\305\214\363=\237\236\261\277\243\233\'\235\220\244\254\277\002\373\264&\2235\317\277\006\313\373D1\207\222?\264\"\362\204\233x\315?\\\t\227\016#o\266\277s^U\337\017\355\253?AGQ;\274\327c\277\207\305\\\006\026\367\206\277\240\263\323\236E\302\255\277\375^\241\213\301v\264\277\210=\260\317\344\221\257?C\364\207c:\\\262\277\'\315_E*J\265?S\252z\363\227\232v?O\3445\020\002\344\300\277\342Le\2436\025\320?&\312\037=\2223\275\277\233Wv\027s\235|?\231\025\273\201C]\266?\023\363I\270<g5?\310\372.\002f\307\213?7MG([\301\246\277P\324\307\211\245\276\273?P\276\327\241\205\215s\277\305\307!\001\022\252\251\277]\232\262\277\223\377\271\2778\376\033j\025\327\317?%\210m\365Fg\222\277W\2362A\020\200\237?\320\343\237\247\211\035\275\277\375\312\352\335\333\276\267\277\017\305\3211\014$h?\276\2246[\035\312\300?\231\'\332C\263O\260\277\255W\302\353w\006\270\277\323\356\020\004\023\205\245\277\233\033o8t6\303\277\226I=IDz\267?\034\247\321\":\257\242?\033\214\207I\256|\263\277\233\231Y\340\314D\265?\356V\353\346\265\007\204\277\\\214\237\356\025\332\271\277\001\210\003\\\300\231\310\277\031\034O2YQ\312\277\237\316|\367\270\010\235\277HR\222[\274\236\301\277\230\177R\021\374\020\236?\366\306\n\234\337(\262\277\212\234\216\'\261#\222?9E L\244\t\243?\355_+[\361\377\240?0~[[\007\246\267?Tr\346\253\213\330\305\277\022\032n\256\035>\277\277\213\372/W\006\014\220?\034(C_X\240\265\277\257\316e\tZ\272\317?c\253CN\261v\266?\240\366\345\216P\n\251?\035D\267\276F\t\254\277\244\234\306\302\243\240\323\277\031\254V\375\362\321\256\277\032]\337%5\243\307\277\311.\323\200\341\376\262?\374\230\323\r\213l\264\277t\353T\225-^\202?(\372\014\357\351=\263?5\256\0068\213%\323?v\263J\026\025\024\237\277\341\235\021\200e\363\240?w7\277\351\275*\301\277\244\325at\307[c\277`g\230\234\202\257\240?c\3221\207a\336\240?\217\325\225\3732 \321\277\371Pv\351\302\372\240\277\215_\315\221\014)\244\277\314\247\243\367\036\273\271?~\246+r$\372\253\277\276\310d\201\321\177\304?\000\231\273\342s\343\310\277\"\310\231K\3102\316?SN\260{:\\\277\277\340\332\r;\237X\264\277\354\351\'\2300\226\220?\333\207\222 $\300\243\277X3$\232L\264\267\277\324!Gg\332\205~\277\033_\306^)\n\331\277fw}\365\242\347\263?\007\032e\266\377:\246?\355\031o\022\244\254\314?[\334,\036r\200\231?\2773\235c\302\314\261?\276\347.\374\377?\245?=\364\017T\273\304\264\277\326\254\022U\373#\216?\023\312O\325\354\002\302?\326\375\366\374\026\254\276\277\2579\177\030.\231\274\277\304\014\003\210\030/r?\344h\206\226Q\372\262?\250\007%F\243\026\252\277\331\022\277\326\300\037\177?\2104g\276w(\236?\230MS\313\334g\271?^:e}sv\223?\236\016\221\334\331\301\307\277\257F\322\333&h\316?\245\003}moB\320?B\317\365\211\"u\243?\250 \361\345;<\223\277<;}\353\314h\310\277\233\205\373\035K\027\262?Uw\246\362\243\235u?,f\300\263\3521\250\277Lw\331\004\356r\304\277.\036|Kz\351\220\277\004d]\300\t\033\255?gI\264\"8\033\234\277W\230P\265k)\253?\272Kh\014B\351\200\277\315\013\303\244\0300\265\277<K\201\220g\337\275?8\250\001g\344\022\260\277\237\260\317V\027\375\254?\300l\021q\335\224\243?\270\312\332CY\351\205\277\371|\377\036\002J\251?\211\224Y~:\307v?\256\243\374\317=\024\301?\2216\373\317\255\274\275?D\206p)\227h\263?\361\361\332\214\260\365\332\277\200\342\210/\275l\247?\025=\366[\317\301\262?\303\027<7\334\332\253\277\275WG\307\353<r\277\365\323\310\007l\005\202?\376u\235\227\347\323\242\277\r\177wD\257H\310\277\352@s\256Bx\277?_mG\337\t\024D\277\232a\264\272\031\355\304\277C\2002\362=\244\316?\253m\321\0017\255_\277jNS{\347\203\315?\366\035\2656\225\366\304?v\207\255%\212\264\252\277p\2169H\230h\310\277in\"w\243i\321?.F\327\237}e\302?\023X\352\344\355\242\312?\037q$\335JK\245?\007\363\034<\201^\307\277q;\272\005\316\323\306\277ql\010\331\334\317\206\277(j\017h\271\273\232\277\354\275\240D\361\006\314?#\376\236\350\030\264\253?Y3\020V\202\274\277\277D\355\303P\223\246\250\277\341O3\206\307\323\216?h\021\263\330\'Mz?\362\363\031i4\226\306\277\215!\014y\314\004\312?\022^p\342|$\264\277\317q\273\006\021\337\245\277\305\255\355\375;\306\213?`i\325\207\037F\301?\351\277\360\374\010\024\205\2777\223\247\357\250\026\244?\257\376\0343\027\234\276?\231X:\265\377\373\303\277\006>=\241\350\352\240?\005\2240\246\220b\310?\217f\227\214F\211\261\277*)5\220\036\201\300\277\247S\347\027\273e\307?j\216\007\321\322\323\311\277\350\250A>Tm\263\277\267P\230\247\335\007\257?Z1X\365\261\031\306?\323\250\024\007s\374\263\277\342\300&\320\306\320\250\277\316\005n\330\234G\263\277\243\251nWOM\241\277\315m\252\365\252\014\260\277\227I\370\367\363\256\244\277\256}\220\037\264\264\305\277T\227\2465\277\314\242\277;\337\302\240~!\220?\270\177\010G\353C\265?@\277}\013:Z\300\277K8\203\n#0\225?\252\216z/\310\351\203\277\353M\240\357\353-\206?\274\264\t6 \334\310?4\313\t.\025>\270?\326\250N\254\277\352\303\277\222\245\341c\373^\277?\220Dx\232\177\321\271?~\372\225\225\363\305\266\277\023{\352\227k\021\266?\332\212\003\002\303\367\271\277\221\263\'\271G\323\264\277\366 \004Ib\222\321?fR\317\r\227\311\264\277\007\247$\031\334\301\231\277\266~\266>\035\247\276?\361\325\224P\023\355\245?\316<V\224\362\251r\277\314\310\331\332X\203\305\277\020.[n\361s\274\277\370\234{\240\367<\317?\246o\353\216\037H}\277\342\204\'\233\001>\300\277;JlvF@\322\277\0035V43\340\244?\033\363\307\363d\211\226?,\343\255DL\234\302?\027\330\221\315\035\234\301?\003\250\254N[\351\232?\345^\t\003m\204\245?tZUh\331\253\302\277\275N\2773\305l\300\277\021lb=\376n\263\277b!\247a3\233\266\277W\306\251\014\r\244\264?\324<\217\262\200\357\254?\342\"%;q\321\267\277\271\225C\334H\n\300?\310\265\344\304\342T~?/}q\010\271u\263?d\322\303\336V\031\255\277\250)\305\330\275\367~?r\253\215\237\025\356\233\277<\327\3404\366z\306\277C\303)\202\334\336\255\277\'\370\005j\214.\245\277\033\001\'u\256\347\304?\r`Rl\227\024\245?\301\316]F\327\000\260?eyx\242\225N\222?\343q\010D~U\267?\n\354e\345((\302\277\267\236Sr\360\255\264\277\316{\203\036\313\353\277?D\262\246\010z\026{\277\210DX\002\355\312\270\277\225\214\336<\216\214\275?\367p\320\347\214\307\312\277|\221\340\r\234\324\243\277\236\243\335\313\310F\261\277?\2233B\262\326\267?\312i\271\0058\314\215?\016A\323VE\377\224?k\375\332\350\321S\261\277\3744\031\371\324\353\233\277\326\342\320}\210\026^?\014g\247\3434>\201\277+\024\320B\375\260\267\277\202W0 \370\312\321?\345\032\025\342\016\220\267?H\336l\334\312\365\241??\330\266\205\242\247\320\277\234R}\206j\300\264?9u\322\014\245\346\264?9\260\211I\3118\303\277fU\253!k\374\303\277\330t\026\312\3720\264\277\021\273\376\230\307\000\243\277\257\311\263-\240\nY\277\233zQ\016_9\247?B\230$\210s\023\243?\272\022\336\271?\022\305?i\245\354\371\330\262\277?f\0073\000\245\345\262\277\277#\266{\026\253\276\277\260\000$\256\274\253\254\277\330\271\243\342F\252\241?\003f0\000\232\340\277\277Vm\006*\264Y\260?\205N\212D\177%\253?\316J\217\267\247\027\321?N\311\265\236\265\354m?x\215\223\332\360\006U?\333\000t\261\\\243\245\277\327\245\341\250aZ\256?\270\267\202\014\256\010\300\277s[\236\037\002e\326?`O\231b(\004\312\277\377}\037a\021\236\271?Wp\210\332\377.\267?\246m\020t\345\366\210?Z\241\335\217\276\224\312?\021\237\'\231\n/\261\277\244\2477L\247%i?dA\263Q\353\272\273\277\360\r\352\357\031\323\243?\3718%3a/\247\277\353J\257g\3234\307\277;xd\344\331\206\267\277\337H[-\t(\260?x\350\231R2\004\241?\330ZWL\2364\263?\017\220\246\271R_\266?.\221\357\377\3606\303?qC\210\252\214\\\323\277\312\t?v0\033\270\277:f\307\314\333\263\300\277\036\231R-\017L\256?T\032\346\254Q\233\316\277\371\212\233\310\243!\262\277\245/@\210\360X\245?j\256r\273\272\272\301?\016U\237\234(\343\264\277>\177vcTQ\300\277\\*\t\244\306\021\307\277e0\271\263r\341\326?\272-\214w\304\376\265\277#J\213\213\307@\305\277\014\300s\014v\251\243\277\333\346\340:t\275\300?Q\016R4k\177\204?\275\371j\212OH\302\277g}ajv\033\271?me\260_}\212\302\277\212r.\034\004\021\273\2774S\220fuZ\307?\0005\270\376M\374\244\277\343?\332\242a\347\275?\365-\327\274\265\025\306?\026\343T\240\253?\325\277\210\017\376\204\303\273\242\277\000\336\266\r>\255\320\277\320\033F\376\\F\233?\317\366y\253*/{?\r\277vS\343[\272\277\035\273djo\242\241?\317\326^`\303\331\177?\314#Y\"<\263\263\277\371\242)\306\340\370\240?\342\376b\347\025\350\261?xey\007\223\267\261?\004\215t\257\343\323\202\277\340g\307Q\370\017\227?\261\230\272-\024r\267?\027\254\252\036{\003\251\277\023\3318\030p\203\230?\245\356\343\333\367\024\265\277:\224\034!\350\017\240\277\321j\\\340&\025\270\277PJo\306\270p\214\277`\371\326[\t\002\242?P1\217\354\276\227\274?@\237\0329\302\022\240?o\022\216\3012\363\263?\003\031\317\266\305\350\242?\332q\274D\217P\300\277;+tB3\357\300?\356\242\377\325\346\317\201\277jeF%\300;\240\277\3130\221a\366\213V?^\241\327i\355\270\225?\t?>\346_\177\265?+\232\371T\030\214\207?\230\2335\205\216\020\254\277\265x\223<\006\033\310?!\247W\360\232Q\262\277\007=\313\275\204&\214?\177\225c,=\203\303\277I\021\224K\334\220\256?\344\007\177s\252\205\262\277\377\225s\206\032r\264\277m<\273\027\2256\205?\322:!\237E\374\256?\214?{TF\034\302\277]\022\214\354\364\316\231\277\234\034\225\003\031\240\237?>t.\276$\265\252?}\0067svP\266?\347\340\ru\351\313\314\277\021M\374\242\345j\213?\255\252S\314\267{\225\277p?\263\022\320]\310?\335k\275\202_O\267?0X%\333\324\'\213?\035\220a\010\300\302\323\277\377\227\372\211\007\327\215\277\331\235!\271vO\257\277\022[?\034\3113\276?b\030\361\030\272\274\250\27763\340\266\240\374\303\277\024MQv\237 \242?C\270S\341\306\306\265\277\272_^R\304\022\230?\\\1774\0236\323\252\277./\311\024=\243\265?\251\022\306\035@\332\275\277\025\270]\323\201\332\261?\321\036\000\247<\335\230?\016\237L\323\303^\244\277h;[I\"<\263?\252-\364\332\t\313\244\277\251\031)\271\352\334\276\277*\301\272\360`i\244?3\3475\273^\220\304?`\273T]\364@\264?>\251\372\242\336\013\224?\024\360\240\033\271\336\304\277\250\365\010\322<\354s\277\352\274\256)\"\224\242?2\341\346\014\365\254\301?\002#\330\301wV\224?gNJ\251\301B\311?\027\355#,\007\244\265\277\370me\305\314\030\272?\204?\204\334p{\272\2778\030\025T\221\355\257?\253\253&\242\352s\224\277\023\304\032di\243\251\277\241Q\357l\356\331\275\277\025\027\014e\255Z\244\277\341\231\301*\342\203\245\2779\251\2279\206\225\250?\236\016\320\005\003\r\247?d\177\026\235~\036\315?\\P\327\205\235\227\245?6\r\246\024\027_\274?v\373\022\313\342\363\323\277\027y\3225[2\321\277~\342Og/c\246?C\030H\271\203\006\247\277\255\203s\013H\371\240?\317\230[Z\031yr?\262\346<\303w\353\264\277\203K\026\363\t\336\266\277|\265\335\265\351\304\254\277cM\016\333\347\'\235?TJm\336\033\261b\277\014\350R\370\177\372\314?\242nb\327%\257`\277\250\355\"\203\347\367\237\277\325\202$\323x\'\301?S\350Q\277\322f\315\277\221\'t\365\331\263\250?\276\301\313P&\250\202\277\024l\362x\250\301p?\020\031\246(\t\003\232?\315s\362 \331\373\227\277\205\270Mb\265)\252?\344<gu\255\016\265?\3717\340\230\020\276\227\277\276\037Z\235*\032\234\277\275\276\235l\332\325\262?\037P\020\355\351\345\230?r\236\325:LO\265\277\266\264@\034,\300\275?\227\031\203\356\266)\273?\345=\302\366\225t\204?\001\031`\211\004[\261?\242VO}\020b\222?Q\260\241\236\027\353\262\2778\030\247\0163\007\303?\307\277\220\025\263]\305?\333x\376\350\202\226\310?5\035\224v\353\325\241?\016P%\210XX\250\277\" &\253\363\215\254\277\376$Bw-\262\263\277\272e\310\256\266\300\264\277#\014\307\210\247\371\226?\347gN\003Y\317\314?\232S\342\344\245\372\243\277\200\030\264\264\305.m\277\264\311\222:\336\014\274\277\034\003\271NK\"\260\277\353B\357\353\275\311\244?%G]\323e6\303?FK\216\351\345L\271\277A\1775o\337\342\266\277\\\376\220\"\262\330\266\277\317\332\230\254\300w\315?\233L\224\347/\275\240?^\245{L60\300\277B\340S\313&b\300\277\027Dh\333\214\376\274?\0033\n&b\204\243\277\357\245\217\310V\376\207?\262\301>\036\001\320\267\2772\017F\252e\213M?\251\037\035?\361\'U\277\252p1\220F\344\234\277\037\317\204}\333\301\265?wr\005\231\311?\311\277\307\226+\"C\246\200?F\034b\372WV\300\277M\1778\365\306\035\267\277v\232\031\005\273\326\276\277^\177\033rd\242\247?j\3239\2676v\305?e\365\334\314i\237\254?f\004\266\016\262\241\256\277\263\312\270\000\214\321\266\277\220\343\035\236\336\325\302?m\253d\231.\265\251?B\340%^>\305\235\277d:\363[\004\t\305?\035\001h\246&\213\256?\307a8\304f\233\270?8\344{M\323B\312\277\250\007\366\203y\332\240?\033\270t\376.lz\277e6\241y\\I\272\277dP2o\206p\274?##\257\262\200\252\261?=`\375\020L\233\302?t\313\373\003\266\035\274\277+\'Sd\347z\307?\005\332\321\225O4\242\277\367{\302\312}@\263?\3525\300K\3232\301\277\342^\207?u4\223\277:3\334$f\300\230?\033\3557\315:\232\320?\217%j\2367V\263\2771\211\240\035\023\314\260\277\026=S\350\354\315\236?\211\327\345\316\334\026\273\277\201i9\030\367`\223?\311\320\000\236s\235\301?g \224W\300\033\320?~\251\236j\3261\261?5\"\206qQ\262\246?\t\310\002\232\327\311\274?\227\353M\307TN\275\277\336M\223\360\345\250\277\277\222v={Abv\277\350\237\003\020\006\r\307\277\201\213\336\004\tT\246?\374#L\207\265r\243?c(\371\271\263I\267?\020\002mJm\010\272?\340\n\314\313\354ev\277\247ZQ*k\210\322?/W\330\310c\327\272\277L,\253\374\376G\306\277#\245j\314R\333\272\277F\274\223\254\'\344\303\277\351\305z\374\315\013\273?\013\332\274<c\226z?\004\345\331\3508N\260\277\215\222\362G\322 \270?z^&+\014l\242\277\302\031\314I\365\277\303\277\327\000$\275a\302\321?r\177\036`\361?\314?\272A\322\032\035F\240\277Y\010\326\306\274\365\252?\343\371\241\220\214\005\263?<=\272\2325\206\300\277\367=x\020q{\303?\306\312\037`w\306\257\277\334\314E(\206\352\257?L\2531l\245\220\251?k\222\352\233\2240u?%\030\257\327\371\223\241\277SC\340\262\345\252\222\277\177\001\352{\234\237\241\277`\315\226Q\320\252\301\277\344\257\224\235\031R\266\277zg\2545\2673\214?\330\306\177\250v\262\267\277\217g\260\355}P\264?\314\274\014C\330\374\213?\021\034\242]\022\221\305\277\354x5V\364a\274?\253|\202O\315\245\240?\366<\230\330\352e\245?8[\214\222SD\305?\332\334H\372b\273\302?\260\255\243\352u/\230\2774\240\255\"\245z\316\277G\323\305\203\300\364\216?}:\300\226\266\023\301?i\316\277\245\022\016\241?_*\007\250\n\034\265\277j\0215\351Z\373\306\277\206N\006X\366\362\272?\214\220\276\220\023D\262?\317\241\255\0013\344\234\277#\017\003\374\\0\214\277\310\214`\201\342h\250\277T\241Q\3325\270\260\277\251U\322V=\255\237\277d\366\340\372\002\271\303\277\213\231\017\263:\206\227?^y\004\263\320\257\263?R9\027\"~\241\317\277\360K\240\251\005i\251\277B\374BD\004=\237?c\345/\361D\302\260?\326\240\271q\325\245\301\277\177\257\312\025JPZ\277~I\257\255\274\270\271\277\036{\323\265\005\240\304?\014a\215jE\234\221?N \255L\335\001\252\277\020\255\304`\225\315\324?\002_5\326.)\256\277\r\206\326\253y\344x\277\276\305\007\234]\316\301\277\272\314\223R5\223\266\277]\337\354SX\250\216\277\"c\202\222\033\237x?\021\315\211\027\032g\261?\026\307\331\362\006\215\272?\376~0\'\263\344\315\277$\321% \2237\257?_;\031QO\"\247\277uw\361`\376c\244\277\322\226\251M\341\333\240?ad\000lP\334\307\277\037\0029Iv\351\252\277P\202I.\201\321\241?\003#\276a\260@\321?\203\315E\3654\002\303\277\372\002F\307_\372{\277\024\017\025\251]\217\207?M\010&\233\250O\303\277q\223\267\177PsZ\277\353,(_\232H\275?\026\022\207n\337\262\311?\3779;K\260v\301?\323#v\237\225\376\273\277\261\320\210\313^?\266?\303\020\353\372:u\313\277\314\333\300\316\364O\275\277P\214\347\t\001\212\244?\345\266r\232\222\020\267\277\375\305iE\033\275\263\2778\277c\363r\201\223\277\3141\335\312\260\340\252\277\370\344P\003\\\202e?a\327\263\371\340\261\256\277\021\315R\335,&\237?\365\353k\203\234P\265?\013\0242\310\020\274\320\277h\240\300\217\261\352\221?l-lM\243\267\203?\201\265\245%#\336\260\277!.tuc\267\255?\323\303\367\312\274\376\261?\263\214@\320\270b\274?S\215pe\006\370V\277\0057#\364p\313\246\277\'e\347\024M[\214\277w\272s\'@J\305\277\201E\256\363B\220\234\277_N\322\322\241\345?\277\241\315\233\232I\241\303?\311\326\326\374\232\321\300\277\272\026\206B\323u}?\327R\273AE\342\267\277a\310&\376V\354\305?\213\251p\343%-\204?\210\325\177\034m0\250\277\315\321\312(v\317\247?\207\237c\263\230g\323\277\r\032D\302\035\361z?\332\332Y\313\364\031\246?\236\212\355e\237I\264?\t\307\310\272`\020{?\372\225\330\206\251\302\300?\365\342=\261\346\201\272?\313\255\007\014\254\342\210?}\246\355\225]\351\274\277\353\0341+x\323\224?\343<\264(Y\373\260\277wO\004\362\213\376\222?p\017J\211\224\006\233?\366\355?\317\3065\265\277\327&\253X\277\344\206\277\016\333\002r+<\177\2774]\215\213\3675\267?.\302\245RX\305\311\277\005\200\220\261\017\203\263\277T\324\354\025V,\266?\032\234\325^\267\330q\277\177\367\316z\322\025\245?\202\353=n\355\345\260?\\\342\255\'\223^\246?\230T\337\273\260\271\303?\021\262\272o\250\027\217?\030d\220\216&\266\275\277\2325\212.n\266\317\277o\254V\201\337\340\255?\355{E6\245\230j?\007\327\242D\373\332\263?\337\254s\023v@\216\277N\227B\263C\323\276\277\303V\256x\277\337\310\277\335\216\351_b\010\265?C\363\277\3715\016\260\2777.\204\355\212\320\267\277\235\302\027\246\353\321\303?5\224\234\241sW\262?s\3247\201\001\"\275\277\320\361\210\347\240\340\255\277\333\t\200\241f\310\325\277\240>\000;\2523\253\277r\232\261\261,\262`\277(q<\n6\337\261\277\313x\035\3614\034\302?\215@1\266~\023\252?\2752\344\270\364\260\277?\3458l\263,\241\230?\377\006\006\3122O\231?A\t\245u\366\246\237\277\337\334}lR5\305\277\341\266\371\223\312\265\250?\340x:>w,\236?\246\304qF\230X\301?n\262*\206+\345\250?m\207\311\232F\360\245?\256\235!\202\307\275p?\355\036;\037\210\263\300\277\334\261\374\"\213^\311\277\335\346\335I\212\333\304?\222\353\311\242\333\037\262\277u\341\275\350\327\305\231\277\027\"\236$\036\005\240\277\322h\016\207O,\214?\266\274\322J\255!|\277\240\262\032\25559\274\277I\311\000\362\350\357\306?V\035\315\316\211\177\303?\374\036su\r,\275?\360\024;\003S\007\263?\022@\251\2378!\247\277\204\234@\313\330z\306\277iq\345\2128J\311\277V~\330\037\037(\311?:\'\212%U\234\274\277\275\235]\353\\\222\251\277\2421\274\250\304\344\310?7\222\333\322w\266\223?\263a\005e\372-\215?~\312\252k\365\306\267?\214\211\013\235\376\217\254\277\252\177\313\273?\220\226?\252\265\223\377\031|\272?\317\337\207\364\254\215\256?\342\002\232\321\026\270\273\277Hk,\335\006\253\262?i%S\310\'\034\262\277!s\270\342\237\210\251?\224\363.V!I\300?\237\215\340\350E\222~?\3175\033\324\273(\334\277\376h\024@\'\271\261\277\240\261\330\363\224\354\254\2770\022 }oJ\262\277 $\371]\363\372\243?\214\037\260\367\253x\272?\017f:t\326\025\266\277\240O\340j\223o\320?+\212\327\315\216\307\226?\024\276\230\034\371N\257\277\022\205V\033\252\037\217\277\321\206a\367\242\300\317\277\013Q\241\341D\r\241\277?\215\364\003)\334\277\277\254\205\377\322\360\227\267?;\347\253\303T\017\271\277K\177mU\037\337\270\277$\034&\214\002\037\243\277CGnW\007\375\270\277\262`>\322\272\350\262?\333\312\241%h-\314\277\263\276\217O\005\300\306?\211\347\315\325\242]\215\277\277\353\323\353G\212\240\277\277\242\0335`\363\311\277[7\304\013b\244\233?\224\354u\355\033\205l?\330\215{\3152\233\266\277\256\004\275>\350\033\310?R\034\266e\tf\276?\317\211\275\232\206\323\253\277\3746H\242Y\017\315?\'$\372\026V(\241\277Q#$\345\036a\271\277\307\024n\242\234\273\315\277\317\202\0004E\212\306\277\030\001\260@\334N\242?V\'\013>\366\254\210\277\207\265\325G\315>\313?,#\014A@\327\315?\332{S\035\335\227\202\277+>E\313~-\302\277\203\324yv2\021\250?\264\374\333\305\267%\251\277ilAe\336\264\263\277\033S+\356\033\216\277?]z)\203\233\321\275?\306\344\363\024\310\252\273?\376\3634\002+\313\306\277\212d\320DB`\275\277\2764\017h\355?\227\277\340\227p9\246\352r?@\260\332\276\271&\272?\300/x>\030\n\305\277\3508\346$\023\272\264?\364b\271S\332\006\263\277R\025\313I,\023\317\277\375\252\031\365?\275\204\277\214\033\037\372\371\013\262?\224w\226\3751C\315?\237DM\n\205\271\303\277\346Q\010\376\347+\232\277\033\2076\323L\271\243?\352_2\355u\013\230\277\334PU\034\377\274\243\277YS\226y\307\266\255?\342\341\231\rg\370\245\277\273\332\325\215$|\201?Jf-u\250,\260\277\206+\301\325V\332\243?\212\007\255\013\343\320\262?\261kBn\351|\264?j\337\007\354\241e\262\277\341Xp\347`\262\243?\033\03329\203\014\221?Htxn\202\223\276\277\003\240\253\2515\334~?\3628\234\242\224\233\254\277\3462\223\377>N\305?i\365\035\007\301\230\265?\014\204-L]v\224?U\203\324\245\362J\206\277\306/\240\312AF\212?\000\272\375\320\241\010\271\277\275\032\304\232\302\315\321\277\246A\010\373\306\035\262\277\241\245\307A\235H\262\277I\372\317L\277\002\265\277Nh\200,\023\265\266?\212\362\"\373c\261\312?\267\272m`\362\367\312\277\177\335\260\371j\346\305\277\3516I\265\312\363\221\277\322\375\010\201\014\211\313\277x\243\2019\020\274\311?\340\360\202\276>\241\252\277\2117C\277:w\305\277B&\252\\\205\247y\277\371\310\261|\344N\243?\260\220\246\001\251[\261?\357\375\267\345U7\257?\262r\010\rH`\301?7\014\201l\351\223\264?\367\246\363x\347@\247\277\224\023\242\273@\225j?[\036\316\365\236C\306?\256\322\214\236\223\201\264\277\365hD/W\304\242\277\261|\272\274\341\375\257\277\355QX\272C2\254\277\377\217\r\314\306\334d?\007\226^\230%I\225\277|\036\357+\034w\311?N\207\355\213r\030\274\277\226\274\025\327\263u\224\277\324B2~V\206\301\277\272\334\203D\235\255\307\277\262\204\206\374*\002\267?\202\036~~\341Q`?\302>@\267\365\267\231?lZn#9\370\250\277\2627\236\336\243B\276\277\271\002y\267\305\266\255\277 j\006HY\367\247\277\003s\026\313\036\357\234\277\323\323\t\277b.\276\277\034?>\232kk\322?\271\311y\233\373\005\207?5qm\275~\361\254?vj\000\016\331_\223?S\351(\037\230y;\277\262M\026x\276\177\306\277\223\375\343\217\004\310\276\277\367Nz\336\001\026\310\277\004\274\212\256\"w\225\277\177\277\365\354\251A\264?Z\r 8\377\311\227?1\227n\206\031\013u\277\221\247y7G\253\303?\213\215\007ib.\215?)\206A V\246\203\277Q\177_vuN\260?=\360.\t\233p\317?\254>\343\316k\224\270?\026\231\243\220\322\260\263\277\203\266\375\273 \373\305\277*7\265F\360>`?(\221\214=\255\276{\277\301[\322\323\237\224\232?)\376*r\0323Q?\217\261\230d\366<\304?\223\006\201\373\216\252\300?\026\005\017B \346\252\277\035R\252\r\204\227\273\277\231hDE\337\320\265?\313\272X\332\001\264f\277\327\216\004\212\201\205\250\277X}M\325\023\033\257?\333\013\272\007\301z\265\277/\014\357f3\304\272?\252\267f\321sV\311\277XY~\250\205\005\322\277\211\242\005\260\213\262\246\277J\247y\nGP\204?\310!\n\2624k\310?\221\330\272\217 \301\236\277\320\347\256\313\314\200\246\277C!\035S\232`\301\277\313\035p\026\256\253\275\277-\\\r\024\350`\212?X\314\361\352\242\346\314\277\034\202yEL\312\220\277\353\223\227\313b\334\222?FV\217\222x\032\267?z8\217\330\253\366\251\277\301\t\030\010\333\255\275?\363?b\364\341\244\243\277\203\3117<\214\333\240?,\274,-\201P\254\277|oA/75\266\277A{\215V\235\030\255\277)\014\026p\222\256\272?\340\013\336\240\003\237\304\277\250\227N\350:\342\225\277;\222\332\246\335\266\274?n\342N\006\014V\267?w\232\255~\031\202\211?\220}\022\275\rz\201?\267\000`\373\313V\251\277E\321CC[|\276\277A,%\360f/\202\277\022\330\236A?\031\276?\311\"\216\010\301=\306\277\004\230\240\001\356t\266?\\#4\254\027\315\254\277 \017\205.8\007\234?v\305\037\317\312\021\320\277\277p\314\255\224\304\267?\022\311\354L\360\243\274\277n\3349\247Z\227\301\277)\214h\334m>\240\277\005H\307/\217\246n?\353\231\000\315\024\254\302\277\306\025_\263\037*\320?aN\252\245{\336\256?\372\260\205\261R\253\222?\r\276\256|\314\276\245?\0059\257\346\027\350\263\277\334\016z\201(\350\200\277\023\310A\177\366\002\241\277\033\323\003\037V\206k?j\312S\234RA\244?\317\rH\264\271\030\253?ZO\020F\262\301\253\277d\036\302Jv@\271\2779Du\242&\035{?m\261\315\310\026N\316?\337\341\025d\224q\304\277\320m\342\377\233\002\216?\202\217\340lP\225\277\277\210\261\233?{}\263?\255\273\034z\213q\303?Y\354P\343p\266\261?\037\202wJy\302\227\277:\232\002\364\233o\307?\305\247\276\000\276\212\300\277\352\265>\032*\202\253?\3740\013r\331#\222\277\026p\002\232\006\n\265\277d%3\271\273\017\243\277\3269Hd\372f\264?\223\010\366ux\235\302?|\352\213\311\261\275\255\277\331\207\247\211\t\210\274\277\3216!\314Y\346\220?\220\203\365\253\241.\221\277\310\014\024\360\032I\300?NC{\376L\255\243\277\275\302q\275\304\276\306\277\263\301\364\357\243\232\216?\322\3732\264\3332Z\277O\314 \322\317\007\225?\"]l\177\024\331\313?S\314\320T\355\217\247?@\250?|\317\024\260?0x[X.\030\311\277\003\2515\257\005M\246?\257\265\017\236\036D\300\277\342\256\201\203\256\317\314?\335+\216\211\203\244\264? \333\261\321p\002\240?\324\307\327r\265M\240\277\217\016\017Q\032)\256\277l\333\316\355\370\233\300?B\336;\242N\346\247\277rs\313\253.k\261\277\241\252\266\017!f\261\277\337\376\353\t\362\206\242\277k\372b\353\372\254\314?K?\0161\035m\325\277]\353f\370\255\250k\277\324\034\300+\"\346\246?[\311e\t\265m\275\277\253\261o\346\2729\265?\032\250\0263\003\034\251\277q>\014W\010h\200\277\245\032>\031i\242\265?\000n>9\355\320\246?~\324\225\251\214d\272?\262#\006!\010\223\303\277\245\340\322&\251\033\263?#0\217\3352\350\221\277\260\326\373\253\177;\325\277\237\377&n~{R\277|\010\224g\357T\256?@<\260q\355\361\250\277\266HG\256HI\232?k\232\355i\227\211\320\277\236\360BZ\257i\277?\325\327;\031\377\240\200\277\244\033(\343&\214\312\277\271i\236\361\346\324\272?\\!\005\322E\003\265\277m\\\242\027\201j\260?x\342-\343q%\230\277\205\003[f!_\273\277\374\363\262[\365\216\262\277\302u\222\003\257\214\250?\365\366&\333 \373\272?WUH\2503\242\245?\007\327\025G\366{\300?}\303\\\347~\261\212\277\033\246\270\201\340+\275?\\\233\306x\245\315g\277\200\340\216\345\355\350\261\277\232#\202/\340n\237?09\245\\\0174\245?/\0130\3619[\266\277\373\316#e\363\363\225\277\255\327\254\271G\312\260?\252\356\356k\263+\260?Op\240\206_\225\305?\305|\332Cz\033\223\277(GC\3061\257-\277\207\301\213\006QA\304\2779\r\031*n\336\244?\3670_\0215K\244?Q9?\000H\272\253\277\313\272\214\211A\221\272\277\014\032\263\224\262({\277\325D\355)H!\246?\367n\262~=\221\277?\363W\341\302v>\254\277\0359\312i;\303\201\277n\177\270|\337\022\262?:)\323\252\356 n?B~\303\3408\365\264?\240\036{\323\370\214\244\277\226(74\270Q\313\277;\0060\316r\242\260\277\362V\366\254\375\214\300\277\230\372~\036\377r\267\277\302p\255\0109;\273?M\000\253\215k\316\233?\245c\242\246\022\037\244?\315E^\037p\307\323?\326\252l\250\000\270\264?i7\373\331RJ\251?\024R\035,\tq\301\277Y\347\001\301\245\004\267\277\201\327y\204l\375z\277\271\236\272\235\366(\304\2778Z\377\016x\371\202\277\260m\333\257\226\212\264\277\323\263\377\205\217\255\221?W\271\215$\214F\252\277\t\377\263\302\263\177\321?\306,\242\257\300\007\276\277\256\302\315,\233Y\254\277N\332\350\006\tt\324\277\036\302f\254\t\306\235\277\375&\007\355\231\013\306?\273\377\355\251\002\225\302\277k\324o\024L\250\245?\321\340\300\034\026\027\221\277\260Y\007kA\213\265?\352\001\215\242\343x\211?z$\032C\230\231\302\277\022\004\024\2229I)\277\257Tg\356\371\346\263\277W\212-\230\372M\320?s\271\255\307;\340\241\277\341\224\341\333B\312\306?\213-\272\005\245\200\230\277?\367\302z\001$\300\277w\023lEFt\232?\357\022h\347\t\231\266?\001\203lS\351\217\277?\345\276\274\235X\003\207\277\315\026\237\364:Q\220?\376\363\263\211\341\345\307?a\021}\337\331\272\304\2773\247-\354\215\311\255\277\003gD_\014\302\247\277\210\001\307\005t\241\255?P\223\341&\241(\220\277< `B\034\222\260\277\224\261\306\212\333\344\273?\311A\313\336u\234\260?8\315\235%\214\233\237?d\274\330\226\334R\270\277m9\301W\326\362\267\277g\302\3574\2760\270\277\227,\264.\3574\310?Q\037\333\364\274C\242\277\317~\317\032-\214\215\277\002>\001\217\373#\221?U\205\010\227\376\360x?\232r\362\203\024\007W?\370Y\246\355\332\346\216?~\265\243\000\227\227\305\277\325\226\266\206\223\364\306\277\313\006\nwXD\274?\250*M\250^hy?^e\276\266\273\235\245?\262\267f\265\312(\267\277\033\374#x\364\010\254\277<t\360\014G\037\264\277+r\362ke\202\241?\274\277\273@\371\201\311\277\330G\306\226\203a\267?\340\3765\244\210M\273\277\310J\3714f\205\267\277\225_\333\311v(\215?\356\362{&\267H\261?\302\257\241\310\241p\200\277\310)\361MK\267\270\277Y\267Z:\207@\302?\345A$\274\352\213\236?\336\'\000Q\326\021\321\277\352{J\207\017\322\231\277\356\241\344G,#\273?\201i\353\3255/\215?\303T\335K_\023{?\260|\336\330\354\303r?\003\342l\214~\004\225\277*\344\221\260\205\303\305?\3043\236\227\035u\203?\to\370\350\035\333\207\277^s\2117\202\033\177?#\260\245\356i8\261?\246\346\225\177W\321\301\277\010r\253\210\306i\320\277\032B\250(\353t\212?@<\302\243\354t\257?P\226L\312\274?\300?]\270An`\002\330?\311\262\276NJE\202\277\177Wvwa\273\272?\032\275&\214sp\247\277kS\316\177\214&\303\277r~{vi\324\240?\301n\215\316%X\212?}%\243+\352\227\303\277\347]\334\n\"\220\251?\317%\003Z\243\t\321?y\317\"\352\264-\263?\272\007;w- \327\277\305)\255\303s\230\263\277\022\330-\335\323\025\223\277\032\277I~\026\323\203\277\017bN\323\323M\217?\336@\027\222\227\371\266?\350w\316&F\342\261\277\245\303re\016\215\242?\345a\233\031R\200\231\277?N\257\304\341\346\272?\236V^<\225l\262?\306_\267O\004b\226\277>\231\320\220\360\273\227?CN\375 T\223\234\2771\016\371\337ci\271\277\022\337E\351\235p\232?\215uw\370Gp\246\277\302\247\221\363\327\321\272\277}\"\267\320f\017w?\036` d\303z\276\277\006\320h\267\014!\252\277\326\272`\025\034w\301\277m\302%\022\363\021\270?\374\214\332\324B\302\260?3q\213t\224I}?\302\256\266\244\260\263\263?\267\034\234CW%\226?u=\353\3746\014\261?\242\212\3408\351\216\304\277LtD\265\247N\220?K\340\211\031})\253?\353\225\241\253\n\"\252?\325G\245\373\030\223\303?\321\255IaM\307\301?<x\002^k\022\274?\272\016\207\311\251]\201\277\346\357q\032k\251\261?Yi\251\231\035\'\271\277n\263\217e\203i\223\2772\303Ve\347\002\242\277\372iO\270J\202\233?(\267%\225\311T\253\277\'\276{\224\202|\272?v\220\214Mt3\251?_:!\314r\273\205?3C.\321\305\037\266?\rP\360\321\307\325\306\277\226\021\217;\314\362_?\213^#\232MO\305?\262\227\250\374\322\334\024\277\274\207\357Q\317\271\255?\364\232]\256\325y\262?\223\320\276]\256\271\252?F^1\335\031e\226\277\333v\320\364\035\304\261?\332\246\334\014\203S\204\277\372N\177\356Q\326\305\277\324)\240L\212u\303\277|\341\331\025Bx\246?\374\025\215h.\257T?\024\255\036\202\342\324\245\277\245\306\374\207mV\256?.\226\362[\202r\265\2770\250\266:\201j\306?\246\232\345\343\304\353\301\277\335\2604\314\032\207\226\277\275\342a8\036\n\320\277\273\231\351f\372\367\303\277\336\3036\243\333Vk?\202\273\254mg\343\304?\202\224\334i2L\261\277\306@\025\332\203\240\224\277\346e\304QJ\316\255?\355?[\026*\333\262\277\\\370\335\305\377\325\261?<y\345f\262\\\270\277\263!w\263\273_\235?UD\355\022\354\\\315?9Iv\226\243\036\244?\221d5\372}X\255?\345\362\220\247<\331\302\277\201\r\210$\334\357\244\277\271\235\340%\'\003\263?\341\347\312\035s\274\270?)K|?{\000\267\277\034x^\321\276\371\200?\260Q\023A\031&\304\277\273\351\307\316V\270\267?!\n\374\026\026\276\300\277\233\305\177\267 T^?\322>\303X\305\001\262?-\223\230\n\376\244\303\277\355\033\330\275K\224\231\277\2416`\3763\000\303\277\365g\373\310>\250\304?\027\326@\360\371\222\252\277\274\371\321lY\303\301\277/\261\025\317\274\251\270\277\360\222%z\017\036\243?\336\277\004\270,h\264?\034\255\361k\023J\217\277\267\201\272\323\362\216\305?\324\005\332\004\251\210\263?\324\204;\230Py\270?\337\251\375\323\360F\257\277O\3237\322Q\024\231?\276U\266\327\266\244\267?\022\271\325X\2503\311\277\231\331\243\367\0223\271?\227\343\262\333[\017\245\277\275\3639\007}\'\303\277Q\206\240\321\005\274\240?\300\223\261\245\315\333\320?\367\024*V\034\263\215\2776\2123\356I2\302?\264~\2550\201\325\320?\324\341\001\367s\316\261?\260\no\234\227S1\277\252n[-\217\325\276?\265\014t2\300\346\271?z\201\2734[\273\237?\346T\304\0219\377\250\277\263\272\257:J\263\254?\3061\201\261-1\241\277\021\322\370\276kQ\304\277?#\010R:}\315?H\235\304w\013C\323\277\225+\330_\363\252\246\277T\033\262\301u\345\255?\371\243\345$\370\224\303?C\327\225_\367_\271\277\"\261\354\374<\313\250?\025\262\256Z\250V\247?\377W\024o\223E\273\277z\363\361\003\367\245\207?\211$\333\257QJ\276?E3\"h\342\003\233?\007\000\370\202\244\273\306?\0264\026\327)\310\200\277D\234\033\347\325\032\320?\037B\341\033\2226\315\277\020\373\260\306\205\210\244\277Q \353y\305\202\217\277\274\343t\300%u\322\277Pn\213e\'\260\237\277V\202l\333a\312\250?\004j\024\315EH\226?\n\016\002~\205I\244\277x\333S\375\367z\240\277\270d\t\276\006\277\200?$\341\005\322\273Wt\277{\3164\242O\245\243?K\225c\3106\206\260?r\nc[\204+\300\277)\351\001{\375-\256?\037\271\314Kxy\244?\216@\374\201W\001\266?\035\024\201\276\212\313\300\277\000k\337i\225A\306?b\302\022F\221\351\267?&\207\303\214\362K\313\277\002h\033\261\230\347\302?LB\251l8\255\221?\200\245\266u\346\205t\277O\351\257ho\231\265\277\374\215\177.\363Z\260\277\272\2647\260@\317\236?X\304\275-\250D\275\277C8:I\233\305\261?\\K\331\005{\202\272?d\216\331\336\324\177\310?\322E\334Z\235\354\277?\367\216\252\3175\305\243\277\351\277\247\217\002\251\273\277\265\215\362\267\034I\200\277\254-\235\355\233\300\273?8n4|\023\326\215?\330T\"\016\333k\266?z\341\370y\307N\327\277ni\372\334\025\211\277\277\2458\372\337\311A\243?\331\365s\334\276\320\265\277\022\321[\0061\236\265\277A:lT-\250\200\2774\273Y\251L-\245?\364))\272\360\003\235?\0331\341\275C{\266?\331\325\277>e\000\274\277\302\000\255\030\310,_\277\344\223bh\370U\207?~\200\217\303\323V\277\277\035\252*\323\017\244\222\277v)\252HV6\326\277\303\211XpxB\250?\342\346\236\021\013\214u\277Q\357W4:\231\240?\241\263>ye\226\270\277\273O\305 &\356\243?\270I\326mV\026\223\277\315e\363m9S\275?\022U\035\377B@\264\277\200\024\250\345\262\r\240?\204o\223\245%\234\303\277\006R{YB\314\306\2770\217\273\034B\246\222\277W\345JW\272\207\210\277V\356]$\341B\274\277\344P\344e\'\310B\277TmY\014\270B\240?\240S\354\323\332C\242?\2168F;\357\363\240?a\347\355\375\022s\231\277\r:\307D\314\200\243\277\351g\267}\021\377\301\277\303\220\343&.\232\320?u\031h\244\th\247?\t\351H\003\004h\255?\315\206\337\221\357X\271?Q^OyZ\331\263?,\036\030a\364J\207\277\335\001d\362 \315\217\277\017C0\246\363\367\255?\305\020 \260\231O\242\277{\236\265\230\235\272u?(`\244M\361\370\275\277\265\014o{l\275\220?-\231\314\331\036A\260\277\t\306\366\3106\006\211\2772\335\223:8%\201?\343\323\017cn0\272?\000y\233\036\347\334\300\277N\001\200<l8\314\277\201b\265\253\305\233\262?\177\372\366\365\326\211l\277\323\036Q\"s^\263?\016t\353\210\023l\266?\247\236\220\256\233[\234?\326~\306\226&\260\302?\341=7\374\351Y\262\277zg\310\263\3368\215?\304\226\205\0077\232\302\277\313\2010\017>\222\212\277!\227\211\300Y\370\221?\376s\207\226i0\253\277`\315\225_\324\323\267?)br\224\201\372\227?\325R\250+\220@\262\277\241\371\020M\276`\244\277\353\323;$\272b\304?\022Fky\271\274\314?\372\315\007\234\3452\300\277\247\215r\237k?\322?\265]*~\220{\265\277KB\227r\037\226\263\277\020\263\320\274zG\274\277#\211\334\221\2120\320?\214\267\030\274E\030\314\277Jm\022\306\030\274\313\277\367\273\375\350\210\014\236?\370\224\346\2116\234\250?\006\362eY\2343\251\277p8\2776:c\221?m\246\000\343\357\252\311?\257g\'\037\264\236\214?\351\333\2562\253x\214?\217\245m\374\314\373\234\277\325k\271p\253\013\253\2778\235\321\337}T\265\277\025aY\021\335\000\301?\335q\302\3262P\264?gagQ\243\231\210\277\247\367\3478\347\263\242\277`\311\3705_\204\210?\203\274\341\010\273n\264?\244\247f\300X\235\304\277\362r\207\223t\225\274\277\216P\206\253\256\033\277\277%\257\302@\037\020\277\277+\220\025\014\007\317\222\277]\217\274\340H}\301?\363\017\272E\3256\327?\006TB0\220Y\300\277\210\310\253\275\203\211`\2776\302\257\000O\200\243?\201\230\274<\2165\262\277\276\365\335\213\355\241{?\242\3306\030.\214\271\277[\366\352\013\'P\214\277\001\237&\345/\326\273?lJ^\014\202\014\235?\216!\253\217\0042\311?\361\337\206\224\235\343\274?\214\177\030J2>\221?{}\262?A\010\207?\'\024\005N#6\243?\325\001\316\267\356\234\266\277\007j\020\313\003S\312\277\240/\014\205\352f\235\277\032qKrqL\270\277\331\033\2041\355A\302?\3064\260M\332\246\265?\232/\2472-\310\306\277\353\002\314\rR\333\231?\rK_\220\031\234\266\277\244\233\260\3210\265\177\277\337|s\362\334>\302?\261\317\356i\231Qr?\361\217q\022-\366\255\2773*\322\027\270&\301?\211\254\360]g3\300?a}\341\342#\343\245\277\340\215-\305\212\032\225\277{=3\361\265\361\302?\013\236daTm\216?\327\024N\360\345\275\227\277r\347\033]\324\027\244?O\370\037k+\244\234\277\341R\371\225\006\223\230\277nK\022)=X\246\277\277]\234\204\013\235\305\277\357\233\2530\337\274\256?D\250h\310vH\306\277tV\207\352\024\320\263?\266I$\364\2450\232?\177-.8\220u\307?Q\230p\030\342\333\245\277\232\330A\232\347\034\301?\034\275\017\233V\205\300\277A\017h\357\037\022\271\277S\005s\343\nP\265\277S\006^\234-\021\307\277|uG\265\226\301\302?\367\r\026\243\360\210\323\277y\261\375\016[\237\300\277\365\010\005\371\332\263\222\277{cUc\3743\304?;\216e\363(#\324?c9`\204\377Z\275?c\203JS\033W\240?<j\210\227\271\307\272?\2407\002+\230\031\273\2775o\325\344ur\300\277\340La\275Ro\220\277\375\371\300\306\220\027\303?\350\035\366sC\252\262\277\224\312\333E\206\344j?\031f`\2474\\\266\277\307N\224)!\241\313\277\314\243\357\345\365\355\305?\336\320\337G\207b\311?\331UNV\247\345\243\277\303D%#rm\275\277[.D\227O\217\277\277H\033\323;)\254\312\277!_-\372I\\\246\277Lf\211\310\331\305\266\277\024\037[\261\000\235\242\277R\324\341\2426P\274?\301\\d\263h\243\240?|\333z\203\222\275\237?\3574?\370\330\211\266?\302p\316+\365\030\242?\025\264\272\256_\202\272\277\210\310\222\377K\371\315?x\362\236\345C1\302\277\016\322\224y\355\245e?#|\201F\374\331\255?S~\"\2466\242e?\370\374\252\034\006\330\250?\237D\226\025,)r\277K\316g7\232\376\225?\021,0?\231~\305?\205_9>\235\177\241?N\376\335\331\345\037\230\277F\237@z5\312\320\277\230.\305\271\007\007\271\277\342\252$\205)\033\234\277m9\006s\222\245\213\277\213\345\206\347\376\215\266?{W\377\270\304\036\251?/ZT\024>\330\202?%\365\362\243\345\364\303?U*\033\371\224g\250?\013\263\320\200\337\323\275?O\345(d\254\366\273\277\030\016>\347\360r\270\277\265s\367\246\362\330\241?P\276$\374\303\225\266\277\257[\3510\361\363\276?u\344\246\325\277\262\206\277>\325==-\345\220\2778\017\377lAH\300\277\244\262\246\276\330\000\315?\302\242i\244t\272\235?\273_\234T\250\317\266?\321\253\352\020\242(\233?\212|~4`\232N?5z\367})r\254\277a\256\200\266\314Qt\277\224y\210\244\r\336\262\277\226\315\300o\205\260\205\277\321\210\233E0.\307?\341\010X\213\001\005\200\277\013\200Sy]\341\253?S\007\273S\002\014I?\277\273\267\357h0\301?iN\221-\016\341\314?c\252\271Y\277\177\216\2779\341D\366\366\024\211\277\347\241M,\377\356\301\277\2201\315\274\300\327\300?\3079dv\211\352\202\277\245\021\263\342\0258\245\277\245/\371uC\321\306?\032\331\232(\344^\225\277\003P\301\301\354\364\231\277k-\242\343\237\374\236\277:~\224t\367h\225?\037\207*\031\256\023\244?\252\013\251|\265&\301\277\245\377.st\232\302?\036\335\343\r\335\252\252?k\224#\365\200\277\244\277\331\355\301\025d\350\313?3\206\272{\303\235\206?<\242\013\217t,\241?\202\325\306\365\361\021\255\277\330\332z.\375\302\206\277\214\220p*\356.\265\277\264n\327\t\236\215\225\277\221F\032Y\321z\221?\300\337)(\'\370\247\277.\301\250\354\267\302e?\245\316k\373\324\217\310?\207K\200ub\034\325\277\'n\026\023\337\265\207\277aE\236DW\257\264\277A<@\251\310\211\263? w\244\2119m\255?\"\204iy\342\213\201\277\343O\247\353\270\265\211\277v\'4V\322-\240\277u6\246p\006\227\300\277m\032\256\362\354U\224?\246y\371\244\313\230\264?$\262\325\351\3345\306?\201\025\311\216\2573\222?\370\207\212F\206f\310\277\274\366\261\264\352{\263?d\263\3355\216\236\244\277d\036#^\302\330\216\277q4jh\005i\247\277R=>\351\027\035X\277\222.,^\256\303~?\237$\360S\310\242\270?-\303uf`\376\233?v)\3542\352\343\221?h\\\t\264y\246\260\277*\260\0237\366\231\271?3$q9\206q\274?QKK\371}\340\303?\312\307\321\237\337a\267\277\307\373S\214\234\366\274?\'\241r\213\360\341\264\277R\302\360\372\304>\226\277\246\320\206\302\247O\301\277\332\306\200:\320\326\301\277\371\005\371\363\026\037\311?\004\226B\003G\326\252?\241TX\021\344\206\321?\246\346\023\007v\215\245?\026\200U\312K\035\300?\222\344\341\007i\322\316\277\214\020W\215\325\342\250\277\032\023\347\373<\353\252\277\312\273\216\374h@\266?j1\253\246\016\266\305\277\203\024\303y\376\025\262?L\375\010\325{h\310\277\253\264#u\250\223\312?x\320\0363\3013\271\277\363\236\365\352X\032\243?\354a(\001\242\340\246\277\032\232\231\334\001s\300?W}\214\014\333?\262\277\267\261\377\014\246Ve?T\301T\"\330\234\237?d1A\376\341q\203\277+\277\027\231\2223\271?\313/\276}\";\257\277u\240M*5\267\331\277\371\\\365\340\311]\243?\334\014Kq\204\270\256\277\005\020G\026\326\031\300\277\252\250\030\303\226\372\265\277+bw\205\244Cg\277\303\200\3306`\267\225?\361\005\260~j\032\226\277k\322\301\264&\005\237?DW\372.z\374\217\277\361\320\030b\250OG\277= \315\221\206%\303?n\312\200\032VN\226\277&f\336&c\233\235\277\212\274\301\273\237\037\227?\036Q!\340\222\304o?\"9j[E\341\225\277n\362h\352\262\277\267\277\361\303M\013`\362\262?\363WH[\277:\267\277\271\354TK\231/\227?\334)\317\270\241a\270?\017v\3240\220\r\254?v\362pT@\277\240\277\341\205\310\373\230\370\220?\373\311Z\360\230u\256?\016\206TEoH\254\277\304\256\223$E\331\266?\302\206\203L\000]H?u\320@\010\016\343\246?i\362\235\270\tZ\272\277\345R\020\312\361\316^\277\255\266g\271\017\n\312?\340~R\227iQ\247\277\024\346\202\030\325\373\224?\251\364\230:\277\256x\2777\315g\332\326\361\270?\014\340\004\207j\347u\277\030Nm\254\333m\324\277\320d5\022S\026\242\277\322\351\017c\030\272\206\277\211H\nS>\310\307?\030\256\336\030\001\235\302?\364\252\211\324F\020\300\277\024\272\366b\t\254\306?\245\001+|\311\332\247\277\273\213O\237\006\013\267\277v\304\205g\214\325\211\277\326\254\031\340\216\236\240\277\246q\\j\n\275\273?y5\316;:\030\223?\355n[\333\305W\300?r8&\007\253:\245?\327\223\245\325\270\310\231\277\252\3133\031e\237\276?\246\332\324\"\244\327\242\277\323\237\254\343\246\274\324\277W\302\3038\'\314{?\301\355\271\366\273%\265\277\325\010>^\307a\255\277\025\t\223\242&\212\302?e\3758\2228\275\220\277A\332\3552\207\217\304?2n\242\022\267\261\322?\303jwV^\275\264\277\260\033\030\222\263\016\304?\014\263\211\"\351<\303\277^<\315/\234\266\273\277r\366N\n\377T\215?\316D\3164*\374\271\277\275B\303`\214nV?\354\345\232 \347\026\267?A\324\226%tO\252\277\273\325l\r\363!\264\277\030\356\322o\204E\304?I%Y\010\006\235\251?p\t\013):t\237\277\353m\340\266i/\312\277c\361Y\305\203?\235?r\003x\257\343\320\213\277\366K0\356\335\375\314\277\245\333\2531In\262?\036\003c\215\340,\234\277rYrR\216\266\301?\317\254\005\000\032Q\315?J\374i\312\334\326\275\277[\321|\237iA\247?p\361\353\345\nH\311?\345\332\033*\272%\313\277\322W\344\025D\353\227?\320+i\034\221$\247?\330\345\007q\373|\267\277\231\316b%p\320\253?\321\024\002\365\233\221\264?\'\377\000\356v\026\302\277j\t\267\271\257\014\264?\323\3122\361E\312\306?\207M\326\353G\221\270\277~\215\033\335@\217\312?9\300\307\374\303\362\305\277w\211\250%\221\020\266\277\335}\374z\275y\272\277\"\347y\275p\031\250\277x\335\027SX\002\247?0\273\034PN\211\313\277^\310\024\310\300\320\221\277w\227\240vD\002\224?C\201\322\224q\327\300\277t\016\2676\032\265\312?\256\002\212\355)o\273\277mDH\357\311N\220?9\346q\364#\277k?\026\361\366Bf\256\245?>\253\02253\221\262?\241\277`4\177\342\256?7x\356\271g\237\305??\314\032e\227\021\236?A\\\247\216T\270\237\277\223\245edf\314\251\277\2748-\330\354\010\256?Y\372WI/w\263\277\330:P\324\305u\271?l[&\033e\320\247?\267\024H\211\213U\262?\360{\001Q\332%\266\277iO?\023E\035t?Rx\017/\211\264\325?\231<\030\302W\014\264?\002\032\272J\366\'\307\277\257\265\325\035E\211\227?[G\261\032\2509\301\277\007\333.\364\201\013\214\277m\250J4l_\242\277\354\211\233*\324\037T?T\n\373\355]F\257\277\224Q!\223\376\347\276?\234\261\375\322\257\235\300?\326\372[\203\237]r\277nn\351\274\230\225\222?\267\323\305\266\377N\251\277w=5\274\005\254\253\277a1(\000\332\272b?b\347\315zj#\273?\0048\213H\243F\233\277G,\237U8\013\250\277\254\304\216\337\376!s\277\321\210\354\257e\023\253?n$>\361\201\272\307?SU~\232\264\353[\277\264\267\216\r\316\tr?\240W9\220D=\307\277\272\002\341\231\373G\252\277\374\0067\303\372g\273?\177\014\211Ex\025\251\277/\037{\362\370\211\272\277\236s\2021{U\255\277\262K\240\026Z4\235\277\021|\262\230<y\264\277h[\005X!\312\216?d\272\\\310!\211\260\277\203\004Nn\317\244\250?r\330\244\\\246\370\311\277tr\017Q\\a\271?E\030a\027\316\273\243?]v\316\216\246\374\315\277\177\r\276\3253\010\243?<\030\255\223\303\347\266\277\340\374\010\253\262\317\247?\322\327\201~\327\265\316\277=\n\307\252\241f\276?}~\205\026\334\223\261\277{\237u\304\202^\322?\271\274\313\013\213\206\300\277\267\017\331Nv&\300?\231\002\017\317ue\210\277\227\264\211\260\305*\301?(*V\230-c\253?\204\347\276\210\021\320\247\277\272JOS{@\305?\274\022&U\302\260\265?\324\335x@+\t\275\277\025[M\007\333\314\254?\270\306\376 *U\267\277\354\375\355E\360\223\216?\2311\357\306T\016\250\277\014\201\024\205\035\215\272?,g^\325\221\277\261\277,\227\207{\025\002\227\277\"\263\305\"\363\332\303\277\022aPp\351\365\304?\233?hd9\335\232\277\240p1\221R.\266?|\006\354Wx\265\273?z\375\017\342\034L\232\277\016\310T\342\203\374\223?O,/\236Hw\276\277\375V\205-3C\275\277\363\034\332\177\027\345D\277\000\200\323C2Q\260?\242\346N\r\253\213\270?\210\330\311V\315\204\303?\344J\263\376\232L\227\277~\340\342\"\"\221\231\277\214\004\202\"}<\312?v\230L\037\306\370\260?\233\217l\036J\033\234\277\006)\235\274\241Tu\277\272G\t7V\213\226?\375\240\3562\211C\272?T(\021\256\"\"\225\277\034\333\255\264\022`\273\277\315\332x\277\010\224\260?\236\234\r\224\320\312\242\277\322\372\010\276\222\273}?J.\030\237\327g\267\277\036@c\317\024\357\237?\246\351&\226\221\344\274?\035\025^\267\270\230\260\277\025TvC\031T\263\277\250\325\345#m\201\255\27791\007P\253\336\311\277Z\003\335hp\352\304?\026\254\272\212\366j\255\277o&\003]$\344\310?KK\372\331\243L\304?n\224jN2t\246?\340+J\373\203A\303?\276\r]\214\241i\265?\351-\215\201\023\243\261\277\006\302+?%e\250?\022\230.G=\264\261?\333@\202F\324Z\252\277\0075\013#:w\245?\341\263\027{\254\224\263\277\303\257\220\215\310\331\311\277\273*\030\325L_\305?S\233\215^\005\032\237?d0\331\246A,\303?\036%\321\264\237\241\252\277\271\265Tvi\274\247?7\010h\354\004\353\215\277\347\246\242\247 \017\235?l\001\332\356\273\354\260\277L\367\347P\311/\216?\362\251a\037K`\301\277\372\0231\301\333\264\304\277\351\353\227\363\ro\237\277S\264\000\'mM\301\277\341\317D\251\262\243\270?8\202\304\2117\024\272\277\225\223`P\347\221\277?(\264\3755Z\226\327\277\363\220\253\376\200\013\321?\346\334\275;G\205\264?t0,\240\276(\316\277\024\344\265\004\261\373\270?iX\371\"\362\371\253?1M\253MB\247\305\277\033.Af\261\221u\277h\r\2179\311\013\246?f\341Rf<\360q?\000x-\377\357\322\304\277\371\230\236\247\342\353\321?\261\nq\226\232\246\302?u\273\335 \371\221\216?\356\310\367wl\227\322\277\331\240\300g\325r\260\277EA\003\225\305\n\227?\364\322\351\354e-\300?\211a\261C.+\233\277\343\002\002\024B\367\274?(\031\004\244-\213\276?\2276\353\025\217r\263?\270\252\375\236\246\304\271?\014\'\357(\252s\304?\010=\0070\235\264\304?\014\355\212\364\273J\314\277\352:h3u\217\\?\033\216\005\263\334\251\301?x\213\352\340\261.\270?<\2129\024$\370\300\277^\257\204\363\321O\245?K\013\310\304J\266\252?(\302\376\340\023\310\320\277M\017\241uk\004\275\277\343\334K\005\345\345\241?\236\325\375;^:\254?\354@\2637\320\243E\277\302\352\010\247|.\262?Sx)\266\342\333\261?\357\336\306\033l\022\311?\263\204B4B|\302?\020\330\307\310\246\014\224\277L\3213\200\357L\242?1<\246\3364\t\323\277L\021X\242O\311\202?\032\220\351\211YR\255\277Y\245h\032\035\231\276\277-7\034\013\233l\303?\252\3708\307[W\262\277\273\245mrZ\326\225\277qRc\'=\360\274?l\327\322nm\'\235?\312\354\030\030\205\234\225\277[\316p\310\t*\266?\177Z!\024\270\020\223?\333\264\310A\177C\260?\274rM\362@\360\322\277\245\232\021\373\252\222\224?\214\226?3\234\310\231?T\tE\000\253u\310\277\265\233\244\003y\231\224\277\215\202]\272\256\353\277?{v\\\271\233@\270\2778\214(\330W\327\251\277\016/\3649W<t\277\225\250Ej\363]\220\2778g\230G6n\255\277\301h\002\263\001\363\265?\320\016_\275B;\260?K\234\230D7\227\273?\3749\216`\310r\212\2774V\265\341\232\016\240?\003)\252\373\255\264\261\277\311\360\363vVl\241\27789\000\215_\315\263\277\210\227U\213\322\230\323\277\233^\270\234\270\023\260?\240n\013\363\2279\251?\265\320\020\323%k\313\277\263\n\226FK\231\232\277\024\231\375\002Y\266\262?\361{\345*\373\037\251?\337\335\271\254a\005\266\277\271R\003UY,\274\2773K?\264\035\353\263\277\003\200\215\207\031\211\303\277V\374\342\362\246\376\260?~\222\314\374k\266\306?\212\236\017J\230<\267?#\2225&\\\322\272?\r\003$\r\3601\261?\233\345\357?E\037\261?@ pTZ\247\303\277\242\246\271\307\006M\212?\020\261@\326a\004\247?\177\332\025q8\007\227?(ZW\277s\246|\277\210\224\034\361P\031E?\220\307I!\362s\251\277\371;q9ry\253?\\\232\226$\366\313\232\277>\033@\276\ns\266?#|\375\335\023 \301\277t\353\377\206*\005\300\277\325\010\377+\300\376\266\277}o?\206o\013\271\2774\373`\201\346>\274\277\272p\243\262\231\241\215?\010\376\373\263\001u\257\277<\025\263\303\313\326\323?]\261\325\337\305W\261\277,\016\224 s6\310?O\353}\244\237\210\265\277\276\357\332l\027\356\316\277>\264\342\343x/\234\277\222\034\312\341it\300?\013`\340$\nM\252\277v\301\266G\3021U?\260B\010\322\030X\303\277\320\262;\377X\367\235?2\000|\256U\306\220?\246q\306\361\241\004\252?|B\272HO\263\241?\243\004\340\206\017\261\263?(( \223Q\241\303\277=2\311\244z,\227\277i\361\210\032h5\331\277\327\336\330\357?\315\264?\010\303\033\025\000\014\266?\342U\206\310:p\265?\377~F8}P\252\277\240yJ\371\tZ\321?s{\310t\333\244b?\221\243\330B\313\316\301?\330}\3674\353\200\215?\220(\342\232\342\213\243\277\305\270\222\nKY\267?H9D\201\357\010\237?i2\225wX\361\242\277\212\346\217f\341-\237?\004\243\226\330\r\227\212\277\227\355l\255\224\036\240\277E\251m\t\364\020\237?-\007\010\205-\343\323\277.I\263\330\343\266\251?\014-\026\025\365\020\273?\245\216>\205!bz?9\324o\230\033\223\267?mW\027U\023P\271\277\251%\241\204\374\026\255?\226K\177\301\005F\277\277\022\301?\303R\307\235?\241 \305\237y\005\277?.tyP\262-\250\277H\267\002\220\200\245\301?\025\272\013\031\365\225\300\277\225\'\375\332\216\343\262\277\'\014j\037\244\263\246\277\3007)\325\\\347\266\277o\245\014\002_S\265\277S`\240\036o\024\246?U\245\321\013\275\331\234\277\363q9\361\307\225\264\277\024\224\231n\375Z\263?1\321\350\304\251\r\263?\324wV\005\007v\274?\253\236\205\300\227\343\254?\306\262\227>\330\030\264?\313\215\275<e\344\243?\373\3533\225T\371\261\277\226A\311\371\351\266\272\277\225\257\323\"\266g\201??\364:^$\272\322\277_\216j\333\344b\234?qPaH\316\226\240?\361MU\224c\314\270\277\320\025\320\001\373\252\231?\220|h\020\204\333\301?S\311\317Zz\261\260?f\355\223\374\333\234\263\277x\325\317\031\032\206n\277\007\032\243EyU\245\2775Mb\2615\337\313\277\003&B\013\313\201\272\277\215\2137\337\207\274\247?\205h\327k\005\361\262\277\223\003z\342\317T\320?\244]\247\026\033\340\266\277\254\325\205o\351E\233\277\271G\335\016B\034\264\277\255\324\017M\237\215\304?\212\n\0048\361\002\265\2773f\305\021em\256\277\346\300\236\311\021\035\241\277\177\325\223\370\232u\247\277\376\324\224\376\244K\203?\246\260\207uj\'\307?\370\2345\314\334\345\245?O\n\025\212\221\245\247\277\257C\235;f\335\231?\311\251_\013\342\376\203?\003;\2524\364\215\256\277\362\366\\R\031C\254?\254\232_\316\373\022\266?\223S\371\034[\352\207?Y\010l\335\335\005\260\277pU\005\3366\213\253\277\3705\024\350\002T\327\277\320\207\216i\371\014\273?,Zc\3422\352\243?BM\345@\0216\253?\037T\320\202\177\350z\277\331\000J\3543\300\265\277\202s5{\'\352-\277\253%\"\305\334\005\240?&\335\021\344\"\022l\277go\337\032i\351\300\277i\037\270\227v\224\231\277\"\301\023W\242}\312?Z&C#,Z\272?U\331#-\247Nz\277\315\314\227q\271\220\213\277.9\002h\306\354\250\277\212\313\322\377L8\273\277T\250m\367Ft\311?y\264\0016\320\354n?\374\331\254\224\356\362\233?\002\224\210?X\250\301?\250! \264\223\010\254\277\357\363\321\301\344p\252?x\254\225\263\304j\303\277\343\262\033\221\3505\273?\251\014\203\226q\343\313?\363l\267\341\231\322\213?\003\031o%\330\317\315?\252]\025C\244\267\274\277\010\204f#\001G\207\277\177\332\275\345\2459\244\277j\226\032>1\216\277\2778T\232\221r7\313\277\251\261\325c:\270\233\277`\221\252J\036e\261\277\221iF\037\306\254\246\277\263\025\234B\274\006\205\277Z\224*\030\325\200\261?\244\210\204\211\374q\225\277 q\334\035\1774d\277\346\341\206l\037\252\220\277\306\321!\321|\311\273?\2730Z\r\034\330\262\277w\222\370\201\345\265\257\277\212\212r\272x\261\226\2770\352\326\017\211\310\263?\246U\265>\255\\\324?\017\203\024\265\376\350\255\277\250S\341\026\217a\300\277\306i\221\016\263\037\223\277X\237\326\030\366\337\242\277\274\033&\364^\223\025?\222\245S\307\305r\264?NR3\334\\O\300?\254p\243\365\023\216\243\277\213\264\247\024\017Z\230\277\270\253\333\304\027\333\236?J\324\373\021j.\306\277\210.\236\362\232b\262?Y\202\376r5\007\275\277S\237\277`\343\352\271?\022\201\347T\213\312\276\277\206*\313\020DXi\277\010\254\215\026\035\247\270?\266e\027]z#\310\277@\236\rX\246E\267\277\221\217\375\255\361\273\235\277}}\022>\n~\304?$\231\007\262\242\347\255\277.\317\316d9\336\261?\022\326\001\022\312l\273?f\247\327@i\323\250\277\227)\360\016\352H\246\277\375\364jF\217\336\310?\377+\204&\314\256\320\277s\\\363 X\367\262?\200\024\217\001\223\315\270?,\333\277\261P\010\251\277\242\376=\031\000D\254\277\217\240b\370\363/\240?\225~:\224d\000\262\277p\273\324\300\352\211\256\277\371\350\332}\263\010\266\277Q\231!\320\250|\274\277f*\307\244\240(\244\277\340\0102a\320\254\305?_\036\255\365\003+\260?\261\331RS\264\343\273?\243\017\331\3350\035\306\277\200p\036\340\207G\213?\222\231s\005\335A\243\277|\347\263\347\241\035\241\277\306\320\004\010.-\253\277?\017_\033\214\n\266?\265\257r)yR\257?D\272\340\030\242\336\213\277\n\227F\200aAw\277B\3279\267\237\350\213\277\n\031\327\302^~\246?\327\242m\302#y\266\277k\026ra\3604\253\277\354\272P\373\233x\220?\327\361\363fZ\007\242\277\n\204t\252k\202\270?\275_\234D-0\276?\314\312\207\324c\311\271\277I\276\034s\253=\303\277>\272\255\364]\253\257?\'\363>\367i\002\200\277p\2528\372m\320\261?\201\240\245\2514\001\226?=\220=\306\203\t\273\277\021\322F\354\314}\240\277\033\333\263\273pI\242?Y^F\273\214?\232\277\261\343E\230\0338\241?\\\212\362\325\342\032\314\277I\305f\236\244\350\263?X\3760\"\027\014\314?\267^>C\"[\311?\317.\377\271\035\361\261?\014\216\322\312[@\224\277}\033Q{\210\234\225?S\323\205\240\217\004\260?2Fq\032\n\274\243\277\3375xz\254g\271?\325O\255\304\226\027\303\277\250\234\"\366\354\037\220\277\357J\312\217b\373\236?\257HI\017\037\363\322?\362\003\216v(R\226?\233EP\007F\036p?\004\204\276%\357\365\267\277\250\302Y`}\375\260\277\321\321\340%\034f\322\277io\334\363\247\337\216\277> \366|\225\225\246\2774\330\356\300\335\363\222?\237Z\242\306\337-\212?\310\335\320\177\305Zl?\323\270\231\002\354\230\314\277\223\372i\321\232\024\302\277\354\321s\3553\361\260?\322\3262&\331\300\300\277Ch\317F,p\270?\231\212?\270\007\207\274?\"\251\311\355\356/\235?\303\247\016\027\255O\303\277\206\246\340<\311\005\260\277m\000\305h&Y\253\277\324\267Lz\261\007\237?\250[\213\255\311\244\303\277w\340\202/\006K\265?\262\303!5\r/y?%\374\234\0314<\254?O\n\231\255\375D\252?L\316\271\211\321[}?\224\003\007\257\203\266\325\277\320\272\370\377\010w\247?\210\355\235k\314/x?639\326#\324\253\277\245w\2171\231L\254\277\361o!\245?\251\262?T\305+\231\017>\302?jL\025\024\365\026\232\277\262\217\231\337\362\035\276\277)\332\224`\346j\226?\234\227\343\312G\270\272?\276\023\327N\232\013\221\277\350\023\221{\210\366\256\277\334\34280\355\333\201?\001\371\261\2326v\271\277r\242G~\355p\301?D\241\370\312\314pr?\030\370\321\220\227\240\244?-\264\010t\244$\230\277\345\250.\305\234\212\222?\373\023\026\321Y%\275?\022\221\374\304N\322\275?\026\320\232\274\363\024\315\277\255\355\3503\3435\302?\001\014\"i?\371\242?\030\332S\252\303\000\272?\364\343SIb\317\212\277(\340\306]\205s\300\277\032E\306\204\253N\314\277T\235#d\321\201F?\271P\206\262?G\247?S\302\210\344\231\t\272?\262FSRH5\310?e\352\251\032\002\260\242\277\347\342\325\0029Y\315?6O\203-\316q\270?\264#*W\2201\267\277\026\014\331\361\274\266\262?\225\246\332\020\251Y\231?\236\024\021\326\205y\307?\344\0244eo\227\272?D\023\010\302\312\000\227?\250`\336F\344\036\264\277\026\213\2342] \337\277\215E\350\310\235\222n\277\276\223\262\n\\\023\232\277\316\232Nls\226\230\277\221\212\360\0130\t\300?Mu^cl\361\253\277\244\007r\225\215\006a\277\362$\305\347\243e\320?\375X\030\016s\000{?\220\022\3035e\277\265?\274_\370a\314=\261?\342$\202\327\020\277\252?\200H\326\306\2436\256\277\334\017\331l\312\006\303\277 =:5\251\275\261?=@\343\365\323\325\270\277\020\t\320\000\341\016\262?x\272\212X\032$\306?\347\344\\\254\300P\316\277\252I`\204\343w\252\277\207\227i\245t\266\304?\0034\027q \354\243\277d\341&\377\363b\255?%H\026{g[\254?\'\360\037\021)\266\304?k\'\320\033\212O\303?\362f`\023\221<\200\277\023\026\341\334<\251\275?HY\314\232dx\301\277\357\227d\021\010\016\232?*h4\177\006a\277?Rt\310\021\226\227\301\277@\261i\315\205q\211\277\274uEV\253rw?I\243\217\035\222\205\276\277:\234\323\215\016\306\275\277\2268\216\253\037P\264?8Rg\333\203\350\273\277\261\002h\022\034VA\277\262\252\325\314J?\276\2779\324\201\033\270J\216?\315\032f&\232\341\243?\276&Vu\233|\306?\301\333\225\000y\016\232\277?M\034\221\032W\257\277@\250.\303\224C\301?\262\235~\343)8\211?\237\265\225\014\347&\235?\36240Z\034\031\260\277\210\002PH\354\336\313\277\250Y\344\2710\032a?\214\340A\340iM\261\277\233\031\275L\026k\306\277\003\215W\277\216\232\226\277ezA\267\005\234\201\277\212\266\366\317\351\362\267\277\216W1B\024\251\321?\233\223\263\343kV\252?\243\021\212\221{^\310?\362\306@[\362=\246?\225\370\353\207\0041\201?\177b](\240\363\242?\333nO\r\"\003\217\277\274]\366J\024j\202\277\021\310\307mR\365\221?n\2725F)\001\266?\330\216\002\207)\325\273\277\341\002\353w\375w\260\277\210\354\337\003u\201\255\277\220\t\305\225\317\314\255?\314%\350\240\310i\300?\275K\231<\021\361\264\277\223\010\3531\021\027\255?}\377\364H\234a\301\277\377\005>\331-{\302?\312<\343\360\225\303\267\277\313\316\233\226m\371\207\277X\243$BrY\257\277(\373\3611Mo\204\277\231\302Ok2k\232?\266\023\210\306\345\347\226?#\277\306f\321\213\221\277\317\374Up\270\231\256?\234\302~$?\253\243?\215\300\352\330\232\313\300?\2638\355\3122\005\253\277\200T\301\021+u\214?E\341`\244\226l\233\277S\352\311\343\314\035\306\277\251\217c\352\301\223\312?\256}\372\344B\231\220?\022\200E\025\230\254a\277I\035\315l\324\222v\277*c\214\376\004\237\247?\314\272fE\024\270\322??jB\265\264\353\263\277V\326.\230\250)\237?\321>\323\226\202S\317?\342\316\035\336\033C\322\277\242\341\030_\360\341\221\277\r\325\250\021\021\233\305?\314k[\235\251V\302?E\206\363\335U\320\320\277}\201\231:\260\353\230?\222\222\235\271E]\243\277\272\314\270x\275J\311?*\365\002\255\243\266\235\277\270\364\305\212\254\220\266\277/h\247\332k\256\267\277\267\374x\340r\330\217\277>\253\252\242\026\366\246\277\225\255\375u\316@\253?\346\"\224\353\037\001\303\277\261\223e\333xU\235?\'\207\223=\316g\234\277;\274\324+\270\022\303\277M*\351&\323\320\267\277+\320\366\263\226\373\260\277\332\031\3259\304\003\300\277\273\363<\303\273\333\303?\355\375\025\230\372\336o?vFU\253\270\021\262?):\242\024\250\221\315\277\315\363;hRo\260?\253\277\024\326\177\273\204?\213O\2779\317m\320\277\3038lw\355z\324?|\372\234\367\335a\235\277&\242\237\220\014\264\257?\302h\310\241\3108\306\277\227\316\006-@\007\250\277\336\301X\350\034-\257\277Vo\240\362F\353\270?\367$1\336\360p\321?A\365\031\200%V\265?\275H\323\2024\207\250?z\277Wj\311=\242\277\001vna\371Dv?\375\3548\352\355\261\233\277e\226\306\343\273e\262\277\006\336\265[5\302\323?l\002Z\301e\014\261\277\004\242v\271\017\321\303\277l5\246\252\273\241\265\277\362F\374\327\3440\215?\034i\207i\202^\241\277\226\374 \020D\226\220?o\r!9\204K\220?\3505\343r\365\027\254\277\n\343\'\323\034\303\265?/-P\352\332\262\261\277\247\271\252s%?\301?-\366G\024\260fo?\364\030@\013\211\276\266\277,\356\267\355\363I\264?qu\021#\267s\202?\366UL\310\261\252\242\277\2429&_N\266\262\277.\302a\340U\252\305?\272!D\257\007,\263?WzE\352\301\312\230?r\370(6-\026q\277\366+<\302\027D\264\277\200\230\027Ag1\307\277\000RB\362`\340\311\277\351\253_g\242\276\262\277\327\304\236\203\304\342\251\277\263\343c{\034L\260?p\306\325\023(\274\252?4J\277\\Gx\227?:n\'j\203\373\276\277=\0211?J\207\305?\310\314\337\211\035\006\301?\031\355~\331\367\324\212?G\356Q\233z\242\217?X\337IQC<\316\277Z\273\306\335K\373\273?\375\322\031\314\024\246\305\277\302Nw \342E\323?i\343\351x0\373\261\277B4\210\304(z\254?\206(\021\371\240\257\242\277\255(\254\2129J\300\277\263\371\243\211)\316\271?\311m\0160\206of?\352\260g\376\352\350\300\277\311N\223\205\021\340\254?\274\272\310\301\220\331\263\277=\371Ls\356T\257?\221j\032\035\321=\305\2775\366\362\313\355\271\304\277KKh\"\027\210\307\277:c\000\253\337\330`\277\034\363\244\372\306\324\272?$h,~\377\223\250?\361\240LDi\321\245\277\000\363{\343\\\013\273?\302B\336[u\257\273\277M\215P\251z\177\236\277\327\210w\241\272]\254\277\0262\00228\006\277\277\373Z\303&-\343\251?\322\331O\250\257W\261?\325\2268\223\367\312\230?\231F\342\215\220\036\241?W\270\362E\251Y\246?c[<\320\370\360\307?\023\t\345\354\237By?\036\325\\\t\372\373\277\277#\010\r]\244\n\266?\366b\241\3476\235\262?GF5R\266\343\244?\340vx\316W\035\226\277\304\254\351o\212\320\316\277\364\014\223\026\014\246\267?\347\216\021\362\031+\241?!\202?\023vy\272?\222[\256\262\276\256\255?\026\033a\241\223}\277\277\247\363\345\303\202\317\267\277#\250P4o\303\256\277\0019KP2\265\250?\206x\355\355\377v\256\2774\327~\017\256\331\220?lj\240W#\235\234?\345U\243\026a\354\257?\007Zt<\337\343}\277\301y\255\314\363F\241\277\336%\3776\'!\271\277\230\017)\217\365+R?\350}D\210\005\377\246\277\256\310\\\272\327\024\216\277jl\242\311\225\177\241?\345^\372W$\263\274\277\213\253\014Y\\x\273\277}ZI\246b`v?\342P\252\354\204\377\311?\343\026\330V\324\t\244?\230\267c\210|I\300?\265\364\252\233\002\246\267\277\037B\303\201\375\343b?\236/\027\205\303\210\323?\357\241a7\260E\331\277\352\234a\326G\240\262?\335\226\320\024\243\"\254?\313\340(\320\305V\260?\376\342\211(\013_\245\277\375\371\307\217\023\310\265\277-\322\311\254\031\350\257\277\215\352)o^\373\215\277\216d\020\320m\260t?-y\221r\337\004\310?\306\303\233\016\357b\265?\205\225U\r\361\242d\277A\010\342?\315o\201\277\243\2459\260x\301\226?Z<xj\251\237\301\277\360\263u;\270\205\266\277.bE\251\377\034\301?f\347\306P\261\035\212?(T\311\275XT\240\277\322q\026\311V\340o?\220\343|V\212b\272\277d\306\347\376v\023\276?\353\302\370\230{\031i\277\302\254\271\220z\342\321?\345\232`\276\356\347\273\277\250\277\377[\375D\263\277\017-\374\236\nY\314?\013\204\375lf\335\261\277=bF\223iR\225\277\347\004\005Z\tZ\265\277r\377\250.\'\360\226?\'\000>?16\244?\340\026l\366\375\266\205\277\235D{\023]G\222?zs\035f\250\337\265\277]\354\251 U\000\236?\261\237\204(\255\301\240?V\206w\016\006\346\276\2771\307\213\275\234oT?\374\023\263\200\266\321\232\277, \374\001,\232\267\2778\217J\340\375\351\270\277\000n\272\365\370\351\264?,\274\324e\025\n\204\277Y\333n\305\240\352\270?\001D\305\r\371\021\305\277\232u\217\021\326\270\272\2770\342l\3656\272\274?G\250\250#\353\201\262?0\373 \275\227k\276?@\361\263\272B)\302\277po\330\211\033\020\260?A\202F\000\223\307\325?\022\223\247\321\217\221\261?\362%\215\200X\370\274\277\370\336p\333;_\272?\276\305\3571\235\275\240?\257c+\316\244)\261\277\277\262\013\t\230\250\250\277\345c\021\271l\200\237\277\361\301\361%}\006\275\277N\363J\200\272{\224\277\306=a&\376\314\256?\241\003\310\237\373:\260?|\330\246\251S\002\260\277\336\017^z N\272\277\244\2053\336=\233\271\277\350\334\000i1\002\261\277=\375\013fe\267\220\277A\211D\2604S\310?\245z\007\360\367\177\213\2772D.\203\360x\243\277(e\022\265\364`\211?\2337$]h\200\236?MBC\306\232\320P\277\216\002\252N\"P\231?\350d\242\356+\332\320?\356\221D\272\355\367k?5esk[\324\253?\376O\232\207L\210\310\277C\004\252\240\371\361\241?\306\034\371)\307y\245\277\'`\210\303H\364\276?\313\023^\352X9\251\277\277crv\337*\244?\361\n\373\235\004c\226\277\223e\004*@\215\304\277\'\254\372\312\237N\243?\312\213\375\021K`\242?K1\211\327\317M\257?\200\226;\371\233x\221\277\212\365\204\002\3443\304?\322&\332\\\350\223\255\277uJA\354k\007\253?\301\307\346\247\\&\310?4\t\233\204\352\234\250\277\211\351_\365=\341\260?J\201K\231\302\344\311\277\360\267\334\017\232\002\276?|\323\316\313\";\262?\342{X\361\347\360\273\277\364\347\361\263\rK\271?\232\004\317\256mO\242\277\330]\261\307\036\233\274\277\014]\244Z\356>\214\277\2342\322u6\334\224\2779\306}\354\217\273\233\277\267\016\331\264\367n\252?\336\005\351\203q\233\253\277w\001\213\210\363\312\221?\371\366|\200(\276\200?4\325\245\036\341!\313?\355\'\206DZ\254\270\277E\304\220\005=\352\300?\025\276\023\350_\330\243?\215n\306>\336\242\273?\"g\212\354\313\343\264\277\356\024\214\376\275\221\227?\323\225\214\331 \306\266?\263\201\323\n\240\364\261?|\200\363\205\202\323\300\277\337U\300\344\316\217t?x}k\035\247w\262\277\254l\335\242z8\236\277\rh /\250\315\274?\331\313\266\355\377;\302?]\023F;r\204\227?PAL`\214Wq\277]vZ\2268\031\260\277\210\304\236\374\325\017\320?\260\307\374\353\317$\256\277\177a\313\2612#\233?\266raN`\352\300\277\330\265!\034\256T\255?B%\366\376\225\270\223?t9O_\251O\242\277\3203J\266\0266\240?Zn>.2\243\255?\272w\2252Wr\217\277\323\314\031\307\007k\277?\255\270\376s%\357\260\277m\307\rs\217\314\203?ry\251\3252/\261\277\202\206\272$,y\311?S6\240M\255b\233\277P\222\332t\014:\247?p\345\214\3171\001\216\277v\200\341\322\343\264\224?ZkZ\021\205\205\274?\317GK\270\341\377Q\277!\306\003\033\212$\331\277e\375\334P\t~\203\277T\344\210\r&\030\301?\002aRS[\234\231?!\330\344?/\241\303\277\377\013r\202\324<~?\207X$\304\253\331\273\277\024\237\340\026cs\210?2\222\030>\334W\241\277:\304$L\365y\275\277\345\261\242\236\t\246\230\277\365V<\327\236h\305\277\'\236\374@\004\244\264\2775\350f\236\322[\243\277\201\270\3127\\\340\312?\340\030\366\236\216\262\267?\211\354Y\247\211\323\246\277 \314\252\312\360\004\326\277\305\227L\346\r\017\301?\264R\266y\325\372\312?\374\322\274\275\000\251\244\277\030^\354\243\261#\265?\\\360\261s\3335\273?\353d\025\270\334\'\240\277\343\254\233fC\030\230\277\n\2327\222\006\325\264?\030\344\207\031\253\326\201?\302\2350\3316\037\302\277%,]U\031G\241?T\256\304\r\215S\201\277EY`\315\342F\301?\260\263\3562\256\275\243?`F\303\276Z\307\251\277\312\215?\353\031\'\234\277\371H\371\226\235\204\241?\334\003\357\004\366,\326\277B\324\373\331\312\260\266\277#bUR\222\333\246?\373\r\245GFFV?J\220\256\236{}\270\277ck8\317\363l\306?{\302\234\201R\027\302\277\021\300\021l\223K\260\277\342\260)\034A\377\222?\367\311^j`\242\240\277\246\204\303\332\205J\301?\340\350_\225\r\215\254?\305E\361\276\227a\222\277u\317\204\245\300st\277\257R\375,\033m\273?\350\031\215$K_\235\277.eh\213Rx\247?=&E\013~\250\242?[[\254\244m:\271\277\245\204cA\224\201\242\277\355\261\003R\370\320\322\277&\214\270A\336\326\300?L\345\215\352\3371\260\277y\226\220hw#l?\003\031\270HC\360\231\277\357\021\215\344\373\266\265\277\014\340-?\325\206k\277k\301K\320\356\300\227?\310\271$\262\262\326\246?)\025f\367\345\203\256\277\335x\311\027\254F\315?\331\210(\273\247n\305\2776\002Y\376w\273\254\277}e\336\302\344\352\261\277\315\025S\346\350\206\251\277\214\376\010\201\236\260\331?\35314\003Jb\227?\275&Y_\232\332\303?\003\237\355\260\222\030\304?\027\005\251x0\027\303\277\212\252 =\325E\261?\326v\366\312\264\363\273\277\336m\006\201/v\245?\372o\222\374\340X\263?@\243\n\030\306\\\232?\306\333\305\212\214\367\303\277\247\206\0332u.\260?\255\302\377\346 \265\214?=\332>\236\200x\276\277\305\\\206\260\013\217B?+*]}\222\352\275\2777\314x\017>\265\302?\2530\3275\037\243\242?\223\330\025\326\327(\266?Iv\273u!\303\255\277\342\330\334\320\024\266\242\277\000\213t&Y\200P\277Y\201\203\253\332\316\243\277\213+\241\213\014\213\302\277E\340\\\330X)\267\277\324Z\007\\\032\263\300\277\237\007E\327\030\007\312?\275\337\206/\233\276\267?c%\000\351#>\275\277\335n\337\247\265e\266\277I-s\315\025\351\317\277\212s\022Z\206\261\307?C[\0342d\030\267?\n\322lH&`\267?\247\213\212\322\320\001\262?\031\006\241\356\312\310\305?\2343\346\364\342\325\207\277\034\263\2175\204\216\205\277\360rp\203V{\313\277@\275H\303\211\223>\277DO\t\264\204\334\252?\304O\271\377>x\275?\321q\005\256!\306\303\277\346Pr%\256\202}?\246\242\310\324\255\334\277\277\025nv\304Hb\271\277o9\205\307fS\227?\354\0243\256C\270\231?\372\222V\r\305E\303?}g\021X\346\017\321?\302r3\273\211\014\253\277\220`L~\027\242\255?\223x\270H\233\251\241\277j\322\243\033~\377y\277\245*\377\326\314c\250\277\220\346/$\256\301\242?\375P\324\360\036n\327\277\023\037\352Q5\300\270\277\242BS\207\274\021\245?\310Z\354\327a\233\265\277\3510\007\207\375Q\271?\017\037@\237\246\356\220?\264O\342\255)J\247\277 =Ph\316\305\247?.\353\367\363\276\233\253?\260\031\240u0l\273?\024\215\001~\340>\261?\030)\370YG\277\241?\263R\211\267\242\223\267\277\245v\347\277T\213\272\277\\\235\021\254\256#\312\277G\'\227@\333\234\356>\326<!\345\210q\241?j\223G\201\025\202\300?\326\352\370nH\221\244\277\251\264x`?\223\276?f7\361~\177\254\201\277\245~\2247\366\323\305\277@\273\232\330d\022\212\277voM\215H\304\232\277\350$:9\235\263\314?\200\346\243\304\272\260\207\277\354\213\306\002\310Jr\277\217\321\224r\235\367\330?I\352i\226\032x\322\277\257MW\263\007\230\237\277\361\362\244nJ}\221\277\312YHj\345\t\306\277\256\257)\321\023\321\273?g\235\024\254\2013\264\277\375\357\307\255\226\260\227?\211\363\3116o\332\274\277\207\"wJ\253\362\300?q\377^]^\030\275?\363q\346{\341 \264\277\327\242A\301\341\210\273?\346\007\306\231Em\273\277\212\305\241\2601\310\250?^1,\253\312x\241?q\037\377\231\376a\224?S\025\254\274\374/\227\277\250\315\237$E\\\307?\242\tR\377\205\234\246?\334ll^\027\002y\277\267_\263\350\274\275\246?\227\300\231\370|\327\264\277\306CH\236\\\347\266?2Fqowm\261\277\030Kh\363^\353\265\277*\353E\235t\323\206?\206w`f\031}\274?\265\251\271\277\r\025\243?a5C&\253a\301?\033\375\242\212\261\201\264\277.\235\237C\217\342\250?x\213\035lO\226\262\277m\337\033R\377\243\250?W3B.N7\211\277\210*/<zd\274\277\210\013\233(\344`\267\277l5\356\032\227\241\262\277\257\244T\233\216\354\211\277\243\231\241.L\210\255\277|\222t\004\3048\253\277~q7y\035r\257?\320\271=\361c\223\300\277\215\210\301\250d\ts?\2735~6\245\224\222?\314e\307Dk\036\304\277\301\020\247a\'\202\207?\272\227\303d\000c}?4ZY\317\016\267\265\277\233\010\206\233\355\277\225\277iSr(H\336\304\277\224\370\213\350w\373\300?\014\365\320\210\340\230v\277 \346\276\004\276\017\277\277\210\032\207[\330j\251?\365a\031\347\227\016\302\277\342\367\232\350^\260\302?\024;{\235Ok\264?_:\315\000\320\365\272?\3336,\374\307\254\300?^\354\312\371]\232\213?>D\035\315\253\033\204?\326\207\247\217!$\240?\330\351O\024\003N\300?\243d\363+f\323\306\277\324A\252\370\227\345v\277@9\247\306\307\326\245\277JG\275\355\326\242\252\277\212w#.\250\362^?\264\2535\261#<\245?\241Rdo\356\322\317?\004\026\210\270\312\264\306?\333\215\275y\334I\243\277q\237\365}\"?\300\277/\221\343\20555\202\277m\332\325\210\025\255\220?\317\033\020\007\014\302\264?cAn\205\260\306\257\277\320\236\001\013\257\370\315\277\001\202\255\010i\203\270?\264\326\245*Rk\301?\315\370^\373r\274\263?%\207H\004\010\302\275?\261\177\217W\345\255\255\277\226\260\267\002\277\257\306?h-\353\0308f\274?2\267\177\026M\232\275?\250\232\353\024\255\271\263\277\232u\351y=\264\267?M\321\004m\000\314\250\277\316^\371ekk\230\277\306\213Q\300\0208\260\277\2364y\346\324\270\211\277\262\270vW\024S\261?\316\0178\303\013X\314?\202\251\323\201\217\037\315\277\037\3617<\005\236\246\277-;%\355\256$]?\\\035\365pn\006\325\277@\007%\274\373\200\317?\220\240e\377sr\247\277L\331\016\274b\371\301\277\223\312Rz`M\265\277\032\311\r\241]\205\243?\376\276\213+\356\326\270\277\240UN{\251\033\306\277\020\330\347AFp\254\277,\311\361\230\032\250\243?\217\242R\223\033\273\276\277/V\220\323\227\334\300?;\033\332\260M\250\247?\323\270\377d@\274\245?\037\230\037y\027\344\323?~d\034p\000\375\250\277W\345\247\022|\215\232?Y\205\035\364\333\'\301\277,$\351-Ngc\277v\010x\340R\022e\277Cmqdl\027\303?\016\236E\013\3344\274\277\207\321\302\272U\203\254\277@b*\316|5\217\277&?BA\210v\221\277\331\001\\\2458\207\300\277\013h\301\216\332L\300?\274\211PAxQA\277\024JJ\346Gl\265?R\237s\305\347X\255?f\200\200\010A4\226?,\225\3446\342b\307?\357\2531\316\r\200\275?\"\261\321![\371\265\277\236\255\345\2461f\255?P\037L\247\254\310\250?\300\263\365\366\316Z\266?\030\017\232\010Q\362\267\277\202 V\374\302\342\240\277T\252DdN\267\240\277\254z\010\231tv\262\277b7/\253j\244\247\277<\'\346\337d\247\314\277Z\353\225.n\360\254\277$\004a\345r;\247?\345X\026\344\213d\271\277\022\035\273\020\307F|?h\256$\016V\243\264?\346 \032(\364\356\273\277l\326\001\267}\027\302\277\231\346\"\333\345\337\207\277/\223J\2067\200\245\277\301 \3455\312F\316?\374O\306\274\303\027\260?\315\206\267\213\303\313\270\277\210\344NQ\007\206\235?Jj\003\006\035\344\301?e2\375@p1\301?\271\224\2744\217n\234\2776\005b\245Z\272\233\277\035#a1Xo\231?\304\243J\334U\346\255\277\255\205\016\320\021\307\277?\314\020\370\377\206\022\213\277\247g`\026D \275?\031a\343R\336Fn\277\255dI\371d\363\304?ujW\\\2408\304\277\217\252\367\232\264\266\254\277\342\232\331\214\014\351\242?\214\307\024\010\235\254\262?x\tq\005\005\226\216?\213D\321\325(\013\263\277<\265T\304L\246\227?6\250\306!\365\246\265\277\336\245\307\312\350\362\230\277g\271\262\225O\215\300\277\301\237\361\235\026\316\223?l`f\224VU\274\277 lm<\302L\201?\264\200\002\024h\314\272?\007\301\340\021\030\000\274?\313\010\263%\342\374\235?\225\025Q\034A6\241\277.:\002\325r~\225?\342@\214\235\257\270\317\277\306\020\336wE\260]\27737B\321\013\014\263\277{\252\336\263DP\223\277\211\306&\240F;\253?\326\340\341\337\010\227\265\277`\007\326\277\371\004\306?:\3744\203y\004\261?8\375\326/\r\352\232\277!4\\\030\211\036\214?6\365q\014\210\371\265\277\361\2076\024\010\230\231?Z\342e\301$6\221\277\024\344\020\013\247\232\225?\353\260\255\306\223\274\275?\253\020\017/B\301\274?d\033t\017xr\263\277\361o\240e\206\031\322\277\243|\006\344\330\373\260?\261\350N.\352\231\222\277f\367,I\330\213\210?\230NK\246\363\252\226?=\2343\037z\370\221?\304\350\010\201Zf\246?\201\326\334\273(\010\265?+\324\240\024f\363\260\277[N\374\232\353\027\260\277\246\236\361^G\227\317\277\321\354\035\234\343\373\222\277\003\253\350tF\363\263\277V\305,,\\\005t\277\010\301\330\376=\350\212??\277\340\366\302n\300?\246\023LI\373\035\260?\233\376\177o\0206\262\277\330\276\312\271\212-\271?\263:\037yM<\261?\300u\366\017\320\316\252\277W\320\320%\2646\300?\330\242\364&\202\372\244?\343\261\217\344\033\257\275\277\221\245}\264\375\324\231?\212\341e\270\253\330\262\277\353\033\265\206\322C\242?;^\np\360\363\276?\332-\377\235\273?\313\277\033\257\222\224\254\037\244\277`n\"\256\305+\252?\332\"u\232p\234\305\277\320\3255\\Y\370\316?|:\260f\025\221\263?b\021\214(\005\242\277\277[\026m\232A\371J?\203}\240\236{\240\305\277\337^1\325A0\307?zU\222\362\210\345\207?\216\313\207\276\3553\324\277\035E\256\376\267\316\222?1w\352q-\r\267\277\371\325\303d\354[\252?\315\236;\217\020\343\272?/\007\241N~\037\303\277A?\306&Rr\276?\211\244\377`\272}\273?d\277/\365\321|\304?\264\206\203\235{B\272?\305z\277\210;\231\266?\177\231`\005\n8\222\277\373\217\231C;r\303\277\014gz\352\250D\245?\367\367c\326\344~\250\277*\312FJ\331\370\251\277\016G\342\340\202C\261\277\274\324rys\213\312?\300.\277\003\036\233\312?EZ\002o\021`\251\277\224\360\271\241\030\331\235\277\224\212P\340\010\325\237?O\356\345>Bw\272\277\231\362bT\r\315\240\277\245\242\374<\204t\277\277\201\222\035\347\365\306\224\277``\366\030\234\367\226?qc\266zO\214\270?\337\026\322\314\337\275\323?\275\3751\"\324\237\230\277\020\376\302D\021\366\222?\242,\321I\013\366\251?\316\322X\242`,\301?\371\006[\030\022\276\217\277\002\"\201\0175\036\223?%\362\210\215`\256\270\277\312\256\356\024\202\315\267?n6\321\341\314\262\220\277\023\252\003\231\'\336\254\277\016\245\304O\363k6\277\245|\005\251*{v\277\217\245\207\250\037\375\262\277\376\033\275^\310\305\316\277\01401b\340\225\303?\331)\351\026\266(\274?\301\344p@F~\311?\373\010#\0043\236\263?e\354K5\357\204\254?8s\\\251,C\210?O\345;\320\030S\211?\013T\334\022\235*v?\302\036\375\243\370\271\277\277A\201\000U\372R\223?\361\"\263\350\357\234\255\277w\344U\037\243\'\246?0\351\350/\365<\300\277\356\375\221N=o\274\277/\265\267\021\3731\222?\377\"L~5W\252\277n\230rs9\242\324\277\315<\241\026\204A\246?\304\r\322\216\221W\252\277\236^b\2702\306\233?\031\266\233\212r(\255?r\356\003\007\254wk\277\351\270\327\031/\266\221\277\233\014\014\037\307B\277\277\344\314S\260\364\351H\277[U\317s\317`\263\277\344\010\327>\000#\313\277\023\205 oD\354\313?\203}=w\033\025\260?\2728\000\250k>\271\277\017H\227\233\262\365\240?\365\3155y\204\000\267\2775\345\310e\275\215\232\277\260\276J\343:\200\273?\323\255?\220`E\255?\201+\257>\007\343\305?\354\274{\257\210\023\245?\333\315;\255R[\277?\177U\271N\276\374q?\265\205\270\201\311:\262?\276\013\220\305\253\013\221\277j\016\222l.N\300\277f\227Kz\341g\267?\272\375\302nk\243\260?0\031iv\215\320\232\277n\003\003B\337b\261?\033*y\254w\026\267?\214\256\206\362\r\036\260\2771\215\365\333\216\'\254?\223r\2149:|\235?{\345 \344l\037\262?Ia@\303\335\345\302\277\255\227\333\344\216 \273?c\3709\\\266\206\300?\003\312\237\033\r\255\220?gpF]l\230\272\277\234\327\320\\#v\247?\232\001\266\224\260\330\270\277\310\204\352\232\345\316\266?\332\336\221P\337\tw?\363\227\300\\K\037\261?Q\327N0D\317\305?\311\3456\226\016\213\262?\276A\331\337!V\274\277\255\274d6\327\324\247\277\257\321\375I\003S\266\277\023?Y\240\350\317\275?\270)r\027sR\262\277\334\273\216R\355\025\200?\017\210\232\356\372i\320?\334\230\231\322NI\267\277\032\214\357\227\007\377\251\277\252\252\270\375\016\310\306?F\201\316\316-\215\252\277\323\272?\360>&\246\277\316_\'\271\212\201\257\277\307T(\370\260\006^\277\017\231)A\013\217\236?\304\274Iu\301\272\253?\203m%:\230\236\235\277C\341xIy\222\260\277x\327N\221\327\334\224?G\276\225;\004[\245?\213<\217\036\252\241\303?\255\254\010?\356\201\241\277O\250\277\247#\322z\277\270\205$\022\270\231\212?\006\327\335\347\n\327\274\277te\321\202\306\356\225?\236\342\274\372\215\206\263?fJ\017\300\235m\314?\205r\010\267+\262\266?\307\221\317\234o\304\263?\2133\255\241\2574\211?r\203\360e\223o\231?\317\374\336|3g\224\277\314<q\200l/\270\2774\223z\260\262\364\306\277\352\361:\203\223W\247?m\t\257.;\227\267\2778\025\344\277\3568\262?\034t\330\024<{\262?\262\300i\213\357\310\245?\334\266yvk8\257?\243T\032\357\343k\304\277\3018\317\255\256\261\210\277\235#\224\235j!?\277\345\255\350l\000s\264?}<<\260\302\327\301?\375\',\334\215\314\224?\340\226/\320\234\023\265\277\257\255?:T\247\307\277a^\301\340\317P\244\277\341Qx\276\026\353\260?7\301\213E\2261\265?8\013-!\210E\204?.\026\231\306\037\326\\\277L\010\244\226g\027\275?Q\245\177\236C\306\245?\374v\267\331Z\301\225\277\374\323\034<\004#\255\277\301\241\226\366\305\315\314?3\364\251\222\2653\263\277\256\253\323\236\006\027\272\277\035\327\022/\233\224m\2779\207A\347\274-\301\277sK\215\344\253\'\261\2774)\035L\326\303\302\277K\315\224~w\251\265?,\347N\'@\363\320?\365\327\177[,\223\236?X\317\024\332\236\000\242?\377bP\267\222\377g\277r3\261\377\331\226\301\277\366\237?%\245l\257\277\345\375D\020O\332\262?\014^\237|\354\346\267?\017%\363\315\027\227\275\277<~4U\026\217\265\2770\003d\031\225+\317\277lL\013\353{\262\215?\322-[#a<\304?\025\363\347NOJ\261?\3015\004\211\207\354\302?;\357\275\'\177\257\307?\t]%\364\360\370\265?eN\223\231\214\n\260\277;\232\246&\247\342\255?\315\333\232\335\356z\311?)$K\212m\323\305\2770\030\251\265dL\300\277\013\321\353\034x\343\263\277V\227\232gX\333\273\277\272\260D&A\255\324?\334LKX\332\366\242?\225\356O\237\024\316\276?XJ\027\'\216U\220?\375\240(\246r\000_?\016k\000\320\310\327\262?`\230Hf\027\'\236?T1\211K\225\377\266\277\225^\t\227vA\274?\251o\360\214G\227\256\277\t\010\354\t\221\334\233?\002\323\026\014\361g\250??\027lA\324*\236?\371U\356F\313\322\304?\256\245\355\243O\352\321\277\271\252\272\305\032\000\264\277\2627\336\226\243_\255?H%#\000\212qi\277\240\246\231\016y\016p\277\004\262\276v\240_\230?\265\031\306\241\007\272`\277o\210\367=\240\366\236?\255\000\210\326\031\026\310\277j$\332J+\377\226?\336\3406\217\364\230\303\277\025\363v\034\273\275\234?\312\007\343T\313~\205?\322$\301\t\314\002\304\277\224\216\014\030%\345\214?\237\0239c\317\262\264\277\327\314qt\236x\306?%\264D\244!\325\301\277\0315\006\024Z\242\302?,\272\246d\2430\272?\307\310\361\017\3037\265\277\255;\214\251\243\220\256?\221\210\304D\222\347\177?\340H\332b\237d\266?\247\217c\177i\254\250\277\327\334k|\371w\241\277\341\257\032gud\231?m\263\364\303\206\021\245\277\030\273\263\013\253\265\304\277\207\270\313:gL\265?L\353\231\374\265t\274?\373\335\2007\037!\317?7\371:B\202\345\267\277\370\037\256\032\373\211w?\313\370\377A\367\366\257?\347\326r@7\216\276\277\321\355\013=83v?\246\266\t\305^x\265?V,A~\302\206\265\277\000\257\221]\273\322\263\2770\206h\201$\350\232\277\023\rX\301\357?\256\277REz\216\315V\261?\324_\300\263\361\221\262\277)\231\276!\225\010\306?rA\2550\001\357\264?\340\260\002E-5\262?~\216\242\335\327\000\267\277y\240\006\202\246\334\275\277\037+p\301\323\372\265?\017\221R\361\306\274\276?\275\336 TR*\237\277\334\313tt\344\272\321?,\033!1\247,\262\277\361_\362(2\357\214?\025$\007Z\035\202\244?\310d\'\206;_\307?\324\332\035\222g\206\204\277\270L\3724\310R\260?[\204\213eb\350\300\277>\326fX\350\331\307\277\223[(\374\376\272\300?\177q7h\\\002\222\277\263r\002\244I\253\241?O7<$a)\262\277\263\371\"\334$A\266?\373\023\367\243_\325\303\277)\"X_^,\304\277\354\240\334Sg)\203\277C\356x\360\201\253\314\277\263\370\343\331\025\001\242?\300\326\000K\'\320\220\277\2464P\023*j\303\277\241\323d\331>\006\301?k\357\365\001Q\241\266?\020$\377\366\r\355\273?!\004H\255\236\007\267\277\230\022\343\300\347\267\317\277w\234\300\342/\014\272?\207}}r\332\010\264\277X\003\325s\337\006\307\277\213\235\277\277\251\332\234?\205G\037\247\324\310\223?=\314>\351hh\325?z%<F\202\315\326\277\316I\r\344o\366\261?\"\200m\200\212y\231\277\257\'\224\240\021\247\272\277\364:\346\335\347\320\307?\342\324f\362{3\261?\031\243\350\"\'\nt?j\324\237Q\202Y\272?\213\354\341\224\035 \221\2774PQ\310\254C\231\277\251N-\315\017D\304?=\261\303\n?\036\237\277\335\244KB\024_\252\277e\341<k\317\215\316\277\207T\276\252\034\036\262\277\034\242\343\256\223\373\222\277\007w\375\031NK\301?\\\370&>\006\233\315\277Ww\324\314\0078\232?\013\177\277A!\246\247?T\343P\033\255\363\214\277\261x\004\t5!\275\277\375\271\020\210\205X\265\277\274Y~\374\352f\243?=|yAK[\250?F\305\207\254\223\367\271\277\272\'\237LA\332\241\277\032G\321\253\005\346D\277{mo\t\237\310\214\277-\261gV\277\331\271?lY\327\3236}\252?\334T\342!\267\032\310?\330\322\330\305\332\306\242?1\010v2\330\363\263?\345\243\254\270\302x\265\277K\006\343Y\375\n\217\277\014\022]\035\353\276\244\2775M\332\273\253\260\245?5\340:|\230\366\303?&\223\263\353Q\206\255\277\227\214|\205{\001\273\277+\03500\307\004\300?n\000^\351%m\311?\025 \335\217\210E\273?\237\203\221*\230\370\261\277{a\224\263\212-\273?\027\232\304\034G\016\311?7\343\377\370m^\227\277}\\\325\177K\212\226\277\366F0\255\234D\307?\307\024.\243\327{\261\277F]Q\224\315\232\303\277\203\305\034N\255\274\261?i\342\210T\007\250\251?AJq\213\313t\266\277x \031b\354C\266\277[S\265\3357\216\263\277\303\nE-5\031\246\277\247\371d<\\\'\261\277\341\234S\003{\203\265\277zG\017\231\331\252\277?\252\367~\0313\373\214\277\363\271\262|C,~?\225{\023\254<\310\270?\257\324\354Wk\210\253\27776\234\374Gg\300?\332\302\311\034H\246\202?\'6J\270+q\214?\356\0316,\314\214\264?R\305Z\304\0103\264?6\304\242\365:#\204?PG\033\003\023\312\267?x\236\336\024>F\245?\030\205\236G\014\366\300?\245\305\361|\224\354\242\277\370\350\277]\022\006\270?\033\003NC\346v\244\277B\214-/\264\035\300\277`I\342v\301\262\242?x\035\233\3279y\211?eb\227}\301\366\235\277\212\250<\303\330:{\277\007S\261O\275\222\255?\300\036\016\257kT\304\277\'\325\334<\013\016\270?\231D\262\004\005\326\234\277\277k0^\372P\253?\365\304\207U^\342\304?~\374aR\217\343\225?\177\202\007\024\020h\303?\313\222\270\201\n\262\213\277\367\347+\357\334O\262?S\265\375\242\327\031\256\277\334\276\0132\362\034\201?\314\263\227\363!\355\315\277\206\321\317\241\002\253\222\277\020\374\234+\370\215\240\277\357J7;\254\010\321?\022\264\263]w\366\251?se\200\310\356\201\234\277\215{\245UG\203\274?\003N\367\025\342B\234\277\322\313\314o\014\334\273?\253\025\350\276\032\277\201\277?iP\302r?\276\277\341\027so\004\232\302\277k\366\250g\001\000\256\277T8FU\265\337\261?\362d\337\324\303\241\314\277\243pTZ%5~\277\264O\002pp\034\237?\0320\362k\257\371K\277\366\005$9\033\013\312?R+C`\362\021\255?>\337\027,\350)\220?jT\001\267?Y\270?\255\202A4F\232\302\277\307\033\236\267\370\316\307?\354\20618\2401\247?\300\231\210\321\346\006\265\277\303\'\001\354\270A\274\277\325:#\035H{\257?up\262\362\312\224\274?\305\354D}\353\023\257\277\207\377|\r\272[\273\277\314^\035\257\360\225\264?+W\271%-#\265?b{\321\272\231\251\255\2773j\307\242Sw\244?$\340\303\312pB\276?O\333\001\273\3516\231?\361\t\223\t\221\006\206\277\264\254\020\305U\344\273\277\231M!\002\300\261{?\010y5e?\365\263?\262\262\332>\315\230\224?\330\354\237\177<\215\232\277\330\350\n\300n\223\277\277Dz\373\021\271D\201?\261\373\373\270\002\223\244\277\251\333L\264}\243\277?\211\245\370Z\033Y\261?\363\302iG\235\372\213\277\272\014\332E\251\033\311?\242\347\201\336Rg\223?\224sO\205\017\237\260\277\222<!Xr\300\314\277\205Y\003\004\274\013\334\277:\035q!\353\260\262?\314\227\r\316\230\353t\277\363,\\\372Gh\242?+\"um\315\254\265?dn\004\252\331\023\275?\235\355\264_\262\355\302\277\203>l\035\2547\301?`\223\010\003r\210\177?s\361\321h$\217\275?\372\'\350\245\3530\302\277qp\264\232\365\353\215\277\220\201\203N\234\\\301\277:\355i\235\3536\306\277\022kVx\305P\300?h\022\031\262b\n\310?\374\266 \376\215\031\271?\301\212\037\345\013\022\300?\271\230\000N\230\366\246?\027h\217\036v\325\246\277;\273B\352~\030\247?\023\216\276\\\363\023\327\277\032t\375\325\372\214\250?\026\003\006\323\341:\322?^\'\244\2063\346\322?\322f\246Q\263\244\256\277\361>\000\022\266+\300?]\263\031\177\027\265\250?E\375!\243Me\277\277\257a\272\211N\322\247?\261\276W\375{V\317?V\253g\233u\353\240?d\270y\013\004\337\305\277\371t\333\366\3151\300\277\031\1770r2\211\230\277BZ\314\377\277\315\262\277\021\353\352\244P\341\227?x\326\330\271\250\263\252?\370\230\037\0258*\267?7w\036\032P\267\304\277\271\257\372#)\364\256?\212\250s\024z2\211\277\352\013\202E]9\276?\334\300\361\372=\014\225\277o\247p\246\345\027{?\n\267\031\036O[\217?\\\301\032\276\355\331g\277B$(\234B\377\270?\310\273\234\210\177\377\246\277\001_A\264\261x\325\2772|N\005\330\267\257\277G\244G\243i\222\256\2772v\305v8\033\233\277j\346\377%\305.\277?\203O\242\021u\177\233\277\227\206fLt`\252?\230\207m\301\025\217\312\277\001Y\023>S\375\231?K\341\327\177\350\220\261?\240\303\327S|*\265?\363 j93f\253\2775b\214\316\201p\231?\331\237G-Ue\255?\352\016`\005O\032\321?\310<\262z\336\350\256\277\247/\007\345$\023\265\277\"H\350A\242\230\273\277\200\226\005o\205\204\217\277p_\244\014\365\025\307\277\206\202xPmB\212?$\205:y\r\377\313\277\302v\336?\035N\305?Iq\\/n\016\275\277\250\307\257\232M\350\312?\323\230\251\212\214Z\244?\371br\016\226\315\261?\270M\364h9\210\310?\374a\266;M\026\310\277Z:F<\256\025\257\277x\353z\366\013O\273\277\303,\236)\222\030\300?A\017\005\211\r8\214?\177\307\026\234\032\010\255?u\242\364:\333z\240?\246\251\216\223\025\343\246\277<\3511\306{\234\244?\355\341\221\273\022<\265\277\264>\332\262X\261\247?\336z\235\325IH\277?\330\023\216\254\214h\251?T\317\371jQ\311\242?\000x\033\365D\021\261\277K\321\236{Ic\244\277\214\024G_e6\240\277\244ZZ\363<\237\315\277+\310\215\235\214\021\250\277\2455\327\177Mf\276\277\266z\'\341F\304\252?\\\207+L-\323\300\277\346\007T}m\001\242\277\201\003Bq\374\220\224\277\201u}N\272\025\312?\006\374N(\316\331\255\277!vr\3125\337\247?\275\365\004G\304\032(?&\373\317dOt\307?\205\"\370\026\337\001\260\277\324\021H\220\307Q\210\277\204\027\256\202\311\326\230?\374\013ge\030\212\260\277to\207\211s\030\227\277:\312\352\276\345\266\201\277^\313\001\273lf\244\277\243\324R\340\361\204\261\277\'\246\345\316s\006\266\277\317,B\027l\335\254?6\260\373u\357\006\251?\3432\222\304\242\206\266?$\006\241\327\327\322\263\277\204=\222\227\"U\252\277\234\316\302\010\343\247p\277\262\327\206d\373\336\267\277>L\363\231-\356\215\277\256\002:\235u\323\274?Lt\341\031R\257\270\277\031\207p4\035\252\267?`\366\247\242\300x\310?13I\317<\317\254?\020\242t\207\246\302\244\277\207\253\216\304\363\033\274\277\213_\313{5>\254\277\257{\207\021^A\264\277KG\031\355!\377\303\277\223Fyu\027q\304\277#f\021\234\0206\205?\210\303\300}\245\001\304?\244\376\001\341\2241\314?\013\334\004k\370-\273\277^\0037:\354\237\260\277Z7ay!\035\301\277\232: \366(\345\205?\3551\006+~\352\272?\263\226[H\\\000~\277ITr\034)\'\272\277\003\364\367\357\027F\253?;s\313Q\320\346\234\277\201]F\245\240\026\305\277\375\350\000\214m\352\322?\311\030h\\\267?\254?y\006\023\364Mu\260?\237\232%\260\355>\226\277\004\332\355HUO\274\277\374\024\275\322\342-\254?\361\022\000\265\340\373\223?\253H\306\206t\002\266?V\001\235\005G\221\270?E0\344\213=A\224?L8;\321\301\027\270\277\213\253\031r\212\273w\277\005\035\256\237\207\035\222?8\177RP\020o\240?\213\274\030\2522\221\304\277\206\314\252\261\206\344\204?\320\033\275l\256\230\235\277\333\303\325\204Y\352\267?\330\203\270+(\"\301?\016/9\316\336\201\227\277\302\242\376\237\225N\224\277\371G\311\204\201n\273\277q,\316\375\261\204\260?\032\243\177\257#\221\270?\331[\013\223B\030\275?\003\255\246\235U.\257\277\217FX\302\261R\211\277\343\001+vm\351\254\277i\252\024n\343#\264?5\307h\216\016B\262\277\017&\217\210\322=\303?H\207#5D,J\277\340i\243V\324r\250?R-H\312\257\307\204\277\244\220\210\243L)\303\277\261\227\365\377#\364\266\277 L\367\024H\372\244\277\361\237\343\217Vx\225\277\r\n%\267n\320\276\277=\251\225af\320\206\277\221\376\023\327(\327\261\277|\345\343\n\277-\316?\260\3170\352x\357\305?\003\353\346\202\354\020\255?\250\275\246\325\234\210\240?\277\26417\033\216\240?\220|\200=\372P\246\277\"\335\002\022\t\305\257\277q\352\017m\0077\263?\016\275fn!\346\255?P\355g\277\372\373\241\277\350\n!W\273V\267?\033s\274\204\227\317\242\2779\301\376A\255\352\236\277\367\240D\346so\247\277\365V\006\307H\304\316\277`\365\311(\0065\323?\242\242*\365\247\200\256\277c\201\002D\021\232\300\277f\031Qa\275Q\302?\030\027\2247p\237\300?\264\317\227\373\";\262?\242\241|\376f\357\245?h2\322D\315\200\225\277w\245$$\232P\252\277\326\223\">O\r\241?;i-kQ~\252?\006\233\221\361\316\016\261?\200k\\\333\223|}?\353\314\027\002\013\311\262?\224\002\037\231\242\037\232?\241r\213\251\274{\312?\235\033\303\255\302\214\251\277\350I(,Fk\255\277$\"\340]\207\341\246\277\341<:}\355\177\214?l\353\237\255\332\237e\277\352\275oX\373\303\310\277MN\030\315\367y\222?3\030\032\236jQ\247?_\226:\354\300\311\253?\307}\266\370(\033\276?\250O\211X~\235\275\2772&\006T\203d\256?\262\374M\267\235o\250\2779\027q\262\037\202\221?L\3321\016\251\203\313?_RMU\376o\276?\260\344|\236\372\202\310?\230\306z\344\231Z\265\277\362`@\374H\260\253\277\2615-F[^\310?\276\3271\026\3131\277?\3441\032\007\363L\254?YBw\243dW\312\277\360\005\243_\260N\204\277b\373\n)\302\024\303\277\177\326\372\3514\276\246\277\334\'\231[o\260\223?&\204d\313\303Z\255?G\312\000\0172\312j?\224\350y!1\305\307?\260\301l\177\227D\252?\2466\024<\333\001\244\277\301\037\313y]G\242\277;\340F\241^\205\261\277\327\244\264\203\177T\240?\216\305\345\310\362\t\300?{\334\312g\320\320\307?\371\373\263\320\226\242\277?\022\027\244Ep7\210\277\350\345\3025\211\"\317\277\003\033\324\006\363>\227\277\201\330\332\310r\000\262?\027\310\324\376>~\206?\032\005\310\331^\356\270?[t\240{&\376\271?f\347\ru\246\227\305\277e\375>0\212>\302\277\034\374\315ev\327\265\277\013C\260\001:\233`\277\006\2127g\250\365\312?\301\333\213\370\215E\301?\335\275\210\302\231L\267\277E\373\';\312U\252?@\006*|\177\221d?\235NT\261r\264\307\277\200$k\336\201\222\265?\357|,\"w]\263\277\001\350\312\256\274\260\335\2779\030\223\325\024\366\236?:\215\362|Q\005\236\277XHIIw(\277\277\325t\232\260\333=\275?\317\001u \006\311\222?\336\346\321\323\024?H\277\257\r\014\312\226\272\272?\355\267R\017\236\031\223\277\223\211D\305\357I\275?WEY\353\355\326\255\277\013\"\023M/\332\274\277\324C\003\310\000\317v\277\313\310!~\2768\300?V\325\004\307\355\366\257\277\r\203\244\325\306\346\262\277D,3\227\370D\302\277<\331\036\211\217U\243?\262\201\246\023\344z\311\277\230\035E(,\245\247?\355\002S\035\311i\274\277\311\315\326\214\326\326\262\277\240B\"\266Q\010\263?hj\370\241\214t\260\277\024\317\204n\261\375\203?\277\321h\321\241S\322?\315\213/\357R\327\253\277\377)E0\025\371\320\277\326\367\225k\310\242\246\277\245\353lfX\'\313?\332\314\364q\366\221\216?\323N\323C\2449\312?wxkP\270\261\266\277\027\314\177\267\242\375\232?\375\366\207z*x\322?]\206\3366\205Ox\277\247J\227\302d\205\241\277\007=l-\356\246\314\277\347\232V\'7\372\250?\344\317\316\370`\256\316?\267\"S\036\016da?\300\005\3257\371\263\316?\251\264V}\032\233\267\277c\307\021U\207\345\320?\363\013\352`2\234\305?\365h\224\n\241\272\245\277\244\003\272.\216\254\273?\271\377TB!\032\206?`3\227\344\241=\263\277\007\2210\350xI\307\277^.\035\010\2103\271\277\303\014\357\242\357\254\254?\277\210C6Fp\323\277\342\261\\\301&i\261?\005\000\321\0219\360\261\277CR\205}\000\005\253?\321\313\334F;\274\327?G]\374J\252x\313\277~\343?\273[<\272\277\276]\261Xs1\240\277\352p\212\276\343\263\230?\360\211\356\202y\345\274?\210O\026\217\344\001\241?\355\377\272\020\021:\267\277\222\032h%l\375\302\277<\375\243\216#\270\267?\021\"R\363\233\307\240?:8\242\245\026[\260\277\217\031\302\232\277P\254\277#\336\377\213\241\252\256\277\315\252\2338\274\262\314\277\313\177\256!~\033\320\277\257\213\022\232\226\007\272?\021%mX\222\203\205?\245X\006\0016\272\264?*\354M\311\007\306\314?\271\301X\360\271\341\260\277[\231g\375S\253\270?\373\016\303\005\210D\235\277\261v\266\200F\216\255?\0311\026\267_\234\255\277\007\256\206,\371\221\305\277 k\241\342wH\261\277\025\312\003\346WT\271?\360-\370\262dqZ\277h\017\253|\220Y\266?U\277\234g\361s\257?\377g\323T\263\235\177?5\250\350\321\256\014\241?y\3249B\312\306\235?\'\332\207\217\205\225\272?he=\214\225\033\304\277\251\003Z\024PN\220\277[\031&\201}\345\260\277\341\217\252\337\2765\251?\200\322\203j\373\264\243\277\370\257\270\003#1\241??\206\315\336Wm\233?\205\026\263 pJ\264?C\312Z\230\221_\225\277C\272\017C\243\374\305?j\006^\377~\257\253?\336\207F\344\241\333\230?k%\300\260\257\305\235?\210aeRr\253\275?1S\347\212\275w\260\277<H\261\177k%\243?\227\201\320%\232 \247?\225\222l3]\231\263\277\352Cc\351\021\330\255\277\t%\373\033.\202\267\277\345\242EF\314\274\225?o`\361\322C\227\265?\026\263X\350\030:\267\277\030\272\205\235\333\223\222?\362\216\263\311\245)\261?1\201\361\330\320\030\316?\252\246\035M=\177\217?3x\2676\266g\270?>\375A\025;\306\275\277S$\371\026my\265\277@\314f\243\2325\261\277k/\250\216-P\261?cp|\333\273\245\321\277\212\254\030\321jo\240?\256l\263\220(\353\322?\321\227\316\220\302\367Y\277\017\253\272Ek\371\261?\177y\001]<\202\241\277\301\230\365\337&w\240\277\020\351\310n\360-\301\277\2043Y\2753I\243?f~\276\371?G\272\277\024\345\255\235z]\210?\256s\007\270\336\253\241?o\205\211\372\242\231\260\277=\310\265*\036U\270?\342J>.~\353\304?\215\r\225\310N\360\256?\215\376\267P3,\270\277\207z#\362\373d\266?\205\n_\014\246i\200\277\242\367\322N\007\275u\277\326\314~H\225r\213\277_[y \036V\307\277\237\326\022\203\036\235\233?\270\263I\265\235\001\257\277\215\344`N\366\271\262?*5\235\276@\350y?\352\341D\010\203 \230?\252\003v\314\265A\324?l\021\350\376\214\331\261\277J\0175\325\316\326\301\277q07\325[\000\310?\361\357\253_\001\335\243\277\247\366\250x\246\202\311\277m~U\340\351\277\266?g\220\265$\313\306\301?\002\236\277\253v\222\300?IE\231m\204\266\237\277*Eb\023x\223\260\277\363!7\202&\314\274\277\375\251\335\017\014\212\205\277\004\007,Q8\364V\277*\355! R\366q?\303\267\272\210[\"\235\277\374\225\302\207\270U\221\277\237\245M?\376l\247\277\272\213V\245\337\306m?Ng\000F\366\316\271?\223\224u\230V\201\247?b@\202\n\256\345\224?\356\233\202\207\364D\220\277\223\253\026\242\n\341\260?E\030C\003d7\256?}\331\264\330\006\233\241?\204\307n\005\3722{?_-\304\266\350\007\220?m\21105\2544\250\277\317\247\257{j\330\214\277t\177\325k\227V\222\277\203M\332TgZ\246\277W\210wAs\000\264\277\353T\217\364\253\361\207?\205\2762\363\246\'\203\277p\317\303\346f\030\241\277x#\020?~<\253\277L\304\320K6\274\211?Z\333\231\265\002m\300?\023>o\320\331/\261\277\376\246\021D\342\312t\277\347\277\345\232\2439\\\2770\352\025\026\313n\302?7\256\254=\014.\310\277R\3013\323\031\267\245\277U\205C.;n\260?\335\212\271\273\002\241\302?*=\346\034\301\334\305\2771w\310\316\351D\240?\327\223\365\020\007)|\277L\262\026\214\301\264\247?\201\214\327\331\036o|\277\023\253t\321\227\372\264\277\262\361\030<\247\255\246\277:\323]\332\001\310\216?2\246\\\270\303\007|\277r\340\361\340H\270\267?\346\247\3725\310\371\216\277\217\327\034\210\215\247\247?\274+&i\347B\255?F\220\010\306\247h\271?\214u\227\233\276\266\227\277\213\001J\332u\302\305\277\230\353\362\341\275\344\266?\311\352z\221=K\265\277_\345\335k\322\003\272\277\305\203\273\227\322\373\210?K\207i|-Y\231\277\357\356L\233wL\226\277\244*\n\233@\254\221?2\204<\354\000\375M?Z\326\'\336\205\207P\277Ke@\373!O\260?C\241q\322\024\"\266\277\350wyC\313\363\234?\"\257]}\342\233\253\277\302\037\362\020?z\302\277\352\260\370dSYt?\306\337+!\307\211\303\277\013bj*\020\240\250\277\340\271\306\334\231\272\261?\252n\234\002\204\215\302\2777\332>/\177\276\214\277fLdZ=\367\255\277L\334\000%\340\312\241?\243\300\025\311\336\342\302\277\tM\215\233\n\267\260?\031\246\335\3278\034\216\277\344\235\206\313\240u\320?\334lyX\245\026\300\277W_\337\241Wv\260?\t\241\005\236\234F\264?#^M\"\316\035\322\277\3224@\241W\377\257?L1\205H\313\242\267?\237\371\271\354\333}\260?\177\245\334T\2643\236\277\236+o\017\022\342\271?]\022\247!\273\263\305\277U\2558\305\240\323\247?\002mG\235\036\205\253\2771\340\347\324\343h\310?\311\337\362\274\365\006\323\277\376\262Iy\305P\262?V\342\246.\001b\265?\'3`\276\305\367\233?\3526\321{\217gr\277\265>\014G\211\303\257?\227\365\326\025\267C\276?\204=o\364/9\302?\030\013\372\346\301:\222\277\031\237\'\001\255\252\203?e\007\264\204\342\033\273?X\227\221\243\236>\271\277\3162;7j\236\261?\364\335\024\206\307\r\253\277;\277[\226\362\016\234\277\363\322\370\355Us\266?\014:\256\256\023\241\266\277l\244i\235b+\300?\266\221\"T\266\020\264?\032\277\\\263L)\201\277{\230\240\253\345\202\030\277_\211P\031\236_\313?*7\213a\364\300c\277V\344\376\007$R\275\277-\237\022\220\213\276\276\277\240z>M\036\320\261?\305.\373\006M\305k\277\324ed\233B\340\254?\204\036\345\211\217\261\222\277\2654\265\005\n\322\235\277\273\310,V\312\334\276\277?J\304\"ms\240\277e<\027z\025\351\234\277\372\277\242\367\350\325\234?I\026=\t]\213\271?\215\307\344\222\037\371\311?\362`m]\351\251\273?\013\233{k\360\332\303\277\221\354\264\353v\374\300\277lVg-Q\354\216\277\n\253Y&\363\204\245?#\313\353\016@\203\266?\026\254\311\335\322\203\236?v\234E\256\326m\225\277\246\244\020I\207\234\254?\212\212/\367\363L\263\277^\013\217\257\361E\266\277\354+Vp\330\013\211\277 \216\375[/\007\250\277t\336Ek\021v\255\277\232\324\r\203~\353\263\277^\332YSn\205\257\277\302B\261Q\014\003\313\277\031\016\311\017\230~\330?\374\252\334\335\034\350\225?S\235\230\366H\233\263\277D\326\r&{\340\262\277\301\206c\230\203b\305??\331\366\016\030\'\253\277\231N_\333Y\264\241?D\027E\320\300\301\302?v&\337\334\235\324\243?\211\324G\304\353z\263?\374\306\327ol\353\316\277\251\244\370\372\265d\262?\177\010|\321\273\250\266\277|\262\223\313\024\366\265?\335Mx\r\030\354\247?w\237\317\315\220\305\260\277\371\262\023\307{=\274\277C\337\021E\r\032\261?\250Xb\232\332\352\246?\255\352\226*\021U\250?\364\020\032\312x\326\304?\335\261y!1\255u?C\304&\313\020\346\212\277X\242L\214?t\243?\2416\017q8\001\204\277\314\032R\312\0069\261?\022\360\337/\0022\244\277\213\006,.\366\237\264\277\211\274\326\300\"\255\303\277\217\332\226\362\206\304\262?\370\333\215\212\371\317\302\277\207mJ\232\001\024\230?\316\344\315\341+\200\247?P\t\270R:.\260\277kR\263\037\211\031\220?\306bX\303\336+\220\277(\325\000N,o\235\277\323:\025\224\363\233\260?\022\304G\263\3733\321?e\037_\371\315\216\214\277o\237\2375\027\314\211\277\031\237\000\025P\360\324?\321\351\242d\237\346\266\277\356\363\007\273\3272\236?\004(\007\2518\245\213\277\207\2243\263\353\307\303?\240T\020!\377Q\232?M\312\032\313\214\262\272?]PE\036QW\307?\003%\244\307\2739\274?\251ir 5\353\263?\022\003\201\243&D\265\277\233H\223\377-\317\277?U\372\252[Y\244\273?`\261\237~v\253\266\277\276\031g\260\017\272\230\277\355\356\345\334\337\301\265?\250b\223\253\357\262)\277\324\341*\005:\t\251?D\341(j&q\315\277\271\300\303\220A\262\234?.\316+~\317x\255?\265\031\2663\356\357\260\277\263\366y_E\207\242?u\030<J\240\345X?\273O\216Q\252P\270?\277\333\226\017}b\277? $\213\323>\300\302\277g\303u\352\336\031\274\277\373\226\216\255o\356\252\277i:\262\335j\341\275?\251\004n%\255\r\226?\347\363\034\323\000\t\301?\333\027\'B13\262?\005\370J\031\276%\300\277s8K$\233\210\234?\003s\352\264\177\275\256?\031\367\267E#`\260\277^\357s\341\217\251\255\277\362\262\203#\257\354\227\2778@\347\317M\025\265?\263\2748\250\0239\277?\\\276E\032\375\362\257?B\234\235\361:\215\330\277m\372\023\236\316e\264\277S\232G;G\222\275?\202\240\363Z\302\301\225?m\343\203\022j:\277\277Xm\034\013\026\251}\277k0\270(\246\265\263?\275\205!]\261\366\264?\361\210 \324F\261\307?\336Z\242\336\310W\263\277\372\210F\332\214x\005?\207,\313&\226\314\242\277\"m\356d\266X\300\277c\261\033=A\024\237?\243\334b2\223_\207?y\202\032\3038 \262?\216\300\306Qr\245\262?\023\324\213\220D\263\317\277\235\210\272\207\350\215\261?\030\3329\210\355\322\221\277:\347\037\304\345!\261\277\231\264U+\301\353\200\277\257\323\335W\377f\270?Y%\271\327^\212\223\277x\017\343y4\363\224\277\331C\223\'\360~\245?@/<O*\317\274\277p\020\326L^\225\250?\026?\336VX\320\245\277\270\331\343\3724\007\267\277`\007b\260\355\336\241\277Q2?\371\261%\305?\207)\340\265 \341\270\277\352\375\031\2209\226\314?\025\301[\006\362\246\261\277\221_\266\33621\216\277J\341\366s\367\007\316?\273\rfR\366,\260?\374\303\'c\3625\245\277>\314\230T\254\200\303?\322\345\331\227\036\006\306\277nS\243\340\260\226\223\277o\246\372\177\313\315\246\277+\247\3527\207r\300\277\217\311\310\213\204\371\244?m\321\021u\266v\304?{\n\004\016\200\311\227?\023\265\204\016\tD\320\277\221E\370\0056\204\224?[\321X\332QU\267?\212ug\304\267[\267\277\234s\003\371t\"\253?\nr\315\231\252\221\301\277\022\372\014Lxi\241?\312\250\003\036m\007\305\277\325\242\332\023\tg\322?\271\0177UH+\240?\207U\246\277\376y\321\277\211Sl\364\212=\267?z\211\364\227CL\226\277\262[4F\265k\263?`\335\211\341w\375\262\277\220^t)\245\272\250?\3769\014\003\246\036\303\277\270\006\035/=^\222\277\200B#W?f\242\2778\267\3532\2073\262?\262y*\254 \314\303?\233\326\310\317\332\364q\277\347\034\212\213O\027@\277\260\025o\304\266\336\254\277=\265\216\025\214\225\307\277\016\266\027(s\346\276?W)!Is\230\253\277\242?>\355T\207\246\277\211\030\"\005\366u\266\277\027 \243.O\347\265?\230\272\256\023\007q\234\277!.\002\344\004\251\307\277\201\217F\2731\237\230\277\276r\022@\235:\254?\222\262\2447\023\336\300\277\312\206\342\322\007K\261?;\354\272\242AI\276\277\215\250\242\txi\270?\330<\271\340<\373\306?\375t`\245+\301\272?\355\337Ht}\325\257?\206\002U!\350&\275\277\2629\010\316\365O\242\277\376\272\'.C}\240\277\374\300\206\236\225\363\202\277\372((\240\363H\300\277i\352\336LFv\260?%))\312\225\300\302?\231h\277;e\261\227\277\3620\n\310\320\354\216\277g\016`\375i\313\222\277\366L\213\233\231\022\262?\257\rbg\303\021\311?h\210+\303\212\024\261?,+\274\263\310\"\302?\272\214\344\203I\366\311\277.t\276\271\303\023\241?\201\205\366\332\214\306\320\277\001\232e\013+)\226\277rUo\272\263\371\261\277\022\310\321\026i\035\262?Fl%\017\0063\277\277\252\350fc\252\365\242?/\005\316\036\361\214\227\277\312\243p\204\030\243\242?\200,E\373\215%\300?\261\244\217\232o\226\330\277\3362\'h\257\231\226?\323\276\251T\344\345\237\277\316\314\342\335\001\321\256?\251I!\'7\267\263?\351.:\340\317\304~\277\325F\222\232:T\253?-\016X\302\n\236\241\277^#\030\327!}\245\277\240\334\226\227\361Z\304\277\353dT\306=\300\307?\315\305\205\034\342\332\300?\272(\220\365\001/\241\277\35421\342\307\003\303?~K\231\200\313\372\310\277\325\201O\303\270\227\217?iW\177\274J\246\232??\372W\243Qr\272\277\177\276\025\031\233\221\253?\223v$\376(\033\257\277Y/$\370\226\030\205?G\n\303$\267\304\274?\237\240\262\014\224\230\266?\'`\237\245\324\027\243?\2757\225\333\213\364\257?\237\346\021\277\221\017\205\277\266\260\226\365\311\335\245\277zQ!\236J\262\321?\240yL\002\336t\225\277\303@\364\302\334(\253\277\0253p\204|\376w?\305\234\337\213J\310\274?\335:(/\330\361\224?{~e\207\304\255\245?\201\325\371\256#\217\266\277\202\334\302O\rN\240\277=l\3656\345\373\260?Q\210\372\343\3068\270\277\003\202\311\213o\250\263?\020\227\003\004q\332\277\2773\034F\274\302\204\253\277:J\327\226\351\027\316?\275\262\220S}\362\261?\311{6\277\212\242\250\277\233M\200\235\341\000\257?\340BR\377\252\032\275\277\225yoy\253\177\257?u)\272\347g\316\305\277c5\302\224\233\353\245?<\214\002|O\277\235\277\032i\252\355\312e\240?i\036\245*\325\202\240?\372d\233\2466\303\302\277\354\022\254\006V9f\277\310\241\376\242X\021\277\277\326N\341\300\256\335\263?\342=+\r\361;\322?\205\366\210\215\370U\270?\202\254H\270\t\315\246\277z>\344\202\313=\300\277s\365\232\225\316n\310?z!\323\334\352&\227?\014\367\277\005``\261\277\214\201\234\223\262\037\274?i7x\371m\306\215\277|\347\350\374\300+\256?\366\007\202\252\013\263\303?\316w\357\242\253\312\260?Q\006\003Q2\265\273\277/\003j\301\201c\246?\340\007\342y\222\344\247?$\215\233\351\221\277\277?3\\\333\020\302(\276\277\300\301\236\237\2558\264\277j\230\257\3645v\257\277&X\203\217C\313\233?|\231\014V\267\364\300\277f\262\006V\214\301\310?)h\266\306\367.n\277\372)\217\024\276\343\264\277\302n\372\200\215\315\262\277Hl=t\035\315\264?z\367[\033\331\303\302?\364\037\210\005\231\204\266?\317\251\354\203\335\221\326\277\331\024>\010\313\036k?4\344\320\344\304O\271\277h+1\220r\\\252?\237\377\313\0136\270\264\277\004\326(]H\206\255\277^~_\002\312\270\257\277\275\010\352\r\235*\215\277\326\207\317\021I\224\273\277\005\026C\2255y\265?u\005\201\230Mb\300\277/Xh\273b\231\246\277wa\007\353\254\031\270?\034\300\023\260R\235\261?\353\212\342\251\241\371\245\277\234u\200{bN\301\277l?Jj\361m\220?\350\310\016\026\261\271\310\2775\033\323i\200y\263?\223\325\032\361D\236\244\277\305wF\224r9\274?U\010k\262\013\311\307\277\353\006\377\036\361\203H?\333\331g\034H)\303\277\004:\315\255\034\363\237?i&f\265\353\222\274\277\377/*3\016\322\237?\367\374\2639\334\246\312\277\030\363\ntC\013\224?/7c\303\027\321\223?:m\211\313|2\250\277\227\036i7\264\013\206?x\261\244\2609\265\222?\261\340w,\252^\272\277\032[\261\303\254\227\224?\001\275\372\354\313\257\240\277\236\230>\3036\333\246\277yQ\302\020\020\373\243?\347\301\267\236+\323\312\277\tD\023\2168\255\243?Le\262\376\232\032\206\277\010\202j\262\370\014\245\277N\273\244@4B\255?n\346\204t\352\272\205\277*\263\330\246|#\223\277\370\317\215\264\032 \275?\250\271\375L)\302\216?\227n\243{\325\341\263?\361\321\251<\222\235\302\277\022\036-\005\201\303\237?\\\345\020\234\326\220\\\277\013\220\252K0\375\260?i\275\263B\362\350\271?X\323\212\003T\203\246?m\"u\250\345(\326?\007\347T\017\n\005\320\277\220a\0345\300r\263?\316\235\010\214\263_\277\277\014\026\377\002\247\337\300\277\032mG\373\342\000\301\277\027\001\031\366\214\207\243\277\355\302\2251\314g\264?#PX,(6\207\277]\016Rf\036d\237\277Y\007+\303[j\250?i\256>@uB\263\277\250d!\377\032Q\215\277\200\032a*\025V\301? \256\364\254\356\032z?:/\223n\370g\214?\355c \247\037\032\235?\272\267e\341\304\250\233?\301\204\354\321\334\"\257?\200\316D\r\255Yy?l\202=\366C.\302\277\014\t\033p\275\227\251\277;qg/\236,\300\277BQ~\202v\276\206?-P\367\2459\364\272\277\254\024\307\373\203\343r?\210\004\036\\\370\302h\277\\H\332{\251\350\256?\n\264\212\022\342W\224?\246\352\261=8\272\313?U\314k\214w\204\262?]\236\272\r\024\361_\277\326s\227\262\005\005\256?\247\262\354\014\307\214\264?\007\372\t;\305R\263?1I\204\013\243\345\245\277\360\t\3251\032\177\302\277)\231O(N\034\304\277W\024\t4A\332P?\246\"\217\352\354^\275\277I\324\251\300\366\305\321\277\255@\265\re\305\261?\203\004{\311\262C\230\2776\363f\271\225A\301?x\027\317[\345\274\320?\236H\221\005\245\255\264\277\315\370\235\350\340\235\307?Kx\353\2626a\243?\264\373\276\200E\255\302\277\027FuOH\010\270\277\372\307\0331\263X\275?$\220A\020D\214\266\277\\\006\026\265P\350\260?\305\357g\0367\301\264\277\300\245\233}S\233\276?\374\273BoyK\220?\020\r\236\020]9\260\277\271!\232\355\014\371\302?4[\216\260\230\006\271\277\252XP\030\220\321\255\277\020\\}t\226C\233\277p\2347!I\210\323?\303\263\'h\030O\236?\027\243T\224\204aq\277\210\020\224\216\363\232\300?\006\023P*^\r\204\277AG;\373\275m\243\277\272u\301\210MP\232?\006>\035\254\271\343\270\277\243\362\013\2531O\253\277$?\311,O\006\271\277\362\371n\005\377_\301\277\310\306U\311|Z\312\277\214\304%\006\316h\307?\364\245\337\027\216B\256\277\006\307\376\272\303k\226\277p%9d\236\311\302\277\357\330\226\325e7\300\277\263\202\271DK\332\217?|\215\235\302[*p?R&\323\355\264#\277\277N\363v[\020\343\240?%\03593\021\351\242?\261Aa\260z\334\227?!@qY\2440\275\277\"\360\226\tX{\262\277\200\360_*\3263\257?\\\273\014\360\3674\252\277\342\275#\021\023U\245?\030\266\313L\264\321\304?\214\211q\262O\211\273?\262\337\367\004/\333\260?\216\253^\"\024\034\313?|\267\343\232\373\347\227?\376]\255\377\360@\241\277\211L .\240H\264\277\315!\307\022\303V\246?$\277\326\240\235\222\215?Uaa\331Ce\222\277\3569\000\234\237\334\265?\224\234\340U]\363\322\277C_z\202A\320\225\277\204\210P2\312\315\223\277\013TA/\253\372\261\277\265\007)&W\023\271?`\260i\323\244\365\261\277\311\266\221V)@\300?7\360\014vw\263\244?$\326\304E-\215\255?\220\nX\361\373,\242?\205\004`\340e\n\302?\\6\'\266a,\213\277\300\271\364\310\003J\206\277\000\236\021\332\211\210\277\277\247\333\245j\r\271\320\277\372:\307i\203 \224?\254;_\211\312\034\303?\366g\3137\222zz\277u\333\020\216\367\370\252?\302(\32279\270\303\277\177\375op_\026\307\277\341l\010W\'\267\302?\n\250d1f1\301?\251\252\222s\004i\300\277\262S\227\361\377B\305?\201\233^\355\357v\241?*\331\314\352\270\341\304?s\245\264k\333\337\313\277\016\2253E\271\372\300\277v\027\347L\222\206\236?l\353\016\226\335p\260?Y\335\241\210\235\316\265\277\0073\023\374fB\224\277\377\006<l\036\246\303\277(\366\244\031^]\250?\006\343\366y\236Z\271\277\332\215[\202\344\315\226?\354\312\275c\307c\261\277\223\240\264\t\255\263\320\277\210Q\307hd\243\266\277/\372iQ{\234\240?\376\375x\032pN\277?\225\263\027k\177\202\243\277\334\341\277m.\266\266?\005jc\020\244X\251\277\242\314F:\351\373\303\277\355J\037\202\016\346\272?\352\263\307c\327\004\255?\t6\366\320\210\201f?\306mho2T\310\277m\2228\237@\305\264?\003\201\270U`\3651\277\355\203\031B\"\233\260?\033\340$\332\332$r?.\032\336\256\342[\254?y\276D/\353\360\320\277i3\255\3151P\267?q\335\235\274\010\226\241\277i\273\211\016\001[\266?\311\337\372\0224U\264\277\237\336}\034\272K\256\277z\357I\320v\252\225\277\346\316\200\333\237\013\264?\257\261R\221\227\367\247\277U.]\000j\242\207\277\301\212[\002@\331\214?\016\272Y\375Y\025\303?\266\245{G\324\212\263?\252d\342\241+\232\312\277u\032`\016\312/\300?\314a4Eq\016\220?\233\362hm\034\010n?\273\347\315\306\021\354\223?\251\237\313\206C\235\257?\212\373G\002\343\332]\277\372\025\301$\2705\211?\244^0\3463W\261\277\352t\346(J8\310?pU\321\235\016b\235?\252\312fnz\353\235\277\250\022\235}i\277\244\277\001O\355.\004\253\241\277\235\023k\306\221\205\240?\370K\254;\027\361\302\277\262\217b\363\217K\302\277\353F\214\251\302\342\276\277\025\377\322\367KA\306?5V\253\377\321\322\326?\373u\317\357\006\213\325\277\301\016\031\357?>\204?&zCX\321|\245?{6\314P\000\237\262?3\206A\351\253\002\236?\350\2145a\211\265\263\277P\205\246`\2652|\277\245\212\r\224\255^\247?R\245\027\202\023\014\306?\215\252D\313\2656\267\277\326\370j)o&\312\277=\263\270\207\006\033\253?\210\376\312\261m\347\313\277T\305N\222\333\335\300\277\222EH\327\337\372\235\277\333.\256cT\333\234?)\003)\355\241\233\224\277p\3751\201s\364\305\277\005\350m\002M&\227?\203\275\024\275\363\237\245?\357\203\004\361\2349\273?\361\213B\032\222$\300\277\375\354\321|*\245\303\277~Z{_xG\326?\321\332\225nG\345\336?g?\341\034\013\001\265?\213{\013?\014<\220\277$\353\254\247]M\253\277\274\263Y\363\032o\303\277\025\204L\034\366\371\265?\232\270\221tc\200a\277\377\362@\320\227\345\257?\360c\0329\316A\230?5\tWM\367\374\307\277\362\300\2315\233t\271\277\251\240\267\024\261\342\274?\013[\354\355\345\374\256\277x\222\001\201R\252\243\277m\333\313\257\276\323\341?\335Hv\210\035\274\241\277-{\246[/(\227?\323\361\256u\302\226\276\277\317?\306\236\t\322\277?n~G\017\303\371\273?\240\254\013Rk\341\267\277\237\030\235u\232\364\300?]\340\013\014o\222\251?Z\347\203\351\302!\226\277\254\216\005P\232\252\306?\021\026\306\024\001\035\265\277P\262\205\232\037ms\277\373\207\002\306\335_\320?\t\200X\201\353\224\327\277\0347\212\221\321\'\250?\236\ni\231\274\237\310?\345;\342\317\303\315\234?\237(\237\215\004\036\300?V\374\371\265\n\321\235\277\3660n\360\004\314\220\277p\337\316\307\240\356\246?\265\013\337\274B\363\243\277t\304\246\333i\352\262?L\003\317\240\316\326\263?\016 \n\032pj\300?\'\254\265\326\024|\217\277\303(\253\270@8\224\277\326\331\217\301s\216\264?/+EqP\363\205?S\\7>\331\241\227?\366+{UW\354\260\277%\304__\270\372\251\277\024\001\241\306\363\246\263\277\271\326s\254|h\223?\321\253(`G\342\254?\013\216*\003\3038\272?\206\352\261d\316\364\273\277:\360\371\007\236\202\273?3J\031Y\"<\260\277\2633\255\202-h{\277\277h\242u\r\305\272?e\271\302\314\251\253\315?\357r\0271\213\177\262\277\263cb\030\275\361\274?\274\001g\334c\252\310\277\323\246\332)\374Q\276\277\333\'\236\337O\317j\277U\352\'\205Ie\241\277>>\247(V\326\303\277T\366\203N\247g\260?2O/-5\000\200?AUuD\027\270\270?Z_:\344\3725\250\277C\321\n.\341\031\246?~s\252\035\303\330\301?\273%e\177|\320\306?\230\261r\224\006L\270?\323\221\255v&\376\272?2h\234\037\245\306\256\277\036\026Z\205\272r\230\277\227uy\270\332\366\303\277G\274w\222\363\332\252?\347n\350f\244\307\247?~^\310\242\351\014\256?lGlB\374\244|?7\244\341\221w\235\253\277\242\020\277L\263\034\300?vb\334\225mf\310?\213\262IME\230\307\27701\367\301\\\320\307\277j\021\330d\005\217\264?G\321\204\222\347\020\310\277G\rz\373\341\017\303\277L\213!\333O1\271?\2663\217\206\3732\265\277\320\007_>\353`p\277\236\345\355\337s\350\260?\031]q\200\364\316\263?N\274{f\314\313\257?\264\217\032\020\364;\325\277\004\250\314\355\000|\262?\374.\367\236\332\345n?\020[\353!\245\341\207?\210\224\227\014x\362\207?\371\376\231\307s\037\215?B\232\341*\tf\274?\242\251\244\r\235\020\270?\344\232\261\333\375o\251\277!W\243U~X\245\277\352\267Lf\202\226\265\277\225\347Y5\311\243\303\277\034\3032>\363\354\240?\355\333qS\365V\241\277Bt=\232\022\355\303?\261\316d\334\321.\243?\003]#\360uK\270\277\300\210\n\262\276C\275\277\003\227:\221\322\220\306?\210\260D\324\332\212\227?zV\262\372\257\013\260\277\221\033(\306\013+\300\277L\"mV\3755\224\277\031\335\215\r>\261\253\277\014\345\335,H\311\306?^^1\0366\253\265?\310\334d\271Q\337\216\277>TiG\314t\257\277\223\361\226\350\2371\240?s\014\217g\255\320\244\277\241\200E\335\3740\240?\333Oi\247V\000\267\277dh\224m\255l\203?N\300\247\364Cp\242?\352x\325\037\363|\247\277\330\330\311b}$\276\277\232\214\364\341\271\362\255\277W\232\370\315\266\323\304?\261YJ\217\022\353\272?\014;\345b\262g|\277\226\350O\252}\245\256?\010\371\255\310\023\'\274\277\360c\340\005\006L\267?\231\326\321\301\373-\244\277!\030B(&\001\262?E\323\345\350/?\240?p\022\\\231\307\336\265\277\253\027\036\215\376\355\221\277\2623Se&\265\243?;N@\312\336D\321\277\236\341+uL\344\226\277`\363\214\262>\357v?\342[\374r\2537\265?@\023\231\205\001\311\256\277\3500\365\334H\270\263?3\263\220b\004\275\222?\234\323\301{N\033\216?I\366\202\336(\226\302\277\311\370\023\264d\225\211\277qCf\350\210\353\305?\"\373\306\376\343}\312?n\273\003\321^\241\203?\331#3\004\253\263\253?\212E\342\030\214\023\261\277J\321\332I\330\t\246\277\246x\256:\007\266}\277>\351\201>\205R\247?4\220\3002\302U\322?\371\307H+k\353\255?\\\263\023\006t:\242?\020\271i\002\375\326\223?\217R\326U\023g\255\277<\276\035\215\3773\250\277\3119H\220\007 \253?\321kS\036\017F\303?A\213\035\271\270/\260\277\324&\3753\201\224\275\277E\215\315K~r\300?qv1\001\343+\324\277=\3413\376-\017\244\277\2600\371s\000\331\300?\275|\\H\352(\321?&G\001[\242`{\277\216j\2170:\362\214?\376\212;6\205\371\274?\212{\210 sk\244\277(3X\320@\010\240?\335Gpp\314\027\300\277\305>1\002=j\227?\201A\301\344\276\242\250\277\310z\346h\301\366\306?\256\210}\220\223\274\277\2772\025\210/\214\355\270?]\250S\325\'i\224\277z\273\376R\233\244\274?\317|\323\273\025\004t\277\025[S\340\316\325\202?\222\177\034\022\312\020\245?\343\267\261l\205\252\311\277\356\214z\000B\013\244?\240\213\205\206\'T\217?\215\006\312\333\321\022\271\277\333\237h\006\250\240\223?%\377>24\236\261?\305\206B\367R\204\262?\224\335\305H\223\363\303?d\322\366\311\331\267\274?D|0\020g:\230?x\241\372Y\374\265\266?\245\247\004/\245/e\277\361\331\215\3001\370\220\277\352s\226~x\241\267\277_\320\327hu\027\260\277\230\364\355\376g\322\266?\233\016\363\221\350\007\241?\264\240\222^\214M\307\277\254\367?\212\276\256\241\277\332*S{i4\237?\272\331\3143A\201\265\277\346\260C\231\365\307\300?f\262\035\210_\351\226?\306Ex\034\311\217\260?\016\272h\250G\327\243\277v i\210\276\223\276\277\367F\3025\246!~?\266kb~\002\014\305?\232\242\243\202Q7\240?[\323_tx\357\256?$^\201\000a:\241\277X\253\255\316?i\236\277}\0139\201\323\213\223\277\2271\220\252\260u\202\277\306\246\351\"\242\202\300\277T\026\230.\373\321\327?\316\243\247\327#\226\300\277\241U\330\003\311\323\240\277n\201z[\312a\241?\274\344\363\327\030\257\241?l\210\205\327\325\341\214\277\'\245\215\313\260\341\220\277p\300\224\216^u\324\277\275[\311\330\305\341\226\277\243\257\2079\236\014N\277&\211\256j\272_\272?\356\3453Z\2147\277?\361\205\354\375g\245\231?w\334?\230\305\215\210\277\244r\n<\2712\263?@\332\216?l\\\266\277\251\360\t\267\224P{\277\034D(0\252\303\241\277\327C\221>\361\214\232?\261\210\355x2\362\262\277\267M\270\330v\030\274?sv\251\267d\337\272\277\353p\344e\375\323\255?\350\024/\243\0225\245?5%A\212aa\323?\010J\253{\202\"\263\277\354v\217V\373y\257?\203\316&\034\252\323\304\277\277\204*\030\265\014\317\277vjg\025_\372\270?oQ\t\315\0131\211?\350\003M\206\251\277\272?p\377=\207\005\307\257\277n\247\250\352\244P\256?\016\305\261\342\263H\276?-uw\224\325Bn?\004\270\217Lf\025\177\277\001\224\267\007C\000\262?e\302\237\376I1\316\277BOd#DH\325?X\375\'R\213\265\211?\302\026~\273\323\334\303?%\370\200\233\3320\310\277X\004\236\203\220\341\250?c\255v\255\025n\301\277\005\273V=+\030\255\277\333);\006\016d\314?\273\3172\216K\327\250?P\3572\030\256D\214\277\246\246\373\373\206\213\252?\324b\031\034M\305\261?\253\326!\016\210@\221?\214@\264\020`)\260\277\273\215\300%+\342\334\277B\\u\0235\227\301?\207#\200\307\327\030\263\277\313\256\246Zf\247\220?}\"\252\357\216\220\264?\233\n\013T\272\027\243?&\2348\344J\311\262?\306\357\335\263][\307\277\367\364\246\032\037\223\247\277,|\007\302:1\261\277\314\350|\207\022\355\256?\023\030=r\324\203\216?\177)\351]\205\335\251\277\226p=+\312y\267?\204v\366\266\374\233\315?\224\307\370lV\021\225\277\350eD\365\316(\312\277-\201\344\367\312`\201?\217\241\364\262\023W\213?s.pS\223)\275\277^\006\367pg\263\251?]\376\"?\234\366\300\277_\"*bZ\230\211\277#F\333\"\257\362\243\277W\"P\300!\317\260\277\0050\221\030\025\354\240?dBP91\024\242\277:Q\214\275\010_\310\277\362\352\246@Y\004\327?agu&9*\277?\363\215\246r\336\272\253\277\304\236\303JpP\240?\023\353O\230\246\333\266?\246\'0E\326\033\200?\250I7Mr\210\206\2777\250M\222.B\263\277)I\213\364ks\301\277w\243H4\240Q\302?%,c\225\354\356\247?\025T\362\246\214\204\274\277\013+\267\246\247>\270\277\246\275\006\316\326\342\307\277\200\225\335\275\330\021\323?\306X\257`J)\251?\366dG\370\235\245\252?\026qTJ\035p\254\277\002\r\232l\214\036\201\277D\177\014t \374\244\277\207\002\327\177r=\265\277hq\033\205\000\352\263\277O\235;\013\371\357Z\277\222V\330M\214\336d?\016\257\216p\323\263\272\277G\247\3568\251F\243\277\375\233\232gX\324\205\277\245n\'\342\335\265\321?\203z8\354\016\032\337?v\016N\237%_\232?l\352GI\320\207\247\277oz\371\307]\001\240\277\300K\343\177P\377\240?\010\215\241\276x\354m?\342s\337\350D%\264\277\314!\277\";\221{?a\025&\202\234#\224\277\304\374\251\337\002\211\273?\325{[\262?w\266\277\3469w\231\261\375_\277\234Y\376-\211}\206\277nc-SjE\304\277\247\355~^\315\252\244\277\0233\354,\030\013\253?\370\326K\327\026\220\261?y\2703\205Z\207p\277\330\036\301\030\213\207\266?\177@\304\203e(\267?\014P\231\026\004\310\227\277\370\007\213\0358\357\310?\372\330\033\224\003\245\261\277\347Qo\303\351B\264\277\027\023F\364K\323\272?Y\374\355\350\204&\251?\210AGOp\276\226?\273\372 \241BJ\312\277\251\322\016\334Uo\271?|\030\261x\313\034\266?XF)\010\310\203\255\277-*\003\340\370\275\262\277\310un)x?\245\277\007\3700\3765J\205\277\247\257\255\204\326D\303?>\242%\244\235z\301?\274\210S\252\262;\250\277N$\361LX\360\261?\224o\r\234ar\250?\231\021\310V\237*\253?\217\023\252\306\2263\260\277\212\t\372~\352\n\304\277\032\306\216\313}\025\262?\242\177K\271\343\213\230\277f\257\247f\035\340\212\277\210\243=\273\001\024\232\277\344\253\3652\363\342\222\277\331\364m+\n\371\260\277\367\317\222\003\006\306\264\277\211!\210\271\314h\303?e\220)\371\224\n\177\277\254z4\020\226\324\303\277\371\371\274[\370\371\316?\376..\276\305\333\270\277\177\366\021\022\221\354\265?\224 j\2452\005\260\277e\307\245\0200i\274\277\257\310$\003\365\252\240?\2339\236P\230\010\273\277\334\260]\030!\237\317\277\234K\237\3009{\263?p\341\273\332\017/\237\277\366\344q\301YA\307\277\324\035\310\234\3660\302\277\2176\037\366fk\251\277\343\352&\2472\036\275?5QT\365FR\271\277\367=\207\203\232\314\245\277\200\363;G\035\313\211\277\304x\263\214QC\263\277\361\002\006\254V\"\276\277\374\362\343^>\220\253\277.\327\r\r\003\021\245?8\212\r\236\006\363\303\277\362\357\021gk`\300?\377\001\344\t\206R\300?F\213\207I\301\000\253\277\270BG\370\260\233\237\277{\020\373\002fc\303\277\014\343\251\2326G\300\277\241\203W\3525\370\275\277\216s\313\321\226Mr\277\243B\365Y\223}\265?L\337\312\375#\271\266\277(\306\017\t\273\030\222?\321\037V\331A\202\221\277\003f\330A\270\221\250\277\224\310\006U\021\373\261\277u\026/\020\017\205\256\277\033\226\323l~\331d??\307o\225\027\376\264\277o\313WqmL\267?C&R\264\206\036\200?\317\375\261\230\306\234\303?\314\354\2174\336\363\231?W\274\233\377\036\264\265\277w$\372Q\323\310\301\277\332$\232W\266\037\306?\321\256\356\226\357\331\266\277^\365\206\306\220l\223\277\025\312\\@\030\267\261\277\304b\313\312%y\223?\311\254.>g7\246\277\201O\3526\300\301\250\277\001\006\345\310\220P\251\277!\236\220z\257\332\266?Y\026\021\212\000\271\237?E\372\377/O\256\276?\207\346\346\255\366\222\265\277B\222\274\317\036\222\273\2770`\331\313\210\341\245\277\230\326\240aNw\257?\014\366\354j%0\312?p7\177\276\351\363\233\277\215\255\010Th\277\317?\017%a\320\255\375\301?\372\022bMH%\247?\221\007\300\375\316K\302\277\326\254,$Y-\276?\344\301T\336l\314\245?V@\021\005\035\222\227?\210%|D\371W\317?\246j4\247\022\273\301?S\020!F\025\213\270?\035\032\331\233\336\256\274\277\205\276\377\027\336\315\314?\027\227\265]\0374\320\277\216}lyY6\256\277\225+\240d\213\222\272?\366\247\203w\205(\267\277\224\262\206\206\275r\236?\373\355\007\017\254\253\217?q\255\327\231\316\342\260\277\263?!\n\022g\220?\363\3452\365\377\235\274?\340\344\272\317+q\313\277\370\264\244O\023\333\262\277\270^\221&P\300\247\277{\002\355\326\212\003\271?\322p\260\337\236\374\266?\362H!8`a\276\277!\345\022^>e\251\277q\010\261\272\0107\300?\200Ls\003i-\255?\265\211\303\013\342\265\220\277q\334.&\254\277\265\2775@\231\372\307*\251\277\035\241\264Ws_\211?\346\327.\233\n\357\260?u?,A\341\032\271?\356\236\307\r\003|\247?\3032K\352\002D\247\277\253f1\\cx\261\277\256\010\266\340\346]\263?1\361%\222H\t\264?\237\231\373;]\376\252\277\026c \206\340\340\245?\373\227\313B\r\303\262\277$n\335v\265\020\204\277\202\262\330\215\277\"\254?\211\337 !\305\203\242?\377 ?\252\330\237\310?4s\240\313Y\214\246?]\"\367\360\323\305\222\277D\030mS\356\"\275?\305w\200%x\260\250\277\216m;\034\340F\254\277\343|i\025\036;\237?\303\225u\316\320\275\300\277\036\333\342M?\323\217\277n\260\031\346:j\301\277_y\276\3107_x?;\020\313\314\221\036\271\277\324\343H4N\244\271?jn\024\270\267\245\231\277Dh \215\036z\305\277\252hM\321\0026\260\277-~A\226\013\313\212\277k\246kB\353(\306?\352\217\034\307U\372\226?\237\360\235>2\326\226?\231\306\tk\344\014\304\277\316\367\333\373#%\275\277L8\346\202J\327~?\231KK\272\232I\301\277\275\314\317\036\276\312\301?\225\217\014\020\256\030\226\2771\224\006\206B\350\305?\363:\314\241\272\222\242?\221L\355T\014^\303?\034\3646\277Wl\203?4\320\211^\3069\271?E\214R\303f\343\261?\007\2065\335\247\321\215?\330\017@\360\3040\242\2778\034!\221w\025\226\277\021\327\335\322R>\323?\217\267k^>\231\264\277!\204&\221\312\203\266\277\234jq\250h\316J\2777\276~\237\"_\312\277\361c\'}j\336\237?\310`\306\n\3135\241\277{\244\315\350\241\271\216?\026=\030\304\324\001C?\370!\201\"\r3\275?.Z\277\307\033\203\272\277\206\217\277u&\320\225\277!\247\357\375&s\260?\245\250\314\2573\002\310?\272\025\346\302\360\260\200\277\353U\327\036\016O\265?\256E\305\302\010~\305\277\312\332\313\n\222\013\261?\341g>\245yD\306?x!\365\020\363\373\261?\352\t\013e\322}\256\277\243\316P>\206W\256\277tJ\222\202\027,j\277?\360\266\262_\212\274\277\3400\2564\300\365\275\277\023\254P\372L\016\242\277\311\261\345\351 \273\260? k`\003UY\244?\321\257\354\311\245\350\241\277\n\315\312\3314\373\263?]\254\221\273\370V\214\277z\315\n\226>,\255??Y\232\327\211.\212\277\244\243|D\235\231\210\277\017\201Q\214Z\332\260\277QO\252\346q\213\262\277;#\317\205\336\017\254?\347P\215!\264\246\273?\274\325\221\232M#\304\277cP\010\270\333\234\225?\000R]{:\213\300?\367\222y\234f\342\273\277\333\3517\315\222\256p\277\220\204\217\217ea\242?\301J`j\340\031\226\277\271\257\230\221\321\217\257\277}\203\264o\363\026\220?\273\215\374\206O`\264\277\316lG\367\313\236\261\277\254\032\212/\340\305v\277\007\223\330\376H\277\267\277c\274\\H/k\264\277\374Am\367\233x\274\277\336)\241\3055NB?M\346H5\025S\274?\345\321\357 (\010\257?\367}\277\027\330\353\273\277\325\232\346\024\271\343\257\277f\030V\343Z\003\237?UWX\022k\014\307?\275\330L\350>\303\257\277DZ\031m^\361\244\277\3551\331\306v\244\217\277\234\005Zi\320\366\220\277$\331\240\372\242\023|\277F^\324\210\246-\260\277-\321\325T\374\020\266?\264q\317\370oU\300?K\016\tW*n\276\277\364\312\347\322\314R\244\277\341\211\037j\315\230\234\277\274\223wj#\334\267?\261C:_K\t\314\277\257\356\360(\242\345\267?\207\004\240\032t\272\273?\220\037\341G\276S\265?\0173w\247\363\303\216?9\237\242\025\372\362\304?\177\tO,\307\374\250\277\242\013\264\24560l\277\321\261t\005\364$\247?\256\031k%\312\323\267\277\266N~\272\034\372\223\277xh\215\360\375\220\305\277\274T\267f\237>\306?k\014\364\335\241]\262\277\212b\013O\251\223\251?\206AL\265%\247\305?\271\0252\247\370\311\313\277\006\2471g\260\370\266\277\242\2468~\345\177\265\277\311x\223o\330\271\257?E\016U\376\026\312@?arU\330B\346\216\277\333\003a\203i\371\201?\205\346\215\332\017\343\265\277\0327\224(\037\252\313\277u\305.\232kF\307?\264\027n\332\211\231\331?r-s(\376\334\266\277\300\201Jv\354\034\274?z\345\236L\365\325\231?`\226@\246\312\025\231\277\265+\302\212\355\262\233?\260\371\013\020\037\316\312\277|\310(I\341_\276?\315^Z\2437\033\244\277\304\351}&`\010\302?Q\004|8\216V\267\2770\324\334\203\351\254\261\277\312\222\002\325\346\245\265?n\340i3\007{\226?\245\263\335\314\003(\250?\320pNr\303\377\211\277\377\240\342\030\341\000\251\277\234\205\\\226\340\204\265?B\360\207\364\010\345\220?\374M+\222C\020\265?vN\275h\263.\213\277\325\271\371\n\267t\232?\tl\216Ir\254\240?%\306x\232\217\320\275?\277\237\245\212&Y\274?\215\307\250\342\020Ts?\310\323x\033b2\300\277Dpv\315\313\253\222?\314:>\23755\266\277\037\231`\331\275\021\242\277\205r\302\370:\327\272?bZ\323\257zB\236?c\364@\231T<\265\277&A\261\272w\023\313\277\377T\245\254\000\257\213\277\344XO\360\362@\265\277*\033\013Y;\242\236\277\013\261\262u\032v\276\277\376\332\331|\027\366\264?\2257o\244\365\366\225\277\244\2640 ,\204\277?q\346\264\036\317?\302\277,\3314(l^\326\277ut\243#\374\364\263\277\006:o\022\216\006\300\277\316 v\232\t\316\311?X\013\301&\363!\267?\n+\366x\"X\237?\0316W\271\213\260\270?\235b\301y\323\217\273?(\002\316\265\352\252\204\277\215\272\236\206{\216\216?\000c\253\026\020A\322\277w\"\204\270\024K\275\277\261&\250\274\375\252\211\277\366\214\000\226\234\\\277?\025\036\236\354=[\317?\206\007\342{\333k\275\277\353|\304\340\214\244\306\277U\231\244\266[\330\322\277\206-\363w\221*\266?\336Y\266\322K\177\306\277_\207\234\253\033-\261?\302\322Y/\016\355\311?L\262\364Md\204\237\277\300\347\225\243\305\201\302?\007\261\255\027_\341\271?\213\327\0319\"\023\265?aT\3548\004\034\265\277v\327\005\346\371\352\306?\315\005]*\261_\212\277\210L\000\274\346\243\234?\245PCZ\205,\262?\037\t\2527\2313\325\277\223\024\333{\211\257\266?1\244\327\010\2401\302?A\212\365\371\035\221\212?D\265\302Rw\030\255?\255QT\030\213Z\260\277/\355{\260m\223\266\277\373\177\2162\325\315\304?\001\024\217a\3405\255?\245]\236|\263\333\246\277\220\374\004\205\247\035\233\277H\370\347\364\334I\320\2777\242Y\2165\260\273\277\210\276\026\216\254\263\251\277\334\n\211p\206Gu?]o\"\234\035G\210?\230C\265)\274\013\302\277\247\332\250\324\225.\302?Z\251\213\312\3328\235?\306\311i}P\235\250?\027%\000m\254J\223\277M\211\240\377F[s?\342\221\026\327\304\350\310\277\365\021\224\241\361\360\300?;V\270\345mM\243?d\311\372\206\355\226\300?;\342\353`\272J\223\277S\006\304\363\374\275\255\277t\326RTSY\313\277\243\231\211\351H\220\255?^=\255\216gU\230?\007_\202\034\311X\255\277JYl\330\266 \241?m\376\367\362\233\034\274?7\205\250,\250\226\220\277\000+\004l\357\251\236\277C\253\242O\n\030\277\277v\232w\212B\017\270?Q\331\321\237\207\222\320\277R\034\177b\213b\302\277\333\246\020\370\002\261\243?\251\020\344\235|\320\301\277\302\006\021\020\3721\242\277\231\330J\347\200J\261?\330\317a\243O\321\265\277>\003\"Y\300;\264\277p\017\320\020&\214\311\277Og\377E\377\335\234\277\313\177\033\222\340J\235?\211\277\t\005\231\372\313\2771`\n\336\207\272\262?\376i\2574\006\200\271?\252\242\241}\005\225\312\277\3602\370dg\327\253\277\276\367\n\364\376y\257?\302x\371WV7[?\345\224\331\3374\315\254?^|U\006\223\343\226\277LP\006\240A\'\246\277\202\334J\370kU\266\277\311\326c:\343\250\304\277\247I\247%\3077o?\277*\301,\303>\253\277\001}\375LIG\261\277\217\020\244\260Vn\274\277a\263\244\252-\265\254?:YY\017\177\372\264\277\366P\327\363\252\337\246\277\215!N4\343\333\205?\241\235\025\303d\r~?\360\252?[\327t\241?\271\212S\361\001\326\270?7LS*\357%\245\277\247\343e\243\2634\246\277<\002\326\0107\213\316\277\3727aqQ\236\242\277\037\030\327/bZ\213?T\265+=\3264\236\277OB\032r0\201\262\277\222;Ms\267\250\302?\201\324Y\216\313\330\264?\324\nZ\371\001\345\274?lz\253\202\345\342\251\277n\241\256\005\225\361\302\277)\221q\236\231\376\261\277\213\237\010%\010\303\272?\027\271/m\2042\261?\t4\261I\362a\260\277\366\217\244\371*\342\313?\243\342:W\227\007\256?\004\331\367\3729\226\244?P\200N\222w\342\320?CL\363Fuk\265?\366\273lc\274#\247\277\024\371\316\334\265a\216\277B=U\020j\007\244\277\346\001;\007\245.\262\2775\352\324\2230\021\222\277\374|\324\254P\354\315\277o\333\365\202/d\267?\206\221\255\360\327\361\251?\347\275\343?\3249\252\277wN\376\233\345\314\254\277h\255\263\202\213\236\301\277\313\223G\363\241\322v\277~G\263\315\010\242\275?\232E`{.\325\260\277;\341\356F\240\204\227?\037\276c!\'\321\200\277\237\204\013^\304\312\300?\341\241`\215C\250\264\277Q(\277\372\t\227\300\277v\266\374\216\351\223\205\277\223\212D\236\3517P?v\"\205O\307\301\273?\340\212\272\231\362\317\256\277\246\351]q\317\322\263?\270\261\2100.\220\260\277M\347\276\242\217\275\224?\027\263\273\233r#\320?r\275\265*\210\302\304\277n\357\277\340\201\260\240\277q\233\331V\017\026\252\277u\371\257V\360\361\316?5O\263g\226\333\256?x\315\021\2122\374\223\277\260)\0337\3309\313\277\202\303\300\270d\203a\277BSU?\237\221\300?\035\313E\016\233\235\244\277q\010\344!\274\243\261?\372\344\301;]\007\223\277l;+\360\001\353\241\277b\345\204\300\232k\220\277\023:\323\311p\033\270\277\351\001\324Z\204\260\251\277$\247\352\261>Y\307?aLL\214\325\354\242\277\341^C\276\370\371\244\277\341\373\350\301a\227\245?Mj\213\266\274\310\256?\207j\204\030\312D\212\277\025d\205\320t~\240?\345\347\265p\377$\266\277\250\356\027\363\342\265\301\277\370\013\347\365g\n\267\277\200\231J\0275l\246?:\372\310\332\361\274\275\277\267\363by\226\007\233?\311\245\322\254z\340\272?\261\r\204/\315c\303?\263\324z\364\241_\264\277Y-eZN/X?\313\377\\\034\355}\257\277\320\376\340\320\224\255\302\277;\223s0/(\271?\244b\025 $\344\260?\315\222\350\335V)\234?\331\\\312\002\365\261\301?\332\302\016\252K\251\227?\355\226/\224\355\027\221?W?\005\276bM\274\277O\313_\245\024\016\266?\r\3302\026\264\344\314\277\2441\366\n\006\211`\277\347]\377\244e\320\317?\246l\223\317\355\223\263?\322\247\241\224\025\037\242?\345\264\177\271`\363\214?\253\"\252\242\263\023\271?Q\031\361\204!o\232\277T\021\341\342h\206\275\277%t|\341D\370\265?K\340\355\3460\316\203\277\217\211I\366h\206\271\277\366\313\372,\2222\303?\350\266\010\2324i\267?e\251\225\014n\214\311\277kj\020%4\177\327?l\334\014\236\177\262\242\277\227l\367\261\006\207\260\277\223#\330\262-A\301?\205K$\223\005}\226?\007\"\016\014\371%\303?f\255.\253\375\272\263\277\341\372\266\301|i\206?\334\224w}\353\324\301\277t\322\226SL\355\226\277+\207\013EN_\226?Q\026\261\r\271\373\322?\240\2239\220+\365\250?t\350C\350\300\236\252?\000)-\272\275\367\242\277\246\305\361\200O\002\306\277]\343\331Y\274\210\242?\275\306\332JO`\300\277\3041Z\363x\357\253\277\277\254\337\344\020D\267\277C\357\323\256\201\334\227\277i\210\211&+\363\267\277\246U=\217\321\267\237?B^\350\216\\\217\257?\230d\347Ps!\202?b\217\361\253\013p\227?q~\204\345s\371\203\277sw\353\201\366\256\221?\363\254i\367\366R\270\277W\210\251\277\035G\217?\227\n\235\\\207]q?\343 1o4\336\302?\350(\371B\027r\274\277\246C\t\327F\305\206\277j\032\035\226\3163\247\277\310h\361\217e\224\300?\316&\305\270\305\203|?;\253\253\231\312\241\222?<Dw\250]\227\260?\t\017\027\037\313K\271?\310h\006:\302i\247?\021\376\242\223)2\261\277\237\213\243\177\273\302\307?\317\\S\341\332\312\315\277pt\025\3415:\235?8\366\262+\252\232\215?4ec\327\014K\247?\340\377\247\364%\246\257?7\304\301\225Q\364\246?VSU]U\364\310?I9\341\262\253\240\226?\024\007sd[\203\261?Z\231$\031\266\030\262\277S\3118\242\331\377\266\277\325N{\024\001\335\267?LO\332\002g\214\242\277\251\037\'W\337\217\261?L\024\305X\252\220\\\277\316\"\342%\272W\227?\r\301H\215\346L~\277\016\370k\000\033\021\274?b\327\265\\\344\334\257\2778\254\317\375&\220\261\277\266{\336H\232\010\310\277\243\300\267$\020N\305?R\257Yw\355;\243\277\235>\352B#IP\277\023\2467\344T \272\277\257|\010{Ql\274?\371\335\330\376\236\372\224?\000\375\375\032\025r\251?\276-}\232\356\373\306\277/@C\323\336\036{\2770\325\366\2422!\266?e]\254\010Lh\254\277\033\305\331\275e\n\204?\363N\271\300\370\365\232\277\252o7\223\352\242\306?\317\224{w\251\373\243?N0$\302\232\205\271\277\323\315H\036\325\224v?\010m\024T\315\023\234\277\206I\275\003\357\214\274?u{|y\013P\311\277\364Q\303\370c\220\260\277^\261\022\203\r\274\322?\345\257\272\006\310\241\261?c\207_\205,\360\260?\346\314\220\343\242\245\235?\263\n\2245Rc\251\277ag\325-8_\253?&?^\374\213\306\255\277H?}\376t,\265\277\022\200q\242\"\340\275\2776y\340\244\032\367\232\277\331HcE\251\315\247\277\274e\315\264\313\016X?\246\327\001\362\333\304\235\277\377\365!\301\363\301\234?@u\371\\\'\004\324?\230\240\005\032\277I\205\277)\263\252qJ\016\267\277\245\014\232)\013\327\267?\332i\367\313\255\024\267\277S\372\036\017\032)\254?\026\304\r\200\366n\250?\240\310$\336A\341\263\277\201\204\0359-\270\265?\016?\324E\353=\204\277!\270\006\300v7\321\277^P\034\244\020k\264?\203\225\2178eq\247?\366\275\0367\307p\307?wo%[\230\312\247?\221\267bg\005P\257?\331\276>\031C\346\263?C\354\353E\2721\302\277\333\266\013\320\371T\267\277\227\236\230\3700\251\256?\302m\"2d\315\221?\252\331L)\302\322\310\277\235\337\362\375\251M\272?s\315\344\360cV\227\277\313\223\362\236J&r\277\277/\334\200\205\315\231\277\367\244&\254\224\376\220\277z\272.\360\r\003\272?\013\243N\247\303\272\271?ll + \261\235\277\244\330\026z\256\022\215?\032D\235\356(\261\300\277Z\t\031\007\025\022\245?\177\313\024(c\266\207\277\307\021\202C\001.\263?\361:\310\305l9\230\277m\200s\350\260\255\240?\322\236\225\232\325\363^\277\2743i$\217\213\306\277\201`\345\032fZ\305?C\322\230\343\330E\251?\257\034\266\263>\244\304?\226)O\002\274\320\304\277>\"X\262 v\240?Q\216\367N\0254O\277\353\3723v\316\210\300?\216}v\327\177\022\275?\337>\344>\336\354\234\277\037\353\261 \234\250\266\277,\260\216\2109\241\250\277x\212\362O\254\363l\277\0336\261\342\037\323\240?\203\3745\243\361(r\277\t\200\321\221\327\212\255\277of\026\350oZ\265\277\202\356\013?\343c\305?7\220\260\316\353\327\267?\257\313\025-\304\020\267?\255t(+\007\305\260?\275h\243[U\310\272\277S\365\030\352?Y\302?\263v\262\301\225)\250?\301\346\210\001\300\262\310\277\262\rE\362\264\377\325\277\034\3532\231\315\243\242?{\033\027\366\255\360\240\277\t^\366(=\215\264\277o\251\246\374\256\206\320\277F/\001\303\307\272\322?*\243W\343\242\350\247?`\020W\365\376\230\304?\021\375\266\354(\220\267\277\322\274\347\254u\356\261\277\374\306U\217{)\320\277\262\372\260q\201=\313\277\264/~\267\212{\272\277\256%\312F\210\336\225?\013\370t\3728\005\260?&[\237O\227\275\240\277}\006\314\234\331\375\234?\306\365\006\250\007*\265\277X6g\221\021=\223?\277\r\342\344\334\307\245?\0006\332\251\341\246\314?[\326r~\277~e?\371.\247H1\263\235\277c}Q\177Bj\215\277\tq\352\361B\355|?\216\036\334\265\3142\200\277\315J\370\001\257\370\246\277\277f\272\030\306\232\274?eb\30428\004\266\277\003\264\204\245\365W\300\277\033m\326\373\t\264\263?\244\251\n\264\351\002\320?\315\000\313\364\366[\275?\017P\006\231-\022\306\277z5\025Ck\215\256?s\2160\002\303\237\272\277\242<\'\217Xx\261\277\025\207\273\372\027\324\303?=\272\226\\\256\322\270\277-\263\311\262T~\253\277\254\\0\356\201\r\250\277\302\336\223\240\002X\321?Y4\314\352\305\211\306?a\231\310\350\307jG\277\2575\200\"\324\005\231\277x\245O\033\016.\303\277\333\2445\"\t\351\305?\370\202\331\204<\274\274\277\262_F\261?\033\270\277\222\273\274\241q\030\241\277\230C,z\315\322\227\277a>>\213\234\\\306?\364\'\314.\272?\240?\247\rD\375\356\010\224\277\213<\217\023`T\266?\220a\247\367\352+\300?\230\343\373\177y\033\304?\264\253\205\207\347\220\254\277\204<\331\255\271Q\252?.H\222\251n\304\206?\311\237q\225\204\207`\277\323\004N\357\035\026\244\277\023O\246\364G\226\254\277\332 \373M\204\333\262\277\350u\024\2500\361u\277\345\316<\247o\r\264\277\267\255\237\n2E\261\277\272\020\220\227S$\271\277\350h\300\362\260Y\263?\234[\265Z\242\206\320\277\323\346NH(\024\276?;\265\3409\263\330\245\277v\311\021\367\216u\200?-\307!\237\311E\265?\261\332\22437\230\241\277VLD\235\215\227\264\277x\305\360EJ\272\260\277\257\303\264\361\357\253\315?\n*\377\233P4\266\2774\022\251\'tz\232?\302\025\022B\210V\312?\325I|C\266&\307?\224\372B\227\203\364\205\277r\245\260\352\271\330\244\277\020\305\030+D\254\253\277\206\226i\377X\r\263\277o\227\010v\213\006\226?\024!b\217\260\020\241\277>\371\032\345\361a\203?\350b\021\t\360t\252\277\200i\246\272i^\257?\353\245\271\350o\210\315?[C\232\004\211\243\205\277\223\343\360%\262\006\233\277O\247\032q1-\240?\246\341\345\020C\020\266\277X\217\230\2777ba\277\244\201\272A\014.\275?\366\275\341b\370=\261?W\260\272\267\002\353\235?\023\205>\310\006\374\246?\336\033x\031\332\366\257?\353/\251[?\033\272\277q\224\034\003\314\347\276\277\210\366\211\026\316C\256\2774\234wV\315I\304?\217~\2430\027\034\243\277\355\276\266K\021(\301?\205\216>&g\233\252\277\021\270\323%\002*\225\277\341F\212\031\226\226\256?<N\371\"\326\342\266?\236\300\201\257I\266\302\277\300IRVx\312\212\2771\373-\2261\022\242?d\342\005\377\372#\221\277\371\030l\276\330\253\263\277\325\3265:bYw?4l8Yx\024\241?o^\256\031I\355\305?\232.q\356\212\203\260?\245\335\346{\017x\240\277\277\207\376\221\341w\237?\315\234r\035\303\201\256\277,\257T\332\231\354\236?}\036\313\252_\245\277?\324\214\323\301\327\331\214?\004\255\256\340\323#\251?\354\250\203ptM\246?\350\234\332L\364R\301\277\304l(iZ)\267\277\265\277\332\000%\354\264\277\221\016\0279V\245\300\277\013#j\333\354\217B?\320\332G\356\037[\265?a\177;Q\313x\246?D\035\373\362(\322\250?\017\223E\311,\301\262?W\267DpX\014{?\366\332\361\300\213j\254?o\021:Q\365\344\241\277\201\203\037\261\n\307\201\277\231\"\204\'\013\317\212\277\234\234\247\307\016\024\225?)V\004C\220\206d?\364\262\375f\356\273\227\277\022j\025\334=\244\307\277J.\314o\376\221\276\277\254O0e\202\270\263?\024\007w\020M!\224\277\342{\317\000\025\276\246?p-\321~X \200?G\257C\246t?\223?\310\240\265\274\276\356\302?\364\201\312pb\025\240\277\273\263\372\t_,\241\277\317\254k\363\tH\305?\325\311\376\001\033\323\302?;\310B\324s}\301??G<_|\322\245\277\0267\346\017c\370\260?\300\216\346\350\272?\313\277wP\014\260\356\274\260\277m,<\026q\255\253\277\305{\355 \300\025\265\277\325U\275\026\215\374\271\2772\321\226\020\245\021\270\277c\276Px6\375\265\277\315MM\'}\325\240\277\373<3\345\324\356\251\277\300\216\342\025\262n\247\277\033q\346,\311\357\227?\214n\204\277Zz\207?\231@\221>\277\322\245?7\377Y\212\263\374\267?\265s(T\331\274\266\277\343\320\3011u\374\302\277\013y\"\216\025\374[\277\322\352\270v\327Z\213\277l\320\277\202S\276\260?\023\363\211y[\251r?\311Z,\353\253\022\217?\006x\347i\321\317\302\277\376\220\037h\333\224\272\277\370\344\263\2527t\231?\354m0vB\266\300\277\327v*\035%z\247\277\212\332\311p\205x~\277\302\227\021w\201\205\247\2771\006\020\350\025\255\267?l\002\220\237e\375\243?g\272\267g\334\233\272?*c\373\307/A\267\277\340\316\270\272\013v\266?\255\333\013\317Ufo?-k\353\370\373\373\243\277\020\024T2\"\342\302\277\223\007\370z\024\265\245\277o\321\221\271\2705\267\277/\001\330FE\316\251\277\0269\005\202y\205\306?v\353\225\354[\255\265?wC\356!\366K\305?\323\225j\022u\365\241\277\325\231_d\364J\266\277P\005m\"\240\024\272?\257P\367t\310\001\261?\371a\326\351\353y\314\277\270Ih\271\277G\231\277\027{\201\342\353\017\273\277\'\336on\230^\267\277\272/>:<\203\227\277\001\336UW\303\310\200?\274?\346\2643%\300?\022\236\010\373*g\262\277r\360\202\n\202\343\212\277:z\374D\274\322\307?\213\256r\264\323\272\273?/|\014\207\244U\221?\004\263Hw\366\252\301?o\322-\377d\246\304\277W7\264\312\374N\251\277\177\272w\331\300`\260\277R\354\021<\263+\261?\257\203\354\361\220Q\242?\013\234E=\322\376\204?\2464\327\224\342\340\260\277\215\334Z\253;6\314\277@\244\231C\021?\246?\336.\004h\244\340\207\277\350\020Z\2468\311\247\277\225\177EI\020\307\320\277\353L\263\347\213\214\226?\307va\300\305t\256?\007\322\277\017\327\260\265?\026\263\324Evd\243?L9k\276\374Rx\277\305\021\330\035\303w\274\277\327\270\225K\253\n\262\277\022\260\330(\371E\235?=\032:\303;\355\273\277n\312h\005\'l\251\277@B\217\027tX\232?\226\341\333b{\337\256?\373\324\215\343\234\003\240\277\216\250w\236\325\250\276\277l\001&n2\267\261?o`\373\251\371\264\227\277\254\320x\014ee\260?\201\306\203\333\362\006\215\277\314`\362}\226\270\273\277U\220\353s\202\023\222?\302\335\324ET=\312?]z\354\361o\021\240?\022\332p]H\003\270\277\222\360\211\246Z>\277\277G.;>P\275\274?\000\017\231!\272x\312\277\326\014a\362\257l\277?s#yK:d\242?\215\177\370\2171\233\275?\316\002\236\006b9\300?\314\230\276\225\242\240\306?U\371GyS)\300?\031a\330;\244&\261\277lg\275\367\353p\267?\343\023\016kE\025\310?\007{\252\261?C\264?^\227\256\326\023\032\310\277\347\300\342\251\244!\261\277\310\\\256_\256\260\230?\001;\310\207 m\205\277\037d\022d\345\212\252\277\223\315\202&a\333\326\277\251~pM\322\366\252?3vT\236v\252\266?!\335\316\233\315-\267\277\334eF\035\013H\223?\226|\017\013(\215\232?<\257\214\224\256\025\302\277\2233\260\030\217\t\273\277\021ff\223LY\215?\027\367n\270O\241\277\277P\315\2357\226!\325?`/\000AK\303\246\277\2040\224\2716Gw\277\245\256\314\001\236\224\241?\246\354=\207\033\006\261?s{\322;z\035\243\277\006\221\225C\033\377\267\277\034\313\367?\267\312\320\277\213Py\027\221\200\213?\307\204\006\235\221Q\252\277\251\352\342\335\306\206\261\277\353\003\033N\372;\306\277\304\355\022c\266C\221?N+\013-S:\261?e\3553\205\323\025\234\277-\273V\302P\024\231?L\202\274HB\211\264?\350\316\033\243\204\375\252\277\364\225\247\206\201>\267?\350#d\342\340B\247?\373\025\036f_el\277\354Nz\367\252\235\262\277\t\235{.\267\261\221\277\232\231\025i\033\022\231\277\0250\225\320\276\375\267?G\234\357\325\232\r\262?\366\005t\005+\004/?@\223\375t[\025\265?;}\222{\240\353\320\277\"\352\343\341a{\267\2778F\317\364\250\223\272?\025|\350\207\247\223\223?\373\3553\354\270\324\222\277\217\211_\254^\007\277\277K\365\032}\217y\266?y\351\242%\311\006\263?u\003\356n\250\363\270\277\177sux\245i\245?B\321\246\035.\243\254?\\\346\335\tH2\276\2774\023\034ba\305\217\277\272Lj\350\235\214:\277*\034\276\331\350\345\302?\230\02466hX\240\277D\274\202tu\222\302?\201\251\262ft\302\252?\271I\222\274\362\262\262\277\235\025~.)I\272\2771\001g\374\342_\272\277E\2219\235\367\346\275\277\246\313z\261\345\217\321\277\370\357\367B\262\313\242?7a\266l\203\034\276\277\231\315\327&J\001\223\277?\002%|\270Y\253?\336\240\273*W6\222?}\255y\267\367.\302\276\347`\217K\025\365\273?Zf\017\017\036vj\277Pm\267\270\003V\264\277X\035P0Y\307\246?\r\315\034\006Dv\213?\214NB=R\214\265?\301\200\300\001\305\351\273\277\247\317u\'\362\307\266?\230b\206\300\301pT\277\013\301\307\373k\r\257\277\'\010\020>\255\263\211?\032z\212\t\220p\301\277i\351\267\372\306\357\266\277\240\r\210MZ9\301?Q\037\226D \032\200\277\333\233\2139)w\300\277\'\266\246x\0030\263?\022uw\240\345\271\244?v\256\201\373\226\204\242\277\317\223T\346\254\021\272\277\\\354\'0\230\035\251\277\250\241S\006\245\r\273?\363\3113g\275\203\220?i\254\373\025Q\203\234?\267\207\030\r\3377\260?;\311\r\323\261^\200\277\204i\354\362\2056\253\277\352\375\357^\227\034\245\277V\237G\n\306\372\270?\325o\263O\212\026\314\2776\213\034\0224\207\253\277|+\263\')\302\321?\315\243\270\004\013\264Q?\0209 \2042P\255?~\374/\017\307\223\272?\030H\035\262\033\344\267?\035vg;8\243W?\261\3664\331\\\034\276?\331\247e\253\3038\276\277F\331\376\314\246@\265?us7-\347@\261\277\225%\001\330\345\200\265\277\201\307\374}\177\010\223?$\t<0\336\205\252\277m\366\314\364R\347\261?H\t\'.\246\347\322?=\232/\224\202\246\265?7c^\r\374\t\213\277\316@e\014F(\223\277\320\321b\246\3345\317\277\335\325\220\325V\030\274?\014\357?\274\003\335\231\277\300+\232 \023\367\275\277\336f{\010\000>)?\356\242&\303\252\226\255?\322\016dP\017\004\270?\247\336\363\231\267\316\220?\0361\326\272\365\023\237\277\017\235\241\267\273\263\232\277\022\270\237(\253\227\277\277_\270\330gYOy?\207\344MY\373`\261?\303\263\nn\n\317\276?\343(;p\372U\314?@P{\370\200+\262?\0168\231\340J\210\264\277\006\002)k<\231\330\277\316\303\220\242h\221\257\2779&v\230\250\216\227?\000(\3326g\255\216\277\360\020\031J\0347\246\277\376\245\373\023\002\240\251\277\225L\253\030b\301\216?4(\371uo\245\306?\303\234\030\230\252\232\247\277\374\347N\221\007\305\251?\340\177\242\335X\356\247?\344nY\336\311<\300?\322.t\325\225\356\271?kt~\355To\214\277\252\3015mC\002\307\277\031%\341\240\215\274\251?~~\277\307=\237\272\277\023L\023\347\023}\312?\232\005\377\214\305\322\265\277\n\224\320\004\023\224\252\277\364\340\250\005\365@\267?\320\251\374\336\234\233v?\031\n\025YCK\211?a\317\001)\004]\300\277\236\327\255\227L\030\320?\026\"\025\204\020k\223\277\254\241\\\300\202U\256\277sj\204\327$_\263?\241\034\243\205o\235\221\277\365q\230\r\376\341\252\277[\265\024y\325\233\260\277b\251\306\361MH\307?1\n`\366\\?\300\277\204\361\227\367\324\271\256\277\242{\230Y\215(\274?\224P(\205\021\256\310?\214\374\016\324s\200\263?na\022xG\241\272\277\231\207\221\271\345V\241\277\244\256!{\347\374\262?)T\372\346\343\305\240?\276$\237\024\267\340\256\277\220>I\365s\225\240?\212}\254\256\214O\261?7@\237c\351\345\250\2777\255iz\252[\314\277\360\261\221\313\245~\204?\276\355w\335\272@\301?8\236\333\342:2\265\277\265w\030\320\340\337\325\277\205\312\366\301x\233\273?\177o\333O\262v\273?\201\305-\337\360\307\310?$sl\353\312h\250?\317\357\374Y2\256\243?D\336\327\344\3604\302\277\272\224\256\030..\233\277\2548vE\310\020\227?\267?\233\360\321Y\214\277 \333\371(\030,\300\277\240\r$\"\034@\242?\027\007_*\226\037\267?\252_zL\306{\243\277f\312\367\002\210\204\241?\020\2219\3753\n\201?\321\267\027$M\262\235?\227\222cxB,\275\277\321#\2626\207\007\241\277\226\337\303\372\210\337\270?\366\307\325/\022\246\260\277\022\365K\334\025\262\220?\210\230\245\360\366\370\251\277\\\"\227\020\026\360\243\277\203%\312\353\310\240\240?\200\342\362\373B\343\177?P\n\231\277\242\201\232?+(\361\271\217\002\265\277\300\264\342\232n \303\277\234\2772\0330\221\246\277r\217`.W\250\266?K\t\2168\352\256\303\277:`Q\311\257\340\261?Dv\322\233\346\014\257?\327\025u\340E\032\301?\352\351!\326\206b\306?\014\3429xH\032\257\277\307/\265\271~\314\261?\222\017x\261\220\365\303\277u\030\006\036Y]\272\277+\320\361I\275\373\277?t\362G\232~\324\227\277,}\024\202>|\273\277\014\340P\272q\257\264\277kC\343?\273\346\211\277\263I\324[\321\264\276?\031\366\323\177\341\214\242\277\242[\312ex\274\226?\212\030\0005`\355\244?\300\220\322\246e\\\304\277E\177\242\305\"\032v\277\232f\335\270Oo\300?\314\277\352\214\"\263\311\277>\313\004\311B!\302\277}\002\376\215@\000\272?\203I\256\306\346\260\253\277\362C\247\372\201p\301?\333=.w\255\000\214\277.\367\':+\375\256\277\3633\316;\320Z\235\277\205\360b\362p/\241?\264\366e2\244,\222?#zq\217\274\033\264\277_\364\371(\241*\237\277\021\271eGly\312\277\t\340\020\\\251\030\312\277\t\242\014\271C\330\237\277E\315\247E@E\315\277{\324\370wfW\262\277\305c\364\'t\236\315\277W\031^\277_F\253\277t\n\022\215\245&\311\277\022\334h\304\247\247\257?\207m\206B:\276\316\277y\327\347\005rT\272\277:\230\023\tq\370\261\277S\370\n\250\262\237p\277\373\207\002\301g\023\311\277yP\246\r\374\307g?k\031\313\036AH\261\277\226W}mV\t\301\277\310\277,R\205\345\262\2777+a\\\326t\274?3@\240\rZ\370\266?\313XJ\203|\235\310\277HR\001 \250\252\245\277O%\213\020b\207\261\277\305t\020\216\340\205\241?\010#\007G& \245?\251RaI/\225f\277\326 \346\324\236k\240?\305l\263q\217\236\247\277:\2006\205K\272\257?.}Y\336y\351~\277\305\020#\261k\036\201\277\235\217\334\260\362q\272?\263\225\217k9\350\274\277\353\010\232i[\200\315?D\254\353\314f(\253\277d-\342\036\334\274\263\277;\246\244_\177\014\256?\263D\202\221k9\210\277\304\206j6\226\264\305?RH\263lgI\276?I\026^\241dw\211?\231U%\220\351\256\275?\334\025\016ls\314\271?\366\205\263\362o\205\222\2779\241\177\'\356\356\300?\026\244\360.\252\251\271?v\036\336\355Q\367\262\277\005L\330\177k\265\267?^\220!\013\366\210\321\277\244\020}r\004R\243\277e\350\304\337\021@\272?\025\324\003{f\033\300?\022\303\335\204\210\244\274?\345\362\357\010\243\333\233?\027\355\301\260\213\363\274?/\315\007\342\t\276\300?\226\023=\274\221\301\246\277/\001B\023\365%w?\222\344\345\026\356\244\301?\235\254x\2223 \231?r\2511\004\035X\311\277\244\245, \275\226\273\277\366#ZU\030\236\310\277\013\205\023\224C\254\210\277{\340w\223\256\252}\277\'u\206kt\310\304\277:\003\241\341c@\302?C)\332\336O\263\225?D\030\254\0167\246\255?\251\244\005\202g\350\310?\"\313z:|\024u\277\332\325\216\3325\340\215?\313\223;\304S\n\266\277\255A\236\320\336a\263\277\024\256Z\307<b\251?Ik\317/>&\257?0/\006\224\255\"\320\277\321|\373dJ\230\275\277\236\340\311<k\000\262\277\314\307S\270\007\272\273\277\250\003\035)\245<\265\277\264\317\226\300\n/\206\277\321\r9c}j\226\277O\177q\262,\201\225\277\033\226\330\023\'\304\243?b\347CK\365\363\262\277,\000\010<\311\214\276?\304\026\352,N\203\203\277N\342i\332J\004\242\277rp\241\"\201\356\301\2779\363G\223?\355\272?D5\252Tf\240d?\331\304\0314C\340\241\277\347\367$Y;\001\232\277\335\251\272O\223,\276\277<\253\316\254\211.\253?\220k\261J\317\333\230\277fD\224e\025\242\300?\361\367k\267F7\261?\223\226l\003a\363\253\277di\246F\370]\255?\177\233\035\302\210\344\230?\215\314\210\261\315~\255?CY\235\244\341@\302\277\216\373\312#\242\254\251\277\212N\310\027\234^\273\277\270\213\216\313\206\327\255?\347\270\031t\306\257\267\277s\311O\227\034k\262\277x\306]\026^`\267\277\246\373\374\2248\345\230\277\274Z\200\010\210j\311\277n8\260\327an\257\277.\3643\345\004\322\234\277T[\t\211\360\367\321?\025\360\342\030\312%\300\277\247L+\006QY\304?`?\030\301i\300\273\277\233\233\276 H+\305\277\257T\267\305X\337\274?\223F\347L\001\005\300\277N\206O&Z\373\275\277a\02146\333\302\246?\3103>\267\262\243\226?L\232\263\331w@\243\277f0\353\365Q\023\211\277\242X\332R\037/\202?\361\313m\212F\206\252\277x]}\257J\336\272?\271\177\016*:\021\221?\355\000\341Wy\027\235\277\343\3442G\215\357\257?\201\351\222\353T\276\307\277\347\223q\t]\272\220?\372\024\216q\276~\272\277l\036!\3604y\314\277\321\330\343+\223\372\264?\374l\370\267]\310\225?\3342 s\327\215\265\277-\356#M\326\334\240\277a\306$\315\t&\213?t\303\376C[\250\300?\241\241\207\364\340\r\321?C\312\265\301\310M\254?f\266t\"^\033\306\277\205Sf_Z\224\213\277\216H\252\321!\344\261?=g\020\307\263\317\240\277\025CK\277\376\013\273?\021\036\307\202\350|\306?\351\207\233\216,\207\245?\250\315\351r!\273\247?YvE\224\342q\271\277\2632\261\264\203\354\245\277\265\241\261H\360\363\204\277D\005f\267\021\350\256\277.O\260\245\345\343\267?X\020\276\340P\014\243\277\353*\330?\325\273\247\277\2201\342\361\273Q\313\277:h;\376\"+\234\277\005\365e\023\246Y\263?\302\336\312\005OV\207\277A\034\3505b\256\234?t\211\200>\363\371b\277\214\263\254\221\322\347\261\277\322Z\205\005k\214\245?\337\031\327\357\250O\210?\342\246\026\3478T~\277\237\232\372\364g\254\225?\304;\177c+J\312?1e\311\267\340d\266?\230\372v\212^a\261?\224\006DT\n\310\275\277\213\353\307e\020L\333\277Z\340\240bR|\304\277\367\3376\005\372\353\200\277\002\025D\327\305\274{\277\022W\026\367\362[\226?\000\334\360\334\315_\237\277A=\373m8\325\261?\027\312Hz8|\276?\032\2520\221\363\323\261\277\230e\367\345J\354\231\277C\006r\234\316K\304?@\333\t\304F\035\226\277g\257;\355\000\216\275\277|\350\230\277\0034\274?\013\023~N\025\302\314\277\006h\'ro\307\267?\252\266Y\364bq\301?\010\233\211\331\351%p?\257\311\207\037\214&\311?YI\363a\r\221\223\2773p\367\202\322\005\274?\202BD\267\204m\266\277\261\005\353\004y\227\272?h\272z\223\276\354\245\277\177b\037\260\n\035\222?\026\323\342r\341\234\246\2770\241\243\3662\264\260?v\253\345\006\240PF?p\037\312\336\232J\270\277f |\302u\301\305?)[\204\336\377\205\203\277\016\340lJE\307\256\277$\311BP\201G\227?w#\230\351\234\314\256\277Z\323@\226\324\276\315\277\010\322\207Br\216\266\277~\342\314\233\020\322\244?S\034\024\231-\203\274\277\021\352\202\325\023M\303\277\350\203gc\230c\266\277\232\261\263}\3229\256?\212\005G\2628\036\265?:l)f\324E\226\27793d\223#\302\241?\370\0102*\375i\252?\256\323w\037\340\242\260?\266\023}\232\263\221\263?\207\204\274\211\205z\\?\222I\315\301\0300\223\277l)\324\230e\225\276?\261fC,\215\212\267?<\'\340\300\370\201\226\277\200p\322+D\207\331?\323D]i\223\t\222\277Y\225\2740\022\013\265\277u\221\017\030n[\272\277\001\267\336\334!\232\303\277\326\313\340\276\351\023\304\277^\302\037\233\036h\217?/\347\352\020\376\025\253?\300=\177\272|C\301\277PH\355w5\314\227\277\315H\241*\245\203\253?\016\302\327\363 0\311?\362=\\z\367t\251\277\336zk\326\335\377\210\277,\270\002\3055\366\304?F\023&\254\200\002\274?Zi%RI\236\315\277\332\337H\234\350[\235\277\374t\250+\343\001\312?\242\307A\034!\036\263?\253\2430\036X$\237?\256>\314\036\014\265\177?@\324cJl\363\300?\342\242\n$\355K\224?\242\266+*\210\034\260?\362\341\025\345C6\251?\022\370PA\301\036\203\277\304\340\204\252\"/\265?c7\277\017\027\330\314\277Y5f%\301\007\223?q\226\007\343\225n\252\277\014\223\026\320c)\265?L\351\331\002n\243\200\277\255\274\234/\001\010\254\277\021]\326\3318\371\257?\351\232\265\253\356\306\326?\251\334\345\224G\363\271?L\254;I\355\203\273?\026~nq>\323\253\277\360|\227v\225\232\246?\262\202}\034\006l\271?\030[y\253!\304\177\277B\253IH\351\276\277\277\023\016z\304\342\272\270?6\326\261d\216V\263?\220)\214@\246\r\274?\204\333\'\241\276\303\341\277\317\211\214\216\304\312\236?j\233\227Eo\250\311?\322\222\013\343\323d\303?\277v\020\336K\242\212?\241n\315\373\264\001\214\277\277h\373;\363\350\227?\265|b\022RTu\277A&-\332x\377\242\277\254\233\230Z\370\274\214?^\231\367]v\261\271?b\"\255\223\215\317\241?N\024G\306]\005\273?\2507\007gFY\265\277k\035f\020\276`\320\277r\034\004\017\"\022\244\277\371\002\341F`\200\302\277u\353\265uS\236\267?\235\2432`%R\316\277\227M\247=uA\236\2777\211\252\307\336*\241?fv\207\001\002\031\300\277\323\270\353u\370M\250?\356\362\304UYf\236\277\310\005\376\027\271\010\250?@\031\320\300x\003\265\277\'}u\202@\306\246?,\226\325\ti-\252\277\370\251\313\353\2017\312?\245\323\033\371-\237\261\277\253\024\350\370\037m\230\277\010?\310&d\311\304\277\005\266f!~\000\263\277\177\256\266\265\337\177\275\277\031o\246t\3541\255?\274\376\201\247\003\204\264?\221\027\214\200e\344\301\277\203\351\374*|}\266\277\024/cq\315\310\327?\243\344\254\365\203x\266?\216i\352\205\242\274\306\277UN\323Rp\361\264?\\\263\007\035\274\236\221\277\214>\326o\253*\266\277\261\232H:\"`\266\277\207\243\261\325m\277\245\277ci\223\314\202\245\271?\r\t\336N\223\211\237?\267NDW\274\247\272\277\374\215c(bv\300?_\315\r\357\251\002\221?V\344g\340\005\215\270\277x!\224i:\233\277?&\303Q\204!\320{\277\261\\2\305^\026\300?|w\325M\355\322\260\277\335\262\016Y\314\373\310\277\332\223f^H\024R\277\314\254\330\021\032\214o?\276.\002\027\031\373\265?\206R3\246\331]\236\277\223\307\033\265\036\247\252\277\031\347:h\215\"\270?\354#\3710\227\250\212\277v\224\335\352\211\376\212\277\3659\277\363<\177\264\277\347\311\274*\346;\236\277W\225?)YU\244?hv\202\226Lu\253\277#\277\227\321\230\363\261?\270-\212$wh\312?\242\026|~B\363\251?^\205\265\254bM\301\277y\276\216\207\257M\272\277Y\205\'(\\a\300\277\250\241[\200\242\201\267\277NY\211\030\343J\307?\317&\265\"\201\250\267\277\266\225\244\253}U\257\277\344\223\323\215\236\274\220?\034\361\336\341\342\355\247?\243\177L\302\302\030\256\277\223\n\335\nlS\235\277\273R\202\300\014\010\251?^\243T\3532=\266\277W\"ci\237\313\300?V\341b\215t\255\260??\253\2367N.\273?D\214jv<\022\236\277.T\326\\,\\P\277\\\323X\215\245<\251\277\034\254!a\245\346\326?\227-\340\317\001\301\202?\363\360b\267\274\255\205?\351\323\032RX<\301?b\031\2701\264\022\264\277u\346\274\352~?\251\277\0071\007y\"\022\300\277\322\0026\375#\340\303?\253uEP\333&\255?\216&\327\306\355\340\303\277<[\345\r\204\025\305\277mu\023\3238\007\243\277\242\347\2135!Y\306\277\027\237i\231\337\376\251\277dG\027\334\275\226\241?\370\225o\255G \202?\2172\245sA\017x?\214\364\336\312\020\241\302?L\370\016h5\033\241\277\306:\305\326;\207\300?\235\2028\005c\303\257\277)\374\3565\203\344\304\277\343\221\210\341\330\252\210?\376\246Yy\370\351\206?qO\271l\216X\226\277%<\231f\006\000\270?x\354\001\210\336\035\203\277\340\007\233\210]\213\266\277\210\035wW\023A\266\277\266-\330Cn-\250?[\271\320\225\374\371\243?I\"\231%\010\022\272\277\350\365\200\026B\006\234?!\245B\247:\307\206?1\025\335\256V\272\311\277\346\250\376\336<\244\307?\336fu\317\002i\271?c;\002\267\037b\231\2779\244\302\355]\001|?vf\023ey{\272\277\371\243`\027`\273\246?\334Pv\342\236\033\205\277\317\212\354\n\356%\270?\265;\363f\274\021\261?vh\350 \201\277\206\277d\372m\237,g\247?v~*^B\026\205\277/\357\275\025Q5\274\277\032O\322\217E\222\240\277\272*\325\320n\311\235?G9\321\324\353\301\230\277}\253w\237\347L\260?!2l\253\324O\301?\005\035\351:\245\377\275?\275\3344\373\326\225\251\277`\337\245\356\025\323\260?Z\241\227G\315I\331\277%\266\311\306\241\036z\277\352\246\321|{\324\213\277\364\007\311\355\2313\244?C\341e\376\025\324\212?=l\220\251\034b\240\277\262Q+n\335\t\300?\342!\244\347\001B\316?3_\307\353} \256?nn#dR2\261\277\334\215\n\333\225ty?6s\275\257\323|\203\277b\224\361\246.\374\244\277Zh\205\213}\234\211?)\201\265\025\376\214\264?\341\366\274\337\357\235j\277>\026\361Z\034\242\214\277\035\013\002\0374\352\264\277\036\023\036w\004^A?m\312_\205\033{\260?i\343\300J:\274z?\346~w\356\274\031\270\277\362\230\347\0360\211\253?\210\271;/\344!\241\277\tL\250\3157;\313\2772\266\360\262\334\336\262?1\341_8\211\n\230\277\271;\020\276\346Q\267?13\\\243\377-i\277\315\002g+\343\357\273?\032\033\256-\350\277x\277m7W\237C\226\251?R\373s\257\033\251\254\277&InH\265\331\267?\217\320\203\t\377\302\241\277/\356P\352\276\326\234?\377nj\330c\013\256\277r\374\032\360\3531\266?\004W\274\245K\210x?\0377\337\220\311 \265?Y c\010F\201\211?\231\302>c]G\311?##\361;\256:\224\277\357\315\355L\366\334\274\277L\305\322X\336j\262?f\322\331\375U!\257?\242\\E\264=\021y\2772;\332\202\324\246g?\245\003\017\037\342\342\245\277\372\234\341\242\263\337\302\277\304\307\302\367\217\020\261\277g\360\'\336:\355\226?\377o\332Ab1\233\277\233\224\210\325<\336\224??\303\240\231{&\222\277\362\246\3063\373\350~\277\0255\267\377B<\307?\216\026>*\307+\205\277\254\037\036\340\372-\271\277\316\266\372\023\317\312\222\277\377\027\233\351z\007\204\277u\332E\237t\233\247\277\"P2P)\005\240\277\024\372>~\252\205\264\277\375\361\032\225\237\316\222\277WH\311V\371\026\231?Ourh\355\234\275?\326\351\036L\036\227\270\277v5*<\207%\201\277xE\326[\274\253y\277\002Q!\264r\361\251\277t\221\362\320\312\253n?\202\246\240ae\205\227\277\375\030!$\326\276\234\277\330\240\254\237\200\020n\277\327&\027\2115\177\240\277\354\230\316\376\321\014\264?\017A\036u\235\t\303?\351\347\366b)\021\246?1\\\350S\315\007\263\277\204\355L\236\275\014\256?\026\373\200\311\3733\304\277A|\212\233\266_\265\277\217\331\030\013U\234\252?\340S\t\267P\324\301?\206k\273\372d\213\300?\377\350f\334\016\030\271?%b\021\227\004\203\245\2775?\"\nx\t\240?\024\353\244\013\'!\230?\020\3228\205\257\024\225\277D\363\354\324\345\221\324?\311\2306\364s\023t?\306\326|\252\006\247\300\277&%4\200h@\253\277&\036\002\272\032L\274\277Ux\217\216\244\375\265?\233\361\203\355\032\n\255?\177\225\327\n\3576\205?\024\216x.\247Y\263?;\234\220\236E\214\264\2771\270\340\245_\352\303?\367\275\253\206x\035\274?J\357\027\t,\003\244?\231\306\2568\234)\304\2776P7q\226u\273?\267J\266)\261\335\240\277\214\020^\303\275f\312\277\203L\237a\231A\224\277e-s;\276\212\213\277.a\264\026\357D\262\277\302\243\265\307\270\213\301\277\352w\306\027l\234\317?\322M\n\022\3324\277?\241\006%H\241R\241\277ZT_Xa\301\311?d\017\177,|\302\235?\353\310\307&\317X\307\277Rvm\033w\237\260\277\353\300_\230\023\300\341\277\230j;wVC\262?\315\216\352\226\250\036\240\277:\302]\332\372\250\272?\346\251\375\226\261\231\310?\t\201[6\375~\252?\211\303?\272^\013\227\277\347K\022_\230\020\304?G\023/\336\037\306\210?\214\006\3656e\030\261?\226\220\017\210Bk\301?\334\033\372\220Z\233\223?\365-d*\373\036\217?\322D\210Ms\234\211\277o\026@\362\'\\\270\2779\311E\341\230\265\204?\377\260\241\351[a\263?w\374\253\301\025\256\250?|\300\346vO\266\260\277w\251Q\307y\352{?,n\307\215\300>\242?\200\317\236\343jM\227?tjQ\222\217b\251?\250]q\364v\321\263?%\234h\217\357\234\202\277\225\002]rL\275\267?\2713D%\310)\265\277u\2029\002;B\246?i\325\216\340D\371\312\277(\340\255C\325`X?\267h\246\376\033\332\267?C\262HV#\210\267?aSzh:\200\272?\322\233W\025\332\'\211?FX\233\324\004\031\252?\013.b\244#\\\232?\357\235u2\373\266\261\277E\206\225\270\312\025f\277\371\360\321\214S\213}\277\331\200\036\362\371\345\303?\343\225Y-E\260\311\277\324\3728@#\t\222\277\2249\241\2234+\332?\0041\204Y\272!\233?\314T\347\341\275\357\302\277w\326\327/\023q\244\277!\311m\322d\021\242\277\3145Ws\354/\265?\032\341\026l.@\304\277\335\262\221\317\326\301\321?\373\263Y\223\341\005\204\277\363lM\222=\252\261?\247QT\306\007L\255\277\377\250\265\354\320d\213?\211\215c\375\256&\263\277~\001&f\n\211\301?\247<\346R?\350\314?\355\205S\275\220V\243?\333O3D\245\207\265\277\234\316\350\320n\302\331\277F\t\234\274\351\005\254?\244\371+\225_\327\233\277lv\361\372\211\240U?\220X\311.Ga\273\277Nj\304Px\021\220?Q\356c\324\270\347\265\277\205+\'\2042\257\300\2774\371.\351\276^\260\277z\367\234G\346\366\252?\217\326<\272\007B\254\277>o=Mf_\247?>\370\306\246\"\365\210\277\223=\"\204\242_\300??F\312\236\202N\214\277ml\003\350\202;K\277I\313\231\314\354\277\221\277\3415:\323x\231\312\277\020\341\313\020^I\241\277\tq#y\034\223\242?\t\327L\234\275\217\273\277\030\304\243\036\3572\223\277q\307\304\233\236Z\255?X-\253\nm\r\313?\014\0210j\030\371\272\277\303\017\346m\326\005\265?K\206\252M\270\245\242?\227\275)\326\203\333\237\277\327UAL\270(\264?\215\211\034\225\364n\313\277-\r\344O ?\246\277\n\024\004\340\355\276\311?\270\267\326\300[\327\272\277\301\342M\273\325/\232?\343A\237\201\3749\226?\374\225\031:\027\253\241\277\306G\312\377)\235l\277\001\306\027\340\013\177\242?\355\027\324\2760\036\250\277G\377\356\244\336\364o\277\036\213\231V\341n\246\277nrz\301<\004\273\277\355\233\342kyZ\272\277\204\312\034e{?\242?y\312\311h\177P\230\277^\322\343\255S\374\262\277\365\235\227\316\364\344\311\2779\244\246\242\355\020\215\277m;\271\256a$\277?\224L\265\333j\352\303\277\262\366%\030\326d\275?@CQ={\302\224?\006\r\257\306Z\364\274?\347\373\227K:\312\313?u\335\203\221\327U\252\277\313\356\203oBD\254?z#\236\201\305\270\276?3=8\217\357\305\240?]\352\1770\2768\212?\014\350tp\010\373\200?\000!k\313\240\242\254??h\215~\221d\272\277y\005\031\323\300\363\262\277\006\325\003\230J\325\262?}V/Ad\207\262?\233|i\311\213\244\270?_H)\254\255\037\302\277\366\313\254z\236)\267\277\207;E\\\036\213\266?A\350\034\224\022k\224\277T\253\2249\334\264\260?\352\236\301V\242\341\311\277\326$\373\204a\007\203\277|\237\036o\322\207\317? P\223C\374\333\300?v\357?\240\225.w\277\232\nw\350\222U\270?\326>s!\212\016\321\277o\272/-\t\355\223?w\303\336\205\325o\305?\254\212!\370\252`\312?\035\333\007\356\001*\322\277\027\272\373\023\334\021\254\277c!\204Zt5\262?6F\205\231\207\310|?\373\270\'\240\207$z\277>9\275\2000\031\225?r`]\273\336{m?L\352oJ`\314\234\277_\366mak\261\253?N:2\351\322v\303\277\257]\306C\232\027\323\277\017h_\020\310\334x\2779P\036\216\354\234\265?6G\034\r\020U\300\277\315\367$\216.\223\302?\366\361\004\216+\261\241?\216$\021\356\2212\262?\251U]U~\200\257\277.\222a\302\365\001\240?g\332\035\232\357\031\207?\020\262\270\220 -\247\277\235\205*\212\250a\264?\005(-\311\366\257\222?\324\213@\313\003\334\265?\347\235p J*\262\277Y\025\304:7\274\214\277\302\324\334\233E\033\234?\215\007\273\370G\213\265?\337\373\301\312\013\017\254\277/\261\027\222w,\261?q\372\300\214b\200\264\2778\305G\307\276!v?\264\367\261\356-\027\266\277\263y\204/\263\021\253\277N \334\264\356H\262?\003<Q2\3565\227?\237\241;w\342[\256\277\340\313\366\003\356\233\237\277X\336g\010Vw\310?\257\375\207\370\264\014\243?\324\237\274\315\323s\241\277\216\275\301%\311\264\215?<\336\363\021P^\273?\323\013\371\355\0062\221\277\212=\323-3\204\307\277\001\250\256\304GZ\275\277\010KY\332\245\341\236\277N\"\016\3479\362n?|\223\216J\250\215\212\277\227\035\243Y`]\250\277\303\2313R\307\272\270\277\242q\265\273\302@\303?\2660\320\205\304\023\267\277\373\267%\007d\020\207?\224\373\304\244:B\266\277\365\'\203f\213\230\311\277\345\006E\376\201\361\305?\225m\004\372\037\376\225?Z\301]8J\362\265?\'\004\341\305\2711\241?B\0258\23561\302?\2647\207\t\347#\263?\355\r\271\320$\216\231?\375\220\242\274\tr\275?\265\255\212\301\314\220\261?m\010I#\246\223\303?\370\274\207g}\332}\277\371?\3148\2116\302?\025\3078Na\215\222?\225\346\303\010\256W\305\277\263\313\010\034\310,\251\277n\362\014h\027(\211\277\207;k\003\353\002\276?_d\242{\373\t\256?J\377\376\207\237\332\231?.\305\204\365-@\244\277\254\317\303\227\003\333\233\277\021v\372\252\022?\263?\\|\241\361\245\221\263\277\267\352o\375\265x\214?\365|k\266n4\321\277\034:\244\014\366\327\255?\317\353/\307\222\367\273?\225\306d\347\304b\273?)\215\0067:\254\257?$\235\220\037\023\024\246\277tB@=~\236\303?\020F\250\236\226\237\302\277-\362\362\010\374\257\221?\345\371\207\033\372\257[\277\371\264\232\356\246J\265\277c\374\373\332o[\251?\2750\312\324\313V\262?\300\236B\352<\227\252\277o\263\2743\274k\247\277V:S\345\211(\236?\244\r\255\243\332\315\252\277w \346\205\261\263\213\277\211\\\006\233?\205\272?\007x\360z\224\221\252?\027\241D(\374q\263?[\365\'\036]\202\251?z\003\034\030\363\232\306\277\373\271\007\345\350C\244\277\0033\377l:\220\265?!Z\217\277pz\300\277\371D\001M\033\301h\277\262+g\203\2340\212\277,\323\300\350\303\363\303?\253Q\231\354\226C\274?n\215\264\203\307\005\265\277#\243\266\324\214\203\300?\220-\026-\332V\271\277\371\305*\272c\356\270\277\336\270\001\237\220\256\300?\311\240\323|\220\005\305?\005\273\205eWo\270?\266\227\255\333\317^\207?(\362\005\311\370\240\233\277\355z\373\372\252\375\310?X\320n\010 \276\244?I\265q\014\372\000\251\277c\266\264\230\320{\271\277P\346}i\355\244\260?\306M\301Y\366W\256\277\324*\327\035\373\365\251\277\266\264\252\373\325\033\200?\335\361\313\334H\364\227?\221gx\007\331R\302?\rK\320\242\264\233\204\277Q>\002\025\302\002\232\277g\254qyl\016\243?:\253I\256\350\216\200?|\353\233\003\273\026\300?\241)\204\004\200\221\270?\255\002;?\316 \213\2779\247$?r$\263\277\000\034f\204\312L\264?0\354\307\226y\256\271\277\020\220\207`\333\215\302?\326\227\205\tI\343\316?\240k\034V\334\330\240\277*\264\250\347\224\276\266?\331 \027\277\000\257\254\277\005\215\277\256\005I\260\277p\356\363$\373\277\225\277Fd\310\227\201\031\302?\242V\277v$\243\301?o-\357l\273\007P\277\r\024\367b`\363 ?w\001\312G7\340\300\277\343\353\317N\016\363\277?\242w\373\373\320_\210\277\035\023\231o~\335\270\277\347\372\3106\r\"\240\277\\\366\330\0256T\262\277\257\021\334\010\326\017\304?]\267e\013\\~\261?\342%5\274\233\342\300\277\024l\376\220\020|\244?\n\256\035s\017\325\305?\243\255I\342\260\032\271\277\027\351<|`\312\262?\\\371\024\301q>\230?b\327\246\210}\240\245?o\2455\306)M\210?\261\315\276\224\266\324\251\277\202..\275\254\315\267?_\365z\003!E\242\277\036\024\001\2721\246x\277j$\342\034\"r\231\277\361Y\221\027#\357\311\277\334Ps\240\363-\216\277\320\201\321\031\335\230\210?\r\207\343\241P\227\255\277\004\004\326\302\264\036\322?\004\202\300\246\203:\255\277\224\021U\203\207\335\273\277\335\204J\240\255\032\233\277W\233\007\242D\313\243?\273I\305\210\261S\277\277W\0213>\231\340\277?\367=\343dz\256\315?QGp\320\245\006\254\277\301\211/\013\254u\276?\037#\244\224\014\217\276\277W-\352\247:E\270?L\370\t\330I.\264?\t2\217\271I\265\312\277I\304\207\240\364\221\252\277\002@p\306\212\320\243?K\317\362(\275\370\263?n+\016\360\267E\325\277\222\031\335\023?9\256?\t\241]\007*\004\233?\032\212k\242c\250\261?\010;Q(%\334\303?\026u\235\177\203\224\247\277c\223\224\237\0267a?\341\336\361\376\261\204\253?g\347\276{\r\241\271?\215\326\266\220\306\032\272\277\230\244\010\373\236\250\262?\343\322\004\330\036y\224\277\365\307\233~\263\337\260?\375\265\"@ta\225?\233\022\312\005\271\370\256\277\rG\225\2109\266\211?\312y\006\351w k\277\225\t E\204\024\303?B\343\222M\312b\266\277\n\332\247e\264\230\253?\314\262\365\206R|\270?\351\200\271\026=\025\273\277\261Z\346\313\272\237\255?\004\302\035\333\362\343\250?\np\215N\277\271\301\277B\327\205\030\372\270\242\277miR\320\362\r\233?\372\327u\"T\215\264\277\023UX/P!\312\277Ct\366\330\312\323\221\277\327,\230\267.\277\237?&\221\212\347.\366\243?\337\'\2077\037\361\311?\237\264\002D\003\303\222\277<\323\005z\037\376\306?+\000\000_\346\\\301?/\227;\253\221\351\246\277\023\372\305Ms\240\214\277\250\371\'z\243\242\301?\013\244FZV+\302?\261\346\"\014\004\256\276\277[\323\204$\344\177\245\277\213\035JIig\236\277\320L(J:\211\202\277\3557M\256\214\340\244?\273\370D\255[\201\250? \001`\037\205\211\234\277\337\373\315aTG\254\277\023\364^(at|?3\264\331\026\342\233s\277-z4\372\t\242\304\277d\215\335\262xLw?\245\213A0/\225\205\277\212\\L\034j\361\262\277\223!\224#j\341\260?rD^\361-\002\221?P%\022\331t|\301?\'\243\023\033\017\341\271\277og?\203\245\307\246?\272\223y\322\277?\265?q\267\303\321\004\275\276\277d\006\030\324\373\367\261?i\302\200\246\250\303\271\277\311x\034l\241\347\266?}4\226\332\377\256\231\2773X\263}\321\200\245\277\265\233\303%8Y\264\277\017\026\204\351\2246\262\277\037\327}\r*\376r\277\224!\375\350\243\021\301\277\177I\361\207\271\314\313?\016\266I\304\366\327\253\277\346\205[e\336\250\226\277\003\236\222\367\214]\202?\242\213N\221\362\222\306?\256\244e\344 \035\247?\320\006gS\353\230\304?\217\317\242\014ID\260?\275\222\214.\021u\263\277\324\225\177\314~n\250?v\353\356\241RE\254\277q\252\244\346o\273\327?\207}^s\235W\223\277\250\304R\353\343\362\237?\213\354\n\257\235\251\316??\256\355\316\316P\234?l\323z\026wO\244\277p>\262#bm\264\277\327\344\230\372G}\320\277\177q\206\017\303\362\270\277\235\366\010\357\2310\272?S\215.d\036\327\243?\001S\274\330`\377\312?:\202\022\217`l\252\277\022\262\341\024\203g\244\277\343\021\211\246\350\251\336?l\230\232\004\342\217\221\277.\321\301-\003\r\205\2770F\261\236\246z\325\277\210\243\365cW\365\267?*\306I\246\2359\215?m\033\245\330\366\363\277?\031v\335z\271\323\313\277\312{\241\033\232\010\245?,\224\317N \270\265?o\277a\263Lu\231\277\r\004\333\257\210+\273\277;\263tHp\024\273?([\211u?\362\243?)\362\341\335\317\273\223?+\005\032=\345\377\221\277\nc\243\305\316\326\244?\033\000\303\371\220r\231\277!\250\203*\023\307\240\277\245\246\307_\342\204y\277,Q\225+\321\263\251?Y\371|\353Mq\320?\355\021\035\256\216\0306?=\n\202\267U\000\230?\325Iy>4\224\267\277|\357\320s\253r\220?\017d\322\320\033<\257\277\311h\277}\260t\253\277\256G:o\"[\257?\320 q\316pu\245\277\340\240\272h\325\000\242\277\342\325\230\363\200\035\325?\230\236\301\035)\260\235\277I\200\306NB3k?]\230\210Aqt\276\277\220|\367\334lU\241?.y\222\2729\272\266\277\003\354Zft\334\207?,\301\217W\024\211\270\277!\323H\rSH\246?\026\024\305R\342\233]?\352\374\342\274S\263\202?\203\265\343Q\001\267\276\277\205\341nG\323c\270\277\222\365\237Y[%\265\277\007\243[M\\\003\260\277p_&t\377\211\302\277(.\322\302\363\365\300?gr\r\336\355\246\255\277\255\3748\330\010\337\306?\007\310vHD\273\304?\010\244?\t\345\t\203?#\205\3018\250\177\261?\212\310n\t\037C\302\277<\201\365k\t\376;\277\024\037\336\303\023/\304\277\277Y\307\2046\r\304?!x1\230\221N\202?\356|\324\352T(\266\277\3644\32050B\273?]\"\257G~\343\274\277\351\377\323\364!\r\220\277g\333\032\270;o\261?-\301\3210p~\266?\322\374\022v\331E\251\277\270\020\370SP\266\266\277)\311\005\341}r\263?L\006}\205\035\260\277\277\304\356v\213\347\227\255\277v\302`\356r\"\260?v\2209\177\017e\271\277\232%\004_\244\256\264\277\245\337cS\031\006\215?\266\267U\215\207\024\273?\300\022\010\312\322w\301?\025\330<\255*\337\277\277\321\3547\232\262\232\275?\341\033\221\344=.\260\277\255\207\304\3410\261\244\277\317\310\233\251\245D\261\277O\037\352\021\337\221\242?\237\226\206}\352&\263?\366\237A\300#\342\273?\376\250\347,N\306\211\277\252Y\320\375lV\251?T5\t/\375\r\260\277\225\'c\354\210\322\262\277\354\2417\261}\315\245\277\001\250JKq\275\241\277a\227+\005\014\023\277\277\204\r\303U&8\262?y5\334J\316\314\303?\235\235c\214\206\304\217?\025\337Q\206\235y\275?R\274\253\2604\037\277?b\027\026\325\352\242\311\277j#\211\014\252\363\306?>\343\244\234\277?\\?\03421\203/\312\270\277\251^;v\257\361\277\277\352\002\325\370\240\350\247?S`%z\034\236\270?\371J\243\375\002\236\262\277\207\233\033nh\275\274\277)\351\262#\235\254\261?b\311\254`\000\374\256\277c\335\244\033\254\343\234?\211\374\223\325\303\n\246\277\342=\3738\253G\241\277wV \265\023\337\263?\027\215(\236>\313\244?;\021\316\022bP\302\277-\253\374\206>\371\260?\034\321^\226Rd\254\277\230\313e\013iS\275?\231D\025\\\337\204\322?\214\255v\022\177\031\202\277Sp=\321\261\231\227?\352b\337\353\216\n\250\277H{|\000\n\271\240?\036\356\233P\002\320\246\277\205Z\371\362\"\n\241\277\026\271U9\360\357\270?e\270\267L\210\323\302\277g\320J\231o\220\246\277\237\\MG?\365\260\277[\256>\013\355\257\247?\364\016n\361W$\271?J\303\336\3424\343\245\277\021\373\302#vS\302\277zM\262\271\371\323\225\277\376F8\204\332\\\261?\312\036\311\345;\221\262\277\377\275!]\337\205\273?\350\215\303\354Q\221\266?\334\351\240;a\317\241\277\037\274[\333\031\177\270?\023\367F\321\221\365\261\277\036s\245\346\362\377\241\277\200\347\337\3725\031\251\277[6\376\217\241\306\301\277X|6\330\366o\213\277\024K\275\263\001\226\300\2771\000\306\334\024\214\242?r\231~a\314I\265?\007<)>\214(\275\277\016&\217\355\205\223\243?C\374~H\224\260\236?\320$\027\370\354\027\240\277\211\237\311\322Y\366\214?\372;\332\037\373O\263?\277\324\315l\205R\205?I?\260\317C\025\224\277\304\342\245m4\256S?$\276\201\'\347\373\307\2774\002E\313\237\233\222?\'\320\317\022~b\307\2779O\313cd\334\324?h\010\326\3507\315\260\277zo1z\256\001\214?\300fy\007Q\265\265?\363v\037\205\240\\\324\277\331\250\260\230\344\337\220?\316\000^\301\310\017\234?2A\256\376P\261\260\277\363e\2678Vc\251?\201\274\241\020\206\316\304\277Z\013\227\302\225\226\266\277\350=\000+\207\025\321?/\270>\003\210\251\254\277\3121\247\252\364\346c\277\251\371\023^^\341\300\277\213\345c\006\010i\235?\032\322\353\240\230\004\306\277{\213\274\323j\275\300\277Oa\037\244:\306\250?\253\205Y\335\027\273\301?\320G\241\273\033\253\270?\030K\333W\333\344\235\277\037v\244r\266\036\253\277\340L\315\030\241\247\267?\3265k\270\371\021\230\277\364\016\016\037\230\221\303?\323u\267\250\272h\300?\307\235\375\372\037\236\263?\325\346A\271\240\212\315\277o\020\271\342r\035\265?/\216\276\0303b\262\277V; R\213\351\315?\3723\352\347\257\035\320\277\357((\306\213\036\257?\337\303@l\366\340\307?d\260y\tz\265>?n\275r\\\216y\236\277\'\332\273\245tm\242?N\342\032\305F\215\267\277\277\014\342\327\361e\273?U2\337\017=\320\256\277\201\332jT\243`\315\277j\243\225\201\372I\315\277\246a\264r\026\001\211\277\351\361\366^r\364\306?;\336\003\334p\241\247\277%\244\214\246Q\337\314\277\317\275\261\311^\373m?\356\246\360O4P\226\277h\006V\260^\357\247\277\373\200\307\001\317\306\253\277)2\367\221.C\264\277\335ci\340\255\036z\277\016&\202\026@M\300\277%G\3716\016\275\251\277\250\237\247\\\223\250\267??\365D\357\335\200\304?L\254\337s\177\315\300\277\327\006\217!\200\312\264\277\341\024\210\253\007=\221\277\276B\245M\'e\306?\246\036\302\357\016\211\266?^H\263p\240?\267?\344#\302q\207\224\270?R8>6\221\215\301\277QP\261\021O\261\263\277\006<\354y(*\252\277\377W\361\353?\355\325\277f\022\315Y\213,\266?\033;\036\326\026\310\224?\361\326\t+\311\343\325?\257\334\2076N\206\256?GX\242v\351\336\247?\375\000\206g\301|\241?\221\315\240\254 \355\302\277\350_~\364\361|\244?\325\307\340\022O$\260?\352\254+\240\206\365\236?+\333\355\261\370\332\240\2772\331u\244sN\276\277\376\301o\364\316\250\274\277\t\332\272\237\201\020\250\277\204^\302n\305\007\231\277\214\352\341\005\014h\261?&\261M\304\252T\301?\005( \324\361\226\276\277@\224\253\251~\037\317?b0@W\330Y\316\277b8\022\006<\261\227?\214E4\n).\231\277\205D_m\352\273\271\2770\253k\333\243\r\250?Xh\372A\202a\243?j$\204z\271\010\244\277i+\030\340\302\314\277?\301\263V\024mT\303\277y\354\376\205\344\276\236?U\242\277-fO\247?\367V0\253\301\255\244?Xr\353}5\026\256?C\360lh\264q\261?H\003\267\034GF\321?F\260p\'\2529\320\277j\246M\304A\022\262?\254g[I$2\302\277\317\331\210\322\257\025\306?\026X\356\300Q\273\317\277\340P\217\272\3126\252?\205\235\213i\342W\262\277\345\230rw\322=\263?\320i\301W\240\343`\277V\366S\334{\024\255?1qQW\262\345\260\277\2229\232v\260\202\272?\366\375\020\217a\372\227?\"\252\366\326R\025\256\277\213\355\366\\\020t\305\277\232\355\244so\365\250\277vM\346W\030R\254\277\336\007\016 \177\355\277?\277T~(r\016\220?\233\206\320\315j\222\272?\244\020\267^\314\365\310?m\313dU\3749\321\277\261\232\2154d4\224\2776)\200\006\230~\270\277\336\263\247lV\332n?K\356\313M\005j\246?\365\\?Y\262N\306?\251h}\3675\032\266?\233\251?\307\013\231\311\277\205\233\003\010a\361z?\313\232\374\261\220\220\262?\"\343f\231\250z\270?\236S4\267\305\316\251?\023\205\360ci\340\254\277%\255*\002\247\377\246\277\323\177\253W\020c\307?\n\206qw\031\rt?\0242\341\257\302\253\264\277(a\024\353\356\372\304?\034\250\305\2307\017\247\277c\367\374z\001`\243\277\276\304(%Q\377\240?&\310jd\300\364\307\277bQi\355Z+\235?(\273Mt\235\376\273\277\336\225s\302\304M\245?\263\234\323\230M\265\303?\227\255\262\227\005qu\277n(\364\032\343[\232\277\200\026Z\346\221\235\237?2\211\342J)l\266?/{\341\300\300\361\264\277\246}/\200sO\304\277\010!b\253\320L\272\277zjI\271\321X\312?\345\202\237\350\026\247\275?\025$\251\235\366\277\253\277M\344\250\004\330\206\257?\271>\023F\237M\300\277\006\305(m\024\320\272\277$\374\256\007\264r\272\277\372\317\311\246/C\210?\017L\276\013\027\245\260\277\216\333\276\257\021\264\302\277S\237\007&it\207?\200\2626ML\270\241\277x(\000\216(\261\260?\353,\231A\377\343\272\277\357\240l\371t\272\261?1\203\340\340\353d\272?\273\002!]!\375\274?\200\375\222\346t\211\251\277$\215\364\0007\276\201\277\206h\365\001J\002\277?\241\232\024\254m\277\270\277\240\3414\312\371\335\262\277\212\035>\025\207\000\267\277GZf\254i/\322\277\360\366=\035\231\032\272\277\'\013\0253-!\237?R\t\360!.\341\226\277\217\312\024b3\343\253?\252\355\265\343\352\023\266?\276\004*\347G<\263\277_\336\326\231y\212\266?E\221O\n\002A\232\277\210|<t\247I\264\277\366\nvH*\037\235\277l$\363\203S\331\256\277b\306\365\207\210Z\216\277P\220R#\022z\310\277\027\"\031\353\301\272\201?\321\343f\317q\303\226?\033\301}q\314\216\302?^\325Yl\237F\314?\357\211\2730F\004\273?\324\262L\322YH\311\277@\226\005v\311\002\300?\275\2037;<\006\312\277p\010L\nF-\251\277^f\\\212\211\275\266?\226\204\005\007\202\241\257?AGC\216xC\304?\203f!}\205h\250\277w\303ls\266\331\261?\353ov8\275b\253\277\3730\266gE\001\251?\376!\320s\264\256\245?M\327\'R\033N\312\277\316\007\321\342\305\244v?\240\014\243A#\346\256?D|\375*\231\223\255\277\030\230\314\343\263\005\261?\317\376X\n\246\nv?\330\001\320CC\032\246?\343[\005\350\221\315\211?\343a\375\240\261\221\250\277+\375m\253\220m\253\277Z\033{$\001\253\227\277\270\331e\332\373/\272?p/;\325\026\005\256\277\230\240\371\221\277\375\264?\001\375\"\\\202\017\306\277\220\337\243F\317\343\263?\206\251+El\262\240?\341\370[\276\360\225\241\277\"\272\3226\330\006\246\277I\306x\025\375\020b\277\207W\353i\342\t\277?C\231+\355\341\357\221?\337\242\247\310\200\360\262\277\030\024\226\255\024\313\303\277hP\2569\030\247\251?4+}\230N/\250\277{g\030\367\t\007\233?\030-\313\214\330\"\222\277^\027\377\nN\355\300\277\267\367\377\352\242C\250\277\024+\214\344\277\332h?\277\204\332\316\365[\300?f\314p\000\213v\214\277\034\204NBe\316\257?D9K_\214%\263?\024A\177\351v\226\274\277\021\270?\372\315\033\266\277\033\014\275\231\223-\263\277J&\270c\017\266\231?\246\303\207\214\243\303\277?O\213-\256\276D\261\277\023\323\224\021X0\261\277\2266\335\276_\332\311\277\340\305T\270\222\276\227\277\363\311\002\2314\277\325?\304\363\007\376v\267\311\277\016n\331\000E\206\321\277-\353\306\304%i\273\277%lY{\211\240\263\277L\002\212\3344\256\222?\347Q\006\n\024\212\200\277.\004\312\317d\025\223\277\010\'\214\352\365)\264\277\362\205\261C\250\212\245?\267\237\254\257\355\357\277?\324f\177\267f\344\266?\245!\013\020$\376\234\277)ti\231\023\226\260\277\247\250a\023\020\203\267?o\016m\021(\014\267?!a\302,\265\227\267\277\017\206VY\335v\260?\317\'\344_\212@\251?Z\014n\240\225T\266\277\024\372\305\313\365+\254\277Fp\275/oY\231\277xE\362\351\323\265\313\277\335\027uq\216\020\255\277\260\211(K\027\367\251\277^\356\034\331w\212\224?:\025\231\346Po\261\277w\330\026\253>\311\251\277\226\357\332c\007\005\273?\312\300\220V\2469\244\277\023H\331\273{\274\322?r\014<!#\245\205?\357>F\021\035V\305\277\223\0176\3150Ti?s\2557\217}\313\243?\335u\374\236m\312\252\277\335X\375\362\035F\250\277\341\037\020\020\014J\276?S#\260\335\0033\264\2778E\221\244@w\275\277\377\246\000\245\367F\266\277U\322\241PJ\254\264?\340\211\274;\217\016\236?;\346\367\267\025F\275?\340\221z\213\215\370\251\277\362\n\206nd\000\273\277\007^\226\r\\\327\234\277\177\3033\004Z\252\276\277\210\3330ev\033\266\277\235\242\2621\331#\246?5\353o3.S\321\277FH?\002\357o\303?\007\262\\9\225f\242?\326\274\345\300\221\023\306\277\"C\336\312\317\237\320\2772\250\337\235g\272\222\277\356\'\245\001\333j\320?\026Z\276X\214\202\320?\317\212\254\341\274Z\267\277\323\322\324O|\341\235?\374&@\330,\024\215?\276\256\323C\3345\245?@T H+\305\263\277\267\334vVd\021\177\277\334\r?\254\232u\255?\354\341P\221\324-\325\277\013\351\273\263\354\006\227?TF\335h~L\252\277\037\2374\215k8\251\277_p\350\0062IF?v\344T\210-\241\276?\212\340\007\020\334\233\310?x\321\267I\344\231\233\277z%\225z`\024\246\277\230y\215W\324\231\242\277\275\215c=a\025\271?\317\321\024\326\366\264\264?\010\363q*%\'\267?n\351\021\332\036q\274?5\007\232\213\363@\246\277\006\032\2662\227\334v?\364\345|\271!\277\273?!\tH\332\310>\260\277?sjD\236M\260?\345\\:\315\367\375\237\277-\302\006\223\360\334\241\277NV\226\2608\\\303\277\236x\372\013\225\017\261?\377\372\033\300\233\203\266?O*{4\tg\275?9\217\361\206ZY\315\277\363\322\216\\\334\300\257\277fNU\352wN\234?\324\014\303\031\025\354\306?\262\226zf\257\001\237\277\222os\274)\326\261\277\r\232 \234\221 \265\277&\230\242\334\343\375>?\241R\014qwz\251?S\233\335+\271\340\265?a\261@c\241\323\303\277&\276\250\272\004K\246\277!S\234\3404h\272\277\256|)+C\212\267\277\002W\373\266P\200\224?\222\362\030\352\255\251\267?\341\334\262\217\006\205\301?J\207,r\303\241\253?\020\326\366\362Q\315\237\277\270/*\030-\331\230\277\263E\336W\240G\264\277\320i\021\361j\320\256\277s\244\232\247\003\217\220?}\213Y[ g\261\2770\233~\301\013\346\311\277\264\203\006^\362\351\265?:\244\320$\364\\\245\277\203\037G:\334\265\243\277\364p\004.$}\236\277H\3363\016\244]\257\277cP\365\352?Q\303\277\003>/\177b\035\267?j\r\024\233\t\337\244?Y\245m\215\016\230\242?\226\2043\254\301\320\272?%\352\301;\272\313\255\277\004u\311\373tU\302\2775K\355bS\340\302?\374K\376\377\202\200\243\277\375,\242\233\000\262\233\277g\205\352\374S\313\300?,\231\216\301\262\207\272\277\205\257\037v\013)\304?\t\230\331\321\345\201\267?\021\000\232\377\370\277m?\230\227\322\225\202\324\307\277\371\366\002\215\345\276\235?\233\025\334\177 \260\226?\260S\177\370\216\010\277?\002\320\205\212\217\205\205?0Xeoa!\255?\206\261/\201\213\267\302\277\224\341B\247\371\314\255\277&\364\353\271\213\345s\277\300NH0\335\027\252?\340f\003\013\014\025\027?\001\223\001\276\203E\263?\346\231m,\303\300\263?\002\017j\322\353\262\305?C\017&\3307*\304\277\365\375\t\357\211\375W?W\351g\013\267K\264?T)\036\335\242\351\264\277\304\270\215\0201\352\247\277IK.V\006H\267\277\226\322[k7\302d?;\345\025\266#\320\274?\001a\227j\203\310\244\277 b\272>}\375\214\277,\312T\0070C\237?i\220\016\361H\377\272?\362o\2509\263\223\201?\3574~\306\373o\256\277\203\251\240f,a\265\277U~\031\245\014L\222?(\276t\274\216\304b?m\237d\236\021F\272?+P\004l\275\330\272\277>\t\206\376{*\237\277\020\347\3026]D\240\277k\305\265\"\301n\313?\t\013\0218\006\326\261\277\340\331\263\374\'~\230?\023\252\243\300-\312\301?5\035{\256\000u\263\277\325\r\000ejgj?\254\344\360Z\016\244\264\277O\312>\256\305\203\242?\346\240\342\221\376\270\271\277\254\346\243\225\035E\220?\363\251Q\177\247/\262\277x\324\003z\242&\266?\352\330\247\365\352\253\236?\257`\210\205\337\302\317\277\004vvV\266\310\314?\374f\356i\317\3701\277\377\231\267\223We\261\277\311\365\273\357\373\227\224\277\031\0067\010\223\333\304\277I\333\204{\201\312\207\277\306\346\353#\252M\222\277\354a\270\"\013\234O?\016\300\376\220\177\340\270?\357\374I\035\322\250\263?2{K\276\224\223\232\277-{\262\206\353\246\250?@\276\256\030\273%\313\277fr\211\226\273\300\275?p\210D\342\001~\277?X\315\034\340\210n\262\277L\324u\346?\346\215?6A~\002\253r\260?O\367\227c\361\322\275?UA\220\256t\n\264?\340!fP\321\032\271\277\274\304\205zy\025\247?\336\334\035\367\010F\222\277\325\367AXw/\247\277\206f\377\334>\224\272\277w\n\304\005\214\302\222?\037\344\330\276\263\036\263?H\023\235\3339\257\313\277UK\277\266\004!\274?\023\245\303\327\301\010\244\277:\213EQ[\302\213\277r\321\0345\340\335\274?\027\235\316\235=G\250\277\275\270\234\027\200#\277\277\216?\273\367`O\264\277\346):\215\355T\260\277\242k;\037\271`\261\277\r[G\221\364\253\266\277\246\327 \316\273gw?\342\245\022\345g.\300?\004&\332\341\253\273\251?\216\017Jna\370\277?]\000@}\366i\242\277\307\214h\375zq\251\277t\365\035\333Z\315\233\277.D\251\224\375\017\275?B\016jv\304u\244\277S\311\rq\360M\301?\337\257v\262\212~\250\277zF\301\364xg\306\277\355\313j\325\n\326\200?\002chK\024\227\215?\311\225 yX\203\316?.0\221\313\252\317\265\277$\347\035\004!\206\276\277\210\177\325JW\236\301?<\024\317\247\364\024\262?\325\243H\341P]\250?\265\360IV:\242W?\326M\305\204\315\275\246?\242\367t\017\225\326\246?\354+\004\026p\202\252\277\332\222T\333\230\355D\277\337*\356%w\t\303?\\\336%\360b0\242\277i\333\032\230\n#\226?\014\352>\372y\032\263\2772\010\312\256\375\207\251?\010o\366\212\022\214\231\277\253\233\265\230v\335\271?r\247\223\300\346\"\305?\261\215\'[\231\253\224?P\032\275\342&\302\267\277\307W\2445/\013\255\277@\364\272X\266\032\260\277\376:\3328\223v\206\277V\366\364\334\232\025\264\277\320y\232\315\352\367\300\277\221vm0\251\377\243?\362\354\006\005\333m\266\277\327\245Z;\024\214\237?\222\360+\277\332\r\226\277_\227hQ\302\333\245?|\2329\323\212\270\300?_\233\216\245\020\262\226\277bx\022\277\365\263\255?n\014g\ti\354\304\277\2508\023\247\270cW?\332\221\335-\017\031\247?f\220\226\370\177\243p?W|\021\236\031k\260\277\346\0267\023X\213\332\277\20019\325\325\024\233\277\346\254\327\232\243m\270\277\006\305\331\223\302f\300?\025(\272\006\3127\307\277kV\212\325\350\251\303?S\001\226eA<\253?\312\010~\327\026N\232\277\035\362P\003\302\006\261?S\204\000\342O\221\253\277`q(\375K\362\243\277\256\021P\013F\212\252?\r4l\207\314\341\266\2776\226\253*\0065\243\277\255\262I\304\344\223\276?U\346\033\t|\371\267\277hHq\341\235w\255?\201\362\243,+\236\323\277\256\204\035`\000\034c\277H\331\315X\211\245\242?\317\023\013\331^n\262?L\025\023\343\207B\262?Q\276\260\002\3630\226?\013\337\223\245K\366\244?;\364\"\364\220\261\213?\244O\262\017\231\261\257?6\033k+\307\312\255?\326\326\231\365\335\320\277?[.\347\316\371\323\332?\267Y\030\205\356\266\274?CI\353h~)\245\277\260Jn>\343[T\277P\233\243\376V\264\222?$\351B\362[e\264\277CW\227&$\320\306\277-\327\245\005*\366\325\277\263\346\025\314\276\215\267?\215U\274\316M\364\246?\224\n\247\301G\324\261? \027\242y\330m\247\277\205\345-\2476>\260\277\356\033\\]\244\362\265?V\022\262\232\300\263\243?\253\207\323\263\205\270\271?\033\372\237\027\272]{??\005\010! M\315\277Kpx.S\334\216\277\206\325\324{\353M\271?\336\275~\002#x\226\277\251[0U\237\234\265\277\024\315^\363\310\026\264\277/\205M\246:L\245?\023G\r\322Nk\205\2776n\033}\256D\277?\376\021\005\331\217\304\256\277t\021v\026\021{\277\277\253\366d\367\243\266\217?lkJ\266\310\325\257\277\224\301\343\345\207\321\271\277\343\260F\245qw\267?\240.\004\255\350L\224\277\242K\356\355\201\267\255\277{\215\014\351\337\257\322?\257\217\014\020\211\364m?\320\322{\312k\330}?\265\337y2\214\031t\277\3552\240\021\231\005\301\277\024 \261\260\237m\227?\250\364\256\362}\000\237?\336%\340\357\313Y\274?|\025\'E\3266\277\277\026\027\010\024\324&\235?\216u\201\330\263K\261\277\r\265\240\304\302\374\241?\272\273R\310N\315\244\277\035\002G\016N\233\303?\256\363\241\215\320$\233?\235\244F.\323$\322\277\244\002\233\203Jo\246?\373\364\2653B7\275\277o\203G\247\201L\251\277\2672\274O<\035\277\277U@\031(T\024\247\277bP@@\236\217\310\277~\311\022.\273\302\307?\272\035\334k:k\233?\241\201\201\325\213B\276\277KA\351\240N?\311?\232(\n\234\314\312r?\2535\032\374\016\006\221?r\261\254e\365\222\260?\213\220\013\032\022~\266?j\374U\217\372\271\203\277w\323\327\243_\345\220?\\H\363\357\313>\250\277\252\030dN}\355\224\277\226DN\270\314\256\212?b\222\214O\312\336\267??\201*T\364\355\242\277P3\331Cr)\267\277\316\226\216\374H\371\301\277\2105\230\203Bs\274\277\362*\217\307\320\367\226\277\361\312\233\344jQ\207?\177\355ES\242@\220?\027\206\351\377\302\227\272\277\225\230\305\265]0\244?K\210\"\262 Q\276\277c\213C{\274a\271?%N\313L\241\265\257?\311\373\360\235=\217\245?\233%v\006`\255\310\277\336j\010..W\201??Kd\350qs\207\277\242\200*S\261\032\257?U\366\235\005\031)\240\277;\366\010\274\330\017\265?M3Pm\021\313\275??\354\226\033\253T\243?\272\332\3638\265\032\316\277D\224\021z}c\246\277\271\372R\332%\026\261\277\234\302\026\213\\\232\224?\374\340\336Z\227\320\272?\201C\024\346\341xU\277O<\204\215\005rh?\010\000\231n\030W\203\277\241\342j\004\013\270\235\277\232\264\316\206\304F\220?8\221\035\211~\235\213\277\300\326ad\023\201\220?I*?\022\225h\260\277\336W\036\235[\271\317\277e\034E\205\227\221\201?\361\335C6%Q\225?\347\222\\\374\245L\274?7\374E>\245\205\263\277$?\213\376\3633\257?G\351\031\306\324\215\203\277\374-\240\305I2\322?\276\342)u\307\307\301\277\254\270Ht\256\307\252\277\n,&\253%\307\276?\010\230\243\274\211O\267?8\333\374k\362\246\211?\214\322\244\275C?\217?\261\006\351\327\326<\313?L27\016~\365\312\277f]\336\204U\305\204?\311\222Tu*\300\300\277a\363\250sd\272\260\277\265\235\276\300X\252\263\277\234\"\0232*\304\261\277$o\210o=L\305?\203_\rsoKf\277\321\205X\004\227n\257?\340\315\377\321nN\256?\257\237\252\031*\020\315\277K\356\253m\225 \241?\264E\375TY\216\261?W\304.\273\261na\277\031\260Sr\277\247\301?\365\331k\2279\343\300?\252\013\031\324~\367\272?\366\003\216@\014\362\227\277!\362#\204\241\372\262\277\206\361@\252h\273v? \001\250\024\261\267\270?\337Gx\"m\236\301\277\307\325ov\031\376\254\277,\373\021\344*\026\264?\207\035\332\034\207\260\304?n\316\264=\262\265\224?\323\330\345e\372\235\227\277\030\206\320\232\211\257\311?\311\331\275\372jj\226\277\032\323\316\277\303\332\260?\323\257d\261\002*\273\277|\235\350\203\351\236\300\277B\351C9\021,\236?\325*\000\350m\263\177\277*\030h\315\354\371\301?W\314\236\221\333\374\267\277\371\316>y?\241\246?a\322\353\315\266\234\277?\021k\375\324\354D\306\277\025)\\\346B\341\270?#\337\036\005\007_\270\277\355\364\256\200\241$\245?\026m\266rrS\330?&(\232\037\250t\300?\347\'>D\233\367\271?g\273\334,x\233\267?\266\271K\r\363\276h\277\325\205\375\234\370{\250?\200\377\352\246\211i\252?L\247\323\267\264&\233?\005H\365BK\256\241?\301\030E\302q\330\262?\335\251\346\366\024\207\275\277q\302\210\361\032\'\220\277\236\000\253\021\233mM?\375\t\254\026\373\370\266?\200\336\275\274|\264\262\277\022*\346Ofu\263\277\033\352\250\0137\225\240\277/P\224C\022\376\266\277\264\233\254\344c\307\217?\253o\267=\213\346\216?\224%\037\031:\266\244?[~4g\337\265\230?/(`S\340\352[?\306\306\300~q\323\225\277\006\n\256\234\242\312\275?Z\026E\224\344\373\260?9o\217\373\326Su\277ef\014\300e\363\246?\353\013\301\355\261\013\263?\253\351\222\n:\301\266?\236A\365\316\306\000\270\277\310\304\274y\2303\200?\036\\\341\367|\241\276\277\235\273\036(Uu\260?\036?\323\330@\177\251?\245\326}\010P\344\246?G\271\262\237%F\221\277\316\tP\211\022m\224?\3703\365\240f\240\300?\235\351\211\253\033\370\260?SX\347\214\203\036\303\277\204\302f\257_\376\323?\316\231\374\320\324-\216\277\327\014\206\261\307=\245?\003\206\'\370\224\017\310\277\3735\017u\303\036\302?3\301\300\305\360\353\304?\316s\235\037&/\254?*Bs\030\200\332\217?g\'\026m\211Ax\277\211\222\226Y\364\222\246?\021\276\030.\245\216\254\277\312y\236\013\246\244\255?\327\322\206\024\013c\234\277\236\027\235Q0\334\217\277\3458\014\177\371\300\276?\032\342\232\n\2362\311\277\027\273P\323\206\"\244\277\022A\230\242Nj\203\277\330\"\216\276\201 \234\277\254\336\017\221+\364\260?\007M \236K\261w?\026\300P9\010!\245?\3053p\020\270\351\257\277\357\373t\271\213H\213\277\362H*\351\004\177\211\277\233\252\240\201m\215\321\277\316\0362\036\022\343\271?R5n\016\207\274\260?\010\216\017\224\325V\241?V`\002\337:W\306\277\347<H\270\334\351w\2771\217#\026=\362\250?Z$\000-\263D\230\277\250\265<>\215\366\300?\306t\217+\\\n\265?\375\303$0wu\261?\216y\032\274/;\316\277\327\326\324\014#\231\272?\203z\200\376\236_\266?\273q\271\313S\033\251?\333\363JY\3313\256?\334hi\223\222\272\301\277E\373P\372\211\232\240\2779\251\"O\306\200\240\277\030\253\336H\304a\212\277\330e\t=\221\326\240\277\270\223\2061\250\010\240?\342\2101\351\3527\275?p\224=\027\014`\245?\222\346S\244\366\002\220\277<\335\347\217<c\275\277<\256$@\017p\227\277\360\017\323\262\227[\256\277\343\372\010\215\224:\303?\275pb\025\027z\243?8\032CHX\237\275?U\227P\371\373\027\204\277\307r>\3459\206\255\277sp\352bwz\240\277\325C\246#\270\361\244\277Z9Oik\225\265\277Fa\355+\353\275\241?\232\352\324\026\273x\277\277\301\350\354.V\t\242?\233\374\326Y\205[\265?\310\3170\324\022\335\261\277\021\363\210A\207\026\263\277\320E\231\370\310\341\273\277\026\313A\327\271\t\256?\223\022x28\001\312\277/{1\345\306\260\317?N\306&\365\037\025j\277\004\333\345\0343\370\306?)9\377\177\353{\245\277\300\\\255\337\370\304\305?\357\037\335\242\016\305\241?\210\211?\310g\357\211?1\253\204\362,\377\236?D\375\335\321\374\222\277?eX\277g\341\305\244\277B{\253o\357\345\256\277\315\200+\257\325\265\310\277\037s\260]\216\314\227\277\343\221\264\200EQ\241?\233\365\255\241\254\335\244\277o\006Ct\235P\323\277\307{\211\262Uc\220?#\353\344\314-\376\253\277\334\323R\342\326\260\260\277\312\376\203\\\201\375\302?\325\301u\337\014\334\240?3\210\"\037C\300\272?\303I*c\'X\306?\254\302o\"U\037\235?\314Z\310\362\242\312\265?\226s\360\003\372\254\264?y\374\305\242\330\231\256?\3624\226\307\224P\243\277Z\252@lH\273\315\277\360\325P\272B\261\255\277\227\256N=Tv\273?\312\213\0056F\332\310?\\$\225\374t\t\246?I\346\230`\311\213y?L4)p\020\202\303?0\361\036\t`\361\275\277\023e\227\273o\"\233?\233MLz4a\250?Dttk\265\230i?[\314|\215@\327\264\277\314\223\271\272\254\260\201\277\2371\207\036\277\211\263?\026X\356+\331\243\222\277\221\250\2453k%\300\277\222_)\202\313.\224\277t\3302\221\020G\247\277j\364\305)\233\'\265?\351P\230p\006E\247?8\024\206\230w\362\244?1\247Y|\327w\271\277\226\330f\364U\331\320\277\336\313\270\035\376.\212\277\343M\255g\334\234\244\277\211v\354\263\307\354\311?F\2173\345Nf\303\277\215\314\354\227\n\224\304?\361!\242C0`\320\277\265I\020\005\316\000\323?\251#\270\267)W\305\277-\036\211;?\255\247\277\027\210\330\\8k\256\2778E\271\333\216\327\211\277\366\307y\366tv\230?Z~:\221\006F\240\277e\002\206\013\327b\312?z\302)\225\017B\264?y\206\274\320^\030\212\277-^\246\210\341\325\226\277\251j\326\367=\303\302\277\234\210\231s%\030\261\277\212\'\372\333\216b\305?\272C\260!Q\027\240\277\341\244<\250YW\243?\302\342\330\031\255\202\267?\331Xjq\017X\220\277\311\t7y\346\003\205?\003\356^\004\267\274\242\277\342\324\3505\035\006\267\277\033w\202\323\237\227\221\277\232\037\310\261\326\244\261?\266\264\007\324X>\262\277\0134\334R\266C\264\277\2230\n\362\024%\204\277\220\341\234,u$}?0\377\356\033\363N\312\277k\033\357\212\377v\241?\'>\001#W\215\260\277\350+\340*=\327\256?$\374\003\265\302\207\245?G\300\236\225\010\022\245\277\343\361\241\024o0\205\277\017H:\334\'\t\235\277~8Q\305\262\034\275?\263`\334\005\324\250\221\277<#qg\007T\246?\315!^\355\205\206\244?J}\004\3562O\242\277\006\213\375\374Y\330\211?\3670l$H\004\265\277\354\351\023\274Q\251\265\277\376k\320\315%FP?\332*A\327:=r\277\201\003\317\302<L\304?\215#(\344\013j\265?\321*-Gbg\227\277\315\r]\314L\374\256?\213\335G\3332w\266?\0030\226\016d\270\215\277\263\361B4\356}\255?\0311\273\234tA\262\277M\265\374\366\224\"\270?\235\253u\312Ov\267\277\241\320\325\327ef~\277\004\220\374\324;Z\305\277\2626\3001x\263\305?\230v]\rn\337\246?\206\340\271\274\353\"\306\277qo\351\344s\014\263?\211\261O\224E\214\267?^f:\221\371\302\251\277\275j\354\203\271\347\231\277\264\324N\010\330o\213?\377s\271a\222J\266?\214\310\307?\354\262\317?\206m\364L\236\206\236\277\226\240\243|L;\262\277\246\360+\254\022C\321\277\277A!\342F_\313?\"\327\364\353\017\030\236\277\306Z\324\334\213:\300\277+\257\373\311\200\270\264?\024\267x\344\363\t\275\277\27779@\311\024\243\277Iw\240\304\330\305\273\277\314c\302\035[\224\206\277\212\025A\255a\024\266\277.wd\224\2746\235\277\000\333u\246\010\252\302?\026\307q\007\312\374\202\277\3619\351\223\261\212\253?C\353\215\260\031\225\244\2778y\3551[\337\316?w\277VF\371\t\260\277\027\227\0071W\357\310\277\3534\343\315RX\311\277\tM\271\236.C\306\277\236\353\223\037\266\205\255?^rM\301\317AU\277\202\355\206\244\\2\240\277\236\311}\357\213T\221\277\271A%I\230\\\264\277\001\242\n\216\243f\300?\"\035\t\177^?\217\277\302\027\266f\314\037\251\277\303\333/0\t\032\200?\333\377*hK\222\314?hBt\247M\265\260?w. \233K\004w?\337y!3^W\246\277aTyH\357-\273?M(^\204\244\035\245?B:C\264\240\242\201\277\307\327\261D\003D\263?t\207\275\355A\216\250?=\n\324\023P\260\265\277]\025%\367O\271\313?\034\032P\355\252\205\261?Q\260\017c\360\246\262\277s\230\322\034I\317\306?\355\004V{\2368\301?>\260\333\004\250\310\235?\316\0070\233\367\272\224?3[D\254n\344\273\277q<\232\276/F\227\277\204\255\370\264\264\215\232\2772\027\250=\361q\274?!\007&q\324\334\276?\367\013K\250\300\272\245?\240\313\311`\374\200\220?\005\276\373yV\217\226\277\256\216\233\267\363\'\242\277!\224?\233\032\237\235?\005n;\250\036=\302?>^)\350!n\322\277H\260\341\366LGa\2771\010\204\005H?k?e\005\301\213\036/\301\277\376.\244\201\322\344\256\277|*\257\021\247\r\264\277\314\263y\221t\262\252?;\276\r9\275\275\312\277\366\306c\257\340\313\206\277t\313\017\004\263\361\223\277\343;K\3704\264Q?\261?\320\206\343g\253\277x\001\3325\306\215\273?g\336\0073&\330\260\277d\375Mh\210\032\266\277#\373hf\252\361\254\277*j\033\232\273\316\272\277\030\2115\21412\226\277\324\311\354:\354\212\250?\326\375>\313\013\031\244\277,<\257\362\014\326\265?\351_Wn=\316\242\277nyY\304G\005\220?\341\245\203Kn$\255?7\177\305\002\331\006\273\277kQ\251:\352c\264\277\353\322\213\337\030O\260?$\327F\323\202\305\310\277d.\305e\000\243\220?\220m\"\255\322_\240\277[\032L\031\212J\223\277\321\212\316R0\244\243?\336{\016\237p\232\242\277\327\005iZ\274A\223\2776\324(\366\334s\303\277\353t\250\030U\016\276\277lb\333z\306]\261?RR\352\377\353`\207\277\t\310\251S\277\335\301?\017\362\240n\t0\222\277\377@\030\"\017\203\226\277\252\256\25240.\323?IE\355\201O\023\273\277\373jKF|I\265?YM\2541\034g\226\277\265\037\316\255\233T\217\277>\'Y\253\244\356\233?\201\302\342G\375\273\260?=\0167\322\233\000w?\354(\340<m?\274?\307\366\037\222\242#\231\277g~\276\355\307\264\215?\373!\177W\236}\304?\220\212\352@\013\017\271\277w\245)V_\336\232\277\356l\322/\276X\264?\266Z\031\r\t\232\306?\224|G\026\324\333\257?\265u\307\257}\356\236\277\013\366\037\003*6\220\277\344\002n1\352J\224?\3562\261\350\"\212\260?\256\221\277\255!\273\227?\337\371\273F\363\026\312?g2\332\034n\000\255\2772\361\360\036n\004\245?\341\214\032\307b~\321\2774A\033\303$\262\260\277\371\024\326\223\250I\255?\264\307\353e\236\316\257\2771\r\024\\\277v\302\277\204\001\275\202\026\232\253\277\004\334\214\327\276n\260\277\354\3346\226@\006\273\277\032\352b\202\221\202\260?\346yO\214\036\177\\\277\330X\240\330&ql?\035\217\202\340\245.\253?\346\361,w\004\342P\277\374L`\237\202z\251\277S{\331\333\361\322\256?\333\037a[\027\002\266\277\304lW\361\232H\245?\004\027\210\3146\213\213?\035WtE\234L\244\277F\267\233\357`\221i?\030\032\323,\324\344\250\277\370\320=Z\251F\251\277\323_a-\251\207\270?m\024h\357\312\212\263\277\205\211m:W;\210\277\344D[~\300A$?4R\006a\301\'\245\277\367\261\317%#\351\313?\346\250\367^\313\337\261?\320R\267\354Q\036\256\277\250$B;\371>\227\277\274\371\367vz\224\307\277\375\374\340\230\333\177\310\277\222\305i\005\037\365\265?\r\371\030\224\270/\257?-p\314\n\r\021\266?\270\315\034,\255k\221\277\240R\273+\354w\220?\207\206i\251\207\254E?\txi\353\222D\220?j\271\341\244\0201\246?\375v\3244?E\253?\364\255\372\264\251\320\267?\325\266\315\352\256*\201\277\200\357\203\215z\223\272\277V\312\333\257\364\350t?\247\021Tp\224H\177?uE\262\373.\014\257?\232\373P\007\256\357\275\277\022\340\251\230\365\247\264?\213\006\033\3423/\236\277\303\252o\270~ml?|\364;\237\315\244\274?I^\003\211\213\253\274?\003\rV\353m\200\247?\266\254@\035\322\313\272?\021O\003a\224\247\261?a]\312\272s\377\275?!\323\303]\264\353\273\277[\351U\343\316\251\244\277\236\366u\214b\242\263\277\335\271\230\347\323\337\212?\221C\300\245\231+\265?\376\3702\324\263(\303?J.\303\324g\"}\277\333\034\3638>\346\252?\305e\300\204\356\241\251\277\343T\230IY\372\262\277\252\223=\214\240\016\300?\177GPJ@\032\275?-\263\335\221\347D\272\277\321\300`W\351\235\301?W\374\365\361\363k\303\277e\244j\001?~\246\277\332\275\361\301\310\207\327?\342Z\372\002o\316\251\277\240\354\366}\264\273\244\277\252HM\320\ns\225\277\221\010\202e\343Uy\277q+b<n\316\242?\\\003~\325\000C\224?\007C\345\233\260\375\277\277\266P\375\334\257j\262\277\357\353\377\220h\200\230\277\013R\372T=\215\325\277T\200#\203\320\307\264\277#\334o\257\351\006\277?\374Ed\310\2005\303?\366%I\026M\277\216\277\255\266T\210\350q\266?sS\355}\314H\270\277P\220\"Ad\272\234?\0277\371/\246\227\304\277O\267jw2\330\270?\0144\354h\030\322\271?\324\016[\030>\373p\2771\366\'\3122\220\302\277)\002^\307\001\013\231?\367\n\014,\211m\267\277\177\030\026\023\2460\251\277\243\364\013\037\252\233\240\277\364T\366!Z\236\275\277\302\370\'\034RM\304?7\340\303\033\261+\272\277\033c\226-\340\316\260?\371\345\350\023\243\230\275\277@X\210\3277#\273??\367\261]\316\217\271?\365rM\352fZ\225?\276\250\271\\\032\020\310?G\3561\271c\024\274\277\353|GJ\345\251\251\277\331\275\375\0175P\301\277\272\367\325\344\234p\215\277.\2627\341sF\226?\031\371\203\302\010j\265\277Z\010T\016\031\\\276\277P\200\030\261\320\246\252?r\363r\247\224\013\225\277\244b}\300\337g\310\277[\201%\002\347\017\271?by\371\273\322\314c\277\233\005Y/\036{\311\277\2035\207\344\367W\253?F\034@\362\306\226\304?k\177\372Z\027\236\263\277Qk\030iM|\266?\275\035\207=\230\331\257?Q<\317?\t\270\261\277.\036\203sXOv\277\032\005\357x\316\356\303?\2728\006\033-%\213?\033\rW\266\352\240\247?\377\224\036\030\030\332\252\277(\340m}\r@i\2775\333\314qW\340\217?MR,o:T\177\277 \246\360\254G\257\226\277\372q\005\347\252\232b?\0373\367\224^\345}\277\030\225=\225\022\240\271?Na\306\211\361\024\272?*\007\370qj\200\303\277\242\037\300^\223~\251?\006\311\203\230\232\345\245?\227\320\340\343+\201\300\277N\213\'\265\032\223\234\277p/\"F\237V\251\277\366+\370u\022\032\203?\230E+b>\"\265?#)\322.p\345\300\277\276h\3742\212\321\276?I1\2075\032\325\250\277:\366\336=\323>\225?C8T(e\252\267?\034H\002\310\321V\220?\017\217\340\366z\331\213\277w\037\235\036\320Y\270?R\251\006\207\243B\273?\305\344\301\237H\203\243\277\320\235C\n@]\262\277\374&o6\r\256\256\277;t\322\375\326v\263?\245\241r\244\252#m\277\315\030\273Z\037.\314?\226L\256\274\2544\274\277\367\316Q\003\231m\277\277+r\010ri\315\266?\373\3053\274\254\251\256\277>\346\376\240\251Y\214\277\261\022s\035;\024\210\277\026P\036\001\032\253\224?\315\0310^\346h\200?\261=\227-\010\014\245?\373{`@2z\275\277kx!\001k\020\233\277_\200\013\255\023\351\231\277g\233[@-\027\257\277\022\361--\242\035\251\277\211\211\235\315)\252\301\277 (\317\373\351\205\260?2#`jK^\244?m\274A\363^\312\303?\026\331\310\302\374\007\242\277[\205\212\353\350j\272?\255\276\352\n$\343\310\277s\275\334\000I\304[\277=\227\340b\204;\222?tTM\005\227o\264\277 \347\026Iq\372p?\371\0361:\367\257\274\277\366w\377~\262j\225\277P\335\321\003#\210\232\277\272\235r\315\2661\206\277\221l{\017|\373\257?:a\362\000[|\255\277\035e=\002\322\332\265\277\013\017\251\367\377~\216?\tR\025\262\001\243\262\2773D{\350J\331\320?\252\243\000i\315p\264?\256L\326\301\254v\253?2\032a\203$\253\231?fag\370%\017\265\277`\236~\351\340W\243?\246\361r\244\021=\251?\005\301\215\215\237F\220\277Zj\000\325\332\366\271\277&\373\345Qq_}\277\237\235\031\232\320\031\245?\362x\236\203BH\330\277\211\006\250xXX\265?\222&\332nL\231\246?\240\330\034\316\323%\222?\364w\303\252\366\037\327?\305\n\357\370\302\023\252?\346r\247<Rw\243?l]\273?^d\265\277\254\223\311\300DW\257?[2\371\351\267\273\207\277\301\2479W\203\246\266?\257\321\325\276m!\265\277\005q\345I\353\247\257?z\341bDb\377i\277\245\342&\225jg\264\277\033Ts\037\005\360\240?1s\320\304;\335x\277\256%^\231\276\306\221\277\022\0031f+|\310?.I\'\n[\252\271?\250\016\332\213.F\247\277\177+3\007a\264u?\250$q\230\033x\267\277\300\177\257\240~*\250?\262\266`L\314\320a?\313)5\366l|\300?\323\032\201\037\332\370\243?\000\27768\276\034\227?\345<\022\256\373\273\252?B\237\362\306\224E\242?\030\220QD\216\273\264\277_\263O0\354\315\275?<L_\362\241\242\322\277i\260\242n^\335\222\277\023L\240H\346G\265?\272\320\256\001\312)Y\277\017\010\3642$\242\263?AZ\027\263X\211\264?B\n\001\364\013\226\227\277\3726\206\325\214e\325\277\270L\355\200\203~\224\277\032S\3125\331\335\033\277/\301Y\005&\032z\277\341\234\277\375*\271\222?\315.\335\314\377]\306?\262\365z\354\202=\307\277\342 Q\257\321\207\314?4\013K\366\313\355\301\277\207\034D3\260l\246\277\357\034\355gq\000\273?-\360\323\301W\306\262\277;\262\001\325*f\251?\351\357\270\330\035&\271?\021ek\317\024\"\310?\320-2\036\310\376\210\277\204\243\322\220\3544\256?\t\254\352n\226\022\272?\313+\360\3407q\244\277\230\231 li\267\274\277\311F[hs\342\242?\340\273\271\302w-\255\277~?\034>\003C\224?\260\225\016\214k\034\251\277\237\307\330h\327\251\303\277\363\0324?\006=q\277N\027\267\202c\200\264\277H~\316\035Z\216D?\266\375\032\300\204\021\313?r\005\035\r{D\216?\325\206\213\236\342\366\250?\033\220\177[\235\362\267?\377\204\335\307\202\236\245\277T\237_\022-\304\242\277lP\230\016\300?~\277D\316n\007\220\331\253\277\023\027s\203q_\201?\306/\022n5\257\241?h\311Oi\324\334\262?\304\'\315\016\256\244\240?r\235\3768|3\262\277\\\026s0t\252j\2776\3649t\3745\270\277\343\343\217\260\350|\252?\214\230\207\365\322B\246?\374m\022-[m\271\277\332\306@\332\340\237\260\277s\300\267\3664\306\217?Q\226\177*\030fs\277\266\222&\364Q\301\264\277\350\374\207e\3540\261?\0065\211\324\316\363\301\277\230\225\224\020G\r\303?\366+\007\232\003\333\260\277\2611\317\307B\376\206\277\332k\272\257-G\301?\312\355Jri\273\254\277;\013\275\304R\244\251?H\305\267\001\356\325\200?\2644\253\357l\226\306\277\016\360\301p9\351\301\277)3\230\255\275e\271?\177\002\350\246\231$\245?\320\002\273.q\360\275?7M\332\3771m\250\277o\304\006\207\235\270\254\277\340k\'\001\306\205\272?\002\303#\2775\361\230\277\351\332\026\303\243Y\224\277Y21\334\302\305\310?\204\010\3630\016y\241?\350n\363\026a{\240\277\267\372\240K\345\311\301?g\003\2712h6\274\277{\256\226&|\311\231?\323]7\001d\240\253?~\310DF\312a\204\277\212\032d\321\276\311\221\277\303v$N\001\232\262\277\362\355\270b\220B\306\277l\001X\322\032*\305\277\276cuiep\272\277\244\'@o(y\265?\351]w\366\334\322\313\277\270Zm\205\261\203\261?\250\276\230J\246\020\301?S\3525\347\245EO\277\351F\270 c\"\311\277\377\345\016p\313\316\221\277\327O\014\333\363\235\214?\345\314\nd\217\206\305?RkL\023\221F\272?\355\326\226\351\253@Z\277\010\355\030\3659\251\275\277Z\374!l\016q\253\277\204\351|\351\230E\247?\371\024\324\000\277\301\302?\003\332\231\257\247\273\223?\200Q\304\313sM\261\277\235\311a\250\344\311\252\277a!\274\303\260\306x?\272\331\3021!?\223\2770\263\203B\021\204\243?\275\346S\277fp\262\277\276\016\025\374\234d\312\277P\362\352h\353\345\314?\276\016b\374\257J\264\277\326\271\002&\000B\273?\327\307D3\035\022\315?\367m\177\244\241,\256?\262R\213\024\340s\242\277\345\230=WH\211\261\277T\360\332$\221\024\315?\304+\373\212Um\243?e\223\030\031\367\322\303?\333\231o\241\032m\271?q\312\224\274B\000\246?\006\271P[\342X\321\277\010\232\330\0027\323\205?+a\256\362\222V\246\277[\336\022\252\013yk\277#c\334\260\376>\224\277J\24482<F\313?\232\261q\022\033\225\206\277\235\341\376\332\017N9\277\270\200\243\203\021\t\231?\352\364\364\345\313\'\332\277.\260N!\367\273}\277\022\016H\347\244\243\267?)\021/\371A\270\301\277?\r\003\312\305\312\262\277Sb\035\323\260\323\253?\237\376\323\023\302\276\267\277v\316\223h\013V\317?B\207c\0011/\202\277\224\231[\247P*\241?\221\206\241\264/\337\263?J\374\0011\334\253\261\277Mu\005f\337\325\245\277\216\2671\202\231\214\221?Gi\371\306\362x\313\277S\252\000\'\261\356\263\277\324,\346\231\310\206\252?p\341\345\264\277\245\240\277\340\302\257\326Zo\217\277\275\322\"\224\201\273\267?\231\302\347p\225\225\253\277\330\241o$LB\301\277\304,c\013\233k^?\250\320]J\035d\265?\306@\"s\321\354\234?\n~\023\343\374\333\300?.Gt_zu\223?\326\316\037\331\177d\216\277\333\225\021\376\336l\212?E\307\311\276\373n\256\277\021\337\177\207\274\250Q?\267\362D\300\003\325\260\277\240\270j\005\253\362\240?R\275\266\202\025\234\316?\274\215g\032\375\225\303\277q\025Ry\231\001\322\277\004\245 ]\376\343\247\277\213\006\265\014B[\270\277n!\257e5o\261\277\300J%\016``\271\277\rk\224\345y\356_?_P\223\242\226\310\323?\017\274\023\027\327\233\303\277\030Z\325ux:\227?n\373\035.g\370\225\277\315\243\221=h\306\273\277~\276\347+xh{\277\005e\266yy\312\266?f\253\001L\333b\205\277\313Y\302\000K\243\315?\177N\026\301\374\245\220?[\266\333L1\242\303\277(\031\251\251\355,\302\2779\255\025\007\222\223\310\277f&a\334\353\014W?*@\021+\351\257\216?\373\360{\272\204\201\321\277W\225\\1\272H\267\277A\313,e\337:\224\277\347\257\007A\303\225\275?zs\251\261\013ls?\037\311cS\021\271j?s\313\r$\303\n\277?\\\231\315=\327\336\266\277\214\260\337\n\300\271\225?\016*\352\305\230K\303\277\232\214\340\336jw\275?\235\270\332\325^=\203?\002O\371\376\330\031\264?{\374\275\033?\255\233\277\332%\221\337w\305\321?h\375\203\265\326\331\270\277%\2132\375\250\315\300\277\263\224\346#\240\000\321\277\'\377\245=\215]\246?}\230\2269\366O\275?\223\264&\367O\311\276?H\377\327/\376\275\242\277\'\th\364I{^?j\024\352B\272\303\301?\256\t2\325\306\276\251\277\302\n\311\0350M\203?f\363\332\354\331\240\240?\345\017\347{\2067\222\277V\325\313\347\177\302\270?\375\353\316c\366\001\242?#\223\007\202\234J\266\277\233.\351/\276\375\306\277-\232Hp\261Jp\277\r\333\000\201\317^\262?1\244\345phh\271?\364\271\274\252\254v\271?\002\371\356\030\321\332\244\277\204\n!Z\346\226\257\277\274\331\273\240\246\017\275\277\375\351\303\030K<\302\277\223\267!\263Q\364s\277\363 \254:\201w\256?\314\354\366\3047\235\300\277E\334\006\350PF\223\277\271\263\204\252\002jw\277\207\301\3557J)\231\277HT\312\270\036\033z?\204\332\037\256\376\374\265\277vY*a]\356\266?\244KV\231t\347\316?9`\324\306\227\"\302\277\266\330\003\240b\347o\277\227/\237\376m\006\262?JX\250\334F\261\252?\225g)\203b\361b?\343\001Y\335\030\333\300\277\316k\3505\223\t\233?\010\034\t\301\261\256\227?*Xy\001\342\341\240\277\316\212\342M5y\321\277\241\007N\212\374\025\211\277\233#\235\371\371\322\255\277\265%i\347\232\233\275?\370*^\224D\360\234?\017\361JL\326u\223?8B#\326\341\300\217?\000,\262\325\363\333\235?4\326#R2\316\301?\224Y\343X\236\036\240?\211I\275$\306I\263?\343d,OM\371\301?$$\021\271\311;F\277ti-\351P\263b?\326\3754\344\021\021\203?e\237\020\363\206\032\250\277\2144Cq4\363\262\277\206\254N\320@\005\270\277B/\372\034=\250\300?\324\247\017\216\345\302\242\277\372\037h\265e\267\253?\301\272j\304\323\020\235?M\333\350\\\217\256a?\364\365d\337n\355\262?\307\357\251\366\236y\221?tN\267uG{\265?\323\373QO\363?\271\277SF\351\220\034T\221?\375>S\365%\334\265?.\270.\374\247^\271?Q\014\326\274\260\215\201\277\351\215s\345x\323\321\277\373\245%\311\n\310\241?NY\265\360!r\267\277\004be\347\2612\264\277\225\376\273\206 d\304?\253\243\362\254\253\346\234\277ZH\270\206.D\261?\220k\330X\336\003\266\277\227\025\361\010e\203\302?\231\302\304\314\354\253\236?k\226\235\021\267\341E?mE\320X\035|\222\277u\\\360\t\031\207\263\277\'\2578\032D\"\274\277\303\277f]\217\374\264?u\003\207x\302\264\257?w\313\2476\025\364\252\277\336\353aKC\323\265?\356\210\014\261P\014\264\277\026\330Q>\253\332\327?P\236A`#\223g\277\317\023\343\250w0r?hK\317N\303M\273?\254\317Pc\\D\237?T\364\251g7y\243?\221\241\210\336\260z\254?\035\221K\004\264\335\243\277\303\366\364\334{\261\304\277^\343\251U\322\317D?s\027\004\205\225\225\307\277%J\207\253\242\352\247?\237f\013\031\227L\245?\234D\034\225D\237\263\277\025\343\242\262\275\236\304?\0060\\\243\335\342\225?\252\364\261\317]\017\235\277\002K\\)\300\216\240\277*\202\206{|\232\267?_\014q\365I\352\250\277`\300\034\262$\362\320\277\251\200J\204\210\014\267\277-\244\211\263\010\337\264\277T&O%\213F\300?\301\200\266\"!\302\316?s\266`\220(\030S?\213l\340H\016c\233?\004e\032k\026z\311?6\272\302\326\217!\271?\317i_T3@\231\277Oz-\270P3\220\277)\301\236c\030~\263\277\3575\306\304\302\337\221\277\3039s\020\034\213\240?\3570\024\332\370L\300\277v\023\036\247};\224\277\243_\006\332\345\263\302?\023\017uq\217\351u?M\205\216F\312\255\312\277b\332\217\255#5\245\277\366\372\307 \276\021\222?,\317\313#\022X\275?\254\232\266\017\221t\274\277\310\236\330a\300\013\255\277\035\\\351\353l\212\264?\355\231\322\224sr\226?j;\257\220\330d\225\277_.i\355f`\304\277\332@\342\020\312\331\321?l#\366\020\307K\260\277\221\204\035d\352\355\257\2775\216\270\307\222\345\262\277\354\322+\337tx\264\277\017\"\325\267.LT\277I\347rT\'b\272?\3754\007\244\326\306\243?\0045\337\213\272\311\243?\365\007\262\\&X\221\277\344\335\363\270[_\263?71\371B\214]\306\277\022\216\252\2013E\264\2777aD6.\230\263?\302\035\340\371\222\264\233\277r4\362\330\232g\213\2771\275o\231\315c\242?\301\032\266\033\335\325\265?r\3166$Y\034\303?\230\021\347\310\237t\265\277\247\335,A\275\316\300\277\007!E\022\253\244\212\277\013\340\001\256\340\231\302\277_\374\370_:{\220?\377\001\217\214yA\264\277\326\'|\304\367\332\270?\025\305.\017\227Q\205\277\323/F\374\271+>\277\351#L`\2628\305\277\231\024\276\272\307\003\241?[U\330\235\317\337\242\277\260\\\324\204\007\222\275\277\271~f\334\377\327\322?\356\321\341\222\273\231\262\2774\023K\315\317\302\264\277e\214Va\035y\270\277\235\350\014\204\021\023\300?W\206\353\225\"\361\256\277\243D+\307E\317\260?\362\370\233i\271\341\271\277\271n\204\336\036\263\240\277\230\364\342\274\335\242\265\277\336\355(\224\376\230\300\277\263\'\267N}\024\242\277\320\310\317[\324x\300?\257\035c\202\231\200\307?\255xX8\227\224\306\277\334\304A\215\200M\264?H?V\241\324\0135\277\004\237\375j\335\316\255?\037t\344\035\2345\275?\310\0047\355\210\r\263?2\220\360\312\222\001\273\277\240\347\301\330\343\266\307\277\035m\025\371\366\357p?\316\351y:X\324\256?[PLn\343P\213?\020\370O\350\314\267\230?\265\2513\023\220L\254\277\366go\245\031(\305\277\255\t\360\330\241\\\320?x\266\210\257\013M\231\277\006\200B\302\207$\267?M\032\247\237M\322y?E>\306\215\"/\275?[\365e}\3525_\277$\271\2310!\216[\277\306C\267\004\247\362\246?!U\200\323{\356\253?\006f.\275k5\255?\037\224~E\267{\245?n7\007\202\266\201\272\277\374\254\237vx\271\270?\233L\200s\375a\274?\031\226|D\302\235\320\277_7\353\033\276\341\204?MN\266\230[\267\302?\203\t\251e\213\342\254?\031\330>\205\362\177\264?.S\220~\324\023\312\277$\326\353B>\335\233\277\241\202d2\326\314\301?\314T\370\037\002\013\260\277&a\342\270\312\n\253?\270J\270\317Qx\275\277? e\210+\030\200?\364b1\373y\365\250\2772b\021\021\256\364\244?\334t\177\21702\261?\312\272\361}\ry\243?\371\325NG\330\357\237\277B\214\370\322\3109\245\277\201\246m9\200\305\263\277\021\3541\235w\367\271\277\200\017c;y\237l?\216\320\232p\370\010\322?<f\336\377)D\253?\242\373r\327!\233\245?\356\215D0\301!\325\2779\025\010#\336\024\252?8\027\021\221LO\261\277\225\340F2_\346\301?\315\250\213I-\342\303\277\342\233\342\257B\374\233?tpl\200\0372\202\277\272s\334\367\027\212\311\277\320\364{\030Y\357\303?N2\352aP*\261?\332\264\371W\216\010\225?\360o\261\275\251\366\220?\273?Z\032\005\301\245\277\017qV\034\2606\231\277\247\346\'\030\256L\234?\343\322\376K\277\217\236?\177\362\233A+\373\250\277\366\333^q\336\330\235?\355\030\303\013\354$\325?\215\301Q\221\321\361\256?\244\254\323(\372k\301\277\370\273\rw\r\345\205?R?\325\274X\306\300\2771\253\023\203;\255\261\277\257\032\251\335\207F\272\277l\251\320o^@\267?V\221pc\212|\255?\366\352F\264JU\267\277_Y\035>\213\010\274?\332\227\201\252\246\343o?\377eN\217!\320\240\277\356\344\370\326\226W\205?\215J\237\361\237\244\303\2776\203\245\024J\312\204?\353q\371\214\273V\265\277I\0228\356qRe?N\330u%S\t\266?\362\335\213\014$\362\242\277a\267l\277J\376\266?\036\206\344\225\306Y\244?j}\227\220o\363\232?d\363\214\2109h\261\277\366\346\221\356\241\371\321\277d\342\330D\357Q\252?\273V\304\035A\204\252?O\255\344\303+\032\267\277\322\316\350\334\226V\307\277\001\254\026\'\024\367\232?kH\263\243\305\202\261\277\331r\367%Cn\257\277\377R-\325\0059\301?X\021\037\242\022.\253\277\240\235\351\271\304\357\213?+\020\345\354mK\300\277\337\024\266\000\312\n\277\277\246\214\032\356\003\202`\277\360\371\030\234>Yr?3\342\336|O\325\214?\272&Qg\332\302\264?\233\177S}\254\200\244?\364\350\014\375\035\235\222?\036\002\307\3662\374\263??\207\013<kW\243\2775+\256\202\032\034\267\277\200 \205%s\371\261\277\244\366\266\005\235O\243?xg\203R\372\343w\277|NI\026\020\345\303\277\271Z>2_\240\245\277J\304%\234\353\301\242?\275\355\265\035\324\027\310\277\305\301\330C\332\351\247\277\276\245\244<\265\210\300?\035i\230\264z5\203?\301\322\300j\226\262\257?\336wN\211\212\327\207?\331\232\242\306\030;\220?\r\264,\270\036\243\223\277\030\313\255\277\245\374\220\277=\201z8%\373\245\277\014\270b\210\020`\301?\237\265$\302\267\263\274\277?\331\233\274B\005\266\277\037\003\027\262\010\324|\277\223\352.\327\014\006\316?Z\"#Xq\241\265\277\304\006\205b\224\345\225?\261\215\212}\353\330\253?! \301\016H\027\303\277\035aA\214}K\274\277p>\242\002\335n\302\277\035\356\257\346@)\261\277\365\204\020\260\204\\\273\277\210W;\332\233\270\247?\347f\211\342t+\307?\tD5T+D\300\277\264o\213\023\303Q\265\277\237\330|\025<\"\206?\343\221\347\346\037r\301\277\317\366\035\270l\265\206?jm\267\265o\217\251\2770\243\263k\006{\260\277\027\020\374\251\324~\321?\241\355\'\356Lx\220\277CG\020\360\321s\275\277\016\246\316\007\314\244\277\277\212\264\014y0c\311\277\016\247\201\'\363\273\274?\230D~^\006S\237\277m\205\207JPgu?\321\255\255\200Y\031\272\277\353\373\026\000f\026\277\277-\255/\317\211%\321?\343\300\351\003\331X\242\277\237%\272\312[\352\266?M\226/\304\354\3054?\t\325\211i\366\037\262\277\360\024o\362\260y\243\277\021\333\0104\372N\207?\367\251\335\025]q\265\277\361l\362\324<4\226?\20448\222\330\260\237\277\020\"H[F\210\263?e3\203\263\357\026\273\277\217\301\261owO\254\277W#\244\251\232s\242\277\177\312}\024J]\307\277M\250\201\230 \014\200?oC\032\002\257 \276?Z\344Rb]\350\242\277\272K\017\374Y\367x?ii\224\326\263[\251\277\030\362\237\364\254\t\327\277\2005?T\355\250\262?\221\354\250\2564p\220\277G\001\220U]\366\250\277J\014-CR\351\236\277\313\314:jJ\010\300\277\"\243\032\321\206.\250\277\373\220\301\034\271\376\302?\023\365#I\340\213\307\277~\231%\312\356~\211\277\267\214I\224d\014\276?\326\031\274[\226\330\266\277\350n\245P\255\004\224?\357\2434\227\263Q\227\277\347l\235q^\031\254?\tr8>\260\222\274?\326\320z\203\010(\254?\347\034\304\374\277\016\243?\374-\370k\217`\276?nW}T\2775\307\277H\312r\343\276/\307\277\361\336\356M\253n\243?\211\177\252\350\nkg?s\377\266yhw\251?\345p\307\207\240\251s?\303\200\303tR\235\266?\257\366\322\364/\200\300\277\353\323:am\201\220?G3\222\220\275\236\242\277wkR\323\262\302\234\277\221TG\350\335;\250?\254\236\363P\301\036\245\277\303\341\370d\322\225\305\277\354\374\364\301b\036\306?\375h:\370\031\035\256\277\342\310O_\374\352\262\277\376\026!v\341\014\307?=\027\207<.\261\204?\031y\303\241\345\204\302?m\306\233\361N\023\230\277\"\320b\245[\017\307\277j\271\016t\335\302\247?\320i\214\370\310\270\300?\336Y6\007\302\226\253?>\240$a\340\231\224\277<\313\207b{P\262\277}\356\264\333\221\374\253\277\307\033:\223\363\250\316\277/\377\335%\237o\236\277\027V\360?\324\247\271\277m\257\3548w\036\235?\353\020\000\210\272\007b?\375\374A\214(\230\261\277\201\307&\376W\350\320?@_\321a\374,\323?#2$%?4\227\277&J\331\302\020I\272\277\344\010\312\370\326*\320?\203\327\230\366B\344\242\277,\331\335\313){\251?\332\300vS\221*\227\277~\263\367\3672J\271\277\231\311\265\211\300\004\265\2772\335]\324\213t\235\277\255\272\255oc|\237?\231r\2729\003/\245?\355\210\365 !0\301?\010a\372\\bN\250\277\000\265[\356\272\312\257\277r\253\225\t\336\312\270?\352>\231\\\335M\252\277\010\302\010v8\027\311?\312N|u\025i\240\277\261|U-j\371\226\277Ul\355\245 \371\271\277jl\372%m\362\277?\246\323\202Ct\220\257\277\377A\215L,\246\310\277B\314L(\317\346\304\277\202#\026\0246\361\257\277IZ<NG\316\272?S\"\312Y?\254\272?t:?\245\'\376\266?Q\036@\265\335\374\265?tXJ\331z\034\257\277/#\340\257\267JT?\345\263ulP.\243\277\"\271\3178S\346\266?VJ\n\211\340\333\300\277\370\'S\306\317Q\254?;$\267\270=F\241?\363l\317z\375#\245\277pn\362\367BV\274\277v\357_H\021\037\216?\270\020J\024N\035\217?\323\003\020\332\204\272\263?\373\320v\252l[\305\2772A\277\301C\344\252\277:\206\273Ta\036\257?\267\036RT\003\022\253\277\014!\207\203=\305\206?I\267\336L\241-\221?\322\363pN=\320\226\277\230<\017%\225\274\261\277\341+\024{P$\270?\316\221kR\266\266`?&`\335\t\337\266\267?4\267\001fD\317\222\277r\366\266D\236J\302\277\t\352dO\224\023\262?\217\226W\203\327\273\304\277v9\200\243=za\277\"\376\364[\t\203\226\277\256PQ\007\004\262\314?\365\205\231\327\313\014\307?$\017\210\332.\311\270?]\303\247\'+<\275?\335\324G\224m\n\275?)\251H\300\026\002\242?\004?C\213\277p\217\277\373\027$\376DY\266\277\314\001\244X&\361\256\2775\260[\334\232\313\242?\260\2527\263\035\261\256\277e\3576\231\302g\216\2777\023\014\350{J\236?ti\334\002\2238\242?4\034\301\215\177\207\243\277\020\n\363m/\207\265?P\325E\364\354d\237\277\245\315\356=[B\237\277L\321\301]&\343\266?\276\372\"\004\267\217\267?\262\335\"\006I\201\276\277\250\250\207\315\3565\226?\034\311T8S2\237?T\266c\375\202[\261?T`\017\215\275T\276\277)\205:\337\204\363\213\277\352\207\317\350\025;\207?-l\327\272\372%\311?\335\341\010/\336\005\216\277j\307\253\373\342\223\207\277\247\251R\345\020\263\211\277\245\364\360\313\214\264\233?!Z\331]\216\242\310\277\304\026\353B\216\354\225\277\326\013\323\274^D\257?\232\204\266\314\230\312\247\277<\320\265}x>\263?\340J\223\375\215}\316?\023\220\343\361\337\375\302\277\216\001\315\'\322\034\220?\210\345\"\223\330\202\263\277\376\244\366z\202\321\266\277\352\322\000YT\230\214\277\316<\267\246?Z\262\277\312\017f\024\330q\276\277\236sVHU\377\264?\200!\321\361\016\004\221?\303e,\364a\016\242\277\177\363o\243\024\253\253\277\206e\267\031\357\023\276\277\252 ^\273\004\231\203?\016~`\335`\021G\277\264\302\265F5z\272?e\031\230\343M\233\200\277\022\2457\205\226\014\211\277\222\311\026\307)\334\271\277X\362\330\001\205s\302\277U\373\3237\270\364\255\277\323\245\010R\354\346\241\277\306\277V\310\240h\243?\260\335&\235?\363\252?\242\367)\336D\327\240?\333\022}\224\205\033\260\277\272\236\342\245\235\302\256?]\345\\\362\341I\265?\210\345\t\371\367\000\234\277\026B\221\374\216.\241\277nB+\311\255`\267\277\035\023\014])O\232?\310~\327\002:\304\211\277\355\341\314s\232\007\245?\"\221\302\314\375\246\302\277\276\313\253\340\215\005\203\277\321\334o\270%\264\304?\272P\"f\354\313\303?`\nc\243@&\276\277\'U8#\014\220{\277d\361\321j\363}\270\277\035i\210h\201=\251\277l\004D\275{!\260?,\244\331\305\036v\266\277\344WSKF\334\311?\345\001\236\365\177r\275\277\223m\303\213\016\343\233\277\306\276v\271\023\010\313\2772\354%\315\201\343\225\277\246.\240\352\217\323\245\277\200\222\326\246$K\303\277\227\337\223:\252k\332?\266\002P\273\232\016\220\277G\324\256\304E\303\272\277\242\004\351\235,:\227?\317J\277\335M\306\270?_r\272!\033\334\304\277\222\231A>\023\025\310?\304\344\024\002\211\326\273\277\235\266\252\245w\271\241\277\202\025Xu\321\214\213?!.$\243T\257\313\277\035\223\361\325L\275\262?^\331W\277\030C\242?\035\236\263;\230\252\306\277\267\205G2\036\330\314?\031\343\352\240m\320\215?\225 \242`f0\277\2778*\220\335<o\320?*b\033\026\227\223\272?\004^-Z\\>\321\277\2404\327\242\321\321\257?\000\331\0149\243\261\321?\257\007#\241/g\234\277\36634\276\320Sr\277\233\241\273\334s\357\267?\316M;<\302\203\272\277I;\332K\346\010\237?\245\372\377e\265\314\266?7AT\335\n;\222\277\177T/\275)(\220?\301\257\317_c\342\247?\3603\006\307Z\215\204\277\333\345\177\225.\\\242\277\002\324\177,\"\024\257\277\004.~RV\303\276?\006G,\315\306\002\225?\177\334Mr\276g\240?\316\365Q\335\262\024\234\277\314\270\217\027 \364\256?w\357La\265v\257\277\300\223\376\236{\256\252?#\313\022\204\350\362\237\277\361}4\037]V\237?\362E\320\235H\217v?X\304\302\2535\305\265\277yt\014\337\355\036\232?\016\372wE\365\304\242?\177l\0255\007S\247\277\023?\333\037\220\234\263?|\002\242\243{^\315\277\345\334\337\246y\223\244\277\t\335(\025u\324\274?Vs-\377/\301\310?Dv\325!C\306\273?j5EP\014c\273?HB]\244\\rC?\267\245\211\256\332\241\313\277\372\276d\247\275s\243?\273\211\"[&\373\230?C tt)\334\254?t\020Y\330tw\243\277(\263\236\356\2546\242\277\334E\366\307\nS\240\277IPW\036\375W\330?\315\362\312\251H\336\227?Kj\201\371@M\266\277?\0070B-T\305?\000\232\236\356\273\355\315\277\002JvY\300\321\274\277a^L\361\261\307\272?+\240?~\205\370\267?\302D\317\210Z%\262?\224\320\372N\2475\300\277D\3611\323\273\301\253\277\267V\014\025hK\262\277eu\347\206nC\301?d\\\236K\333\344\303?\347\345R\326\251c\240\277\214\237\023\035>\232\220\277Y\331\224\032\224\330\227\277\203\311\305\036Q\177\230\277\326l\036\206\304!\315\277\333\211\301.\351M\262\277@2E\206\n\024\265?\261\375\300,M\224\273\277\345\353\2510\324/\207\277\200\020\274|fx\255?\231\262\035\274@(\302?\002u\201\360wQ\317\277\017\313\2541\345\225\224?{\242M\254\346\275\235?\2362\240\336\254c\254\2776&\033\240\243{\243\277\362k\017\300\3347\310\277\200P\253\214\006X\261\277\010\305\352A\211\261\311\2770T\243\350\270\207\220?\021R\320\302#y\211?\322\254\335@\312\323\302\277\315@o*\367\373\276?\232\375\\\265L@\266?\0066\314\235\273\363a\277\016\002\rN\"\306\265\277\271G}\231\270\344\253\277\347\354\005\\\307*\245?\024\332\331\235l\246\310?\240k\204\252\327A\241\277_q\303\007\307\214\246?\314\275\226\025\342E\242?\021\331\327\205\304:\223?\022Sv\001b\227\274\277\215I\200\267\267\253\222?\250\272\344\231\333\256\204?cDp4\331\027\240?IM\272\n8\343z\277E\252w\336\022H\245?\222\331\210\302k\023\263?\361\220\255K*\200\266\277p*\3310\347\257\254\277q\001\203\177y\353s?6\323\022XF\255\222?`\211\361\365\344\215\232\277\247\222\301%6\213\272?\2246x\004\210*\232?\004:_\237\376\263q\277`\343ae\242\024\273\277\355\313o\002M\000\213\277\214\254/Y./\254\2777\250\346\266\314\014\224?\234)S^\365\367\311\277\274\325I\212\271sF\277\361\317\2022!\201\305?1\3753\002\\\276\252\277\314\204@\224*a\201\277\254L\030\373Nu\244?\236s[z\234t\263\277\224\212\274J\321\241w?\226\245|\316\300\025\304\277\234\213\325\177e\036\217?\362\236~\014\362\301\314\277\301\300\361\335\304\227\221?\365:\033v\250\207\276?\r\351Q]>\245\250\277\007\037\242\253\326-\320?\355\350\376\372\350\364\252?q\306\373V\377\335\255\277<ny\233]\325\270?_\356df\243\261\264\277\'\265xT\342\242\247\277(\250\372\372\334\335\237\277+\213\247\204\233\255\276\277\263 \002z\304\274\274\2777k0\351\233\307\213\277\001Y\360\317*i\244?k92CV\264\257\277\302\201\014\224\265\004\274\277,\350<\362\252+\244?\036\024I\252\251\017\253?$\350\007\250\032\023\220?5|\244\213\206\007}?$\217\347\235\245\037\307?a\212\212\"\311z\322\277h\343\272R\303\312s?\3474bL\002\320\264\277\026\016\266\335\202\340\231?x\236|\215\022/\321?\365Y\322\356Q\266\264?L\nH\224\351\352\262?\307\227bD\350G\260\277\227r\300\241\263>\225?\241\230\254\2475\365\221?1!\014\323\275\n\301\277\2067\233\325\246\314\251?\263\225\357\360`Z\305?\004\356\n\350\003L\273\277{\215\033\301\372?\261\277%\200\323\360\355:\264\277;\246EU\355W\262\277\240Z\267d\026%\322\277\353\275E\371\362\232\262\277\377\264\351,\\^\262?\0359\214\325*\224\240\277H\343zJ\225\265n\277\307\034\263\3367\224\245\277\231\310eH\245\201z?\3349\371\325\271\367\264\277\314B\020\010\2000\242?o\246\344\014\000\204\243?\254\2604\227\024\005\223\277\200\213[f\037\260\262\277\371\t}\035\216v\206\277\'\343\276L\304\037\235\277\177\243\233\272~\272\261\277\250>\372\025\273\207\271\277}.\214\361\021\"\255\277\265\322\213F\337&\264?=\307\033\014\271\351\275?\372\343\306\026i>\212?\007\352\320\302\325\232\234\277f\274I[#.\301\277\022\022*UL$\276\277\341\247\257k\256\203\253?\277\007\367\333\311\216\224\277\236\"\002f\313\251\302?\330\236\335\247\"(\303?\037L\222\177\235\233\236?\373\235\221\205\371R\256\277)7\263\335\255\227\261\277\323&\201\365\246\001\260\277$\003=\206R\310\242?\006\232P=\303\277\307?o\302\313\356]\037\305?\311X\374\376\026\205\301\277o\006e\25045\242\277(+X|L\361\244?\3439\024\3653\303\302\277\231)j\031\243\241\264\277\272\330~\277\272\204\303\277\034\250\323\340K\364\244?\367\001`\"\237\006\266?@\034J\232\277\360\267\277J7C\013 \350\247\277v\247\321\244\323\034\240?~\221\002\275\221\227\245?\217.&$/\342\302\277\376w\230F_\014\272?F\340\302\251\207\010\265\277i{`\3677\211\270?I\260JD\320\302\260?\211\361\203\0372\007\314\277\305\\L\246\216`\306?\267\207\203sB\331\313?\027:\\\242\227\326g?C`9\021\311\354\245?6\t\350G\177\026\203\277\"^\016\353\276\271\252?G\3605\264V^\253?u\265iJ \235\271\277\030}j\355\3658\307?6\255x\263Q0\201?\205\000G?D\253\264?F\222\216\247\305F\261\277\261Ua\274\331\243\212?-\336\203\013K\203\225?{\032\035\0136\'\266?\313\334\221L\372^\304?\344z\216\365 Vr\277\305x{\302\033r\230\277I\324\337[\255_\303\277u\316\211*\016^\262?x\230\333y\350o\273\277\345\'m\0240\307\263?y\240\245\223\366\205\177?_\351\035`\025?x?v\315#\226\2335\261?\026upS\212\212\223\277tn\'\177\036\016\275\277\222\2340c\016\350\277?\357\037\361\277]\370\251\277\310\371+\253I\321\322\277\007$\222\354?\360\250\277\256a\373\223\263:\240\277U\217\326\206K\253\226?\2141)\223\nhy?\"\211t\362\253\237\377\276\333CT\245\325\223\237?U3\n\323\366\322\323\277\017\276\276\273y\204\234?\210\244\\\245\213\362\255?\251?\336M[E\270?.1n\243{\030\253\277\270\261Lxq>\241\277\276\263\005\320\372\'\267\277Q\007\246\353\212\367\231?\357\312\3764\376\332\261?\270\374\362u}\255\242?\371\370}\350\233\215\307\277\334@`\226J@\270?\237\201\004We\301\240\277n\370\037\374\366/\312\277\363\313\316_ =\260?+\202\371\342\342R\205\277L\362\347\312\226\311\231?\201\203#$(^\300\277qa\224\364\357\270\257?\033\226\360\274ke\255\277\357\316\207d\363\301\201?\201\2354<\344\234\261\2771\024?\310\223S\311?e\320T\262\022\276\266?\375i\003\334\005\305\201\277|:\261q\361\356\300?\334\277\313\247\340\313\223?\245[@\214Q\030\252\277\360\376\342\300\204&\267?\321>\272\003}\203\270\277\007\353\246,m\267\221\277p^REJ)\303?\320\250O2\2768\234?2\204\013\250\255H\255?^\337=\360\037\246\270?\233\311\370\340\3313\327\277\301\274;\036\321\243\270?K\301R;\330\336p?6\353<\230\252\370\206?S\322]\373\321\235\231?\211\312G\n\300\247\250\277\214\032\035\361\000\236\301\277\021!\367\215\377\217\300?\206\357\244\014\214\201\273\277\331\204\270R\315\302\214?\206\244\013\216\010S\242?aAj\375\337\347\224?\362\266\034~7\270\310?\261uTv\022\r\214?\324\250 \261\010m\265\277W\200\243\245;\345\216?\330P\231\226\0212\270\277\267\021\330D\207\223\312\277\"\235p\035j\003\310\277\370X9_\373\221\271\277\006\2351[<+\247?\344VU\t?\364\265\277+1e\036o\262\232?\317\013\327\001\225;\250\277\023%N\355\337Y\264?\013\372\200q\305\235\275\277\206\347\267\201\217\315\300?;\345\366_\236\002\263\277\357\020[\r\270\222\253\277\357\376\223\302\231[~?O\314\254q\252F\302\277\032|\307\250\326\362\302?\263\367\221\211\007 \251?\377\014T}\362\337\247?\373\266\024\360`\303\263\277I\227?e;\276\250\277,3\006w\r\303\250\277\325/)\262A\017\255\277J\215\353\366T%\273?\272\303\235\260\342O\235\277\371\325]\205_\365\207\277v\324\221\215\004i\223\277\2465aA\374\362\240?t\213\022_$\214\221?\373~\202\244J\003\223?\322\230\243\030%\034\306\277rB!I\367\367\273\277WjarU{\266?\220\314\316yb\222r\277L\225^+\241\021\222?\027\033\346\000(\226\227\277\361\336?\265\233\025\255?:R\233c\326\005\226\277\005 )\252\277\005~\277\266-\310\243:\017\224?\265k\210\323Sp\263?\016S\025\266\236\232\311?p\300\265O\027\244\254\277\254\272z\332q\311\251\277\206\363\026\303\365\312\266\277\032\002&,f\016\267\277:\253P\333M\'\240?K]\267p\375\245\311\277\237\236\337\227>F\250\277\201QK9\232\317\206\277\001\025\0200x\207\266\277\275~H\345\327)\250\277\241\306k]\031\322u\277\016\301\327\036\303{\233\277\230\306KAot\260\277\3328\333Bt[\266?Q\274\202\034\360\307\265?_#-\366)>\222\277\330\227,1\341\306\262?\014\"\367\t\014\334\207\277\243\307\355Up\246\236?\263)\377Sj\240\266?\032\370\236`@Dv\277\214\177\235\321\241\211~?1\303\325\344\021]^\277\003G\032\221\303A\305?\326\367A\026\024\326\243\2777GO\214\312\370\215?/2\255\2409\000\220\277\205YDBZ-\271\277\253\3636\270\271P\260\277xe>\nP\320l\277\313\240A\315\240\341\260?(\342\020\254OE\305\277\253\336\327\021\350<\273\277\r\257\375\366\371_\231\2772\223\376\010\017nt?\237\342\352+>\014\273?\024\236u\376\255\017\225?\307R;\005@\214\301\277\367*\230;\n\342u\277\200\343\243Z\364\242\224?\372\376\234\236/C\243?\003~H(ap\265\277\313g\270U\020\303\205?e2\207\347Wh\252?\315\361\2675\202\330\300?\372\315\220+\0234s?\226p;\306\262\t\262?|*\250\263\365\254\274?\377\220\274\327g\263\274\277\'\237\334w\365{\264\277\213\317\357v\314\213\260\277\216\340i\317\0135\214?\255\330R\245\230\346\244?\240\02262b\007\201\2779#C\370b\353\271?\2724\213\366\242H\245?\270/\373\203\032\032\233\277~Xg\'\306_\270\277\\6\244\204\373b\205?\253)\3539\356u\256\277r8\232\305\361\257\242\277G\272\021\0168\263\224\277x|\010\244\340k\210\277\234\274\325\212r\002\253\277\353\007\254\247\030uj?(gC\247\332\203\315\277\007\272n\0364i\227?:k\2236\201\334\273\277\267\363\026+0^\313?\003\350\375\363\3539n\277X\020\200\320\037u\272?\270`\344\303U8\262\277#\374y\243\333\340\223?7>\301\014\023\027\306?\025\265}9}\347\267?\227\337{\314\372\002\302\277\277~\313U\252&\256?\250U>\332\336\010\243?\331\376\376Ck\237\211?\335\037\374\027\333\352\243\277?\030(\335\313;}\277\023\256\003q\312\364\270\277\016\351\326L\234b\254?\361lOP\307\213\303\277\201\244{ \037\207\262?\221Lp\255h\372\265?\274\242\235N_\276\257\277\303~-\"`j\222?S\275\263Y\215\311\300?\377[)o\352\002\251?4\207\351\313\3251\213?\n\311\215\232ip\273?\372y2\375\310\354&\277^CZ\304\352\351\307?\024\322N\022\343\334\265\277\024\337\002\347\364\346\261\277\n\315\222\033y.\264\277\274YZ4\371\302\250?R\177FX\271\270\250\277\317\336\324\304\2010\277?,U\373\211\177\204\264\277\r\335\340;\303\343\235\277Bg\003-)\032\276?iD\301u:-\302\277\315\207\355\3039*\303?^\016#\307\374\375\265?G\036\216\357jA\242\277<\347BV\021\331\251?\361:T\3759M\024\277\273=\266\252\356\302\312\277q\223TE\'\310\324?\231^\230\271J\364\240?r\354\007#\301\027\263?\031gS\025\276\206\317?\021I\226\201\001w\267\277\0278G\3716v\300?\361\313\310\337@\272\236\277\3504\271\2463?\273\277\026\023\242:\272\230\263?\201\326`hW]\222?\311|\t]\215\321\261\277\rwVeQ\375\306?Tb\247\322tq\250\277E=lz\262\253\267\277\351\033(\213\310+\267?vE\226+x\237\222?\r\336Y\320\321\261\235\277+\007\335v!\213\214\277\350\027\013\nm\271\277\277\210\327y\215\371\361\265?r\013*Po\335\202?\030>b\2325\360\235?\r\205y\t\3366\260?m\037V\201\374f\272?\0258\323\010\0019\310?K\365EF\352\220\277?0FJ\016\306\260\231\277\217_v\307}(\236\277QF\225\334\237\226\324\277\361\341is@\230\246?}HO\277\367,\230\277\030\352\327c\212\342\273\277\272!\266.\303\t\260?\322\264\252=\215\004\233?\n\033\223U\364\203\273\277\302\027\332\010\374\223\272\277\257F|\301k\213\270\277&\242dZ\363x\252\277q\023\355x\354o\272\277\363H\261\312#\035\270?E!,,\300\377w?\343&\222\262Ap\311?\235\'n\300\233#\252?\335\360x\375\313\224\306\277@\347\234P\222\237u\277\205\".\246-i\213\277\260\016\265\006\374\205\262?\200?z\037r\320~\277\t\035\026\265\253\356\246\277\216\224\371\272\235k\277?\350\365v\270\025J\311?\254\207\3522\364Lr\277z\030\320{\236\n\222\277Z\005)\022>\327\245\277\204\354\362\214\375r\215?\352\253\374\274z&\252\277\304\037l\245\006\223x?J\227\235\221\340\377H?\265\200\252m\033M\201?A\236\262\371\332\311\301\277\232QE\347\351\341\302?\"\253\003\265\364T\245\277I5S\350\237\264v?\002\n\345F\327\355l?\262\275\211\000\0200\217\277\273InZ\361\334\270?\220R\"\376\361/\263\277\347\200,g\307\322\316?c\001\000\327\265\252\241\277\360\374\361X!\037\206?\250S\352i\261\237\266\277\270\305\3261\376\211\265\277\300\237U\325H\266\267\277+\303\232\200\3129\311\277\001\363\354\272e\372\302?\003\177\202p\326\267\301?\277\256#\252}9\233?&\361;\026\251\334\243?/\177FgCk\220\277\327\210\001n%\340N\277\215\217\001\302\235O\255\2778$\032^\217<\262?2+\202\n\0216\221?4\200\271I<\\\252?8-S\221\241\022\233?\007\373\212]\211f\263\277\034\004\3500\307\202\231\277\326\\>js\315\257\277\205\363\363X\304\315\301\277\325\353\254\373\202\360\253\277\025\010\313l\300\232\270?\277\243\030\226:^\265?\0074\357\247\211\016\301\277 \273\364\252\242?\263\277\204~\\4= \242\277\224#\3103\341G\263?\3117~\221>K\241?\363\323d[D\253\267\277\275\330!\031\2721\200\277d\234\272\267\274\345\271?5:\003\227z\243X?)\241\327*\"\245\247?\246\272\236\023*\207\241\277N\004\325ZQ\016\255\277\234\325\363\3676\332\260\277O\177|\014\030m\257?<\201\261\030\227E\272\277S\360!\007C\374\244?\377\306{\327k\266\223\277\r\255\027\236\316\025\320?\376\230 \347\024_\276\277\207C4\351P#\225\277\001\360U\232\232 \277\277\337-\276\017\257\221\261\277\211\026\245g\361m\266?\255~&\310\356H\257?-\024\230Z\004\004\267\2779*o\010\224J\231?\0179\"zD\323\226\277\261$\212\300\356\004\231?\246\022\257f\010\336\250\277\202\001\300\037\363\262\260\277q\312\014\232\341\'\275?6sKT3\000\251\277d\320B\331\005\013\255\277\027%r.<\374\201?q\365\343C\262=m?]f\204\034\020\360\261\277s\222\335\037\251\376\264\277bz\237\265\222\010\227?\r\3144\026\224\305\300?-\244BDv\206G\277ia\251w\254y\237\277w?\374\n4}\300?\325\226\371p\023>\307\277<\262I4q\217\270\277\226\"\'\031\377X\260?_a\246\341n\357\202?\226\314\243\265\276Q\234\277\231\n\240\364\\\206\310?\n%\034j7\324\305\2774\020\276\020\306\'\313\277\302(\\\304A@\244?A\334\002\351\207j\251?\235\337\315\033\223v\204?\276%\021\355\355s\261\277U7A\026\265.\251\277\303\022R\207\325\271\256?z\036\001M)B\320\277H\266N\223T]\227?\253\324x6\312\200\266?\035;\343.\321\022\303?v\366^\206\356S\261?\326\001\210%z\354\313\277\251X|\330\262o\245?\254\355{\035\200\360\321?\336B!\362C\250\242\277lc\265wO\244\215?\036\243\357\204\037q\221?A\tr\025T\235\204?\345\314v\026\260\204\265\277\'\226J\344+M\301\2779\361\243!\351\350\317\2772\240\032\207l\371\237?f\225\373T+\220\246\277\324\371\n\036\2401\234\277I\201\237\220\207[z\277\300\222\354\252\212.\202\277\322=\300\361Kr\263\277\"dM\310@\307\312?\031Q\350\236\025pb\277\n\244\356U\260V\220?\315m\260\361h\257\311?\002\354\350\375\277\330\244?\352,z\367\000T\266\277\004,+\345\340\251\301?/\014_\010\325\306\304\277\236H,\023\232(\264\277\227L G\271\200\251\277\352\037}\021D\320\250\277\373;!U\351\304\270\277\241\246\367\336\203N\240?\3478#\022\036\342\252?@j4\341MN\255?\005\207\032\324\307\233\264?\336\342\235\271\242W\276\277\355\000\315\364D\231\221\277E\005c\367\2514\261?H\223\215\311\366\262\247?\213\261\246T\010\337\256?)\023>F\271\256\243?ON\025\220\004E\256?\245\307\365h]\362\241?\313}A\307\021 \313?U\017\277\216\274:\250?]\014\277\357\271\275p\277\221l\376\253\343A\257?\276\003\351\240.\020\275\277\020z\265\267px\226?\272O&w92\261?m\230\307@\321\220\251?=\310y\201,5\304?A\244\272\375\245\351\263\277J[\237\311o\366\242\277\260\023\377\212\325\203\235?\226\016\207\336D\363Q?y8\207v\212Y\263?\271\234\211)\020&v?\245;:\271\353t0\277\363N\314\251c\010\273\277f~\311(\254\214\273?`\355\217\263\271\023\262?\323\241\201\211vX\244?xC\240\246\225\301x?\320\315\263@4\036\242?\253\262\240\275\242N\257?\203\325\372>o\320\306?\274\323\277\332\273\017\302\277\262\206\321\326\310U\320?\257\\u\037\261`\246\277q\241\230\366\207\250\260?K\341\300\273\221R\244\277 \035\016\224\207G\220?O\032R+m\302\251?\rF\003\224\241\244\262\277>(X\255\n\005\315\277\2652\216\207\304:\240\277+[\211\200a\207\224?\211\277\337\214@V\301\277\332\357\032\333\205&\240?\360\3038RB\375\261?\002\007\260\321\376W\245?\203\022D\366\032O\332\277\320\246M\264\347\372\253?O)\273\374\246C\247?\320\013\263\372}f\323?\320}\204\321 \311\276\277v\262:*\246\335\264?\205U\304JX\327\302\277\333\251>\334\203\321\275?\227x\037X\027o\277\277M\237+\257\026O\243?\032\355\'\2414\244\242?\207\327\023\\\355\312\247?\001l8J\273;\265\277\014\300)\032\005W\271?\025\275=\340i\363\241?\273\200\315\321\202\274~?C\276)q\274\347\253?;\027\231\247L\261\301?\355>W\355\211\020\257?F\300\'\tw\033\302?\341\004\261\224\252\223\301\277U\003k.\031G\320\277\321F\211\224G\353\235\277\353\375\252\373\315\210\214?\206\321`DTi\226\277\023\335\261N\375\266\300\277%\335\271^\245\004\231\277\305Q\333\232\232\244\266\277`\004%\207~\177\267?%\001\325z\202o\241\277\317},\203\274\244\250\277\362\336s\313z7\276?!\027\274g\323\212\246\277\360\216kg/\313\212\277\361`/\342\305@\266\277Av\0132\232\305\263\277,\301*pu\213\277?\005~\355\234\032\272\232?Y\267J$&\215\300?\237Q\010\026Z\220\261\277\240\335\001\310x\271\253\277\r\370\315\016a\363\272\277HK,\365\275\331\267?\273\000\307\036\314\020j?\203\016\315\376L\265\261\277\331\200\373\305[X\266\277M\237E\313\256*\232\277\376\234\313\244\354\253w?V\024\363b\325\021\303\277\023\rE\321\025\\\300?<\200r\353\316\035\244\277\315\200|\335\267\265\202?\264-Z\n\210]\223\277B\"\3700\205\277\220?\253\352\263\241\231(\245?\344\230\272!\017\031\261?m\200\367\032\362R\240?Y\3663\235\026\253\305?\241\370J\032\234r\252?M\177\253;F\246\275\277\003\265,\323q\222\245?\362\357\356un\260\304\277N;\017\005\240\255\262\277\300_\365x<(\304?\223\203\341\3018\327\214\277\316\275 (97\256?\317\301\202\266\271\014\261\277\356\374\367q\246\374\255?N\376Od\020K\233\277\270B\207\272K\010\230?h\\ms4D\307?\024z\014\253\3526\263\277\240E\265\235p\240\202\277b(\227\377\3252\317?\2301Q\242\221\261\254?\231E7\260\352\006\261\27730M\266\2546\312\277!u\242\005b\252\265\277\305\016&\337\220\237\244?\ntI\31472\246?\323G\212\243\031\213\243?\213\002\367\305\204\\\277\277[\001\204k\256\302\203?+\345\277\357\235\326s?\370w\014\334\276\233\320?\204F\217)\315\007\236\277\020\267\177:\265\353\264\277A\212\203@@\000\231\277\300Au{\030\215\264?{b\340\007\315h\202?\366/\323]b\302\310?\342L\3007\272\363\324\277f\226\233d[}\226?z\225Zo./\301?\361\250\245:\010v\310\277\312\373\014q\302\360\273?\315\277\324F\006\r\230?\245\207\'\227\215^\261?\216\254\366qB\310\254\277\340jGu\353\275\255\2779\232^\212}\371\262\277\341\224%k\371*\277\277R\212\rX:\310\232?V\325\235E\341p\246\277\311\027\234\332\022\247\272?\232\'\351\364\373\334\311?}t\217@\360\032\220?\263\256\2058\213\000\263\277\303\242\006\2632\351\203\277\365\244Bi\367f\277?\325\246.\303\366\356\272?L.\272^x\356\202\277\312\212\330-\005\362\242?\232\357\354\240\014?\201?\364\001*_\025\303\264?\007\215VI\226\273\272\277\t\357\231%\251\331\220?\242\253c\226\222\336\304\277\375\010=\030J\031\211?\365\206c\n\343%\254\277\323\303Q!\254/\245\277BWtr\366\325\275?Fk\272\345\307\272\247?\3173\357\031\013\336\267?-l?&%\033\262?\0328#H\350\267\260?i\024G\265N\276\271?3Z\251\235\307o\304\277\323\271\263\357h]\241\277J\230\237@e]\262\277\017=\006\323\365\273\256?\010\257J\320\262\177\303?`\267\3252v\304\271\277\2355\350s\245r\306\277n\251\260\237\305\306\261\277\211L\341\250\352\351\257\277:\241\366;\0366\317?\0208\224\305\326C\276\277\2659\360\373*\377\244\277\276\360]\005\275\303\215?\314\262\025\017\376\243\272\277?\211\"^\201\231\304\277\002O\351\365]_\243?_\032A!\357R\313?5\ny\035\177E\275\277\326\262H\321\0357\300?v0\236\223\177t\301?\3743n.r\261\322?\0031\236\311u;\246\277z\245 Q2\277\311\277\3208c\331\335\235\275\277Gt^b\014c\277\277\327v\020\177\274q\300\277%\2758\371M+\315\277\202\257|\005\254\006\317?\343T\260=\002\333\234?\256\272\326\3674G\235\277\023\340\351\267\233\234\304?\357\377);\007B\251\277v\257\243\037o\240\260\2772Md[\232p\260?\245\336v\255\306\347\322\277S\246\035\"c\347z?$M)\263\215\357\300?$7\2542\210\032\317\277`\306s\273\027\335\276?ut\221\323@n\220\277\201\263\0029&h\302\277\242\200\032\277U\304\214\277\244pE\r\337T\266\277\214\374b>\331\240\265\277\203\336\234\033We\275\277\262\346-\274 \003\220\277\334Z\306\007|\317\261?ndL\337\224Y\262?=\377\346p\374\312\261?5?\246\322\304\214j\277fxE\267\375\207\306?\242\223\241|T\374\314\277X\221\253\311\364D\260?\374\203\274\022\312\242\241?i\362\263c\2356\246\277\327\320\242)#\373\307?\001o\364\017\226n\255\277\273\321\367a\034\037\245\277\t\273-Cb\244\255?\000)3\0136\322z\277\321\206c\351\332\204\262\277\021\227\356^\230\341\245?\027\322hBK]\241\277\025\332\273o\037\370\243\277\370n\025\243~\225\247?4t*h<\373\267?~\277\373\213b*\243?\326\007;\324 A\261?\266>[*\340\310\251?K\0174\371\213\315\310?\252\007\325\233@i\272\277M\255\253\346\264\225\265\277\243\357\260\252\266\013\303\277\206\206\376\344\346l\264?q7\233\352;\336\310\277\261A\205#\030\207\310?\230\267\325\261\025\t\252?H\214\333\265$\005\227?\324w\225\004\327A\254?|\375.7\326\366\307?x%\227\250\000X\256\277\266\327\214V\003\201\241?F\240n^\214\344\264?\266t\270\322\023\261\264\277\356/\207\326\311\205\264\277\317\205+\211U\367\262?2\376\240~C\370\311?\327\251\234\307a7\250?3\343\363\347b\217\241\277\246\364\326\317e\310\245?\301t\316\3060%\310\277\271\267j\241\251\352\225\277\370\273Z\312\205\357\254?\212\261cm\250\336\271?\205\3052\013\023h\243?\253\357\246\037@\326\240\277V\362)\317vn\207?y\025zD(\211\322\277\256\241y\343\005\376\202?U8\316\331\362\261\254\277\327\2777\241\314S\303\277|UQ\361\351\326\223\277\215\253\262\027H\024\324?U(\236\231\216\366\273?\004\230h\245X\360\326\277\016\242`\273\273\007\223\277kD\231.!\220x?\305+\034\352\272\371\263\277\"\202u\210Q\243\300?\022G\245\240-i\272?pu\271\304wf\244\277\206n\201[_T\242\277\335\014\367V\240\035\275\277\367\262\001\230-s\262?\305\025v8\021\215\301?\330\344\376\251\311s\217\277\251\370\356{y\261\270?\255C)\207\263S\233?\217\230]\334\303\347\244\277\027M\350\2764\265\233\277\223\\\034\002\2175\265?\243X\343C1\002\303\277j\243\362\017\030\007\227?\375\364\364\260\232\221\264?\3653\350\033p\360\251?M\226\244\023\317\357\301\277\374\200\367m\2610\231\277\270\230\272\226\262A\301?\267^@\317j\027\277\277\001;\204J\253\037\212?\300Af\017\006\314`?X\323p\273\327:\245?<\362\374\374\202F\316?\033@-Q\224\303\214\277\025\205k\025\t\277\255?R>g\322M\214\235?\240\372)S\030j\234\277\023\275\315U|\210\200\277\211&\0104r \253?\207\244\"u\252\272\263?4L\342\247\302\302\267\277\034z+\31647\260\277K\220\252\017bn\314\277\037\272\223f\345\314\241\277R\321\201\246\007M\266?`\310\0274`Y\307??\315Nj\211\365\274\277oT\257\222\001\357\234?\204\252\231b1\267\267\277j<T\205\237T\257\277\271\3535J\232j\265?\200b\371\n:\343\240?\343\327V\241\304\217\236?^\330%\313\215\344j?<U\267\321\314\366\240\277\311\010\253\2058c\221?`V\034\333\271\247\215\277a\257\275\216\315\244\242?WFvW\375\353\270\277\335\000\224\371h\337\306?/\230\220\261\014\366\303?\006\232\227,n\367\254\277\000k*\237\237\201\240\277\032\225\237\365\213*\204\277v\241\3440\271\261\275?5\336\326w\021\266x\277/\017\235E\335Pr\277\244\r\nqA\376\273\277\355\344c\213\235\342\261?\237hq\257\223?\305?\303i\275/E\266\261?\364\327\037k\336\000\242\277\366\362\003G]\033\267?\211\321\375\217{_\274\277%\211i\350\221\020\307\277l\243\003s\351\003\264?\327*[\034\245!\240?\371\310\337b\t\303\235?_\323\270jc\311\260?\\1\337u\363\360\277\277\026\250\2125o\030\267?\221\326\204\344\306\212\322\277\3473_\264\240\315\230?\302K\232\225\260V\222?\n$\003\336x\022\303\277\233[\327\272\234\334\267\277x\364z\3506\203\240?\232\024\274g\324\003\275\277QE\244\361\311\351\334?V\372\375\254\261\365\257?\325I\210\370D\035\263\277\3664O\321F\331\250\277x\330\\\2450\237\272\277\356*\207\221\031\222\221\277\251\230*\225\333\320\257?p\274\233\034\252\331\271\277\014\001\330\276\242\341\253\277\345\312\355\247\212\377\255\277\264\337\035`i\376\272?\006p\352?\006p\254\277\346;\025^\026\222\220\277o\251\262\226\014x\247\277X\372\017\270l\235\312?&\013K\\\245\236\263?\340\221+`6\022\217? T\023\243\360\203\307?\217\201\364\227(\375\277\277\270p\267%s\267\277\277\273\362&\231\341\236\260\277\003>\030\332\265\376\274?=\235\343\033\036\364\212?\367\334\031\327\334\335\303\277\226\2573P\217\235K?2>\273\377<\024\220?\204\030\240URv\265\277\307jc\350\013\270\311?\346\026\255\340\267\350\240?\225\335\374\217\206\330\205?\230IP\265!\321\214\277k\340&(\021{\302\277r\350\367%\223\242\307\277\336\021\1773r@\243?\254\341\317\2050\314\200\277<\352%\277\231t\273?\017\207g\205\362\220\221?\254\223\372\260\025\307\252?\017B\354\375\337x\301\277r\032\316\371\213\326H?J;\271\027\313\274\215?\334\300\373\351\243\204\273\277\236\265\203FUt\322?B\205_\321d\255\260? \310\311\006\252L\304\277\007`\323\332GK\301\277iK\332\236\264>\260\277J\234\036\271\374\r\241?\372\330\272^\205\025\260?\265X\027\372\013r\302\277\210E\003\200\375I\277\277\266!qK\246\313\256?\302?/\214\305\234\276?\213\367\001Y*s\241?\350\243\021)Qj\266?\3202XSM\217\245\277\020$m\216\201\211\257\277\242\007\225unWy?\351\3157\007*?\273\277\351\225w\257i*\273\277`&\031%\370g\304?\302\003\334\247\202\277\202?\205\247TF\250-\242\277\026]\022\276\247\340\304\277e\352(\244 y\253?\037\202E\345\023C\233\277zT/3\364\362\247\277lK\212\324\374\355\221?j\263\334\023\272v\261?\347t@\004\302\304\237\277\261\254sJ\\\305\251\277\301\034\306\207\305\031\275?\203\306wg*\226e\277\261\327\225\370J\005\242\277\020\234\026\363\336\332\241\277\324\320\206\207!\216\267\277\377>\204Nh\205\264?ZG\334\273\215\366\252?\"\021\251\002\363\323\212\277\205\316\370\327\210\324\267?\230#JDV@\325\277\252\n\220\"\224=\223\277>\330\310\366J\246\271?D\321|\256\200A\271\277\311\216\247\304E\036\323?]\304\363\333\251L\266\277\255\324\351\307\330\303\266\277\345\355\237\230\277\014\267\277/\247\256\364\3039\265\277Wp\020=\246\357\206\277v\313\246\267!D\260?\253\016\271\004f\361\311?;P\377\006=\223\246?\000\034\367\301\377\244\223?\224\0318\020\312\335\326\277\030\263\032\007\277~\223\277X\337\026\313b\371\252\277x\200l\201,\214\200?R:QH\266O\301?|^\204SS\321\260?6\024V0\027\251\253?\2326\261\322\252\236\264?\232\254\"#\367F\253\277\022\033.\342\325Wd?\177i1\205\233\352\243\277\226\226\362Z\235W\305\277\330l\205\2639\353\246?\244\331(\207N\017\244\277-\215\372\204\207%\305\277\275:\260Y\276X\240\277x@\306\326\237\'\211?nc\307-\234\250\301?\226\343I\3538E\305?\266_@\343H\327\215?K\237\221Vs\271\233\277\302\365\006\211\367b\245\277\030b\214\334\201*\276\277\211\343N\306\350\326\242\277\212\2633\276sL\233?\025\021\377\335\330|\312?\322@\001D&M\232\277S\321jS\tD\301\2772k*T\371F\241\277.:\266\310\314\r\251\2773\177\331\330z\237\262?\212\374\261\314\016\342\274\277\325iTK\363\021\301?\021\212\211\300\201\001d?\3416S\355\031\021\257\277\1770\310\227\276A\260\277\036P\231\344\014k\244?\327\313\375\357\207z\265\277?m\023\370\006\262\217\277\332I`S\337y\273?%\313t\032\254\333\274?\027\224\217\232D\006\272\277\210@\226\357vL\304\277t\264\2514d\036\241\277k\td:5\354\203\277\236\322\227u\214%\206?5`\351;\031\356\320?E\323\232\036A\341\213\277t;A\t\212\006\300\277\355\364w\353\340\022\261?\344\241\276>\364\225\202?\355\017\030%h\264\264?\204\203#m>\246\253\277\211\355{\310\345\376\301\277\351\262\304>\256\246\260?\347\255m\3345\033\267\277\304\037\362/\260\311\313\277\216\371\213\3068\004\202?\233\250\2158\252O\306?\214\364\347\214\'\220\246?\316I\212(\242?\262\277\215\230\372\250\216\301\256\277\036ZTm\342\261\241\277\362V\007\355<\225\302\277\212g!\022\035g\257?\036\270\351\035Z\260r\277\372.\364e^\t\243?\367\200\305gZd\276?\274\324\366\247\312\337\207\277\024\343H\360\311q\273\277\n\025\023S\241\026\320?\243Q\236\013\210\016\236\277~\301KB\350\363\255\277\240\017\214\375\030W\304\277B\265\343\000S-\266\277\231\tt\210\272\013\230\277\014\270Xh\250\"\300?\"\254\371C\345O\256?\266\353n\3013\220\261\277\236 kR\305\324\262\277\332\251b\025\304@\261?\335I\252OU_\314\27748Z\223\255\232\201\277\335\311\232\006K\334\264?\343\230\235Q\312c\271?\245\255f\026\3778\177?Y\212\203\353\244\302\267\277Q\266SO\023\357\276?\362\334\256\240\311\233\324\277`8o\305\233\310Q??@\344\313\226\323\227\277ohp\332\321A\263?\006.q\365\263\236\241\277\206\300XD\276\211\243\277jr)\253\331\003\202\277\236\376\336\365v\034\242\277\252\201\313b\352R\230\277O\363\207\206r}\256\277\262\342\230\312\001\377\303?F>\007\202\347\204\266\277\235\215v\356\376\277\316?/\324\000\177Y\265t?\332\257\203*@-\250\277Y\334\225^\370\225\270\277\247\333]\334\035\375\275\277\022=Z\243K\350\301\277\304\240g\365\215\014\264\277\206\3431^\302\235\225?^P\273]\017\007\260?\330K&\261\270\037\266\277\372c\202\377\267\034\300\277\325\237\257\334\343\375t?\323\026n\036\220\211\273\277\\3\244427\256\277w{\235\241\2178\320?s\004g:\3411\273\277\354\221\035\272\224\027\276?\363\217a\257\231\346\246\277\362:\347\343@\251\231?\342\231\330\244\216\255\235?R\325=\2443\201\222\2771<\235\322\263a\231\277\310\257\335\272\246\371\261\277\205|\375\207b8\302\277\214\\\372*7\034\246\277\354@hS\005\014\242\277R\372W\354c\230a?\312m\234\242\266]}?A\216\224\263L>\266?9\025\220\275\321q\241\277\361?\036\336a\316\302?j\341=\213M\017\246?!\230-\265\376\264r?\201yit55\262\277\301l\253\270?u\237\277\241\024E\306\273\353\221\277\217\335\246P\370\365\253?Z\310\243\002\373%\223\277\221OzG\n\264\276\277\336\r7>\310\317\262?\3144(\241\202\376\231?\265[\360\006*[\246\277\310\347-\345\313\351\267?\306 \024\305\345Fw\277\203\034)I\353\010\320\277E\326\233\204\313\210\241\277OX\334\277~x\274?d\326+\345\221\244\251?\035&0\261&\254\271\277\243\212\210\2059\314\265?X\021\273\277\003$\250\277\027\214\230#\027\t\250?f\314\007Z\224\351\255\277\357\036\221\243\007\262\217\277\300\374\233\346\330\020\237?\247_\217\362\025\354\255?\260\377\007\260\222*\252\277\207\360\245\315\205W\234?[7\321c\003\001\264\277\370c\325\331\223g\246\277\262Z8Y\'i\304?c\224Y\241\367G\264?)1\222\021\231\355\243\277f\212\230hR\274\232?\224c\315Tc\262\304\277(D\031\256E\371\310\277:Rbp6\320\221?L\0348\340\333\226\304?\0262\202\322\324_\245?\327\326WA\222My?P0\243\\\007\n\264\277\301\000F\316P\336\231?\234\301\322\003\034<\323?\261lM(w\367\302\277\347\026|A\323\023\277?a{\244\204\210\201\227\277\312!\256\206\245\242\232?\321\340\031\327V\\\303?\"ld\007>\257\241\277\\\230\222\001`\366\264\277\316/z\365\014>\200?;\350\024)\004j\245?\310\017\222<58\250?\274\325\321\232\356\r\253\277\332_\311\243e\375\264?\344\'M*\236\233\260\277\265]\022\204\276\262\211?\320C.\333\037;\250?\374IHa\001|\246\277\200\014\376T\304n\262\277v\001J\370e\272\301\277\274\334@Q\017\247\203?oS\026_z\272\246?F\317\032\002?=\251\277\r\037\010\244\301R\207\277\250\270\303\234_\351\247\277\203\007G\033yI\325?\025\277g\341T\300\257?\321\233N\026\275\375\250\277\013,\000ZF\025\311\277x[\346\236\372\014\257\277\221\350\340\326\337\271\226\277m\230vcX\275\245\277\260/\343{\341u\263\277X\035\"\317o\341w\277\317<\306\210\035\324\270?\275\0321s\034\376\272?\253O\316;\265\351\331\277H\302\221yl\314\273\277\205\320\331\272\224h\261?\310g\217\212\244k\242\277\354\306t\003\201\362\271?\236B9\013jX\321?\273\237\034@1U\301\277\356&\303\273c\304\246\277>D\200P\267\265G?;<\215\n\247\005\300\277\223\021[#\372\253s?&\357\324\032~\375\306\277\32712\250f\340\231?\'M\315;r\265\242?V\353 V\n\206\257\2776\363$\r\235\001\220?\257\215\326W\254\320~\277\373\236A\344\243\223\301\277\017\325\271\372\233\027\212\277\305\335\037\343n\302\266?M\324\023\265\010\000\313?i7\343\371\371\007\270\277\340\275\310\205\"zm?\350t\344q\261\350\241?\037\311\275\211zU\231?\365$\207C\334}\264\277\356r\222\245\317bz?\234\252_8\216\022\254?\242\003\221$\227\355\275\277w\235\240!I\364\215?dt\333\'\221/\241\277\224\351m\032\241\356\317?Q\253w\004[H\260\277\263v\273\203v\210\240\277\254\341\236e\376{\245\277S\255\254\025\007\352\203\277\025v\005\036.\220\265\277\321\206\277\361\262\313x\277&2\327\335\030\r\225\277\264\371\017^3\314\304?\351N\274;\033\205\267\277m)\210\235\362\327\263?\342EM\014\241N\271\2778\365P\213\345\222\230?QB`\324iD\231?\272Jq\244yz\316?\024GV\271\276*\244?\034b&\232\334\375\301\277\337F\333\207.\016\267\2771\010Ht\216\214\266?\331\024\301\267m\344\265\277 +\237\362\r\307\241?\227\274\202\366CT\245\277\203\307\nF\264\224\242?\323\304~aR\212\300\277\340\036\031\324/\016\272\277\025Z`\276Vr\232\277K\240\020\004\356\\\255?\204<\350\355\330\034\263\277\217F\327\273\264\254\316?/\036j\207\034I\266\277O\265\321Y\231\233\200\277\023)+f\367\334\272\277Y\034\326I\222L\277?f\211.\332.o\225\277@\177+G\027\3002?&\\\270\371r\315\263\277Ddv\243\231\242\205?.I\244\004\273\376\253?\332\301\324it\251\253\277\276\037\034m\345\367\262?\212\214\331\021\221\365\223\277\231\310\3030i\257\263\277x)\360vZ\226\271?\370\205OV\006\213~?\310\356\315\324\003\265\313?\274Mrwe\t\264\277k,\236\261u\331\263\277\242ep.\314+\256?B\362\263\367\006\376\220\277s\252\361\234\0038\322\277}\315\213\347\375\210\241?\366\005\034?{\024\236\277\244\356\225\017S\227\257\277\312\025\256(5\356\242\277^b)\346c\033\241\277s\207h+\234\230\220?_\010&\201\213\314\224?n\345\210~\r\010V?3\340\217U\250\251\224?oC\247Z3\362\313\277\207vw\362\233\212\310?\305o\242\007~\355p\277\263\343$d\254\320\262?~H9\314e\034\251\277\2322BK\264\261\304?\355>.\211\315S\261\277\272V\334\324\364\004\216?C\2209\204\007\n\225?\030\037\306b\205\376\244?\r:@\342\210\037\266\277\221]~\037\305\310\263?\235\033\021f\361L\207\277\272\313\376H\2115\263?\0203\032\325E\352\300\2775\307+ZS\371\250?p}u7\235\021x\277\000LG\231\305\260\304\277{);\007t\204\261?\316\350a@o\371{?{M\251\320\303\342\232?YV\212\333\2526\250\277WGWy9\216\243\277\225\341\354\247W\213\263?\237}\347D\365`\252?:\024\324\305\205F\276?%\007\226\325Xk\245?\313\302\374\335\265W\257\277\3542]+\272\321\315?\320\004\243p\033\337\260?\207\373@\2172tv?\246\310^\364\245)\320?\235\367.\201@\363\253\277\217\364\311\263Cy\256?p\200e\370<\232\301?\307\221\t#\263\374\300\277\026\210ac\235+\221\277j1s\322Fp\234\277kl\231\242\013N\275?\226/:\0301R\246?\240\026T;\n\002y\277\017\245\375\001x\224\267?\371\350Hu\271\266\317\277\223\315\367-\237\262\302\277\237b#\371\246\316\203\277\216Uk@)p\247? U3g(Z\276?\337QM\005\374\tL?FX\004\035\250\232u?\177\003\270\254P\217\244\277s\010\265.k\256\253?\356\201X\347:\310\264\277j\270\037co\217\206?l\315\233\336\202?\323?\345\001w\2665\255\202\277%j\027\277S\334\304\277\016\305\331=\202\302\305?tF\257\266\210\201\256\277\325\247\311\330\363!\261\277$\024\245` \306\237\277\003a\036![\356\300?\254\346\251\241\353\003\260?\201\230%\014\214\226\203\277c\"\315\260-]\302\277\240\364\3640\t\320\313\2773\377g\352\210\304\252\277\374>J\343\014\014\216\277\301F2\303\2777\327?\001\001\307x\363O\303\277\366En\226~\353\257?\336\362\246\260\364\221\265\277\321\031\313\026Ao\271\277V\321\366)Y\340\221\277H[\262L\260&\301?.\243\252\347Em\267\277=\376\031\344\024}\256?\037@\216SM\357\254?|\241 \221\265\302\301\277m\251\307\355\304\346\253?x\250\036\332\351\333\245?L\260fQm\"\206\277s\322\362\206\344\323\320?J\321\212?-\005\211?\007\353\244\312\226W\303\277\370\311Q\n\311\204\264?+T\223r\242\265\263\277\243}\270\256w\252\203\2771\315s>+\236J\277t/?6\331\206\200?\243[\210\357j\351\267\277<\026S\024+e\226?b\326y\344\360S\220\277{\341`\346_\034\252\277\2354\304\340!\002\242?\225\274_d\210,\240\277\017\247\247\240+T\313\277qM2\364\033\272\244?\030\007\273h\024\347\213?\260w\343\252\202\201\256?\306\005\314\021\367N\307?\026\0273l@\202\235?\337A\336\'\\\010\205?d\331\313>\305)\307\277\361\232\031!\344\336\304\277\354@A\031}\263\263\2775\2769A\362z\305\277\217\000`}8:\276\277\261\025\352\252\2575\253?zFL^\311\252\244\2776\010/\276j\341\302?t\353k\351\325L\245\277\330R\263\343\211o\277?\221\223^v\007\362\271?\026\265j\323\255\234\323\277(\217\210\230NO\274\277y\033]\332\016u\235?\305:\360\306\331Q\277\277\302\353\324\245\346\014\361>\327\027*\273Uo\246?9&\264\233t\026\276?\3210\362W\340\303\304?\264\312V5\217\234\232?\230\017\035\301\001\311\224\277\347s\321\322D\260\320\277\n\206\254\315\335\240\263?o\003\203R\177\211\221?Q\265s\0271?\302\277t\020\250\273`\262\307\277\241\342$\362pd\221?e\2504\322.\361\246?\037\361g\312\216\013\251?9\324\004\313U\025\304\277\317\321\246\005\320u\265?\251-\322\256Q\252\247?\030^\022\033Zb\305\277\026\036D\014d\334\242?5\023\347:-\343\205?\347\320\036\211\0330\301\277\245\334r\222,g\256\277\302\022\372\226\212Q\206\277\244\217\032\201\216\223\241?^\227x\352\270 \233?\207m\223\224\215\033\254\277]\345\332P\367/\211\277h\250\023\326\210\346\265?]\334D\201C\005\241?\376\177\202<\245/\261\277\"\377/\216\335q\255\277\267{\007\2108\r\260\277\017\210\023\205\234:\211?\316\214\372\370\310\\\267?C\251\225\277fr\300?7\030\016]L\356\227\277\200%W\265\205\267Q?\306\006\177\362\332Q\177\277\360\374\243\036\032\024\313?)Q\006\230\322\242\212?<\233\0334\313Y\267\277W\342\321\276\305\335\276?A\243\"\350\370\277\300\277\322\200\266\260\034\333\205\277\tF\333\t\003\010\265\277/\260;\3045\337\326?\244\001\314\375HM\264?HF|\255\265\377\251?\306:\312\303\247\370\320\277\023\275\0213\263\201\266\277\007\001%\243I\323\204\277\254\3433\010\314|\322?Ne\360\021\002!\247\277\275.f\251^\230\205\277\255\201\206\336\342#\311?\036\307\255\351\024\332\240\277B\034\203\345\371\232\267?d\376\220t\215\354\240?\3778\036\033M0\207?\311\254\334\210Y\346\312\277\021\326`j<\025\227?\364\205:\230DC\260?\245l\351\322\277\271\221?\217\241>t\371\256\304\277x\264\021NoC\260?\223P\"t6_\201?\367\333\353+V\357\326\277\337\331\325\266g\203\241?\3731\236=\360\027\262\277\253D\002\361\030\366\251\277\203\201\365\366ML\267?\240\357\"\005\274\222\256\277%N\307\334\320\234\300?\0005\240\204\3235\300\277?\016\255\267\362\345\266\277\204\367\016\247\212\017\213\277\177\342^\033\025\344~?\031\321\3169f\377\252\277m+\342\364\211\230\256?c^\370\016C3\223?<\376/R\202Z\276\277\342\326}s}\270\252\277\327\256\221l_\262\300\277>\360\232\366N\377\242?,7\177J\270*\260\277{\340\313\032gf\263\277\t\033\377\271\224\024\274\277\200\244T Q\345\300?Q\032\232\\\203\017\266\277\376\207\035pb\244\233\277e\237a\016\321\364\302?\034\377\307\341\271\376\235\277\037\"\300\032\242\234\270?\277\371\034\350R\263\247?\327F\271o\321\344\316\277\353\271U\213eG\264\277\036\325\224\253\330\215\317\277\364%\346\202\032\370\254\277_H\240F\364\345\262?\360#\026 h\345\270?\227\336\2236\371\306\252?\271Y\351\240%\365\311?\311\336Td\r\202\200?\005N\376B5\231\260?\273\376\242\373\034\221\244\277\016\353\343\220\017\300\245\277\254\333\010\342*\036\256\277hF\264 \324\204\206?\256\323\311\246\323\226\271\277fS#\004\223\253q\277b\257\322\242\247\332\242\277~\326l]\007\326\305\277\024\231^\216\013,\252?\234C\237_J\374\265\277\331m\235\353\'\230\310\277\225\347\\\3331\304\261?\255t\202J\004\311\237?\314\245\211\371\0254\244\277|2\317_\322\232\262?\004J\313kJ\034\225\277\244\374\0109\247\254\263?SoG\320\027\277\204\2771\251\245\246\371\022\262?F\224k\223\330,\307\277\006o:\n\273T\222\277qdi\232oU\230\277?u\026f\024\375\230?\221\356v\222\264\252\225\277\002\024\243\314\010_\314\2772=7[/\211\271?\203\006\r\274\n\243\237\277P\2513\316]\203\312\277p*\265?\204\351\262?y\275\272\261L\356\266?<z\310&x\363\260?\336tG\177[\252\265\277\370\314\301\252q\371\264\277\333k\232oN\317\227?|\332b\342A\310\242\277\320\261U\230G\274\263?\016R\241\303a;\275\277\304-z\253\252\007\250\277dV\353\023\247m\263\277\262\357\374\016\305=\230\277\270[R(\r\"\220?\372\211\365\260\371\225\253\277G\344F>\353\363\274?\245%\2358\303Y\200?\317\221\233Mi\357\225?z\260\020f\024b\251?A\'\034P\337#\307\277I\353\365>\304f\246\277\311\235\353\3268H\224?\003Tf}\335o\303\277\224\350\360\320\020\232\245\277\004o\030\335\247\035\203?\214\001\035}\2635\265\277\236\277C\032[\301\250\277s\254\232\260S\336\302?^\201\220Z\007\002\241\277\306#\323%\304\256\306?&\346\030\372\265\367\261?\013.\342\217\237\264\245\277s`\326\037GO\260?(\224\334\313\002\210\263?\234O\032]\344I\242\277\255\262U\364Dm\251?\366QRc\317\231\300\2772L?\376\232[\276?\313\312\331\240{0\215?\026g*uk\266\265?\314\305D\210{-\320\277\3512\211D{\327\260\277\237\223\235J\334\314\275\277h\n\005\000\325\324\300?\270\206\311\376$\364\222\277sz_x9\266\265?\335o\267\320?X\270?c\231\362\235\025\355\220?\021W}\310\360q\210??\264\242\302z\354\305\277.\245\2067J\244\205?\233\325\234}m<\273\277\274\020\342\201\013\322\241\277A&\353\321>H\303?);\233@\373\367\246\277\321L/\315\305\005\226?\327\247\332\"\331\311y?\013\304\265\311\210\225V?\350PG\207\271-i\277L\3125\260}\356\202?E\372t$\352v\261\277\326\021U`It\271\277B(\264\257\230L\262?n\354e\306R\356\257\277\233bl\251\2655\255?\207\\N\323\366\331\303?&Je\000\303)\235\277\311Lx\275G/\252?uO\0343\007\361\254?\325\365h\205\2171q\277\026\300\"\364\357\021\270\277\3451f\372\314j\322\277\230b\"\204\262\277\245\277\037\205\256\002\231:\226\277\314\253\341g\223F\221\277Z\341F\237\337\234\234?\331\370\023~\325\262\270?\222}\375\367;\334\226\277]\034Y\r\200x\301?\226)\032\311\030\213\245?\271dN\036\352\234\213?\005o\037\311\327\357\266\277\001\223-\367\0044\232?\'\261\017L\354\001\273?Ne\245\2672S\255?2>\274\250\036\373\302\277\006\025\000@\362s~?\352T\332zkQ\226\277=\277\247l\354V\306\277W>\001\236\351w\301?i\025\213\341\377U\201?\213`\005\372\371\243\266?s\266\336a\277\340\271?\252r\257<\020q\274?4\331\205\371\223\344\273?\177\334\2532\310H\307?I\267%\253\021\r\263?\024%\303\025I\321\252?)\346\342\345\304|\305?\320(\264\323Bq\236?\207\375\272\362X)\213?\036\207W\304\006,\272\277\217\251$3\364\304\216?\225!\275_,6h\277/&+Te\265\311\277\027n\331\254\356Y\263?\340(\322\254\025_\252\277*\324\232\005b\333k?S?\327\273!\235\254\277\337\314)?\336R\261?$\336\017G\005B\303?\320N)\300\307\325\254\277^o]\221\271\333b\277\244M\235\211A\301\311?\\\017],%J\265?\'G\036\020\266\224\307\277N\027Q\324\025\233\263\277\216\327\244\277\007\224\241\277\"\251\320\362P\006\247\277\267\2325\320\032h\300?\302\304\037\346\021\307\241\277\216\\\212#\370e\240?\003\372\026\007\020\351\225\2775\241*\322\373=\230?oE\n\037\203\267\240\277f\245\022\240\361d\243\277\347w\360\026\201#3?\210\213\246uBO\320\277\247+\252CK\204\224\277\301\235\366\235\321\010\210?O\377:7\322\245\267\277I\307\336n\031\317\323?\252\337m\014U~\243\277\rP\007\257\024\t\253\277\320\001\374Td\347\300?$\374n\210\361\336\302\277\034m\016\302\306\250\242?\347\211,\342\305\236\251\277\325\024\201\024\024@\220\277\'\030\351\344~\236\234?\n\377\354_\325N\262?\267\364\355\275\245j\302\277\025\0039\235n\022n?\212M\371Jm\022\255?\004\020\223\2434\305\244?G\212[\203\360D\321?\256uH]L=n?\274\211Z\323\010\201\235?\362\242U\032\226\216\310\277\n\355\243\257\216w\274?f_\006%\257\371\262?\025U@\223\221\334\301?\347\372\317z\275\307\251\277\027\"}\236\005\246\270\277\346\320\321\372A\276\246?\220\376h\026TU\320?g\0145\233\341<\246?\020\3662X\317\247W\277\266\254\324d\370\373\300\277\241!~\376\t?\277\277\375H|\263\'\255\222?B\031qC1{\264?\232\315\373\3674\260\235?\024u\210#Y+\201\277k:l\342f\250\275?\305\222\354z{\247\246?\210\313\310T\345\321\326\277\n\177\364\372\227\355\256\277\257\376H\224;1\253\277\350\327b\371\317\303\223?\243\3769\360\274\233\301\277C\037\023\362\271C\274?.ga\373&\366\312?j\343\315<MZ\305?\220\245\350\361/\031\261\277\314sy\2733\020\275?\227j\003\245\307g\245\277\304\373bp\237\236\300\277\367\336\327\224\371i\270\277\201\017\252\312\336\027\306\277 \207J\\\243J\262?\300\353\014\016\200\227\244?\001AvA\206\377\305\277\361\355\321\025\353\231\272?@\345\340O\212\372o\277#9(\317sx\277\277\277\263\313\311\371\246\255?\271\3722\341\365b\302?\336/\377U\354\353\301\277\361y\271\276\333\206\300?\351\274\177\276\247\314\310?QCC\r0\256\303?\237n\364\315\223\330|?\302\247\266h\376N\302\277K\372\355#A\t\220\2772\035\002\216\323\032G?{\n%\346Ko\255\277\342uA6\350`\240?\311\330/U1\007\243?\347Wb\251#s\312?\034\210\243\224}\366\265\277\327\277\034\3377Y\327\277\254\232\374W\230\033\250?\252\243\354\276 \027\266\277\253\254/\304\207\353\271\277M\272^\264\001\203w?,\014TB\262F\302\277\240\365\007=\001\360\301?8\377\377\207Z\235\305\277\212|o\361\246b\221?\021\n\353\305\377\367\274?\366\177\314S]\266\207\277\344\261\251^=I\271?;\226\371\234\217\341\240?\030\322_\354^\347\314\277\370\003\027\346w\243\304\277\202\030\010\260`\347\210?SV\3323\357\t\275\277\333x\363\312\267b\224\277\236=\266\370\332\230\231?\3340\346%`N\223\277\327\202\340\325M\244|\277\327\305zg\370\260\301\277,\341b\002\356\323\262\277K\246\372p=e\235\277x\360\332l\035\300\255\277R\254y:\374\003\251\277/\244\354\370F\315\252?\020\037O\016i\034\223\277\2176\243F\211\222\304?,\317.\000\211u\250?U\227s\305\263\346\256\277\236\350\300+rT\234?\037\320W2D\003\274\277}+t^wD]?\303~\217\004.\350\242?t\0324=\327\203\270?}\"\236\241y\"\230?U\242k\307\356\235\256\277r\221\n\220\002\022\271?Wo\\\236\300!\301\277\252M\362s)\002[?\205\214\242\013\347b\227?\206l\216\251j\030\334?\006\004\233\344\0238\250?}Kc|\207\235\300\277\252b$\303\320\370\267?\320(t\335~f\305\277\341p\372\300\240\034\244\277\357\'\230YW\265|?!\313\256\246y\214\244\277\331\005\001Pr-\277\277\00511^\246\225~\277\376\372\214\260=\264\237?\277*\233\336\260O\210?\\y\270\201>\217\254?\n\2604\350\247\300\223\277\273\242V\365\236\201\260\277\215\331\211\001\272\305\254?\302\362\255\342\276\276p\277\022\275\374\004\377\350D?\243\031\270\263\256\321\275\277\260P\301\010\257O\300?\270\366\232l\275<\312?\366\243<\030~\017\304\277\364PU\3729I\265\277\035.\233\225\023\335\252?X\322\376J\013\344\273\277V\r\026\320\321\344Y\277\232,/\254\216C\241\277N\274v\206\344\307\304\277\231\221\017^\217X\261?\304!r\342\374\207\267\277\004C\371\246\016\230D?\277\303Y\204T]\311?\202\201\261\363\203#\247?\365\333.\021V\350\246?\314\222\231\233\213\001\257?\340Lk0\003\r\302?:\362:T\257\036\207?\"\310\'+\366rs?\230\257\340\260v9\255?{\022\333l\344\252\235?\322\'4\024\204\002\274\277r\200(K\301\314\260?$\010\310rOy\273?S9X\256\263A6?\206\010\025\222\212\rs\277X\317Y\"\"\353\277\277\304\345\340p\033\177\270\277\3716\2138\247\322\240\277\274h\013\315u\035\252?\255\344\227\225\220\016\234\2779\245\315\201\2029\267\277\013\350\321\374\340<\224?\240!\346/p?\274\277\224\231\263\007\341W\241?\t\322\202\314\275:\270\277\273b\206\253\375\244\261?\223\n#m,\221\300?\334\225\\yB\274\253?\217>\\\205\314\225\242?B~\337i#r\230?[\205\014\036G}\264?\316\264\271C\367\303\260?5\275\257n\235\304F\277H\'s\256\311\275\242\277\376\355\204\017\334=\213?@V\277\362z\273\264?\324\031\354\344\277\221\251?\252\341\312\3513\225\242?\023h6\035\320\026\236\277mK\014&\\\031\202?\334)\226K\034\n\270\277\265\027\241\347^\305\260\277</l?\250\204\254?z(\3726\001\374\230\277\017\017\371J\303\217\266?\reM\352]j\254\277\374\310\2054\267x\253?\342\242\327\014%\236\314\277\342{z\366\260\006\243?)\303\326 \030\372\232\277\032\355w`m$\317\277\213f\205\264\\\230\255?1\323Y9\215J\261?{\3219\013\212f\306?e(x-;w\242?&\027\233\222\022\001\250?K>z\365|s\271?8\024\364rI\311\216?\342\331>\374\263K\304\277\277\267\341\000\0344\301\277\372\212\033\202B\324\202\277~\\\3756\t&\305\277\317\242*G\235\033\272\277\026\367\204\331\005\t\263?\305v-\237q\273\257\277\204|Ci\017!\301\277\243\316\020\310P\200\302?\013y\323\300\240\363\320\277Mxm&\t\253\245?\366\323,\243V\264\242\277C\316/`6\\\226?q\217\335\234\201\211\251?\003.r\313\311D\304\277a\3550\261\202S\261\277>lNZ\252\177\245\277.\3524\005\337w\306?^\223\002\010G\377\234\277\017\267\007\214\265\037\272\277\335UTu\0270\302?\013$\013c\230\367\275\277\267\252\224\027W\247\273?2\004\332\303\274#\310\277\301H\370\300\361\221\324\277&\264\257\336\302i\242?\027\206\243\303\224v\244\277f\to\350\333\277\276\277\305\262v>e\255\213\277\014\003\014\001\306_\202\277=\024I\007\365y\241?\003\265\370\242]\347\265\277\206>n\266\243\260\267\277h\305\265\263\222H\270?W[\316\355\0331\320\277\003\2770\320\323\020\277?:\372$\300\356\375\275?\335\357\304p\264\307\301?\233\316\204\237\t\212\303\277\205\n\025\271\254&\202?\241u\261\223\254\220\260?$\315L~\361\315\246\277\205Gn\335\001L\240\277\032\226@\3103x_\277\2460\304\261\306\343\230?\345\214\353\342\t\'\232?\362\201T\267\212\342\242\277i\3622u\237\217\235\277,R\013gH\021\227?\330}\0270o\341\242? \351T0\355\001\247\277\001\234\036>\325\222\223\277\225E>\342\312\375\306?\266\372\344&\3000\241?%b\220\326\325\270\301?f\220\004h\230Y\316?$\021\013\352\332\032\212\277\300\205\213^vgu?\t\271\314(\"k\201?%\031\177\233\010n\222\277W\000\252\354\345\354\271\2775v\262\243\351\224\231?WbP-\271#\307?\331\220\271\341\246$\266\277/\345~\3548\010\222?\376\372\343\204\\\340\246\277\275\367.\210\330\311\303\277Oh\37545\350\255?\256\355\262\021\003\n\250?*\002jn\r)\264?\262F1\003\327\r\305\277\207\354\372\203\302\320\311\277\272\201\277\343\334\247\266?\005oa\177S\311\210\277\341\016I\371M\231\217\277\252\212B;1e\231?\262l\010\260\207[\272?\002\270Zk\267\244\216?\207\301\262d\363<\265\277\263 \301\212\023*\302\277\322\357/\330}\252\244?\022\220\"\353\2643\262\277\017\215\222rB8\237\277Kk\rO\353\020\256\277\241\025\014\226\325\254\302?\360\234\312\305\252/\247?\232\223\306\236\237\212\240\277\rf\270\236\322\221\316?\251\242F\304L\324\202\277\030\242\357\3618\206\243\277\237Tc\262=6\321?a6V\020\004\371\245\277a\336\0343Y\025\241\277S]i\250\201=\306\277\225\023l\202U\017\311\2770]\343\316\006Y\217\2776\014\034\010\\dc\277\216\312\'\266\3574\310\27771\335\242\214r\274?a[[qL\347\232\277\374\326\024\214\177\310l?B\250@L\372\014\237?B\\\340z%\321\210\277A\222\345\261\311c\262\277\003\315\315\366T\204\263?j\345\246\031,\377\216?\022$N\343J\304\246\277G\235|uta\214\277W\302\263\021w\216\324?\242\304\211?f\370\264?+R\003\306\206\247\303\277aGL\255}\266\257\277\'\242\241\3234_\260\277S\253\343\331\371*\244?\272]\272\037\262\305\262\277\316\243&z[\177\254\277\004s\375/\271.\211\277\341|l:\231\237\243?l\341\2717\276\\\205\277\320Nc\263U2\271\277p\226\r\0078\224\253\277\310H\221Si\354\266?v{\033\277\\\337m\277A\251\220\234zJ\257\277\276d\'\276n\236\265\277fe\267v,Z\272\277\033\245\0039\"S\222?\346\3279*]]|\277\035\035{\237\002]\235?\313\305;_\243|\270?\213\307&\356\225\204}\27722\345\255U\000\262\277 \227\027\356\022\212\270?\250\257,\353\324\017\272?\310_\271\225/\255\236\277\205\313\312v\2468\275?\301\226\210\315\373T\307\277S\353Q\227\346*\264?&\002x\244H\234\260\277\213\024&\203k\000\300\277\355\003\337Jm\222\270?\242\201\335\260\327\225\275?T_\275\314\256\235\232\277,@\377y\242\217\307\277r-@0\024\020\236\277\201n#\324\204W\273?G\234G\315\212\251\302\277\254!\317O\303\035\241?\327\234\324{QM\273?\267\2502\202h;\260\277\216\r\203\243\262q\213\277\364x\255}\000I\001?\202\316\261\031N\014\264\277\3760\030\315\006\235\235\277\255\206Y\277s$\221?\262\025\247L\240S\244?\023\340\226\013\343;\315?\006*\014\000\324\302\270?\302\324\321\265A\310\262?\351O\003\366N\032\261?\223\000\311\257\257\343\253?p\034\201\247\227\250\235?\\\272\207E:4\260\277\326\337#\323\357\304\251\277~\210#\223 q\310\277\375v@F\346\345q\277\223\001\001\322\251\031\274?\366\261\241~p\237\302\277[\212\363H\337n\214\277ym\274\265\203\211u?\033\244\365\201\215\003\260\277\341 \tH5\302\301?\244\000^\331H0\241?K\331a2Cs`\277\306\261O\001\2422\301?\224\341,\247\363d\303\277p:\013\224\311 \262\277@\214\004\375\243`\301\277\177k\264\310S\362\201\277L\370\360G\325\177\270\277\030\200R\271cG\274\277\361e\022\275\254\035\272\277\007\236\265\207\337\227\244?\245\241E\326O\006\266?\246\3436c\005\305\234\277D\th\003>\004\243?\r\322\215\322\323he?\232\315\263\250\223t\261?\252\272\363\'\362\357\251\277XceAS\014\241?\347-\335\265uH\312\277\217n\223\310aF\255?\355A\002\341\n\222\220\277\253\037\272\3764\301\251?\345m\343[\361\321\255?\332\371\313\240.\300\276?\013F\034\014u\241\243?B\335=i\006x\275?\255\345\375a,`\275\277j`!\304\005l\317?\252l\246\014\3272\257?\341\350:z\013`\255\277\201\256O\202\000\001\265\277)-[\272\347U\254?\2748\211|\320m\305\277&[\r\256\316\374\225?\352\023\375`\372\302\314?\314oA\010\nZ\261\277\240\3018D\033\000\256?c\337g\330\021\033\274\277a\3616eT\024\271\277\336\224\213\30327\243?oP\356b\264\213\252\277G\204\310\331\244\324\300\277s\216KG\321o\234\277\353_\364[\325s\266?b\307\243P:\366\322?\321_\002\323]\300\263?B\250\2401\350\365j?0\215(dU\215\265\277\367yU\376P\355\310\277\270\371\247O5[\241\277\tz\0374\260t\270?\352-\373et\337\273?\001Z\3254\317\027\236\277=\260\344\373\033GM?\234\235\332\312\236\364\251?\357\246\205\314\243}\264?O\000D<\350\014\257?\021z\256\335\226\034\251?f\301\256\207,%\312\277\264b\334\355\324\317\246?\025\203\023\353\030D\266\277\227\340\006\200\370\363\252\277\n~\275&9i\247\277t\327\205q\313\341\251?Q\216n@\223\266\274\277X\333\306\305F[\220\277\220\221;: \252\305\277-\203\375!|I\231\277\375\225(.\247\314\222\277\230}SY\276T\270\277\020\277\352\264\245\\\266\277\376\316\247\236\243\213\215?\260\034U{O|\252\277\003A8w$\033\250?\002\036\262\267gk\274?\331]N;z\261\262?\356\346$\362Vb\303\277-t@\247\nR\265?\214+\370\334\022\340\210\277\245\332\017\240\033q\262?\211\325.h\224\366\253?\334\203I\341\261s\225\277I\257r\010Kc\251\277\347\253\016+\324\247|\277\0075\360O\340_\260?S\332\205\363U\340\275?#\232\310\370d\t\205?\014\013\333\030>7\243\277\022\006\244\312Ew\260?%\352\343\237Bu\312\277\010w!\323\242\353\273\277o\274\250y\362\256\255?r\367\254\373G\035\250\277\273\303\006\017[\261\266?qQ\233u#\224\264\277\260\030\312\004Dd\226\277\204\221\372\210\332^\235?4lls\212\020\247?\021\366\261\000)R\256?sB\302\301\t\227\244?\271\254\303\322\3160\267?\025\017\344z\364I\227?a\024h\322\240\010A?k\254\222\013}\203\270?\023X\207\203\245a\323\277\030xN\362$\301\241?\206\337\263\362\363\375\273\277\252\347E\337\276\017\265?7\002\003\217\225\211\244?\276\270%\277\342\222\227\277\314\303f\316\024\323\245\277>^<r1\211\206\277\022\270k\007&\371\274\277\206\036_\316\223\025\267?Dk\300\240\036X\273\277\031<+\205\343\322\245\277\326a\002\027f$\246\277\341\217\323\314\262\000u\277$}\261\016wN\326?\251\374e\3216c\223?u6n\037\303,\260\277v\260\346Zx\214\266?y\002\252c\300\027\305?\224O\022q\221d\257\277\351\250y\021,\275\244?\235edg\216+\305\277[\360\035a7\264\276?q\312\323\036\212\212\262?\224R(\314\243k\311?A|6\023\216\004\252?\325\275\237h\312(\300\277\276<\346\244U\200\257\277\212\351\326\027\005@\321\277J\001S@\352\325\271?}\266kM\372\233\252?>\036\230|\373E\301?\207\353\367a\247\356\263?\327Y\320\334\006\312\225\277\326\024/N,\340\234?kL\370\203\030\204\275\277\025\237\376\226\231\233\243?\372\247\261\275\206Y\247?\376\243\332\322\334\237\265\277:\340\024OQ\205\255\277/\267f\357\\d\253\277\204\t\376\266\346p\317?u\226\254\203_\246\312\277\000^0\227o\030\220?\362G/\265\325\312\310\277\010\337\235\005\022\373\247?\370\311\201\300\224\320\262\2778\203\010\216\303\203\254\277\262L\245s}\233c\277\020\224-\213\351g\230\277Qk\311\236=\353\272?}i%\367\270\373\261?\250\315\t\360\017n\300\277\t\374\331n\0014\263\277\002vH\332\335h\257\277T`\277\327\335\277\253?\256\212\364O=\231\320?\215\010\r\211\264f\247\277S\246\265\r\243\004\274?oT\276\003\302T\303?\226\317[\204P?\216\277&F)\367q\367\261\277\235\212\242\2447\305\300?\222>5\000A\261\246\2779\302%u@H\267\277\233\003\026\314B\035=\277\221d\223\036B\027\322?\346m\235\352\305\242\230\277\364\312\333\031\023\001\251?\001\351v\365\321\375\330\277\327\377\n\363\240\030\257\277\251\t\357X\200\332\277?\377\340\334\235\275n\266\277)b\177T\246\375\313?\031\346\375\242\270\327\260\2777e@\250\0374\305?9\014\214E\320\254\244?\211(t\2769I\265?]\000\003\332)\t\253?\354\317\216\266)\367\212\277\034@\245\021\344\301\305\277\014\240\331\245\363\357X?\344@\305\341\"\305\263\277\353\220\240<\375\312\200\277\225\3210\244k~\317?d\337\2651\343\227z\277\331\351\033\201J\232\273?gZ\033\214\261\177\265\277j\226\324#\314K\304?\204\276\205E\315\035\301\277\242\020[m\366\343\300?\261\241\374\221\010g\311\277L8OtY\253\257? \253\373\212\256\023\301?\206Mt.\316\002\312\277c\005CW\210L\262?\253\203\036\204\224\324\235\277\023\243<\357%,\255??\375\367l\351\234\272?\350\206#\035\346\203\201?\357J\244R\322\315N?Hb\255E\357yJ?/\347g\251/\t\304\277n\350\362\004+\373\260\277\200\364\315\206\322\022\270\277-\351@R\221q\310?l\222\201\274\266\313\254?\305G>\265\027>\304\277T\307Z\364\215\221\323\2779\352\203\315\277s\267\277\267\350\177\230\216\004\270?\346\377\326\234Qi\260?U[\247\210[\204\310?[b\375\302\032\223\246?\350\202S`B\334\300\277B\317\241\210,\002\264\277\323\366\220g\225+\305\277\356qr\321\376\275u\277\307\225\245L\365\r\254\277Y\364\375\343\314\213\264\2772\234\224\355\333\017\241\277\262-\035\377\336~\227\277\2631\264\277\342\354}\277\262\276\211\007\214T\247\277\276\344\366Hr5\265?\260\266c.\344\341\311\277\301d\3461\224S\245?X\016^\373aF\220?H`\314\"\017)\257\277\027\2504K$I\242?\r\231\271\r\262,\251\277\\.\221\305\217|\266\277K\022\242\004\272\321\275\277\2615\022\rMD\270\277;\236dWo,\235?rD\331\"b\253\274?\373\332[<\033\372m\277o\221\006\372Y\202\216?\343\335\321\324Zj\254?\024`\235\014d\304s?.\237\355\331\271\211\306?\212\363d\242Y\360\277\277\223\220\0249\212\013\262?\327a=\342\220\003\300?\001\233\225\237\242C\300?\310\235f\2713[\233??xp\361*o\235\277y\326\307\001\257\226\262?pj\361\013$\002\242\277\001\252?=\006t\236\277L\'\004\"\232\243\302?\251\336\343\253\301X\226?N\357\210\206M\311\240?\034\240\325\032*\341\252\277\274\203\034\027\335\n\242?\226*\206\341\342z\301\277\006*\253\371\211\320\264\277TV\247}\373\027\245\277\177\266\020\227L\000\214?\347\262\310z\252\262\226?\026-\333\304\270c\202\277\003\200\031\370\023m\223?\336\300\332\211\r\207\274?\205\037\230\021\321\333\250?\271\267-\256\255{\320\277\216\307\367\371?o\207?\274\350\300\217\332\241\244?%^\304\301\204\002\262?\335\375\337\372#\027\302\277\016x\003\022\267\004\231\277\177tA\303P\324\272\277\252\013\353W\3768\306\277#]zs&y\251?\320\2468\342\327\250\256?\330\222gI)W\272?\217\344\214$\246\304\305\277wg\367_\346\246\260?4a:\215\226\343\264?\030\033\341\177%~\223?\362)\307\324\035\025\207\277\017\257\252A\357g\244?\306\227\201\316KW\273?+~KAf\246\312\277\353a/\010j5v\277\0046,vJ\322\276\277\037\004\022\207\362\357\207?J~\316\236z\322\251?C\241\327\"\375\t\261?\221N\361\277\356\310\266?\231\202\224R&\242\245?\245\320D\261\001\310\253\277\"\203\031\354\n\322\262?\351f`\376\035\037\306?\316F\352\233\347\022\265?y\365,\217\223\023\273\277\320sQ6\353\034\251?\203\311\t\304\301\221\325\277\335[z\353\351f\240\277\212br\n\222\020\260?eD\t\226u\002\312?\205\230\231\272\371\314r?$Q\017\026\370_\261\277`Zjy%\350\301\277>=\337F`\215\233\277\020\302R\317\305b\255\277YQu\027\254\223\262\277\021Ao\362r\037\257?\375\361\233\204\374\252\203?2H\345\223\374\341\253\277\t\3776\312\013\002\261?.\237\252T8j\265?\372\260\331\344\231\251\227?\331\247\242LS\254K\277\340\004\330\035*\217\242\277\\\315\276\333\226(\257?9\024\270\013\335\037\221\277+\354@\313\267b\206?\226\356\177\311l\357\262?R\r\373&wy\221\277\231\272A\373R\231\250?}&\306@T\214\260?\252#\024O\224\350\256?\257\330M~~g\267?&N\333;W\274n?\3173\300\302aa\322\277\356\372,\235\213\237\245\277\276\000m\326\255\355\266\277Hb\037\3318T\203?\250\366Z\217\005\273z\277\202\224\035\243BH\271\277\363X\347\363iw\230?-ig\245\377\232\265?\345\277\321\002n\362\241?\362C\207d\357^\226?\327\365\270\226\354\024\201\277\021\244\006v\037\311\260?\256B\253\3378B\306\277\347\322\256\317\222_\307\277OYG\216M\344\326?\254\372\354J\t\020\271??\177\274k\032{\215?\037\325k\265\237\'\212?\361\336\270\032/\320\226\277\027\375+\237\217\272\204?8\326\242kYs\247?\017\203\207@\321\221\265?\013\024\247\035+V\203\277\030\242\036o\231\300\260?s\233\3071\327b\277?\323u4]\233\240\267?Lf\323\2253h\211\277\023\\\207w\315i\257?^\214c\374\254M\311\277m\352(\333\265@\214?N\217u\275\276\321\235?61\374\341\314C\270\277\004c\224\212\r\017\277?\\\242\307\001\357\304\237\277\031\232\330\373\t\033\272?q\250\250\226\"\017\307\277\225\020\334\305\006t}?\021\t\'yW\022\211?M\372I\324-\235\261\277\001\2304\347Q\337\302\277\344\007/r\332m\264?\003n\244Ij\337\277\277e\203%\273h\264\204\277\236\227\262w\255\246h\277)\374\013\351\201\360\270\277\267\265*\325\337l\273\277W\014\356FRx\272?\026\017Xn\253\016\220?\223\276\004\246\221\364\314\277\253\'!\022\033\266\266\277\322\001\253\262&\212\243\277\245\334\224tX\322\232\277]\202\241,\374\273\254?r\366A\264\243\005\272\277\377\277\030B9V\210\277i\312\0068%\274\256?4\000\037\253l\272\330?\014\217\373P;`\251?\231\204668q\246?+\330s\273\3548\220?\001*u\3609\244\250\277\236\207%\006\317j\245?\322\233t\373=\n\255?\253\033Fc\003*\313?\371\323\345K\"\215\244\277\202\224\223H^\225\236?\337?m\010\323\330\216\277[-\376\026\230\236\263?\252\260\350\344<\244\277\277\211\313\307\303\301N\203?\2325\027\370\243R\304\277\330#\367\\\031\223\256\277%\234\265\205\327\374\226\277\335\357\211\244\352\211\251\277\026lg\304\304\374h?r\310\370i\371\315\275?\336\244\323\024\243\337\267?\203\376\207i\224q\240\277m\246\350B\331\t\232?\310\365\311\322\313\340\252\2773\271\361\356Q\376\260?\337 \367I\213^\253\2778?H\267\213S\243\277T.\024\265\035\230\256?\340\254\371\257\351\334\301\277G\267\374S>\231\256?\n\037TtP!\270?$\247\023lK\221\234?\304x\233(\274\003\264?C\325\017YGBk?\214\257\207\252\203\365\272?#.\227p{\232\274\277\345\242\220S\353\221\206?;\231$\310F\334\272\277\345~\270c\323\213\274\277\346\034\275\221\254\031\220\277\3631\340\034\311\324\236?\014\344i\330\210\007|\277\375[\265\321#h\241?\214\336Ym\301\t\272?\310\'\203f6\374\253?Q\373f\313\3563\261\277\344\301\300\337|\332\273?\342\250[t1s\202\277\026^\325x\223\036\300\277\370\365#\274M\365\315\277\004W\310L06u?n\311d3\244\370\264\2778&\241\221\220\223\317?\276E\tf\"\373\240\277X\2150\324\373\255\247?\356\3233\322\302^\223?k#\243N\327\203\242\277\375\001?\216\320\225\262\277N\332\037\\{\321\272?\252A\311E2\211\244?\246\332\300b\321\244\301\277\001\302\337\357tH\263?\006\177{k\014S\264?O\037\261\027\001\233\274?\316\020n\261?2\243?\245R\211\007\r\300}?\260u?&\335\212\255\277IF\311\005\2740\275?\206\331z6\236T\247\277\216:\240\271i\246\312?\031\244\307J{\347\306\277^82\\\310\301\262?\200{I4<\311\263\277m\207\006\323Z\362\266?W\322:t\270\370\270\277\277\000\230\307z\323\245?\347\005\317\337\351\030\271\277\r\373\346\211+y\265\277\216\3667\217\351:\266\277\300\353\004-\007\200\257\277$^\210\375\266\331\203?\033\342\'\251\031\277\222?l\225\335\337\206\275g\277\361\223\364\020\370p\264?\241\177\316#\363T\271?\276\013\035\353^\243\217\277\203y\302\243\312Mu\277\371 G\256\337\304\225?\306\242\200xN\010\260\277\256\220\005\003o\270\245?\256\362\340\261>K\305\277B)\325\217f\376\234?\211\020\256\343!?\257\277\024\217\002\004\251\017\262\277@$u\266\301C\247?{\310\266Nj\341\246\277N\nv\330\322\211\247?\253S\201q\243G}\277\244\'\024\2672k\232?\3159\312\366q\331q?\217\010\023&\231V\241\277d\006c\025vu\261?\215\203\2726p!\321?=9\366j\2271\227?\260\343a\263\034n\256?J\254\304\267\360E\321?\376o\213\037\263C\274?\310\013KDrA~?!\224\343\'\333\221\274\277\266s3\200\236\305\240?LK\336@\025V\257\277\224\316\373\336\242[\303?\304\340\276\326\250|\215\277,\272\001OW\320\254\277\374\013\004\n\312\021\261\277\177\'<\001\024\367\241?\223R\'\343\313\204\301\277\272\223\205\223\340\366\223\277\t\014.\221\336\330\206?\206/9\037V\"\256?\344j\276\003\371w\232?N\000\363\372R\027\207\277\002=\356n{\372\314\277>\256\300\233f\245\253?\325#\244{5\315q?\027\205\021\007\371>\210?4\220.\013|\231\325?\275t\310-\036(\261\277H\215#U\215,\256?\016\327S\322Z\n\307\277J\t\'r\013T\301\277\031\030\244\372\234\240\270?q\265P\333U\204\251\277\215\304z\342x&\310\277\273.\324\000@\356\256?\243r\375\007~lv?\033\3438a\315\202\274?o\223\026\232\310\225b\277\202uD7\344\351\266\277\375\3334\210\304N\246\277\370\024\250\031\347\235\312?\020q\2719\315\371\247\277\206jd\244e\027\213?\260v\3266\222\217\307?\364\312\030\313\033\260\262?\177=\264\327Ux\261\277\200\350\312qc@\304?[H6\213\263*\273\277C\311\024\265i\266s?\336XCu\254u\225?\'\212S!\273\244\262?\226y\023XY\275\213?h-T\261\323\003\250\277\234\366/B\006\016\270?\263\257\254X\272k\311\277\241\007\243\346\'\r\235\277\245z\201,%\035\270?f\360\327\003\2300\205\277tJ\221?\323\355v\277\026\204Z\000h#\251?D\322X\223jY\225\277#n\357\275\031\354\312?\251\200\025\220\213\356\231\277\254\034\243-\2240\204?T\360\177E\237p\267\277=\205\360\235\310\266\262\277Y\260iT\337\314\250?\320\361C\353V\200\273\277\0060\020+\264\306\304\277\021\3360\257\321S\256?G\016\314\276(1\225?\332\237\2070?j\244?\265\032\311,\300K\277\277\367\337B\340\247\004\252\277\245\370=h\327\357\225\277\343\341\032\023\203\026\264\277\030`\227zM\226\261?\037\375\361\2173\303\243\277\360@\336\331\204x\304?^IE\204\n[\256?\206\261\346\266Y\305\275\277Q\323\332\341Y\366\261?\266\000\200\262\t\351\260\277u@|\213N\311\216\2777\231\315\031R|\253?GW\336\\\331\266\265?$\354\021\036\350\311r\277\006<w\315G\325\255\277\035\357\254\205L\360\225?\365\337/\272\2222\250\277\016\200\352ou\345\254\277\007\302\270\377\214\367\220\277vrTG\274\305\205?\364+(\255\375\275\243?|\200p.\032u\211\277\344j\377\276\274\251\200\2771\037\000A\352\025\274?M\220?\273\372\225\221?h\2544o\020u\224?\007\'\237\257\326\366\261?\031\307\007\242\272\016u\277R&\'8!\035\230?\373R\222\204\261\"\303?W\023\314\206\0257\274\277\302\226\023\034#\244\304\277\242\231\353\264\370\313\301\277DX\307\354R\305\305\277G.L\326\301\316\224?\333\1775A\031,\203\277N\006\243\310\202l\303?\305\346\354\304\276[\207\277\031\262cdd\231F\277oK7K\343\226\274\277\256\207\236\263|\357\310?\342\270\333Y\000.\246\277`\321\2547\345q\211?\341\370\325J\033K\221?*cf\260\354\017\305?jy\271\245\250\347\256?\271\343k\346\345\376\252?M\255#~\032\304\325?\324Q\347\362sz\266?F\242\203\240=\364\315\277a\317:\250dQ\233\277\221$G\360\3356\253\277\330;\241\3773\270\233?\345\275xFNc\263\277\335\226Z.HJ\256\277vu\255v\333\220\271?\266/\231\227\232\242\277?\316\321\255E\346\210\270?\033\360IP\303\254\301?\n\\\243\350\017\244\253?y\311fF\353\351q\277c\262\205\205\0332\242\277\177\335T[\336\245\250?\345\030\032\032xh\265\277\377\225:\217oI\276\277\332H\341j\006N\326?\"6\336\002).\302?\325\315\201\276t\225\200?\250h\341H\363`y\277\345\365\371\313\001/\316\277\375A\241\347\037\206\301?\020\261\244\344\217 \305\277\205\177\024\362;\374\307\277\351\342\230\271\370\207\244\277\207\270Oq&\314\276\277\313g\240\331t\237\300?T\243\375\201\0271\256\277&uh1\356r\266?\343\217\332\214\306N\227?/:=\2657K\240?ArD\rQ]\262?2\033\311\364\254S\251?\334J\020(:\344\246\277&mIi\267\362\255\2777J\233\210r\031\266\277\333\360\212\330\342\336R\277\\\341\343\351#\023\261\277\020G]\357\221c\214\277*\360 \303\263\226\270?0\261}X\215:\255\27701*\364f\000\214\277\325\326\346\0311\230\270?\030\320n\321d\232m\277\033\253\346\326+Y\271?g\315Y@\222\271\227\277\227\236W-\344\242\265?+\034\346V\273\341\223\277\037\205\221\240\222b\245\277\\\026\213\034\313\222\222\277 _Y[r\250\241\2779\276\321\302\005\n\257\277eW\231&\206\207\273\277\001_\314\355E\276\300?\034\356\311NB\356\315?1y\311\374\202\303\270\277;\3206\036u,\260\277\355\362u\341=\201\275\277{\005\271v\035\177\260\277O\2705P\221e\247?^\227\233\006\221\207\275\277\204\000i\224\253\221\262\277T\"\335\007\210I\253\277\014\010\241<\t6\300\277HL\322\223\366\323v\277\233h\035\221f?\322?4\342\321\275\016\236\261?0\\b\237\206_\264?\223\rB\266\345\017\216\277@=\254\356^\256\207?\202\0219\237\377,\257\277\035I\031\251P^\277?\302\276[\372\351Ry\277\233\206\256u\332A\246\277\270A\336:\245E\235\277\304b\333g\371\361\301\277\344\030\371\345\2078\241?`D\345:\325\023\261?\340s\315~\035\241\264?\177\217\227\352UJ\270?\364D\'\3420f\255?\372\253\306H\275\263\251\277\310!\376i\250\355\301\277\207\361\321\016\221\360v\277\355\030\336\202\205\233\215?\376\020\033\216y\236Q\277\337\334\225\370\334T\231?\007\313\207\324\352\004\227?\340l\357\360o\250\305\277\243\251f\257\265\346 \277\317l1,\320\266\265?\270\264\t\225\305\030b\277zI\3003e4\271\277\202\002\236\266\\\226\250?\377w\254\r\367:\224\277\364\030a\220|\020\242\277:H\202\246\230l\235\277\232e1`O\261\230\277I(\255\033k\333\255?\251)D\217\r@\303?3$\340Sy\267j?\236\326\264\217\2128\260\277\263O\365\236s\375\215\277\317\010,\340HQ\226?\204\3502@H\224\243?<\033d\017\304Q\272\277\r\324\377\001\257E\220?\356\270\033\213\240\366\270?\251\225\255!\r\262]\2772\363\177\350\352za?\032\346M\357\306\001\317?\361W-\373\227{\243?\365#h\256C\020\322\277\241]\356\263\205?\243?\231\006\205a\350\022\323?n\247\245\202\352~\207\277\007\230|&#\242\307?\231\234\334\316\000\217\216?\240\177P\227\351\256\273?\211ls\306nh\244?d}\354\363H\003\252?\321\342\300\031\316\273\305?\352\326\t\313N\270\223?\205\360*\325_\212\236\277\027\303$\254\220o\256?\350\275\010\310\024\252\250?\327\032\276U\001\016k\277\"D4\304\377\025\302\277\013\327\230F\363a\327\277\002\243g\261\016@\233?\004g\240v%\000\267?\314\323Vr\014\375\264\277\020\226\032)\207c\277?*\304\230\311\333\313\245?\026`\275Y\231\234\273\277\357\337\352\245\237~\264\277)\365-\r\305\223\251?\356tQv\323\225\244?\256\360P\305%>\177?,\253\240z\341\232\261\277\306f\311\360\312f\274\277\357\260\031\245>\315\311?$\270w\242!\007\253\277\271\023N\r\372\347u\277\237<\365\305\330\341\244?\207\023\247\257\352c\232?\227\207\247\245\314\211\305?\0326&\377#\254h?&u\234\357\242-\221?\365~\255\000+I\251\277*~~\343h\034\237\277_W\211\207I\366\235?\203\223\210~\345[\224\277\232x\345\201\026S\260?\225\206 \222\224\254\271?\222\342^a\332 \274\277\365\324@\004\355\212\310\277\376\2614Y\2360\265?\351%\321v\023r\304\277\275r\300\217\355\370\247?D\363\255,\223\213\312\277\220\321\307\244\267w\220\277\236\362\341\345\027\026\270?I ]\367\034\335\247?\377\232\221\002\'\314\240\277t\354\244\356\234\275\274?\362\365N\'\323\310\310?\326\205K&\177N\244?\025\226e\216\027\035\265?D\217\220\374Y#\265\277\237k\311!-\371\307\277\031Tu)S\375\237\277\241\322I\3314\341\231\277CM\331W\204l\300?\356\344b\340\0072\256\277qf\036\356\333M\242?t\212\222\361\356\200\301?\213VA-d\000\243?\234R\016M|\004M\277\223\275\217\035\263+\252?y\317**\344\252\250\277R\212\306\307\245\372\207\277\027\316\010\204N\271\250\277\374\303\"\331\026}\245\277\311\351\363\303\260\266\223\277.)\005\333\026@\247?\305\036\024s\256C\304\277\330\"\2368g\253\272?\2050sr\334d\241\277\272f\273\203#\337\262?L\026j\224!fd\277\330\203\330\243 \333\310\277|9KL]\274)?\"|\031\357\217\216\270?k\336\357\000\370}\242\277\3015\027\3541{\254\277N\317RT\222\235\302?\317!\237B\200\030\262\277\376\300{\302\367\317\255\277\341\304\034X\315\247\177?Q\336h\344\3670\261?\224\312\242\'\300\017\250\277\340\254\334\266\260r\247\277j\211\025\234\212g\273\277T|\207[f;\303\277`\365j\031\360<\305?\2064-=iu\267?\004\226\273\027\353\355`\277\3635\370\232.\252\302\277A\201\025\022\'m\262?\234 \203\363\'\247\275?\247nPg\327\361\307?U\247\224.bY\274\277\2235\371\257\005s\262?gkr\371]_\243?\277\315\244\241\324y\275?\244\366\376\215\236@\301\277%\024q\347\000\370~?\302W\214\031\265k\223\277\306\224\367\346q2\307?\270\323s\376\002^\273?i?\333-V\226\257\277\361\323\017\0372\324\256\277y\225\177\221Pn\217\277\364\277\211)\005\205\227?\2427\023\362+\344\320?\030\241\362\342\345\367\266?6\277o\312ct\225\277\315\274U&\344X\233\277\366\236L\235\252\366\232\277\205\257jG\000c\301?\254!\321T\335K\210?\271\256m\231\014#\251?\3074\247vbp\302\277\257\204\221Bq\205\276?\214\211\325\330w\204{?\201\010zE\340\262\320\277\354\243j\301+\014\233?\2407\306\2252\330m?\tp\006Q\262\361\276?\002\360\330\035TK\315?{9Zq\313A\252\277\341\312\370.;\314\264?!f^w\2274\272\277\214I\"\025\020\220\260?3\363\305\211\'\257\230\277k\337E\204\257[\207?\213\272W\222\202\345\302\277\212=\265X\201\010\270?\255\244\311f\030h\225\277I\363fP#G\303?qm\"o\'8\261\277x\266\222\251\206g\264?\275Ol\234x\253e?\345\262\377w\'5\310\277\324\331\223J\221_\256?N-\205]F|\277\277\207\341=~q\224\276\277\277\033\023W\341y\225?\317\353\312\330\344*v?i*H\277P\310x\277\337\264<\367\177U\217?x\213\350\020uw\266\277\230D\032i\375w\261\277\235k\006\027\266\252\257?\230\246\33721\303\254\277j<\366\226\314\352\241\277\234Z*\350E\337\266\277j$\0059\377C\306?\364\237L*\016\033y\277\005vU4q\\\205\277Gq\201%Y\303\276\277\215\207\003`\261\204\237\277O\r\376\270\r\267\263?P+\234\037\004\237\275\277\302\354\232\236\035\036\203\277\262\243\247\026\3239\265\2772\275_au\304\276?.\236\336\2515\326\235\277\310\036lu\024C\251\277\325\270\375\372\357.p?\204\004\277\262\034\237u?\252\035\271\202\251\240\262?\262\024\3522\000\332\306\277H\302-_\276\026\262\277,\374\r)6\217\265?\230>)\305\365&\270\277\262\222\323\330\247\321\237\277H:=\277\370\320\265\277\312\214{\037eV\241?o\326$\223\362>h\277\260\nQ\232:\360\272?z\337\315\306\277\356\272\277}X\317\325\013\357\245?|\233m\205[\244\304?\250\374\375\350\270\217\263\277\3513a$\207>\301\277g\"\325\347>@\267\277\201j 7X\004\261\277\2526\210c\3469x?H\313\314\032d\322\177\277fQ;\004\251y\242\277\312\312\031A\177\266\243\277\372\304>\026\366,\270?\341\006%j\337\317\300?U\240*ZDX\274\277\003r\247\315\321\356\323?\362}\344\341\"\013\241?\330\320Y>\000@\261\277vnr\307\375\333\301\277\263\376\257w\367\334f\277rdz\375\336\013\244\277\372\230M&\032\277\265?q\313GZ\025\346\305?:\270x\035\344\202\223\277\334\177F\325\226g\252\277\324\006\030f\343/\261?\345\035\204\037\302+\314?\242\247*\271\256c\262?\n\365\025^6\306\263?I\214\366S\002\366\307?QB\213\003d\216\267?\r\250:\232\373G\244\277#3`\341\276`\306\277Q\020\367\260\264\037\263\277JhO\310\351\r\242?\256e\263f\207\345\264?q[\370H\214\252\313\2772gey\003\014\305\277\302=HC\030=\261\277\007\026Q\374\250\211\254\277\322oW\356\207v\301?P\277:\210\3568\203\277\364\362\214\234\302A\216\277\033\320-Q\344\276v\277\257\277T\262\344a`\277\230\341U\006\002\206\264\277\222\257\263\n/\224\261\277\222j\352\335\306\257\306\277J\340t\034\356\315\277?\362\207\215\332\007_\271?\224\272\3365a.\272?\216\324\215\330I\361\243?p\330\316Ta\236[\2776)f\014\215a\250?\343J\253\025b\324\245\277\217\243~\316#\375\244?\323I\206\345\321>\226\277\013\265f\304(Q\302\277\351\261\241!\255Q\263\2770\201w>\344\363\274?L\270\374\276\313&\300?\021\217eHq\213\267\277\333\267\332\340\214\353\211\277\261\3560\023=A\254\277\203\n\206\277\326\341\240\277\206\235\n\227j\342\264?\223\342\234\364\335\304\267\277\035\314\021U\253\324\221\277m\312R3z\243\265?m\250\207\202\224\252\276?\232\356\014\237\336Q\231?\004WBE\261\023\264\277\005\315K1M)\232\277O\002\344\335\302\025\264?_\270B\343\327\217\267\277\212!\316-+/\316\277{\201$\243s\344\252?\375\317\014\245}\023\271\277\353\355\232t2\264\303?\002~\017\341A\215\264?\237n\343\3575\314\240?~t<\000/C\267\277\210\246\301\247\223\363\301?\375\2648hP`\213?\357\245\314ulh\270\277\327c\340\322W\"\274\277\227@\010\321\211\001\304?F\346\205\n\301\255\241?\223)\023\262\205\'\226?_|\253\010\245\007\311\277\004\242!\277y\022\254?\201\357\261\322hC\266?\374\010\234\346j\033\260?\004\277\0070\2462\240\2775[g5~b\252?~\357\003Cq1\274\277\353xH0R\000\301\277\311\212\210\353\014|\270?\177f\013\366\322\336}?\206\216\223e\253\327u?I\354\036\255\2610\236?|\340\273\327(G\225\277\n\225\232\2750\217\303\2778F\024\302\2546\320?\226>\315\260\210\347\255\277\037\224\213(_\030\224?\246\365\331\236\327\340\216\277\003P8\313\215B\307?\320n$\"z\202\252\277\236\244_L_\n\252\277\020\352\032\231\227\003\307\277d\344F\010m7\265?\331\335,\262m\375\275\277\246\261\362\362^-\321?}\337\237.\2245\260\277xl\024\217\023\237\256?F\213\252\026\021\274\310\277\241D\364\320\372\210~\277\025}\270\337\251\244\206?\264P\364\221\317\021\302?\270\341\3341\250\266\252?=\026g\246]4\241\277\336\325\"?\213D\202\277\345\331\022\342V\212\254?g\315\020\301~\344\277\277m\240$N\371\021\262? >\210\322\007\233\260\277\305!4<\374y\265\277\342m\305\310\377A\272?\322\233D1\030\206\232\277\220Tf\376R\247\261??)\243.m\035\302?\201p<\317s\363\264?\221=\211Ns\252\301?\022\242\325\201x\201\301\277\235#\332\230\324C\266\277uuv)\030\212\277?\271+u\031\231\r\265?\275\236!\022\334\034\304?v\274vg\275y\300?~\254h\027R\221\214\277*\"\000i\201\005\244?_;y\271\226\345\231\277\030\300\225){\252\277?vJ&\277\021\312\301\277\265\355Hp?\236\320\277h+p\304\210\351\177?q\237\363o\234z\264\277$\226\331\252b\035\263\277D\363\237\313\333P\244?\342O\037\213\217_\262\277\274\251\343K?\313\275\277l\2527.\255\356\310?\305\256\017\305\033\240\236\277\276\'\"\357/\310\262?\003\326\034R\352\306\221\277\375\205\016X\002\017\245\277M\004px\372\327\316?\275\3713HB\243\273\277\021K\000\232I\254\276\277\277\2377\374\272<\224\277\2706\362\230,\335\245?[n\314TP\240\315\277\034y\273\205\025\247\266\277\025.\323\307\000\017\234\277\313\033\243\010\013p\250?\303K\353FK0\227?n\3021\322\354\237\236\2776\275\266uF\017\250\277\373\331_\356\037\217\257\277\245\237lr\276\177\271?\344\013\321\004{\037\300\277x\002\206\344\203\365\206\277L`\211~7\241\315?\"\235\344\032\371\247{?\332\344\261\001\373\034\230?4&\025\374\303\277\250?\307\333U\220\272\261\241?\216\205\343\204}bz\277wngOp\351\237\277F\257a\251\306\242\300?/\267\025,\216\204\302?\302\332\214\023\364\364\272\277\365\226\256\307\273\324\314?\371.$\210Z\265\243?\241\235\030\200\313q\272\277\256\271&Y\366h\272\277\014\322\250+\270\367\302?\313\032c\035F\177\255?i6\\O[\342\260\277\3058_\006\367\\\262?\217S\007\315\217\306\261?\314\223\203X\370|\251\2772\037\217\214\2278\224? \346\216.\031\'\314?7\341\362\333g\213f?|\r\306\355P\022\226?\207\356B\365_\356\264?W\323\353\336^\335\301?\2159$\315\223\207\301\277\355P\243\203\264x\276\277NC\277\322qH\216\277]\212\246\324\235\001\264?\216(\337\221\004o\266?\275\242\231\254^\224\277\277}i\203\366\357\030[?6\334G\325S \241\277/|\251\372\200\030\273\277\261\265\032Zn`\323?i\034\020\036\344\277\247\277\361\031\376\275\t\330\246\277N\3519W\270i\305\277~>9\207#\223\236?s\013\3559\345\220\222?\2708\203\031+\353\266\277\024\330\252\204w\214\236?`z\371\031#\211\214?3\372\215\306sf4?\237\214R5\021\266\301\277\263\017_L\320\220\300\277\362\330\267\327d\330\231?$\343\025\365\206\270\236?\236p[\221a\362\243?\tfo{\225\032\254?\365q\n\2401\244\251?\345\375lQj\317\302?\344A&\306\3112v\277\035\330\307 u8\240\277\314\250\331]\316>\276\277\333\257\021P\364\244\225?\244`\374\225*\241~\277R\235\032\235F\325\220\277|q\2726\362\236\227\277y7\034u}\256\251\277\177\000\367I\310\355\221?\255\337\240\336\240\216\261?Qn\224<\223\230\254?\240\325\177\254\241\371\233\277\357 s\326\251i\272?\001\307\201\331\376\275\321\277\251;\275Z\345|\261\277v\277\306\207\025\245\302?L\024\366\202\243O\255\277\203EQ\336\227\225\202\277RC~#\022-\264?@\233\277d\027Q\261?\3336\010\201\270\021\217?K=?\256\024\032\270?\331;\301Xy%\224\277\203jf\250C\271\270\277\0333\341O\327\265\256\277\254\201\266G\001u{?\234\341\325\272\223\230\203?2S\243\033\254u\275?T_z\302\311\212\263\277zH,Z\336\363\303\277cH\314\374\253\353\255?\304\306\242\343\350\027\340?\001\363nD\270f\252\277Vm\335\355\267\262\265\277\256\n\035\256Z\007\265\277Gu\3059\323f\307\277\207w[\273\313\"\246\277V\000\267\317\253\007\226?V\033\257O\324\272\272\277\331\265\357\327\351\241\227\277\004Y\305\217*\340\201?C[\033\321%n\266\277g\331g\330y\316\221\277\265\254\374\207\177D\230?n\020\254\351\254\036\225\277c9\025\262\233\330\274?\316\354\2707S\216r\277u\336\354{\367\217l?\303\270\007Y\233\270\263?<8\224\367Q\024\264\277\222c\204\242 .\213\277j\234T\001\3061\300?vb}\264kA\270?\033o\245\314\246Z\247?\320P6\334U7\303?\207z\231\335\271\032\237\277(\007b\317\364o\201\277\301\372pJ0\205\277\277\254\201=\225x\232\321\277\314\213\344\342\226\"\302\277i\337\343\265\235\001\242\277\007\030\221#3\200\247?\007\370\361\216\220?\321?X=R&\0324\261?-\220\272Rp\363\215\277]\205$\024\002O\271?8\005\024bb\007\255\277\210\257O_\351\335\255\277\353l\357NWp\241?vBvO\367\023\317\2773\030zt\234\254\223\277\322\203\302\213\330`\277\277\215\211*:q.\320?\303s\n\006\034\231\250?\375\017\204\275U\315\241?O\224\240\035\334\357\314\277g\233A\r\357\305\222?\272%\371\212\214\001\325?\037\364T\311\nr\261?\004\206A\354~i\226\277\352\362\"w\235e\305\277UC<B\031%\261?Ks\224\2244\270\225\277\014\300\236\330\265\241\265\277s\267t\375\240j\272\277V<\377\210\230^\306?\226\203\022\005R\335\273\277\264\331\035\310aX\276?nc52\360;\250\277\017U\3721\230\217Q?\203@\213\311\\;\244\277\317\252\232\377$k\255\277\2531(g\265n\273?\254*\336\000\244p\276\277\207\245s\235\274\224\231\277\010\225VW\332\270\220?\362\212n*Q\370\246\277f\321\253\267\321\265\266?\373?\207\240\347*\200?tUA\202B\247\265?\300A\317+\251i\265?\344v\022\3746m\313\277\247u!%\250\034\262?\264JDt\035\271\241\277\205\t/\177;\270\245?D5\263\202\352\332\246?\264\267\024\205\034\347\231?\315\001\242\270\031\224\254\277\201\227\242\302\234\227\324?\025@\337^\004v\303\277\2638W\024\2137\263?\262uE+\314\240\264\277v\226\350\313\010O\236\2772\343\310\224\t|\231?\217\377\207\203\325X\227?\202\2151\333\265\223\302\277\310\022t\356\232\310\271\277\376;\001~I\021\237\277T\212\231\033\r\377\257?\301V\364\220j,\303\277\'\275\346\274\236\323N\277y]\210\220\304\360\263\277p\344\302\na\236\215\277\202:P\376\357\312e?\212E8\035n\001\252?q\3207\255\355\225\313?pB\342\006\242\273\252\277\320\341c\207a\n\266\277Q\240c\377\257\035\273?4\316\310\250O\335\323\277\231\336\200`\236A\240?\237\244\264\302u\270\220?G\202\330f\324\214\303?B\304om\002\022\247?\224\220g\307\313\255\250?\311\n\365NVqq??\311 \324\366\237\320\277\177,\333.w\355\262\277\3210S\261\002=\251?\244\212H\211\350Z\322\277l\343E\033\232\247j?\206m0{\324]\215\277\\}\255\235y\353\301\277\372\242?\325 \311\327?\333\342_\021\340\326\261?\257u!\024o\320\250?\351\037\2378<\002\253?\323\036\246N8\346\301?f\212\220\227%,\253\277F\202q\003\313\252\263?0\302S\215\024\024\301?hk\003\006W\320\250\277P\227\225\307\205\232\300\277\262\021\345J\362\261\232\277\361@\306\207\205=\273?\302\217\036D\020X\207\277l}E1\2427\235\277U\334\t\322Z_\263?+\021^\270\217\330\256?\335\270\362\317\276e\257?\2313u\343\363v\271\277\226\026\025H\360\030\225?\350d\375Z\010\220\260\277vS\247={<\300?\355s\343\2111\236\321?\2374\304\251\024I>?D\272\377:0\243\263?Q\023\215)\242\210\251\277q\350\300;\374\240\275?\036\272\305\'Jm\312\277\264\315\327\035\013\300\271?/\306\341\335\230&\301\277\000G\023n\007\224\217\277\205\257g\302J\265\216\277\323\372\202\')i\254?\237\021K\322\\\001\255?\375CX\341\205z\243\277\243\304\373\215\351\304\300\277\310E\240\005\247.\313?\304\334]\250\317\313\225?t\025\341<\377\235\241\277WSf\221u\274\300\277=n\205\343\330\236\264\277\027\374\343\241\267\235\252?w\316x\023o\305\241\277\237\241Z|\256\201\316?\201\026\263\007\316\235\246\277\250J[\252_\227\231?\306\253\213+\272\034w\277C\264\314\336\353\275\273\277\344M?[\214i\252\277l\003s\273$\321\256\277\361sJ~\t\337\270\277#\335\220\224\371\n\221\277\307\177\037\323Fo\213?\342\003-c\026i\331?\307\272.\031\327\340\261\277N\211\224\355\ta\300?\304\215\346U\360\210\274\277\363F\330\020u\005\326\277g\212;\\M\010\213?\027\177\000b\243\025\236\277\261\024\321J\260\211\265\277f>\243\253\217.\240\277F\235\003\0027\340\260\277^?O\316\226\316\271?\0354\2271\2661\305?B\264\001[\203\005\021?y=\366\255\373\316\250\277\311\203\252\030~q\260\277\350\206\265\271\231\000\252\277\360/\007\330\362\347\245?\244\263cR5\n\242\277)\226V\002>\346\224\277\037g\237\345\034\202\265?\021o\240\021\264p\260?\214@\014d\r\032\212?\243\371\325\177\307S\276?\304\2346B2\007\220?&\215\022\305&\326\307\277{\362\204\336\325\240\212?z\300\305\272\246\005\262\277\350\345\017N\n\004\266\277\030\200\363\335g\003\310??\354\007\311r\032\245\277=\350:k\255\210\264?\353\026\366\232\323\256\273\277\036\251!\014i$\217?-\267\373F\3268\223\277\212\331V\247\001\364\267\277TR\000!\000z\217\277K\201\024z\177\023\220?\361F\336\332\350\233\237?~Y\332\r\222\023\216?\020\262c\342\025J\272\277.\231\236\300\353K\266\277\013\276\223\353`\322\241?d\001%,Tl\275?\232\336[?\230/\246\277\252\313\020]\342\270\251?\032\213w\225\342I\276\277\331\222?\036\246eu?\350umGr\252\203?\365\375<\245\246\344\254\277\200\227\202\240\207B\212?\330\013\333P\235\257\261?\246\007\342\013\254\264\241\277C=y9\331o\223\2779\240<\307\317\311\301?F\315x\002\366c\177\277\252\370\207sF\334\277\277f6\207\261\3175\251?\001$\001\330W\227\241?6\323\014\237K6\260?j\\\231\367\025B\245\277_\355\016\353\216\270\330\277*\270\240\303U\030p\2777F\025\2744r\266?uT\2114)(\274?\372\264\230\217f\230\253?\301\007\237\255\207\352\270?<f\336\224)>\275\277\251\322I\034\334\305\252?c\n\032\232\327\326\300\277~\224\357\330 \313\251\277\274\340\276\304\333\007\277?c\277\343\332\214\327\270?\215\000\352\312\211\353q\277\206\347\007!\327\243\323?\322\215\267\326\t\363\263?\017;@\372\223\332\266?\353\317\017!\231\361\252\277\242\207z\001\270\235\260?`0\205\022\250L\240\277\030S\010\234\315\206\272?\016-#\243\224\030\270?\204\301B\361\242\316\216?\315\310\242%\'\004\244?\006%O\3125\351\256?\014?@\375Y\325\276\277\215Jb\206\264N\261\277\271\341\244\006\271\232\241\277\204W\205\237\021d\257\277j\321_\n\214\321\273\277\376\204\342\272@ \257?\376\363\376x\254\302\232\277\324\214\322p\350\312\262\277&\205\036\260\220\207\254?\037D\375\r\010\310\263\277\241\350\023\024\363y\232\277\206\314]T=\337\267\277z\257\227\322\017\347z?\212\314m\236\355\367\276\277D\310z\334\000\002\243\277\350j\306Y\"t\262?\211\235KL)\325\273?\233\245}\213N\346\301\277(\262\221N\031\346\266\277\003\217\200\317\312\023\304\277T\341\324\201$\022\265\277\245Rms\033\363\260?\256\214O`\013M\211\277\270\177r\r\226\"\302?\265\225\255O\270\004\265?\305X\360\311\205\024\271\277\273\227\tc\300\356\236?\253H\242R\t_\300?c\333\362\334\030\235\265?\205\274\303\2531{\262?\366[\306\032\240\222\303\277\374\332T\321\301m\261\277\304\321\223\327\240\r\323?T;\222\352%\217\245\277\275z\212B\223ww\277\003\311\214\224\273>\206\277O\'\017\357M\303\254?\267\267\301\001P\277\217\277&\313\216\332\323\260\267\277Z\361g\3303\030\273?\312\266\305F\304H\260\277\002\343\217\203\022\350e\2777\025\250\210\376\266\275\277Fp\326D+6\220?\222\374X\026\005Qx?{\362\302\3453z\231\277%=\r\311fd\271\277a \332\327\004\016p\277\035\3528(N\207\256?\226\'\276\352z\310\240\2776\321\004\357\356\200\301?.\213\263\344HY\244?y$*\031Y5\233?p\310~\250\303\330\254\277\357\303[\327YEx\277\331\313\023\357\227\030\251?\033\341\200\303-\026\326?_\223\211\246\221\267\247?\211\322[\352\257\271\227\277\375y\343GE\031\260\277\027u(\247\255\363\325\277\246\245\225\0303\271\253?L\256P\364\233:\303\277\201J\020\304|\021\304?\235A\276\3636\035\236?hvc\324<\337\271\277Po-\250g\320\260\277\223\363\260\363\307\363\327\277hD\377[\317\236\242\277\355\354G2\237\365\256?\257>\337#\344\233\320?\265\320`>k[\233?\031p}\325H\334\260?\230\336H\263^@\300\277?U-3r\364\211?o\007`\210\010\226\204\277\240Q\216\347<\'\250\27731OE\266O\307?mY\353\217\276\244\222\277\254\273\314o\032\276\251\277\364js5\324\t\234?a\024.\203\024=\322?-\217\034\237\005\006\241?\322\035\2363B\352\255\277\254=3\327\232\342\253\277\355\226\320t\000\036\242?\246;M\362\356,\226?rA\020\026d\347\260?\3716\026\362\326\277\201?\275\023\026\356\364\335\244?\002VOzk\333\264?:\273\240#\270B\222\277o\026\014\302\271\364\305\277\"\373\rM\352\212\236?\304bj\360V\261\306\277Gw\273\274q$\262\277c\236\365\367E\200\270?,\023\216\312\021\006\213\277P\026C\364_\212\313?\237\236\350\311BZ\263?\006t4\036\357k\225?u\205e4\345\233\271?G\227\023~\203\003\322\277\373/^Lc\n\310\277 \270v\213\276s\263\277\272.ww\253\356\260?e\312Ge\265\220\225?\362e\005\245\367\333\242\277\345\36642\201\006\272\277\033v\216\207\341j\246?\036\361\330\275\230\233\242?\341\376\237k\317*\301\277WGT\006[\372\302\277\256zIL\215\315\225\277\257\002\241q\272x\256?Y\245{\314\007\330\302\277\320\3409\335\346\332\314?V!\320\331>+\252?|\303B\334R!\271?\025\224\301\376\2142\270\277m\252\253.0C\304\277\342\203D`?\322\217?\274\362s\344\231\247\246?;\223\203|\347*\302\277\3144;\310\032ek\2770\300tI8y\276?\336Q\316AC\232\267\2770\341\267\301\033\020u\277#\370\3630\257\345<?\336\023@\311\263\352\305?\343\010M\303\273M\251\277\010\374[\235T\373\240\277;\376\034&d\331\213\277\226;\321\320\377\317\277?O\343r\233\204\273\257?|\326j\236\213\000\235?\243\262\250\006\332_\250\277]r\024\256!h\235?\007\2223\371\2030\231\277\022\334\226\304\3311\230?\272\262lg<\265\263\277\0373I\310\213U\250\277<\332\376\34569\311?U\205\225\326\363\257\266\277\035\367\304\006\t\360\236?\0334{b\334\361\240\277A\351f\243\340T\253\277\241v\362\272\325-\224?u[y\222.\"\267\277\021O\343\035<\310\260?\031\3532\272\257\330\225\277\005\240r\336\203\200\300\277\376\216\231\337\0277\252\277\033\247\306\334\177\221\265\277D\007\355\215\030U\271\277(\005\264\353\330\215\267\277\0249\t\243\240\246U\277\261\353\273@\230\214\262?o\354\024\315\257\363\306?\350\376V\t3q\276?\237Y\027\264<d\240?\221\302\214>\374\006\306?\306\267\231Z\264\241\231\277Z\274\212\327\'\212\241?\264\213\375V$\027\263?P.\273\253\032b\305?G1\010\033\016m\241\277\265\362vE\230u\266\27789\372\257\315\374\237\277f\2406\314=\273\257?\246\221\037\362\337:\266?W\361\305\304)D\226\277\231\354\023\2208\225\263?\242\334\246\255{\314\245\277hH\327\227\210F\257\277dMc\244\314\254\302\277\221\337\324FT\264\313\277e\301\312_8G\232\277\307\367\002\343\311\270;?\022\001\001ZXA\314?\0108\231\037f0\224\277\315\030f\023\361$\244\277\203%\033\315~\301\307?iJ\236WW\303\302?\022d\355L\033\252\314\277n\037\314k[\212\303?(\205\271\013\321\255\312?\250j\370asO\263\277\2141^w\022g\300\277\031gl\357\307\375\264?a\350\201\303R\335|\277\357t\327\3543\026\260?l\361\005_~a\311?\246\3054\305|&\255?\317V\355\235\316\212\263\277\0369\356\002\365d\241?l[\371\357\301\214\241?]\177E4\241\253\221\277{\203\233~\316c\246?\036a\"kX\315\244\277n\316\317\314\355\257\275\277\347A\032\016\316\024\300\277F\325\244\330\025\305\264\277\343\333\032\030u\315\240\277\027\366\207\025\3344\272?\356\214\205\361r\177\220\277m\372\363`>\226\235\277$1\'[X\300\301\277\356\002\366V\242\377\267\277\320\'\023\250\232\206\247?\232\216/\2729\034\213?\371_u\314n\215}?\3501\264\327\200\231\311?+\006\263\3561\010\271\277=EIie\234\267?3\251\234\250\333m\227?\261\351E\353\3613\302\277\222S\375\213\335R\245?\220\304\220\211\243\033\241\277\346\375\312\366\001\307\222\277\222\270\211\rsR2\277\360\004!%\222\360\265?\014M\314a\251\n\260\277\214i\234\322\311\207\303\277\032\353F\211L\317\212?\017\375I\326s\246\255\277\034\025\226\2443\325\313?!b\303\016\304\230\237?\362\341\346\206C\204\247\277\350\373\336\177Hum?\334\200MP{V\240\277\024p7A\036\304\305\277\373\277\251L{\275\250\277R\317+fViu?\n\302\254\356\025N\300?\220rg\361\226\306\303?\245Z\034\324 \250\242\277\025rF`\241\256\220?\007|\005\356\271\360\264\277^\271\363\005\354\331\260?\312\346\321#&\234\254?U\t\336~\014\260\220?.\025\210\227\"\035\222?\023\317\361\326_\240\246\277,\3452bSI\260?\002\270\227\210\274\276\272\277\247<\367\222\241C\236\277\265J\215\201\233=p\277\347\273\202:\321\000\266\277\233\n\223\317\216w\272?\374\272g\271\315\177\212?!\206\271.\312\323\226?\304\000\303It;\252\277k\330\033\034\361\334\244?\242\005y\263\220P\262?\244g\234\342?\376\306\277l$.\001\247\326\212\277f=\341_\016s\263\277\224V\236z\224\323\257?\032h\0055\243t\302\277\301\263\204D\376C\263\277\026\362\035t\002\234\235\277\235\n\330\212\004|\266?\352\311\314\333\324\346\242\277\360\2046rQ\261\225\277\362/\243\177\221\273\242?c5\256i\352\230\237?\357|\344^m\372\246\277|k\341K\251\033\247\277nG\275\322\273\022\267?D\251\030%B\264\271\277\345*\3079[d\246\277\202\311,\350\264\177\246\277\232E\337bgP\275\277\321\254(\340g\024\236?\201\321\254\313\007+\241?\226}Q\246\315\272\240\277gL\005\344]Z\245\2770o\366x\265\034\251\277Y\\\246wF\303\240\277Yq\r\236\262t\320?\313\356,\020E\006\253?mq\363\207=U\266\277u9\311oM\374\243?\272\2349\006\310\300\234\277e\361\005\300\324`\264\277\264\313vR\017\024\253\277\335c\2235o\t\277\277\224\355\274\005\023\003\301\277\010\265LF\336\336\304\277G\323+\202v@\243\277\230\362+u\202#\310?^\235f6)\177|?\351\210\250\'\370\213Z?\016\006\375)\361\177\314\277\347\023\2277\217\257\221\277\213\002bY\222\201\302\277\362\212\313\375\220\267\324\277&\357T\007D\036\333?\343\262\351;\370A\237?V\252\207\035\210\034\240\277\251\314\253\275P)\324?\2413A\005\370l\306?P\222gU\350,\267?l\217\016\204~s\270?hh\023\252\274\227\312\277\010\311\336\264\333\262\262?>\306\202\224D\230\262?g\017\2477L\235\311\277W\223\245`V\021\215?\232\251X\304\211\302\260?p\204\233\362\334\\\256\277\252\256\260\377\301\036\275?\020\357\371v\236c\264\277\345\313\353\202~\236\247?\000a\217\370\213[\241\277K\014\236\241\364|\261\277\356bCf\214\035\240\277P\262\354\201\331\343\262?pdI\206\304\301\245\277)\357@\256\214\320\255?\212}y\017\035=\232\277fc\305j\314P\225?\211\017zGr\316\261\277\346`\005\300A\365\221?\234P\023\246W3\265?\356qW\354\263\024\333\277\230\374_\'\r\233\246\2773Vf6\022\307\310?\312\312\360\303\017\204\265?0\230\000\377\314\r\217?\236#\322\313\201\312\233\277\364\037{tal\303?\260\r\211m)L\216\277\253\t\033\256\356m\303\277\337_\332\230\257\006\267?\311\223\277,\364\344\266\277\207\273\234R\014\312\230\277}\345\022)\331&\200?\017\0105\330q\247\311\277\322bR\257\266\235\310?\336m\234\274\245\257\225\277\322\303v20\036\311\277u\260mV8Dh\277\263\210\344\260\3474\277?]\355\360\275\300Q\222\277{\205\305\032\033\274\272\277\364J\2455s\345\276\277\312\240\270l\267a\250?\2436-a\360M\301?\225\022\334X\0339\305?\324\276Y\300\007\030\263?\270\326i\370yh\305\277\347\210\342\220/0\244?\274\025\332\224\307N\300?iC\2661\002Y\265?\244\350\263\264\244e\303?\037Vk\373,\257\235\277\355\255 \375#|\274?a\373-\261\252Y\213?x:\\\004\251\030\242?Nj\2018\325\364\250\277\030\374\200\236F\230t?\235\272\334\0042Z\204?\037T$\365\310\340\310?\003z\220?p\316\252\277\2755=\304\352T\314?\320\266\222\370\246\204\216\277&\016`\367yx\303\277\351x\003\267\222\264q?\226G\325E\213C\237?gm $\252\376\252\277\211\376\361\377?\273\306\277\211\014*\026Q\246p\277\221\01495+w\246?\3601\337\275h\266\220\277\014\300\"\035\214\256\225\277A7!\020g6\201?\324z6\342\336r\317?1OiaZ1\265?@\014\345\252y\007\261\277X\360/\233p\362\300\277\365\033\303]\266>\326\277\207\356@\010\372\207\216?\234\033\260u\366\360\262?8\327\32706\256\263\277\330\237?\333\t~\305?\255)\034\342\316\223\201?%\027\367K\351\001\257?@b\266K-!\232\277\346\305\262\257`\232\260?\376\364)\277.\326\271?9Nu\"\247\333\220?\035\"9\003\257\345\255\277\220\205\217\345\230\320\260\2772\351\273\234\222\204\277?\256B?7X\271\253\277 {\210\225\207\225\266?=\357\3167\204\037\242?\244\266El\205\351\240\277\037\370\260\\\317\312\226?D\320a^Tr\206?\027nw\034\004}\242?\355\305\236\245}\362\315?\325\305\270d\216\242\210\277\004\317Gi\3559\243?c\222\001\254=\216\313?\033\353z\325\001\032\205?g\024dN\347\323\267\277\032\277\235~\005\264\251\277\373\212\340\207\244\275\257?\211\232\224\264\240<\222\277\034\350\255\3764\260\234?7]2\217\347\270\243\2773\023,@\243\033\266\277\225\205\t\242\324n\223?sO\347\324\350\372\305\277\364\302\020Qn\241\214?\277\321L\365/&\264\277}\301j-\020\342\210\277\302\315;b\r\356\273?\316\345K\2269#x?\031\306C\\\372c\302\277\373f\225\234v\302\305?o\355v\237\321`\276?a\261\243\333\300\221v\277\'\241\264\376A\315\244?\231\327(Z\311R\272? \210\010\315;^\241?|\212X.\230\\\260?\'\207Q\363\212I\231?\303\016\315\013\220L\303?\034\345\376:\206\nq\277\206>\246\315\r\004\206?\261\361\302\177\306f\272\277\327\324Cc\001\315\250\277p\343\317*\'\221\221?;U2\001\265\277\305\277^\250\226\372\316\020$\277\352\235X\247x\313\254?\363\355tA(Y\300?\003\333go\316\375\275\277\347\352\277O\367a{\277N-Ri\327\247\222?Kj\2279\324\231\234?>\351@\354\3277\230\277[\267\2050\234T\243\277\2656\322\222n\005p\277)\205\'\340\263\n\274\277\215\344\371\251]w\270?\000KfF\304V\241\277%x\271\224\251\354\261\277\277*_*\375.\275\277\004\320\351\004T\234\233?\207\256\2423\317\243\273?65\'\216Q&\224?\001\372\377}\340\036\274?\362\022\031R\016s\205?\006u\374\023XZ\244?\032}\214\226\347\251\255\277SN\223\264\r\206\251?\320U\241\036R\304\300\277\310X\260\203\'\201\327\277\220RR\216\256\306\214?J2\373\2116\033\276?\277\274E\205~\213\256\277\277\237\001\234\357\303\304?\t\3072\340\371\"\272?n\233\020K\217\232\250\277\346\227\203\to\033\272\277\353\224\030):o\262?n\rT\227\215\315\243?!R\371no{\225\277\260U\311\253\336\244\276\277f\375mI\365\252\242?X\371|\216\221{\276\277&\234\222\250\324\312\320?#~\2326\211\241\227?\336\336\343\324J\373\272?\007\323\256N^\360\210?\353\274\314o\271\347\216\277\2646\370\211\020y\251?jY\177\365T\305\270?^\243\206\247\300\375\232?U@\010\311\236\227\200\277\016RW\202\275\322\240?\227/\037-@\037\267?k;\315\212\312Z\243\277\325\356\204\245N~\205?\341\326\0334\272w\225\277\356\230U\210\306\305\312\277\033|oF\304\375\227\277\025\367s\230S\233\271?IU\276d#\243\246\2776\354go=\276\243\277]\230i\370\376\206\262\277h\013&\345[\024\273\277\344\037\315o\301\355o\277\320#vZ\007U\226\277#\005\372\330\316\264\220?\345hk\035uZ\270?\005?m\341@\273\300\277\370\267\337\277\037q\261\277b\017Q,\033P\277?\356S@\364 2\250\277]{.yk9\262\277\375p\275%\034\363\267?%\216,6\256v\306\277\r\227\240\215&R\303?\200B\353\304\322\361\261\277e\331\323\361\002g\260\277K#S\251\376\360\241\277\371\331\356\215\267\252\221\277\031\313q\264\314Tp?1m\316z%\032\261\2773\016\263)(n\300\277\005\'\362\2432\334\227?\363\n\330\245p\344Z?YN\033*Z\322\241?\321\267%\302\341#\257?Yz5\037A$\303\277\357\221\353\337s\207\267?u\245\030@\361H\303?\026\375??Q\231\212\277h\267N\031\246\t\276?\241B\024c\326\225\307?\326\n\355I\274\332y?\247\0376\253\224\035\267?I\255\347_;`\270\277\276\226g\373\334=\237\277\332\274.|\243e\243\277t\376z\355u\361\242\277eT\254\244r\207\307?h\370>[G\255\202?zG\356yk\317\303?\314\266\351\232\206\223\312\277\217\344\242g\303\377\305?G\244yBk\356\246\277\0039KM\306\311\270?\237u\211\324a\265\244?\226s\342\375 \272\231?\356X\017a\377\203\235\277\263\2374TCI\272?\374(|\335\327Z\300\277\332m\376]\000\017\206\277`1\3731\023\r\267\277\204op;j\200\266\277\236\224\327/]\035\271\277\004\336\365\223f\213\264?\025#\262<\210-\262\277K@\207U?\220\260?\336\227#~\216\207\261?H\232r\271hS\260\277K\376\n5\254\335\251\277\364\354}i\022\303\243\277\324\0170!\260H\200\277\310M:\237`\315\261?\305\305\340y9\005\271\277NB:Q\270\274\241\277HP\243\214\247]\254\277\247d\333\322c\023\300\277YD\0362\272\205\267?&6\'q\034\352\262?\020\210\374\261Eg\250?;\003\2555p\006\300\277\275C\364\344\032\344\230\277\367\014\014\017\273\035\227\277\370Y\020A*\226\251?;\177s9\326\353\245?A\3144\200ykd\277\3663\312\022\017\006\250?L\261\274j\3465\300?\334j\372g\3150\202\277\037\020N\222\300\326\203\277\245\336\335\270QV\221\277\344\235:\021\376\331\241\277\006}B&x\362\207\277\371x\230O\025\343\306?\223k\207]l\316\302\277\323{Qr\265N\237?\007\306\377/\333)\235\277b(\226\020\035h\271?\217\033!/\322d\310\2770\177\032\267\257q\264?\316\373Ez\014\320\243?0\322\300O\241:\310?\2726.Kw\354\215\277\021\017o\204\364-\177?\244V\202\377P6\313\277\355\256c\2611Lp\277L\001\025\021J)\265\277\275L\272N\272\205\262?v\325r&h\246\266\277\311\346\234F\364\202\237\277=\253W\221\325\034\250?\352\2070\323\363\320\261\277\314\246=\r\017/\302\277M6L\307\\\240\272?\277\273\311\354q\014\250\277\353\310x\273\332*b\277\0160y\213\203q\252?\241\265>\031\216\217\264\277\257y\344\034\271\275\330?\025\210\\f\3413\222\277\374\206\204\342\331\306\202?\035g\215\351\374q\310?Z\227\241<(D\322\277N.\317`\277\255}\277\246*\033\021H\376\246?\270\223\030\'\201\330\305\277nA\326\002/\201\243?B\232s=\014\375\242?Xr\204Z\201\335\266?$\240\210n\257\216\304?\264\006\300i\033\206\264?\302\322\254T\207k\234?#\267\025U_\227\261\277{\247\335\273rp\260\277\223@%8\224<\312?\232n\332H\321\321\266\277\367\311\3037x-\322\277\215\027\353\220E\'\253?\332|I\210>\216\225\277{\322\006\213\352:\263?j\225\031\261\221\231\037\277\277\240\357\335\237\344\252?e\002\315\3652\'\271?\tt\306\256p(\307\277\364\323-\203\340m\230\277Q\376\031-\004\300n?\003\317ie#f\270\277\023\032\361\330\375\200\235?8\247\335\257{\372\244?S\003\\\230\267\336\241?\035D)2\275\373\277?Q\346\243\217\370\243\270\277\r\274u\243\246\364\233\277\036 \025\317U\034{?\275?\'sF\260\302?dM\323\200\017\226\220\277TzpF\364P4?\372\354#\216\213c\323\2771\234\316\216Mi\271\277_\271\234\031\262\263\227\277\365s;\035#b\301?\014\2254\375\272\350\211?\332\325\026\032\362\315\260?^\341\334\032\231\277\323\277\264:\343\3163\322\316?\253;@\303I\264\272\277\223\300\236\262\233L\256\277\210\301\330\347R\215\241?\2576\231\212\234_\260\277<\342n\320\003\007\263\277\322\346_^\236w\300\277\263\204t\213x\331\247\277\350\0231VHu\252?\020\002\334\351\302\177\213?\354\372\331\317\002\266\302\277\306\364\203\027Z\237\241?\272~\221\360Y\3361?I\350\220\034\177\210\225\277\006\234\337\037\004\205\334?\361\262E\252\3179^?E\264b4\020\310\265\277\2069\366\315\343e\277\277\007W\265\016p\223\267?&\221\204\300P\006\177?kI]\317\363\207\223\277#+\217\004\212\223\300?\212\214\001YY\020\214\277c\373\2074\245\024\273?\261\033\302/\242@\262\277\266\235\003\0376b\252?\256<UY\367\327\302\277XQ\241\021\025\245\306?\016\303{\024\200\364\320?\200\017\273\210\3232\231\277:Y\004j\201\003\225\277\261\0077\332\262W\270?\357%\240(\331\211\236?|\307T\r\010\270T?\377;M\315\030\251\304\277/\\\325I\206\247\315?\304\311d\317,\275\266?\215\025W\010\250\037S?-j\031e\242g\305\277\267\236\276\245\324e\217\277\'\2776\310Z\233S?5)\276a\333\332\270\277*\247\366\354\300\352\324?\217?:\341\270D\261?\331\237\004?gF\267\277\332#\321\036\231\373\254?0\315\306\335\335\261\266\277H\261\034I\316\030\233?\314\343\365y\305\253\236?\\G\340\253T3\311\277M\035|\370\222\254\225\277CR\241\217[(\276?h\021\225\323p.\216\277\303g\236}\271I{?M\177\221\202~\361\245\277n\305\253\331\351W\304?\220\306\001z\247\013\251?\304Vy\3126\376\301\2773\013\007I>(\211\277\212\013\3366\232\033\243?p\'\0209\360\215\250\277b\323Ycn\273\246\277\260u@&\323\250\265?5x\277\212=\241\274\277\370J\342\321|\t\253?r\225J\010\345\307\257\277\220\265\240\027\027\262\310?O\200\027\257})\242?VU\"h14\246\277?\377\276F\367\363\240\277\221\315\356+\232\331\320\277+\336\374\214y\266\270?C\312\021\333\362\237\260\277A\226\025%\254~\262?\034\220\343)\345d\250?\223OCY\005\202l?\216D\0176\007R\260?\220AT\213\355@\304\277tgoV4\246\226\277\323\332g\312LZ\255?W\376\031y\241\030\306\277;\323\300\32275\232\2770\330!R~\244\270\277\016\272\355N\356\014\227?B\026\375.\354\010\273\277\344\023*\302\370J\302?\376\233>\266tK\306?\211K:\360n\236\262\277B7{\303\333\014\311?1\320\350,\377:\244\277H\226\275\350\353\246\264\277\271\225\356x<\261\257\277}\224\265\312\332\200\236\277\223\237;\022\265\336\300\277\271\257N\320\367\034\307?\370 \372m\030\350\207?\213rZ<\014@\247\277{\320s\177\202\336\271\277\332\325\246\200!j\261?\255\273\324\370\315\224\273?\376\025)b\022A\234\277\207_\306a\233\343\273?=-CM\222\'\262?\032[\214\0230\365u?\354\221\207S\207\025\220?\255\31317\\\224\306?\217\214\362\373\266#\242?1\004D\323\300x\276?O\031\006\025\266\276\330\277\324\345F3\230i\232?\\\241h\242Wvd\277\342p\203t\331\345i?]z\341\2505\333\266\277\274\332>\212\207+\224\277c;\nd\343\314\272?\275\nA0\350\t\247\277\202\010\324\303\034\202\246?E\000\324S\321/\253?\242\342\376\311Wrc?\'\023\252\034\010\033\257\277\263a@\264\340\355\225\277L\231\016\177O^\303?\222\010\273\007X\003\307\277\346\251\214\002\"\347\177\277\342\225\2511\212j\274\277\327\266\026\226\267\232\203?R\311\344\014\221\315\325?Ua\377\226\270S\306\277`\314\376\352)\275\260\277s\343=\\V\363\255\277\245}s<\364\237\261?I\203\371\301\204(\257?\344\363\016W\214\347\242?9\353\035\231\267\361\267\277*U\362X\262\321\225?\177\377i\323\027\216\301\277|T\027K\220G\271?\364\245hm\016\231r\277Q\273\r+\nv\242?\360X\013\303G+\260\277d\264Vr*I\262\277\036\307\005\004fb\274?|u!\245~{\230?\007\\\261\221k\241\255\277Btx\245\362{\252\277+(O\244\346\256\245\2770\263\212\230\225\315\265?\006=1Q\2216\252?\223hE\023e\377y\277C(\300\242w\016\252\277,\002P\2726\254G\277\305\214\326\325\0175\265?\254\001`lAN\276\277\364O\222)<\001\253\277>\274\243\210\257=\305?\321.\230.\025\031\301?G\271%\265|\370\251?\337\2626i\2475\225?yxy\r\245\252\233?\377S\260\tb_\210?6t>3tG\257\277*\206\246{\300/\305?\336\026iwTp\177\277)\352\'\223\325S\224?^\222\240x\360\236\300\277\307\355\263\326\202.\273\277K\311\364k_\305\207?\244wS\3777\370\301\277\342l#7\355\334\257\277\271\223\006\2027\204\260\277\016C\317\216\320\240\271\277\322\032\224\336n@\277?\035w>BI\236\301?\255f\324!\201/\250?X\227\215N\330\033\266\277\010\013\010\222\244\'\321?\232<\230\000\024/\262?\277\260\224\226\371A\206\277Q\261y\374\326\245\256\277\326\227\224\257\271%\243\277\270P\305\212(\351\255\277\370;\221\"Tn\270?\376;\332\263\271\365\305\277\272\201\005\337\227\243\243?n\240\264N+\247\300?\315\231\2160\246\222\256?`M\316\350\303\010\202?\232qo\\\344\256\215\277)\360\177\300c\254\204\277\245\242CI\231\225\270?\017_\314\023aB\231?\263\232K\305\3676\274\277\317~\222\3643-\261\277\206|\314\0145\262\257\277*w\202\253\335n\305?\222GC\000\210!\275\277f\014\r 1U\340\277\005\312\276\251\330\235\264?:$\0332\311\261\243?;\264\233\376\034\034\261?\275:\224\353{\200\241?.\326\307p^\351\241\277/\321:A\370~\203?\205\360\025H{\000\217\277\264\335\222fK\217\277?\210\241QW\356\376\251\277v\273\337\357\207/\221?I}O}@\375\260\277\302\000\266\341\332\334\270?Al\271\360$\320\276\277\000`a\251XZ\323\277K)]\234\222\307\250\2774\t\256{\247\231\262?\245\017\337\370Q\004\234?\310]\rk\005\261\205?Rd_\273\373\277\243\277\341\377\354\326\364R\214?\214Z]\213xX\311?<;\234\310\3625\246?\361Zp\271\261Q\256?\351\333\242\360\333\036\325\277\367\257\235E\300\370\237\277}\356\355?\332\033\302?\013\324\273\322\347\205<\2773q\274b\352\347\220\277\255sR\216\307g\261\277\007\375L2O\307\216?ZU\274\247\256\254\275?\276Fs\210\017\331\274\277(\003\234TMe\232\277\243\2260{\252\n\232?\034\t\340U9#\302\277\3724I{\224\225\270\277L\377\272\177\366T\266\277\261\257\253\261pz\272\277\233\311\213\230Py\234?\002N\212\265u\036\300\277#91\021%f\231?\273ck\336i|\271?\366\037\036\241]\014\277?\026\233\365\315\201\244\247\277\273\262\224\221\351\320\303?\335F\013\354\217\333\264?\2740\211\367h\235\276?\221\232\347\363\025!\277\277\365\240v\"|\006\276?\306\260\342k\267\377\261\277a\330)2n\311\270\277\227\r\3254\035w\303?\n\224\023\360\255\241\303\277|\246%\022\326\r\253?\353\314+VC\230\275\277Cy&s\227S\264?\264\267\362\345\034\261\300?\0371@\242}\253\237?\017\206E\324M\001\272\277$\317\342E\005\364u\277\362\205\312}\336\032\262\277\374\2318\365M^\252\277\032\346\327^o2\262?\244\203R0K\206\204?\234K\221\314$\217\243??\311\216)\216%\307?(hq\262\334\000\237\277\236\036]+%\315\300\277\272\327\252+\356\004\273\277\312GA\'E}\325?\343\234\366p\237\330\266\277\250\230\0174\307\265\264\277\010Z\371g\306\340\246\277\026I\004\212Ml\303\277\272\357\027n\304\020\223?\t\307\345\032p\356\220?&\340,\271\320\305\271?\204\217\347x\377\001\204?\350\211D;\237\037\270\277\225\304\312\321\365\031\310?\355\346\206#\330\214\302?\347\337\323\001\376\305\266\277\326s\025\311\241o\251\277i\326\334{\002\200\265?\276W8\350\352\264\216?\007w\004\332_{\266\277\313Q\352\314\250l\204?\022-\n_\312\327\304\277S\223i>\273\225\270?\001\224\363_4 w?E`\227k\274\r\255\277\266\024\203\276\201\364B?\326\235\r\374\324\264\302\277\341\214 \230\277\244\314?p\323 9\371\355\263?\257|F\030W7\214\277\2674\3357\276\212\305\277\251\224\007\312\'\344\261\277\004G\342\014\221\320\300?\246\251\013\302TC\237?\026ou\235=L\302\277\016\257Xt\364\373\304?\026\013\314Kj8\226?\306\236\217\351\271\005\261?\235\257\324\322N\334\310?!\373y\273\035Ha?O\273\010nK\007\241\277S\373k~\2653\263\277`8\030*\020\310\231?\213s|\000\177\370\250\277kS\256\007\204\320\251?\255\272Na\301\013\251?\201W\003\346\300\304\261?\3673\260\254\"\311\262\277\203\331|\330\2674\266\277\006\337\3265gv|\277\237\362\356\376\035\271\272?\244\317\246sT\262\220\277G<\241\007\336X\261?5\363\333\306\326\265\221?\365\212\000\313%\211{?\222\251\253`\006\317\272?\227D\033\253l\025\234\277\373^\000\2407j\253?\232\3416\036\352\372\256?\262\261;\265Im\323\277\325\022#\004\t\205\303\277\033\332\001\355\354\351i\277]:9\222\355t\305?-P\362\233C>\266\277Sf^\341\177s\251\277\227~\325\\}\225\234\277\241\353\305{cT\303\277\316\275z\233Sbz\277\321t\277\"\304\301\246\277`y<\365\366\200~?Ve\\j\022\252\266\277{\315\007\217\316\304\230\277+S=\230Bm\237?\236<\021\274EJ\324?\225e\330\271\371\035\273?\356M\023\207\030>\260\277\334\024K\356\356\245\277\277\214\307 \211\246]\256?J\307\177\207m\261\235?<\234\307\375\273>\257\277\220\214\321\2301o\330\277\240\030}\210m\236\247\277\333\353\'\247\222\365\202?<\333\363A/\211\271\277\205\306\325\243\033\352\243?\335\313\213\0230\036\302?\335\003\323\314\027\272\302\277 \005w\325T\021\300?\014o\341\267I\370\271\277\007\272s\361\221`\245?\232\303\016\007>\323\302\277\311\264\034\235\244u\253?\r\313\353/\200\256\270?\002\255+E\247\333\264?7b%\035\223\341\307?X\340~E\343\303\237?\226\032\036\267\007\215Y\277K3\307\233\004\323\244?\274\370\3637\"o\246\277\033\2219\000[\377\247\277\215\023,x\274\341\255\277g\036\026~\347\217\217\277\030c\'\000\032\260x?W\246\034\320a\216\241\277\351\215\225\221\2470\300\277\364QU\001\000\177\254?\227\t$j\026\314\252\277\275\347q\264\345\270\267?\336\034\331\023\\\321\321?t(\362\0136\003\262\277\237\027\300f\316\244\233\277\347\266\344\321\261\007\235\2777M&\022\323^\251?w\237\360\351\004\375\207?\364\323L>w\256\276\277tS~\201\224\372\311\277\360\352\211IB\303\252\277\247\036\224\356B\335\262\277\324l\245\342\336\310\227?X\031\374o:\001\177?+3\313\376\225\305\213\277\207\241\032\261\360\r\241?\243\022Rr9b\205\277[\323\370\310\336)\206\277h\335\035\237\325\332\240?\030\306\300\3445R\245\277\347m\277\216\rA\271\277\025\240%zp\020\243?\306\'\356\035\2149\317?\343\020\275{F\232\302\277\342#\260\277\244I\226\277)\3460L\374\261\275\277\317`!\200\334-\260?\236\325\'l\303\235\311\277\234\002\266\004\272\033c\277*03}\224\345\260?j\277\301o\255\013\205\277?\001\206\227O\300\300?\262\322\333J\347\372\247\2774\00662pn\275?\220h\000S\016\000\246\277I\031\207\302\277r\233\277r\001Pv\231k\221\277\245\004nF\375\342\261?\356/y\243\275L\242\277\236\304\215\313/\r\270\277\032\335>\321U\305\235?\277p\354\341#y\270?\0069\202a\256\271g?\353\305\263^\177\261\263?\250\023\247\305\032^\302?b\355\205[\025L\212\277\2164\203N\300\242\270?\312\325\247\302\234\020\310\277\265u\351\257\365\226\210\277\345a\265\216\225\231\301?S\304K\363\325\031\273\277s\027\356\335\006\205\302\277\334o\033nC\001\237?\343\312\270v.\262\251?\353\312\0074\r\356\304\277L\245\030\260\006\323\300?\252\224\2251\027\251\246?>\314\375\007\374\256\254\277\343h\315\361\363\255\303?H{)G\274\257\260?\023\352\236\016\233\237\254?\236\263\000\246\355>\244?J\017c\312\322\217\236?\373\273RN\313+\224\277O\226\301\214bO\253\277\236\321\r\007\247~\275\277=\303\"P\t\322\260?*\250\007c/\360\232?\'o\227f\025\344\263\277)\273\314\235h\230a\277M\035D\316\254\337\240\2774:\022,6\372\271?\257;mcT\263\322?\205\252N\226\206\035|\277(\241g\222\206@\262\277\341\352\246e\263\314\304\277\321\377\321\247\256I\242\277\246\002\"b\217\262\262?K\374*x\375\022\300\277BJ|x3m\235?b+\177\000\370v\261\277\276\t\217X\230\314\265?S6\266\n\3256\310\277\025/k\222\200\371\304?\005Q\351P\357\030\244\277\010\252\357^\n\312\241\277\275\013\200\316\221\010\301?n\324\237\343m\314\317?\253\336uN\275\032\204?\221\256\332\262\361\261\227\277\203\025\036i\265\006g\277\272\0378\305k\301\252\277aQ\267x\263\355\261\277\254u_L\000\354\276?\322D\330\314^2\247\277\263\213\013\323v\221\266?\301\322\306[\202\257\277\277\315\304\307\317\322\234\206?\034\246fsU\210\250\277\005\255\025\214v\262\261\277=wlV\226{\247\2776Y\013N<\032\244\277&e\255\230\233d\255\277\217\210|\260\276S\255\2771\025\326q\231Zw\277URC\360*\266\260\277\310MW\021\355\327\254\277\363\025\342\342\375\016\330?Y\3045X\014`\254\277#SWU\262$\240\277#$eE\207\334\252\277\345E\204\203\3665\240?\250\t\252\302\347\010\246\277\377\233\233o\'\'\265\277\247\026\203\025g\216\267\277\307\374a\245Od\260?\013zo=s\023\227?\'eag9\351\226?\336\210h\247V\322w\277\034\007\352\315\010\036\300\277\335\322_\330\031\001\241\277\372i\321\317\211\340\332?|Z\035\207\322\016\227?\232\216\223H\007\025\253\277\241\244\337?S\275\240?\312\205\310\344<\211\225\277\322\220\246\222\365\030\257?*S\360\253C\260\300?|\244e\3625\330\214?SQ\270\020\237\330\227\277;\027|1\256\354\270?K\026\350pp\253\323\277\307\371d\211\367\243}\277`\322\342\353\223{\277\277\312\325VXM\373\273\277\014\344\363Q9\365\320?\326\227M\t4\254\247\277\371\303\007\300\324n\243?\311\262\261q\027\"\231\277\3347\035\001\220f\263?\243\206RJ7\302\225\277\215\275\355\306\364\332\266?\r7\346\033\331\360\315?Q1z\305\352\000\255?\213\324(\032U:\263?\367\324\315j3U\325\277\330\234\312\344];\226\277\023\205\367\260\251\300\302\277\352\030\002[R\272\217\277?t\255\033\262,\272?Kw@\335\340%\227?{\352\220\205o \274?\035\317h\251`\236F\277\236\005\222\210\005>\303?\305\0042\263X\340\221?\331&\364c\231u\263\277\244Ki\232\247\350\225?\nF\212\255\236y\245\277\301\010\234P\203F\272\277\252\251\250C\261\\\307\277\323\010\000\033l\200\250?\267\037\3554\020-\240?\274\336\360\257\357\255\315?\005\260\261:\356.}\277\016\306\037\351ER\260?\3358\026\377\2339\257?\364(\233\212\361\236\263\277\3247\374\n\262S\260?g[\260i\375\204\221?1\344\355\273\360\242\240?\013\n\303\223\342\203q?5\212\025\214\177\370t\277\234\266\361\267\032|\260\277\000J\032\356\201\323c?\247f4{\004\313x\277M\276\343\376\342\032\261?\247\025\331\245#\201\223\277\331\326\002\334\266\\\202?J\032\222\ro\254}\277\255\367w\263\026\031\310\277\274\336\375u$I\302\277\032\t@\257tY\247?\221\326\210aP\322\273\277\t\342\236,\330\206\304\277?R\316f\367\244\303\277VV\222\262H\263\235\2779\027]\305v\237\305?\325\031\271;\336\310\240?F\327\307\t\3254\252?: =\177\220`\233\277.\352\352\367\250\325\322?h\2230_\224\360\223\277\r\002\357\r\313c\252\277/\375\326\305\177\270\266?\255\271[\240\307\356\270?\361\224\312YC\333y\277\370\270\352\326h\007\211?\215/0\211|7\252\277\034J2l\367\230}?\215\\\024\365\030\357\204?\255t\223\207\206\343\272?\336=\305\213\232\351\264\277\202[Q~o}\221?\215\301\205\001\241\373\303?\t\343o\375\\\331X\277|J\321\207i\321\276\277\230nb\316\217\262\254\277f\373T\264\246\260b?9\'\360\216C\013\270?v@XJ\"4\263?-\204(hi\352[?\366?s\246\263\022\234\277cx\200\365j?\265?\026\270H\252\233\336S??>q\311n\026\301?\205\342\376\305\272\022\321?\240\0303\312\333K\251?\326:\361^A\257\271\277\241?\372y\241H\307\277\341\035A]?v\312?\365\337\213=\330\265\225\277o\337\262\243\2068\252\277QK\223fVs\300?\r\246\313D@#\277?\2322snIfy\277\274\033\270\202q9\261\277\032}\264\300\024\207\316\277\2060\275\376\0373\277\277\313\002\334\226\310\332\231?}K\224Y\277)\230\277\362\206/\027\345\234\232\277\333)p\231b\252\246\277\003\261\n\321\360\027\222\277\004\311)0\310n\300\277\025\250\235u\035\027\253\277\330c\276-\021Wl?\342\216\231)*\235\317?>\313\202\215\r\377\272?\240\030y(\030\260\271?\0075=d^?\205?\016\341\323\362\256\367\266\277n\225\034\256\315@\271\277x\371\215\340<\253\272?\235\345\017\241D \306\277\022R1\230\335\n\277\277\022\241\002n\253/\271?\005\231\374\014\375t\252?\344~!.p\010\305?.u\323\277L\233\242\277l\235O%\375\265\307\277\262\274\213\t]\032r?\312\304;\214\365\017\247\277\352i\326\331\374\264\263\277\271\354\345V!r\276\277bs1\251e\001\300\277\320\347rX&S\244?\214\234\341M\365\334\221\277=\213~\247[b\270\277\314\251\035&Vy\267\277\010(\311U\211\264\260\277\r{\346b\241\250\263\277\371s\235\205\013\266\306\277b\234\272+\307\000\267?\362\325\353\236\263\005\261?hd\026\354)m\307?Q\342@\356\225@\204?M\317\373\371\027R\302?^\227\006\037\031L\252\277\202`\242\016\357D\276\277:\235\345\270\316\003\227\2775\262\303.\336\323\221?\274\223\"+\345\335\333?\356\234\312\300P4\305\277|\226\t\256\236\276\270?\361i\346\336uy\270\277\022\340Uq\324\310\266\277\017\325Z\301\220\350\232?\032\373\250\372:\303\261?kb\027\240q\351\316\277po\255\245I\323\312\277\376\202\311\033~R\265\277Z\0018\r\242%\224\277\210\243\243\235\007\241\307?\267(\030|Sl\261?\243\375\306\347\034\033\301\277V\215\275\226\340\375\312?Hu\211S\371d\260?9,\023q\345\307\277\277K\027\301\361=\031\261?\\\261e_!\336\177?\321\212\247\371\251\003\266?R;\255\310f\313\305?\260\367~\372\364\243\223?\230\361x3\306D\300\277\200\367\360\302\343\321\230?\027\312\263\222\034\033\257?s\024I\273\026\347\244\277\321\273\2459I\014\304?1\212Fg\242.\251?q\014J\236\345\327\271?\351\004\336\277n\231\227\277\214d\200\354\260_\261\277w\305.o\310=\301?\031F}\376\216i\243\277\210\003\316\024\030q\217\277\314\300\3431\250[\300?7\000\013\313B\354\244\277\3232\001\254\344\"\225\277a^\362&\272\346\224?|\003>\352e\224\300\277\034\010\013=Z\214\300\277{\226\227\300.sq?\025\032\022\363\031E\254?\355m\305\030\032\266\276?\245\277\"&\244\261\245?O\325\021\230\215\"\217\277\177\242P\244\331h\261\277\365f\345\372\017\025\306?@>`w\240\371\256\277\206\005\210)\030\372\260\277v\000Z\337B\303h?\315\022\376Tdl\306\277jd\343;\244!\277?s>\034,\2142\266\277\177\355N\246\370\234\211?\234|\261\213Al\302?\245\004\306\242\235I\271?\300\310\010\250=\314\266\277\305vg\251\212\035\244?\247\254^my\252\270\277\351\276t2\024r\317?\307\212\216\372E\214\267\277?\270\247\337\377\230\263\277\021\004c\251\346\024\314?\333\245\264\375C7\263\277\251\224{\203G\001\212?C0\'V\020q\204\277u\243\000\t!\213\234?\260c_\300\007\245\200\277N\030\205\031\344D\246?\213D`\005[\252\200\277\224\235\203\'i\240l\277&\\8\000!\373\236?\347\n\021\304\307\330\301\277P\262+\265\310j\254?q\263\201\370>\200\263\277\024\016\321rK\265\273?N\007\271T\n#\274?[\344\360\277\223\221\274?\226i\311*\220\016\253\277\336\333\030\251\247\363\266?rnn\355Q(\312\277\017\275\350\004\226e\212\277\325Q\nV\307\321\306?\367\023\363_G\203\241?\005\222\215\375\274\"\262?\022\n\252\3467\272\241?9\\\013%\004(\240\277v\361}\361\231\236\310?W\257t\333#\315\200?\354\270\370d\3717\267\277\304\024#b\303\273{\277\t\360\371\334\366\345@\277\257\202Pk\026Z\230\277\007\376\2169- \271\277k1\010t\263\243\235?\373\006\220\334?O\310\277\321\374\202<\"j\261\277\255\205~\244\211\343\270\277]<\215\246\272\010\252?\246\300W\022(\306\245\277\300y\357\007\342,\206?z\263\275\312\210\351\267\277nTW\366\0208\301?\030\217\362D+\226\272\277z\270@\0266t\241\277 \352\006\200\237\235\251\277*\367\275\226\033\310v?\362\324\307\000\340\337\221\277U[\332\022)n\253\277\343\236\345$EJ\317\277\372\030\0069\\\264\264\277\300\016\004\342}F\221\2779\335\255\021\365\214\302?\330\205\346\224\370\364\221?\204\372&\377\262\356\314?\346\302\357i\014:\265\277\203Xf\033\261\365\317\277\2147;!\334N~?\274_\017v\231\254\314\277Fc\r}eg\273?)l\023\036\007I\303\277_\036\213\024H\256\211\277\002\360\245\343\275\026\201?\212\340K\207\267\'\263?I\271\231@\240\034\242\277k\203\257\353H\321\252\277\326\216t\223\205\246\311?\t\303\327+\326\374}\277\223Q\010\353R\177\267\277w\264\277\007\375\221\240?-\357w\350\2406\320\277M%f\216lJ\246?\0020\013\016\272\337\271?\225\201\264!~]\303\277F\021\334rS\250\265?\003\247\304ZL\363\246\277c\344^:\257 \313\277\377\327\234C\363=\251?\032-,S\355\274\240?V\345\373\031\375\365\263\277\311\"\323\267\314\216\325?%\2147\316\030\255\243?\020h\245h\336\007\260\277o\251\237$7v\271\277\002$\202l\325\342\254\277\315e\346\354\215p\233\277U\311\363S\333\250\276?i3w\351\221@\277?v\247w\336\227\274\232?MZ\303\233\037\365F\277\007p\337\311\237n\241?ZWg\326\237\330\201\277h\207\355\303\320\035\244?]\026\014\232\036\016\272?\n\225\3047\rr\252\277\347\206\'\357Wi\256?ms\177}w-\237\277\223\026\027\361l\335y?f\363\321\2459\336\271\277Fg\"4\230\306\256\277cC\031\363\354M\244\277\210\235\200\327\327`\270?\215\031\'\201;\243\314?\327H\r\336\251S\251?g\323i\354\0169\241\277\177\355\302\221\262\221\310?\274.~C73\273?@\255\335\232\304f\236?\027\314m\022\003\356\262?\006\205#t\367J\242?2\323w\220\014\370\260\277t)\321\230\262#\302\277\274Smq\036\343\261\277\265\025\023\3448\267\255\277\267\201\376ma\177\250?\377m\306\347\030%\270\277\235+\n\245.Z\277\277\377\363\317\013\215\236s\277\307h\202\224R=\260\277U\201\372C>\032\256\277\236\t\214\317\340\224\255?\\\"\210$\266\267\223?\254\3717\307\237\001\275?J\300\260b\346!\257\277\256[b\3563\206\264\277\264\363\233\232Q\220\273?\001y\270\237\236z\250\2774\200\002\261+\"\234\277\306K{\031*G\270?L\003\311o,\030\274?2\021\266$\311y\221\277E\013\343\217M\030\224?O\301>2\326!\272?\216\t8\201N\231\300?\363=(\273\247\227\255?\r\337\374\323H\262\250?\234\255\346\361\314\244\230\277{R\231\\\3608\232\277\027\2212\"\377*\263\277\362]\022\221~\027\245\277\305U\310\017\217{\304?-*<?\tO\252\277e$a\200\2029m?\010\340 \225s\312\304\277\351\333\233cZ\"q?\272\312\373Y\004\325@?,\333\375\274]\264l\277\373u?$\252F\330?`s\310\377tL\232?\tB\317l\206\337\260\277|}\037\300\307\361\316?\3303C,\354\311\225?\036{\025#\007\260\312\277\340\237:\233\t\355\300?\314\365\t*\023\"\243\277\n\357,W\264\227\261?\252\233\227\236\024\006\262?\251Q\037e\237\307\260\277\373\272z\307\357?\242?zw\031Sv\344\244?\264_\n\n\227\225\263?\010HR\364t\301\273\277=C\007\0148\037\250\277\234(\315\037\007\361T\277\"ze\230W\363\245\277\026\273ZRu\213\221?\023\353\245\361\305\332\272\277\331,W\n\207g\256?\242\001u\013w\230\265?@\310i\030\365\215\243\277*\357Alu\214\204\277\332\202\257\363\025\306\271?\205&\024l\221\307\301?\313\300\331\307k\211\224?\367c\\\317\013\312\264?\326E\203@\212\260\251?\363rs\222\367\371\242\277N\214\307\355K;\276\277\001N\365\322\245j\311\2771>\213KF@\221?S\257\004\247%\026\240?\023\251<Dz\217\300?]\323\266\244\036\244\253?\243Pc\272\351\220\271??>\213\274Dk\274?N\323\372\330\036l\261\277\264B\336\314\364.\303\277#\301%\014$k\212?\217\033\327\363\211\324\305?\t\251MX\001\027\241\277\252~\276\033\201\363\225?\213\357_y\2117\235\277\271[O\355\207\020\307?,\024\326\372\301\243\277?\024$\301\234j\002\273\277\246or\023\235\256\304\277\333qK\310[\230\306?\242\247BC\351R\257\277\2635\234*V\236\270\277D\234\366\320\364\235\253?\215j\365\024w\362\264\277Z\247@2\347K\247\277.d\013\206\313H\252\277\002\025\252\036eJ\270\277\247\260\256\364mN\243\277o%U\200\213>\246\277BS\001\310\342t\315?\300\226\304Y\232\371\177\277\224\024\230\205\365;`\277W\0357E\372`q\277uJ\271\212%t\243?\357\353\331\203\260\332\240\277\023j\312\330j\016\266\277\301\304k\016\362\223\304\277\017\200\250<\323c\245?*O6)\207\317\270\277\307\333\265Z\021n\271?x\232\313un\216\205?fc\370\r>\234\244?\261M%\256\014\324\300?\256\222v\n\0004\260?\003\233j\306\320\274\242\277\020 H\216h\024\257\277\320\025\267\207J\376\266\277\306\253\377\376\324l\266\277>\324t\311\252\'\240\277n\252O\020\327\'\264?\021\241\034\025pr\233?\243\337\245\231\332\365\257?\260/\232\302\335\240\203\277 \\\235W\021\317\267?\026\203\314\272\232\032\311\277\035>%\"\264A\250?\204\266t\024\204\330\266?D\204\0109\201\032\310\277\350Tkb\373\002\244?I\342\244\313{\016\220\277=\240kY\360\343\304?\352\201x8|\345\177\277\345?\022\217\201E\205\277\\\225q\215q\325\307\277\3646J\351!\300\261?\213;v\245\300\031\270\277Z\005X\221cJw\277\342\037\357j6\211\237?X\342\245#\360\217\240? \235\307\207\\\376\274?\237p.)\346\265\275?2\372\337\000\024\272\242?\020\353eA>\270u?\366\016\233\345(\360\310\277\257\27225\270O\276?\375S\353\313\367\257\310?\210>\270\r\021j\255\277\341{X^\305\233\263?E+\3416\376l\201\277.\310\360\233D7\301??\004\343\006n\311\271?a\343]b\276\354\236\277\313N\202\227\226w\253\277\367\021{\264\237\240\227?\001\211\344\016\271\347\244?^t\034\016tR\324\277\274\2259)\036\324\317\277]5\2225\210\323\261?\257\342\033dy\304s\277\337\207\211\256M\r\324?\264\014\273\204\270\354\275\277\362g`\336\033\221\261?\373\017\372\234\262\275\277\277u\2263\014]\203\263?E&X\024)\201\221\277=\273L\254J-\216?\273\017\271FD8\270?\331\320\026\234,\352\225\277\016\370H\"\006\370\257\277\242\200\006\207\032\357\300\277\363\317iJ\267\321\225\277coH\000Q\257\305?\311.\351\232}\020\326\277\027\354\332\356]\n\273\277L\010\004S\325\210\206\277\'\0221\215c\323\255\277\240\377\267\334O#\251\277\336!\254E\023\\\271\277\373 \236-\200O\242?\366\322\026\341\232`\233?A\200\203\364\272\305\240?\220\024P\366\210\376\264?\030G&^g&\237\277\326\220\370+\340\322\246\277\275\324\024b\251C\210\277\240\377\311z8\345\275?/.\016o\035\226\204?<\325\033\242\220\244\260\277\356^C3\352Dw?*T\342J8*\266\277o2zj\201\375\243\277\021\277/se\373\273?\354N\377\357h\346R?\027{\005Dq\272\274?1\326\271PK\330\305?\247\375WG\027\236\221\277\206\312\332\03120\241\277u\005\232\355I \261\277\353\341\366\002\035\344\275?\022\265$\317\250j\243?\217\030\3420\231\311\305\2779k-.\351\010\324\277a\027\270\331`k\260?I\255\366pj\261\263?\247\342\324\214j\322\233?\247ax\220\341B\226\277E\251\277301\201?\206\035\343\361\216o\254\277\240\353@\350\264\213\257\277\r\326\013\341\2376\234?\212 \024\212*\204\237?\021$Y\223\346.\236\277v/.\030k)\276\277\262\367K\372\2562\300?G?>\326\365\223\276\277\301K\362\376\177/\316\277\342J\"2\310\341\263\2770<\312\200\270\261\237\277x\334\245\235\303\n\210\277HR\324\202\203\270\262\277\203\361r\301\205[\244\277\316\346\256\">\370\301\277\237\316}\375\200:\317?\025\347x\257}M\255?0K\202\236\374\366\222\277\026*\270\265\346`\302\277\243\023\202\252zb\224?m\022\305eX\004q?\025\007q\333\322\030\270?\252\356\300\201\0350\227?\366\312b\206\307>\303\277f\344v\321\240\247\243?\334\366\370;\310\352\214?T\224d\372\007\253\261?\234\334\035q\334\234\227\277WI\340Ej\214\205?/\213\220\317(^\224\2778\024\346\001\360\257\250?p\020\327c]\277\221\277\234O\214\310,\354\266\277\365\200\223\343\302\003\215?/\303\330\363\340\313\177?M\034\234\352P\351\234?\270\276\370w<\255\225?\206s}No\301\252\277\006f\037-\217\332\245\277\322\260\366\316!\336c?\337\272\307\220lK\252?w\246~\022\213S\213?\256B\356\360\233\224\275?F\252\320\332l\214\311\277u/\254\366\307Y\211?d\025\253.\270\307\300?S\203l\257[\337\333\277+>x2_z\260\277K*hi\311D\247?\230\013\250n\323\357\273?\272\252*\226\224\312\322?*\366\253U\211\301\216?i\362+\313\357A\300\277X.(U\234\350\241\277\312\032\320\243(\332\265?m\252\347\310@\223\247\277@\363Z*p\225\310?=l\235\026H\215\267\277\300\313S\364\036\325l\277\371\301\'\202\3274\275?\231\302\252\037)\353\320?\307\366\241\217Y\026\221\277\334\325\334\363\025\245g?2\247\002\200\210?\261\277\366w\370l\210\266\323\277,BV\2205\266\224\277\207<\337es\033\256\277h\307\231\207\004\017\245?3\240\210b\2045\261?\207\200[\341]\316\212?9DF\327p\300\251\277\326.A\363\360vt\277\252\33640\333f\247?\344\322\311\361\344>\270\277\"\006\271\330\304\227\304\277\254\020\020W\344\361u\277\220\343\351\3666\014\242?\362\025\271\262\t\257\273?M@\252\244L\r\317?N\271\356\224E\207\231?\0260\301yG1\240?\023\340T^;O\277\277\263\tE\371w?\262\277\365\361\005\370-\030\270\277\210\3544\276\022\377\247?\222C/\364(\347\226\277\366\341\353*\215l\206\277i\265\320\366\362>\267?\007\327\023\303\245G\311?\032\177\213d\035D\214\277\234_~\000\3411\262?\337\024S7\342\032\220\277*\005\313\030.c\260\277\266\t\241_d6\262\277w\037\274\245\r\341\224?O$*8\014R\266\277\237\217\272\207Rg\235\277\343\320p\310\366\020\207?\260\016\270\277\211\260\222\2773s\327O\373\226\320\277G\214\247\357\377\360z\277g\224\002\214@o\232?a\250\301\326\250\030\261\277>i\317V\311+\206\277j#\r\340yW\311?3\304y\340H\363\304\277Xf\255cS\355\300?wE`\334\202\344\255?\230\356\022\317\363\362\250\277\240\220\225!\203\030\261?\331u\002\253:\264\275\277\025\025\2020\325\335\271\277S\362\277\244)\261\254\277\336\257\234\274s\370\302\277\267\363\333v\236\315\253\277\233\263\267\230B\240\251?\207k\301\267Y\315\272\277\250e\200\001\245K\240\277\221O\255\373\177\350\242?\352\344\204qo\316\272?\314\\Y\302\016y\306?\367\327OVPr\261\277\'\265Ne\342\227\241\277<D\005\037\244\207\267\277w\364\027\341\017N\261\277\032\350\177\3525#g\2770Tj?\232\265\265?\256\305K\325\036\360\320\277\247\261\325\215\021^\274\277\370\313\203E\2163\225\277\207\nA\274\227\343\301\277\221\256\276\023\017\331\272\277\243e4z\313\223\261?w\376|\006\030R\267\277\234\351\022Fs\021\263?\262\362t\02726\235?~\373\376Uy\272\254?\322?\333C\251\361\255\277\357\205\316d\004\316\267\277\215\374\321i\363\230\225?@\361\245q\2528\277?\310\222\317\206\303!\303?\256\260\016j\327*\265\277\006\234\310\326V\332\270?\244\222M\267\312\213\326\277\272A\023S[\320\266\277\263\364\026\260\210\001Y\277\252\214Q<]\353\303?\220\305?\277+\260\270?\005\316F\006K\222\236\277&t\033\234\277\037\240?\013)\244T\342Dx\277\367vZX\260\027z?\210\021c;\243\305\212?\277\314\365;\361\252\265\277\305j\243\226\347\274\314?s\021\314\316\273\307\276?\350\351z\267\317\317g\277\236\253\364Q\347\033\312?\366\320b\247\244\254\227\277qQB\333ev\303\277\245\314\020\367\035Hu?\306Gp\213\231\306y?\370\204\257\321\362\271\245\277\363L\206\032\313\302z\277\215\002\241OCI\273?w\301;\300\260F\221?]\215\023\310\024\005\220\277.\355vO\241\231\206\2770\023\244:\222\347\205\277_s\n\034\275\311\242??\006E=y\213\217\277\372\"b\305\224\201\254\277\007\300\357\005\225\216\224?\342K\304\250j\002\242\277nWO\226S\217y?\354\324\254m3\336\331?\375\242w\372&7\251?\256\023\237%\247\314\232?\205\030X1;\233\272\277\342%\003~#z\264\277\221\036\305x\236\007\241?\313w\270\233{\246\301\277-\346!O\001j\311\277K\007qC\302\203\210\277rc\233\354f+\261\277\276\304\262{\277g\221\2771\370\0307\025\263\243?-\244\214\353\274\331\262?\206`\212UH\'\315?UE[\203\330\\f?\317\007\243\232;\276\272?\207\311\025\343\323\307s\277\215\244\003\036\211\0312\277\023\351\376\376(\013\311\277*\232v\221\354\361\244\277b\243\240l\005{\232\277\305\231\252\355\313\217\261?\"\270 O%\010\253?\362\347\022\206\225?\246?\033\244\250Q\364\357\303\277wh_\300\260\002\272?\376Z\255\307\016\026\231\277G\206,/\340\313\271?U\243\331\334\275\375\314\277amQ\275/\247\237??\244\352\324\345\302\230?\371`\346\355=\263\276\277\276XG\347\022\007\261\277\241\255\302\343\377\236\262\277\353\222G\203%o\205?\2071\302\374\324\341\227?\026\003\241e<\035\263?c*g1\324 \300?w\310\t!\013/\257\277\336\373$\235\372\222u\277\206\347\374\253\010\303\250\277\365\223\030s-\035\275?\361\366c\'\n8\315\277p\311\271\210\371\232\273?\314Z\251@\200\t\307?O>\313\354\356\243\244\277C\211o\255\014\342\261?\030\254\312`\343\004\227?s\255\310\226\025\245\312?\231\200.s9\356\311?\270\366\221\255\263\230\231?\367\n\034o\213\346\220\277N1(\226\276\374\301\277]\201}$\313J\271?\263\207\214\220dP\263\277=\014O\002j\321\262?mHEL\364\367\307\277h,\037\322\325\307\253\277\322\340\336\330\020\252\247\277Sv\335Xi\356\242?\313\214wW\324\302\241?\2340=\025\365\247\272\277T\273\000c\370\322\301?\032\303\265\237\226%\306\277\356\323)\345\345\340\217\277bA\\=f^t?\205\305\233\304\2519\242?\215\350>S\256j\247?\226\325\221\211\307s\240?\267\0370\264\007\'c?\351\271-,t(\313\277\264\021{\344\r\356\264\277\222@\232\303F\274\254?C\372gv0{\256?\267{\014\016\246\255\265?r\335\346\233:$\272\277b0\374\350\252\265\265?r|\224\255\236\346\201?\247GaA\314\023\252\277\212\001\352J!\347\241?\360\332p\234pr\320\277\341\031\262\335/X\260?\277\016\t\355\277\220\210\277y\030\235\1771 \270?\034\266\354ZB\237\311?\261\027\177h\366\216\264\277\034`\265tgm\260\277a\304\3130u\277\313?\310\\\001\233\206S\261\277\376\362h%\334i\225?1\017\221\216\332E\273\277\250\024\263\274\2450m\277\r\224d\365\325\363\240?u5 r\347\025\271?\2221S`}X\320?\316\267\216\027hm\300\277\007\205rw\322\204\235\277@\n\255/\200^\311?U0\035\rd\256\303\277y\221|\303\272\255o\277fVF\365\231(\251\277\266\tfA\232\225\312?hU\332\375\357^\261?22wg\261\345\252\277/>h>\010\370q\277\035\371\312Ut-\253\277\030C\372^h\016\255\277\347\352S\360\027\321\256?\337.Z\0102\355\277\277\335\251S\330}j\266?\344\352\021*\320\356\205?\346\2322\213\207\344\255\277\3454\271\355\250|\255?\251\205\"D\016\020\271?\336\345\"jen\263?b6n\250\325\302\302?\264b\300\021<\017\257?\306p\225\3654\203\226\277v\267\3510c\351|?D\240\366@\240\242\257?\355\250\2607\373\304\245?|\353\367\236\221\030\237?Hj\323V\333\320\272\277\004\247\276Y#a\237\277\255\224\346\3356\213\271?\341KE\207\rF\252?~G\200i\350_\221\277[@\207\236\210\025{?\022\255X\267\315o\261?r\326\204!\356\253\313\277\007\017\335\331\276\016\212\277\373\263\205\344\262\256\265\277\216\005EO\205L\264?||A\362\026\000\306?*\333\337\336\351n\262\277g#\210\024\271\213\300\277\303y\'\237w}\265\277v\303\246y\350\032\211?0T\276p\326\300\303\277\357p\346\354r\206\252?;\\\272\302V<\320?R\371\024\267\200\326U\277\335\nApo\364\250?sfl\351\315\266\272?\000Y\255F\325\351\225\277Q\353\330\326d\237\247?\031\342X;\002\234\243?\227\341\314pn\357\266\2777\234|\251\367/\177?\341Z\0326\233\351\246?\364\3509\332\205z\260?pd\221C@\316\265\277\345Y\214w\016\343\260\277\016\217d>\270\361\266\277\250\000\007=t7\327?\370u\247\206\251R\200?\352\0374\330\030\360\246\277\352-\006\035\333\217\272?\036h\007\215\037\356\246\277\363\0068\346z\241\256?\255\205\275\271\273pd?C\312\314\235\234[\263?\202\367|\026\242]\246?\022\266\202\246\301-\277\277r1L\005\006\017\216?\365\236\367\256\317E\271\2775p\016-j\243\303?Rq\303\242\021F\263?\263\233\340\2368\250\264?\245~\030\320\027R\306?\224\343.\226%\203\257\277V`~\236\035U\211\277\240\373\240\337\227\346\307\2778\231\370\273\\x\204\277\350\272\233O\272k\306\277y\315f\004w\013\307?\377\322\252\322Z\212\245?6\326\211\235LH\230?m\014@o\307r\274?\177\200w\256\237\220s?\321\017.c\246\303\266\277hH\341\333Th\276\277\255X\365\225\245\312\262?u\"H\225(\277\216?3\031e\233\251G\246\277\352\014\236\n\310)\274?\311\350~\323Qb}?\252\224\250\256\201Q\203?\371\034N*\2362a?\340\256\351\275\364t\263\277w\241\260:\240|\302?fk\252\344\014\006\301?\333\017\020\233\023\304\306\277\017\016iel?\205\277\265\t\245\235p\260\210\277\317\245\224f\3755\264\277\007\207Hq\024\314\240\277-t\224u\210h\267?2\"\350\246\267[\240?j:\263\205\351\275\222? +#p\207\227\265\277\312\334\022\024\210>\211\277\t\241\277\032\267\013\314\277\266\200\363\034\355\261\306?\311<l6@\"\276?\337\346\247\2237!\262?Jx=Q\270\222\302\277(\214\335\351\246w\260\2779\262ia\035T\224\277\222ik.\304-\204\277\037\037{\2347\312\325?\351\225\303\013\234\032\225?V[\242\357i\271\302?\235\271=\3679\034\241?\244nL\017\220r\260?!8\222f^\217\246\277\222t\317\275\357s\253?t\374\333U\351\021\307\277_\343I\032\311\217\200?\034\265i\204>\226\250\277{w\273mu\007\315?\230w\312\343L2\222\277\314Nt\333\361\365\250?\034\226\014\324\217\337\250\277=\031\351]\217\003\325\277\304\271C\270\360\037\221\277#\256fy\014f\270\277\237h\201u-\213\236?S\243\226\207\225\344\261?\256E\255\224\311\322\234\277.\tNebv\264?\226 \272\210w\'\322\277>i\210\013~>v\277\3216\340\357\303\240\253\277jRx\240vT\207\277Jq\352w\272.\201\2777Z0I\205\n\260?\266l\345\244\024\333\270?\211^\2066\327\350\301?\202\265\250\273\317\254\304?uk=e\345\341\210?\207[\210\240>\217}?\302\271\033\031\252.\201?\366\357\250\333\0239\266\277NqR\362\034\262\302?\201\210.$0\255\241?\210\306ZC\177\330\225\2770e]t\"6\234?\366\177\2347]Q\305\277\037IZ\031\020\001\261\277\031\267\226\003l\177\242\277\307\375\tKE\355\260?\007\316\2433\300\251\310?\035\037\3635E\334\257?%\300:\002\036\002\300?8@~\313\271\327\265?%\210\177g\312\227\227\2774\214\275\374D\000\242\277\027\271\334r\240\261\266?\301\342o\016\221\307\231\277\022s%\220\337\343\267?\271\252\370\374\034\003\273?\027U\377\270\2555\302?\333\241\261\247\\w\201?%[0\0318\367\301\277+\022g\311\372\270\300?\3531\256\231\002\027\332\277\336\225K\206\330\256\263\277\022>\206Z\230\236\276?\361\354Z\311%\242\263\277\253x:\364D\034\306?LHL\271V\306\220??\236\t\362\334\226\254\277\335\346\311\213\031\r\332?\223\005\301\370q\372\212?~\"Z\251\210\363\265?\241\017R\273A\337\223\277\3778\003.lP\247?*F\177]\264\254\252\277\313\305\010\254\304\266\261\277\233\237\307\214\241fu?\211\206l\303\347\271\220?I\004t\213\327\304\205\277,*\325\021\n\365\221\277t\225:b\242\024\264\277\2700(\243\232#\262\277R\332\307;\353\331\226\277q\374\200\"pM\311?\335\t?\324\303\333\223\277\213\250\340R\221\034\274?\001O|k\230\013\304\277\301\226^\371\021\254\243?\340\320B2D\177\256\2775^\024\345_\033\273?\265\350\371\263\315\354\304?`\322i0\205\014\241?\t\260@\363\037\316\274\277L\343R\360Gg\237\277\203\033\200\262{\013\252\277\231j\203IY\315\215\277\234\220\221\240\361\'\267?\251\0271\223\022\352\273?eTWcyb\264\277^\360\330\242\255\024\211?9\234\031\2059\277\323\277\3728\033v\323.\272?\\(\2408$9\267?\303\021\327\250\016\267\322?2`\005\345\330*\317\277T\202\036\312MQ\201\277\026=\006\361\362\312\300?\005}%`\211\'\204?mU<\207\310\020\274\277\353$\271aI5\260\277\253\254\333\027\242\026\277?\"\257\267\222\006\340\236\277\214\375\211\256\331\246\263\2773x\006\225\340\331\272?](\237\022\270P\310\277j\370\237/\330\321\303\277Z\247\220\376\207\275\320?WEQ\365\310\315\223\277J\340\027\205eo\325\277\366\344\340\324Q\266\274\277\317\030\241;\2158\271\277c\327\237\207D4\266\277Yx\210\263Ep\251?63z\376c\"\247\277\344\201\243\303L\251\264?\366\331\001W\330\270\327\277z\303Y\254\204\200\250\277^t6\025_\240\252\277\304\'S\177\263d\265?\203\204Q,6t\266\277\314\350\034\332\362(\273?C\353h\257\314\240\272?(\026\316\207,\030\317\277-PJ\034#\007\262?\222\247\026\303\251Z\240\277\231\016\032j^G\265\277\017i1\367\334\225\244?\023V\250\0368\270\233\277\271\361\234su\004\254\277\200\342\247B\304\227\264\277|\343&/\r\010\241\277|\243\246\260\035\204\224?\326\340\321o\030\017\300\277\200A)\231t\032\257?:SI0\271)\235?\232\224v\005qd|\277L!W\031\333\002\304?2/cm\'\221\247?\n5\253\010\376\242\255\277H\207\376%!\272\320?\035y>\340)\321\220\277!\242\252\036V.\267?0[~\230m\010\277?\357\217D!\332\007\242\277K\255\342\276Jl\246\277@|\024\001\256\326\252\277!\262\315\024\034\325\305\277\367\032<Y\303\216\227\277u\334CTs\343\313?\367;B\232:\314\321\277\363C5\343,R\301?wX\262\3052\346\263\277+ \202\037W\210\236\277\370\232x\247\366\357\247?\233RK\330\310\025\304?\"\"\265\2467\316q?\326\222\t\031\202\337\271\277\246\336\256\305\365\366\274\277\200\005\010\325\030\264\241?l\344k_\340\217\260\277\356p\001\204\023\351\246?\351\014\"<j\251\257?Zs\251\276j\220^?\013y6\210[&\266\277\274\265\nt:\315\327?I\320\204\200\3354\232\277\006\035\303\305\n\004w?,\372B5\271a\273\277p\241\337\273\367!\307\277\355\277\304\375\0024\224?\260c\3567Vy\273\277\347\255\020r9`\305?\267\247\316U\261\234\251\277\036\344%kD?\222??%79\353\227\321?^\252\327\313\347~\250\277\222\034\250\352,\211\213?\2323\341\276\327\265\210\277\000\005\363T\030\242\232\277Ud\345\333\2449\303\277\213Y-R\310\374\257?\311\017\362\020\365\"\236?L\277Eh8w\252?\007\360U`\002`\240\277\347O>\207\232\036\240?\262 j\032S\262\317?*^]\202!\371\310\277F\372\330\353\356\223|?\216\2319J\377M\264?\334k\351\210?9\242\277\306\246N\224\345\371\243?X\344\357\017\2350\266\277H\032\307\202\0005\262\277r\n\200\2113\236\257?\214aaG\255\271\271\277\003\037\265s-u\303?\245\342\373g?\310\236\277\317\377\352\221\274Z\231\277#\245v\235lb\265\277i\234\200_\033\377z\277[D)-\307:\236?\351\037\033\350\010+\264\277 pb3i\032\316?\034E\344\353%\360\207?\033C\002\013\300\376}\277~\005\313P\355\217\206?\275\2179s\320\371\301\277\217\024\344\230\273\323\221?\226\372U\324\347%\251\277\277~\3224\253`\227\277Ar\367h\236~\254?\310\201PA\375\035\232\277\037\243\253\204\335\344\273\277a\261\024\rN\245\272?\244\302lW\220\350\264\277\333kK{^i\242?7\326\003!aK\321?F2\177\")\255\226\277k\344\334\275\031\260\231?6\234bk\207\306\305\277zN\245zF\372\312\277\317r\017\342\207u\220\277\026-Y\272\246m\214\277<\005e\322F\010\261\277%;\247\036\222\217\242?1$K~\002\237.\277\211\204:T\305\327\265\277\250d\250\337\327T\315\277\226\231\306\204\322\203\300\277\321\003<k\tl\233\277\244K6f\374\223\307\277\216\243\301\235\301\363\246?\002\307\365\2401\206\304?Q\335}k@e\241?\315l\003\331\273,\272?\006\373I\250\034\257\301\277I6\374\321\255[\266?=\212+\260\024+\251?\000|\304\022\326\263\244?\250\021\241K\311\225\211\277\007\327\377\364x\036\227?\020\233Ku\203\355\277?\372}}\227x\271\273\277m\007\323e\006\016\264?\203\315?\277\016\236\253\277\341)T\215\354K\262\277\037\266\032\375>a\261\277M\336\375&\260\000\326?\313\226\205\267\341\364\250\277Bw\235hC\232\204?\371\375#$\371W\232?\356\034\223;\354\306\261\277\357\210_\323\272L\230?\277\354$P\037\212n?\274\036\225\t/([\277\204\033\221^t/\300\277\366\3708\220\020+\246\2772\\\376\237\242\037\253\277Z\342/,x\037\321?\342.&\371\336\210\245?\034\204\351\212\230\034\217\277\031\203m\2014\025\265\277\244\365\226G\214\376\246?\022t@\371\020\032\274\277\336b\033\307\003\010\271?=\337\366\243\n\361\302\277\310\261\t\t\"\210\255\277\024:\007\027\256V\247\277\351\352_\360\333-\277\277\333\227`\335\2433\310\277\273C;3]\233\244?nP|\300\234\351\206\277\310\304{Nqg\252?u\250\251\327\357N\205\277~\200E\030\232q\277?/<\020\0378\374\260\277i*\232\210n\026\302?_\324\021\240\003;\260\277\3636\233\323X\316\241\277\250\371\372E^\306\274?\243t\0078|\367\263\277~\216\352\206\031\306\265\277aY\223q\212\333\247\277\232\"\313\276\033,\232\2771\275\302L+\201\265?\203.r\034\226\277x?\263y\336\366\224\031\270?\353\364\320\364\035\375o?/\331\035\360\013\261\274?\321\233=<\244j\302\2773S\224\225q\342\334\277\221|\372\264\363g\264?\r\3604\270\222Z\264\277\306\227\020\206\014\016\227?t$+7A<\260?%|\370u\303\340\215\277\214\347y\225!h\300?\352{%g\"cB?\316-\322\n\223?\226\277$\024i\313\005+\240?\024\223b\031\324\006\274?\025\372\361C\"\336\256?\3163\366\377\222l\302\277\260\323\306{\246\367w\277\014y\'\223\270\337\265\277*\332\352D\227x\274?o@\267\235n\337\305\277$\217\\6\036\323}\277]\037\031\023\3221\246?\207\034\022\251\250w\275?n\353\260=\222d\237\2776\336b\303\010\036\322\277\237\220\312U\243\221\250?\"J\364\262\027:\301?I\335\276o\001\340\302?\351\245Y\323\027\032\277\277\205\233\361q\n\'\235\277\325\021\r\216\3627\256?\177ER\353#\376\275?\214\263\336P\332\331\244\277\235l\250\355\2128\220?\312\326\257A\330z\243?S\250\322rX\261\255?PWd\352\232\340\226?\271\'\004\300B\271R\277i\232\224FB\000\275?\264\014\307\343\320S\251?\256\246\242X\270A\213?\210\t\007\345*\377\301? k\326\234\323\241\267?\334P\314\373\377\302\266\277\301>\207#^\227\274\277,\2023\010>\203\301\277\341\372g\306Mc\251\277\372!\373\333\250K\250\277\374K\310d?\303\254\277\332\250S\017\177j\254?j\224\372d]\354\250?\364\313,\246&[\250?\356vv!\004\361\262\277A\2555nV\337\260\277\275\031\323#F\030\301\277\246K\310\260\r\000\222\277\250\313A_\'\344\255?\276}\034\362l\207\231?\342\261a\013\027i\261?v\215\227\317\252\206\256?T\355\026\002\234\032\266?Z\327\377P\2334\276\277)%\022b\t\233\266?7\241\177GQ4{\277\216\225\365Y[.h\277\334\337\212\376<@\300?\347\267\004\366\232\370\262\277\332\361gv\033\345\262\277\253M\202\325\200t\224?l\266LW\251\000\315\277J\213R\262\025\334\242?\212\323>\216\227\321\252\277\037\202|\354*]\250?y\317\036\rP\264\300\277\356\367\310\234g>\241\277\340~\214\006\342\345\264?}+0\255\200+\263?\330\007\235_\030\241\252?c\007\210\344\001)\274?;\374\334|\3006\267\277<zf\372\211\272\276?\203\325\031\235\223\257\236?\"\366\025\000\251\n\250\277^\271\207\002k\004\307?q\326\326\356l<\243?\277H,\222\013\020\254\277\314X`\321\036\237\277?\201v\353\004\316\261\316?\361\2527S\000\237p\277\242\300\240\0200\240\272?\235Yo\321\370\177\263\277H=\355\0107\327\246?\312GK\014=\032\236\277\036\034\272\353\000\351\266\277bF\262=\371\004\300?1\320\244mc\245\200?\357\252\347\3418\024\243?\344z~\342\007\313\226?XD\267l\035\267\214\277US|5 \244\257?\302\273\253\325\314D\255\277\n$\025\3279a\223?\323I\030W\013\246\205\277\362\240\211\n\223\301\242?e\350\033\337\'|\315\277\217\231\002\312\356Z\203\277{\357\210[1\032\246\2778~\014\\\177\213\222\277\236\243\212\340d\034\271\277\201B\252;{\221\277?\035\036\246\375P\033\265?!k\375( \026\306\277,\336\354JB\256\200?\222\232\212\271\351\337\276\277K>\350\326\021\027\270?\370\224\024\022L\245\313?y\374a\243B\343\246?\306\250h\227>\245\267\277\333]%\243q\255\260?Hi+\263\366%\260?+\023\324\325\014\247\242?\302|\364M\367\337\261?\242T\244\000\313\261\270\277\353\220\033C\235\005\246?\273\206Y\266\0377\262\277\277\315M\202\335\017\255?&\265P\353E\266\256?\351\315\352\277A\336\243\277k\334\217I\323\214\261\277\024_e\374\000E\271?\203&\230,[\336\266?\215\373\236.>\245\223\277\251\232\326\2646:\246\2777\252?\270R\r\224?\227M G\373\207\256?[F\273\036\305\236\220?\261\213 \246\351\t\301\277D\010oZ?\202\232?X\335 \020B\252\221?\245m\013\325\177E\322?\022\255t\276\2664\307?\240\310\251\315T\301\266\277J%s-}\243\326\277\361U\320?;\274\323?*L\3744R\223\237\277\337\331\377\222P\241\200\277\262@\372\242\377\256\251?\373\215~\036\020\316\202\277e\014\313\013\263\"\270\277A\262\301\374\363j\261?\315B\261)X\306\267\277F\333\260$\030W\224?\3538\272\212J\t\234\277J\366\247?\365c\224\2777\300\260Mf6\213?\347\"\021^s\245\307?f\325!\364\202\340\315\277HZ\3474S\026\266\277G+\214\271=\016\276?=\356Jl\243@\220\277E\262\374\205.n\242?\271\004\227Y\260\240t?Eh\371U\244P\271\277\257a.)\367\332\204\277\000\214\3158=p\267?;\251\230\256\350\240\234?s\354\315\370\r\177\232?@\214\"O\227O\313\277\310\354\250\236\303T\204\277Q\226\312\036\013\220\227?\361\257\036\250\025\356\201\277\303\211\211y9\316\227?\336\270K3\360\243\232?\330\013\035Z\256|\273?\360c\272\261-\t\260?\231c\273\202\231\027\253?\375\253\031\271\354\305\204\277\365\311$f$7\262\277\246c\004\"\211\013\310?Y\003M\227\340x\267?F\347\321\307\320\207\244?\032\023\035\330\243\312\262? m\032\360\266\001\261?\253\315\177\347\273{\263\277(\316\027\362H\326\275\277\352\362@1\316\363\227?\345\300\347\2205\337\245\277\271K\3441\'S\261?\241|%\036\337\313w?Y\270\235\305%A\270\277\224\313\266\211m\022\255\277\034.\201\3420\317\227\277K\274s\245u\t\273\277]$\001x\207\024\303\277B\300\315\371\020\344\277?\253\2453\202\t\306\262\277j\026\310\035\177\230\202?\253\243\017C\341\216\302?R\016\206\316\036\264\240\277\227\202\235eI6\240?\"\027\021\360>\377\243\277\373w\333\252\004\247\243?\312\007\0143B\315\274?\313\202\361\232Y\250\205?$\352\367\r\3667\233\277\241\t8\306o\375\272\277D\r\202\261\240\215\212\277=3\007\354\025\237\265?*\263\010Y\327\213\300?%;y\227\031\350\272\277\026\242\275\262\263!\253?\2124\222\026\314\233\257?Ns*,\031|\311?\305<\231\310=p+\277\316;_[\242f\251\277\231\005A\306\221?\233\277>\224>\330\004\350\306\277b\017\314\314\303\310\316?KO\220?Q\263\275?\231\271\330\301v\207\243\277\244s_\371\243\255\270?\244\321\013\000\2652\216\277\010)\014w\317\027\240?>\302\332r\314D\270?lx\320\270\277\223\264\277\'\n\320Pbw\224?\300F\331\335\001N\270?\177\215\202=R\377\261\277}\373^\323\004s\237\277\354\251(K\301I\307?=\331\001\243\036\236\325\277J\nh&\\Vt\277\273\007Z\352S\252\225\277\220\355~\2103\013\300?\313\370`\005\340`\310?\214\363\035\301\354A\205?\320)\276Wa\305\270?\317`D]#\270\277\277\220\372\035\263t$\234?]q\341l\2068|\277/\000\0204\351\023\326\277\000\311\300\231f\036\311?\366\277k\274z6\261?\212\257\255\234\370\231\204\277B\310~!\006wh\277D\274\255,\347\344\302\277\227\311)=\260\010\261\277\321^\337\357\354h\265\277\376\261D\365\027\255\273\277I\261BC\331\304\240\277\373C\335\370\033\364\307\277\026\242\223\344\370D\277\277\353\306\177\367\236v\225?\223\317\272\002\305\352\225?\362\003~\363C\233\200\277\305B;e\354\312\321?Y\n0oE0\244?\216\257\233Q\241\025\300?\215\2511\251\"}\231\277\2001\240\212\207\177\260?0\223\350\304\200\314\230\277\256Cf\021\222\355\177\277\200@m(\245\025\215\277\256\305N\247\342E\273\277m\343\031\366\374\272\256?:\030\342k\301\017\300\277\303/\360F\203\300\310?\243\335N\243Z\215\261\277`\356\007\357\261\360\314?\315\273\317\333\225\201\300?(\267\223\233\326\350\261?v\001\212\276\310\230\271?\274\233\007\204\237\266\322?t\3339\260\251>\251?\274\306\316$#\331\234?\265\214\225\333\300#\253?\252(\270\031\344n\261\277\002\203\340\212`\377\242\277\347eV4u0\254\277h\247\210\212\250\261\301\277\353\017\246S\332\313\261?D,\231\034f2w\277\376xy\340\003\375\276?\260V\022T1l\205\277\304\236o\252Q\363\244\277\235u\366\275\276\334\223\277\224\233Q\371\014\350\251\277\332\200\270mt\351\252?%\232\235\220],\253?z\236\362\376\277*\302\277\215J\314\341\277\275\277?\266L#[\212n\300?1]0\215\334O\255?\345\333Tg9M\213?\362V\205j\006\273\266?\'\215\354\360\253q\263\277\270N0\330(\035\250\2778\307\316*Rr\260\277S/\231r\264o\252\277C\356\\<\201\036~?\227\347!U\355L\245?\207c\371\234t\327\265\277\301\367\245\"\325\315\312?I\006\277\374\3060\274\277L:\244\364\275W\202?p\221\264\233h\264}?N\226\343H\253\017\263\277\230\212b\023\363\263\327\277\321\221\206\226\020b\226\2778\271u7\322\204\240\277{\312\311\035\272#\266\277\212\271;\235\266\367\325?\"\204d&\031x\225?]H\317\317z\373\266?M\217;\364\357\260\244?~\250\215q\036\334\224\277\323\307t\023\275\236\272\277\306\362I\262\327\037\277\277\320 \364*\305\276\261\277)7Q\222:\355\273?\320\262\277\331\303`\274?\373\234]BF\275\322\277\t\2745b\240F\252\2775d\371\022(J\253?\320\3032\2269\240\211?:\"u.\277\016\246?I\246J\021\2228\242?\\\010\256%<\247\256\277@\177\325}\361J\301?\'k\366J(n\251?V\211\341\227{\217\272?\303\366\007\361wF\307\277,N\"\0137\320\276?\027J\317\220\3550\260?\362h\315\270~&\266?\3608\303\244U<\327\277Xn\366\2530.\241\277\315\354\000\346\370 \222\277a\375\354C\272\250\270?&E\321\'=\304\275\277\211p4\223X\316\260\277\306\212\306\030ct\301?!V\026\274?\\\214?\243\021\215\306\325`\260?\177&v\020\372\355\215\277\3169\327Q\211+\260?\223\253-\317\271\353\300?\010\352\260C\374\235\242?\350\013\245\262\360\364\226\277\225\270\313\314\260t\224\277\233\0264\370\nU\236?\332y7\021d\315\250?\350?\316x\004\213\265\277_\005Rf\372s\216\2778\232\002<\210\354\255?w<t\311\304+\220?\224y\021\324\022F\316\277\230Y\tR\320\306\211?]\260\320\331.\034\245\277\003\001\320U\255yx?\324,\t\037X\212\244\277n\320\245g\"Y\265?\310\246*V\241\221\247\277,\205\312*\312\304\314\277dS\236\210Q[\260?\216\357\342\363b\375y?~\034\214\010I\205\246\277\330\016\305F\206W\320?GE\300\357\nI\247?\371\010\304&\r\222\257\277\310Z\252\373\204F\265?\036\013\263Y\320\t\273\277\324\177\301]\302\243\221\277M\213G\302\333\213\266?\312\024\201\031Q\247\267?\347G\373\227\210\267\242?^\247\344\207\341\010\211\277\376\267Y\262t\373\244\277^\343s\374v\270y\277\333%+<Q!\254\277\303\377\213\0202\002\220\277\325P\351\236\245Q\305\277S\260\263\246\n\353p?zQ\005u:\215\220?\225\256\301\262\356~\270?\322\375\035W\274\253\230?}\376D\2372\303\237?\227\007J\332\362\253\261?L>J\207\225R\301?JP\355-\317\215\253\277\205$\332\241\260\261\244?\222/\204\245\312\300\234\277O\273\355XJ\363\251\277\001Js\343\026\027\303\2775\321\301\245\237|\247\277D(\234\212\010\225\303\277ft\010M\3443\220\277\377D4w_\354\201\277\306\214\203\350b\251\274?\371\227\374\2711$\247\277O\237\231B\370\365\271?\362\r/\003ea\243?f\233\306\001\371\t\233?\r\257\202\267\333h\241?\327\226\223\373\207-\304?ZHo\010=v\301?,\0309\334\262\357\272\277\331\245\2523#q\264\277MG\276j\322\272\276??t\257\006&\212\200?\343-cD\274\375b\277\374\231\222\274\347\216\232\277\244A\250?\205\354\256?\346A\021\222\335~\302?\231+E\3376?\271\277\343A\270\252\177\370\275?\213\241K\325\207>\225?\005\363\264Z\377\353\233\277\371\023d\223\022e\261?\020\360]\360\270v\300\277\300\363\231\362\335c\203?\205\317\004\357;\364\243\277@\314\270\350(\030\260?\214\307\336,\327\326\273?ip0\334\323\225\274?H\244\231\013\0255\300\277\371w\332A\333\027\261\2777\221\\)y\233\263?\252\257\245\014w<\266?\251\373xQ] \232\277is\324o+\341\314?2v\305dQ\033\265\277\262c\223iIM\230\277U\034M\224\373\377\252\277)\325\255&av\206?\021[\026g\267\327\226?\213\227\347\331\346j\272\277Z\3146\250\347;\241\277\240!d%\2361\224?\005\354\371\177\364,\264?aT\207\016sV\263\277\306U\n\177x{\260\277\201\030\245R7\014\265\27742\254\014\231\253b\277\376\255\000{\037\333\254\277\246c\274\357\225\242\222?\303)\315\311\r\361\221\2774\213\237\311K\376\267?\317\354\177\325bl\202\277\364\nf\260\004\352\303\277\204\240\273\277W%\246\277\022\027\\\201\342\246\275?\026\035\r\016:\371\241?S\003\213N\353\264\253?\346\372\273\361\216]\302?\017\265\344\200\372\267\307\277q*X\037\362o\205\277:\310\026\007N\256\261\277(NZ@\367\002\231?\016\305`\0307\374\254?*\307b\276\361\373\246\277F\341@\001\350\274\267\277\302\256\010\204\261\321\273\277\356\205\255\365\363L\312?\300\263\361\342@\230\226?\003\337\254\321\254\267\312\277\267\246i(\220\276\271\277\031\241S\371=L\256?%\031=A\260C\262\277\030\364\010\214.H\263\277T)\026\177\2602\262\277\262\237\277s\314\022\262\277K\014\177\\t\025\305\277{\250\366\373\304\021\251\277|<i\3714\263\226\277\006\361T\370j\200\254\277\364\2103\376_5\253\277V\273G\005\305W\222\277g\201\002\301\314^\265?;\267\017#/(\305\277\010\361\342\025\'\330\304\277\034)\353,T\277\304\277\3526\234\023\306\037\313?\034\235\376\353\3611\217\277\260Ae*\262\027\260\277\035H\215\337\033\351\274\277\223\210\244\234J\024\250?\0373O,\326D\261\277\314\220\315`\301\004\301\277\n<tzZ\252\316\277\355\377\314\325b\276\264\277\266M\267Ew\252w?\017\307\234^\354k\303\277\024\006\221\177\344\310\332?\311\342\261+b\372\212?u\255\300\302\232a\221\277FEAw\347\356\254?\220\234\335\\\026\032\313\277\345\254P\224\230)\300\277`\330\367M\010\323\303\277\217\347~\214\322\371\266\277f\362\024\023\332k\254?\273\253\034e\021\320\277\277\212\031\033\306\023~\224?\255\000\023\035\203\340\211?Dh\221\351\252\352\251\277\263\241G3AK\265\277\303]5\246\\\315\246\277,\n\271Q\324Z\276?Rq\314\361K\271\237\277!\007YK\322s\302?\372\241\222e_\002\300\277\221\302\031\024\312#\242?\341cv\221K\007\265\277\242\230\275\363\233g\246?\343\324)?l\262\262?\033`\315\'\232}\236?l\013U\255\226\326\224?\002o\254/\2213\235\277D)_+\331\253\241\277&\202\271Pk\215\301?K\301\253\233\013\247\262\277\313\'\314s\364`\271?n\"\214\232\3227\302?mT\230\303\362W\202?\036\027 @\345\355\255\277\344\032:L\247\360\266\277\206zB:\343\245\263?\327I\346(\305\307\277\277\261\204\023T\276k\273?\206\033\322\331\331=\274?D\024\355\t\r\001\251?^\265\213<]\304\261?\351\r\211\241\221\370\263\277\222S/<\n \260?\275\365\317Pj\'\271?\263\265\r7>#\264\277\207)R\216\215\350\263?;\024\372:\356\346\254\277\375\006\023\364\014u\221\277\212\374Le\371p\204\277\3752(\033<q\241\277\026u\325\001QB\301?`q\335\202+0\267?\021n\024OSS\213?_\312\320EA\363\232?7\'\316\240iK\257\277\212G\232\322\240\277\304\277#v#\224 \264\265?\271\364\254=\240\214\265\277\316[a\256\320\032\247?\200\034i\374\244=\300?5r\027<Q\267\312?\020\200\242\017\3115\246?$\372\367\204\257e\223?\007\035O\361\350\251\260?\030\371\360\263\035\327\271?\303y%{\221\355\245?\030\212|\025*\222\215?f\3425\007+\245\250?\222|\0253\014A\226\277\325/\275w\323\205\260\277\212 \377W\370\001\261\277}a\336g\003R\275\2775\304\227}w]{\277\213:s\030\347$\307?\311A\030\336\305[\261\277g3\330\347\361\003\243\277{\353\215%\261\n\241?\276)\r\r\344\031\300?\272\226f\177\021}\266?\243\365]\342\002\003\245?\260\030<A\303\n\250?%n\331\236\225\256\270?q_\240-P\356\251\277\223h\316z\253\362\247\277c@\036\027\315y\225?+\313>b\302\324\247?\201yC\304\236\237\250\277\341W\323\016\022\224\263?A\300e\214s\277\302\277A\272\227\346\036/\216?\276t\343.An\242\2775eu\347n~\251?\274bN\177\331\366\310\277$1\245\033C\246\236?:NT\246\342o\254?\207\206J\240\370O\204?_\224)\222=\200\241\277K\035n;\020\222\262?Y\266#\234\313\003\207?\233\010$\326\2670\247\277U\222\256\211\316\360\240?X6dVa\031\221?D*/\tq\343\310?\255A\300\356\247\231\305\277=\324J\375\271\005\276\277\230!\312\205\346:\265?w,\331\212%\345r?.\373w\022\371\274\253\277\236\255_\223\204\337\264?k\025E\366\213|\313?\366\211\0169&f\222\277\240\344\376]\003\256\215?)[=\243\201S\222\277\227\312\376\036B\323\265\277eH3\\X\225\272\277\375ttX\265\350\237\277N\363\253\337\232\323\236?\t\377}\341\255\300\262\277\233j6\204\273\355\270?\250\233$<\r\344\244\277\301\242*\225\001\305\244\277\355@\025\0366P\212?\267\216%6\274}\263\277uu\353\340\232:\314?\316\000\245\017>\350N?\273\212\354\217\365\361\232\277\003\177\262\346\030\320\244\277\024J1\236\007\354\250?\237\246vFz%\231?\367\300[\341Q\311\260?\351\026\326\314\0175\246\277C\223\243u\332\245\274?T\264\311>\244\224a\277\212O\257|\0019\251\277\263\024\311\350|r\311?Y}\013\244H9\222?\273\343\301\354:_\301\277)\313z7\237\314\263\277f\246\237\306\356\013\264?\305X\303\030\325\257\250\277\3255<\3672;\313\277i\372X.\316\220\324?\016\332\214\333s\"\255?M\3174\235M\223\271?5\203\230\363\225\000\255\277\372\271\237\233Mw\242?\020\023\354\216\263ps\277\214\275^i\2279\271?J\017\237\376\2528\303?\237\243\312?\376\266\266\277?Ka\262;\301\216\277\351\275Ws\363\341\260\277\301\"\271\366\200v\244\277%q\2158\026\207\307?\325I\301{\'2\320\277Y\305\367\202\001\321\302\277\033\307\351\254\266\r\245?a\334J\346\255\243\263?\372\223\223t,\201\250\277\3764u\270X\005\256\277*\367-\265\000l\240\277\210\265\001~\244\272\211?\013\tqs\035\275\214?\244\0376dV\321\256?\261M\220\004_*\236\277\367\210A`\206d\272?\235\r\350l\250\317\250?\205\2001\345\266\245\301\277\034kt\247\212)\277\277\340\376`ix\313\201?N\205b/\013\200\265\2772\243\252\3162,\245?\231\235\350\236$\034\270?\022\002_\275\365\005\242?l\001\223\260h\352\256?\032\273\311\263\007N\247?{\\,\333\272o\211?\027{\370\223\240\221\232?\302^\213E\361\270\217\277%\007\325;\206\245\264?V7\242\204%a\253?\235\0369M\333r\306\2772n\232\237*\007\240?H\014\3360\322;\250?QRW\006\252\203\250?c2\014/\240\274I?\345\357~b\224\224\253\277e\032\021w\350a\315?<\307\332 \'\355\275?p\020\000{\003\326\272?\252\307\033}\252N\276?\000\225\311\350\343\300\241\277\274}$/\276E\220?\232\222\214Zx\250\271\277\222\220\215c\221\221\245?\004\035s\325\032\206\267\277j\310;\307\365\346\270\277\2444F\224*\365\322?\213\300\3548t\351\263\277\r\335\316\233\3712x?*\356\370\002\330\337\266?\342\016\213\334\267|\305\277\270\000+\242\334D\254?\264\000\226\007\201^\260?\025o\020\226\336F\321\277\000\247\r\026\2034\250\277[\007v\032oK\205?\014c\311\211\316\214\202?\275eN\033hP\240\277\213\226j*e`\223?\027\233\"\204N\006\237?6\314Z\357\020\265\321\277\340Xl\323\233_\234?\004\016d}\367}\261\277\327\243-\225G\270\237\2777)\341\023Ue\303?\022\t\030\241>+\264?\305\006\356\371\307\221\275\277l4\271P\231P\313?\036\336\370@0\217\247\277P|\244\370\226\310\252?\307\320[\372\342\274q\277\375\233T^\340\324\300?\253\353\227\363\367\314\303\277\276W\316\216\302\316\272?\3368\177\251\372K\274\277\335\350\364\001\277v\254\277p_3AjMe?\201\332\250*\364\256\327?\215\241\373\345k\337\260?)F\324\td\005\256?\347t\347\002\246P\264\277^XsY\207{\275\277P=dJ-G\261?D\236G\205l\231\273?\211&\253\212\'\377\310?HZ\321\341\205 \252\277\230E9\272\r\242\240?\276\207s\353n\203\301\277\024\305cz\215\366\262\277\361Q\366 @D\246\277\223Cw\343\366e\237?\313_a\277m\020\253?\306\266a\006\363z=\277\211\212\336+\226\027\307\277\315\363]&\2334\316?\203\251qq\246\033\315\277\203o\rb\276\324\235\277\357\213\264\270\237\024\277?\316\257\312\303J\315X\277\024=\003\245S\233\271?\227\314x\243\222\273\303\277m\010\202\216\351\352\237?X\314\226]~\264\306?\313\245:\272c\373_\277\005\301\261\327lR\301\277\275i^d\234r\310?\010 \350\300\344\361\256\277i\036\230\364l\252\223?d;\201@\271\354\257\277;\342@\202\2402\251?\306\353\215m\376\251\227\277\251\304\005\271v\274\214\277\241I\005<\330\224\225\277\255\205L\2377\013\247\277\021\360\\2\221|\264?{&\253\032\275\\\306?W\250\355(J\344\313\277,e\376\226L`\223\277\017:\303E\304\010\202\277X\263\321\230;\363\243\277\335\364\004\271\210\367\250?\227i\331\334\026\033@\277\352\341\353\0315\351\266?]\276\"\232\361s\226\277:\342\376\352\007\207\261\277\277\313\314\205\244^\247\277c\006\2056q.\261\277]U\375\251\004\303\201\277\016+\317\371\371\014\264\277\004k\371X\216\031\262?\327\007\261>)\361\327?v\341\312da\276\271?\321q-Nn]\222\277 D\037\021\232\365\321\277\005\231C\244\317\356\264?\270~\247fG\334\300?S|S\277\206\341\276\277\254z\237\355\n\031\243\277\376q\014\361\200]\240\277\007?L!\272\257\260\277\243J\261/\023p\275?3\017\371\226l\325\241?\306\355\362h\036d\251\277\245\276zj\2435\251?\260E\260\330\216\006y?\205p\0354\201O\305\277\242\266g\303\311\322\266?\326\206:n\275\000\213\277\221\306mJ#\337\235\277\210-\373b\270+\267?\325s`\336\342\266\274?\340\326Qe\353Q\304\277\030\242m:\371w\262\277\021\367\210\356\333\014\233?t\327\265\263\270\247\301?\364`MK\035Q\252\277\340\353q\221\246_\256?\236\010!\001\224\202\266\277B/(\366\305E\273\277i\320\253\027}\243\261?rUV\352\224m\245?\035\270\013\241Z\t\245?\226;\321\202$\014\250\277\322!\235`,\372\240?u}=\220T\342\177?>\355\215\341X\000\230\277\370\351F\n\377d\272?\364\266PN\310\332\240\277m\230J\272\346?\307?vJ5l\212\200\301\277\201$\206\223\344\260\260?m\210\276\230\273x\321\277+p\333\003\332d\307\277\252[\273o\000\227\247?\306\361\323v\305\245\246\277\026c\336JO<\200\277m\370\0105\036T\200?s<\300\247\330D\261\277\237h\277\"\336\200l?]$\263p\377\214\222?-\032\025\221\036\256\256\277,^[\032u\353\250?\310U\220dR\340\260?\225\267\322\202\205\205\240?2ro\344\265\331X\277\216xr\347\341k\262\277\310QG\032\027k\253?\023\300$\222(M\265?p?\016\203\026\373\243\277\020h[\030#\335\261?\331K\374r\317\271\262\277y\257+\327\341j\270\277\214\014\257.\020t\267\277c\213\0348JI\321?^\316\231\000\253\366\203\277\314\210\272\257 e\221\277\361\362\302\014wl\245\277\030\324\365\020Q)\236\277\360\206&\356\261j\236?\357f\006\236\314\007\304?\254\330KC\315\347\254\277\356\252\345 \241#\244\277\255tT\236+,\205?v\210\221\300\025\'\272\277\325\331~\330C\005r\277\235N\200\207\321\265\267\277\330\336\323\356\206\276\256\277\0255\227)\\H\301\277X\211\033&\017\277\263?\371W\375\035\354e\212\277\277\327\363\177\254m\214\277\214?C\252\020g\255\277\314(\236v_\351\212?\247\235\023\241\320\024\270?:\357&p\3730\265?\025\325d\026#\276\220?\035\254\331\351\\\355\274?D\303\032M\003\375\265\277\031\250\'aV\275\312?GF\263.@\216\236?V\223\317X{\372z?m\275\tJQ\276\300\277\376\334R%\037\222\272?$b\333c\307\032\254?\370\337\2430\r\246\273?\371\367*\313\267L\246\277\000@\235\211\327]\224\277\2624\355\337?\274\223?\321_)\355\213\313\314?M\310t\344\245P\252?9gM\310L,\304\277\031\tP\"\016\225\251\2772\323\316{\332\322\265?0\267!\273\225\370\222?\244\343\227_%\351\261?\037\'\344\313\352>\314?\022\024G\205\2736\273\277\233\000\360\240QE\222?\303\033W\210\357\260\201?\243\r2}\n\004\245?\203K\352\217\177\364\277\277\352\025\232\r\342N\311\277d\317\004\330\311\'\301\277R~&W\2020\263\2777YK\352Fi\241?\322\277\310i\254\206\261?a\311\372\313\206\266\261?a\327\3354;\013\205?n\264\006\261+\023\227?\270\227a\241\224\246\273?\302\200\323\010\357\253\244\277\364D\374(t\016\207?\003H\301\"\031\215\266?1\374H@\377f\252?5\276#\234\216v\272\277\255 \320\217X\301\241\277\202\231\005\035\035\242\301\277!\364=t\276\201\253?\214?\210\361\tU\263?\273\017\353\343\213$\300?IqQ\371\367$\304?\315\202\361\017\240\021\264?\371*\264?y\037\231?G\356V\005\310\222\233\277\261|\331\253Io\247\277\334<\023\367$\037\242\277\263W\237\367\356\376\300\277\264RA\225\037@\227?\367a+0?\204\204?\246\333\266\214tL\306?Np\342\247\356\021\274\277\240\305\003\201\004\362\177\277cn\353\n\3104\254?\370r\rt\026_\270?]+BF u\302?\324\237[m\256\331\231\277"
+ }
+ }
+ }
+ }
+ node {
+ name: "Const_1"
+ op: "Const"
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_DOUBLE
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_DOUBLE
+ tensor_shape {
+ dim {
+ size: 14
+ }
+ }
+ tensor_content: "\247X\347\2675\264\217\2776y\323\323 \301\226\277\214\261\027Ns\000\225\2772\204\nm\231\232\237\277\306i\204\000\242\315\231\277\245D\217\003\3660\226\277\021\365B\360P\003\226?\331X2\327\340\225\235\277#\335\372\000|\000\242\277p\370\027\263\343\200\227\277\2660{\320?\205\221\277\233\341\253q5<\246\277\357\211\251\233\2604\242\277\300\245M\305}\017\241\277"
+ }
+ }
+ }
+ }
+ node {
+ name: "MatMul"
+ op: "MatMul"
+ input: "input"
+ input: "Const"
+ attr {
+ key: "T"
+ value {
+ type: DT_DOUBLE
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ attr {
+ key: "transpose_a"
+ value {
+ b: false
+ }
+ }
+ attr {
+ key: "transpose_b"
+ value {
+ b: false
+ }
+ }
+ }
+ node {
+ name: "add"
+ op: "Add"
+ input: "MatMul"
+ input: "Const_1"
+ attr {
+ key: "T"
+ value {
+ type: DT_DOUBLE
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ node {
+ name: "predict"
+ op: "Identity"
+ input: "add"
+ attr {
+ key: "T"
+ value {
+ type: DT_DOUBLE
+ }
+ }
+ attr {
+ key: "_output_shapes"
+ value {
+ list {
+ shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ }
+ }
+ versions {
+ producer: 27
+ }
+ }
+ signature_def {
+ key: "serving_default"
+ value {
+ inputs {
+ key: "input"
+ value {
+ name: "input:0"
+ dtype: DT_DOUBLE
+ tensor_shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 1536
+ }
+ }
+ }
+ }
+ outputs {
+ key: "output"
+ value {
+ name: "predict:0"
+ dtype: DT_DOUBLE
+ tensor_shape {
+ dim {
+ size: -1
+ }
+ dim {
+ size: 14
+ }
+ }
+ }
+ }
+ method_name: "tensorflow/serving/predict"
+ }
+ }
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java
index 4abc4182dd5..aa537d4f69a 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java
@@ -109,6 +109,15 @@ public class DockerOperationsImpl implements DockerOperations {
addMounts(context, command);
+ // TODO: Enforce disk constraints
+ long minMainMemoryAvailableMb = (long) (context.node().memoryGb() * 1024);
+ if (minMainMemoryAvailableMb > 0) {
+ // VESPA_TOTAL_MEMORY_MB is used to make any jdisc container think the machine
+ // only has this much physical memory (overrides total memory reported by `free -m`).
+ // TODO: Remove after all tenants are running > 7.67
+ command.withEnvironment("VESPA_TOTAL_MEMORY_MB", Long.toString(minMainMemoryAvailableMb));
+ }
+
logger.info("Creating new container with args: " + command);
command.create();
}
@@ -276,6 +285,7 @@ public class DockerOperationsImpl implements DockerOperations {
context.pathInNodeUnderVespaHome("var/db/vespa"),
context.pathInNodeUnderVespaHome("var/jdisc_container"),
context.pathInNodeUnderVespaHome("var/mediasearch"), // TODO: Remove when Vespa 6 is gone
+ context.pathInNodeUnderVespaHome("var/run"), // TODO: Remove - contains .pid files
context.pathInNodeUnderVespaHome("var/vespa"),
context.pathInNodeUnderVespaHome("var/yca"),
context.pathInNodeUnderVespaHome("var/zookeeper") // Tenant content nodes, config server and controller
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainer.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainer.java
index 167ca15bdbf..f4355ed3afa 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainer.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainer.java
@@ -86,7 +86,7 @@ public class StorageMaintainer {
throw new RuntimeException("Result from disk usage command not as expected: " + output);
}
- return 1024 * Long.valueOf(results[0]);
+ return 1024 * Long.parseLong(results[0]);
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollector.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollector.java
index 2cddee6aa2a..6e5f65a5d48 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollector.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollector.java
@@ -77,10 +77,23 @@ public class CoreCollector {
List<String> readBacktrace(NodeAgentContext context, Path coredumpPath, Path binPath, boolean allThreads) {
String threads = allThreads ? "thread apply all bt" : "bt";
String[] command = {gdb.toString(), "-n", "-ex", threads, "-batch", binPath.toString(), coredumpPath.toString()};
+
ProcessResult result = docker.executeCommandInContainerAsRoot(context, command);
- if (result.getExitStatus() != 0) {
+ if (result.getExitStatus() != 0)
throw new RuntimeException("Failed to read backtrace " + result + ", Command: " + Arrays.toString(command));
- }
+
+ return Arrays.asList(result.getOutput().split("\n"));
+ }
+
+ List<String> readJstack(NodeAgentContext context, Path coredumpPath, Path binPath) {
+ String[] command = isRunningVespa6(context) ?
+ new String[] {"jstack", binPath.toString(), coredumpPath.toString()} :
+ new String[] {"jhsdb", "jstack", "--exe", binPath.toString(), "--core", coredumpPath.toString()};
+
+ ProcessResult result = docker.executeCommandInContainerAsRoot(context, command);
+ if (result.getExitStatus() != 0)
+ throw new RuntimeException("Failed to read jstack " + result + ", Command: " + Arrays.toString(command));
+
return Arrays.asList(result.getOutput().split("\n"));
}
@@ -96,11 +109,19 @@ public class CoreCollector {
Path binPath = readBinPath(context, coredumpPath);
data.put("bin_path", binPath.toString());
- data.put("backtrace", readBacktrace(context, coredumpPath, binPath, false));
- data.put("backtrace_all_threads", readBacktrace(context, coredumpPath, binPath, true));
+ if (binPath.getFileName().toString().equals("java")) {
+ data.put("backtrace_all_threads", readJstack(context, coredumpPath, binPath));
+ } else {
+ data.put("backtrace", readBacktrace(context, coredumpPath, binPath, false));
+ data.put("backtrace_all_threads", readBacktrace(context, coredumpPath, binPath, true));
+ }
} catch (RuntimeException e) {
context.log(logger, Level.WARNING, "Failed to extract backtrace", e);
}
return data;
}
+
+ private static boolean isRunningVespa6(NodeAgentContext context) {
+ return context.node().wantedVespaVersion().map(v -> v.getMajor() == 6).orElse(false);
+ }
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContextImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContextImpl.java
index ef8ea60bee3..e20480e14ef 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContextImpl.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContextImpl.java
@@ -18,6 +18,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;
import java.util.Optional;
+import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -183,8 +184,8 @@ public class NodeAgentContextImpl implements NodeAgentContext {
.flavor("d-2-8-50");
}
- public Builder nodeType(NodeType nodeType) {
- this.nodeSpecBuilder.type(nodeType);
+ public Builder nodeSpecBuilder(Function<NodeSpec.Builder, NodeSpec.Builder> nodeSpecBuilderModifier) {
+ this.nodeSpecBuilder = nodeSpecBuilderModifier.apply(nodeSpecBuilder);
return this;
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImpl.java
index 77c08133e82..b7e7b97cdd8 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImpl.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImpl.java
@@ -355,8 +355,7 @@ public class NodeAgentImpl implements NodeAgent {
}
private boolean noCpuCap(ZoneApi zone) {
- return zone.getEnvironment() == Environment.dev
- || (zone.getSystemName().isCd() && zone.getEnvironment() != Environment.prod);
+ return zone.getEnvironment() == Environment.dev || zone.getSystemName().isCd();
}
private boolean downloadImageIfNeeded(NodeSpec node, Optional<Container> container) {
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/StoredInteger.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/StoredInteger.java
index a815515ac83..9151dde19a6 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/StoredInteger.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/StoredInteger.java
@@ -34,7 +34,7 @@ public class StoredInteger implements Supplier<OptionalInt> {
if (!hasBeenRead) {
try {
String value = new String(Files.readAllBytes(path));
- this.value = OptionalInt.of(Integer.valueOf(value));
+ this.value = OptionalInt.of(Integer.parseInt(value));
} catch (NoSuchFileException e) {
this.value = OptionalInt.empty();
} catch (IOException e) {
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollectorTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollectorTest.java
index d809d9cbf96..37156ade064 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollectorTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollectorTest.java
@@ -1,6 +1,7 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.maintenance.coredump;
+import com.yahoo.component.Version;
import com.yahoo.vespa.hosted.dockerapi.ProcessResult;
import com.yahoo.vespa.hosted.node.admin.docker.DockerOperations;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContext;
@@ -9,9 +10,6 @@ import org.junit.Test;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -25,13 +23,14 @@ import static org.mockito.Mockito.when;
*/
public class CoreCollectorTest {
private final String GDB_PATH = "/my/path/to/gdb";
+ private final String JDK_PATH = "/path/to/jdk/java";
private final DockerOperations docker = mock(DockerOperations.class);
private final CoreCollector coreCollector = new CoreCollector(docker, Paths.get(GDB_PATH));
private final NodeAgentContext context = new NodeAgentContextImpl.Builder("container-123.domain.tld").build();
private final Path TEST_CORE_PATH = Paths.get("/tmp/core.1234");
private final Path TEST_BIN_PATH = Paths.get("/usr/bin/program");
- private final List<String> GDB_BACKTRACE = Arrays.asList("[New Thread 2703]",
+ private final List<String> GDB_BACKTRACE = List.of("[New Thread 2703]",
"Core was generated by `/usr/bin/program\'.", "Program terminated with signal 11, Segmentation fault.",
"#0 0x00000000004004d8 in main (argv=0x1) at main.c:4", "4\t printf(argv[3]);",
"#0 0x00000000004004d8 in main (argv=0x1) at main.c:4");
@@ -127,10 +126,10 @@ public class CoreCollectorTest {
"/usr/bin/program", "/tmp/core.1234"},
String.join("\n", GDB_BACKTRACE));
- Map<String, Object> expectedData = new HashMap<>();
- expectedData.put("bin_path", TEST_BIN_PATH.toString());
- expectedData.put("backtrace", new ArrayList<>(GDB_BACKTRACE));
- expectedData.put("backtrace_all_threads", new ArrayList<>(GDB_BACKTRACE));
+ Map<String, Object> expectedData = Map.of(
+ "bin_path", TEST_BIN_PATH.toString(),
+ "backtrace", GDB_BACKTRACE,
+ "backtrace_all_threads", GDB_BACKTRACE);
assertEquals(expectedData, coreCollector.collect(context, TEST_CORE_PATH));
}
@@ -142,16 +141,57 @@ public class CoreCollectorTest {
mockExec(new String[]{GDB_PATH + " -n -ex bt -batch /usr/bin/program /tmp/core.1234"},
"", "Failure");
- Map<String, Object> expectedData = new HashMap<>();
- expectedData.put("bin_path", TEST_BIN_PATH.toString());
+ Map<String, Object> expectedData = Map.of("bin_path", TEST_BIN_PATH.toString());
assertEquals(expectedData, coreCollector.collect(context, TEST_CORE_PATH));
}
+ @Test
+ public void reportsJstackInsteadOfGdbForJdkCores() {
+ mockExec(new String[]{"file", TEST_CORE_PATH.toString()},
+ "dump.core.5954: ELF 64-bit LSB core file x86-64, version 1 (SYSV), too many program header sections (33172)");
+ mockExec(new String[]{"/bin/sh", "-c", GDB_PATH + " -n -batch -core /tmp/core.1234 | grep '^Core was generated by'"},
+ "Core was generated by `" + JDK_PATH + " -Dconfig.id=default/container.11 -XX:+Pre'.");
+
+ String jstack = "jstack11";
+ mockExec(new String[]{"jhsdb", "jstack", "--exe", JDK_PATH, "--core", "/tmp/core.1234"},
+ jstack);
+
+ Map<String, Object> expectedData = Map.of(
+ "bin_path", JDK_PATH,
+ "backtrace_all_threads", List.of(jstack));
+ assertEquals(expectedData, coreCollector.collect(context, TEST_CORE_PATH));
+ }
+
+ @Test
+ public void reportsJstackInsteadOfGdbForJdkCoresVespa6() {
+ NodeAgentContext contextVespa6 = new NodeAgentContextImpl.Builder("container-123.domain.tld")
+ .nodeSpecBuilder(n -> n.wantedVespaVersion(Version.fromString("6.330.51")))
+ .build();
+
+ mockExec(contextVespa6, new String[]{"file", TEST_CORE_PATH.toString()},
+ "dump.core.5954: ELF 64-bit LSB core file x86-64, version 1 (SYSV), too many program header sections (33172)", "");
+ mockExec(contextVespa6, new String[]{"/bin/sh", "-c", GDB_PATH + " -n -batch -core /tmp/core.1234 | grep '^Core was generated by'"},
+ "Core was generated by `" + JDK_PATH + " -Dconfig.id=default/container.11 -XX:+Pre'.", "");
+
+ String jstack = "jstack8";
+ mockExec(contextVespa6, new String[]{"jstack", JDK_PATH, "/tmp/core.1234"},
+ jstack, "");
+
+ Map<String, Object> expectedData = Map.of(
+ "bin_path", JDK_PATH,
+ "backtrace_all_threads", List.of(jstack));
+ assertEquals(expectedData, coreCollector.collect(contextVespa6, TEST_CORE_PATH));
+ }
+
private void mockExec(String[] cmd, String output) {
mockExec(cmd, output, "");
}
private void mockExec(String[] cmd, String output, String error) {
+ mockExec(context, cmd, output, error);
+ }
+
+ private void mockExec(NodeAgentContext context, String[] cmd, String output, String error) {
when(docker.executeCommandInContainerAsRoot(context, cmd))
.thenReturn(new ProcessResult(error.isEmpty() ? 0 : 1, output, error));
}
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java
index ca9b05a3ff6..b33f52ff629 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java
@@ -1,11 +1,8 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.nodeadmin;
-import com.yahoo.config.provision.NodeType;
import com.yahoo.test.ManualClock;
import com.yahoo.vespa.hosted.dockerapi.metrics.Metrics;
-import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.NodeSpec;
-import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.NodeState;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContext;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContextImpl;
import org.junit.Test;
@@ -157,14 +154,7 @@ public class NodeAdminImplTest {
}
private NodeAgentContext createNodeAgentContext(String hostname) {
- NodeSpec nodeSpec = new NodeSpec.Builder()
- .hostname(hostname)
- .state(NodeState.active)
- .type(NodeType.tenant)
- .flavor("default")
- .build();
-
- return new NodeAgentContextImpl.Builder(nodeSpec).build();
+ return new NodeAgentContextImpl.Builder(hostname).build();
}
private NodeAgentWithScheduler mockNodeAgentWithSchedulerFactory(NodeAgentContext context) {
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityChecker.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityChecker.java
new file mode 100644
index 00000000000..48f846d5e7f
--- /dev/null
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityChecker.java
@@ -0,0 +1,526 @@
+package com.yahoo.vespa.hosted.provision.maintenance;
+
+import com.yahoo.config.provision.NodeResources;
+import com.yahoo.config.provision.NodeType;
+import com.yahoo.vespa.hosted.provision.Node;
+import com.yahoo.vespa.hosted.provision.NodeRepository;
+import com.yahoo.vespa.hosted.provision.node.Allocation;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+public class CapacityChecker {
+ private List<Node> hosts;
+ Map<String, Node> nodeMap;
+ private Map<Node, List<Node>> nodeChildren;
+ private Map<Node, AllocationResources> availableResources;
+
+ public AllocationHistory allocationHistory = null;
+
+ public CapacityChecker(NodeRepository nodeRepository) {
+ this.hosts = getHosts(nodeRepository);
+ List<Node> tenants = getTenants(nodeRepository, hosts);
+ nodeMap = constructHostnameToNodeMap(hosts);
+ this.nodeChildren = constructNodeChildrenMap(tenants, hosts, nodeMap);
+ this.availableResources = constructAvailableResourcesMap(hosts, nodeChildren);
+ }
+
+ public List<Node> getHosts() {
+ return hosts;
+ }
+
+ public Optional<HostFailurePath> worstCaseHostLossLeadingToFailure() {
+ Map<Node, Integer> timesNodeCanBeRemoved = computeMaximalRepeatedRemovals(hosts, nodeChildren, availableResources);
+ return greedyHeuristicFindFailurePath(timesNodeCanBeRemoved, hosts, nodeChildren, availableResources);
+ }
+
+ protected List<Node> findOvercommittedHosts() {
+ return findOvercommittedNodes(availableResources);
+ }
+
+ public List<Node> nodesFromHostnames(List<String> hostnames) {
+ List<Node> nodes = hostnames.stream()
+ .filter(h -> nodeMap.containsKey(h))
+ .map(h -> nodeMap.get(h))
+ .collect(Collectors.toList());
+ if (nodes.size() != hostnames.size()) {
+ Set<String> notFoundNodes = new HashSet<>(hostnames);
+ notFoundNodes.removeAll(nodes.stream().map(Node::hostname).collect(Collectors.toList()));
+ throw new IllegalArgumentException(String.format("Host(s) not found: [ %s ]",
+ String.join(", ", notFoundNodes)));
+ }
+
+ return nodes;
+ }
+
+ public Optional<HostFailurePath> findHostRemovalFailure(List<Node> hostsToRemove) {
+ var removal = findHostRemovalFailure(hostsToRemove, hosts, nodeChildren, availableResources);
+ if (removal.isEmpty()) return Optional.empty();
+ HostFailurePath failurePath = new HostFailurePath();
+ failurePath.hostsCausingFailure = hostsToRemove;
+ failurePath.failureReason = removal.get();
+ return Optional.of(failurePath);
+ }
+
+ // We only care about nodes in one of these states.
+ private static Node.State[] relevantNodeStates = {
+ Node.State.active,
+ Node.State.inactive,
+ Node.State.dirty,
+ Node.State.provisioned,
+ Node.State.ready,
+ Node.State.reserved
+ };
+
+ private List<Node> getHosts(NodeRepository nodeRepository) {
+ return nodeRepository.getNodes(NodeType.host, relevantNodeStates);
+ }
+
+ private List<Node> getTenants(NodeRepository nodeRepository, List<Node> hosts) {
+ var parentNames = hosts.stream().map(Node::hostname).collect(Collectors.toSet());
+ return nodeRepository.getNodes(NodeType.tenant, relevantNodeStates).stream()
+ .filter(t -> parentNames.contains(t.parentHostname().orElse("")))
+ .collect(Collectors.toList());
+ }
+
+ private Optional<HostFailurePath> greedyHeuristicFindFailurePath(Map<Node, Integer> heuristic, List<Node> hosts,
+ Map<Node, List<Node>> nodeChildren,
+ Map<Node, AllocationResources> availableResources) {
+ if (hosts.size() == 0) return Optional.empty();
+
+ List<Node> parentRemovalPriorityList = heuristic.entrySet().stream()
+ .sorted(Comparator.comparingInt(Map.Entry::getValue))
+ .map(Map.Entry::getKey)
+ .collect(Collectors.toList());
+
+ for (int i = 1; i <= parentRemovalPriorityList.size(); i++) {
+ List<Node> hostsToRemove = parentRemovalPriorityList.subList(0, i);
+ var hostRemovalFailure = findHostRemovalFailure(hostsToRemove, hosts, nodeChildren, availableResources);
+ if (hostRemovalFailure.isPresent()) {
+ HostFailurePath failurePath = new HostFailurePath();
+ failurePath.hostsCausingFailure = hostsToRemove;
+ failurePath.failureReason = hostRemovalFailure.get();
+ return Optional.of(failurePath);
+ }
+ }
+
+ throw new IllegalStateException("No path to failure found. This should be impossible!");
+ }
+
+ private Map<String, Node> constructHostnameToNodeMap(List<Node> nodes) {
+ return nodes.stream().collect(Collectors.toMap(Node::hostname, n -> n));
+ }
+
+ private Map<Node, List<Node>> constructNodeChildrenMap(List<Node> tenants, List<Node> hosts, Map<String, Node> hostnameToNode) {
+ Map<Node, List<Node>> nodeChildren = tenants.stream()
+ .filter(n -> n.parentHostname().isPresent())
+ .filter(n -> hostnameToNode.containsKey(n.parentHostname().get()))
+ .collect(Collectors.groupingBy(
+ n -> hostnameToNode.get(n.parentHostname().orElseThrow())));
+
+ for (var host : hosts) nodeChildren.putIfAbsent(host, List.of());
+
+ return nodeChildren;
+ }
+
+ private Map<Node, AllocationResources> constructAvailableResourcesMap(List<Node> hosts, Map<Node, List<Node>> nodeChildren) {
+ Map<Node, AllocationResources> availableResources = new HashMap<>();
+ for (var host : hosts) {
+ NodeResources hostResources = host.flavor().resources();
+ int occupiedIps = 0;
+ Set<String> ipPool = host.ipAddressPool().asSet();
+ for (var child : nodeChildren.get(host)) {
+ hostResources = hostResources.subtract(child.flavor().resources().withDiskSpeed(NodeResources.DiskSpeed.any));
+ occupiedIps += child.ipAddresses().stream().filter(ipPool::contains).count();
+ }
+ availableResources.put(host, new AllocationResources(hostResources, host.ipAddressPool().asSet().size() - occupiedIps));
+ }
+
+ return availableResources;
+ }
+
+ /**
+ * Computes a heuristic for each host, with a lower score indicating a higher perceived likelihood that removing
+ * the host causes an unrecoverable state
+ */
+ private Map<Node, Integer> computeMaximalRepeatedRemovals(List<Node> hosts, Map<Node, List<Node>> nodeChildren,
+ Map<Node, AllocationResources> availableResources) {
+ Map<Node, Integer> timesNodeCanBeRemoved = hosts.stream().collect(Collectors.toMap(
+ Function.identity(),
+ _x -> Integer.MAX_VALUE
+ ));
+ for (Node host : hosts) {
+ List<Node> children = nodeChildren.get(host);
+ if (children.size() == 0) continue;
+ Map<Node, AllocationResources> resourceMap = new HashMap<>(availableResources);
+ Map<Node, List<Allocation>> containedAllocations = collateAllocations(nodeChildren);
+
+ int timesHostCanBeRemoved = 0;
+ Optional<Node> unallocatedNode;
+ while (timesHostCanBeRemoved < 1000) { // Arbritrary upper bound
+ unallocatedNode = tryAllocateNodes(nodeChildren.get(host), hosts, resourceMap, containedAllocations);
+ if (unallocatedNode.isEmpty()) {
+ timesHostCanBeRemoved++;
+ } else break;
+ }
+ timesNodeCanBeRemoved.put(host, timesHostCanBeRemoved);
+ }
+
+ return timesNodeCanBeRemoved;
+ }
+
+ private List<Node> findOvercommittedNodes(Map<Node, AllocationResources> availableResources) {
+ List<Node> overcommittedNodes = new ArrayList<>();
+ for (var entry : availableResources.entrySet()) {
+ var resources = entry.getValue().nodeResources;
+ if (resources.vcpu() < 0 || resources.memoryGb() < 0 || resources.diskGb() < 0) {
+ overcommittedNodes.add(entry.getKey());
+ }
+ }
+ return overcommittedNodes;
+ }
+
+ private Map<Node, List<Allocation>> collateAllocations(Map<Node, List<Node>> nodeChildren) {
+ return nodeChildren.entrySet().stream().collect(Collectors.toMap(
+ Map.Entry::getKey,
+ e -> e.getValue().stream()
+ .map(Node::allocation).flatMap(Optional::stream)
+ .collect(Collectors.toList())
+ ));
+ }
+
+ /**
+ * Tests whether it's possible to remove the provided hosts.
+ * Does not mutate any input variable.
+ * @return Empty optional if removal is possible, information on what caused the failure otherwise
+ */
+ private Optional<HostRemovalFailure> findHostRemovalFailure(List<Node> hostsToRemove, List<Node> allHosts,
+ Map<Node, List<Node>> nodechildren,
+ Map<Node, AllocationResources> availableResources) {
+ var containedAllocations = collateAllocations(nodechildren);
+ var resourceMap = new HashMap<>(availableResources);
+ List<Node> validAllocationTargets = allHosts.stream()
+ .filter(h -> !hostsToRemove.contains(h))
+ .collect(Collectors.toList());
+ if (validAllocationTargets.size() == 0) {
+ return Optional.of(HostRemovalFailure.none());
+ }
+
+ allocationHistory = new AllocationHistory();
+ for (var host : hostsToRemove) {
+ Optional<Node> unallocatedNode = tryAllocateNodes(nodechildren.get(host),
+ validAllocationTargets, resourceMap, containedAllocations, true);
+
+ if (unallocatedNode.isPresent()) {
+ AllocationFailureReasonList failures = collateAllocationFailures(unallocatedNode.get(),
+ validAllocationTargets, resourceMap, containedAllocations);
+ return Optional.of(HostRemovalFailure.create(host, unallocatedNode.get(), failures));
+ }
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Attempts to allocate the listed nodes to a new host, mutating availableResources and containedAllocations,
+ * optionally returning the first node to fail, if one does.
+ * */
+ private Optional<Node> tryAllocateNodes(List<Node> nodes, List<Node> hosts,
+ Map<Node, AllocationResources> availableResources,
+ Map<Node, List<Allocation>> containedAllocations) {
+ return tryAllocateNodes(nodes, hosts, availableResources, containedAllocations, false);
+ }
+ private Optional<Node> tryAllocateNodes(List<Node> nodes, List<Node> hosts,
+ Map<Node, AllocationResources> availableResources,
+ Map<Node, List<Allocation>> containedAllocations, boolean withHistory) {
+ for (var node : nodes) {
+ var newParent = tryAllocateNode(node, hosts, availableResources, containedAllocations);
+ if (newParent.isEmpty()) {
+ if (withHistory) allocationHistory.addEntry(node, null, 0);
+ return Optional.of(node);
+ }
+ if (withHistory) {
+ long eligibleParents =
+ hosts.stream().filter(h ->
+ !violatesParentHostPolicy(node, h, containedAllocations)
+ && availableResources.get(h).satisfies(AllocationResources.from(node.flavor().resources()))).count();
+ allocationHistory.addEntry(node, newParent.get(), eligibleParents + 1);
+ }
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * @return The parent to which the node was allocated, if it was successfully allocated.
+ */
+ private Optional<Node> tryAllocateNode(Node node, List<Node> hosts,
+ Map<Node, AllocationResources> availableResources,
+ Map<Node, List<Allocation>> containedAllocations) {
+ AllocationResources requiredNodeResources = AllocationResources.from(node.flavor().resources());
+ for (var host : hosts) {
+ var availableHostResources = availableResources.get(host);
+ if (violatesParentHostPolicy(node, host, containedAllocations)) {
+ continue;
+ }
+ if (availableHostResources.satisfies(requiredNodeResources)) {
+ availableResources.put(host, availableHostResources.subtract(requiredNodeResources));
+ if (node.allocation().isPresent()) {
+ containedAllocations.get(host).add(node.allocation().get());
+ }
+ return Optional.of(host);
+ }
+ }
+
+ return Optional.empty();
+ }
+
+ private static boolean violatesParentHostPolicy(Node node, Node host, Map<Node, List<Allocation>> containedAllocations) {
+ if (node.allocation().isEmpty()) return false;
+ Allocation nodeAllocation = node.allocation().get();
+ for (var allocation : containedAllocations.get(host)) {
+ if (allocation.membership().cluster().equalsIgnoringGroupAndVespaVersion(nodeAllocation.membership().cluster())
+ && allocation.owner().equals(nodeAllocation.owner())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private AllocationFailureReasonList collateAllocationFailures(Node node, List<Node> hosts,
+ Map<Node, AllocationResources> availableResources,
+ Map<Node, List<Allocation>> containedAllocations) {
+ List<AllocationFailureReason> allocationFailureReasons = new ArrayList<>();
+ for (var host : hosts) {
+ AllocationFailureReason reason = new AllocationFailureReason(host);
+ var availableHostResources = availableResources.get(host);
+ reason.violatesParentHostPolicy = violatesParentHostPolicy(node, host, containedAllocations);
+
+ NodeResources l = availableHostResources.nodeResources;
+ NodeResources r = node.flavor().resources();
+ if (l.vcpu() < r.vcpu()) { reason.insufficientVcpu = true; }
+ if (l.memoryGb() < r.memoryGb()) { reason.insufficientMemoryGb = true; }
+ if (l.diskGb() < r.diskGb()) { reason.insufficientDiskGb = true; }
+ if (r.diskSpeed() != NodeResources.DiskSpeed.any && r.diskSpeed() != l.diskSpeed())
+ { reason.incompatibleDiskSpeed = true; }
+ if (availableHostResources.availableIPs < 1) { reason.insufficientAvailableIPs = true; }
+
+ allocationFailureReasons.add(reason);
+ }
+
+ return new AllocationFailureReasonList(allocationFailureReasons);
+ }
+
+ /**
+ * Contains the list of hosts that, upon being removed, caused an unrecoverable state,
+ * as well as the specific host and tenant which caused it.
+ */
+ public static class HostFailurePath {
+ public List<Node> hostsCausingFailure;
+ public HostRemovalFailure failureReason;
+ }
+
+ /**
+ * Data class used for detailing why removing the given tenant from the given host was unsuccessful.
+ * A failure might not be caused by failing to allocate a specific tenant, in which case the fields
+ * will be empty.
+ */
+ public static class HostRemovalFailure {
+ public Optional<Node> host;
+ public Optional<Node> tenant;
+ public AllocationFailureReasonList allocationFailures;
+
+ public static HostRemovalFailure none() {
+ return new HostRemovalFailure(
+ Optional.empty(),
+ Optional.empty(),
+ new AllocationFailureReasonList(List.of()));
+ }
+
+ public static HostRemovalFailure create(Node host, Node tenant, AllocationFailureReasonList failureReasons) {
+ return new HostRemovalFailure(
+ Optional.of(host),
+ Optional.of(tenant),
+ failureReasons);
+ }
+
+ private HostRemovalFailure(Optional<Node> host, Optional<Node> tenant, AllocationFailureReasonList allocationFailures) {
+ this.host = host;
+ this.tenant = tenant;
+ this.allocationFailures = allocationFailures;
+ }
+
+ @Override
+ public String toString() {
+ if (host.isEmpty() || tenant.isEmpty()) return "No removal candidates exists.";
+ return String.format(
+ "Failure to remove host %s" +
+ "\n\tNo new host found for tenant %s:" +
+ "\n\t\tSingular Reasons: %s" +
+ "\n\t\tTotal Reasons: %s",
+ this.host.get().hostname(),
+ this.tenant.get().hostname(),
+ this.allocationFailures.singularReasonFailures().toString(),
+ this.allocationFailures.toString()
+ );
+ }
+ }
+
+ /**
+ * Used to describe the resources required for a tenant, and available to a host.
+ */
+ private static class AllocationResources {
+ NodeResources nodeResources;
+ int availableIPs;
+
+ public static AllocationResources from(NodeResources nodeResources) {
+ return new AllocationResources(nodeResources, 1);
+ }
+
+ public AllocationResources(NodeResources nodeResources, int availableIPs) {
+ this.nodeResources = nodeResources;
+ this.availableIPs = availableIPs;
+ }
+
+ public boolean satisfies(AllocationResources other) {
+ if (!this.nodeResources.satisfies(other.nodeResources)) return false;
+ return this.availableIPs >= other.availableIPs;
+ }
+
+ public AllocationResources subtract(AllocationResources other) {
+ return new AllocationResources(this.nodeResources.subtract(other.nodeResources), this.availableIPs - other.availableIPs);
+ }
+ }
+
+ /**
+ * Keeps track of the reason why a host rejected an allocation.
+ */
+ private static class AllocationFailureReason {
+ Node host;
+ public AllocationFailureReason (Node host) {
+ this.host = host;
+ }
+ public boolean insufficientVcpu = false;
+ public boolean insufficientMemoryGb = false;
+ public boolean insufficientDiskGb = false;
+ public boolean incompatibleDiskSpeed = false;
+ public boolean insufficientAvailableIPs = false;
+ public boolean violatesParentHostPolicy = false;
+
+ public int numberOfReasons() {
+ int n = 0;
+ if (insufficientVcpu) n++;
+ if (insufficientMemoryGb) n++;
+ if (insufficientDiskGb) n++;
+ if (incompatibleDiskSpeed) n++;
+ if (insufficientAvailableIPs) n++;
+ if (violatesParentHostPolicy) n++;
+ return n;
+ }
+
+ @Override
+ public String toString() {
+ List<String> reasons = new ArrayList<>();
+ if (insufficientVcpu) reasons.add("insufficientVcpu");
+ if (insufficientMemoryGb) reasons.add("insufficientMemoryGb");
+ if (insufficientDiskGb) reasons.add("insufficientDiskGb");
+ if (incompatibleDiskSpeed) reasons.add("incompatibleDiskSpeed");
+ if (insufficientAvailableIPs) reasons.add("insufficientAvailableIPs");
+ if (violatesParentHostPolicy) reasons.add("violatesParentHostPolicy");
+
+ return String.format("[%s]", String.join(", ", reasons));
+ }
+ }
+
+ /**
+ * Provides convenient methods for tallying failures.
+ */
+ public static class AllocationFailureReasonList {
+ private List<AllocationFailureReason> allocationFailureReasons;
+ public AllocationFailureReasonList(List<AllocationFailureReason> allocationFailureReasons) {
+ this.allocationFailureReasons = allocationFailureReasons;
+ }
+
+ public long insufficientVcpu() { return allocationFailureReasons.stream().filter(r -> r.insufficientVcpu).count(); }
+ public long insufficientMemoryGb() { return allocationFailureReasons.stream().filter(r -> r.insufficientMemoryGb).count(); }
+ public long insufficientDiskGb() { return allocationFailureReasons.stream().filter(r -> r.insufficientDiskGb).count(); }
+ public long incompatibleDiskSpeed() { return allocationFailureReasons.stream().filter(r -> r.incompatibleDiskSpeed).count(); }
+ public long insufficientAvailableIps() { return allocationFailureReasons.stream().filter(r -> r.insufficientAvailableIPs).count(); }
+ public long violatesParentHostPolicy() { return allocationFailureReasons.stream().filter(r -> r.violatesParentHostPolicy).count(); }
+
+ public AllocationFailureReasonList singularReasonFailures() {
+ return new AllocationFailureReasonList(allocationFailureReasons.stream()
+ .filter(reason -> reason.numberOfReasons() == 1).collect(Collectors.toList()));
+ }
+ public AllocationFailureReasonList multipleReasonFailures() {
+ return new AllocationFailureReasonList(allocationFailureReasons.stream()
+ .filter(reason -> reason.numberOfReasons() > 1).collect(Collectors.toList()));
+ }
+ public long size() {
+ return allocationFailureReasons.size();
+ }
+ @Override
+ public String toString() {
+ return String.format("CPU (%3d), Memory (%3d), Disk size (%3d), Disk speed (%3d), IP (%3d), Parent-Host Policy (%3d)",
+ insufficientVcpu(), insufficientMemoryGb(), insufficientDiskGb(),
+ incompatibleDiskSpeed(), insufficientAvailableIps(), violatesParentHostPolicy());
+ }
+ }
+
+ public static class AllocationHistory {
+ public static class Entry {
+ public Node tenant;
+ public Node newParent;
+ public long eligibleParents;
+
+ public Entry(Node tenant, Node newParent, long eligibleParents) {
+ this.tenant = tenant;
+ this.newParent = newParent;
+ this.eligibleParents = eligibleParents;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%-20s %-65s -> %15s [%3d valid]",
+ tenant.hostname().replaceFirst("\\..+", ""),
+ tenant.flavor().resources(),
+ newParent == null ? "x" : newParent.hostname().replaceFirst("\\..+", ""),
+ this.eligibleParents
+ );
+ }
+ }
+
+ public List<Entry> historyEntries;
+
+ public AllocationHistory() {
+ this.historyEntries = new ArrayList<>();
+ }
+
+ public void addEntry(Node tenant, Node newParent, long eligibleParents) {
+ this.historyEntries.add(new Entry(tenant, newParent, eligibleParents));
+ }
+
+ public Set<String> oldParents() {
+ Set<String> oldParents = new HashSet<>();
+ for (var entry : historyEntries)
+ entry.tenant.parentHostname().ifPresent(oldParents::add);
+ return oldParents;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder out = new StringBuilder();
+
+ String currentParent = "";
+ for (var entry : historyEntries) {
+ String parentName = entry.tenant.parentHostname().orElseThrow();
+ if (!parentName.equals(currentParent)) {
+ currentParent = parentName;
+ out.append(parentName).append("\n");
+ }
+ out.append(entry.toString()).append("\n");
+ }
+
+ return out.toString();
+ }
+ }
+}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityReportMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityReportMaintainer.java
index 44d43081ef2..3c47e418b94 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityReportMaintainer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityReportMaintainer.java
@@ -1,23 +1,15 @@
package com.yahoo.vespa.hosted.provision.maintenance;
-import com.yahoo.config.provision.NodeResources;
-import com.yahoo.config.provision.NodeType;
import com.yahoo.jdisc.Metric;
import com.yahoo.log.LogLevel;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import java.time.Duration;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
import java.util.logging.Logger;
import java.util.stream.Collectors;
-import com.yahoo.vespa.hosted.provision.node.Allocation;
-
import java.util.*;
-import java.util.function.Function;
/**
* Performs analysis on the node repository to produce metrics that pertain to the capacity of the node repository.
@@ -29,7 +21,6 @@ import java.util.function.Function;
* @author mgimle
*/
public class CapacityReportMaintainer extends Maintainer {
-
private final Metric metric;
private final NodeRepository nodeRepository;
private static final Logger log = Logger.getLogger(CapacityReportMaintainer.class.getName());
@@ -44,403 +35,20 @@ public class CapacityReportMaintainer extends Maintainer {
@Override
protected void maintain() {
- metric.set("overcommittedHosts", countOvercommittedHosts(), null);
-
- Optional<HostFailurePath> failurePath = worstCaseHostLossLeadingToFailure();
- if (failurePath.isPresent()) {
- int worstCaseHostLoss = failurePath.get().hostsCausingFailure.size();
- metric.set("spareHostCapacity", worstCaseHostLoss - 1, null);
- }
- }
-
- protected Optional<HostFailurePath> worstCaseHostLossLeadingToFailure() {
- List<Node> hosts = getHosts();
- List<Node> tenants = getTenants(hosts);
- Map<String, Node> nodeMap = constructHostnameToNodeMap(hosts);
- Map<Node, List<Node>> nodeChildren = constructNodeChildrenMap(tenants, hosts, nodeMap);
- Map<Node, AllocationResources> availableResources = constructAvailableResourcesMap(hosts, nodeChildren);
-
- Map<Node, Integer> timesNodeCanBeRemoved = computeMaximalRepeatedRemovals(hosts, nodeChildren, availableResources);
- return greedyHeuristicFindFailurePath(timesNodeCanBeRemoved, hosts, nodeChildren, availableResources);
- }
-
- // We only care about nodes in one of these states.
- private Node.State[] relevantNodeStates = {
- Node.State.active,
- Node.State.inactive,
- Node.State.dirty,
- Node.State.provisioned,
- Node.State.ready,
- Node.State.reserved
- };
-
- private List<Node> getHosts() {
- return nodeRepository.getNodes(NodeType.host, relevantNodeStates);
- }
-
- private List<Node> getTenants(List<Node> hosts) {
- var parentNames = hosts.stream().map(Node::hostname).collect(Collectors.toSet());
- return nodeRepository.getNodes(NodeType.tenant, relevantNodeStates).stream()
- .filter(t -> parentNames.contains(t.parentHostname().orElse("")))
- .collect(Collectors.toList());
- }
-
- private Optional<HostFailurePath> greedyHeuristicFindFailurePath(Map<Node, Integer> heuristic, List<Node> hosts,
- Map<Node, List<Node>> nodeChildren,
- Map<Node, AllocationResources> availableResources) {
- if (hosts.size() == 0) return Optional.empty();
- List<Node> parentRemovalPriorityList = heuristic.entrySet().stream()
- .sorted(Comparator.comparingInt(Map.Entry::getValue))
- .map(Map.Entry::getKey)
- .collect(Collectors.toList());
- for (int i = 1; i <= parentRemovalPriorityList.size(); i++) {
- List<Node> hostsToRemove = parentRemovalPriorityList.subList(0, i);
- var hostRemovalFailure = findHostRemovalFailure(hostsToRemove, hosts, nodeChildren, availableResources);
- if (hostRemovalFailure.isPresent()) {
- HostFailurePath failurePath = new HostFailurePath();
- failurePath.hostsCausingFailure = hostsToRemove;
- failurePath.failureReason = hostRemovalFailure.get();
- return Optional.of(failurePath);
+ if (!nodeRepository.zone().cloud().value().equals("aws")) {
+ CapacityChecker capacityChecker = new CapacityChecker(this.nodeRepository);
+ List<Node> overcommittedHosts = capacityChecker.findOvercommittedHosts();
+ if (overcommittedHosts.size() != 0) {
+ log.log(LogLevel.WARNING, String.format("%d nodes are overcommitted! [ %s ]", overcommittedHosts.size(),
+ overcommittedHosts.stream().map(Node::hostname).collect(Collectors.joining(", "))));
}
- }
-
- throw new IllegalStateException("No path to failure found. This should be impossible!");
- }
-
- protected int countOvercommittedHosts() {
- List<Node> hosts = getHosts();
- List<Node> tenants = getTenants(hosts);
- var nodeMap = constructHostnameToNodeMap(hosts);
- var nodeChildren = constructNodeChildrenMap(tenants, hosts, nodeMap);
- var availableResources = constructAvailableResourcesMap(hosts, nodeChildren);
-
- List<Node> overcommittedNodes = findOvercommittedNodes(availableResources);
- if (overcommittedNodes.size() != 0) {
- log.log(LogLevel.WARNING, String.format("%d nodes are overcommitted! [ %s ]", overcommittedNodes.size(),
- overcommittedNodes.stream().map(Node::hostname).collect(Collectors.joining(", "))));
- }
- return overcommittedNodes.size();
- }
-
- private Map<String, Node> constructHostnameToNodeMap(List<Node> nodes) {
- return nodes.stream().collect(Collectors.toMap(Node::hostname, n -> n));
- }
-
- private Map<Node, List<Node>> constructNodeChildrenMap(List<Node> tenants, List<Node> hosts, Map<String, Node> hostnameToNode) {
- Map<Node, List<Node>> nodeChildren = tenants.stream()
- .filter(n -> n.parentHostname().isPresent())
- .filter(n -> hostnameToNode.containsKey(n.parentHostname().get()))
- .collect(Collectors.groupingBy(
- n -> hostnameToNode.get(n.parentHostname().orElseThrow())));
-
- for (var host : hosts) nodeChildren.putIfAbsent(host, List.of());
-
- return nodeChildren;
- }
-
- private Map<Node, AllocationResources> constructAvailableResourcesMap(List<Node> hosts, Map<Node, List<Node>> nodeChildren) {
- Map<Node, AllocationResources> availableResources = new HashMap<>();
- for (var host : hosts) {
- NodeResources hostResources = host.flavor().resources();
- int occupiedIps = 0;
- Set<String> ipPool = host.ipAddressPool().asSet();
- for (var child : nodeChildren.get(host)) {
- hostResources = hostResources.subtract(child.flavor().resources());
- occupiedIps += child.ipAddresses().stream().filter(ipPool::contains).count();
- }
- availableResources.put(host, new AllocationResources(hostResources, host.ipAddressPool().asSet().size() - occupiedIps));
- }
-
- return availableResources;
- }
-
- /**
- * Computes a heuristic for each host, with a lower score indicating a higher perceived likelihood that removing
- * the host causes an unrecoverable state
- */
- private Map<Node, Integer> computeMaximalRepeatedRemovals(List<Node> hosts, Map<Node, List<Node>> nodeChildren,
- Map<Node, AllocationResources> availableResources) {
- Map<Node, Integer> timesNodeCanBeRemoved = hosts.stream().collect(Collectors.toMap(
- Function.identity(),
- _x -> Integer.MAX_VALUE
- ));
- for (Node host : hosts) {
- List<Node> children = nodeChildren.get(host);
- if (children.size() == 0) continue;
- Map<Node, AllocationResources> resourceMap = new HashMap<>(availableResources);
- Map<Node, List<Allocation>> containedAllocations = collateAllocations(nodeChildren);
-
- int timesHostCanBeRemoved = 0;
- Optional<Node> unallocatedTenant;
- while (timesHostCanBeRemoved < 1000) { // Arbritrary upper bound
- unallocatedTenant = tryAllocateNodes(nodeChildren.get(host), hosts, resourceMap, containedAllocations);
- if (unallocatedTenant.isEmpty()) {
- timesHostCanBeRemoved++;
- } else break;
- }
- timesNodeCanBeRemoved.put(host, timesHostCanBeRemoved);
- }
-
- return timesNodeCanBeRemoved;
- }
-
- private List<Node> findOvercommittedNodes(Map<Node, AllocationResources> availableResources) {
- List<Node> overcommittedNodes = new ArrayList<>();
- for (var entry : availableResources.entrySet()) {
- var resources = entry.getValue().nodeResources;
- if (resources.vcpu() < 0 || resources.memoryGb() < 0 || resources.diskGb() < 0) {
- overcommittedNodes.add(entry.getKey());
- }
- }
- return overcommittedNodes;
- }
-
- private Map<Node, List<Allocation>> collateAllocations(Map<Node, List<Node>> nodeChildren) {
- return nodeChildren.entrySet().stream().collect(Collectors.toMap(
- Map.Entry::getKey,
- e -> e.getValue().stream()
- .map(Node::allocation).flatMap(Optional::stream)
- .collect(Collectors.toList())
- ));
- }
-
- /**
- * Tests whether it's possible to remove the provided hosts.
- * Does not mutate any input variable.
- * @return Empty optional if removal is possible, information on what caused the failure otherwise
- */
- private Optional<HostRemovalFailure> findHostRemovalFailure(List<Node> hostsToRemove, List<Node> allHosts,
- Map<Node, List<Node>> nodechildren,
- Map<Node, AllocationResources> availableResources) {
- var containedAllocations = collateAllocations(nodechildren);
- var resourceMap = new HashMap<>(availableResources);
- List<Node> validAllocationTargets = allHosts.stream()
- .filter(h -> !hostsToRemove.contains(h))
- .collect(Collectors.toList());
- if (validAllocationTargets.size() == 0) {
- return Optional.of(HostRemovalFailure.none());
- }
-
- for (var host : hostsToRemove) {
- Optional<Node> unallocatedNode = tryAllocateNodes(nodechildren.get(host),
- validAllocationTargets, resourceMap, containedAllocations);
-
- if (unallocatedNode.isPresent()) {
- AllocationFailureReasonList failures = collateAllocationFailures(unallocatedNode.get(),
- validAllocationTargets, resourceMap, containedAllocations);
- return Optional.of(HostRemovalFailure.create(host, unallocatedNode.get(), failures));
- }
- }
- return Optional.empty();
- }
+ metric.set("overcommittedHosts", overcommittedHosts.size(), null);
- /**
- * Attempts to allocate the listed nodes to a new host, mutating availableResources and containedAllocations,
- * optionally returning the first node to fail, if one does.
- * */
- private Optional<Node> tryAllocateNodes(List<Node> nodes, List<Node> hosts,
- Map<Node, AllocationResources> availableResources,
- Map<Node, List<Allocation>> containedAllocations) {
- for (var node : nodes) {
- if (!tryAllocateNode(node, hosts, availableResources, containedAllocations)) {
- return Optional.of(node);
+ Optional<CapacityChecker.HostFailurePath> failurePath = capacityChecker.worstCaseHostLossLeadingToFailure();
+ if (failurePath.isPresent()) {
+ int worstCaseHostLoss = failurePath.get().hostsCausingFailure.size();
+ metric.set("spareHostCapacity", worstCaseHostLoss - 1, null);
}
}
- return Optional.empty();
- }
-
- private boolean tryAllocateNode(Node node, List<Node> hosts,
- Map<Node, AllocationResources> availableResources,
- Map<Node, List<Allocation>> containedAllocations) {
- AllocationResources requiredNodeResources = AllocationResources.from(node.flavor().resources());
- for (var host : hosts) {
- var availableHostResources = availableResources.get(host);
- if (violatesParentHostPolicy(node, host, containedAllocations)) {
- continue;
- }
- if (availableHostResources.satisfies(requiredNodeResources)) {
- availableResources.put(host, availableHostResources.subtract(requiredNodeResources));
- if (node.allocation().isPresent()) {
- containedAllocations.get(host).add(node.allocation().get());
- }
- return true;
- }
- }
-
- return false;
- }
-
- private boolean violatesParentHostPolicy(Node node, Node host, Map<Node, List<Allocation>> containedAllocations) {
- if (node.allocation().isEmpty()) return false;
- Allocation nodeAllocation = node.allocation().get();
- for (var allocation : containedAllocations.get(host)) {
- if (allocation.membership().cluster().equalsIgnoringGroupAndVespaVersion(nodeAllocation.membership().cluster())
- && allocation.owner().equals(nodeAllocation.owner())) {
- return true;
- }
- }
- return false;
- }
-
- private AllocationFailureReasonList collateAllocationFailures(Node node, List<Node> hosts,
- Map<Node, AllocationResources> availableResources,
- Map<Node, List<Allocation>> containedAllocations) {
- List<AllocationFailureReason> allocationFailureReasons = new ArrayList<>();
- for (var host : hosts) {
- AllocationFailureReason reason = new AllocationFailureReason(host);
- var availableHostResources = availableResources.get(host);
- reason.violatesParentHostPolicy = violatesParentHostPolicy(node, host, containedAllocations);
-
- NodeResources l = availableHostResources.nodeResources;
- NodeResources r = node.flavor().resources();
- if (l.vcpu() < r.vcpu()) { reason.insufficientVcpu = true; }
- if (l.memoryGb() < r.memoryGb()) { reason.insufficientMemoryGb = true; }
- if (l.diskGb() < r.diskGb()) { reason.insufficientDiskGb = true; }
- if (r.diskSpeed() != NodeResources.DiskSpeed.any && r.diskSpeed() != l.diskSpeed())
- { reason.incompatibleDiskSpeed = true; }
- if (availableHostResources.availableIPs < 1) { reason.insufficientAvailableIPs = true; }
-
- allocationFailureReasons.add(reason);
- }
-
- return new AllocationFailureReasonList(allocationFailureReasons);
- }
-
- /**
- * Contains the list of hosts that, upon being removed, caused an unrecoverable state,
- * as well as the specific host and tenant which caused it.
- */
- public static class HostFailurePath {
- List<Node> hostsCausingFailure;
- HostRemovalFailure failureReason;
- }
-
- /**
- * Data class used for detailing why removing the given tenant from the given host was unsuccessful.
- * A failure might not be caused by failing to allocate a specific tenant, in which case the fields
- * will be empty.
- */
- public static class HostRemovalFailure {
- Optional<Node> host;
- Optional<Node> tenant;
- AllocationFailureReasonList failureReasons;
- public static HostRemovalFailure none() {
- return new HostRemovalFailure(
- Optional.empty(),
- Optional.empty(),
- new AllocationFailureReasonList(List.of()));
- }
- public static HostRemovalFailure create(Node host, Node tenant, AllocationFailureReasonList failureReasons) {
- return new HostRemovalFailure(
- Optional.of(host),
- Optional.of(tenant),
- failureReasons);
- }
- private HostRemovalFailure(Optional<Node> host, Optional<Node> tenant, AllocationFailureReasonList failureReasons) {
- this.host = host;
- this.tenant = tenant;
- this.failureReasons = failureReasons;
- }
- }
-
- /**
- * Used to describe the resources required for a tenant, and available to a host.
- */
- private static class AllocationResources {
- NodeResources nodeResources;
- int availableIPs;
-
- public static AllocationResources from(NodeResources nodeResources) {
- return new AllocationResources(nodeResources, 1);
- }
-
- public AllocationResources(NodeResources nodeResources, int availableIPs) {
- this.nodeResources = nodeResources;
- this.availableIPs = availableIPs;
- }
-
- public boolean satisfies(AllocationResources other) {
- if (!this.nodeResources.satisfies(other.nodeResources)) return false;
- return this.availableIPs >= other.availableIPs;
- }
-
- public AllocationResources subtract(AllocationResources other) {
- return new AllocationResources(this.nodeResources.subtract(other.nodeResources), this.availableIPs - other.availableIPs);
- }
- }
-
- /**
- * Keeps track of the reason why a host rejected an allocation.
- */
- private class AllocationFailureReason {
- Node host;
- public AllocationFailureReason (Node host) {
- this.host = host;
- }
- public boolean insufficientVcpu = false;
- public boolean insufficientMemoryGb = false;
- public boolean insufficientDiskGb = false;
- public boolean incompatibleDiskSpeed = false;
- public boolean insufficientAvailableIPs = false;
- public boolean violatesParentHostPolicy = false;
-
- public int numberOfReasons() {
- int n = 0;
- if (insufficientVcpu) n++;
- if (insufficientMemoryGb) n++;
- if (insufficientDiskGb) n++;
- if (incompatibleDiskSpeed) n++;
- if (insufficientAvailableIPs) n++;
- if (violatesParentHostPolicy) n++;
- return n;
- }
-
- @Override
- public String toString() {
- List<String> reasons = new ArrayList<>();
- if (insufficientVcpu) reasons.add("insufficientVcpu");
- if (insufficientMemoryGb) reasons.add("insufficientMemoryGb");
- if (insufficientDiskGb) reasons.add("insufficientDiskGb");
- if (incompatibleDiskSpeed) reasons.add("incompatibleDiskSpeed");
- if (insufficientAvailableIPs) reasons.add("insufficientAvailableIPs");
- if (violatesParentHostPolicy) reasons.add("violatesParentHostPolicy");
-
- return String.format("[%s]", String.join(", ", reasons));
- }
- }
-
- /**
- * Provides convenient methods for tallying failures.
- */
- public static class AllocationFailureReasonList {
- private List<AllocationFailureReason> allocationFailureReasons;
- public AllocationFailureReasonList(List<AllocationFailureReason> allocationFailureReasons) {
- this.allocationFailureReasons = allocationFailureReasons;
- }
-
- long insufficientVcpu() { return allocationFailureReasons.stream().filter(r -> r.insufficientVcpu).count(); }
- long insufficientMemoryGb() { return allocationFailureReasons.stream().filter(r -> r.insufficientMemoryGb).count(); }
- long insufficientDiskGb() { return allocationFailureReasons.stream().filter(r -> r.insufficientDiskGb).count(); }
- long incompatibleDiskSpeed() { return allocationFailureReasons.stream().filter(r -> r.incompatibleDiskSpeed).count(); }
- long insufficientAvailableIps() { return allocationFailureReasons.stream().filter(r -> r.insufficientAvailableIPs).count(); }
- long violatesParentHostPolicy() { return allocationFailureReasons.stream().filter(r -> r.violatesParentHostPolicy).count(); }
-
- public AllocationFailureReasonList singularReasonFailures() {
- return new AllocationFailureReasonList(allocationFailureReasons.stream()
- .filter(reason -> reason.numberOfReasons() == 1).collect(Collectors.toList()));
- }
- public AllocationFailureReasonList multipleReasonFailures() {
- return new AllocationFailureReasonList(allocationFailureReasons.stream()
- .filter(reason -> reason.numberOfReasons() > 1).collect(Collectors.toList()));
- }
- public long size() {
- return allocationFailureReasons.size();
- }
- @Override
- public String toString() {
- return String.format("CPU (%3d), Memory (%3d), Disk size (%3d), Disk speed (%3d), IP (%3d), Parent-Host Policy (%3d)",
- insufficientVcpu(), insufficientMemoryGb(), insufficientDiskGb(),
- incompatibleDiskSpeed(), insufficientAvailableIps(), violatesParentHostPolicy());
- }
}
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRepositoryMaintenance.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRepositoryMaintenance.java
index f661977d933..bb1ff637f08 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRepositoryMaintenance.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRepositoryMaintenance.java
@@ -82,7 +82,7 @@ public class NodeRepositoryMaintenance extends AbstractComponent {
new HostProvisionMaintainer(nodeRepository, durationFromEnv("host_provisioner_interval").orElse(defaults.hostProvisionerInterval), hostProvisioner, flagSource));
hostDeprovisionMaintainer = provisionServiceProvider.getHostProvisioner().map(hostProvisioner ->
new HostDeprovisionMaintainer(nodeRepository, durationFromEnv("host_deprovisioner_interval").orElse(defaults.hostDeprovisionerInterval), hostProvisioner, flagSource));
- capacityReportMaintainer = new CapacityReportMaintainer(nodeRepository, metric, durationFromEnv("alert_interval").orElse(defaults.nodeAlerterInterval));
+ capacityReportMaintainer = new CapacityReportMaintainer(nodeRepository, metric, durationFromEnv("capacity_report_interval").orElse(defaults.capacityReportInterval));
// The DuperModel is filled with infrastructure applications by the infrastructure provisioner, so explicitly run that now
infrastructureProvisioner.maintain();
@@ -143,7 +143,7 @@ public class NodeRepositoryMaintenance extends AbstractComponent {
private final Duration dirtyExpiry;
private final Duration provisionedExpiry;
private final Duration rebootInterval;
- private final Duration nodeAlerterInterval;
+ private final Duration capacityReportInterval;
private final Duration metricsInterval;
private final Duration retiredInterval;
private final Duration infrastructureProvisionInterval;
@@ -162,7 +162,7 @@ public class NodeRepositoryMaintenance extends AbstractComponent {
failedExpirerInterval = Duration.ofMinutes(10);
provisionedExpiry = Duration.ofHours(4);
rebootInterval = Duration.ofDays(30);
- nodeAlerterInterval = Duration.ofHours(1);
+ capacityReportInterval = Duration.ofHours(1);
metricsInterval = Duration.ofMinutes(1);
infrastructureProvisionInterval = Duration.ofMinutes(1);
throttlePolicy = NodeFailer.ThrottlePolicy.hosted;
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Agent.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Agent.java
index 812c370df5f..f46e2f501bc 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Agent.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Agent.java
@@ -7,5 +7,5 @@ package com.yahoo.vespa.hosted.provision.node;
* @author bratseth
*/
public enum Agent {
- system, application, operator, NodeRetirer, NodeFailer
+ system, application, operator, NodeFailer
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java
index cf6531c0748..6198183be89 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java
@@ -385,7 +385,6 @@ public class NodeSerializer {
case "application" : return Agent.application;
case "system" : return Agent.system;
case "operator" : return Agent.operator;
- case "NodeRetirer" : return Agent.system; // TODO: Remove after 7.67
case "NodeFailer" : return Agent.NodeFailer;
}
throw new IllegalArgumentException("Unknown node event agent '" + eventAgentField.asString() + "'");
@@ -395,7 +394,6 @@ public class NodeSerializer {
case application : return "application";
case system : return "system";
case operator : return "operator";
- case NodeRetirer : return "system"; // TODO: Remove after 7.67
case NodeFailer : return "NodeFailer";
}
throw new IllegalArgumentException("Serialized form of '" + agent + "' not defined");
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java
index 94db765c08a..77ca4b01cf2 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java
@@ -5,7 +5,6 @@ import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.NodeResources;
-import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.Zone;
import com.yahoo.config.provision.NodeFlavors;
@@ -64,29 +63,20 @@ public class CapacityPolicies {
if (requestedResources.isEmpty())
return defaultNodeResources(cluster.type());
- // Flavor is specified and is allocateByLegacyName: Handle legacy flavor specs
- if (zone.system() == SystemName.cd)
- return flavors.exists(requestedResources.get().legacyName().get()) ? requestedResources.get()
- : defaultNodeResources(cluster.type());
- else {
- switch (zone.environment()) {
- case dev: case test: case staging: return defaultNodeResources(cluster.type());
- default:
- flavors.getFlavorOrThrow(requestedResources.get().legacyName().get()); // verify existence
- // Return this spec containing the legacy flavor name, not the flavor's capacity object
- // which describes the flavors capacity, as the point of legacy allocation is to match
- // by name, not by resources
- return requestedResources.get();
- }
+ switch (zone.environment()) {
+ case dev: case test: case staging: return defaultNodeResources(cluster.type());
+ default:
+ flavors.getFlavorOrThrow(requestedResources.get().legacyName().get()); // verify existence
+ // Return this spec containing the legacy flavor name, not the flavor's capacity object
+ // which describes the flavors capacity, as the point of legacy allocation is to match
+ // by name, not by resources
+ return requestedResources.get();
}
}
private NodeResources defaultNodeResources(ClusterSpec.Type clusterType) {
if (clusterType == ClusterSpec.Type.admin)
- return new NodeResources(0.5, 3, 50);
-
- if (zone.system().isCd() && zone.environment().isTest())
- new NodeResources(4, 4, 50);
+ return nodeResourcesForAdminCluster();
return new NodeResources(1.5, 8, 50);
}
@@ -114,4 +104,9 @@ public class CapacityPolicies {
return nodeCount;
}
+ private NodeResources nodeResourcesForAdminCluster() {
+ double memoryInGb = (zone.system().isCd() ? 2 : 3);
+ return new NodeResources(0.5, memoryInGb, 50);
+ }
+
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisioner.java
index 4f0081b6a7f..ea30fba9798 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisioner.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisioner.java
@@ -14,7 +14,7 @@ import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.lb.LoadBalancer;
import com.yahoo.vespa.hosted.provision.lb.LoadBalancerId;
import com.yahoo.vespa.hosted.provision.lb.LoadBalancerInstance;
-import com.yahoo.vespa.hosted.provision.lb.LoadBalancerServiceException;
+import com.yahoo.config.provision.exception.LoadBalancerServiceException;
import com.yahoo.vespa.hosted.provision.lb.LoadBalancerService;
import com.yahoo.vespa.hosted.provision.lb.Real;
import com.yahoo.vespa.hosted.provision.node.IP;
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/HostCapacityResponse.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/HostCapacityResponse.java
new file mode 100644
index 00000000000..7b0eb38b628
--- /dev/null
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/HostCapacityResponse.java
@@ -0,0 +1,161 @@
+package com.yahoo.vespa.hosted.provision.restapi.v2;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.yahoo.container.jdisc.HttpRequest;
+import com.yahoo.container.jdisc.HttpResponse;
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.JsonFormat;
+import com.yahoo.slime.Slime;
+import com.yahoo.vespa.hosted.provision.Node;
+import com.yahoo.vespa.hosted.provision.NodeRepository;
+import com.yahoo.vespa.hosted.provision.maintenance.CapacityChecker;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+
+public class HostCapacityResponse extends HttpResponse {
+ private final StringBuilder text;
+ private final Slime slime;
+ private final CapacityChecker capacityChecker;
+ private final boolean json;
+
+ public HostCapacityResponse(NodeRepository nodeRepository, HttpRequest request) {
+ super(200);
+ capacityChecker = new CapacityChecker(nodeRepository);
+
+ json = request.getBooleanProperty("json");
+ String hostsJson = request.getProperty("hosts");
+
+ text = new StringBuilder();
+ slime = new Slime();
+ Cursor root = slime.setObject();
+
+ if (hostsJson != null) {
+ List<Node> hosts = parseHostList(hostsJson);
+ hostRemovalResponse(root, hosts);
+ } else {
+ zoneFailureReponse(root);
+ }
+ }
+
+ private List<Node> parseHostList(String hosts) {
+ List<String> hostNames = Arrays.asList(hosts.split(","));
+ try {
+ return capacityChecker.nodesFromHostnames(hostNames);
+ } catch (IllegalArgumentException e) {
+ throw new NotFoundException(e.getMessage());
+ }
+ }
+
+ private void hostRemovalResponse(Cursor root, List<Node> hosts) {
+ var failure = capacityChecker.findHostRemovalFailure(hosts);
+ if (failure.isPresent() && failure.get().failureReason.allocationFailures.size() == 0) {
+ root.setBool("removalPossible", false);
+ error(root, "Removing all hosts is trivially impossible.");
+ } else {
+ if (json) hostLossPossibleToSlime(root, failure, hosts);
+ else hostLossPossibleToText(failure, hosts);
+ }
+ }
+
+ private void zoneFailureReponse(Cursor root) {
+ var failurePath = capacityChecker.worstCaseHostLossLeadingToFailure();
+ if (failurePath.isPresent()) {
+ if (json) zoneFailurePathToSlime(root, failurePath.get());
+ else zoneFailurePathToText(failurePath.get());
+ } else {
+ error(root, "Node repository contained no hosts.");
+ }
+ }
+
+ private void error(Cursor root, String errorMessage) {
+ if (json) root.setString("error", errorMessage);
+ else text.append(errorMessage);
+ }
+
+ private void hostLossPossibleToText(Optional<CapacityChecker.HostFailurePath> failure, List<Node> hostsToRemove) {
+ text.append(String.format("Attempting to remove %d hosts: ", hostsToRemove.size()));
+ CapacityChecker.AllocationHistory history = capacityChecker.allocationHistory;
+ if (failure.isEmpty()) {
+ text.append("OK\n\n");
+ text.append(history);
+ if (history.oldParents().size() != hostsToRemove.size()) {
+ long emptyHostCount = hostsToRemove.size() - history.oldParents().size();
+ text.append(String.format("\nTrivially removed %d empty host%s.", emptyHostCount, emptyHostCount > 1 ? "s" : ""));
+ }
+ } else {
+ text.append("FAILURE\n\n");
+ text.append(history).append("\n");
+ text.append(failure.get().failureReason).append("\n\n");
+ }
+ }
+
+ private void zoneFailurePathToText(CapacityChecker.HostFailurePath failurePath) {
+ text.append(String.format("Found %d hosts. Failure upon trying to remove %d hosts:\n\n",
+ capacityChecker.getHosts().size(),
+ failurePath.hostsCausingFailure.size()));
+ text.append(capacityChecker.allocationHistory).append("\n");
+ text.append(failurePath.failureReason);
+ }
+
+ private void hostLossPossibleToSlime(Cursor root, Optional<CapacityChecker.HostFailurePath> failure, List<Node> hostsToRemove) {
+ var hosts = root.setArray("hostsToRemove");
+ hostsToRemove.forEach(h -> hosts.addString(h.hostname()));
+ CapacityChecker.AllocationHistory history = capacityChecker.allocationHistory;
+ root.setBool("removalPossible", failure.isEmpty());
+ var arr = root.setArray("history");
+ for (var entry : history.historyEntries) {
+ var object = arr.addObject();
+ object.setString("tenant", entry.tenant.hostname());
+ if (entry.newParent != null) {
+ object.setString("newParent", entry.newParent.hostname());
+ }
+ object.setLong("eligibleParents", entry.eligibleParents);
+ }
+ }
+
+ private void zoneFailurePathToSlime(Cursor object, CapacityChecker.HostFailurePath failurePath) {
+ object.setLong("totalHosts", capacityChecker.getHosts().size());
+ object.setLong("couldLoseHosts", failurePath.hostsCausingFailure.size());
+ failurePath.failureReason.host.ifPresent(host ->
+ object.setString("failedTenantParent", host.hostname())
+ );
+ failurePath.failureReason.tenant.ifPresent(tenant -> {
+ object.setString("failedTenant", tenant.hostname());
+ object.setString("failedTenantResources", tenant.flavor().resources().toString());
+ tenant.allocation().ifPresent(allocation ->
+ object.setString("failedTenantAllocation", allocation.toString())
+ );
+ var explanation = object.setObject("hostCandidateRejectionReasons");
+ allocationFailureReasonListToSlime(explanation.setObject("singularReasonFailures"),
+ failurePath.failureReason.allocationFailures.singularReasonFailures());
+ allocationFailureReasonListToSlime(explanation.setObject("totalFailures"),
+ failurePath.failureReason.allocationFailures);
+ });
+ var details = object.setObject("details");
+ hostLossPossibleToSlime(details, Optional.of(failurePath), failurePath.hostsCausingFailure);
+ }
+
+ private void allocationFailureReasonListToSlime(Cursor root, CapacityChecker.AllocationFailureReasonList allocationFailureReasonList) {
+ root.setLong("insufficientVcpu", allocationFailureReasonList.insufficientVcpu());
+ root.setLong("insufficientMemoryGb", allocationFailureReasonList.insufficientMemoryGb());
+ root.setLong("insufficientDiskGb", allocationFailureReasonList.insufficientDiskGb());
+ root.setLong("incompatibleDiskSpeed", allocationFailureReasonList.incompatibleDiskSpeed());
+ root.setLong("insufficientAvailableIps", allocationFailureReasonList.insufficientAvailableIps());
+ root.setLong("violatesParentHostPolicy", allocationFailureReasonList.violatesParentHostPolicy());
+ }
+
+ @Override
+ public void render(OutputStream stream) throws IOException {
+ if (json) new JsonFormat(true).encode(stream, slime);
+ else stream.write(text.toString().getBytes());
+ }
+
+ @Override
+ public String getContentType() {
+ return json ? "application/json" : "text/plain";
+ }
+}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/LoadBalancersResponse.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/LoadBalancersResponse.java
index bfbf7775031..9f8f4a804d1 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/LoadBalancersResponse.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/LoadBalancersResponse.java
@@ -77,10 +77,6 @@ public class LoadBalancersResponse extends HttpResponse {
realObject.setString("ipAddress", real.ipAddress());
realObject.setLong("port", real.port());
});
-
- // TODO(mpolden): The following fields preserves API compatibility. These can be removed once clients stop expecting them
- lbObject.setArray("rotations");
- lbObject.setBool("inactive", lb.state() == LoadBalancer.State.inactive);
});
new JsonFormat(true).encode(stream, slime);
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesApiHandler.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesApiHandler.java
index 22318f1ddb4..b2f0998189d 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesApiHandler.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesApiHandler.java
@@ -102,6 +102,7 @@ public class NodesApiHandler extends LoggingRequestHandler {
if (path.equals( "/nodes/v2/command/")) return ResourcesResponse.fromStrings(request.getUri(), "restart", "reboot");
if (path.equals( "/nodes/v2/maintenance/")) return new JobsResponse(nodeRepository.jobControl());
if (path.equals( "/nodes/v2/upgrade/")) return new UpgradeResponse(nodeRepository.infrastructureVersions(), nodeRepository.osVersions(), nodeRepository.dockerImages());
+ if (path.startsWith("/nodes/v2/capacity")) return new HostCapacityResponse(nodeRepository, request);
throw new NotFoundException("Nothing at path '" + path + "'");
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityReportMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityCheckerTest.java
index a486f8619c5..1f2112673d1 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityReportMaintainerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityCheckerTest.java
@@ -8,20 +8,19 @@ import org.junit.Test;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.*;
+
import static org.junit.Assert.fail;
import static org.junit.Assert.*;
/**
* @author mgimle
*/
-public class CapacityReportMaintainerTest {
- private CapacityReportMaintainerTester tester;
- private CapacityReportMaintainer capacityReporter;
+public class CapacityCheckerTest {
+ private CapacityCheckerTester tester;
@Before
public void setup() {
- tester = new CapacityReportMaintainerTester();
- capacityReporter = tester.makeCapacityReportMaintainer();
+ tester = new CapacityCheckerTester();
}
@Test
@@ -30,10 +29,9 @@ public class CapacityReportMaintainerTest {
tester.cleanRepository();
tester.restoreNodeRepositoryFromJsonFile(Paths.get(path));
- var failurePath = capacityReporter.worstCaseHostLossLeadingToFailure();
- if (failurePath.isPresent()) {
- assertTrue(tester.nodeRepository.getNodes(NodeType.host).containsAll(failurePath.get().hostsCausingFailure));
- } else fail();
+ var failurePath = tester.capacityChecker.worstCaseHostLossLeadingToFailure();
+ assertTrue(failurePath.isPresent());
+ assertTrue(tester.nodeRepository.getNodes(NodeType.host).containsAll(failurePath.get().hostsCausingFailure));
}
@Test
@@ -41,7 +39,7 @@ public class CapacityReportMaintainerTest {
tester.createNodes(7, 4,
10, new NodeResources(-1, 10, 100), 10,
0, new NodeResources(1, 10, 100), 10);
- int overcommittedHosts = capacityReporter.countOvercommittedHosts();
+ int overcommittedHosts = tester.capacityChecker.findOvercommittedHosts().size();
assertEquals(tester.nodeRepository.getNodes(NodeType.host).size(), overcommittedHosts);
}
@@ -50,14 +48,14 @@ public class CapacityReportMaintainerTest {
tester.createNodes(1, 1,
0, new NodeResources(1, 10, 100), 10,
0, new NodeResources(1, 10, 100), 10);
- var failurePath = capacityReporter.worstCaseHostLossLeadingToFailure();
+ var failurePath = tester.capacityChecker.worstCaseHostLossLeadingToFailure();
assertFalse("Computing worst case host loss with no hosts should return an empty optional.", failurePath.isPresent());
// Odd edge case that should never be able to occur in prod
tester.createNodes(1, 10,
10, new NodeResources(10, 1000, 10000), 100,
1, new NodeResources(10, 1000, 10000), 100);
- failurePath = capacityReporter.worstCaseHostLossLeadingToFailure();
+ failurePath = tester.capacityChecker.worstCaseHostLossLeadingToFailure();
assertTrue(failurePath.isPresent());
assertTrue("Computing worst case host loss if all hosts have to be removed should result in an non-empty failureReason with empty nodes.",
failurePath.get().failureReason.tenant.isEmpty() && failurePath.get().failureReason.host.isEmpty());
@@ -66,10 +64,10 @@ public class CapacityReportMaintainerTest {
tester.createNodes(3, 30,
10, new NodeResources(0, 0, 10000), 1000,
0, new NodeResources(0, 0, 0), 0);
- failurePath = capacityReporter.worstCaseHostLossLeadingToFailure();
+ failurePath = tester.capacityChecker.worstCaseHostLossLeadingToFailure();
assertTrue(failurePath.isPresent());
if (failurePath.get().failureReason.tenant.isPresent()) {
- var failureReasons = failurePath.get().failureReason.failureReasons;
+ var failureReasons = failurePath.get().failureReason.allocationFailures;
assertEquals("When there are multiple lacking resources, all failures are multipleReasonFailures",
failureReasons.size(), failureReasons.multipleReasonFailures().size());
assertEquals(0, failureReasons.singularReasonFailures().size());
@@ -81,10 +79,10 @@ public class CapacityReportMaintainerTest {
tester.createNodes(1, 10,
10, new NodeResources(10, 1000, 10000), 1,
10, new NodeResources(10, 1000, 10000), 1);
- var failurePath = capacityReporter.worstCaseHostLossLeadingToFailure();
+ var failurePath = tester.capacityChecker.worstCaseHostLossLeadingToFailure();
assertTrue(failurePath.isPresent());
if (failurePath.get().failureReason.tenant.isPresent()) {
- var failureReasons = failurePath.get().failureReason.failureReasons;
+ var failureReasons = failurePath.get().failureReason.allocationFailures;
assertEquals("All failures should be due to hosts having a lack of available ip addresses.",
failureReasons.singularReasonFailures().insufficientAvailableIps(), failureReasons.size());
} else fail();
@@ -96,10 +94,10 @@ public class CapacityReportMaintainerTest {
tester.createNodes(1, 10,
10, new NodeResources(1, 100, 1000), 100,
10, new NodeResources(0, 100, 1000), 100);
- var failurePath = capacityReporter.worstCaseHostLossLeadingToFailure();
+ var failurePath = tester.capacityChecker.worstCaseHostLossLeadingToFailure();
assertTrue(failurePath.isPresent());
if (failurePath.get().failureReason.tenant.isPresent()) {
- var failureReasons = failurePath.get().failureReason.failureReasons;
+ var failureReasons = failurePath.get().failureReason.allocationFailures;
assertEquals("All failures should be due to hosts lacking cpu cores.",
failureReasons.singularReasonFailures().insufficientVcpu(), failureReasons.size());
} else fail();
@@ -107,10 +105,10 @@ public class CapacityReportMaintainerTest {
tester.createNodes(1, 10,
10, new NodeResources(10, 1, 1000), 100,
10, new NodeResources(10, 0, 1000), 100);
- failurePath = capacityReporter.worstCaseHostLossLeadingToFailure();
+ failurePath = tester.capacityChecker.worstCaseHostLossLeadingToFailure();
assertTrue(failurePath.isPresent());
if (failurePath.get().failureReason.tenant.isPresent()) {
- var failureReasons = failurePath.get().failureReason.failureReasons;
+ var failureReasons = failurePath.get().failureReason.allocationFailures;
assertEquals("All failures should be due to hosts lacking memory.",
failureReasons.singularReasonFailures().insufficientMemoryGb(), failureReasons.size());
} else fail();
@@ -118,10 +116,10 @@ public class CapacityReportMaintainerTest {
tester.createNodes(1, 10,
10, new NodeResources(10, 100, 10), 100,
10, new NodeResources(10, 100, 0), 100);
- failurePath = capacityReporter.worstCaseHostLossLeadingToFailure();
+ failurePath = tester.capacityChecker.worstCaseHostLossLeadingToFailure();
assertTrue(failurePath.isPresent());
if (failurePath.get().failureReason.tenant.isPresent()) {
- var failureReasons = failurePath.get().failureReason.failureReasons;
+ var failureReasons = failurePath.get().failureReason.allocationFailures;
assertEquals("All failures should be due to hosts lacking disk space.",
failureReasons.singularReasonFailures().insufficientDiskGb(), failureReasons.size());
} else fail();
@@ -130,10 +128,10 @@ public class CapacityReportMaintainerTest {
tester.createNodes(1, 10, List.of(new NodeResources(1, 10, 100)),
10, new NodeResources(0, 0, 0), 100,
10, new NodeResources(10, 1000, 10000, NodeResources.DiskSpeed.slow), 100);
- failurePath = capacityReporter.worstCaseHostLossLeadingToFailure();
+ failurePath = tester.capacityChecker.worstCaseHostLossLeadingToFailure();
assertTrue(failurePath.isPresent());
if (failurePath.get().failureReason.tenant.isPresent()) {
- var failureReasons = failurePath.get().failureReason.failureReasons;
+ var failureReasons = failurePath.get().failureReason.allocationFailures;
assertEquals("All empty hosts should be invalid due to having incompatible disk speed.",
failureReasons.singularReasonFailures().incompatibleDiskSpeed(), emptyHostsWithSlowDisk);
} else fail();
@@ -146,10 +144,10 @@ public class CapacityReportMaintainerTest {
tester.createNodes(1, 1,
10, new NodeResources(1, 100, 1000), 100,
10, new NodeResources(10, 1000, 10000), 100);
- var failurePath = capacityReporter.worstCaseHostLossLeadingToFailure();
+ var failurePath = tester.capacityChecker.worstCaseHostLossLeadingToFailure();
assertTrue(failurePath.isPresent());
if (failurePath.get().failureReason.tenant.isPresent()) {
- var failureReasons = failurePath.get().failureReason.failureReasons;
+ var failureReasons = failurePath.get().failureReason.allocationFailures;
assertEquals("With only one type of tenant, all failures should be due to violation of the parent host policy.",
failureReasons.singularReasonFailures().violatesParentHostPolicy(), failureReasons.size());
} else fail();
@@ -157,10 +155,10 @@ public class CapacityReportMaintainerTest {
tester.createNodes(1, 2,
10, new NodeResources(10, 100, 1000), 1,
0, new NodeResources(0, 0, 0), 0);
- failurePath = capacityReporter.worstCaseHostLossLeadingToFailure();
+ failurePath = tester.capacityChecker.worstCaseHostLossLeadingToFailure();
assertTrue(failurePath.isPresent());
if (failurePath.get().failureReason.tenant.isPresent()) {
- var failureReasons = failurePath.get().failureReason.failureReasons;
+ var failureReasons = failurePath.get().failureReason.allocationFailures;
assertNotEquals("Fewer distinct children than hosts should result in some parent host policy violations.",
failureReasons.size(), failureReasons.singularReasonFailures().violatesParentHostPolicy());
assertNotEquals(0, failureReasons.singularReasonFailures().violatesParentHostPolicy());
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityReportMaintainerTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityCheckerTester.java
index ccea4691f10..f5fd0e0526d 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityReportMaintainerTester.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityCheckerTester.java
@@ -20,7 +20,6 @@ import com.yahoo.vespa.hosted.provision.testutils.MockNameResolver;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.time.Duration;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;
@@ -29,22 +28,23 @@ import java.util.stream.IntStream;
/**
* @author mgimle
*/
-public class CapacityReportMaintainerTester {
+public class CapacityCheckerTester {
public static final Zone zone = new Zone(Environment.prod, RegionName.from("us-east"));
// Components with state
public final ManualClock clock = new ManualClock();
public final NodeRepository nodeRepository;
+ public CapacityChecker capacityChecker;
- CapacityReportMaintainerTester() {
+ CapacityCheckerTester() {
Curator curator = new MockCurator();
NodeFlavors f = new NodeFlavors(new FlavorConfigBuilder().build());
nodeRepository = new NodeRepository(f, curator, clock, zone, new MockNameResolver().mockAnyLookup(),
DockerImage.fromString("docker-registry.domain.tld:8080/dist/vespa"), true);
}
- CapacityReportMaintainer makeCapacityReportMaintainer() {
- return new CapacityReportMaintainer(nodeRepository, new MetricsReporterTest.TestMetric(), Duration.ofDays(1));
+ private void updateCapacityChecker() {
+ this.capacityChecker = new CapacityChecker(this.nodeRepository);
}
List<NodeModel> createDistinctChildren(int amount, List<NodeResources> childResources) {
@@ -167,9 +167,9 @@ public class CapacityReportMaintainerTester {
nodes.addAll(createEmptyHosts(numHosts, numEmptyHosts, emptyHostExcessCapacity, emptyHostExcessIps));
nodeRepository.addNodes(nodes);
+ updateCapacityChecker();
}
-
NodeResources containingNodeResources(List<NodeResources> resources, NodeResources excessCapacity) {
NodeResources usedByChildren = resources.stream()
.reduce(new NodeResources(0, 0, 0), NodeResources::add);
@@ -278,6 +278,7 @@ public class CapacityReportMaintainerTester {
}
nodeRepository.addNodes(nodes);
+ updateCapacityChecker();
}
void cleanRepository() {
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java
index bfb24d30284..8452e8f93bb 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java
@@ -21,6 +21,7 @@ import java.io.IOException;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -819,6 +820,28 @@ public class RestApiTest {
"{\"message\":\"Cancelled outstanding requests for firmware checks\"}");
}
+ @Test
+ public void test_capacity() throws Exception {
+ assertFile(new Request("http://localhost:8080/nodes/v2/capacity/?json=true"), "capacity-zone.json");
+ assertFile(new Request("http://localhost:8080/nodes/v2/capacity?json=true"), "capacity-zone.json");
+
+ List<String> hostsToRemove = List.of(
+ "dockerhost1.yahoo.com",
+ "dockerhost2.yahoo.com",
+ "dockerhost3.yahoo.com",
+ "dockerhost4.yahoo.com"
+ );
+ String requestUriTemplate = "http://localhost:8080/nodes/v2/capacity/?json=true&hosts=%s";
+
+ assertFile(new Request(String.format(requestUriTemplate,
+ String.join(",", hostsToRemove.subList(0, 3)))),
+ "capacity-hostremoval-possible.json");
+ assertFile(new Request(String.format(requestUriTemplate,
+ String.join(",", hostsToRemove))),
+ "capacity-hostremoval-impossible.json");
+ }
+
+
/** Tests the rendering of each node separately to make it easier to find errors */
@Test
public void test_single_node_rendering() throws Exception {
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/capacity-hostremoval-impossible.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/capacity-hostremoval-impossible.json
new file mode 100644
index 00000000000..f3c73e61c91
--- /dev/null
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/capacity-hostremoval-impossible.json
@@ -0,0 +1,20 @@
+{
+ "hostsToRemove": [
+ "dockerhost1.yahoo.com",
+ "dockerhost2.yahoo.com",
+ "dockerhost3.yahoo.com",
+ "dockerhost4.yahoo.com"
+ ],
+ "removalPossible": false,
+ "history": [
+ {
+ "tenant": "host4.yahoo.com",
+ "newParent": "dockerhost5.yahoo.com",
+ "eligibleParents": 1
+ },
+ {
+ "tenant": "test-node-pool-101-2",
+ "eligibleParents": 0
+ }
+ ]
+} \ No newline at end of file
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/capacity-hostremoval-possible.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/capacity-hostremoval-possible.json
new file mode 100644
index 00000000000..b896fd9d63a
--- /dev/null
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/capacity-hostremoval-possible.json
@@ -0,0 +1,20 @@
+{
+ "hostsToRemove": [
+ "dockerhost1.yahoo.com",
+ "dockerhost2.yahoo.com",
+ "dockerhost3.yahoo.com"
+ ],
+ "removalPossible": true,
+ "history": [
+ {
+ "tenant": "host4.yahoo.com",
+ "newParent": "dockerhost4.yahoo.com",
+ "eligibleParents": 2
+ },
+ {
+ "tenant": "test-node-pool-101-2",
+ "newParent": "dockerhost5.yahoo.com",
+ "eligibleParents": 1
+ }
+ ]
+} \ No newline at end of file
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/capacity-zone.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/capacity-zone.json
new file mode 100644
index 00000000000..9895948e69d
--- /dev/null
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/capacity-zone.json
@@ -0,0 +1,46 @@
+{
+ "totalHosts": 5,
+ "couldLoseHosts": 4,
+ "failedTenantParent": "dockerhost1.yahoo.com",
+ "failedTenant": "host4.yahoo.com",
+ "failedTenantResources": "[vcpu: 1.0, memory: 1.0 Gb, disk 100.0 Gb]",
+ "failedTenantAllocation": "allocated to tenant3.application3.instance3 as 'content/id3/0/0'",
+ "hostCandidateRejectionReasons": {
+ "singularReasonFailures": {
+ "insufficientVcpu": 0,
+ "insufficientMemoryGb": 0,
+ "insufficientDiskGb": 0,
+ "incompatibleDiskSpeed": 0,
+ "insufficientAvailableIps": 0,
+ "violatesParentHostPolicy": 1
+ },
+ "totalFailures": {
+ "insufficientVcpu": 0,
+ "insufficientMemoryGb": 0,
+ "insufficientDiskGb": 0,
+ "incompatibleDiskSpeed": 0,
+ "insufficientAvailableIps": 0,
+ "violatesParentHostPolicy": 1
+ }
+ },
+ "details": {
+ "hostsToRemove": [
+ "dockerhost2.yahoo.com",
+ "dockerhost1.yahoo.com",
+ "dockerhost4.yahoo.com",
+ "dockerhost3.yahoo.com"
+ ],
+ "removalPossible": false,
+ "history": [
+ {
+ "tenant": "test-node-pool-101-2",
+ "newParent": "dockerhost5.yahoo.com",
+ "eligibleParents": 1
+ },
+ {
+ "tenant": "host4.yahoo.com",
+ "eligibleParents": 0
+ }
+ ]
+ }
+} \ No newline at end of file
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/load-balancers-single.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/load-balancers-single.json
index 67d2c3bfa4b..19e65c2fc25 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/load-balancers-single.json
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/load-balancers-single.json
@@ -28,9 +28,7 @@
"ipAddress": "127.0.14.1",
"port": 4080
}
- ],
- "rotations": [],
- "inactive": false
+ ]
}
]
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/load-balancers.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/load-balancers.json
index c9a45a9c3da..0b05a41af0a 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/load-balancers.json
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/load-balancers.json
@@ -28,9 +28,7 @@
"ipAddress": "127.0.10.1",
"port": 4080
}
- ],
- "rotations": [],
- "inactive": false
+ ]
},
{
"id": "tenant4:application4:instance4:id4",
@@ -60,9 +58,7 @@
"ipAddress": "127.0.14.1",
"port": 4080
}
- ],
- "rotations": [],
- "inactive": false
+ ]
}
]
}
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/RetryingClusterControllerClientFactory.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/RetryingClusterControllerClientFactory.java
index 33e74235862..4b82f278f23 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/RetryingClusterControllerClientFactory.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/RetryingClusterControllerClientFactory.java
@@ -2,11 +2,11 @@
package com.yahoo.vespa.orchestrator.controller;
import com.google.inject.Inject;
+import com.yahoo.component.AbstractComponent;
import com.yahoo.vespa.applicationmodel.HostName;
-import com.yahoo.vespa.jaxrs.client.JaxRsClientFactory;
import com.yahoo.vespa.jaxrs.client.JaxRsStrategy;
import com.yahoo.vespa.jaxrs.client.JaxRsStrategyFactory;
-import com.yahoo.vespa.jaxrs.client.JerseyJaxRsClientFactory;
+import com.yahoo.vespa.jaxrs.client.VespaJerseyJaxRsClientFactory;
import java.util.HashSet;
import java.util.List;
@@ -14,21 +14,21 @@ import java.util.List;
/**
* @author bakksjo
*/
-public class RetryingClusterControllerClientFactory implements ClusterControllerClientFactory {
+public class RetryingClusterControllerClientFactory extends AbstractComponent implements ClusterControllerClientFactory {
// TODO: Figure this port out dynamically.
public static final int HARDCODED_CLUSTERCONTROLLER_PORT = 19050;
public static final String CLUSTERCONTROLLER_API_PATH = "/";
public static final String CLUSTERCONTROLLER_SCHEME = "http";
- private JaxRsClientFactory jaxRsClientFactory;
+ private final VespaJerseyJaxRsClientFactory jaxRsClientFactory;
@Inject
public RetryingClusterControllerClientFactory() {
- this(new JerseyJaxRsClientFactory());
+ this(new VespaJerseyJaxRsClientFactory("orchestrator-cluster-controller-client"));
}
- public RetryingClusterControllerClientFactory(JaxRsClientFactory jaxRsClientFactory) {
+ RetryingClusterControllerClientFactory(VespaJerseyJaxRsClientFactory jaxRsClientFactory) {
this.jaxRsClientFactory = jaxRsClientFactory;
}
@@ -50,4 +50,9 @@ public class RetryingClusterControllerClientFactory implements ClusterController
.setMaxIterations(clusterControllers.size() > 1 ? 1 : 2);
return new ClusterControllerClientImpl(jaxRsApi, clusterName);
}
+
+ @Override
+ public void deconstruct() {
+ jaxRsClientFactory.close();
+ }
}
diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/RetryingClusterControllerClientFactoryTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/RetryingClusterControllerClientFactoryTest.java
index f47b43fa27b..8d5110ac0b6 100644
--- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/RetryingClusterControllerClientFactoryTest.java
+++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/RetryingClusterControllerClientFactoryTest.java
@@ -2,7 +2,7 @@ package com.yahoo.vespa.orchestrator.controller;
import com.yahoo.test.ManualClock;
import com.yahoo.vespa.applicationmodel.HostName;
-import com.yahoo.vespa.jaxrs.client.JaxRsClientFactory;
+import com.yahoo.vespa.jaxrs.client.VespaJerseyJaxRsClientFactory;
import com.yahoo.vespa.orchestrator.OrchestratorContext;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -27,7 +27,7 @@ public class RetryingClusterControllerClientFactoryTest {
@Test
public void verifyJerseyCallForSetNodeState() throws IOException {
- JaxRsClientFactory clientFactory = mock(JaxRsClientFactory.class);
+ VespaJerseyJaxRsClientFactory clientFactory = mock(VespaJerseyJaxRsClientFactory.class);
ClusterControllerJaxRsApi api = mock(ClusterControllerJaxRsApi.class);
when(clientFactory.createClient(any())).thenReturn(api);
RetryingClusterControllerClientFactory factory = new RetryingClusterControllerClientFactory(clientFactory);
diff --git a/persistence/src/vespa/persistence/conformancetest/conformancetest.cpp b/persistence/src/vespa/persistence/conformancetest/conformancetest.cpp
index f77d7705a66..3ae13d6b21c 100644
--- a/persistence/src/vespa/persistence/conformancetest/conformancetest.cpp
+++ b/persistence/src/vespa/persistence/conformancetest/conformancetest.cpp
@@ -157,7 +157,7 @@ doIterate(PersistenceProvider& spi,
Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
IterateResult result(spi.iterate(id, maxByteSize, context));
- EXPECT_EQ(Result::NONE, result.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, result.getErrorCode());
chunks.push_back(Chunk{result.steal_entries()});
if (result.isCompleted()
@@ -214,13 +214,13 @@ iterateBucket(PersistenceProvider& spi,
versions,
context);
- EXPECT_EQ(Result::NONE, iter.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, iter.getErrorCode());
while (true) {
IterateResult result =
spi.iterate(iter.getIteratorId(),
std::numeric_limits<int64_t>().max(), context);
- if (result.getErrorCode() != Result::NONE) {
+ if (result.getErrorCode() != Result::ErrorType::NONE) {
return std::vector<DocEntry::UP>();
}
auto list = result.steal_entries();
@@ -639,7 +639,7 @@ TEST_F(ConformanceTest, testPutNewDocumentVersion)
GetResult gr = spi->get(bucket, document::AllFields(), doc1->getId(),
context);
- EXPECT_EQ(Result::NONE, gr.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, gr.getErrorCode());
EXPECT_EQ(Timestamp(4), gr.getTimestamp());
if (!((*doc2)==gr.getDocument())) {
@@ -691,7 +691,7 @@ TEST_F(ConformanceTest, testPutOlderDocumentVersion)
GetResult gr = spi->get(bucket, document::AllFields(), doc1->getId(),
context);
- EXPECT_EQ(Result::NONE, gr.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, gr.getErrorCode());
EXPECT_EQ(Timestamp(5), gr.getTimestamp());
EXPECT_EQ(*doc1, gr.getDocument());
}
@@ -822,7 +822,7 @@ TEST_F(ConformanceTest, testRemove)
doc1->getId(),
context);
- EXPECT_EQ(Result::NONE, getResult.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, getResult.getErrorCode());
EXPECT_EQ(Timestamp(0), getResult.getTimestamp());
EXPECT_TRUE(!getResult.hasDocument());
}
@@ -848,7 +848,7 @@ TEST_F(ConformanceTest, testRemoveMerge)
removeId,
context);
spi->flush(bucket, context);
- EXPECT_EQ(Result::NONE, removeResult.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, removeResult.getErrorCode());
EXPECT_EQ(false, removeResult.wasFound());
}
{
@@ -876,7 +876,7 @@ TEST_F(ConformanceTest, testRemoveMerge)
removeId,
context);
spi->flush(bucket, context);
- EXPECT_EQ(Result::NONE, removeResult.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, removeResult.getErrorCode());
EXPECT_EQ(false, removeResult.wasFound());
}
// Old entry may or may not be present, depending on the provider.
@@ -904,7 +904,7 @@ TEST_F(ConformanceTest, testRemoveMerge)
removeId,
context);
spi->flush(bucket, context);
- EXPECT_EQ(Result::NONE, removeResult.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, removeResult.getErrorCode());
EXPECT_EQ(false, removeResult.wasFound());
}
{
@@ -957,7 +957,7 @@ TEST_F(ConformanceTest, testUpdate)
context);
spi->flush(bucket, context);
- EXPECT_EQ(Result::NONE, result.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, result.getErrorCode());
EXPECT_EQ(Timestamp(3), result.getExistingTimestamp());
}
@@ -967,7 +967,7 @@ TEST_F(ConformanceTest, testUpdate)
doc1->getId(),
context);
- EXPECT_EQ(Result::NONE, result.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, result.getErrorCode());
EXPECT_EQ(Timestamp(4), result.getTimestamp());
EXPECT_EQ(document::IntFieldValue(42),
static_cast<document::IntFieldValue&>(
@@ -983,7 +983,7 @@ TEST_F(ConformanceTest, testUpdate)
doc1->getId(),
context);
- EXPECT_EQ(Result::NONE, result.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, result.getErrorCode());
EXPECT_EQ(Timestamp(0), result.getTimestamp());
EXPECT_TRUE(!result.hasDocument());
}
@@ -993,13 +993,13 @@ TEST_F(ConformanceTest, testUpdate)
context);
spi->flush(bucket, context);
- EXPECT_EQ(Result::NONE, result.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, result.getErrorCode());
EXPECT_EQ(Timestamp(0), result.getExistingTimestamp());
}
{
GetResult result = spi->get(bucket, document::AllFields(), doc1->getId(), context);
- EXPECT_EQ(Result::NONE, result.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, result.getErrorCode());
EXPECT_EQ(Timestamp(0), result.getTimestamp());
EXPECT_TRUE(!result.hasDocument());
}
@@ -1010,13 +1010,13 @@ TEST_F(ConformanceTest, testUpdate)
// but since CreateIfNonExistent is set it should be auto-created anyway.
UpdateResult result = spi->update(bucket, Timestamp(7), update, context);
spi->flush(bucket, context);
- EXPECT_EQ(Result::NONE, result.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, result.getErrorCode());
EXPECT_EQ(Timestamp(7), result.getExistingTimestamp());
}
{
GetResult result = spi->get(bucket, document::AllFields(), doc1->getId(), context);
- EXPECT_EQ(Result::NONE, result.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, result.getErrorCode());
EXPECT_EQ(Timestamp(7), result.getTimestamp());
EXPECT_EQ(document::IntFieldValue(42),
reinterpret_cast<document::IntFieldValue&>(
@@ -1039,7 +1039,7 @@ TEST_F(ConformanceTest, testGet)
GetResult result = spi->get(bucket, document::AllFields(),
doc1->getId(), context);
- EXPECT_EQ(Result::NONE, result.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, result.getErrorCode());
EXPECT_EQ(Timestamp(0), result.getTimestamp());
}
@@ -1060,7 +1060,7 @@ TEST_F(ConformanceTest, testGet)
GetResult result = spi->get(bucket, document::AllFields(),
doc1->getId(), context);
- EXPECT_EQ(Result::NONE, result.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, result.getErrorCode());
EXPECT_EQ(Timestamp(0), result.getTimestamp());
}
}
@@ -1077,7 +1077,7 @@ TEST_F(ConformanceTest, testIterateCreateIterator)
spi::CreateIteratorResult result(
createIterator(*spi, b, createSelection("")));
- EXPECT_EQ(Result::NONE, result.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, result.getErrorCode());
// Iterator ID 0 means invalid iterator, so cannot be returned
// from a successful createIterator call.
EXPECT_TRUE(result.getIteratorId() != IteratorId(0));
@@ -1096,7 +1096,7 @@ TEST_F(ConformanceTest, testIterateWithUnknownId)
IteratorId unknownId(123);
IterateResult result(spi->iterate(unknownId, 1024, context));
- EXPECT_EQ(Result::PERMANENT_ERROR, result.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::PERMANENT_ERROR, result.getErrorCode());
}
TEST_F(ConformanceTest, testIterateDestroyIterator)
@@ -1111,7 +1111,7 @@ TEST_F(ConformanceTest, testIterateDestroyIterator)
CreateIteratorResult iter(createIterator(*spi, b, createSelection("")));
{
IterateResult result(spi->iterate(iter.getIteratorId(), 1024, context));
- EXPECT_EQ(Result::NONE, result.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, result.getErrorCode());
}
{
@@ -1122,7 +1122,7 @@ TEST_F(ConformanceTest, testIterateDestroyIterator)
// Iteration should now fail
{
IterateResult result(spi->iterate(iter.getIteratorId(), 1024, context));
- EXPECT_EQ(Result::PERMANENT_ERROR, result.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::PERMANENT_ERROR, result.getErrorCode());
}
{
Result destroyResult(
@@ -1422,7 +1422,7 @@ TEST_F(ConformanceTest, testIterationRequiringDocumentIdOnlyMatching)
CreateIteratorResult iter(
createIterator(*spi, b, sel, NEWEST_DOCUMENT_OR_REMOVE));
- EXPECT_TRUE(iter.getErrorCode() == Result::NONE);
+ EXPECT_TRUE(iter.getErrorCode() == Result::ErrorType::NONE);
std::vector<Chunk> chunks = doIterate(*spi, iter.getIteratorId(), 4096);
std::vector<DocAndTimestamp> docs;
@@ -1444,14 +1444,14 @@ TEST_F(ConformanceTest, testIterateBadDocumentSelection)
{
CreateIteratorResult iter(
createIterator(*spi, b, createSelection("the muppet show")));
- if (iter.getErrorCode() == Result::NONE) {
+ if (iter.getErrorCode() == Result::ErrorType::NONE) {
IterateResult result(
spi->iterate(iter.getIteratorId(), 4096, context));
- EXPECT_EQ(Result::NONE, result.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, result.getErrorCode());
EXPECT_EQ(size_t(0), result.getEntries().size());
EXPECT_EQ(true, result.isCompleted());
} else {
- EXPECT_EQ(Result::PERMANENT_ERROR, iter.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::PERMANENT_ERROR, iter.getErrorCode());
EXPECT_EQ(IteratorId(0), iter.getIteratorId());
}
}
@@ -1461,14 +1461,14 @@ TEST_F(ConformanceTest, testIterateBadDocumentSelection)
b,
createSelection(
"unknownddoctype.something=thatthing")));
- if (iter.getErrorCode() == Result::NONE) {
+ if (iter.getErrorCode() == Result::ErrorType::NONE) {
IterateResult result(spi->iterate(
iter.getIteratorId(), 4096, context));
- EXPECT_EQ(Result::NONE, result.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, result.getErrorCode());
EXPECT_EQ(size_t(0), result.getEntries().size());
EXPECT_EQ(true, result.isCompleted());
} else {
- EXPECT_EQ(Result::PERMANENT_ERROR, iter.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::PERMANENT_ERROR, iter.getErrorCode());
EXPECT_EQ(IteratorId(0), iter.getIteratorId());
}
}
@@ -1491,7 +1491,7 @@ TEST_F(ConformanceTest, testIterateAlreadyCompleted)
verifyDocs(docs, chunks);
IterateResult result(spi->iterate(iter.getIteratorId(), 4096, context));
- EXPECT_EQ(Result::NONE, result.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, result.getErrorCode());
EXPECT_EQ(size_t(0), result.getEntries().size());
EXPECT_TRUE(result.isCompleted());
@@ -1511,7 +1511,7 @@ TEST_F(ConformanceTest, testIterateEmptyBucket)
CreateIteratorResult iter(createIterator(*spi, b, sel));
IterateResult result(spi->iterate(iter.getIteratorId(), 4096, context));
- EXPECT_EQ(Result::NONE, result.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, result.getErrorCode());
EXPECT_EQ(size_t(0), result.getEntries().size());
EXPECT_TRUE(result.isCompleted());
@@ -1556,7 +1556,7 @@ testDeleteBucketPostCondition(const PersistenceProvider::UP &spi,
doc1.getId(),
context);
- EXPECT_EQ(Result::NONE, result.getErrorCode());
+ EXPECT_EQ(Result::ErrorType::NONE, result.getErrorCode());
EXPECT_EQ(Timestamp(0), result.getTimestamp());
}
}
@@ -2152,7 +2152,7 @@ TEST_F(ConformanceTest, testMaintain)
spi->put(bucket, Timestamp(3), doc1, context);
spi->flush(bucket, context);
- EXPECT_EQ(Result::NONE,
+ EXPECT_EQ(Result::ErrorType::NONE,
spi->maintain(bucket, LOW).getErrorCode());
}
diff --git a/persistence/src/vespa/persistence/dummyimpl/dummypersistence.cpp b/persistence/src/vespa/persistence/dummyimpl/dummypersistence.cpp
index 65759a0c783..6834f453695 100644
--- a/persistence/src/vespa/persistence/dummyimpl/dummypersistence.cpp
+++ b/persistence/src/vespa/persistence/dummyimpl/dummypersistence.cpp
@@ -409,7 +409,7 @@ DummyPersistence::setActiveState(const Bucket& b,
BucketContentGuard::UP bc(acquireBucketWithLock(b));
if (!bc.get()) {
- return BucketInfoResult(Result::TRANSIENT_ERROR, "Bucket not found");
+ return BucketInfoResult(Result::ErrorType::TRANSIENT_ERROR, "Bucket not found");
}
(*bc)->setActive(newState == BucketInfo::ACTIVE);
return Result();
@@ -424,7 +424,7 @@ DummyPersistence::getBucketInfo(const Bucket& b) const
if (!bc.get()) {
LOG(debug, "getBucketInfo(%s) : (bucket not found)",
b.toString().c_str());
- return BucketInfoResult(Result::TRANSIENT_ERROR, "Bucket not found");
+ return BucketInfoResult(Result::ErrorType::TRANSIENT_ERROR, "Bucket not found");
}
BucketInfo info((*bc)->getBucketInfo());
@@ -446,7 +446,7 @@ DummyPersistence::put(const Bucket& b, Timestamp t, const Document::SP& doc,
assert(b.getBucketSpace() == FixedBucketSpaces::default_space());
BucketContentGuard::UP bc(acquireBucketWithLock(b));
if (!bc.get()) {
- return BucketInfoResult(Result::TRANSIENT_ERROR, "Bucket not found");
+ return BucketInfoResult(Result::ErrorType::TRANSIENT_ERROR, "Bucket not found");
}
DocEntry::SP existing = (*bc)->getEntry(t);
@@ -454,7 +454,7 @@ DummyPersistence::put(const Bucket& b, Timestamp t, const Document::SP& doc,
if (doc->getId() == *existing->getDocumentId()) {
return Result();
} else {
- return Result(Result::TIMESTAMP_EXISTS,
+ return Result(Result::ErrorType::TIMESTAMP_EXISTS,
"Timestamp already existed");
}
}
@@ -474,7 +474,7 @@ DummyPersistence::maintain(const Bucket& b,
if (_simulateMaintainFailure) {
BucketContentGuard::UP bc(acquireBucketWithLock(b));
if (!bc.get()) {
- return BucketInfoResult(Result::TRANSIENT_ERROR, "Bucket not found");
+ return BucketInfoResult(Result::ErrorType::TRANSIENT_ERROR, "Bucket not found");
}
if (!(*bc)->_entries.empty()) {
@@ -503,7 +503,7 @@ DummyPersistence::remove(const Bucket& b,
BucketContentGuard::UP bc(acquireBucketWithLock(b));
if (!bc.get()) {
- return RemoveResult(Result::TRANSIENT_ERROR, "Bucket not found");
+ return RemoveResult(Result::ErrorType::TRANSIENT_ERROR, "Bucket not found");
}
DocEntry::SP entry((*bc)->getEntry(did));
@@ -564,13 +564,13 @@ DummyPersistence::createIterator(
true).release());
if (!docSelection.get()) {
return CreateIteratorResult(
- Result::PERMANENT_ERROR,
+ Result::ErrorType::PERMANENT_ERROR,
"Got invalid/unparseable document selection string");
}
}
BucketContentGuard::UP bc(acquireBucketWithLock(b, LockMode::Shared));
if (!bc.get()) {
- return CreateIteratorResult(Result::TRANSIENT_ERROR, "Bucket not found");
+ return CreateIteratorResult(Result::ErrorType::TRANSIENT_ERROR, "Bucket not found");
}
Iterator* it;
@@ -650,7 +650,7 @@ DummyPersistence::iterate(IteratorId id, uint64_t maxByteSize, Context& ctx) con
vespalib::MonitorGuard lock(_monitor);
std::map<IteratorId, Iterator::UP>::iterator iter(_iterators.find(id));
if (iter == _iterators.end()) {
- return IterateResult(Result::PERMANENT_ERROR,
+ return IterateResult(Result::ErrorType::PERMANENT_ERROR,
"Bug! Used iterate without sending createIterator first");
}
it = iter->second.get();
@@ -659,7 +659,7 @@ DummyPersistence::iterate(IteratorId id, uint64_t maxByteSize, Context& ctx) con
BucketContentGuard::UP bc(acquireBucketWithLock(it->_bucket, LockMode::Shared));
if (!bc.get()) {
ctx.trace(9, "finished iterate(); bucket not found");
- return IterateResult(Result::TRANSIENT_ERROR, "Bucket not found");
+ return IterateResult(Result::ErrorType::TRANSIENT_ERROR, "Bucket not found");
}
LOG(debug, "Iterator %" PRIu64 " acquired bucket lock", uint64_t(id));
@@ -776,7 +776,7 @@ DummyPersistence::split(const Bucket& source,
BucketContentGuard::UP sourceGuard(acquireBucketWithLock(source));
if (!sourceGuard.get()) {
LOG(debug, "%s not found", source.toString().c_str());
- return Result(Result::TRANSIENT_ERROR, "Bucket not found");
+ return Result(Result::ErrorType::TRANSIENT_ERROR, "Bucket not found");
}
BucketContentGuard::UP target1Guard(acquireBucketWithLock(target1));
BucketContentGuard::UP target2Guard(acquireBucketWithLock(target2));
@@ -863,7 +863,7 @@ DummyPersistence::revert(const Bucket& b, Timestamp t, Context&)
BucketContentGuard::UP bc(acquireBucketWithLock(b));
if (!bc.get()) {
- return BucketInfoResult(Result::TRANSIENT_ERROR, "Bucket not found");
+ return BucketInfoResult(Result::ErrorType::TRANSIENT_ERROR, "Bucket not found");
}
BucketContent& content(**bc);
diff --git a/persistence/src/vespa/persistence/spi/result.cpp b/persistence/src/vespa/persistence/spi/result.cpp
index 4aa01d22649..024f5595102 100644
--- a/persistence/src/vespa/persistence/spi/result.cpp
+++ b/persistence/src/vespa/persistence/spi/result.cpp
@@ -3,6 +3,7 @@
#include "result.h"
#include <vespa/document/fieldvalue/document.h>
#include <vespa/vespalib/stllike/asciistream.h>
+#include <ostream>
namespace storage::spi {
@@ -13,7 +14,7 @@ Result::~Result() { }
vespalib::string
Result::toString() const {
vespalib::asciistream os;
- os << "Result(" << _errorCode << ", " << _errorMessage << ")";
+ os << "Result(" << static_cast<int>(_errorCode) << ", " << _errorMessage << ")";
return os.str();
}
@@ -22,6 +23,10 @@ operator << (std::ostream & os, const Result & r) {
return os << r.toString();
}
+std::ostream & operator << (std::ostream & os, const Result::ErrorType &errorCode) {
+ return os << static_cast<int>(errorCode);
+}
+
GetResult::GetResult(Document::UP doc, Timestamp timestamp)
: Result(),
_timestamp(timestamp),
diff --git a/persistence/src/vespa/persistence/spi/result.h b/persistence/src/vespa/persistence/spi/result.h
index 0daa784caa4..fe8e74706bb 100644
--- a/persistence/src/vespa/persistence/spi/result.h
+++ b/persistence/src/vespa/persistence/spi/result.h
@@ -13,7 +13,7 @@ class Result {
public:
typedef std::unique_ptr<Result> UP;
- enum ErrorType {
+ enum class ErrorType {
NONE,
TRANSIENT_ERROR,
PERMANENT_ERROR,
@@ -26,7 +26,7 @@ public:
/**
* Constructor to use for a result where there is no error.
*/
- Result() : _errorCode(NONE), _errorMessage() {}
+ Result() : _errorCode(ErrorType::NONE), _errorMessage() {}
/**
* Constructor to use when an error has been detected.
@@ -46,7 +46,7 @@ public:
}
bool hasError() const {
- return _errorCode != NONE;
+ return _errorCode != ErrorType::NONE;
}
ErrorType getErrorCode() const {
@@ -66,6 +66,8 @@ private:
std::ostream & operator << (std::ostream & os, const Result & r);
+std::ostream & operator << (std::ostream & os, const Result::ErrorType &errorCode);
+
class BucketInfoResult : public Result {
public:
/**
diff --git a/searchcommon/src/vespa/searchcommon/common/schemaconfigurer.cpp b/searchcommon/src/vespa/searchcommon/common/schemaconfigurer.cpp
index a46f99d158d..3750f8d1d62 100644
--- a/searchcommon/src/vespa/searchcommon/common/schemaconfigurer.cpp
+++ b/searchcommon/src/vespa/searchcommon/common/schemaconfigurer.cpp
@@ -28,9 +28,9 @@ Schema::DataType
convertIndexDataType(const IndexschemaConfig::Indexfield::Datatype &type)
{
switch (type) {
- case IndexschemaConfig::Indexfield::STRING:
+ case IndexschemaConfig::Indexfield::Datatype::STRING:
return DataType::STRING;
- case IndexschemaConfig::Indexfield::INT64:
+ case IndexschemaConfig::Indexfield::Datatype::INT64:
return DataType::INT64;
}
return DataType::STRING;
@@ -41,11 +41,11 @@ Schema::CollectionType
convertIndexCollectionType(const IndexschemaConfig::Indexfield::Collectiontype &type)
{
switch (type) {
- case IndexschemaConfig::Indexfield::SINGLE:
+ case IndexschemaConfig::Indexfield::Collectiontype::SINGLE:
return CollectionType::SINGLE;
- case IndexschemaConfig::Indexfield::ARRAY:
+ case IndexschemaConfig::Indexfield::Collectiontype::ARRAY:
return CollectionType::ARRAY;
- case IndexschemaConfig::Indexfield::WEIGHTEDSET:
+ case IndexschemaConfig::Indexfield::Collectiontype::WEIGHTEDSET:
return CollectionType::WEIGHTEDSET;
}
return CollectionType::SINGLE;
diff --git a/searchcore/src/apps/proton/downpersistence.cpp b/searchcore/src/apps/proton/downpersistence.cpp
index 33ad4bc5024..4b911eb6d2b 100644
--- a/searchcore/src/apps/proton/downpersistence.cpp
+++ b/searchcore/src/apps/proton/downpersistence.cpp
@@ -10,7 +10,7 @@ namespace storage::spi {
namespace {
-Result errorResult(Result::FATAL_ERROR, "Node is down");
+Result errorResult(Result::ErrorType::FATAL_ERROR, "Node is down");
}
diff --git a/searchcore/src/tests/proton/documentdb/documentdbconfigscout/documentdbconfigscout_test.cpp b/searchcore/src/tests/proton/documentdb/documentdbconfigscout/documentdbconfigscout_test.cpp
index ce07858ea0b..f76743b9480 100644
--- a/searchcore/src/tests/proton/documentdb/documentdbconfigscout/documentdbconfigscout_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/documentdbconfigscout/documentdbconfigscout_test.cpp
@@ -216,9 +216,9 @@ setupLiveAttributes(AttributesConfigBuilder::AttributeVector &attributes)
attributes.push_back(setupFastSearchAttribute("a0"));
attributes.push_back(setupFastSearchAndMoreAttribute("a1"));
attributes.push_back(setupFastSearchAttribute("a2"));
- attributes.back().datatype = AttributesConfig::Attribute::INT8;
+ attributes.back().datatype = AttributesConfig::Attribute::Datatype::INT8;
attributes.push_back(setupFastSearchAttribute("a3"));
- attributes.back().collectiontype = AttributesConfig::Attribute::ARRAY;
+ attributes.back().collectiontype = AttributesConfig::Attribute::Collectiontype::ARRAY;
attributes.push_back(setupFastSearchAttribute("a4"));
attributes.back().createifnonexistent = true;
}
diff --git a/searchcore/src/tests/proton/documentdb/feedhandler/feedhandler_test.cpp b/searchcore/src/tests/proton/documentdb/feedhandler/feedhandler_test.cpp
index 39fc93d5725..4b3b68a85ea 100644
--- a/searchcore/src/tests/proton/documentdb/feedhandler/feedhandler_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/feedhandler/feedhandler_test.cpp
@@ -684,7 +684,7 @@ TEST_F("require that put is rejected if resource limit is reached", FeedHandlerF
FeedTokenContext token;
f.handler.performOperation(std::move(token.token), std::move(op));
EXPECT_EQUAL(0, f.feedView.put_count);
- EXPECT_EQUAL(Result::RESOURCE_EXHAUSTED, token.getResult()->getErrorCode());
+ EXPECT_EQUAL(Result::ErrorType::RESOURCE_EXHAUSTED, token.getResult()->getErrorCode());
EXPECT_EQUAL("Put operation rejected for document 'id:test:searchdocument::foo' of type 'searchdocument': 'Attribute resource limit reached'",
token.getResult()->getErrorMessage());
}
@@ -700,7 +700,7 @@ TEST_F("require that update is rejected if resource limit is reached", FeedHandl
f.handler.performOperation(std::move(token.token), std::move(op));
EXPECT_EQUAL(0, f.feedView.update_count);
EXPECT_TRUE(dynamic_cast<const UpdateResult *>(token.getResult()));
- EXPECT_EQUAL(Result::RESOURCE_EXHAUSTED, token.getResult()->getErrorCode());
+ EXPECT_EQUAL(Result::ErrorType::RESOURCE_EXHAUSTED, token.getResult()->getErrorCode());
EXPECT_EQUAL("Update operation rejected for document 'id:test:searchdocument::foo' of type 'searchdocument': 'Attribute resource limit reached'",
token.getResult()->getErrorMessage());
}
@@ -715,7 +715,7 @@ TEST_F("require that remove is NOT rejected if resource limit is reached", FeedH
FeedTokenContext token;
f.handler.performOperation(std::move(token.token), std::move(op));
EXPECT_EQUAL(1, f.feedView.remove_count);
- EXPECT_EQUAL(Result::NONE, token.getResult()->getErrorCode());
+ EXPECT_EQUAL(Result::ErrorType::NONE, token.getResult()->getErrorCode());
EXPECT_EQUAL("", token.getResult()->getErrorMessage());
}
@@ -738,7 +738,7 @@ checkUpdate(FeedHandlerFixture &f, SchemaContext &schemaContext,
EXPECT_TRUE(dynamic_cast<const UpdateResult *>(token.getResult()));
if (expectReject) {
TEST_DO(f.feedView.checkCounts(0, 0u, 0, 0u));
- EXPECT_EQUAL(Result::TRANSIENT_ERROR, token.getResult()->getErrorCode());
+ EXPECT_EQUAL(Result::ErrorType::TRANSIENT_ERROR, token.getResult()->getErrorCode());
if (fieldName == "tensor2") {
EXPECT_EQUAL("Update operation rejected for document 'id:test:searchdocument::foo' of type 'searchdocument': 'Wrong tensor type: Field tensor type is 'tensor(x{},y{})' but other tensor type is 'tensor(x{})''",
token.getResult()->getErrorMessage());
@@ -752,7 +752,7 @@ checkUpdate(FeedHandlerFixture &f, SchemaContext &schemaContext,
} else {
TEST_DO(f.feedView.checkCounts(0, 0u, 1, 16u));
}
- EXPECT_EQUAL(Result::NONE, token.getResult()->getErrorCode());
+ EXPECT_EQUAL(Result::ErrorType::NONE, token.getResult()->getErrorCode());
EXPECT_EQUAL("", token.getResult()->getErrorMessage());
}
}
diff --git a/searchcore/src/tests/proton/persistenceengine/persistenceengine_test.cpp b/searchcore/src/tests/proton/persistenceengine/persistenceengine_test.cpp
index 5484b8eacf0..cdc0e8656d8 100644
--- a/searchcore/src/tests/proton/persistenceengine/persistenceengine_test.cpp
+++ b/searchcore/src/tests/proton/persistenceengine/persistenceengine_test.cpp
@@ -469,7 +469,7 @@ TEST_F("require that puts are routed to handler", SimpleFixture)
assertHandler(bucket1, tstamp1, docId1, f.hset.handler1);
assertHandler(bucket1, tstamp1, docId2, f.hset.handler2);
- EXPECT_EQUAL(Result(Result::PERMANENT_ERROR, "No handler for document type 'type3'"),
+ EXPECT_EQUAL(Result(Result::ErrorType::PERMANENT_ERROR, "No handler for document type 'type3'"),
f.engine.put(bucket1, tstamp1, doc3, context));
}
@@ -477,7 +477,7 @@ TEST_F("require that puts are routed to handler", SimpleFixture)
TEST_F("require that puts with old id scheme are rejected", SimpleFixture) {
storage::spi::LoadType loadType(0, "default");
Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
- EXPECT_EQUAL(Result(Result::PERMANENT_ERROR, "Old id scheme not supported in elastic mode (doc:old:id-scheme)"),
+ EXPECT_EQUAL(Result(Result::ErrorType::PERMANENT_ERROR, "Old id scheme not supported in elastic mode (doc:old:id-scheme)"),
f.engine.put(bucket1, tstamp1, old_doc, context));
}
@@ -490,7 +490,7 @@ TEST_F("require that put is rejected if resource limit is reached", SimpleFixtur
storage::spi::LoadType loadType(0, "default");
Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
EXPECT_EQUAL(
- Result(Result::RESOURCE_EXHAUSTED,
+ Result(Result::ErrorType::RESOURCE_EXHAUSTED,
"Put operation rejected for document 'doc:old:id-scheme': 'Disk is full'"),
f.engine.put(bucket1, tstamp1, old_doc, context));
}
@@ -512,7 +512,7 @@ TEST_F("require that updates are routed to handler", SimpleFixture)
assertHandler(bucket1, tstamp1, docId2, f.hset.handler2);
EXPECT_EQUAL(tstamp3, ur.getExistingTimestamp());
- EXPECT_EQUAL(Result(Result::PERMANENT_ERROR, "No handler for document type 'type3'"),
+ EXPECT_EQUAL(Result(Result::ErrorType::PERMANENT_ERROR, "No handler for document type 'type3'"),
f.engine.update(bucket1, tstamp1, upd3, context));
}
@@ -522,7 +522,7 @@ TEST_F("require that updates with old id scheme are rejected", SimpleFixture)
storage::spi::LoadType loadType(0, "default");
Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
- EXPECT_EQUAL(UpdateResult(Result::PERMANENT_ERROR, "Old id scheme not supported in elastic mode (doc:old:id-scheme)"),
+ EXPECT_EQUAL(UpdateResult(Result::ErrorType::PERMANENT_ERROR, "Old id scheme not supported in elastic mode (doc:old:id-scheme)"),
f.engine.update(bucket1, tstamp1, old_upd, context));
}
@@ -531,7 +531,7 @@ TEST_F("require that updates with bad ids are rejected", SimpleFixture)
storage::spi::LoadType loadType(0, "default");
Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
- EXPECT_EQUAL(UpdateResult(Result::PERMANENT_ERROR, "Update operation rejected due to bad id (id:type2:type2::1, type1)"),
+ EXPECT_EQUAL(UpdateResult(Result::ErrorType::PERMANENT_ERROR, "Update operation rejected due to bad id (id:type2:type2::1, type1)"),
f.engine.update(bucket1, tstamp1, bad_id_upd, context));
}
@@ -544,7 +544,7 @@ TEST_F("require that update is rejected if resource limit is reached", SimpleFix
Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
EXPECT_EQUAL(
- Result(Result::RESOURCE_EXHAUSTED,
+ Result(Result::ErrorType::RESOURCE_EXHAUSTED,
"Update operation rejected for document 'id:type1:type1::1': 'Disk is full'"),
f.engine.update(bucket1, tstamp1, upd1, context));
}
@@ -559,7 +559,7 @@ TEST_F("require that removes are routed to handlers", SimpleFixture)
assertHandler(bucket0, tstamp0, docId0, f.hset.handler2);
EXPECT_FALSE(rr.wasFound());
EXPECT_TRUE(rr.hasError());
- EXPECT_EQUAL(Result(Result::PERMANENT_ERROR, "No handler for document type 'type3'"), rr);
+ EXPECT_EQUAL(Result(Result::ErrorType::PERMANENT_ERROR, "No handler for document type 'type3'"), rr);
f.hset.handler1.setExistingTimestamp(tstamp2);
rr = f.engine.remove(bucket1, tstamp1, docId1, context);
@@ -590,7 +590,7 @@ TEST_F("require that removes with old id scheme are rejected", SimpleFixture)
storage::spi::LoadType loadType(0, "default");
Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
- EXPECT_EQUAL(RemoveResult(Result::PERMANENT_ERROR, "Old id scheme not supported in elastic mode (doc:old:id-scheme)"),
+ EXPECT_EQUAL(RemoveResult(Result::ErrorType::PERMANENT_ERROR, "Old id scheme not supported in elastic mode (doc:old:id-scheme)"),
f.engine.remove(bucket1, tstamp1, old_docId, context));
}
@@ -626,11 +626,11 @@ TEST_F("require that setClusterState() is routed to handlers", SimpleFixture)
TEST_F("require that setActiveState() is routed to handlers and merged", SimpleFixture)
{
- f.hset.handler1.bucketStateResult = Result(Result::TRANSIENT_ERROR, "err1");
- f.hset.handler2.bucketStateResult = Result(Result::PERMANENT_ERROR, "err2");
+ f.hset.handler1.bucketStateResult = Result(Result::ErrorType::TRANSIENT_ERROR, "err1");
+ f.hset.handler2.bucketStateResult = Result(Result::ErrorType::PERMANENT_ERROR, "err2");
Result result = f.engine.setActiveState(bucket1, storage::spi::BucketInfo::NOT_ACTIVE);
- EXPECT_EQUAL(Result::PERMANENT_ERROR, result.getErrorCode());
+ EXPECT_EQUAL(Result::ErrorType::PERMANENT_ERROR, result.getErrorCode());
EXPECT_EQUAL("err1, err2", result.getErrorMessage());
EXPECT_EQUAL(storage::spi::BucketInfo::NOT_ACTIVE, f.hset.handler1.lastBucketState);
EXPECT_EQUAL(storage::spi::BucketInfo::NOT_ACTIVE, f.hset.handler2.lastBucketState);
@@ -655,11 +655,11 @@ TEST_F("require that createBucket() is routed to handlers and merged", SimpleFix
{
storage::spi::LoadType loadType(0, "default");
Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
- f.hset.handler1._createBucketResult = Result(Result::TRANSIENT_ERROR, "err1a");
- f.hset.handler2._createBucketResult = Result(Result::PERMANENT_ERROR, "err2a");
+ f.hset.handler1._createBucketResult = Result(Result::ErrorType::TRANSIENT_ERROR, "err1a");
+ f.hset.handler2._createBucketResult = Result(Result::ErrorType::PERMANENT_ERROR, "err2a");
Result result = f.engine.createBucket(bucket1, context);
- EXPECT_EQUAL(Result::PERMANENT_ERROR, result.getErrorCode());
+ EXPECT_EQUAL(Result::ErrorType::PERMANENT_ERROR, result.getErrorCode());
EXPECT_EQUAL("err1a, err2a", result.getErrorMessage());
}
@@ -668,11 +668,11 @@ TEST_F("require that deleteBucket() is routed to handlers and merged", SimpleFix
{
storage::spi::LoadType loadType(0, "default");
Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
- f.hset.handler1.deleteBucketResult = Result(Result::TRANSIENT_ERROR, "err1");
- f.hset.handler2.deleteBucketResult = Result(Result::PERMANENT_ERROR, "err2");
+ f.hset.handler1.deleteBucketResult = Result(Result::ErrorType::TRANSIENT_ERROR, "err1");
+ f.hset.handler2.deleteBucketResult = Result(Result::ErrorType::PERMANENT_ERROR, "err2");
Result result = f.engine.deleteBucket(bucket1, context);
- EXPECT_EQUAL(Result::PERMANENT_ERROR, result.getErrorCode());
+ EXPECT_EQUAL(Result::ErrorType::PERMANENT_ERROR, result.getErrorCode());
EXPECT_EQUAL("err1, err2", result.getErrorMessage());
}
@@ -754,7 +754,7 @@ TEST_F("require that iterate requires valid iterator", SimpleFixture) {
Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
IterateResult it_result = f.engine.iterate(IteratorId(1), max_size, context);
EXPECT_TRUE(it_result.hasError());
- EXPECT_EQUAL(Result::PERMANENT_ERROR, it_result.getErrorCode());
+ EXPECT_EQUAL(Result::ErrorType::PERMANENT_ERROR, it_result.getErrorCode());
EXPECT_EQUAL("Unknown iterator with id 1", it_result.getErrorMessage());
CreateIteratorResult result =
@@ -799,7 +799,7 @@ TEST_F("require that destroyIterator prevents iteration", SimpleFixture) {
uint64_t max_size = 1024;
IterateResult it_result = f.engine.iterate(create_result.getIteratorId(), max_size, context);
EXPECT_TRUE(it_result.hasError());
- EXPECT_EQUAL(Result::PERMANENT_ERROR, it_result.getErrorCode());
+ EXPECT_EQUAL(Result::ErrorType::PERMANENT_ERROR, it_result.getErrorCode());
string msg_prefix = "Unknown iterator with id";
EXPECT_EQUAL(msg_prefix, it_result.getErrorMessage().substr(0, msg_prefix.size()));
}
diff --git a/searchcore/src/tests/proton/server/disk_mem_usage_filter/disk_mem_usage_filter_test.cpp b/searchcore/src/tests/proton/server/disk_mem_usage_filter/disk_mem_usage_filter_test.cpp
index fb0c9b1f3a9..5c8ed28cd66 100644
--- a/searchcore/src/tests/proton/server/disk_mem_usage_filter/disk_mem_usage_filter_test.cpp
+++ b/searchcore/src/tests/proton/server/disk_mem_usage_filter/disk_mem_usage_filter_test.cpp
@@ -7,7 +7,7 @@
using namespace proton;
-namespace fs = std::experimental::filesystem;
+namespace fs = std::filesystem;
struct Fixture
{
diff --git a/searchcore/src/vespa/searchcore/fdispatch/program/fdispatch.cpp b/searchcore/src/vespa/searchcore/fdispatch/program/fdispatch.cpp
index b85e706397d..3047834be85 100644
--- a/searchcore/src/vespa/searchcore/fdispatch/program/fdispatch.cpp
+++ b/searchcore/src/vespa/searchcore/fdispatch/program/fdispatch.cpp
@@ -271,7 +271,7 @@ CompressionConfig::Type
convert(InternalFdispatchrcType::Packetcompresstype type)
{
switch (type) {
- case InternalFdispatchrcType::LZ4: return CompressionConfig::LZ4;
+ case InternalFdispatchrcType::Packetcompresstype::LZ4: return CompressionConfig::LZ4;
default: return CompressionConfig::LZ4;
}
}
diff --git a/searchcore/src/vespa/searchcore/fdispatch/search/configdesc.h b/searchcore/src/vespa/searchcore/fdispatch/search/configdesc.h
index 32f85e904ae..e0b0f0d7403 100644
--- a/searchcore/src/vespa/searchcore/fdispatch/search/configdesc.h
+++ b/searchcore/src/vespa/searchcore/fdispatch/search/configdesc.h
@@ -69,9 +69,9 @@ public:
class QueryDistributionMode {
public:
enum Mode {
- RANDOM = PartitionsConfig::Dataset::RANDOM,
- AUTOMATIC = PartitionsConfig::Dataset::AUTOMATIC,
- FIXEDROW = PartitionsConfig::Dataset::FIXEDROW
+ RANDOM = static_cast<int>(PartitionsConfig::Dataset::Querydistribution::RANDOM),
+ AUTOMATIC = static_cast<int>(PartitionsConfig::Dataset::Querydistribution::AUTOMATIC),
+ FIXEDROW = static_cast<int>(PartitionsConfig::Dataset::Querydistribution::FIXEDROW)
};
QueryDistributionMode(Mode mode, double minGroupCoverage, double latencyDecayRate) :
diff --git a/searchcore/src/vespa/searchcore/proton/common/CMakeLists.txt b/searchcore/src/vespa/searchcore/proton/common/CMakeLists.txt
index 84d1cfb471a..4db36039b3c 100644
--- a/searchcore/src/vespa/searchcore/proton/common/CMakeLists.txt
+++ b/searchcore/src/vespa/searchcore/proton/common/CMakeLists.txt
@@ -22,5 +22,5 @@ vespa_add_library(searchcore_pcommon STATIC
DEPENDS
searchcore_proton_metrics
searchcore_fconfig
- stdc++fs
+ ${VESPA_STDCXX_FS_LIB}
)
diff --git a/searchcore/src/vespa/searchcore/proton/common/hw_info_sampler.cpp b/searchcore/src/vespa/searchcore/proton/common/hw_info_sampler.cpp
index 1492a34b241..642d5a40ac7 100644
--- a/searchcore/src/vespa/searchcore/proton/common/hw_info_sampler.cpp
+++ b/searchcore/src/vespa/searchcore/proton/common/hw_info_sampler.cpp
@@ -8,7 +8,7 @@
#include <vespa/fastos/file.h>
#include <vespa/searchcore/config/config-hwinfo.h>
#include <vespa/vespalib/io/fileutil.h>
-#include <experimental/filesystem>
+#include <filesystem>
#include <thread>
#include <vespa/log/log.h>
LOG_SETUP(".proton.common.hw_info_sampler");
@@ -32,8 +32,8 @@ sampleDiskSizeBytes(const std::string &pathStr, const HwInfoSampler::Config &cfg
if (cfg.diskSizeBytes != 0) {
return cfg.diskSizeBytes;
}
- std::experimental::filesystem::path path(pathStr);
- auto space_info = std::experimental::filesystem::space(path);
+ std::filesystem::path path(pathStr);
+ auto space_info = std::filesystem::space(path);
return space_info.capacity;
}
diff --git a/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp b/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp
index 57e26f77cca..1f862b07048 100644
--- a/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp
+++ b/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp
@@ -325,7 +325,7 @@ PersistenceEngine::put(const Bucket& b, Timestamp t, const document::Document::S
if (!_writeFilter.acceptWriteOperation()) {
IResourceWriteFilter::State state = _writeFilter.getAcceptState();
if (!state.acceptWriteOperation()) {
- return Result(Result::RESOURCE_EXHAUSTED,
+ return Result(Result::ErrorType::RESOURCE_EXHAUSTED,
make_string("Put operation rejected for document '%s': '%s'",
doc->getId().toString().c_str(), state.message().c_str()));
}
@@ -335,12 +335,12 @@ PersistenceEngine::put(const Bucket& b, Timestamp t, const document::Document::S
LOG(spam, "put(%s, %" PRIu64 ", (\"%s\", \"%s\"))", b.toString().c_str(), static_cast<uint64_t>(t.getValue()),
docType.toString().c_str(), doc->getId().toString().c_str());
if (!doc->getId().hasDocType()) {
- return Result(Result::PERMANENT_ERROR,
+ return Result(Result::ErrorType::PERMANENT_ERROR,
make_string("Old id scheme not supported in elastic mode (%s)", doc->getId().toString().c_str()));
}
IPersistenceHandler::SP handler = getHandler(b.getBucketSpace(), docType);
if (!handler) {
- return Result(Result::PERMANENT_ERROR,
+ return Result(Result::ErrorType::PERMANENT_ERROR,
make_string("No handler for document type '%s'", docType.toString().c_str()));
}
TransportLatch latch(1);
@@ -356,13 +356,13 @@ PersistenceEngine::remove(const Bucket& b, Timestamp t, const DocumentId& did, C
LOG(spam, "remove(%s, %" PRIu64 ", \"%s\")", b.toString().c_str(),
static_cast<uint64_t>(t.getValue()), did.toString().c_str());
if (!did.hasDocType()) {
- return RemoveResult(Result::PERMANENT_ERROR,
+ return RemoveResult(Result::ErrorType::PERMANENT_ERROR,
make_string("Old id scheme not supported in elastic mode (%s)", did.toString().c_str()));
}
DocTypeName docType(did.getDocType());
IPersistenceHandler::SP handler = getHandler(b.getBucketSpace(), docType);
if (!handler) {
- return RemoveResult(Result::PERMANENT_ERROR,
+ return RemoveResult(Result::ErrorType::PERMANENT_ERROR,
make_string("No handler for document type '%s'", docType.toString().c_str()));
}
TransportLatch latch(1);
@@ -378,7 +378,7 @@ PersistenceEngine::update(const Bucket& b, Timestamp t, const DocumentUpdate::SP
if (!_writeFilter.acceptWriteOperation()) {
IResourceWriteFilter::State state = _writeFilter.getAcceptState();
if (!state.acceptWriteOperation()) {
- return UpdateResult(Result::RESOURCE_EXHAUSTED,
+ return UpdateResult(Result::ErrorType::RESOURCE_EXHAUSTED,
make_string("Update operation rejected for document '%s': '%s'",
upd->getId().toString().c_str(), state.message().c_str()));
}
@@ -386,16 +386,16 @@ PersistenceEngine::update(const Bucket& b, Timestamp t, const DocumentUpdate::SP
try {
upd->eagerDeserialize();
} catch (document::FieldNotFoundException & e) {
- return UpdateResult(Result::TRANSIENT_ERROR,
+ return UpdateResult(Result::ErrorType::TRANSIENT_ERROR,
make_string("Update operation rejected for document '%s' of type '%s': 'Field not found'",
upd->getId().toString().c_str(), upd->getType().getName().c_str()));
} catch (document::DocumentTypeNotFoundException & e) {
- return UpdateResult(Result::TRANSIENT_ERROR,
+ return UpdateResult(Result::ErrorType::TRANSIENT_ERROR,
make_string("Update operation rejected for document '%s' of type '%s'.",
upd->getId().toString().c_str(), e.getDocumentTypeName().c_str()));
} catch (document::WrongTensorTypeException &e) {
- return UpdateResult(Result::TRANSIENT_ERROR,
+ return UpdateResult(Result::ErrorType::TRANSIENT_ERROR,
make_string("Update operation rejected for document '%s' of type '%s': 'Wrong tensor type: %s'",
upd->getId().toString().c_str(),
upd->getType().getName().c_str(),
@@ -407,11 +407,11 @@ PersistenceEngine::update(const Bucket& b, Timestamp t, const DocumentUpdate::SP
b.toString().c_str(), static_cast<uint64_t>(t.getValue()), docType.toString().c_str(),
upd->getId().toString().c_str(), (upd->getCreateIfNonExistent() ? "true" : "false"));
if (!upd->getId().hasDocType()) {
- return UpdateResult(Result::PERMANENT_ERROR,
+ return UpdateResult(Result::ErrorType::PERMANENT_ERROR,
make_string("Old id scheme not supported in elastic mode (%s)", upd->getId().toString().c_str()));
}
if (upd->getId().getDocType() != docType.getName()) {
- return UpdateResult(Result::PERMANENT_ERROR,
+ return UpdateResult(Result::ErrorType::PERMANENT_ERROR,
make_string("Update operation rejected due to bad id (%s, %s)", upd->getId().toString().c_str(), docType.getName().c_str()));
}
IPersistenceHandler::SP handler = getHandler(b.getBucketSpace(), docType);
@@ -423,7 +423,7 @@ PersistenceEngine::update(const Bucket& b, Timestamp t, const DocumentUpdate::SP
latch.await();
return latch.getUpdateResult();
} else {
- return UpdateResult(Result::PERMANENT_ERROR, make_string("No handler for document type '%s'", docType.toString().c_str()));
+ return UpdateResult(Result::ErrorType::PERMANENT_ERROR, make_string("No handler for document type '%s'", docType.toString().c_str()));
}
}
@@ -493,11 +493,11 @@ PersistenceEngine::iterate(IteratorId id, uint64_t maxByteSize, Context&) const
std::lock_guard<std::mutex> guard(_iterators_lock);
auto it = _iterators.find(id);
if (it == _iterators.end()) {
- return IterateResult(Result::PERMANENT_ERROR, make_string("Unknown iterator with id %" PRIu64, id.getValue()));
+ return IterateResult(Result::ErrorType::PERMANENT_ERROR, make_string("Unknown iterator with id %" PRIu64, id.getValue()));
}
iteratorEntry = it->second;
if (iteratorEntry->in_use) {
- return IterateResult(Result::TRANSIENT_ERROR, make_string("Iterator with id %" PRIu64 " is already in use", id.getValue()));
+ return IterateResult(Result::ErrorType::TRANSIENT_ERROR, make_string("Iterator with id %" PRIu64 " is already in use", id.getValue()));
}
iteratorEntry->in_use = true;
}
@@ -509,7 +509,7 @@ PersistenceEngine::iterate(IteratorId id, uint64_t maxByteSize, Context&) const
iteratorEntry->in_use = false;
return result;
} catch (const std::exception & e) {
- IterateResult result(Result::PERMANENT_ERROR, make_string("Caught exception during visitor iterator.iterate() = '%s'", e.what()));
+ IterateResult result(Result::ErrorType::PERMANENT_ERROR, make_string("Caught exception during visitor iterator.iterate() = '%s'", e.what()));
LOG(warning, "Caught exception during visitor iterator.iterate() = '%s'", e.what());
std::lock_guard<std::mutex> guard(_iterators_lock);
iteratorEntry->in_use = false;
@@ -528,7 +528,7 @@ PersistenceEngine::destroyIterator(IteratorId id, Context&)
return Result();
}
if (it->second->in_use) {
- return Result(Result::TRANSIENT_ERROR, make_string("Iterator with id %" PRIu64 " is currently in use", id.getValue()));
+ return Result(Result::ErrorType::TRANSIENT_ERROR, make_string("Iterator with id %" PRIu64 " is currently in use", id.getValue()));
}
delete it->second;
_iterators.erase(it);
diff --git a/searchcore/src/vespa/searchcore/proton/server/CMakeLists.txt b/searchcore/src/vespa/searchcore/proton/server/CMakeLists.txt
index d47e87e9e03..92cf186f697 100644
--- a/searchcore/src/vespa/searchcore/proton/server/CMakeLists.txt
+++ b/searchcore/src/vespa/searchcore/proton/server/CMakeLists.txt
@@ -121,5 +121,5 @@ vespa_add_library(searchcore_server STATIC
searchcore_fconfig
searchcore_reference
configdefinitions
- stdc++fs
+ ${VESPA_STDCXX_FS_LIB}
)
diff --git a/searchcore/src/vespa/searchcore/proton/server/buckethandler.cpp b/searchcore/src/vespa/searchcore/proton/server/buckethandler.cpp
index af9038cba66..1d3b2165c80 100644
--- a/searchcore/src/vespa/searchcore/proton/server/buckethandler.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/buckethandler.cpp
@@ -26,7 +26,7 @@ BucketHandler::performSetCurrentState(BucketId bucketId,
IGenericResultHandler *resultHandler)
{
if (!_nodeUp) {
- Result result(Result::TRANSIENT_ERROR,
+ Result result(Result::ErrorType::TRANSIENT_ERROR,
"Cannot set bucket active state when node is down");
resultHandler->handle(result);
return;
diff --git a/searchcore/src/vespa/searchcore/proton/server/disk_mem_usage_filter.h b/searchcore/src/vespa/searchcore/proton/server/disk_mem_usage_filter.h
index 982e3bba6fd..b9047d47793 100644
--- a/searchcore/src/vespa/searchcore/proton/server/disk_mem_usage_filter.h
+++ b/searchcore/src/vespa/searchcore/proton/server/disk_mem_usage_filter.h
@@ -8,7 +8,7 @@
#include <vespa/searchcore/proton/persistenceengine/i_resource_write_filter.h>
#include <vespa/vespalib/util/process_memory_stats.h>
#include <atomic>
-#include <experimental/filesystem>
+#include <filesystem>
#include <mutex>
namespace proton {
@@ -21,7 +21,7 @@ namespace proton {
class DiskMemUsageFilter : public IResourceWriteFilter,
public IDiskMemUsageNotifier {
public:
- using space_info = std::experimental::filesystem::space_info;
+ using space_info = std::filesystem::space_info;
using Mutex = std::mutex;
using Guard = std::lock_guard<Mutex>;
diff --git a/searchcore/src/vespa/searchcore/proton/server/disk_mem_usage_sampler.cpp b/searchcore/src/vespa/searchcore/proton/server/disk_mem_usage_sampler.cpp
index 7ef8c53d4dd..37f1664841a 100644
--- a/searchcore/src/vespa/searchcore/proton/server/disk_mem_usage_sampler.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/disk_mem_usage_sampler.cpp
@@ -3,7 +3,7 @@
#include "disk_mem_usage_sampler.h"
#include <vespa/vespalib/util/timer.h>
#include <vespa/vespalib/util/lambdatask.h>
-#include <experimental/filesystem>
+#include <filesystem>
#include <unistd.h>
using vespalib::makeLambdaTask;
@@ -47,7 +47,7 @@ DiskMemUsageSampler::sampleUsage()
namespace {
-namespace fs = std::experimental::filesystem;
+namespace fs = std::filesystem;
uint64_t
sampleDiskUsageOnFileSystem(const fs::path &path, const HwInfo::Disk &disk)
diff --git a/searchcore/src/vespa/searchcore/proton/server/disk_mem_usage_sampler.h b/searchcore/src/vespa/searchcore/proton/server/disk_mem_usage_sampler.h
index 4ed48613f6a..5a439e69003 100644
--- a/searchcore/src/vespa/searchcore/proton/server/disk_mem_usage_sampler.h
+++ b/searchcore/src/vespa/searchcore/proton/server/disk_mem_usage_sampler.h
@@ -13,7 +13,7 @@ namespace proton {
*/
class DiskMemUsageSampler {
DiskMemUsageFilter _filter;
- std::experimental::filesystem::path _path;
+ std::filesystem::path _path;
double _sampleInterval;
std::unique_ptr<vespalib::Timer> _periodicTimer;
diff --git a/searchcore/src/vespa/searchcore/proton/server/documentdbconfigmanager.cpp b/searchcore/src/vespa/searchcore/proton/server/documentdbconfigmanager.cpp
index 53f7f544980..a562408b64d 100644
--- a/searchcore/src/vespa/searchcore/proton/server/documentdbconfigmanager.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/documentdbconfigmanager.cpp
@@ -152,9 +152,9 @@ template<typename T>
CompressionConfig
deriveCompression(const T & config) {
CompressionConfig compression;
- if (config.type == T::LZ4) {
+ if (config.type == T::Type::LZ4) {
compression.type = CompressionConfig::LZ4;
- } else if (config.type == T::ZSTD) {
+ } else if (config.type == T::Type::ZSTD) {
compression.type = CompressionConfig::ZSTD;
}
compression.compressionLevel = config.level;
diff --git a/searchcore/src/vespa/searchcore/proton/server/feedhandler.cpp b/searchcore/src/vespa/searchcore/proton/server/feedhandler.cpp
index fd38b74f584..338fc738040 100644
--- a/searchcore/src/vespa/searchcore/proton/server/feedhandler.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/feedhandler.cpp
@@ -476,7 +476,7 @@ void feedOperationRejected(FeedToken & token, const vespalib::string &opType, co
if (token) {
auto message = make_string("%s operation rejected for document '%s' of type '%s': '%s'",
opType.c_str(), docId.c_str(), docTypeName.toString().c_str(), rejectMessage.c_str());
- token->setResult(make_unique<ResultType>(Result::RESOURCE_EXHAUSTED, message), false);
+ token->setResult(make_unique<ResultType>(Result::ErrorType::RESOURCE_EXHAUSTED, message), false);
token->fail();
}
}
@@ -527,7 +527,7 @@ FeedHandler::considerUpdateOperationForRejection(FeedToken &token, UpdateOperati
if (token) {
auto message = make_string("Update operation rejected for document '%s' of type '%s': 'Field not found'",
update.getId().toString().c_str(), _docTypeName.toString().c_str());
- token->setResult(make_unique<UpdateResult>(Result::TRANSIENT_ERROR, message), false);
+ token->setResult(make_unique<UpdateResult>(Result::ErrorType::TRANSIENT_ERROR, message), false);
token->fail();
}
return true;
@@ -536,7 +536,7 @@ FeedHandler::considerUpdateOperationForRejection(FeedToken &token, UpdateOperati
update.getId().toString().c_str(),
e.getDocumentTypeName().c_str(),
_docTypeName.toString().c_str());
- token->setResult(make_unique<UpdateResult>(Result::TRANSIENT_ERROR, message), false);
+ token->setResult(make_unique<UpdateResult>(Result::ErrorType::TRANSIENT_ERROR, message), false);
token->fail();
return true;
} catch (document::WrongTensorTypeException &e) {
@@ -544,7 +544,7 @@ FeedHandler::considerUpdateOperationForRejection(FeedToken &token, UpdateOperati
update.getId().toString().c_str(),
_docTypeName.toString().c_str(),
e.getMessage().c_str());
- token->setResult(make_unique<UpdateResult>(Result::TRANSIENT_ERROR, message), false);
+ token->setResult(make_unique<UpdateResult>(Result::ErrorType::TRANSIENT_ERROR, message), false);
token->fail();
return true;
}
diff --git a/searchcore/src/vespa/searchcore/proton/server/proton.cpp b/searchcore/src/vespa/searchcore/proton/server/proton.cpp
index 06f19eb06cc..5db499601f3 100644
--- a/searchcore/src/vespa/searchcore/proton/server/proton.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/proton.cpp
@@ -65,7 +65,7 @@ CompressionConfig::Type
convert(InternalProtonType::Packetcompresstype type)
{
switch (type) {
- case InternalProtonType::LZ4: return CompressionConfig::LZ4;
+ case InternalProtonType::Packetcompresstype::LZ4: return CompressionConfig::LZ4;
default: return CompressionConfig::LZ4;
}
}
@@ -74,10 +74,10 @@ void
setBucketCheckSumType(const ProtonConfig & proton)
{
switch (proton.bucketdb.checksumtype) {
- case InternalProtonType::Bucketdb::LEGACY:
+ case InternalProtonType::Bucketdb::Checksumtype::LEGACY:
bucketdb::BucketState::setChecksumType(bucketdb::BucketState::ChecksumType::LEGACY);
break;
- case InternalProtonType::Bucketdb::XXHASH64:
+ case InternalProtonType::Bucketdb::Checksumtype::XXHASH64:
bucketdb::BucketState::setChecksumType(bucketdb::BucketState::ChecksumType::XXHASH64);
break;
}
@@ -273,7 +273,7 @@ Proton::init(const BootstrapConfig::SP & configSnapshot)
IFlushStrategy::SP strategy;
const ProtonConfig::Flush & flush(protonConfig.flush);
switch (flush.strategy) {
- case ProtonConfig::Flush::MEMORY: {
+ case ProtonConfig::Flush::Strategy::MEMORY: {
auto memoryFlush = std::make_shared<MemoryFlush>(
MemoryFlushConfigUpdater::convertConfig(flush.memory, hwInfo.memory()), fastos::ClockSystem::now());
_memoryFlushConfigUpdater = std::make_unique<MemoryFlushConfigUpdater>(memoryFlush, flush.memory, hwInfo.memory());
@@ -281,7 +281,7 @@ Proton::init(const BootstrapConfig::SP & configSnapshot)
strategy = memoryFlush;
break;
}
- case ProtonConfig::Flush::SIMPLE:
+ case ProtonConfig::Flush::Strategy::SIMPLE:
default:
strategy = std::make_shared<SimpleFlush>();
break;
diff --git a/searchcore/src/vespa/searchcore/proton/server/searchview.h b/searchcore/src/vespa/searchcore/proton/server/searchview.h
index 186d6154706..28c7ffd2b36 100644
--- a/searchcore/src/vespa/searchcore/proton/server/searchview.h
+++ b/searchcore/src/vespa/searchcore/proton/server/searchview.h
@@ -17,8 +17,10 @@ public:
typedef std::shared_ptr<SearchView> SP;
SearchView(const ISummaryManager::ISummarySetup::SP &summarySetup, const MatchView::SP &matchView);
- SearchView(SearchView &&) = default;
- SearchView &operator=(SearchView &&) = default;
+ SearchView(const SearchView &) = delete;
+ SearchView(SearchView &&) = delete;
+ SearchView &operator=(const SearchView &) = delete;
+ SearchView &operator=(SearchView &&) = delete;
~SearchView();
const ISummaryManager::ISummarySetup::SP & getSummarySetup() const { return _summarySetup; }
diff --git a/searchlib/src/tests/attribute/attributemanager/attributemanager_test.cpp b/searchlib/src/tests/attribute/attributemanager/attributemanager_test.cpp
index 09f89494019..62cde4d2c9c 100644
--- a/searchlib/src/tests/attribute/attributemanager/attributemanager_test.cpp
+++ b/searchlib/src/tests/attribute/attributemanager/attributemanager_test.cpp
@@ -222,27 +222,29 @@ AttributeManagerTest::testConfigConvert()
// typedef AttributeVector::Config AVC;
typedef BT AVBT;
typedef CollectionType AVCT;
- typedef AttributesConfig::Attribute CACA;
+ using CACA = AttributesConfig::Attribute;
+ using CACAD = CACA::Datatype;
+ using CACAC = CACA::Collectiontype;
typedef ConfigConverter CC;
- EXPECT_TRUE(assertDataType(AVBT::STRING, CACA::STRING));
- EXPECT_TRUE(assertDataType(AVBT::INT8, CACA::INT8));
- EXPECT_TRUE(assertDataType(AVBT::INT16, CACA::INT16));
- EXPECT_TRUE(assertDataType(AVBT::INT32, CACA::INT32));
- EXPECT_TRUE(assertDataType(AVBT::INT64, CACA::INT64));
- EXPECT_TRUE(assertDataType(AVBT::FLOAT, CACA::FLOAT));
- EXPECT_TRUE(assertDataType(AVBT::DOUBLE, CACA::DOUBLE));
- EXPECT_TRUE(assertDataType(AVBT::PREDICATE, CACA::PREDICATE));
- EXPECT_TRUE(assertDataType(AVBT::TENSOR, CACA::TENSOR));
- EXPECT_TRUE(assertDataType(AVBT::NONE, CACA::NONE));
-
- EXPECT_TRUE(assertCollectionType(AVCT::SINGLE, CACA::SINGLE));
- EXPECT_TRUE(assertCollectionType(AVCT::ARRAY, CACA::ARRAY));
- EXPECT_TRUE(assertCollectionType(AVCT::WSET, CACA::WEIGHTEDSET));
+ EXPECT_TRUE(assertDataType(AVBT::STRING, CACAD::STRING));
+ EXPECT_TRUE(assertDataType(AVBT::INT8, CACAD::INT8));
+ EXPECT_TRUE(assertDataType(AVBT::INT16, CACAD::INT16));
+ EXPECT_TRUE(assertDataType(AVBT::INT32, CACAD::INT32));
+ EXPECT_TRUE(assertDataType(AVBT::INT64, CACAD::INT64));
+ EXPECT_TRUE(assertDataType(AVBT::FLOAT, CACAD::FLOAT));
+ EXPECT_TRUE(assertDataType(AVBT::DOUBLE, CACAD::DOUBLE));
+ EXPECT_TRUE(assertDataType(AVBT::PREDICATE, CACAD::PREDICATE));
+ EXPECT_TRUE(assertDataType(AVBT::TENSOR, CACAD::TENSOR));
+ EXPECT_TRUE(assertDataType(AVBT::NONE, CACAD::NONE));
+
+ EXPECT_TRUE(assertCollectionType(AVCT::SINGLE, CACAC::SINGLE));
+ EXPECT_TRUE(assertCollectionType(AVCT::ARRAY, CACAC::ARRAY));
+ EXPECT_TRUE(assertCollectionType(AVCT::WSET, CACAC::WEIGHTEDSET));
EXPECT_TRUE(assertCollectionType(AVCT(AVCT::SINGLE, true, false),
- CACA::SINGLE, true, false));
+ CACAC::SINGLE, true, false));
EXPECT_TRUE(assertCollectionType(AVCT(AVCT::SINGLE, false, true),
- CACA::SINGLE, false, true));
+ CACAC::SINGLE, false, true));
{ // fastsearch
CACA a;
@@ -270,7 +272,7 @@ AttributeManagerTest::testConfigConvert()
}
{ // tensor
CACA a;
- a.datatype = CACA::TENSOR;
+ a.datatype = CACAD::TENSOR;
a.tensortype = "tensor(x[5])";
AttributeVector::Config out = ConfigConverter::convert(a);
EXPECT_EQUAL("tensor(x[5])", out.tensorType().to_spec());
diff --git a/searchlib/src/tests/memoryindex/field_index_remover/field_index_remover_test.cpp b/searchlib/src/tests/memoryindex/field_index_remover/field_index_remover_test.cpp
index c0e8871b80a..f11df42f6b1 100644
--- a/searchlib/src/tests/memoryindex/field_index_remover/field_index_remover_test.cpp
+++ b/searchlib/src/tests/memoryindex/field_index_remover/field_index_remover_test.cpp
@@ -19,7 +19,7 @@ struct WordFieldPair {
WordFieldPair(vespalib::stringref word, uint32_t fieldId)
: _word(word), _fieldId(fieldId)
{}
- bool operator<(const WordFieldPair &rhs) {
+ bool operator<(const WordFieldPair &rhs) const {
if (_word != rhs._word) {
return _word < rhs._word;
}
diff --git a/searchlib/src/tests/queryeval/queryeval.cpp b/searchlib/src/tests/queryeval/queryeval.cpp
index db7f8d1cda1..56c6f7e1282 100644
--- a/searchlib/src/tests/queryeval/queryeval.cpp
+++ b/searchlib/src/tests/queryeval/queryeval.cpp
@@ -575,7 +575,14 @@ getExpectedSlime() {
}
TEST("testDump") {
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wshadow"
+#endif
typedef SourceBlenderSearch::Child Source;
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
SearchIterator::UP search(
AndSearch::create(
Collect<SearchIterator*, MultiSearch::Children>()
diff --git a/searchlib/src/vespa/searchlib/CMakeLists.txt b/searchlib/src/vespa/searchlib/CMakeLists.txt
index e4e1f92898f..f6f9ad2259a 100644
--- a/searchlib/src/vespa/searchlib/CMakeLists.txt
+++ b/searchlib/src/vespa/searchlib/CMakeLists.txt
@@ -32,7 +32,7 @@ vespa_add_library(searchlib
INSTALL lib64
DEPENDS
staging_vespalib
- atomic
+ ${VESPA_ATOMIC_LIB}
)
vespa_add_target_package_dependency(searchlib Protobuf)
diff --git a/searchlib/src/vespa/searchlib/attribute/configconverter.cpp b/searchlib/src/vespa/searchlib/attribute/configconverter.cpp
index ae08204f5bc..535e81fc032 100644
--- a/searchlib/src/vespa/searchlib/attribute/configconverter.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/configconverter.cpp
@@ -19,20 +19,20 @@ DataTypeMap
getDataTypeMap()
{
DataTypeMap map;
- map[AttributesConfig::Attribute::STRING] = BasicType::STRING;
- map[AttributesConfig::Attribute::BOOL] = BasicType::BOOL;
- map[AttributesConfig::Attribute::UINT2] = BasicType::UINT2;
- map[AttributesConfig::Attribute::UINT4] = BasicType::UINT4;
- map[AttributesConfig::Attribute::INT8] = BasicType::INT8;
- map[AttributesConfig::Attribute::INT16] = BasicType::INT16;
- map[AttributesConfig::Attribute::INT32] = BasicType::INT32;
- map[AttributesConfig::Attribute::INT64] = BasicType::INT64;
- map[AttributesConfig::Attribute::FLOAT] = BasicType::FLOAT;
- map[AttributesConfig::Attribute::DOUBLE] = BasicType::DOUBLE;
- map[AttributesConfig::Attribute::PREDICATE] = BasicType::PREDICATE;
- map[AttributesConfig::Attribute::TENSOR] = BasicType::TENSOR;
- map[AttributesConfig::Attribute::REFERENCE] = BasicType::REFERENCE;
- map[AttributesConfig::Attribute::NONE] = BasicType::NONE;
+ map[AttributesConfig::Attribute::Datatype::STRING] = BasicType::STRING;
+ map[AttributesConfig::Attribute::Datatype::BOOL] = BasicType::BOOL;
+ map[AttributesConfig::Attribute::Datatype::UINT2] = BasicType::UINT2;
+ map[AttributesConfig::Attribute::Datatype::UINT4] = BasicType::UINT4;
+ map[AttributesConfig::Attribute::Datatype::INT8] = BasicType::INT8;
+ map[AttributesConfig::Attribute::Datatype::INT16] = BasicType::INT16;
+ map[AttributesConfig::Attribute::Datatype::INT32] = BasicType::INT32;
+ map[AttributesConfig::Attribute::Datatype::INT64] = BasicType::INT64;
+ map[AttributesConfig::Attribute::Datatype::FLOAT] = BasicType::FLOAT;
+ map[AttributesConfig::Attribute::Datatype::DOUBLE] = BasicType::DOUBLE;
+ map[AttributesConfig::Attribute::Datatype::PREDICATE] = BasicType::PREDICATE;
+ map[AttributesConfig::Attribute::Datatype::TENSOR] = BasicType::TENSOR;
+ map[AttributesConfig::Attribute::Datatype::REFERENCE] = BasicType::REFERENCE;
+ map[AttributesConfig::Attribute::Datatype::NONE] = BasicType::NONE;
return map;
}
@@ -40,9 +40,9 @@ CollectionTypeMap
getCollectionTypeMap()
{
CollectionTypeMap map;
- map[AttributesConfig::Attribute::SINGLE] = CollectionType::SINGLE;
- map[AttributesConfig::Attribute::ARRAY] = CollectionType::ARRAY;
- map[AttributesConfig::Attribute::WEIGHTEDSET] = CollectionType::WSET;
+ map[AttributesConfig::Attribute::Collectiontype::SINGLE] = CollectionType::SINGLE;
+ map[AttributesConfig::Attribute::Collectiontype::ARRAY] = CollectionType::ARRAY;
+ map[AttributesConfig::Attribute::Collectiontype::WEIGHTEDSET] = CollectionType::WSET;
return map;
}
diff --git a/searchlib/src/vespa/searchlib/attribute/enumattribute.hpp b/searchlib/src/vespa/searchlib/attribute/enumattribute.hpp
index 075eaceb11b..047de7de3c7 100644
--- a/searchlib/src/vespa/searchlib/attribute/enumattribute.hpp
+++ b/searchlib/src/vespa/searchlib/attribute/enumattribute.hpp
@@ -101,13 +101,18 @@ EnumAttribute<B>::insertNewUniqueValues(EnumStoreBase::IndexVector & newIndexes)
do {
// perform compaction on EnumStore if necessary
if (extraBytesNeeded > this->_enumStore.getRemaining() ||
- this->_enumStore.getPendingCompact()) {
+ this->_enumStore.getPendingCompact())
+ {
+ this->logEnumStoreEvent("enumstorecompact", "reserve");
this->removeAllOldGenerations();
this->_enumStore.clearPendingCompact();
EnumIndexMap old2New(this->_enumStore.getNumUniques()*3);
+ this->logEnumStoreEvent("enumstorecompact", "start");
if (!this->_enumStore.performCompaction(extraBytesNeeded, old2New)) {
+ this->logEnumStoreEvent("enumstorecompact", "failed_compact");
// fallback to resize strategy
this->_enumStore.fallbackResize(extraBytesNeeded);
+ this->logEnumStoreEvent("enumstorecompact", "fallbackresize_complete");
if (extraBytesNeeded > this->_enumStore.getRemaining()) {
HDR_ABORT("Cannot fallbackResize enumStore");
}
@@ -120,6 +125,7 @@ EnumAttribute<B>::insertNewUniqueValues(EnumStoreBase::IndexVector & newIndexes)
for (auto & data : this->_changes) {
data._enumScratchPad = ChangeBase::UNSET_ENUM;
}
+ this->logEnumStoreEvent("enumstorecompact", "complete");
}
} while (0);
diff --git a/searchlib/src/vespa/searchlib/attribute/multi_value_mapping.hpp b/searchlib/src/vespa/searchlib/attribute/multi_value_mapping.hpp
index eb4d5fadd2a..4460d1757a2 100644
--- a/searchlib/src/vespa/searchlib/attribute/multi_value_mapping.hpp
+++ b/searchlib/src/vespa/searchlib/attribute/multi_value_mapping.hpp
@@ -11,7 +11,14 @@ namespace search::attribute {
template <typename EntryT, typename RefT>
MultiValueMapping<EntryT,RefT>::MultiValueMapping(const datastore::ArrayStoreConfig &storeCfg,
const vespalib::GrowStrategy &gs)
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wuninitialized"
+#endif
: MultiValueMappingBase(gs, _store.getGenerationHolder()),
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
_store(storeCfg)
{
}
diff --git a/searchlib/src/vespa/searchlib/attribute/singleenumattribute.hpp b/searchlib/src/vespa/searchlib/attribute/singleenumattribute.hpp
index 4f5cd3f4276..3c248342696 100644
--- a/searchlib/src/vespa/searchlib/attribute/singleenumattribute.hpp
+++ b/searchlib/src/vespa/searchlib/attribute/singleenumattribute.hpp
@@ -137,8 +137,10 @@ template <typename B>
void
SingleValueEnumAttribute<B>::reEnumerate(const EnumIndexMap & old2New)
{
+ this->logEnumStoreEvent("reenumerate", "reserved");
auto newIndexes = std::make_unique<vespalib::Array<EnumIndex>>();
newIndexes->reserve(_enumIndices.capacity());
+ this->logEnumStoreEvent("reenumerate", "start");
for (uint32_t i = 0; i < _enumIndices.size(); ++i) {
EnumIndex oldIdx = _enumIndices[i];
EnumIndex newIdx;
@@ -154,6 +156,7 @@ SingleValueEnumAttribute<B>::reEnumerate(const EnumIndexMap & old2New)
_enumIndices.replaceVector(std::move(newIndexes));
}
this->logEnumStoreEvent("compactfixup", "complete");
+ this->logEnumStoreEvent("reenumerate", "complete");
}
template <typename B>
diff --git a/searchlib/src/vespa/searchlib/bitcompression/compression.h b/searchlib/src/vespa/searchlib/bitcompression/compression.h
index de206d33b8d..350932263c3 100644
--- a/searchlib/src/vespa/searchlib/bitcompression/compression.h
+++ b/searchlib/src/vespa/searchlib/bitcompression/compression.h
@@ -1143,7 +1143,7 @@ public:
DecodeContext64Base()
: search::ComprFileDecodeContext(),
_valI(nullptr),
- _valE(static_cast<const uint64_t *>(nullptr) - 1),
+ _valE(reinterpret_cast<const uint64_t *>(PTRDIFF_MAX)),
_realValE(nullptr),
_val(0),
_cacheInt(0),
@@ -1325,7 +1325,7 @@ public:
DecodeContext64(const uint64_t *compr,
int bitOffset)
: DecodeContext64Base(compr + 1,
- static_cast<const uint64_t *>(nullptr) - 1,
+ reinterpret_cast<const uint64_t *>(PTRDIFF_MAX),
nullptr,
0,
EC::bswap(*compr),
diff --git a/searchlib/src/vespa/searchlib/common/tunefileinfo.h b/searchlib/src/vespa/searchlib/common/tunefileinfo.h
index d431a9cf78f..bcd6765845b 100644
--- a/searchlib/src/vespa/searchlib/common/tunefileinfo.h
+++ b/searchlib/src/vespa/searchlib/common/tunefileinfo.h
@@ -26,10 +26,10 @@ public:
template <typename Config>
void setFromConfig(const enum Config::Io &config) {
switch (config) {
- case Config::NORMAL:
+ case Config::Io::NORMAL:
_tuneControl = NORMAL;
break;
- case Config::DIRECTIO:
+ case Config::Io::DIRECTIO:
_tuneControl = DIRECTIO;
break;
default:
@@ -70,13 +70,13 @@ public:
template <typename Config>
void setFromConfig(const enum Config::Io &config) {
switch (config) {
- case Config::NORMAL:
+ case Config::Io::NORMAL:
_tuneControl = NORMAL;
break;
- case Config::OSYNC:
+ case Config::Io::OSYNC:
_tuneControl = OSYNC;
break;
- case Config::DIRECTIO:
+ case Config::Io::DIRECTIO:
_tuneControl = DIRECTIO;
break;
default:
diff --git a/searchlib/src/vespa/searchlib/common/tunefileinfo.hpp b/searchlib/src/vespa/searchlib/common/tunefileinfo.hpp
index 17d7949e9b9..5f28f82c526 100644
--- a/searchlib/src/vespa/searchlib/common/tunefileinfo.hpp
+++ b/searchlib/src/vespa/searchlib/common/tunefileinfo.hpp
@@ -12,9 +12,9 @@ template <typename TuneControlConfig, typename MMapConfig>
void
TuneFileRandRead::setFromConfig(const enum TuneControlConfig::Io & tuneControlConfig, const MMapConfig & mmapFlags) {
switch ( tuneControlConfig) {
- case TuneControlConfig::NORMAL: _tuneControl = NORMAL; break;
- case TuneControlConfig::DIRECTIO: _tuneControl = DIRECTIO; break;
- case TuneControlConfig::MMAP: _tuneControl = MMAP; break;
+ case TuneControlConfig::Io::NORMAL: _tuneControl = NORMAL; break;
+ case TuneControlConfig::Io::DIRECTIO: _tuneControl = DIRECTIO; break;
+ case TuneControlConfig::Io::MMAP: _tuneControl = MMAP; break;
default: _tuneControl = NORMAL; break;
}
setFromMmapConfig(mmapFlags);
@@ -25,15 +25,15 @@ void
TuneFileRandRead::setFromMmapConfig(const MMapConfig & mmapFlags) {
for (size_t i(0), m(mmapFlags.options.size()); i < m; i++) {
switch (mmapFlags.options[i]) {
- case MMapConfig::MLOCK: _mmapFlags |= MAP_LOCKED; break;
- case MMapConfig::POPULATE: _mmapFlags |= MAP_POPULATE; break;
- case MMapConfig::HUGETLB: _mmapFlags |= MAP_HUGETLB; break;
+ case MMapConfig::Options::MLOCK: _mmapFlags |= MAP_LOCKED; break;
+ case MMapConfig::Options::POPULATE: _mmapFlags |= MAP_POPULATE; break;
+ case MMapConfig::Options::HUGETLB: _mmapFlags |= MAP_HUGETLB; break;
}
}
switch (mmapFlags.advise) {
- case MMapConfig::NORMAL: setAdvise(POSIX_FADV_NORMAL); break;
- case MMapConfig::RANDOM: setAdvise(POSIX_FADV_RANDOM); break;
- case MMapConfig::SEQUENTIAL: setAdvise(POSIX_FADV_SEQUENTIAL); break;
+ case MMapConfig::Advise::NORMAL: setAdvise(POSIX_FADV_NORMAL); break;
+ case MMapConfig::Advise::RANDOM: setAdvise(POSIX_FADV_RANDOM); break;
+ case MMapConfig::Advise::SEQUENTIAL: setAdvise(POSIX_FADV_SEQUENTIAL); break;
}
}
diff --git a/searchlib/src/vespa/searchlib/expression/functionnodes.cpp b/searchlib/src/vespa/searchlib/expression/functionnodes.cpp
index 93655ec2925..0eb85cba4ba 100644
--- a/searchlib/src/vespa/searchlib/expression/functionnodes.cpp
+++ b/searchlib/src/vespa/searchlib/expression/functionnodes.cpp
@@ -212,7 +212,14 @@ void MultiArgFunctionNode::onPrepareResult()
} else if (_args.size() > 1) {
setResultType(std::unique_ptr<ResultNode>(static_cast<ResultNode *>(_args[0]->getResult().clone())));
for(size_t i(1), m(_args.size()); i < m; i++) {
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wtautological-undefined-compare"
+#endif
if (&_args[i]->getResult() != NULL) {
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
setResultType(_ArithmeticTypeConversion.getType(getResult(), _args[i]->getResult()));
}
}
diff --git a/searchlib/src/vespa/searchlib/features/attributefeature.cpp b/searchlib/src/vespa/searchlib/features/attributefeature.cpp
index 4fff5ae5b3f..b1d3356dd62 100644
--- a/searchlib/src/vespa/searchlib/features/attributefeature.cpp
+++ b/searchlib/src/vespa/searchlib/features/attributefeature.cpp
@@ -12,6 +12,7 @@
#include <vespa/searchlib/tensor/dense_tensor_attribute.h>
#include <vespa/searchlib/fef/indexproperties.h>
#include <vespa/searchlib/attribute/singlenumericattribute.h>
+#include <vespa/searchlib/attribute/multinumericattribute.h>
#include <vespa/log/log.h>
LOG_SETUP(".features.attributefeature");
@@ -35,6 +36,7 @@ using search::fef::FeatureType;
using namespace search::fef::indexproperties;
+namespace search::features {
namespace {
template <typename X, typename Y>
bool equals(X lhs, Y rhs) {
@@ -52,17 +54,17 @@ isUndefined(T value, BasicType::Type type)
{
switch (type) {
case BasicType::INT8:
- return search::attribute::isUndefined<int8_t>(static_cast<int8_t>(value));
+ return attribute::isUndefined<int8_t>(static_cast<int8_t>(value));
case BasicType::INT16:
- return search::attribute::isUndefined<int16_t>(static_cast<int16_t>(value));
+ return attribute::isUndefined<int16_t>(static_cast<int16_t>(value));
case BasicType::INT32:
- return search::attribute::isUndefined<int32_t>(static_cast<int32_t>(value));
+ return attribute::isUndefined<int32_t>(static_cast<int32_t>(value));
case BasicType::INT64:
- return search::attribute::isUndefined<int64_t>(static_cast<int64_t>(value));
+ return attribute::isUndefined<int64_t>(static_cast<int64_t>(value));
case BasicType::FLOAT:
- return search::attribute::isUndefined<float>(static_cast<float>(value));
+ return attribute::isUndefined<float>(static_cast<float>(value));
case BasicType::DOUBLE:
- return search::attribute::isUndefined<double>(static_cast<double>(value));
+ return attribute::isUndefined<double>(static_cast<double>(value));
default:
return false;
}
@@ -76,28 +78,22 @@ isUndefined<vespalib::stringref>(vespalib::stringref, BasicType::Type)
}
template <typename T>
-search::feature_t
+feature_t
considerUndefined(T value, BasicType::Type type)
{
if (isUndefined(value, type)) {
- return search::attribute::getUndefined<search::feature_t>();
+ return attribute::getUndefined<feature_t>();
}
- return search::features::util::getAsFeature(value);
+ return util::getAsFeature(value);
}
template <>
-search::feature_t
+feature_t
considerUndefined<ConstCharPtr>(ConstCharPtr value, BasicType::Type )
{
- return search::features::util::getAsFeature(value);
+ return util::getAsFeature(value);
}
-
-}
-
-
-namespace search::features {
-
/**
* Implements the executor for fetching values from a single or array attribute vector
*/
@@ -115,6 +111,24 @@ public:
void execute(uint32_t docId) override;
};
+/**
+ * Implements the executor for fetching values from a single or array attribute vector
+ */
+template <typename T>
+class MultiAttributeExecutor : public fef::FeatureExecutor {
+private:
+ const T & _attribute;
+ uint32_t _idx;
+public:
+ /**
+ * Constructs an executor.
+ *
+ * @param attribute The attribute vector to use.
+ */
+ MultiAttributeExecutor(const T & attribute, uint32_t idx) : _attribute(attribute), _idx(idx) { }
+ void execute(uint32_t docId) override;
+};
+
class CountOnlyAttributeExecutor : public fef::FeatureExecutor {
private:
const attribute::IAttributeVector & _attribute;
@@ -182,21 +196,37 @@ SingleAttributeExecutor<T>::execute(uint32_t docId)
{
typename T::LoadedValueType v = _attribute.getFast(docId);
// value
- outputs().set_number(0, __builtin_expect(attribute::isUndefined(v), false)
- ? attribute::getUndefined<feature_t>()
- : util::getAsFeature(v));
- outputs().set_number(1, 0.0f); // weight
- outputs().set_number(2, 0.0f); // contains
- outputs().set_number(3, 1.0f); // count
+ auto o = outputs().get_bound();
+ o[0].as_number = __builtin_expect(attribute::isUndefined(v), false)
+ ? attribute::getUndefined<feature_t>()
+ : util::getAsFeature(v);
+ o[1].as_number = 0; // weight
+ o[2].as_number = 0; // contains
+ o[3].as_number = 1; // contains
+}
+
+template <typename T>
+void
+MultiAttributeExecutor<T>::execute(uint32_t docId)
+{
+ const multivalue::Value<typename T::BaseType> * values = nullptr;
+ uint32_t numValues = _attribute.getRawValues(docId, values);
+
+ auto o = outputs().get_bound();
+ o[0].as_number = __builtin_expect(_idx < numValues, true) ? values[_idx].value() : 0;
+ o[1].as_number = 0; // weight
+ o[2].as_number = 0; // contains
+ o[3].as_number = 0; // count
}
void
CountOnlyAttributeExecutor::execute(uint32_t docId)
{
- outputs().set_number(0, 0.0f); // value
- outputs().set_number(1, 0.0f); // weight
- outputs().set_number(2, 0.0f); // contains
- outputs().set_number(3, _attribute.getValueCount(docId)); // count
+ auto o = outputs().get_bound();
+ o[0].as_number = 0; // value
+ o[1].as_number = 0; // weight
+ o[2].as_number = 0; // contains
+ o[3].as_number = _attribute.getValueCount(docId); // count
}
template <typename T>
@@ -220,10 +250,11 @@ AttributeExecutor<T>::execute(uint32_t docId)
if (_idx < _buffer.size()) {
value = considerUndefined(_buffer[_idx], _attrType);
}
- outputs().set_number(0, value); // value
- outputs().set_number(1, 0.0f); // weight
- outputs().set_number(2, 0.0f); // contains
- outputs().set_number(3, _defaultCount); // count
+ auto o = outputs().get_bound();
+ o[0].as_number = value; // value
+ o[1].as_number = 0; // weight
+ o[2].as_number = 0; // contains
+ o[3].as_number = _defaultCount; // count
}
@@ -265,70 +296,39 @@ WeightedSetAttributeExecutor<BT, T>::execute(uint32_t docId)
outputs().set_number(3, count); // count
}
-
-AttributeBlueprint::AttributeBlueprint() :
- fef::Blueprint("attribute"),
- _attrName(),
- _attrKey(),
- _extra(),
- _tensorType(ValueType::double_type())
-{
-}
-
-AttributeBlueprint::~AttributeBlueprint() = default;
-
-void
-AttributeBlueprint::visitDumpFeatures(const fef::IIndexEnvironment &,
- fef::IDumpFeatureVisitor &) const
-{
-}
-
-bool
-AttributeBlueprint::setup(const fef::IIndexEnvironment & env,
- const fef::ParameterList & params)
-{
- // params[0] = attribute name
- // params[1] = index (array attribute) or key (weighted set attribute)
- _attrName = params[0].getValue();
- _attrKey = createAttributeKey(_attrName);
- if (params.size() == 2) {
- _extra = params[1].getValue();
- }
- vespalib::string attrType = type::Attribute::lookup(env.getProperties(), _attrName);
- if (!attrType.empty()) {
- _tensorType = ValueType::from_spec(attrType);
- if (_tensorType.is_error()) {
- LOG(error, "%s: invalid type: '%s'", getName().c_str(), attrType.c_str());
- }
+template <typename T>
+struct SingleValueExecutorCreator {
+ using AttrType = SingleValueNumericAttribute<T>;
+ using PtrType = const AttrType *;
+ using ExecType = SingleAttributeExecutor<AttrType>;
+ SingleValueExecutorCreator() : ptr(nullptr) {}
+ bool handle(const IAttributeVector *attribute) {
+ ptr = dynamic_cast<PtrType>(attribute);
+ return ptr != nullptr;
}
- FeatureType output_type = _tensorType.is_double()
- ? FeatureType::number()
- : FeatureType::object(_tensorType);
- describeOutput("value", "The value of a single value attribute, "
- "the value at the given index of an array attribute, "
- "the given key of a weighted set attribute, or"
- "the tensor of a tensor attribute", output_type);
- if (!_tensorType.is_tensor()) {
- describeOutput("weight", "The weight associated with the given key in a weighted set attribute.");
- describeOutput("contains", "1 if the given key is present in a weighted set attribute, 0 otherwise.");
- describeOutput("count", "Returns the number of elements in this array or weighted set attribute.");
+ fef::FeatureExecutor & create(vespalib::Stash &stash) const {
+ return stash.create<ExecType>(*ptr);
}
- env.hintAttributeAccess(_attrName);
- return !_tensorType.is_error();
-}
-
-fef::Blueprint::UP
-AttributeBlueprint::createInstance() const
-{
- return std::make_unique<AttributeBlueprint>();
-}
+private:
+ PtrType ptr;
+};
-#define CREATE_AND_RETURN_IF_SINGLE_NUMERIC(a, T) \
- if (dynamic_cast<const SingleValueNumericAttribute<T> *>(a) != nullptr) { \
- return stash.create<SingleAttributeExecutor<SingleValueNumericAttribute<T>>>(*static_cast<const SingleValueNumericAttribute<T> *>(a)); \
+template <typename T>
+struct MultiValueExecutorCreator {
+ using AttrType = MultiValueNumericAttribute<T, multivalue::Value<typename T::BaseType>>;
+ using PtrType = const AttrType *;
+ using ExecType = MultiAttributeExecutor<AttrType>;
+ MultiValueExecutorCreator() : ptr(nullptr) {}
+ bool handle(const IAttributeVector *attribute) {
+ ptr = dynamic_cast<PtrType>(attribute);
+ return ptr != nullptr;
}
-
-namespace {
+ fef::FeatureExecutor & create(vespalib::Stash &stash, uint32_t idx) const {
+ return stash.create<ExecType>(*ptr, idx);
+ }
+private:
+ PtrType ptr;
+};
fef::FeatureExecutor &
createAttributeExecutor(const IAttributeVector *attribute, const vespalib::string &attrName, const vespalib::string &extraParam, vespalib::Stash &stash)
@@ -354,10 +354,10 @@ createAttributeExecutor(const IAttributeVector *attribute, const vespalib::strin
}
} else { // SINGLE or ARRAY
if ((attribute->getCollectionType() == CollectionType::SINGLE) && (attribute->isIntegerType() || attribute->isFloatingPointType())) {
- CREATE_AND_RETURN_IF_SINGLE_NUMERIC(attribute, FloatingPointAttributeTemplate<double>);
- CREATE_AND_RETURN_IF_SINGLE_NUMERIC(attribute, FloatingPointAttributeTemplate<float>);
- CREATE_AND_RETURN_IF_SINGLE_NUMERIC(attribute, IntegerAttributeTemplate<int32_t>);
- CREATE_AND_RETURN_IF_SINGLE_NUMERIC(attribute, IntegerAttributeTemplate<int64_t>);
+ { SingleValueExecutorCreator<FloatingPointAttributeTemplate<double>> creator; if (creator.handle(attribute)) return creator.create(stash); }
+ { SingleValueExecutorCreator<FloatingPointAttributeTemplate<float>> creator; if (creator.handle(attribute)) return creator.create(stash); }
+ { SingleValueExecutorCreator<IntegerAttributeTemplate<int32_t>> creator; if (creator.handle(attribute)) return creator.create(stash); }
+ { SingleValueExecutorCreator<IntegerAttributeTemplate<int64_t>> creator; if (creator.handle(attribute)) return creator.create(stash); }
}
{
uint32_t idx = 0;
@@ -369,8 +369,12 @@ createAttributeExecutor(const IAttributeVector *attribute, const vespalib::strin
if (attribute->isStringType()) {
return stash.create<AttributeExecutor<ConstCharContent>>(attribute, idx);
} else if (attribute->isIntegerType()) {
+ { MultiValueExecutorCreator<IntegerAttributeTemplate<int32_t>> creator; if (creator.handle(attribute)) return creator.create(stash, idx); }
+ { MultiValueExecutorCreator<IntegerAttributeTemplate<int64_t>> creator; if (creator.handle(attribute)) return creator.create(stash, idx); }
return stash.create<AttributeExecutor<IntegerContent>>(attribute, idx);
} else { // FLOAT
+ { MultiValueExecutorCreator<FloatingPointAttributeTemplate<double>> creator; if (creator.handle(attribute)) return creator.create(stash, idx); }
+ { MultiValueExecutorCreator<FloatingPointAttributeTemplate<float>> creator; if (creator.handle(attribute)) return creator.create(stash, idx); }
return stash.create<AttributeExecutor<FloatContent>>(attribute, idx);
}
}
@@ -415,6 +419,63 @@ createTensorAttributeExecutor(const IAttributeVector *attribute, const vespalib:
}
+AttributeBlueprint::AttributeBlueprint() :
+ fef::Blueprint("attribute"),
+ _attrName(),
+ _attrKey(),
+ _extra(),
+ _tensorType(ValueType::double_type())
+{
+}
+
+AttributeBlueprint::~AttributeBlueprint() = default;
+
+void
+AttributeBlueprint::visitDumpFeatures(const fef::IIndexEnvironment &,
+ fef::IDumpFeatureVisitor &) const
+{
+}
+
+bool
+AttributeBlueprint::setup(const fef::IIndexEnvironment & env,
+ const fef::ParameterList & params)
+{
+ // params[0] = attribute name
+ // params[1] = index (array attribute) or key (weighted set attribute)
+ _attrName = params[0].getValue();
+ _attrKey = createAttributeKey(_attrName);
+ if (params.size() == 2) {
+ _extra = params[1].getValue();
+ }
+ vespalib::string attrType = type::Attribute::lookup(env.getProperties(), _attrName);
+ if (!attrType.empty()) {
+ _tensorType = ValueType::from_spec(attrType);
+ if (_tensorType.is_error()) {
+ LOG(error, "%s: invalid type: '%s'", getName().c_str(), attrType.c_str());
+ }
+ }
+ FeatureType output_type = _tensorType.is_double()
+ ? FeatureType::number()
+ : FeatureType::object(_tensorType);
+ describeOutput("value", "The value of a single value attribute, "
+ "the value at the given index of an array attribute, "
+ "the given key of a weighted set attribute, or"
+ "the tensor of a tensor attribute", output_type);
+ if (!_tensorType.is_tensor()) {
+ describeOutput("weight", "The weight associated with the given key in a weighted set attribute.");
+ describeOutput("contains", "1 if the given key is present in a weighted set attribute, 0 otherwise.");
+ describeOutput("count", "Returns the number of elements in this array or weighted set attribute.");
+ }
+ env.hintAttributeAccess(_attrName);
+ return !_tensorType.is_error();
+}
+
+fef::Blueprint::UP
+AttributeBlueprint::createInstance() const
+{
+ return std::make_unique<AttributeBlueprint>();
+}
+
void
AttributeBlueprint::prepareSharedState(const fef::IQueryEnvironment & env, fef::IObjectStore & store) const
{
diff --git a/searchlib/src/vespa/searchlib/features/dotproductfeature.cpp b/searchlib/src/vespa/searchlib/features/dotproductfeature.cpp
index e3f4cee4836..8998f01b59e 100644
--- a/searchlib/src/vespa/searchlib/features/dotproductfeature.cpp
+++ b/searchlib/src/vespa/searchlib/features/dotproductfeature.cpp
@@ -444,6 +444,9 @@ ArrayParam<T>::~ArrayParam() = default;
// FIXME this feels a bit dirty, consider breaking up ArrayParam to remove dependencies
// on templated vector parsing. This is why it's defined in this translation unit as it is.
template ArrayParam<int64_t>::ArrayParam(const Property & prop);
+#ifdef __clang__
+template ArrayParam<int64_t>::~ArrayParam();
+#endif
template struct ArrayParam<double>;
template struct ArrayParam<float>;
diff --git a/searchlib/src/vespa/searchlib/fef/featureexecutor.h b/searchlib/src/vespa/searchlib/fef/featureexecutor.h
index dc8a4ba6075..1e52aaee8ad 100644
--- a/searchlib/src/vespa/searchlib/fef/featureexecutor.h
+++ b/searchlib/src/vespa/searchlib/fef/featureexecutor.h
@@ -58,10 +58,10 @@ public:
};
class Outputs {
- vespalib::ArrayRef<NumberOrObject> _outputs;
public:
+ using OutputArray = vespalib::ArrayRef<NumberOrObject>;
Outputs() : _outputs() {}
- void bind(vespalib::ArrayRef<NumberOrObject> outputs) { _outputs = outputs; }
+ void bind(OutputArray outputs) { _outputs = outputs; }
void set_number(size_t idx, feature_t value) {
_outputs[idx].as_number = value;
}
@@ -83,7 +83,12 @@ public:
const NumberOrObject *get_raw(size_t idx) const {
return &_outputs[idx];
}
+ OutputArray get_bound() const {
+ return _outputs;
+ }
size_t size() const { return _outputs.size(); }
+ private:
+ vespalib::ArrayRef<NumberOrObject> _outputs;
};
private:
diff --git a/searchlib/src/vespa/searchlib/predicate/predicate_index.h b/searchlib/src/vespa/searchlib/predicate/predicate_index.h
index 196c1df16de..b0fb0eda4c5 100644
--- a/searchlib/src/vespa/searchlib/predicate/predicate_index.h
+++ b/searchlib/src/vespa/searchlib/predicate/predicate_index.h
@@ -29,7 +29,7 @@ class PredicateIndex : public PopulateInterface {
using FeatureMap = std::unordered_map<uint64_t, std::vector<IntervalT>>;
using generation_t = vespalib::GenerationHandler::generation_t;
template <typename T>
- using optional = std::experimental::optional<T>;
+ using optional = std::optional<T>;
public:
typedef std::unique_ptr<PredicateIndex> UP;
diff --git a/searchlib/src/vespa/searchlib/predicate/simple_index.h b/searchlib/src/vespa/searchlib/predicate/simple_index.h
index 4edc0ff2d14..986b46d7008 100644
--- a/searchlib/src/vespa/searchlib/predicate/simple_index.h
+++ b/searchlib/src/vespa/searchlib/predicate/simple_index.h
@@ -6,7 +6,7 @@
#include <vespa/vespalib/btree/btreestore.h>
#include <vespa/vespalib/data/databuffer.h>
#include <vespa/vespalib/util/rcuvector.h>
-#include <experimental/optional>
+#include <optional>
namespace search::predicate {
@@ -139,7 +139,7 @@ private:
using GenerationHolder = vespalib::GenerationHolder;
using generation_t = vespalib::GenerationHandler::generation_t;
template <typename T>
- using optional = std::experimental::optional<T>;
+ using optional = std::optional<T>;
Dictionary _dictionary;
BTreeStore _btree_posting_lists;
diff --git a/searchlib/src/vespa/searchlib/queryeval/predicate_blueprint.h b/searchlib/src/vespa/searchlib/queryeval/predicate_blueprint.h
index 3d92b19c421..16b725cd4b0 100644
--- a/searchlib/src/vespa/searchlib/queryeval/predicate_blueprint.h
+++ b/searchlib/src/vespa/searchlib/queryeval/predicate_blueprint.h
@@ -56,7 +56,7 @@ private:
using BTreeIterator = predicate::SimpleIndex<datastore::EntryRef>::BTreeIterator;
using VectorIterator = predicate::SimpleIndex<datastore::EntryRef>::VectorIterator;
template <typename T>
- using optional = std::experimental::optional<T>;
+ using optional = std::optional<T>;
using Alloc = vespalib::alloc::Alloc;
const PredicateAttribute & predicate_attribute() const {
diff --git a/searchlib/src/vespa/searchlib/transactionlog/translogserverapp.cpp b/searchlib/src/vespa/searchlib/transactionlog/translogserverapp.cpp
index 0b02d10ffab..b8d21fb7465 100644
--- a/searchlib/src/vespa/searchlib/transactionlog/translogserverapp.cpp
+++ b/searchlib/src/vespa/searchlib/transactionlog/translogserverapp.cpp
@@ -28,9 +28,9 @@ DomainPart::Crc
getCrc(searchlib::TranslogserverConfig::Crcmethod crcType)
{
switch (crcType) {
- case searchlib::TranslogserverConfig::ccitt_crc32:
+ case searchlib::TranslogserverConfig::Crcmethod::ccitt_crc32:
return DomainPart::ccitt_crc32;
- case searchlib::TranslogserverConfig::xxh64:
+ case searchlib::TranslogserverConfig::Crcmethod::xxh64:
return DomainPart::xxh64;
}
LOG_ABORT("should not be reached");
diff --git a/security-tools/src/main/sh/vespa-curl-wrapper b/security-tools/src/main/sh/vespa-curl-wrapper
index da857984c01..da1465a07bc 100755
--- a/security-tools/src/main/sh/vespa-curl-wrapper
+++ b/security-tools/src/main/sh/vespa-curl-wrapper
@@ -6,7 +6,7 @@
set -e
-eval $(vespa-security-env)
+eval $($VESPA_HOME/bin/vespa-security-env)
CURL_PARAMETERS=("$@")
diff --git a/staging_vespalib/src/tests/xmlserializable/xmlserializabletest.cpp b/staging_vespalib/src/tests/xmlserializable/xmlserializabletest.cpp
index f2990d7b511..a389ada2214 100644
--- a/staging_vespalib/src/tests/xmlserializable/xmlserializabletest.cpp
+++ b/staging_vespalib/src/tests/xmlserializable/xmlserializabletest.cpp
@@ -56,7 +56,7 @@ Test::testEscaping()
std::ostringstream ost;
XmlOutputStream xos(ost);
using namespace vespalib::xml;
- xos << XmlTag("!#trash%-", CONVERT_ILLEGAL_CHARACTERS)
+ xos << XmlTag("!#trash%-", XmlTagFlags::CONVERT_ILLEGAL_CHARACTERS)
<< XmlTag("foo")
<< XmlAttribute("bar", "<100%\" &\n>")
<< XmlEndTag()
diff --git a/staging_vespalib/src/vespa/vespalib/util/xmlstream.cpp b/staging_vespalib/src/vespa/vespalib/util/xmlstream.cpp
index b5fd5b10844..e1778a881be 100644
--- a/staging_vespalib/src/vespa/vespalib/util/xmlstream.cpp
+++ b/staging_vespalib/src/vespa/vespalib/util/xmlstream.cpp
@@ -361,7 +361,7 @@ XmlTag::XmlTag(const std::string& name, XmlTagFlags flags)
_content(),
_flags(flags)
{
- if (_flags == CONVERT_ILLEGAL_CHARACTERS) {
+ if (_flags == XmlTagFlags::CONVERT_ILLEGAL_CHARACTERS) {
convertToLegalName(_name);
}
if (!isLegalName(_name)) {
diff --git a/staging_vespalib/src/vespa/vespalib/util/xmlstream.h b/staging_vespalib/src/vespa/vespalib/util/xmlstream.h
index 5455251eea1..01f3104a595 100644
--- a/staging_vespalib/src/vespa/vespalib/util/xmlstream.h
+++ b/staging_vespalib/src/vespa/vespalib/util/xmlstream.h
@@ -42,7 +42,7 @@ class XmlOutputStream;
bool isLegalName(const std::string& name);
-enum XmlTagFlags { NONE = 0, CONVERT_ILLEGAL_CHARACTERS = 1 };
+enum class XmlTagFlags { NONE = 0, CONVERT_ILLEGAL_CHARACTERS = 1 };
/**
* @class document::XmlTag
@@ -56,7 +56,7 @@ class XmlTag {
XmlTagFlags _flags;
public:
XmlTag(const XmlTag&);
- XmlTag(const std::string& name, XmlTagFlags = NONE);
+ XmlTag(const std::string& name, XmlTagFlags = XmlTagFlags::NONE);
~XmlTag();
const std::string& getName() const { return _name; }
diff --git a/storage/CMakeLists.txt b/storage/CMakeLists.txt
index 418d8dbe430..52423f031e5 100644
--- a/storage/CMakeLists.txt
+++ b/storage/CMakeLists.txt
@@ -47,7 +47,7 @@ vespa_define_module(
TEST_DEPENDS
messagebus_messagebus-test
vdstestlib
- atomic
+ ${VESPA_ATOMIC_LIB}
TESTS
src/tests
diff --git a/storage/src/tests/distributor/distributortest.cpp b/storage/src/tests/distributor/distributortest.cpp
index 2710ed67717..dfc67fd6f5a 100644
--- a/storage/src/tests/distributor/distributortest.cpp
+++ b/storage/src/tests/distributor/distributortest.cpp
@@ -607,15 +607,15 @@ TEST_F(DistributorTest, host_info_reporter_config_is_propagated_to_reporter) {
TEST_F(DistributorTest, replica_counting_mode_is_configured_to_trusted_by_default) {
setupDistributor(Redundancy(2), NodeCount(2), "storage:2 distributor:1");
- EXPECT_EQ(ConfigBuilder::TRUSTED, currentReplicaCountingMode());
+ EXPECT_EQ(ConfigBuilder::MinimumReplicaCountingMode::TRUSTED, currentReplicaCountingMode());
}
TEST_F(DistributorTest, replica_counting_mode_config_is_propagated_to_metric_updater) {
setupDistributor(Redundancy(2), NodeCount(2), "storage:2 distributor:1");
ConfigBuilder builder;
- builder.minimumReplicaCountingMode = ConfigBuilder::ANY;
+ builder.minimumReplicaCountingMode = ConfigBuilder::MinimumReplicaCountingMode::ANY;
configureDistributor(builder);
- EXPECT_EQ(ConfigBuilder::ANY, currentReplicaCountingMode());
+ EXPECT_EQ(ConfigBuilder::MinimumReplicaCountingMode::ANY, currentReplicaCountingMode());
}
TEST_F(DistributorTest, bucket_activation_is_enabled_by_default) {
diff --git a/storage/src/tests/persistence/common/persistenceproviderwrapper.cpp b/storage/src/tests/persistence/common/persistenceproviderwrapper.cpp
index c804354b0ee..05e51993f49 100644
--- a/storage/src/tests/persistence/common/persistenceproviderwrapper.cpp
+++ b/storage/src/tests/persistence/common/persistenceproviderwrapper.cpp
@@ -14,7 +14,7 @@
#define CHECK_ERROR(className, failType) \
{ \
- if (_result.getErrorCode() != spi::Result::NONE && (_failureMask & (failType))) { \
+ if (_result.getErrorCode() != spi::Result::ErrorType::NONE && (_failureMask & (failType))) { \
return className(_result.getErrorCode(), _result.getErrorMessage()); \
} \
}
@@ -41,7 +41,7 @@ includedVersionsToString(spi::IncludedVersions versions)
PersistenceProviderWrapper::PersistenceProviderWrapper(spi::PersistenceProvider& spi)
: _spi(spi),
- _result(spi::Result(spi::Result::NONE, "")),
+ _result(spi::Result(spi::Result::ErrorType::NONE, "")),
_log(),
_failureMask(0)
{ }
diff --git a/storage/src/tests/persistence/common/persistenceproviderwrapper.h b/storage/src/tests/persistence/common/persistenceproviderwrapper.h
index 1f0dc93c44c..511ced02118 100644
--- a/storage/src/tests/persistence/common/persistenceproviderwrapper.h
+++ b/storage/src/tests/persistence/common/persistenceproviderwrapper.h
@@ -65,7 +65,7 @@ public:
_result = result;
}
void clearResult() {
- _result = spi::Result(spi::Result::NONE, "");
+ _result = spi::Result(spi::Result::ErrorType::NONE, "");
}
const spi::Result& getResult() const { return _result; }
/**
diff --git a/storage/src/tests/persistence/mergehandlertest.cpp b/storage/src/tests/persistence/mergehandlertest.cpp
index 8dedf3f18df..0b2baab5652 100644
--- a/storage/src/tests/persistence/mergehandlertest.cpp
+++ b/storage/src/tests/persistence/mergehandlertest.cpp
@@ -631,7 +631,7 @@ TEST_F(MergeHandlerTest, spi_flush_guard) {
MergeHandler handler(providerWrapper, getEnv());
providerWrapper.setResult(
- spi::Result(spi::Result::PERMANENT_ERROR, "who you gonna call?"));
+ spi::Result(spi::Result::ErrorType::PERMANENT_ERROR, "who you gonna call?"));
setUpChain(MIDDLE);
// Fail applying unrevertable remove
@@ -826,7 +826,7 @@ TEST_F(MergeHandlerTest, merge_bucket_spi_failures) {
PersistenceProviderWrapper providerWrapper(getPersistenceProvider());
MergeHandler handler(providerWrapper, getEnv());
providerWrapper.setResult(
- spi::Result(spi::Result::PERMANENT_ERROR, "who you gonna call?"));
+ spi::Result(spi::Result::ErrorType::PERMANENT_ERROR, "who you gonna call?"));
setUpChain(MIDDLE);
ExpectedExceptionSpec exceptions[] = {
@@ -858,7 +858,7 @@ TEST_F(MergeHandlerTest, get_bucket_diff_spi_failures) {
PersistenceProviderWrapper providerWrapper(getPersistenceProvider());
MergeHandler handler(providerWrapper, getEnv());
providerWrapper.setResult(
- spi::Result(spi::Result::PERMANENT_ERROR, "who you gonna call?"));
+ spi::Result(spi::Result::ErrorType::PERMANENT_ERROR, "who you gonna call?"));
setUpChain(MIDDLE);
ExpectedExceptionSpec exceptions[] = {
@@ -893,7 +893,7 @@ TEST_F(MergeHandlerTest, apply_bucket_diff_spi_failures) {
PersistenceProviderWrapper providerWrapper(getPersistenceProvider());
MergeHandler handler(providerWrapper, getEnv());
providerWrapper.setResult(
- spi::Result(spi::Result::PERMANENT_ERROR, "who you gonna call?"));
+ spi::Result(spi::Result::ErrorType::PERMANENT_ERROR, "who you gonna call?"));
setUpChain(MIDDLE);
ExpectedExceptionSpec exceptions[] = {
@@ -960,7 +960,7 @@ TEST_F(MergeHandlerTest, get_bucket_diff_reply_spi_failures) {
PersistenceProviderWrapper providerWrapper(getPersistenceProvider());
MergeHandler handler(providerWrapper, getEnv());
providerWrapper.setResult(
- spi::Result(spi::Result::PERMANENT_ERROR, "who you gonna call?"));
+ spi::Result(spi::Result::ErrorType::PERMANENT_ERROR, "who you gonna call?"));
HandleGetBucketDiffReplyInvoker invoker;
setUpChain(FRONT);
@@ -1051,7 +1051,7 @@ TEST_F(MergeHandlerTest, apply_bucket_diff_reply_spi_failures) {
invoker.setChainPos(pos);
MergeHandler handler(providerWrapper, getEnv());
providerWrapper.setResult(
- spi::Result(spi::Result::PERMANENT_ERROR, "who you gonna call?"));
+ spi::Result(spi::Result::ErrorType::PERMANENT_ERROR, "who you gonna call?"));
ExpectedExceptionSpec exceptions[] = {
{ PersistenceProviderWrapper::FAIL_CREATE_ITERATOR, "create iterator" },
diff --git a/storage/src/tests/persistence/provider_error_wrapper_test.cpp b/storage/src/tests/persistence/provider_error_wrapper_test.cpp
index 36238abb238..2ba5218003b 100644
--- a/storage/src/tests/persistence/provider_error_wrapper_test.cpp
+++ b/storage/src/tests/persistence/provider_error_wrapper_test.cpp
@@ -64,7 +64,7 @@ TEST_F(ProviderErrorWrapperTest, fatal_error_invokes_listener) {
Fixture f(getPersistenceProvider());
auto listener = std::make_shared<MockErrorListener>();
f.errorWrapper.register_error_listener(listener);
- f.providerWrapper.setResult(spi::Result(spi::Result::FATAL_ERROR, "eject! eject!"));
+ f.providerWrapper.setResult(spi::Result(spi::Result::ErrorType::FATAL_ERROR, "eject! eject!"));
EXPECT_FALSE(listener->_seen_fatal_error);
f.perform_spi_operation();
@@ -78,7 +78,7 @@ TEST_F(ProviderErrorWrapperTest, resource_exhaustion_error_invokes_listener) {
Fixture f(getPersistenceProvider());
auto listener = std::make_shared<MockErrorListener>();
f.errorWrapper.register_error_listener(listener);
- f.providerWrapper.setResult(spi::Result(spi::Result::RESOURCE_EXHAUSTED, "out of juice"));
+ f.providerWrapper.setResult(spi::Result(spi::Result::ErrorType::RESOURCE_EXHAUSTED, "out of juice"));
EXPECT_FALSE(listener->_seen_resource_exhaustion_error);
f.perform_spi_operation();
@@ -103,8 +103,8 @@ TEST_F(ProviderErrorWrapperTest, listener_not_invoked_on_regular_errors) {
auto listener = std::make_shared<MockErrorListener>();
f.errorWrapper.register_error_listener(listener);
- EXPECT_NO_FATAL_FAILURE(f.check_no_listener_invoked_for_error(*listener, spi::Result::TRANSIENT_ERROR));
- EXPECT_NO_FATAL_FAILURE(f.check_no_listener_invoked_for_error(*listener, spi::Result::PERMANENT_ERROR));
+ EXPECT_NO_FATAL_FAILURE(f.check_no_listener_invoked_for_error(*listener, spi::Result::ErrorType::TRANSIENT_ERROR));
+ EXPECT_NO_FATAL_FAILURE(f.check_no_listener_invoked_for_error(*listener, spi::Result::ErrorType::PERMANENT_ERROR));
}
TEST_F(ProviderErrorWrapperTest, multiple_listeners_can_be_registered) {
@@ -114,7 +114,7 @@ TEST_F(ProviderErrorWrapperTest, multiple_listeners_can_be_registered) {
f.errorWrapper.register_error_listener(listener1);
f.errorWrapper.register_error_listener(listener2);
- f.providerWrapper.setResult(spi::Result(spi::Result::RESOURCE_EXHAUSTED, "out of juice"));
+ f.providerWrapper.setResult(spi::Result(spi::Result::ErrorType::RESOURCE_EXHAUSTED, "out of juice"));
f.perform_spi_operation();
EXPECT_TRUE(listener1->_seen_resource_exhaustion_error);
diff --git a/storage/src/vespa/storage/distributor/operations/idealstate/idealstateoperation.h b/storage/src/vespa/storage/distributor/operations/idealstate/idealstateoperation.h
index 8d31a228543..08b545057cf 100644
--- a/storage/src/vespa/storage/distributor/operations/idealstate/idealstateoperation.h
+++ b/storage/src/vespa/storage/distributor/operations/idealstate/idealstateoperation.h
@@ -218,7 +218,7 @@ public:
virtual bool shouldBlockThisOperation(uint32_t messageType, uint8_t priority) const;
protected:
- friend class IdealStateManagerTest;
+ friend struct IdealStateManagerTest;
friend class IdealStateManager;
IdealStateManager* _manager;
diff --git a/storage/src/vespa/storage/persistence/bucketprocessor.cpp b/storage/src/vespa/storage/persistence/bucketprocessor.cpp
index c512cdd461a..d4a570ee062 100644
--- a/storage/src/vespa/storage/persistence/bucketprocessor.cpp
+++ b/storage/src/vespa/storage/persistence/bucketprocessor.cpp
@@ -52,7 +52,7 @@ BucketProcessor::iterateAll(spi::PersistenceProvider& provider,
versions,
context));
- if (createIterResult.getErrorCode() != spi::Result::NONE) {
+ if (createIterResult.getErrorCode() != spi::Result::ErrorType::NONE) {
vespalib::asciistream ss;
ss << "Failed to create iterator: "
<< createIterResult.getErrorMessage();
@@ -65,7 +65,7 @@ BucketProcessor::iterateAll(spi::PersistenceProvider& provider,
while (true) {
spi::IterateResult result(
provider.iterate(iteratorId, UINT64_MAX, context));
- if (result.getErrorCode() != spi::Result::NONE) {
+ if (result.getErrorCode() != spi::Result::ErrorType::NONE) {
vespalib::asciistream ss;
ss << "Failed: " << result.getErrorMessage();
throw std::runtime_error(ss.str());
diff --git a/storage/src/vespa/storage/persistence/mergehandler.cpp b/storage/src/vespa/storage/persistence/mergehandler.cpp
index f5d7bc57354..37e1d818bb8 100644
--- a/storage/src/vespa/storage/persistence/mergehandler.cpp
+++ b/storage/src/vespa/storage/persistence/mergehandler.cpp
@@ -169,7 +169,7 @@ MergeHandler::populateMetaData(
spi::ALL_VERSIONS,
context));
- if (createIterResult.getErrorCode() != spi::Result::NONE) {
+ if (createIterResult.getErrorCode() != spi::Result::ErrorType::NONE) {
std::ostringstream ss;
ss << "Failed to create iterator for "
<< bucket
@@ -183,7 +183,7 @@ MergeHandler::populateMetaData(
while (true) {
spi::IterateResult result(
_spi.iterate(iteratorId, UINT64_MAX, context));
- if (result.getErrorCode() != spi::Result::NONE) {
+ if (result.getErrorCode() != spi::Result::ErrorType::NONE) {
std::ostringstream ss;
ss << "Failed to iterate for "
<< bucket
@@ -226,7 +226,7 @@ MergeHandler::buildBucketInfoList(
if (entry.exist()) {
spi::BucketInfoResult infoResult(_spi.getBucketInfo(bucket));
- if (infoResult.getErrorCode() != spi::Result::NONE) {
+ if (infoResult.getErrorCode() != spi::Result::ErrorType::NONE) {
std::ostringstream ss;
ss << "Failed to get bucket info for "
<< bucket << ": "
@@ -432,7 +432,7 @@ MergeHandler::fetchLocalData(
spi::NEWEST_DOCUMENT_OR_REMOVE,
context));
- if (createIterResult.getErrorCode() != spi::Result::NONE) {
+ if (createIterResult.getErrorCode() != spi::Result::ErrorType::NONE) {
std::ostringstream ss;
ss << "Failed to create iterator for "
<< bucket.toString()
@@ -451,7 +451,7 @@ MergeHandler::fetchLocalData(
while (true) {
spi::IterateResult result(
_spi.iterate(iteratorId, remainingSize, context));
- if (result.getErrorCode() != spi::Result::NONE) {
+ if (result.getErrorCode() != spi::Result::ErrorType::NONE) {
std::ostringstream ss;
ss << "Failed to iterate for "
<< bucket.toString()
@@ -712,7 +712,7 @@ MergeHandler::applyDiffLocally(
flushGuard.flush();
spi::BucketInfoResult infoResult(_spi.getBucketInfo(bucket));
- if (infoResult.getErrorCode() != spi::Result::NONE) {
+ if (infoResult.getErrorCode() != spi::Result::ErrorType::NONE) {
LOG(warning, "Failed to get bucket info for %s: %s",
bucket.toString().c_str(),
infoResult.getErrorMessage().c_str());
diff --git a/storage/src/vespa/storage/persistence/persistenceutil.cpp b/storage/src/vespa/storage/persistence/persistenceutil.cpp
index 4bee5df4d04..9cea0e6c5d7 100644
--- a/storage/src/vespa/storage/persistence/persistenceutil.cpp
+++ b/storage/src/vespa/storage/persistence/persistenceutil.cpp
@@ -182,16 +182,16 @@ uint32_t
PersistenceUtil::convertErrorCode(const spi::Result& response)
{
switch (response.getErrorCode()) {
- case spi::Result::NONE:
+ case spi::Result::ErrorType::NONE:
return 0;
- case spi::Result::TIMESTAMP_EXISTS:
+ case spi::Result::ErrorType::TIMESTAMP_EXISTS:
return api::ReturnCode::TIMESTAMP_EXIST;
- case spi::Result::TRANSIENT_ERROR:
- case spi::Result::FATAL_ERROR:
+ case spi::Result::ErrorType::TRANSIENT_ERROR:
+ case spi::Result::ErrorType::FATAL_ERROR:
return mbus::ErrorCode::APP_TRANSIENT_ERROR;
- case spi::Result::RESOURCE_EXHAUSTED:
+ case spi::Result::ErrorType::RESOURCE_EXHAUSTED:
return api::ReturnCode::NO_SPACE;
- case spi::Result::PERMANENT_ERROR:
+ case spi::Result::ErrorType::PERMANENT_ERROR:
default:
return mbus::ErrorCode::APP_FATAL_ERROR;
}
diff --git a/storage/src/vespa/storage/persistence/processallhandler.cpp b/storage/src/vespa/storage/persistence/processallhandler.cpp
index 51ef67dc7ac..7cb373279a6 100644
--- a/storage/src/vespa/storage/persistence/processallhandler.cpp
+++ b/storage/src/vespa/storage/persistence/processallhandler.cpp
@@ -39,7 +39,7 @@ public:
*entry.getDocumentId(),
_context);
- if (removeResult.getErrorCode() != spi::Result::NONE) {
+ if (removeResult.getErrorCode() != spi::Result::ErrorType::NONE) {
std::ostringstream ss;
ss << "Failed to do remove for removelocation: "
<< removeResult.getErrorMessage();
diff --git a/storage/src/vespa/storage/persistence/provider_error_wrapper.cpp b/storage/src/vespa/storage/persistence/provider_error_wrapper.cpp
index 056561e8e21..fe947bff4d5 100644
--- a/storage/src/vespa/storage/persistence/provider_error_wrapper.cpp
+++ b/storage/src/vespa/storage/persistence/provider_error_wrapper.cpp
@@ -9,9 +9,9 @@ template <typename ResultType>
ResultType
ProviderErrorWrapper::checkResult(ResultType&& result) const
{
- if (result.getErrorCode() == spi::Result::FATAL_ERROR) {
+ if (result.getErrorCode() == spi::Result::ErrorType::FATAL_ERROR) {
trigger_shutdown_listeners(result.getErrorMessage());
- } else if (result.getErrorCode() == spi::Result::RESOURCE_EXHAUSTED) {
+ } else if (result.getErrorCode() == spi::Result::ErrorType::RESOURCE_EXHAUSTED) {
trigger_resource_exhaustion_listeners(result.getErrorMessage());
}
return std::forward<ResultType>(result);
diff --git a/storageapi/src/vespa/storageapi/messageapi/maintenancecommand.h b/storageapi/src/vespa/storageapi/messageapi/maintenancecommand.h
index 029365973c5..1b149780c9d 100644
--- a/storageapi/src/vespa/storageapi/messageapi/maintenancecommand.h
+++ b/storageapi/src/vespa/storageapi/messageapi/maintenancecommand.h
@@ -12,8 +12,10 @@ public:
MaintenanceCommand(const MessageType& type, const document::Bucket &bucket)
: BucketInfoCommand(type, bucket)
{}
+ MaintenanceCommand(const MaintenanceCommand &) = default;
MaintenanceCommand(MaintenanceCommand &&) = default;
- MaintenanceCommand & operator = (MaintenanceCommand &&) = default;
+ MaintenanceCommand & operator = (const MaintenanceCommand &) = delete;
+ MaintenanceCommand & operator = (MaintenanceCommand &&) = delete;
~MaintenanceCommand();
const vespalib::string& getReason() const { return _reason; };
diff --git a/storageserver/src/apps/storaged/storage.cpp b/storageserver/src/apps/storaged/storage.cpp
index 0261d8e587e..b1eafcbad93 100644
--- a/storageserver/src/apps/storaged/storage.cpp
+++ b/storageserver/src/apps/storaged/storage.cpp
@@ -37,8 +37,8 @@ Process::UP createProcess(vespalib::stringref configId) {
if (serverConfig->isDistributor) {
return Process::UP(new DistributorProcess(configId));
} else switch (serverConfig->persistenceProvider.type) {
- case vespa::config::content::core::StorServerConfig::PersistenceProvider::STORAGE:
- case vespa::config::content::core::StorServerConfig::PersistenceProvider::DUMMY:
+ case vespa::config::content::core::StorServerConfig::PersistenceProvider::Type::STORAGE:
+ case vespa::config::content::core::StorServerConfig::PersistenceProvider::Type::DUMMY:
return Process::UP(new DummyServiceLayerProcess(configId));
default:
throw vespalib::IllegalStateException("Unknown persistence provider.", VESPA_STRLOC);
diff --git a/storageserver/src/vespa/storageserver/app/distributorprocess.cpp b/storageserver/src/vespa/storageserver/app/distributorprocess.cpp
index 6ef391fa56a..57fdcdeb248 100644
--- a/storageserver/src/vespa/storageserver/app/distributorprocess.cpp
+++ b/storageserver/src/vespa/storageserver/app/distributorprocess.cpp
@@ -32,7 +32,7 @@ DistributorProcess::setupConfig(uint64_t subscribeTimeout)
std::unique_ptr<vespa::config::content::core::StorServerConfig> config =
config::ConfigGetter<vespa::config::content::core::StorServerConfig>::getConfig(_configUri.getConfigId(), _configUri.getContext(), subscribeTimeout);
if (config->persistenceProvider.type
- != vespa::config::content::core::StorServerConfig::PersistenceProvider::STORAGE)
+ != vespa::config::content::core::StorServerConfig::PersistenceProvider::Type::STORAGE)
{
_activeFlag = DistributorNode::NEED_ACTIVE_BUCKET_STATES_SET;
}
diff --git a/streamingvisitors/src/vespa/searchvisitor/rankmanager.cpp b/streamingvisitors/src/vespa/searchvisitor/rankmanager.cpp
index 4ed4e9462d3..ba94caa8965 100644
--- a/streamingvisitors/src/vespa/searchvisitor/rankmanager.cpp
+++ b/streamingvisitors/src/vespa/searchvisitor/rankmanager.cpp
@@ -43,7 +43,7 @@ RankManager::Snapshot::detectFields(const VsmfieldsHandle & fields)
{
for (uint32_t i = 0; i < fields->fieldspec.size(); ++i) {
const VsmfieldsConfig::Fieldspec & fs = fields->fieldspec[i];
- bool isAttribute = (fs.fieldtype == VsmfieldsConfig::Fieldspec::ATTRIBUTE);
+ bool isAttribute = (fs.fieldtype == VsmfieldsConfig::Fieldspec::Fieldtype::ATTRIBUTE);
LOG(debug, "Adding field of type '%s' and name '%s' with id '%u' the index environment.",
isAttribute ? "ATTRIBUTE" : "INDEX", fs.name.c_str(), i);
// This id must match the vsm specific field id
diff --git a/vdslib/src/main/java/com/yahoo/vdslib/distribution/Group.java b/vdslib/src/main/java/com/yahoo/vdslib/distribution/Group.java
index e3caaeb455c..680021893f7 100644
--- a/vdslib/src/main/java/com/yahoo/vdslib/distribution/Group.java
+++ b/vdslib/src/main/java/com/yahoo/vdslib/distribution/Group.java
@@ -237,7 +237,9 @@ public class Group implements Comparable<Group> {
}
this.distributionSpec = distributionSpec;
// Create the pre calculated results
- if (maxRedundancy <= 0 || maxRedundancy > 255) throw new IllegalArgumentException("The max redundancy must be a positive number in the range 1-255.");
+ if (maxRedundancy <= 0 || maxRedundancy > 255) {
+ throw new IllegalArgumentException("The max redundancy (" + maxRedundancy + ") must be a positive number in the range 1-255.");
+ }
int asterixCount = distributionSpec.length - firstAsterix;
int[][] preCalculations = new int[maxRedundancy + 1][];
for (int i=1; i<=maxRedundancy; ++i) {
diff --git a/vdslib/src/test/java/com/yahoo/vdslib/distribution/GroupTestCase.java b/vdslib/src/test/java/com/yahoo/vdslib/distribution/GroupTestCase.java
index c71216b192d..e81d77d5972 100644
--- a/vdslib/src/test/java/com/yahoo/vdslib/distribution/GroupTestCase.java
+++ b/vdslib/src/test/java/com/yahoo/vdslib/distribution/GroupTestCase.java
@@ -48,7 +48,7 @@ public class GroupTestCase {
assertDistribution("*|*|*", 5, "2,2,1");
assertDistribution("*|*|*|*", 5, "2,1,1,1");
- assertDistributionFailure("2|*", 0, "The max redundancy must be a positive number in the range 1-255.");
+ assertDistributionFailure("2|*", 0, "The max redundancy (0) must be a positive number in the range 1-255.");
assertDistributionFailure("*|2", 3, "Illegal distribution spec \"*|2\". Asterix specification must be tailing the specification.");
assertDistributionFailure("*|2|*", 3, "Illegal distribution spec \"*|2|*\". Asterix specification must be tailing the specification.");
assertDistributionFailure("0|*", 3, "Illegal distribution spec \"0|*\". Copy counts must be in the range 1-255.");
diff --git a/vespa-hadoop/pom.xml b/vespa-hadoop/pom.xml
index 1ee29c36b7d..ff21404a2c7 100644
--- a/vespa-hadoop/pom.xml
+++ b/vespa-hadoop/pom.xml
@@ -165,7 +165,16 @@
<shadedPattern>shaded.vespa</shadedPattern>
</relocation>
</relocations>
-
+ <filters>
+ <filter>
+ <artifact>*:*</artifact>
+ <excludes>
+ <exclude>META-INF/*.SF</exclude>
+ <exclude>META-INF/*.DSA</exclude>
+ <exclude>META-INF/*.RSA</exclude>
+ </excludes>
+ </filter>
+ </filters>
</configuration>
</execution>
</executions>
diff --git a/vespa-http-client/pom.xml b/vespa-http-client/pom.xml
index 18dd34ee7be..96aa6defbe6 100644
--- a/vespa-http-client/pom.xml
+++ b/vespa-http-client/pom.xml
@@ -65,6 +65,22 @@
<artifactId>airline</artifactId>
<version>0.6</version>
</dependency>
+ <dependency>
+ <!-- Needed for Vespa TLS configuration. Standard jar artifact -->
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>http-utils</artifactId>
+ <version>${project.version}</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <!-- Needed for Vespa TLS configuration. -->
+ <!-- This artifact is packaged as an OSGi bundle - make sure to manually include or exclude transitive dependencies as necessary -->
+ <!-- Note: includes BouncyCastle to compile scope transitively. -->
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>security-utils</artifactId>
+ <version>${project.version}</version>
+ <scope>compile</scope>
+ </dependency>
<!-- Test dependencies -->
<dependency>
@@ -140,6 +156,17 @@
</goals>
<configuration>
<outputFile>target/${project.artifactId}-jar-with-dependencies.jar</outputFile>
+ <filters>
+ <filter>
+ <!-- Don't include signature files in uber jar (most likely from bouncycastle). -->
+ <artifact>*:*</artifact>
+ <excludes>
+ <exclude>META-INF/*.SF</exclude>
+ <exclude>META-INF/*.DSA</exclude>
+ <exclude>META-INF/*.RSA</exclude>
+ </excludes>
+ </filter>
+ </filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.yahoo.vespa.http.client.runner.Runner</mainClass>
diff --git a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/config/ConnectionParams.java b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/config/ConnectionParams.java
index f503190864b..ec9471e68ed 100644
--- a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/config/ConnectionParams.java
+++ b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/config/ConnectionParams.java
@@ -8,6 +8,7 @@ import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
+import java.time.Duration;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -43,6 +44,17 @@ public final class ConnectionParams {
private int traceLevel = 0;
private int traceEveryXOperation = 0;
private boolean printTraceToStdErr = true;
+ private boolean useTlsConfigFromEnvironment = false;
+ private Duration connectionTimeToLive = Duration.ofSeconds(15);
+
+ /**
+ * Use TLS configuration through the standard Vespa environment variables.
+ * Setting this to 'true' will override any other TLS/HTTPS related configuration.
+ */
+ public Builder setUseTlsConfigFromEnvironment(boolean useTlsConfigFromEnvironment) {
+ this.useTlsConfigFromEnvironment = useTlsConfigFromEnvironment;
+ return this;
+ }
/**
* Sets the SSLContext for the connection to the gateway when SSL is enabled for Endpoint.
@@ -217,6 +229,13 @@ public final class ConnectionParams {
return this;
}
+ /**
+ * Set the maximum time to live for persistent connections
+ */
+ public Builder setConnectionTimeToLive(Duration connectionTimeToLive) {
+ this.connectionTimeToLive = connectionTimeToLive;
+ return this;
+ }
public ConnectionParams build() {
return new ConnectionParams(
@@ -233,7 +252,9 @@ public final class ConnectionParams {
dryRun,
traceLevel,
traceEveryXOperation,
- printTraceToStdErr);
+ printTraceToStdErr,
+ useTlsConfigFromEnvironment,
+ connectionTimeToLive);
}
public int getNumPersistentConnectionsPerEndpoint() {
@@ -273,6 +294,14 @@ public final class ConnectionParams {
public HostnameVerifier getHostnameVerifier() {
return hostnameVerifier;
}
+
+ public boolean useTlsConfigFromEnvironment() {
+ return useTlsConfigFromEnvironment;
+ }
+
+ public Duration getConnectionTimeToLive() {
+ return connectionTimeToLive;
+ }
}
private final SSLContext sslContext;
private final HostnameVerifier hostnameVerifier;
@@ -288,6 +317,8 @@ public final class ConnectionParams {
private final int traceLevel;
private final int traceEveryXOperation;
private final boolean printTraceToStdErr;
+ private final boolean useTlsConfigFromEnvironment;
+ private final Duration connectionTimeToLive;
private ConnectionParams(
SSLContext sslContext,
@@ -303,9 +334,13 @@ public final class ConnectionParams {
boolean dryRun,
int traceLevel,
int traceEveryXOperation,
- boolean printTraceToStdErr) {
+ boolean printTraceToStdErr,
+ boolean useTlsConfigFromEnvironment,
+ Duration connectionTimeToLive) {
this.sslContext = sslContext;
this.hostnameVerifier = hostnameVerifier;
+ this.useTlsConfigFromEnvironment = useTlsConfigFromEnvironment;
+ this.connectionTimeToLive = connectionTimeToLive;
this.headers.putAll(headers);
this.headerProviders.putAll(headerProviders);
this.numPersistentConnectionsPerEndpoint = numPersistentConnectionsPerEndpoint;
@@ -378,6 +413,14 @@ public final class ConnectionParams {
return printTraceToStdErr;
}
+ public boolean useTlsConfigFromEnvironment() {
+ return useTlsConfigFromEnvironment;
+ }
+
+ public Duration getConnectionTimeToLive() {
+ return connectionTimeToLive;
+ }
+
/**
* A header provider that provides a header value. {@link #getHeaderValue()} is called each time a new HTTP request
* is constructed by {@link com.yahoo.vespa.http.client.FeedClient}.
diff --git a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/ApacheGatewayConnection.java b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/ApacheGatewayConnection.java
index 5289a7a562a..00f52d6337f 100644
--- a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/ApacheGatewayConnection.java
+++ b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/ApacheGatewayConnection.java
@@ -17,14 +17,8 @@ import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
-import org.apache.http.config.Registry;
-import org.apache.http.config.RegistryBuilder;
-import org.apache.http.conn.socket.ConnectionSocketFactory;
-import org.apache.http.conn.socket.PlainConnectionSocketFactory;
-import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.HttpClientBuilder;
-import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import java.io.ByteArrayInputStream;
@@ -400,25 +394,24 @@ class ApacheGatewayConnection implements GatewayConnection {
}
public HttpClient createClient() {
- HttpClientBuilder clientBuilder = HttpClientBuilder.create();
- if (useSsl && connectionParams.getSslContext() != null) {
- Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
- .register("https", new SSLConnectionSocketFactory(
- connectionParams.getSslContext(), connectionParams.getHostnameVerifier()))
- .register("http", PlainConnectionSocketFactory.INSTANCE)
- .build();
- PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
- clientBuilder.setConnectionManager(connMgr);
-
+ HttpClientBuilder clientBuilder;
+ if (connectionParams.useTlsConfigFromEnvironment()) {
+ clientBuilder = VespaTlsAwareClientBuilder.createHttpClientBuilder();
+ } else {
+ clientBuilder = HttpClientBuilder.create();
+ if (useSsl && connectionParams.getSslContext() != null) {
+ clientBuilder.setSslcontext(connectionParams.getSslContext());
+ clientBuilder.setSSLHostnameVerifier(connectionParams.getHostnameVerifier());
+ }
}
- clientBuilder.setUserAgent(String.format("vespa-http-client (%s)", Vtag.currentVersion));
- clientBuilder.setDefaultHeaders(Collections.singletonList(new BasicHeader(Headers.CLIENT_VERSION, Vtag.currentVersion)));
clientBuilder.setMaxConnPerRoute(1);
clientBuilder.setMaxConnTotal(1);
+ clientBuilder.setConnectionTimeToLive(connectionParams.getConnectionTimeToLive().getSeconds(), TimeUnit.SECONDS);
+ clientBuilder.setUserAgent(String.format("vespa-http-client (%s)", Vtag.currentVersion));
+ clientBuilder.setDefaultHeaders(Collections.singletonList(new BasicHeader(Headers.CLIENT_VERSION, Vtag.currentVersion)));
clientBuilder.disableContentCompression();
// Try to disable the disabling to see if system tests become stable again.
// clientBuilder.disableAutomaticRetries();
- clientBuilder.setConnectionTimeToLive(15, TimeUnit.SECONDS);
{
RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
requestConfigBuilder.setSocketTimeout(0);
diff --git a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/VespaTlsAwareClientBuilder.java b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/VespaTlsAwareClientBuilder.java
new file mode 100644
index 00000000000..be67e11963e
--- /dev/null
+++ b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/VespaTlsAwareClientBuilder.java
@@ -0,0 +1,26 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.http.client.core.communication;
+
+import org.apache.http.impl.client.HttpClientBuilder;
+
+/**
+ * A static factory for VespaHttpClientBuilder.
+ * The main purpose of this class is to avoid references to classes not compiled with JDK8.
+ *
+ * @author bjorncs
+ */
+// TODO Remove use of reflection once vespa-http-client only targets JDK11
+// The VespaTlsAwareClientBuilder class refers to classes in security-utils / http-utils that targets JDK11+.
+class VespaTlsAwareClientBuilder {
+
+ private VespaTlsAwareClientBuilder() {}
+
+ static HttpClientBuilder createHttpClientBuilder() {
+ try {
+ Class<?> builderClass = Class.forName("ai.vespa.util.http.VespaHttpClientBuilder");
+ return (HttpClientBuilder) builderClass.getMethod("create").invoke(null);
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/Vtag.java b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/Vtag.java
index f21a86dfc1b..f44a30208e7 100644
--- a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/Vtag.java
+++ b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/Vtag.java
@@ -3,5 +3,5 @@
package com.yahoo.vespa.http.client.core.communication;
class Vtag {
- static final String currentVersion = "7.0.0";
+ static final String currentVersion = "7.1.0";
}
diff --git a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/runner/CommandLineArguments.java b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/runner/CommandLineArguments.java
index 4e2c8f1509e..8a2a1652b4a 100644
--- a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/runner/CommandLineArguments.java
+++ b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/runner/CommandLineArguments.java
@@ -20,6 +20,7 @@ import org.apache.http.message.BasicLineParser;
import javax.inject.Inject;
import java.net.MalformedURLException;
import java.net.URL;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -110,6 +111,8 @@ public class CommandLineArguments {
}
}
+ // TODO Don't duplicate default values from ConnectionParams.Builder. Some defaults are already inconsistent.
+
@Inject
private HelpOption helpOption;
@@ -209,6 +212,14 @@ public class CommandLineArguments {
description = "Add http header to every request. Header must have the format '<Name>: <Value>'. Use this parameter multiple times for multiple headers")
private List<String> headers = new ArrayList<>();
+ @Option(name = {"--vespaTls"},
+ description = "BETA! Use Vespa TLS configuration from environment if available. Other HTTPS/TLS configuration will be ignored if this is set.")
+ private boolean useTlsConfigFromEnvironment = false;
+
+ @Option(name = {"--connectionTimeToLive"},
+ description = "Maximum time to live for persistent connections. Specified as integer, in seconds.")
+ private long connectionTimeToLive = 15;
+
private final List<Header> parsedHeaders = new ArrayList<>();
int getWhenVerboseEnabledPrintMessageForEveryXDocuments() {
@@ -252,6 +263,8 @@ public class CommandLineArguments {
.setTraceEveryXOperation(traceEveryXOperation)
.setPrintTraceToStdErr(traceArg > 0)
.setNumPersistentConnectionsPerEndpoint(numPersistentConnectionsPerEndpoint)
+ .setUseTlsConfigFromEnvironment(useTlsConfigFromEnvironment)
+ .setConnectionTimeToLive(Duration.ofSeconds(connectionTimeToLive))
.build()
)
// Enable dynamic throttling.
diff --git a/vespabase/src/vespa-configserver.service.in b/vespabase/src/vespa-configserver.service.in
index b5bf28c1d73..7ee170bbdca 100644
--- a/vespabase/src/vespa-configserver.service.in
+++ b/vespabase/src/vespa-configserver.service.in
@@ -1,6 +1,7 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
[Unit]
Description=Vertical Search Platform Config Server
+After=network.target
[Service]
Type=forking
diff --git a/vespabase/src/vespa.service.in b/vespabase/src/vespa.service.in
index dec88ecb7cc..3c32f2c2cab 100644
--- a/vespabase/src/vespa.service.in
+++ b/vespabase/src/vespa.service.in
@@ -1,6 +1,7 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
[Unit]
Description=Vertical Search Platform
+After=network.target
[Service]
Type=forking
diff --git a/vespaclient-container-plugin/pom.xml b/vespaclient-container-plugin/pom.xml
index 959fb687692..53e708601d7 100644
--- a/vespaclient-container-plugin/pom.xml
+++ b/vespaclient-container-plugin/pom.xml
@@ -55,6 +55,10 @@
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</exclusion>
+ <exclusion>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpcore</artifactId>
+ </exclusion>
</exclusions>
</dependency>
<dependency>
diff --git a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandler.java b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandler.java
index a6fdcb10a00..848fe4b5726 100644
--- a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandler.java
+++ b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandler.java
@@ -101,6 +101,10 @@ public interface OperationHandler {
default Optional<String> get(RestUri restUri, Optional<String> fieldSet) throws RestApiException {
return get(restUri);
}
+
+ default Optional<String> get(RestUri restUri, Optional<String> fieldSet, Optional<String> cluster) throws RestApiException {
+ return get(restUri, fieldSet);
+ }
/** Called just before this is disposed of */
default void shutdown() {}
diff --git a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandlerImpl.java b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandlerImpl.java
index 4ca43f5fde2..333f1d2f8c9 100644
--- a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandlerImpl.java
+++ b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandlerImpl.java
@@ -279,9 +279,13 @@ public class OperationHandlerImpl implements OperationHandler {
}
@Override
- public Optional<String> get(RestUri restUri, Optional<String> fieldSet) throws RestApiException {
+ public Optional<String> get(RestUri restUri, Optional<String> fieldSet, Optional<String> cluster) throws RestApiException {
SyncSession syncSession = syncSessions.alloc();
- setRoute(syncSession, Optional.empty());
+ // Explicit unary used instead of map() due to unhandled exceptions, blargh.
+ Optional<String> route = cluster.isPresent()
+ ? Optional.of(clusterDefToRoute(resolveClusterDef(cluster, clusterEnumerator.enumerateClusters())))
+ : Optional.empty();
+ setRoute(syncSession, route);
try {
DocumentId id = new DocumentId(restUri.generateFullId());
final Document document = syncSession.get(id, fieldSet.orElse(restUri.getDocumentType() + ":[document]"), DocumentProtocol.Priority.NORMAL_1);
@@ -301,6 +305,11 @@ public class OperationHandlerImpl implements OperationHandler {
}
@Override
+ public Optional<String> get(RestUri restUri, Optional<String> fieldSet) throws RestApiException {
+ return get(restUri, fieldSet, Optional.empty());
+ }
+
+ @Override
public Optional<String> get(RestUri restUri) throws RestApiException {
return get(restUri, Optional.empty());
}
diff --git a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/RestApi.java b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/RestApi.java
index d2fd8d92495..7e572dae941 100644
--- a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/RestApi.java
+++ b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/RestApi.java
@@ -262,8 +262,9 @@ public class RestApi extends LoggingRequestHandler {
}
private HttpResponse handleGet(RestUri restUri, HttpRequest request) throws RestApiException {
- final Optional<String> fieldSet = requestProperty("fieldSet", request);
- final Optional<String> getDocument = operationHandler.get(restUri, fieldSet);
+ final Optional<String> fieldSet = requestProperty(FIELD_SET, request);
+ final Optional<String> cluster = requestProperty(CLUSTER, request);
+ final Optional<String> getDocument = operationHandler.get(restUri, fieldSet, cluster);
final ObjectNode resultNode = mapper.createObjectNode();
if (getDocument.isPresent()) {
final JsonNode parseNode;
diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/OperationHandlerImplTest.java b/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/OperationHandlerImplTest.java
index 374c91c13d3..e98e6babf84 100644
--- a/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/OperationHandlerImplTest.java
+++ b/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/OperationHandlerImplTest.java
@@ -107,7 +107,7 @@ public class OperationHandlerImplTest {
VisitorControlHandler.CompletionCode completionCode = VisitorControlHandler.CompletionCode.SUCCESS;
int bucketsVisited = 0;
Map<String, String> bucketSpaces = new HashMap<>();
- SyncSession mockSyncSession = mock(MessageBusSyncSession.class); // MBus session needed to avoid setRoute throwing.
+ MessageBusSyncSession mockSyncSession = mock(MessageBusSyncSession.class); // MBus session needed to avoid setRoute throwing.
OperationHandlerImplFixture() {
bucketSpaces.put("foo", "global");
@@ -317,6 +317,25 @@ public class OperationHandlerImplTest {
}
@Test
+ public void get_route_has_default_value_if_no_cluster_is_provided() throws Exception {
+ OperationHandlerImplFixture fixture = new OperationHandlerImplFixture();
+ OperationHandlerImpl handler = fixture.createHandler();
+ handler.get(dummyGetUri(), Optional.empty(), Optional.empty());
+
+ // TODO shouldn't this be default-get?
+ verify(fixture.mockSyncSession).setRoute(eq("default"));
+ }
+
+ @Test
+ public void provided_get_cluster_is_propagated_as_route_to_sync_session() throws Exception {
+ OperationHandlerImplFixture fixture = new OperationHandlerImplFixture();
+ OperationHandlerImpl handler = fixture.createHandler();
+ handler.get(dummyGetUri(), Optional.empty(), Optional.of("foo"));
+
+ verify(fixture.mockSyncSession).setRoute(eq("[Storage:cluster=foo;clusterconfigid=configId]"));
+ }
+
+ @Test
public void api_root_visit_uri_requires_cluster_set() throws Exception {
OperationHandlerImplFixture fixture = new OperationHandlerImplFixture();
OperationHandlerImpl handler = fixture.createHandler();
diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/MockedOperationHandler.java b/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/MockedOperationHandler.java
index 895c34436ce..0b3ee6d0792 100644
--- a/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/MockedOperationHandler.java
+++ b/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/MockedOperationHandler.java
@@ -58,11 +58,20 @@ public class MockedOperationHandler implements OperationHandler {
}
@Override
- public Optional<String> get(RestUri restUri, Optional<String> fieldSet) throws RestApiException {
+ public Optional<String> get(RestUri restUri, Optional<String> fieldSet, Optional<String> cluster) throws RestApiException {
log.append("GET: " + restUri.generateFullId());
// This is _not_ an elegant way to return data back to the test.
// An alternative is removing this entire class in favor of explicit mock expectations.
- return fieldSet.map(fs -> String.format("{\"fields\": {\"fieldset\": \"%s\"}}", fs));
+ if (!fieldSet.isPresent() && !cluster.isPresent()) {
+ return Optional.empty();
+ }
+ return Optional.of(String.format("{\"fields\": {\"fieldset\": \"%s\",\"cluster\":\"%s\"}}",
+ fieldSet.orElse(""), cluster.orElse("")));
+ }
+
+ @Override
+ public Optional<String> get(RestUri restUri, Optional<String> fieldSet) throws RestApiException {
+ return get(restUri, fieldSet, Optional.empty());
}
@Override
diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/RestApiTest.java b/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/RestApiTest.java
index 773d45b6b18..aeddc762586 100644
--- a/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/RestApiTest.java
+++ b/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/RestApiTest.java
@@ -288,12 +288,19 @@ public class RestApiTest {
}
@Test
- public void get_fieldset_parameter_is_propagated() throws IOException {
+ public void get_fieldset_parameter_is_propagated() {
Request request = new Request(String.format("http://localhost:%s/document/v1/namespace/document-type/docid/bar?fieldSet=foo,baz", getFirstListenPort()));
HttpGet get = new HttpGet(request.getUri());
assertHttp200ResponseContains(doRest(get), "\"fieldset\":\"foo,baz\"");
}
+ @Test
+ public void get_cluster_parameter_is_propagated() {
+ Request request = new Request(String.format("http://localhost:%s/document/v1/namespace/document-type/docid/bar?cluster=my_cool_cluster", getFirstListenPort()));
+ HttpGet get = new HttpGet(request.getUri());
+ assertHttp200ResponseContains(doRest(get), "\"cluster\":\"my_cool_cluster\"");
+ }
+
String visit_test_uri = "/document/v1/namespace/document-type/docid/?continuation=abc";
String visit_response_part1 = "\"documents\":[List of json docs, cont token abc, doc selection: '']";
String visit_response_part2 = "\"continuation\":\"token\"";
diff --git a/vespaclient/CMakeLists.txt b/vespaclient/CMakeLists.txt
index 8be8751f2c9..b0f4b2190f9 100644
--- a/vespaclient/CMakeLists.txt
+++ b/vespaclient/CMakeLists.txt
@@ -19,9 +19,13 @@ vespa_define_module(
src/vespa/vespaclient/vesparoute
)
-vespa_install_script(src/perl/bin/SetNodeState.pl vespa-set-node-state bin)
-vespa_install_script(src/perl/bin/GetNodeState.pl vespa-get-node-state bin)
-vespa_install_script(src/perl/bin/GetClusterState.pl vespa-get-cluster-state bin)
+vespa_install_script(src/sh/vespa-set-node-state vespa-set-node-state bin)
+vespa_install_script(src/sh/vespa-get-node-state vespa-get-node-state bin)
+vespa_install_script(src/sh/vespa-get-cluster-state vespa-get-cluster-state bin)
+
+vespa_install_script(src/perl/bin/SetNodeState.pl libexec/vespa)
+vespa_install_script(src/perl/bin/GetNodeState.pl libexec/vespa)
+vespa_install_script(src/perl/bin/GetClusterState.pl libexec/vespa)
install(DIRECTORY src/perl/lib/Yahoo/Vespa/
DESTINATION lib/perl5/site_perl/Yahoo/Vespa
diff --git a/vespaclient/src/perl/lib/Yahoo/Vespa/Http.pm b/vespaclient/src/perl/lib/Yahoo/Vespa/Http.pm
index 3a32aa64d22..f48d25906b4 100644
--- a/vespaclient/src/perl/lib/Yahoo/Vespa/Http.pm
+++ b/vespaclient/src/perl/lib/Yahoo/Vespa/Http.pm
@@ -97,6 +97,21 @@ sub setHttpExecutor { # (Function)
sub initialize { # ()
%LEGAL_TYPES = map { $_ => 1 } ( 'GET', 'POST', 'PUT', 'DELETE');
$BROWSER = LWP::UserAgent->new;
+ my $tls_enabled = $ENV{'VESPA_TLS_ENABLED'};
+ if (defined $tls_enabled and $tls_enabled eq '1') {
+ $BROWSER->ssl_opts( SSL_version => 'TLSv12');
+ $BROWSER->ssl_opts( SSL_verifycn_scheme => 'none');
+ $BROWSER->ssl_opts( SSL_cipher_list => 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-CHACHA20-POLY1305:TLS13-AES-128-GCM-SHA256:TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256' );
+ }
+ if (defined $ENV{'VESPA_TLS_CA_CERT'}) {
+ $BROWSER->ssl_opts( SSL_ca_file => $ENV{'VESPA_TLS_CA_CERT'} );
+ }
+ if (defined $ENV{'VESPA_TLS_CERT'}) {
+ $BROWSER->ssl_opts( SSL_cert_file => $ENV{'VESPA_TLS_CERT'} );
+ }
+ if (defined $ENV{'VESPA_TLS_PRIVATE_KEY'}) {
+ $BROWSER->ssl_opts( SSL_key_file => $ENV{'VESPA_TLS_PRIVATE_KEY'} );
+ }
$BROWSER->agent('Vespa-perl-script');
$EXECUTE = \&execute;
}
@@ -146,7 +161,8 @@ sub execute { # (Type, Host, Port, Path, Params, Content, Headers) -> Response
}
sub buildUri { # (Host, Port, Path) -> UriString
my ($host, $port, $path) = @_;
- my $uri = "http:";
+ my $tls_enabled = $ENV{'VESPA_TLS_ENABLED'};
+ my $uri = (defined $tls_enabled and $tls_enabled eq '1') ? "https:" : "http:";
if (defined $host) {
$uri .= '//' . $host;
if (defined $port) {
diff --git a/vespaclient/src/sh/vespa-get-cluster-state b/vespaclient/src/sh/vespa-get-cluster-state
new file mode 100755
index 00000000000..d1b0482cf3a
--- /dev/null
+++ b/vespaclient/src/sh/vespa-get-cluster-state
@@ -0,0 +1,75 @@
+#!/usr/bin/env bash
+# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+# BEGIN environment bootstrap section
+# Do not edit between here and END as this section should stay identical in all scripts
+
+findpath () {
+ myname=${0}
+ mypath=${myname%/*}
+ myname=${myname##*/}
+ if [ "$mypath" ] && [ -d "$mypath" ]; then
+ return
+ fi
+ mypath=$(pwd)
+ if [ -f "${mypath}/${myname}" ]; then
+ return
+ fi
+ echo "FATAL: Could not figure out the path where $myname lives from $0"
+ exit 1
+}
+
+COMMON_ENV=libexec/vespa/common-env.sh
+
+source_common_env () {
+ if [ "$VESPA_HOME" ] && [ -d "$VESPA_HOME" ]; then
+ export VESPA_HOME
+ common_env=$VESPA_HOME/$COMMON_ENV
+ if [ -f "$common_env" ]; then
+ . $common_env
+ return
+ fi
+ fi
+ return 1
+}
+
+findroot () {
+ source_common_env && return
+ if [ "$VESPA_HOME" ]; then
+ echo "FATAL: bad VESPA_HOME value '$VESPA_HOME'"
+ exit 1
+ fi
+ if [ "$ROOT" ] && [ -d "$ROOT" ]; then
+ VESPA_HOME="$ROOT"
+ source_common_env && return
+ fi
+ findpath
+ while [ "$mypath" ]; do
+ VESPA_HOME=${mypath}
+ source_common_env && return
+ mypath=${mypath%/*}
+ done
+ echo "FATAL: missing VESPA_HOME environment variable"
+ echo "Could not locate $COMMON_ENV anywhere"
+ exit 1
+}
+
+findhost () {
+ if [ "${VESPA_HOSTNAME}" = "" ]; then
+ VESPA_HOSTNAME=$(vespa-detect-hostname || hostname -f || hostname || echo "localhost") || exit 1
+ fi
+ validate="${VESPA_HOME}/bin/vespa-validate-hostname"
+ if [ -f "$validate" ]; then
+ "$validate" "${VESPA_HOSTNAME}" || exit 1
+ fi
+ export VESPA_HOSTNAME
+}
+
+findroot
+findhost
+
+# END environment bootstrap section
+
+eval $($VESPA_HOME/bin/vespa-security-env)
+
+exec $VESPA_HOME/libexec/vespa/GetClusterState.pl "$@"
diff --git a/vespaclient/src/sh/vespa-get-node-state b/vespaclient/src/sh/vespa-get-node-state
new file mode 100755
index 00000000000..d6d5a6905cf
--- /dev/null
+++ b/vespaclient/src/sh/vespa-get-node-state
@@ -0,0 +1,75 @@
+#!/usr/bin/env bash
+# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+# BEGIN environment bootstrap section
+# Do not edit between here and END as this section should stay identical in all scripts
+
+findpath () {
+ myname=${0}
+ mypath=${myname%/*}
+ myname=${myname##*/}
+ if [ "$mypath" ] && [ -d "$mypath" ]; then
+ return
+ fi
+ mypath=$(pwd)
+ if [ -f "${mypath}/${myname}" ]; then
+ return
+ fi
+ echo "FATAL: Could not figure out the path where $myname lives from $0"
+ exit 1
+}
+
+COMMON_ENV=libexec/vespa/common-env.sh
+
+source_common_env () {
+ if [ "$VESPA_HOME" ] && [ -d "$VESPA_HOME" ]; then
+ export VESPA_HOME
+ common_env=$VESPA_HOME/$COMMON_ENV
+ if [ -f "$common_env" ]; then
+ . $common_env
+ return
+ fi
+ fi
+ return 1
+}
+
+findroot () {
+ source_common_env && return
+ if [ "$VESPA_HOME" ]; then
+ echo "FATAL: bad VESPA_HOME value '$VESPA_HOME'"
+ exit 1
+ fi
+ if [ "$ROOT" ] && [ -d "$ROOT" ]; then
+ VESPA_HOME="$ROOT"
+ source_common_env && return
+ fi
+ findpath
+ while [ "$mypath" ]; do
+ VESPA_HOME=${mypath}
+ source_common_env && return
+ mypath=${mypath%/*}
+ done
+ echo "FATAL: missing VESPA_HOME environment variable"
+ echo "Could not locate $COMMON_ENV anywhere"
+ exit 1
+}
+
+findhost () {
+ if [ "${VESPA_HOSTNAME}" = "" ]; then
+ VESPA_HOSTNAME=$(vespa-detect-hostname || hostname -f || hostname || echo "localhost") || exit 1
+ fi
+ validate="${VESPA_HOME}/bin/vespa-validate-hostname"
+ if [ -f "$validate" ]; then
+ "$validate" "${VESPA_HOSTNAME}" || exit 1
+ fi
+ export VESPA_HOSTNAME
+}
+
+findroot
+findhost
+
+# END environment bootstrap section
+
+eval $($VESPA_HOME/bin/vespa-security-env)
+
+exec $VESPA_HOME/libexec/vespa/GetNodeState.pl "$@"
diff --git a/vespaclient/src/sh/vespa-set-node-state b/vespaclient/src/sh/vespa-set-node-state
new file mode 100755
index 00000000000..292ad8aef27
--- /dev/null
+++ b/vespaclient/src/sh/vespa-set-node-state
@@ -0,0 +1,75 @@
+#!/usr/bin/env bash
+# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+# BEGIN environment bootstrap section
+# Do not edit between here and END as this section should stay identical in all scripts
+
+findpath () {
+ myname=${0}
+ mypath=${myname%/*}
+ myname=${myname##*/}
+ if [ "$mypath" ] && [ -d "$mypath" ]; then
+ return
+ fi
+ mypath=$(pwd)
+ if [ -f "${mypath}/${myname}" ]; then
+ return
+ fi
+ echo "FATAL: Could not figure out the path where $myname lives from $0"
+ exit 1
+}
+
+COMMON_ENV=libexec/vespa/common-env.sh
+
+source_common_env () {
+ if [ "$VESPA_HOME" ] && [ -d "$VESPA_HOME" ]; then
+ export VESPA_HOME
+ common_env=$VESPA_HOME/$COMMON_ENV
+ if [ -f "$common_env" ]; then
+ . $common_env
+ return
+ fi
+ fi
+ return 1
+}
+
+findroot () {
+ source_common_env && return
+ if [ "$VESPA_HOME" ]; then
+ echo "FATAL: bad VESPA_HOME value '$VESPA_HOME'"
+ exit 1
+ fi
+ if [ "$ROOT" ] && [ -d "$ROOT" ]; then
+ VESPA_HOME="$ROOT"
+ source_common_env && return
+ fi
+ findpath
+ while [ "$mypath" ]; do
+ VESPA_HOME=${mypath}
+ source_common_env && return
+ mypath=${mypath%/*}
+ done
+ echo "FATAL: missing VESPA_HOME environment variable"
+ echo "Could not locate $COMMON_ENV anywhere"
+ exit 1
+}
+
+findhost () {
+ if [ "${VESPA_HOSTNAME}" = "" ]; then
+ VESPA_HOSTNAME=$(vespa-detect-hostname || hostname -f || hostname || echo "localhost") || exit 1
+ fi
+ validate="${VESPA_HOME}/bin/vespa-validate-hostname"
+ if [ -f "$validate" ]; then
+ "$validate" "${VESPA_HOSTNAME}" || exit 1
+ fi
+ export VESPA_HOSTNAME
+}
+
+findroot
+findhost
+
+# END environment bootstrap section
+
+eval $($VESPA_HOME/bin/vespa-security-env)
+
+exec $VESPA_HOME/libexec/vespa/SetNodeState.pl "$@"
diff --git a/vespalib/src/tests/net/tls/openssl_impl/openssl_impl_test.cpp b/vespalib/src/tests/net/tls/openssl_impl/openssl_impl_test.cpp
index 8ee10deead1..78838ce2cd2 100644
--- a/vespalib/src/tests/net/tls/openssl_impl/openssl_impl_test.cpp
+++ b/vespalib/src/tests/net/tls/openssl_impl/openssl_impl_test.cpp
@@ -97,7 +97,7 @@ struct Fixture {
static std::unique_ptr<CryptoCodec> create_openssl_codec(
const std::shared_ptr<TlsContext>& ctx, CryptoCodec::Mode mode) {
auto ctx_impl = std::dynamic_pointer_cast<impl::OpenSslTlsContextImpl>(ctx);
- return std::make_unique<impl::OpenSslCryptoCodecImpl>(std::move(ctx_impl), mode);
+ return std::make_unique<impl::OpenSslCryptoCodecImpl>(std::move(ctx_impl), SocketAddress(), mode);
}
EncodeResult do_encode(CryptoCodec& codec, Output& buffer, vespalib::stringref plaintext) {
diff --git a/vespalib/src/vespa/vespalib/CMakeLists.txt b/vespalib/src/vespa/vespalib/CMakeLists.txt
index d13875a9383..4249f6333a4 100644
--- a/vespalib/src/vespa/vespalib/CMakeLists.txt
+++ b/vespalib/src/vespa/vespalib/CMakeLists.txt
@@ -25,7 +25,7 @@ vespa_add_library(vespalib
$<TARGET_OBJECTS:vespalib_vespalib_websocket>
INSTALL lib64
DEPENDS
- gcc
+ ${VESPA_GCC_LIB}
)
vespa_add_target_package_dependency(vespalib OpenSSL)
diff --git a/vespalib/src/vespa/vespalib/btree/btreenode.h b/vespalib/src/vespa/vespalib/btree/btreenode.h
index 3bcc0fd100f..dd8fe09ec76 100644
--- a/vespalib/src/vespa/vespalib/btree/btreenode.h
+++ b/vespalib/src/vespa/vespalib/btree/btreenode.h
@@ -161,6 +161,7 @@ public:
static const NoAggregated &getEmptyAggregated() { return _instance; }
};
+template <> MinMaxAggregated BTreeNodeAggregatedWrap<MinMaxAggregated>::_instance;
template <typename KeyT, uint32_t NumSlots>
class BTreeNodeT : public BTreeNode {
diff --git a/vespalib/src/vespa/vespalib/net/socket_address.cpp b/vespalib/src/vespa/vespalib/net/socket_address.cpp
index d71441b0989..1a7a04b2925 100644
--- a/vespalib/src/vespa/vespalib/net/socket_address.cpp
+++ b/vespalib/src/vespa/vespalib/net/socket_address.cpp
@@ -8,6 +8,7 @@
#include <ifaddrs.h>
#include <netdb.h>
#include <cassert>
+#include <cerrno>
namespace vespalib {
diff --git a/vespalib/src/vespa/vespalib/net/tls/crypto_codec.cpp b/vespalib/src/vespa/vespalib/net/tls/crypto_codec.cpp
index 680d6472470..c54990b3782 100644
--- a/vespalib/src/vespa/vespalib/net/tls/crypto_codec.cpp
+++ b/vespalib/src/vespa/vespalib/net/tls/crypto_codec.cpp
@@ -6,10 +6,12 @@
namespace vespalib::net::tls {
-std::unique_ptr<CryptoCodec> CryptoCodec::create_default_codec(std::shared_ptr<TlsContext> ctx, Mode mode) {
+std::unique_ptr<CryptoCodec> CryptoCodec::create_default_codec(
+ std::shared_ptr<TlsContext> ctx, const SocketAddress& peer_address, Mode mode)
+{
auto ctx_impl = std::dynamic_pointer_cast<impl::OpenSslTlsContextImpl>(ctx); // only takes by const ref
assert(ctx_impl);
- return std::make_unique<impl::OpenSslCryptoCodecImpl>(std::move(ctx_impl), mode);
+ return std::make_unique<impl::OpenSslCryptoCodecImpl>(std::move(ctx_impl), peer_address, mode);
}
}
diff --git a/vespalib/src/vespa/vespalib/net/tls/crypto_codec.h b/vespalib/src/vespa/vespalib/net/tls/crypto_codec.h
index fc5303dbc5b..5d9684461d7 100644
--- a/vespalib/src/vespa/vespalib/net/tls/crypto_codec.h
+++ b/vespalib/src/vespa/vespalib/net/tls/crypto_codec.h
@@ -1,6 +1,7 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#pragma once
+#include <vespa/vespalib/net/socket_address.h>
#include <memory>
namespace vespalib::net::tls {
@@ -178,7 +179,9 @@ public:
*
* Throws CryptoException if resources cannot be allocated for the codec.
*/
- static std::unique_ptr<CryptoCodec> create_default_codec(std::shared_ptr<TlsContext> ctx, Mode mode);
+ static std::unique_ptr<CryptoCodec> create_default_codec(std::shared_ptr<TlsContext> ctx,
+ const SocketAddress& peer_address,
+ Mode mode);
};
}
diff --git a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.cpp b/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.cpp
index 3049c02b798..5315754d53a 100644
--- a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.cpp
+++ b/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.cpp
@@ -17,7 +17,7 @@
#include <openssl/err.h>
#include <openssl/pem.h>
-#include <vespa/log/log.h>
+#include <vespa/log/bufferedlogger.h>
LOG_SETUP(".vespalib.net.tls.openssl_crypto_codec_impl");
#if (OPENSSL_VERSION_NUMBER < 0x10000000L)
@@ -159,15 +159,23 @@ vespalib::string ssl_error_from_stack() {
return vespalib::string(buf);
}
-void log_ssl_error(const char* source, int ssl_error) {
- LOG(error, "%s returned unexpected error: %s (%s)",
- source, ssl_error_to_str(ssl_error), ssl_error_from_stack().c_str());
+void log_ssl_error(const char* source, const SocketAddress& peer_address, int ssl_error) {
+ // Buffer the emitted log messages on the peer's IP address. This prevents a single misbehaving
+ // client from flooding our logs, while at the same time ensuring that logs for other clients
+ // aren't lost.
+ LOGBT(warning, peer_address.ip_address(),
+ "%s (with peer '%s') returned unexpected error: %s (%s)",
+ source, peer_address.spec().c_str(),
+ ssl_error_to_str(ssl_error), ssl_error_from_stack().c_str());
}
} // anon ns
-OpenSslCryptoCodecImpl::OpenSslCryptoCodecImpl(std::shared_ptr<OpenSslTlsContextImpl> ctx, Mode mode)
+OpenSslCryptoCodecImpl::OpenSslCryptoCodecImpl(std::shared_ptr<OpenSslTlsContextImpl> ctx,
+ const SocketAddress& peer_address,
+ Mode mode)
: _ctx(std::move(ctx)),
+ _peer_address(peer_address),
_ssl(::SSL_new(_ctx->native_context())),
_mode(mode),
_deferred_handshake_params(),
@@ -214,6 +222,10 @@ OpenSslCryptoCodecImpl::OpenSslCryptoCodecImpl(std::shared_ptr<OpenSslTlsContext
} else {
::SSL_set_accept_state(_ssl.get());
}
+ // Store self-reference that can be fished out of SSL object during certificate verification callbacks
+ if (SSL_set_app_data(_ssl.get(), this) != 1) {
+ throw CryptoException("SSL_set_app_data() failed");
+ }
}
OpenSslCryptoCodecImpl::~OpenSslCryptoCodecImpl() = default;
@@ -302,7 +314,7 @@ HandshakeResult OpenSslCryptoCodecImpl::do_handshake_and_consume_peer_input_byte
ConnectionStatistics::get(_mode == Mode::Server).inc_tls_connections();
return handshake_consumed_bytes_and_is_complete(static_cast<size_t>(consumed));
} else {
- log_ssl_error("SSL_do_handshake()", ssl_result);
+ log_ssl_error("SSL_do_handshake()", _peer_address, ssl_result);
ConnectionStatistics::get(_mode == Mode::Server).inc_failed_tls_handshakes();
return handshake_failed();
}
@@ -327,7 +339,7 @@ EncodeResult OpenSslCryptoCodecImpl::encode(const char* plaintext, size_t plaint
// SSL_write encodes plaintext to ciphertext and writes to _output_bio
const int consumed = ::SSL_write(_ssl.get(), plaintext, to_consume);
if (consumed < 0) {
- log_ssl_error("SSL_write()", ::SSL_get_error(_ssl.get(), consumed));
+ log_ssl_error("SSL_write()", _peer_address, ::SSL_get_error(_ssl.get(), consumed));
ConnectionStatistics::get(_mode == Mode::Server).inc_broken_tls_connections();
return encode_failed(); // TODO explicitly detect and log TLS renegotiation error (SSL_ERROR_WANT_READ)?
} else if (consumed != to_consume) {
@@ -391,7 +403,7 @@ DecodeResult OpenSslCryptoCodecImpl::remap_ssl_read_failure_to_decode_result(int
LOG(debug, "SSL_read() returned SSL_ERROR_ZERO_RETURN; connection has been shut down normally by the peer");
return decode_peer_has_closed();
default:
- log_ssl_error("SSL_read()", ssl_error);
+ log_ssl_error("SSL_read()", _peer_address, ssl_error);
ConnectionStatistics::get(_mode == Mode::Server).inc_broken_tls_connections();
return decode_failed();
}
@@ -403,7 +415,7 @@ EncodeResult OpenSslCryptoCodecImpl::half_close(char* ciphertext, size_t ciphert
const int pending_before = BIO_pending(_output_bio);
int ssl_result = ::SSL_shutdown(_ssl.get());
if (ssl_result < 0) {
- log_ssl_error("SSL_shutdown()", ::SSL_get_error(_ssl.get(), ssl_result));
+ log_ssl_error("SSL_shutdown()", _peer_address, ::SSL_get_error(_ssl.get(), ssl_result));
return encode_failed();
}
const int pending_after = BIO_pending(_output_bio);
diff --git a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.h b/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.h
index ef7e0998994..14200de449a 100644
--- a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.h
+++ b/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.h
@@ -2,6 +2,7 @@
#pragma once
#include "openssl_typedefs.h"
+#include <vespa/vespalib/net/socket_address.h>
#include <vespa/vespalib/net/tls/transport_security_options.h>
#include <vespa/vespalib/net/tls/crypto_codec.h>
#include <memory>
@@ -45,6 +46,7 @@ class OpenSslCryptoCodecImpl : public CryptoCodec {
// The context maintains shared verification callback state, so it must be
// kept alive explictly for at least as long as any codecs.
std::shared_ptr<OpenSslTlsContextImpl> _ctx;
+ SocketAddress _peer_address;
SslPtr _ssl;
::BIO* _input_bio; // Owned by _ssl
::BIO* _output_bio; // Owned by _ssl
@@ -52,7 +54,7 @@ class OpenSslCryptoCodecImpl : public CryptoCodec {
std::optional<DeferredHandshakeParams> _deferred_handshake_params;
std::optional<HandshakeResult> _deferred_handshake_result;
public:
- OpenSslCryptoCodecImpl(std::shared_ptr<OpenSslTlsContextImpl> ctx, Mode mode);
+ OpenSslCryptoCodecImpl(std::shared_ptr<OpenSslTlsContextImpl> ctx, const SocketAddress& peer_address, Mode mode);
~OpenSslCryptoCodecImpl() override;
/*
@@ -85,6 +87,8 @@ public:
DecodeResult decode(const char* ciphertext, size_t ciphertext_size,
char* plaintext, size_t plaintext_size) noexcept override;
EncodeResult half_close(char* ciphertext, size_t ciphertext_size) noexcept override;
+
+ const SocketAddress& peer_address() const noexcept { return _peer_address; }
private:
HandshakeResult do_handshake_and_consume_peer_input_bytes() noexcept;
DecodeResult drain_and_produce_plaintext_from_ssl(char* plaintext, size_t plaintext_size) noexcept;
diff --git a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.cpp b/vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.cpp
index c87dc1d2148..98675ec6b0b 100644
--- a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.cpp
+++ b/vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.cpp
@@ -2,6 +2,7 @@
#include "iana_cipher_map.h"
#include "openssl_typedefs.h"
#include "openssl_tls_context_impl.h"
+#include "openssl_crypto_codec_impl.h"
#include <vespa/vespalib/net/tls/crypto_exception.h>
#include <vespa/vespalib/net/tls/statistics.h>
#include <vespa/vespalib/net/tls/transport_security_options.h>
@@ -17,7 +18,7 @@
#include <openssl/asn1.h>
#include <openssl/pem.h>
-#include <vespa/log/log.h>
+#include <vespa/log/bufferedlogger.h>
LOG_SETUP(".vespalib.net.tls.openssl_tls_context_impl");
#if (OPENSSL_VERSION_NUMBER < 0x10000000L)
@@ -402,8 +403,6 @@ bool fill_certificate_subject_alternate_names(::X509* cert, PeerCredentials& cre
} // anon ns
-// TODO if/when we want to move per-connection peer credentials into the crypto codec/socket
-// itself, we probably need to set the verification callback (data) on _SSL_, not _SSL_CTX_..!
// Note: we try to be as conservative as possible. If anything looks out of place, we fail
// secure by denying the connection.
//
@@ -426,19 +425,22 @@ int OpenSslTlsContextImpl::verify_cb_wrapper(int preverified_ok, ::X509_STORE_CT
void* data = ::X509_STORE_CTX_get_ex_data(store_ctx, ::SSL_get_ex_data_X509_STORE_CTX_idx());
LOG_ASSERT(data != nullptr);
auto* ssl = static_cast<::SSL*>(data);
+ data = SSL_get_app_data(ssl);
+ LOG_ASSERT(data != nullptr);
+ auto* codec_impl = static_cast<OpenSslCryptoCodecImpl*>(data);
const ::SSL_CTX* ssl_ctx = ::SSL_get_SSL_CTX(ssl);
LOG_ASSERT(ssl_ctx != nullptr);
auto* self = static_cast<OpenSslTlsContextImpl*>(SSL_CTX_get_app_data(ssl_ctx));
LOG_ASSERT(self != nullptr);
- if (self->verify_trusted_certificate(store_ctx)) {
+ if (self->verify_trusted_certificate(store_ctx, codec_impl->peer_address())) {
return 1;
}
ConnectionStatistics::get(SSL_in_accept_init(ssl) != 0).inc_invalid_peer_credentials();
return 0;
}
-bool OpenSslTlsContextImpl::verify_trusted_certificate(::X509_STORE_CTX* store_ctx) {
+bool OpenSslTlsContextImpl::verify_trusted_certificate(::X509_STORE_CTX* store_ctx, const SocketAddress& peer_address) {
const auto authz_mode = authorization_mode();
// TODO consider if we want to fill in peer credentials even if authorization is disabled
if (authz_mode == AuthorizationMode::Disable) {
@@ -459,13 +461,16 @@ bool OpenSslTlsContextImpl::verify_trusted_certificate(::X509_STORE_CTX* store_c
try {
const bool verified_by_cb = _cert_verify_callback->verify(creds);
if (!verified_by_cb) {
- // TODO we should print the peer's remote address too, but that information is
- // not currently available to us here.
- LOG(warning, "Certificate verification failed for %s", to_string(creds).c_str());
+ // Buffer warnings on peer IP address to avoid log flooding.
+ LOGBT(warning, peer_address.ip_address(),
+ "Certificate verification of peer '%s' failed with %s",
+ peer_address.spec().c_str(), to_string(creds).c_str());
return (authz_mode != AuthorizationMode::Enforce);
}
} catch (std::exception& e) {
- LOG(error, "Got exception during certificate verification callback: %s", e.what());
+ LOGBT(error, peer_address.ip_address(),
+ "Got exception during certificate verification callback for peer '%s': %s",
+ peer_address.spec().c_str(), e.what());
return false;
} // we don't expect any non-std::exception derived exceptions, so let them terminate the process.
return true;
diff --git a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.h b/vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.h
index 31814dad8ba..c519b1ae874 100644
--- a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.h
+++ b/vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.h
@@ -2,6 +2,7 @@
#pragma once
#include "openssl_typedefs.h"
+#include <vespa/vespalib/net/socket_address.h>
#include <vespa/vespalib/net/tls/tls_context.h>
#include <vespa/vespalib/net/tls/transport_security_options.h>
#include <vespa/vespalib/net/tls/certificate_verification_callback.h>
@@ -46,7 +47,7 @@ private:
void set_ssl_ctx_self_reference();
void set_accepted_cipher_suites(const std::vector<vespalib::string>& ciphers);
- bool verify_trusted_certificate(::X509_STORE_CTX* store_ctx);
+ bool verify_trusted_certificate(::X509_STORE_CTX* store_ctx, const SocketAddress& peer_address);
static int verify_cb_wrapper(int preverified_ok, ::X509_STORE_CTX* store_ctx);
};
diff --git a/vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.cpp b/vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.cpp
index 7d0d3287965..58d99cc7108 100644
--- a/vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.cpp
+++ b/vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.cpp
@@ -15,7 +15,7 @@ std::unique_ptr<TlsCryptoSocket>
TlsCryptoEngine::create_tls_crypto_socket(SocketHandle socket, bool is_server)
{
auto mode = is_server ? net::tls::CryptoCodec::Mode::Server : net::tls::CryptoCodec::Mode::Client;
- auto codec = net::tls::CryptoCodec::create_default_codec(_tls_ctx, mode);
+ auto codec = net::tls::CryptoCodec::create_default_codec(_tls_ctx, SocketAddress::peer_address(socket.get()), mode);
return std::make_unique<net::tls::CryptoCodecAdapter>(std::move(socket), std::move(codec));
}
diff --git a/vespalib/src/vespa/vespalib/stllike/hashtable.cpp b/vespalib/src/vespa/vespalib/stllike/hashtable.cpp
index 9c0db3ae817..52639fd6275 100644
--- a/vespalib/src/vespa/vespalib/stllike/hashtable.cpp
+++ b/vespalib/src/vespa/vespalib/stllike/hashtable.cpp
@@ -1,5 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/stllike/hashtable.hpp>
+#include <algorithm>
namespace {
diff --git a/vespamalloc/src/tests/doubledelete/doubledelete.cpp b/vespamalloc/src/tests/doubledelete/doubledelete.cpp
index aa8d9b1204f..299583456cf 100644
--- a/vespamalloc/src/tests/doubledelete/doubledelete.cpp
+++ b/vespamalloc/src/tests/doubledelete/doubledelete.cpp
@@ -1,11 +1,14 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <stdlib.h>
+void *savedptr;
+
int main(int argc, char *argv[])
{
(void) argc;
(void) argv;
- char * a = new char [100];
+ char * a = new char;
+ savedptr = a;
delete a;
delete a;
}
diff --git a/vespamalloc/src/tests/test1/testatomic.cpp b/vespamalloc/src/tests/test1/testatomic.cpp
index 54a0b406116..9e44bc8a913 100644
--- a/vespamalloc/src/tests/test1/testatomic.cpp
+++ b/vespamalloc/src/tests/test1/testatomic.cpp
@@ -14,12 +14,8 @@ TEST("verify lock freeness of atomics"){
{
std::atomic<vespamalloc::TaggedPtr> taggedPtr;
ASSERT_EQUAL(16u, sizeof(vespamalloc::TaggedPtr));
-#if __GNUC__ < 7
// See https://gcc.gnu.org/ml/gcc-patches/2017-01/msg02344.html for background
- ASSERT_TRUE(taggedPtr.is_lock_free());
-#else
ASSERT_TRUE(taggedPtr.is_lock_free() || !taggedPtr.is_lock_free());
-#endif
}
}
diff --git a/vespamalloc/src/vespamalloc/malloc/common.h b/vespamalloc/src/vespamalloc/malloc/common.h
index 9cc3d319e59..b21a2f63ed5 100644
--- a/vespamalloc/src/vespamalloc/malloc/common.h
+++ b/vespamalloc/src/vespamalloc/malloc/common.h
@@ -74,7 +74,7 @@ public:
static inline size_t classSize(SizeClassT sc) { return (size_t(1) << (sc + MinClassSizeC)); }
};
-inline void crash() { *((unsigned *) NULL) = 0; }
+inline void crash() { *((volatile unsigned *) NULL) = 0; }
template <typename T>
inline void swap(T & a, T & b) { T tmp(a); a = b; b = tmp; }
diff --git a/vespamalloc/src/vespamalloc/malloc/globalpool.h b/vespamalloc/src/vespamalloc/malloc/globalpool.h
index 6a05d14a901..bc13231bb85 100644
--- a/vespamalloc/src/vespamalloc/malloc/globalpool.h
+++ b/vespamalloc/src/vespamalloc/malloc/globalpool.h
@@ -27,10 +27,7 @@ public:
DataSegment<MemBlockPtrT> & dataSegment() { return _dataSegment; }
void enableThreadSupport() __attribute__((noinline));
- static void setParams(size_t alwaysReuseLimit, size_t threadCacheLimit) {
- _alwaysReuseLimit = alwaysReuseLimit;
- _threadCacheLimit = threadCacheLimit;
- }
+ static void setParams(size_t alwaysReuseLimit, size_t threadCacheLimit);
void info(FILE * os, size_t level=0) __attribute__((noinline));
private:
diff --git a/vespamalloc/src/vespamalloc/malloc/globalpool.hpp b/vespamalloc/src/vespamalloc/malloc/globalpool.hpp
index 5cc172750c5..c954c1aae26 100644
--- a/vespamalloc/src/vespamalloc/malloc/globalpool.hpp
+++ b/vespamalloc/src/vespamalloc/malloc/globalpool.hpp
@@ -35,6 +35,14 @@ void AllocPoolT<MemBlockPtrT>::enableThreadSupport()
}
template <typename MemBlockPtrT>
+void
+AllocPoolT<MemBlockPtrT>::setParams(size_t alwaysReuseLimit, size_t threadCacheLimit)
+{
+ _alwaysReuseLimit = alwaysReuseLimit;
+ _threadCacheLimit = threadCacheLimit;
+}
+
+template <typename MemBlockPtrT>
typename AllocPoolT<MemBlockPtrT>::ChunkSList *
AllocPoolT<MemBlockPtrT>::getFree(SizeClassT sc)
{
diff --git a/vespamalloc/src/vespamalloc/malloc/memblock.h b/vespamalloc/src/vespamalloc/malloc/memblock.h
index 85dd725749c..118fb0e046c 100644
--- a/vespamalloc/src/vespamalloc/malloc/memblock.h
+++ b/vespamalloc/src/vespamalloc/malloc/memblock.h
@@ -45,7 +45,7 @@ public:
static size_t unAdjustSize(size_t sz) { return sz; }
static void dumpInfo(size_t level);
static void dumpFile(FILE * fp) { _logFile = fp; }
- static void bigBlockLimit(size_t lim) { _bigBlockLimit = lim; }
+ static void bigBlockLimit(size_t lim);
static void setFill(uint8_t ) { }
static bool verifySizeClass(int sc) { (void) sc; return true; }
static size_t getMinSizeForAlignment(size_t align, size_t sz) {
@@ -60,6 +60,8 @@ private:
};
typedef MemBlockT<5, 20> MemBlock;
+template <> void MemBlock::dumpInfo(size_t level);
+extern template class MemBlockT<5, 20>;
}
diff --git a/vespamalloc/src/vespamalloc/malloc/memblock.hpp b/vespamalloc/src/vespamalloc/malloc/memblock.hpp
index e6df3bcc67e..57b5a8cac51 100644
--- a/vespamalloc/src/vespamalloc/malloc/memblock.hpp
+++ b/vespamalloc/src/vespamalloc/malloc/memblock.hpp
@@ -29,6 +29,13 @@ MemBlockT<MinSizeClassC, MaxSizeClassMultiAllocC>::logBigBlock(size_t exact, siz
}
template <size_t MinSizeClassC, size_t MaxSizeClassMultiAllocC>
+void
+MemBlockT<MinSizeClassC, MaxSizeClassMultiAllocC>::bigBlockLimit(size_t lim)
+{
+ _bigBlockLimit = lim;
+}
+
+template <size_t MinSizeClassC, size_t MaxSizeClassMultiAllocC>
FILE * MemBlockT<MinSizeClassC, MaxSizeClassMultiAllocC>::_logFile = stderr;
template <size_t MinSizeClassC, size_t MaxSizeClassMultiAllocC>
size_t MemBlockT<MinSizeClassC, MaxSizeClassMultiAllocC>::_bigBlockLimit = 0x80000000;
diff --git a/vespamalloc/src/vespamalloc/malloc/overload.h b/vespamalloc/src/vespamalloc/malloc/overload.h
index 1845703aad8..7883578cc28 100644
--- a/vespamalloc/src/vespamalloc/malloc/overload.h
+++ b/vespamalloc/src/vespamalloc/malloc/overload.h
@@ -13,6 +13,9 @@ public:
vespamalloc::createAllocator();
}
private:
+#ifdef __clang__
+ [[maybe_unused]]
+#endif
unsigned _initialized;
};
@@ -119,8 +122,8 @@ int posix_memalign(void** ptr, size_t align, size_t sz) __THROW
return retval;
}
-void *valloc(size_t size) __attribute__((visibility ("default")));
-void *valloc(size_t size)
+void *valloc(size_t size) __THROW __attribute__((visibility ("default")));
+void *valloc(size_t size) __THROW
{
return memalign(sysconf(_SC_PAGESIZE),size);
}
@@ -131,12 +134,24 @@ void free(void * ptr) {
}
#define ALIAS(x) __attribute__ ((weak, alias (x), visibility ("default")))
+#ifdef __clang__
+void* __libc_malloc(size_t sz) __THROW __attribute__((malloc, alloc_size(1))) ALIAS("malloc");
+void* __libc_realloc(void* ptr, size_t sz) __THROW __attribute__((malloc, alloc_size(2))) ALIAS("realloc");
+void* __libc_calloc(size_t n, size_t sz) __THROW __attribute__((malloc, alloc_size(2))) ALIAS("calloc");
+void cfree(void *) __THROW ALIAS("free");
+void __libc_free(void* ptr) __THROW ALIAS("free");
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wignored-attributes"
+void __libc_cfree(void* ptr) __THROW ALIAS("cfree");
+#pragma clang diagnostic pop
+#else
void* __libc_malloc(size_t sz) __THROW __attribute__((leaf, malloc, alloc_size(1))) ALIAS("malloc");
void* __libc_realloc(void* ptr, size_t sz) __THROW __attribute__((leaf, malloc, alloc_size(2))) ALIAS("realloc");
void* __libc_calloc(size_t n, size_t sz) __THROW __attribute__((leaf, malloc, alloc_size(2))) ALIAS("calloc");
void cfree(void *) __THROW __attribute__((leaf)) ALIAS("free");
void __libc_free(void* ptr) __THROW __attribute__((leaf)) ALIAS("free");
void __libc_cfree(void* ptr) __THROW __attribute__((leaf)) ALIAS("cfree");
+#endif
void* __libc_memalign(size_t align, size_t s) ALIAS("memalign");
int __posix_memalign(void** r, size_t a, size_t s) __THROW __nonnull((1)) ALIAS("posix_memalign");
#undef ALIAS
diff --git a/vespamalloc/src/vespamalloc/malloc/threadlist.h b/vespamalloc/src/vespamalloc/malloc/threadlist.h
index a8f28ebcb67..6e518091f0f 100644
--- a/vespamalloc/src/vespamalloc/malloc/threadlist.h
+++ b/vespamalloc/src/vespamalloc/malloc/threadlist.h
@@ -48,4 +48,7 @@ private:
static __thread ThreadPool * _myPool TLS_LINKAGE;
};
+template <typename MemBlockPtrT, typename ThreadStatT>
+__thread ThreadPoolT<MemBlockPtrT, ThreadStatT> * ThreadListT<MemBlockPtrT, ThreadStatT>::_myPool TLS_LINKAGE = nullptr;
+
}
diff --git a/vespamalloc/src/vespamalloc/malloc/threadlist.hpp b/vespamalloc/src/vespamalloc/malloc/threadlist.hpp
index ebeb23b2883..e437981febc 100644
--- a/vespamalloc/src/vespamalloc/malloc/threadlist.hpp
+++ b/vespamalloc/src/vespamalloc/malloc/threadlist.hpp
@@ -73,7 +73,5 @@ bool ThreadListT<MemBlockPtrT, ThreadStatT>::initThisThread()
return retval;
}
-template <typename MemBlockPtrT, typename ThreadStatT>
-__thread ThreadPoolT<MemBlockPtrT, ThreadStatT> * ThreadListT<MemBlockPtrT, ThreadStatT>::_myPool TLS_LINKAGE = NULL;
}
diff --git a/vsm/src/tests/searcher/searcher.cpp b/vsm/src/tests/searcher/searcher.cpp
index f5f7508a256..02555f9d6f6 100644
--- a/vsm/src/tests/searcher/searcher.cpp
+++ b/vsm/src/tests/searcher/searcher.cpp
@@ -766,7 +766,7 @@ TEST("FieldSearchSpec constrution") {
EXPECT_EQUAL(0x100000u, f.maxLength());
}
{
- FieldSearchSpec f(7, "f0", VsmfieldsConfig::Fieldspec::AUTOUTF8, "substring", 789);
+ FieldSearchSpec f(7, "f0", VsmfieldsConfig::Fieldspec::Searchmethod::AUTOUTF8, "substring", 789);
EXPECT_TRUE(f.valid());
EXPECT_EQUAL(7u, f.id());
EXPECT_EQUAL("f0", f.name());
@@ -777,8 +777,8 @@ TEST("FieldSearchSpec constrution") {
TEST("snippet modifier manager") {
FieldSearchSpecMapT specMap;
- specMap[0] = FieldSearchSpec(0, "f0", VsmfieldsConfig::Fieldspec::AUTOUTF8, "substring", 1000);
- specMap[1] = FieldSearchSpec(1, "f1", VsmfieldsConfig::Fieldspec::AUTOUTF8, "", 1000);
+ specMap[0] = FieldSearchSpec(0, "f0", VsmfieldsConfig::Fieldspec::Searchmethod::AUTOUTF8, "substring", 1000);
+ specMap[1] = FieldSearchSpec(1, "f1", VsmfieldsConfig::Fieldspec::Searchmethod::AUTOUTF8, "", 1000);
IndexFieldMapT indexMap;
indexMap["i0"].push_back(0);
indexMap["i1"].push_back(1);
diff --git a/vsm/src/vespa/vsm/vsm/docsumfieldspec.cpp b/vsm/src/vespa/vsm/vsm/docsumfieldspec.cpp
index c5e63fde7ba..1440b894934 100644
--- a/vsm/src/vespa/vsm/vsm/docsumfieldspec.cpp
+++ b/vsm/src/vespa/vsm/vsm/docsumfieldspec.cpp
@@ -16,7 +16,7 @@ DocsumFieldSpec::FieldIdentifier::FieldIdentifier(FieldIdT id, FieldPath path) :
DocsumFieldSpec::DocsumFieldSpec() :
_resultType(search::docsummary::RES_INT),
- _command(VsmsummaryConfig::Fieldmap::NONE),
+ _command(VsmsummaryConfig::Fieldmap::Command::NONE),
_outputField(),
_inputFields()
{ }
diff --git a/vsm/src/vespa/vsm/vsm/docsumfilter.cpp b/vsm/src/vespa/vsm/vsm/docsumfilter.cpp
index 872ccf6acf8..3794e78cf5b 100644
--- a/vsm/src/vespa/vsm/vsm/docsumfilter.cpp
+++ b/vsm/src/vespa/vsm/vsm/docsumfilter.cpp
@@ -188,7 +188,7 @@ DocsumFilter::getFieldValue(const DocsumFieldSpec::FieldIdentifier & fieldId,
return nullptr;
}
switch (command) {
- case VsmsummaryConfig::Fieldmap::FLATTENJUNIPER:
+ case VsmsummaryConfig::Fieldmap::Command::FLATTENJUNIPER:
if (_snippetModifiers != nullptr) {
FieldModifier * mod = _snippetModifiers->getModifier(fId);
if (mod != nullptr) {
@@ -319,7 +319,7 @@ DocsumFilter::writeSlimeField(const DocsumFieldSpec & fieldSpec,
const Document & docsum,
ResultPacker & packer)
{
- if (fieldSpec.getCommand() == VsmsummaryConfig::Fieldmap::NONE) {
+ if (fieldSpec.getCommand() == VsmsummaryConfig::Fieldmap::Command::NONE) {
const DocsumFieldSpec::FieldIdentifier & fieldId = fieldSpec.getOutputField();
const document::FieldValue * fv = docsum.getField(fieldId.getId());
if (fv != nullptr) {
@@ -347,7 +347,7 @@ DocsumFilter::writeFlattenField(const DocsumFieldSpec & fieldSpec,
const Document & docsum,
ResultPacker & packer)
{
- if (fieldSpec.getCommand() == VsmsummaryConfig::Fieldmap::NONE) {
+ if (fieldSpec.getCommand() == VsmsummaryConfig::Fieldmap::Command::NONE) {
LOG(debug, "writeFlattenField: Cannot handle command NONE");
packer.AddEmpty();
return;
@@ -362,7 +362,7 @@ DocsumFilter::writeFlattenField(const DocsumFieldSpec & fieldSpec,
}
switch (fieldSpec.getCommand()) {
- case VsmsummaryConfig::Fieldmap::FLATTENJUNIPER:
+ case VsmsummaryConfig::Fieldmap::Command::FLATTENJUNIPER:
_flattenWriter.setSeparator("\x1E"); // record separator (same as juniper uses)
break;
default:
@@ -453,7 +453,7 @@ DocsumFilter::getMappedDocsum(uint32_t id)
// this really means 'structured data'
writeSlimeField(*it, doc, _packer);
} else {
- if (it->getInputFields().size() == 1 && it->getCommand() == VsmsummaryConfig::Fieldmap::NONE) {
+ if (it->getInputFields().size() == 1 && it->getCommand() == VsmsummaryConfig::Fieldmap::Command::NONE) {
const DocsumFieldSpec::FieldIdentifier & fieldId = it->getInputFields()[0];
const document::FieldValue * field = doc.getField(fieldId.getId());
if (field != nullptr) {
@@ -461,7 +461,7 @@ DocsumFilter::getMappedDocsum(uint32_t id)
} else {
writeEmpty(type, _packer); // void input
}
- } else if (it->getInputFields().size() == 0 && it->getCommand() == VsmsummaryConfig::Fieldmap::NONE) {
+ } else if (it->getInputFields().size() == 0 && it->getCommand() == VsmsummaryConfig::Fieldmap::Command::NONE) {
LOG(spam, "0 inputfields for output field %u", it->getOutputField().getId());
writeEmpty(type, _packer); // no input
} else {
diff --git a/vsm/src/vespa/vsm/vsm/fieldsearchspec.cpp b/vsm/src/vespa/vsm/vsm/fieldsearchspec.cpp
index bb30cdd89e9..8d4cf72b824 100644
--- a/vsm/src/vespa/vsm/vsm/fieldsearchspec.cpp
+++ b/vsm/src/vespa/vsm/vsm/fieldsearchspec.cpp
@@ -45,7 +45,7 @@ FieldSearchSpec::FieldSearchSpec() :
_name(),
_maxLength(0x100000),
_searcher(),
- _searchMethod(VsmfieldsConfig::Fieldspec::NONE),
+ _searchMethod(VsmfieldsConfig::Fieldspec::Searchmethod::NONE),
_arg1(),
_reconfigured(false)
{
@@ -65,12 +65,12 @@ FieldSearchSpec::FieldSearchSpec(const FieldIdT & fid, const vespalib::string &
{
switch(searchDef) {
default:
- LOG(warning, "Unknown searchdef = %d. Defaulting to AUTOUTF8", searchDef);
+ LOG(warning, "Unknown searchdef = %d. Defaulting to AUTOUTF8", static_cast<int>(searchDef));
[[fallthrough]];
- case VsmfieldsConfig::Fieldspec::AUTOUTF8:
- case VsmfieldsConfig::Fieldspec::NONE:
- case VsmfieldsConfig::Fieldspec::SSE2UTF8:
- case VsmfieldsConfig::Fieldspec::UTF8:
+ case VsmfieldsConfig::Fieldspec::Searchmethod::AUTOUTF8:
+ case VsmfieldsConfig::Fieldspec::Searchmethod::NONE:
+ case VsmfieldsConfig::Fieldspec::Searchmethod::SSE2UTF8:
+ case VsmfieldsConfig::Fieldspec::Searchmethod::UTF8:
if (arg1 == "substring") {
_searcher = UTF8SubStringFieldSearcher(fid);
} else if (arg1 == "suffix") {
@@ -79,25 +79,25 @@ FieldSearchSpec::FieldSearchSpec(const FieldIdT & fid, const vespalib::string &
_searcher = UTF8ExactStringFieldSearcher(fid);
} else if (arg1 == "word") {
_searcher = UTF8ExactStringFieldSearcher(fid);
- } else if (searchDef == VsmfieldsConfig::Fieldspec::UTF8) {
+ } else if (searchDef == VsmfieldsConfig::Fieldspec::Searchmethod::UTF8) {
_searcher = UTF8StrChrFieldSearcher(fid);
} else {
_searcher = FUTF8StrChrFieldSearcher(fid);
}
break;
- case VsmfieldsConfig::Fieldspec::BOOL:
+ case VsmfieldsConfig::Fieldspec::Searchmethod::BOOL:
_searcher = BoolFieldSearcher(fid);
break;
- case VsmfieldsConfig::Fieldspec::INT8:
- case VsmfieldsConfig::Fieldspec::INT16:
- case VsmfieldsConfig::Fieldspec::INT32:
- case VsmfieldsConfig::Fieldspec::INT64:
+ case VsmfieldsConfig::Fieldspec::Searchmethod::INT8:
+ case VsmfieldsConfig::Fieldspec::Searchmethod::INT16:
+ case VsmfieldsConfig::Fieldspec::Searchmethod::INT32:
+ case VsmfieldsConfig::Fieldspec::Searchmethod::INT64:
_searcher = IntFieldSearcher(fid);
break;
- case VsmfieldsConfig::Fieldspec::FLOAT:
+ case VsmfieldsConfig::Fieldspec::Searchmethod::FLOAT:
_searcher = FloatFieldSearcher(fid);
break;
- case VsmfieldsConfig::Fieldspec::DOUBLE:
+ case VsmfieldsConfig::Fieldspec::Searchmethod::DOUBLE:
_searcher = DoubleFieldSearcher(fid);
break;
}
@@ -114,10 +114,10 @@ FieldSearchSpec::reconfig(const search::QueryTerm & term)
return;
}
switch (_searchMethod) {
- case VsmfieldsConfig::Fieldspec::NONE:
- case VsmfieldsConfig::Fieldspec::AUTOUTF8:
- case VsmfieldsConfig::Fieldspec::UTF8:
- case VsmfieldsConfig::Fieldspec::SSE2UTF8:
+ case VsmfieldsConfig::Fieldspec::Searchmethod::NONE:
+ case VsmfieldsConfig::Fieldspec::Searchmethod::AUTOUTF8:
+ case VsmfieldsConfig::Fieldspec::Searchmethod::UTF8:
+ case VsmfieldsConfig::Fieldspec::Searchmethod::SSE2UTF8:
if ((term.isSubstring() && _arg1 != "substring") ||
(term.isSuffix() && _arg1 != "suffix") ||
(term.isExactstring() && _arg1 != "exact") ||
diff --git a/vsm/src/vespa/vsm/vsm/vsm-adapter.cpp b/vsm/src/vespa/vsm/vsm/vsm-adapter.cpp
index 1cd35e7ca61..f87c327e86a 100644
--- a/vsm/src/vespa/vsm/vsm/vsm-adapter.cpp
+++ b/vsm/src/vespa/vsm/vsm/vsm-adapter.cpp
@@ -51,7 +51,7 @@ GetDocsumsStateCallback::~GetDocsumsStateCallback() = default;
DocsumTools::FieldSpec::FieldSpec() :
_outputName(),
_inputNames(),
- _command(VsmsummaryConfig::Fieldmap::NONE)
+ _command(VsmsummaryConfig::Fieldmap::Command::NONE)
{ }
DocsumTools::FieldSpec::~FieldSpec() = default;