summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--README.md4
-rw-r--r--annotations/src/main/java/com/yahoo/api/annotations/PublicApi.java2
-rw-r--r--annotations/src/main/java/com/yahoo/osgi/annotation/ExportPackage.java2
-rw-r--r--annotations/src/main/java/com/yahoo/osgi/annotation/Version.java2
-rw-r--r--annotations/src/main/resources/.gitignore1
-rw-r--r--application-deploy-plugin/pom.xml3
-rw-r--r--application/pom.xml20
-rw-r--r--application/src/main/java/com/yahoo/application/Application.java2
-rw-r--r--application/src/main/java/com/yahoo/application/container/handler/Headers.java2
-rw-r--r--application/src/test/java/com/yahoo/application/container/handler/HeadersTestCase.java2
-rw-r--r--athenz-identity-provider-service/pom.xml32
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslKeyStoreConfigurator.java123
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGenerator.java45
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentResource.java25
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/AthenzCertificateClient.java40
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceConfirmationResource.java3
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceRefreshResource.java44
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidator.java37
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGeneratorTest.java17
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java23
-rwxr-xr-xbootstrap.sh10
-rw-r--r--bundle-plugin-test/pom.xml21
-rw-r--r--bundle-plugin-test/src/test/java/com/yahoo/BundleIT.java6
-rw-r--r--bundle-plugin/pom.xml25
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/bundle/AnalyzeBundle.java94
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/bundle/TransformExportPackages.java62
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/Analyze.java87
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/AnalyzeClassVisitor.java162
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/AnalyzeFieldVisitor.java49
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/AnalyzeMethodVisitor.java168
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/AnalyzeSignatureVisitor.java119
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/ClassFileMetaData.java35
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/ExportPackageAnnotation.java62
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/ImportCollector.java35
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/PackageTally.java79
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/Packages.java43
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/Artifacts.java69
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/AssembleContainerPluginMojo.java163
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/GenerateBundleClassPathMappingsMojo.java115
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/GenerateOsgiManifestMojo.java314
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/GenerateSourcesMojo.java2
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/osgi/ExportPackageParser.java283
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/osgi/ExportPackages.java70
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/osgi/ImportPackages.java97
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/osgi/ProjectBundleClassPaths.java2
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/util/Files.java30
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/util/IO.java41
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/util/JarFiles.java36
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/util/Maps.java31
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/util/Strings.java26
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/util/ThrowingFunction.java11
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/bundle/AnalyzeBundle.scala72
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/bundle/TransformExportPackages.scala54
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/Analyze.scala28
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeClassVisitor.scala102
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeMethodVisitor.scala88
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeSignatureVisitor.scala68
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnnotationVisitorTrait.scala39
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AttributeVisitorTrait.scala15
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/ClassFileMetaData.scala10
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/ExportPackageAnnotation.scala24
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/PackageTally.scala46
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/Packages.scala27
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/SubVisitorTrait.scala19
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/package.scala27
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/mojo/Artifacts.scala29
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/mojo/AssembleContainerPluginMojo.scala116
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/mojo/GenerateBundleClassPathMappingsMojo.scala96
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/mojo/GenerateOsgiManifestMojo.scala286
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/osgi/ExportPackageParser.scala89
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/osgi/ExportPackages.scala27
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/osgi/ImportPackages.scala51
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/Extractors.scala17
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/Files.scala23
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/IO.scala46
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/Iteration.scala14
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/JarFiles.scala24
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/Maps.scala19
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/Strings.scala14
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/bundle/AnalyzeBundleTest.java85
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/AnalyzeClassTest.java167
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/AnalyzeMethodBodyTest.java73
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/TestUtilities.java40
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Base.java2
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/CatchException.java2
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/ClassAnnotation.java2
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/ClassReference.java3
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/ClassWithMethod.java2
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Derived.java2
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Dummy.java2
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/DummyAnnotation.java2
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Fields.java2
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Interface1.java2
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Interface2.java2
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/MethodAnnotation.java2
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/MethodInvocation.java3
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Methods.java3
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/mojo/GenerateSourcesMojoTest.java2
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/osgi/ExportPackageParserTest.java295
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/osgi/ImportPackageTest.java130
-rw-r--r--bundle-plugin/src/test/scala/com/yahoo/container/plugin/bundle/AnalyzeBundleTest.scala41
-rw-r--r--bundle-plugin/src/test/scala/com/yahoo/container/plugin/classanalysis/AnalyzeClassTest.scala118
-rw-r--r--bundle-plugin/src/test/scala/com/yahoo/container/plugin/classanalysis/AnalyzeMethodBodyTest.scala52
-rw-r--r--bundle-plugin/src/test/scala/com/yahoo/container/plugin/classanalysis/TestUtilities.scala18
-rw-r--r--bundle-plugin/src/test/scala/com/yahoo/container/plugin/osgi/ExportPackageParserTest.scala63
-rw-r--r--bundle-plugin/src/test/scala/com/yahoo/container/plugin/osgi/ImportPackageTest.scala95
-rw-r--r--bundle-plugin/src/test/scala/com/yahoo/container/plugin/util/IOTest.scala45
-rw-r--r--clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/FleetController.java6
-rw-r--r--clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/RemoteClusterControllerTask.java7
-rw-r--r--clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/VersionDependentTaskCompletion.java10
-rw-r--r--clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/restapiv2/Request.java4
-rw-r--r--clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/restapiv2/requests/SetNodeStateRequest.java27
-rw-r--r--clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/restapiv2/requests/SetNodeStatesForClusterRequest.java19
-rw-r--r--clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/restapiv2/requests/WantedStateSetter.java26
-rw-r--r--clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/restapiv2/SetNodeStateTest.java41
-rw-r--r--clustercontroller-utils/pom.xml5
-rw-r--r--clustercontroller-utils/src/main/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/requests/SetUnitStateRequest.java2
-rw-r--r--clustercontroller-utils/src/main/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/server/JsonReader.java7
-rw-r--r--clustercontroller-utils/src/main/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/server/RestApiHandler.java54
-rw-r--r--clustercontroller-utils/src/test/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/server/RestApiHandlerTest.java20
-rw-r--r--component/src/main/java/com/yahoo/container/util/Util.java2
-rw-r--r--config-application-package/src/main/java/com/yahoo/config/model/application/provider/Bundle.java2
-rw-r--r--config-application-package/src/main/java/com/yahoo/config/model/application/provider/MockFileRegistry.java2
-rw-r--r--config-application-package/src/main/java/com/yahoo/config/model/application/provider/PreGeneratedFileRegistry.java2
-rw-r--r--config-application-package/src/main/java/com/yahoo/config/model/application/provider/SchemaValidator.java2
-rw-r--r--config-application-package/src/test/java/com/yahoo/config/model/application/provider/PreGeneratedFileRegistryTestCase.java2
-rw-r--r--config-bundle/pom.xml7
-rw-r--r--config-class-plugin/src/main/java/com/yahoo/vespa/CloverChecker.java2
-rw-r--r--config-model-api/pom.xml1
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationMetaData.java1
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/application/api/ComponentInfo.java2
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/application/api/FileRegistry.java2
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/application/api/UnparsedConfigDefinition.java3
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeAction.java1
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRefeedAction.java1
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRestartAction.java1
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ConfigDefinitionRepo.java3
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ConfigDefinitionStore.java3
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ConfigModelPlugin.java2
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ConfigServerSpec.java2
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/FileDistribution.java13
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/HostInfo.java2
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/HostProvisioner.java3
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java2
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ModelCreateResult.java1
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ModelFactory.java9
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ModelState.java2
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ServiceInfo.java3
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ValidationParameters.java50
-rw-r--r--config-model-api/src/test/java/com/yahoo/config/application/api/ApplicationFileTest.java4
-rw-r--r--config-model-fat/pom.xml55
-rw-r--r--config-model/pom.xml2
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/ConfigModelInstanceFactory.java3
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/deploy/DeployProperties.java30
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java17
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/producer/AbstractConfigProducerRoot.java2
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/provision/InMemoryProvisioner.java21
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/test/MockRoot.java2
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/test/TestDriver.java2
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java44
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/derived/AttributeFields.java22
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/derived/Juniperrc.java2
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/document/ComplexAttributeFieldUtils.java77
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/AttributeOperation.java16
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/parser/SimpleCharStream.java2
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/processing/ImplicitSummaries.java2
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/processing/IndexingInputs.java2
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/processing/IndexingOutputs.java2
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/processing/IndexingValidation.java2
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/processing/ReservedDocumentNames.java2
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/processing/TextMatch.java2
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/processing/TypedTransformProvider.java2
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/processing/ValidateFieldTypes.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/documentmodel/SummaryTransform.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/Client.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/ConfigProducerRoot.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java3
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java23
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/Configserver.java29
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/ZooKeepersConfigProvider.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java34
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidator.java85
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/SearchDataTypeValidator.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java33
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidator.java6
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/StructFieldAttributeChangeValidator.java132
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/BinaryScaledAmountParser.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/BinaryUnit.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminBuilderBase.java4
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV2Builder.java22
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java11
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomComponentBuilder.java4
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomFilterBuilder.java4
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomHandlerBuilder.java12
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java55
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/ServletBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/ChainSpecificationBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/ChainedComponentModelBuilder.java4
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/ChainsBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/ComponentsBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/DependenciesBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/DomBuilderCreator.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/DomChainBuilderBase.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/DomChainsBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/GenericChainedComponentModelBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/InheritanceBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomFederationSearcherBuilder.java3
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomGenericTargetBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomProviderBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearchChainBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearchChainsBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearcherBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSourceBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/FederationOptionsBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/SearchChainsBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/TimeParser.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/ContainerModel.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/component/AccessLogComponent.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/component/Component.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/component/ComponentGroup.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/component/ConfigProducerGroup.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/component/FileStatusHandlerComponent.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/component/HttpFilter.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/component/StatisticsComponent.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/Chain.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/ChainedComponent.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/ChainedComponentConfigGenerator.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/Chains.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/ChainsConfigGenerator.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/configserver/option/package-info.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/Filter.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/FilterChains.java2
-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/http/xml/FilterBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/FilterChainBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/FilterChainsBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/HttpBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/jersey/Jersey2Servlet.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/GUIHandler.java27
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/FederationSearcher.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/GenericTarget.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/HttpProvider.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/HttpProviderSearcher.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/LocalProvider.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/Provider.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/SearchChain.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/SearchChains.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/Searcher.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/Source.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/SourceGroup.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/SourceGroupRegistry.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/defaultsearchchains/LocalClustersCreator.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/defaultsearchchains/VespaSearchChainsCreator.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/AccessLogBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilder.java10
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java28
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerServiceBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/InconsistentSchemaAndCodeError.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearch.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/SearchCoverage.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/StorageGroup.java4
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/TuningDispatch.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java4
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomContentSearchBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomSearchCoverageBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/routing/DocumentProtocol.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/routing/Protocol.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/routing/Routing.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/search/SearchColumn.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/search/SearchDefinition.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/utils/FreezableMap.java2
-rw-r--r--config-model/src/main/javacc/SDParser.jj4
-rwxr-xr-xconfig-model/src/main/perl/vespa-deploy7
-rw-r--r--config-model/src/test/derived/array_of_struct_attribute/attributes.cfg2
-rw-r--r--config-model/src/test/derived/array_of_struct_attribute/test.sd1
-rw-r--r--config-model/src/test/derived/map_attribute/attributes.cfg2
-rw-r--r--config-model/src/test/derived/map_attribute/test.sd1
-rw-r--r--config-model/src/test/derived/map_of_struct_attribute/attributes.cfg4
-rw-r--r--config-model/src/test/derived/map_of_struct_attribute/test.sd2
-rw-r--r--config-model/src/test/java/com/yahoo/config/model/ApplicationDeployTest.java6
-rw-r--r--config-model/src/test/java/com/yahoo/config/model/MockModelContext.java6
-rw-r--r--config-model/src/test/java/com/yahoo/config/model/builder/xml/test/DomBuilderTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/config/model/deploy/DeployStateTest.java16
-rw-r--r--config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/config/model/test/MockHosts.java2
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/RankingExpressionInliningTestCase.java95
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/derived/AttributeListTestCase.java25
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/derived/IndexSchemaTestCase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/document/ComplexAttributeFieldUtilsTestCase.java191
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/AssertIndexingScript.java2
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/AssertSearchBuilder.java2
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/ImplicitSummariesTestCase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingInputsTestCase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingOutputsTestCase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingScriptRewriterTestCase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingValidationTestCase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingValuesTestCase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/ReservedDocumentNamesTestCase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/VespaModelFactoryTest.java13
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/admin/AdminTestCase.java4
-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/application/validation/ComplexAttributeFieldsValidatorTestCase.java71
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/DeploymentFileValidatorTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/NoPrefixForIndexesTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/SearchDataTypeValidatorTestCase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/SecretStoreValidatorTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ConfigChangeTestUtils.java4
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidatorTest.java12
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/StructFieldAttributeChangeValidatorTestCase.java146
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/first/AccessControlValidatorTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/chains/DependenciesBuilderTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomFederationSearcherBuilderTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearchChainsBuilderTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearcherBuilderTest.java2
-rwxr-xr-xconfig-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java6
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/configserver/ConfigserverClusterTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/http/FilterChainsTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/FederationTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/SearchChainsTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/SearchChainsTestBase.java2
-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/xml/BundleInstantiationSpecificationBuilderTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.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.java27
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/ContentSearchTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/SearchCoverageTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/TuningDispatchTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/cluster/ClusterTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/cluster/DomContentSearchBuilderTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/cluster/DomSearchCoverageBuilderTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/cluster/DomTuningDispatchBuilderTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/utils/ContentClusterUtils.java6
-rwxr-xr-xconfig-model/src/test/java/com/yahoo/vespa/model/routing/test/RoutingTestCase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/search/TldTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/test/VespaModelTestCase.java12
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/test/VespaModelTester.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/test/utils/CommonVespaModelSetup.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/test/utils/VespaModelCreatorWithFilePkg.java11
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/test/utils/VespaModelCreatorWithMockPkg.java11
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/Capacity.java54
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java13
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/Deployer.java39
-rw-r--r--config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ClientUpdater.java1
-rw-r--r--config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ConfigSourceClient.java2
-rw-r--r--config-proxy/src/main/java/com/yahoo/vespa/config/proxy/DelayedResponse.java6
-rw-r--r--config-proxy/src/main/java/com/yahoo/vespa/config/proxy/DelayedResponses.java1
-rw-r--r--config-proxy/src/main/java/com/yahoo/vespa/config/proxy/MemoryCache.java2
-rw-r--r--config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ProxyServer.java2
-rw-r--r--config-proxy/src/main/java/com/yahoo/vespa/config/proxy/RpcConfigSourceClient.java3
-rw-r--r--config-proxy/src/main/java/com/yahoo/vespa/config/proxy/Subscriber.java1
-rw-r--r--config-proxy/src/main/java/com/yahoo/vespa/config/proxy/UpstreamConfigSubscriber.java10
-rwxr-xr-xconfig-proxy/src/main/sh/vespa-config-ctl.sh4
-rwxr-xr-xconfig/pom.xml7
-rw-r--r--config/src/apps/vespa-get-config/getconfig.cpp6
-rw-r--r--config/src/main/java/com/yahoo/config/subscription/ConfigURI.java3
-rw-r--r--config/src/main/java/com/yahoo/config/subscription/FileSource.java6
-rw-r--r--config/src/main/java/com/yahoo/config/subscription/impl/ConfigSubscription.java1
-rw-r--r--config/src/main/java/com/yahoo/config/subscription/impl/GenericConfigHandle.java11
-rw-r--r--config/src/main/java/com/yahoo/config/subscription/impl/GenericConfigSubscriber.java2
-rw-r--r--config/src/main/java/com/yahoo/config/subscription/impl/GenericJRTConfigSubscription.java11
-rw-r--r--config/src/main/java/com/yahoo/config/subscription/impl/JRTConfigSubscription.java1
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/ConfigCacheKey.java4
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/ConfigFileFormat.java2
-rwxr-xr-xconfig/src/main/java/com/yahoo/vespa/config/ConfigKey.java1
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/ConfigPayloadApplier.java2
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/ConfigTransformer.java2
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/GetConfigRequest.java7
-rwxr-xr-xconfig/src/main/java/com/yahoo/vespa/config/RawConfig.java13
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/protocol/JRTConfigRequest.java15
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/protocol/SlimeRequestData.java7
-rw-r--r--config/src/vespa/config/frt/frtconfigresponsev3.cpp2
-rw-r--r--config/src/vespa/config/print/fileconfigformatter.cpp5
-rw-r--r--configd/src/apps/sentinel/config-handler.cpp3
-rw-r--r--configd/src/apps/sentinel/service.cpp29
-rw-r--r--configdefinitions/src/vespa/configserver.def12
-rw-r--r--configserver/pom.xml12
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java219
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/ConfigServerBootstrap.java89
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/ConfigServerDB.java1
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/ConfigServerSpec.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/GlobalComponentRegistry.java1
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/InjectedGlobalComponentRegistry.java9
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/ReloadHandler.java3
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/ReloadListener.java5
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/SuperModelRequestHandler.java3
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java86
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/application/PermanentApplicationPackage.java5
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java18
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java13
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/TenantFileSystemDirs.java5
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClient.java4
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileServer.java64
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpConfigResponse.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpGetConfigHandler.java2
-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/ListTenantsResponse.java19
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/TenantHandler.java4
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ConfigServerMaintenance.java19
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/FileDistributionMaintainer.java8
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/Maintainer.java5
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/SessionsMaintainer.java35
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/TenantsMaintainer.java6
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ZooKeeperDataMaintainer.java16
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java31
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ModelsBuilder.java79
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/PreparedModelsBuilder.java12
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessor.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java5
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSession.java15
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionRepo.java58
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionStateWatcher.java76
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java19
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSession.java32
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionRepo.java77
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionStateWatcher.java (renamed from configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionStateWatcher.java)22
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java7
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionFactoryImpl.java16
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java7
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepo.java33
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionZooKeeperClient.java14
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SilentDeployLogger.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantBuilder.java6
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandler.java3
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/SessionCounter.java1
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ZKLiveApp.java10
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/package-info.java2
-rw-r--r--configserver/src/main/resources/configserver-app/services.xml1
-rwxr-xr-xconfigserver/src/main/sh/start-configserver2
-rw-r--r--configserver/src/test/apps/hosted-no-write-access-control/searchdefinitions/music.sd10
-rw-r--r--configserver/src/test/apps/hosted-no-write-access-control/services.xml25
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java127
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/ConfigServerBootstrapTest.java97
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/InjectedGlobalComponentRegistryTest.java3
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java5
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/ModelFactoryRegistryTest.java3
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/TestComponentRegistry.java8
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/TestConfigDefinitionRepo.java6
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/application/ConfigConvergenceCheckerTest.java262
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployTester.java73
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java59
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/deploy/MockDeployer.java18
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/deploy/RedeployTest.java52
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/filedistribution/FileDirectoryTest.java30
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/SessionHandlerTest.java3
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java24
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionActiveHandlerTest.java2
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java3
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareHandlerTest.java7
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/maintenance/TenantsMaintainerTest.java19
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/model/LbServicesProducerTest.java2
-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/model/TestModelFactory.java5
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/provision/StaticProvisionerTest.java2
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionRepoTest.java2
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionTest.java6
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/MockFileDistributionFactory.java6
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionTest.java4
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionPreparerTest.java79
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/zookeeper/ZKApplicationPackageTest.java2
-rw-r--r--container-accesslogging/src/main/java/com/yahoo/container/logging/AccessLog.java2
-rw-r--r--container-accesslogging/src/main/java/com/yahoo/container/logging/AccessLogEntry.java2
-rw-r--r--container-accesslogging/src/main/java/com/yahoo/container/logging/AccessLogInterface.java2
-rw-r--r--container-accesslogging/src/main/java/com/yahoo/container/logging/JSONAccessLog.java2
-rw-r--r--container-accesslogging/src/main/java/com/yahoo/container/logging/YApacheFormatter.java2
-rw-r--r--container-accesslogging/src/test/java/com/yahoo/container/logging/YApacheLogTestCase.java2
-rw-r--r--container-core/pom.xml10
-rw-r--r--container-core/src/main/java/com/yahoo/container/config/StatisticsRequestHandler.java4
-rw-r--r--container-core/src/main/java/com/yahoo/container/core/config/BundleLoader.java2
-rw-r--r--container-core/src/main/java/com/yahoo/container/core/config/HandlersConfigurerDi.java20
-rw-r--r--container-core/src/main/java/com/yahoo/container/core/config/testutil/HandlersConfigurerTestWrapper.java8
-rw-r--r--container-core/src/main/java/com/yahoo/container/core/slobrok/SlobrokConfigurator.java2
-rw-r--r--container-core/src/main/java/com/yahoo/container/handler/AccessLogRequestHandler.java2
-rw-r--r--container-core/src/main/java/com/yahoo/container/handler/ThreadPoolProvider.java22
-rw-r--r--container-core/src/main/java/com/yahoo/container/handler/test/MockService.java2
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/RequestHandlerTestDriver.java18
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/ThreadedRequestHandler.java2
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/VespaHeaders.java2
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/CountMetric.java2
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/GaugeMetric.java2
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/MetricDimensions.java2
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/MetricSet.java2
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/MetricValue.java2
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/StateMetricConsumer.java2
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/StateMonitor.java2
-rw-r--r--container-core/src/main/java/com/yahoo/osgi/MockOsgi.java2
-rw-r--r--container-core/src/main/java/com/yahoo/osgi/Osgi.java2
-rw-r--r--container-core/src/main/java/com/yahoo/osgi/OsgiImpl.java2
-rw-r--r--container-core/src/main/java/com/yahoo/processing/handler/AbstractProcessingHandler.java2
-rw-r--r--container-core/src/main/java/com/yahoo/processing/handler/ProcessingHandler.java2
-rw-r--r--container-core/src/main/java/com/yahoo/processing/rendering/Renderer.java2
-rw-r--r--container-core/src/main/java/com/yahoo/restapi/Path.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/Path.java)6
-rw-r--r--container-core/src/main/java/com/yahoo/restapi/package-info.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/restapi/impl/package-info.java)7
-rw-r--r--container-core/src/test/java/com/yahoo/container/jdisc/ThreadedRequestHandlerTestCase.java2
-rw-r--r--container-core/src/test/java/com/yahoo/container/jdisc/state/MetricConsumerProviders.java2
-rw-r--r--container-core/src/test/java/com/yahoo/container/jdisc/state/StateMonitorBenchmarkTest.java2
-rw-r--r--container-core/src/test/java/com/yahoo/processing/handler/ProcessingHandlerTestCase.java2
-rw-r--r--container-core/src/test/java/com/yahoo/restapi/PathTest.java (renamed from controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/PathTest.java)15
-rw-r--r--container-dependencies-enforcer/pom.xml9
-rw-r--r--container-dependency-versions/pom.xml31
-rw-r--r--container-dev/pom.xml40
-rw-r--r--container-di/pom.xml46
-rw-r--r--container-di/src/main/java/com/yahoo/container/bundle/MockBundle.java264
-rw-r--r--container-di/src/main/java/com/yahoo/container/di/CloudSubscriberFactory.java148
-rw-r--r--container-di/src/main/java/com/yahoo/container/di/ComponentDeconstructor.java2
-rw-r--r--container-di/src/main/java/com/yahoo/container/di/ConfigRetriever.java208
-rw-r--r--container-di/src/main/java/com/yahoo/container/di/Container.java268
-rw-r--r--container-di/src/main/java/com/yahoo/container/di/Osgi.java43
-rw-r--r--container-di/src/main/java/com/yahoo/container/di/componentgraph/core/ComponentGraph.java407
-rw-r--r--container-di/src/main/java/com/yahoo/container/di/componentgraph/core/ComponentNode.java304
-rw-r--r--container-di/src/main/java/com/yahoo/container/di/componentgraph/core/ComponentRegistryNode.java105
-rw-r--r--container-di/src/main/java/com/yahoo/container/di/componentgraph/core/Exceptions.java45
-rw-r--r--container-di/src/main/java/com/yahoo/container/di/componentgraph/core/GuiceNode.java78
-rw-r--r--container-di/src/main/java/com/yahoo/container/di/componentgraph/core/JerseyNode.java92
-rw-r--r--container-di/src/main/java/com/yahoo/container/di/componentgraph/core/Keys.java37
-rw-r--r--container-di/src/main/java/com/yahoo/container/di/componentgraph/core/Node.java164
-rw-r--r--container-di/src/main/java/com/yahoo/container/di/config/RestApiContext.java4
-rw-r--r--container-di/src/main/java/com/yahoo/container/di/osgi/BundleClasses.java27
-rw-r--r--container-di/src/main/java/com/yahoo/container/di/osgi/OsgiUtil.java168
-rw-r--r--container-di/src/main/java/com/yahoo/container/di/osgi/package-info.java2
-rw-r--r--container-di/src/main/scala/com/yahoo/container/bundle/MockBundle.scala94
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/CloudSubscriberFactory.scala104
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/ConfigRetriever.scala135
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/Container.scala265
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/Osgi.scala40
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentGraph.scala334
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentNode.scala213
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentRegistryNode.scala71
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/GuiceNode.scala41
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/JerseyNode.scala92
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/Node.scala117
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/osgi/OsgiUtil.scala143
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/package.scala44
-rw-r--r--container-di/src/test/java/com/yahoo/component/provider/test/ComponentRegistryTestCase.java2
-rw-r--r--container-di/src/test/java/com/yahoo/container/di/ConfigRetrieverTest.java137
-rw-r--r--container-di/src/test/java/com/yahoo/container/di/ContainerTest.java378
-rw-r--r--container-di/src/test/java/com/yahoo/container/di/ContainerTestBase.java120
-rw-r--r--container-di/src/test/java/com/yahoo/container/di/DirConfigSource.java69
-rw-r--r--container-di/src/test/java/com/yahoo/container/di/componentgraph/core/ComponentGraphTest.java674
-rw-r--r--container-di/src/test/java/com/yahoo/container/di/componentgraph/core/JerseyNodeTest.java68
-rw-r--r--container-di/src/test/java/com/yahoo/container/di/componentgraph/core/ReuseComponentsTest.java254
-rw-r--r--container-di/src/test/java/demo/Base.java6
-rw-r--r--container-di/src/test/java/demo/ComponentConfigTest.java2
-rw-r--r--container-di/src/test/java/demo/ComponentRegistryTest.java2
-rw-r--r--container-di/src/test/java/demo/ContainerTestBase.java71
-rw-r--r--container-di/src/test/java/demo/DeconstructTest.java3
-rw-r--r--container-di/src/test/java/demo/FallbackToGuiceInjectorTest.java2
-rw-r--r--container-di/src/test/scala/com/yahoo/container/di/ConfigRetrieverTest.scala107
-rw-r--r--container-di/src/test/scala/com/yahoo/container/di/ContainerTest.scala398
-rw-r--r--container-di/src/test/scala/com/yahoo/container/di/DirConfigSource.scala50
-rw-r--r--container-di/src/test/scala/com/yahoo/container/di/componentgraph/core/ComponentGraphTest.scala540
-rw-r--r--container-di/src/test/scala/com/yahoo/container/di/componentgraph/core/JerseyNodeTest.scala66
-rw-r--r--container-di/src/test/scala/com/yahoo/container/di/componentgraph/core/ReuseComponentsTest.scala249
-rw-r--r--container-disc/pom.xml1
-rw-r--r--container-disc/src/main/java/com/yahoo/container/FilterConfigProvider.java2
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java23
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/DisableOsgiFramework.java2
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/component/Deconstructor.java4
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/metric/DisableGuiceMetric.java2
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/metric/ForwardingMetricConsumer.java2
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProvider.java2
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricProvider.java2
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/metric/state/StateMetricConsumerFactory.java2
-rwxr-xr-xcontainer-disc/src/main/sh/vespa-start-container-daemon.sh8
-rw-r--r--container-disc/src/test/java/com/yahoo/container/jdisc/ContainerThreadFactoryTest.java2
-rw-r--r--container-disc/src/test/java/com/yahoo/container/jdisc/metric/ForwardingMetricConsumerTest.java2
-rw-r--r--container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerFactories.java2
-rw-r--r--container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderTest.java4
-rw-r--r--container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviders.java2
-rw-r--r--container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricProviderTest.java2
-rw-r--r--container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricProviders.java2
-rw-r--r--container-integration-test/.gitignore2
-rw-r--r--container-integration-test/OWNERS1
-rw-r--r--container-integration-test/README.md9
-rw-r--r--container-integration-test/pom.xml44
-rw-r--r--container-integration-test/src/test/java/com/yahoo/search/query/gui/GUIHandlerTest.java84
-rw-r--r--container-jersey2/pom.xml20
-rw-r--r--container-jersey2/src/main/java/com/yahoo/container/jaxrs/annotation/Component.java2
-rw-r--r--container-jersey2/src/main/java/com/yahoo/container/jaxrs/annotation/package-info.java2
-rw-r--r--container-jersey2/src/main/java/com/yahoo/container/servlet/jersey/util/ResourceConfigUtil.java2
-rw-r--r--container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/AbstractResource.java2
-rw-r--r--container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/DummyAnnotation.java2
-rw-r--r--container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/InnerClass.java2
-rw-r--r--container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/InterfaceResource.java2
-rw-r--r--container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/NestedClass.java2
-rw-r--r--container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/NonPublicNestedClass.java2
-rw-r--r--container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/Provider.java2
-rw-r--r--container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/Resource.java2
-rw-r--r--container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/ResourceOrProviderClassVisitorTest.java78
-rw-r--r--container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/ResourceOrProviderClassVisitorTest.scala75
-rw-r--r--container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/ResourceWithMultipleAnnotations.java2
-rw-r--r--container-messagebus/src/main/java/com/yahoo/container/jdisc/messagebus/MbusClientProvider.java2
-rw-r--r--container-messagebus/src/main/java/com/yahoo/container/jdisc/messagebus/MbusServerProvider.java2
-rw-r--r--container-search-gui/.gitignore2
-rw-r--r--container-search-gui/CMakeLists.txt2
-rw-r--r--container-search-gui/OWNERS1
-rw-r--r--container-search-gui/README1
-rw-r--r--container-search-gui/pom.xml88
-rw-r--r--container-search-gui/src/main/java/com/yahoo/search/query/gui/GUIHandler.java210
-rw-r--r--container-search-gui/src/main/java/com/yahoo/search/query/restapi/ErrorResponse.java80
-rw-r--r--container-search-gui/src/main/resources/gui/_includes/css/agency.css876
-rw-r--r--container-search-gui/src/main/resources/gui/_includes/css/bootstrap.min.css7
-rw-r--r--container-search-gui/src/main/resources/gui/_includes/css/vespa.css653
-rw-r--r--container-search-gui/src/main/resources/gui/_includes/index.html987
-rw-r--r--container-search-gui/src/main/resources/gui/css/font-awesome/css/font-awesome.css2086
-rw-r--r--container-search-gui/src/main/resources/gui/css/font-awesome/css/font-awesome.min.css4
-rw-r--r--container-search-gui/src/main/resources/gui/css/font-awesome/fonts/FontAwesome.otfbin0 -> 109688 bytes
-rw-r--r--container-search-gui/src/main/resources/gui/css/font-awesome/fonts/fontawesome-webfont.eotbin0 -> 70807 bytes
-rw-r--r--container-search-gui/src/main/resources/gui/css/font-awesome/fonts/fontawesome-webfont.svg655
-rw-r--r--container-search-gui/src/main/resources/gui/css/font-awesome/fonts/fontawesome-webfont.ttfbin0 -> 142072 bytes
-rw-r--r--container-search-gui/src/main/resources/gui/css/font-awesome/fonts/fontawesome-webfont.woffbin0 -> 83588 bytes
-rw-r--r--container-search-gui/src/main/resources/gui/css/font-awesome/fonts/fontawesome-webfont.woff2bin0 -> 66624 bytes
-rwxr-xr-xcontainer-search-gui/src/main/resources/gui/editarea/edit_area/edit_area.css545
-rw-r--r--container-search-gui/src/main/resources/gui/editarea/edit_area/edit_area_full.js52
-rwxr-xr-xcontainer-search-gui/src/main/resources/gui/editarea/edit_area/langs/en.js62
-rw-r--r--container-search-gui/src/main/resources/gui/editarea/edit_area/plugins/autocompletion/autocompletion.js510
-rw-r--r--container-search-gui/src/main/resources/gui/editarea/edit_area/plugins/autocompletion/langs/en.js0
-rwxr-xr-xcontainer-search-gui/src/main/resources/gui/editarea/edit_area/reg_syntax/yql.js109
-rw-r--r--container-search-gui/src/main/resources/gui/icons/android-chrome-192x192.pngbin0 -> 10282 bytes
-rw-r--r--container-search-gui/src/main/resources/gui/icons/android-chrome-384x384.pngbin0 -> 25683 bytes
-rw-r--r--container-search-gui/src/main/resources/gui/icons/apple-touch-icon.pngbin0 -> 7357 bytes
-rw-r--r--container-search-gui/src/main/resources/gui/icons/browserconfig.xml10
-rw-r--r--container-search-gui/src/main/resources/gui/icons/favicon-16x16.pngbin0 -> 1168 bytes
-rw-r--r--container-search-gui/src/main/resources/gui/icons/favicon-32x32.pngbin0 -> 1672 bytes
-rw-r--r--container-search-gui/src/main/resources/gui/icons/favicon.icobin0 -> 15086 bytes
-rw-r--r--container-search-gui/src/main/resources/gui/icons/manifest.json18
-rw-r--r--container-search-gui/src/main/resources/gui/icons/mstile-150x150.pngbin0 -> 6854 bytes
-rw-r--r--container-search-gui/src/main/resources/gui/icons/safari-pinned-tab.svg22
-rw-r--r--container-search-gui/src/main/resources/gui/img/Vespa-V2.pngbin0 -> 29139 bytes
-rw-r--r--container-search-gui/src/main/resources/gui/img/VespaIcon.pngbin0 -> 19776 bytes
-rw-r--r--container-search-gui/src/main/resources/gui/img/copy.svg7
-rw-r--r--container-search-gui/src/main/resources/gui/img/down-arrow.svg1
-rw-r--r--container-search-gui/src/main/resources/gui/img/features-help.pngbin0 -> 2554 bytes
-rw-r--r--container-search-gui/src/main/resources/gui/img/information.svg10
-rw-r--r--container-search-gui/src/main/resources/gui/img/paste.svg6
-rw-r--r--container-search-gui/src/main/resources/gui/img/reload.svg6
-rw-r--r--container-search-gui/src/main/resources/gui/js/agency.js39
-rw-r--r--container-search-gui/src/main/resources/gui/js/bootstrap.js2114
-rw-r--r--container-search-gui/src/main/resources/gui/js/bootstrap.min.js6
-rw-r--r--container-search-gui/src/main/resources/gui/js/classie.js83
-rw-r--r--container-search-gui/src/main/resources/gui/js/jquery-1.11.0.js4
-rw-r--r--container-search-gui/src/main/resources/gui/js/jquery.easing.min.js44
-rw-r--r--container-search/src/main/java/com/yahoo/fs4/DocumentInfo.java6
-rw-r--r--container-search/src/main/java/com/yahoo/fs4/HexByteIterator.java2
-rw-r--r--container-search/src/main/java/com/yahoo/fs4/PacketDumper.java2
-rw-r--r--container-search/src/main/java/com/yahoo/fs4/PacketListener.java2
-rw-r--r--container-search/src/main/java/com/yahoo/fs4/PacketNotificationsBroadcaster.java2
-rw-r--r--container-search/src/main/java/com/yahoo/fs4/PacketQueryTracer.java2
-rw-r--r--container-search/src/main/java/com/yahoo/fs4/QueryResultPacket.java2
-rw-r--r--container-search/src/main/java/com/yahoo/fs4/mplex/Backend.java20
-rw-r--r--container-search/src/main/java/com/yahoo/fs4/mplex/FS4Channel.java18
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/cluster/ClusterSearcher.java3
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/fastsearch/CacheControl.java8
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/fastsearch/ClusterParams.java13
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/fastsearch/FS4ResourcePool.java6
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/fastsearch/FastHit.java15
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/fastsearch/FastSearcher.java11
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/fastsearch/PacketWrapper.java20
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/fastsearch/VespaBackEndSearcher.java17
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/query/WeakAndItem.java2
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/query/parser/CustomParser.java2
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/query/parser/ProgrammaticParser.java2
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/query/textualrepresentation/Discloser.java2
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/query/textualrepresentation/TextualQueryRepresentation.java2
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/querytransform/RecallSearcher.java4
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/searcher/MultipleResultsSearcher.java2
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/templates/HitContext.java2
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/templates/LogExceptionUserTemplateDelegator.java2
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/templates/SearchRendererAdaptor.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/Query.java34
-rw-r--r--container-search/src/main/java/com/yahoo/search/config/dispatchprototype/package-info.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/Dispatcher.java3
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/SearchCluster.java9
-rw-r--r--container-search/src/main/java/com/yahoo/search/federation/http/ConfiguredHTTPClientSearcher.java1
-rw-r--r--container-search/src/main/java/com/yahoo/search/federation/http/ConfiguredHTTPProviderSearcher.java1
-rw-r--r--container-search/src/main/java/com/yahoo/search/federation/http/HTTPClientSearcher.java1
-rw-r--r--container-search/src/main/java/com/yahoo/search/federation/http/HTTPProviderSearcher.java1
-rw-r--r--container-search/src/main/java/com/yahoo/search/federation/http/HTTPSearcher.java1
-rw-r--r--container-search/src/main/java/com/yahoo/search/federation/selection/FederationTarget.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/federation/selection/TargetSelector.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainInvocationSpec.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainResolver.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/federation/sourceref/SingleTarget.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/federation/sourceref/SourceRefResolver.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/federation/sourceref/Target.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/federation/sourceref/UnresolvedProviderException.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/federation/sourceref/UnresolvedSearchChainException.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/federation/sourceref/UnresolvedSourceRefException.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/federation/vespa/ResultBuilder.java3
-rw-r--r--container-search/src/main/java/com/yahoo/search/federation/vespa/VespaSearcher.java3
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/Continuation.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/GroupingQueryParser.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/GroupingRequest.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/GroupingValidator.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/UnavailableAttributeException.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/AddFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/AggregatorNode.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/AllOperation.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/AndFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/ArrayAtLookup.java3
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/AttributeFunction.java7
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/AttributeValue.java7
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/AvgAggregator.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/AvgFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/BucketResolver.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/BucketValue.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/CatFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/ConstantValue.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/CountAggregator.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/DateFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/DayOfMonthFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/DayOfWeekFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/DayOfYearFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/DivFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/DocIdNsSpecificValue.java3
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/DocumentValue.java3
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/DoubleBucket.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/DoublePredefined.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/DoubleValue.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/EachOperation.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/ExpressionVisitor.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/FixedWidthFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/FunctionNode.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/GroupingExpression.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/GroupingNode.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/GroupingOperation.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/HourOfDayFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/LongBucket.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/LongPredefined.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/LongValue.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathResolver.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MaxAggregator.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MaxFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/Md5Function.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MinAggregator.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MinFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MinuteOfHourFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/ModFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MonthOfYearFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MulFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/NegFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/NowFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/OrFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/PredefinedFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/RelevanceValue.java3
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/SecondOfMinuteFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/SizeFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/StrCatFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/StrLenFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/StringBucket.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/StringPredefined.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/StringValue.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/SubFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/SumAggregator.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/SummaryValue.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/TimeFunctions.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/XorAggregator.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/XorBitFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/XorFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/YearFunction.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/YmumValue.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/parser/GroupingParserInput.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/result/AbstractList.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/result/BucketGroupId.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/result/DoubleBucketId.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/result/DoubleId.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/result/Group.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/result/GroupId.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/result/GroupList.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/result/HitList.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/result/HitRenderer.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/result/LongBucketId.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/result/LongId.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/result/NullId.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/result/RawId.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/result/RootGroup.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/result/RootId.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/result/StringBucketId.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/result/StringId.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/result/ValueGroupId.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/vespa/CompositeContinuation.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/vespa/ContinuationDecoder.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/vespa/EncodableContinuation.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/vespa/ExpressionConverter.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/vespa/GroupingExecutor.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/vespa/GroupingTransform.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/vespa/HitConverter.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/vespa/IntegerDecoder.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/vespa/IntegerEncoder.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/vespa/OffsetContinuation.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/vespa/RequestBuilder.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/vespa/ResultBuilder.java4
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/vespa/ResultId.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/handler/SearchHandler.java76
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/Model.java8
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/parser/Parsable.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/parser/Parser.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/parser/ParserEnvironment.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/parser/ParserFactory.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/config/QueryProfileConfigurer.java72
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/textserialize/TextSerialize.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/textserialize/item/AndNotRestConverter.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/textserialize/item/CompositeConverter.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/textserialize/item/IntConverter.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemArguments.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemContext.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemExecutorRegistry.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemFormConverter.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemFormHandler.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemInitializer.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/textserialize/item/ListUtil.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/textserialize/item/NearConverter.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/textserialize/item/PrefixConverter.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/textserialize/item/SubStringConverter.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/textserialize/item/SuffixConverter.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/textserialize/item/TermConverter.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/textserialize/item/TypeCheck.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/textserialize/item/WordConverter.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/textserialize/parser/DispatchFormHandler.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/textserialize/serializer/DispatchForm.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/textserialize/serializer/ItemIdMapper.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/textserialize/serializer/QueryTreeSerializer.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/textserialize/serializer/Serializer.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/rendering/DefaultRenderer.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java37
-rw-r--r--container-search/src/main/java/com/yahoo/search/rendering/Renderer.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/rendering/SectionedRenderer.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/rendering/SyncDefaultRenderer.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/result/MetaHitsFirstComparator.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/result/Relevance.java4
-rw-r--r--container-search/src/main/java/com/yahoo/search/searchchain/model/VespaSearchers.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/searchchain/model/federation/FederationOptions.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/searchchain/model/federation/FederationSearcherModel.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/searchchain/model/federation/HttpProviderSpec.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/searchchain/model/federation/LocalProviderSpec.java5
-rw-r--r--container-search/src/main/java/com/yahoo/search/searchers/ContainerLatencySearcher.java37
-rw-r--r--container-search/src/main/java/com/yahoo/search/yql/VespaGroupingStep.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/yql/YqlParser.java2
-rw-r--r--container-search/src/main/javacc/com/yahoo/search/query/textserialize/parser/Parser.jj2
-rw-r--r--container-search/src/test/java/com/yahoo/fs4/test/HexByteIteratorTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/IndexFactsFactory.java2
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/fastsearch/SlimeSummaryTestCase.java29
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/fastsearch/partial-summary3.cfg10
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/fastsearch/test/FastSearcherTestCase.java10
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/fastsearch/test/FastSearcherTester.java3
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/fastsearch/test/fs4mock/MockFS4ResourcePool.java3
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/query/parser/TestLinguistics.java2
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/query/textualrepresentation/test/TextualQueryRepresentationTestCase.java2
-rwxr-xr-xcontainer-search/src/test/java/com/yahoo/prelude/querytransform/test/RecallSearcherTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/searcher/test/MultipleResultsTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/federation/sourceref/test/SearchChainResolverTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/federation/sourceref/test/SourceRefResolverTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/federation/test/AddHitsWithRelevanceSearcher.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/federation/test/BlockingSearcher.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/federation/test/FederationSearcherTest.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/federation/test/FederationTester.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/federation/test/HitCountTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/federation/test/SetHitCountsSearcher.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/ContinuationTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/GroupingQueryParserTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/GroupingRequestTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/GroupingValidatorTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/request/BucketResolverTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/request/ExpressionVisitorTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/request/GroupingOperationTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/request/MathResolverTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/request/RequestTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/request/parser/GroupingParserBenchmarkTest.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/request/parser/GroupingParserTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/result/GroupIdTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/result/GroupListTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/result/GroupTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/result/HitListTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/result/HitRendererTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/vespa/CompositeContinuationTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/vespa/GroupingExecutorTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/vespa/GroupingTransformTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/vespa/HitConverterTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/vespa/IntegerDecoderTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/vespa/IntegerEncoderTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/vespa/OffsetContinuationTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/vespa/RequestBuilderTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/vespa/ResultBuilderTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/grouping/vespa/ResultIdTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/handler/test/JSONSearchHandlerTestCase.java466
-rw-r--r--container-search/src/test/java/com/yahoo/search/handler/test/SearchHandlerTestCase.java8
-rw-r--r--container-search/src/test/java/com/yahoo/search/pagetemplates/engine/test/MapSectionsToSectionsResult.xml6
-rw-r--r--container-search/src/test/java/com/yahoo/search/pagetemplates/engine/test/MapSourcesToSectionsResult.xml2
-rw-r--r--container-search/src/test/java/com/yahoo/search/pagetemplates/engine/test/TwoSectionsFourSourcesResult.xml2
-rw-r--r--container-search/src/test/java/com/yahoo/search/query/profile/test/QueryProfileVariantsCloneTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/query/textserialize/item/test/ParseItemTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/query/textserialize/serializer/test/SerializeItemTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java48
-rw-r--r--container-search/src/test/java/com/yahoo/search/searchchain/config/test/DependencyConfigTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/searchchain/test/SimpleSearchChain.java2
-rw-r--r--container-test-jars/bundle-with-provided-bundle/pom.xml26
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/EnvironmentResource.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/SubmitOptions.java55
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/SubmitResult.java37
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/bcp/BrooklynStatusResource.java23
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/BuildService.java9
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/LogStore.java22
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzClientFactory.java4
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/InvalidTokenException.java11
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsClient.java6
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsKeystore.java18
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZtsClient.java16
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZtsException.java19
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/chef/ChefMock.java28
-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/Node.java53
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepository.java15
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ServiceConvergence.java35
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/ArtifactRepository.java13
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/JobType.java98
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/RunId.java54
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/Testers.java59
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/github/GitHubMock.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/DeploymentIssues.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/Issue.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/IssueId.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/Organization.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/OwnershipIssues.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/User.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/LoggingDeploymentIssues.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockBuildService.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockLogStore.java38
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockTesters.java38
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneFilter.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneFilterMock.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneId.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneList.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/nonpublic/HeaderFields.java14
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/statuspage/StatusPageResource.java24
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/common/ContextAttributes.java13
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/common/NotFoundCheckedException.java23
-rw-r--r--controller-server/pom.xml49
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java3
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java19
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java20
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java12
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/TenantController.java16
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/api/ActivateResult.java1
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/DeploymentJobs.java118
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/JobList.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/JobStatus.java18
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/SystemApplication.java29
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/config/package-info.java10
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzPrincipalFilter.java19
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/filter/NTokenValidator.java81
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/filter/UserAuthWithAthenzPrincipalFilter.java9
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzClientFactoryImpl.java14
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/ZmsClientImpl.java36
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/ZmsKeystoreImpl.java120
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/ZtsClientImpl.java59
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/AthenzClientFactoryMock.java9
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/ZmsClientMock.java12
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/ZtsClientMock.java62
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/concurrent/TimeoutException.java15
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DelegatingBuildService.java30
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentOrder.java68
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentSteps.java112
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java230
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DummyStepRunner.java12
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalBuildService.java39
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java550
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java306
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobProfile.java75
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/LockedStep.java11
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunDetails.java26
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunResult.java31
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunStatus.java174
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Step.java86
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/StepRunner.java25
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Versions.java114
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/ZipBuilder.java65
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/package-info.java (renamed from controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/statuspage/package-info.java)2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmer.java2
-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/ControllerMaintenance.java10
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporter.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunner.java101
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Maintainer.java3
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporter.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/SystemUpgrader.java38
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Upgrader.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/VersionStatusUpdater.java8
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java10
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java108
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializer.java157
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/proxy/ConfigServerRestExecutorImpl.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/proxy/package-info.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java90
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/EmptyJsonResponse.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java122
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiHandler.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/BlockingRequestFilter.java1
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilter.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/screwdriver/ScrewdriverApiHandler.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageClient.java117
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyHandler.java67
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiHandler.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiHandler.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VersionStatus.java14
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/restapi/impl/StatusPageResource.java55
-rw-r--r--controller-server/src/main/resources/configdefinitions/athenz.def3
-rw-r--r--controller-server/src/main/resources/configdefinitions/statuspage.def4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java422
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java32
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/TestIdentities.java10
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzPrincipalFilterTest.java151
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzTestUtils.java22
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ApplicationPackageBuilder.java14
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/BuildJob.java20
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTester.java48
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java570
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/IntegerationStepRunnerTest.java34
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ZipBuilderTest.java66
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ArtifactRepositoryMock.java (renamed from controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ArtifactRepositoryMock.java)17
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/AthenzFilterMock.java (renamed from controller-server/src/test/java/com/yahoo/vespa/hosted/controller/AthenzFilterMock.java)2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java (renamed from controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ConfigServerMock.java)36
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerProxyMock.java (renamed from controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ConfigServerProxyMock.java)2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/MetricsMock.java (renamed from controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MetricsMock.java)2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/MetricsServiceMock.java (renamed from controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/MockMetricsService.java)4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryClientMock.java (renamed from controller-server/src/test/java/com/yahoo/vespa/hosted/controller/NodeRepositoryClientMock.java)37
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java (renamed from controller-server/src/test/java/com/yahoo/vespa/hosted/controller/NodeRepositoryMock.java)2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/RoutingGeneratorMock.java (renamed from controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/MockRoutingGenerator.java)4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/SecretStoreMock.java48
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java (renamed from controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ZoneRegistryMock.java)2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmerTest.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterInfoMaintainerTest.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporterTest.java14
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainerTest.java7
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DnsMaintainerTest.java4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/FailureRedeployerTest.java174
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunnerTest.java262
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporterTest.java28
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployerTest.java14
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/SystemUpgraderTest.java109
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UpgraderTest.java42
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java56
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java90
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/VersionStatusSerializerTest.java6
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/testdata/run-status.json21
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ApplicationRequestToDiscFilterRequestWrapper.java8
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerControllerTester.java12
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerTester.java44
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java22
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java115
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java158
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-reference-2.json5
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application2.json78
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json6
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-west-1.json6
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-corp-us-east-1.json6
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json3
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiTest.java20
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java14
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/screwdriver/ScrewdriverApiTest.java6
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageClientTest.java44
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyApiTest.java93
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiTest.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiTest.java4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/VersionStatusTest.java13
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/restapi/impl/StatusPageResourceTest.java65
-rw-r--r--controller-server/src/test/resources/job/job-type-response.json145
-rw-r--r--controller-server/src/test/resources/job/run-details-response.json5
-rw-r--r--controller-server/src/test/resources/job/run-status-response.json128
-rw-r--r--controller-server/src/test/resources/test_runner_services.xml-cd36
-rw-r--r--defaults/src/apps/printdefault/printdefault.cpp26
-rw-r--r--defaults/src/main/java/com/yahoo/vespa/defaults/Defaults.java74
-rw-r--r--defaults/src/test/java/com/yahoo/vespa/defaults/DefaultsTestCase.java25
-rw-r--r--defaults/src/vespa/defaults.cpp105
-rwxr-xr-xdist/getversion.pl34
-rw-r--r--docker-api/pom.xml26
-rw-r--r--docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/DockerImplTest.java2
-rw-r--r--docproc/src/main/java/com/yahoo/docproc/jdisc/messagebus/ProcessingFactory.java2
-rw-r--r--docproc/src/main/java/com/yahoo/docproc/proxy/ProxyDocument.java7
-rw-r--r--docproc/src/test/java/com/yahoo/docproc/AccessesAnnotationTestCase.java2
-rw-r--r--docprocs/src/main/java/com/yahoo/docprocs/indexing/DocumentScript.java2
-rw-r--r--docprocs/src/main/java/com/yahoo/docprocs/indexing/FastLogger.java2
-rw-r--r--docprocs/src/main/java/com/yahoo/docprocs/indexing/IndexingProcessor.java2
-rw-r--r--docprocs/src/main/java/com/yahoo/docprocs/indexing/ScriptManager.java2
-rw-r--r--docprocs/src/test/java/com/yahoo/docprocs/indexing/DocumentScriptTestCase.java2
-rw-r--r--docprocs/src/test/java/com/yahoo/docprocs/indexing/IndexingProcessorTestCase.java2
-rw-r--r--docprocs/src/test/java/com/yahoo/docprocs/indexing/ScriptManagerTestCase.java2
-rw-r--r--document/src/main/java/com/yahoo/document/BucketDistribution.java2
-rw-r--r--document/src/main/java/com/yahoo/document/Document.java34
-rw-r--r--document/src/main/java/com/yahoo/document/DocumentUpdate.java38
-rw-r--r--document/src/main/java/com/yahoo/document/GlobalId.java2
-rw-r--r--document/src/main/java/com/yahoo/document/PositionDataType.java2
-rw-r--r--document/src/main/java/com/yahoo/document/SimpleDocument.java2
-rw-r--r--document/src/main/java/com/yahoo/document/annotation/AnnotationTypes.java2
-rw-r--r--document/src/main/java/com/yahoo/document/annotation/SpanTrees.java2
-rw-r--r--document/src/main/java/com/yahoo/document/datatypes/PredicateFieldValue.java2
-rw-r--r--document/src/main/java/com/yahoo/document/datatypes/Raw.java13
-rw-r--r--document/src/main/java/com/yahoo/document/select/BucketSet.java2
-rw-r--r--document/src/main/java/com/yahoo/document/select/Result.java2
-rw-r--r--document/src/main/java/com/yahoo/document/select/parser/SelectInput.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/rule/ArithmeticNode.java2
-rw-r--r--document/src/main/java/com/yahoo/document/select/rule/AttributeNode.java2
-rw-r--r--document/src/main/java/com/yahoo/document/select/rule/ComparisonNode.java2
-rw-r--r--document/src/main/java/com/yahoo/document/select/rule/DocumentNode.java2
-rw-r--r--document/src/main/java/com/yahoo/document/select/rule/EmbracedNode.java2
-rw-r--r--document/src/main/java/com/yahoo/document/select/rule/ExpressionNode.java2
-rw-r--r--document/src/main/java/com/yahoo/document/select/rule/IdNode.java2
-rw-r--r--document/src/main/java/com/yahoo/document/select/rule/LiteralNode.java2
-rw-r--r--document/src/main/java/com/yahoo/document/select/rule/LogicNode.java2
-rw-r--r--document/src/main/java/com/yahoo/document/select/rule/NegationNode.java2
-rw-r--r--document/src/main/java/com/yahoo/document/select/rule/SearchColumnNode.java2
-rw-r--r--document/src/main/java/com/yahoo/document/select/rule/VariableNode.java2
-rwxr-xr-xdocument/src/main/javacc/SelectParser.jj2
-rw-r--r--document/src/test/java/com/yahoo/document/DataTypeNameTestCase.java2
-rw-r--r--document/src/test/java/com/yahoo/document/DocumentTestCase.java33
-rw-r--r--document/src/test/java/com/yahoo/document/DocumentUpdateTestCase.java58
-rw-r--r--document/src/test/java/com/yahoo/document/PositionTypeTestCase.java2
-rw-r--r--document/src/test/java/com/yahoo/document/SimpleDocumentTestCase.java2
-rw-r--r--document/src/test/java/com/yahoo/document/annotation/AnnotationTypesTestCase.java2
-rw-r--r--document/src/test/java/com/yahoo/document/datatypes/PredicateFieldValueTest.java2
-rw-r--r--document/src/test/java/com/yahoo/document/datatypes/UriFieldValueTest.java2
-rw-r--r--document/src/test/java/com/yahoo/document/select/DocumentSelectorTestCase.java2
-rw-r--r--document/src/test/java/com/yahoo/document/serialization/PredicateFieldValueSerializationTestCase.java2
-rw-r--r--document/src/test/java/com/yahoo/document/serialization/VespaDocumentSerializerTestCase.java2
-rw-r--r--document/src/test/java/com/yahoo/document/serialization/XmlDocumentWriterTestCase.java2
-rw-r--r--document/src/test/java/com/yahoo/vespaxmlparser/PositionParserTestCase.java2
-rw-r--r--document/src/test/java/com/yahoo/vespaxmlparser/UriParserTestCase.java2
-rw-r--r--document/src/test/java/com/yahoo/vespaxmlparser/VespaXmlFieldReaderTestCase.java2
-rw-r--r--document/src/test/java/com/yahoo/vespaxmlparser/VespaXmlUpdateReaderTestCase.java2
-rw-r--r--document/src/tests/documentselectparsertest.cpp3
-rw-r--r--document/src/tests/documentupdatetestcase.cpp349
-rw-r--r--document/src/tests/fieldpathupdatetestcase.cpp103
-rw-r--r--document/src/tests/testxml.cpp2
-rw-r--r--document/src/vespa/document/base/forcelink.cpp2
-rw-r--r--document/src/vespa/document/base/globalid.cpp5
-rw-r--r--document/src/vespa/document/datatype/datatype.cpp11
-rw-r--r--document/src/vespa/document/datatype/datatype.h3
-rw-r--r--document/src/vespa/document/datatype/primitivedatatype.cpp7
-rw-r--r--document/src/vespa/document/datatype/structureddatatype.h3
-rw-r--r--document/src/vespa/document/fieldvalue/document.h18
-rw-r--r--document/src/vespa/document/fieldvalue/stringfieldvalue.h5
-rw-r--r--document/src/vespa/document/repo/document_type_repo_factory.cpp6
-rw-r--r--document/src/vespa/document/repo/documenttyperepo.cpp214
-rw-r--r--document/src/vespa/document/repo/documenttyperepo.h3
-rw-r--r--document/src/vespa/document/repo/fixedtyperepo.cpp28
-rw-r--r--document/src/vespa/document/repo/fixedtyperepo.h15
-rw-r--r--document/src/vespa/document/select/operator.cpp7
-rw-r--r--document/src/vespa/document/select/result.h6
-rw-r--r--document/src/vespa/document/serialization/vespadocumentserializer.cpp16
-rw-r--r--document/src/vespa/document/update/addfieldpathupdate.cpp11
-rw-r--r--document/src/vespa/document/update/addfieldpathupdate.h3
-rw-r--r--document/src/vespa/document/update/addvalueupdate.cpp43
-rw-r--r--document/src/vespa/document/update/addvalueupdate.h8
-rw-r--r--document/src/vespa/document/update/arithmeticvalueupdate.cpp9
-rw-r--r--document/src/vespa/document/update/arithmeticvalueupdate.h10
-rw-r--r--document/src/vespa/document/update/assignfieldpathupdate.cpp14
-rw-r--r--document/src/vespa/document/update/assignfieldpathupdate.h4
-rw-r--r--document/src/vespa/document/update/assignvalueupdate.cpp11
-rw-r--r--document/src/vespa/document/update/assignvalueupdate.h14
-rw-r--r--document/src/vespa/document/update/clearvalueupdate.cpp11
-rw-r--r--document/src/vespa/document/update/clearvalueupdate.h6
-rw-r--r--document/src/vespa/document/update/documentupdate.cpp324
-rw-r--r--document/src/vespa/document/update/documentupdate.h87
-rw-r--r--document/src/vespa/document/update/fieldpathupdate.cpp28
-rw-r--r--document/src/vespa/document/update/fieldpathupdate.h21
-rw-r--r--document/src/vespa/document/update/fieldpathupdates.h6
-rw-r--r--document/src/vespa/document/update/fieldupdate.cpp27
-rw-r--r--document/src/vespa/document/update/fieldupdate.h33
-rw-r--r--document/src/vespa/document/update/mapvalueupdate.cpp18
-rw-r--r--document/src/vespa/document/update/mapvalueupdate.h10
-rw-r--r--document/src/vespa/document/update/removefieldpathupdate.cpp5
-rw-r--r--document/src/vespa/document/update/removefieldpathupdate.h6
-rw-r--r--document/src/vespa/document/update/removevalueupdate.cpp7
-rw-r--r--document/src/vespa/document/update/removevalueupdate.h6
-rw-r--r--document/src/vespa/document/update/updates.h17
-rw-r--r--document/src/vespa/document/update/valueupdate.cpp20
-rw-r--r--document/src/vespa/document/update/valueupdate.h30
-rw-r--r--documentapi/src/main/java/com/yahoo/documentapi/DocumentAccess.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/DocumentAccessParams.java2
-rw-r--r--documentapi/src/main/java/com/yahoo/documentapi/RemoveResponse.java2
-rw-r--r--documentapi/src/main/java/com/yahoo/documentapi/ResponseHandler.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/SyncParameters.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/SyncSession.java2
-rw-r--r--documentapi/src/main/java/com/yahoo/documentapi/UpdateResponse.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/MessageBusSession.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/MessageBusSyncSession.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/ANDPolicy.java2
-rw-r--r--documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/AbstractRoutableFactory.java2
-rw-r--r--documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/BatchDocumentUpdateMessage.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentMessage.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentProtocol.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentReply.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentRouteSelectorPolicy.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/ErrorPolicy.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/ExternPolicy.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetBucketStateMessage.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetBucketStateReply.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetDocumentMessage.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetDocumentReply.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/LocalServicePolicy.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/PutDocumentMessage.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RemoveDocumentMessage.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RemoveDocumentReply.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoundRobinPolicy.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutableFactories50.java4
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutableFactory.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutableRepository.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutingPolicyFactories.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutingPolicyFactory.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutingPolicyRepository.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/SubsetServicePolicy.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/UpdateDocumentMessage.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/UpdateDocumentReply.java2
-rw-r--r--documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/WrongDistributionReply.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/systemstate/rule/Argument.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/systemstate/rule/Location.java2
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/systemstate/rule/NodeState.java2
-rwxr-xr-xdocumentapi/src/main/javacc/StateParser.jj2
-rw-r--r--documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/DocumentProtocolTest.java2
-rw-r--r--documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/LoadBalancerTestCase.java2
-rw-r--r--documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/Messages50TestCase.java2
-rw-r--r--documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/Messages51TestCase.java2
-rwxr-xr-xdocumentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/MessagesTestBase.java2
-rwxr-xr-xdocumentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/PolicyFactoryTestCase.java2
-rwxr-xr-xdocumentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/PolicyTestCase.java2
-rwxr-xr-xdocumentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/PolicyTestFrame.java2
-rw-r--r--documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/PriorityTestCase.java2
-rwxr-xr-xdocumentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/RoutableFactoryTestCase.java2
-rw-r--r--documentapi/src/tests/messagebus/messagebus_test.cpp4
-rw-r--r--documentapi/src/tests/messages/messages50test.cpp13
-rw-r--r--documentapi/src/tests/messages/messages52test.cpp2
-rw-r--r--documentapi/src/tests/policies/policies_test.cpp4
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/policies/andpolicy.h2
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/policies/errorpolicy.cpp4
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/policies/localservicepolicy.h2
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/policies/roundrobinpolicy.h2
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/policies/subsetservicepolicy.h2
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/routablefactories50.h6
-rw-r--r--documentgen-test/pom.xml7
-rw-r--r--eval/src/vespa/eval/eval/aggr.cpp5
-rw-r--r--eval/src/vespa/eval/eval/basic_nodes.h3
-rw-r--r--eval/src/vespa/eval/eval/llvm/compiled_function.cpp5
-rw-r--r--eval/src/vespa/eval/eval/tensor_function.cpp5
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_add_dimension_optimizer.cpp3
-rw-r--r--eval/src/vespa/eval/tensor/serialization/typed_binary_format.cpp5
-rw-r--r--eval/src/vespa/eval/tensor/sparse/direct_sparse_tensor_builder.h5
-rw-r--r--eval/src/vespa/eval/tensor/wrapped_simple_tensor.cpp12
-rw-r--r--fastlib/src/vespa/fastlib/io/bufferedfile.h3
-rw-r--r--fat-model-dependencies/pom.xml11
-rw-r--r--fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/FileAcquirerFactory.java2
-rw-r--r--fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/FileReferenceDoesNotExistException.java2
-rw-r--r--fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/FileReferenceRemovedException.java2
-rw-r--r--fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/MockFileAcquirer.java2
-rw-r--r--fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/TimeoutException.java2
-rw-r--r--fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/Timer.java2
-rw-r--r--fileacquirer/src/test/java/MockFileAcquirerTest.java2
-rw-r--r--filedistribution/src/main/sh/vespa-status-filedistribution.sh2
-rw-r--r--fnet/ethereal/packet-fnetrpc.c2
-rw-r--r--fnet/src/vespa/fnet/controlpacket.cpp7
-rw-r--r--fnet/src/vespa/fnet/dummypacket.cpp6
-rw-r--r--fnet/src/vespa/fnet/frt/invoker.cpp2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/AdapterFactory.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/DocumentAdapter.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ExpressionConverter.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ExpressionSearcher.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ExpressionVisitor.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldPathUpdateAdapter.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldPathUpdateHelper.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldUpdateAdapter.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldUpdateHelper.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldValueConverter.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ScriptParser.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ScriptParserContext.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/SimpleAdapterFactory.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/SimpleDocumentAdapter.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/StringFieldConverter.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/TypedExpressionConverter.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/UpdateAdapter.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ValueTransformProvider.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ArithmeticExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/AttributeExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Base64DecodeExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Base64EncodeExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/CatExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ClearStateExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/CompositeExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/EchoExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExactExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExecutionContext.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Expression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionList.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/FieldTypeAdapter.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/FieldValueAdapter.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/FlattenExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ForEachExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GetFieldExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GetVarExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GuardExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HexDecodeExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HexEncodeExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HostNameExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/IfThenExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/IndexExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/InputExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/JoinExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/LowerCaseExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/MathResolver.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/NormalizeExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/NowExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/OptimizePredicateExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/OutputExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ParenthesisExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/RandomExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ScriptExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SelectInputExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SetLanguageExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SetValueExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SetVarExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SplitExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/StatementExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SubstringExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SummaryExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SwitchExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ThisExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToArrayExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToByteExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToDoubleExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToFloatExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToIntegerExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToLongExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToPositionExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToStringExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToWsetExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/TokenizeExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/TrimExpression.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedDataType.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedFieldValue.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationContext.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationException.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ZCurveExpression.java4
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/linguistics/AnnotatorConfig.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/linguistics/LinguisticsAnnotator.java2
-rw-r--r--indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/parser/IndexingInput.java2
-rw-r--r--indexinglanguage/src/main/javacc/IndexingParser.jj2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/DocumentTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/DocumentToPathUpdateTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/DocumentToValueUpdateTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/DocumentUpdateTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ExpressionConverterTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ExpressionSearcherTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ExpressionVisitorTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/FieldValueConverterTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/PathUpdateToDocumentTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ScriptParserTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ScriptTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/SimpleDocumentAdapterTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/SimpleTestAdapter.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/TypedExpressionConverterTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ValueTransformProviderTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ValueUpdateToDocumentTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ArithmeticTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/AttributeExpressionTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/Base64DecodeTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/Base64EncodeTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/CatTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ClearStateTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/CompositeExpressionTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/EchoTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExactTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExecutionContextTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionAssert.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionAssertTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/FlattenTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ForEachTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GetFieldTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GetVarTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GuardTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/HexDecodeTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/HexEncodeTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/HostNameTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/IfThenTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/IndexExpressionTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/InputTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/JoinTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/LowerCaseTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/MathResolverTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/NormalizeTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/NowTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/OptimizePredicateTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/OutputAssert.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/OutputAssertTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ParenthesisTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/RandomTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ScriptTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SelectInputTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SetLanguageTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SetValueTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SetVarTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SimpleExpression.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SimpleExpressionTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SplitTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/StatementTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SubstringTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SummaryExpressionTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SwitchTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ThisTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToArrayTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToByteTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToDoubleTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToFloatTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToIntegerTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToLongTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToPositionTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToStringTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToWsetTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/TokenizeTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/TrimTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedDataTypeTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedFieldValueTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationContextTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationExceptionTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ZCurveTestCase.java12
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/linguistics/AnnotatorConfigTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/linguistics/LinguisticsAnnotatorTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/DefaultFieldNameTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/ExpressionTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/FieldNameTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/IdentifierTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/MathTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/NumberTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/PrecedenceTestCase.java2
-rw-r--r--indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/ScriptTestCase.java2
-rw-r--r--jaxrs_client_utils/src/main/java/com/yahoo/vespa/jaxrs/client/JaxRsStrategyFactory.java2
-rw-r--r--jaxrs_client_utils/src/main/java/com/yahoo/vespa/jaxrs/client/RetryingJaxRsStrategy.java17
-rw-r--r--jaxrs_client_utils/src/test/java/com/yahoo/vespa/jaxrs/client/RetryingJaxRsStrategyTest.java31
-rw-r--r--jdisc-security-filters/pom.xml7
-rw-r--r--jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzAuthorizationFilter.java145
-rw-r--r--jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/RequestResourceMapper.java37
-rw-r--r--jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/StaticRequestResourceMapper.java33
-rw-r--r--jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/package-info.java8
-rw-r--r--jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/base/JsonSecurityRequestFilterBase.java1
-rw-r--r--jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/misc/SecurityHeadersResponseFilter.java1
-rw-r--r--jdisc-security-filters/src/main/resources/configdefinitions/athenz-authorization-filter.def8
-rw-r--r--jdisc-security-filters/src/main/resources/configdefinitions/static-request-resource-mapper.def8
-rw-r--r--jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzAuthorizationFilterTest.java103
-rw-r--r--jdisc_core/pom.xml30
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/AbstractResource.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/Container.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/HeaderFields.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/Metric.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/Request.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/Response.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/SharedResource.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/TimeoutManager.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/Timer.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/AbstractApplication.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/Application.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/ApplicationNotReadyException.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/BindingRepository.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/BindingSet.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/BindingSetSelector.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/BundleInstallationException.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/BundleInstaller.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/ContainerActivator.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/ContainerBuilder.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/ContainerThread.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/DeactivatedContainer.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/GlobPattern.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/GuiceRepository.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/MetricConsumer.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/MetricImpl.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/MetricNullProvider.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/MetricProvider.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/OsgiFramework.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/OsgiHeader.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/ResourcePool.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/ServerRepository.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/UriPattern.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/client/AbstractClientApplication.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/client/ClientApplication.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/client/ClientDriver.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/core/ActiveContainer.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/core/ApplicationConfigModule.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/core/ApplicationEnvironmentModule.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/core/ApplicationLoader.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/core/BootstrapDaemon.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/core/BootstrapLoader.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/core/BundleLocationResolver.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/core/ConsoleLogFormatter.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/core/ContainerSnapshot.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/core/ContainerTermination.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/core/DefaultBindingSelector.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/core/ExportPackages.java20
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/core/FelixFramework.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/core/FelixParams.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/core/Main.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/core/ScheduledQueue.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/core/SystemTimer.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/core/TimeoutManagerImpl.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/handler/AbstractRequestHandler.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/handler/BindingNotFoundException.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/handler/BlockingContentWriter.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/handler/BufferedContentChannel.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/handler/CompletionHandler.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/handler/ContentChannel.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/handler/ContentInputStream.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/handler/FutureCompletion.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/handler/FutureConjunction.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/handler/FutureResponse.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/handler/NullContent.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/handler/ReadableContentChannel.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/handler/RequestDeniedException.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/handler/RequestHandler.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/handler/ResponseHandler.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/handler/ThreadedRequestHandler.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/handler/UnsafeContentInputStream.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/service/AbstractClientProvider.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/service/AbstractServerProvider.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/service/BindingSetNotFoundException.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/service/ClientProvider.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/service/ContainerNotReadyException.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/service/CurrentContainer.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/service/NoBindingSetSelectedException.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/service/ServerProvider.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingClientProvider.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingCompletionHandler.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingContentChannel.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingOsgiFramework.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingRequest.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingRequestHandler.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingResponseHandler.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingServerProvider.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/test/TestDriver.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/AbstractResourceTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/ContainerTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/HeaderFieldsTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/ProxyRequestHandlerTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/RequestTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/ResponseTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/application/AbstractApplicationTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/application/ApplicationNotReadyTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingMatchTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingRepositoryTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingSetTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/application/BundleInstallationExceptionTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/application/ContainerBuilderTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/application/ContainerThreadTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/application/GlobPatternTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/application/GuiceRepositoryTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/application/MetricImplTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/application/OsgiHeaderTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/application/OsgiRepositoryTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/application/ResourcePoolTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/application/ServerRepositoryTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/application/UriPatternTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/BindingMatchingTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/LatencyTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/ThroughputTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/UriMatchingTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/client/AbstractClientApplicationTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/client/ClientDriverTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/ActiveContainerTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationConfigModuleTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationEnvironmentModuleTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationLoaderTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationRestartTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationShutdownTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/BootstrapDaemonTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/BundleLocationResolverTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/ConsoleLogFormatterTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/ConsoleLogManagerTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerResourceTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerShutdownTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerSnapshotTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerTerminationTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/DefaultBindingSelectorTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/ExportPackagesTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/FelixFrameworkTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/FelixParamsTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogHandlerTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogManagerTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogServiceTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/ScheduledQueueTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/SystemTimerTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/core/TimeoutManagerImplTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/handler/AbstractContentOutputStreamTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/handler/AbstractRequestHandlerTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/handler/BindingNotFoundTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/handler/BlockingContentWriterTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/handler/BufferedContentChannelTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/handler/CallableRequestDispatchTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/handler/CallableResponseDispatchTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/handler/ContentInputStreamTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/handler/FastContentOutputStreamTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/handler/FastContentWriterTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureCompletionTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureConjunctionTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureResponseTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/handler/NullContentTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/handler/ReadableContentChannelTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/handler/RequestDeniedTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/handler/RequestDispatchTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/handler/ResponseDispatchTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/handler/RunnableLatch.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/handler/ThreadedRequestHandlerTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/handler/UnsafeContentInputStreamTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/service/AbstractClientProviderTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/service/AbstractServerProviderTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/service/BindingSetNotFoundTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/service/ConnectToHandlerTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/service/ContainerNotReadyTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/service/CurrentContainerTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/service/NoBindingSetSelectedTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingClientTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingCompletionHandlerTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingContentChannelTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingOsgiFrameworkTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingRequestHandlerTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingRequestTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingResponseHandlerTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingServerTestCase.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/test/ServerProviderConformanceTestTest.java2
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/test/TestDriverTestCase.java2
-rw-r--r--jdisc_core_test/integration_test/pom.xml28
-rw-r--r--jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/AbstractApplicationTestCase.java2
-rw-r--r--jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/BundleActivatorIntegrationTest.java2
-rw-r--r--jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/BundleInstallerIntegrationTest.java2
-rw-r--r--jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/GuiceRepositoryIntegrationTest.java2
-rw-r--r--jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/ServerRepositoryIntegrationTest.java2
-rw-r--r--jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/client/ClientDriverIntegrationTest.java2
-rw-r--r--jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/ApplicationLoaderIntegrationTest.java2
-rw-r--r--jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/BootstrapDaemonIntegrationTest.java2
-rw-r--r--jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/ExportPackagesIntegrationTest.java2
-rw-r--r--jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/FelixFrameworkIntegrationTest.java2
-rw-r--r--jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/OsgiLogManagerIntegrationTest.java2
-rw-r--r--jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/test/TestDriverIntegrationTest.java2
-rw-r--r--jdisc_core_test/test_bundles/app-a/src/main/java/com/yahoo/jdisc/bundle/ApplicationA.java2
-rw-r--r--jdisc_core_test/test_bundles/app-b-priv/src/main/java/com/yahoo/jdisc/bundle/ApplicationB.java2
-rw-r--r--jdisc_core_test/test_bundles/app-ca/src/main/java/com/yahoo/jdisc/bundle/ApplicationC.java2
-rw-r--r--jdisc_core_test/test_bundles/app-dj/src/main/java/com/yahoo/jdisc/bundle/ApplicationD.java2
-rw-r--r--jdisc_core_test/test_bundles/app-ej-priv/src/main/java/com/yahoo/jdisc/bundle/ApplicationE.java2
-rw-r--r--jdisc_core_test/test_bundles/app-f-more/src/main/java/com/yahoo/jdisc/bundle/ApplicationF.java2
-rw-r--r--jdisc_core_test/test_bundles/app-g-act/src/main/java/com/yahoo/jdisc/bundle/ApplicationG.java2
-rw-r--r--jdisc_core_test/test_bundles/app-g-act/src/main/java/com/yahoo/jdisc/bundle/MyBundleActivator.java2
-rw-r--r--jdisc_core_test/test_bundles/app-g-act/src/main/java/com/yahoo/jdisc/bundle/MyService.java2
-rw-r--r--jdisc_core_test/test_bundles/app-h-log/src/main/java/com/yahoo/jdisc/bundle/ApplicationH.java2
-rw-r--r--jdisc_core_test/test_bundles/cert-a/src/main/java/com/yahoo/jdisc/bundle/a/CertificateA.java2
-rw-r--r--jdisc_core_test/test_bundles/cert-b/pom.xml13
-rw-r--r--jdisc_core_test/test_bundles/cert-b/src/main/java/com/yahoo/jdisc/bundle/b/CertificateB.java2
-rw-r--r--jdisc_core_test/test_bundles/cert-b/src/main/java/com/yahoo/jdisc/bundle/b/package-info.java4
-rw-r--r--jdisc_core_test/test_bundles/cert-ca/src/main/java/com/yahoo/jdisc/bundle/c/CertificateC.java2
-rw-r--r--jdisc_core_test/test_bundles/cert-dc/src/main/java/com/yahoo/jdisc/bundle/d/CertificateD.java2
-rw-r--r--jdisc_core_test/test_bundles/cert-eab/src/main/java/com/yahoo/jdisc/bundle/e/CertificateE.java2
-rw-r--r--jdisc_core_test/test_bundles/cert-fac/src/main/java/com/yahoo/jdisc/bundle/f/CertificateF.java2
-rw-r--r--jdisc_core_test/test_bundles/cert-gg/src/main/java/com/yahoo/jdisc/bundle/g/CertificateG.java2
-rw-r--r--jdisc_core_test/test_bundles/cert-hi/src/main/java/com/yahoo/jdisc/bundle/h/CertificateH.java2
-rw-r--r--jdisc_core_test/test_bundles/cert-ih/src/main/java/com/yahoo/jdisc/bundle/i/CertificateI.java2
-rw-r--r--jdisc_core_test/test_bundles/cert-j-priv/src/main/java/com/yahoo/jdisc/bundle/j/CertificateJ.java2
-rw-r--r--jdisc_core_test/test_bundles/cert-k-pkgs/pom.xml7
-rw-r--r--jdisc_core_test/test_bundles/cert-k-pkgs/src/main/java/com/yahoo/jdisc/bundle/k/CertificateK.java70
-rw-r--r--jdisc_core_test/test_bundles/cert-l1/src/main/java/com/yahoo/jdisc/bundle/l/CertificateL.java2
-rw-r--r--jdisc_core_test/test_bundles/cert-l2/src/main/java/com/yahoo/jdisc/bundle/l/CertificateL.java2
-rw-r--r--jdisc_core_test/test_bundles/cert-ml/src/main/java/com/yahoo/jdisc/bundle/m/CertificateM.java2
-rw-r--r--jdisc_core_test/test_bundles/cert-nac/src/main/java/com/yahoo/jdisc/bundle/n/CertificateN.java2
-rw-r--r--jdisc_core_test/test_bundles/cert-oa-path/src/main/java/com/yahoo/jdisc/bundle/o/CertificateO.java2
-rw-r--r--jdisc_core_test/test_bundles/cert-p-jar/src/main/java/com/yahoo/jdisc/bundle/p/CertificateP.java2
-rw-r--r--jdisc_core_test/test_bundles/cert-q-frag/src/main/java/com/yahoo/jdisc/bundle/q/CertificateQ.java2
-rw-r--r--jdisc_core_test/test_bundles/cert-rq/src/main/java/com/yahoo/jdisc/bundle/r/CertificateR.java2
-rw-r--r--jdisc_core_test/test_bundles/cert-s-act/src/main/java/com/yahoo/jdisc/bundle/s/CertificateS.java2
-rw-r--r--jdisc_core_test/test_bundles/cert-s-act/src/main/java/com/yahoo/jdisc/bundle/s/MyBundleActivator.java2
-rw-r--r--jdisc_core_test/test_bundles/cert-tp/src/main/java/com/yahoo/jdisc/bundle/t/CertificateT.java2
-rw-r--r--jdisc_core_test/test_bundles/cert-us/src/main/java/com/yahoo/jdisc/bundle/u/CertificateU.java2
-rw-r--r--jdisc_core_test/test_bundles/my-bundle-activator/src/main/java/com/yahoo/jdisc/bundle/MyBundleActivator.java2
-rw-r--r--jdisc_core_test/test_bundles/my-bundle-activator/src/main/java/com/yahoo/jdisc/bundle/MyService.java2
-rw-r--r--jdisc_core_test/test_bundles/my-guice-module/src/main/java/com/yahoo/jdisc/bundle/MyGuiceModule.java2
-rw-r--r--jdisc_core_test/test_bundles/my-server-provider/src/main/java/com/yahoo/jdisc/bundle/MyServerProvider.java2
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/core/CompletionHandlers.java2
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/RequestView.java2
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/SecurityFilterInvoker.java2
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/SecurityRequestFilter.java2
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/chain/ResponseFilterChain.java2
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/chain/ResponseHandlerGuard.java2
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/AsyncCompleteListener.java2
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/Exceptions.java2
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/FilterInvokingPrintWriter.java2
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/FilterInvokingServletOutputStream.java2
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/HttpRequestDispatch.java26
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscFilterInvokerFilter.java4
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServlet.java5
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscServerConnector.java39
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyHttpServer.java22
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/MetricReporter.java14
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/OneTimeRunnable.java2
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletOutputStreamWriter.java2
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletRequestReader.java7
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletResponseController.java2
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/UnsupportedFilterInvoker.java2
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/CookieTestCase.java2
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/HttpHeadersTestCase.java2
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/HttpRequestTestCase.java2
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/HttpResponseTestCase.java2
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/filter/EmptyRequestFilterTestCase.java2
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/filter/EmptyResponseFilterTestCase.java2
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/filter/ResponseHeaderFilter.java2
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/guiceModules/ConnectorFactoryRegistryModule.java2
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/guiceModules/ServletModule.java2
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java16
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServletTest.java2
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/SimpleHttpClient.java11
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/TestDriver.java2
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/TestDrivers.java2
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/servlet/JDiscFilterForServletTest.java2
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/servlet/ServletTestBase.java2
-rw-r--r--jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/IgnoredCompletionHandler.java2
-rw-r--r--jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusClient.java2
-rw-r--r--jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusRequest.java2
-rw-r--r--jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusRequestHandler.java2
-rw-r--r--jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusResponse.java2
-rw-r--r--jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusServer.java2
-rw-r--r--jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/StatusCodes.java2
-rw-r--r--jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/ClientTestDriver.java2
-rw-r--r--jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/MessageQueue.java2
-rw-r--r--jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/RemoteClient.java2
-rw-r--r--jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/RemoteServer.java2
-rw-r--r--jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/ReplyQueue.java2
-rw-r--r--jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/ServerTestDriver.java2
-rw-r--r--jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/ClientSession.java2
-rw-r--r--jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/ServerSession.java2
-rw-r--r--jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/SharedDestinationSession.java2
-rw-r--r--jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/SharedIntermediateSession.java2
-rw-r--r--jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/SharedMessageBus.java2
-rw-r--r--jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/SharedSourceSession.java2
-rw-r--r--jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/ClientThreadingTestCase.java2
-rw-r--r--jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusClientTestCase.java2
-rw-r--r--jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusRequestHandlerTestCase.java2
-rw-r--r--jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusRequestTestCase.java2
-rw-r--r--jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusResponseTestCase.java2
-rw-r--r--jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusServerConformanceTest.java2
-rw-r--r--jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusServerTestCase.java2
-rw-r--r--jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/ServerThreadingTestCase.java2
-rw-r--r--jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/test/ClientTestDriverTestCase.java2
-rw-r--r--jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/test/ServerTestDriverTestCase.java2
-rw-r--r--jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/shared/SharedDestinationSessionTestCase.java2
-rw-r--r--jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/shared/SharedIntermediateSessionTestCase.java2
-rw-r--r--jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/shared/SharedMessageBusTestCase.java2
-rw-r--r--jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/shared/SharedSourceSessionTestCase.java2
-rw-r--r--jrt/src/com/yahoo/jrt/slobrok/api/IMirror.java2
-rw-r--r--jrt/tests/com/yahoo/jrt/slobrok/api/SlobrokListTestCase.java2
-rw-r--r--juniper/src/vespa/juniper/SummaryConfig.h5
-rw-r--r--juniper/src/vespa/juniper/appender.h5
-rw-r--r--juniper/src/vespa/juniper/queryparser.cpp2
-rw-r--r--linguistics/pom.xml8
-rw-r--r--linguistics/src/main/java/com/yahoo/language/Linguistics.java9
-rw-r--r--linguistics/src/main/java/com/yahoo/language/LinguisticsCase.java2
-rw-r--r--linguistics/src/main/java/com/yahoo/language/LocaleFactory.java2
-rw-r--r--linguistics/src/main/java/com/yahoo/language/detect/AbstractDetector.java2
-rw-r--r--linguistics/src/main/java/com/yahoo/language/opennlp/OpenNlpLinguistics.java11
-rw-r--r--linguistics/src/main/java/com/yahoo/language/opennlp/OpenNlpTokenizer.java135
-rw-r--r--linguistics/src/main/java/com/yahoo/language/opennlp/package-info.java (renamed from controller-api/src/main/java/com/yahoo/vespa/hosted/controller/common/package-info.java)2
-rw-r--r--linguistics/src/main/java/com/yahoo/language/process/ProcessingException.java2
-rw-r--r--linguistics/src/main/java/com/yahoo/language/process/StemmerImpl.java2
-rw-r--r--linguistics/src/main/java/com/yahoo/language/simple/SimpleDetector.java58
-rw-r--r--linguistics/src/main/java/com/yahoo/language/simple/SimpleNormalizer.java2
-rw-r--r--linguistics/src/main/java/com/yahoo/language/simple/SimpleToken.java2
-rw-r--r--linguistics/src/main/java/com/yahoo/language/simple/SimpleTokenType.java2
-rw-r--r--linguistics/src/main/java/com/yahoo/language/simple/SimpleTransformer.java2
-rw-r--r--linguistics/src/test/java/com/yahoo/language/LocaleFactoryTestCase.java2
-rw-r--r--linguistics/src/test/java/com/yahoo/language/detect/AbstractDetectorTestCase.java2
-rw-r--r--linguistics/src/test/java/com/yahoo/language/opennlp/OpenNlpTokenizationTestCase.java237
-rw-r--r--linguistics/src/test/java/com/yahoo/language/process/AbstractTokenizerTestCase.java2
-rw-r--r--linguistics/src/test/java/com/yahoo/language/process/ProcessingExceptionTestCase.java2
-rw-r--r--linguistics/src/test/java/com/yahoo/language/process/SegmenterImplTestCase.java2
-rw-r--r--linguistics/src/test/java/com/yahoo/language/process/StemModeTestCase.java2
-rw-r--r--linguistics/src/test/java/com/yahoo/language/process/StemmerImplTestCase.java2
-rw-r--r--linguistics/src/test/java/com/yahoo/language/process/TokenTypeTestCase.java2
-rw-r--r--linguistics/src/test/java/com/yahoo/language/process/TokenizationTestCase.java2
-rw-r--r--linguistics/src/test/java/com/yahoo/language/simple/SimpleDetectorTestCase.java7
-rw-r--r--linguistics/src/test/java/com/yahoo/language/simple/SimpleNormalizerTestCase.java2
-rw-r--r--linguistics/src/test/java/com/yahoo/language/simple/SimpleTokenTestCase.java2
-rw-r--r--linguistics/src/test/java/com/yahoo/language/simple/SimpleTransformerTestCase.java2
-rwxr-xr-xlogserver/bin/logserver-start.sh4
-rw-r--r--maven-plugins/pom.xml2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/CallStack.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/ConfigAgent.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/ConfigHandler.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/DestinationSession.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/DestinationSessionParams.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/DynamicThrottlePolicy.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/EmptyReply.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/Error.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/ErrorCode.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/IntermediateSession.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/IntermediateSessionParams.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/Message.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/MessageBus.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/MessageBusParams.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/MessageHandler.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/Messenger.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/Protocol.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/ProtocolRepository.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/RPCMessageBus.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/Reply.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/ReplyHandler.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/Routable.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/SendProxy.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/Sequencer.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/SourceSession.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/SourceSessionParams.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/StaticThrottlePolicy.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/ThrottlePolicy.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/Trace.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/TraceLevel.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/TraceNode.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/network/Identity.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/network/ServiceAddress.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/network/local/LocalServiceAddress.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCNetworkParams.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCSendAdapter.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCSendV1.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCServicePool.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCTarget.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/network/rpc/SlobrokConfigSubscriber.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/network/rpc/test/SlobrokState.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/routing/ApplicationSpec.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/routing/ErrorDirective.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/routing/HopBlueprint.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/routing/HopDirective.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/routing/HopSpec.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/routing/PolicyDirective.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/routing/Resender.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/routing/RetryPolicy.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/routing/RetryTransientErrorsPolicy.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/routing/Route.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/routing/RouteDirective.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/routing/RouteSpec.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/routing/RoutingContext.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/routing/RoutingNode.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/routing/RoutingNodeIterator.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingPolicy.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingSpec.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingTable.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingTableSpec.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/routing/TcpDirective.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/routing/VerbatimDirective.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/routing/test/CustomPolicy.java2
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/routing/test/CustomPolicyFactory.java2
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/test/SimpleProtocol.java2
-rwxr-xr-xmessagebus/src/test/java/com/yahoo/messagebus/ChokeTestCase.java2
-rwxr-xr-xmessagebus/src/test/java/com/yahoo/messagebus/ConfigAgentTestCase.java2
-rwxr-xr-xmessagebus/src/test/java/com/yahoo/messagebus/ErrorTestCase.java2
-rw-r--r--messagebus/src/test/java/com/yahoo/messagebus/MessengerTestCase.java2
-rw-r--r--messagebus/src/test/java/com/yahoo/messagebus/ProtocolRepositoryTestCase.java2
-rw-r--r--messagebus/src/test/java/com/yahoo/messagebus/SendProxyTestCase.java2
-rw-r--r--messagebus/src/test/java/com/yahoo/messagebus/SequencerTestCase.java2
-rwxr-xr-xmessagebus/src/test/java/com/yahoo/messagebus/SimpleTripTestCase.java2
-rw-r--r--messagebus/src/test/java/com/yahoo/messagebus/ThrottlerTestCase.java2
-rwxr-xr-xmessagebus/src/test/java/com/yahoo/messagebus/TimeoutTestCase.java2
-rwxr-xr-xmessagebus/src/test/java/com/yahoo/messagebus/TraceTestCase.java2
-rwxr-xr-xmessagebus/src/test/java/com/yahoo/messagebus/TraceTripTestCase.java2
-rw-r--r--messagebus/src/test/java/com/yahoo/messagebus/network/IdentityTestCase.java2
-rw-r--r--messagebus/src/test/java/com/yahoo/messagebus/network/local/LocalNetworkTest.java2
-rw-r--r--messagebus/src/test/java/com/yahoo/messagebus/network/rpc/RPCNetworkTestCase.java2
-rwxr-xr-xmessagebus/src/test/java/com/yahoo/messagebus/network/rpc/SendAdapterTestCase.java2
-rwxr-xr-xmessagebus/src/test/java/com/yahoo/messagebus/network/rpc/ServiceAddressTestCase.java2
-rw-r--r--messagebus/src/test/java/com/yahoo/messagebus/network/rpc/ServicePoolTestCase.java2
-rwxr-xr-xmessagebus/src/test/java/com/yahoo/messagebus/network/rpc/TargetPoolTestCase.java2
-rwxr-xr-xmessagebus/src/test/java/com/yahoo/messagebus/routing/AdvancedRoutingTestCase.java2
-rwxr-xr-xmessagebus/src/test/java/com/yahoo/messagebus/routing/ResenderTestCase.java2
-rw-r--r--messagebus/src/test/java/com/yahoo/messagebus/routing/RetryPolicyTestCase.java2
-rwxr-xr-xmessagebus/src/test/java/com/yahoo/messagebus/routing/RouteParserTestCase.java2
-rwxr-xr-xmessagebus/src/test/java/com/yahoo/messagebus/routing/RoutingContextTestCase.java2
-rwxr-xr-xmessagebus/src/test/java/com/yahoo/messagebus/routing/RoutingSpecTestCase.java2
-rw-r--r--messagebus/src/test/java/com/yahoo/messagebus/routing/RoutingTestCase.java2
-rw-r--r--messagebus/src/test/java/com/yahoo/messagebus/test/QueueAdapterTestCase.java2
-rw-r--r--messagebus/src/test/java/com/yahoo/messagebus/test/ReceptorTestCase.java2
-rw-r--r--messagebus/src/test/java/com/yahoo/messagebus/test/SimpleMessageTestCase.java2
-rw-r--r--messagebus/src/test/java/com/yahoo/messagebus/test/SimpleProtocolTestCase.java2
-rw-r--r--messagebus/src/test/java/com/yahoo/messagebus/test/SimpleReplyTestCase.java2
-rw-r--r--messagebus/src/vespa/messagebus/destinationsessionparams.h2
-rw-r--r--messagebus/src/vespa/messagebus/intermediatesessionparams.h2
-rw-r--r--messagebus/src/vespa/messagebus/messagebusparams.h2
-rw-r--r--messagebus/src/vespa/messagebus/network/iserviceaddress.h2
-rw-r--r--messagebus/src/vespa/messagebus/network/rpcnetwork.cpp11
-rw-r--r--messagebus/src/vespa/messagebus/network/rpcnetwork.h10
-rw-r--r--messagebus/src/vespa/messagebus/network/rpcnetworkparams.cpp3
-rw-r--r--messagebus/src/vespa/messagebus/network/rpcnetworkparams.h31
-rw-r--r--messagebus/src/vespa/messagebus/network/rpcsend.cpp27
-rw-r--r--messagebus/src/vespa/messagebus/network/rpcsend.h2
-rw-r--r--messagebus/src/vespa/messagebus/routing/errordirective.h2
-rw-r--r--messagebus/src/vespa/messagebus/routing/hopspec.h2
-rw-r--r--messagebus/src/vespa/messagebus/routing/ihopdirective.h2
-rw-r--r--messagebus/src/vespa/messagebus/routing/policydirective.h2
-rw-r--r--messagebus/src/vespa/messagebus/routing/routedirective.h2
-rw-r--r--messagebus/src/vespa/messagebus/routing/routespec.h2
-rw-r--r--messagebus/src/vespa/messagebus/routing/routingspec.h2
-rw-r--r--messagebus/src/vespa/messagebus/routing/routingtable.h2
-rw-r--r--messagebus/src/vespa/messagebus/routing/routingtablespec.h2
-rw-r--r--messagebus/src/vespa/messagebus/routing/tcpdirective.h2
-rw-r--r--messagebus/src/vespa/messagebus/routing/verbatimdirective.h2
-rw-r--r--messagebus/src/vespa/messagebus/sourcesessionparams.h2
-rw-r--r--metrics/src/test/java/com/yahoo/metrics/AveragedDoubleValueMetric.java2
-rw-r--r--metrics/src/test/java/com/yahoo/metrics/AveragedLongValueMetric.java2
-rw-r--r--metrics/src/test/java/com/yahoo/metrics/SummedDoubleValueMetric.java2
-rw-r--r--metrics/src/test/java/com/yahoo/metrics/SummedLongValueMetric.java2
-rw-r--r--model-inference/pom.xml78
-rw-r--r--model-inference/src/main/java/ai/vespa/models/evaluation/FunctionEvaluator.java54
-rw-r--r--model-inference/src/main/java/ai/vespa/models/evaluation/LazyArrayContext.java213
-rw-r--r--model-inference/src/main/java/ai/vespa/models/evaluation/LazyValue.java150
-rw-r--r--model-inference/src/main/java/ai/vespa/models/evaluation/Model.java106
-rw-r--r--model-inference/src/main/java/ai/vespa/models/evaluation/ModelsEvaluator.java46
-rw-r--r--model-inference/src/main/java/ai/vespa/models/evaluation/RankProfilesConfigImporter.java93
-rw-r--r--model-inference/src/test/java/ai/vespa/models/evaluation/ModelsEvaluatorTest.java51
-rw-r--r--model-inference/src/test/java/ai/vespa/models/evaluation/RankProfilesImporterTest.java60
-rw-r--r--model-inference/src/test/resources/config/rankexpression/rank-profiles.cfg296
-rw-r--r--model-inference/src/test/resources/config/rankexpression/rankexpression.sd327
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java1
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainer.java1
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java9
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/Template.java2
-rw-r--r--node-maintainer/pom.xml1
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java6
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ApplicationMaintainer.java27
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRepositoryMaintenance.java8
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainer.java3
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java50
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabaseClient.java15
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java6
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java2
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java5
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java23
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizationFilter.java1
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/Authorizer.java (renamed from node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/Authorizer.java)18
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodeIdentifier.java4
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockDeployer.java28
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTest.java25
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InfrastructureProvisionerTest.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java19
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRetirerTester.java4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainerTest.java8
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java64
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java13
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/SerializationTest.java5
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationSimulator.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisioningTest.java125
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/MultigroupProvisioningTest.java15
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeTypeProvisioningTest.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java27
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizerTest.java (renamed from node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/AuthorizerTest.java)11
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodeIdentifierTest.java22
-rw-r--r--orchestrator/pom.xml6
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorContext.java40
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorImpl.java50
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClient.java9
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientImpl.java38
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerJaxRsApi.java3
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/RetryingClusterControllerClientFactory.java8
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/StorageNode.java3
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/StorageNodeImpl.java13
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicy.java31
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/policy/Policy.java21
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/InMemoryStatusService.java7
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/MutableStatusRegistry.java2
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/NoThrow.java2
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ReadOnlyStatusRegistry.java2
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/StatusService.java7
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ZookeeperStatusService.java5
-rw-r--r--orchestrator/src/test/java/com/yahoo/vespa/orchestrator/TestIds.java2
-rw-r--r--orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientFactoryMock.java5
-rw-r--r--orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientTest.java8
-rw-r--r--orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/SingleInstanceClusterControllerClientFactoryTest.java9
-rw-r--r--orchestrator/src/test/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicyTest.java15
-rw-r--r--orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/HostResourceTest.java49
-rw-r--r--parent/pom.xml125
-rw-r--r--persistence/src/vespa/persistence/conformancetest/conformancetest.cpp6
-rw-r--r--persistence/src/vespa/persistence/dummyimpl/dummypersistence.cpp48
-rw-r--r--persistence/src/vespa/persistence/dummyimpl/dummypersistence.h26
-rw-r--r--persistence/src/vespa/persistence/spi/persistenceprovider.h9
-rw-r--r--persistence/src/vespa/persistence/spi/read_consistency.cpp6
-rw-r--r--pom.xml5
-rw-r--r--predicate-search-core/src/main/java/com/yahoo/document/predicate/BinaryFormat.java2
-rw-r--r--predicate-search-core/src/main/java/com/yahoo/document/predicate/BooleanPredicate.java2
-rw-r--r--predicate-search-core/src/main/java/com/yahoo/document/predicate/Conjunction.java2
-rw-r--r--predicate-search-core/src/main/java/com/yahoo/document/predicate/Disjunction.java2
-rw-r--r--predicate-search-core/src/main/java/com/yahoo/document/predicate/FeatureRange.java2
-rw-r--r--predicate-search-core/src/main/java/com/yahoo/document/predicate/FeatureSet.java2
-rw-r--r--predicate-search-core/src/main/java/com/yahoo/document/predicate/Negation.java2
-rw-r--r--predicate-search-core/src/main/java/com/yahoo/document/predicate/PredicateOperator.java2
-rw-r--r--predicate-search-core/src/main/java/com/yahoo/document/predicate/PredicateValue.java2
-rw-r--r--predicate-search-core/src/main/java/com/yahoo/document/predicate/Predicates.java2
-rw-r--r--predicate-search-core/src/main/java/com/yahoo/document/predicate/SimplePredicates.java2
-rw-r--r--predicate-search-core/src/test/java/com/yahoo/document/predicate/BinaryFormatTest.java2
-rw-r--r--predicate-search-core/src/test/java/com/yahoo/document/predicate/BooleanPredicateTest.java2
-rw-r--r--predicate-search-core/src/test/java/com/yahoo/document/predicate/ConjunctionTest.java2
-rw-r--r--predicate-search-core/src/test/java/com/yahoo/document/predicate/DisjunctionTest.java2
-rw-r--r--predicate-search-core/src/test/java/com/yahoo/document/predicate/FeatureRangeTest.java2
-rw-r--r--predicate-search-core/src/test/java/com/yahoo/document/predicate/FeatureSetTest.java2
-rw-r--r--predicate-search-core/src/test/java/com/yahoo/document/predicate/NegationTest.java2
-rw-r--r--predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateOperatorTest.java2
-rw-r--r--predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateTest.java2
-rw-r--r--predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateValueTest.java2
-rw-r--r--predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicatesTest.java2
-rw-r--r--processing/src/main/java/com/yahoo/processing/execution/chain/ChainRegistry.java2
-rw-r--r--searchcommon/src/vespa/searchcommon/attribute/attributecontent.h11
-rw-r--r--searchcommon/src/vespa/searchcommon/attribute/basictype.cpp8
-rw-r--r--searchcommon/src/vespa/searchcommon/attribute/basictype.h5
-rw-r--r--searchcommon/src/vespa/searchcommon/attribute/collectiontype.cpp8
-rw-r--r--searchcommon/src/vespa/searchcommon/attribute/collectiontype.h5
-rw-r--r--searchcommon/src/vespa/searchcommon/attribute/config.cpp7
-rw-r--r--searchcommon/src/vespa/searchcommon/attribute/config.h6
-rw-r--r--searchcommon/src/vespa/searchcommon/attribute/i_search_context.h3
-rw-r--r--searchcommon/src/vespa/searchcommon/attribute/iattributecontext.h9
-rw-r--r--searchcommon/src/vespa/searchcommon/attribute/iattributevector.h33
-rw-r--r--searchcommon/src/vespa/searchcommon/attribute/predicate_params.h6
-rw-r--r--searchcommon/src/vespa/searchcommon/attribute/status.cpp17
-rw-r--r--searchcommon/src/vespa/searchcommon/attribute/status.h36
-rw-r--r--searchcommon/src/vespa/searchcommon/common/iblobconverter.h5
-rw-r--r--searchcore/src/apps/vespa-gen-testdocs/vespa-gen-testdocs.cpp2
-rw-r--r--searchcore/src/apps/vespa-transactionlog-inspect/vespa-transactionlog-inspect.cpp3
-rw-r--r--searchcore/src/tests/applyattrupdates/applyattrupdates.cpp2
-rw-r--r--searchcore/src/tests/grouping/grouping.cpp3
-rw-r--r--searchcore/src/tests/proton/attribute/attribute_aspect_delayer/attribute_aspect_delayer_test.cpp48
-rw-r--r--searchcore/src/tests/proton/attribute/attribute_initializer/attribute_initializer_test.cpp3
-rw-r--r--searchcore/src/tests/proton/attribute/attribute_manager/attribute_manager_test.cpp44
-rw-r--r--searchcore/src/tests/proton/attribute/attribute_test.cpp6
-rw-r--r--searchcore/src/tests/proton/attribute/attributeflush_test.cpp13
-rw-r--r--searchcore/src/tests/proton/attribute/exclusive_attribute_read_accessor/exclusive_attribute_read_accessor_test.cpp2
-rw-r--r--searchcore/src/tests/proton/common/document_type_inspector/document_type_inspector_test.cpp135
-rw-r--r--searchcore/src/tests/proton/docsummary/docsummary.cpp43
-rw-r--r--searchcore/src/tests/proton/docsummary/summaryfieldconverter_test.cpp2
-rw-r--r--searchcore/src/tests/proton/document_iterator/document_iterator_test.cpp3
-rw-r--r--searchcore/src/tests/proton/documentdb/clusterstatehandler/clusterstatehandler_test.cpp3
-rw-r--r--searchcore/src/tests/proton/documentdb/combiningfeedview/combiningfeedview_test.cpp11
-rw-r--r--searchcore/src/tests/proton/documentdb/document_subdbs/document_subdbs_test.cpp1
-rw-r--r--searchcore/src/tests/proton/documentdb/documentbucketmover/documentbucketmover_test.cpp3
-rw-r--r--searchcore/src/tests/proton/documentdb/feedhandler/feedhandler_test.cpp2
-rw-r--r--searchcore/src/tests/proton/documentdb/feedview/feedview_test.cpp5
-rw-r--r--searchcore/src/tests/proton/documentdb/maintenancecontroller/maintenancecontroller_test.cpp4
-rw-r--r--searchcore/src/tests/proton/documentdb/storeonlyfeedview/storeonlyfeedview_test.cpp2
-rw-r--r--searchcore/src/tests/proton/documentmetastore/lidreusedelayer/lidreusedelayer_test.cpp2
-rw-r--r--searchcore/src/tests/proton/feedoperation/feedoperation_test.cpp19
-rw-r--r--searchcore/src/tests/proton/matching/matching_stats_test.cpp59
-rw-r--r--searchcore/src/tests/proton/matching/matching_test.cpp2
-rw-r--r--searchcore/src/tests/proton/matching/query_test.cpp58
-rw-r--r--searchcore/src/tests/proton/matching/querynodes_test.cpp1
-rw-r--r--searchcore/src/tests/proton/persistenceengine/persistenceengine_test.cpp4
-rw-r--r--searchcore/src/tests/proton/proton_configurer/proton_configurer_test.cpp20
-rw-r--r--searchcore/src/tests/proton/reference/document_db_reference/document_db_reference_test.cpp1
-rw-r--r--searchcore/src/tests/proton/reprocessing/attribute_reprocessing_initializer/attribute_reprocessing_initializer_test.cpp12
-rw-r--r--searchcore/src/tests/proton/server/documentretriever_test.cpp3
-rw-r--r--searchcore/src/tests/proton/server/feedstates_test.cpp10
-rw-r--r--searchcore/src/vespa/searchcore/config/proton.def5
-rw-r--r--searchcore/src/vespa/searchcore/fdispatch/search/datasetcollection.cpp13
-rw-r--r--searchcore/src/vespa/searchcore/fdispatch/search/fnet_search.cpp2
-rw-r--r--searchcore/src/vespa/searchcore/grouping/groupingmanager.cpp4
-rw-r--r--searchcore/src/vespa/searchcore/proton/attribute/attribute_aspect_delayer.cpp16
-rw-r--r--searchcore/src/vespa/searchcore/proton/attribute/attribute_directory.cpp5
-rw-r--r--searchcore/src/vespa/searchcore/proton/attribute/attribute_writer.cpp61
-rw-r--r--searchcore/src/vespa/searchcore/proton/attribute/attribute_writer.h14
-rw-r--r--searchcore/src/vespa/searchcore/proton/attribute/attributemanager.cpp9
-rw-r--r--searchcore/src/vespa/searchcore/proton/attribute/exclusive_attribute_read_accessor.cpp8
-rw-r--r--searchcore/src/vespa/searchcore/proton/attribute/filter_attribute_manager.cpp5
-rw-r--r--searchcore/src/vespa/searchcore/proton/attribute/flushableattribute.cpp7
-rw-r--r--searchcore/src/vespa/searchcore/proton/common/attributefieldvaluenode.cpp4
-rw-r--r--searchcore/src/vespa/searchcore/proton/common/attrupdate.cpp3
-rw-r--r--searchcore/src/vespa/searchcore/proton/common/document_type_inspector.cpp29
-rw-r--r--searchcore/src/vespa/searchcore/proton/common/hw_info_sampler.cpp4
-rw-r--r--searchcore/src/vespa/searchcore/proton/docsummary/summarymanager.cpp4
-rw-r--r--searchcore/src/vespa/searchcore/proton/docsummary/summarymanagerinitializer.cpp2
-rw-r--r--searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastore.cpp4
-rw-r--r--searchcore/src/vespa/searchcore/proton/documentmetastore/search_context.cpp21
-rw-r--r--searchcore/src/vespa/searchcore/proton/feedoperation/compact_lid_space_operation.cpp4
-rw-r--r--searchcore/src/vespa/searchcore/proton/feedoperation/feedoperation.h4
-rw-r--r--searchcore/src/vespa/searchcore/proton/feedoperation/lidvectorcontext.cpp1
-rw-r--r--searchcore/src/vespa/searchcore/proton/feedoperation/lidvectorcontext.h6
-rw-r--r--searchcore/src/vespa/searchcore/proton/feedoperation/newconfigoperation.h29
-rw-r--r--searchcore/src/vespa/searchcore/proton/feedoperation/pruneremoveddocumentsoperation.cpp7
-rw-r--r--searchcore/src/vespa/searchcore/proton/feedoperation/removedocumentsoperation.cpp9
-rw-r--r--searchcore/src/vespa/searchcore/proton/feedoperation/spoolerreplayoperation.cpp22
-rw-r--r--searchcore/src/vespa/searchcore/proton/feedoperation/spoolerreplayoperation.h17
-rw-r--r--searchcore/src/vespa/searchcore/proton/feedoperation/updateoperation.cpp22
-rw-r--r--searchcore/src/vespa/searchcore/proton/feedoperation/updateoperation.h4
-rw-r--r--searchcore/src/vespa/searchcore/proton/feedoperation/wipehistoryoperation.cpp13
-rw-r--r--searchcore/src/vespa/searchcore/proton/feedoperation/wipehistoryoperation.h9
-rw-r--r--searchcore/src/vespa/searchcore/proton/flushengine/flushengine.cpp134
-rw-r--r--searchcore/src/vespa/searchcore/proton/flushengine/flushengine.h28
-rw-r--r--searchcore/src/vespa/searchcore/proton/flushengine/iflushhandler.h7
-rw-r--r--searchcore/src/vespa/searchcore/proton/flushengine/tls_stats_map.cpp4
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/i_match_loop_communicator.h8
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/match_loop_communicator.cpp8
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/match_loop_communicator.h20
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/match_master.cpp18
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/match_master.h7
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/match_thread.h2
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/match_tools.cpp3
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/matcher.cpp15
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/matching_stats.cpp22
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/query.cpp40
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/query.h2
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/result_processor.cpp20
-rw-r--r--searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp15
-rw-r--r--searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.h49
-rw-r--r--searchcore/src/vespa/searchcore/proton/reference/gid_to_lid_change_listener.cpp2
-rw-r--r--searchcore/src/vespa/searchcore/proton/reprocessing/attribute_reprocessing_initializer.cpp6
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/CMakeLists.txt2
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/combiningfeedview.cpp13
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/combiningfeedview.h13
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/document_db_config_owner.cpp21
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/document_db_config_owner.h25
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/document_db_directory_holder.cpp33
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/document_db_directory_holder.h20
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/documentdb.cpp3
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/documentdb.h4
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/documentdbconfig.cpp2
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/documentdbconfigmanager.cpp18
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/fast_access_feed_view.cpp3
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/feedhandler.cpp3
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/feedstate.h3
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/feedstates.cpp1
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/feedstates.h24
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/i_proton_configurer_owner.h14
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/ireplaypackethandler.h22
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/matchview.cpp8
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/prepare_restart_handler.cpp3
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/proton.cpp64
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/proton.h2
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/proton_configurer.cpp14
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/proton_configurer.h3
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/replaypacketdispatcher.cpp5
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/replaypacketdispatcher.h8
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.cpp1
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.h5
-rw-r--r--searchcore/src/vespa/searchcore/proton/test/attribute_utils.h3
-rw-r--r--searchcore/src/vespa/searchcore/proton/test/mock_attribute_manager.h7
-rw-r--r--searchlib/pom.xml1
-rw-r--r--searchlib/src/apps/tests/memoryindexstress_test.cpp1
-rw-r--r--searchlib/src/apps/vespa-fileheader-inspect/vespa-fileheader-inspect.cpp2
-rw-r--r--searchlib/src/apps/vespa-index-inspect/vespa-index-inspect.cpp2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/aggregation/AggregationResult.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/aggregation/AverageAggregationResult.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/aggregation/CountAggregationResult.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/aggregation/HitsAggregationResult.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/aggregation/MaxAggregationResult.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/aggregation/MinAggregationResult.java2
-rwxr-xr-xsearchlib/src/main/java/com/yahoo/searchlib/aggregation/RawData.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/aggregation/SumAggregationResult.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/aggregation/XorAggregationResult.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/AddFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/AndFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/AttributeNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/BitFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/BucketResultNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/CatFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/ConstantNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/DivideFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/DocumentAccessorNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/DocumentFieldNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/ExpressionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/FixedWidthBucketFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/FloatBucketResultNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/FloatBucketResultNodeVector.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/FloatResultNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/FloatResultNodeVector.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/FunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/GetDocIdNamespaceSpecificFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/GetYMUMChecksumFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/Int16ResultNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/Int16ResultNodeVector.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/Int32ResultNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/Int32ResultNodeVector.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/Int8ResultNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/Int8ResultNodeVector.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerBucketResultNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerBucketResultNodeVector.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerResultNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerResultNodeVector.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/MD5BitFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/MathFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/MaxFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/MinFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/ModuloFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/MultiArgFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/MultiplyFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/NegateFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/NormalizeSubjectFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/NullResultNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/NumElemFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/NumericResultNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/OrFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/RangeBucketPreDefFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/RawResultNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/RawResultNodeVector.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/RelevanceNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/ResultNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/ResultNodeVector.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/StrCatFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/StrLenFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/StringBucketResultNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/StringBucketResultNodeVector.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/StringResultNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/StringResultNodeVector.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/TimeStampFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/ToFloatFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/ToIntFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/ToStringFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/UnaryBitFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/UnaryFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/XorBitFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/expression/XorFunctionNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/gbdt/GbdtConverter.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/gbdt/GbdtModel.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/gbdt/ResponseNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/gbdt/XmlHelper.java2
-rwxr-xr-xsearchlib/src/main/java/com/yahoo/searchlib/rankingexpression/ExpressionFunction.java4
-rwxr-xr-xsearchlib/src/main/java/com/yahoo/searchlib/rankingexpression/FeatureList.java2
-rwxr-xr-xsearchlib/src/main/java/com/yahoo/searchlib/rankingexpression/RankingExpression.java16
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/AbstractArrayContext.java147
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/ArrayContext.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/ContextIndex.java26
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/ExpressionOptimizer.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/Optimizer.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/Value.java8
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/gbdtoptimization/GBDTForestOptimizer.java5
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/gbdtoptimization/GBDTOptimizer.java12
-rwxr-xr-xsearchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ConstantNode.java2
-rwxr-xr-xsearchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/EmbracedNode.java2
-rwxr-xr-xsearchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ExpressionNode.java2
-rwxr-xr-xsearchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/FunctionNode.java2
-rwxr-xr-xsearchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/IfNode.java2
-rwxr-xr-xsearchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/NameNode.java2
-rwxr-xr-xsearchlib/src/main/java/com/yahoo/searchlib/treenet/TreeNetConverter.java2
-rwxr-xr-xsearchlib/src/main/java/com/yahoo/searchlib/treenet/rule/ComparisonCondition.java2
-rwxr-xr-xsearchlib/src/main/java/com/yahoo/searchlib/treenet/rule/Response.java2
-rwxr-xr-xsearchlib/src/main/java/com/yahoo/searchlib/treenet/rule/Tree.java2
-rwxr-xr-xsearchlib/src/main/java/com/yahoo/searchlib/treenet/rule/TreeNet.java2
-rwxr-xr-xsearchlib/src/main/java/com/yahoo/searchlib/treenet/rule/TreeNode.java2
-rwxr-xr-xsearchlib/src/main/javacc/TreeNetParser.jj2
-rw-r--r--searchlib/src/test/java/com/yahoo/searchlib/aggregation/GroupTestCase.java2
-rw-r--r--searchlib/src/test/java/com/yahoo/searchlib/aggregation/GroupingTestCase.java2
-rwxr-xr-xsearchlib/src/test/java/com/yahoo/searchlib/aggregation/MergeTestCase.java2
-rw-r--r--searchlib/src/test/java/com/yahoo/searchlib/expression/FixedWidthBucketFunctionTestCase.java2
-rwxr-xr-xsearchlib/src/test/java/com/yahoo/searchlib/expression/ObjectVisitorTestCase.java2
-rw-r--r--searchlib/src/test/java/com/yahoo/searchlib/expression/RangeBucketPreDefFunctionTestCase.java2
-rw-r--r--searchlib/src/test/java/com/yahoo/searchlib/expression/TimeStampFunctionTestCase.java2
-rw-r--r--searchlib/src/test/java/com/yahoo/searchlib/expression/ZCurveFunctionTestCase.java2
-rw-r--r--searchlib/src/test/java/com/yahoo/searchlib/gbdt/GbdtConverterTestCase.java2
-rw-r--r--searchlib/src/test/java/com/yahoo/searchlib/gbdt/GbdtModelTestCase.java2
-rw-r--r--searchlib/src/test/java/com/yahoo/searchlib/gbdt/ReferenceNodeTestCase.java2
-rw-r--r--searchlib/src/test/java/com/yahoo/searchlib/gbdt/ResponseNodeTestCase.java2
-rw-r--r--searchlib/src/test/java/com/yahoo/searchlib/gbdt/TreeNodeTestCase.java2
-rw-r--r--searchlib/src/test/java/com/yahoo/searchlib/gbdt/XmlHelperTestCase.java2
-rwxr-xr-xsearchlib/src/test/java/com/yahoo/searchlib/rankingexpression/FeatureListTestCase.java2
-rwxr-xr-xsearchlib/src/test/java/com/yahoo/searchlib/rankingexpression/RankingExpressionTestCase.java2
-rw-r--r--searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/evaluation/Benchmark.java2
-rw-r--r--searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/integration/ml/VariableConverterTestCase.java2
-rw-r--r--searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/rule/ArgumentsTestCase.java2
-rw-r--r--searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/rule/ReferenceNodeTestCase.java2
-rwxr-xr-xsearchlib/src/test/java/com/yahoo/searchlib/treenet/TreeNetParserTestCase.java2
-rw-r--r--searchlib/src/tests/aggregator/attr_test.cpp3
-rw-r--r--searchlib/src/tests/aggregator/perdocexpr.cpp4
-rw-r--r--searchlib/src/tests/attribute/attribute_test.cpp30
-rw-r--r--searchlib/src/tests/attribute/benchmark/attributebenchmark.cpp5
-rw-r--r--searchlib/src/tests/attribute/benchmark/attributeupdater.h5
-rw-r--r--searchlib/src/tests/attribute/document_weight_iterator/document_weight_iterator_test.cpp3
-rw-r--r--searchlib/src/tests/attribute/enumstore/enumstore_test.cpp38
-rw-r--r--searchlib/src/tests/attribute/imported_search_context/imported_search_context_test.cpp2
-rw-r--r--searchlib/src/tests/attribute/postinglistattribute/postinglistattribute_test.cpp1
-rw-r--r--searchlib/src/tests/attribute/searchable/attribute_searchable_adapter_test.cpp7
-rw-r--r--searchlib/src/tests/attribute/searchable/attribute_weighted_set_blueprint_test.cpp6
-rw-r--r--searchlib/src/tests/attribute/searchcontext/searchcontext.cpp46
-rw-r--r--searchlib/src/tests/attribute/stringattribute/stringattribute_test.cpp2
-rw-r--r--searchlib/src/tests/btree/frozenbtree_test.cpp2
-rw-r--r--searchlib/src/tests/common/sequencedtaskexecutor/sequencedtaskexecutor_test.cpp11
-rw-r--r--searchlib/src/tests/diskindex/fusion/fusion_test.cpp3
-rw-r--r--searchlib/src/tests/diskindex/pagedict4/pagedict4_hugeword_cornercase_test.cpp3
-rw-r--r--searchlib/src/tests/docstore/logdatastore/logdatastore_test.cpp157
-rw-r--r--searchlib/src/tests/features/max_reduce_prod_join_replacer/max_reduce_prod_join_replacer_test.cpp5
-rw-r--r--searchlib/src/tests/features/native_dot_product/native_dot_product_test.cpp21
-rw-r--r--searchlib/src/tests/features/prod_features.cpp56
-rw-r--r--searchlib/src/tests/features/prod_features.h1
-rw-r--r--searchlib/src/tests/fef/termfieldmodel/termfieldmodel_test.cpp44
-rw-r--r--searchlib/src/tests/grouping/grouping_test.cpp3
-rw-r--r--searchlib/src/tests/groupingengine/groupingengine_test.cpp3
-rw-r--r--searchlib/src/tests/predicate/simple_index_test.cpp1
-rw-r--r--searchlib/src/tests/queryeval/queryeval.cpp6
-rw-r--r--searchlib/src/tests/queryeval/same_element/same_element_test.cpp24
-rw-r--r--searchlib/src/tests/util/bufferwriter/bm.cpp2
-rw-r--r--searchlib/src/tests/util/ioerrorhandler/ioerrorhandler_test.cpp12
-rw-r--r--searchlib/src/tests/util/sigbushandler/sigbushandler_test.cpp6
-rw-r--r--searchlib/src/vespa/searchlib/aggregation/hitsaggregationresult.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/attribute/attribute_header.cpp4
-rw-r--r--searchlib/src/vespa/searchlib/attribute/attribute_header.h6
-rw-r--r--searchlib/src/vespa/searchlib/attribute/attribute_weighted_set_blueprint.cpp56
-rw-r--r--searchlib/src/vespa/searchlib/attribute/attribute_weighted_set_blueprint.h16
-rw-r--r--searchlib/src/vespa/searchlib/attribute/attributefile.cpp4
-rw-r--r--searchlib/src/vespa/searchlib/attribute/attributefilewriter.cpp37
-rw-r--r--searchlib/src/vespa/searchlib/attribute/attributeiterators.cpp26
-rw-r--r--searchlib/src/vespa/searchlib/attribute/attributeiterators.h73
-rw-r--r--searchlib/src/vespa/searchlib/attribute/attributeiterators.hpp60
-rw-r--r--searchlib/src/vespa/searchlib/attribute/attributevector.cpp15
-rw-r--r--searchlib/src/vespa/searchlib/attribute/attributevector.h19
-rw-r--r--searchlib/src/vespa/searchlib/attribute/attrvector.cpp18
-rw-r--r--searchlib/src/vespa/searchlib/attribute/attrvector.h1
-rw-r--r--searchlib/src/vespa/searchlib/attribute/attrvector.hpp3
-rw-r--r--searchlib/src/vespa/searchlib/attribute/createarraystd.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/attribute/createsetstd.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/attribute/createsinglefastsearch.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/attribute/createsinglestd.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/attribute/diversity.cpp6
-rw-r--r--searchlib/src/vespa/searchlib/attribute/diversity.h8
-rw-r--r--searchlib/src/vespa/searchlib/attribute/diversity.hpp10
-rw-r--r--searchlib/src/vespa/searchlib/attribute/enumattribute.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/attribute/enumattribute.hpp4
-rw-r--r--searchlib/src/vespa/searchlib/attribute/enumcomparator.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/attribute/enumstore.cpp6
-rw-r--r--searchlib/src/vespa/searchlib/attribute/enumstore.h1
-rw-r--r--searchlib/src/vespa/searchlib/attribute/enumstore.hpp14
-rw-r--r--searchlib/src/vespa/searchlib/attribute/enumstorebase.cpp21
-rw-r--r--searchlib/src/vespa/searchlib/attribute/enumstorebase.h6
-rw-r--r--searchlib/src/vespa/searchlib/attribute/extendableattributes.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/attribute/fixedsourceselector.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/attribute/flagattribute.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/attribute/floatbase.h1
-rw-r--r--searchlib/src/vespa/searchlib/attribute/floatbase.hpp12
-rw-r--r--searchlib/src/vespa/searchlib/attribute/imported_attribute_vector_read_guard.cpp5
-rw-r--r--searchlib/src/vespa/searchlib/attribute/imported_attribute_vector_read_guard.h2
-rw-r--r--searchlib/src/vespa/searchlib/attribute/imported_search_context.cpp9
-rw-r--r--searchlib/src/vespa/searchlib/attribute/imported_search_context.h2
-rw-r--r--searchlib/src/vespa/searchlib/attribute/integerbase.h2
-rw-r--r--searchlib/src/vespa/searchlib/attribute/integerbase.hpp13
-rw-r--r--searchlib/src/vespa/searchlib/attribute/multinumericattribute.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/attribute/multinumericenumattribute.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/attribute/multistringattribute.h5
-rw-r--r--searchlib/src/vespa/searchlib/attribute/multistringattribute.hpp6
-rw-r--r--searchlib/src/vespa/searchlib/attribute/multistringpostattribute.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/attribute/not_implemented_attribute.cpp6
-rw-r--r--searchlib/src/vespa/searchlib/attribute/not_implemented_attribute.h2
-rw-r--r--searchlib/src/vespa/searchlib/attribute/postinglistsearchcontext.cpp8
-rw-r--r--searchlib/src/vespa/searchlib/attribute/postinglistsearchcontext.h18
-rw-r--r--searchlib/src/vespa/searchlib/attribute/postinglistsearchcontext.hpp20
-rw-r--r--searchlib/src/vespa/searchlib/attribute/postingstore.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/attribute/predicate_attribute.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/attribute/reference_attribute.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/attribute/singleenumattribute.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/attribute/singlenumericattribute.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/attribute/singlenumericenumattribute.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/attribute/singlenumericpostattribute.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/attribute/singlesmallnumericattribute.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/attribute/singlestringattribute.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/attribute/singlestringattribute.h3
-rw-r--r--searchlib/src/vespa/searchlib/attribute/singlestringattribute.hpp6
-rw-r--r--searchlib/src/vespa/searchlib/attribute/stringbase.cpp6
-rw-r--r--searchlib/src/vespa/searchlib/attribute/stringbase.h3
-rw-r--r--searchlib/src/vespa/searchlib/bitcompression/pagedict4.cpp5
-rw-r--r--searchlib/src/vespa/searchlib/bitcompression/posocccompression.cpp11
-rw-r--r--searchlib/src/vespa/searchlib/btree/btreeinserter.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/btree/btreeiterator.cpp4
-rw-r--r--searchlib/src/vespa/searchlib/btree/btreeiterator.hpp10
-rw-r--r--searchlib/src/vespa/searchlib/btree/btreeroot.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/btree/btreestore.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/common/bitvector.cpp7
-rw-r--r--searchlib/src/vespa/searchlib/common/bitvector.h8
-rw-r--r--searchlib/src/vespa/searchlib/common/isequencedtaskexecutor.h8
-rw-r--r--searchlib/src/vespa/searchlib/common/packets.cpp4
-rw-r--r--searchlib/src/vespa/searchlib/datastore/datastorebase.cpp7
-rw-r--r--searchlib/src/vespa/searchlib/diskindex/fileheader.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/diskindex/fusion.cpp6
-rw-r--r--searchlib/src/vespa/searchlib/diskindex/indexbuilder.cpp6
-rw-r--r--searchlib/src/vespa/searchlib/diskindex/pagedict4file.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/diskindex/zcposting.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/docstore/documentstore.cpp129
-rw-r--r--searchlib/src/vespa/searchlib/docstore/documentstore.h18
-rw-r--r--searchlib/src/vespa/searchlib/docstore/filechunk.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/docstore/logdatastore.cpp8
-rw-r--r--searchlib/src/vespa/searchlib/docstore/logdocumentstore.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/features/CMakeLists.txt1
-rw-r--r--searchlib/src/vespa/searchlib/features/fieldmatch/computer.h2
-rw-r--r--searchlib/src/vespa/searchlib/features/fieldmatch/metrics.h2
-rw-r--r--searchlib/src/vespa/searchlib/features/fieldmatch/params.h2
-rw-r--r--searchlib/src/vespa/searchlib/features/fieldmatch/segmentstart.h2
-rw-r--r--searchlib/src/vespa/searchlib/features/internal_max_reduce_prod_join_feature.cpp16
-rw-r--r--searchlib/src/vespa/searchlib/features/internal_max_reduce_prod_join_feature.h4
-rw-r--r--searchlib/src/vespa/searchlib/features/native_dot_product_feature.cpp29
-rw-r--r--searchlib/src/vespa/searchlib/features/native_dot_product_feature.h5
-rw-r--r--searchlib/src/vespa/searchlib/features/queryterm.h8
-rw-r--r--searchlib/src/vespa/searchlib/features/random_normal_feature.cpp43
-rw-r--r--searchlib/src/vespa/searchlib/features/random_normal_feature.h17
-rw-r--r--searchlib/src/vespa/searchlib/features/random_normal_stable_feature.cpp80
-rw-r--r--searchlib/src/vespa/searchlib/features/random_normal_stable_feature.h63
-rw-r--r--searchlib/src/vespa/searchlib/features/randomfeature.cpp6
-rw-r--r--searchlib/src/vespa/searchlib/features/randomfeature.h8
-rw-r--r--searchlib/src/vespa/searchlib/features/rankingexpressionfeature.cpp10
-rw-r--r--searchlib/src/vespa/searchlib/features/rankingexpressionfeature.h8
-rw-r--r--searchlib/src/vespa/searchlib/features/raw_score_feature.cpp14
-rw-r--r--searchlib/src/vespa/searchlib/features/raw_score_feature.h6
-rw-r--r--searchlib/src/vespa/searchlib/features/setup.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/fef/featureexecutor.h8
-rw-r--r--searchlib/src/vespa/searchlib/fef/featurenamebuilder.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/fef/indexproperties.h13
-rw-r--r--searchlib/src/vespa/searchlib/fef/iqueryenvironment.h8
-rw-r--r--searchlib/src/vespa/searchlib/fef/itermdata.h7
-rw-r--r--searchlib/src/vespa/searchlib/fef/itermfielddata.h7
-rw-r--r--searchlib/src/vespa/searchlib/fef/number_or_object.h6
-rw-r--r--searchlib/src/vespa/searchlib/fef/objectstore.h4
-rw-r--r--searchlib/src/vespa/searchlib/fef/ranksetup.cpp8
-rw-r--r--searchlib/src/vespa/searchlib/fef/ranksetup.h6
-rw-r--r--searchlib/src/vespa/searchlib/fef/termfieldmatchdata.cpp34
-rw-r--r--searchlib/src/vespa/searchlib/fef/termfieldmatchdata.h14
-rw-r--r--searchlib/src/vespa/searchlib/index/docbuilder.h1
-rw-r--r--searchlib/src/vespa/searchlib/memoryindex/compact_document_words_store.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/memoryindex/dictionary.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/memoryindex/memoryfieldindex.cpp5
-rw-r--r--searchlib/src/vespa/searchlib/memoryindex/ordereddocumentinserter.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/memoryindex/postingiterator.cpp4
-rw-r--r--searchlib/src/vespa/searchlib/parsequery/simplequerystack.cpp23
-rw-r--r--searchlib/src/vespa/searchlib/parsequery/stackdumpiterator.cpp26
-rw-r--r--searchlib/src/vespa/searchlib/predicate/document_features_store.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/predicate/predicate_index.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/predicate/predicate_interval_store.cpp4
-rw-r--r--searchlib/src/vespa/searchlib/predicate/predicate_tree_annotator.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/predicate/predicate_zero_constraint_posting_list.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/predicate/simple_index.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/andnotsearch.cpp6
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/andnotsearch.h8
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/blueprint.cpp59
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/blueprint.h10
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/fake_search.cpp29
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/fake_search.h4
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/field_spec.h42
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/leaf_blueprints.cpp7
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/leaf_blueprints.h7
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/predicate_blueprint.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/same_element_search.h1
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/scores.h7
-rw-r--r--searchlib/src/vespa/searchlib/tensor/dense_tensor_attribute.cpp5
-rw-r--r--searchlib/src/vespa/searchlib/test/fakedata/fakeegcompr64filterocc.cpp5
-rw-r--r--searchlib/src/vespa/searchlib/test/fakedata/fakememtreeocc.cpp5
-rw-r--r--searchlib/src/vespa/searchlib/transactionlog/domainpart.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/transactionlog/translogserverapp.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/util/random_normal.h67
-rw-r--r--searchlib/src/vespa/searchlib/util/statebuf.cpp10
-rw-r--r--searchlib/src/vespa/searchlib/util/statefile.cpp33
-rw-r--r--searchsummary/src/tests/docsummary/positionsdfw_test.cpp10
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/rankfeaturesdfw.cpp12
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/ApplicationHealthMonitor.java25
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthClient.java137
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthEndpoint.java35
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthInfo.java14
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitor.java40
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitorManager.java31
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HttpHealthEndpoint.java44
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HttpsHealthEndpoint.java53
-rw-r--r--service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/ApplicationHealthMonitorTest.java112
-rw-r--r--service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/HealthClientTest.java165
-rw-r--r--service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitorTest.java26
-rw-r--r--slobrok/src/vespa/slobrok/sbmirror.cpp6
-rw-r--r--slobrok/src/vespa/slobrok/server/cmd.cpp2
-rw-r--r--socket_test/src/main/java/com/yahoo/socket/test/SocketTestApp.java2
-rw-r--r--staging_vespalib/src/tests/stllike/cache_test.cpp54
-rw-r--r--staging_vespalib/src/vespa/vespalib/objects/identifiable.cpp2
-rw-r--r--staging_vespalib/src/vespa/vespalib/stllike/cache.h3
-rw-r--r--staging_vespalib/src/vespa/vespalib/stllike/cache.hpp18
-rw-r--r--staging_vespalib/src/vespa/vespalib/util/polymorphicarrays.h5
-rw-r--r--staging_vespalib/src/vespa/vespalib/util/process_memory_stats.cpp2
-rw-r--r--standalone-container/pom.xml4
-rw-r--r--standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneContainerApplication.java2
-rwxr-xr-xstandalone-container/src/main/sh/standalone-container.sh2
-rw-r--r--standalone-container/vespa-standalone-container.spec1
-rw-r--r--statistics/src/main/java/com/yahoo/statistics/Statistics.java2
-rw-r--r--storage/src/tests/bucketdb/bucketmanagertest.cpp1
-rw-r--r--storage/src/tests/bucketdb/judymultimaptest.cpp3
-rw-r--r--storage/src/tests/bucketdb/lockablemaptest.cpp3
-rw-r--r--storage/src/tests/distributor/externaloperationhandlertest.cpp1
-rw-r--r--storage/src/tests/distributor/twophaseupdateoperationtest.cpp8
-rw-r--r--storage/src/tests/distributor/updateoperationtest.cpp46
-rw-r--r--storage/src/tests/persistence/common/filestortestfixture.cpp29
-rw-r--r--storage/src/tests/persistence/common/filestortestfixture.h8
-rw-r--r--storage/src/tests/persistence/filestorage/filestormanagertest.cpp1
-rw-r--r--storage/src/tests/persistence/filestorage/mergeblockingtest.cpp2
-rw-r--r--storage/src/tests/persistence/filestorage/operationabortingtest.cpp12
-rw-r--r--storage/src/tests/persistence/persistencequeuetest.cpp196
-rw-r--r--storage/src/tests/persistence/persistencetestutils.cpp26
-rw-r--r--storage/src/tests/persistence/persistencetestutils.h3
-rw-r--r--storage/src/tests/persistence/testandsettest.cpp2
-rw-r--r--storage/src/tests/storageserver/changedbucketownershiphandlertest.cpp18
-rw-r--r--storage/src/tests/storageserver/documentapiconvertertest.cpp8
-rw-r--r--storage/src/tests/visiting/visitortest.cpp41
-rw-r--r--storage/src/vespa/storage/bucketdb/judymultimap.hpp9
-rw-r--r--storage/src/vespa/storage/bucketdb/lockablemap.hpp4
-rw-r--r--storage/src/vespa/storage/bucketdb/storbucketdb.cpp3
-rw-r--r--storage/src/vespa/storage/config/stor-communicationmanager.def13
-rw-r--r--storage/src/vespa/storage/distributor/maintenance/maintenancescheduler.cpp6
-rw-r--r--storage/src/vespa/storage/distributor/operations/external/statbucketlistoperation.h3
-rw-r--r--storage/src/vespa/storage/distributor/throttlingoperationstarter.h5
-rw-r--r--storage/src/vespa/storage/frameworkimpl/thread/appkiller.cpp2
-rw-r--r--storage/src/vespa/storage/persistence/filestorage/filestorhandler.cpp4
-rw-r--r--storage/src/vespa/storage/persistence/filestorage/filestorhandler.h5
-rw-r--r--storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.cpp143
-rw-r--r--storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.h47
-rw-r--r--storage/src/vespa/storage/persistence/messages.h6
-rw-r--r--storage/src/vespa/storage/persistence/persistenceutil.cpp7
-rw-r--r--storage/src/vespa/storage/storageserver/CMakeLists.txt1
-rw-r--r--storage/src/vespa/storage/storageserver/bouncer.cpp2
-rw-r--r--storage/src/vespa/storage/storageserver/bucketintegritychecker.cpp3
-rw-r--r--storage/src/vespa/storage/storageserver/communicationmanager.cpp11
-rw-r--r--storage/src/vespa/storage/storageserver/config_logging.cpp22
-rw-r--r--storage/src/vespa/storage/storageserver/config_logging.h11
-rw-r--r--storage/src/vespa/storage/storageserver/mergethrottler.cpp2
-rw-r--r--storage/src/vespa/storage/storageserver/storagenode.cpp43
-rw-r--r--storage/src/vespa/storage/visiting/stor-visitor.def1
-rw-r--r--storage/src/vespa/storage/visiting/visitorthread.cpp7
-rw-r--r--storageapi/src/tests/mbusprot/storageprotocoltest.cpp4
-rw-r--r--storageapi/src/vespa/storageapi/mbusprot/protocolserialization5_0.cpp3
-rw-r--r--storageapi/src/vespa/storageapi/message/persistence.h26
-rw-r--r--storageapi/src/vespa/storageapi/messageapi/storagemessage.cpp15
-rw-r--r--storageapi/src/vespa/storageapi/messageapi/storagemessage.h19
-rw-r--r--storageframework/src/vespa/storageframework/defaultimplementation/thread/threadpoolimpl.cpp4
-rw-r--r--storageserver/src/vespa/storageserver/app/servicelayerprocess.cpp3
-rw-r--r--streamingvisitors/src/tests/searchvisitor/searchvisitor.cpp4
-rw-r--r--vagrant/README.md39
-rw-r--r--vagrant/Vagrantfile58
-rw-r--r--vdslib/src/main/java/com/yahoo/vdslib/BucketDistribution.java2
-rw-r--r--vdslib/src/test/java/com/yahoo/vdslib/BucketDistributionTestCase.java2
-rw-r--r--vdslib/src/vespa/vdslib/distribution/distribution.cpp4
-rw-r--r--vespa-application-maven-plugin/src/main/java/com/yahoo/container/plugin/mojo/ApplicationMojo.java2
-rw-r--r--vespa-application-maven-plugin/src/main/java/com/yahoo/container/plugin/mojo/Compression.java2
-rw-r--r--vespa-athenz/pom.xml28
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzIdentityCertificate.java27
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzPrincipal.java22
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzPublicKey.java49
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzResourceName.java74
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzRole.java5
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/ZToken.java30
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/DefaultZtsClient.java57
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/Identity.java29
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/ZtsClient.java39
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/ZtsClientException.java22
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/ErrorResponseEntity.java23
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/IdentityRefreshRequestEntity.java24
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/IdentityResponseEntity.java40
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceIdentityCredentials.java13
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceRefreshInformation.java8
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/RoleCertificateRequestEntity.java13
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/RoleCertificateResponseEntity.java13
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/TenantDomainsResponseEntity.java21
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/Pkcs10CsrSerializer.java20
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/X509CertificateDeserializer.java21
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/X509CertificateListDeserializer.java22
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/package-info.java8
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/utils/IdentityCsrGenerator.java37
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/utils/package-info.java8
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapper.java7
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/SignedIdentityDocument.java1
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java2
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentials.java9
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java103
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java100
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSigner.java85
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/ZtsClient.java64
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/AthenzX509CertificateUtils.java58
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/KeyUtils.java14
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/SignatureAlgorithm.java3
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/X509CertificateUtils.java51
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/SiaUtils.java73
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/ntoken/AthenzConfTruststore.java57
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/ntoken/AthenzTruststore.java15
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/ntoken/NTokenValidator.java72
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/ntoken/package-info.java8
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/zpe/AuthorizationResult.java46
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/zpe/DefaultZpe.java29
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/zpe/Zpe.java17
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/zpe/package-info.java8
-rw-r--r--vespa-athenz/src/test/java/com/yahoo/vespa/athenz/api/AthenzResourceNameTest.java21
-rw-r--r--vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImplTest.java4
-rw-r--r--vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java50
-rw-r--r--vespa-athenz/src/test/java/com/yahoo/vespa/athenz/tls/KeyUtilsTest.java4
-rw-r--r--vespa-athenz/src/test/java/com/yahoo/vespa/athenz/tls/TestUtils.java5
-rw-r--r--vespa-athenz/src/test/java/com/yahoo/vespa/athenz/tls/X509CertificateUtilsTest.java25
-rw-r--r--vespa-athenz/src/test/java/com/yahoo/vespa/athenz/utils/ntoken/NTokenValidatorTest.java (renamed from controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/NTokenValidatorTest.java)52
-rw-r--r--vespa-documentgen-plugin/pom.xml7
-rw-r--r--vespa-http-client/pom.xml6
-rw-r--r--vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/ApacheGatewayConnection.java26
-rw-r--r--vespa-http-client/src/test/java/com/yahoo/vespa/http/client/core/communication/ApacheGatewayConnectionTest.java64
-rw-r--r--vespa_feed_perf/src/main/java/com/yahoo/vespa/feed/perf/FeederParams.java2
-rw-r--r--vespa_feed_perf/src/main/java/com/yahoo/vespa/feed/perf/SimpleFeeder.java2
-rw-r--r--vespa_feed_perf/src/test/java/com/yahoo/vespa/feed/perf/FeederParamsTest.java2
-rw-r--r--vespa_feed_perf/src/test/java/com/yahoo/vespa/feed/perf/SimpleFeederTest.java2
-rw-r--r--vespa_feed_perf/src/test/java/com/yahoo/vespa/feed/perf/SimpleServer.java2
-rwxr-xr-xvespabase/src/rhel-prestart.sh1
-rw-r--r--vespaclient-java/pom.xml21
-rwxr-xr-xvespaclient-java/src/main/sh/vespa-feeder.sh2
-rw-r--r--vespaclient-java/src/main/sh/vespa-get.sh2
-rw-r--r--vespaclient-java/src/main/sh/vespa-stat.sh2
-rwxr-xr-xvespaclient-java/src/main/sh/vespa-summary-benchmark.sh2
-rwxr-xr-xvespaclient-java/src/main/sh/vespa-visit-target.sh2
-rwxr-xr-xvespaclient-java/src/main/sh/vespa-visit.sh2
-rw-r--r--vespaclient/src/perl/lib/Yahoo/Vespa/VespaModel.pm26
-rw-r--r--vespajlib/pom.xml6
-rw-r--r--vespajlib/src/main/java/com/yahoo/binaryprefix/BinaryPrefix.java2
-rw-r--r--vespajlib/src/main/java/com/yahoo/binaryprefix/BinaryScaledAmount.java2
-rw-r--r--vespajlib/src/main/java/com/yahoo/collections/CollectionUtil.java2
-rw-r--r--vespajlib/src/main/java/com/yahoo/collections/LazyMap.java2
-rw-r--r--vespajlib/src/main/java/com/yahoo/collections/LazySet.java2
-rw-r--r--vespajlib/src/main/java/com/yahoo/concurrent/EventBarrier.java2
-rw-r--r--vespajlib/src/main/java/com/yahoo/concurrent/SystemTimer.java2
-rw-r--r--vespajlib/src/main/java/com/yahoo/concurrent/Timer.java2
-rw-r--r--vespajlib/src/main/java/com/yahoo/data/access/simple/JsonRender.java7
-rw-r--r--vespajlib/src/main/java/com/yahoo/errorhandling/Results.java2
-rw-r--r--vespajlib/src/main/java/com/yahoo/geo/BoundingBoxParser.java5
-rw-r--r--vespajlib/src/main/java/com/yahoo/io/reader/NamedReader.java24
-rw-r--r--vespajlib/src/main/java/com/yahoo/javacc/FastCharStream.java2
-rw-r--r--vespajlib/src/main/java/com/yahoo/javacc/UnicodeUtilities.java2
-rw-r--r--vespajlib/src/main/java/com/yahoo/lang/SettableOptional.java5
-rw-r--r--vespajlib/src/main/java/com/yahoo/net/Url.java2
-rw-r--r--vespajlib/src/main/java/com/yahoo/net/UrlToken.java2
-rw-r--r--vespajlib/src/main/java/com/yahoo/net/UrlTokenizer.java2
-rw-r--r--vespajlib/src/main/java/com/yahoo/osgi/maven/ProjectBundleClassPaths.java2
-rw-r--r--vespajlib/src/main/java/com/yahoo/reflection/Casting.java2
-rw-r--r--vespajlib/src/main/java/com/yahoo/reflection/package-info.java2
-rw-r--r--vespajlib/src/main/java/com/yahoo/slime/JsonDecoder.java1
-rw-r--r--vespajlib/src/main/java/com/yahoo/slime/JsonFormat.java7
-rw-r--r--vespajlib/src/main/java/com/yahoo/text/Ascii.java2
-rw-r--r--vespajlib/src/main/java/com/yahoo/text/DoubleFormatter.java517
-rw-r--r--vespajlib/src/main/java/com/yahoo/text/DoubleParser.java182
-rw-r--r--vespajlib/src/main/java/com/yahoo/time/TimeBudget.java76
-rw-r--r--vespajlib/src/main/java/com/yahoo/time/WallClockSource.java118
-rw-r--r--vespajlib/src/main/java/com/yahoo/time/package-info.java5
-rw-r--r--vespajlib/src/main/java/com/yahoo/vespa/objects/Identifiable.java2
-rwxr-xr-xvespajlib/src/main/java/com/yahoo/vespa/objects/ObjectDumper.java2
-rwxr-xr-xvespajlib/src/main/java/com/yahoo/vespa/objects/ObjectOperation.java2
-rwxr-xr-xvespajlib/src/main/java/com/yahoo/vespa/objects/ObjectPredicate.java2
-rwxr-xr-xvespajlib/src/main/java/com/yahoo/vespa/objects/ObjectVisitor.java2
-rw-r--r--vespajlib/src/test/java/com/yahoo/binaryprefix/BinaryScaledAmountTestCase.java2
-rw-r--r--vespajlib/src/test/java/com/yahoo/collections/CollectionUtilTest.java2
-rw-r--r--vespajlib/src/test/java/com/yahoo/collections/LazyMapTest.java2
-rw-r--r--vespajlib/src/test/java/com/yahoo/collections/LazySetTest.java2
-rw-r--r--vespajlib/src/test/java/com/yahoo/concurrent/EventBarrierTestCase.java2
-rw-r--r--vespajlib/src/test/java/com/yahoo/io/HexDumpTestCase.java2
-rw-r--r--vespajlib/src/test/java/com/yahoo/io/reader/NamedReaderTestCase.java3
-rw-r--r--vespajlib/src/test/java/com/yahoo/javacc/FastCharStreamTestCase.java2
-rw-r--r--vespajlib/src/test/java/com/yahoo/javacc/UnicodeUtilitiesTestCase.java2
-rw-r--r--vespajlib/src/test/java/com/yahoo/net/UrlTestCase.java2
-rw-r--r--vespajlib/src/test/java/com/yahoo/net/UrlTokenTestCase.java2
-rw-r--r--vespajlib/src/test/java/com/yahoo/net/UrlTokenizerTestCase.java2
-rw-r--r--vespajlib/src/test/java/com/yahoo/slime/JsonFormatTestCase.java9
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/AsciiTest.java2
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/DoubleFormatterTestCase.java15
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/DoubleParserTestCase.java1
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/DoubleToStringBenchmark.java1
-rw-r--r--vespajlib/src/test/java/com/yahoo/time/TimeBudgetTest.java53
-rw-r--r--vespajlib/src/test/java/com/yahoo/time/WallClockSourceTestCase.java86
-rw-r--r--vespalib/src/apps/make_fixture_macros/make_fixture_macros.cpp5
-rw-r--r--vespalib/src/tests/delegatelist/delegatelist.cpp2
-rw-r--r--vespalib/src/tests/objects/nbostream/nbostream_test.cpp39
-rw-r--r--vespalib/src/vespa/vespalib/component/.gitignore1
-rw-r--r--vespalib/src/vespa/vespalib/data/databuffer.cpp16
-rw-r--r--vespalib/src/vespa/vespalib/data/databuffer.h14
-rw-r--r--vespalib/src/vespa/vespalib/data/slime/binary_format.cpp7
-rw-r--r--vespalib/src/vespa/vespalib/data/slime/inject.cpp5
-rw-r--r--vespalib/src/vespa/vespalib/data/slime/json_format.cpp5
-rw-r--r--vespalib/src/vespa/vespalib/hwaccelrated/iaccelrated.cpp5
-rw-r--r--vespalib/src/vespa/vespalib/net/selector.cpp3
-rw-r--r--vespalib/src/vespa/vespalib/objects/nbostream.cpp28
-rw-r--r--vespalib/src/vespa/vespalib/objects/nbostream.h6
-rw-r--r--vespalib/src/vespa/vespalib/stllike/asciistream.cpp5
-rw-r--r--vespalib/src/vespa/vespalib/testkit/test_master.cpp5
-rw-r--r--vespalib/src/vespa/vespalib/testkit/time_bomb.cpp4
-rw-r--r--vespalib/src/vespa/vespalib/util/CMakeLists.txt7
-rw-r--r--vespalib/src/vespa/vespalib/util/hashmap.h2
-rw-r--r--vespalib/src/vespa/vespalib/util/hdr_abort.cpp23
-rw-r--r--vespalib/src/vespa/vespalib/util/hdr_abort.h14
-rw-r--r--vespalog/src/main/java/com/yahoo/log/LogLevel.java20
-rw-r--r--vespalog/src/main/java/com/yahoo/log/UncloseableOutputStream.java2
-rw-r--r--vespalog/src/test/java/com/yahoo/log/UncloseableOutputStreamTestCase.java2
-rw-r--r--vsm/src/vespa/vsm/searcher/fold.cpp4
-rw-r--r--vsm/src/vespa/vsm/searcher/futf8strchrfieldsearcher.cpp4
-rw-r--r--yolean/src/main/java/com/yahoo/yolean/chain/After.java2
-rw-r--r--yolean/src/main/java/com/yahoo/yolean/chain/Before.java2
-rw-r--r--yolean/src/main/java/com/yahoo/yolean/chain/Chain.java2
-rw-r--r--yolean/src/main/java/com/yahoo/yolean/chain/ChainBuilder.java2
-rw-r--r--yolean/src/main/java/com/yahoo/yolean/chain/ChainCycleException.java2
-rw-r--r--yolean/src/main/java/com/yahoo/yolean/chain/Dependencies.java2
-rw-r--r--yolean/src/main/java/com/yahoo/yolean/chain/DirectedGraph.java2
-rw-r--r--yolean/src/main/java/com/yahoo/yolean/chain/EnumeratedIdentitySet.java2
-rw-r--r--yolean/src/main/java/com/yahoo/yolean/chain/Provides.java2
-rw-r--r--yolean/src/main/java/com/yahoo/yolean/chain/Vertex.java2
-rw-r--r--yolean/src/test/java/com/yahoo/yolean/chain/ChainBuilderTest.java2
-rw-r--r--yolean/src/test/java/com/yahoo/yolean/chain/ChainTest.java2
-rw-r--r--yolean/src/test/java/com/yahoo/yolean/chain/ContainsSameElements.java2
-rw-r--r--yolean/src/test/java/com/yahoo/yolean/trace/TraceVisitorTestCase.java2
-rw-r--r--zkfacade/pom.xml1
-rw-r--r--zkfacade/src/main/java/com/yahoo/vespa/curator/Lock.java5
2703 files changed, 38022 insertions, 15667 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 23dcef6d96d..6c557179750 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -45,6 +45,7 @@ add_subdirectory(container-disc)
add_subdirectory(container-jersey2)
add_subdirectory(container-messagebus)
add_subdirectory(container-search)
+add_subdirectory(container-search-gui)
add_subdirectory(container-search-and-docproc)
add_subdirectory(clustercontroller-apps)
add_subdirectory(clustercontroller-apputil)
diff --git a/README.md b/README.md
index d84ebbe4456..acbeb6ffcec 100644
--- a/README.md
+++ b/README.md
@@ -15,8 +15,8 @@ Travis-CI build status: [![Build Status](https://travis-ci.org/vespa-engine/vesp
## Get started developing
### Setup build environment
-C++ building is supported on CentOS 7. The Java source can be built on any platform having Java 8 and Maven installed.
-We recommend using the following environment: [Create C++ dev environment on CentOS using VirtualBox and Vagrant](vagrant/README.md).
+C++ and Java building is supported on CentOS 7. The Java source can also be built on any platform having Java 8 and Maven installed.
+We recommend using the following environment: [Create C++ / Java dev environment on CentOS using VirtualBox and Vagrant](vagrant/README.md).
You can also setup CentOS 7 natively and install the following build dependencies:
sudo yum-config-manager --add-repo https://copr.fedorainfracloud.org/coprs/g/vespa/vespa/repo/epel-7/group_vespa-vespa-epel-7.repo
diff --git a/annotations/src/main/java/com/yahoo/api/annotations/PublicApi.java b/annotations/src/main/java/com/yahoo/api/annotations/PublicApi.java
index 0cc51abc6bd..261915ac3d9 100644
--- a/annotations/src/main/java/com/yahoo/api/annotations/PublicApi.java
+++ b/annotations/src/main/java/com/yahoo/api/annotations/PublicApi.java
@@ -12,7 +12,7 @@ import java.lang.annotation.Target;
*
* Must be placed in a file called package-info.java in the
* package that is to be public.
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
* @since 5.1.5
*/
diff --git a/annotations/src/main/java/com/yahoo/osgi/annotation/ExportPackage.java b/annotations/src/main/java/com/yahoo/osgi/annotation/ExportPackage.java
index 57d11d3460e..f34c9545c69 100644
--- a/annotations/src/main/java/com/yahoo/osgi/annotation/ExportPackage.java
+++ b/annotations/src/main/java/com/yahoo/osgi/annotation/ExportPackage.java
@@ -13,7 +13,7 @@ import java.lang.annotation.Target;
* Must be placed in a file called package-info.java in the
* package that is to be exported.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.PACKAGE)
diff --git a/annotations/src/main/java/com/yahoo/osgi/annotation/Version.java b/annotations/src/main/java/com/yahoo/osgi/annotation/Version.java
index f53d3931173..9d71f5d16dc 100644
--- a/annotations/src/main/java/com/yahoo/osgi/annotation/Version.java
+++ b/annotations/src/main/java/com/yahoo/osgi/annotation/Version.java
@@ -5,7 +5,7 @@ package com.yahoo.osgi.annotation;
* Version of an exported package
* The default version is 1.0.0
* @see <a href="http://www.osgi.org/javadoc/r4v43/org/osgi/framework/Version.html">Osgi version documentation</a>
- * @author tonytv
+ * @author Tony Vaagenes
*/
public @interface Version {
diff --git a/annotations/src/main/resources/.gitignore b/annotations/src/main/resources/.gitignore
index b347c417aa1..e69de29bb2d 100644
--- a/annotations/src/main/resources/.gitignore
+++ b/annotations/src/main/resources/.gitignore
@@ -1 +0,0 @@
-/getversion
diff --git a/application-deploy-plugin/pom.xml b/application-deploy-plugin/pom.xml
index 501dee385a6..5497466bfec 100644
--- a/application-deploy-plugin/pom.xml
+++ b/application-deploy-plugin/pom.xml
@@ -20,7 +20,6 @@
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
- <version>3.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
@@ -29,7 +28,6 @@
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
- <version>3.5</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -48,7 +46,6 @@
</plugin>
<plugin>
<artifactId>maven-plugin-plugin</artifactId>
- <version>3.5</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
diff --git a/application/pom.xml b/application/pom.xml
index db298451a8b..10d2a14721e 100644
--- a/application/pom.xml
+++ b/application/pom.xml
@@ -46,6 +46,11 @@
</dependency>
<dependency>
<groupId>com.yahoo.vespa</groupId>
+ <artifactId>container-search-gui</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
<artifactId>zkfacade</artifactId>
<version>${project.version}</version>
</dependency>
@@ -81,6 +86,17 @@
<artifactId>icu4j</artifactId>
</dependency>
<dependency>
+ <groupId>com.optimaize.languagedetector</groupId>
+ <artifactId>language-detector</artifactId>
+ <exclusions>
+ <exclusion>
+ <!-- We want to get this via jdisc-core -->
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
</dependency>
@@ -97,6 +113,10 @@
<artifactId>commons-exec</artifactId>
</dependency>
<dependency>
+ <groupId>org.apache.opennlp</groupId>
+ <artifactId>opennlp-tools</artifactId>
+ </dependency>
+ <dependency>
<groupId>org.hdrhistogram</groupId>
<artifactId>HdrHistogram</artifactId>
</dependency>
diff --git a/application/src/main/java/com/yahoo/application/Application.java b/application/src/main/java/com/yahoo/application/Application.java
index 5221f95f897..88140873b7b 100644
--- a/application/src/main/java/com/yahoo/application/Application.java
+++ b/application/src/main/java/com/yahoo/application/Application.java
@@ -112,7 +112,7 @@ public final class Application implements AutoCloseable {
.applicationPackage(FilesApplicationPackage.fromFile(path.toFile(),
/* Include source files */ true))
.deployLogger((level, s) -> { })
- .build(true);
+ .build();
return new VespaModel(new NullConfigModelRegistry(), deployState);
} catch (IOException | SAXException e) {
throw new IllegalArgumentException("Error creating application from '" + path + "'", e);
diff --git a/application/src/main/java/com/yahoo/application/container/handler/Headers.java b/application/src/main/java/com/yahoo/application/container/handler/Headers.java
index cb4ebc3d8b9..8086960df25 100644
--- a/application/src/main/java/com/yahoo/application/container/handler/Headers.java
+++ b/application/src/main/java/com/yahoo/application/container/handler/Headers.java
@@ -16,7 +16,7 @@ import java.util.Set;
* @see Request
* @see Response
* @author Einar M R Rosenvinge
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
@NotThreadSafe
@Beta
diff --git a/application/src/test/java/com/yahoo/application/container/handler/HeadersTestCase.java b/application/src/test/java/com/yahoo/application/container/handler/HeadersTestCase.java
index cda5ddd2ed9..e747329722d 100644
--- a/application/src/test/java/com/yahoo/application/container/handler/HeadersTestCase.java
+++ b/application/src/test/java/com/yahoo/application/container/handler/HeadersTestCase.java
@@ -21,7 +21,7 @@ import static org.junit.Assert.fail;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
* @author Einar M R Rosenvinge
*/
public class HeadersTestCase {
diff --git a/athenz-identity-provider-service/pom.xml b/athenz-identity-provider-service/pom.xml
index 982cb89f2bf..16e18e99e1b 100644
--- a/athenz-identity-provider-service/pom.xml
+++ b/athenz-identity-provider-service/pom.xml
@@ -63,38 +63,6 @@
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
</dependency>
- <dependency>
- <groupId>com.yahoo.athenz</groupId>
- <artifactId>athenz-zts-java-client</artifactId>
- <scope>compile</scope>
- <exclusions>
- <!--Exclude all bundles provided by JDisc -->
- <exclusion>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk15on</artifactId>
- </exclusion>
- <exclusion>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk15on</artifactId>
- </exclusion>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </exclusion>
- <exclusion>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-core</artifactId>
- </exclusion>
- <exclusion>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-databind</artifactId>
- </exclusion>
- <exclusion>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-annotations</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
<!-- TEST -->
<dependency>
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslKeyStoreConfigurator.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslKeyStoreConfigurator.java
index 2a517e06ae2..801eb04d19c 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslKeyStoreConfigurator.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslKeyStoreConfigurator.java
@@ -8,24 +8,30 @@ import com.yahoo.config.provision.Zone;
import com.yahoo.jdisc.http.ssl.SslKeyStoreConfigurator;
import com.yahoo.jdisc.http.ssl.SslKeyStoreContext;
import com.yahoo.log.LogLevel;
+import com.yahoo.vespa.athenz.api.AthenzService;
+import com.yahoo.vespa.athenz.client.zts.DefaultZtsClient;
+import com.yahoo.vespa.athenz.client.zts.Identity;
+import com.yahoo.vespa.athenz.client.zts.ZtsClient;
import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider;
import com.yahoo.vespa.athenz.tls.KeyStoreBuilder;
import com.yahoo.vespa.athenz.tls.KeyStoreType;
+import com.yahoo.vespa.athenz.tls.KeyUtils;
+import com.yahoo.vespa.athenz.utils.SiaUtils;
import com.yahoo.vespa.defaults.Defaults;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.AthenzCertificateClient;
-import java.io.IOException;
-import java.nio.file.Files;
+import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.security.GeneralSecurityException;
+import java.security.KeyPair;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.PrivateKey;
+import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.time.Instant;
+import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.Executors;
@@ -33,7 +39,6 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
-import static com.yahoo.vespa.athenz.tls.KeyStoreUtils.writeKeyStoreToFile;
import static com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.Utils.getZoneConfig;
/**
@@ -47,13 +52,14 @@ public class AthenzSslKeyStoreConfigurator extends AbstractComponent implements
private static final Logger log = Logger.getLogger(AthenzSslKeyStoreConfigurator.class.getName());
private static final String CERTIFICATE_ALIAS = "athenz";
private static final Duration EXPIRATION_MARGIN = Duration.ofHours(6);
+ private static final Path VESPA_SIA_DIRECTORY = Paths.get(Defaults.getDefaults().underVespaHome("var/vespa/sia"));
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
- private final AthenzCertificateClient certificateClient;
+ private final ZtsClient ztsClient;
private final KeyProvider keyProvider;
private final AthenzProviderServiceConfig.Zones zoneConfig;
private final Duration updatePeriod;
- private final Path keystoreDirectory;
+ private final AthenzService configserverIdentity;
private volatile KeyStoreAndPassword currentKeyStore;
@Inject
@@ -63,55 +69,40 @@ public class AthenzSslKeyStoreConfigurator extends AbstractComponent implements
Zone zone,
ConfigserverConfig configserverConfig) {
AthenzProviderServiceConfig.Zones zoneConfig = getZoneConfig(config, zone);
- Path keystoreDirectory = createKeystoreCacheDirectory(configserverConfig);
- AthenzCertificateClient certificateClient = new AthenzCertificateClient(bootstrapIdentity, zoneConfig);
+ AthenzService configserverIdentity = new AthenzService(zoneConfig.domain(), zoneConfig.serviceName());
Duration updatePeriod = Duration.ofDays(config.updatePeriodDays());
- this.certificateClient = certificateClient;
+ DefaultZtsClient ztsClient = new DefaultZtsClient(URI.create(zoneConfig.ztsUrl()).resolve("/zts/v1"), bootstrapIdentity); // TODO Remove URI.resolve() once config in hosted is updated
+ this.ztsClient = ztsClient;
this.keyProvider = keyProvider;
this.zoneConfig = zoneConfig;
- this.currentKeyStore = initializeKeystore(keyProvider, certificateClient, zoneConfig, keystoreDirectory, updatePeriod);
+ this.currentKeyStore = initializeKeystore(configserverIdentity, keyProvider, ztsClient, zoneConfig, updatePeriod);
this.updatePeriod = updatePeriod;
- this.keystoreDirectory = keystoreDirectory;
+ this.configserverIdentity = configserverIdentity;
}
- private static KeyStoreAndPassword initializeKeystore(KeyProvider keyProvider,
- AthenzCertificateClient certificateClient,
- AthenzProviderServiceConfig.Zones zoneConfig,
- Path keystoreCacheDirectory,
- Duration updatePeriod) {
- return tryReadKeystoreFile(keystoreCacheDirectory, updatePeriod)
- .orElseGet(() -> downloadCertificate(keyProvider, certificateClient, zoneConfig, keystoreCacheDirectory));
+ private static KeyStoreAndPassword initializeKeystore(AthenzService configserverIdentity,
+ KeyProvider keyProvider,
+ ZtsClient ztsClient,
+ AthenzProviderServiceConfig.Zones keystoreCacheDirectory,
+ Duration updatePeriod) {
+ return tryReadKeystoreFile(configserverIdentity, updatePeriod)
+ .orElseGet(() -> downloadCertificate(configserverIdentity, keyProvider, ztsClient, keystoreCacheDirectory));
}
- private static Optional<KeyStoreAndPassword> tryReadKeystoreFile(Path keystoreDirectory, Duration updatePeriod) {
- try {
- Path keystoreFile = keystoreFile(keystoreDirectory);
- Path keystoreSecretFile = keystoreSecretFile(keystoreDirectory);
- if (!Files.exists(keystoreFile) || !Files.exists(keystoreSecretFile)) return Optional.empty();
- KeyStore keyStore = KeyStoreBuilder.withType(KeyStoreType.JKS)
- .fromFile(keystoreFile.toFile())
- .build();
- Instant minimumExpiration = Instant.now().plus(updatePeriod).plus(EXPIRATION_MARGIN);
- boolean isExpired = getCertificateExpiry(keyStore).isBefore(minimumExpiration);
- if (isExpired) return Optional.empty();
- char[] pwd = new String(Files.readAllBytes(keystoreSecretFile)).toCharArray();
- return Optional.of(new KeyStoreAndPassword(keyStore, pwd));
- } catch (GeneralSecurityException | IOException e) {
- log.log(LogLevel.ERROR, "Failed to read keystore from disk: " + e.getMessage(), e);
- return Optional.empty();
- }
- }
-
- private static Path createKeystoreCacheDirectory(ConfigserverConfig configserverConfig) {
- return Paths.get(Defaults.getDefaults().underVespaHome(configserverConfig.configServerDBDir()));
- }
-
- private static Path keystoreFile(Path keystoreDirectory) {
- return keystoreDirectory.resolve("server-x509-athenz-cert.jks");
- }
-
- private static Path keystoreSecretFile(Path keystoreDirectory) {
- return keystoreDirectory.resolve("keystore-secret");
+ private static Optional<KeyStoreAndPassword> tryReadKeystoreFile(AthenzService configserverIdentity,
+ Duration updatePeriod) {
+ Optional<X509Certificate> certificate = SiaUtils.readCertificateFile(VESPA_SIA_DIRECTORY, configserverIdentity);
+ if (!certificate.isPresent()) return Optional.empty();
+ Optional<PrivateKey> privateKey = SiaUtils.readPrivateKeyFile(VESPA_SIA_DIRECTORY, configserverIdentity);
+ if (!privateKey.isPresent()) return Optional.empty();
+ Instant minimumExpiration = Instant.now().plus(updatePeriod).plus(EXPIRATION_MARGIN);
+ boolean isExpired = certificate.get().getNotAfter().toInstant().isBefore(minimumExpiration);
+ if (isExpired) return Optional.empty();
+ char[] password = generateKeystorePassword();
+ KeyStore keyStore = KeyStoreBuilder.withType(KeyStoreType.JKS)
+ .withKeyEntry(CERTIFICATE_ALIAS, privateKey.get(), password, certificate.get())
+ .build();
+ return Optional.of(new KeyStoreAndPassword(keyStore, password));
}
@Override
@@ -128,6 +119,7 @@ public class AthenzSslKeyStoreConfigurator extends AbstractComponent implements
try {
scheduler.shutdownNow();
scheduler.awaitTermination(30, TimeUnit.SECONDS);
+ ztsClient.close();
} catch (InterruptedException e) {
throw new RuntimeException("Failed to shutdown Athenz certificate updater on time", e);
}
@@ -142,12 +134,18 @@ public class AthenzSslKeyStoreConfigurator extends AbstractComponent implements
return certificate.getNotAfter().toInstant();
}
- private static KeyStoreAndPassword downloadCertificate(KeyProvider keyProvider,
- AthenzCertificateClient certificateClient,
- AthenzProviderServiceConfig.Zones zoneConfig,
- Path keystoreDirectory) {
+ private static KeyStoreAndPassword downloadCertificate(AthenzService configserverIdentity,
+ KeyProvider keyProvider,
+ ZtsClient ztsClient,
+ AthenzProviderServiceConfig.Zones zoneConfig) {
PrivateKey privateKey = keyProvider.getPrivateKey(zoneConfig.secretVersion());
- X509Certificate certificate = certificateClient.updateCertificate(privateKey);
+ PublicKey publicKey = KeyUtils.extractPublicKey(privateKey);
+ Identity serviceIdentity = ztsClient.getServiceIdentity(configserverIdentity,
+ Integer.toString(zoneConfig.secretVersion()),
+ new KeyPair(publicKey, privateKey),
+ zoneConfig.certDnsSuffix());
+ X509Certificate certificate = serviceIdentity.certificate();
+ writeCredentials(configserverIdentity, certificate, serviceIdentity.caCertificates(), privateKey);
Instant expirationTime = certificate.getNotAfter().toInstant();
Duration expiry = Duration.between(certificate.getNotBefore().toInstant(), expirationTime);
log.log(LogLevel.INFO, String.format("Got Athenz x509 certificate with expiry %s (expires %s)", expiry, expirationTime));
@@ -156,18 +154,15 @@ public class AthenzSslKeyStoreConfigurator extends AbstractComponent implements
KeyStore keyStore = KeyStoreBuilder.withType(KeyStoreType.JKS)
.withKeyEntry(CERTIFICATE_ALIAS, privateKey, keystorePassword, certificate)
.build();
- KeyStoreAndPassword keyStoreAndPassword = new KeyStoreAndPassword(keyStore, keystorePassword);
- tryWriteKeystore(keyStoreAndPassword, keystoreDirectory);
- return keyStoreAndPassword;
+ return new KeyStoreAndPassword(keyStore, keystorePassword);
}
- private static void tryWriteKeystore(KeyStoreAndPassword keyStore, Path keystoreDirectory) {
- try {
- writeKeyStoreToFile(keyStore.keyStore, keystoreFile(keystoreDirectory).toFile());
- Files.write(keystoreSecretFile(keystoreDirectory), new String(keyStore.password).getBytes());
- } catch (Exception e) {
- log.log(LogLevel.ERROR, "Failed to write keystore to disk: " + e.getMessage(), e);
- }
+ private static void writeCredentials(AthenzService configserverIdentity,
+ X509Certificate certificate,
+ List<X509Certificate> caCertificates,
+ PrivateKey privateKey) {
+ SiaUtils.writeCertificateFile(VESPA_SIA_DIRECTORY, configserverIdentity, certificate);
+ SiaUtils.writePrivateKeyFile(VESPA_SIA_DIRECTORY, configserverIdentity, privateKey);
}
private static char[] generateKeystorePassword() {
@@ -186,7 +181,7 @@ public class AthenzSslKeyStoreConfigurator extends AbstractComponent implements
public void run() {
try {
log.log(LogLevel.INFO, "Updating Athenz certificate from ZTS");
- currentKeyStore = downloadCertificate(keyProvider, certificateClient, zoneConfig, keystoreDirectory);
+ currentKeyStore = downloadCertificate(configserverIdentity, keyProvider, ztsClient, zoneConfig);
sslKeyStoreContext.updateKeyStore(currentKeyStore.keyStore, new String(currentKeyStore.password));
log.log(LogLevel.INFO, "Athenz certificate reload successfully completed");
} catch (Throwable e) {
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGenerator.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGenerator.java
index 59126fd023f..0abbb5a64f5 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGenerator.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGenerator.java
@@ -5,11 +5,11 @@ import com.google.inject.Inject;
import com.yahoo.config.provision.Zone;
import com.yahoo.net.HostName;
import com.yahoo.vespa.athenz.api.AthenzService;
-import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper;
import com.yahoo.vespa.athenz.identityprovider.api.IdentityDocument;
import com.yahoo.vespa.athenz.identityprovider.api.IdentityType;
import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument;
import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId;
+import com.yahoo.vespa.athenz.identityprovider.client.IdentityDocumentSigner;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.KeyProvider;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.Utils;
@@ -17,14 +17,10 @@ import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.node.Allocation;
-import java.net.InetAddress;
import java.net.URI;
import java.security.PrivateKey;
-import java.security.Signature;
import java.time.Instant;
-import java.util.Base64;
import java.util.HashSet;
-import java.util.Objects;
import java.util.Set;
/**
@@ -35,6 +31,7 @@ import java.util.Set;
*/
public class IdentityDocumentGenerator {
+ private final IdentityDocumentSigner signer = new IdentityDocumentSigner();
private final NodeRepository nodeRepository;
private final Zone zone;
private final KeyProvider keyProvider;
@@ -55,16 +52,13 @@ public class IdentityDocumentGenerator {
Node node = nodeRepository.getNode(hostname).orElseThrow(() -> new RuntimeException("Unable to find node " + hostname));
try {
IdentityDocument identityDocument = generateIdDocument(node, identityType);
- String identityDocumentString = Utils.getMapper().writeValueAsString(EntityBindingsMapper.toIdentityDocumentEntity(identityDocument));
-
- String encodedIdentityDocument =
- Base64.getEncoder().encodeToString(identityDocumentString.getBytes());
- Signature sigGenerator = Signature.getInstance("SHA512withRSA");
PrivateKey privateKey = keyProvider.getPrivateKey(zoneConfig.secretVersion());
- sigGenerator.initSign(privateKey);
- sigGenerator.update(encodedIdentityDocument.getBytes());
- String signature = Base64.getEncoder().encodeToString(sigGenerator.sign());
+ AthenzService providerService = new AthenzService(zoneConfig.domain(), zoneConfig.serviceName());
+
+ String signature = signer.generateSignature(
+ identityDocument.providerUniqueId(), providerService, identityDocument.configServerHostname(),
+ identityDocument.instanceHostname(), identityDocument.createdAt(), identityDocument.ipAddresses(), identityType, privateKey);
return new SignedIdentityDocument(
identityDocument,
@@ -72,7 +66,7 @@ public class IdentityDocumentGenerator {
SignedIdentityDocument.DEFAULT_KEY_VERSION,
identityDocument.providerUniqueId(),
toZoneDnsSuffix(zone, zoneConfig.certDnsSuffix()),
- new AthenzService(zoneConfig.domain(), zoneConfig.serviceName()),
+ providerService,
URI.create(zoneConfig.ztsUrl()),
SignedIdentityDocument.DEFAULT_DOCUMENT_VERSION,
identityDocument.configServerHostname(),
@@ -110,28 +104,5 @@ public class IdentityDocumentGenerator {
return zone.environment().value() + "-" + zone.region().value() + "." + dnsSuffix;
}
- /*
- * Basic access control until we have mutual auth where athenz x509certs are distributed on all docker nodes by node admin
- * Checks:
- * If remote hostname == requested hostname --> OK
- * If remote hostname is parent of requested hostname in node repo --> OK
- * Otherwise NOT OK
- */
- // TODO Move this check to AuthorizationFilter in node-repository
- boolean validateAccess(String hostname, String remoteAddr) {
- try {
- InetAddress addr = InetAddress.getByName(remoteAddr);
- String remoteHostname = addr.getHostName();
- if (Objects.equals(hostname, remoteHostname)) {
- return true;
- }
- Node node = nodeRepository.getNode(hostname).orElseThrow(() -> new RuntimeException("Unable to find node " + hostname));
- return node.parentHostname()
- .map(parent -> Objects.equals(parent, remoteHostname))
- .orElse(false);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentResource.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentResource.java
index 219e12c7223..7151de9ccc9 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentResource.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentResource.java
@@ -3,27 +3,24 @@ package com.yahoo.vespa.hosted.athenz.instanceproviderservice.identitydocument;
import com.google.inject.Inject;
import com.yahoo.container.jaxrs.annotation.Component;
-import com.yahoo.jdisc.http.servlet.ServletRequest;
import com.yahoo.log.LogLevel;
import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper;
import com.yahoo.vespa.athenz.identityprovider.api.IdentityType;
import com.yahoo.vespa.athenz.identityprovider.api.bindings.IdentityDocumentApi;
import com.yahoo.vespa.athenz.identityprovider.api.bindings.SignedIdentityDocumentEntity;
-import com.yahoo.vespa.hosted.provision.restapi.v2.filter.NodePrincipal;
-import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.BadRequestException;
-import javax.ws.rs.ForbiddenException;
import javax.ws.rs.GET;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
-import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import java.util.logging.Logger;
/**
+ * An API that issues signed identity documents for Vespa nodes.
+ *
* @author bjorncs
*/
@Path("/identity-document")
@@ -32,32 +29,16 @@ public class IdentityDocumentResource implements IdentityDocumentApi {
private static final Logger log = Logger.getLogger(IdentityDocumentResource.class.getName());
private final IdentityDocumentGenerator identityDocumentGenerator;
- private final HttpServletRequest request;
@Inject
- public IdentityDocumentResource(@Component IdentityDocumentGenerator identityDocumentGenerator,
- @Context HttpServletRequest request) {
+ public IdentityDocumentResource(@Component IdentityDocumentGenerator identityDocumentGenerator) {
this.identityDocumentGenerator = identityDocumentGenerator;
- this.request = request;
}
private SignedIdentityDocumentEntity getIdentityDocument(String hostname, IdentityType identityType) {
if (hostname == null) {
throw new BadRequestException("The 'hostname' query parameter is missing");
}
- NodePrincipal principal = (NodePrincipal) request.getAttribute(ServletRequest.JDISC_REQUEST_PRINCIPAL);
- String remoteHost;
- if (principal == null) {
- // TODO Remove once self-signed certs are gone
- log.warning("Client is not authenticated - fallback to remote ip");
- remoteHost = request.getRemoteAddr();
- } else {
- remoteHost = principal.getHostIdentityName();
- }
- // TODO Move this check to AuthorizationFilter in node-repository
- if (!identityDocumentGenerator.validateAccess(hostname, remoteHost)) {
- throw new ForbiddenException();
- }
try {
return EntityBindingsMapper.toSignedIdentityDocumentEntity(identityDocumentGenerator.generateSignedIdentityDocument(hostname, identityType));
} catch (Exception e) {
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/AthenzCertificateClient.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/AthenzCertificateClient.java
deleted file mode 100644
index 193a573c98d..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/AthenzCertificateClient.java
+++ /dev/null
@@ -1,40 +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.hosted.athenz.instanceproviderservice.impl;
-
-import com.yahoo.athenz.zts.InstanceRefreshRequest;
-import com.yahoo.athenz.zts.ZTSClient;
-import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider;
-import com.yahoo.vespa.athenz.tls.X509CertificateUtils;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
-
-import javax.net.ssl.SSLContext;
-import java.security.PrivateKey;
-import java.security.cert.X509Certificate;
-
-/**
- * @author bjorncs
- */
-public class AthenzCertificateClient {
-
- private final AthenzProviderServiceConfig.Zones zoneConfig;
- private final ServiceIdentityProvider bootstrapIdentity;
-
- public AthenzCertificateClient(ServiceIdentityProvider bootstrapIdentity,
- AthenzProviderServiceConfig.Zones zoneConfig) {
- this.bootstrapIdentity = bootstrapIdentity;
- this.zoneConfig = zoneConfig;
- }
-
- public X509Certificate updateCertificate(PrivateKey privateKey) {
- SSLContext bootstrapSslContext = bootstrapIdentity.getIdentitySslContext();
- ZTSClient ztsClient = new ZTSClient(zoneConfig.ztsUrl(), bootstrapSslContext);
- InstanceRefreshRequest req =
- ZTSClient.generateInstanceRefreshRequest(
- zoneConfig.domain(), zoneConfig.serviceName(), privateKey, zoneConfig.certDnsSuffix(), /*expiryTime*/0);
- req.setKeyId(Integer.toString(zoneConfig.secretVersion()));
- String pemEncoded = ztsClient.postInstanceRefreshRequest(zoneConfig.domain(), zoneConfig.serviceName(), req)
- .getCertificate();
- return X509CertificateUtils.fromPem(pemEncoded);
- }
-
-}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceConfirmationResource.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceConfirmationResource.java
index 4c71cb7855d..7a4e82d134b 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceConfirmationResource.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceConfirmationResource.java
@@ -16,7 +16,7 @@ import java.util.logging.Logger;
/**
* @author bjorncs
*/
-@Path("/{path: instance|refresh}")
+@Path("/instance")
public class InstanceConfirmationResource {
private static final Logger log = Logger.getLogger(InstanceConfirmationResource.class.getName());
@@ -32,6 +32,7 @@ public class InstanceConfirmationResource {
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public InstanceConfirmation confirmInstance(InstanceConfirmation instanceConfirmation) {
+ log.log(LogLevel.DEBUG, instanceConfirmation.toString());
if (!instanceValidator.isValidInstance(instanceConfirmation)) {
log.log(LogLevel.ERROR, "Invalid instance: " + instanceConfirmation);
throw new ForbiddenException("Instance is invalid");
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceRefreshResource.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceRefreshResource.java
new file mode 100644
index 00000000000..53972a39c97
--- /dev/null
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceRefreshResource.java
@@ -0,0 +1,44 @@
+// 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.athenz.instanceproviderservice.instanceconfirmation;
+
+import com.google.inject.Inject;
+import com.yahoo.container.jaxrs.annotation.Component;
+import com.yahoo.log.LogLevel;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.ForbiddenException;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import java.util.logging.Logger;
+
+/**
+ * ZTS calls this resource when it's requested to refresh an instance certificate
+ *
+ * @author bjorncs
+ */
+@Path("/refresh")
+public class InstanceRefreshResource {
+
+ private static final Logger log = Logger.getLogger(InstanceRefreshResource.class.getName());
+
+ private final InstanceValidator instanceValidator;
+
+ @Inject
+ public InstanceRefreshResource(@Component InstanceValidator instanceValidator) {
+ this.instanceValidator = instanceValidator;
+ }
+
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public InstanceConfirmation confirmInstanceRefresh(InstanceConfirmation instanceConfirmation) {
+ log.log(LogLevel.DEBUG, instanceConfirmation.toString());
+ if (!instanceValidator.isValidRefresh(instanceConfirmation)) {
+ log.log(LogLevel.ERROR, "Invalid instance refresh: " + instanceConfirmation);
+ throw new ForbiddenException("Instance is invalid");
+ }
+ return instanceConfirmation;
+ }
+}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidator.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidator.java
index 0201c46b253..dcaf50c1c04 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidator.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidator.java
@@ -11,15 +11,10 @@ import com.yahoo.vespa.athenz.api.AthenzService;
import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper;
import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument;
import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId;
-import com.yahoo.vespa.athenz.identityprovider.api.bindings.SignedIdentityDocumentEntity;
+import com.yahoo.vespa.athenz.identityprovider.client.IdentityDocumentSigner;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.KeyProvider;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.util.Base64;
import java.util.Optional;
import java.util.logging.Logger;
@@ -35,6 +30,7 @@ public class InstanceValidator {
static final String SERVICE_PROPERTIES_DOMAIN_KEY = "identity.domain";
static final String SERVICE_PROPERTIES_SERVICE_KEY = "identity.service";
+ private final IdentityDocumentSigner signer = new IdentityDocumentSigner();
private final KeyProvider keyProvider;
private final SuperModelProvider superModelProvider;
@@ -55,7 +51,9 @@ public class InstanceValidator {
}
log.log(LogLevel.INFO, () -> String.format("Validating instance %s.", providerUniqueId));
- if (isInstanceSignatureValid(instanceConfirmation)) {
+
+ PublicKey publicKey = keyProvider.getPublicKey(signedIdentityDocument.signingKeyVersion());
+ if (signer.hasValidSignature(signedIdentityDocument, publicKey)) {
log.log(LogLevel.INFO, () -> String.format("Instance %s is valid.", providerUniqueId));
return true;
}
@@ -63,22 +61,15 @@ public class InstanceValidator {
return false;
}
- boolean isInstanceSignatureValid(InstanceConfirmation instanceConfirmation) {
- SignedIdentityDocumentEntity signedIdentityDocument = instanceConfirmation.signedIdentityDocument;
-
- PublicKey publicKey = keyProvider.getPublicKey(signedIdentityDocument.signingKeyVersion);
- return isSignatureValid(publicKey, signedIdentityDocument.rawIdentityDocument, signedIdentityDocument.signature);
- }
-
- public static boolean isSignatureValid(PublicKey publicKey, String rawIdentityDocument, String signature) {
- try {
- Signature signatureVerifier = Signature.getInstance("SHA512withRSA");
- signatureVerifier.initVerify(publicKey);
- signatureVerifier.update(rawIdentityDocument.getBytes());
- return signatureVerifier.verify(Base64.getDecoder().decode(signature));
- } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
- throw new RuntimeException(e);
- }
+ // TODO Add actual validation. Cannot reuse isValidInstance as identity document is not part of the refresh request.
+ // We'll have to perform some validation on the instance id and other fields of the attribute map.
+ // Separate between tenant and node certificate as well.
+ public boolean isValidRefresh(InstanceConfirmation confirmation) {
+ log.log(LogLevel.INFO, () -> String.format("Accepting refresh for instance with identity '%s', provider '%s', instanceId '%s'.",
+ new AthenzService(confirmation.domain, confirmation.service).getFullName(),
+ confirmation.provider,
+ confirmation.attributes.get("sanDNS").toString()));
+ return true;
}
// If/when we dont care about logging exactly whats wrong, this can be simplified
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGeneratorTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGeneratorTest.java
index 078ef1b7e39..a1839ec62a2 100644
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGeneratorTest.java
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGeneratorTest.java
@@ -14,14 +14,12 @@ import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.Zone;
-import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper;
import com.yahoo.vespa.athenz.identityprovider.api.IdentityType;
import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument;
import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId;
-import com.yahoo.vespa.athenz.identityprovider.api.bindings.SignedIdentityDocumentEntity;
+import com.yahoo.vespa.athenz.identityprovider.client.IdentityDocumentSigner;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.AutoGeneratedKeyProvider;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.instanceconfirmation.InstanceValidator;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.node.Allocation;
@@ -48,7 +46,7 @@ public class IdentityDocumentGeneratorTest {
private static final Zone ZONE = new Zone(SystemName.cd, Environment.dev, RegionName.from("us-north-1"));
@Test
- public void generates_valid_identity_document() throws Exception {
+ public void generates_valid_identity_document() {
String parentHostname = "docker-host";
String containerHostname = "docker-container";
@@ -85,7 +83,7 @@ public class IdentityDocumentGeneratorTest {
SignedIdentityDocument signedIdentityDocument = identityDocumentGenerator.generateSignedIdentityDocument(containerHostname, IdentityType.TENANT);
// Verify attributes
- assertEquals(containerHostname, signedIdentityDocument.identityDocument().instanceHostname());
+ assertEquals(containerHostname, signedIdentityDocument.instanceHostname());
String environment = "dev";
String region = "us-north-1";
@@ -97,14 +95,11 @@ public class IdentityDocumentGeneratorTest {
assertEquals(expectedProviderUniqueId, signedIdentityDocument.providerUniqueId());
// Validate that container ips are present
- assertThat(signedIdentityDocument.identityDocument().ipAddresses(), Matchers.containsInAnyOrder("::1"));
+ assertThat(signedIdentityDocument.ipAddresses(), Matchers.containsInAnyOrder("::1"));
- SignedIdentityDocumentEntity signedIdentityDocumentEntity = EntityBindingsMapper.toSignedIdentityDocumentEntity(signedIdentityDocument);
+ IdentityDocumentSigner signer = new IdentityDocumentSigner();
// Validate signature
- assertTrue("Message", InstanceValidator.isSignatureValid(keyProvider.getPublicKey(0),
- signedIdentityDocumentEntity.rawIdentityDocument,
- signedIdentityDocument.signature()));
-
+ assertTrue(signer.hasValidSignature(signedIdentityDocument, keyProvider.getPublicKey(0)));
}
}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java
index 54411b424eb..04c4d4da51a 100644
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java
@@ -14,8 +14,6 @@ import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper;
import com.yahoo.vespa.athenz.identityprovider.api.bindings.IdentityDocumentEntity;
import com.yahoo.vespa.athenz.identityprovider.api.bindings.SignedIdentityDocumentEntity;
import com.yahoo.vespa.athenz.identityprovider.api.bindings.VespaUniqueInstanceIdEntity;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.AutoGeneratedKeyProvider;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.KeyProvider;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.Utils;
import org.junit.Test;
@@ -49,27 +47,6 @@ public class InstanceValidatorTest {
private final String domain = "domain";
private final String service = "service";
- @Test
- public void valid_signature() throws Exception {
- KeyProvider keyProvider = new AutoGeneratedKeyProvider();
- InstanceValidator instanceValidator = new InstanceValidator(keyProvider, null);
- InstanceConfirmation instanceConfirmation = createInstanceConfirmation(
- keyProvider.getPrivateKey(0), applicationId, domain, service);
-
- assertTrue(instanceValidator.isInstanceSignatureValid(instanceConfirmation));
- }
-
- @Test
- public void invalid_signature() throws Exception {
- KeyProvider keyProvider = new AutoGeneratedKeyProvider();
- InstanceValidator instanceValidator = new InstanceValidator(keyProvider, null);
-
- KeyProvider fakeKeyProvider = new AutoGeneratedKeyProvider();
- InstanceConfirmation instanceConfirmation = createInstanceConfirmation(
- fakeKeyProvider.getPrivateKey(0), applicationId, domain, service);
-
- assertFalse(instanceValidator.isInstanceSignatureValid(instanceConfirmation));
- }
@Test
public void application_does_not_exist() {
diff --git a/bootstrap.sh b/bootstrap.sh
index 491f707d9f8..163834a78b1 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -26,7 +26,7 @@ else
fi
mvn_install() {
- mvn --quiet --batch-mode --threads 1.5C --no-snapshot-updates clean install -Dmaven.test.skip=true -Dmaven.javadoc.skip=true "$@"
+ mvn --quiet --batch-mode --no-snapshot-updates clean install -Dmaven.test.skip=true -Dmaven.javadoc.skip=true "$@"
}
# Generate vtag map
@@ -48,17 +48,17 @@ $top/dist/getversion.pl -M $top > $top/dist/vtag.map
echo "Downloading all dependencies. This may take a few minutes with an empty Maven cache."
(
cd container-dependency-versions
- mvn_install
+ mvn_install --threads 1.5C
)
(
cd parent
- mvn_install
+ mvn_install --threads 1.5C
)
-mvn_install -N
+mvn_install --threads 1.5C -N
# and build plugins first:
echo "Building Vespa Maven plugins."
-mvn_install -f maven-plugins/pom.xml
+mvn_install --threads 1 -f maven-plugins/pom.xml
# now everything else should just work with normal maven dependency resolution:
diff --git a/bundle-plugin-test/pom.xml b/bundle-plugin-test/pom.xml
index 53be71352c8..4b36435529e 100644
--- a/bundle-plugin-test/pom.xml
+++ b/bundle-plugin-test/pom.xml
@@ -44,36 +44,15 @@
<version>${project.version}</version>
</dependency>
<dependency>
- <groupId>org.scala-lang</groupId>
- <artifactId>scala-library</artifactId>
- <scope>provided</scope>
- </dependency>
-
- <dependency>
<!-- Added to verify that module-info.class can be handled by bundle-plugin without throwing an exception. -->
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
-
</dependencies>
<build>
<plugins>
<plugin>
- <groupId>net.alchim31.maven</groupId>
- <artifactId>scala-maven-plugin</artifactId>
- <executions>
- <execution>
- <goals>
- <goal>add-source</goal>
- <goal>compile</goal>
- <goal>testCompile</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
-
- <plugin>
<groupId>com.yahoo.vespa</groupId>
<artifactId>bundle-plugin</artifactId>
<version>${project.version}</version>
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 f088007ebb7..ccee4844b49 100644
--- a/bundle-plugin-test/src/test/java/com/yahoo/BundleIT.java
+++ b/bundle-plugin-test/src/test/java/com/yahoo/BundleIT.java
@@ -30,7 +30,7 @@ import static org.junit.Assert.assertThat;
/**
* Verifies the bundle jar file built and its manifest.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class BundleIT {
private JarFile jarFile;
@@ -64,8 +64,8 @@ public class BundleIT {
}
@Test
- @Ignore
- public void require_that_manifest_version_matches_pom_version() {
+ @Ignore // TODO Vespa 7: Should we fix this? Why not?
+ public void require_that_bundle_version_matches_pom_version() {
assertThat(mainAttributes.getValue("Bundle-Version"), is("5.1.0"));
}
diff --git a/bundle-plugin/pom.xml b/bundle-plugin/pom.xml
index b68ffa811b2..a5cb3c9fbcc 100644
--- a/bundle-plugin/pom.xml
+++ b/bundle-plugin/pom.xml
@@ -39,16 +39,8 @@
<scope>test</scope>
</dependency>
<dependency>
- <groupId>org.scala-lang</groupId>
- <artifactId>scala-library</artifactId>
- </dependency>
- <dependency>
- <groupId>org.scala-lang.modules</groupId>
- <artifactId>scala-parser-combinators_${scala.major-version}</artifactId>
- </dependency>
- <dependency>
- <groupId>org.scalatest</groupId>
- <artifactId>scalatest_${scala.major-version}</artifactId>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-library</artifactId>
<scope>test</scope>
</dependency>
<dependency>
@@ -95,19 +87,6 @@
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
- <groupId>net.alchim31.maven</groupId>
- <artifactId>scala-maven-plugin</artifactId>
- <executions>
- <execution>
- <goals>
- <goal>add-source</goal>
- <goal>compile</goal>
- <goal>testCompile</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
</plugin>
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
new file mode 100644
index 00000000000..798fea2644e
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/bundle/AnalyzeBundle.java
@@ -0,0 +1,94 @@
+// 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.bundle;
+
+import com.yahoo.container.plugin.osgi.ExportPackageParser;
+import com.yahoo.container.plugin.osgi.ExportPackages.Export;
+import com.yahoo.container.plugin.util.JarFiles;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.jar.Manifest;
+import java.util.stream.Collectors;
+
+/**
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class AnalyzeBundle {
+ public static class PublicPackages {
+ public final List<Export> exports;
+ public final List<String> globals;
+
+ public PublicPackages(List<Export> exports, List<String> globals) {
+ this.exports = exports;
+ this.globals = globals;
+ }
+ }
+
+ public static PublicPackages publicPackagesAggregated(Collection<File> jarFiles) {
+ List<Export> exports = new ArrayList<>();
+ List<String> globals = new ArrayList<>();
+
+ for (File jarFile : jarFiles) {
+ PublicPackages pp = publicPackages(jarFile);
+ exports.addAll(pp.exports);
+ globals.addAll(pp.globals);
+ }
+ return new PublicPackages(exports, globals);
+ }
+
+ public static PublicPackages publicPackages(File jarFile) {
+ try {
+ Optional<Manifest> jarManifest = JarFiles.getManifest(jarFile);
+ if (jarManifest.isPresent()) {
+ Manifest manifest = jarManifest.get();
+ if (isOsgiManifest(manifest)) {
+ return new PublicPackages(parseExports(manifest), parseGlobals(manifest));
+ }
+ }
+ return new PublicPackages(Collections.emptyList(), Collections.emptyList());
+ } catch (Exception e) {
+ throw new RuntimeException(String.format("Invalid manifest in bundle '%s'", jarFile.getPath()), e);
+ }
+ }
+
+ public static Optional<String> bundleSymbolicName(File jarFile) {
+ return JarFiles.getManifest(jarFile).flatMap(AnalyzeBundle::getBundleSymbolicName);
+ }
+
+ private static List<Export> parseExportsFromAttribute(Manifest manifest, String attributeName) {
+ return getMainAttributeValue(manifest, attributeName).map(ExportPackageParser::parseExports).orElseGet(() -> new ArrayList<>());
+ }
+
+ private static List<Export> parseExports(Manifest jarManifest) {
+ return parseExportsFromAttribute(jarManifest, "Export-Package");
+ }
+
+ private static List<String> parseGlobals(Manifest manifest) {
+ List<Export> globals = parseExportsFromAttribute(manifest, "Global-Package");
+
+ for (Export export : globals) {
+ if (export.getParameters().isEmpty() == false) {
+ throw new RuntimeException("Parameters not valid for Global-Package.");
+ }
+ }
+
+ return globals.stream().flatMap(g -> g.getPackageNames().stream()).collect(Collectors.toList());
+ }
+
+ private static Optional<String> getMainAttributeValue(Manifest manifest, String attributeName) {
+ return Optional.ofNullable(manifest.getMainAttributes().getValue(attributeName));
+ }
+
+ private static boolean isOsgiManifest(Manifest mf) {
+ return getBundleSymbolicName(mf).isPresent();
+ }
+
+ private static Optional<String> getBundleSymbolicName(Manifest mf) {
+ return getMainAttributeValue(mf, "Bundle-SymbolicName");
+ }
+}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/bundle/TransformExportPackages.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/bundle/TransformExportPackages.java
new file mode 100644
index 00000000000..8686fef0a55
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/bundle/TransformExportPackages.java
@@ -0,0 +1,62 @@
+// 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.bundle;
+
+import com.yahoo.container.plugin.osgi.ExportPackages.Export;
+import com.yahoo.container.plugin.osgi.ExportPackages.Parameter;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class TransformExportPackages {
+ public static List<Export> replaceVersions(List<Export> exports, String newVersion) {
+ List<Export> ret = new ArrayList<>();
+
+ for (Export export : exports) {
+ List<Parameter> newParams = new ArrayList<>();
+ for (Parameter param : export.getParameters()) {
+ if ("version".equals(param.getName())) {
+ newParams.add(new Parameter("version", newVersion));
+ } else {
+ newParams.add(param);
+ }
+ }
+ ret.add(new Export(export.getPackageNames(), newParams));
+ }
+ return ret;
+ }
+
+ public static List<Export> removeUses(List<Export> exports) {
+ List<Export> ret = new ArrayList<>();
+
+ for (Export export : exports) {
+ List<Parameter> newParams = new ArrayList<>();
+ for (Parameter param : export.getParameters()) {
+ if ("uses".equals(param.getName()) == false) {
+ newParams.add(param);
+ }
+ }
+ ret.add(new Export(export.getPackageNames(), newParams));
+ }
+ return ret;
+ }
+
+ public static String toExportPackageProperty(List<Export> exports) {
+ return exports.stream().map(exp -> {
+ String oneExport = String.join(";", exp.getPackageNames());
+ if (exp.getParameters().size() > 0) {
+ String paramString = exp.getParameters().stream().map(param -> param.getName() + "=" + quote(param.getValue())).collect(Collectors.joining(";"));
+ oneExport += ";" + paramString;
+ }
+ return oneExport;
+ }).collect(Collectors.joining(","));
+ }
+
+ public static String quote(String s) {
+ return "\"" + s + "\"";
+ }
+}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/Analyze.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/Analyze.java
new file mode 100644
index 00000000000..c59f8559405
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/Analyze.java
@@ -0,0 +1,87 @@
+// 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 org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Optional;
+
+import static com.yahoo.container.plugin.util.IO.withFileInputStream;
+
+/**
+ * Main entry point for class analysis
+ *
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class Analyze {
+ public static ClassFileMetaData analyzeClass(File classFile) {
+ try {
+ return withFileInputStream(classFile, Analyze::analyzeClass);
+ } catch (RuntimeException e) {
+ throw new RuntimeException("An error occurred when analyzing " + classFile.getPath(), e);
+ }
+ }
+
+ public static ClassFileMetaData analyzeClass(InputStream inputStream) {
+ try {
+ AnalyzeClassVisitor visitor = new AnalyzeClassVisitor();
+ new ClassReader(inputStream).accept(visitor, ClassReader.SKIP_DEBUG);
+ return visitor.result();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ static Optional<String> internalNameToClassName(String internalClassName) {
+ if(internalClassName == null) {
+ return Optional.empty();
+ } else {
+ return getClassName(Type.getObjectType(internalClassName));
+ }
+ }
+
+ static Optional<String> getClassName(Type aType) {
+ switch (aType.getSort()) {
+ case Type.ARRAY:
+ return getClassName(aType.getElementType());
+ case Type.OBJECT:
+ return Optional.of(aType.getClassName());
+ default:
+ return Optional.empty();
+ }
+ }
+
+ static AnnotationVisitor visitAnnotationDefault(ImportCollector collector) {
+ return new AnnotationVisitor(Opcodes.ASM6) {
+ @Override
+ public void visit(String name, Object value) {
+ }
+
+ @Override
+ public void visitEnum(String name, String desc, String value) {
+ collector.addImportWithTypeDesc(desc);
+ }
+
+ @Override
+ public AnnotationVisitor visitArray(String name) {
+ return this;
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String name, String desc) {
+ collector.addImportWithTypeDesc(desc);
+ return this;
+ }
+
+ @Override
+ public void visitEnd() {
+ }
+ };
+ }
+}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/AnalyzeClassVisitor.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/AnalyzeClassVisitor.java
new file mode 100644
index 00000000000..d9519fd7986
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/AnalyzeClassVisitor.java
@@ -0,0 +1,162 @@
+// 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.yahoo.osgi.annotation.ExportPackage;
+import com.yahoo.osgi.annotation.Version;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * Picks up classes used in class files.
+ *
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+class AnalyzeClassVisitor extends ClassVisitor implements ImportCollector {
+ private String name = null;
+ private Set<String> imports = new HashSet<>();
+ private Optional<ExportPackageAnnotation> exportPackageAnnotation = Optional.empty();
+
+ AnalyzeClassVisitor() {
+ super(Opcodes.ASM6);
+ }
+
+ @Override
+ public Set<String> imports() {
+ return imports;
+ }
+
+ @Override
+ public void visitAttribute(Attribute attribute) {
+ addImport(Type.getObjectType(attribute.type));
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
+ Analyze.getClassName(Type.getReturnType(desc)).ifPresent(imports::add);
+ Arrays.asList(Type.getArgumentTypes(desc)).forEach(argType -> Analyze.getClassName(argType).ifPresent(imports::add));
+ if (exceptions != null) {
+ Arrays.asList(exceptions).forEach(ex -> Analyze.internalNameToClassName(ex).ifPresent(imports::add));
+ }
+
+ AnalyzeSignatureVisitor.analyzeMethod(signature, this);
+ return new AnalyzeMethodVisitor(this);
+ }
+
+ @Override
+ public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
+ Analyze.getClassName(Type.getType(desc)).ifPresent(imports::add);
+
+ AnalyzeSignatureVisitor.analyzeField(signature, this);
+ return new AnalyzeFieldVisitor(this);
+ }
+
+ @Override
+ public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
+ this.name = Analyze.internalNameToClassName(name)
+ .orElseThrow(() -> new RuntimeException("Unable to resolve class name for " + name));
+
+ addImportWithInternalName(superName);
+ Arrays.asList(interfaces).forEach(this::addImportWithInternalName);
+
+ AnalyzeSignatureVisitor.analyzeClass(signature, this);
+ }
+
+ @Override
+ public void visitInnerClass(String name, String outerName, String innerName, int access) {
+ }
+
+ @Override
+ public void visitOuterClass(String owner, String name, String desc) {
+ }
+
+ @Override
+ public void visitSource(String source, String debug) {
+ }
+
+ @Override
+ public void visitEnd() {
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T> T defaultVersionValue(String name) {
+ try {
+ return (T) Version.class.getMethod(name).getDefaultValue();
+ } catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException("Could not locate method " + name);
+ }
+ }
+
+ private AnnotationVisitor visitExportPackage() {
+ return new AnnotationVisitor(Opcodes.ASM6) {
+ private int major = defaultVersionValue("major");
+ private int minor = defaultVersionValue("minor");
+ private int micro = defaultVersionValue("micro");
+ private String qualifier = defaultVersionValue("qualifier");
+
+ @Override
+ public void visit(String name, Object value) {
+ if (name != null) {
+ switch (name) {
+ case "major":
+ major = (int) value;
+ break;
+ case "minor":
+ minor = (int) value;
+ break;
+ case "micro":
+ micro = (int) value;
+ break;
+ case "qualifier":
+ qualifier = (String) value;
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void visitEnd() {
+ exportPackageAnnotation = Optional.of(new ExportPackageAnnotation(major, minor, micro, qualifier));
+ }
+
+ @Override
+ public void visitEnum(String name, String desc, String value) {
+ }
+
+ @Override
+ public AnnotationVisitor visitArray(String name) {
+ return this;
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String name, String desc) {
+ return this;
+ }
+ };
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ if (ExportPackage.class.getName().equals(Type.getType(desc).getClassName())) {
+ return visitExportPackage();
+ } else {
+ addImportWithTypeDesc(desc);
+ return Analyze.visitAnnotationDefault(this);
+ }
+ }
+
+ ClassFileMetaData result() {
+ assert (!imports.contains("int"));
+ return new ClassFileMetaData(name, imports, exportPackageAnnotation);
+ }
+}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/AnalyzeFieldVisitor.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/AnalyzeFieldVisitor.java
new file mode 100644
index 00000000000..ea10b6ef0aa
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/AnalyzeFieldVisitor.java
@@ -0,0 +1,49 @@
+// 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 org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author ollivir
+ */
+public class AnalyzeFieldVisitor extends FieldVisitor implements ImportCollector {
+ private final AnalyzeClassVisitor analyzeClassVisitor;
+ private final Set<String> imports = new HashSet<>();
+
+ public AnalyzeFieldVisitor(AnalyzeClassVisitor analyzeClassVisitor) {
+ super(Opcodes.ASM6);
+ this.analyzeClassVisitor = analyzeClassVisitor;
+ }
+
+ @Override
+ public Set<String> imports() {
+ return imports;
+ }
+
+ @Override
+ public void visitAttribute(Attribute attribute) {
+ addImport(Type.getObjectType(attribute.type));
+ }
+
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ addImportWithTypeDesc(desc);
+
+ return Analyze.visitAnnotationDefault(this);
+ }
+
+ public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
+ return visitAnnotation(desc, visible);
+ }
+
+ @Override
+ public void visitEnd() {
+ analyzeClassVisitor.addImports(imports);
+ }
+}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/AnalyzeMethodVisitor.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/AnalyzeMethodVisitor.java
new file mode 100644
index 00000000000..b1a92f9c10b
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/AnalyzeMethodVisitor.java
@@ -0,0 +1,168 @@
+// 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 org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Picks up classes used in method bodies.
+ *
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+class AnalyzeMethodVisitor extends MethodVisitor implements ImportCollector {
+ private final Set<String> imports = new HashSet<>();
+ private final AnalyzeClassVisitor analyzeClassVisitor;
+
+ AnalyzeMethodVisitor(AnalyzeClassVisitor analyzeClassVisitor) {
+ super(Opcodes.ASM6);
+ this.analyzeClassVisitor = analyzeClassVisitor;
+ }
+
+ public Set<String> imports() {
+ return imports;
+ }
+
+ @Override
+ public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
+ return visitAnnotation(desc, visible);
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotationDefault() {
+ return Analyze.visitAnnotationDefault(this);
+ }
+
+ @Override
+ public void visitAttribute(Attribute attribute) {
+ addImport(Type.getObjectType(attribute.type));
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ addImportWithTypeDesc(desc);
+
+ return Analyze.visitAnnotationDefault(this);
+ }
+
+ @Override
+ public void visitEnd() {
+ super.visitEnd();
+ analyzeClassVisitor.addImports(imports);
+ }
+
+ @Override
+ public void visitMultiANewArrayInsn(String desc, int dims) {
+ addImportWithTypeDesc(desc);
+ }
+
+ @Override
+ public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
+ addImportWithInternalName(owner);
+ Arrays.asList(Type.getArgumentTypes(desc)).forEach(this::addImport);
+ addImport(Type.getReturnType(desc));
+ }
+
+ @Override
+ public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+ addImportWithInternalName(owner);
+ addImportWithTypeDesc(desc);
+ }
+
+ @Override
+ public void visitTypeInsn(int opcode, String type) {
+ addImportWithInternalName(type);
+ }
+
+ @Override
+ public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+ if (type != null) { //null means finally block
+ addImportWithInternalName(type);
+ }
+ }
+
+ @Override
+ public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
+ addImportWithTypeDesc(desc);
+ }
+
+ @Override
+ public void visitLdcInsn(Object constant) {
+ if (constant instanceof Type) {
+ addImport((Type) constant);
+ }
+ }
+
+ @Override
+ public void visitInvokeDynamicInsn(String name, String desc, Handle bootstrapMethod, Object... bootstrapMethodArgs) {
+ for (Object arg : bootstrapMethodArgs) {
+ if (arg instanceof Type) {
+ addImport((Type) arg);
+ } else if (arg instanceof Handle) {
+ addImportWithInternalName(((Handle) arg).getOwner());
+ Arrays.asList(Type.getArgumentTypes(desc)).forEach(this::addImport);
+ } else if ((arg instanceof Number) == false && (arg instanceof String) == false) {
+ throw new AssertionError("Unexpected type " + arg.getClass() + " with value '" + arg + "'");
+ }
+ }
+ }
+
+ @Override
+ public void visitMaxs(int maxStack, int maxLocals) {
+ }
+
+ @Override
+ public void visitLineNumber(int line, Label start) {
+ }
+
+ //only for debugging
+ @Override
+ public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+ }
+
+ @Override
+ public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
+ super.visitTableSwitchInsn(min, max, dflt, labels);
+ }
+
+ @Override
+ public void visitIincInsn(int variable, int increment) {
+ }
+
+ @Override
+ public void visitLabel(Label label) {
+ }
+
+ @Override
+ public void visitJumpInsn(int opcode, Label label) {
+ }
+
+ @Override
+ public void visitVarInsn(int opcode, int variable) {
+ }
+
+ @Override
+ public void visitIntInsn(int opcode, int operand) {
+ }
+
+ @Override
+ public void visitInsn(int opcode) {
+ }
+
+ @Override
+ public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
+ }
+
+ @Override
+ public void visitCode() {
+ }
+}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/AnalyzeSignatureVisitor.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/AnalyzeSignatureVisitor.java
new file mode 100644
index 00000000000..0f5fcf89f6a
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/AnalyzeSignatureVisitor.java
@@ -0,0 +1,119 @@
+// 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 org.objectweb.asm.Opcodes;
+import org.objectweb.asm.signature.SignatureReader;
+import org.objectweb.asm.signature.SignatureVisitor;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+
+class AnalyzeSignatureVisitor extends SignatureVisitor implements ImportCollector {
+ private final AnalyzeClassVisitor analyzeClassVisitor;
+ private Set<String> imports = new HashSet<>();
+
+ AnalyzeSignatureVisitor(AnalyzeClassVisitor analyzeClassVisitor) {
+ super(Opcodes.ASM6);
+ this.analyzeClassVisitor = analyzeClassVisitor;
+ }
+
+ public Set<String> imports() {
+ return imports;
+ }
+
+ @Override
+ public void visitEnd() {
+ super.visitEnd();
+ analyzeClassVisitor.addImports(imports);
+ }
+
+ @Override
+ public void visitClassType(String className) {
+ addImportWithInternalName(className);
+ }
+
+ @Override
+ public void visitFormalTypeParameter(String name) {
+ }
+
+ @Override
+ public SignatureVisitor visitClassBound() {
+ return this;
+ }
+
+ @Override
+ public SignatureVisitor visitInterfaceBound() {
+ return this;
+ }
+
+ @Override
+ public SignatureVisitor visitSuperclass() {
+ return this;
+ }
+
+ @Override
+ public SignatureVisitor visitInterface() {
+ return this;
+ }
+
+ @Override
+ public SignatureVisitor visitParameterType() {
+ return this;
+ }
+
+ @Override
+ public SignatureVisitor visitReturnType() {
+ return this;
+ }
+
+ @Override
+ public SignatureVisitor visitExceptionType() {
+ return this;
+ }
+
+ @Override
+ public void visitBaseType(char descriptor) {
+ }
+
+ @Override
+ public void visitTypeVariable(String name) {
+ }
+
+ @Override
+ public SignatureVisitor visitArrayType() {
+ return this;
+ }
+
+ @Override
+ public void visitInnerClassType(String name) {
+ }
+
+ @Override
+ public void visitTypeArgument() {
+ }
+
+ @Override
+ public SignatureVisitor visitTypeArgument(char wildcard) {
+ return this;
+ }
+
+ static void analyzeClass(String signature, AnalyzeClassVisitor analyzeClassVisitor) {
+ if (signature != null) {
+ new SignatureReader(signature).accept(new AnalyzeSignatureVisitor(analyzeClassVisitor));
+ }
+ }
+
+ static void analyzeMethod(String signature, AnalyzeClassVisitor analyzeClassVisitor) {
+ analyzeClass(signature, analyzeClassVisitor);
+ }
+
+ static void analyzeField(String signature, AnalyzeClassVisitor analyzeClassVisitor) {
+ if (signature != null)
+ new SignatureReader(signature).acceptType(new AnalyzeSignatureVisitor(analyzeClassVisitor));
+ }
+}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/ClassFileMetaData.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/ClassFileMetaData.java
new file mode 100644
index 00000000000..198618cabc4
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/ClassFileMetaData.java
@@ -0,0 +1,35 @@
+// 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 java.util.Optional;
+import java.util.Set;
+
+/**
+ * The result of analyzing a .class file.
+ *
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class ClassFileMetaData {
+ private final String name;
+ private final Set<String> referencedClasses;
+ private final Optional<ExportPackageAnnotation> exportPackage;
+
+ public ClassFileMetaData(String name, Set<String> referencedClasses, Optional<ExportPackageAnnotation> exportPackage) {
+ this.name = name;
+ this.referencedClasses = referencedClasses;
+ this.exportPackage = exportPackage;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Set<String> getReferencedClasses() {
+ return referencedClasses;
+ }
+
+ public Optional<ExportPackageAnnotation> getExportPackage() {
+ return exportPackage;
+ }
+}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/ExportPackageAnnotation.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/ExportPackageAnnotation.java
new file mode 100644
index 00000000000..d2da9b5a226
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/ExportPackageAnnotation.java
@@ -0,0 +1,62 @@
+// 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 java.util.Objects;
+import java.util.regex.Pattern;
+
+/**
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class ExportPackageAnnotation {
+ private final int major;
+ private final int minor;
+ private final int micro;
+ private final String qualifier;
+
+ private static final Pattern QUALIFIER_PATTERN = Pattern.compile("[\\p{Alpha}\\p{Digit}_-]*");
+
+ public ExportPackageAnnotation(int major, int minor, int micro, String qualifier) {
+ this.major = major;
+ this.minor = minor;
+ this.micro = micro;
+ this.qualifier = qualifier;
+
+ requireNonNegative(major, "major");
+ requireNonNegative(minor, "minor");
+ requireNonNegative(micro, "micro");
+ if (QUALIFIER_PATTERN.matcher(qualifier).matches() == false) {
+ throw new IllegalArgumentException(
+ exportPackageError(String.format("qualifier must follow the format (alpha|digit|'_'|'-')* but was '%s'.", qualifier)));
+ }
+ }
+
+ public String osgiVersion() {
+ return String.format("%d.%d.%d", major, minor, micro) + (qualifier.isEmpty() ? "" : "." + qualifier);
+ }
+
+ private static String exportPackageError(String msg) {
+ return "ExportPackage anntotation: " + msg;
+ }
+
+ private static void requireNonNegative(int i, String fieldName) {
+ if (i < 0) {
+ throw new IllegalArgumentException(exportPackageError(String.format("%s must be non-negative but was %d.", fieldName, i)));
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ ExportPackageAnnotation that = (ExportPackageAnnotation) o;
+ return major == that.major && minor == that.minor && micro == that.micro && Objects.equals(qualifier, that.qualifier);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(major, minor, micro, qualifier);
+ }
+}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/ImportCollector.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/ImportCollector.java
new file mode 100644
index 00000000000..3946fe297f9
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/ImportCollector.java
@@ -0,0 +1,35 @@
+// 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 org.objectweb.asm.Type;
+
+import java.util.Collection;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * @author ollivir
+ */
+public interface ImportCollector {
+ Set<String> imports();
+
+ default void addImportWithTypeDesc(String typeDescriptor) {
+ addImport(Type.getType(typeDescriptor));
+ }
+
+ default void addImport(Type type) {
+ addImport(Analyze.getClassName(type));
+ }
+
+ default void addImportWithInternalName(String name) {
+ addImport(Analyze.internalNameToClassName(name));
+ }
+
+ default void addImports(Collection<String> imports) {
+ imports().addAll(imports);
+ }
+
+ default void addImport(Optional<String> anImport) {
+ anImport.ifPresent(pkg -> imports().add(pkg));
+ }
+}
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
new file mode 100644
index 00000000000..13bbc63192c
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/PackageTally.java
@@ -0,0 +1,79 @@
+// 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.collect.Sets;
+import com.yahoo.container.plugin.util.Maps;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+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) {
+ this.definedPackagesMap = definedPackagesMap;
+ this.referencedPackagesUnfiltered = referencedPackagesUnfiltered;
+ }
+
+ public Set<String> definedPackages() {
+ return definedPackagesMap.keySet();
+ }
+
+ public Set<String> referencedPackages() {
+ return Sets.difference(referencedPackagesUnfiltered, definedPackages());
+ }
+
+ public Map<String, ExportPackageAnnotation> exportedPackages() {
+ Map<String, ExportPackageAnnotation> ret = new HashMap<>();
+ definedPackagesMap.forEach((k, v) -> {
+ v.ifPresent(annotation -> ret.put(k, annotation));
+ });
+ return ret;
+ }
+
+ /**
+ * Represents the classes for two package tallies that are deployed as a single unit.
+ * <p>
+ * ExportPackageAnnotations from this has precedence over the other.
+ */
+ public PackageTally combine(PackageTally other) {
+ Map<String, Optional<ExportPackageAnnotation>> map = Maps.combine(this.definedPackagesMap, other.definedPackagesMap,
+ (l, r) -> l.isPresent() ? l : r);
+ Set<String> referencedPkgs = new HashSet<>(this.referencedPackagesUnfiltered);
+ referencedPkgs.addAll(other.referencedPackagesUnfiltered);
+
+ return new PackageTally(map, referencedPkgs);
+ }
+
+ public static PackageTally combine(Collection<PackageTally> packageTallies) {
+ Map<String, Optional<ExportPackageAnnotation>> map = new HashMap<>();
+ Set<String> referencedPkgs = new HashSet<>();
+
+ for (PackageTally pt : packageTallies) {
+ pt.definedPackagesMap.forEach((k, v) -> map.merge(k, v, (l, r) -> l.isPresent() ? l : r));
+ referencedPkgs.addAll(pt.referencedPackagesUnfiltered);
+ }
+ return new PackageTally(map, referencedPkgs);
+ }
+
+ public static PackageTally fromAnalyzedClassFiles(Collection<ClassFileMetaData> analyzedClassFiles) {
+ Map<String, Optional<ExportPackageAnnotation>> map = new HashMap<>();
+ Set<String> referencedPkgs = new HashSet<>();
+
+ for (ClassFileMetaData metaData : analyzedClassFiles) {
+ String packageName = Packages.packageName(metaData.getName());
+ map.merge(packageName, metaData.getExportPackage(), (l, r) -> l.isPresent() ? l : r);
+ metaData.getReferencedClasses().forEach(className -> referencedPkgs.add(Packages.packageName(className)));
+ }
+ return new PackageTally(map, referencedPkgs);
+ }
+}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/Packages.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/Packages.java
new file mode 100644
index 00000000000..f9c6503c475
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/Packages.java
@@ -0,0 +1,43 @@
+// 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 java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Utility methods related to packages.
+ *
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class Packages {
+ public static class PackageMetaData {
+ public final Set<String> definedPackages;
+ public final Set<String> referencedExternalPackages;
+
+ public PackageMetaData(Set<String> definedPackages, Set<String> referencedExternalPackages) {
+ this.definedPackages = definedPackages;
+ this.referencedExternalPackages = referencedExternalPackages;
+ }
+ }
+
+ public static String packageName(String fullClassName) {
+ int index = fullClassName.lastIndexOf('.');
+ if (index == -1) {
+ return "";
+ } else {
+ return fullClassName.substring(0, index);
+ }
+ }
+
+ public static PackageMetaData analyzePackages(Set<ClassFileMetaData> allClasses) {
+ Set<String> definedPackages = new HashSet<>();
+ Set<String> referencedPackages = new HashSet<>();
+ for (ClassFileMetaData metaData : allClasses) {
+ definedPackages.add(packageName(metaData.getName()));
+ metaData.getReferencedClasses().forEach(className -> referencedPackages.add(packageName(className)));
+ }
+ referencedPackages.removeAll(definedPackages);
+ return new PackageMetaData(definedPackages, referencedPackages);
+ }
+}
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
new file mode 100644
index 00000000000..fff88d413d0
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/Artifacts.java
@@ -0,0 +1,69 @@
+// 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.mojo;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.project.MavenProject;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class Artifacts {
+ public static class ArtifactSet {
+ private final List<Artifact> jarArtifactsToInclude;
+ private final List<Artifact> jarArtifactsProvided;
+ private final List<Artifact> nonJarArtifacts;
+
+ private ArtifactSet(List<Artifact> jarArtifactsToInclude, List<Artifact> jarArtifactsProvided, List<Artifact> nonJarArtifacts) {
+ this.jarArtifactsToInclude = jarArtifactsToInclude;
+ this.jarArtifactsProvided = jarArtifactsProvided;
+ this.nonJarArtifacts = nonJarArtifacts;
+ }
+
+ public List<Artifact> getJarArtifactsToInclude() {
+ return jarArtifactsToInclude;
+ }
+
+ public List<Artifact> getJarArtifactsProvided() {
+ return jarArtifactsProvided;
+ }
+
+ public List<Artifact> getNonJarArtifacts() {
+ return nonJarArtifacts;
+ }
+ }
+
+ public static ArtifactSet getArtifacts(MavenProject project) {
+
+ List<Artifact> jarArtifactsToInclude = new ArrayList<>();
+ List<Artifact> jarArtifactsProvided = new ArrayList<>();
+ List<Artifact> nonJarArtifactsToInclude = new ArrayList<>();
+ List<Artifact> nonJarArtifactsProvided = new ArrayList<>();
+
+ for (Artifact artifact : project.getArtifacts()) {
+ if ("jar".equals(artifact.getType())) {
+ if (Artifact.SCOPE_COMPILE.equals(artifact.getScope())) {
+ jarArtifactsToInclude.add(artifact);
+ } else if (Artifact.SCOPE_PROVIDED.equals(artifact.getScope())) {
+ jarArtifactsProvided.add(artifact);
+ }
+ } else {
+ if (Artifact.SCOPE_COMPILE.equals(artifact.getScope())) {
+ nonJarArtifactsToInclude.add(artifact);
+ } else if (Artifact.SCOPE_PROVIDED.equals(artifact.getScope())) {
+ nonJarArtifactsProvided.add(artifact);
+ }
+ }
+ }
+ nonJarArtifactsToInclude.addAll(nonJarArtifactsProvided);
+ return new ArtifactSet(jarArtifactsToInclude, jarArtifactsProvided, nonJarArtifactsToInclude);
+ }
+
+ public static Collection<Artifact> getArtifactsToInclude(MavenProject project) {
+ return getArtifacts(project).getJarArtifactsToInclude();
+ }
+}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/AssembleContainerPluginMojo.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/AssembleContainerPluginMojo.java
new file mode 100644
index 00000000000..30ffc54225f
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/AssembleContainerPluginMojo.java
@@ -0,0 +1,163 @@
+// 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.mojo;
+
+import com.yahoo.container.plugin.util.Files;
+import com.yahoo.container.plugin.util.JarFiles;
+import org.apache.maven.archiver.MavenArchiveConfiguration;
+import org.apache.maven.archiver.MavenArchiver;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.model.Build;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.plugins.annotations.ResolutionScope;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.MavenProjectHelper;
+import org.codehaus.plexus.archiver.jar.JarArchiver;
+
+import java.io.File;
+import java.nio.channels.Channels;
+import java.util.EnumMap;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+/**
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+@Mojo(name = "assemble-container-plugin", requiresDependencyResolution = ResolutionScope.COMPILE, threadSafe = true)
+public class AssembleContainerPluginMojo extends AbstractMojo {
+ private static enum Dependencies {
+ WITH, WITHOUT
+ }
+
+ @Parameter(defaultValue = "${project}")
+ private MavenProject project = null;
+
+ @Component
+ private MavenProjectHelper projectHelper;
+
+ @Parameter(defaultValue = "${session}", readonly = true, required = true)
+ private MavenSession session = null;
+
+ @Parameter
+ private MavenArchiveConfiguration archiveConfiguration = new MavenArchiveConfiguration();
+
+ @Parameter(alias = "UseCommonAssemblyIds", defaultValue = "false")
+ private boolean useCommonAssemblyIds = false;
+
+ @Parameter(alias = "AttachBundle", defaultValue = "false")
+ private boolean attachBundleToArtifact;
+
+ @Parameter(alias = "BundleClassifier", defaultValue = "bundle")
+ private String bundleClassifierName;
+
+ @Override
+ public void execute() throws MojoExecutionException {
+ Map<Dependencies, String> jarSuffixes = new EnumMap<Dependencies, String>(Dependencies.class);
+
+ if (useCommonAssemblyIds) {
+ jarSuffixes.put(Dependencies.WITHOUT, ".jar");
+ jarSuffixes.put(Dependencies.WITH, "-jar-with-dependencies.jar");
+ } else {
+ jarSuffixes.put(Dependencies.WITHOUT, "-without-dependencies.jar");
+ jarSuffixes.put(Dependencies.WITH, "-deploy.jar");
+ }
+
+ Map<Dependencies, File> jarFiles = new EnumMap<Dependencies, File>(Dependencies.class);
+ jarSuffixes.forEach((dep, suffix) -> {
+ jarFiles.put(dep, jarFileInBuildDirectory(build().getFinalName(), suffix));
+ });
+
+ // force recreating the archive
+ archiveConfiguration.setForced(true);
+ archiveConfiguration.setManifestFile(new File(new File(build().getOutputDirectory()), JarFile.MANIFEST_NAME));
+
+ JarArchiver jarWithoutDependencies = new JarArchiver();
+ addClassesDirectory(jarWithoutDependencies);
+ createArchive(jarFiles.get(Dependencies.WITHOUT), jarWithoutDependencies);
+ project.getArtifact().setFile(jarFiles.get(Dependencies.WITHOUT));
+
+ JarArchiver jarWithDependencies = new JarArchiver();
+ addClassesDirectory(jarWithDependencies);
+ addDependencies(jarWithDependencies);
+ createArchive(jarFiles.get(Dependencies.WITH), jarWithDependencies);
+
+ if (attachBundleToArtifact) {
+ projectHelper.attachArtifact(project,
+ project.getArtifact().getType(),
+ bundleClassifierName,
+ jarFiles.get(Dependencies.WITH));
+ }
+ }
+
+ private File jarFileInBuildDirectory(String name, String suffix) {
+ return new File(build().getDirectory(), name + suffix);
+ }
+
+ private void addClassesDirectory(JarArchiver jarArchiver) {
+ File classesDirectory = new File(build().getOutputDirectory());
+ if (classesDirectory.isDirectory()) {
+ jarArchiver.addDirectory(classesDirectory);
+ }
+ }
+
+ private void createArchive(File jarFile, JarArchiver jarArchiver) throws MojoExecutionException {
+ MavenArchiver mavenArchiver = new MavenArchiver();
+ mavenArchiver.setArchiver(jarArchiver);
+ mavenArchiver.setOutputFile(jarFile);
+ try {
+ mavenArchiver.createArchive(session, project, archiveConfiguration);
+ } catch (Exception e) {
+ throw new MojoExecutionException("Error creating archive " + jarFile.getName(), e);
+ }
+ }
+
+ private void addDependencies(JarArchiver jarArchiver) {
+ Artifacts.getArtifactsToInclude(project).forEach(artifact -> {
+ if ("jar".equals(artifact.getType())) {
+ jarArchiver.addFile(artifact.getFile(), "dependencies/" + artifact.getFile().getName());
+ copyConfigDefinitions(artifact.getFile(), jarArchiver);
+ } else {
+ getLog().warn("Unkown artifact type " + artifact.getType());
+ }
+ });
+ }
+
+ private void copyConfigDefinitions(File file, JarArchiver jarArchiver) {
+ JarFiles.withJarFile(file, jarFile -> {
+ for (Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements();) {
+ JarEntry entry = entries.nextElement();
+ String name = entry.getName();
+ if (name.startsWith("configdefinitions/") && name.endsWith(".def")) {
+ copyConfigDefinition(jarFile, entry, jarArchiver);
+ }
+ }
+ return null;
+ });
+ }
+
+ private void copyConfigDefinition(JarFile jarFile, ZipEntry entry, JarArchiver jarArchiver) {
+ JarFiles.withInputStream(jarFile, entry, input -> {
+ String defPath = entry.getName().replace("/", File.separator);
+ File destinationFile = new File(build().getOutputDirectory(), defPath);
+ destinationFile.getParentFile().mkdirs();
+
+ Files.withFileOutputStream(destinationFile, output -> {
+ output.getChannel().transferFrom(Channels.newChannel(input), 0, Long.MAX_VALUE);
+ return null;
+ });
+ jarArchiver.addFile(destinationFile, entry.getName());
+ return null;
+ });
+ }
+
+ private Build build() {
+ return project.getBuild();
+ }
+}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/GenerateBundleClassPathMappingsMojo.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/GenerateBundleClassPathMappingsMojo.java
new file mode 100644
index 00000000000..b2abf13695f
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/GenerateBundleClassPathMappingsMojo.java
@@ -0,0 +1,115 @@
+// 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.mojo;
+
+import com.google.common.base.Preconditions;
+import com.yahoo.container.plugin.bundle.AnalyzeBundle;
+import com.yahoo.container.plugin.osgi.ProjectBundleClassPaths;
+import com.yahoo.container.plugin.osgi.ProjectBundleClassPaths.BundleClasspathMapping;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.plugins.annotations.ResolutionScope;
+import org.apache.maven.project.MavenProject;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Generates mapping from Bundle-SymbolicName to classpath elements, e.g myBundle -&gt; [.m2/repository/com/mylib/Mylib.jar,
+ * myBundleProject/target/classes] The mapping in stored in a json file.
+ *
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+@Mojo(name = "generate-bundle-classpath-mappings", requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, threadSafe = true)
+public class GenerateBundleClassPathMappingsMojo extends AbstractMojo {
+ @Parameter(defaultValue = "${project}")
+ private MavenProject project = null;
+
+ //TODO: Combine with com.yahoo.container.plugin.mojo.GenerateOsgiManifestMojo.bundleSymbolicName
+ @Parameter(alias = "Bundle-SymbolicName", defaultValue = "${project.artifactId}")
+ private String bundleSymbolicName = null;
+
+ /* Sample output -- target/test-classes/bundle-plugin.bundle-classpath-mappings.json
+ {
+ "mainBundle": {
+ "bundleSymbolicName": "bundle-plugin-test",
+ "classPathElements": [
+ "/Users/tonyv/Repos/vespa/bundle-plugin-test/target/classes",
+ "/Users/tonyv/.m2/repository/com/yahoo/vespa/jrt/6-SNAPSHOT/jrt-6-SNAPSHOT.jar",
+ "/Users/tonyv/.m2/repository/com/yahoo/vespa/annotations/6-SNAPSHOT/annotations-6-SNAPSHOT.jar"
+ ]
+ },
+ "providedDependencies": [
+ {
+ "bundleSymbolicName": "jrt",
+ "classPathElements": [
+ "/Users/tonyv/.m2/repository/com/yahoo/vespa/jrt/6-SNAPSHOT/jrt-6-SNAPSHOT.jar"
+ ]
+ }
+ ]
+ }
+ */
+ @Override
+ public void execute() throws MojoExecutionException {
+ Preconditions.checkNotNull(bundleSymbolicName);
+
+ Artifacts.ArtifactSet artifacts = Artifacts.getArtifacts(project);
+ List<Artifact> embeddedArtifacts = artifacts.getJarArtifactsToInclude();
+ List<Artifact> providedJarArtifacts = artifacts.getJarArtifactsProvided();
+
+ List<File> embeddedArtifactsFiles = embeddedArtifacts.stream().map(Artifact::getFile).collect(Collectors.toList());
+
+ List<String> classPathElements = Stream.concat(Stream.of(outputDirectory()), embeddedArtifactsFiles.stream())
+ .map(File::getAbsolutePath).collect(Collectors.toList());
+
+ ProjectBundleClassPaths classPathMappings = new ProjectBundleClassPaths(
+ new BundleClasspathMapping(bundleSymbolicName, classPathElements),
+ providedJarArtifacts.stream().map(f -> createDependencyClasspathMapping(f)).filter(Optional::isPresent).map(Optional::get)
+ .collect(Collectors.toList()));
+
+ try {
+ ProjectBundleClassPaths.save(testOutputPath().resolve(ProjectBundleClassPaths.CLASSPATH_MAPPINGS_FILENAME), classPathMappings);
+ } catch (IOException e) {
+ throw new MojoExecutionException("Error saving to file " + testOutputPath(), e);
+ }
+ }
+
+ private File outputDirectory() {
+ return new File(project.getBuild().getOutputDirectory());
+ }
+
+ private Path testOutputPath() {
+ return Paths.get(project.getBuild().getTestOutputDirectory());
+ }
+
+ /* TODO:
+ * 1) add the dependencies of the artifact in the future(i.e. dependencies of dependencies)
+ * or
+ * 2) obtain bundles with embedded dependencies from the maven repository,
+ * and support loading classes from the nested jar files in those bundles.
+ */
+ Optional<BundleClasspathMapping> createDependencyClasspathMapping(Artifact artifact) {
+ return bundleSymbolicNameForArtifact(artifact)
+ .map(name -> new BundleClasspathMapping(name, Arrays.asList(artifact.getFile().getAbsolutePath())));
+ }
+
+ private static Optional<String> bundleSymbolicNameForArtifact(Artifact artifact) {
+ if (artifact.getFile().getName().endsWith(".jar")) {
+ return AnalyzeBundle.bundleSymbolicName(artifact.getFile());
+ } else {
+ // Not the best heuristic. The other alternatives are parsing the pom file or
+ // storing information in target/classes when building the provided bundles.
+ return Optional.of(artifact.getArtifactId());
+ }
+ }
+}
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
new file mode 100644
index 00000000000..973854aa59a
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/GenerateOsgiManifestMojo.java
@@ -0,0 +1,314 @@
+// 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.mojo;
+
+import com.google.common.collect.Sets;
+import com.yahoo.container.plugin.bundle.AnalyzeBundle;
+import com.yahoo.container.plugin.classanalysis.Analyze;
+import com.yahoo.container.plugin.classanalysis.ClassFileMetaData;
+import com.yahoo.container.plugin.classanalysis.ExportPackageAnnotation;
+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;
+import org.apache.maven.project.MavenProject;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+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;
+import static com.yahoo.container.plugin.util.JarFiles.withJarFile;
+
+/**
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+@Mojo(name = "generate-osgi-manifest", requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true)
+public class GenerateOsgiManifestMojo extends AbstractMojo {
+
+ @Parameter(defaultValue = "${project}")
+ private MavenProject project = null;
+
+ @Parameter
+ private String discApplicationClass = null;
+
+ @Parameter
+ private String discPreInstallBundle = null;
+
+ @Parameter(alias = "Bundle-Version", defaultValue = "${project.version}")
+ private String bundleVersion = null;
+
+ // TODO Vespa 7: default should be ${project.groupId}.${project.artifactId}
+ @Parameter(alias = "Bundle-SymbolicName", defaultValue = "${project.artifactId}")
+ private String bundleSymbolicName = null;
+
+ @Parameter(alias = "Bundle-Activator")
+ private String bundleActivator = null;
+
+ @Parameter(alias = "X-JDisc-Privileged-Activator")
+ private String jdiscPrivilegedActivator = null;
+
+ @Parameter(alias = "X-Config-Models")
+ private String configModels = null;
+
+ @Parameter(alias = "Import-Package")
+ private String importPackage = null;
+
+ @Parameter(alias = "WebInfUrl")
+ private String webInfUrl = null;
+
+ @Parameter(alias = "Main-Class")
+ private String mainClass = null;
+
+ @Parameter(alias = "X-Jersey-Binding")
+ private String jerseyBinding = null;
+
+ public void execute() throws MojoExecutionException, MojoFailureException {
+ try {
+ Artifacts.ArtifactSet artifactSet = Artifacts.getArtifacts(project);
+ warnOnUnsupportedArtifacts(artifactSet.getNonJarArtifacts());
+
+ AnalyzeBundle.PublicPackages publicPackagesFromProvidedJars = AnalyzeBundle.publicPackagesAggregated(
+ artifactSet.getJarArtifactsProvided().stream().map(Artifact::getFile).collect(Collectors.toList()));
+ PackageTally includedJarPackageTally = definedPackages(artifactSet.getJarArtifactsToInclude());
+
+ PackageTally projectPackageTally = analyzeProjectClasses();
+ PackageTally pluginPackageTally = projectPackageTally.combine(includedJarPackageTally);
+
+ Set<String> definedPackages = new HashSet<>(projectPackageTally.definedPackages());
+ definedPackages.addAll(includedJarPackageTally.definedPackages());
+
+ warnIfPackagesDefinedOverlapsGlobalPackages(definedPackages, publicPackagesFromProvidedJars.globals);
+
+ 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(", ")));
+ }
+
+ Map<String, Import> calculatedImports = ImportPackages.calculateImports(pluginPackageTally.referencedPackages(),
+ pluginPackageTally.definedPackages(), ExportPackages.exportsByPackageName(publicPackagesFromProvidedJars.exports));
+
+ Map<String, Optional<String>> manualImports = emptyToNone(importPackage).map(GenerateOsgiManifestMojo::getManualImports)
+ .orElseGet(HashMap::new);
+ for (String packageName : manualImports.keySet()) {
+ calculatedImports.remove(packageName);
+ }
+ createManifestFile(new File(project.getBuild().getOutputDirectory()), manifestContent(project,
+ artifactSet.getJarArtifactsToInclude(), manualImports, calculatedImports.values(), pluginPackageTally));
+
+ } catch (Exception e) {
+ throw new MojoExecutionException("Failed generating osgi manifest.", e);
+ }
+ }
+
+ 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) {
+ throw new MojoExecutionException(
+ "The following packages are both global and included in the bundle:\n " + String.join("\n ", overlap));
+ }
+ }
+
+ private Collection<String> osgiExportPackages(Map<String, ExportPackageAnnotation> exportedPackages) {
+ return exportedPackages.entrySet().stream().map(entry -> entry.getKey() + ";version=" + entry.getValue().osgiVersion())
+ .collect(Collectors.toList());
+ }
+
+ private static String trimWhitespace(Optional<String> lines) {
+ return Stream.of(lines.orElse("").split(",")).map(String::trim).collect(Collectors.joining(","));
+ }
+
+ private Map<String, String> manifestContent(MavenProject project, Collection<Artifact> jarArtifactsToInclude,
+ Map<String, Optional<String>> manualImports, Collection<Import> imports, PackageTally pluginPackageTally) {
+ Map<String, String> ret = new HashMap<>();
+ String importPackage = Stream.concat(manualImports.entrySet().stream().map(e -> asOsgiImport(e.getKey(), e.getValue())),
+ imports.stream().map(Import::asOsgiImport)).sorted().collect(Collectors.joining(","));
+ String exportPackage = osgiExportPackages(pluginPackageTally.exportedPackages()).stream().sorted().collect(Collectors.joining(","));
+
+ for (Pair<String, String> element : Arrays.asList(//
+ Pair.of("Created-By", "vespa container maven plugin"), //
+ Pair.of("Bundle-ManifestVersion", "2"), //
+ Pair.of("Bundle-Name", project.getName()), //
+ Pair.of("Bundle-SymbolicName", bundleSymbolicName), //
+ Pair.of("Bundle-Version", asBundleVersion(bundleVersion)), //
+ Pair.of("Bundle-Vendor", "Yahoo!"), //
+ Pair.of("Bundle-ClassPath", bundleClassPath(jarArtifactsToInclude)), //
+ Pair.of("Bundle-Activator", bundleActivator), //
+ Pair.of("X-JDisc-Privileged-Activator", jdiscPrivilegedActivator), //
+ Pair.of("Main-Class", mainClass), //
+ Pair.of("X-JDisc-Application", discApplicationClass), //
+ Pair.of("X-JDisc-Preinstall-Bundle", trimWhitespace(Optional.ofNullable(discPreInstallBundle))), //
+ Pair.of("X-Config-Models", configModels), //
+ Pair.of("X-Jersey-Binding", jerseyBinding), //
+ Pair.of("WebInfUrl", webInfUrl), //
+ Pair.of("Import-Package", importPackage), //
+ Pair.of("Export-Package", exportPackage))) {
+ if (element.getValue() != null && element.getValue().isEmpty() == false) {
+ ret.put(element.getKey(), element.getValue());
+ }
+ }
+ return ret;
+ }
+
+ private static String asOsgiImport(String packageName, Optional<String> version) {
+ return version.map(s -> packageName + ";version=" + quote(s)).orElse(packageName);
+ }
+
+ private static String quote(String s) {
+ return "\"" + s + "\"";
+ }
+
+ private static void createManifestFile(File outputDirectory, Map<String, String> manifestContent) {
+ Manifest manifest = toManifest(manifestContent);
+
+ withFileOutputStream(new File(outputDirectory, JarFile.MANIFEST_NAME), outputStream -> {
+ manifest.write(outputStream);
+ return null;
+ });
+ }
+
+ private static Manifest toManifest(Map<String, String> manifestContent) {
+ Manifest manifest = new Manifest();
+ Attributes mainAttributes = manifest.getMainAttributes();
+
+ mainAttributes.put(Attributes.Name.MANIFEST_VERSION, "1.0");
+ manifestContent.forEach(mainAttributes::putValue);
+
+ return manifest;
+ }
+
+ private static String bundleClassPath(Collection<Artifact> artifactsToInclude) {
+ return Stream.concat(Stream.of("."), artifactsToInclude.stream().map(GenerateOsgiManifestMojo::dependencyPath))
+ .collect(Collectors.joining(","));
+ }
+
+ private static String dependencyPath(Artifact artifact) {
+ return "dependencies/" + artifact.getFile().getName();
+ }
+
+ private static String asBundleVersion(String projectVersion) {
+ if (projectVersion == null) {
+ throw new IllegalArgumentException("Missing project version.");
+ }
+
+ String[] parts = projectVersion.split("-", 2);
+ List<String> numericPart = Stream.of(parts[0].split("\\.")).map(s -> Strings.replaceEmptyString(s, "0")).limit(3)
+ .collect(Collectors.toList());
+ while (numericPart.size() < 3) {
+ numericPart.add("0");
+ }
+
+ return String.join(".", numericPart);
+ }
+
+ private void warnOnUnsupportedArtifacts(Collection<Artifact> nonJarArtifacts) {
+ List<Artifact> unsupportedArtifacts = nonJarArtifacts.stream().filter(a -> "pom".equals(a.getType()) == false)
+ .collect(Collectors.toList());
+
+ unsupportedArtifacts.forEach(artifact -> getLog()
+ .warn(String.format("Unsupported artifact '%s': Type '%s' is not supported. Please file a feature request.",
+ artifact.getId(), artifact.getType())));
+ }
+
+ private PackageTally analyzeProjectClasses() {
+ File outputDirectory = new File(project.getBuild().getOutputDirectory());
+
+ List<ClassFileMetaData> analyzedClasses = allDescendantFiles(outputDirectory).filter(file -> file.getName().endsWith(".class"))
+ .map(Analyze::analyzeClass).collect(Collectors.toList());
+
+ return PackageTally.fromAnalyzedClassFiles(analyzedClasses);
+ }
+
+ private static PackageTally definedPackages(Collection<Artifact> jarArtifacts) {
+ return PackageTally.combine(jarArtifacts.stream().map(ja -> withJarFile(ja.getFile(), GenerateOsgiManifestMojo::definedPackages))
+ .collect(Collectors.toList()));
+ }
+
+ private static PackageTally definedPackages(JarFile jarFile) throws MojoExecutionException {
+ 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")) {
+ analyzedClasses.add(analyzeClass(jarFile, entry));
+ }
+ }
+ return PackageTally.fromAnalyzedClassFiles(analyzedClasses);
+ }
+
+ private static ClassFileMetaData analyzeClass(JarFile jarFile, JarEntry entry) throws MojoExecutionException {
+ try {
+ return withInputStream(jarFile, entry, Analyze::analyzeClass);
+ } catch (Exception e) {
+ throw new MojoExecutionException(
+ String.format("While analyzing the class '%s' in jar file '%s'", entry.getName(), jarFile.getName()), e);
+ }
+ }
+
+ private static Map<String, Optional<String>> getManualImports(String importPackage) {
+ try {
+ Map<String, Optional<String>> ret = new HashMap<>();
+ List<Export> imports = parseImportPackages(importPackage);
+ for (Export imp : imports) {
+ Optional<String> version = getVersionThrowOthers(imp.getParameters());
+ imp.getPackageNames().forEach(pn -> ret.put(pn, version));
+ }
+
+ return ret;
+ } catch (Exception e) {
+ throw new RuntimeException("Error in Import-Package:" + importPackage, e);
+ }
+ }
+
+ private static Optional<String> getVersionThrowOthers(List<ExportPackages.Parameter> parameters) {
+ if (parameters.size() == 1 && "version".equals(parameters.get(0).getName())) {
+ return Optional.of(parameters.get(0).getValue());
+ } else if (parameters.size() == 0) {
+ return Optional.empty();
+ } else {
+ List<String> paramNames = parameters.stream().map(ExportPackages.Parameter::getName).collect(Collectors.toList());
+ throw new RuntimeException("A single, optional version parameter expected, but got " + paramNames);
+ }
+ }
+
+ private static List<Export> parseImportPackages(String importPackages) {
+ return ExportPackageParser.parseExports(importPackages);
+ }
+
+ private static Optional<String> emptyToNone(String str) {
+ return Optional.ofNullable(str).map(String::trim).filter(s -> s.isEmpty() == false);
+ }
+
+ 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/mojo/GenerateSourcesMojo.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/GenerateSourcesMojo.java
index 11f5696c589..2d174f6bb4b 100644
--- a/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/GenerateSourcesMojo.java
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/GenerateSourcesMojo.java
@@ -27,7 +27,7 @@ import static org.twdata.maven.mojoexecutor.MojoExecutor.*;
/**
* Calls the generate-sources phase in the container lifecycle defined in lifecycle.xml.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
@Mojo(name = "generateSources", requiresDependencyResolution = ResolutionScope.COMPILE, threadSafe = true)
public class GenerateSourcesMojo extends AbstractMojo {
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/osgi/ExportPackageParser.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/osgi/ExportPackageParser.java
new file mode 100644
index 00000000000..16858808a58
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/osgi/ExportPackageParser.java
@@ -0,0 +1,283 @@
+// 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.osgi;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Consumer;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class ExportPackageParser {
+ public static List<ExportPackages.Export> parseExports(String exportAttribute) {
+ ParsingContext p = new ParsingContext(exportAttribute.trim());
+
+ List<ExportPackages.Export> exports = parseExportPackage(p);
+ if (exports.isEmpty()) {
+ p.fail("Expected a list of exports");
+ } else if (p.atEnd() == false) {
+ p.fail("Exports not fully processed");
+ }
+ return exports;
+ }
+
+ private static class ParsingContext {
+ private enum State {
+ Invalid, WantMore, End
+ }
+
+ private CharSequence input;
+ private int pos;
+ private State state;
+ private int length;
+ private char ch;
+
+ private ParsingContext(CharSequence input) {
+ this.input = input;
+ this.pos = 0;
+ }
+
+ private Optional<String> read(Consumer<ParsingContext> rule) {
+ StringBuilder ret = new StringBuilder();
+
+ parse: while (true) {
+ if (input.length() < pos + 1) {
+ break;
+ }
+ ch = input.charAt(pos);
+ state = State.WantMore;
+ length = ret.length();
+ rule.accept(this);
+
+ switch (state) {
+ case Invalid:
+ if (ret.length() == 0) {
+ break parse;
+ } else {
+ String printable = Character.isISOControl(ch) ? "#" + Integer.toString((int) ch)
+ : "[" + Character.toString(ch) + "]";
+ pos++;
+ fail("Character " + printable + " was not acceptable");
+ }
+ break;
+ case WantMore:
+ ret.append(ch);
+ pos++;
+ break;
+ case End:
+ break parse;
+ }
+ }
+
+ if (ret.length() == 0) {
+ return Optional.empty();
+ } else {
+ return Optional.of(ret.toString());
+ }
+ }
+
+ private Optional<String> regexp(Pattern pattern) {
+ Matcher matcher = pattern.matcher(input);
+ matcher.region(pos, input.length());
+ if (matcher.lookingAt()) {
+ String value = matcher.group();
+ pos += value.length();
+ return Optional.of(value);
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ private Optional<String> exactly(String string) {
+ if (input.length() - pos < string.length()) {
+ return Optional.empty();
+ }
+ if (input.subSequence(pos, pos + string.length()).equals(string)) {
+ pos += string.length();
+ return Optional.of(string);
+ }
+ return Optional.empty();
+ }
+
+ private boolean atEnd() {
+ return pos == input.length();
+ }
+
+ private void invalid() {
+ this.state = State.Invalid;
+ }
+
+ private void end() {
+ this.state = State.End;
+ }
+
+ private void fail(String message) {
+ throw new RuntimeException("Failed parsing Export-Package: " + message + " at position " + pos);
+ }
+ }
+
+ /* ident = ? a valid Java identifier ? */
+ private static Optional<String> parseIdent(ParsingContext p) {
+ Optional<String> ident = p.read(ctx -> {
+ if (ctx.length == 0) {
+ if (Character.isJavaIdentifierStart(ctx.ch) == false) {
+ ctx.invalid();
+ }
+ } else {
+ if (Character.isJavaIdentifierPart(ctx.ch) == false) {
+ ctx.end();
+ }
+ }
+ });
+ return ident;
+ }
+
+ /* stringLiteral = ? sequence of any character except double quotes, control characters or backslash,
+ a backslash followed by another backslash, a single or double quote, or one of the letters b,f,n,r or t
+ a backslash followed by u followed by four hexadecimal digits ? */
+ private static Pattern STRING_LITERAL_PATTERN = Pattern
+ .compile("\"" + "(?:[^\"\\p{Cntrl}\\\\]|\\\\[\\\\'\"bfnrt]|\\\\u[0-9a-fA-F]{4})+" + "\"");
+
+ private static Optional<String> parseStringLiteral(ParsingContext p) {
+ return p.regexp(STRING_LITERAL_PATTERN).map(quoted -> quoted.substring(1, quoted.length() - 1));
+ }
+
+ /* extended = { \p{Alnum} | '_' | '-' | '.' }+ */
+ private static Pattern EXTENDED_PATTERN = Pattern.compile("[\\p{Alnum}_.-]+");
+
+ private static Optional<String> parseExtended(ParsingContext p) {
+ return p.regexp(EXTENDED_PATTERN);
+ }
+
+ /* argument = extended | stringLiteral | ? failure ? */
+ private static String parseArgument(ParsingContext p) {
+ Optional<String> argument = parseExtended(p);
+ if (argument.isPresent() == false) {
+ argument = parseStringLiteral(p);
+ }
+ if (argument.isPresent() == false) {
+ p.fail("Expected an extended token or a string literal");
+ }
+ return argument.get();
+ }
+
+ /*
+ * parameter = ( directive | attribute )
+ * directive = extended, ':=', argument
+ * attribute = extended, '=', argument
+ */
+ private static Pattern DIRECTIVE_OR_ATTRIBUTE_SEPARATOR_PATTERN = Pattern.compile("\\s*:?=\\s*");
+
+ private static Optional<ExportPackages.Parameter> parseParameter(ParsingContext p) {
+ int backtrack = p.pos;
+ Optional<String> ext = parseExtended(p);
+ if (ext.isPresent()) {
+ Optional<String> sep = p.regexp(DIRECTIVE_OR_ATTRIBUTE_SEPARATOR_PATTERN);
+ if (sep.isPresent() == false) {
+ p.pos = backtrack;
+ return Optional.empty();
+ }
+ String argument = parseArgument(p);
+ return Optional.of(new ExportPackages.Parameter(ext.get(), argument));
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ /* parameters = parameter, { ';' parameter } */
+ private static Pattern PARAMETER_SEPARATOR_PATTERN = Pattern.compile("\\s*;\\s*");
+
+ private static List<ExportPackages.Parameter> parseParameters(ParsingContext p) {
+ List<ExportPackages.Parameter> params = new ArrayList<>();
+ boolean wantMore = true;
+ do {
+ Optional<ExportPackages.Parameter> param = parseParameter(p);
+ if (param.isPresent()) {
+ params.add(param.get());
+ wantMore = p.regexp(PARAMETER_SEPARATOR_PATTERN).isPresent();
+ } else {
+ wantMore = false;
+ }
+ } while (wantMore);
+
+ return params;
+ }
+
+ /* packageName = ident, { '.', ident } */
+ private static Optional<String> parsePackageName(ParsingContext p) {
+ StringBuilder ret = new StringBuilder();
+
+ boolean wantMore = true;
+ do {
+ Optional<String> ident = parseIdent(p);
+ if (ident.isPresent()) {
+ ret.append(ident.get());
+ Optional<String> separator = p.exactly(".");
+ if (separator.isPresent()) {
+ ret.append(separator.get());
+ wantMore = true;
+ } else {
+ wantMore = false;
+ }
+ } else {
+ wantMore = false;
+ }
+ } while (wantMore);
+
+ if (ret.length() > 0) {
+ return Optional.of(ret.toString());
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ /* export = packageName, [ ';', ( parameters | export ) ] */
+ private static ExportPackages.Export parseExport(ParsingContext p) {
+ List<String> exports = new ArrayList<>();
+
+ boolean wantMore = true;
+ do {
+ if (exports.isEmpty() == false) { // second+ iteration
+ List<ExportPackages.Parameter> params = parseParameters(p);
+ if (params.isEmpty() == false) {
+ return new ExportPackages.Export(exports, params);
+ }
+ }
+
+ Optional<String> packageName = parsePackageName(p);
+ if (packageName.isPresent()) {
+ exports.add(packageName.get());
+ } else {
+ p.fail(exports.isEmpty() ? "Expected a package name" : "Expected either a package name or a parameter list");
+ }
+
+ wantMore = p.regexp(PARAMETER_SEPARATOR_PATTERN).isPresent();
+ } while (wantMore);
+
+ return new ExportPackages.Export(exports, new ArrayList<>());
+ }
+
+ /* exportPackage = export, { ',', export } */
+ private static Pattern EXPORT_SEPARATOR_PATTERN = Pattern.compile("\\s*,\\s*");
+
+ private static List<ExportPackages.Export> parseExportPackage(ParsingContext p) {
+ List<ExportPackages.Export> exports = new ArrayList<>();
+
+ boolean wantMore = true;
+ do {
+ ExportPackages.Export export = parseExport(p);
+ if (export.getPackageNames().isEmpty()) {
+ wantMore = false;
+ } else {
+ exports.add(export);
+ wantMore = p.regexp(EXPORT_SEPARATOR_PATTERN).isPresent();
+ }
+ } while (wantMore);
+
+ return exports;
+ }
+}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/osgi/ExportPackages.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/osgi/ExportPackages.java
new file mode 100644
index 00000000000..253e0727050
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/osgi/ExportPackages.java
@@ -0,0 +1,70 @@
+// 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.osgi;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class ExportPackages {
+ public static class Export {
+ private final List<String> packageNames;
+ private final List<Parameter> parameters;
+
+ public Export(List<String> packageNames, List<Parameter> parameters) {
+ this.packageNames = packageNames;
+ this.parameters = parameters;
+ }
+
+ public Optional<String> version() {
+ for (Parameter par : parameters) {
+ if ("version".equals(par.getName())) {
+ return Optional.of(par.getValue());
+ }
+ }
+ return Optional.empty();
+ }
+
+ public List<String> getPackageNames() {
+ return packageNames;
+ }
+
+ public List<Parameter> getParameters() {
+ return parameters;
+ }
+ }
+
+ public static class Parameter {
+ private final String name;
+ private final String value;
+
+ public Parameter(String name, String value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+ }
+
+ public static Map<String, Export> exportsByPackageName(Collection<Export> exports) {
+ Map<String, Export> ret = new HashMap<>();
+ for (Export export : exports) {
+ for (String packageName : export.getPackageNames()) {
+ //ensure that earlier exports of a package overrides later exports.
+ ret.computeIfAbsent(packageName, ign -> export);
+ }
+ }
+ return ret;
+ }
+}
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
new file mode 100644
index 00000000000..b58248ec4a6
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/osgi/ImportPackages.java
@@ -0,0 +1,97 @@
+// 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.osgi;
+
+import com.google.common.collect.Sets;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class ImportPackages {
+ public static final int INFINITE_VERSION = 99999;
+ private static final String GUAVA_BASE_PACKAGE = "com.google.common";
+
+ public static class Import {
+ private final String packageName;
+ private final List<Integer> versionNumber;
+
+ public Import(String packageName, Optional<String> version) {
+ this.packageName = packageName;
+ this.versionNumber = new ArrayList<>();
+
+ if (version.isPresent()) {
+ try {
+ Arrays.stream(version.get().split("\\.")).map(Integer::parseInt).limit(3).forEach(this.versionNumber::add);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException(
+ String.format("Invalid version number '%s' for package '%s'.", version.get(), packageName), e);
+ }
+ }
+ }
+
+ public Optional<Integer> majorVersion() {
+ if (versionNumber.size() >= 1) {
+ return Optional.of(versionNumber.get(0));
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ public String packageName() {
+ return packageName;
+ }
+
+ public String version() {
+ return versionNumber.stream().map(Object::toString).collect(Collectors.joining("."));
+ }
+
+ // TODO: Detecting guava packages should be based on Bundle-SymbolicName, not package name.
+ public Optional<String> importVersionRange() {
+ if (versionNumber.isEmpty()) {
+ return Optional.empty();
+ } else {
+ int upperLimit = isGuavaPackage() ? INFINITE_VERSION // guava increases major version for each release
+ : versionNumber.get(0) + 1;
+ return Optional.of(String.format("[%s,%d)", version(), upperLimit));
+ }
+ }
+
+ public boolean isGuavaPackage() {
+ return packageName.equals(GUAVA_BASE_PACKAGE) || packageName.startsWith(GUAVA_BASE_PACKAGE + ".");
+ }
+
+ public String asOsgiImport() {
+ return packageName + importVersionRange().map(version -> ";version=\"" + version + '"').orElse("");
+ }
+ }
+
+ 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);
+ if (export != null) {
+ ret.put(undefinedPackage, new Import(undefinedPackage, version(export)));
+ }
+ }
+ return ret;
+ }
+
+ private static Optional<String> version(ExportPackages.Export export) {
+ for (ExportPackages.Parameter param : export.getParameters()) {
+ if ("version".equals(param.getName())) {
+ return Optional.of(param.getValue());
+ }
+ }
+ return Optional.empty();
+ }
+}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/osgi/ProjectBundleClassPaths.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/osgi/ProjectBundleClassPaths.java
index 545b76b32d7..42033f6ac73 100644
--- a/bundle-plugin/src/main/java/com/yahoo/container/plugin/osgi/ProjectBundleClassPaths.java
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/osgi/ProjectBundleClassPaths.java
@@ -15,7 +15,7 @@ import java.util.Objects;
* Represents the bundles in a maven project and the classpath elements
* corresponding to code that would end up in the bundle.
*
- * @author tonytv
+ * @author Tony Vaagenes
* @author bjorncs
*/
public class ProjectBundleClassPaths {
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/util/Files.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/util/Files.java
new file mode 100644
index 00000000000..bcd5d3768f3
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/util/Files.java
@@ -0,0 +1,30 @@
+// 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.util;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.stream.Stream;
+
+/**
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class Files {
+ public static Stream<File> allDescendantFiles(File file) {
+ if (file.isFile()) {
+ return Stream.of(file);
+ } else if (file.isDirectory()) {
+ return Stream.of(file.listFiles()).flatMap(Files::allDescendantFiles);
+ } else {
+ return Stream.empty();
+ }
+ }
+
+ public static <T> T withFileOutputStream(File file, ThrowingFunction<FileOutputStream, T> f) {
+ try (FileOutputStream fos = new FileOutputStream(file)) {
+ return f.apply(fos);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/util/IO.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/util/IO.java
new file mode 100644
index 00000000000..a1e313b920b
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/util/IO.java
@@ -0,0 +1,41 @@
+// 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.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+
+/**
+ * Utility methods relating to IO
+ *
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class IO {
+ public static <T> T withFileInputStream(File file, ThrowingFunction<FileInputStream, T> f) {
+ try (FileInputStream fis = new FileInputStream(file)) {
+ return f.apply(fis);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Creates a new file and all its parent directories, and provides a file output stream to the file.
+ */
+ public static <T> T withFileOutputStream(File file, ThrowingFunction<OutputStream, T> f) {
+ makeDirectoriesRecursive(file.getParentFile());
+ try (FileOutputStream fos = new FileOutputStream(file)) {
+ return f.apply(fos);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static void makeDirectoriesRecursive(File file) {
+ if (!file.mkdirs() && !file.isDirectory()) {
+ throw new RuntimeException("Could not create directory " + file.getPath());
+ }
+ }
+}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/util/JarFiles.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/util/JarFiles.java
new file mode 100644
index 00000000000..398b2f5a72a
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/util/JarFiles.java
@@ -0,0 +1,36 @@
+// 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.util;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Optional;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class JarFiles {
+ public static <T> T withJarFile(File file, ThrowingFunction<JarFile, T> action) {
+ try (JarFile jar = new JarFile(file)) {
+ return action.apply(jar);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static <T> T withInputStream(ZipFile zipFile, ZipEntry zipEntry, ThrowingFunction<InputStream, T> action) {
+ try (InputStream is = zipFile.getInputStream(zipEntry)) {
+ return action.apply(is);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static Optional<Manifest> getManifest(File jarFile) {
+ return withJarFile(jarFile, jar -> Optional.ofNullable(jar.getManifest()));
+ }
+}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/util/Maps.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/util/Maps.java
new file mode 100644
index 00000000000..5aa14402d4e
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/util/Maps.java
@@ -0,0 +1,31 @@
+// 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.util;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiFunction;
+
+/**
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class Maps {
+ public static <K, V> Map<K, V> combine(Map<K, V> left, Map<K, V> right, BiFunction<V, V, V> combiner) {
+ Map<K, V> ret = new HashMap<>();
+ Set<K> keysRight = new HashSet<>(right.keySet());
+
+ left.forEach((k, v) -> {
+ if (keysRight.contains(k)) {
+ ret.put(k, combiner.apply(v, right.get(k)));
+ keysRight.remove(k);
+ } else {
+ ret.put(k, v);
+ }
+ });
+ keysRight.forEach(k -> ret.put(k, right.get(k)));
+
+ return ret;
+ }
+}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/util/Strings.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/util/Strings.java
new file mode 100644
index 00000000000..15bdfb153ad
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/util/Strings.java
@@ -0,0 +1,26 @@
+// 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.util;
+
+import java.util.Optional;
+
+/**
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class Strings {
+ public static String replaceEmptyString(String s, String replacement) {
+ if (s == null || s.isEmpty()) {
+ return replacement;
+ } else {
+ return s;
+ }
+ }
+
+ public static Optional<String> noneIfEmpty(String s) {
+ if (s == null || s.isEmpty()) {
+ return Optional.empty();
+ } else {
+ return Optional.of(s);
+ }
+ }
+} \ No newline at end of file
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/util/ThrowingFunction.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/util/ThrowingFunction.java
new file mode 100644
index 00000000000..9ca64aabd73
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/util/ThrowingFunction.java
@@ -0,0 +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.container.plugin.util;
+
+/* Equivalent to java.util.function.Function, but allows throwing of Exceptions */
+
+/**
+ * @author ollivir
+ */
+public interface ThrowingFunction<T, U> {
+ U apply(T input) throws Exception;
+}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/bundle/AnalyzeBundle.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/bundle/AnalyzeBundle.scala
deleted file mode 100644
index 1b3979476bd..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/bundle/AnalyzeBundle.scala
+++ /dev/null
@@ -1,72 +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.container.plugin.bundle
-
-import java.io.File
-import java.util.jar.{Manifest => JarManifest}
-
-import com.yahoo.container.plugin.osgi.ExportPackageParser
-import com.yahoo.container.plugin.osgi.ExportPackages.Export
-import com.yahoo.container.plugin.util.JarFiles
-
-
-/**
- * @author tonytv
- */
-object AnalyzeBundle {
- case class PublicPackages(exports : List[Export], globals : List[String])
-
- def publicPackagesAggregated(jarFiles : Iterable[File]) = aggregate(jarFiles map {publicPackages(_)})
-
- def aggregate(publicPackagesList : Iterable[PublicPackages]) =
- (PublicPackages(List(), List()) /: publicPackagesList) { (a,b) =>
- PublicPackages(a.exports ++ b.exports, a.globals ++ b.globals)
- }
-
- def publicPackages(jarFile: File): PublicPackages = {
- try {
-
- (for {
- manifest <- JarFiles.getManifest(jarFile)
- if isOsgiManifest(manifest)
- } yield PublicPackages(parseExports(manifest), parseGlobals(manifest))).
- getOrElse(PublicPackages(List(), List()))
-
- } catch {
- case e : Exception => throw new RuntimeException("Invalid manifest in bundle '%s'".format(jarFile.getPath), e)
- }
- }
-
- def bundleSymbolicName(jarFile: File): Option[String] = {
- JarFiles.getManifest(jarFile).flatMap(getBundleSymbolicName)
- }
-
- private def parseExportsFromAttribute(manifest : JarManifest, attributeName : String) = {
- (for (export <- getMainAttributeValue(manifest, attributeName)) yield
- ExportPackageParser.parseAll(export) match {
- case noSuccess: ExportPackageParser.NoSuccess => throw new RuntimeException(
- "Failed parsing %s: %s".format(attributeName, noSuccess))
- case success => success.get
- }).
- getOrElse(List())
- }
-
- private def parseExports = parseExportsFromAttribute(_ : JarManifest, "Export-Package")
-
- private def parseGlobals(manifest : JarManifest) = {
- //TODO: Use separate parser for global packages.
- val globals = parseExportsFromAttribute(manifest, "Global-Package")
-
- if (globals map {_.parameters} exists {!_.isEmpty}) {
- throw new RuntimeException("Parameters not valid for Global-Package.")
- }
-
- globals flatMap {_.packageNames}
- }
-
- private def getMainAttributeValue(manifest: JarManifest, name: String): Option[String] =
- Option(manifest.getMainAttributes.getValue(name))
-
- private def isOsgiManifest = getBundleSymbolicName(_: JarManifest).isDefined
-
- private def getBundleSymbolicName = getMainAttributeValue(_: JarManifest, "Bundle-SymbolicName")
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/bundle/TransformExportPackages.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/bundle/TransformExportPackages.scala
deleted file mode 100644
index f924b12c539..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/bundle/TransformExportPackages.scala
+++ /dev/null
@@ -1,54 +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.container.plugin.bundle
-
-import java.io.File
-import com.yahoo.container.plugin.osgi.ExportPackages.{Export, Parameter}
-import com.yahoo.container.plugin.osgi.ExportPackages.Export
-
-/**
- * @author tonytv
- */
-object TransformExportPackages extends App {
- def replaceVersions(exports: List[Export], newVersion: String): List[Export] = {
- mapParameters(exports) { parameters =>
- parameters map replaceVersion(newVersion)
- }
- }
-
- def removeUses(exports: List[Export]): List[Export] = {
- mapParameters(exports) { parameters =>
- parameters filter {_.name != "uses"}
- }
- }
-
- def mapParameters(exports: List[Export])(f: List[Parameter] => List[Parameter]): List[Export] = {
- exports map { case Export(packageNames: List[String], parameters: List[Parameter]) =>
- Export(packageNames, f(parameters))
- }
- }
-
- private def replaceVersion(newVersion: String)(parameter: Parameter) = {
- parameter match {
- case Parameter("version", _) => Parameter("version", newVersion)
- case other => other
- }
- }
-
- def toExportPackageProperty(exports: List[Export]): String = {
- val exportPackages =
- exports map { case Export(packageNames: List[String], parameters: List[Parameter]) =>
- val parameterString = nameEqualsValue(parameters)
- (packageNames ++ parameterString) mkString ";"
- }
-
- exportPackages mkString ","
- }
-
- private def nameEqualsValue(parameters: List[Parameter]) = {
- parameters map { case Parameter(name, value) =>
- s"$name=${quote(value)}"
- }
- }
-
- def quote(s: String) = '"' + s + '"'
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/Analyze.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/Analyze.scala
deleted file mode 100644
index 2f2d034679d..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/Analyze.scala
+++ /dev/null
@@ -1,28 +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.container.plugin.classanalysis
-
-import org.objectweb.asm._
-import java.io.{InputStream, File}
-import com.yahoo.container.plugin.util.IO.withFileInputStream
-
-/**
- * Main entry point for class analysis
- * @author tonytv
- */
-object Analyze {
- def analyzeClass(classFile : File) : ClassFileMetaData = {
- try {
- withFileInputStream(classFile) { fileInputStream =>
- analyzeClass(fileInputStream)
- }
- } catch {
- case e : RuntimeException => throw new RuntimeException("An error occurred when analyzing " + classFile.getPath, e)
- }
- }
-
- def analyzeClass(inputStream : InputStream) : ClassFileMetaData = {
- val visitor = new AnalyzeClassVisitor()
- new ClassReader(inputStream).accept(visitor, ClassReader.SKIP_DEBUG)
- visitor.result
- }
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeClassVisitor.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeClassVisitor.scala
deleted file mode 100644
index 539684f2024..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeClassVisitor.scala
+++ /dev/null
@@ -1,102 +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.container.plugin.classanalysis
-
-import org.objectweb.asm._
-import com.yahoo.osgi.annotation.{ExportPackage, Version}
-import collection.mutable
-
-/**
- * Picks up classes used in class files.
- * @author tonytv
- */
-private class AnalyzeClassVisitor extends ClassVisitor(Opcodes.ASM6) with AnnotationVisitorTrait with AttributeVisitorTrait {
- private var name : String = null
- protected val imports : ImportsSet = mutable.Set()
- protected var exportPackageAnnotation: Option[ExportPackageAnnotation] = None
-
-
- override def visitAttribute(attribute: Attribute): Unit = super.visitAttribute(attribute)
-
- override def visitMethod(access: Int, name: String, desc: String, signature: String,
- exceptions: Array[String]): MethodVisitor = {
-
- imports ++= (Type.getReturnType(desc) +: Type.getArgumentTypes(desc)).flatMap(getClassName)
-
- imports ++= Option(exceptions) getOrElse(Array()) flatMap internalNameToClassName
-
- AnalyzeSignatureVisitor.analyzeMethod(signature, this)
- new AnalyzeMethodVisitor(this)
- }
-
- override def visitField(access: Int, name: String, desc: String, signature: String, value: AnyRef): FieldVisitor = {
- imports ++= getClassName(Type.getType(desc)).toList
-
- AnalyzeSignatureVisitor.analyzeField(signature, this)
- new FieldVisitor(Opcodes.ASM6) with SubVisitorTrait with AttributeVisitorTrait with AnnotationVisitorTrait {
- val analyzeClassVisitor = AnalyzeClassVisitor.this
-
- override def visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor = super.visitAnnotation(desc, visible)
- override def visitAttribute(attribute: Attribute): Unit = super.visitAttribute(attribute)
- override def visitEnd(): Unit = super.visitEnd()
- }
- }
-
- override def visit(version: Int, access: Int, name: String, signature: String, superName: String, interfaces: Array[String]) {
- this.name = internalNameToClassName(name).get
-
- imports ++= (superName +: interfaces) flatMap internalNameToClassName
- AnalyzeSignatureVisitor.analyzeClass(signature, this)
- }
-
- override def visitInnerClass(name: String, outerName: String, innerName: String, access: Int) {}
- override def visitOuterClass(owner: String, name: String, desc: String) {}
- override def visitSource(source: String, debug: String) {}
- override def visitEnd() {}
-
- def addImports(imports: TraversableOnce[String]) {
- this.imports ++= imports
- }
-
- override def visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor = {
- if (Type.getType(desc).getClassName == classOf[ExportPackage].getName) {
- visitExportPackage()
- } else {
- super.visitAnnotation(desc, visible)
- }
- }
-
- def visitExportPackage(): AnnotationVisitor = {
- def defaultVersionValue[T](name: String) = classOf[Version].getMethod(name).getDefaultValue().asInstanceOf[T]
-
- new AnnotationVisitor(Opcodes.ASM6) {
- var major: Int = defaultVersionValue("major")
- var minor: Int = defaultVersionValue("minor")
- var micro: Int = defaultVersionValue("micro")
- var qualifier: String = defaultVersionValue("qualifier")
-
- override def visit(name: String, value: AnyRef) {
- def valueAsInt = value.asInstanceOf[Int]
-
- name match {
- case "major" => major = valueAsInt
- case "minor" => minor = valueAsInt
- case "micro" => micro = valueAsInt
- case "qualifier" => qualifier = value.asInstanceOf[String]
- }
- }
-
- override def visitEnd() {
- exportPackageAnnotation = Some(ExportPackageAnnotation(major, minor, micro, qualifier))
- }
-
- override def visitEnum(name: String, desc: String, value: String) {}
- override def visitArray(name: String): AnnotationVisitor = this
- override def visitAnnotation(name: String, desc: String): AnnotationVisitor = this
- }
- }
-
- def result = {
- assert(!imports.contains("int"))
- new ClassFileMetaData(name, imports.toSet, exportPackageAnnotation)
- }
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeMethodVisitor.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeMethodVisitor.scala
deleted file mode 100644
index a8032b6a912..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeMethodVisitor.scala
+++ /dev/null
@@ -1,88 +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.container.plugin.classanalysis
-
-import org.objectweb.asm._
-
-/**
- * Picks up classes used in method bodies.
- * @author tonytv
- */
-private class AnalyzeMethodVisitor(val analyzeClassVisitor : AnalyzeClassVisitor)
- extends MethodVisitor(Opcodes.ASM6) with AnnotationVisitorTrait with AttributeVisitorTrait with SubVisitorTrait {
-
-
- override def visitParameterAnnotation(parameter: Int, desc: String, visible: Boolean): AnnotationVisitor = super.visitParameterAnnotation(parameter, desc, visible)
- override def visitAnnotationDefault(): AnnotationVisitor = super.visitAnnotationDefault()
- override def visitAttribute(attribute: Attribute): Unit = super.visitAttribute(attribute)
- override def visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor = super.visitAnnotation(desc, visible)
- override def visitEnd(): Unit = super.visitEnd()
-
- override def visitMultiANewArrayInsn(desc: String, dims: Int) {
- imports ++= getClassName(Type.getType(desc)).toList
- }
-
-
- override def visitMethodInsn(opcode: Int, owner: String, name: String, desc: String, itf: Boolean) {
- imports ++= internalNameToClassName(owner)
- imports ++= Type.getArgumentTypes(desc).flatMap(getClassName)
- imports ++= getClassName(Type.getReturnType(desc))
- }
-
- override def visitFieldInsn(opcode: Int, owner: String, name: String, desc: String) {
- imports ++= internalNameToClassName(owner) ++ getClassName(Type.getType(desc)).toList
-
- }
-
- override def visitTypeInsn(opcode: Int, `type` : String) {
- imports ++= internalNameToClassName(`type`)
- }
-
- override def visitTryCatchBlock(start: Label, end: Label, handler: Label, `type` : String) {
- if (`type` != null) //null means finally block
- imports ++= internalNameToClassName(`type`)
- }
-
- override def visitLocalVariable(name: String, desc: String, signature: String, start: Label, end: Label, index: Int) {
- imports += Type.getType(desc).getClassName
- }
-
- override def visitLdcInsn(constant: AnyRef) {
- constant match {
- case typeConstant: Type => imports ++= getClassName(typeConstant)
- case _ =>
- }
- }
-
- override def visitInvokeDynamicInsn(name: String, desc: String, bootstrapMethod: Handle, bootstrapMethodArgs: AnyRef*) {
- bootstrapMethodArgs.foreach {
- case typeConstant: Type =>
- imports ++= getClassName(typeConstant)
- case handle: Handle =>
- imports ++= internalNameToClassName(handle.getOwner)
- imports ++= Type.getArgumentTypes(desc).flatMap(getClassName)
- case _ : Number =>
- case _ : String =>
- case other => throw new AssertionError(s"Unexpected type ${other.getClass} with value '$other'")
- }
- }
-
- override def visitMaxs(maxStack: Int, maxLocals: Int) {}
- override def visitLineNumber(line: Int, start: Label) {}
- //only for debugging
- override def visitLookupSwitchInsn(dflt: Label, keys: Array[Int], labels: Array[Label]) {}
-
-
- override def visitTableSwitchInsn(min: Int, max: Int, dflt: Label, labels: Label*): Unit = super.visitTableSwitchInsn(min, max, dflt, labels: _*)
- override def visitIincInsn(`var` : Int, increment: Int) {}
- override def visitLabel(label: Label) {}
- override def visitJumpInsn(opcode: Int, label: Label) {}
- override def visitVarInsn(opcode: Int, `var` : Int) {}
- override def visitIntInsn(opcode: Int, operand: Int) {}
- override def visitInsn(opcode: Int) {}
- override def visitFrame(`type` : Int, nLocal: Int, local: Array[AnyRef], nStack: Int, stack: Array[AnyRef]) {}
- override def visitCode() {}
-}
-
-
-
-
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeSignatureVisitor.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeSignatureVisitor.scala
deleted file mode 100644
index 5bb8304cf1e..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeSignatureVisitor.scala
+++ /dev/null
@@ -1,68 +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.container.plugin.classanalysis
-
-import org.objectweb.asm.Opcodes
-import org.objectweb.asm.signature.{SignatureReader, SignatureVisitor}
-
-
-/**
- * @author tonytv
- */
-
-private class AnalyzeSignatureVisitor(val analyzeClassVisitor: AnalyzeClassVisitor)
- extends SignatureVisitor(Opcodes.ASM6)
- with SubVisitorTrait {
-
-
- override def visitEnd(): Unit = super.visitEnd()
-
- override def visitClassType(className: String) {
- imports ++= internalNameToClassName(className)
- }
-
- override def visitFormalTypeParameter(name: String) {}
-
- override def visitClassBound() = this
-
- override def visitInterfaceBound() = this
-
- override def visitSuperclass() = this
-
- override def visitInterface() = this
-
- override def visitParameterType() = this
-
- override def visitReturnType() = this
-
- override def visitExceptionType() = this
-
- override def visitBaseType(descriptor: Char) {}
-
- override def visitTypeVariable(name: String) {}
-
- override def visitArrayType() = this
-
- override def visitInnerClassType(name: String) {}
-
- override def visitTypeArgument() {}
-
- override def visitTypeArgument(wildcard: Char) = this
-}
-
-
-object AnalyzeSignatureVisitor {
- def analyzeClass(signature: String, analyzeClassVisitor: AnalyzeClassVisitor) {
- if (signature != null) {
- new SignatureReader(signature).accept(new AnalyzeSignatureVisitor(analyzeClassVisitor))
- }
- }
-
- def analyzeMethod(signature: String, analyzeClassVisitor: AnalyzeClassVisitor) {
- analyzeClass(signature, analyzeClassVisitor)
- }
-
- def analyzeField(signature: String, analyzeClassVisitor: AnalyzeClassVisitor) {
- if (signature != null)
- new SignatureReader(signature).acceptType(new AnalyzeSignatureVisitor(analyzeClassVisitor))
- }
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnnotationVisitorTrait.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnnotationVisitorTrait.scala
deleted file mode 100644
index 0bf6ee4a6b4..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnnotationVisitorTrait.scala
+++ /dev/null
@@ -1,39 +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.container.plugin.classanalysis
-
-import org.objectweb.asm.{Opcodes, AnnotationVisitor, Type}
-
-/**
- * Picks up classes used in annotations.
- * @author tonytv
- */
-private trait AnnotationVisitorTrait {
- protected val imports: ImportsSet
-
- def visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor = {
- imports ++= getClassName(Type.getType(desc)).toList
-
- visitAnnotationDefault()
- }
-
- def visitAnnotationDefault(): AnnotationVisitor =
- new AnnotationVisitor(Opcodes.ASM6) {
- override def visit(name: String, value: AnyRef) {}
-
- override def visitEnum(name: String, desc: String, value: String) {
- imports ++= getClassName(Type.getType(desc)).toList
- }
-
- override def visitArray(name: String): AnnotationVisitor = this
-
- override def visitAnnotation(name: String, desc: String): AnnotationVisitor = {
- imports ++= getClassName(Type.getType(desc)).toList
- this
- }
-
- override def visitEnd() {}
- }
-
- def visitParameterAnnotation(parameter: Int, desc: String, visible: Boolean): AnnotationVisitor =
- visitAnnotation(desc, visible)
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AttributeVisitorTrait.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AttributeVisitorTrait.scala
deleted file mode 100644
index 454bea99f29..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AttributeVisitorTrait.scala
+++ /dev/null
@@ -1,15 +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.container.plugin.classanalysis
-
-import org.objectweb.asm.{Type, Attribute}
-
-/**
- * @author tonytv
- */
-private trait AttributeVisitorTrait {
- protected val imports: ImportsSet
-
- def visitAttribute(attribute: Attribute) {
- imports ++= getClassName(Type.getObjectType(attribute.`type`)).toList
- }
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/ClassFileMetaData.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/ClassFileMetaData.scala
deleted file mode 100644
index fb395749895..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/ClassFileMetaData.scala
+++ /dev/null
@@ -1,10 +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.container.plugin.classanalysis
-
-/**
- * The result of analyzing a .class file.
- * @author tonytv
- */
-sealed case class ClassFileMetaData(name:String,
- referencedClasses : Set[String],
- exportPackage : Option[ExportPackageAnnotation])
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/ExportPackageAnnotation.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/ExportPackageAnnotation.scala
deleted file mode 100644
index f9dc459fcc7..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/ExportPackageAnnotation.scala
+++ /dev/null
@@ -1,24 +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.container.plugin.classanalysis
-
-import com.yahoo.container.plugin.util.Strings
-
-/**
- * @author tonytv
- */
-case class ExportPackageAnnotation(major: Int, minor: Int, micro: Int, qualifier: String) {
- requireNonNegative(major, "major")
- requireNonNegative(minor, "minor")
- requireNonNegative(micro, "micro")
- require(qualifier.matches("""(\p{Alpha}|\p{Digit}|_|-)*"""),
- exportPackageError("qualifier must follow the format (alpha|digit|'_'|'-')* but was '%s'.".format(qualifier)))
-
-
- private def requireNonNegative(i: Int, fieldName: String) {
- require(i >= 0, exportPackageError("%s must be non-negative but was %d.".format(fieldName, i)))
- }
-
- private def exportPackageError(s: String) = "ExportPackage anntotation: " + s
-
- def osgiVersion : String = (List(major, minor, micro) ++ Strings.noneIfEmpty(qualifier)).mkString(".")
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/PackageTally.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/PackageTally.scala
deleted file mode 100644
index 2d2460cc9fd..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/PackageTally.scala
+++ /dev/null
@@ -1,46 +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.container.plugin.classanalysis
-
-import com.yahoo.container.plugin.util.Maps
-
-/**
- *
- * @author tonytv
- */
-final class PackageTally (private val definedPackagesMap : Map[String, Option[ExportPackageAnnotation]],
- referencedPackagesUnfiltered : Set[String]) {
-
- val referencedPackages = referencedPackagesUnfiltered diff definedPackages
-
- def definedPackages = definedPackagesMap.keySet
-
- def exportedPackages = definedPackagesMap collect { case (name, Some(export)) => (name, export) }
-
- /**
- * Represents the classes for two package tallies that are deployed as a single unit.
- *
- * ExportPackageAnnotations from this has precedence over the other.
- */
- def combine(other: PackageTally): PackageTally = {
- new PackageTally(
- Maps.combine(definedPackagesMap, other.definedPackagesMap)(_ orElse _),
- referencedPackages ++ other.referencedPackages)
- }
-}
-
-
-object PackageTally {
- def fromAnalyzedClassFiles(analyzedClassFiles : Seq[ClassFileMetaData]) : PackageTally = {
- combine(
- for (metaData <- analyzedClassFiles)
- yield {
- new PackageTally(
- Map(Packages.packageName(metaData.name) -> metaData.exportPackage),
- metaData.referencedClasses.map(Packages.packageName))
- })
- }
-
- def combine(packageTallies : Iterable[PackageTally]) : PackageTally = (empty /: packageTallies)(_.combine(_))
-
- val empty : PackageTally = new PackageTally(Map(), Set())
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/Packages.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/Packages.scala
deleted file mode 100644
index 517ef2626f9..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/Packages.scala
+++ /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.container.plugin.classanalysis
-
-/**
- * Utility methods related to packages.
- * @author tonytv
- */
-object Packages {
- case class PackageMetaData(definedPackages: Set[String], referencedExternalPackages: Set[String])
-
- def packageName(fullClassName: String) = {
- def nullIfNotFound(index : Int) = if (index == -1) 0 else index
-
- fullClassName.substring(0, nullIfNotFound(fullClassName.lastIndexOf(".")))
- }
-
-
-
- def analyzePackages(allClasses: Seq[ClassFileMetaData]): PackageMetaData = {
- val (definedPackages, referencedClasses) =
- (for (classMetaData <- allClasses)
- yield (packageName(classMetaData.name), classMetaData.referencedClasses.map(packageName))).
- unzip
-
- PackageMetaData(definedPackages.toSet, referencedClasses.flatten.toSet diff definedPackages.toSet)
- }
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/SubVisitorTrait.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/SubVisitorTrait.scala
deleted file mode 100644
index 06ccfa7b88c..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/SubVisitorTrait.scala
+++ /dev/null
@@ -1,19 +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.container.plugin.classanalysis
-
-import collection.mutable
-
-/**
- * A visitor that's run for sub construct of a class
- * and forwards all its imports the the owning ClassVisitor at the end.
- * @author tonytv
- */
-private trait SubVisitorTrait {
- val analyzeClassVisitor : AnalyzeClassVisitor
-
- val imports : ImportsSet = mutable.Set()
-
- def visitEnd() {
- analyzeClassVisitor.addImports(imports)
- }
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/package.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/package.scala
deleted file mode 100644
index 631884c58e3..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/package.scala
+++ /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.container.plugin
-
-import org.objectweb.asm.Type
-import collection.mutable
-
-package object classanalysis {
- type ImportsSet = mutable.Set[String]
-
- def internalNameToClassName(internalClassName: String) : Option[String] = {
- internalClassName match {
- case null => None
- case _ => getClassName(Type.getObjectType(internalClassName))
- }
- }
-
- def getClassName(aType: Type): Option[String] = {
- import Type._
-
- aType.getSort match {
- case ARRAY => getClassName(aType.getElementType)
- case OBJECT => Some(aType.getClassName)
- case _ => None
- }
- }
-}
-
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/mojo/Artifacts.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/mojo/Artifacts.scala
deleted file mode 100644
index 8370f8c615e..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/mojo/Artifacts.scala
+++ /dev/null
@@ -1,29 +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.container.plugin.mojo
-
-import org.apache.maven.artifact.Artifact
-import org.apache.maven.project.MavenProject
-
-import scala.collection.JavaConverters._
-
-/**
- * @author tonytv
- */
-object Artifacts {
- def getArtifacts(project : MavenProject) = {
- type artifactSet = java.util.Set[Artifact]
- val artifacts = project.getArtifacts.asInstanceOf[artifactSet].asScala.groupBy(_.getScope)
-
- def isTypeJar(artifact : Artifact) = artifact.getType == "jar"
- def getByScope(scope: String) =
- artifacts.getOrElse(scope, Iterable.empty).partition(isTypeJar)
-
-
- val (jarArtifactsToInclude, nonJarArtifactsToInclude) = getByScope(Artifact.SCOPE_COMPILE)
- val (jarArtifactsProvided, nonJarArtifactsProvided) = getByScope(Artifact.SCOPE_PROVIDED)
-
- (jarArtifactsToInclude, jarArtifactsProvided, nonJarArtifactsToInclude ++ nonJarArtifactsProvided)
- }
-
- def getArtifactsToInclude(project: MavenProject) = getArtifacts(project)._1
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/mojo/AssembleContainerPluginMojo.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/mojo/AssembleContainerPluginMojo.scala
deleted file mode 100644
index 4104e349208..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/mojo/AssembleContainerPluginMojo.scala
+++ /dev/null
@@ -1,116 +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.container.plugin.mojo
-
-import java.io.File
-import java.nio.channels.Channels
-import java.util.jar.JarFile
-import java.util.zip.ZipEntry
-
-import com.yahoo.container.plugin.util.{Files, JarFiles}
-import org.apache.maven.archiver.{MavenArchiveConfiguration, MavenArchiver}
-import org.apache.maven.execution.MavenSession
-import org.apache.maven.plugin.AbstractMojo
-import org.apache.maven.plugins.annotations.{Mojo, Parameter, ResolutionScope}
-import org.apache.maven.project.MavenProject
-import org.codehaus.plexus.archiver.jar.JarArchiver
-
-import scala.collection.JavaConverters._
-
-/**
- * @author tonytv
- */
-@Mojo(name = "assemble-container-plugin", requiresDependencyResolution = ResolutionScope.COMPILE, threadSafe = true)
-class AssembleContainerPluginMojo extends AbstractMojo {
- object withDependencies
- object withoutDependencies
-
- @Parameter(defaultValue = "${project}")
- var project: MavenProject = null
-
- @Parameter(defaultValue = "${session}", readonly = true, required = true)
- var session: MavenSession = null
-
- @Parameter
- var archiveConfiguration: MavenArchiveConfiguration = new MavenArchiveConfiguration
-
- @Parameter(alias = "UseCommonAssemblyIds", defaultValue = "false")
- var useCommonAssemblyIds: Boolean = false
-
-
- def execute() {
- val jarSuffixes =
- if (useCommonAssemblyIds) Map(withoutDependencies -> ".jar", withDependencies -> "-jar-with-dependencies.jar")
- else Map(withoutDependencies -> "-without-dependencies.jar", withDependencies -> "-deploy.jar")
-
- val jarFiles = jarSuffixes mapValues jarFileInBuildDirectory(build.getFinalName)
-
- //force recreating the archive
- archiveConfiguration.setForced(true)
- archiveConfiguration.setManifestFile(new File(new File(project.getBuild.getOutputDirectory), JarFile.MANIFEST_NAME))
-
- val jarWithoutDependencies = new JarArchiver()
- addClassesDirectory(jarWithoutDependencies)
- createArchive(jarFiles(withoutDependencies), jarWithoutDependencies)
- project.getArtifact.setFile(jarFiles(withoutDependencies))
-
- val jarWithDependencies = new JarArchiver()
- addClassesDirectory(jarWithDependencies)
- addDependencies(jarWithDependencies)
- createArchive(jarFiles(withDependencies), jarWithDependencies)
- }
-
- private def jarFileInBuildDirectory(name: String)(jarSuffix: String) = {
- new File(build.getDirectory, name + jarSuffix)
- }
-
- private def addClassesDirectory(jarArchiver: JarArchiver) {
- val classesDirectory = new File(build.getOutputDirectory)
- if (classesDirectory.isDirectory) {
- jarArchiver.addDirectory(classesDirectory)
- }
- }
-
- private def createArchive(jarFile: File, jarArchiver: JarArchiver) {
- val mavenArchiver = new MavenArchiver
- mavenArchiver.setArchiver(jarArchiver)
- mavenArchiver.setOutputFile(jarFile)
- mavenArchiver.createArchive(session, project, archiveConfiguration)
- }
-
- private def addDependencies(jarArchiver: JarArchiver) {
- Artifacts.getArtifactsToInclude(project).foreach { artifact =>
- if (artifact.getType == "jar") {
- jarArchiver.addFile(artifact.getFile, "dependencies/" + artifact.getFile.getName)
- copyConfigDefinitions(artifact.getFile, jarArchiver)
- }
- else
- getLog.warn("Unkown artifact type " + artifact.getType)
- }
- }
-
- private def copyConfigDefinitions(file: File, jarArchiver: JarArchiver) {
- JarFiles.withJarFile(file) { jarFile =>
- for {
- entry <- jarFile.entries().asScala
- name = entry.getName
- if name.startsWith("configdefinitions/") && name.endsWith(".def")
-
- } copyConfigDefinition(jarFile, entry, jarArchiver)
- }
- }
-
- private def copyConfigDefinition(jarFile: JarFile, entry: ZipEntry, jarArchiver: JarArchiver) {
- JarFiles.withInputStream(jarFile, entry) { input =>
- val defPath = entry.getName.replace("/", File.separator)
- val destinationFile = new File(project.getBuild.getOutputDirectory, defPath)
- destinationFile.getParentFile.mkdirs()
-
- Files.withFileOutputStream(destinationFile) { output =>
- output.getChannel.transferFrom(Channels.newChannel(input), 0, Long.MaxValue)
- }
- jarArchiver.addFile(destinationFile, entry.getName)
- }
- }
-
- private def build = project.getBuild
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/mojo/GenerateBundleClassPathMappingsMojo.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/mojo/GenerateBundleClassPathMappingsMojo.scala
deleted file mode 100644
index 2bd146a3920..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/mojo/GenerateBundleClassPathMappingsMojo.scala
+++ /dev/null
@@ -1,96 +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.container.plugin.mojo
-
-import java.io.File
-import java.nio.file.Paths
-
-import com.google.common.base.Preconditions
-import com.yahoo.container.plugin.bundle.AnalyzeBundle
-import com.yahoo.container.plugin.osgi.ProjectBundleClassPaths
-import com.yahoo.container.plugin.osgi.ProjectBundleClassPaths.BundleClasspathMapping
-import org.apache.maven.artifact.Artifact
-import org.apache.maven.plugin.AbstractMojo
-import org.apache.maven.plugins.annotations.{Mojo, Parameter, ResolutionScope}
-import org.apache.maven.project.MavenProject
-
-import scala.collection.JavaConverters._
-
-
-
-/**
- * Generates mapping from Bundle-SymbolicName to classpath elements, e.g
- * myBundle -> List(.m2/repository/com/mylib/Mylib.jar, myBundleProject/target/classes)
- * The mapping in stored in a json file.
- * @author tonytv
- */
-@Mojo(name = "generate-bundle-classpath-mappings", requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, threadSafe = true)
-class GenerateBundleClassPathMappingsMojo extends AbstractMojo {
- @Parameter(defaultValue = "${project}")
- private var project: MavenProject = null
-
- //TODO: Combine with com.yahoo.container.plugin.mojo.GenerateOsgiManifestMojo.bundleSymbolicName
- @Parameter(alias = "Bundle-SymbolicName", defaultValue = "${project.artifactId}")
- private var bundleSymbolicName: String = null
-
-
- /* Sample output -- target/test-classes/bundle-plugin.bundle-classpath-mappings.json
- {
- "mainBundle": {
- "bundleSymbolicName": "bundle-plugin-test",
- "classPathElements": [
- "/Users/tonyv/Repos/vespa/bundle-plugin-test/target/classes",
- "/Users/tonyv/.m2/repository/com/yahoo/vespa/jrt/6-SNAPSHOT/jrt-6-SNAPSHOT.jar",
- "/Users/tonyv/.m2/repository/com/yahoo/vespa/annotations/6-SNAPSHOT/annotations-6-SNAPSHOT.jar"
- ]
- },
- "providedDependencies": [
- {
- "bundleSymbolicName": "jrt",
- "classPathElements": [
- "/Users/tonyv/.m2/repository/com/yahoo/vespa/jrt/6-SNAPSHOT/jrt-6-SNAPSHOT.jar"
- ]
- }
- ]
- }
- */
- override def execute(): Unit = {
- Preconditions.checkNotNull(bundleSymbolicName)
-
- val (embeddedArtifacts, providedJarArtifacts, _) = asLists(Artifacts.getArtifacts(project))
-
- val embeddedArtifactsFiles = embeddedArtifacts.map(_.getFile)
-
- val classPathElements = (outputDirectory +: embeddedArtifactsFiles).map(_.getAbsolutePath)
-
- val classPathMappings = new ProjectBundleClassPaths(
- new BundleClasspathMapping(bundleSymbolicName, classPathElements.asJava),
- providedJarArtifacts.flatMap(createDependencyClasspathMapping).asJava)
-
- ProjectBundleClassPaths.save(
- testOutputPath.resolve(ProjectBundleClassPaths.CLASSPATH_MAPPINGS_FILENAME),
- classPathMappings)
- }
-
- private def outputDirectory = new File(project.getBuild.getOutputDirectory)
- private def testOutputPath = Paths.get(project.getBuild.getTestOutputDirectory)
-
- /* TODO:
- * 1) add the dependencies of the artifact in the future(i.e. dependencies of dependencies)
- * or
- * 2) obtain bundles with embedded dependencies from the maven repository,
- * and support loading classes from the nested jar files in those bundles.
- */
- def createDependencyClasspathMapping(artifact: Artifact): Option[BundleClasspathMapping] = {
- for (bundleSymbolicName <- bundleSymbolicNameForArtifact(artifact))
- yield new BundleClasspathMapping(bundleSymbolicName, List(artifact.getFile.getAbsolutePath).asJava)
- }
-
- def bundleSymbolicNameForArtifact(artifact: Artifact): Option[String] = {
- if (artifact.getFile.getName.endsWith(".jar")) AnalyzeBundle.bundleSymbolicName(artifact.getFile)
- else Some(artifact.getArtifactId) //Not the best heuristic. The other alternatives are parsing the pom file or
- //storing information in target/classes when building the provided bundles.
- }
-
- def asLists[A](tuple: (Iterable[A], Iterable[A], Iterable[A])) =
- (tuple._1.toList, tuple._2.toList, tuple._3.toList)
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/mojo/GenerateOsgiManifestMojo.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/mojo/GenerateOsgiManifestMojo.scala
deleted file mode 100644
index 67ce45ed7c6..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/mojo/GenerateOsgiManifestMojo.scala
+++ /dev/null
@@ -1,286 +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.container.plugin.mojo
-
-import java.io.File
-import java.util.jar.{Attributes, JarEntry, JarFile}
-import java.util.regex.Pattern
-
-import com.yahoo.container.plugin.bundle.AnalyzeBundle
-import com.yahoo.container.plugin.classanalysis.{Analyze, ExportPackageAnnotation, PackageTally}
-import com.yahoo.container.plugin.mojo.GenerateOsgiManifestMojo._
-import com.yahoo.container.plugin.osgi.ExportPackages
-import com.yahoo.container.plugin.osgi.ExportPackages.Export
-import com.yahoo.container.plugin.osgi.ImportPackages.Import
-import com.yahoo.container.plugin.osgi.{ExportPackageParser, ExportPackages, ImportPackages}
-import com.yahoo.container.plugin.util.Files.allDescendantFiles
-import com.yahoo.container.plugin.util.IO.withFileOutputStream
-import com.yahoo.container.plugin.util.Iteration.toStream
-import com.yahoo.container.plugin.util.JarFiles.{withInputStream, withJarFile}
-import com.yahoo.container.plugin.util.Strings
-import org.apache.maven.artifact.Artifact
-import org.apache.maven.plugin.{AbstractMojo, MojoExecutionException, MojoFailureException}
-import org.apache.maven.plugins.annotations.{Mojo, Parameter, ResolutionScope}
-import org.apache.maven.project.MavenProject
-
-import scala.collection.immutable.Map
-
-
-/**
- * @author tonytv
- */
-@Mojo(name = "generate-osgi-manifest", requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true)
-class GenerateOsgiManifestMojo extends AbstractMojo {
-
- @Parameter(defaultValue = "${project}")
- var project: MavenProject = null
-
- @Parameter
- var discApplicationClass: String = null
-
- @Parameter
- var discPreInstallBundle: String = null
-
- @Parameter(alias = "Bundle-Version", defaultValue = "${project.version}")
- var bundleVersion: String = null
-
- @Parameter(alias = "Bundle-SymbolicName", defaultValue = "${project.artifactId}")
- var bundleSymbolicName: String = null
-
- @Parameter(alias = "Bundle-Activator")
- var bundleActivator: String = null
-
- @Parameter(alias = "X-JDisc-Privileged-Activator")
- var jdiscPrivilegedActivator: String = null
-
- @Parameter(alias = "X-Config-Models")
- var configModels: String = null
-
- @Parameter(alias = "Import-Package")
- var importPackage: String = null
-
- @Parameter(alias = "WebInfUrl")
- var webInfUrl: String = null
-
- @Parameter(alias = "Main-Class")
- var mainClass: String = null
-
- @Parameter(alias = "X-Jersey-Binding")
- var jerseyBinding: String = null
-
- case class PackageInfo(name : String, exportAnnotation : Option[ExportPackageAnnotation])
-
- def execute() {
- try {
- val (jarArtifactsToInclude, jarArtifactsProvided, nonJarArtifacts) = Artifacts.getArtifacts(project)
- warnOnUnsupportedArtifacts(nonJarArtifacts)
-
- val publicPackagesFromProvidedJars = AnalyzeBundle.publicPackagesAggregated(jarArtifactsProvided.map(_.getFile))
- val includedJarPackageTally = definedPackages(jarArtifactsToInclude)
-
- val projectPackageTally = analyzeProjectClasses()
-
- val pluginPackageTally = projectPackageTally.combine(includedJarPackageTally)
-
- warnIfPackagesDefinedOverlapsGlobalPackages(projectPackageTally.definedPackages ++ includedJarPackageTally.definedPackages,
- publicPackagesFromProvidedJars.globals)
-
- if (getLog.isDebugEnabled) {
- getLog.debug("Referenced packages = " + pluginPackageTally.referencedPackages)
- getLog.debug("Defined packages = " + pluginPackageTally.definedPackages)
- getLog.debug("Exported packages of dependencies = " +
- publicPackagesFromProvidedJars.exports.map(e => (e.packageNames, e.version getOrElse "")).toSet )
- }
-
- val calculatedImports = ImportPackages.calculateImports(
- pluginPackageTally.referencedPackages,
- pluginPackageTally.definedPackages,
- ExportPackages.exportsByPackageName(publicPackagesFromProvidedJars.exports))
-
- val manualImports = emptyToNone(importPackage) map getManualImports getOrElse Map()
-
- createManifestFile(new File(project.getBuild.getOutputDirectory),
- manifestContent(
- project,
- jarArtifactsToInclude,
- manualImports,
- (calculatedImports -- manualImports.keys).values.toSet,
- pluginPackageTally))
-
- } catch {
- case e: MojoFailureException => throw e
- case e: MojoExecutionException => throw e
- case e: Exception => throw new MojoExecutionException("Failed generating osgi manifest.", e)
- }
- }
-
- //TODO: Tell which dependency overlaps
- private def warnIfPackagesDefinedOverlapsGlobalPackages(internalPackages: Set[String], globalPackages: List[String]) {
- val overlap = internalPackages intersect globalPackages.toSet
- if (overlap.nonEmpty)
- throw new MojoExecutionException(
- "The following packages are both global and included in the bundle:\n%s".format(overlap map (" " + _) mkString ("\n")))
- }
-
-
- def osgiExportPackages(exportedPackages: Map[String, ExportPackageAnnotation]): Iterable[String] = {
- for ((name, annotation) <- exportedPackages)
- yield name + ";version=" + annotation.osgiVersion
- }
-
- def trimWhitespace(lines: Option[String]): String = {
- lines.getOrElse("").split(",").map(_.trim).mkString(",")
- }
-
- def manifestContent(project: MavenProject, jarArtifactsToInclude: Traversable[Artifact],
- manualImports: Map[String, Option[String]], imports : Set[Import],
- pluginPackageTally : PackageTally) = {
- Map[String, String](
- "Created-By" -> "vespa container maven plugin",
- "Bundle-ManifestVersion" -> "2",
- "Bundle-Name" -> project.getName,
- "Bundle-SymbolicName" -> bundleSymbolicName,
- "Bundle-Version" -> asBundleVersion(bundleVersion),
- "Bundle-Vendor" -> "Yahoo!",
- "Bundle-ClassPath" -> bundleClassPath(jarArtifactsToInclude),
- "Bundle-Activator" -> bundleActivator,
- "X-JDisc-Privileged-Activator" -> jdiscPrivilegedActivator,
- "Main-Class" -> mainClass,
- "X-JDisc-Application" -> discApplicationClass,
- "X-JDisc-Preinstall-Bundle" -> trimWhitespace(Option(discPreInstallBundle)),
- "X-Config-Models" -> configModels,
- "X-Jersey-Binding" -> jerseyBinding,
- "WebInfUrl" -> webInfUrl,
- "Import-Package" -> ((manualImports map asOsgiImport) ++ (imports map {_.asOsgiImport})).toList.sorted.mkString(","),
- "Export-Package" -> osgiExportPackages(pluginPackageTally.exportedPackages).toList.sorted.mkString(","))
- .filterNot { case (key, value) => value == null || value.isEmpty }
-
- }
-
- def asOsgiImport(importSpec: (String, Option[String])) = importSpec match {
- case (packageName, Some(version)) => packageName + ";version=" + quote(version)
- case (packageName, None) => packageName
- }
-
- def quote(s: String) = '"' + s + '"'
-
- def createManifestFile(outputDirectory: File, manifestContent: Map[String, String]) {
- val manifest = toManifest(manifestContent)
-
- withFileOutputStream(new File(outputDirectory, JarFile.MANIFEST_NAME)) {
- outputStream =>
- manifest.write(outputStream)
- }
- }
-
- def toManifest(manifestContent: Map[String, String]) = {
- val manifest = new java.util.jar.Manifest
- val mainAttributes = manifest.getMainAttributes
-
- mainAttributes.put(Attributes.Name.MANIFEST_VERSION, "1.0")
- for ((key, value) <- manifestContent)
- mainAttributes.putValue(key, value)
-
- manifest
- }
-
- private def bundleClassPath(artifactsToInclude : Traversable[Artifact]) =
- ("." +: artifactsToInclude.map(dependencyPath).toList).mkString(",")
-
- private def dependencyPath(artifact : Artifact) =
- "dependencies/" + artifact.getFile.getName
-
- private def asBundleVersion(projectVersion: String) = {
- require(projectVersion != null, "Missing project version.")
-
- val parts = projectVersion.split(Pattern.quote("-"), 2)
- val numericPart = parts.head.split('.').map(Strings.emptyStringTo("0")).padTo(3, "0").toList
-
- val majorMinorMicro = numericPart take 3
- majorMinorMicro.mkString(".")
- }
-
- private def warnOnUnsupportedArtifacts(nonJarArtifacts: Traversable[Artifact]) {
- val unsupportedArtifacts = nonJarArtifacts.toSet.filter(_.getType != "pom")
-
- for (artifact <- unsupportedArtifacts) {
- getLog.warn(s"Unsupported artifact '${artifact.getId}': Type '${artifact.getType}' is not supported. Please file a feature request.")
- }
- }
-
- private def analyzeProjectClasses() : PackageTally = {
- val outputDirectory = new File(project.getBuild.getOutputDirectory)
-
- val analyzedClasses = allDescendantFiles(outputDirectory).filter(file => isClassToAnalyze(file.getName)).
- map(Analyze.analyzeClass)
-
- PackageTally.fromAnalyzedClassFiles(analyzedClasses)
- }
-
- def definedPackages(jarArtifacts: Iterable[Artifact]) : PackageTally = {
- PackageTally.combine(
- for (jarArtifact <- jarArtifacts) yield {
- withJarFile(jarArtifact.getFile) { jarFile =>
- definedPackages(jarFile)
- }
- })
- }
-
- def definedPackages(jarFile: JarFile) = {
- val analyzedClasses =
- for {
- entry <- toStream(jarFile.entries())
- if !entry.isDirectory
- if isClassToAnalyze(entry.getName)
- metaData = analyzeClass(jarFile, entry)
- } yield metaData
-
- PackageTally.fromAnalyzedClassFiles(analyzedClasses)
- }
-
- def analyzeClass(jarFile : JarFile, entry : JarEntry) = {
- try {
- withInputStream(jarFile, entry)(Analyze.analyzeClass)
- } catch {
- case e : Exception =>
- throw new MojoExecutionException(
- "While analyzing the class '%s' in jar file '%s'".format(entry.getName, jarFile.getName),
- e)
- }
- }
-}
-
-object GenerateOsgiManifestMojo {
- def getManualImports(importPackage: String): Map[String, Option[String]] = {
- try {
- (for {
- importDirective <- parseImportPackages(importPackage)
- packageName <- importDirective.packageNames
- } yield packageName -> getVersionThrowOthers(importDirective.parameters)).
- toMap
-
- } catch {
- case e: Exception => throw new RuntimeException("Error in Import-Package:" + importPackage, e)
- }
- }
-
- def getVersionThrowOthers(parameters: List[ExportPackages.Parameter]): Option[String] = {
- parameters match {
- case List() => None
- case List(ExportPackages.Parameter("version", v)) => Some(v)
- case default => throw new RuntimeException("A single, optional version parameter expected, but got " + default)
- }
- }
-
- def parseImportPackages(importPackages: String): List[Export] = {
- ExportPackageParser.parseAll(importPackages) match {
- case ExportPackageParser.NoSuccess(msg, _) => throw new RuntimeException(msg)
- case ExportPackageParser.Success(packages, _) => packages
- }
- }
-
- def isClassToAnalyze(name: String): Boolean =
- name.endsWith(".class") && ! name.endsWith("module-info.class")
-
- def emptyToNone(str: String) =
- Option(str) map {_.trim} filterNot {_.isEmpty}
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/osgi/ExportPackageParser.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/osgi/ExportPackageParser.scala
deleted file mode 100644
index 5cd93e84e87..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/osgi/ExportPackageParser.scala
+++ /dev/null
@@ -1,89 +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.container.plugin.osgi
-
-import scala.util.parsing.combinator.JavaTokenParsers
-import ExportPackages.{Parameter, Export}
-import com.yahoo.container.plugin.util.Extractors.ListOf
-import scala.util.parsing.input.CharSequenceReader
-import scala.annotation.tailrec
-
-/**
- * @author tonytv
- */
-object ExportPackageParser extends JavaTokenParsers {
- val ListOfParameter = new ListOf(classOf[Parameter])
-
-
- def exportPackage = rep1sep(export, ",")
-
- //TODO: remove when fix is in current scala library
- //Fix for https://github.com/scala/scala-parser-combinators/pull/4
- def stringLiteral_fixed: Parser[String] = ("\""+"""([^"\p{Cntrl}\\]|\\[\\'"bfnrt]|\\u[a-fA-F0-9]{4})*+"""+"\"").r
-
- @SuppressWarnings(Array("unchecked"))
- def export : Parser[Export] = packageName ~ opt(";" ~> (parameters | export)) ^^ {
- case (packageName : String) ~ optional => {
- optional match {
- case None => Export(List(packageName.asInstanceOf[String]), List())
- case Some(e: Export) => e.copy(packageNames = packageName +: e.packageNames)
- case Some(ListOfParameter(parameters)) => Export(List(packageName), parameters)
- }
- }
- }
-
- def parameters = rep1sep(parameter, ";")
-
- def parameter = (directive | attribute) ^^ {
- case k ~ v => Parameter(k.toString, v.toString)
- }
-
- def directive = (extended_ <~ ":=") ~ argument
- def attribute = (extended_ <~ "=") ~ argument
-
- def packageName = rep1sep(ident_, ".") ^^ {
- x => x.mkString(".")
- }
-
- def extended = rep1("""\p{Alnum}""".r | "_" | "-" | ".") ^^ {
- _.mkString
- }
-
- def argument = (extended_ | stringLiteral_ | failure("argument expected")) ^^ {
- val quote = '"'.toString
- _.toString.stripPrefix(quote).stripSuffix(quote)
- }
-
- def parseAll(in: CharSequence): ParseResult[List[Export]] = {
- try {
- parseAll(exportPackage, in)
- } catch {
- case e: StackOverflowError =>
- throw new RuntimeException("Failed parsing Export-Package: '''\n" + in + "\n'''", e)
- }
- }
-
- //*** For debugging StackOverflow error **/
- def ident_ = printStackOverflow(ident)("ident")
- def stringLiteral_ = printStackOverflow(stringLiteral_fixed)("stringLiteral_fixed")
- def extended_ = printStackOverflow(extended)("extended")
-
- def printStackOverflow[T](p: => Parser[T])(name: String): Parser[T] = Parser{ in =>
- try {
- p(in)
- } catch {
- case e: StackOverflowError =>
- val input = in match {
- case reader: CharSequenceReader => readerToString(reader)
- case other => other.toString
- }
- println(s"***StackOverflow for $name with input '''$input'''")
- throw e
- }
- }
-
- @tailrec
- def readerToString(reader: CharSequenceReader, current: String = ""): String = {
- if (reader.atEnd) current
- else readerToString(reader.rest, current + reader.first)
- }
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/osgi/ExportPackages.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/osgi/ExportPackages.scala
deleted file mode 100644
index 4a973c0b9b1..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/osgi/ExportPackages.scala
+++ /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.container.plugin.osgi
-
-/**
- * @author tonytv
- */
-object ExportPackages {
-
- case class Export(packageNames: List[String], parameters: List[Parameter]) {
- def version: Option[String] = {
- (for (
- param <- parameters if param.name == "version"
- ) yield param.value).
- headOption
- }
- }
-
- case class Parameter(name: String, value: String)
-
- def exportsByPackageName(exports: Seq[Export]): Map[String, Export] = {
- (for {
- export <- exports.reverse //ensure that earlier exports of a package overrides later exports.
- packageName <- export.packageNames
- } yield packageName -> export).
- toMap
- }
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/osgi/ImportPackages.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/osgi/ImportPackages.scala
deleted file mode 100644
index b6b47b954c0..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/osgi/ImportPackages.scala
+++ /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.container.plugin.osgi
-
-import ExportPackages.Export
-import util.control.Exception
-
-/**
- * @author tonytv
- */
-object ImportPackages {
- case class Import(packageName : String, version : Option[String]) {
- val majorMinorMicroVersion = Exception.handling(classOf[NumberFormatException]).
- by( e => throw new IllegalArgumentException(
- "Invalid version number '%s' for package '%s'.".format(version.get, packageName), e)) {
-
- version map { _.split('.') take 3 map {_.toInt} }
- }
-
- def majorVersion = majorMinorMicroVersion map { _.head }
-
- // TODO: Detecting guava packages should be based on Bundle-SymbolicName, not package name.
- def importVersionRange = {
- def upperLimit =
- if (isGuavaPackage) InfiniteVersion // guava increases major version for each release
- else majorVersion.get + 1
-
- version map (v => "[%s,%s)".format(majorMinorMicroVersion.get.mkString("."), upperLimit))
- }
-
- def isGuavaPackage = packageName.equals(GuavaBasePackage) || packageName.startsWith(GuavaBasePackage + ".")
-
- def asOsgiImport = packageName + (importVersionRange map {";version=\"" + _ + '"'} getOrElse(""))
- }
-
-
- val GuavaBasePackage = "com.google.common"
- val InfiniteVersion = 99999
-
- def calculateImports(referencedPackages : Set[String],
- implementedPackages : Set[String],
- exportedPackages : Map[String, Export]) : Map[String, Import] = {
- (for {
- undefinedPackage <- referencedPackages diff implementedPackages
- export <- exportedPackages.get(undefinedPackage)
- } yield undefinedPackage -> Import(undefinedPackage, version(export)))(
- collection.breakOut)
- }
-
- def version(export: Export): Option[String] =
- export.parameters.find(_.name == "version").map(_.value)
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/Extractors.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/Extractors.scala
deleted file mode 100644
index 83f23f905fb..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/Extractors.scala
+++ /dev/null
@@ -1,17 +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.container.plugin.util
-
-/**
-* @author tonytv
-*/
-object Extractors {
- class ListOf[C](val c : Class[C]) {
- def unapply[X](xs : X) : Option[List[C]] = {
- xs match {
- case x :: xr if c.isInstance(x) => unapply(xr) map ( c.cast(x) :: _)
- case Nil => Some(Nil)
- case _ => None
- }
- }
- }
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/Files.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/Files.scala
deleted file mode 100644
index d32e57784da..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/Files.scala
+++ /dev/null
@@ -1,23 +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.container.plugin.util
-
-import java.io.{FileOutputStream, File}
-import com.yahoo.container.plugin.util.IO._
-
-/**
- * @author tonytv
- */
-object Files {
- def allDescendantFiles(file: File): Stream[File] = {
- if (file.isFile)
- Stream(file)
- else if (file.isDirectory)
- file.listFiles().toStream.map(allDescendantFiles).flatten
- else
- Stream.empty
- }
-
- def withFileOutputStream[T](file: File)(f: FileOutputStream => T): T = {
- using(new FileOutputStream(file), readOnly = false)(f)
- }
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/IO.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/IO.scala
deleted file mode 100644
index 90f3eb3e7fd..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/IO.scala
+++ /dev/null
@@ -1,46 +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.container.plugin.util
-
-import java.io.{Closeable, FileOutputStream, OutputStream, FileInputStream, File}
-import util.control.Exception
-import scala.Either
-
-/** Utility methods relating to IO
- * @author tonytv
- */
-object IO {
- def withFileInputStream[T](file : File)(f : FileInputStream => T) = {
- using(new FileInputStream(file), readOnly = true)(f)
- }
-
- /**
- * Creates a new file and all it's parent directories,
- * and provides a file output stream to the file.
- *
- * Exceptions from closing have priority over exceptions from f.
- */
- def withFileOutputStream[T](file: File)(f: OutputStream => T) {
- makeDirectoriesRecursive(file.getParentFile)
- using(new FileOutputStream(file), readOnly = false )(f)
- }
-
- def makeDirectoriesRecursive(file: File) {
- if (!file.mkdirs() && !file.isDirectory) {
- throw new RuntimeException("Could not create directory " + file.getPath)
- }
- }
-
- def using[RESOURCE <: Closeable, T](resource : RESOURCE, readOnly : Boolean)(f : RESOURCE => T) : T = {
- def catchPromiscuously = Exception.catchingPromiscuously(classOf[Throwable])
-
- val resultOrException = catchPromiscuously either f(resource)
- val closeException = Exception.allCatch either resource.close()
-
- prioritizeFirstException(
- resultOrException,
- if (readOnly) Right(()) else closeException) fold (throw _, identity)
- }
-
- private def prioritizeFirstException[T](first: Either[Throwable, T], second: Either[Throwable, Unit]) =
- first fold ( Left(_), value => second fold ( Left(_), _ => Right(value) ) )
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/Iteration.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/Iteration.scala
deleted file mode 100644
index fa50f5cdd17..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/Iteration.scala
+++ /dev/null
@@ -1,14 +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.container.plugin.util
-
- /**
- * @author tonytv
- */
-object Iteration {
- def toStream[T](enumeration: java.util.Enumeration[T]): Stream[T] = {
- if (enumeration.hasMoreElements)
- Stream.cons(enumeration.nextElement(), toStream(enumeration))
- else
- Stream.Empty
- }
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/JarFiles.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/JarFiles.scala
deleted file mode 100644
index d7578f2b338..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/JarFiles.scala
+++ /dev/null
@@ -1,24 +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.container.plugin.util
-
-import java.util.jar.JarFile
-import java.util.zip.{ZipFile, ZipEntry}
-import IO.using
-import java.io.{Closeable, InputStream, File}
-
-/**
- * @author tonytv
- */
-object JarFiles {
- def withJarFile[T](file : File)(f : JarFile => T ) : T =
- using(new JarFile(file) with Closeable, readOnly = true)(f)
-
- def withInputStream[T](zipFile: ZipFile, zipEntry: ZipEntry)(f: InputStream => T): T =
- using(zipFile.getInputStream(zipEntry), readOnly = true)(f)
-
- def getManifest(jarFile : File) : Option[java.util.jar.Manifest] = {
- withJarFile(jarFile) { jar =>
- Option(jar.getManifest)
- }
- }
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/Maps.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/Maps.scala
deleted file mode 100644
index cafc8431b0e..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/Maps.scala
+++ /dev/null
@@ -1,19 +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.container.plugin.util
-
-import collection.mutable.MultiMap
-
-/**
- * @author tonytv
- */
-object Maps {
- def combine[K, V](map1 : Map[K, V], map2 : Map[K, V])(f : (V, V) => V) : Map[K, V] = {
- def logicError : V = throw new RuntimeException("Logic error.")
- def combineValues(key : K) = key -> f(map1.getOrElse(key, logicError), map2.getOrElse(key, logicError))
-
- val keysInBoth = map1.keySet intersect map2.keySet
- def notInBoth = !keysInBoth.contains(_ : K)
-
- map1.filterKeys(notInBoth) ++ map2.filterKeys(notInBoth) ++ keysInBoth.map(combineValues)
- }
-}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/Strings.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/Strings.scala
deleted file mode 100644
index 5f854fda944..00000000000
--- a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/util/Strings.scala
+++ /dev/null
@@ -1,14 +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.container.plugin.util
-
- /**
- * @author tonytv
- */
-object Strings {
- def emptyStringTo(replacement: String)(s: String) = {
- if (s.isEmpty) replacement
- else s
- }
-
- def noneIfEmpty(s: String) = Option(s).filterNot(_.isEmpty)
-}
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
new file mode 100644
index 00000000000..8cccf0598ab
--- /dev/null
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/bundle/AnalyzeBundleTest.java
@@ -0,0 +1,85 @@
+// 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.bundle;
+
+import com.yahoo.container.plugin.bundle.AnalyzeBundle.PublicPackages;
+import com.yahoo.container.plugin.osgi.ExportPackages;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import static com.yahoo.container.plugin.classanalysis.TestUtilities.throwableMessage;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class AnalyzeBundleTest {
+ private final List<ExportPackages.Export> exports;
+ private final Map<String, ExportPackages.Export> exportsByPackageName;
+
+ File jarDir = new File("src/test/resources/jar");
+
+ public AnalyzeBundleTest() {
+ File notOsgi = new File(jarDir, "notAOsgiBundle.jar");
+ File simple = new File(jarDir, "simple1.jar");
+ PublicPackages pp = AnalyzeBundle.publicPackagesAggregated(Arrays.asList(notOsgi, simple));
+ this.exports = pp.exports;
+ this.exportsByPackageName = ExportPackages.exportsByPackageName(exports);
+ }
+
+ private File jarFile(String name) {
+ return new File(jarDir, name);
+ }
+
+ @Test
+ public void require_that_non_osgi_bundles_are_ignored() {
+ assertThat(exportsByPackageName.keySet(), not(hasItem("com.yahoo.sample.exported.package.ignored")));
+ }
+
+ @Test
+ public void require_that_exports_are_retrieved_from_manifest_in_jars() {
+ assertThat(exportsByPackageName.keySet().size(), is(1));
+ assertThat(exportsByPackageName.keySet(), hasItem("com.yahoo.sample.exported.package"));
+ }
+
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ @Test
+ public void require_that_invalid_exports_throws_exception() {
+ exception.expect(Exception.class);
+
+ exception.expectMessage(containsString("Invalid manifest in bundle"));
+ exception.expectMessage(matchesPattern("Invalid manifest in bundle '.*errorExport.jar'"));
+ exception.expectCause(throwableMessage(startsWith("Failed parsing Export-Package")));
+
+ AnalyzeBundle.publicPackages(jarFile("errorExport.jar"));
+ }
+
+ private TypeSafeMatcher<String> matchesPattern(String pattern) {
+ return new TypeSafeMatcher<String>() {
+ @Override
+ protected boolean matchesSafely(String s) {
+ return s.matches(pattern);
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("expects String that matches the pattern ").appendValue(pattern);
+ }
+ };
+ }
+}
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/AnalyzeClassTest.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/AnalyzeClassTest.java
new file mode 100644
index 00000000000..aba6e8f14e8
--- /dev/null
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/AnalyzeClassTest.java
@@ -0,0 +1,167 @@
+// 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.yahoo.container.plugin.classanalysis.sampleclasses.Base;
+import com.yahoo.container.plugin.classanalysis.sampleclasses.ClassAnnotation;
+import com.yahoo.container.plugin.classanalysis.sampleclasses.Derived;
+import com.yahoo.container.plugin.classanalysis.sampleclasses.DummyAnnotation;
+import com.yahoo.container.plugin.classanalysis.sampleclasses.Fields;
+import com.yahoo.container.plugin.classanalysis.sampleclasses.Interface1;
+import com.yahoo.container.plugin.classanalysis.sampleclasses.Interface2;
+import com.yahoo.container.plugin.classanalysis.sampleclasses.MethodAnnotation;
+import com.yahoo.container.plugin.classanalysis.sampleclasses.MethodInvocation;
+import com.yahoo.osgi.annotation.ExportPackage;
+import com.yahoo.osgi.annotation.Version;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import javax.security.auth.login.LoginException;
+import java.awt.Image;
+import java.awt.image.ImagingOpException;
+import java.awt.image.Kernel;
+import java.util.Optional;
+
+import static com.yahoo.container.plugin.classanalysis.TestUtilities.analyzeClass;
+import static com.yahoo.container.plugin.classanalysis.TestUtilities.classFile;
+import static com.yahoo.container.plugin.classanalysis.TestUtilities.name;
+import static com.yahoo.container.plugin.classanalysis.TestUtilities.throwableMessage;
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.anyOf;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Tests that analysis of class files works.
+ *
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class AnalyzeClassTest {
+ @Test
+ public void require_that_full_class_name_is_returned() {
+ assertThat(analyzeClass(Base.class).getName(), is(name(Base.class)));
+ }
+
+ @Test
+ public void require_that_base_class_is_included() {
+ assertThat(analyzeClass(Derived.class).getReferencedClasses(), hasItem(name(Base.class)));
+ }
+
+ @Test
+ public void require_that_implemented_interfaces_are_included() {
+ assertThat(analyzeClass(Base.class).getReferencedClasses(),
+ allOf(hasItem(name(Interface1.class)), hasItem(name(Interface2.class))));
+ }
+
+ @Test
+ public void require_that_interface_can_be_analyzed() {
+ ClassFileMetaData classMetaData = analyzeClass(Interface1.class);
+
+ assertThat(classMetaData.getName(), is(name(Interface1.class)));
+ assertThat(classMetaData.getReferencedClasses(), hasItem(name(Interface2.class)));
+ }
+
+ @Test
+ public void require_that_return_type_is_included() {
+ assertThat(analyzeClass(Interface1.class).getReferencedClasses(), hasItem(name(Image.class)));
+ }
+
+ @Test
+ public void require_that_parameters_are_included() {
+ assertThat(analyzeClass(Interface1.class).getReferencedClasses(), hasItem(name(Kernel.class)));
+ }
+
+ @Test
+ public void require_that_exceptions_are_included() {
+ assertThat(analyzeClass(Interface1.class).getReferencedClasses(), hasItem(name(ImagingOpException.class)));
+ }
+
+ @Test
+ public void require_that_basic_types_ignored() {
+ assertThat(analyzeClass(Interface1.class).getReferencedClasses(), not(anyOf(hasItem("int"), hasItem("float"))));
+ }
+
+ @Test
+ public void require_that_arrays_of_basic_types_ignored() {
+ assertThat(analyzeClass(Interface1.class).getReferencedClasses(), not(anyOf(hasItem("int[]"), hasItem("int[][]"))));
+ }
+
+ @Test
+ public void require_that_instance_field_types_are_included() {
+ assertThat(analyzeClass(Fields.class).getReferencedClasses(), hasItem(name(String.class)));
+ }
+
+ @Test
+ public void require_that_static_field_types_are_included() {
+ assertThat(analyzeClass(Fields.class).getReferencedClasses(), hasItem(name(java.util.List.class)));
+ }
+
+ @Test
+ public void require_that_field_annotation_is_included() {
+ assertThat(analyzeClass(Fields.class).getReferencedClasses(), hasItem(name(DummyAnnotation.class)));
+ }
+
+ @Test
+ public void require_that_class_annotation_is_included() {
+ assertThat(analyzeClass(ClassAnnotation.class).getReferencedClasses(), hasItem(name(DummyAnnotation.class)));
+ }
+
+ @Test
+ public void require_that_method_annotation_is_included() {
+ assertThat(analyzeClass(MethodAnnotation.class).getReferencedClasses(), hasItem(name(DummyAnnotation.class)));
+ }
+
+ @Test
+ public void require_that_export_package_annotations_are_ignored() {
+ assertThat(Analyze.analyzeClass(classFile("com.yahoo.container.plugin.classanalysis.sampleclasses.package-info"))
+ .getReferencedClasses(), not(anyOf(hasItem(name(ExportPackage.class)), hasItem(name(Version.class)))));
+ }
+
+ @Test
+ public void require_that_export_annotations_are_processed() {
+ assertThat(
+ Analyze.analyzeClass(classFile("com.yahoo.container.plugin.classanalysis.sampleclasses.package-info")).getExportPackage(),
+ is(Optional.of(new ExportPackageAnnotation(3, 1, 4, "TEST_QUALIFIER-2"))));
+ }
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void require_that_export_annotations_are_validated() {
+ expectedException.expect(RuntimeException.class);
+ expectedException.expectMessage(containsString("invalid/package-info"));
+ expectedException.expectCause(throwableMessage(containsString("qualifier must follow the format")));
+ expectedException.expectCause(throwableMessage(containsString("'EXAMPLE INVALID QUALIFIER'")));
+
+ Analyze.analyzeClass(classFile("com.yahoo.container.plugin.classanalysis.sampleclasses.invalid.package-info"));
+ }
+
+ @Test
+ public void require_that_catch_clauses_are_included() {
+ assertThat(Analyze.analyzeClass(classFile("com.yahoo.container.plugin.classanalysis.sampleclasses.CatchException"))
+ .getReferencedClasses(), hasItem(name(LoginException.class)));
+ }
+
+ @Test
+ public void require_that_class_references_are_included() {
+ assertThat(Analyze.analyzeClass(classFile("com.yahoo.container.plugin.classanalysis.sampleclasses.ClassReference"))
+ .getReferencedClasses(), hasItem(name(Interface1.class)));
+ }
+
+ @Test
+ public void require_that_return_type_of_method_invocations_are_included() {
+ assertThat(analyzeClass(MethodInvocation.class).getReferencedClasses(), hasItem(name(Image.class)));
+ }
+
+ @Test
+ public void require_that_attributes_are_included() {
+ //Uses com/coremedia/iso/Utf8.class from com.googlecode.mp4parser:isoparser:1.0-RC-1
+ assertThat(Analyze.analyzeClass(classFile("class/Utf8")).getReferencedClasses(),
+ hasItem("org.aspectj.weaver.MethodDeclarationLineNumber"));
+ }
+}
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/AnalyzeMethodBodyTest.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/AnalyzeMethodBodyTest.java
new file mode 100644
index 00000000000..5ca8ed349a4
--- /dev/null
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/AnalyzeMethodBodyTest.java
@@ -0,0 +1,73 @@
+// Copyright 2017 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.yahoo.container.plugin.classanalysis.sampleclasses.Base;
+import com.yahoo.container.plugin.classanalysis.sampleclasses.ClassWithMethod;
+import com.yahoo.container.plugin.classanalysis.sampleclasses.Derived;
+import com.yahoo.container.plugin.classanalysis.sampleclasses.Dummy;
+import com.yahoo.container.plugin.classanalysis.sampleclasses.Fields;
+import com.yahoo.container.plugin.classanalysis.sampleclasses.Interface1;
+import com.yahoo.container.plugin.classanalysis.sampleclasses.Methods;
+import org.junit.Test;
+
+import java.io.PrintStream;
+
+import static com.yahoo.container.plugin.classanalysis.TestUtilities.analyzeClass;
+import static com.yahoo.container.plugin.classanalysis.TestUtilities.name;
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Tests that classes used in method bodies are included in the imports list.
+ *
+ * @author Tony Vaagenes
+ */
+public class AnalyzeMethodBodyTest {
+ @Test
+ public void require_that_class_of_locals_are_included() {
+ assertThat(analyzeClass(Methods.class).getReferencedClasses(), hasItem(name(Base.class)));
+ }
+
+ @Test
+ public void require_that_class_of_locals_in_static_method_are_included() {
+ assertThat(analyzeClass(Methods.class).getReferencedClasses(), hasItem(name(Derived.class)));
+ }
+
+ @Test
+ public void require_that_field_references_are_included() {
+ assertThat(analyzeClass(Methods.class).getReferencedClasses(),
+ allOf(hasItem(name(java.util.List.class)), hasItem(name(Fields.class))));
+ }
+
+ @Test
+ public void require_that_class_owning_field_is_included() {
+ assertThat(analyzeClass(Methods.class).getReferencedClasses(), hasItem(name(System.class)));
+ }
+
+ @Test
+ public void require_that_class_containing_method_is_included() {
+ assertThat(analyzeClass(Methods.class).getReferencedClasses(), hasItem(name(PrintStream.class)));
+ }
+
+ @Test
+ public void require_that_element_of_new_multidimensional_array_is_included() {
+ assertThat(analyzeClass(Methods.class).getReferencedClasses(), hasItem(name(Interface1.class)));
+ }
+
+ @Test
+ public void require_that_basic_arrays_are_not_included() {
+ assertThat(analyzeClass(Methods.class).getReferencedClasses(), not(hasItem("int[]")));
+ }
+
+ @Test
+ public void require_that_container_generic_parameters_are_included() {
+ assertThat(analyzeClass(Methods.class).getReferencedClasses(), hasItem(name(Dummy.class)));
+ }
+
+ @Test
+ public void require_that_class_owning_method_handler_is_included() {
+ assertThat(analyzeClass(Methods.class).getReferencedClasses(), hasItem(name(ClassWithMethod.class)));
+ }
+}
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/TestUtilities.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/TestUtilities.java
new file mode 100644
index 00000000000..6ff6bafd816
--- /dev/null
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/TestUtilities.java
@@ -0,0 +1,40 @@
+// 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 org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+import java.io.File;
+
+/**
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class TestUtilities {
+ public static ClassFileMetaData analyzeClass(Class<?> clazz) {
+ return Analyze.analyzeClass(classFile(name(clazz)));
+ }
+
+ public static File classFile(String className) {
+ return new File("target/test-classes/" + className.replace('.', '/') + ".class");
+ }
+
+ public static String name(Class<?> clazz) {
+ return clazz.getName();
+ }
+
+ public static TypeSafeMatcher<Throwable> throwableMessage(final Matcher<String> matcher) {
+ return new TypeSafeMatcher<Throwable>() {
+ @Override
+ protected boolean matchesSafely(Throwable throwable) {
+ return matcher.matches(throwable.getMessage());
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("expects Throwable and a message matching ").appendDescriptionOf(matcher);
+ }
+ };
+ }
+}
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Base.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Base.java
index e8d12e51ab8..cdada15a96f 100644
--- a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Base.java
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Base.java
@@ -7,7 +7,7 @@ import java.awt.image.Kernel;
/**
* Input for class analysis tests.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class Base implements Interface1, Interface2 {
@Override
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/CatchException.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/CatchException.java
index 1029de707c8..d4687120ab3 100644
--- a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/CatchException.java
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/CatchException.java
@@ -4,7 +4,7 @@ package com.yahoo.container.plugin.classanalysis.sampleclasses;
import javax.security.auth.login.LoginException;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class CatchException {
void ignored() throws Exception{
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/ClassAnnotation.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/ClassAnnotation.java
index f1659593a45..fc6a3de7b80 100644
--- a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/ClassAnnotation.java
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/ClassAnnotation.java
@@ -3,7 +3,7 @@ package com.yahoo.container.plugin.classanalysis.sampleclasses;
/**
* Input for class analysis tests.*
- * @author tonytv
+ * @author Tony Vaagenes
*/
@DummyAnnotation
public class ClassAnnotation {
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/ClassReference.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/ClassReference.java
index b4a4f90398f..0da35015861 100644
--- a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/ClassReference.java
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/ClassReference.java
@@ -2,10 +2,11 @@
package com.yahoo.container.plugin.classanalysis.sampleclasses;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ClassReference {
void classReference() {
+ @SuppressWarnings("unused")
Class<?> clazz = Interface1.class;
}
}
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/ClassWithMethod.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/ClassWithMethod.java
index 1b56bca1422..c3a4ad8639d 100644
--- a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/ClassWithMethod.java
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/ClassWithMethod.java
@@ -2,7 +2,7 @@
package com.yahoo.container.plugin.classanalysis.sampleclasses;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ClassWithMethod {
public static void test() {}
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Derived.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Derived.java
index 318039a4e98..8b542c6d407 100644
--- a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Derived.java
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Derived.java
@@ -3,7 +3,7 @@ package com.yahoo.container.plugin.classanalysis.sampleclasses;
/**
* Input for class analysis tests.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class Derived extends Base {
}
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Dummy.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Dummy.java
index b1ca82589bd..b0ee9e7be88 100644
--- a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Dummy.java
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Dummy.java
@@ -2,6 +2,6 @@
package com.yahoo.container.plugin.classanalysis.sampleclasses;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class Dummy {}
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/DummyAnnotation.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/DummyAnnotation.java
index 692245e4e5e..14c360c26d6 100644
--- a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/DummyAnnotation.java
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/DummyAnnotation.java
@@ -3,7 +3,7 @@ package com.yahoo.container.plugin.classanalysis.sampleclasses;
/**
* Input for class analysis tests.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public @interface DummyAnnotation {
}
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Fields.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Fields.java
index 886ba853a6b..c080a4d410b 100644
--- a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Fields.java
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Fields.java
@@ -5,7 +5,7 @@ import java.util.List;
/**
* Input for class analysis tests.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class Fields {
@DummyAnnotation
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Interface1.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Interface1.java
index d96180d7453..a1d6de8be73 100644
--- a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Interface1.java
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Interface1.java
@@ -8,7 +8,7 @@ import java.awt.image.Kernel;
/**
* Input for class analysis tests.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public interface Interface1 extends Interface2 {
Image methodSignatureTest(Kernel kernel, BufferedImage image);
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Interface2.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Interface2.java
index 5e037d544de..977638c9550 100644
--- a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Interface2.java
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Interface2.java
@@ -3,7 +3,7 @@ package com.yahoo.container.plugin.classanalysis.sampleclasses;
/**
* Input for class analysis tests.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public interface Interface2 {
}
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/MethodAnnotation.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/MethodAnnotation.java
index b0d6f406102..fb6d4bb6e6a 100644
--- a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/MethodAnnotation.java
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/MethodAnnotation.java
@@ -3,7 +3,7 @@ package com.yahoo.container.plugin.classanalysis.sampleclasses;
/**
* Input for class analysis tests.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public interface MethodAnnotation {
@DummyAnnotation
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/MethodInvocation.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/MethodInvocation.java
index 992799e3032..7343a1f1a79 100644
--- a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/MethodInvocation.java
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/MethodInvocation.java
@@ -2,11 +2,12 @@
package com.yahoo.container.plugin.classanalysis.sampleclasses;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class MethodInvocation {
void invokeMethod() {
Interface1 interface1 = null;
+ @SuppressWarnings({ "unused", "null" })
Object o = interface1.methodSignatureTest(null, null);
}
}
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Methods.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Methods.java
index 568a5070961..ca8050698c0 100644
--- a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Methods.java
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/Methods.java
@@ -6,8 +6,9 @@ import java.util.Map;
/**
* Input for class analysis tests.
- * @author tonytv
+ * @author Tony Vaagenes
*/
+@SuppressWarnings("unused")
public class Methods {
public void method1() {
Base b = new Base();
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/mojo/GenerateSourcesMojoTest.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/mojo/GenerateSourcesMojoTest.java
index 328d5765b26..640bfa07c71 100644
--- a/bundle-plugin/src/test/java/com/yahoo/container/plugin/mojo/GenerateSourcesMojoTest.java
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/mojo/GenerateSourcesMojoTest.java
@@ -8,7 +8,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeNotNull;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class GenerateSourcesMojoTest {
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/osgi/ExportPackageParserTest.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/osgi/ExportPackageParserTest.java
new file mode 100644
index 00000000000..d869b8ec4d9
--- /dev/null
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/osgi/ExportPackageParserTest.java
@@ -0,0 +1,295 @@
+// 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.osgi;
+
+import com.yahoo.container.plugin.osgi.ExportPackages.Export;
+import com.yahoo.container.plugin.osgi.ExportPackages.Parameter;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.empty;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class ExportPackageParserTest {
+ private final Parameter versionParameter = new Parameter("version", "1.2.3.sample");
+
+ @Test
+ public void require_that_package_is_parsed_correctly() {
+ List<Export> exports = ExportPackageParser.parseExports("sample.exported.package");
+
+ assertThat(exports.size(), is(1));
+ assertThat(exports.get(0).getParameters(), empty());
+ assertThat(exports.get(0).getPackageNames(), contains("sample.exported.package"));
+ }
+
+ @Test
+ public void require_that_version_is_parsed_correctly() {
+ List<Export> exports = ExportPackageParser.parseExports("com.yahoo.sample.exported.package;version=\"1.2.3.sample\"");
+
+ assertThat(exports.size(), is(1));
+ Export export = exports.get(0);
+ assertThat(export.getPackageNames(), contains("com.yahoo.sample.exported.package"));
+ assertThat(export.getParameters(), contains(parameterMatching(versionParameter)));
+ }
+
+ @Test
+ public void require_that_multiple_packages_with_same_parameters_is_parsed_correctly() {
+ List<Export> exports = ExportPackageParser.parseExports("exported.package1;exported.package2;version=\"1.2.3.sample\"");
+
+ assertThat(exports.size(), is(1));
+ Export export = exports.get(0);
+ assertThat(export.getPackageNames(), contains("exported.package1", "exported.package2"));
+ assertThat(export.getParameters(), contains(parameterMatching(versionParameter)));
+ }
+
+ @Test
+ public void require_that_spaces_between_separators_are_allowed() {
+ List<Export> exports = ExportPackageParser.parseExports("exported.package1 , exported.package2 ; version = \"1.2.3.sample\" ");
+
+ assertThat(exports.size(), is(2));
+ Export export = exports.get(0);
+ assertThat(export.getPackageNames(), contains("exported.package1"));
+ export = exports.get(1);
+ assertThat(export.getPackageNames(), contains("exported.package2"));
+ assertThat(export.getParameters(), contains(parameterMatching(versionParameter)));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void require_that_multiple_parameters_for_a_package_is_parsed_correctly() {
+ List<Export> exports = ExportPackageParser.parseExports("exported.package;version=\"1.2.3.sample\";param2=true");
+
+ assertThat(exports.size(), is(1));
+ Export export = exports.get(0);
+ assertThat(export.getParameters(), contains(parameterMatching(versionParameter), parameterMatching("param2", "true")));
+ }
+
+ @Test
+ public void require_that_multiple_exports_are_parsed_correctly() {
+ List<Export> exports = ExportPackageParser.parseExports("exported.package1,exported.package2");
+ assertThat(exports.size(), is(2));
+ Export export = exports.get(0);
+ assertThat(export.getPackageNames(), contains("exported.package1"));
+ assertThat(export.getParameters(), empty());
+ export = exports.get(1);
+ assertThat(export.getPackageNames(), contains("exported.package2"));
+ assertThat(export.getParameters(), empty());
+
+ exports = ExportPackageParser.parseExports("exported.package1;version=\"1.2.3.sample\",exported.package2");
+ assertThat(exports.size(), is(2));
+ export = exports.get(0);
+ assertThat(export.getPackageNames(), contains("exported.package1"));
+ assertThat(export.getParameters(), contains(parameterMatching(versionParameter)));
+ export = exports.get(1);
+ assertThat(export.getPackageNames(), contains("exported.package2"));
+ assertThat(export.getParameters(), empty());
+
+ exports = ExportPackageParser.parseExports("exported.package1,exported.package2;version=\"1.2.3.sample\"");
+ assertThat(exports.size(), is(2));
+ export = exports.get(0);
+ assertThat(export.getPackageNames(), contains("exported.package1"));
+ assertThat(export.getParameters(), empty());
+ export = exports.get(1);
+ assertThat(export.getPackageNames(), contains("exported.package2"));
+ assertThat(export.getParameters(), contains(parameterMatching(versionParameter)));
+ }
+
+ @Test
+ public void require_that_long_string_literals_do_not_cause_stack_overflow_error() {
+ //From jersey-server-1.13.jar
+ String exportHeader = "com.sun.jersey.server.impl.wadl;uses:=\"com.sun.jersey.api.model,com.sun.resea"
+ + "rch.ws.wadl,com.sun.jersey.api.wadl.config,com.sun.jersey.server.wadl,com.sun."
+ + "jersey.api.core,javax.xml.bind,javax.ws.rs.core,com.sun.jersey.server.impl.uri"
+ + ",com.sun.jersey.core.spi.factory,com.sun.jersey.server.impl.model.method,com.s"
+ + "un.jersey.api.uri,com.sun.jersey.core.header,com.sun.jersey.spi.dispatch,javax"
+ + ".ws.rs,com.sun.jersey.spi.resource\";version=\"1.13.0\",com.sun.jersey.server."
+ + "impl.model.parameter.multivalued;uses:=\"com.sun.jersey.spi,javax.ws.rs.core,c"
+ + "om.sun.jersey.api.container,com.sun.jersey.impl,javax.xml.parsers,org.xml.sax,"
+ + "javax.xml.transform,javax.xml.bind.annotation,javax.xml.transform.sax,com.sun."
+ + "jersey.spi.inject,javax.xml.bind,javax.ws.rs.ext,com.sun.jersey.api.model,com."
+ + "sun.jersey.core.reflection,javax.ws.rs,com.sun.jersey.core.spi.component,com.s"
+ + "un.jersey.core.header\";version=\"1.13.0\",com.sun.jersey.server.impl.model.pa"
+ + "rameter;uses:=\"com.sun.jersey.api.model,com.sun.jersey.core.spi.component,com"
+ + ".sun.jersey.server.impl.model.parameter.multivalued,com.sun.jersey.spi.inject,"
+ + "com.sun.jersey.api,com.sun.jersey.api.core,com.sun.jersey.server.impl.inject,j"
+ + "avax.ws.rs.core,javax.ws.rs,com.sun.jersey.core.header,com.sun.jersey.api.repr"
+ + "esentation\";version=\"1.13.0\",com.sun.jersey.server.impl.application;uses:="
+ + "\"com.sun.jersey.core.spi.component,com.sun.jersey.api.core,com.sun.jersey.spi"
+ + ",com.sun.jersey.spi.inject,javax.ws.rs.core,com.sun.jersey.api.container,javax"
+ + ".ws.rs.ext,com.sun.jersey.spi.container,com.sun.jersey.core.reflection,com.sun"
+ + ".jersey.api.model,com.sun.jersey.impl,com.sun.jersey.spi.dispatch,com.sun.jers"
+ + "ey.server.impl.model,com.sun.jersey.server.impl.wadl,com.sun.jersey.server.imp"
+ + "l.uri,com.sun.jersey.core.spi.factory,com.sun.jersey.api.uri,com.sun.jersey.se"
+ + "rver.impl.uri.rules,com.sun.jersey.spi.uri.rules,com.sun.jersey.server.spi.com"
+ + "ponent,com.sun.jersey.core.util,com.sun.jersey.core.header,com.sun.jersey.core"
+ + ".spi.component.ioc,javax.ws.rs,com.sun.jersey.server.impl,com.sun.jersey.serve"
+ + "r.wadl,com.sun.jersey.server.impl.inject,com.sun.jersey.server.impl.component,"
+ + "com.sun.jersey.spi.monitoring,com.sun.jersey.server.impl.monitoring,com.sun.je"
+ + "rsey.api.container.filter,com.sun.jersey.server.impl.model.parameter.multivalu"
+ + "ed,com.sun.jersey.server.impl.model.parameter,com.sun.jersey.server.impl.templ"
+ + "ate,com.sun.jersey.spi.template,com.sun.jersey.server.impl.resource,com.sun.je"
+ + "rsey.server.impl.modelapi.annotation,com.sun.jersey.server.impl.container.filt"
+ + "er,com.sun.jersey.server.impl.modelapi.validation,com.sun.jersey.api,com.sun.j"
+ + "ersey.spi.service\";version=\"1.13.0\",com.sun.jersey.server.impl.component;us"
+ + "es:=\"com.sun.jersey.api.model,com.sun.jersey.core.spi.component,com.sun.jerse"
+ + "y.server.spi.component,com.sun.jersey.api.core,com.sun.jersey.core.spi.compone"
+ + "nt.ioc,com.sun.jersey.server.impl.inject,com.sun.jersey.server.impl.resource,c"
+ + "om.sun.jersey.api.container,com.sun.jersey.core.reflection,com.sun.jersey.spi."
+ + "inject\";version=\"1.13.0\",com.sun.jersey.server.impl.provider;uses:=\"com.su"
+ + "n.jersey.core.spi.factory,com.sun.jersey.api.container,com.sun.jersey.api.core"
+ + ",javax.ws.rs.core\";version=\"1.13.0\",com.sun.jersey.server.impl.template;use"
+ + "s:=\"com.sun.jersey.core.spi.component,com.sun.jersey.api.view,com.sun.jersey."
+ + "spi.template,javax.ws.rs.core,com.sun.jersey.core.header,com.sun.jersey.server"
+ + ".impl.model.method,com.sun.jersey.spi.dispatch,com.sun.jersey.api.uri,javax.ws"
+ + ".rs,com.sun.jersey.spi.inject,javax.ws.rs.ext,com.sun.jersey.server.impl.uri.r"
+ + "ules,com.sun.jersey.server.probes,com.sun.jersey.core.reflection,com.sun.jerse"
+ + "y.spi.uri.rules,com.sun.jersey.spi.container,com.sun.jersey.api.core\";version"
+ + "=\"1.13.0\",com.sun.jersey.server.osgi;uses:=\"com.sun.jersey.server.impl.prov"
+ + "ider,org.osgi.framework,javax.ws.rs.ext\";version=\"1.13.0\",com.sun.jersey.se"
+ + "rver.wadl.generators.resourcedoc.model;uses:=\"javax.xml.bind.annotation,javax"
+ + ".xml.namespace\";version=\"1.13.0\",com.sun.jersey.server.impl.resource;uses:="
+ + "\"com.sun.jersey.api.model,com.sun.jersey.core.spi.component,com.sun.jersey.se"
+ + "rver.spi.component,com.sun.jersey.api.core,com.sun.jersey.api.container,javax."
+ + "ws.rs,com.sun.jersey.server.impl.inject,com.sun.jersey.core.spi.component.ioc,"
+ + "javax.ws.rs.core\";version=\"1.13.0\",com.sun.jersey.server.impl.monitoring;us"
+ + "es:=\"com.sun.jersey.spi.monitoring,com.sun.jersey.spi.service,com.sun.jersey."
+ + "api.model,com.sun.jersey.spi.container,javax.ws.rs.ext,com.sun.jersey.core.spi"
+ + ".component\";version=\"1.13.0\",com.sun.jersey.server.impl.modelapi.annotation"
+ + ";uses:=\"com.sun.jersey.api.model,javax.ws.rs.core,javax.ws.rs,com.sun.jersey."
+ + "core.reflection,com.sun.jersey.core.header,com.sun.jersey.impl\";version=\"1.1"
+ + "3.0\",com.sun.jersey.server.impl.container;uses:=\"com.sun.jersey.server.impl."
+ + "application,com.sun.jersey.spi.container,com.sun.jersey.api.container\";versio"
+ + "n=\"1.13.0\",com.sun.jersey.server.wadl;uses:=\"javax.ws.rs.core,com.sun.resea"
+ + "rch.ws.wadl,javax.xml.namespace,com.sun.jersey.api.model,javax.xml.bind,javax."
+ + "ws.rs,com.sun.jersey.server.wadl.generators,com.sun.jersey.server.impl.modelap"
+ + "i.annotation,com.sun.jersey.server.impl\";version=\"1.13.0\",com.sun.jersey.se"
+ + "rver.impl.model.method.dispatch;uses:=\"com.sun.jersey.api.model,com.sun.jerse"
+ + "y.api.core,com.sun.jersey.spi.container,com.sun.jersey.server.impl.inject,com."
+ + "sun.jersey.api,javax.ws.rs.core,com.sun.jersey.core.spi.factory,com.sun.jersey"
+ + ".spi.inject,com.sun.jersey.spi.dispatch,com.sun.jersey.core.spi.component,java"
+ + "x.ws.rs,com.sun.jersey.server.impl.model.parameter.multivalued,com.sun.jersey."
+ + "api.representation,com.sun.jersey.api.container\";version=\"1.13.0\",com.sun.j"
+ + "ersey.server.impl;uses:=\"javax.naming,com.sun.jersey.api.core,com.sun.jersey."
+ + "core.header,javax.ws.rs.core,com.sun.jersey.server.impl.model,com.sun.jersey.s"
+ + "pi.container\";version=\"1.13.0\",com.sun.jersey.server.wadl.generators.resour"
+ + "cedoc;uses:=\"com.sun.jersey.api.model,com.sun.jersey.server.wadl.generators.r"
+ + "esourcedoc.model,com.sun.jersey.server.wadl.generators.resourcedoc.xhtml,com.s"
+ + "un.research.ws.wadl,javax.xml.namespace,com.sun.jersey.server.wadl,javax.xml.b"
+ + "ind,javax.ws.rs.core\";version=\"1.13.0\",com.sun.jersey.server.impl.container"
+ + ".httpserver;uses:=\"com.sun.net.httpserver,com.sun.jersey.spi.container,javax."
+ + "ws.rs.core,com.sun.jersey.core.header,com.sun.jersey.api.container,com.sun.jer"
+ + "sey.core.util,com.sun.jersey.api.core\";version=\"1.13.0\",com.sun.jersey.serv"
+ + "er.impl.container.filter;uses:=\"com.sun.jersey.api.model,com.sun.jersey.spi.c"
+ + "ontainer,com.sun.jersey.core.spi.component,com.sun.jersey.api.core,javax.ws.rs"
+ + ",com.sun.jersey.server.impl.uri,javax.ws.rs.core\";version=\"1.13.0\",com.sun."
+ + "jersey.server.wadl.generators.resourcedoc.xhtml;uses:=\"javax.xml.bind,javax.x"
+ + "ml.namespace,javax.xml.bind.annotation\";version=\"1.13.0\",com.sun.jersey.ser"
+ + "ver.impl.uri.rules;uses:=\"com.sun.jersey.spi.uri.rules,com.sun.jersey.api.uri"
+ + ",com.sun.jersey.api.core,com.sun.jersey.server.impl.model.method,com.sun.jerse"
+ + "y.spi.dispatch,com.sun.jersey.core.header,javax.ws.rs.core,com.sun.jersey.api."
+ + "model,com.sun.jersey.server.probes,com.sun.jersey.core.reflection,com.sun.jers"
+ + "ey.server.impl.template,com.sun.jersey.spi.monitoring,com.sun.jersey.api,com.s"
+ + "un.jersey.spi.container,com.sun.jersey.server.impl.uri,javax.ws.rs,com.sun.jer"
+ + "sey.api.container,com.sun.jersey.server.impl.inject,com.sun.jersey.spi.inject,"
+ + "com.sun.jersey.server.impl.uri.rules.automata\";version=\"1.13.0\",com.sun.jer"
+ + "sey.server.spi.component;uses:=\"com.sun.jersey.spi.inject,com.sun.jersey.api."
+ + "model,com.sun.jersey.core.spi.component,com.sun.jersey.api.core,com.sun.jersey"
+ + ".server.impl.inject,com.sun.jersey.api.container,com.sun.jersey.core.spi.compo"
+ + "nent.ioc\";version=\"1.13.0\",com.sun.jersey.server.probes;version=\"1.13.0\","
+ + "com.sun.jersey.server.wadl.generators;uses:=\"com.sun.research.ws.wadl,javax.x"
+ + "ml.bind.annotation,com.sun.jersey.api.model,com.sun.jersey.server.wadl,javax.x"
+ + "ml.bind,javax.ws.rs.core,com.sun.jersey.api,javax.xml.namespace,javax.xml.tran"
+ + "sform,javax.xml.transform.stream\";version=\"1.13.0\",com.sun.jersey.server.im"
+ + "pl.modelapi.validation;uses:=\"com.sun.jersey.api.model,javax.ws.rs,com.sun.je"
+ + "rsey.impl,com.sun.jersey.api.core,com.sun.jersey.core.reflection,javax.ws.rs.c"
+ + "ore\";version=\"1.13.0\",com.sun.jersey.server.impl.model.method;uses:=\"com.s"
+ + "un.jersey.api.container,com.sun.jersey.spi.dispatch,com.sun.jersey.api.uri,com"
+ + ".sun.jersey.api.model,com.sun.jersey.server.impl.container.filter,com.sun.jers"
+ + "ey.impl,com.sun.jersey.spi.container,com.sun.jersey.spi.inject,com.sun.jersey."
+ + "api.core,javax.ws.rs.core,com.sun.jersey.core.header\";version=\"1.13.0\",com."
+ + "sun.jersey.server.impl.model;uses:=\"javax.ws.rs,com.sun.jersey.impl,com.sun.j"
+ + "ersey.api.container,com.sun.jersey.core.header,com.sun.jersey.core.header.read"
+ + "er,com.sun.jersey.api.core,javax.ws.rs.core,com.sun.jersey.server.impl.model.m"
+ + "ethod,com.sun.jersey.server.impl.container.filter,com.sun.jersey.api.model,com"
+ + ".sun.jersey.server.impl.wadl,com.sun.jersey.spi.monitoring,com.sun.jersey.serv"
+ + "er.impl.uri,com.sun.jersey.spi.container,com.sun.jersey.server.impl.inject,com"
+ + ".sun.jersey.spi.inject,com.sun.jersey.api.uri,com.sun.jersey.core.spi.componen"
+ + "t,com.sun.jersey.server.impl.uri.rules,com.sun.jersey.server.impl.template,com"
+ + ".sun.jersey.api.view,com.sun.jersey.spi.uri.rules\";version=\"1.13.0\",com.sun"
+ + ".jersey.server.impl.uri.rules.automata;uses:=\"com.sun.jersey.server.impl.uri,"
+ + "com.sun.jersey.spi.uri.rules,com.sun.jersey.server.impl.uri.rules,com.sun.jers"
+ + "ey.api.uri\";version=\"1.13.0\",com.sun.jersey.server.impl.uri;uses:=\"com.sun"
+ + ".jersey.api.uri,javax.ws.rs.core\";version=\"1.13.0\",com.sun.jersey.server.im"
+ + "pl.inject;uses:=\"com.sun.jersey.api.core,com.sun.jersey.spi.inject,javax.ws.r"
+ + "s,com.sun.jersey.api.container,com.sun.jersey.api.model,com.sun.jersey.core.sp"
+ + "i.component,com.sun.jersey.core.spi.factory\";version=\"1.13.0\",com.sun.jerse"
+ + "y.spi.scanning;uses:=\"org.objectweb.asm,com.sun.jersey.core.reflection,com.su"
+ + "n.jersey.core.spi.scanning,javax.ws.rs,javax.ws.rs.ext\";version=\"1.13.0\",co"
+ + "m.sun.jersey.spi.resource;uses:=\"com.sun.jersey.server.impl.resource,com.sun."
+ + "jersey.server.spi.component\";version=\"1.13.0\",com.sun.jersey.spi.template;u"
+ + "ses:=\"com.sun.jersey.api.view,javax.ws.rs.core,com.sun.jersey.api.container\""
+ + ";version=\"1.13.0\",com.sun.jersey.spi.dispatch;uses:=\"com.sun.jersey.api.cor"
+ + "e\";version=\"1.13.0\",com.sun.jersey.spi.uri.rules;uses:=\"com.sun.jersey.api"
+ + ".core,com.sun.jersey.api.model,com.sun.jersey.spi.container,com.sun.jersey.api"
+ + ".uri\";version=\"1.13.0\",com.sun.jersey.spi.container;uses:=\"javax.ws.rs,com"
+ + ".sun.jersey.api.representation,com.sun.jersey.core.header,com.sun.jersey.spi,j"
+ + "avax.ws.rs.core,com.sun.jersey.api.container,com.sun.jersey.api.core,com.sun.j"
+ + "ersey.core.util,com.sun.jersey.core.header.reader,com.sun.jersey.server.impl,c"
+ + "om.sun.jersey.core.reflection,javax.ws.rs.ext,com.sun.jersey.server.impl.model"
+ + ",com.sun.jersey.api,com.sun.jersey.api.uri,com.sun.jersey.core.spi.factory,com"
+ + ".sun.jersey.spi.monitoring,com.sun.jersey.api.model,com.sun.jersey.core.spi.co"
+ + "mponent,com.sun.jersey.server.impl.application,com.sun.jersey.impl,com.sun.jer"
+ + "sey.spi.inject,com.sun.jersey.spi.dispatch,com.sun.jersey.server.impl.inject,c"
+ + "om.sun.jersey.core.spi.component.ioc,com.sun.jersey.spi.service\";version=\"1."
+ + "13.0\",com.sun.jersey.spi.monitoring;uses:=\"com.sun.jersey.api.model,com.sun."
+ + "jersey.spi.container,javax.ws.rs.ext\";version=\"1.13.0\",com.sun.jersey.api;u"
+ + "ses:=\"javax.ws.rs,javax.ws.rs.core,com.sun.jersey.core.header,com.sun.jersey."
+ + "core.spi.factory\";version=\"1.13.0\",com.sun.jersey.api.core;uses:=\"javax.ws"
+ + ".rs.core,com.sun.jersey.core.spi.scanning,com.sun.jersey.api.model,com.sun.jer"
+ + "sey.api.uri,javax.ws.rs,com.sun.jersey.core.header,com.sun.jersey.api.represen"
+ + "tation,com.sun.jersey.core.util,javax.ws.rs.ext,com.sun.jersey.api.container,c"
+ + "om.sun.jersey.spi.scanning,com.sun.jersey.spi.container,com.sun.jersey.server."
+ + "impl.application\";version=\"1.13.0\",com.sun.jersey.api.wadl.config;uses:=\"c"
+ + "om.sun.jersey.server.wadl,com.sun.jersey.api.core,com.sun.jersey.core.reflecti"
+ + "on,com.sun.jersey.server.wadl.generators\";version=\"1.13.0\",com.sun.jersey.a"
+ + "pi.model;uses:=\"javax.ws.rs.core,com.sun.jersey.spi.container\";version=\"1.1"
+ + "3.0\",com.sun.jersey.api.view;version=\"1.13.0\",com.sun.jersey.api.container."
+ + "filter;uses:=\"javax.ws.rs,com.sun.jersey.spi.container,javax.ws.rs.core,com.s"
+ + "un.jersey.api.container,com.sun.jersey.api.core,com.sun.jersey.core.util,com.s"
+ + "un.jersey.core.header,com.sun.jersey.api.representation,com.sun.jersey.api.mod"
+ + "el,javax.annotation.security\";version=\"1.13.0\",com.sun.jersey.api.container"
+ + ";uses:=\"com.sun.jersey.api.core,com.sun.jersey.spi.container,com.sun.jersey.s"
+ + "pi.service,com.sun.jersey.core.spi.component.ioc\";version=\"1.13.0\",com.sun."
+ + "jersey.api.container.httpserver;uses:=\"com.sun.net.httpserver,com.sun.jersey."
+ + "api.core,com.sun.jersey.api.container,com.sun.jersey.core.spi.component.ioc\";"
+ + "version=\"1.13.0\",com.sun.research.ws.wadl;uses:=\"javax.xml.bind.annotation,"
+ + "javax.xml.bind.annotation.adapters,javax.xml.namespace\";version=\"1.13.0\"";
+ ExportPackageParser.parseExports(exportHeader);
+ }
+
+ private static TypeSafeMatcher<Parameter> parameterMatching(final String name, final String value) {
+ return new TypeSafeMatcher<Parameter>() {
+ @Override
+ protected boolean matchesSafely(Parameter parameter) {
+ return parameter.getName().equals(name) && parameter.getValue().equals(value);
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Parameter with name ").appendValue(name).appendText(" with value ").appendValue(value);
+ }
+ };
+ }
+
+ private static TypeSafeMatcher<Parameter> parameterMatching(final Parameter param) {
+ return parameterMatching(param.getName(), param.getValue());
+ }
+}
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
new file mode 100644
index 00000000000..23960323a31
--- /dev/null
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/osgi/ImportPackageTest.java
@@ -0,0 +1,130 @@
+// 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.osgi;
+
+import com.yahoo.container.plugin.osgi.ExportPackages.Export;
+import com.yahoo.container.plugin.osgi.ExportPackages.Parameter;
+import com.yahoo.container.plugin.osgi.ImportPackages.Import;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.emptySet;
+import static java.util.Collections.singletonList;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class ImportPackageTest {
+ private static final String PACKAGE_NAME = "com.yahoo.exported";
+ private Set<String> referencedPackages = Collections.singleton(PACKAGE_NAME);
+ private Map<String, Export> exports = exportByPackageName(new Export(singletonList(PACKAGE_NAME), Collections.emptyList()));
+ private Map<String, Export> exportsWithVersion = exportByPackageName(
+ new Export(singletonList(PACKAGE_NAME), singletonList(new Parameter("version", "1.3"))));
+
+ private static Map<String, Export> exportByPackageName(Export export) {
+ return ExportPackages.exportsByPackageName(singletonList(export));
+ }
+
+ @Test
+ public void require_that_non_implemented_import_with_matching_export_is_included() {
+ Set<Import> imports = calculateImports(referencedPackages, emptySet(), exports);
+ assertThat(imports, contains(importMatching(PACKAGE_NAME, "")));
+ }
+
+ @Test
+ public void require_that_non_implemented_import_without_matching_export_is_excluded() {
+ Set<Import> imports = calculateImports(referencedPackages, emptySet(), emptyMap());
+ assertThat(imports, empty());
+ }
+
+ @Test
+ public void require_that_implemented_import_with_matching_export_is_excluded() {
+ Set<Import> imports = calculateImports(referencedPackages, referencedPackages, exports);
+
+ assertThat(imports, empty());
+ }
+
+ @Test
+ public void require_that_version_is_included() {
+ Set<Import> imports = calculateImports(referencedPackages, emptySet(), exportsWithVersion);
+
+ assertThat(imports, contains(importMatching(PACKAGE_NAME, "1.3")));
+ }
+
+ @Test
+ public void require_that_all_versions_up_to_the_next_major_version_is_in_range() {
+ assertThat(new Import("foo", Optional.of("1.2")).importVersionRange().get(), is("[1.2,2)"));
+ }
+
+ // TODO: Detecting guava packages should be based on bundle-symbolicName, not package name.
+ @Test
+ public void require_that_for_guava_all_future_major_versions_are_in_range() {
+ Optional<String> rangeWithInfiniteUpperLimit = Optional.of("[18.1," + ImportPackages.INFINITE_VERSION + ")");
+ assertThat(new Import("com.google.common", Optional.of("18.1")).importVersionRange(), is(rangeWithInfiniteUpperLimit));
+ assertThat(new Import("com.google.common.foo", Optional.of("18.1")).importVersionRange(), is(rangeWithInfiniteUpperLimit));
+ assertThat(new Import("com.google.commonality", Optional.of("18.1")).importVersionRange(), is(Optional.of("[18.1,19)")));
+ }
+
+ @Test
+ public void require_that_none_version_gives_non_version_range() {
+ assertThat(new Import("foo", Optional.empty()).importVersionRange(), is(Optional.empty()));
+ }
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void require_that_exception_is_thrown_when_major_component_is_non_numeric() {
+ expectedException.expect(IllegalArgumentException.class);
+ new Import("foo", Optional.of("1notValid.2"));
+ }
+
+ @Test
+ public void require_that_osgi_import_supports_missing_version() {
+ assertThat(new Import("com.yahoo.exported", Optional.empty()).asOsgiImport(), is("com.yahoo.exported"));
+ }
+
+ @Test
+ public void require_that_osgi_import_version_range_includes_all_versions_from_the_current_up_to_the_next_major_version() {
+ assertThat(new Import("com.yahoo.exported", Optional.of("1.2")).asOsgiImport(), is("com.yahoo.exported;version=\"[1.2,2)\""));
+ }
+
+ @Test
+ public void require_that_osgi_import_version_range_ignores_qualifier() {
+ assertThat(new Import("com.yahoo.exported", Optional.of("1.2.3.qualifier")).asOsgiImport(),
+ is("com.yahoo.exported;version=\"[1.2.3,2)\""));
+ }
+
+ private static Set<Import> calculateImports(Set<String> referencedPackages, Set<String> implementedPackages,
+ Map<String, Export> exportedPackages) {
+ return new HashSet<>(ImportPackages.calculateImports(referencedPackages, implementedPackages, exportedPackages).values());
+ }
+
+ private static TypeSafeMatcher<Import> importMatching(String packageName, String version) {
+ return new TypeSafeMatcher<Import>() {
+ @Override
+ protected boolean matchesSafely(Import anImport) {
+ return anImport.packageName().equals(packageName) && anImport.version().equals(version);
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("an Import of package ").appendValue(packageName).appendText(" with version ").appendValue(version);
+ }
+ };
+ }
+}
diff --git a/bundle-plugin/src/test/scala/com/yahoo/container/plugin/bundle/AnalyzeBundleTest.scala b/bundle-plugin/src/test/scala/com/yahoo/container/plugin/bundle/AnalyzeBundleTest.scala
deleted file mode 100644
index 3ef1b1fea87..00000000000
--- a/bundle-plugin/src/test/scala/com/yahoo/container/plugin/bundle/AnalyzeBundleTest.scala
+++ /dev/null
@@ -1,41 +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.container.plugin.bundle
-
-import org.scalatest.junit.{AssertionsForJUnit, JUnitSuite}
-import org.junit.Test
-import com.yahoo.container.plugin.bundle.AnalyzeBundle.PublicPackages
-import com.yahoo.container.plugin.osgi.ExportPackages
-import java.io.File
-
-import org.scalatest.Matchers
-
-/**
- * @author tonytv
- */
-class AnalyzeBundleTest extends JUnitSuite with AssertionsForJUnit with Matchers {
- val jarDir = new File("src/test/resources/jar")
-
- val PublicPackages(exports, globals) = AnalyzeBundle.publicPackagesAggregated(
- List("notAOsgiBundle.jar", "simple1.jar").map(jarFile))
-
- val exportsByPackageName = ExportPackages.exportsByPackageName(exports)
-
- def jarFile(name : String) : File = new File(jarDir, name)
-
- @Test
- def require_that_non_osgi_bundles_are_ignored() {
- exportsByPackageName should not contain key("com.yahoo.sample.exported.package.ignored")
- }
-
- @Test
- def require_that_exports_are_retrieved_from_manifest_in_jars() {
- exportsByPackageName.keySet should be (Set("com.yahoo.sample.exported.package"))
- }
-
- @Test
- def require_that_invalid_exports_throws_exception() {
- val exception = intercept[Exception](AnalyzeBundle.publicPackages(jarFile("errorExport.jar")))
- exception.getMessage should startWith regex ("Invalid manifest in bundle '.*errorExport.jar'")
- exception.getCause.getMessage should startWith ("Failed parsing Export-Package")
- }
-}
diff --git a/bundle-plugin/src/test/scala/com/yahoo/container/plugin/classanalysis/AnalyzeClassTest.scala b/bundle-plugin/src/test/scala/com/yahoo/container/plugin/classanalysis/AnalyzeClassTest.scala
deleted file mode 100644
index e2d0e3c6f10..00000000000
--- a/bundle-plugin/src/test/scala/com/yahoo/container/plugin/classanalysis/AnalyzeClassTest.scala
+++ /dev/null
@@ -1,118 +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.container.plugin.classanalysis
-
-import org.junit.Test
-import org.scalatest.junit.{AssertionsForJUnit, JUnitSuite}
-import java.awt.Image
-import java.awt.image.{ImagingOpException, Kernel}
-
-import sampleclasses._
-import TestUtilities._
-import com.yahoo.osgi.annotation.{ExportPackage, Version}
-import javax.security.auth.login.LoginException
-
-import org.scalatest.Matchers
-
-/**
- * Tests that analysis of class files works.
- * @author tonytv
- */
-class AnalyzeClassTest extends JUnitSuite with AssertionsForJUnit with Matchers {
- @Test def require_that_full_class_name_is_returned() {
- analyzeClass[Base].name should be(name[Base])
- }
-
- @Test def require_that_base_class_is_included() {
- analyzeClass[Derived].referencedClasses should contain (name[Base])
- }
-
- @Test def require_that_implemented_interfaces_are_included() {
- analyzeClass[Base].referencedClasses should (contain (name[Interface1]) and contain (name[Interface2]))
- }
-
- @Test def require_that_interface_can_be_analyzed() {
- val classMetaData = analyzeClass[Interface1]
-
- classMetaData.name should be(name[Interface1])
- classMetaData.referencedClasses should contain(name[Interface2])
- }
-
- @Test def require_that_return_type_is_included() {
- analyzeClass[Interface1].referencedClasses should contain (name[Image])
- }
-
- @Test def require_that_parameters_are_included() {
- analyzeClass[Interface1].referencedClasses should contain (name[Kernel])
- }
-
- @Test def require_that_exceptions_are_included() {
- analyzeClass[Interface1].referencedClasses should contain (name[ImagingOpException])
- }
-
- @Test def require_that_basic_types_ignored() {
- analyzeClass[Interface1].referencedClasses should not (contain ("int") or contain ("float"))
- }
-
- @Test def require_that_arrays_of_basic_types_ignored() {
- analyzeClass[Interface1].referencedClasses should not (contain ("int[]") or contain ("int[][]"))
- }
-
- @Test def require_that_instance_field_types_are_included() {
- analyzeClass[Fields].referencedClasses should contain (name[String])
- }
-
- @Test def require_that_static_field_types_are_included() {
- analyzeClass[Fields].referencedClasses should contain (name[java.util.List[_]])
- }
-
- @Test def require_that_field_annotation_is_included() {
- analyzeClass[Fields].referencedClasses should contain (name[DummyAnnotation])
- }
-
- @Test def require_that_class_annotation_is_included() {
- analyzeClass[ClassAnnotation].referencedClasses should contain(name[DummyAnnotation])
- }
-
- @Test def require_that_method_annotation_is_included() {
- analyzeClass[MethodAnnotation].referencedClasses should contain(name[DummyAnnotation])
- }
-
- @Test def require_that_export_package_annotations_are_ignored() {
- Analyze.analyzeClass(classFile("com.yahoo.container.plugin.classanalysis.sampleclasses.package-info")).
- referencedClasses should not (contain (name[ExportPackage]) or contain (name[Version]))
- }
-
- @Test def require_that_export_annotations_are_processed() {
- Analyze.analyzeClass(classFile("com.yahoo.container.plugin.classanalysis.sampleclasses.package-info")).
- exportPackage should be (Some(ExportPackageAnnotation(3, 1, 4, "TEST_QUALIFIER-2")))
- }
-
- @Test def require_that_export_annotations_are_validated() {
- val exception = intercept[RuntimeException] {
- Analyze.analyzeClass(classFile("com.yahoo.container.plugin.classanalysis.sampleclasses.invalid.package-info"))
- }
-
- exception.getMessage should include ("invalid/package-info")
- exception.getCause.getMessage should include ("qualifier must follow the format")
- exception.getCause.getMessage should include ("'EXAMPLE INVALID QUALIFIER'")
- }
-
- @Test def require_that_catch_clauses_are_included() {
- Analyze.analyzeClass(classFile("com.yahoo.container.plugin.classanalysis.sampleclasses.CatchException")).
- referencedClasses should contain(name[LoginException])
- }
-
- @Test def require_that_class_references_are_included() {
- Analyze.analyzeClass(classFile("com.yahoo.container.plugin.classanalysis.sampleclasses.ClassReference")).
- referencedClasses should contain(name[Interface1])
- }
-
- @Test def require_that_return_type_of_method_invocations_are_included() {
- analyzeClass[MethodInvocation].referencedClasses should contain(name[Image])
- }
-
- @Test def require_that_attributes_are_included() {
- //Uses com/coremedia/iso/Utf8.class from com.googlecode.mp4parser:isoparser:1.0-RC-1
- Analyze.analyzeClass(classFile("class/Utf8")).referencedClasses should contain("org.aspectj.weaver.MethodDeclarationLineNumber")
- }
-}
diff --git a/bundle-plugin/src/test/scala/com/yahoo/container/plugin/classanalysis/AnalyzeMethodBodyTest.scala b/bundle-plugin/src/test/scala/com/yahoo/container/plugin/classanalysis/AnalyzeMethodBodyTest.scala
deleted file mode 100644
index 43f52884f39..00000000000
--- a/bundle-plugin/src/test/scala/com/yahoo/container/plugin/classanalysis/AnalyzeMethodBodyTest.scala
+++ /dev/null
@@ -1,52 +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.container.plugin.classanalysis
-
-import org.scalatest.junit.{AssertionsForJUnit, JUnitSuite}
-import sampleclasses._
-import TestUtilities._
-import org.junit.Test
-import java.io.PrintStream
-
-import org.scalatest.Matchers
-
-/**
- * Tests that classes used in method bodies are included in the imports list.
- * @author tonytv
- */
-class AnalyzeMethodBodyTest extends JUnitSuite with AssertionsForJUnit with Matchers {
- @Test def require_that_class_of_locals_are_included() {
- analyzeClass[Methods].referencedClasses should contain(name[Base])
- }
-
- @Test def require_that_class_of_locals_in_static_method_are_included() {
- analyzeClass[Methods].referencedClasses should contain(name[Derived])
- }
-
- @Test def require_that_field_references_are_included() {
- analyzeClass[Methods].referencedClasses should (contain (name[java.util.List[_]]) and contain (name[Fields]))
- }
-
- @Test def require_that_class_owning_field_is_included() {
- analyzeClass[Methods].referencedClasses should contain (name[System])
- }
-
- @Test def require_that_class_containing_method_is_included() {
- analyzeClass[Methods].referencedClasses should contain (name[PrintStream])
- }
-
- @Test def require_that_element_of_new_multidimensional_array_is_included() {
- analyzeClass[Methods].referencedClasses should contain (name[Interface1])
- }
-
- @Test def require_that_basic_arrays_are_not_included() {
- analyzeClass[Methods].referencedClasses should not (contain("int[]"))
- }
-
- @Test def require_that_container_generic_parameters_are_included() {
- analyzeClass[Methods].referencedClasses should contain(name[Dummy])
- }
-
- @Test def require_that_class_owning_method_handler_is_included() {
- analyzeClass[Methods].referencedClasses should contain(name[ClassWithMethod])
- }
-}
diff --git a/bundle-plugin/src/test/scala/com/yahoo/container/plugin/classanalysis/TestUtilities.scala b/bundle-plugin/src/test/scala/com/yahoo/container/plugin/classanalysis/TestUtilities.scala
deleted file mode 100644
index fbc3554e9e9..00000000000
--- a/bundle-plugin/src/test/scala/com/yahoo/container/plugin/classanalysis/TestUtilities.scala
+++ /dev/null
@@ -1,18 +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.container.plugin.classanalysis
-
-import scala.reflect.ClassTag
-import java.io.File
-
-/**
- * @author tonytv
- */
-object TestUtilities {
- def analyzeClass[T](implicit m: ClassTag[T]) =
- Analyze.analyzeClass(classFile(name(m)))
-
- def classFile(className : String) =
- new File("target/test-classes/" + className.replace('.', '/') + ".class")
-
- def name[T](implicit m: ClassTag[T]) = m.runtimeClass.getName
-}
diff --git a/bundle-plugin/src/test/scala/com/yahoo/container/plugin/osgi/ExportPackageParserTest.scala b/bundle-plugin/src/test/scala/com/yahoo/container/plugin/osgi/ExportPackageParserTest.scala
deleted file mode 100644
index 5ddc7fef16d..00000000000
--- a/bundle-plugin/src/test/scala/com/yahoo/container/plugin/osgi/ExportPackageParserTest.scala
+++ /dev/null
@@ -1,63 +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.container.plugin.osgi
-
-import org.scalatest.junit.{AssertionsForJUnit, JUnitSuite}
-import org.junit.Test
-import ExportPackages.{Export, Parameter}
-import org.scalatest.Matchers
-
-/**
- * @author tonytv
- */
-class ExportPackageParserTest extends JUnitSuite with AssertionsForJUnit with Matchers {
- val versionParameter = Parameter("version", "1.2.3.sample")
-
- @Test
- def require_that_package_is_parsed_correctly() {
- ExportPackageParser.parseAll("sample.exported.package").get should
- be (List(Export(List("sample.exported.package"), List())))
- }
-
- @Test
- def require_that_version_is_parsed_correctly() {
- ExportPackageParser.parseAll("com.yahoo.sample.exported.package;version=\"1.2.3.sample\"").get should
- be (List(Export(List("com.yahoo.sample.exported.package"), List(versionParameter))))
- }
-
- @Test
- def require_that_multiple_packages_with_same_parameters_is_parsed_correctly() {
- ExportPackageParser.parseAll("exported.package1;exported.package2;version=\"1.2.3.sample\"").get should
- be (List(Export(List("exported.package1", "exported.package2"), List(versionParameter))))
- }
-
- @Test
- def require_that_multiple_parameters_for_a_package_is_parsed_correctly() {
- ExportPackageParser.parseAll("exported.package;version=\"1.2.3.sample\";param2=true").get.head.parameters should
- be (List(versionParameter, Parameter("param2", "true")))
- }
-
- @Test
- def require_that_multiple_exports_are_parsed_correctly() {
- ExportPackageParser.parseAll("exported.package1,exported.package2").get should
- be(List(
- Export(List("exported.package1"), List()),
- Export(List("exported.package2"), List())))
-
- ExportPackageParser.parseAll("exported.package1;version=\"1.2.3.sample\",exported.package2").get should
- be (List(
- Export(List("exported.package1"), List(versionParameter)),
- Export(List("exported.package2"), List())))
-
- ExportPackageParser.parseAll("exported.package1,exported.package2;version=\"1.2.3.sample\"").get should
- be(List(
- Export(List("exported.package1"), List()),
- Export(List("exported.package2"), List(versionParameter))))
- }
-
- @Test
- def require_that_long_string_literals_do_not_cause_stack_overflow_error() {
- //From jersey-server-1.13.jar
- val export = """com.sun.jersey.server.impl.wadl;uses:="com.sun.jersey.api.model,com.sun.research.ws.wadl,com.sun.jersey.api.wadl.config,com.sun.jersey.server.wadl,com.sun.jersey.api.core,javax.xml.bind,javax.ws.rs.core,com.sun.jersey.server.impl.uri,com.sun.jersey.core.spi.factory,com.sun.jersey.server.impl.model.method,com.sun.jersey.api.uri,com.sun.jersey.core.header,com.sun.jersey.spi.dispatch,javax.ws.rs,com.sun.jersey.spi.resource";version="1.13.0",com.sun.jersey.server.impl.model.parameter.multivalued;uses:="com.sun.jersey.spi,javax.ws.rs.core,com.sun.jersey.api.container,com.sun.jersey.impl,javax.xml.parsers,org.xml.sax,javax.xml.transform,javax.xml.bind.annotation,javax.xml.transform.sax,com.sun.jersey.spi.inject,javax.xml.bind,javax.ws.rs.ext,com.sun.jersey.api.model,com.sun.jersey.core.reflection,javax.ws.rs,com.sun.jersey.core.spi.component,com.sun.jersey.core.header";version="1.13.0",com.sun.jersey.server.impl.model.parameter;uses:="com.sun.jersey.api.model,com.sun.jersey.core.spi.component,com.sun.jersey.server.impl.model.parameter.multivalued,com.sun.jersey.spi.inject,com.sun.jersey.api,com.sun.jersey.api.core,com.sun.jersey.server.impl.inject,javax.ws.rs.core,javax.ws.rs,com.sun.jersey.core.header,com.sun.jersey.api.representation";version="1.13.0",com.sun.jersey.server.impl.application;uses:="com.sun.jersey.core.spi.component,com.sun.jersey.api.core,com.sun.jersey.spi,com.sun.jersey.spi.inject,javax.ws.rs.core,com.sun.jersey.api.container,javax.ws.rs.ext,com.sun.jersey.spi.container,com.sun.jersey.core.reflection,com.sun.jersey.api.model,com.sun.jersey.impl,com.sun.jersey.spi.dispatch,com.sun.jersey.server.impl.model,com.sun.jersey.server.impl.wadl,com.sun.jersey.server.impl.uri,com.sun.jersey.core.spi.factory,com.sun.jersey.api.uri,com.sun.jersey.server.impl.uri.rules,com.sun.jersey.spi.uri.rules,com.sun.jersey.server.spi.component,com.sun.jersey.core.util,com.sun.jersey.core.header,com.sun.jersey.core.spi.component.ioc,javax.ws.rs,com.sun.jersey.server.impl,com.sun.jersey.server.wadl,com.sun.jersey.server.impl.inject,com.sun.jersey.server.impl.component,com.sun.jersey.spi.monitoring,com.sun.jersey.server.impl.monitoring,com.sun.jersey.api.container.filter,com.sun.jersey.server.impl.model.parameter.multivalued,com.sun.jersey.server.impl.model.parameter,com.sun.jersey.server.impl.template,com.sun.jersey.spi.template,com.sun.jersey.server.impl.resource,com.sun.jersey.server.impl.modelapi.annotation,com.sun.jersey.server.impl.container.filter,com.sun.jersey.server.impl.modelapi.validation,com.sun.jersey.api,com.sun.jersey.spi.service";version="1.13.0",com.sun.jersey.server.impl.component;uses:="com.sun.jersey.api.model,com.sun.jersey.core.spi.component,com.sun.jersey.server.spi.component,com.sun.jersey.api.core,com.sun.jersey.core.spi.component.ioc,com.sun.jersey.server.impl.inject,com.sun.jersey.server.impl.resource,com.sun.jersey.api.container,com.sun.jersey.core.reflection,com.sun.jersey.spi.inject";version="1.13.0",com.sun.jersey.server.impl.provider;uses:="com.sun.jersey.core.spi.factory,com.sun.jersey.api.container,com.sun.jersey.api.core,javax.ws.rs.core";version="1.13.0",com.sun.jersey.server.impl.template;uses:="com.sun.jersey.core.spi.component,com.sun.jersey.api.view,com.sun.jersey.spi.template,javax.ws.rs.core,com.sun.jersey.core.header,com.sun.jersey.server.impl.model.method,com.sun.jersey.spi.dispatch,com.sun.jersey.api.uri,javax.ws.rs,com.sun.jersey.spi.inject,javax.ws.rs.ext,com.sun.jersey.server.impl.uri.rules,com.sun.jersey.server.probes,com.sun.jersey.core.reflection,com.sun.jersey.spi.uri.rules,com.sun.jersey.spi.container,com.sun.jersey.api.core";version="1.13.0",com.sun.jersey.server.osgi;uses:="com.sun.jersey.server.impl.provider,org.osgi.framework,javax.ws.rs.ext";version="1.13.0",com.sun.jersey.server.wadl.generators.resourcedoc.model;uses:="javax.xml.bind.annotation,javax.xml.namespace";version="1.13.0",com.sun.jersey.server.impl.resource;uses:="com.sun.jersey.api.model,com.sun.jersey.core.spi.component,com.sun.jersey.server.spi.component,com.sun.jersey.api.core,com.sun.jersey.api.container,javax.ws.rs,com.sun.jersey.server.impl.inject,com.sun.jersey.core.spi.component.ioc,javax.ws.rs.core";version="1.13.0",com.sun.jersey.server.impl.monitoring;uses:="com.sun.jersey.spi.monitoring,com.sun.jersey.spi.service,com.sun.jersey.api.model,com.sun.jersey.spi.container,javax.ws.rs.ext,com.sun.jersey.core.spi.component";version="1.13.0",com.sun.jersey.server.impl.modelapi.annotation;uses:="com.sun.jersey.api.model,javax.ws.rs.core,javax.ws.rs,com.sun.jersey.core.reflection,com.sun.jersey.core.header,com.sun.jersey.impl";version="1.13.0",com.sun.jersey.server.impl.container;uses:="com.sun.jersey.server.impl.application,com.sun.jersey.spi.container,com.sun.jersey.api.container";version="1.13.0",com.sun.jersey.server.wadl;uses:="javax.ws.rs.core,com.sun.research.ws.wadl,javax.xml.namespace,com.sun.jersey.api.model,javax.xml.bind,javax.ws.rs,com.sun.jersey.server.wadl.generators,com.sun.jersey.server.impl.modelapi.annotation,com.sun.jersey.server.impl";version="1.13.0",com.sun.jersey.server.impl.model.method.dispatch;uses:="com.sun.jersey.api.model,com.sun.jersey.api.core,com.sun.jersey.spi.container,com.sun.jersey.server.impl.inject,com.sun.jersey.api,javax.ws.rs.core,com.sun.jersey.core.spi.factory,com.sun.jersey.spi.inject,com.sun.jersey.spi.dispatch,com.sun.jersey.core.spi.component,javax.ws.rs,com.sun.jersey.server.impl.model.parameter.multivalued,com.sun.jersey.api.representation,com.sun.jersey.api.container";version="1.13.0",com.sun.jersey.server.impl;uses:="javax.naming,com.sun.jersey.api.core,com.sun.jersey.core.header,javax.ws.rs.core,com.sun.jersey.server.impl.model,com.sun.jersey.spi.container";version="1.13.0",com.sun.jersey.server.wadl.generators.resourcedoc;uses:="com.sun.jersey.api.model,com.sun.jersey.server.wadl.generators.resourcedoc.model,com.sun.jersey.server.wadl.generators.resourcedoc.xhtml,com.sun.research.ws.wadl,javax.xml.namespace,com.sun.jersey.server.wadl,javax.xml.bind,javax.ws.rs.core";version="1.13.0",com.sun.jersey.server.impl.container.httpserver;uses:="com.sun.net.httpserver,com.sun.jersey.spi.container,javax.ws.rs.core,com.sun.jersey.core.header,com.sun.jersey.api.container,com.sun.jersey.core.util,com.sun.jersey.api.core";version="1.13.0",com.sun.jersey.server.impl.container.filter;uses:="com.sun.jersey.api.model,com.sun.jersey.spi.container,com.sun.jersey.core.spi.component,com.sun.jersey.api.core,javax.ws.rs,com.sun.jersey.server.impl.uri,javax.ws.rs.core";version="1.13.0",com.sun.jersey.server.wadl.generators.resourcedoc.xhtml;uses:="javax.xml.bind,javax.xml.namespace,javax.xml.bind.annotation";version="1.13.0",com.sun.jersey.server.impl.uri.rules;uses:="com.sun.jersey.spi.uri.rules,com.sun.jersey.api.uri,com.sun.jersey.api.core,com.sun.jersey.server.impl.model.method,com.sun.jersey.spi.dispatch,com.sun.jersey.core.header,javax.ws.rs.core,com.sun.jersey.api.model,com.sun.jersey.server.probes,com.sun.jersey.core.reflection,com.sun.jersey.server.impl.template,com.sun.jersey.spi.monitoring,com.sun.jersey.api,com.sun.jersey.spi.container,com.sun.jersey.server.impl.uri,javax.ws.rs,com.sun.jersey.api.container,com.sun.jersey.server.impl.inject,com.sun.jersey.spi.inject,com.sun.jersey.server.impl.uri.rules.automata";version="1.13.0",com.sun.jersey.server.spi.component;uses:="com.sun.jersey.spi.inject,com.sun.jersey.api.model,com.sun.jersey.core.spi.component,com.sun.jersey.api.core,com.sun.jersey.server.impl.inject,com.sun.jersey.api.container,com.sun.jersey.core.spi.component.ioc";version="1.13.0",com.sun.jersey.server.probes;version="1.13.0",com.sun.jersey.server.wadl.generators;uses:="com.sun.research.ws.wadl,javax.xml.bind.annotation,com.sun.jersey.api.model,com.sun.jersey.server.wadl,javax.xml.bind,javax.ws.rs.core,com.sun.jersey.api,javax.xml.namespace,javax.xml.transform,javax.xml.transform.stream";version="1.13.0",com.sun.jersey.server.impl.modelapi.validation;uses:="com.sun.jersey.api.model,javax.ws.rs,com.sun.jersey.impl,com.sun.jersey.api.core,com.sun.jersey.core.reflection,javax.ws.rs.core";version="1.13.0",com.sun.jersey.server.impl.model.method;uses:="com.sun.jersey.api.container,com.sun.jersey.spi.dispatch,com.sun.jersey.api.uri,com.sun.jersey.api.model,com.sun.jersey.server.impl.container.filter,com.sun.jersey.impl,com.sun.jersey.spi.container,com.sun.jersey.spi.inject,com.sun.jersey.api.core,javax.ws.rs.core,com.sun.jersey.core.header";version="1.13.0",com.sun.jersey.server.impl.model;uses:="javax.ws.rs,com.sun.jersey.impl,com.sun.jersey.api.container,com.sun.jersey.core.header,com.sun.jersey.core.header.reader,com.sun.jersey.api.core,javax.ws.rs.core,com.sun.jersey.server.impl.model.method,com.sun.jersey.server.impl.container.filter,com.sun.jersey.api.model,com.sun.jersey.server.impl.wadl,com.sun.jersey.spi.monitoring,com.sun.jersey.server.impl.uri,com.sun.jersey.spi.container,com.sun.jersey.server.impl.inject,com.sun.jersey.spi.inject,com.sun.jersey.api.uri,com.sun.jersey.core.spi.component,com.sun.jersey.server.impl.uri.rules,com.sun.jersey.server.impl.template,com.sun.jersey.api.view,com.sun.jersey.spi.uri.rules";version="1.13.0",com.sun.jersey.server.impl.uri.rules.automata;uses:="com.sun.jersey.server.impl.uri,com.sun.jersey.spi.uri.rules,com.sun.jersey.server.impl.uri.rules,com.sun.jersey.api.uri";version="1.13.0",com.sun.jersey.server.impl.uri;uses:="com.sun.jersey.api.uri,javax.ws.rs.core";version="1.13.0",com.sun.jersey.server.impl.inject;uses:="com.sun.jersey.api.core,com.sun.jersey.spi.inject,javax.ws.rs,com.sun.jersey.api.container,com.sun.jersey.api.model,com.sun.jersey.core.spi.component,com.sun.jersey.core.spi.factory";version="1.13.0",com.sun.jersey.spi.scanning;uses:="org.objectweb.asm,com.sun.jersey.core.reflection,com.sun.jersey.core.spi.scanning,javax.ws.rs,javax.ws.rs.ext";version="1.13.0",com.sun.jersey.spi.resource;uses:="com.sun.jersey.server.impl.resource,com.sun.jersey.server.spi.component";version="1.13.0",com.sun.jersey.spi.template;uses:="com.sun.jersey.api.view,javax.ws.rs.core,com.sun.jersey.api.container";version="1.13.0",com.sun.jersey.spi.dispatch;uses:="com.sun.jersey.api.core";version="1.13.0",com.sun.jersey.spi.uri.rules;uses:="com.sun.jersey.api.core,com.sun.jersey.api.model,com.sun.jersey.spi.container,com.sun.jersey.api.uri";version="1.13.0",com.sun.jersey.spi.container;uses:="javax.ws.rs,com.sun.jersey.api.representation,com.sun.jersey.core.header,com.sun.jersey.spi,javax.ws.rs.core,com.sun.jersey.api.container,com.sun.jersey.api.core,com.sun.jersey.core.util,com.sun.jersey.core.header.reader,com.sun.jersey.server.impl,com.sun.jersey.core.reflection,javax.ws.rs.ext,com.sun.jersey.server.impl.model,com.sun.jersey.api,com.sun.jersey.api.uri,com.sun.jersey.core.spi.factory,com.sun.jersey.spi.monitoring,com.sun.jersey.api.model,com.sun.jersey.core.spi.component,com.sun.jersey.server.impl.application,com.sun.jersey.impl,com.sun.jersey.spi.inject,com.sun.jersey.spi.dispatch,com.sun.jersey.server.impl.inject,com.sun.jersey.core.spi.component.ioc,com.sun.jersey.spi.service";version="1.13.0",com.sun.jersey.spi.monitoring;uses:="com.sun.jersey.api.model,com.sun.jersey.spi.container,javax.ws.rs.ext";version="1.13.0",com.sun.jersey.api;uses:="javax.ws.rs,javax.ws.rs.core,com.sun.jersey.core.header,com.sun.jersey.core.spi.factory";version="1.13.0",com.sun.jersey.api.core;uses:="javax.ws.rs.core,com.sun.jersey.core.spi.scanning,com.sun.jersey.api.model,com.sun.jersey.api.uri,javax.ws.rs,com.sun.jersey.core.header,com.sun.jersey.api.representation,com.sun.jersey.core.util,javax.ws.rs.ext,com.sun.jersey.api.container,com.sun.jersey.spi.scanning,com.sun.jersey.spi.container,com.sun.jersey.server.impl.application";version="1.13.0",com.sun.jersey.api.wadl.config;uses:="com.sun.jersey.server.wadl,com.sun.jersey.api.core,com.sun.jersey.core.reflection,com.sun.jersey.server.wadl.generators";version="1.13.0",com.sun.jersey.api.model;uses:="javax.ws.rs.core,com.sun.jersey.spi.container";version="1.13.0",com.sun.jersey.api.view;version="1.13.0",com.sun.jersey.api.container.filter;uses:="javax.ws.rs,com.sun.jersey.spi.container,javax.ws.rs.core,com.sun.jersey.api.container,com.sun.jersey.api.core,com.sun.jersey.core.util,com.sun.jersey.core.header,com.sun.jersey.api.representation,com.sun.jersey.api.model,javax.annotation.security";version="1.13.0",com.sun.jersey.api.container;uses:="com.sun.jersey.api.core,com.sun.jersey.spi.container,com.sun.jersey.spi.service,com.sun.jersey.core.spi.component.ioc";version="1.13.0",com.sun.jersey.api.container.httpserver;uses:="com.sun.net.httpserver,com.sun.jersey.api.core,com.sun.jersey.api.container,com.sun.jersey.core.spi.component.ioc";version="1.13.0",com.sun.research.ws.wadl;uses:="javax.xml.bind.annotation,javax.xml.bind.annotation.adapters,javax.xml.namespace";version="1.13.0"""
- ExportPackageParser.parseAll(export) //throws StackOverflow exception on scala library 2.10.2
- }
-}
diff --git a/bundle-plugin/src/test/scala/com/yahoo/container/plugin/osgi/ImportPackageTest.scala b/bundle-plugin/src/test/scala/com/yahoo/container/plugin/osgi/ImportPackageTest.scala
deleted file mode 100644
index 4011f170da9..00000000000
--- a/bundle-plugin/src/test/scala/com/yahoo/container/plugin/osgi/ImportPackageTest.scala
+++ /dev/null
@@ -1,95 +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.container.plugin.osgi
-
-import org.scalatest.junit.{AssertionsForJUnit, JUnitSuite}
-import org.junit.Test
-import ImportPackages.Import
-import ExportPackages.{Export, Parameter}
-import org.scalatest.Matchers
-
-/**
- * @author tonytv
- */
-class ImportPackageTest extends JUnitSuite with AssertionsForJUnit with Matchers {
- val referencedPackages = Set("com.yahoo.exported")
- val exports = exportByPackageName(Export(List("com.yahoo.exported"), List()))
- val exportsWithVersion = exportByPackageName(exports.head._2.copy(parameters = List(Parameter("version", "1.3"))))
-
- def exportByPackageName(export : Export) = ExportPackages.exportsByPackageName(List(export))
- @Test
- def require_that_non_implemented_import_with_matching_export_is_included() {
- val imports = calculateImports(referencedPackages, implementedPackages = Set(), exportedPackages = exports)
- imports should be (Set(Import("com.yahoo.exported", None)))
- }
-
-
- @Test
- def require_that_non_implemented_import_without_matching_export_is_excluded() {
- val imports = calculateImports(referencedPackages, implementedPackages = Set(), exportedPackages = Map())
- imports should be (Set())
- }
-
- @Test
- def require_that_implemented_import_with_matching_export_is_excluded() {
- val imports = calculateImports(
- referencedPackages,
- implementedPackages = referencedPackages,
- exportedPackages = exports)
-
- imports should be (Set())
- }
-
- @Test
- def require_that_version_is_included() {
- val imports = calculateImports(referencedPackages, implementedPackages = Set(), exportedPackages = exportsWithVersion)
-
- imports should be (Set(Import("com.yahoo.exported", Some("1.3"))))
- }
-
- @Test
- def require_that_all_versions_up_to_the_next_major_version_is_in_range() {
- Import("foo", Some("1.2")).importVersionRange should be (Some("[1.2,2)"))
- }
-
- // TODO: Detecting guava packages should be based on bundle-symbolicName, not package name.
- @Test
- def require_that_for_guava_all_future_major_versions_are_in_range() {
- val rangeWithInfiniteUpperLimit = Some("[18.1," + ImportPackages.InfiniteVersion + ")")
- Import("com.google.common", Some("18.1")).importVersionRange should be (rangeWithInfiniteUpperLimit)
- Import("com.google.common.foo", Some("18.1")).importVersionRange should be (rangeWithInfiniteUpperLimit)
-
- Import("com.google.commonality", Some("18.1")).importVersionRange should be (Some("[18.1,19)"))
- }
-
- @Test
- def require_that_none_version_gives_non_version_range() {
- Import("foo", None).importVersionRange should be (None)
- }
-
- @Test
- def require_that_exception_is_thrown_when_major_component_is_non_numeric() {
- intercept[IllegalArgumentException](Import("foo", Some("1notValid.2")))
- }
-
- @Test
- def require_that_osgi_import_supports_missing_version() {
- Import("com.yahoo.exported", None).asOsgiImport should be ("com.yahoo.exported")
- }
-
- @Test
- def require_that_osgi_import_version_range_includes_all_versions_from_the_current_up_to_the_next_major_version() {
- Import("com.yahoo.exported", Some("1.2")).asOsgiImport should be ("com.yahoo.exported;version=\"[1.2,2)\"")
- }
-
- @Test
- def require_that_osgi_import_version_range_ignores_qualifier() {
- Import("com.yahoo.exported", Some("1.2.3.qualifier")).asOsgiImport should be ("com.yahoo.exported;version=\"[1.2.3,2)\"")
- }
-
-
- def calculateImports(referencedPackages : Set[String],
- implementedPackages : Set[String],
- exportedPackages : Map[String, Export]) : Set[Import] = {
- ImportPackages.calculateImports(referencedPackages, implementedPackages, exportedPackages).values.toSet
- }
-}
diff --git a/bundle-plugin/src/test/scala/com/yahoo/container/plugin/util/IOTest.scala b/bundle-plugin/src/test/scala/com/yahoo/container/plugin/util/IOTest.scala
deleted file mode 100644
index 7efb0392f5e..00000000000
--- a/bundle-plugin/src/test/scala/com/yahoo/container/plugin/util/IOTest.scala
+++ /dev/null
@@ -1,45 +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.container.plugin.util
-
-import org.scalatest.junit.{AssertionsForJUnit, JUnitSuite}
-import org.junit.Test
-import IO.using
-import java.io.Closeable
-
-import org.scalatest.Matchers
-
-/**
- * @author tonytv
- */
-class IOTest extends JUnitSuite with AssertionsForJUnit with Matchers {
- class ClosingException extends RuntimeException
- class FunctionException extends RuntimeException
-
- object throwWhenClosingResource extends Closeable {
- def close() {
- throw new ClosingException()
- }
- }
-
- def throwFunction(r : throwWhenClosingResource.type) = throw new FunctionException
- def nonThrowingFunction(r : throwWhenClosingResource.type) = 42
-
- @Test
- def require_that_function_exception_is_prioritized_over_closing_exception() {
- intercept[FunctionException]{
- using(throwWhenClosingResource, readOnly = false)(throwFunction)
- }
- }
-
- @Test
- def require_that_closing_exception_is_ignored_when_read_only() {
- using(throwWhenClosingResource, readOnly = true)(nonThrowingFunction) should be (nonThrowingFunction(null))
- }
-
- @Test
- def require_that_closing_exception_is_rethrown_when_not_read_only() {
- intercept[ClosingException] {
- using(throwWhenClosingResource, readOnly = false)(nonThrowingFunction)
- }
- }
-}
diff --git a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/FleetController.java b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/FleetController.java
index c2956fe738c..56fe679fc6a 100644
--- a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/FleetController.java
+++ b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/FleetController.java
@@ -746,7 +746,7 @@ public class FleetController implements NodeStateOrHostInfoChangeHandler, NodeAd
taskCompletion.getTask().notifyCompleted();
taskCompletionQueue.remove();
} else if (taskCompletion.getDeadlineTimePointMs() <= now) {
- log.fine(() -> String.format("Deferred task of type '%s' has exceeded wait deadline; completing with failure",
+ log.log(LogLevel.WARNING, () -> String.format("Deferred task of type '%s' has exceeded wait deadline; completing with failure",
taskCompletion.getTask().getClass().getName()));
taskCompletion.getTask().handleFailure(RemoteClusterControllerTask.FailureCondition.DEADLINE_EXCEEDED);
taskCompletion.getTask().notifyCompleted();
@@ -882,11 +882,11 @@ public class FleetController implements NodeStateOrHostInfoChangeHandler, NodeAd
*/
private void scheduleVersionDependentTasksForFutureCompletion(int completeAtVersion) {
// TODO expose and use monotonic clock instead of system clock
- final long deadlineTimePointMs = timer.getCurrentTimeInMillis() + options.getMaxDeferredTaskVersionWaitTime().toMillis();
+ final long maxDeadlineTimePointMs = timer.getCurrentTimeInMillis() + options.getMaxDeferredTaskVersionWaitTime().toMillis();
for (RemoteClusterControllerTask task : tasksPendingStateRecompute) {
log.finest(() -> String.format("Adding task of type '%s' to be completed at version %d",
task.getClass().getName(), completeAtVersion));
- taskCompletionQueue.add(new VersionDependentTaskCompletion(completeAtVersion, task, deadlineTimePointMs));
+ taskCompletionQueue.add(new VersionDependentTaskCompletion(completeAtVersion, task, maxDeadlineTimePointMs));
}
tasksPendingStateRecompute.clear();
}
diff --git a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/RemoteClusterControllerTask.java b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/RemoteClusterControllerTask.java
index e96209c083a..8382e127e13 100644
--- a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/RemoteClusterControllerTask.java
+++ b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/RemoteClusterControllerTask.java
@@ -5,6 +5,9 @@ import com.yahoo.vdslib.state.ClusterState;
import com.yahoo.vespa.clustercontroller.core.listeners.NodeAddedOrRemovedListener;
import com.yahoo.vespa.clustercontroller.core.listeners.NodeStateOrHostInfoChangeHandler;
+import java.time.Instant;
+import java.util.Optional;
+
public abstract class RemoteClusterControllerTask {
public static class Context {
@@ -65,6 +68,10 @@ public abstract class RemoteClusterControllerTask {
*/
public void handleFailure(FailureCondition condition) {}
+ public Optional<Instant> getDeadline() {
+ return Optional.empty();
+ }
+
public boolean isCompleted() {
synchronized (monitor) {
return completed;
diff --git a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/VersionDependentTaskCompletion.java b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/VersionDependentTaskCompletion.java
index 5d6a4f66467..28df5a8e35a 100644
--- a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/VersionDependentTaskCompletion.java
+++ b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/VersionDependentTaskCompletion.java
@@ -16,10 +16,12 @@ class VersionDependentTaskCompletion {
private final RemoteClusterControllerTask task;
private final long deadlineTimePointMs;
- VersionDependentTaskCompletion(long minimumVersion, RemoteClusterControllerTask task, long deadlineTimePointMs) {
+ VersionDependentTaskCompletion(long minimumVersion, RemoteClusterControllerTask task, long maxDeadlineTimePointMs) {
this.minimumVersion = minimumVersion;
this.task = task;
- this.deadlineTimePointMs = deadlineTimePointMs;
+ this.deadlineTimePointMs = task.getDeadline().map(deadline ->
+ Math.max(0, Math.min(deadline.toEpochMilli(), maxDeadlineTimePointMs)))
+ .orElse(maxDeadlineTimePointMs);
}
long getMinimumVersion() {
@@ -30,7 +32,9 @@ class VersionDependentTaskCompletion {
return task;
}
- long getDeadlineTimePointMs() { return deadlineTimePointMs; }
+ long getDeadlineTimePointMs() {
+ return deadlineTimePointMs;
+ }
@Override
public boolean equals(Object o) {
diff --git a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/restapiv2/Request.java b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/restapiv2/Request.java
index 5ac15e75127..4ef62ad3fdf 100644
--- a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/restapiv2/Request.java
+++ b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/restapiv2/Request.java
@@ -17,8 +17,8 @@ public abstract class Request<Result> extends RemoteClusterControllerTask {
// TODO a lot of this logic could be replaced with a CompleteableFuture
private Exception failure = null;
- private boolean resultSet = false;
- private Result result = null;
+ protected boolean resultSet = false;
+ protected Result result = null;
private final MasterState masterState;
diff --git a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/restapiv2/requests/SetNodeStateRequest.java b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/restapiv2/requests/SetNodeStateRequest.java
index ada068808f7..4d6738940a8 100644
--- a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/restapiv2/requests/SetNodeStateRequest.java
+++ b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/restapiv2/requests/SetNodeStateRequest.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.clustercontroller.core.restapiv2.requests;
import com.yahoo.log.LogLevel;
+import com.yahoo.time.TimeBudget;
import com.yahoo.vdslib.state.ClusterState;
import com.yahoo.vdslib.state.Node;
import com.yahoo.vdslib.state.NodeState;
@@ -21,8 +22,10 @@ import com.yahoo.vespa.clustercontroller.utils.staterestapi.requests.SetUnitStat
import com.yahoo.vespa.clustercontroller.utils.staterestapi.response.SetResponse;
import com.yahoo.vespa.clustercontroller.utils.staterestapi.response.UnitState;
+import java.time.Instant;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.logging.Logger;
public class SetNodeStateRequest extends Request<SetResponse> {
@@ -32,26 +35,33 @@ public class SetNodeStateRequest extends Request<SetResponse> {
private final Map<String, UnitState> newStates;
private final SetUnitStateRequest.Condition condition;
private final SetUnitStateRequest.ResponseWait responseWait;
-
+ private final WantedStateSetter wantedState;
+ private final TimeBudget timeBudget;
public SetNodeStateRequest(Id.Node id, SetUnitStateRequest setUnitStateRequest) {
+ this(id, setUnitStateRequest, SetNodeStateRequest::setWantedState);
+ }
+
+ /** Public for tests. */
+ public SetNodeStateRequest(Id.Node id, SetUnitStateRequest setUnitStateRequest, WantedStateSetter wantedState) {
super(MasterState.MUST_BE_MASTER);
this.id = id;
this.newStates = setUnitStateRequest.getNewState();
this.condition = setUnitStateRequest.getCondition();
this.responseWait = setUnitStateRequest.getResponseWait();
+ this.wantedState = wantedState;
+ this.timeBudget = setUnitStateRequest.timeBudget();
}
@Override
public SetResponse calculateResult(RemoteClusterControllerTask.Context context) throws StateRestApiException {
- SetResponse setResponse = setWantedState(
+ return wantedState.set(
context.cluster,
condition,
newStates,
id.getNode(),
context.nodeStateOrHostInfoChangeHandler,
context.currentConsolidatedState);
- return setResponse;
}
static NodeState getRequestedNodeState(Map<String, UnitState> newStates, Node n) throws StateRestApiException {
@@ -73,6 +83,17 @@ public class SetNodeStateRequest extends Request<SetResponse> {
return (responseWait == SetUnitStateRequest.ResponseWait.WAIT_UNTIL_CLUSTER_ACKED);
}
+ @Override
+ public Optional<Instant> getDeadline() {
+ return timeBudget.deadline();
+ }
+
+ @Override
+ public boolean isFailed() {
+ // Failure to set a node state is propagated as a 200 with wasModified false.
+ return super.isFailed() || (resultSet && !result.getWasModified());
+ }
+
static SetResponse setWantedState(
ContentCluster cluster,
SetUnitStateRequest.Condition condition,
diff --git a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/restapiv2/requests/SetNodeStatesForClusterRequest.java b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/restapiv2/requests/SetNodeStatesForClusterRequest.java
index 3f9e2b48eb5..b4d189bcd55 100644
--- a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/restapiv2/requests/SetNodeStatesForClusterRequest.java
+++ b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/restapiv2/requests/SetNodeStatesForClusterRequest.java
@@ -1,8 +1,10 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.clustercontroller.core.restapiv2.requests;
+import com.yahoo.time.TimeBudget;
import com.yahoo.vdslib.distribution.ConfiguredNode;
-import com.yahoo.vdslib.state.*;
+import com.yahoo.vdslib.state.Node;
+import com.yahoo.vdslib.state.NodeType;
import com.yahoo.vespa.clustercontroller.core.RemoteClusterControllerTask;
import com.yahoo.vespa.clustercontroller.core.restapiv2.Id;
import com.yahoo.vespa.clustercontroller.core.restapiv2.Request;
@@ -13,7 +15,9 @@ import com.yahoo.vespa.clustercontroller.utils.staterestapi.requests.SetUnitStat
import com.yahoo.vespa.clustercontroller.utils.staterestapi.response.SetResponse;
import com.yahoo.vespa.clustercontroller.utils.staterestapi.response.UnitState;
+import java.time.Instant;
import java.util.Map;
+import java.util.Optional;
import java.util.logging.Logger;
public class SetNodeStatesForClusterRequest extends Request<SetResponse> {
@@ -22,6 +26,7 @@ public class SetNodeStatesForClusterRequest extends Request<SetResponse> {
private final Id.Cluster cluster;
private final Map<String, UnitState> newStates;
private final SetUnitStateRequest.Condition condition;
+ private final TimeBudget timeBudget;
public SetNodeStatesForClusterRequest(Id.Cluster cluster, SetUnitStateRequest request) {
@@ -29,6 +34,7 @@ public class SetNodeStatesForClusterRequest extends Request<SetResponse> {
this.cluster = cluster;
this.newStates = request.getNewState();
this.condition = request.getCondition();
+ this.timeBudget = request.timeBudget();
}
@Override
@@ -77,4 +83,15 @@ public class SetNodeStatesForClusterRequest extends Request<SetResponse> {
// 'true' here means the current state now equals the request's wanted state.
return new SetResponse("ok", true);
}
+
+ @Override
+ public Optional<Instant> getDeadline() {
+ return timeBudget.deadline();
+ }
+
+ @Override
+ public boolean isFailed() {
+ // Failure to set a node state is propagated as a 200 with wasModified false.
+ return super.isFailed() || (resultSet && !result.getWasModified());
+ }
}
diff --git a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/restapiv2/requests/WantedStateSetter.java b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/restapiv2/requests/WantedStateSetter.java
new file mode 100644
index 00000000000..6fa7d536c67
--- /dev/null
+++ b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/restapiv2/requests/WantedStateSetter.java
@@ -0,0 +1,26 @@
+// 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.restapiv2.requests;
+
+import com.yahoo.vdslib.state.ClusterState;
+import com.yahoo.vdslib.state.Node;
+import com.yahoo.vespa.clustercontroller.core.ContentCluster;
+import com.yahoo.vespa.clustercontroller.core.listeners.NodeStateOrHostInfoChangeHandler;
+import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.StateRestApiException;
+import com.yahoo.vespa.clustercontroller.utils.staterestapi.requests.SetUnitStateRequest;
+import com.yahoo.vespa.clustercontroller.utils.staterestapi.response.SetResponse;
+import com.yahoo.vespa.clustercontroller.utils.staterestapi.response.UnitState;
+
+import java.util.Map;
+
+/**
+ * @author hakon
+ */
+@FunctionalInterface
+public interface WantedStateSetter {
+ SetResponse set(ContentCluster cluster,
+ SetUnitStateRequest.Condition condition,
+ Map<String, UnitState> newStates,
+ Node node,
+ NodeStateOrHostInfoChangeHandler stateListener,
+ ClusterState currentClusterState) throws StateRestApiException;
+}
diff --git a/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/restapiv2/SetNodeStateTest.java b/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/restapiv2/SetNodeStateTest.java
index 4fb244666a4..f3a4be5ac2f 100644
--- a/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/restapiv2/SetNodeStateTest.java
+++ b/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/restapiv2/SetNodeStateTest.java
@@ -1,13 +1,17 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.clustercontroller.core.restapiv2;
+import com.yahoo.time.TimeBudget;
import com.yahoo.vdslib.state.NodeType;
+import com.yahoo.vespa.clustercontroller.core.MasterInterface;
import com.yahoo.vespa.clustercontroller.core.RemoteClusterControllerTask;
import com.yahoo.vespa.clustercontroller.core.restapiv2.requests.SetNodeStateRequest;
+import com.yahoo.vespa.clustercontroller.core.restapiv2.requests.WantedStateSetter;
import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.DeadlineExceededException;
import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.InvalidContentException;
import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.MissingUnitException;
import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.OperationNotSupportedForUnitException;
+import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.StateRestApiException;
import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.UnknownMasterException;
import com.yahoo.vespa.clustercontroller.utils.staterestapi.requests.SetUnitStateRequest;
import com.yahoo.vespa.clustercontroller.utils.staterestapi.response.SetResponse;
@@ -17,6 +21,8 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import java.time.Clock;
+import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -26,6 +32,9 @@ import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
public class SetNodeStateTest extends StateRestApiTest {
@@ -36,6 +45,7 @@ public class SetNodeStateTest extends StateRestApiTest {
private Map<String, UnitState> newStates = new LinkedHashMap<>();
private Condition condition = Condition.FORCE;
private ResponseWait responseWait = ResponseWait.WAIT_UNTIL_CLUSTER_ACKED;
+ private TimeBudget timeBudget = TimeBudget.fromNow(Clock.systemUTC(), Duration.ofSeconds(10));
public SetUnitStateRequestImpl(String req) {
super(req, 0);
@@ -83,6 +93,11 @@ public class SetNodeStateTest extends StateRestApiTest {
public ResponseWait getResponseWait() {
return responseWait;
}
+
+ @Override
+ public TimeBudget timeBudget() {
+ return timeBudget;
+ }
}
private void verifyStateSet(String state, String reason) throws Exception {
@@ -426,4 +441,30 @@ public class SetNodeStateTest extends StateRestApiTest {
request.getResult();
}
+ @Test
+ public void no_fail_if_modified() throws StateRestApiException {
+ assertFalse(isFailed(true));
+ }
+
+ @Test
+ public void fail_if_not_modified() throws StateRestApiException {
+ assertTrue(isFailed(false));
+ }
+
+ private boolean isFailed(boolean wasModified) throws StateRestApiException {
+ WantedStateSetter wantedStateSetter = mock(WantedStateSetter.class);
+ SetNodeStateRequest request = new SetNodeStateRequest(
+ createDummyId(),
+ new SetUnitStateRequestImpl("music/storage/1").setNewState("user", "maintenance", "whatever reason."),
+ wantedStateSetter);
+ SetResponse response = new SetResponse("some reason", wasModified);
+ when(wantedStateSetter.set(any(), any(), any(), any(), any(), any())).thenReturn(response);
+
+ RemoteClusterControllerTask.Context context = mock(RemoteClusterControllerTask.Context.class);
+ MasterInterface masterInterface = mock(MasterInterface.class);
+ context.masterInfo = masterInterface;
+ when(masterInterface.isMaster()).thenReturn(true);
+ request.doRemoteFleetControllerTask(context);
+ return request.isFailed();
+ }
}
diff --git a/clustercontroller-utils/pom.xml b/clustercontroller-utils/pom.xml
index ecfc6df3bd0..e176ce9d1e5 100644
--- a/clustercontroller-utils/pom.xml
+++ b/clustercontroller-utils/pom.xml
@@ -40,6 +40,11 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <scope>provided</scope>
+ </dependency>
</dependencies>
<build>
<plugins>
diff --git a/clustercontroller-utils/src/main/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/requests/SetUnitStateRequest.java b/clustercontroller-utils/src/main/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/requests/SetUnitStateRequest.java
index d583e4ecc27..a28ddb3539b 100644
--- a/clustercontroller-utils/src/main/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/requests/SetUnitStateRequest.java
+++ b/clustercontroller-utils/src/main/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/requests/SetUnitStateRequest.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.clustercontroller.utils.staterestapi.requests;
+import com.yahoo.time.TimeBudget;
import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.InvalidContentException;
import com.yahoo.vespa.clustercontroller.utils.staterestapi.response.UnitState;
@@ -62,4 +63,5 @@ public interface SetUnitStateRequest extends UnitRequest {
}
ResponseWait getResponseWait();
+ TimeBudget timeBudget();
}
diff --git a/clustercontroller-utils/src/main/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/server/JsonReader.java b/clustercontroller-utils/src/main/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/server/JsonReader.java
index 3d0856669f1..d871a8ed6bc 100644
--- a/clustercontroller-utils/src/main/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/server/JsonReader.java
+++ b/clustercontroller-utils/src/main/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/server/JsonReader.java
@@ -12,7 +12,6 @@ import java.util.HashMap;
import java.util.Map;
public class JsonReader {
-
private static class UnitStateImpl implements UnitState {
private final String id;
private final String reason;
@@ -37,7 +36,9 @@ public class JsonReader {
final Map<String, UnitState> stateMap;
final SetUnitStateRequest.Condition condition;
final SetUnitStateRequest.ResponseWait responseWait;
- public SetRequestData(Map<String, UnitState> stateMap, SetUnitStateRequest.Condition condition,
+
+ public SetRequestData(Map<String, UnitState> stateMap,
+ SetUnitStateRequest.Condition condition,
SetUnitStateRequest.ResponseWait responseWait) {
this.stateMap = stateMap;
this.condition = condition;
@@ -98,7 +99,7 @@ public class JsonReader {
}
stateMap.put(type, new UnitStateImpl(code, reason));
}
+
return new SetRequestData(stateMap, condition, responseWait);
}
-
}
diff --git a/clustercontroller-utils/src/main/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/server/RestApiHandler.java b/clustercontroller-utils/src/main/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/server/RestApiHandler.java
index 71e6bc36de1..d3cab74c66f 100644
--- a/clustercontroller-utils/src/main/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/server/RestApiHandler.java
+++ b/clustercontroller-utils/src/main/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/server/RestApiHandler.java
@@ -1,33 +1,52 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.clustercontroller.utils.staterestapi.server;
+import com.google.common.util.concurrent.UncheckedTimeoutException;
import com.yahoo.log.LogLevel;
-import com.yahoo.yolean.Exceptions;
+import com.yahoo.time.TimeBudget;
import com.yahoo.vespa.clustercontroller.utils.communication.http.HttpRequest;
import com.yahoo.vespa.clustercontroller.utils.communication.http.HttpRequestHandler;
import com.yahoo.vespa.clustercontroller.utils.communication.http.HttpResult;
import com.yahoo.vespa.clustercontroller.utils.communication.http.JsonHttpResult;
import com.yahoo.vespa.clustercontroller.utils.staterestapi.StateRestAPI;
-import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.*;
+import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.DeadlineExceededException;
+import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.InvalidContentException;
+import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.InvalidOptionValueException;
+import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.OtherMasterException;
+import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.StateRestApiException;
+import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.UnknownMasterException;
import com.yahoo.vespa.clustercontroller.utils.staterestapi.requests.SetUnitStateRequest;
import com.yahoo.vespa.clustercontroller.utils.staterestapi.requests.UnitStateRequest;
-import com.yahoo.vespa.clustercontroller.utils.staterestapi.response.*;
+import com.yahoo.vespa.clustercontroller.utils.staterestapi.response.SetResponse;
+import com.yahoo.vespa.clustercontroller.utils.staterestapi.response.UnitResponse;
+import com.yahoo.vespa.clustercontroller.utils.staterestapi.response.UnitState;
+import com.yahoo.yolean.Exceptions;
-import java.util.*;
+import java.time.Clock;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
public class RestApiHandler implements HttpRequestHandler {
+ public static final Duration MAX_TIMEOUT = Duration.ofHours(1);
private final static Logger log = Logger.getLogger(RestApiHandler.class.getName());
private final StateRestAPI restApi;
private final JsonWriter jsonWriter;
private final JsonReader jsonReader = new JsonReader();
+ private final Clock clock;
public RestApiHandler(StateRestAPI restApi) {
this.restApi = restApi;
this.jsonWriter = new JsonWriter();
+ this.clock = Clock.systemUTC();
}
public RestApiHandler setDefaultPathPrefix(String defaultPathPrefix) {
@@ -43,6 +62,8 @@ public class RestApiHandler implements HttpRequestHandler {
@Override
public HttpResult handleRequest(HttpRequest request) {
+ Instant start = clock.instant();
+
try{
final String[] unitPath = createUnitPath(request);
if (request.getHttpOperation().equals(HttpRequest.HttpOp.GET)) {
@@ -60,6 +81,7 @@ public class RestApiHandler implements HttpRequestHandler {
return new JsonHttpResult().setJson(jsonWriter.createJson(data));
} else {
final JsonReader.SetRequestData setRequestData = jsonReader.getStateRequestData(request);
+ final Optional<Duration> timeout = parseTimeout(request.getOption("timeout", null));
SetResponse setResponse = restApi.setUnitState(new SetUnitStateRequest() {
@Override
public Map<String, UnitState> getNewState() {
@@ -73,6 +95,8 @@ public class RestApiHandler implements HttpRequestHandler {
public Condition getCondition() { return setRequestData.condition; }
@Override
public ResponseWait getResponseWait() { return setRequestData.responseWait; }
+ @Override
+ public TimeBudget timeBudget() { return TimeBudget.from(clock, start, timeout); }
});
return new JsonHttpResult().setJson(jsonWriter.createJson(setResponse));
}
@@ -89,7 +113,7 @@ public class RestApiHandler implements HttpRequestHandler {
result.setHttpCode(503, "Service Unavailable");
result.setJson(jsonWriter.createErrorJson(exception.getMessage()));
return result;
- } catch (DeadlineExceededException exception) {
+ } catch (DeadlineExceededException | UncheckedTimeoutException exception) {
logRequestException(request, exception, Level.WARNING);
JsonHttpResult result = new JsonHttpResult();
result.setHttpCode(504, "Gateway Timeout");
@@ -172,4 +196,24 @@ public class RestApiHandler implements HttpRequestHandler {
return value;
}
+ static Optional<Duration> parseTimeout(String timeoutOption) throws InvalidContentException {
+ if (timeoutOption == null) {
+ return Optional.empty();
+ }
+
+ float timeoutSeconds;
+ try {
+ timeoutSeconds = Float.parseFloat(timeoutOption);
+ } catch (NumberFormatException e) {
+ throw new InvalidContentException("value of timeout->" + timeoutOption + " is not a float");
+ }
+
+ if (timeoutSeconds <= 0.0) {
+ return Optional.of(Duration.ZERO);
+ } else if (timeoutSeconds <= MAX_TIMEOUT.getSeconds()) {
+ return Optional.of(Duration.ofMillis(Math.round(timeoutSeconds * 1000)));
+ } else {
+ throw new InvalidContentException("value of timeout->" + timeoutOption + " exceeds max timeout " + MAX_TIMEOUT);
+ }
+ }
}
diff --git a/clustercontroller-utils/src/test/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/server/RestApiHandlerTest.java b/clustercontroller-utils/src/test/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/server/RestApiHandlerTest.java
new file mode 100644
index 00000000000..f7f109fffc8
--- /dev/null
+++ b/clustercontroller-utils/src/test/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/server/RestApiHandlerTest.java
@@ -0,0 +1,20 @@
+// 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.utils.staterestapi.server;
+
+import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.InvalidContentException;
+import org.junit.Test;
+
+import java.time.Duration;
+import java.util.Optional;
+
+import static org.junit.Assert.assertEquals;
+
+public class RestApiHandlerTest {
+ @Test
+ public void testParsingOfTimeout() throws InvalidContentException {
+ assertEquals(Optional.empty(), RestApiHandler.parseTimeout(null));
+ assertEquals(Optional.of(Duration.ofMillis(12500)), RestApiHandler.parseTimeout("12.5"));
+ assertEquals(Optional.of(Duration.ofMillis(0)), RestApiHandler.parseTimeout("-1"));
+ assertEquals(Optional.of(Duration.ofMillis(0)), RestApiHandler.parseTimeout("0.0001"));
+ }
+} \ No newline at end of file
diff --git a/component/src/main/java/com/yahoo/container/util/Util.java b/component/src/main/java/com/yahoo/container/util/Util.java
index dd762af98cc..605ae75643b 100644
--- a/component/src/main/java/com/yahoo/container/util/Util.java
+++ b/component/src/main/java/com/yahoo/container/util/Util.java
@@ -4,7 +4,7 @@ package com.yahoo.container.util;
/**
* TODO: What is this?
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
// TODO: Move to a a more appropriate package in vespajlib
// TODO: Fix name
diff --git a/config-application-package/src/main/java/com/yahoo/config/model/application/provider/Bundle.java b/config-application-package/src/main/java/com/yahoo/config/model/application/provider/Bundle.java
index 3eb1c89db3e..c74dda9cc0c 100644
--- a/config-application-package/src/main/java/com/yahoo/config/model/application/provider/Bundle.java
+++ b/config-application-package/src/main/java/com/yahoo/config/model/application/provider/Bundle.java
@@ -17,7 +17,7 @@ import java.util.zip.ZipException;
* A Bundle represents an OSGi bundle inside the model, and provides utilities
* for accessing resources within that bundle.
*
- * @author tonytv, lulf
+ * @author Tony Vaagenes, lulf
* @since 5.1
*/
public class Bundle {
diff --git a/config-application-package/src/main/java/com/yahoo/config/model/application/provider/MockFileRegistry.java b/config-application-package/src/main/java/com/yahoo/config/model/application/provider/MockFileRegistry.java
index 515477641a8..e9c100938dd 100644
--- a/config-application-package/src/main/java/com/yahoo/config/model/application/provider/MockFileRegistry.java
+++ b/config-application-package/src/main/java/com/yahoo/config/model/application/provider/MockFileRegistry.java
@@ -12,7 +12,7 @@ import java.util.Set;
/**
* A file registry for testing, and, it seems, doubling as a null registry in some code paths.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class MockFileRegistry implements FileRegistry {
diff --git a/config-application-package/src/main/java/com/yahoo/config/model/application/provider/PreGeneratedFileRegistry.java b/config-application-package/src/main/java/com/yahoo/config/model/application/provider/PreGeneratedFileRegistry.java
index ed85b987a3d..957531b9f7f 100644
--- a/config-application-package/src/main/java/com/yahoo/config/model/application/provider/PreGeneratedFileRegistry.java
+++ b/config-application-package/src/main/java/com/yahoo/config/model/application/provider/PreGeneratedFileRegistry.java
@@ -13,7 +13,7 @@ import java.util.regex.Pattern;
/**
* Registry of files added earlier (i.e. during deployment)
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class PreGeneratedFileRegistry implements FileRegistry {
diff --git a/config-application-package/src/main/java/com/yahoo/config/model/application/provider/SchemaValidator.java b/config-application-package/src/main/java/com/yahoo/config/model/application/provider/SchemaValidator.java
index d0cca38b375..50268fc1e08 100644
--- a/config-application-package/src/main/java/com/yahoo/config/model/application/provider/SchemaValidator.java
+++ b/config-application-package/src/main/java/com/yahoo/config/model/application/provider/SchemaValidator.java
@@ -22,7 +22,7 @@ import java.util.logging.Level;
/**
* Validates xml files against a schema.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class SchemaValidator {
diff --git a/config-application-package/src/test/java/com/yahoo/config/model/application/provider/PreGeneratedFileRegistryTestCase.java b/config-application-package/src/test/java/com/yahoo/config/model/application/provider/PreGeneratedFileRegistryTestCase.java
index 19e288e30f5..e2b75542a5f 100644
--- a/config-application-package/src/test/java/com/yahoo/config/model/application/provider/PreGeneratedFileRegistryTestCase.java
+++ b/config-application-package/src/test/java/com/yahoo/config/model/application/provider/PreGeneratedFileRegistryTestCase.java
@@ -11,7 +11,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class PreGeneratedFileRegistryTestCase {
@Test
diff --git a/config-bundle/pom.xml b/config-bundle/pom.xml
index 07262141801..61d1065bee4 100644
--- a/config-bundle/pom.xml
+++ b/config-bundle/pom.xml
@@ -34,13 +34,6 @@
<groupId>com.yahoo.vespa</groupId>
<artifactId>configgen</artifactId>
<version>${project.version}</version>
- <!-- TODO: uncomment when the oldest available config-model uses the Java version of createClassName()
- <exclusions>
- <exclusion>
- <groupId>org.scala-lang.modules</groupId>
- <artifactId>scala-xml_${scala.major-version}</artifactId>
- </exclusion>
- </exclusions> -->
</dependency>
<dependency>
<groupId>com.yahoo.vespa</groupId>
diff --git a/config-class-plugin/src/main/java/com/yahoo/vespa/CloverChecker.java b/config-class-plugin/src/main/java/com/yahoo/vespa/CloverChecker.java
index 305d38dc132..5b9e1d9a408 100644
--- a/config-class-plugin/src/main/java/com/yahoo/vespa/CloverChecker.java
+++ b/config-class-plugin/src/main/java/com/yahoo/vespa/CloverChecker.java
@@ -9,7 +9,7 @@ import java.nio.file.Paths;
import java.util.function.Predicate;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
class CloverChecker {
private final Log log;
diff --git a/config-model-api/pom.xml b/config-model-api/pom.xml
index 743164afb16..109093beda9 100644
--- a/config-model-api/pom.xml
+++ b/config-model-api/pom.xml
@@ -72,7 +72,6 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava-testlib</artifactId>
- <version>17.0</version>
<scope>test</scope>
</dependency>
</dependencies>
diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationMetaData.java b/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationMetaData.java
index a3769299cf8..236a954f483 100644
--- a/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationMetaData.java
+++ b/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationMetaData.java
@@ -171,4 +171,5 @@ public class ApplicationMetaData {
throw new RuntimeException("Unable to encode metadata", e);
}
}
+
}
diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/ComponentInfo.java b/config-model-api/src/main/java/com/yahoo/config/application/api/ComponentInfo.java
index 3dd10054d13..ac4bc90c845 100644
--- a/config-model-api/src/main/java/com/yahoo/config/application/api/ComponentInfo.java
+++ b/config-model-api/src/main/java/com/yahoo/config/application/api/ComponentInfo.java
@@ -5,7 +5,7 @@ package com.yahoo.config.application.api;
/**
* Describes a component residing in the components directory.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
// TODO: add support for component versions.
public class ComponentInfo {
diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/FileRegistry.java b/config-model-api/src/main/java/com/yahoo/config/application/api/FileRegistry.java
index 15ae4294762..7df0e941731 100644
--- a/config-model-api/src/main/java/com/yahoo/config/application/api/FileRegistry.java
+++ b/config-model-api/src/main/java/com/yahoo/config/application/api/FileRegistry.java
@@ -7,7 +7,7 @@ import com.yahoo.config.FileReference;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public interface FileRegistry {
diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/UnparsedConfigDefinition.java b/config-model-api/src/main/java/com/yahoo/config/application/api/UnparsedConfigDefinition.java
index 36a44583ec9..ea3b1632571 100644
--- a/config-model-api/src/main/java/com/yahoo/config/application/api/UnparsedConfigDefinition.java
+++ b/config-model-api/src/main/java/com/yahoo/config/application/api/UnparsedConfigDefinition.java
@@ -6,8 +6,7 @@ import com.yahoo.vespa.config.ConfigDefinition;
/**
* A config definition that has not been parsed.
*
- * @author lulf
- * @since 5.20
+ * @author Ulf Lilleengen
*/
public interface UnparsedConfigDefinition {
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeAction.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeAction.java
index 6580e315ef5..e453aaf5bdb 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeAction.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeAction.java
@@ -8,7 +8,6 @@ import java.util.List;
* between the current active model and the next model to prepare.
*
* @author geirst
- * @since 5.40
*/
public interface ConfigChangeAction {
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRefeedAction.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRefeedAction.java
index f99086b6633..1f3cb1ef83d 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRefeedAction.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRefeedAction.java
@@ -5,7 +5,6 @@ package com.yahoo.config.model.api;
* Represents an action to re-feed a document type in order to handle a config change.
*
* @author geirst
- * @since 5.43
*/
public interface ConfigChangeRefeedAction extends ConfigChangeAction {
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRestartAction.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRestartAction.java
index 2f15caac1e8..f178180b6e0 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRestartAction.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRestartAction.java
@@ -5,7 +5,6 @@ package com.yahoo.config.model.api;
* Represents an action to restart services in order to handle a config change.
*
* @author geirst
- * @since 5.43
*/
public interface ConfigChangeRestartAction extends ConfigChangeAction {
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigDefinitionRepo.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigDefinitionRepo.java
index d2b8dc75d22..676dad9656e 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigDefinitionRepo.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigDefinitionRepo.java
@@ -21,7 +21,6 @@ public interface ConfigDefinitionRepo {
/**
* Gets a config definition from repo or null if not found
*/
- // TODO: Remove default implementation when 6.246 is the oldest version in use
- default ConfigDefinition get(ConfigDefinitionKey key) { return null; }
+ ConfigDefinition get(ConfigDefinitionKey key);
}
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigDefinitionStore.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigDefinitionStore.java
index de041232a71..10c9e42e553 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigDefinitionStore.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigDefinitionStore.java
@@ -5,8 +5,7 @@ import com.yahoo.vespa.config.ConfigDefinition;
import com.yahoo.vespa.config.ConfigDefinitionKey;
/**
- * @author lulf
- * @since 5.1
+ * @author Ulf Lilleengen
*/
public interface ConfigDefinitionStore {
ConfigDefinition getConfigDefinition(ConfigDefinitionKey defKey);
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigModelPlugin.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigModelPlugin.java
index fa97016d2ba..b3862dd77d1 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigModelPlugin.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigModelPlugin.java
@@ -4,7 +4,7 @@ package com.yahoo.config.model.api;
/**
* Interface of config model plugins.
*
- * @author lulf
+ * @author Ulf Lilleengen
*/
public interface ConfigModelPlugin {
}
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigServerSpec.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigServerSpec.java
index 1e373d07818..853c446a1c6 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigServerSpec.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigServerSpec.java
@@ -4,7 +4,7 @@ package com.yahoo.config.model.api;
/**
* Provides information about a configserver instance.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public interface ConfigServerSpec {
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/FileDistribution.java b/config-model-api/src/main/java/com/yahoo/config/model/api/FileDistribution.java
index 9b457f49bd2..4a3034b9cea 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/FileDistribution.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/FileDistribution.java
@@ -2,7 +2,6 @@
package com.yahoo.config.model.api;
import com.yahoo.config.FileReference;
-import com.yahoo.vespa.defaults.Defaults;
import java.io.File;
import java.util.Set;
@@ -23,18 +22,6 @@ public interface FileDistribution {
*/
void startDownload(String hostName, int port, Set<FileReference> fileReferences);
- // TODO: Remove when 6.244 is oldest version in use
- @Deprecated
- static String getDefaultFileDBRoot() {
- return Defaults.getDefaults().underVespaHome("var/db/vespa/filedistribution");
- }
-
- // TODO: Remove when 6.244 is oldest version in use
- @Deprecated
- static File getDefaultFileDBPath() {
- return new File(getDefaultFileDBRoot());
- }
-
File getFileReferencesDir();
}
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/HostInfo.java b/config-model-api/src/main/java/com/yahoo/config/model/api/HostInfo.java
index 589aee50d7c..3cbc165f2b9 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/HostInfo.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/HostInfo.java
@@ -6,7 +6,7 @@ import java.util.Collection;
/*
* Contains information about a host and what services are running on it.
*
- * @author lulf
+ * @author Ulf Lilleengen
*/
public class HostInfo {
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/HostProvisioner.java b/config-model-api/src/main/java/com/yahoo/config/model/api/HostProvisioner.java
index e182b11522b..bf58000dd36 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/HostProvisioner.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/HostProvisioner.java
@@ -4,14 +4,13 @@ package com.yahoo.config.model.api;
import com.yahoo.config.provision.*;
import java.util.List;
-import java.util.logging.Level;
/**
* Interface towards the host provisioner used to build a {@link Model}. The difference between this provisioner
* and {@link com.yahoo.config.provision.Provisioner}, is that this interface only exposes methods needed
* to build the model.
*
- * @author lulf
+ * @author Ulf Lilleengen
*/
public interface HostProvisioner {
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 ebc9aa247d8..c75174cd999 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
@@ -49,6 +49,8 @@ public interface ModelContext {
boolean hostedVespa();
Zone zone();
Set<Rotation> rotations();
+ boolean isBootstrap();
+ boolean isFirstTimeDeployment();
}
}
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelCreateResult.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelCreateResult.java
index 237ec24800d..8cc99d1f308 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelCreateResult.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelCreateResult.java
@@ -7,7 +7,6 @@ import java.util.List;
* The result after creating and validating a Model.
*
* @author geirst
- * @since 5.39
*/
public class ModelCreateResult {
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelFactory.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelFactory.java
index 572c470b6aa..b2d9244cc9d 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelFactory.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelFactory.java
@@ -17,8 +17,8 @@ public interface ModelFactory {
/**
* Creates an instance of a {@link Model}. The resulting instance will be used to serve config. No model
- * validation will be done, calling this method assumes that {@link #createAndValidateModel} has already
- * been called at some point for this model.
+ * validation will be done, calling this method assumes that{@link #createAndValidateModel(ModelContext, ValidationParameters)}
+ * has already been called at some point for this model.
*
* @param modelContext an instance of {@link ModelContext}, containing dependencies for creating a {@link Model}.
* @return a {@link Model} instance.
@@ -30,9 +30,8 @@ public interface ModelFactory {
* of a {@link Model} and the {@link ModelContext} can be done in this method.
*
* @param modelContext an instance of {@link ModelContext}, containing dependencies for creating a {@link Model}
- * @param ignoreValidationErrors true if validation errors should not trigger exceptions
+ * @param validationParameters validation parameters
* @return a {@link ModelCreateResult} instance.
*/
- ModelCreateResult createAndValidateModel(ModelContext modelContext, boolean ignoreValidationErrors);
-
+ ModelCreateResult createAndValidateModel(ModelContext modelContext, ValidationParameters validationParameters);
}
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelState.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelState.java
index c6ac913ad14..9142a8cc1c3 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelState.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelState.java
@@ -2,7 +2,7 @@
package com.yahoo.config.model.api;
/**
- * @author lulf
+ * @author Ulf Lilleengen
*/
public interface ModelState {
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ServiceInfo.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ServiceInfo.java
index 1099b0423ac..15cc4c6c917 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/ServiceInfo.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ServiceInfo.java
@@ -9,8 +9,7 @@ import java.util.Collection;
/**
* Contains information about a service.
*
- * @author lulf
- * @since 5.37
+ * @author Ulf Lilleengen
*/
public class ServiceInfo {
private final String serviceName;
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ValidationParameters.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ValidationParameters.java
new file mode 100644
index 00000000000..6e081d0d668
--- /dev/null
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ValidationParameters.java
@@ -0,0 +1,50 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.config.model.api;
+
+/**
+ * @author hmusum
+ */
+public class ValidationParameters {
+
+ public enum IgnoreValidationErrors {TRUE, FALSE}
+
+ public enum FailOnIncompatibleChange {TRUE, FALSE} //Note: Default is FALSE
+
+ public enum CheckRouting {TRUE, FALSE}
+
+ private final IgnoreValidationErrors ignoreValidationErrors;
+ private final FailOnIncompatibleChange failOnIncompatibleChange;
+ private final CheckRouting checkRouting;
+
+ public ValidationParameters() {
+ this(IgnoreValidationErrors.FALSE);
+ }
+
+ public ValidationParameters(IgnoreValidationErrors ignoreValidationErrors) {
+ this(ignoreValidationErrors, FailOnIncompatibleChange.FALSE, CheckRouting.TRUE);
+ }
+
+ public ValidationParameters(CheckRouting checkRouting) {
+ this(IgnoreValidationErrors.FALSE, FailOnIncompatibleChange.FALSE, checkRouting);
+ }
+
+ public ValidationParameters(IgnoreValidationErrors ignoreValidationErrors,
+ FailOnIncompatibleChange failOnIncompatibleChange,
+ CheckRouting checkRouting) {
+ this.ignoreValidationErrors = ignoreValidationErrors;
+ this.failOnIncompatibleChange = failOnIncompatibleChange;
+ this.checkRouting = checkRouting;
+ }
+
+ public boolean ignoreValidationErrors() {
+ return ignoreValidationErrors == IgnoreValidationErrors.TRUE;
+ }
+
+ public boolean failOnIncompatibleChanges() {
+ return failOnIncompatibleChange == FailOnIncompatibleChange.TRUE;
+ }
+
+ public boolean checkRouting() {
+ return checkRouting == CheckRouting.TRUE;
+ }
+}
diff --git a/config-model-api/src/test/java/com/yahoo/config/application/api/ApplicationFileTest.java b/config-model-api/src/test/java/com/yahoo/config/application/api/ApplicationFileTest.java
index 34b0b051851..b7ae6d9dcdf 100644
--- a/config-model-api/src/test/java/com/yahoo/config/application/api/ApplicationFileTest.java
+++ b/config-model-api/src/test/java/com/yahoo/config/application/api/ApplicationFileTest.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.config.application.api;
-import com.yahoo.config.application.api.ApplicationFile;
import com.yahoo.io.IOUtils;
import com.yahoo.path.Path;
import com.yahoo.vespa.config.util.ConfigUtils;
@@ -14,8 +13,7 @@ import static org.hamcrest.core.Is.is;
import static org.junit.Assert.*;
/**
- * @author lulf
- * @since 5.1
+ * @author Ulf Lilleengen
*/
public abstract class ApplicationFileTest {
protected void writeAppTo(File destFolder) throws IOException {
diff --git a/config-model-fat/pom.xml b/config-model-fat/pom.xml
index 649d8a37bf6..0231f59ce82 100644
--- a/config-model-fat/pom.xml
+++ b/config-model-fat/pom.xml
@@ -20,20 +20,6 @@
</dependency>
<dependency>
- <!-- TODO: remove, we probably don't need version 13. -->
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- <version>13.0.1</version>
- </dependency>
- <dependency>
- <!-- TODO: can probably be removed. Added to get the same set of embedded deps with maven-bundle-plugin 3.5 as with 2.4. -->
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>annotations</artifactId>
- <version>${project.version}</version>
- </dependency>
-
-
- <dependency>
<groupId>com.yahoo.vespa</groupId>
<artifactId>config-model-api</artifactId>
<version>${project.version}</version>
@@ -51,47 +37,6 @@
</exclusion>
</exclusions>
</dependency>
-
- <!-- TODO: remove all test deps, should not be needed -->
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest-all</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava-testlib</artifactId>
- <version>17.0</version>
- <scope>test</scope>
- <exclusions>
- <exclusion>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>jrt</artifactId>
- </exclusion>
- <exclusion>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>config-lib</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>testutil</artifactId>
- <version>${project.version}</version>
- <scope>test</scope>
- <exclusions>
- <exclusion>
- <!-- OPTIMIZATION: very large (44 MB) and only used for query sorting -->
- <groupId>com.ibm.icu</groupId>
- <artifactId>icu4j</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
</dependencies>
<build>
diff --git a/config-model/pom.xml b/config-model/pom.xml
index bab2f37e667..e78c7f5c0fd 100644
--- a/config-model/pom.xml
+++ b/config-model/pom.xml
@@ -43,7 +43,6 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava-testlib</artifactId>
- <version>17.0</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -53,7 +52,6 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
- <version>13.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
diff --git a/config-model/src/main/java/com/yahoo/config/model/ConfigModelInstanceFactory.java b/config-model/src/main/java/com/yahoo/config/model/ConfigModelInstanceFactory.java
index 83aca36b8b1..af7f5776f74 100644
--- a/config-model/src/main/java/com/yahoo/config/model/ConfigModelInstanceFactory.java
+++ b/config-model/src/main/java/com/yahoo/config/model/ConfigModelInstanceFactory.java
@@ -4,8 +4,7 @@ package com.yahoo.config.model;
/**
* Interface for factories of config models.
*
- * @author lulf
- * @since 5.1
+ * @author Ulf Lilleengen
*/
public interface ConfigModelInstanceFactory<MODEL extends ConfigModel> {
diff --git a/config-model/src/main/java/com/yahoo/config/model/deploy/DeployProperties.java b/config-model/src/main/java/com/yahoo/config/model/deploy/DeployProperties.java
index d3e91f8866c..53c70399e94 100644
--- a/config-model/src/main/java/com/yahoo/config/model/deploy/DeployProperties.java
+++ b/config-model/src/main/java/com/yahoo/config/model/deploy/DeployProperties.java
@@ -25,6 +25,8 @@ public class DeployProperties {
private final String athenzDnsSuffix;
private final boolean hostedVespa;
private final Version vespaVersion;
+ private final boolean isBootstrap;
+ private final boolean isFirstTimeDeployment;
private DeployProperties(boolean multitenant,
ApplicationId applicationId,
@@ -33,7 +35,9 @@ public class DeployProperties {
boolean hostedVespa,
URI ztsUrl,
String athenzDnsSuffix,
- Version vespaVersion) {
+ Version vespaVersion,
+ boolean isBootstrap,
+ boolean isFirstTimeDeployment) {
this.loadBalancerName = loadBalancerName;
this.ztsUrl = ztsUrl;
this.athenzDnsSuffix = athenzDnsSuffix;
@@ -42,9 +46,10 @@ public class DeployProperties {
this.applicationId = applicationId;
this.serverSpecs.addAll(configServerSpecs);
this.hostedVespa = hostedVespa;
+ this.isBootstrap = isBootstrap;
+ this.isFirstTimeDeployment = isFirstTimeDeployment;
}
-
public boolean multitenant() {
return multitenant;
}
@@ -78,6 +83,12 @@ public class DeployProperties {
return vespaVersion;
}
+ /** Returns whether this deployment happens during bootstrap *prepare* (not set on activate) */
+ public boolean isBootstrap() { return isBootstrap; }
+
+ /** Returns whether this is the first deployment for this application (used during *prepare*, not set on activate) */
+ public boolean isFirstTimeDeployment() { return isFirstTimeDeployment; }
+
public static class Builder {
private ApplicationId applicationId = ApplicationId.defaultId();
@@ -88,6 +99,8 @@ public class DeployProperties {
private String athenzDnsSuffix;
private boolean hostedVespa = false;
private Version vespaVersion = Version.fromIntValues(1, 0, 0);
+ private boolean isBootstrap = false;
+ private boolean isFirstTimeDeployment = false;
public Builder applicationId(ApplicationId applicationId) {
this.applicationId = applicationId;
@@ -129,8 +142,19 @@ public class DeployProperties {
return this;
}
+ public Builder isBootstrap(boolean isBootstrap) {
+ this.isBootstrap = isBootstrap;
+ return this;
+ }
+
+ public Builder isFirstTimeDeployment(boolean isFirstTimeDeployment) {
+ this.isFirstTimeDeployment = isFirstTimeDeployment;
+ return this;
+ }
+
public DeployProperties build() {
- return new DeployProperties(multitenant, applicationId, configServerSpecs, loadBalancerName, hostedVespa, ztsUrl, athenzDnsSuffix, vespaVersion);
+ return new DeployProperties(multitenant, applicationId, configServerSpecs, loadBalancerName, hostedVespa,
+ ztsUrl, athenzDnsSuffix, vespaVersion, isBootstrap, isFirstTimeDeployment);
}
}
diff --git a/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java b/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java
index 3dafb521dc8..8b0285ec2b4 100644
--- a/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java
+++ b/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java
@@ -10,6 +10,7 @@ import com.yahoo.config.application.api.UnparsedConfigDefinition;
import com.yahoo.config.model.api.ConfigDefinitionRepo;
import com.yahoo.config.model.api.HostProvisioner;
import com.yahoo.config.model.api.Model;
+import com.yahoo.config.model.api.ValidationParameters;
import com.yahoo.config.model.application.provider.BaseDeployLogger;
import com.yahoo.config.model.application.provider.MockFileRegistry;
import com.yahoo.config.model.provision.HostsXmlProvisioner;
@@ -70,11 +71,11 @@ public class DeployState implements ConfigDefinitionStore {
private final HostProvisioner provisioner;
public static DeployState createTestState() {
- return new Builder().build(true);
+ return new Builder().build();
}
public static DeployState createTestState(ApplicationPackage applicationPackage) {
- return new Builder().applicationPackage(applicationPackage).build(true);
+ return new Builder().applicationPackage(applicationPackage).build();
}
private DeployState(ApplicationPackage applicationPackage, SearchDocumentModel searchDocumentModel, RankProfileRegistry rankProfileRegistry,
@@ -288,11 +289,15 @@ public class DeployState implements ConfigDefinitionStore {
return this;
}
- public DeployState build(boolean validate) {
+ public DeployState build() {
+ return build(new ValidationParameters());
+ }
+
+ public DeployState build(ValidationParameters validationParameters) {
RankProfileRegistry rankProfileRegistry = new RankProfileRegistry();
QueryProfiles queryProfiles = new QueryProfilesBuilder().build(applicationPackage);
SemanticRules semanticRules = new SemanticRuleBuilder().build(applicationPackage);
- SearchDocumentModel searchDocumentModel = createSearchDocumentModel(rankProfileRegistry, logger, queryProfiles, validate);
+ SearchDocumentModel searchDocumentModel = createSearchDocumentModel(rankProfileRegistry, logger, queryProfiles, validationParameters);
return new DeployState(applicationPackage, searchDocumentModel, rankProfileRegistry, fileRegistry, logger, hostProvisioner,
properties, permanentApplicationPackage, configDefinitionRepo, previousModel, rotations,
zone, queryProfiles, semanticRules, now, wantedNodeVespaVersion);
@@ -301,7 +306,7 @@ public class DeployState implements ConfigDefinitionStore {
private SearchDocumentModel createSearchDocumentModel(RankProfileRegistry rankProfileRegistry,
DeployLogger logger,
QueryProfiles queryProfiles,
- boolean validate) {
+ ValidationParameters validationParameters) {
Collection<NamedReader> readers = applicationPackage.getSearchDefinitions();
Map<String, String> names = new LinkedHashMap<>();
SearchBuilder builder = new SearchBuilder(applicationPackage, rankProfileRegistry, queryProfiles.getRegistry());
@@ -324,7 +329,7 @@ public class DeployState implements ConfigDefinitionStore {
closeIgnoreException(reader.getReader());
}
}
- builder.build(validate, logger);
+ builder.build(! validationParameters.ignoreValidationErrors(), logger);
return SearchDocumentModel.fromBuilderAndNames(builder, names);
}
diff --git a/config-model/src/main/java/com/yahoo/config/model/producer/AbstractConfigProducerRoot.java b/config-model/src/main/java/com/yahoo/config/model/producer/AbstractConfigProducerRoot.java
index 6b583650e9d..3f0be5bda8e 100644
--- a/config-model/src/main/java/com/yahoo/config/model/producer/AbstractConfigProducerRoot.java
+++ b/config-model/src/main/java/com/yahoo/config/model/producer/AbstractConfigProducerRoot.java
@@ -13,7 +13,7 @@ import java.util.Optional;
/**
* The parent class of classes having the role as the root of a config producer tree.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public abstract class AbstractConfigProducerRoot extends AbstractConfigProducer<AbstractConfigProducer<?>>
implements ConfigProducerRoot {
diff --git a/config-model/src/main/java/com/yahoo/config/model/provision/InMemoryProvisioner.java b/config-model/src/main/java/com/yahoo/config/model/provision/InMemoryProvisioner.java
index e6b89d7f390..c744c509b9a 100644
--- a/config-model/src/main/java/com/yahoo/config/model/provision/InMemoryProvisioner.java
+++ b/config-model/src/main/java/com/yahoo/config/model/provision/InMemoryProvisioner.java
@@ -24,7 +24,8 @@ import java.util.Optional;
import java.util.Set;
/**
- * In memory host provisioner. NB! ATM cannot be reused after allocate has been called.
+ * In memory host provisioner for testing only.
+ * NB! ATM cannot be reused after allocate has been called.
*
* @author hmusum
* @author bratseth
@@ -97,6 +98,9 @@ public class InMemoryProvisioner implements HostProvisioner {
return hosts;
}
+ /** Returns the current allocations of this as a mutable map */
+ public Map<ClusterSpec, List<HostSpec>> allocations() { return allocations; }
+
@Override
public HostSpec allocateHost(String alias) {
if (legacyMapping.containsKey(alias)) return legacyMapping.get(alias);
@@ -129,14 +133,16 @@ public class InMemoryProvisioner implements HostProvisioner {
allocation.addAll(allocateHostGroup(cluster.with(Optional.of(ClusterSpec.Group.from(0))),
flavor,
capacity,
- startIndexForClusters));
+ startIndexForClusters,
+ requestedCapacity.canFail()));
}
else {
for (int i = 0; i < groups; i++) {
allocation.addAll(allocateHostGroup(cluster.with(Optional.of(ClusterSpec.Group.from(i))),
flavor,
capacity / groups,
- allocation.size()));
+ allocation.size(),
+ requestedCapacity.canFail()));
}
}
for (ListIterator<HostSpec> i = allocation.listIterator(); i.hasNext(); ) {
@@ -155,13 +161,18 @@ public class InMemoryProvisioner implements HostProvisioner {
host.version());
}
- private List<HostSpec> allocateHostGroup(ClusterSpec clusterGroup, String flavor, int nodesInGroup, int startIndex) {
+ private List<HostSpec> allocateHostGroup(ClusterSpec clusterGroup, String flavor, int nodesInGroup, int startIndex, boolean canFail) {
List<HostSpec> allocation = allocations.getOrDefault(clusterGroup, new ArrayList<>());
allocations.put(clusterGroup, allocation);
int nextIndex = nextIndexInCluster.getOrDefault(new Pair<>(clusterGroup.type(), clusterGroup.id()), startIndex);
while (allocation.size() < nodesInGroup) {
- if (freeNodes.get(flavor).isEmpty()) throw new IllegalArgumentException("Insufficient capacity of flavor '" + flavor + "'");
+ if (freeNodes.get(flavor).isEmpty()) {
+ if (canFail)
+ throw new IllegalArgumentException("Insufficient capacity of flavor '" + flavor + "'");
+ else
+ break;
+ }
Host newHost = freeNodes.removeValue(flavor, 0);
ClusterMembership membership = ClusterMembership.from(clusterGroup, nextIndex++);
allocation.add(new HostSpec(newHost.hostname(), newHost.aliases(), newHost.flavor(), Optional.of(membership), newHost.version()));
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 e3d766e710f..29d151857cc 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
@@ -55,7 +55,7 @@ public class MockRoot extends AbstractConfigProducerRoot {
}
public MockRoot(String rootConfigId, ApplicationPackage applicationPackage) {
- this(rootConfigId, new DeployState.Builder().applicationPackage(applicationPackage).build(true));
+ this(rootConfigId, new DeployState.Builder().applicationPackage(applicationPackage).build());
}
public MockRoot(String rootConfigId, DeployState deployState) {
diff --git a/config-model/src/main/java/com/yahoo/config/model/test/TestDriver.java b/config-model/src/main/java/com/yahoo/config/model/test/TestDriver.java
index 9fe0c92141f..b538468d0bc 100644
--- a/config-model/src/main/java/com/yahoo/config/model/test/TestDriver.java
+++ b/config-model/src/main/java/com/yahoo/config/model/test/TestDriver.java
@@ -80,7 +80,7 @@ public class TestDriver {
* @return a producer root capable of answering getConfig requests.
*/
public TestRoot buildModel(ApplicationPackage applicationPackage) {
- return buildModel(new DeployState.Builder().applicationPackage(applicationPackage).build(true));
+ return buildModel(new DeployState.Builder().applicationPackage(applicationPackage).build());
}
/**
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 2005f49213e..5855dedc415 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java
@@ -694,19 +694,14 @@ public class RankProfile implements Serializable, Cloneable {
private void compileThis(QueryProfileRegistry queryProfiles) {
parseExpressions();
-
checkNameCollisions(getMacros(), getConstants());
- Map<String, Macro> compiledMacros = new LinkedHashMap<>();
- for (Map.Entry<String, Macro> macroEntry : getMacros().entrySet()) {
- Macro compiledMacro = macroEntry.getValue().clone();
- compiledMacro.setRankingExpression(compile(macroEntry.getValue().getRankingExpression(),
- queryProfiles,
- getConstants(), Collections.<String, Macro>emptyMap()));
- compiledMacros.put(macroEntry.getKey(), compiledMacro);
- }
- macros = compiledMacros;
- Map<String, Macro> inlineMacros = keepInline(compiledMacros);
+ // Macro compiling first pass: compile inline macros without resolving other macros
+ Map<String, Macro> inlineMacros = compileMacros(getInlineMacros(), queryProfiles, Collections.emptyMap());
+
+ // Macro compiling second pass: compile all macros and insert previously compiled inline macros
+ macros = compileMacros(getMacros(), queryProfiles, inlineMacros);
+
firstPhaseRanking = compile(this.getFirstPhaseRanking(), queryProfiles, getConstants(), inlineMacros);
secondPhaseRanking = compile(this.getSecondPhaseRanking(), queryProfiles, getConstants(), inlineMacros);
}
@@ -719,12 +714,22 @@ public class RankProfile implements Serializable, Cloneable {
}
}
- private Map<String, Macro> keepInline(Map<String, Macro> macros) {
- Map<String, Macro> inlineMacros = new HashMap<>();
- for (Map.Entry<String, Macro> entry : macros.entrySet())
- if (entry.getValue().getInline())
- inlineMacros.put(entry.getKey(), entry.getValue());
- return inlineMacros;
+ private Map<String, Macro> getInlineMacros() {
+ return getMacros().entrySet().stream().filter(x -> x.getValue().getInline())
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+ }
+
+ private Map<String, Macro> compileMacros(Map<String, Macro> macros,
+ QueryProfileRegistry queryProfiles,
+ Map<String, Macro> inlineMacros) {
+ Map<String, Macro> compiledMacros = new LinkedHashMap<>();
+ for (Map.Entry<String, Macro> entry : macros.entrySet()) {
+ Macro macro = entry.getValue().clone();
+ RankingExpression exp = compile(macro.getRankingExpression(), queryProfiles, getConstants(), inlineMacros);
+ macro.setRankingExpression(exp);
+ compiledMacros.put(entry.getKey(), macro);
+ }
+ return compiledMacros;
}
private RankingExpression compile(RankingExpression expression,
@@ -927,8 +932,8 @@ public class RankProfile implements Serializable, Cloneable {
public static class Macro implements Serializable, Cloneable {
private final String name;
- private String textualExpression=null;
- private RankingExpression expression=null;
+ private String textualExpression = null;
+ private RankingExpression expression = null;
private List<String> formalParams = new ArrayList<>();
/** True if this should be inlined into calling expressions. Useful for very cheap macros. */
@@ -993,6 +998,7 @@ public class RankProfile implements Serializable, Cloneable {
}
public static final class DiversitySettings {
+
private String attribute = null;
private int minGroups = 0;
private double cutoffFactor = 10;
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/derived/AttributeFields.java b/config-model/src/main/java/com/yahoo/searchdefinition/derived/AttributeFields.java
index 0f1918f99d6..55f3a94bb70 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/derived/AttributeFields.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/derived/AttributeFields.java
@@ -8,6 +8,7 @@ import com.yahoo.searchdefinition.Search;
import com.yahoo.searchdefinition.document.Attribute;
import com.yahoo.searchdefinition.document.ImmutableSDField;
import com.yahoo.searchdefinition.document.Ranking;
+import com.yahoo.searchdefinition.document.SDDocumentType;
import com.yahoo.searchdefinition.document.Sorting;
import com.yahoo.vespa.config.search.AttributesConfig;
import com.yahoo.vespa.indexinglanguage.expressions.ToPositionExpression;
@@ -18,10 +19,12 @@ import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
import static com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils.isArrayOfSimpleStruct;
import static com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils.isMapOfPrimitiveType;
import static com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils.isMapOfSimpleStruct;
+import static com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils.isSupportedComplexField;
/**
* The set of all attribute fields defined by a search definition
@@ -45,14 +48,14 @@ public class AttributeFields extends Derived implements AttributesConfig.Produce
/** Derives everything from a field */
@Override
protected void derive(ImmutableSDField field, Search search) {
- if (unsupportedFieldType(field)) {
+ if (unsupportedFieldType(field, search.getDocument())) {
return; // Ignore complex struct and map fields for indexed search (only supported for streaming search)
}
if (field.isImportedField()) {
deriveImportedAttributes(field);
- } else if (isArrayOfSimpleStruct(field)) {
+ } else if (isArrayOfSimpleStruct(field, search.getDocument())) {
deriveArrayOfSimpleStruct(field);
- } else if (isMapOfSimpleStruct(field)) {
+ } else if (isMapOfSimpleStruct(field, search.getDocument())) {
deriveMapOfSimpleStruct(field);
} else if (isMapOfPrimitiveType(field)) {
deriveMapOfPrimitiveType(field);
@@ -61,11 +64,9 @@ public class AttributeFields extends Derived implements AttributesConfig.Produce
}
}
- private static boolean unsupportedFieldType(ImmutableSDField field) {
+ private static boolean unsupportedFieldType(ImmutableSDField field, SDDocumentType docType) {
return (field.usesStructOrMap() &&
- !isArrayOfSimpleStruct(field) &&
- !isMapOfSimpleStruct(field) &&
- !isMapOfPrimitiveType(field) &&
+ !isSupportedComplexField(field, docType) &&
!field.getDataType().equals(PositionDataType.INSTANCE) &&
!field.getDataType().equals(DataType.getArray(PositionDataType.INSTANCE)));
}
@@ -153,6 +154,13 @@ public class AttributeFields extends Derived implements AttributesConfig.Produce
return Collections.unmodifiableCollection(attributes.values());
}
+ public Collection<Attribute> structFieldAttributes(String baseFieldName) {
+ String structPrefix = baseFieldName + ".";
+ return attributes().stream()
+ .filter(attribute -> attribute.getName().startsWith(structPrefix))
+ .collect(Collectors.toList());
+ }
+
public String toString() {
return "attributes " + getName();
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/derived/Juniperrc.java b/config-model/src/main/java/com/yahoo/searchdefinition/derived/Juniperrc.java
index acef6b1a23f..9ea1b5c9b9f 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/derived/Juniperrc.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/derived/Juniperrc.java
@@ -11,7 +11,7 @@ import java.util.Set;
/**
* Generated juniperrc-config for controlling juniper.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Juniperrc extends Derived implements JuniperrcConfig.Producer {
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/document/ComplexAttributeFieldUtils.java b/config-model/src/main/java/com/yahoo/searchdefinition/document/ComplexAttributeFieldUtils.java
index 89d72756512..72eb1c96e0f 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/document/ComplexAttributeFieldUtils.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/document/ComplexAttributeFieldUtils.java
@@ -1,3 +1,4 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.searchdefinition.document;
import com.yahoo.document.ArrayDataType;
@@ -6,6 +7,11 @@ import com.yahoo.document.Field;
import com.yahoo.document.MapDataType;
import com.yahoo.document.PositionDataType;
import com.yahoo.document.StructDataType;
+import com.yahoo.document.TemporaryStructuredDataType;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Optional;
/**
* Utils used to check whether a complex field supports being represented as struct field attributes.
@@ -13,34 +19,64 @@ import com.yahoo.document.StructDataType;
* Currently we support:
* - array of simple struct
* - map of primitive type to simple struct
+ * - map of primitive type to primitive type
*
* @author geirst
*/
public class ComplexAttributeFieldUtils {
- public static boolean isArrayOfSimpleStruct(ImmutableSDField field) {
- DataType fieldType = field.getDataType();
+ public static boolean isSupportedComplexField(ImmutableSDField field, SDDocumentType docType) {
+ return (isArrayOfSimpleStruct(field, docType) ||
+ isMapOfSimpleStruct(field, docType) ||
+ isMapOfPrimitiveType(field));
+ }
+
+ public static boolean isSupportedComplexField(DataType fieldType) {
+ return (isArrayOfSimpleStruct(fieldType) ||
+ isMapOfSimpleStruct(fieldType) ||
+ isMapOfPrimitiveType(fieldType));
+ }
+
+ public static boolean isArrayOfSimpleStruct(ImmutableSDField field, SDDocumentType docType) {
+ return isArrayOfSimpleStruct(field.getDataType(), Optional.of(docType));
+ }
+
+ public static boolean isArrayOfSimpleStruct(DataType fieldType) {
+ return isArrayOfSimpleStruct(fieldType, Optional.empty());
+ }
+
+ private static boolean isArrayOfSimpleStruct(DataType fieldType, Optional<SDDocumentType> docType) {
if (fieldType instanceof ArrayDataType) {
ArrayDataType arrayType = (ArrayDataType)fieldType;
- return isSimpleStruct(arrayType.getNestedType());
+ return isSimpleStruct(arrayType.getNestedType(), docType);
} else {
return false;
}
}
- public static boolean isMapOfSimpleStruct(ImmutableSDField field) {
- DataType fieldType = field.getDataType();
+ public static boolean isMapOfSimpleStruct(ImmutableSDField field, SDDocumentType docType) {
+ return isMapOfSimpleStruct(field.getDataType(), Optional.of(docType));
+ }
+
+ public static boolean isMapOfSimpleStruct(DataType fieldType) {
+ return isMapOfSimpleStruct(fieldType, Optional.empty());
+ }
+
+ private static boolean isMapOfSimpleStruct(DataType fieldType, Optional<SDDocumentType> docType) {
if (fieldType instanceof MapDataType) {
MapDataType mapType = (MapDataType)fieldType;
return isPrimitiveType(mapType.getKeyType()) &&
- isSimpleStruct(mapType.getValueType());
+ isSimpleStruct(mapType.getValueType(), docType);
} else {
return false;
}
}
public static boolean isMapOfPrimitiveType(ImmutableSDField field) {
- DataType fieldType = field.getDataType();
+ return isMapOfPrimitiveType(field.getDataType());
+ }
+
+ public static boolean isMapOfPrimitiveType(DataType fieldType) {
if (fieldType instanceof MapDataType) {
MapDataType mapType = (MapDataType)fieldType;
return isPrimitiveType(mapType.getKeyType()) &&
@@ -50,11 +86,15 @@ public class ComplexAttributeFieldUtils {
}
}
- private static boolean isSimpleStruct(DataType type) {
+ private static boolean isSimpleStruct(DataType type, Optional<SDDocumentType> docType) {
if (type instanceof StructDataType &&
!(type.equals(PositionDataType.INSTANCE))) {
StructDataType structType = (StructDataType) type;
- for (Field innerField : structType.getFields()) {
+ Collection<Field> structFields = getStructFields(structType, docType);
+ if (structFields.isEmpty()) {
+ return false;
+ }
+ for (Field innerField : structFields) {
if (!isPrimitiveType(innerField.getDataType())) {
return false;
}
@@ -65,6 +105,19 @@ public class ComplexAttributeFieldUtils {
}
}
+ private static Collection<Field> getStructFields(StructDataType structType, Optional<SDDocumentType> docType) {
+ // The struct data type might be unresolved at this point. If so we use the document type to resolve it.
+ if (docType.isPresent() && (structType instanceof TemporaryStructuredDataType)) {
+ SDDocumentType realStructType = docType.get().getOwnedType(structType.getName());
+ if (structType != null) {
+ return realStructType.getDocumentType().getFields();
+ }
+ return Collections.emptyList();
+ } else {
+ return structType.getFields();
+ }
+ }
+
private static boolean isPrimitiveType(DataType dataType) {
return dataType.equals(DataType.BYTE) ||
dataType.equals(DataType.INT) ||
@@ -74,10 +127,10 @@ public class ComplexAttributeFieldUtils {
dataType.equals(DataType.STRING);
}
- public static boolean isComplexFieldWithOnlyStructFieldAttributes(ImmutableSDField field) {
- if (isArrayOfSimpleStruct(field)) {
+ public static boolean isComplexFieldWithOnlyStructFieldAttributes(ImmutableSDField field, SDDocumentType docType) {
+ if (isArrayOfSimpleStruct(field, docType)) {
return hasOnlyStructFieldAttributes(field);
- } else if (isMapOfSimpleStruct(field)) {
+ } else if (isMapOfSimpleStruct(field, docType)) {
return hasSingleAttribute(field.getStructField("key")) &&
hasOnlyStructFieldAttributes(field.getStructField("value"));
} else if (isMapOfPrimitiveType(field)) {
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/AttributeOperation.java b/config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/AttributeOperation.java
index 28abbe43968..46ac3cc1691 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/AttributeOperation.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/AttributeOperation.java
@@ -122,10 +122,16 @@ public class AttributeOperation implements FieldOperation, FieldOperationContain
}
public void apply(SDField field) {
- Attribute attribute = field.getAttributes().get(name);
+ Attribute attribute = null;
+ if (attributeIsSuffixOfStructField(field.getName())) {
+ attribute = field.getAttributes().get(field.getName());
+ }
if (attribute == null) {
- attribute = new Attribute(name, field.getDataType());
- field.addAttribute(attribute);
+ attribute = field.getAttributes().get(name);
+ if (attribute == null) {
+ attribute = new Attribute(name, field.getDataType());
+ field.addAttribute(attribute);
+ }
}
if (huge != null) {
@@ -154,4 +160,8 @@ public class AttributeOperation implements FieldOperation, FieldOperationContain
}
}
+ private boolean attributeIsSuffixOfStructField(String fieldName) {
+ return ((fieldName.indexOf('.') != -1) && fieldName.endsWith(name));
+ }
+
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/parser/SimpleCharStream.java b/config-model/src/main/java/com/yahoo/searchdefinition/parser/SimpleCharStream.java
index 1ac858bb83d..b9b6d9b6a47 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/parser/SimpleCharStream.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/parser/SimpleCharStream.java
@@ -4,7 +4,7 @@ package com.yahoo.searchdefinition.parser;
import com.yahoo.javacc.FastCharStream;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings("deprecation")
public class SimpleCharStream extends FastCharStream implements com.yahoo.searchdefinition.parser.CharStream,
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/processing/ImplicitSummaries.java b/config-model/src/main/java/com/yahoo/searchdefinition/processing/ImplicitSummaries.java
index b51524b7e62..2d2fc10d9c5 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/processing/ImplicitSummaries.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/processing/ImplicitSummaries.java
@@ -83,7 +83,7 @@ public class ImplicitSummaries extends Processor {
}
}
- if (addedSummaryField != null && isComplexFieldWithOnlyStructFieldAttributes(field)) {
+ if (addedSummaryField != null && isComplexFieldWithOnlyStructFieldAttributes(field, search.getDocument())) {
addedSummaryField.setTransform(SummaryTransform.ATTRIBUTECOMBINER);
}
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 f6e95a81e77..419268468c2 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
@@ -18,7 +18,7 @@ import com.yahoo.vespa.model.container.search.QueryProfiles;
* This processor modifies all indexing scripts so that they input the value of the owning field by default. It also
* ensures that all fields used as input exist.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class IndexingInputs extends Processor {
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/processing/IndexingOutputs.java b/config-model/src/main/java/com/yahoo/searchdefinition/processing/IndexingOutputs.java
index 420d0fc619b..6f04184c512 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/processing/IndexingOutputs.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/processing/IndexingOutputs.java
@@ -20,7 +20,7 @@ import java.util.*;
* any output expression from writing to any field except for the owning field. Finally, for <tt>SummaryExpression</tt>,
* this processor expands to write all appropriate summary fields.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class IndexingOutputs extends Processor {
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/processing/IndexingValidation.java b/config-model/src/main/java/com/yahoo/searchdefinition/processing/IndexingValidation.java
index 7ee22be1490..b73151768fd 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/processing/IndexingValidation.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/processing/IndexingValidation.java
@@ -16,7 +16,7 @@ import java.util.HashSet;
import java.util.Set;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class IndexingValidation extends Processor {
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/processing/ReservedDocumentNames.java b/config-model/src/main/java/com/yahoo/searchdefinition/processing/ReservedDocumentNames.java
index 43a58fc9634..f2aa31bb9c3 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/processing/ReservedDocumentNames.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/processing/ReservedDocumentNames.java
@@ -11,7 +11,7 @@ import java.util.HashSet;
import java.util.Set;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class ReservedDocumentNames extends Processor {
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/processing/TextMatch.java b/config-model/src/main/java/com/yahoo/searchdefinition/processing/TextMatch.java
index b45f7d61a37..645ed5121ea 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/processing/TextMatch.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/processing/TextMatch.java
@@ -25,7 +25,7 @@ import java.util.Set;
import java.util.TreeSet;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class TextMatch extends Processor {
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/processing/TypedTransformProvider.java b/config-model/src/main/java/com/yahoo/searchdefinition/processing/TypedTransformProvider.java
index b329cc3bea3..9c681f62578 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/processing/TypedTransformProvider.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/processing/TypedTransformProvider.java
@@ -9,7 +9,7 @@ import com.yahoo.vespa.indexinglanguage.ValueTransformProvider;
import com.yahoo.vespa.indexinglanguage.expressions.*;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public abstract class TypedTransformProvider extends ValueTransformProvider {
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/processing/ValidateFieldTypes.java b/config-model/src/main/java/com/yahoo/searchdefinition/processing/ValidateFieldTypes.java
index 45e4b994ac9..54ad9f13f6f 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/processing/ValidateFieldTypes.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/processing/ValidateFieldTypes.java
@@ -19,7 +19,7 @@ import java.util.Map;
* explicitly disregards whether a field is an index field, an attribute or a summary field. This is a requirement if we
* hope to move to a model where index fields, attributes and summary fields share a common field class.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class ValidateFieldTypes extends Processor {
diff --git a/config-model/src/main/java/com/yahoo/vespa/documentmodel/SummaryTransform.java b/config-model/src/main/java/com/yahoo/vespa/documentmodel/SummaryTransform.java
index 082265c7694..4f85dd0c84e 100644
--- a/config-model/src/main/java/com/yahoo/vespa/documentmodel/SummaryTransform.java
+++ b/config-model/src/main/java/com/yahoo/vespa/documentmodel/SummaryTransform.java
@@ -24,7 +24,7 @@ public enum SummaryTransform {
private String name;
- private SummaryTransform(String name) {
+ SummaryTransform(String name) {
this.name=name;
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/Client.java b/config-model/src/main/java/com/yahoo/vespa/model/Client.java
index 9efb2762f28..0d6688dea8b 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/Client.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/Client.java
@@ -8,7 +8,7 @@ import com.yahoo.config.model.producer.AbstractConfigProducer;
* This is a placeholder config producer that makes global configuration available through a single identifier. This
* is added directly to the {@link ApplicationConfigProducerRoot} producer, and so can be accessed by the simple "client" identifier.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class Client extends AbstractConfigProducer {
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 1d900176dc6..c686f22392a 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
@@ -13,7 +13,7 @@ import java.util.Set;
/**
* Intended to be used as an external interface to the vespa model root.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public interface ConfigProducerRoot extends ConfigProducer {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java b/config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java
index 73fb532cfb4..f3e7a9623d1 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java
@@ -18,6 +18,7 @@ import com.yahoo.config.model.NullConfigModelRegistry;
import com.yahoo.config.model.api.FileDistribution;
import com.yahoo.config.model.api.HostInfo;
import com.yahoo.config.model.api.Model;
+import com.yahoo.config.model.api.ValidationParameters;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.producer.AbstractConfigProducer;
import com.yahoo.config.model.producer.AbstractConfigProducerRoot;
@@ -123,7 +124,7 @@ public final class VespaModel extends AbstractConfigProducerRoot implements Seri
* to instantiate config models
*/
public VespaModel(ApplicationPackage app, ConfigModelRegistry configModelRegistry) throws IOException, SAXException {
- this(configModelRegistry, new DeployState.Builder().applicationPackage(app).build(true));
+ this(configModelRegistry, new DeployState.Builder().applicationPackage(app).build());
}
/**
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java b/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java
index a6d24f33b5d..75f70d03fcc 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java
@@ -14,6 +14,7 @@ import com.yahoo.config.model.api.Model;
import com.yahoo.config.model.api.ModelContext;
import com.yahoo.config.model.api.ModelCreateResult;
import com.yahoo.config.model.api.ModelFactory;
+import com.yahoo.config.model.api.ValidationParameters;
import com.yahoo.config.model.application.provider.ApplicationPackageXmlFilesValidator;
import com.yahoo.config.model.builder.xml.ConfigModelBuilder;
import com.yahoo.config.model.deploy.DeployProperties;
@@ -88,15 +89,15 @@ public class VespaModelFactory implements ModelFactory {
@Override
public Model createModel(ModelContext modelContext) {
- return buildModel(createDeployState(modelContext, false));
+ return buildModel(createDeployState(modelContext, new ValidationParameters(ValidationParameters.IgnoreValidationErrors.TRUE)));
}
@Override
- public ModelCreateResult createAndValidateModel(ModelContext modelContext, boolean ignoreValidationErrors) {
- validateXml(modelContext, ignoreValidationErrors);
- DeployState deployState = createDeployState(modelContext, true);
+ public ModelCreateResult createAndValidateModel(ModelContext modelContext, ValidationParameters validationParameters) {
+ validateXml(modelContext, validationParameters.ignoreValidationErrors());
+ DeployState deployState = createDeployState(modelContext, validationParameters);
VespaModel model = buildModel(deployState);
- List<ConfigChangeAction> changeActions = validateModel(model, deployState, ignoreValidationErrors);
+ List<ConfigChangeAction> changeActions = validateModel(model, deployState, validationParameters);
return new ModelCreateResult(model, changeActions);
}
@@ -126,7 +127,7 @@ public class VespaModelFactory implements ModelFactory {
}
}
- private DeployState createDeployState(ModelContext modelContext, boolean validate) {
+ private DeployState createDeployState(ModelContext modelContext, ValidationParameters validationParameters) {
DeployState.Builder builder = new DeployState.Builder()
.applicationPackage(modelContext.applicationPackage())
.deployLogger(modelContext.deployLogger())
@@ -140,7 +141,7 @@ public class VespaModelFactory implements ModelFactory {
.now(clock.instant())
.wantedNodeVespaVersion(modelContext.wantedNodeVespaVersion());
modelContext.previousModel().ifPresent(builder::previousModel);
- return builder.build(validate);
+ return builder.build(validationParameters);
}
private DeployProperties createDeployProperties(ModelContext.Properties properties) {
@@ -153,6 +154,8 @@ public class VespaModelFactory implements ModelFactory {
.multitenant(properties.multitenant())
.hostedVespa(properties.hostedVespa())
.vespaVersion(getVersion())
+ .isBootstrap(properties.isBootstrap())
+ .isFirstTimeDeployment(properties.isFirstTimeDeployment())
.build();
}
@@ -171,11 +174,11 @@ public class VespaModelFactory implements ModelFactory {
}
}
- private List<ConfigChangeAction> validateModel(VespaModel model, DeployState deployState, boolean ignoreValidationErrors) {
+ private List<ConfigChangeAction> validateModel(VespaModel model, DeployState deployState, ValidationParameters validationParameters) {
try {
- return Validation.validate(model, ignoreValidationErrors, deployState);
+ return Validation.validate(model, validationParameters, deployState);
} catch (IllegalArgumentException e) {
- rethrowUnlessIgnoreErrors(e, ignoreValidationErrors);
+ rethrowUnlessIgnoreErrors(e, validationParameters.ignoreValidationErrors());
} catch (Exception e) {
throw new RuntimeException(e);
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/Configserver.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/Configserver.java
index e8f1f59310b..2a32549b6bf 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/Configserver.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/Configserver.java
@@ -1,16 +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.admin;
-import java.io.BufferedReader;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-
-import java.util.logging.Logger;
-
import com.yahoo.config.model.api.ConfigServerSpec;
-import com.yahoo.log.LogLevel;
import com.yahoo.config.model.producer.AbstractConfigProducer;
-import static com.yahoo.vespa.defaults.Defaults.getDefaults;
import com.yahoo.vespa.model.AbstractService;
/**
@@ -26,11 +18,13 @@ import com.yahoo.vespa.model.AbstractService;
*/
public class Configserver extends AbstractService {
private static final long serialVersionUID = 1L;
- private static final int defaultPort = 19070;
- private static final Logger log = Logger.getLogger(Configserver.class.getName());
+ public static final int defaultRpcPort = 19070;
- public Configserver(AbstractConfigProducer parent, String name) {
+ private final int rpcPort;
+
+ public Configserver(AbstractConfigProducer parent, String name, int rpcPort) {
super(parent, name);
+ this.rpcPort = rpcPort;
portsMeta.on(0).tag("rpc").tag("config");
portsMeta.on(1).tag("http").tag("config").tag("state");
setProp("clustertype", "admin");
@@ -41,16 +35,7 @@ public class Configserver extends AbstractService {
* Returns the desired base port for this service.
*/
public int getWantedPort() {
- try {
- // TODO: Provide configserver port as argument when creating this service instead
- Process process = new ProcessBuilder(getDefaults().underVespaHome("bin/vespa-print-default"), "configserver_rpc_port").start();
- InputStream in = process.getInputStream();
- BufferedReader reader = new BufferedReader(new InputStreamReader(in));
- return Integer.parseInt(reader.readLine().trim());
- } catch (Exception exception) {
- log.log(LogLevel.DEBUG, "Error reading port from script, using " + defaultPort);
- return defaultPort;
- }
+ return rpcPort;
}
/**
@@ -84,7 +69,7 @@ public class Configserver extends AbstractService {
return getRelativePort(1);
}
- public ConfigServerSpec getConfigServerSpec() {
+ ConfigServerSpec getConfigServerSpec() {
return new Spec(getHostName(), getConfigServerRpcPort(), getConfigServerHttpPort(), ZooKeepersConfigProvider.zkPort);
}
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 1c5048e2df8..143df20d557 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
@@ -9,7 +9,7 @@ import java.util.ArrayList;
import java.util.List;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ZooKeepersConfigProvider implements ZookeepersConfig.Producer {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java
index fc46ed18dde..a7a93e75293 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java
@@ -122,6 +122,18 @@ public class VespaMetricSet {
metrics.add(new Metric("athenz-tenant-cert.expiry.seconds.last", "athenz-tenant-cert.expiry.seconds"));
+ metrics.add(new Metric("jdisc.http.request.prematurely_closed.rate"));
+
+ metrics.add(new Metric("http.status.1xx.rate"));
+ metrics.add(new Metric("http.status.2xx.rate"));
+ metrics.add(new Metric("http.status.3xx.rate"));
+ metrics.add(new Metric("http.status.4xx.rate"));
+ metrics.add(new Metric("http.status.5xx.rate"));
+
+ metrics.add(new Metric("jdisc.http.request.uri_length.average"));
+ metrics.add(new Metric("jdisc.http.request.uri_length.max"));
+ metrics.add(new Metric("jdisc.http.request.content_size.average"));
+ metrics.add(new Metric("jdisc.http.request.content_size.max"));
return metrics;
}
@@ -161,6 +173,7 @@ public class VespaMetricSet {
metrics.add(new Metric("active_queries.average", "active_queries"));
metrics.add(new Metric("feed.latency.average"));
metrics.add(new Metric("queries.rate", "queries"));
+ metrics.add(new Metric("query_container_latency.average"));
metrics.add(new Metric("query_latency.average", "mean_query_latency"));
metrics.add(new Metric("query_latency.max", "max_query_latency"));
metrics.add(new Metric("query_latency.95percentile", "95p_query_latency"));
@@ -189,11 +202,6 @@ public class VespaMetricSet {
metrics.add(new Metric("error.result_with_errors.rate","error.result_with_errors"));
metrics.add(new Metric("error.unspecified.rate","error.unspecified"));
metrics.add(new Metric("error.unhandled_exception.rate","error.unhandled_exception"));
- metrics.add(new Metric("http.status.1xx.rate"));
- metrics.add(new Metric("http.status.2xx.rate"));
- metrics.add(new Metric("http.status.3xx.rate"));
- metrics.add(new Metric("http.status.4xx.rate"));
- metrics.add(new Metric("http.status.5xx.rate"));
return metrics;
}
@@ -303,11 +311,11 @@ public class VespaMetricSet {
metrics.add(new Metric("content.proton.documentdb.matching.queries.rate"));
metrics.add(new Metric("content.proton.documentdb.matching.query_latency.average"));
metrics.add(new Metric("content.proton.documentdb.matching.query_collateral_time.average"));
- metrics.add(new Metric("content.proton.documentdb.matching.docs_matched.average"));
+ metrics.add(new Metric("content.proton.documentdb.matching.docs_matched.rate"));
metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.queries.rate"));
metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.query_collateral_time.average"));
metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.query_latency.average"));
- metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.docs_matched.average"));
+ metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.docs_matched.rate"));
return metrics;
}
@@ -319,10 +327,11 @@ public class VespaMetricSet {
metrics.add(new Metric("vds.datastored.alldisks.bytes.average","bytes"));
metrics.add(new Metric("vds.visitor.allthreads.averagevisitorlifetime.sum.average","visitorlifetime"));
metrics.add(new Metric("vds.visitor.allthreads.averagequeuewait.sum.average","visitorqueuewait"));
- metrics.add(new Metric("vds.filestor.alldisks.allthreads.put.sum.count.rate","put"));
- metrics.add(new Metric("vds.filestor.alldisks.allthreads.remove.sum.count.rate","remove"));
- metrics.add(new Metric("vds.filestor.alldisks.allthreads.get.sum.count.rate","get"));
- metrics.add(new Metric("vds.filestor.alldisks.allthreads.update.sum.count.rate","update"));
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.put.sum.count.rate"));
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.remove.sum.count.rate"));
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.get.sum.count.rate"));
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.update.sum.count.rate"));
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.visit.sum.count.rate"));
metrics.add(new Metric("vds.filestor.alldisks.queuesize.average","diskqueuesize"));
metrics.add(new Metric("vds.filestor.alldisks.averagequeuewait.sum.average","diskqueuewait"));
@@ -334,8 +343,11 @@ public class VespaMetricSet {
metrics.add(new Metric("vds.filestor.alldisks.allthreads.remove.sum.latency.average"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.get.sum.latency.average"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.update.sum.latency.average"));
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.visit.sum.latency.average"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.splitbuckets.count.rate"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.joinbuckets.count.rate"));
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.deletebuckets.count.rate"));
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.deletebuckets.latency.average"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.setbucketstates.count.rate"));
metrics.add(new Metric("vds.filestor.spi.put.success.average"));
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidator.java
new file mode 100644
index 00000000000..005c1dd8b2e
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidator.java
@@ -0,0 +1,85 @@
+// 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;
+
+import com.yahoo.config.model.deploy.DeployState;
+import com.yahoo.document.DataType;
+import com.yahoo.document.PositionDataType;
+import com.yahoo.searchdefinition.Search;
+import com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils;
+import com.yahoo.searchdefinition.document.ImmutableSDField;
+import com.yahoo.vespa.model.VespaModel;
+import com.yahoo.vespa.model.search.AbstractSearchCluster;
+import com.yahoo.vespa.model.search.SearchCluster;
+import org.apache.commons.lang.StringUtils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Validates that complex fields (of type struct or map) that have struct field attributes are supported.
+ *
+ * Only applies for indexed search clusters.
+ *
+ * @author geirst
+ */
+public class ComplexAttributeFieldsValidator extends Validator {
+
+ @Override
+ public void validate(VespaModel model, DeployState deployState) {
+ List<AbstractSearchCluster> searchClusters = model.getSearchClusters();
+ for (AbstractSearchCluster cluster : searchClusters) {
+ if (cluster.isStreaming()) {
+ continue;
+ }
+ SearchCluster searchCluster = (SearchCluster) cluster;
+ for (AbstractSearchCluster.SearchDefinitionSpec spec : searchCluster.getLocalSDS()) {
+ validateComplexFields(searchCluster.getClusterName(), spec.getSearchDefinition().getSearch());
+ }
+ }
+ }
+
+ private static void validateComplexFields(String clusterName, Search search) {
+ String unsupportedFields = search.allFields()
+ .filter(field -> isUnsupportedComplexField(field))
+ .map(ComplexAttributeFieldsValidator::toString)
+ .collect(Collectors.joining(", "));
+
+ if (!unsupportedFields.isEmpty()) {
+ throw new IllegalArgumentException(
+ String.format("For cluster '%s', search '%s': The following complex fields do not support using struct field attributes: %s. " +
+ "Only supported for the following complex field types: array or map of struct with primitive types, map of primitive types",
+ clusterName, search.getName(), unsupportedFields));
+ }
+ }
+
+ private static boolean isUnsupportedComplexField(ImmutableSDField field) {
+ return (field.usesStructOrMap() &&
+ !isSupportedComplexField(field) &&
+ hasStructFieldAttributes(field.getStructFields()));
+ }
+
+ private static boolean isSupportedComplexField(ImmutableSDField field) {
+ return (ComplexAttributeFieldUtils.isSupportedComplexField(field.getDataType()) ||
+ field.getDataType().equals(PositionDataType.INSTANCE) ||
+ field.getDataType().equals(DataType.getArray(PositionDataType.INSTANCE)));
+ }
+
+ private static String toString(ImmutableSDField field) {
+ return field.getName() + " (" + StringUtils.join(getStructFieldAttributes(field.getStructFields()), ", ") + ")";
+ }
+
+ private static boolean hasStructFieldAttributes(Collection<? extends ImmutableSDField> structFields) {
+ return !getStructFieldAttributes(structFields).isEmpty();
+ }
+
+ private static List<String> getStructFieldAttributes(Collection<? extends ImmutableSDField> structFields) {
+ List<String> result = new ArrayList<>();
+ for (ImmutableSDField structField : structFields) {
+ structField.getAttributes().values().forEach(attr -> result.add(attr.getName()));
+ result.addAll(getStructFieldAttributes(structField.getStructFields()));
+ }
+ return result;
+ }
+}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/SearchDataTypeValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/SearchDataTypeValidator.java
index f4d81762e14..240d4f5501a 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/SearchDataTypeValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/SearchDataTypeValidator.java
@@ -15,7 +15,7 @@ import java.util.List;
* This Validator iterates through all search cluster in the given VespaModel to make sure that there are no custom
* structs defined in any of its search definitions.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SearchDataTypeValidator extends Validator {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java
index 523ced52306..e44acf61466 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java
@@ -5,6 +5,7 @@ import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.config.application.api.ValidationOverrides;
import com.yahoo.config.model.api.ConfigChangeAction;
import com.yahoo.config.model.api.Model;
+import com.yahoo.config.model.api.ValidationParameters;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.vespa.model.VespaModel;
import com.yahoo.vespa.model.application.validation.change.ChangeValidator;
@@ -21,8 +22,8 @@ import com.yahoo.vespa.model.application.validation.change.StreamingSearchCluste
import com.yahoo.vespa.model.application.validation.first.AccessControlValidator;
import java.time.Instant;
-import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Optional;
@@ -35,37 +36,37 @@ import static java.util.stream.Collectors.toList;
*/
public class Validation {
- /** Validate everything */
- public static List<ConfigChangeAction> validate(VespaModel model, boolean force, DeployState deployState) {
- return validate(model, true, force, deployState);
- }
-
/**
- * Validate with optional checking of routing, which cannot always be valid in unit tests
+ * Validates the model supplied, and if there already exists a model for the application validates changes
+ * between the previous and current model
*
* @return a list of required changes needed to make this configuration live
*/
- public static List<ConfigChangeAction> validate(VespaModel model, boolean checkRouting, boolean force, DeployState deployState) {
- if (checkRouting) {
+ public static List<ConfigChangeAction> validate(VespaModel model, ValidationParameters validationParameters, DeployState deployState) {
+ if (validationParameters.checkRouting()) {
new RoutingValidator().validate(model, deployState);
new RoutingSelectorValidator().validate(model, deployState);
}
new ComponentValidator().validate(model, deployState);
new SearchDataTypeValidator().validate(model, deployState);
+ new ComplexAttributeFieldsValidator().validate(model, deployState);
new StreamingValidator().validate(model, deployState);
- new RankSetupValidator(force).validate(model, deployState);
+ new RankSetupValidator(validationParameters.ignoreValidationErrors()).validate(model, deployState);
new NoPrefixForIndexes().validate(model, deployState);
new DeploymentFileValidator().validate(model, deployState);
new RankingConstantsValidator().validate(model, deployState);
new SecretStoreValidator().validate(model, deployState);
- Optional<Model> currentActiveModel = deployState.getPreviousModel();
- if (currentActiveModel.isPresent() && (currentActiveModel.get() instanceof VespaModel))
- return validateChanges((VespaModel)currentActiveModel.get(), model,
- deployState.validationOverrides(), deployState.getDeployLogger(), deployState.now());
- else
+ List<ConfigChangeAction> result = Collections.emptyList();
+ if (deployState.getProperties().isFirstTimeDeployment()) {
validateFirstTimeDeployment(model, deployState);
- return new ArrayList<>();
+ } else {
+ Optional<Model> currentActiveModel = deployState.getPreviousModel();
+ if (currentActiveModel.isPresent() && (currentActiveModel.get() instanceof VespaModel))
+ result = validateChanges((VespaModel) currentActiveModel.get(), model,
+ deployState.validationOverrides(), deployState.getDeployLogger(), deployState.now());
+ }
+ return result;
}
private static List<ConfigChangeAction> validateChanges(VespaModel currentModel, VespaModel nextModel,
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidator.java
index bd287f83a1a..deae0a89c56 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidator.java
@@ -36,6 +36,7 @@ public class DocumentDatabaseChangeValidator {
public List<VespaConfigChangeAction> validate(ValidationOverrides overrides, Instant now) {
List<VespaConfigChangeAction> result = new ArrayList<>();
result.addAll(validateAttributeChanges(overrides, now));
+ result.addAll(validateStructFieldAttributeChanges(overrides, now));
result.addAll(validateIndexingScriptChanges(overrides, now));
result.addAll(validateDocumentTypeChanges(overrides, now));
return result;
@@ -49,6 +50,11 @@ public class DocumentDatabaseChangeValidator {
nextDatabase.getDerivedConfiguration().getIndexSchema(), nextDocType).validate(overrides, now);
}
+ private List<VespaConfigChangeAction> validateStructFieldAttributeChanges(ValidationOverrides overrides, Instant now) {
+ return new StructFieldAttributeChangeValidator(currentDocType, currentDatabase.getDerivedConfiguration().getAttributeFields(),
+ nextDocType, nextDatabase.getDerivedConfiguration().getAttributeFields()).validate(overrides, now);
+ }
+
private List<VespaConfigChangeAction> validateIndexingScriptChanges(ValidationOverrides overrides, Instant now) {
return new IndexingScriptChangeValidator(currentDatabase.getDerivedConfiguration().getSearch(),
nextDatabase.getDerivedConfiguration().getSearch()).validate(overrides, now);
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/StructFieldAttributeChangeValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/StructFieldAttributeChangeValidator.java
new file mode 100644
index 00000000000..520494f1697
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/StructFieldAttributeChangeValidator.java
@@ -0,0 +1,132 @@
+// 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.search;
+
+import com.yahoo.config.application.api.ValidationOverrides;
+import com.yahoo.document.ArrayDataType;
+import com.yahoo.document.DataType;
+import com.yahoo.document.Field;
+import com.yahoo.document.MapDataType;
+import com.yahoo.document.StructDataType;
+import com.yahoo.documentmodel.NewDocumentType;
+import com.yahoo.searchdefinition.derived.AttributeFields;
+import com.yahoo.searchdefinition.document.Attribute;
+import com.yahoo.vespa.model.application.validation.change.VespaConfigChangeAction;
+import com.yahoo.vespa.model.application.validation.change.VespaRefeedAction;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.stream.Collectors;
+
+import static com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils.isArrayOfSimpleStruct;
+import static com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils.isMapOfPrimitiveType;
+import static com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils.isMapOfSimpleStruct;
+
+/**
+ * Validates the changes between the current and next set of struct field attributes in a document database.
+
+ * Complex fields of the following types are considered (as they might have struct field attributes):
+ * - array of simple struct
+ * - map of simple struct
+ * - map of primitive types
+ *
+ * @author geirst
+ */
+public class StructFieldAttributeChangeValidator {
+
+ private final NewDocumentType currentDocType;
+ private final AttributeFields currentAttributes;
+ private final NewDocumentType nextDocType;
+ private final AttributeFields nextAttributes;
+
+ public StructFieldAttributeChangeValidator(NewDocumentType currentDocType,
+ AttributeFields currentAttributes,
+ NewDocumentType nextDocType,
+ AttributeFields nextAttributes) {
+ this.currentDocType = currentDocType;
+ this.currentAttributes = currentAttributes;
+ this.nextDocType = nextDocType;
+ this.nextAttributes = nextAttributes;
+ }
+
+ public List<VespaConfigChangeAction> validate(ValidationOverrides overrides, Instant now) {
+ List<VespaConfigChangeAction> result = new ArrayList();
+ for (Field currentField : currentDocType.getAllFields()) {
+ Field nextField = nextDocType.getField(currentField.getName());
+ if (nextField != null) {
+ result.addAll(validateAddAttributeAspect(new Context(currentField, currentAttributes),
+ new Context(nextField, nextAttributes),
+ overrides, now));
+ }
+ }
+ return result;
+ }
+
+ private List<VespaConfigChangeAction> validateAddAttributeAspect(Context current, Context next, ValidationOverrides overrides, Instant now) {
+ return next.structFieldAttributes.stream()
+ .filter(nextAttr -> current.hasFieldFor(nextAttr) &&
+ !current.hasStructFieldAttribute(nextAttr))
+ .map(nextAttr -> VespaRefeedAction.of("field-type-change",
+ overrides,
+ new ChangeMessageBuilder(nextAttr.getName())
+ .addChange("add attribute aspect").build(),
+ now))
+ .collect(Collectors.toList());
+ }
+
+ private static class Context {
+ public Field field;
+ public Collection<Attribute> structFieldAttributes;
+
+ public Context(Field field, AttributeFields attributes) {
+ this.field = field;
+ this.structFieldAttributes = attributes.structFieldAttributes(field.getName());
+ }
+
+ public DataType dataType() {
+ return field.getDataType();
+ }
+
+ public boolean hasStructFieldAttribute(Attribute structFieldAttribute) {
+ return structFieldAttributes.stream()
+ .anyMatch(attr -> attr.getName().equals(structFieldAttribute.getName()));
+ }
+
+ public boolean hasFieldFor(Attribute structFieldAttribute) {
+ StringTokenizer fieldNames = new StringTokenizer(structFieldAttribute.getName(), ".");
+ if (!fieldNames.nextToken().equals(field.getName())) {
+ return false;
+ }
+ if (isArrayOfSimpleStruct(dataType())) {
+ StructDataType nestedType = (StructDataType)((ArrayDataType)dataType()).getNestedType();
+ if (hasLastFieldInStructType(fieldNames, nestedType)) {
+ return true;
+ }
+ } else if (isMapOfSimpleStruct(dataType())) {
+ MapDataType mapType = (MapDataType)dataType();
+ StructDataType valueType = (StructDataType)mapType.getValueType();
+ String subFieldName = fieldNames.nextToken();
+ if (subFieldName.equals("key") && !fieldNames.hasMoreTokens()) {
+ return true;
+ } else if (subFieldName.equals("value") && hasLastFieldInStructType(fieldNames, valueType)) {
+ return true;
+ }
+ } else if (isMapOfPrimitiveType(dataType())) {
+ String subFieldName = fieldNames.nextToken();
+ if ((subFieldName.equals("key") || subFieldName.equals("value")) &&
+ !fieldNames.hasMoreTokens()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean hasLastFieldInStructType(StringTokenizer fieldNames, StructDataType structType) {
+ return structType.getField(fieldNames.nextToken()) != null && !fieldNames.hasMoreTokens();
+ }
+
+ }
+
+}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/BinaryScaledAmountParser.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/BinaryScaledAmountParser.java
index 665ccd09447..5f858467f7c 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/BinaryScaledAmountParser.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/BinaryScaledAmountParser.java
@@ -8,7 +8,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class BinaryScaledAmountParser {
//The pattern must match the one given in the schema
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/BinaryUnit.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/BinaryUnit.java
index c97e181527c..d953d072087 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/BinaryUnit.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/BinaryUnit.java
@@ -7,7 +7,7 @@ import java.util.regex.Pattern;
import static com.yahoo.text.Lowercase.toLowerCase;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class BinaryUnit {
//The pattern must match the one given in the schema
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminBuilderBase.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminBuilderBase.java
index 96e120cae92..297c4a6e9b9 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminBuilderBase.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminBuilderBase.java
@@ -34,7 +34,7 @@ public abstract class DomAdminBuilderBase extends VespaDomBuilder.DomConfigProdu
private static final String DEFAULT_CLUSTER_NAME = "vespa";
private final ApplicationType applicationType;
- private final List<ConfigServerSpec> configServerSpecs;
+ protected final List<ConfigServerSpec> configServerSpecs;
private final FileRegistry fileRegistry;
protected final boolean multitenant;
@@ -52,7 +52,7 @@ public abstract class DomAdminBuilderBase extends VespaDomBuilder.DomConfigProdu
HostSystem hostSystem = parent.getHostSystem();
HostResource host = new HostResource(Host.createConfigServerHost(hostSystem, spec.getHostName()));
hostSystem.addBoundHost(host);
- Configserver configserver = new Configserver(parent, spec.getHostName());
+ Configserver configserver = new Configserver(parent, spec.getHostName(), spec.getConfigServerPort());
configserver.setHostResource(host);
configserver.setBasePort(configserver.getWantedPort());
configserver.initService();
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV2Builder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV2Builder.java
index da8c73c0958..75cd755a91d 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV2Builder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV2Builder.java
@@ -23,6 +23,7 @@ import org.w3c.dom.Element;
import java.util.List;
import java.util.ArrayList;
+import java.util.Objects;
import java.util.logging.Level;
/**
@@ -102,7 +103,7 @@ public class DomAdminV2Builder extends DomAdminBuilderBase {
return cluster;
}
- // Extra stupid because configservers tag is voluntary
+ // Extra stupid because configservers tag is optional
private List<Configserver> getConfigServers(AbstractConfigProducer parent, Element adminE) {
SimpleConfigProducer configServers = new SimpleConfigProducer(parent, "configservers");
List<Configserver> cfgs = new ArrayList<>();
@@ -114,7 +115,7 @@ public class DomAdminV2Builder extends DomAdminBuilderBase {
} else {
parent.deployLogger().log(LogLevel.INFO, "Specifying configserver without parent element configservers in services.xml is deprecated");
}
- Configserver cfgs0 = new ConfigserverBuilder(0).build(configServers, configserverE);
+ Configserver cfgs0 = new ConfigserverBuilder(0, configServerSpecs).build(configServers, configserverE);
cfgs0.setProp("index", 0);
cfgs.add(cfgs0);
return cfgs;
@@ -122,7 +123,7 @@ public class DomAdminV2Builder extends DomAdminBuilderBase {
// configservers tag in use
int i = 0;
for (Element configserverE : XML.getChildren(configserversE, "configserver")) {
- Configserver cfgsrv = new ConfigserverBuilder(i).build(configServers, configserverE);
+ Configserver cfgsrv = new ConfigserverBuilder(i, configServerSpecs).build(configServers, configserverE);
cfgsrv.setProp("index", i);
cfgs.add(cfgsrv);
i++;
@@ -161,16 +162,21 @@ public class DomAdminV2Builder extends DomAdminBuilderBase {
}
private static class ConfigserverBuilder extends DomConfigProducerBuilder<Configserver> {
- int i;
+ private final int i;
+ private final int rpcPort;
- public ConfigserverBuilder(int i) {
+ public ConfigserverBuilder(int i, List<ConfigServerSpec> configServerSpec) {
this.i = i;
+ Objects.requireNonNull(configServerSpec);
+ if (configServerSpec.size() > 0)
+ this.rpcPort = configServerSpec.get(0).getConfigServerPort();
+ else
+ this.rpcPort = Configserver.defaultRpcPort;
}
@Override
- protected Configserver doBuild(AbstractConfigProducer parent,
- Element spec) {
- return new Configserver(parent, "configserver." + i);
+ protected Configserver doBuild(AbstractConfigProducer parent, Element spec) {
+ return new Configserver(parent, "configserver." + i, rpcPort);
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java
index 44de53991f4..ff8d3211d47 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java
@@ -45,17 +45,16 @@ public class DomAdminV4Builder extends DomAdminBuilderBase {
protected void doBuildAdmin(Admin admin, Element w3cAdminElement) {
ModelElement adminElement = new ModelElement(w3cAdminElement);
admin.addConfigservers(getConfigServersFromSpec(admin));
- Version version = context.getDeployState().getWantedNodeVespaVersion();
-
+
// Note: These two elements only exists in admin version 4.0
// This build handles admin version 3.0 by ignoring its content (as the content is not useful)
Optional<NodesSpecification> requestedSlobroks =
- NodesSpecification.optionalDedicatedFromParent(adminElement.getChild("slobroks"), version);
+ NodesSpecification.optionalDedicatedFromParent(adminElement.getChild("slobroks"), context);
Optional<NodesSpecification> requestedLogservers =
- NodesSpecification.optionalDedicatedFromParent(adminElement.getChild("logservers"), version);
+ NodesSpecification.optionalDedicatedFromParent(adminElement.getChild("logservers"), context);
- assignSlobroks(requestedSlobroks.orElse(NodesSpecification.nonDedicated(3, version)), admin);
- assignLogserver(requestedLogservers.orElse(NodesSpecification.nonDedicated(1, version)), admin);
+ assignSlobroks(requestedSlobroks.orElse(NodesSpecification.nonDedicated(3, context)), admin);
+ assignLogserver(requestedLogservers.orElse(NodesSpecification.nonDedicated(1, context)), admin);
addLogForwarders(adminElement.getChild("logforwarding"), admin);
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomComponentBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomComponentBuilder.java
index 6c5d7092af8..81f19f00fbb 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomComponentBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomComponentBuilder.java
@@ -12,7 +12,7 @@ import org.w3c.dom.Element;
/**
* @author gjoranv
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class DomComponentBuilder extends VespaDomBuilder.DomConfigProducerBuilder<Component> {
@@ -36,7 +36,7 @@ public class DomComponentBuilder extends VespaDomBuilder.DomConfigProducerBuilde
private Component buildComponent(Element spec) {
BundleInstantiationSpecification bundleSpec =
- BundleInstantiationSpecificationBuilder.build(spec, false).nestInNamespace(namespace);
+ BundleInstantiationSpecificationBuilder.build(spec).nestInNamespace(namespace);
return new Component<Component<?, ?>, ComponentModel>(new ComponentModel(bundleSpec));
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomFilterBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomFilterBuilder.java
index 122a13a7b74..74cc2b8421c 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomFilterBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomFilterBuilder.java
@@ -8,11 +8,11 @@ import com.yahoo.vespa.model.container.xml.BundleInstantiationSpecificationBuild
import org.w3c.dom.Element;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class DomFilterBuilder extends VespaDomBuilder.DomConfigProducerBuilder<Component> {
@Override
protected Component doBuild(AbstractConfigProducer ancestor, Element element) {
- return new HttpFilter(BundleInstantiationSpecificationBuilder.build(element, false));
+ return new HttpFilter(BundleInstantiationSpecificationBuilder.build(element));
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomHandlerBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomHandlerBuilder.java
index 23f65fd9fb1..28a4b9dfea5 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomHandlerBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomHandlerBuilder.java
@@ -12,18 +12,8 @@ import org.w3c.dom.Element;
/**
* @author gjoranv
- * @since 5.1.6
*/
public class DomHandlerBuilder extends VespaDomBuilder.DomConfigProducerBuilder<Handler> {
- private final boolean legacyMode;
-
- public DomHandlerBuilder(boolean legacyMode) {
- this.legacyMode = legacyMode;
- }
-
- public DomHandlerBuilder() {
- this(false);
- }
@Override
protected Handler doBuild(AbstractConfigProducer ancestor, Element handlerElement) {
@@ -41,7 +31,7 @@ public class DomHandlerBuilder extends VespaDomBuilder.DomConfigProducerBuilder<
}
protected Handler<? super Component<?, ?>> getHandler(Element handlerElement) {
- BundleInstantiationSpecification bundleSpec = BundleInstantiationSpecificationBuilder.build(handlerElement, legacyMode);
+ BundleInstantiationSpecification bundleSpec = BundleInstantiationSpecificationBuilder.build(handlerElement);
return new Handler<>(
new ComponentModel(bundleSpec));
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java
index f7ed7241133..94359e8672e 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.model.builder.xml.dom;
import com.yahoo.component.Version;
+import com.yahoo.config.model.ConfigModelContext;
import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterMembership;
import com.yahoo.config.provision.ClusterSpec;
@@ -34,34 +35,38 @@ public class NodesSpecification {
* at the discretion of the component fulfilling it
*/
private final boolean required;
-
- /** The flavor the nodes should have, or empty to use the default */
- private final Optional<String> flavor;
+ private final boolean canFail;
+
private final boolean exclusive;
+ /** The flavor the nodes should have, or empty to use the default */
+ private final Optional<String> flavor;
+
/** The identifier of the custom docker image layer to use (not supported yet) */
private final Optional<String> dockerImage;
- private NodesSpecification(boolean dedicated, int count, int groups, Version version, boolean required,
- boolean exclusive,
+ private NodesSpecification(boolean dedicated, int count, int groups, Version version,
+ boolean required, boolean canFail, boolean exclusive,
Optional<String> flavor, Optional<String> dockerImage) {
this.dedicated = dedicated;
this.count = count;
this.groups = groups;
this.version = version;
this.required = required;
+ this.canFail = canFail;
this.exclusive = exclusive;
this.flavor = flavor;
this.dockerImage = dockerImage;
}
- private NodesSpecification(boolean dedicated, Version version, ModelElement nodesElement) {
+ private NodesSpecification(boolean dedicated, boolean canFail, Version version, ModelElement nodesElement) {
this(dedicated,
nodesElement.requiredIntegerAttribute("count"),
nodesElement.getIntegerAttribute("groups", 1),
version,
nodesElement.getBooleanAttribute("required", false),
+ canFail,
nodesElement.getBooleanAttribute("exclusive", false),
Optional.ofNullable(nodesElement.getStringAttribute("flavor")),
Optional.ofNullable(nodesElement.getStringAttribute("docker-image")));
@@ -70,8 +75,11 @@ public class NodesSpecification {
/**
* Returns a requirement for dedicated nodes taken from the given <code>nodes</code> element
*/
- public static NodesSpecification from(ModelElement nodesElement, Version version) {
- return new NodesSpecification(true, version, nodesElement);
+ public static NodesSpecification from(ModelElement nodesElement, ConfigModelContext context) {
+ return new NodesSpecification(true,
+ ! context.getDeployState().getProperties().isBootstrap(),
+ context.getDeployState().getWantedNodeVespaVersion(),
+ nodesElement);
}
/**
@@ -79,11 +87,11 @@ public class NodesSpecification {
* contained in the given parent element, or empty if the parent element is null, or the nodes elements
* is not present.
*/
- public static Optional<NodesSpecification> fromParent(ModelElement parentElement, Version version) {
+ public static Optional<NodesSpecification> fromParent(ModelElement parentElement, ConfigModelContext context) {
if (parentElement == null) return Optional.empty();
ModelElement nodesElement = parentElement.getChild("nodes");
if (nodesElement == null) return Optional.empty();
- return Optional.of(from(nodesElement, version));
+ return Optional.of(from(nodesElement, context));
}
/**
@@ -91,18 +99,28 @@ public class NodesSpecification {
* contained in the given parent element, or empty if the parent element is null, or the nodes elements
* is not present.
*/
- public static Optional<NodesSpecification> optionalDedicatedFromParent(ModelElement parentElement, Version version) {
+ public static Optional<NodesSpecification> optionalDedicatedFromParent(ModelElement parentElement,
+ ConfigModelContext context) {
if (parentElement == null) return Optional.empty();
ModelElement nodesElement = parentElement.getChild("nodes");
if (nodesElement == null) return Optional.empty();
return Optional.of(new NodesSpecification(nodesElement.getBooleanAttribute("dedicated", false),
- version, nodesElement));
+ ! context.getDeployState().getProperties().isBootstrap(),
+ context.getDeployState().getWantedNodeVespaVersion(),
+ nodesElement));
}
/** Returns a requirement from <code>count</code> nondedicated nodes in one group */
- public static NodesSpecification nonDedicated(int count, Version version) {
- return new NodesSpecification(false, count, 1, version, false, false,
- Optional.empty(), Optional.empty());
+ public static NodesSpecification nonDedicated(int count, ConfigModelContext context) {
+ return new NodesSpecification(false,
+ count,
+ 1,
+ context.getDeployState().getWantedNodeVespaVersion(),
+ false,
+ ! context.getDeployState().getProperties().isBootstrap(),
+ false,
+ Optional.empty(),
+ Optional.empty());
}
/**
@@ -124,9 +142,12 @@ public class NodesSpecification {
/** Returns the number of host groups this specifies. Default is 1 */
public int groups() { return groups; }
- public Map<HostResource, ClusterMembership> provision(HostSystem hostSystem, ClusterSpec.Type clusterType, ClusterSpec.Id clusterId, DeployLogger logger) {
+ public Map<HostResource, ClusterMembership> provision(HostSystem hostSystem,
+ ClusterSpec.Type clusterType,
+ ClusterSpec.Id clusterId,
+ DeployLogger logger) {
ClusterSpec cluster = ClusterSpec.request(clusterType, clusterId, version, exclusive);
- return hostSystem.allocateHosts(cluster, Capacity.fromNodeCount(count, flavor, required), groups, logger);
+ return hostSystem.allocateHosts(cluster, Capacity.fromNodeCount(count, flavor, required, canFail), groups, logger);
}
@Override
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/ServletBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/ServletBuilder.java
index 3d779289340..d2d9e9d2ca6 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/ServletBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/ServletBuilder.java
@@ -27,7 +27,7 @@ public class ServletBuilder extends VespaDomBuilder.DomConfigProducerBuilder<Ser
}
private SimpleComponent createServletComponent(Element servletElement) {
- ComponentModel componentModel = new ComponentModel(BundleInstantiationSpecificationBuilder.build(servletElement, false));
+ ComponentModel componentModel = new ComponentModel(BundleInstantiationSpecificationBuilder.build(servletElement));
return new SimpleComponent(componentModel);
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/ChainSpecificationBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/ChainSpecificationBuilder.java
index 57b027de8b9..760e9b9e829 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/ChainSpecificationBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/ChainSpecificationBuilder.java
@@ -17,7 +17,7 @@ import java.util.Set;
/**
* Creates a partial ChainSpecification without inner components
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ChainSpecificationBuilder {
private final ComponentId componentId;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/ChainedComponentModelBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/ChainedComponentModelBuilder.java
index 65fc4728060..68b37d938a0 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/ChainedComponentModelBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/ChainedComponentModelBuilder.java
@@ -8,14 +8,14 @@ import org.w3c.dom.Element;
/**
* Builds a regular ChainedComponentModel from an element.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ChainedComponentModelBuilder extends GenericChainedComponentModelBuilder {
protected final BundleInstantiationSpecification bundleInstantiationSpec;
public ChainedComponentModelBuilder(Element spec) {
super(spec);
- bundleInstantiationSpec = BundleInstantiationSpecificationBuilder.build(spec, false);
+ bundleInstantiationSpec = BundleInstantiationSpecificationBuilder.build(spec);
}
public ChainedComponentModel build() {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/ChainsBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/ChainsBuilder.java
index a2761b1301b..332f75db339 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/ChainsBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/ChainsBuilder.java
@@ -10,7 +10,7 @@ import org.w3c.dom.Element;
import java.util.*;
/**
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
*/
public class ChainsBuilder<COMPONENT extends ChainedComponent<?>, CHAIN extends Chain<COMPONENT>> {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/ComponentsBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/ComponentsBuilder.java
index d9377723cf9..c94db473699 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/ComponentsBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/ComponentsBuilder.java
@@ -23,7 +23,7 @@ import java.util.*;
/**
* Creates component models and component references from xml for a given scope.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ComponentsBuilder<T extends ChainedComponent<?>> {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/DependenciesBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/DependenciesBuilder.java
index b41b0b1413c..ec5c0338f70 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/DependenciesBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/DependenciesBuilder.java
@@ -10,7 +10,7 @@ import java.util.Set;
/**
* Builds Dependencies (provides, before, after) from an element.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class DependenciesBuilder {
private final Dependencies dependencies;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/DomBuilderCreator.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/DomBuilderCreator.java
index 3dc42bc8f1e..0fb55dc5633 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/DomBuilderCreator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/DomBuilderCreator.java
@@ -6,7 +6,7 @@ import java.lang.reflect.InvocationTargetException;
/**
* Utility class for instantiating a builder using reflection.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class DomBuilderCreator {
public static <T> T create(Class<T> builderClass, Object... parameters) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/DomChainBuilderBase.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/DomChainBuilderBase.java
index 85147b9bb66..acc07e77d75 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/DomChainBuilderBase.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/DomChainBuilderBase.java
@@ -14,7 +14,7 @@ import java.util.Map;
/**
* Base functionality for all chain builders (docprocChain, searchChain, provider, source)
- * @author tonytv
+ * @author Tony Vaagenes
*/
public abstract class DomChainBuilderBase<COMPONENT extends ChainedComponent<?>, CHAIN extends Chain<COMPONENT>>
extends VespaDomBuilder.DomConfigProducerBuilder<CHAIN> {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/DomChainsBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/DomChainsBuilder.java
index c3fd777e94a..eb96221d839 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/DomChainsBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/DomChainsBuilder.java
@@ -18,7 +18,7 @@ import java.util.Map;
/**
* NOTE: This class _must_ be abstract, due to calling subclass method in ctor.
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
*/
public abstract
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/GenericChainedComponentModelBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/GenericChainedComponentModelBuilder.java
index 6a9d1499f6f..84ff290ab22 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/GenericChainedComponentModelBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/GenericChainedComponentModelBuilder.java
@@ -9,7 +9,7 @@ import org.w3c.dom.Element;
/**
* reads the common attributes and elements of all chained component elements.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public abstract class GenericChainedComponentModelBuilder {
//The componentId might be used as a spec later(for example as class or
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/InheritanceBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/InheritanceBuilder.java
index 34ec6dcea57..869cd54471e 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/InheritanceBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/InheritanceBuilder.java
@@ -11,7 +11,7 @@ import java.util.*;
/**
* Build an Inheritance object from an inheritance section.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class InheritanceBuilder {
final ChainSpecification.Inheritance inheritance;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomFederationSearcherBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomFederationSearcherBuilder.java
index 40a2f2ee144..947ae07342c 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomFederationSearcherBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomFederationSearcherBuilder.java
@@ -15,7 +15,6 @@ import com.yahoo.vespa.model.container.component.Component;
import com.yahoo.vespa.model.container.search.searchchain.FederationSearcher;
import com.yahoo.vespa.model.container.search.searchchain.Searcher;
import org.w3c.dom.Element;
-import scala.Option;
import java.util.ArrayList;
import java.util.List;
@@ -23,7 +22,7 @@ import java.util.Optional;
/**
* Builds a federation searcher config producer from an element.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class DomFederationSearcherBuilder extends VespaDomBuilder.DomConfigProducerBuilder<Searcher<?>> {
static class FederationSearcherModelBuilder extends GenericChainedComponentModelBuilder {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomGenericTargetBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomGenericTargetBuilder.java
index 65820348ce1..df7c479b575 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomGenericTargetBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomGenericTargetBuilder.java
@@ -13,7 +13,7 @@ import java.util.Arrays;
import java.util.Map;
/**
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
* Base functionality for all target chain builders (provider, source)
*/
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomProviderBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomProviderBuilder.java
index 56484161097..ffacb146cee 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomProviderBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomProviderBuilder.java
@@ -31,7 +31,7 @@ import java.util.Map;
* since the mangling is an intrinsic of the configuration language,
* not the model itself.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class DomProviderBuilder extends DomGenericTargetBuilder<Provider> {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearchChainBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearchChainBuilder.java
index 4ee486e355e..e5086ccd9f9 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearchChainBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearchChainBuilder.java
@@ -14,7 +14,7 @@ import java.util.Map;
/**
* Builds a Search chain from xml.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class DomSearchChainBuilder extends DomChainBuilderBase<Searcher<?>, SearchChain> {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearchChainsBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearchChainsBuilder.java
index ed6c11c1fbd..c5802f8e578 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearchChainsBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearchChainsBuilder.java
@@ -17,7 +17,7 @@ import java.util.Map;
/**
* Builds the search chains model from xml.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class DomSearchChainsBuilder extends DomChainsBuilder<Searcher<?>, SearchChain, SearchChains> {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearcherBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearcherBuilder.java
index 6464ac47512..86c30fc13d0 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearcherBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearcherBuilder.java
@@ -10,7 +10,7 @@ import org.w3c.dom.Element;
/**
* Builds a Searcher from XML.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class DomSearcherBuilder extends VespaDomBuilder.DomConfigProducerBuilder<Searcher<?>> {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSourceBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSourceBuilder.java
index 4131da1e2cd..177f77e9348 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSourceBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSourceBuilder.java
@@ -12,7 +12,7 @@ import java.util.Map;
/**
* Builds a source from xml.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class DomSourceBuilder extends DomGenericTargetBuilder<Source> {
DomSourceBuilder(Map<String, ComponentsBuilder.ComponentType> outerSearcherTypeByComponentName) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/FederationOptionsBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/FederationOptionsBuilder.java
index 9cba9303426..3a4bdea400d 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/FederationOptionsBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/FederationOptionsBuilder.java
@@ -7,7 +7,7 @@ import org.w3c.dom.Element;
/**
* Builds federation options from a federations options element
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class FederationOptionsBuilder {
public static final String federationOptionsElement = "federationoptions";
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/SearchChainsBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/SearchChainsBuilder.java
index 54b0052ddbd..fad5a66be98 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/SearchChainsBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/SearchChainsBuilder.java
@@ -12,7 +12,7 @@ import org.w3c.dom.Element;
import java.util.*;
/**
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
* Creates top level search chains(searchchain, provider) from xml.
*/
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/TimeParser.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/TimeParser.java
index 71813fb098a..e22cd0c72ea 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/TimeParser.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/TimeParser.java
@@ -7,7 +7,7 @@ import java.util.regex.Pattern;
/**
* Utility class for parsing timeout fields.
*
- * @author tonytv
+ * @author Tony Vaagenes
* @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
*/
public class TimeParser {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerModel.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerModel.java
index 668596c3f18..83d749067b4 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerModel.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerModel.java
@@ -17,7 +17,7 @@ import java.util.TreeMap;
/**
* A model of a container cluster.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ContainerModel extends ConfigModel {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/AccessLogComponent.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/AccessLogComponent.java
index 06f8c38d9a9..5373d603227 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/AccessLogComponent.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/AccessLogComponent.java
@@ -11,7 +11,7 @@ import edu.umd.cs.findbugs.annotations.Nullable;
import static com.yahoo.container.core.AccessLogConfig.FileHandler.RotateScheme;
/**
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
* @since 5.1.4
*/
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/Component.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/Component.java
index 99ad7a291fc..08b6e321aa3 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/Component.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/Component.java
@@ -13,7 +13,7 @@ import java.util.Set;
/**
* @author gjoranv
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class Component<CHILD extends AbstractConfigProducer<?>, MODEL extends ComponentModel>
extends AbstractConfigProducer<CHILD> implements Comparable<Component<?, ?>> {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/ComponentGroup.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/ComponentGroup.java
index d370418ee8a..a476b4a77fe 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/ComponentGroup.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/ComponentGroup.java
@@ -4,7 +4,7 @@ package com.yahoo.vespa.model.container.component;
import com.yahoo.config.model.producer.AbstractConfigProducer;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ComponentGroup <CHILD extends Component<?, ?>> extends ConfigProducerGroup<CHILD> {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/ConfigProducerGroup.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/ConfigProducerGroup.java
index 5460407e7cc..e8142999433 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/ConfigProducerGroup.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/ConfigProducerGroup.java
@@ -9,7 +9,7 @@ import java.util.*;
/**
* A group of config producers that have a component id.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ConfigProducerGroup<CHILD extends AbstractConfigProducer<?>> extends AbstractConfigProducer<CHILD> {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/FileStatusHandlerComponent.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/FileStatusHandlerComponent.java
index c25158f5d99..dc04aa54ba0 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/FileStatusHandlerComponent.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/FileStatusHandlerComponent.java
@@ -6,7 +6,7 @@ import com.yahoo.osgi.provider.model.ComponentModel;
/**
* Sets up VipStatusHandler that answers OK when a certain file is present.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class FileStatusHandlerComponent extends Handler implements VipStatusConfig.Producer {
public static final String CLASS = "com.yahoo.container.handler.VipStatusHandler";
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/HttpFilter.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/HttpFilter.java
index e943da41dd3..1bf577deebc 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/HttpFilter.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/HttpFilter.java
@@ -12,7 +12,7 @@ import com.yahoo.osgi.provider.model.ComponentModel;
*
* TODO: Remove when 'filter' directly under 'jdisc' can be removed from services.xml
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class HttpFilter extends SimpleComponent {
private static final ComponentSpecification filterConfigProviderClass =
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/StatisticsComponent.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/StatisticsComponent.java
index b35d2319666..2cb06ea3a30 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/StatisticsComponent.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/StatisticsComponent.java
@@ -5,7 +5,7 @@ import com.yahoo.vespa.model.admin.monitoring.Monitoring;
import com.yahoo.container.StatisticsConfig;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class StatisticsComponent extends SimpleComponent implements StatisticsConfig.Producer {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/Chain.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/Chain.java
index 5d845775bd6..f795e481f62 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/Chain.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/Chain.java
@@ -16,7 +16,7 @@ import static com.yahoo.container.core.ChainsConfig.Chains.Type;
* Represents a component chain in the vespa model.
* The inner components are represented as children.
*
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
*/
public class Chain<T extends ChainedComponent<?>> extends AbstractConfigProducer<AbstractConfigProducer<?>> {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/ChainedComponent.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/ChainedComponent.java
index 196a3bab46f..40440f3456b 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/ChainedComponent.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/ChainedComponent.java
@@ -8,7 +8,7 @@ import com.yahoo.vespa.model.container.component.Component;
/**
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
*
* Base class for all ChainedComponent config producers.
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/ChainedComponentConfigGenerator.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/ChainedComponentConfigGenerator.java
index 41b3fda8d7b..603e91047b5 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/ChainedComponentConfigGenerator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/ChainedComponentConfigGenerator.java
@@ -10,7 +10,7 @@ import java.util.Set;
import static com.yahoo.container.core.ChainsConfig.Components;
/**
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
*
* Generates config for all the chained components.
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/Chains.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/Chains.java
index 7bd6867f80c..0fc5c38c3b3 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/Chains.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/Chains.java
@@ -13,7 +13,7 @@ import java.util.Set;
/**
* Root config producer the whole chains model(contains chains and components).
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
*/
public class Chains<CHAIN extends Chain<?>>
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/ChainsConfigGenerator.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/ChainsConfigGenerator.java
index 3890b118512..67e4d2e6cab 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/ChainsConfigGenerator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/ChainsConfigGenerator.java
@@ -13,7 +13,7 @@ import java.util.List;
import static com.yahoo.container.core.ChainsConfig.Chains.*;
/**
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
*
* Generates config for a all the chains.
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/configserver/option/package-info.java b/config-model/src/main/java/com/yahoo/vespa/model/container/configserver/option/package-info.java
index 3472bb93dc0..7c019e0bdae 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/configserver/option/package-info.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/configserver/option/package-info.java
@@ -1,6 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
@ExportPackage
package com.yahoo.vespa.model.container.configserver.option;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/Filter.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/Filter.java
index 20cbb2a09b2..e489b984a62 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/Filter.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/Filter.java
@@ -5,7 +5,7 @@ import com.yahoo.component.chain.model.ChainedComponentModel;
import com.yahoo.vespa.model.container.component.chain.ChainedComponent;
/**
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
*/
public class Filter extends ChainedComponent<ChainedComponentModel> {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/FilterChains.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/FilterChains.java
index d7ba88694e9..ecc7013021a 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/FilterChains.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/FilterChains.java
@@ -13,7 +13,7 @@ import com.yahoo.vespa.model.container.component.chain.Chains;
import java.util.Collections;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class FilterChains extends Chains<Chain<Filter>> {
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 14537d98479..4eed3628bfd 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
@@ -17,7 +17,7 @@ import java.util.Optional;
/**
* Represents the http servers and filters of a Jdisc cluster.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class Http extends AbstractConfigProducer<AbstractConfigProducer<?>>
implements HttpServerConfig.Producer, ServerConfig.Producer {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/FilterBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/FilterBuilder.java
index 08b94d8dc6f..137a59c1311 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/FilterBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/FilterBuilder.java
@@ -11,7 +11,7 @@ import com.yahoo.vespa.model.container.http.FilterConfigProvider;
import org.w3c.dom.Element;
/**
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
*/
public class FilterBuilder extends VespaDomBuilder.DomConfigProducerBuilder<Filter> {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/FilterChainBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/FilterChainBuilder.java
index c7b85056cbd..8749968606b 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/FilterChainBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/FilterChainBuilder.java
@@ -15,7 +15,7 @@ import java.util.Map;
import static com.yahoo.vespa.model.builder.xml.dom.chains.ComponentsBuilder.ComponentType;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class FilterChainBuilder extends DomChainBuilderBase<Filter, Chain<Filter>> {
private static Collection<ComponentType<Filter>> allowedComponentTypes = Collections.singleton(ComponentType.filter);
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/FilterChainsBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/FilterChainsBuilder.java
index f20b7e3f5cf..d5448d47ad0 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/FilterChainsBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/FilterChainsBuilder.java
@@ -16,7 +16,7 @@ import org.w3c.dom.Element;
import java.util.*;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class FilterChainsBuilder extends DomChainsBuilder<Filter, Chain<Filter>, FilterChains> {
private static final Collection<ComponentType<Filter>> allowedComponentTypes =
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/HttpBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/HttpBuilder.java
index 0a14d445fe2..410e5fda491 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/HttpBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/HttpBuilder.java
@@ -26,7 +26,7 @@ import java.util.Optional;
import static com.yahoo.vespa.model.container.http.AccessControl.ACCESS_CONTROL_CHAIN_ID;
/**
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
*/
public class HttpBuilder extends VespaDomBuilder.DomConfigProducerBuilder<Http> {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/jersey/Jersey2Servlet.java b/config-model/src/main/java/com/yahoo/vespa/model/container/jersey/Jersey2Servlet.java
index b8ed5d296a4..06775d59654 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/jersey/Jersey2Servlet.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/jersey/Jersey2Servlet.java
@@ -9,7 +9,7 @@ import com.yahoo.osgi.provider.model.ComponentModel;
import com.yahoo.vespa.model.container.component.Servlet;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class Jersey2Servlet extends Servlet {
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 5afba6fa373..c711f268534 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
@@ -25,7 +25,7 @@ import java.util.*;
/**
* @author gjoranv
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ContainerSearch extends ContainerSubsystem<SearchChains>
implements
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/GUIHandler.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/GUIHandler.java
new file mode 100644
index 00000000000..025075be8fd
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/GUIHandler.java
@@ -0,0 +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.model.container.search;
+
+import com.yahoo.config.model.producer.AbstractConfigProducer;
+import com.yahoo.container.bundle.BundleInstantiationSpecification;
+import com.yahoo.osgi.provider.model.ComponentModel;
+import com.yahoo.vespa.model.container.component.Handler;
+
+
+/**
+ * @author Henrik Høiness
+ */
+
+public class GUIHandler extends Handler<AbstractConfigProducer<?>> {
+ public static final String BUNDLE = "container-search-gui";
+ public static final String CLASS = "com.yahoo.search.query.gui.GUIHandler";
+ public static final String BINDING = "*/querybuilder/*";
+
+ public GUIHandler() {
+ super(new ComponentModel(bundleSpec(CLASS, BUNDLE)));
+ }
+
+ public static BundleInstantiationSpecification bundleSpec(String className, String bundle) {
+ return BundleInstantiationSpecification.getFromStrings(className, className, bundle);
+ }
+
+}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/FederationSearcher.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/FederationSearcher.java
index 093bb39130a..19d014e0a1d 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/FederationSearcher.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/FederationSearcher.java
@@ -15,7 +15,7 @@ import java.util.*;
/**
* Config producer for the FederationSearcher.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class FederationSearcher extends Searcher<FederationSearcherModel> implements FederationConfig.Producer {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/GenericTarget.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/GenericTarget.java
index de963150d4d..875de1465c6 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/GenericTarget.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/GenericTarget.java
@@ -6,7 +6,7 @@ import com.yahoo.search.searchchain.model.federation.FederationOptions;
/**
* A search chain that is intended to be used for federation (i.e. providers, sources)
- * @author tonytv
+ * @author Tony Vaagenes
*/
abstract public class GenericTarget extends SearchChain {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/HttpProvider.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/HttpProvider.java
index 0dcd45e4e64..3cc3fd1ca00 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/HttpProvider.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/HttpProvider.java
@@ -19,7 +19,7 @@ import static com.yahoo.search.federation.ProviderConfig.Yca;
/**
* A provider containing a http searcher.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class HttpProvider extends Provider implements ProviderConfig.Producer,
QrBinaryCacheConfig.Producer,
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/HttpProviderSearcher.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/HttpProviderSearcher.java
index a096389baf9..56b4e544e9f 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/HttpProviderSearcher.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/HttpProviderSearcher.java
@@ -9,7 +9,7 @@ import java.util.List;
/**
-* @author tonytv
+* @author Tony Vaagenes
*/
public class HttpProviderSearcher extends Searcher<ChainedComponentModel> {
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 3c61bba6298..cb1d94717f6 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
@@ -23,7 +23,7 @@ import java.util.*;
/**
* Config producer for search chain responsible for sending queries to a local cluster.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class LocalProvider extends Provider implements
DocumentdbInfoConfig.Producer,
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/Provider.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/Provider.java
index ed737612922..ee4edf3fd8c 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/Provider.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/Provider.java
@@ -11,7 +11,7 @@ import java.util.Collection;
/**
* Base config producer for search chains that communicate with backends.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class Provider extends GenericTarget {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/SearchChain.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/SearchChain.java
index dcf9d06e38b..2605736e23b 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/SearchChain.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/SearchChain.java
@@ -11,7 +11,7 @@ import java.util.List;
/**
* Represents a search chain in the vespa model.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class SearchChain extends Chain<Searcher<?>> {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/SearchChains.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/SearchChains.java
index 9393c08ff11..c5913528435 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/SearchChains.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/SearchChains.java
@@ -16,7 +16,7 @@ import java.util.Map;
/**
* Root config producer of the whole search chains model (contains searchchains and searchers).
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class SearchChains extends Chains<SearchChain> {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/Searcher.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/Searcher.java
index a25b6bd7665..413b3e2814f 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/Searcher.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/Searcher.java
@@ -7,7 +7,7 @@ import com.yahoo.vespa.model.container.component.chain.ChainedComponent;
/**
* @author gjoranv
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class Searcher<T extends ChainedComponentModel> extends ChainedComponent<T> {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/Source.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/Source.java
index 407ac361737..cebf27b1700 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/Source.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/Source.java
@@ -12,7 +12,7 @@ import java.util.Arrays;
/**
* Config producer for source, which is contained in a provider.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class Source extends GenericTarget {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/SourceGroup.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/SourceGroup.java
index 31b183cef2b..5d51f48f6ae 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/SourceGroup.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/SourceGroup.java
@@ -12,7 +12,7 @@ import java.util.Set;
* A set of sources with the same name,
* each associated with a different provider,
* that fills the same role.
- * @author tonytv
+ * @author Tony Vaagenes
*/
final class SourceGroup {
private final ComponentId id;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/SourceGroupRegistry.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/SourceGroupRegistry.java
index 31fb960ed60..e073d82f5da 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/SourceGroupRegistry.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/SourceGroupRegistry.java
@@ -13,7 +13,7 @@ import java.util.List;
/**
* Owns all the source groups in the search chains model.
- * @author tonytv
+ * @author Tony Vaagenes
*/
class SourceGroupRegistry {
private final ComponentRegistry<ComponentAdaptor<SourceGroup>> sourceGroups
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/defaultsearchchains/LocalClustersCreator.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/defaultsearchchains/LocalClustersCreator.java
index ef2a63c0530..a1b14017743 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/defaultsearchchains/LocalClustersCreator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/defaultsearchchains/LocalClustersCreator.java
@@ -16,7 +16,7 @@ import java.util.Set;
/**
* Adds default search chains for all local clusters not mentioned explicitly
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class LocalClustersCreator {
static ChainSpecification emptySearchChainSpecification(String componentName) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/defaultsearchchains/VespaSearchChainsCreator.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/defaultsearchchains/VespaSearchChainsCreator.java
index d1ec9c3a3c8..7d53adbb233 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/defaultsearchchains/VespaSearchChainsCreator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/defaultsearchchains/VespaSearchChainsCreator.java
@@ -18,7 +18,7 @@ import java.util.*;
* Creates the search chains vespaPhases, vespa and native.
*
* <p>TODO: refactor</p>
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class VespaSearchChainsCreator {
private static class PhasesCreator {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/AccessLogBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/AccessLogBuilder.java
index 199dbfede42..6afed7c9718 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/AccessLogBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/AccessLogBuilder.java
@@ -17,7 +17,7 @@ import static com.yahoo.config.model.builder.xml.XmlHelper.getOptionalAttribute;
import static com.yahoo.config.model.builder.xml.XmlHelper.nullIfEmpty;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class AccessLogBuilder {
/*
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilder.java
index 69ee9048b28..2828fcf09d0 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilder.java
@@ -4,11 +4,10 @@ package com.yahoo.vespa.model.container.xml;
import com.yahoo.config.model.builder.xml.XmlHelper;
import com.yahoo.container.bundle.BundleInstantiationSpecification;
import com.yahoo.component.ComponentSpecification;
-import com.yahoo.processing.handler.ProcessingHandler;
-import com.yahoo.search.handler.SearchHandler;
import org.w3c.dom.Element;
-import java.util.*;
+import java.util.Arrays;
+import java.util.List;
/**
* This object builds a bundle instantiation spec from an XML element.
@@ -17,14 +16,13 @@ import java.util.*;
*/
public class BundleInstantiationSpecificationBuilder {
- public static BundleInstantiationSpecification build(Element spec, boolean legacyMode) {
+ public static BundleInstantiationSpecification build(Element spec) {
ComponentSpecification id = XmlHelper.getIdRef(spec);
ComponentSpecification classId = getComponentSpecification(spec, "class");
ComponentSpecification bundle = getComponentSpecification(spec, "bundle");
BundleInstantiationSpecification instSpec = new BundleInstantiationSpecification(id, classId, bundle);
- if ( ! legacyMode) // TODO: Remove?
- validate(instSpec);
+ validate(instSpec);
return bundle == null ? setBundleForKnownClass(instSpec) : instSpec;
}
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 2572b0d772b..d81026c54d1 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
@@ -23,6 +23,7 @@ import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.Rotation;
import com.yahoo.config.provision.Zone;
import com.yahoo.container.jdisc.config.MetricDefaultsConfig;
+import com.yahoo.osgi.provider.model.ComponentModel;
import com.yahoo.search.rendering.RendererRegistry;
import com.yahoo.text.XML;
import com.yahoo.vespa.defaults.Defaults;
@@ -47,6 +48,7 @@ import com.yahoo.vespa.model.container.IdentityProvider;
import com.yahoo.vespa.model.container.SecretStore;
import com.yahoo.vespa.model.container.component.Component;
import com.yahoo.vespa.model.container.component.FileStatusHandlerComponent;
+import com.yahoo.vespa.model.container.component.Handler;
import com.yahoo.vespa.model.container.component.chain.ProcessingHandler;
import com.yahoo.vespa.model.container.docproc.ContainerDocproc;
import com.yahoo.vespa.model.container.docproc.DocprocChains;
@@ -55,6 +57,7 @@ import com.yahoo.vespa.model.container.http.xml.HttpBuilder;
import com.yahoo.vespa.model.container.jersey.xml.RestApiBuilder;
import com.yahoo.vespa.model.container.processing.ProcessingChains;
import com.yahoo.vespa.model.container.search.ContainerSearch;
+import com.yahoo.vespa.model.container.search.GUIHandler;
import com.yahoo.vespa.model.container.search.PageTemplates;
import com.yahoo.vespa.model.container.search.QueryProfiles;
import com.yahoo.vespa.model.container.search.SemanticRules;
@@ -151,7 +154,6 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
private void addClusterContent(ContainerCluster cluster, Element spec, ConfigModelContext context) {
DocumentFactoryBuilder.buildDocumentFactories(cluster, spec);
-
addConfiguredComponents(cluster, spec);
addSecretStore(cluster, spec);
addHandlers(cluster, spec);
@@ -379,6 +381,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
cluster.setSearch(buildSearch(cluster, searchElement, queryProfiles, semanticRules));
addSearchHandler(cluster, searchElement);
+ addGUIHandler(cluster);
validateAndAddConfiguredComponents(cluster, searchElement, "renderer", ContainerModelBuilder::validateRendererElement);
}
}
@@ -514,7 +517,11 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
ClusterSpec.Id.from(cluster.getName()),
context.getDeployState().getWantedNodeVespaVersion(),
false);
- return cluster.getHostSystem().allocateHosts(clusterSpec, Capacity.fromNodeCount(1), 1, logger).keySet().iterator().next();
+ Capacity capacity = Capacity.fromNodeCount(1,
+ Optional.empty(),
+ false,
+ ! context.getDeployState().getProperties().isBootstrap());
+ return cluster.getHostSystem().allocateHosts(clusterSpec, capacity, 1, logger).keySet().iterator().next();
}
} else {
return cluster.getHostSystem().getHost(Container.SINGLENODE_CONTAINER_SERVICESPEC);
@@ -522,8 +529,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
}
private List<Container> createNodesFromNodeCount(ContainerCluster cluster, Element nodesElement, ConfigModelContext context) {
- NodesSpecification nodesSpecification = NodesSpecification.from(new ModelElement(nodesElement),
- context.getDeployState().getWantedNodeVespaVersion());
+ NodesSpecification nodesSpecification = NodesSpecification.from(new ModelElement(nodesElement), context);
Map<HostResource, ClusterMembership> hosts = nodesSpecification.provision(cluster.getRoot().getHostSystem(),
ClusterSpec.Type.container,
ClusterSpec.Id.from(cluster.getName()),
@@ -559,8 +565,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
cluster.setHostClusterId(referenceId);
Map<HostResource, ClusterMembership> hosts =
- StorageGroup.provisionHosts(NodesSpecification.from(new ModelElement(referencedNodesElement),
- context.getDeployState().getWantedNodeVespaVersion()),
+ StorageGroup.provisionHosts(NodesSpecification.from(new ModelElement(referencedNodesElement), context),
referenceId,
cluster.getRoot().getHostSystem(),
context.getDeployLogger());
@@ -582,9 +587,9 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
NodesSpecification nodesSpec;
if (contentNodesElementOrNull == null)
- nodesSpec = NodesSpecification.nonDedicated(1, context.getDeployState().getWantedNodeVespaVersion());
+ nodesSpec = NodesSpecification.nonDedicated(1, context);
else
- nodesSpec = NodesSpecification.from(new ModelElement(contentNodesElementOrNull), context.getDeployState().getWantedNodeVespaVersion());
+ nodesSpec = NodesSpecification.from(new ModelElement(contentNodesElementOrNull), context);
Map<HostResource, ClusterMembership> hosts =
StorageGroup.provisionHosts(nodesSpec,
@@ -669,6 +674,13 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
cluster.addComponent(searchHandler);
}
+ private void addGUIHandler(ContainerCluster cluster) {
+ Handler<?> guiHandler = new GUIHandler();
+ guiHandler.addServerBindings("http://"+GUIHandler.BINDING, "https://"+GUIHandler.BINDING);
+ cluster.addComponent(guiHandler);
+ }
+
+
private String[] serverBindings(Element searchElement, String... defaultBindings) {
List<Element> bindings = XML.getChildren(searchElement, "binding");
if (bindings.isEmpty())
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerServiceBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerServiceBuilder.java
index 14b7360b968..81dc6a5e4f9 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerServiceBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerServiceBuilder.java
@@ -15,7 +15,7 @@ import java.util.List;
import java.util.logging.Logger;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ContainerServiceBuilder extends VespaDomBuilder.DomConfigProducerBuilder<Container> {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/InconsistentSchemaAndCodeError.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/InconsistentSchemaAndCodeError.java
index d6aec8cb521..5617dfd7582 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/InconsistentSchemaAndCodeError.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/InconsistentSchemaAndCodeError.java
@@ -2,6 +2,6 @@
package com.yahoo.vespa.model.container.xml;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class InconsistentSchemaAndCodeError extends Error {}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearch.java b/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearch.java
index 17ce1352816..f1647bd15c1 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearch.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearch.java
@@ -2,7 +2,7 @@
package com.yahoo.vespa.model.content;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ContentSearch {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/SearchCoverage.java b/config-model/src/main/java/com/yahoo/vespa/model/content/SearchCoverage.java
index 06027caaf51..343c72cc78b 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/SearchCoverage.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/SearchCoverage.java
@@ -4,7 +4,7 @@ package com.yahoo.vespa.model.content;
import com.google.common.base.Preconditions;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class SearchCoverage {
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 edf3f7c840d..1b5baefe4e4 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
@@ -409,9 +409,9 @@ public class StorageGroup {
Optional<NodesSpecification> nodeRequirement;
if (nodesElement.isPresent() && nodesElement.get().getStringAttribute("count") != null ) // request these nodes
- nodeRequirement = Optional.of(NodesSpecification.from(nodesElement.get(), context.getDeployState().getWantedNodeVespaVersion()));
+ nodeRequirement = Optional.of(NodesSpecification.from(nodesElement.get(), context));
else if (! nodesElement.isPresent() && subGroups.isEmpty() && owner.getRoot().getDeployState().isHosted()) // request one node
- nodeRequirement = Optional.of(NodesSpecification.nonDedicated(1, context.getDeployState().getWantedNodeVespaVersion()));
+ nodeRequirement = Optional.of(NodesSpecification.nonDedicated(1, context));
else // Nodes or groups explicitly listed, and/opr not hosted - resolve in GroupBuilder
nodeRequirement = Optional.empty();
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/TuningDispatch.java b/config-model/src/main/java/com/yahoo/vespa/model/content/TuningDispatch.java
index 8dd4e941154..022611fa4f7 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/TuningDispatch.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/TuningDispatch.java
@@ -2,7 +2,7 @@
package com.yahoo.vespa.model.content;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class TuningDispatch {
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 68ae4d2b242..154f719ff10 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
@@ -292,8 +292,8 @@ public class ContentCluster extends AbstractConfigProducer implements
else if (admin.multitenant()) {
String clusterName = contentClusterName + "-controllers";
NodesSpecification nodesSpecification =
- NodesSpecification.optionalDedicatedFromParent(contentElement.getChild("controllers"), context.getDeployState().getWantedNodeVespaVersion())
- .orElse(NodesSpecification.nonDedicated(3, context.getDeployState().getWantedNodeVespaVersion()));
+ NodesSpecification.optionalDedicatedFromParent(contentElement.getChild("controllers"), context)
+ .orElse(NodesSpecification.nonDedicated(3, context));
Collection<HostResource> hosts = nodesSpecification.isDedicated() ?
getControllerHosts(nodesSpecification, admin, clusterName, context) :
drawControllerHosts(nodesSpecification.count(), rootGroup, containers);
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomContentSearchBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomContentSearchBuilder.java
index f3d5e3db152..9ef64e0b288 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomContentSearchBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomContentSearchBuilder.java
@@ -5,7 +5,7 @@ import com.yahoo.vespa.model.content.ContentSearch;
import com.yahoo.vespa.model.builder.xml.dom.ModelElement;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class DomContentSearchBuilder {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomSearchCoverageBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomSearchCoverageBuilder.java
index 503aafd1be1..cfc110d7a13 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomSearchCoverageBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomSearchCoverageBuilder.java
@@ -5,7 +5,7 @@ import com.yahoo.vespa.model.content.SearchCoverage;
import com.yahoo.vespa.model.builder.xml.dom.ModelElement;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class DomSearchCoverageBuilder {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/routing/DocumentProtocol.java b/config-model/src/main/java/com/yahoo/vespa/model/routing/DocumentProtocol.java
index 6fe6c2ac344..1d1b6c6c43a 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/routing/DocumentProtocol.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/routing/DocumentProtocol.java
@@ -29,7 +29,7 @@ import java.util.TreeMap;
* the code within could really be part of {@link Routing}, but it has been partitioned out to allow better readability
* and also more easily maintainable as the number of protocols increase.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class DocumentProtocol implements Protocol, DocumentrouteselectorpolicyConfig.Producer {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/routing/Protocol.java b/config-model/src/main/java/com/yahoo/vespa/model/routing/Protocol.java
index 23d111f45c2..ad684894176 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/routing/Protocol.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/routing/Protocol.java
@@ -8,7 +8,7 @@ import com.yahoo.messagebus.routing.RoutingTableSpec;
* This interface defines the necessary api for {@link Routing} to prepare and combine routing tables for all available
* protocols.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface Protocol {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/routing/Routing.java b/config-model/src/main/java/com/yahoo/vespa/model/routing/Routing.java
index 67fa5906e07..16f51935f2a 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/routing/Routing.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/routing/Routing.java
@@ -14,7 +14,7 @@ import java.util.*;
* explicitly by the user in the optional &lt;routing&gt; element. If there is no such element, only default routes and
* hops will be available.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Routing extends ConfigModel {
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
index 5674a432a11..b45db0d1dc3 100644
--- 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
@@ -7,7 +7,7 @@ import java.util.LinkedList;
import java.util.List;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SearchColumn extends AbstractConfigProducer {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/search/SearchDefinition.java b/config-model/src/main/java/com/yahoo/vespa/model/search/SearchDefinition.java
index fd7752dbdc8..860f89792e2 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/search/SearchDefinition.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/search/SearchDefinition.java
@@ -6,7 +6,7 @@ import com.yahoo.searchdefinition.Search;
import java.util.Collection;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class SearchDefinition {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/utils/FreezableMap.java b/config-model/src/main/java/com/yahoo/vespa/model/utils/FreezableMap.java
index ba32e647352..6b1cdb8361a 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/utils/FreezableMap.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/utils/FreezableMap.java
@@ -11,7 +11,7 @@ import java.util.Set;
* Delegates to a map that can be frozen.
* Not thread safe.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class FreezableMap<K, V> implements Map<K, V> {
private boolean frozen = false;
diff --git a/config-model/src/main/javacc/SDParser.jj b/config-model/src/main/javacc/SDParser.jj
index c4c1a04da0b..1bf87754ce0 100644
--- a/config-model/src/main/javacc/SDParser.jj
+++ b/config-model/src/main/javacc/SDParser.jj
@@ -979,8 +979,7 @@ String fieldBody(SDField field, Search search, SDDocumentType document) : { }
/**
* This rule consumes a single element of a struct subfield body block.
- * Only elements that make sense for streaming search are allowed,
- * since only streaming search supports structs for now.
+ * Only elements that are supported in streaming search and indexed search (with complex attributes) are allowed.
*
* @param field The field being built.
* @param search The search object to add content to.
@@ -991,6 +990,7 @@ String structFieldBody(FieldOperationContainer field, Search search, SDDocumentT
{
( summaryInField(field) |
indexing(field) |
+ attribute(field) |
match(field) |
queryCommand(field) |
structField(field, search, document) |
diff --git a/config-model/src/main/perl/vespa-deploy b/config-model/src/main/perl/vespa-deploy
index 0d22e493c78..8d2d65b5551 100755
--- a/config-model/src/main/perl/vespa-deploy
+++ b/config-model/src/main/perl/vespa-deploy
@@ -87,7 +87,7 @@ readConfFile();
use strict;
use warnings;
use feature qw(switch say);
-use vars qw/ $opt_c $opt_h $opt_n $opt_v $opt_f $opt_t $opt_a $opt_e $opt_E $opt_r $opt_i $opt_p $opt_H $opt_R $opt_F /;
+use vars qw/ $opt_c $opt_h $opt_n $opt_v $opt_f $opt_t $opt_a $opt_e $opt_E $opt_r $opt_i $opt_p $opt_H $opt_R $opt_F $opt_V /;
use Env qw($HOME);
use JSON;
use Getopt::Std;
@@ -117,7 +117,7 @@ my $version = "v2";
my $configserver = "";
my $port = "19071";
-getopts('c:fhnt:ve:E:r:a:i:p:HR:F:');
+getopts('c:fhnt:ve:E:r:a:i:p:HR:F:V:');
if ($opt_h) {
usage();
@@ -237,7 +237,7 @@ sub usage {
} elsif ($command eq "fetch") {
usage_fetch();
} else {
- print "Usage: vespa-deploy [-h] [-v] [-f] [-t] [-p] [<command>] [args]\n";
+ print "Usage: vespa-deploy [-h] [-v] [-f] [-t] [-p] [-V] [<command>] [args]\n";
print "Supported commands: 'upload', 'prepare', 'activate', 'fetch' and 'help'\n";
print "Supported options: '-h' (help), '-v' (verbose), '-f' (force/ignore validation errors), '-t' (timeout in seconds), '-p' (config server http port)\n";
print "Try 'vespa-deploy help <command>' to get more help\n";
@@ -462,6 +462,7 @@ sub http_prepare {
$url = add_url_property_from_option($url, $opt_i, "instance");
$url = add_url_property_from_option($url, $opt_t, "timeout");
$url = add_url_property_from_option($url, $opt_R, "rotations");
+ $url = add_url_property_from_option($url, $opt_V, "vespaVersion");
print "Preparing session $session_id using $url\n";
`$CURL_PUT \"$url\"`;
}
diff --git a/config-model/src/test/derived/array_of_struct_attribute/attributes.cfg b/config-model/src/test/derived/array_of_struct_attribute/attributes.cfg
index 9e6b5cea55e..d42ed223045 100644
--- a/config-model/src/test/derived/array_of_struct_attribute/attributes.cfg
+++ b/config-model/src/test/derived/array_of_struct_attribute/attributes.cfg
@@ -3,7 +3,7 @@ attribute[].datatype STRING
attribute[].collectiontype ARRAY
attribute[].removeifzero false
attribute[].createifnonexistent false
-attribute[].fastsearch false
+attribute[].fastsearch true
attribute[].huge false
attribute[].sortascending true
attribute[].sortfunction UCA
diff --git a/config-model/src/test/derived/array_of_struct_attribute/test.sd b/config-model/src/test/derived/array_of_struct_attribute/test.sd
index 5b2d50cbdba..3678c2942b5 100644
--- a/config-model/src/test/derived/array_of_struct_attribute/test.sd
+++ b/config-model/src/test/derived/array_of_struct_attribute/test.sd
@@ -8,6 +8,7 @@ search test {
indexing: summary
struct-field name {
indexing: attribute
+ attribute: fast-search
}
struct-field weight {
indexing: attribute
diff --git a/config-model/src/test/derived/map_attribute/attributes.cfg b/config-model/src/test/derived/map_attribute/attributes.cfg
index cdd929a1818..4e0c3d60338 100644
--- a/config-model/src/test/derived/map_attribute/attributes.cfg
+++ b/config-model/src/test/derived/map_attribute/attributes.cfg
@@ -3,7 +3,7 @@ attribute[].datatype STRING
attribute[].collectiontype ARRAY
attribute[].removeifzero false
attribute[].createifnonexistent false
-attribute[].fastsearch false
+attribute[].fastsearch true
attribute[].huge false
attribute[].sortascending true
attribute[].sortfunction UCA
diff --git a/config-model/src/test/derived/map_attribute/test.sd b/config-model/src/test/derived/map_attribute/test.sd
index da31f575396..87fc13d48ed 100644
--- a/config-model/src/test/derived/map_attribute/test.sd
+++ b/config-model/src/test/derived/map_attribute/test.sd
@@ -4,6 +4,7 @@ search test {
indexing: summary
struct-field key {
indexing: attribute
+ attribute: fast-search
}
struct-field value {
indexing: attribute
diff --git a/config-model/src/test/derived/map_of_struct_attribute/attributes.cfg b/config-model/src/test/derived/map_of_struct_attribute/attributes.cfg
index 95cf8f761a0..7e38ba06f74 100644
--- a/config-model/src/test/derived/map_of_struct_attribute/attributes.cfg
+++ b/config-model/src/test/derived/map_of_struct_attribute/attributes.cfg
@@ -3,7 +3,7 @@ attribute[].datatype STRING
attribute[].collectiontype ARRAY
attribute[].removeifzero false
attribute[].createifnonexistent false
-attribute[].fastsearch false
+attribute[].fastsearch true
attribute[].huge false
attribute[].sortascending true
attribute[].sortfunction UCA
@@ -83,7 +83,7 @@ attribute[].datatype STRING
attribute[].collectiontype ARRAY
attribute[].removeifzero false
attribute[].createifnonexistent false
-attribute[].fastsearch false
+attribute[].fastsearch true
attribute[].huge false
attribute[].sortascending true
attribute[].sortfunction UCA
diff --git a/config-model/src/test/derived/map_of_struct_attribute/test.sd b/config-model/src/test/derived/map_of_struct_attribute/test.sd
index e139a7f8168..929fa8dcbbe 100644
--- a/config-model/src/test/derived/map_of_struct_attribute/test.sd
+++ b/config-model/src/test/derived/map_of_struct_attribute/test.sd
@@ -8,6 +8,7 @@ search test {
indexing: summary
struct-field key {
indexing: attribute
+ attribute: fast-search
}
struct-field value.name {
indexing: attribute
@@ -23,6 +24,7 @@ search test {
}
struct-field value.name {
indexing: attribute
+ attribute: fast-search
}
}
}
diff --git a/config-model/src/test/java/com/yahoo/config/model/ApplicationDeployTest.java b/config-model/src/test/java/com/yahoo/config/model/ApplicationDeployTest.java
index d3e8136cdec..643a3bd0b91 100644
--- a/config-model/src/test/java/com/yahoo/config/model/ApplicationDeployTest.java
+++ b/config-model/src/test/java/com/yahoo/config/model/ApplicationDeployTest.java
@@ -238,7 +238,7 @@ public class ApplicationDeployTest {
}
private List<SearchDefinition> getSearchDefinitions(FilesApplicationPackage app) {
- return new DeployState.Builder().applicationPackage(app).build(true).getSearchDefinitions();
+ return new DeployState.Builder().applicationPackage(app).build().getSearchDefinitions();
}
public FilesApplicationPackage createAppPkg(String appPkg) throws IOException {
@@ -374,7 +374,7 @@ public class ApplicationDeployTest {
final File appDir = new File("src/test/cfg/application/configdeftest");
FilesApplicationPackage app = FilesApplicationPackage.fromFile(appDir);
- DeployState deployState = new DeployState.Builder().applicationPackage(app).build(true);
+ DeployState deployState = new DeployState.Builder().applicationPackage(app).build();
ConfigDefinition def = deployState.getConfigDefinition(new ConfigDefinitionKey("foo", CNode.DEFAULT_NAMESPACE)).get();
assertThat(def.getNamespace(), is(CNode.DEFAULT_NAMESPACE));
@@ -394,7 +394,7 @@ public class ApplicationDeployTest {
@Test(expected=IllegalArgumentException.class)
public void testDifferentNameOfSdFileAndSearchName() throws IOException {
FilesApplicationPackage app = createAppPkg(TESTDIR + "sdfilenametest");
- new DeployState.Builder().applicationPackage(app).build(true);
+ new DeployState.Builder().applicationPackage(app).build();
}
}
diff --git a/config-model/src/test/java/com/yahoo/config/model/MockModelContext.java b/config-model/src/test/java/com/yahoo/config/model/MockModelContext.java
index c3fffa96076..5bd95334396 100644
--- a/config-model/src/test/java/com/yahoo/config/model/MockModelContext.java
+++ b/config-model/src/test/java/com/yahoo/config/model/MockModelContext.java
@@ -127,6 +127,12 @@ public class MockModelContext implements ModelContext {
public Set<Rotation> rotations() {
return new HashSet<>();
}
+
+ @Override
+ public boolean isBootstrap() { return false; }
+
+ @Override
+ public boolean isFirstTimeDeployment() { return false; }
};
}
}
diff --git a/config-model/src/test/java/com/yahoo/config/model/builder/xml/test/DomBuilderTest.java b/config-model/src/test/java/com/yahoo/config/model/builder/xml/test/DomBuilderTest.java
index 2a601daa469..9859a5de685 100644
--- a/config-model/src/test/java/com/yahoo/config/model/builder/xml/test/DomBuilderTest.java
+++ b/config-model/src/test/java/com/yahoo/config/model/builder/xml/test/DomBuilderTest.java
@@ -12,7 +12,7 @@ import org.w3c.dom.Element;
* For an example,
* @see com.yahoo.vespa.model.builder.xml.dom.chains.DependenciesBuilderTest
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
abstract public class DomBuilderTest {
diff --git a/config-model/src/test/java/com/yahoo/config/model/deploy/DeployStateTest.java b/config-model/src/test/java/com/yahoo/config/model/deploy/DeployStateTest.java
index 2a9547df839..aa29ebd9825 100644
--- a/config-model/src/test/java/com/yahoo/config/model/deploy/DeployStateTest.java
+++ b/config-model/src/test/java/com/yahoo/config/model/deploy/DeployStateTest.java
@@ -41,7 +41,7 @@ public class DeployStateTest {
DeployState.Builder builder = new DeployState.Builder();
HostProvisioner provisioner = new InMemoryProvisioner(true, "foo.yahoo.com");
builder.modelHostProvisioner(provisioner);
- DeployState state = builder.build(true);
+ DeployState state = builder.build();
assertThat(state.getProvisioner(), is(provisioner));
}
@@ -50,7 +50,7 @@ public class DeployStateTest {
DeployState.Builder builder = new DeployState.Builder();
ApplicationPackage app = MockApplicationPackage.createEmpty();
builder.permanentApplicationPackage(Optional.of(app));
- DeployState state = builder.build(true);
+ DeployState state = builder.build();
assertThat(state.getPermanentApplicationPackage().get(), is(app));
}
@@ -58,20 +58,20 @@ public class DeployStateTest {
public void testPreviousModelIsProvided() throws IOException, SAXException {
VespaModel prevModel = new VespaModel(MockApplicationPackage.createEmpty());
DeployState.Builder builder = new DeployState.Builder();
- assertThat(builder.previousModel(prevModel).build(true).getPreviousModel().get(), is(prevModel));
+ assertThat(builder.previousModel(prevModel).build().getPreviousModel().get(), is(prevModel));
}
@Test
public void testProperties() {
DeployState.Builder builder = new DeployState.Builder();
- DeployState state = builder.build(true);
+ DeployState state = builder.build();
assertThat(state.getProperties().applicationId(), is(ApplicationId.defaultId()));
ApplicationId customId = new ApplicationId.Builder()
.tenant("bar")
.applicationName("foo").instanceName("quux").build();
DeployProperties properties = new DeployProperties.Builder().applicationId(customId).build();
builder.properties(properties);
- state = builder.build(true);
+ state = builder.build();
assertThat(state.getProperties().applicationId(), is(customId));
}
@@ -112,11 +112,11 @@ public class DeployStateTest {
@Test
public void testRotations() {
Set<Rotation> rotations = new HashSet<>();
- assertThat(new DeployState.Builder().rotations(rotations).build(true).getRotations().size(), is(0));
+ assertThat(new DeployState.Builder().rotations(rotations).build().getRotations().size(), is(0));
for (String name : new String[]{"rotation-001.vespa.a02.yahoodns.net", "rotation-002.vespa.a02.yahoodns.net"}) {
rotations.add(new Rotation(name));
}
- assertThat(new DeployState.Builder().rotations(rotations).build(true).getRotations(), equalTo(rotations));
+ assertThat(new DeployState.Builder().rotations(rotations).build().getRotations(), equalTo(rotations));
}
private DeployState createDeployState(ApplicationPackage app, Map<ConfigDefinitionKey, com.yahoo.vespa.config.buildergen.ConfigDefinition> defs) {
@@ -131,7 +131,7 @@ public class DeployStateTest {
return null;
}
});
- return builder.build(true);
+ return builder.build();
}
}
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 56b9ad04f78..85c75309d23 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
@@ -1523,7 +1523,7 @@ public class ModelProvisioningTest {
ApplicationPackage appPkg = modelCreatorWithMockPkg.appPkg;
DeployState deployState = new DeployState.Builder().applicationPackage(appPkg).
properties((new DeployProperties.Builder()).multitenant(multitenant).build()).
- build(true);
+ build();
return modelCreatorWithMockPkg.create(false, deployState);
}
diff --git a/config-model/src/test/java/com/yahoo/config/model/test/MockHosts.java b/config-model/src/test/java/com/yahoo/config/model/test/MockHosts.java
index b186125114a..50b3f70abdf 100644
--- a/config-model/src/test/java/com/yahoo/config/model/test/MockHosts.java
+++ b/config-model/src/test/java/com/yahoo/config/model/test/MockHosts.java
@@ -5,7 +5,7 @@ import com.yahoo.vespa.model.Host;
import com.yahoo.vespa.model.SimpleConfigProducer;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class MockHosts {
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 97e1ab9aeb9..a0dd18aeea9 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/RankingExpressionInliningTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/RankingExpressionInliningTestCase.java
@@ -8,9 +8,10 @@ import com.yahoo.searchdefinition.derived.RawRankProfile;
import com.yahoo.searchdefinition.parser.ParseException;
import org.junit.Test;
-import java.util.List;
+import java.util.Optional;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
/**
* @author bratseth
@@ -67,6 +68,7 @@ public class RankingExpressionInliningTestCase extends SearchDefinitionTestCase
assertEquals("7.0 * (9 + attribute(a))",
child.getFirstPhaseRanking().getRoot().toString());
}
+
@Test
public void testConstants() throws ParseException {
RankProfileRegistry rankProfileRegistry = new RankProfileRegistry();
@@ -122,36 +124,63 @@ public class RankingExpressionInliningTestCase extends SearchDefinitionTestCase
RankProfile parent = rankProfileRegistry.getRankProfile(s, "parent").compile(new QueryProfileRegistry());
assertEquals("17.0", parent.getFirstPhaseRanking().getRoot().toString());
assertEquals("0.0", parent.getSecondPhaseRanking().getRoot().toString());
- List<Pair<String, String>> parentRankProperties = new RawRankProfile(parent,
- new QueryProfileRegistry(),
- new AttributeFields(s)).configProperties();
- assertEquals("(rankingExpression(foo).rankingScript,10.0)",
- parentRankProperties.get(0).toString());
- assertEquals("(rankingExpression(firstphase).rankingScript,17.0)",
- parentRankProperties.get(2).toString());
- assertEquals("(rankingExpression(secondphase).rankingScript,0.0)",
- parentRankProperties.get(4).toString());
+ assertEquals("10.0", getRankingExpression("foo", parent, s));
+ assertEquals("17.0", getRankingExpression("firstphase", parent, s));
+ assertEquals("0.0", getRankingExpression("secondphase", parent, s));
RankProfile child = rankProfileRegistry.getRankProfile(s, "child").compile(new QueryProfileRegistry());
assertEquals("31.0 + bar + arg(4.0)", child.getFirstPhaseRanking().getRoot().toString());
assertEquals("24.0", child.getSecondPhaseRanking().getRoot().toString());
- List<Pair<String, String>> childRankProperties = new RawRankProfile(child,
- new QueryProfileRegistry(),
- new AttributeFields(s)).configProperties();
- assertEquals("(rankingExpression(foo).rankingScript,12.0)",
- childRankProperties.get(0).toString());
- assertEquals("(rankingExpression(bar).rankingScript,14.0)",
- childRankProperties.get(1).toString());
- assertEquals("(rankingExpression(boz).rankingScript,3.0)",
- childRankProperties.get(2).toString());
- assertEquals("(rankingExpression(baz).rankingScript,9.0 + rankingExpression(boz))",
- childRankProperties.get(3).toString());
- assertEquals("(rankingExpression(arg).rankingScript,a1 * 2)",
- childRankProperties.get(4).toString());
- assertEquals("(rankingExpression(firstphase).rankingScript,31.0 + rankingExpression(bar) + rankingExpression(arg@))",
- censorBindingHash(childRankProperties.get(7).toString()));
- assertEquals("(rankingExpression(secondphase).rankingScript,24.0)",
- childRankProperties.get(9).toString());
+ assertEquals("12.0", getRankingExpression("foo", child, s));
+ assertEquals("12.0", getRankingExpression("baz", child, s));
+ assertEquals("3.0", getRankingExpression("boz", child, s));
+ assertEquals("14.0", getRankingExpression("bar", child, s));
+ assertEquals("a1 * 2", getRankingExpression("arg", child, s));
+ assertEquals("31.0 + rankingExpression(bar) + rankingExpression(arg@)", getRankingExpression("firstphase", child, s));
+ assertEquals("24.0", getRankingExpression("secondphase", child, s));
+ }
+
+ @Test
+ public void testNonTopLevelInlining() throws ParseException {
+ RankProfileRegistry rankProfileRegistry = new RankProfileRegistry();
+ SearchBuilder builder = new SearchBuilder(rankProfileRegistry);
+ builder.importString(
+ "search test {\n" +
+ " document test { \n" +
+ " field a type double { \n" +
+ " indexing: attribute \n" +
+ " }\n" +
+ " field b type double { \n" +
+ " indexing: attribute \n" +
+ " }\n" +
+ " }\n" +
+ " \n" +
+ " rank-profile test {\n" +
+ " first-phase {\n" +
+ " expression: A + C + D\n" +
+ " }\n" +
+ " macro inline D() {\n" +
+ " expression: B + 1\n" +
+ " }\n" +
+ " macro C() {\n" +
+ " expression: A + B\n" +
+ " }\n" +
+ " macro inline B() {\n" +
+ " expression: attribute(b)\n" +
+ " }\n" +
+ " macro inline A() {\n" +
+ " expression: attribute(a)\n" +
+ " }\n" +
+ " }\n" +
+ "\n" +
+ "}\n");
+ builder.build();
+ Search s = builder.getSearch();
+
+ RankProfile test = rankProfileRegistry.getRankProfile(s, "test").compile(new QueryProfileRegistry());
+ assertEquals("attribute(a) + C + (attribute(b) + 1)", test.getFirstPhaseRanking().getRoot().toString());
+ assertEquals("attribute(a) + attribute(b)", getRankingExpression("C", test, s));
+ assertEquals("attribute(b) + 1", getRankingExpression("D", test, s));
}
/**
@@ -177,4 +206,16 @@ public class RankingExpressionInliningTestCase extends SearchDefinitionTestCase
return b.toString();
}
+ private String getRankingExpression(String name, RankProfile rankProfile, Search search) {
+ Optional<String> rankExpression =
+ new RawRankProfile(rankProfile, new QueryProfileRegistry(), new AttributeFields(search))
+ .configProperties()
+ .stream()
+ .filter(r -> r.getFirst().equals("rankingExpression(" + name + ").rankingScript"))
+ .map(Pair::getSecond)
+ .findFirst();
+ assertTrue(rankExpression.isPresent());
+ return censorBindingHash(rankExpression.get());
+ }
+
}
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/AttributeListTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/AttributeListTestCase.java
index bd762c64c00..da28dd56694 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/AttributeListTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/AttributeListTestCase.java
@@ -74,8 +74,8 @@ public class AttributeListTestCase extends SearchDefinitionTestCase {
Search search = SearchBuilder.buildFromFile("src/test/derived/array_of_struct_attribute/test.sd");
Iterator<Attribute> attributes = new AttributeFields(search).attributeIterator();
- assertAttribute("elem_array.name", Attribute.Type.STRING, Attribute.CollectionType.ARRAY, attributes.next());
- assertAttribute("elem_array.weight", Attribute.Type.INTEGER, Attribute.CollectionType.ARRAY, attributes.next());
+ assertAttribute("elem_array.name", Attribute.Type.STRING, Attribute.CollectionType.ARRAY, true, attributes.next());
+ assertAttribute("elem_array.weight", Attribute.Type.INTEGER, Attribute.CollectionType.ARRAY, false, attributes.next());
assertTrue(!attributes.hasNext());
}
@@ -84,18 +84,19 @@ public class AttributeListTestCase extends SearchDefinitionTestCase {
Search search = SearchBuilder.buildFromFile("src/test/derived/map_of_struct_attribute/test.sd");
Iterator<Attribute> attributes = new AttributeFields(search).attributeIterator();
- assertAttribute("str_elem_map.key", Attribute.Type.STRING, Attribute.CollectionType.ARRAY, attributes.next());
- assertAttribute("str_elem_map.value.name", Attribute.Type.STRING, Attribute.CollectionType.ARRAY, attributes.next());
- assertAttribute("str_elem_map.value.weight", Attribute.Type.INTEGER, Attribute.CollectionType.ARRAY, attributes.next());
- assertAttribute("int_elem_map.key", Attribute.Type.INTEGER, Attribute.CollectionType.ARRAY, attributes.next());
- assertAttribute("int_elem_map.value.name", Attribute.Type.STRING, Attribute.CollectionType.ARRAY, attributes.next());
+ assertAttribute("str_elem_map.key", Attribute.Type.STRING, Attribute.CollectionType.ARRAY, true, attributes.next());
+ assertAttribute("str_elem_map.value.name", Attribute.Type.STRING, Attribute.CollectionType.ARRAY, false, attributes.next());
+ assertAttribute("str_elem_map.value.weight", Attribute.Type.INTEGER, Attribute.CollectionType.ARRAY, false, attributes.next());
+ assertAttribute("int_elem_map.key", Attribute.Type.INTEGER, Attribute.CollectionType.ARRAY, false, attributes.next());
+ assertAttribute("int_elem_map.value.name", Attribute.Type.STRING, Attribute.CollectionType.ARRAY, true, attributes.next());
assertTrue(!attributes.hasNext());
}
- private static void assertAttribute(String name, Attribute.Type type, Attribute.CollectionType collection, Attribute attr) {
+ private static void assertAttribute(String name, Attribute.Type type, Attribute.CollectionType collection, boolean isFastSearch, Attribute attr) {
assertEquals(name, attr.getName());
assertEquals(type, attr.getType());
assertEquals(collection, attr.getCollectionType());
+ assertEquals(isFastSearch, attr.isFastSearch());
}
@Test
@@ -110,7 +111,7 @@ public class AttributeListTestCase extends SearchDefinitionTestCase {
"}")).getSearch();
Iterator<Attribute> attributes = new AttributeFields(search).attributeIterator();
- assertAttribute("pos_array_zcurve", Attribute.Type.LONG, Attribute.CollectionType.ARRAY, attributes.next());
+ assertAttribute("pos_array_zcurve", Attribute.Type.LONG, Attribute.CollectionType.ARRAY, true, attributes.next());
assertTrue(!attributes.hasNext());
}
@@ -119,9 +120,9 @@ public class AttributeListTestCase extends SearchDefinitionTestCase {
Search search = SearchBuilder.buildFromFile("src/test/derived/map_attribute/test.sd");
Iterator<Attribute> attributes = new AttributeFields(search).attributeIterator();
- assertAttribute("str_map.key", Attribute.Type.STRING, Attribute.CollectionType.ARRAY, attributes.next());
- assertAttribute("str_map.value", Attribute.Type.STRING, Attribute.CollectionType.ARRAY, attributes.next());
- assertAttribute("int_map.key", Attribute.Type.INTEGER, Attribute.CollectionType.ARRAY, attributes.next());
+ assertAttribute("str_map.key", Attribute.Type.STRING, Attribute.CollectionType.ARRAY, true, attributes.next());
+ assertAttribute("str_map.value", Attribute.Type.STRING, Attribute.CollectionType.ARRAY, false, attributes.next());
+ assertAttribute("int_map.key", Attribute.Type.INTEGER, Attribute.CollectionType.ARRAY, false, attributes.next());
assertTrue(!attributes.hasNext());
}
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/IndexSchemaTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/IndexSchemaTestCase.java
index 33dee6d9f2f..ec96d1f82d8 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/IndexSchemaTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/IndexSchemaTestCase.java
@@ -15,7 +15,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings({ "deprecation" })
public class IndexSchemaTestCase {
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/document/ComplexAttributeFieldUtilsTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/document/ComplexAttributeFieldUtilsTestCase.java
index 4cdc48b330e..91a89c204c9 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/document/ComplexAttributeFieldUtilsTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/document/ComplexAttributeFieldUtilsTestCase.java
@@ -1,3 +1,4 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.searchdefinition.document;
import com.yahoo.searchdefinition.Search;
@@ -6,126 +7,214 @@ import com.yahoo.searchdefinition.parser.ParseException;
import org.junit.Test;
import static com.yahoo.config.model.test.TestUtil.joinLines;
-import static com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils.isArrayOfSimpleStruct;
-import static com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils.isComplexFieldWithOnlyStructFieldAttributes;
-import static com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils.isMapOfPrimitiveType;
-import static com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils.isMapOfSimpleStruct;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class ComplexAttributeFieldUtilsTestCase {
- private static ImmutableSDField createField(String fieldName, String sdFieldContent) throws ParseException {
- String sdContent = joinLines("search test {",
- " document test {",
- " struct elem {",
- " field name type string {}",
- " field weight type string {}",
- " }",
- sdFieldContent,
- " }",
- "}");
- Search search = SearchBuilder.createFromString(sdContent).getSearch();
- return search.getConcreteField(fieldName);
+ private static class FixtureBase {
+ private final Search search;
+ private final ImmutableSDField field;
+
+ public FixtureBase(String fieldName, String sdContent) throws ParseException {
+ search = SearchBuilder.createFromString(sdContent).getSearch();
+ field = search.getConcreteField(fieldName);
+ }
+
+ public ImmutableSDField field() {
+ return field;
+ }
+
+ public SDDocumentType docType() {
+ return search.getDocument();
+ }
+
+ public boolean isSupportedComplexField() {
+ return ComplexAttributeFieldUtils.isSupportedComplexField(field(), docType());
+ }
+
+ public boolean isArrayOfSimpleStruct() {
+ return ComplexAttributeFieldUtils.isArrayOfSimpleStruct(field(), docType());
+ }
+
+ public boolean isMapOfSimpleStruct() {
+ return ComplexAttributeFieldUtils.isMapOfSimpleStruct(field(), docType());
+ }
+
+ public boolean isMapOfPrimitiveType() {
+ return ComplexAttributeFieldUtils.isMapOfPrimitiveType(field());
+ }
+
+ public boolean isComplexFieldWithOnlyStructFieldAttributes() {
+ return ComplexAttributeFieldUtils.isComplexFieldWithOnlyStructFieldAttributes(field(), docType());
+ }
+ }
+
+ private static class Fixture extends FixtureBase {
+
+ public Fixture(String fieldName, String sdFieldContent) throws ParseException {
+ super(fieldName, joinLines("search test {",
+ " document test {",
+ " struct elem {",
+ " field name type string {}",
+ " field weight type string {}",
+ " }",
+ sdFieldContent,
+ " }",
+ "}"));
+ }
+ }
+
+ private static class ComplexFixture extends FixtureBase {
+
+ public ComplexFixture(String fieldName, String sdFieldContent) throws ParseException {
+ super(fieldName, joinLines("search test {",
+ " document test {",
+ " struct elem {",
+ " field name type string {}",
+ " field weight type array<string> {}",
+ " }",
+ sdFieldContent,
+ " }",
+ "}"));
+ }
}
@Test
public void array_of_struct_with_only_struct_field_attributes_is_tagged_as_such() throws ParseException {
- ImmutableSDField field = createField("elem_array",
+ Fixture f = new Fixture("elem_array",
joinLines("field elem_array type array<elem> {",
" indexing: summary",
" struct-field name { indexing: attribute }",
" struct-field weight { indexing: attribute }",
"}"));
- assertTrue(isArrayOfSimpleStruct(field));
- assertTrue(isComplexFieldWithOnlyStructFieldAttributes(field));
+ assertTrue(f.isSupportedComplexField());
+ assertTrue(f.isArrayOfSimpleStruct());
+ assertTrue(f.isComplexFieldWithOnlyStructFieldAttributes());
}
@Test
public void array_of_struct_with_some_struct_field_attributes_is_tagged_as_such() throws ParseException {
- ImmutableSDField field = createField("elem_array",
+ Fixture f = new Fixture("elem_array",
joinLines("field elem_array type array<elem> {",
" indexing: summary",
" struct-field weight { indexing: attribute }",
"}"));
- assertTrue(isArrayOfSimpleStruct(field));
- assertFalse(isComplexFieldWithOnlyStructFieldAttributes(field));
+ assertTrue(f.isSupportedComplexField());
+ assertTrue(f.isArrayOfSimpleStruct());
+ assertFalse(f.isComplexFieldWithOnlyStructFieldAttributes());
}
@Test
public void map_of_struct_with_only_struct_field_attributes_is_tagged_as_such() throws ParseException {
- ImmutableSDField field = createField("elem_map",
+ Fixture f = new Fixture("elem_map",
joinLines("field elem_map type map<string, elem> {",
" indexing: summary",
" struct-field key { indexing: attribute }",
" struct-field value.name { indexing: attribute }",
" struct-field value.weight { indexing: attribute }",
"}"));
- assertTrue(isMapOfSimpleStruct(field));
- assertFalse(isMapOfPrimitiveType(field));
- assertTrue(isComplexFieldWithOnlyStructFieldAttributes(field));
+ assertTrue(f.isSupportedComplexField());
+ assertTrue(f.isMapOfSimpleStruct());
+ assertFalse(f.isMapOfPrimitiveType());
+ assertTrue(f.isComplexFieldWithOnlyStructFieldAttributes());
}
@Test
public void map_of_struct_with_some_struct_field_attributes_is_tagged_as_such() throws ParseException {
{
- ImmutableSDField field = createField("elem_map",
+ Fixture f = new Fixture("elem_map",
joinLines("field elem_map type map<int, elem> {",
" indexing: summary",
" struct-field value.name { indexing: attribute }",
" struct-field value.weight { indexing: attribute }",
"}"));
- assertTrue(isMapOfSimpleStruct(field));
- assertFalse(isMapOfPrimitiveType(field));
- assertFalse(isComplexFieldWithOnlyStructFieldAttributes(field));
+ assertTrue(f.isSupportedComplexField());
+ assertTrue(f.isMapOfSimpleStruct());
+ assertFalse(f.isMapOfPrimitiveType());
+ assertFalse(f.isComplexFieldWithOnlyStructFieldAttributes());
}
{
- ImmutableSDField field = createField("elem_map",
+ Fixture f = new Fixture("elem_map",
joinLines("field elem_map type map<int, elem> {",
" indexing: summary",
" struct-field key { indexing: attribute }",
" struct-field value.weight { indexing: attribute }",
"}"));
- assertTrue(isMapOfSimpleStruct(field));
- assertFalse(isMapOfPrimitiveType(field));
- assertFalse(isComplexFieldWithOnlyStructFieldAttributes(field));
+ assertTrue(f.isSupportedComplexField());
+ assertTrue(f.isMapOfSimpleStruct());
+ assertFalse(f.isMapOfPrimitiveType());
+ assertFalse(f.isComplexFieldWithOnlyStructFieldAttributes());
}
}
@Test
public void map_of_primitive_type_with_only_struct_field_attributes_is_tagged_as_such() throws ParseException {
- ImmutableSDField field = createField("str_map",
- joinLines("field str_map type map<string, string> {",
- " indexing: summary",
- " struct-field key { indexing: attribute }",
- " struct-field value { indexing: attribute }",
- "}"));
- assertTrue(isMapOfPrimitiveType(field));
- assertFalse(isMapOfSimpleStruct(field));
- assertTrue(isComplexFieldWithOnlyStructFieldAttributes(field));
+ Fixture f = new Fixture("str_map",
+ joinLines("field str_map type map<string, string> {",
+ " indexing: summary",
+ " struct-field key { indexing: attribute }",
+ " struct-field value { indexing: attribute }",
+ "}"));
+ assertTrue(f.isSupportedComplexField());
+ assertTrue(f.isMapOfPrimitiveType());
+ assertFalse(f.isMapOfSimpleStruct());
+ assertTrue(f.isComplexFieldWithOnlyStructFieldAttributes());
}
@Test
public void map_of_primitive_type_with_some_struct_field_attributes_is_tagged_as_such() throws ParseException {
{
- ImmutableSDField field = createField("int_map",
+ Fixture f = new Fixture("int_map",
joinLines("field int_map type map<int, int> {",
" indexing: summary",
" struct-field key { indexing: attribute }",
"}"));
- assertTrue(isMapOfPrimitiveType(field));
- assertFalse(isMapOfSimpleStruct(field));
- assertFalse(isComplexFieldWithOnlyStructFieldAttributes(field));
+ assertTrue(f.isSupportedComplexField());
+ assertTrue(f.isMapOfPrimitiveType());
+ assertFalse(f.isMapOfSimpleStruct());
+ assertFalse(f.isComplexFieldWithOnlyStructFieldAttributes());
}
{
- ImmutableSDField field = createField("int_map",
+ Fixture f = new Fixture("int_map",
joinLines("field int_map type map<int, int> {",
" indexing: summary",
" struct-field value { indexing: attribute }",
"}"));
- assertTrue(isMapOfPrimitiveType(field));
- assertFalse(isMapOfSimpleStruct(field));
- assertFalse(isComplexFieldWithOnlyStructFieldAttributes(field));
+ assertTrue(f.isSupportedComplexField());
+ assertTrue(f.isMapOfPrimitiveType());
+ assertFalse(f.isMapOfSimpleStruct());
+ assertFalse(f.isComplexFieldWithOnlyStructFieldAttributes());
+ }
+ }
+
+ @Test
+ public void unsupported_complex_field_is_tagged_as_such() throws ParseException {
+ {
+ ComplexFixture f = new ComplexFixture("elem_array",
+ joinLines("field elem_array type array<elem> {",
+ " struct-field name { indexing: attribute }",
+ " struct-field weight { indexing: attribute }",
+ "}"));
+ assertFalse(f.isSupportedComplexField());
+ assertFalse(f.isArrayOfSimpleStruct());
+ assertFalse(f.isMapOfSimpleStruct());
+ assertFalse(f.isMapOfPrimitiveType());
+ assertFalse(f.isComplexFieldWithOnlyStructFieldAttributes());
+ }
+ {
+ ComplexFixture f = new ComplexFixture("elem_map",
+ joinLines("field elem_map type map<int, elem> {",
+ " indexing: summary",
+ " struct-field key { indexing: attribute }",
+ " struct-field value.weight { indexing: attribute }",
+ "}"));
+ assertFalse(f.isSupportedComplexField());
+ assertFalse(f.isArrayOfSimpleStruct());
+ assertFalse(f.isMapOfSimpleStruct());
+ assertFalse(f.isMapOfPrimitiveType());
+ assertFalse(f.isComplexFieldWithOnlyStructFieldAttributes());
}
}
+
}
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/AssertIndexingScript.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/AssertIndexingScript.java
index f76eb46bbf0..aafa53261ba 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/AssertIndexingScript.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/AssertIndexingScript.java
@@ -13,7 +13,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public abstract class AssertIndexingScript {
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/AssertSearchBuilder.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/AssertSearchBuilder.java
index cc48030aaaa..4ae3fe37d9a 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/AssertSearchBuilder.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/AssertSearchBuilder.java
@@ -9,7 +9,7 @@ import java.io.IOException;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public abstract class AssertSearchBuilder {
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/ImplicitSummariesTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/ImplicitSummariesTestCase.java
index da0732dfbaa..6bed57e6ec8 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/ImplicitSummariesTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/ImplicitSummariesTestCase.java
@@ -20,7 +20,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ImplicitSummariesTestCase {
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingInputsTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingInputsTestCase.java
index 796357034b6..8fb40a73f0a 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingInputsTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingInputsTestCase.java
@@ -9,7 +9,7 @@ import java.io.IOException;
import static com.yahoo.searchdefinition.processing.AssertSearchBuilder.assertBuildFails;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class IndexingInputsTestCase {
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingOutputsTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingOutputsTestCase.java
index ee9ecd5d0d0..4a62eb92ba0 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingOutputsTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingOutputsTestCase.java
@@ -10,7 +10,7 @@ import static com.yahoo.searchdefinition.processing.AssertSearchBuilder.assertBu
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class IndexingOutputsTestCase {
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingScriptRewriterTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingScriptRewriterTestCase.java
index c1001bd328b..876e852aea1 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingScriptRewriterTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingScriptRewriterTestCase.java
@@ -26,7 +26,7 @@ import static com.yahoo.searchdefinition.processing.AssertIndexingScript.assertI
import static org.junit.Assert.assertEquals;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class IndexingScriptRewriterTestCase extends SearchDefinitionTestCase {
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 674c59d5db5..df323ddfe09 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
@@ -14,7 +14,7 @@ import static com.yahoo.searchdefinition.processing.AssertIndexingScript.assertI
import static com.yahoo.searchdefinition.processing.AssertSearchBuilder.assertBuildFails;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class IndexingValidationTestCase extends AbstractExportingTestCase {
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 885f6e7c56b..8d3f1ba0020 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
@@ -11,7 +11,7 @@ import static com.yahoo.searchdefinition.processing.AssertSearchBuilder.assertBu
import static com.yahoo.searchdefinition.processing.AssertSearchBuilder.assertBuilds;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class IndexingValuesTestCase {
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/ReservedDocumentNamesTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/ReservedDocumentNamesTestCase.java
index b2dd58e9fdf..b863e5b51d3 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/ReservedDocumentNamesTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/ReservedDocumentNamesTestCase.java
@@ -11,7 +11,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ReservedDocumentNamesTestCase extends AbstractExportingTestCase {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/VespaModelFactoryTest.java b/config-model/src/test/java/com/yahoo/vespa/model/VespaModelFactoryTest.java
index 4d221af45a0..094494073df 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/VespaModelFactoryTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/VespaModelFactoryTest.java
@@ -12,6 +12,7 @@ import com.yahoo.config.model.api.Model;
import com.yahoo.config.model.api.ModelContext;
import com.yahoo.config.model.api.ModelCreateResult;
import com.yahoo.config.model.api.ServiceInfo;
+import com.yahoo.config.model.api.ValidationParameters;
import com.yahoo.config.model.test.MockApplicationPackage;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ApplicationName;
@@ -65,14 +66,14 @@ public class VespaModelFactoryTest {
@Test(expected = IllegalArgumentException.class)
public void testThatFactoryModelValidationFailsWithIllegalArgumentException() {
VespaModelFactory modelFactory = new VespaModelFactory(new NullConfigModelRegistry());
- modelFactory.createAndValidateModel(new MockModelContext(createApplicationPackageThatFailsWhenValidating()), false);
+ modelFactory.createAndValidateModel(new MockModelContext(createApplicationPackageThatFailsWhenValidating()), new ValidationParameters());
}
// Uses a MockApplicationPackage that throws throws UnsupportedOperationException (rethrown as RuntimeException) when validating
@Test(expected = RuntimeException.class)
public void testThatFactoryModelValidationFails() {
VespaModelFactory modelFactory = new VespaModelFactory(new NullConfigModelRegistry());
- modelFactory.createAndValidateModel(testModelContext, false);
+ modelFactory.createAndValidateModel(testModelContext, new ValidationParameters());
}
@Test
@@ -80,7 +81,7 @@ public class VespaModelFactoryTest {
VespaModelFactory modelFactory = new VespaModelFactory(new NullConfigModelRegistry());
ModelCreateResult createResult = modelFactory.createAndValidateModel(
new MockModelContext(createApplicationPackageThatFailsWhenValidating()),
- true);
+ new ValidationParameters(ValidationParameters.IgnoreValidationErrors.TRUE));
assertNotNull(createResult.getModel());
assertNotNull(createResult.getConfigChangeActions());
assertTrue(createResult.getConfigChangeActions().isEmpty());
@@ -209,6 +210,12 @@ public class VespaModelFactoryTest {
public String athenzDnsSuffix() {
return null;
}
+
+ @Override
+ public boolean isBootstrap() { return false; }
+
+ @Override
+ public boolean isFirstTimeDeployment() { return false; }
};
}
};
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/admin/AdminTestCase.java b/config-model/src/test/java/com/yahoo/vespa/model/admin/AdminTestCase.java
index 5d8a49b86eb..9cafe6541e4 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/admin/AdminTestCase.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/admin/AdminTestCase.java
@@ -161,7 +161,7 @@ public class AdminTestCase {
.instanceName("bim")
.build())
.build())
- .build(true);
+ .build();
TestRoot root = new TestDriver().buildModel(state);
String localhost = HostName.getLocalhost();
SentinelConfig config = root.getConfig(SentinelConfig.class, "hosts/" + localhost);
@@ -293,7 +293,7 @@ public class AdminTestCase {
applicationName("foo").instanceName("bim")
.build())
.build())
- .build(true);
+ .build();
TestRoot root = new TestDriver().buildModel(state);
String localhost = HostName.getLocalhost();
SentinelConfig sentinelConfig = root.getConfig(SentinelConfig.class, "hosts/" + localhost);
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 dbff72b4125..7b586354394 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
@@ -204,7 +204,7 @@ public class DedicatedAdminV4Test {
return new VespaModel(new NullConfigModelRegistry(),
new DeployState.Builder().applicationPackage(app).modelHostProvisioner(
new InMemoryProvisioner(Hosts.readFrom(app.getHosts()), true))
- .build(true));
+ .build());
}
}
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
new file mode 100644
index 00000000000..6483933385d
--- /dev/null
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidatorTestCase.java
@@ -0,0 +1,71 @@
+// 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;
+
+import com.yahoo.config.application.api.ApplicationPackage;
+import com.yahoo.config.model.NullConfigModelRegistry;
+import com.yahoo.config.model.api.ValidationParameters;
+import com.yahoo.config.model.api.ValidationParameters.CheckRouting;
+import com.yahoo.config.model.deploy.DeployState;
+import com.yahoo.config.model.test.MockApplicationPackage;
+import com.yahoo.vespa.model.VespaModel;
+import com.yahoo.vespa.model.content.utils.ContentClusterBuilder;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+
+import static com.yahoo.config.model.test.TestUtil.joinLines;
+
+/**
+ * @author geirst
+ */
+public class ComplexAttributeFieldsValidatorTestCase {
+
+ @Rule
+ public final ExpectedException exceptionRule = ExpectedException.none();
+
+ @Test
+ public void throws_exception_when_unsupported_complex_fields_have_struct_field_attributes() throws IOException, SAXException {
+ exceptionRule.expect(IllegalArgumentException.class);
+ exceptionRule.expectMessage("For cluster 'mycluster', search 'test': " +
+ "The following complex fields do not support using struct field attributes: " +
+ "struct_array (struct_array.s1), struct_map (struct_map.key, struct_map.value.s1). " +
+ "Only supported for the following complex field types: array or map of struct with primitive types, map of primitive types");
+
+ createModelAndValidate(joinLines("search test {",
+ " document test {",
+ " struct s { field s1 type array<int> {} }",
+ " field struct_array type array<s> {",
+ " struct-field s1 { indexing: attribute }",
+ " }",
+ " field struct_map type map<string,s> {",
+ " struct-field key { indexing: attribute }",
+ " struct-field value.s1 { indexing: attribute }",
+ " }",
+ " }",
+ "}"));
+ }
+
+ private static void createModelAndValidate(String searchDefinition) throws IOException, SAXException {
+ DeployState deployState = createDeployState(servicesXml(), searchDefinition);
+ VespaModel model = new VespaModel(new NullConfigModelRegistry(), deployState);
+ ValidationParameters validationParameters = new ValidationParameters(CheckRouting.FALSE);
+ Validation.validate(model, validationParameters, deployState);
+ }
+
+ private static DeployState createDeployState(String servicesXml, String searchDefinition) {
+ ApplicationPackage app = new MockApplicationPackage.Builder()
+ .withServices(servicesXml)
+ .withSearchDefinition(searchDefinition)
+ .build();
+ return new DeployState.Builder().applicationPackage(app).build();
+ }
+
+ private static String servicesXml() {
+ return joinLines("<services version='1.0'>",
+ new ContentClusterBuilder().getXml(),
+ "</services>");
+ }
+}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/DeploymentFileValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/DeploymentFileValidatorTest.java
index 9f8ff485d72..86e9ccb8a29 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/DeploymentFileValidatorTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/DeploymentFileValidatorTest.java
@@ -56,7 +56,7 @@ public class DeploymentFileValidatorTest {
.build();
DeployState.Builder builder = new DeployState.Builder().applicationPackage(app);
try {
- final DeployState deployState = builder.build(true);
+ final DeployState deployState = builder.build();
VespaModel model = new VespaModel(new NullConfigModelRegistry(), deployState);
new DeploymentFileValidator().validate(model, deployState);
fail("Did not get expected exception");
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/NoPrefixForIndexesTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/NoPrefixForIndexesTest.java
index 4f5bed667f0..ec7f8f8a179 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/NoPrefixForIndexesTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/NoPrefixForIndexesTest.java
@@ -8,7 +8,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Harald Musum</a>
+ * @author Harald Musum
*/
public class NoPrefixForIndexesTest {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/SearchDataTypeValidatorTestCase.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/SearchDataTypeValidatorTestCase.java
index af6e8306596..b9387d8b665 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/SearchDataTypeValidatorTestCase.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/SearchDataTypeValidatorTestCase.java
@@ -8,7 +8,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class SearchDataTypeValidatorTestCase {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/SecretStoreValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/SecretStoreValidatorTest.java
index cac3e65de89..03d004e93a4 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/SecretStoreValidatorTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/SecretStoreValidatorTest.java
@@ -84,7 +84,7 @@ public class SecretStoreValidatorTest {
.properties(new DeployProperties.Builder()
.hostedVespa(true)
.build());
- final DeployState deployState = builder.build(true);
+ final DeployState deployState = builder.build();
assertTrue("Test must emulate a hosted deployment.", deployState.isHosted());
return deployState;
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ConfigChangeTestUtils.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ConfigChangeTestUtils.java
index 9572cfebb2a..65ac6c7625e 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ConfigChangeTestUtils.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ConfigChangeTestUtils.java
@@ -22,6 +22,10 @@ public class ConfigChangeTestUtils {
return new VespaRestartAction(message, services);
}
+ public static VespaConfigChangeAction newRefeedAction(String name, String message) {
+ return VespaRefeedAction.of(name, ValidationOverrides.empty, message, Instant.now());
+ }
+
public static VespaConfigChangeAction newRefeedAction(String name, ValidationOverrides overrides, String message, Instant now) {
return VespaRefeedAction.of(name, overrides, message, now);
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidatorTest.java
index ba736af2159..339f8514f9f 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidatorTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidatorTest.java
@@ -31,14 +31,20 @@ public class DocumentDatabaseChangeValidatorTest {
@Test
public void requireThatAttributeIndexAndDocumentTypeChangesAreDiscovered() throws Exception {
- Fixture f = new Fixture("field f1 type string { indexing: summary } " +
+ Fixture f = new Fixture("struct s { field s1 type string {} } " +
+ "field f1 type string { indexing: summary } " +
"field f2 type string { indexing: summary } " +
- "field f3 type int { indexing: summary }",
+ "field f3 type int { indexing: summary } " +
+ "field f4 type array<s> { } ",
+ "struct s { field s1 type string {} } " +
"field f1 type string { indexing: attribute | summary } " +
"field f2 type string { indexing: index | summary } " +
- "field f3 type string { indexing: summary }");
+ "field f3 type string { indexing: summary } " +
+ "field f4 type array<s> { struct-field s1 { indexing: attribute } }");
f.assertValidation(Arrays.asList(
newRestartAction("Field 'f1' changed: add attribute aspect"),
+ newRefeedAction("field-type-change",
+ "Field 'f4.s1' changed: add attribute aspect"),
newRefeedAction("indexing-change",
ValidationOverrides.empty,
"Field 'f2' changed: add index aspect, indexing script: '{ input f2 | summary f2; }' -> " +
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/StructFieldAttributeChangeValidatorTestCase.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/StructFieldAttributeChangeValidatorTestCase.java
new file mode 100644
index 00000000000..c224e801fa3
--- /dev/null
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/StructFieldAttributeChangeValidatorTestCase.java
@@ -0,0 +1,146 @@
+// 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.search;
+
+import com.yahoo.config.application.api.ValidationOverrides;
+import com.yahoo.vespa.model.application.validation.change.VespaConfigChangeAction;
+import org.junit.Test;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.yahoo.vespa.model.application.validation.change.ConfigChangeTestUtils.newRefeedAction;
+
+/**
+ * @author geirst
+ */
+public class StructFieldAttributeChangeValidatorTestCase {
+
+ private static class Fixture extends ContentClusterFixture {
+ private StructFieldAttributeChangeValidator structFieldAttributeValidator;
+ private DocumentTypeChangeValidator docTypeValidator;
+
+ public Fixture(String currentSd, String nextSd) throws Exception {
+ super(currentSd, nextSd);
+ structFieldAttributeValidator = new StructFieldAttributeChangeValidator(currentDocType(),
+ currentDb().getDerivedConfiguration().getAttributeFields(),
+ nextDocType(),
+ nextDb().getDerivedConfiguration().getAttributeFields());
+ docTypeValidator = new DocumentTypeChangeValidator(currentDocType(), nextDocType());
+ }
+
+ @Override
+ public List<VespaConfigChangeAction> validate() {
+ List<VespaConfigChangeAction> result = new ArrayList<>();
+ result.addAll(structFieldAttributeValidator.validate(ValidationOverrides.empty, Instant.now()));
+ result.addAll(docTypeValidator.validate(ValidationOverrides.empty, Instant.now()));
+ return result;
+ }
+ }
+
+ private static void validate(String currentSd, String nextSd) throws Exception {
+ new Fixture(currentSd, nextSd).assertValidation();
+ }
+
+ private static void validate(String currentSd, String nextSd, VespaConfigChangeAction expAction) throws Exception {
+ new Fixture(currentSd, nextSd).assertValidation(expAction);
+ }
+
+ @Test
+ public void adding_attribute_aspect_to_struct_field_requires_refeed() throws Exception {
+ validate(arrayOfStruct(oneFieldStruct(), ""),
+ arrayOfStruct(oneFieldStruct(), structAttribute("s1")),
+ newRefeedAction("field-type-change", "Field 'f1.s1' changed: add attribute aspect"));
+
+ validate(mapOfStruct(oneFieldStruct(), ""),
+ mapOfStruct(oneFieldStruct(), structAttribute("key")),
+ newRefeedAction("field-type-change", "Field 'f1.key' changed: add attribute aspect"));
+
+ validate(mapOfStruct(oneFieldStruct(), ""),
+ mapOfStruct(oneFieldStruct(), structAttribute("value.s1")),
+ newRefeedAction("field-type-change", "Field 'f1.value.s1' changed: add attribute aspect"));
+
+ validate(mapOfPrimitive(""), mapOfPrimitive(structAttribute("key")),
+ newRefeedAction("field-type-change", "Field 'f1.key' changed: add attribute aspect"));
+
+ validate(mapOfPrimitive(""), mapOfPrimitive(structAttribute("value")),
+ newRefeedAction("field-type-change", "Field 'f1.value' changed: add attribute aspect"));
+ }
+
+ @Test
+ public void removing_attribute_aspect_from_struct_field_is_ok() throws Exception {
+ validate(arrayOfStruct(oneFieldStruct(), structAttribute("s1")),
+ arrayOfStruct(oneFieldStruct(), ""));
+
+ validate(mapOfStruct(oneFieldStruct(), structAttribute("key")),
+ mapOfStruct(oneFieldStruct(), ""));
+
+ validate(mapOfStruct(oneFieldStruct(), structAttribute("value.s1")),
+ mapOfStruct(oneFieldStruct(), ""));
+
+ validate(mapOfPrimitive(structAttribute("key")), mapOfPrimitive(""));
+
+ validate(mapOfPrimitive(structAttribute("value")), mapOfPrimitive(""));
+ }
+
+ @Test
+ public void adding_struct_field_with_attribute_aspect_is_ok() throws Exception {
+ validate(arrayOfStruct(oneFieldStruct(), ""),
+ arrayOfStruct(twoFieldStruct(), structAttribute("s2")));
+
+ validate(mapOfStruct(oneFieldStruct(), ""),
+ mapOfStruct(twoFieldStruct(), structAttribute("value.s2")));
+ }
+
+ @Test
+ public void removing_struct_field_with_attribute_aspect_is_ok() throws Exception {
+ validate(arrayOfStruct(twoFieldStruct(), structAttribute("s2")),
+ arrayOfStruct(oneFieldStruct(), ""));
+
+ validate(mapOfStruct(twoFieldStruct(), structAttribute("value.s2")),
+ mapOfStruct(oneFieldStruct(), ""));
+ }
+
+ @Test
+ public void adding_struct_field_without_attribute_aspect_is_ok() throws Exception {
+ validate(arrayOfStruct(oneFieldStruct(), ""),
+ arrayOfStruct(twoFieldStruct(), ""));
+
+ validate(mapOfStruct(oneFieldStruct(), ""),
+ mapOfStruct(twoFieldStruct(), ""));
+ }
+
+ @Test
+ public void removing_struct_field_without_attribute_aspect_is_ok() throws Exception {
+ validate(arrayOfStruct(twoFieldStruct(), ""),
+ arrayOfStruct(oneFieldStruct(), ""));
+
+ validate(mapOfStruct(twoFieldStruct(), ""),
+ mapOfStruct(oneFieldStruct(), ""));
+ }
+
+ private static String oneFieldStruct() {
+ return "struct s { field s1 type string {} }";
+ }
+
+ private static String twoFieldStruct() {
+ return "struct s { field s1 type string {} field s2 type int {} }";
+ }
+
+ private static String structAttribute(String fieldName) {
+ return "struct-field " + fieldName + " { indexing: attribute }";
+ }
+
+ private static String arrayOfStruct(String struct, String structField) {
+ return struct + " field f1 type array<s> { " + structField + "}";
+ }
+
+ private static String mapOfStruct(String struct, String structField) {
+ return struct + " field f1 type map<string,s> { " + structField + " }";
+ }
+
+ private static String mapOfPrimitive(String structField) {
+ return "field f1 type map<string,int> { " + structField + " }";
+ }
+
+}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/first/AccessControlValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/first/AccessControlValidatorTest.java
index 3f109b53bd9..d3549eb6513 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/first/AccessControlValidatorTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/first/AccessControlValidatorTest.java
@@ -144,7 +144,7 @@ public class AccessControlValidatorTest {
.properties(new DeployProperties.Builder()
.hostedVespa(true)
.build());
- final DeployState deployState = builder.build(true);
+ final DeployState deployState = builder.build();
assertTrue("Test must emulate a hosted deployment.", deployState.isHosted());
assertEquals("Test must emulate a prod environment.", prod, deployState.zone().environment());
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/chains/DependenciesBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/chains/DependenciesBuilderTest.java
index b2d66631ffb..ed6b1ae07f0 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/chains/DependenciesBuilderTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/chains/DependenciesBuilderTest.java
@@ -12,7 +12,7 @@ import static org.junit.Assert.assertEquals;
/**
* Basic tests of DependencyBuilder
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class DependenciesBuilderTest extends DomBuilderTest {
private Set<String> set(String str) {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomFederationSearcherBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomFederationSearcherBuilderTest.java
index 3993e926802..93efadba451 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomFederationSearcherBuilderTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomFederationSearcherBuilderTest.java
@@ -21,7 +21,7 @@ import static org.junit.Assert.assertThat;
/**
* Test of DomFederationSearcherBuilder.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class DomFederationSearcherBuilderTest extends DomBuilderTest {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearchChainsBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearchChainsBuilderTest.java
index b36b782495b..a13f9252e5f 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearchChainsBuilderTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearchChainsBuilderTest.java
@@ -33,7 +33,7 @@ import static org.junit.Assert.fail;
/**
* Test of Search chains builder.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class DomSearchChainsBuilderTest extends DomBuilderTest {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearcherBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearcherBuilderTest.java
index f0c3861ecaa..96d51677213 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearcherBuilderTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomSearcherBuilderTest.java
@@ -10,7 +10,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class DomSearcherBuilderTest extends DomBuilderTest {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java
index 2dbd15b600b..d9c151480fe 100755
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java
@@ -78,7 +78,7 @@ public class ContainerClusterTest {
public void requreThatWeCanGetTheZoneConfig() {
DeployState state = new DeployState.Builder().properties(new DeployProperties.Builder().hostedVespa(true).build())
.zone(new Zone(SystemName.cd, Environment.test, RegionName.from("some-region")))
- .build(true);
+ .build();
MockRoot root = new MockRoot("foo", state);
ContainerCluster cluster = new ContainerCluster(root, "container0", "container1");
ConfigserverConfig.Builder builder = new ConfigserverConfig.Builder();
@@ -107,7 +107,7 @@ public class ContainerClusterTest {
}
private ContainerCluster createContainerCluster(boolean isHosted, boolean isCombinedCluster,
Optional<Integer> memoryPercentage, Optional<ContainerClusterVerifier> extraComponents) {
- DeployState state = new DeployState.Builder().properties(new DeployProperties.Builder().hostedVespa(isHosted).build()).build(true);
+ DeployState state = new DeployState.Builder().properties(new DeployProperties.Builder().hostedVespa(isHosted).build()).build();
MockRoot root = new MockRoot("foo", state);
ContainerCluster cluster = extraComponents.isPresent()
@@ -255,7 +255,7 @@ public class ContainerClusterTest {
@Test
public void requireThatRoutingProviderIsDisabledForNonHosted() {
- DeployState state = new DeployState.Builder().properties(new DeployProperties.Builder().hostedVespa(false).build()).build(true);
+ DeployState state = new DeployState.Builder().properties(new DeployProperties.Builder().hostedVespa(false).build()).build();
MockRoot root = new MockRoot("foo", state);
ContainerCluster cluster = new ContainerCluster(root, "container0", "container1");
RoutingProviderConfig.Builder builder = new RoutingProviderConfig.Builder();
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/configserver/ConfigserverClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/configserver/ConfigserverClusterTest.java
index 0c91b8d4a16..d4209c9c788 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/configserver/ConfigserverClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/configserver/ConfigserverClusterTest.java
@@ -38,7 +38,7 @@ public class ConfigserverClusterTest {
new ConfigServerContainerModelBuilder(new TestOptions().rpcPort(12345).useVespaVersionInRequest(true)
.hostedVespa(true).environment("test").region("bar")
.numParallelTenantLoaders(99))
- .build(new DeployState.Builder().build(true), null, root, XML.getDocument(services).getDocumentElement());
+ .build(new DeployState.Builder().build(), null, root, XML.getDocument(services).getDocumentElement());
root.freezeModelTopology();
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/http/FilterChainsTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/http/FilterChainsTest.java
index 651ef10864d..26b9f025dba 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/http/FilterChainsTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/http/FilterChainsTest.java
@@ -16,7 +16,7 @@ import static org.junit.Assert.assertThat;
/**
* @author gjoranv
- * @author tonytv
+ * @author Tony Vaagenes
* @since 5.1.26
*/
public class FilterChainsTest extends DomBuilderTest {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/FederationTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/FederationTest.java
index a3e1e7331f2..4bf611f19f4 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/FederationTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/search/searchchain/FederationTest.java
@@ -12,7 +12,7 @@ import static org.junit.Assert.*;
/**
* Test generated config for federation.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class FederationTest extends SearchChainsTestBase {
@Override
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 80863ffc74d..d93a07a64fa 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
@@ -21,7 +21,7 @@ import static org.junit.Assert.*;
/**
* Test of search chains config
* <p>TODO: examine the actual values in the configs.</p>
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class SearchChainsTest extends SearchChainsTestBase {
private ChainsConfig chainsConfig;
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 3e0e8a2b5f4..91d2ffea4b8 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
@@ -9,7 +9,7 @@ import org.junit.Before;
import org.w3c.dom.Element;
/** Creates SearchChains model from xml input.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public abstract class SearchChainsTestBase extends DomBuilderTest {
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 11bbeb88099..d14905ddab0 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
@@ -17,7 +17,7 @@ import static org.junit.Assert.fail;
import static org.hamcrest.Matchers.containsString;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class SourceGroupTest {
private MockRoot root;
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilderTest.java
index c58f3308ced..a651bbb7772 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilderTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilderTest.java
@@ -53,7 +53,7 @@ public class BundleInstantiationSpecificationBuilderTest {
InputStream xmlStream = IOUtils.toInputStream(xml);
Element component = XmlHelper.getDocumentBuilder().parse(xmlStream).getDocumentElement();
- BundleInstantiationSpecification spec = BundleInstantiationSpecificationBuilder.build(component, false);
+ BundleInstantiationSpecification spec = BundleInstantiationSpecificationBuilder.build(component);
assertThat(spec.bundle, is(ComponentSpecification.fromString(expectedBundle)));
}
}
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 0fbe44742de..e3dfa093735 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
@@ -116,7 +116,7 @@ public class ContainerModelBuilderTest extends ContainerModelBuilderTestBase {
.properties(new DeployProperties.Builder()
.hostedVespa(true)
.build())
- .build(true));
+ .build());
assertFalse(logger.msgs.isEmpty());
assertThat(logger.msgs.get(0).getSecond(), containsString(String.format("You cannot set port to anything else than %d", Container.BASEPORT)));
}
@@ -512,7 +512,7 @@ public class ContainerModelBuilderTest extends ContainerModelBuilderTestBase {
" </nodes>",
"</jdisc>");
- DeployState deployState = new DeployState.Builder().zone(new Zone(Environment.dev, RegionName.from("us-east-1"))).build(true);
+ DeployState deployState = new DeployState.Builder().zone(new Zone(Environment.dev, RegionName.from("us-east-1"))).build();
createModel(root, deployState, clusterElem);
assertEquals(0, getContainerCluster("default").serviceAliases().size());
assertEquals(0, getContainerCluster("default").endpointAliases().size());
@@ -532,7 +532,7 @@ public class ContainerModelBuilderTest extends ContainerModelBuilderTestBase {
.multitenant(true)
.hostedVespa(true)
.build())
- .build(true));
+ .build());
assertEquals(1, model.getHostSystem().getHosts().size());
}
@@ -565,7 +565,7 @@ public class ContainerModelBuilderTest extends ContainerModelBuilderTestBase {
.properties(new DeployProperties.Builder()
.hostedVespa(true)
.build())
- .build(true));
+ .build());
AbstractConfigProducerRoot modelRoot = model.getRoot();
VipStatusConfig vipStatusConfig = modelRoot.getConfig(VipStatusConfig.class, "jdisc/component/status.html-status-handler");
@@ -593,7 +593,7 @@ public class ContainerModelBuilderTest extends ContainerModelBuilderTestBase {
VespaModel model = new VespaModel(new NullConfigModelRegistry(), new DeployState.Builder()
.applicationPackage(applicationPackage)
.properties(new DeployProperties.Builder().build())
- .build(true));
+ .build());
String hostname = HostName.getLocalhost(); // Using the same way of getting hostname as filedistribution model
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 8ba6aacdc41..a2f32694340 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
@@ -67,7 +67,7 @@ public class RoutingBuilderTest extends ContainerModelBuilderTestBase {
DeployState deployState = new DeployState.Builder()
.applicationPackage(applicationPackage)
.zone(new Zone(Environment.prod, RegionName.from(region)))
- .build(true);
+ .build();
root = new MockRoot("root", deployState);
createModel(root, deployState, clusterElem);
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 cc8733fc9e2..30f1df6a394 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
@@ -8,6 +8,8 @@ import com.yahoo.searchdefinition.parser.ParseException;
import com.yahoo.vespa.model.VespaModel;
import com.yahoo.vespa.model.container.Container;
import com.yahoo.vespa.model.container.ContainerCluster;
+import com.yahoo.vespa.model.container.component.Handler;
+import com.yahoo.vespa.model.container.search.GUIHandler;
import com.yahoo.vespa.model.test.utils.ApplicationPackageUtils;
import com.yahoo.vespa.model.test.utils.VespaModelCreatorWithMockPkg;
import org.junit.Test;
@@ -32,6 +34,31 @@ public class SearchBuilderTest extends ContainerModelBuilderTestBase {
return root.getConfig(ChainsConfig.class, "default/component/com.yahoo.search.handler.SearchHandler");
}
+ @Test
+ public void gui_search_handler_is_always_included_when_search_is_specified() throws Exception{
+ Element clusterElem = DomBuilderTest.parse(
+ "<jdisc id='default' version='1.0'>",
+ " <search />",
+ nodesXml,
+ "</jdisc>");
+
+ createModel(root, clusterElem);
+
+ String discBindingsConfig = root.getConfig(JdiscBindingsConfig.class, "default").toString();
+ assertThat(discBindingsConfig, containsString(GUIHandler.BINDING));
+
+ ContainerCluster cluster = (ContainerCluster)root.getChildren().get("default");
+
+ GUIHandler guiHandler = null;
+ for (Handler<?> handler : cluster.getHandlers()) {
+ if (handler instanceof GUIHandler) {
+ guiHandler = (GUIHandler) handler;
+ }
+ }
+ if (guiHandler == null) fail();
+ }
+
+
@Test
public void search_handler_bindings_can_be_overridden() throws Exception {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/ContentSearchTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/ContentSearchTest.java
index e185e50ea08..8bdfd68dd62 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/ContentSearchTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/ContentSearchTest.java
@@ -7,7 +7,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ContentSearchTest {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/SearchCoverageTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/SearchCoverageTest.java
index a4585cf1c82..d2f3c92ee70 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/SearchCoverageTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/SearchCoverageTest.java
@@ -8,7 +8,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class SearchCoverageTest {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/TuningDispatchTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/TuningDispatchTest.java
index 5cfdc8eb6b6..14b7f045ca8 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/TuningDispatchTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/TuningDispatchTest.java
@@ -8,7 +8,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class TuningDispatchTest {
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 03ba42e190b..b4994e5d009 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
@@ -22,7 +22,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ClusterTest {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/cluster/DomContentSearchBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/cluster/DomContentSearchBuilderTest.java
index e6dc48e42ad..e5d6e149c70 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/cluster/DomContentSearchBuilderTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/cluster/DomContentSearchBuilderTest.java
@@ -13,7 +13,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class DomContentSearchBuilderTest {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/cluster/DomSearchCoverageBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/cluster/DomSearchCoverageBuilderTest.java
index 7f1be1ca163..d9db6234f1c 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/cluster/DomSearchCoverageBuilderTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/cluster/DomSearchCoverageBuilderTest.java
@@ -13,7 +13,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class DomSearchCoverageBuilderTest {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/cluster/DomTuningDispatchBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/cluster/DomTuningDispatchBuilderTest.java
index a8d928fe236..8c6fe110b0a 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/cluster/DomTuningDispatchBuilderTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/cluster/DomTuningDispatchBuilderTest.java
@@ -14,7 +14,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class DomTuningDispatchBuilderTest {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/utils/ContentClusterUtils.java b/config-model/src/test/java/com/yahoo/vespa/model/content/utils/ContentClusterUtils.java
index 8e0c0d0b253..b0d6c94947a 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/utils/ContentClusterUtils.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/utils/ContentClusterUtils.java
@@ -39,10 +39,10 @@ public class ContentClusterUtils {
private static MockRoot createMockRoot(HostProvisioner provisioner, List<String> searchDefinitions, DeployState.Builder deployStateBuilder) {
ApplicationPackage applicationPackage = new MockApplicationPackage.Builder().withSearchDefinitions(searchDefinitions).build();
- deployStateBuilder.applicationPackage(applicationPackage)
+ DeployState deployState = deployStateBuilder.applicationPackage(applicationPackage)
.modelHostProvisioner(provisioner)
- .build(true);
- return new MockRoot("", deployStateBuilder.build(true));
+ .build();
+ return new MockRoot("", deployState);
}
public static MockRoot createMockRoot(String[] hosts, List<String> searchDefinitions) {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/routing/test/RoutingTestCase.java b/config-model/src/test/java/com/yahoo/vespa/model/routing/test/RoutingTestCase.java
index 2cd0f906a30..31d0084570a 100755
--- a/config-model/src/test/java/com/yahoo/vespa/model/routing/test/RoutingTestCase.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/routing/test/RoutingTestCase.java
@@ -21,7 +21,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class RoutingTestCase {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/search/TldTest.java b/config-model/src/test/java/com/yahoo/vespa/model/search/TldTest.java
index bfe05ec54ce..c3cbc58860b 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/search/TldTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/search/TldTest.java
@@ -11,7 +11,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class TldTest {
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 ce72e784125..1a5ce0a10f4 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
@@ -10,6 +10,7 @@ import com.yahoo.config.application.api.ApplicationPackage;
import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.config.model.NullConfigModelRegistry;
import com.yahoo.config.model.api.HostInfo;
+import com.yahoo.config.model.api.ValidationParameters;
import com.yahoo.config.model.application.provider.FilesApplicationPackage;
import com.yahoo.config.model.deploy.DeployProperties;
import com.yahoo.config.model.deploy.DeployState;
@@ -24,7 +25,6 @@ import com.yahoo.messagebus.MessagebusConfig;
import com.yahoo.net.HostName;
import com.yahoo.vespa.config.UnknownConfigIdException;
import com.yahoo.vespa.model.ConfigProducer;
-import com.yahoo.vespa.model.HostSystem;
import com.yahoo.vespa.model.VespaModel;
import com.yahoo.vespa.model.admin.Admin;
import com.yahoo.vespa.model.admin.Configserver;
@@ -246,9 +246,9 @@ public class VespaModelTestCase {
.withHosts(simpleHosts)
.withServices(services)
.build();
- DeployState deployState = builder.deployLogger(logger).applicationPackage(app).build(true);
+ DeployState deployState = builder.deployLogger(logger).applicationPackage(app).build();
VespaModel model = new VespaModel(new NullConfigModelRegistry(), deployState);
- Validation.validate(model, true, deployState);
+ Validation.validate(model, new ValidationParameters(ValidationParameters.IgnoreValidationErrors.TRUE), deployState);
assertFalse(logger.msgs.isEmpty());
}
@@ -283,7 +283,7 @@ public class VespaModelTestCase {
.configServerSpecs(Arrays.asList(new Configserver.Spec("cfghost", 1234, 1235, 1236)))
.multitenant(true)
.build())
- .build(true);
+ .build();
VespaModel model = new VespaModel(new NullConfigModelRegistry(), deployState);
AllocatedHosts info = model.allocatedHosts();
assertEquals("Admin version 3 is ignored, and there are no other hosts to borrow for admin services", 0, info.getHosts().size());
@@ -302,9 +302,9 @@ public class VespaModelTestCase {
public void testPermanentServices() throws IOException, SAXException {
ApplicationPackage app = MockApplicationPackage.createEmpty();
DeployState.Builder builder = new DeployState.Builder().applicationPackage(app);
- VespaModel model = new VespaModel(new NullConfigModelRegistry(), builder.build(true));
+ VespaModel model = new VespaModel(new NullConfigModelRegistry(), builder.build());
assertThat(model.getContainerClusters().size(), is(0));
- model = new VespaModel(new NullConfigModelRegistry(), builder.permanentApplicationPackage(Optional.of(FilesApplicationPackage.fromFile(new File(TESTDIR, "app_permanent")))).build(true));
+ model = new VespaModel(new NullConfigModelRegistry(), builder.permanentApplicationPackage(Optional.of(FilesApplicationPackage.fromFile(new File(TESTDIR, "app_permanent")))).build());
assertThat(model.getContainerClusters().size(), is(1));
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/test/VespaModelTester.java b/config-model/src/test/java/com/yahoo/vespa/model/test/VespaModelTester.java
index 715b84c7093..8cc5144c2a3 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/test/VespaModelTester.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/test/VespaModelTester.java
@@ -124,7 +124,7 @@ public class VespaModelTester {
.applicationPackage(appPkg)
.modelHostProvisioner(provisioner)
.properties(properties)
- .build(true);
+ .build();
return modelCreatorWithMockPkg.create(false, deployState, configModelRegistry);
}
}
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
index abfe28d3c7e..3791331e40c 100644
--- 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
@@ -8,7 +8,7 @@ import com.yahoo.vespa.model.VespaModel;
import java.io.File;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
//TODO Remove, use VespaModelCreatorWithMockPkg or VespaModelCreatorWithFilePkg instead
public class CommonVespaModelSetup {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/test/utils/VespaModelCreatorWithFilePkg.java b/config-model/src/test/java/com/yahoo/vespa/model/test/utils/VespaModelCreatorWithFilePkg.java
index 18fbf68497f..3c9618ceccd 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/test/utils/VespaModelCreatorWithFilePkg.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/test/utils/VespaModelCreatorWithFilePkg.java
@@ -4,6 +4,10 @@ package com.yahoo.vespa.model.test.utils;
import com.yahoo.component.Version;
import com.yahoo.config.model.ConfigModelRegistry;
import com.yahoo.config.model.NullConfigModelRegistry;
+import com.yahoo.config.model.api.ValidationParameters;
+import com.yahoo.config.model.api.ValidationParameters.CheckRouting;
+import com.yahoo.config.model.api.ValidationParameters.FailOnIncompatibleChange;
+import com.yahoo.config.model.api.ValidationParameters.IgnoreValidationErrors;
import com.yahoo.config.model.application.provider.*;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.vespa.model.VespaModel;
@@ -15,7 +19,7 @@ import java.io.IOException;
/**
* For testing purposes only
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class VespaModelCreatorWithFilePkg {
@@ -56,12 +60,13 @@ public class VespaModelCreatorWithFilePkg {
if (validateApplicationWithSchema) {
validate();
}
- DeployState deployState = new DeployState.Builder().applicationPackage(applicationPkg).build(true);
+ DeployState deployState = new DeployState.Builder().applicationPackage(applicationPkg).build();
VespaModel model = new VespaModel(configModelRegistry, deployState);
// Validate, but without checking configSources or routing (routing
// is constructed in a special way and cannot always be validated in
// this step for unit tests)
- Validation.validate(model, false, false, deployState);
+ ValidationParameters validationParameters = new ValidationParameters(IgnoreValidationErrors.TRUE, FailOnIncompatibleChange.TRUE, CheckRouting.FALSE);
+ Validation.validate(model, validationParameters, deployState);
return model;
} catch (Exception e) {
throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/test/utils/VespaModelCreatorWithMockPkg.java b/config-model/src/test/java/com/yahoo/vespa/model/test/utils/VespaModelCreatorWithMockPkg.java
index 211589c100d..814ec008285 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/test/utils/VespaModelCreatorWithMockPkg.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/test/utils/VespaModelCreatorWithMockPkg.java
@@ -6,6 +6,8 @@ import com.yahoo.config.application.api.ApplicationPackage;
import com.yahoo.config.model.ConfigModelRegistry;
import com.yahoo.config.model.NullConfigModelRegistry;
import com.yahoo.config.model.api.ConfigChangeAction;
+import com.yahoo.config.model.api.ValidationParameters;
+import com.yahoo.config.model.api.ValidationParameters.CheckRouting;
import com.yahoo.config.model.application.provider.SchemaValidators;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.test.MockApplicationPackage;
@@ -17,7 +19,7 @@ import java.util.List;
/**
* For testing purposes only.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class VespaModelCreatorWithMockPkg {
@@ -38,12 +40,12 @@ public class VespaModelCreatorWithMockPkg {
}
public VespaModel create() {
- DeployState deployState = new DeployState.Builder().applicationPackage(appPkg).build(true);
+ DeployState deployState = new DeployState.Builder().applicationPackage(appPkg).build();
return create(true, deployState);
}
public VespaModel create(DeployState.Builder deployStateBuilder) {
- return create(true, deployStateBuilder.applicationPackage(appPkg).build(true));
+ return create(true, deployStateBuilder.applicationPackage(appPkg).build());
}
public VespaModel create(boolean validate, DeployState deployState) {
@@ -72,7 +74,8 @@ public class VespaModelCreatorWithMockPkg {
// Validate, but without checking configSources or routing (routing
// is constructed in a special way and cannot always be validated in
// this step for unit tests)
- configChangeActions = Validation.validate(model, false, false, deployState);
+ ValidationParameters validationParameters = new ValidationParameters(CheckRouting.FALSE);
+ configChangeActions = Validation.validate(model, validationParameters, deployState);
}
return model;
} catch (Exception e) {
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/Capacity.java b/config-provisioning/src/main/java/com/yahoo/config/provision/Capacity.java
index 299b7282e4a..5204da08307 100644
--- a/config-provisioning/src/main/java/com/yahoo/config/provision/Capacity.java
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/Capacity.java
@@ -15,13 +15,16 @@ public final class Capacity {
private final boolean required;
+ private final boolean canFail;
+
private final Optional<String> flavor;
private final NodeType type;
- private Capacity(int nodeCount, Optional<String> flavor, boolean required, NodeType type) {
+ private Capacity(int nodeCount, Optional<String> flavor, boolean required, boolean canFail, NodeType type) {
this.nodeCount = nodeCount;
this.required = required;
+ this.canFail = canFail;
this.flavor = flavor;
this.type = type;
}
@@ -39,6 +42,13 @@ public final class Capacity {
public boolean isRequired() { return required; }
/**
+ * Returns true if an exception should be thrown if the specified capacity can not be satisfied
+ * (to whatever policies are applied and taking required true/false into account).
+ * Returns false if it is preferable to still succeed with partially satisfied capacity.
+ */
+ public boolean canFail() { return canFail; }
+
+ /**
* Returns the node type (role) requested. This is tenant nodes by default.
* If some other type is requested the node count and flavor may be ignored
* and all nodes of the requested type returned instead.
@@ -52,46 +62,22 @@ public final class Capacity {
/** Creates this from a desired node count: The request may be satisfied with a smaller number of nodes. */
public static Capacity fromNodeCount(int capacity) {
- return fromNodeCount(capacity, Optional.empty(), false);
+ return fromNodeCount(capacity, Optional.empty(), false, true);
}
+ // TODO: Remove after July 2018
+ @Deprecated
public static Capacity fromNodeCount(int nodeCount, Optional<String> flavor, boolean required) {
- return new Capacity(nodeCount, flavor, required, NodeType.tenant);
- }
-
- /** Creates this from a node type */
- public static Capacity fromRequiredNodeType(NodeType type) {
- return new Capacity(0, Optional.empty(), true, type);
- }
-
- /** Creates this from a desired node count: The request may be satisfied with a smaller number of nodes. */
- // TODO: Remove after April 2018
- public static Capacity fromNodeCount(int nodeCount, String flavor) {
- return fromNodeCount(nodeCount, Optional.of(flavor));
- }
-
- /** Creates this from a desired node count: The request may be satisfied with a smaller number of nodes. */
- // TODO: Remove after April 2018
- public static Capacity fromNodeCount(int nodeCount, Optional<String> flavor) {
- return new Capacity(nodeCount, flavor, false, NodeType.tenant);
+ return new Capacity(nodeCount, flavor, required, true, NodeType.tenant);
}
- /** Creates this from a required node count: Requests must fail unless the node count can be satisfied exactly */
- // TODO: Remove after April 2018
- public static Capacity fromRequiredNodeCount(int nodeCount) {
- return fromRequiredNodeCount(nodeCount, Optional.empty());
+ public static Capacity fromNodeCount(int nodeCount, Optional<String> flavor, boolean required, boolean canFail) {
+ return new Capacity(nodeCount, flavor, required, canFail, NodeType.tenant);
}
- /** Creates this from a required node count: Requests must fail unless the node count can be satisfied exactly */
- // TODO: Remove after April 2018
- public static Capacity fromRequiredNodeCount(int nodeCount, String flavor) {
- return fromRequiredNodeCount(nodeCount, Optional.of(flavor));
- }
-
- /** Creates this from a required node count: Requests must fail unless the node count can be satisfied exactly */
- // TODO: Remove after April 2018
- public static Capacity fromRequiredNodeCount(int nodeCount, Optional<String> flavor) {
- return new Capacity(nodeCount, flavor, true, NodeType.tenant);
+ /** Creates this from a node type */
+ public static Capacity fromRequiredNodeType(NodeType type) {
+ return new Capacity(0, Optional.empty(), true, false, type);
}
}
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java
index 837e062e356..caacabd09b5 100644
--- a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java
@@ -51,11 +51,6 @@ public final class ClusterSpec {
*/
public boolean isExclusive() { return exclusive; }
- // TODO: Remove after April 2018
- public ClusterSpec changeGroup(Optional<Group> newGroup) {
- return with(newGroup);
- }
-
public ClusterSpec with(Optional<Group> newGroup) {
return new ClusterSpec(type, id, newGroup, vespaVersion, exclusive);
}
@@ -64,18 +59,10 @@ public final class ClusterSpec {
return new ClusterSpec(type, id, groupId, vespaVersion, exclusive);
}
- // TODO: Remove after April 2018
- public static ClusterSpec request(Type type, Id id, Version vespaVersion) {
- return request(type, id, vespaVersion, false);
- }
public static ClusterSpec request(Type type, Id id, Version vespaVersion, boolean exclusive) {
return new ClusterSpec(type, id, Optional.empty(), vespaVersion, exclusive);
}
- // TODO: Remove after April 2018
- public static ClusterSpec from(Type type, Id id, Group groupId, Version vespaVersion) {
- return new ClusterSpec(type, id, Optional.of(groupId), vespaVersion, false);
- }
public static ClusterSpec from(Type type, Id id, Group groupId, Version vespaVersion, boolean exclusive) {
return new ClusterSpec(type, id, Optional.of(groupId), vespaVersion, exclusive);
}
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/Deployer.java b/config-provisioning/src/main/java/com/yahoo/config/provision/Deployer.java
index 111073aca77..2e53d4384a4 100644
--- a/config-provisioning/src/main/java/com/yahoo/config/provision/Deployer.java
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/Deployer.java
@@ -2,6 +2,7 @@
package com.yahoo.config.provision;
import java.time.Duration;
+import java.time.Instant;
import java.util.Optional;
/**
@@ -11,16 +12,42 @@ import java.util.Optional;
*/
public interface Deployer {
+ /**
+ * Creates a new deployment from the active application, if available. Will use the default timeout for deployment.
+ *
+ * @param application the active application to be redeployed
+ * @return a new deployment from the local active, or empty if a local active application
+ * was not present for this id (meaning it either is not active or deployed at another
+ * node in the config server cluster)
+ */
+ default Optional<Deployment> deployFromLocalActive(ApplicationId application) {
+ return deployFromLocalActive(application, false);
+ }
/**
* Creates a new deployment from the active application, if available. Will use the default timeout for deployment.
*
* @param application the active application to be redeployed
+ * @param bootstrap the deployment is done when bootstrapping
+ * @return a new deployment from the local active, or empty if a local active application
+ * was not present for this id (meaning it either is not active or deployed at another
+ * node in the config server cluster)
+ */
+ Optional<Deployment> deployFromLocalActive(ApplicationId application, boolean bootstrap);
+
+ /**
+ * Creates a new deployment from the active application, if available. Prefer {@link #deployFromLocalActive(ApplicationId)}
+ * if possible, this method is for testing and will override the default timeout for deployment.
+ *
+ * @param application the active application to be redeployed
+ * @param timeout the timeout to use for each individual deployment operation
* @return a new deployment from the local active, or empty if a local active application
- * was not present for this id (meaning it either is not active or active on another
- * node in the config server cluster)
+ * was not present for this id (meaning it either is not active or active on another
+ * node in the config server cluster)
*/
- Optional<Deployment> deployFromLocalActive(ApplicationId application);
+ default Optional<Deployment> deployFromLocalActive(ApplicationId application, Duration timeout) {
+ return deployFromLocalActive(application, timeout, false);
+ }
/**
* Creates a new deployment from the active application, if available. Prefer {@link #deployFromLocalActive(ApplicationId)}
@@ -28,10 +55,14 @@ public interface Deployer {
*
* @param application the active application to be redeployed
* @param timeout the timeout to use for each individual deployment operation
+ * @param bootstrap the deployment is done when bootstrapping
* @return a new deployment from the local active, or empty if a local active application
* was not present for this id (meaning it either is not active or active on another
* node in the config server cluster)
*/
- Optional<Deployment> deployFromLocalActive(ApplicationId application, Duration timeout);
+ Optional<Deployment> deployFromLocalActive(ApplicationId application, Duration timeout, boolean bootstrap);
+
+ /** Returns the time the current local active session was created, or empty if there is no local active session */
+ Optional<Instant> lastDeployTime(ApplicationId application);
}
diff --git a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ClientUpdater.java b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ClientUpdater.java
index 0ed5d04e36e..8b8f75a3f99 100644
--- a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ClientUpdater.java
+++ b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ClientUpdater.java
@@ -15,6 +15,7 @@ import java.util.logging.Logger;
* @author hmusum
*/
class ClientUpdater {
+
private final static Logger log = Logger.getLogger(ClientUpdater.class.getName());
private final ConfigProxyStatistics statistics;
diff --git a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ConfigSourceClient.java b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ConfigSourceClient.java
index f636e723828..5bab27c27a5 100644
--- a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ConfigSourceClient.java
+++ b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ConfigSourceClient.java
@@ -11,7 +11,6 @@ import java.util.List;
* getting config.
*
* @author hmusum
- * @since 5.1.9
*/
interface ConfigSourceClient {
@@ -24,4 +23,5 @@ interface ConfigSourceClient {
String getActiveSourceConnection();
List<String> getSourceConnections();
+
}
diff --git a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/DelayedResponse.java b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/DelayedResponse.java
index 76c5cb424e8..047576ee647 100644
--- a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/DelayedResponse.java
+++ b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/DelayedResponse.java
@@ -15,6 +15,7 @@ import java.util.concurrent.TimeUnit;
* @see DelayedResponseHandler
*/
public class DelayedResponse implements Delayed {
+
private final JRTServerConfigRequest request;
private final long returnTime;
@@ -58,8 +59,7 @@ public class DelayedResponse implements Delayed {
@Override
public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append(request.getShortDescription()).append(", delayLeft=").append(getDelay(TimeUnit.MILLISECONDS)).append(" ms");
- return sb.toString();
+ return request.getShortDescription() + ", delayLeft=" + getDelay(TimeUnit.MILLISECONDS) + " ms";
}
+
}
diff --git a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/DelayedResponses.java b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/DelayedResponses.java
index 00f948ef690..953aeefbfd1 100644
--- a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/DelayedResponses.java
+++ b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/DelayedResponses.java
@@ -7,7 +7,6 @@ import java.util.concurrent.DelayQueue;
* Queue for requests that have no corresponding config in cache and which we are awaiting response from server for
*
* @author hmusum
- * @since 5.1.7
*/
class DelayedResponses {
diff --git a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/MemoryCache.java b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/MemoryCache.java
index 7914b7a80b6..0fe7fe01467 100644
--- a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/MemoryCache.java
+++ b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/MemoryCache.java
@@ -21,6 +21,7 @@ import java.util.logging.Logger;
* @author hmusum
*/
public class MemoryCache {
+
private static final Logger log = Logger.getLogger(MemoryCache.class.getName());
// Separator in file names between different fields of config key
@@ -35,6 +36,7 @@ public class MemoryCache {
/**
* Put in cache, except when config has an error
+ *
* @param config config to put in cache
*/
public void put(RawConfig config) {
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 28bcca9db13..6390c9ca165 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
@@ -132,7 +132,7 @@ public class ProxyServer implements Runnable {
}
static boolean configOrGenerationHasChanged(RawConfig config, JRTServerConfigRequest request) {
- return (config != null && (!config.hasEqualConfig(request) || config.hasNewerGeneration(request)));
+ return (config != null && ( ! config.hasEqualConfig(request) || config.hasNewerGeneration(request)));
}
Mode getMode() {
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 0f80f228b36..b4eda05fde4 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
@@ -148,7 +148,8 @@ class RpcConfigSourceClient implements ConfigSourceClient {
log.log(LogLevel.DEBUG, () -> "Already a subscriber running for: " + configCacheKey);
} else {
log.log(LogLevel.DEBUG, () -> "Could not find good config in cache, creating subscriber for: " + configCacheKey);
- UpstreamConfigSubscriber subscriber = new UpstreamConfigSubscriber(input, clientUpdater, configSourceSet, timingValues, requesterPool, memoryCache);
+ UpstreamConfigSubscriber subscriber = new UpstreamConfigSubscriber(input, clientUpdater, configSourceSet,
+ timingValues, requesterPool, memoryCache);
try {
subscriber.subscribe();
activeSubscribers.put(configCacheKey, subscriber);
diff --git a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/Subscriber.java b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/Subscriber.java
index 32b1c661a75..74c99b7670b 100644
--- a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/Subscriber.java
+++ b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/Subscriber.java
@@ -5,7 +5,6 @@ package com.yahoo.vespa.config.proxy;
* Interface for subscribing to config from upstream config sources.
*
* @author hmusum
- * @since 5.5
*/
public interface Subscriber extends Runnable {
diff --git a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/UpstreamConfigSubscriber.java b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/UpstreamConfigSubscriber.java
index 528c61fe132..ceaf57e9cf1 100644
--- a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/UpstreamConfigSubscriber.java
+++ b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/UpstreamConfigSubscriber.java
@@ -17,9 +17,9 @@ import java.util.logging.Logger;
/**
* @author hmusum
- * @since 5.5
*/
public class UpstreamConfigSubscriber implements Subscriber {
+
private final static Logger log = Logger.getLogger(UpstreamConfigSubscriber.class.getName());
private final RawConfig config;
@@ -33,8 +33,7 @@ public class UpstreamConfigSubscriber implements Subscriber {
UpstreamConfigSubscriber(RawConfig config, ClientUpdater clientUpdater, ConfigSource configSourceSet,
TimingValues timingValues, Map<ConfigSourceSet, JRTConfigRequester> requesterPool,
- MemoryCache memoryCache)
- {
+ MemoryCache memoryCache) {
this.config = config;
this.clientUpdater = clientUpdater;
this.configSourceSet = configSourceSet;
@@ -46,7 +45,7 @@ public class UpstreamConfigSubscriber implements Subscriber {
void subscribe() {
subscriber = new GenericConfigSubscriber(requesterPool);
ConfigKey<?> key = config.getKey();
- handle = subscriber.subscribe(new ConfigKey<RawConfig>(key.getName(), key.getConfigId(), key.getNamespace()),
+ handle = subscriber.subscribe(new ConfigKey<>(key.getName(), key.getConfigId(), key.getNamespace()),
config.getDefContent(), configSourceSet, timingValues);
}
@@ -66,7 +65,7 @@ public class UpstreamConfigSubscriber implements Subscriber {
}
private void updateWithNewConfig(GenericConfigHandle handle) {
- final RawConfig newConfig = handle.getRawConfig();
+ RawConfig newConfig = handle.getRawConfig();
if (log.isLoggable(LogLevel.DEBUG)) {
log.log(LogLevel.DEBUG, "config to be returned for '" + newConfig.getKey() +
"', generation=" + newConfig.getGeneration() +
@@ -82,4 +81,5 @@ public class UpstreamConfigSubscriber implements Subscriber {
subscriber.close();
}
}
+
}
diff --git a/config-proxy/src/main/sh/vespa-config-ctl.sh b/config-proxy/src/main/sh/vespa-config-ctl.sh
index 8d88281207c..a670e69cdbf 100755
--- a/config-proxy/src/main/sh/vespa-config-ctl.sh
+++ b/config-proxy/src/main/sh/vespa-config-ctl.sh
@@ -108,14 +108,14 @@ case $1 in
if [ "$userargs" == "" ]; then
userargs=$services__jvmargs_configproxy
fi
- jvmopts="-Xms32M -Xmx256M -XX:ThreadStackSize=256 -XX:MaxJavaStackTraceDepth=-1"
+ jvmopts="-Xms32M -Xmx256M -XX:ThreadStackSize=256 -XX:MaxJavaStackTraceDepth=1000000"
VESPA_SERVICE_NAME=configproxy
export VESPA_SERVICE_NAME
echo "Starting config proxy using $configsources as config source(s)"
vespa-runserver -r 10 -s configproxy -p $P_CONFIG_PROXY -- \
java ${jvmopts} \
- -XX:OnOutOfMemoryError="kill -9 %p" $(getJavaOptionsIPV46) \
+ -XX:+ExitOnOutOfMemoryError $(getJavaOptionsIPV46) \
-Dproxyconfigsources="${configsources}" ${userargs} \
-cp $cp com.yahoo.vespa.config.proxy.ProxyServer 19090
diff --git a/config/pom.xml b/config/pom.xml
index 291c96514d1..e1e04e17033 100755
--- a/config/pom.xml
+++ b/config/pom.xml
@@ -65,13 +65,11 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
- <version>13.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava-testlib</artifactId>
- <version>17.0</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -88,11 +86,6 @@
<groupId>net.jpountz.lz4</groupId>
<artifactId>lz4</artifactId>
</dependency>
- <dependency>
- <groupId>org.scala-lang.modules</groupId>
- <artifactId>scala-xml_${scala.major-version}</artifactId>
- <scope>test</scope>
- </dependency>
</dependencies>
<profiles>
<profile>
diff --git a/config/src/apps/vespa-get-config/getconfig.cpp b/config/src/apps/vespa-get-config/getconfig.cpp
index 4c42779ce91..9e58c9e3777 100644
--- a/config/src/apps/vespa-get-config/getconfig.cpp
+++ b/config/src/apps/vespa-get-config/getconfig.cpp
@@ -49,7 +49,8 @@ GetConfig::usage()
fprintf(stderr, "usage: %s -n name -i configId\n", _argv[0]);
fprintf(stderr, "-n name (config name, including namespace, on the form <namespace>.<name>)\n");
fprintf(stderr, "-i configId (config id, optional)\n");
- fprintf(stderr, "-j (output config as json)\n");
+ fprintf(stderr, "-j (output config as json, optional)\n");
+ fprintf(stderr, "-l (output config in legacy cfg format, optional)\n");
fprintf(stderr, "-a schema (config def schema file, optional)\n");
fprintf(stderr, "-v defVersion (config definition version, optional, deprecated)\n");
fprintf(stderr, "-m defMd5 (definition md5sum, optional)\n");
@@ -135,6 +136,9 @@ GetConfig::Main()
case 'j':
printAsJson = true;
break;
+ case 'l':
+ printAsJson = false;
+ break;
case 'm':
defMD5 = optArg;
break;
diff --git a/config/src/main/java/com/yahoo/config/subscription/ConfigURI.java b/config/src/main/java/com/yahoo/config/subscription/ConfigURI.java
index 537aff096a9..850dbcc1bf4 100644
--- a/config/src/main/java/com/yahoo/config/subscription/ConfigURI.java
+++ b/config/src/main/java/com/yahoo/config/subscription/ConfigURI.java
@@ -10,9 +10,9 @@ import com.yahoo.config.subscription.impl.JRTConfigRequester;
* object to simplify parameter passing.
*
* @author lulf
- * @since 5.1
*/
public class ConfigURI {
+
private String configId;
private ConfigSource source;
@@ -54,4 +54,5 @@ public class ConfigURI {
public static ConfigURI createFromIdAndSource(String configId, ConfigSource source) {
return new ConfigURI(configId, source);
}
+
}
diff --git a/config/src/main/java/com/yahoo/config/subscription/FileSource.java b/config/src/main/java/com/yahoo/config/subscription/FileSource.java
index d69e2429dbb..27129853eae 100644
--- a/config/src/main/java/com/yahoo/config/subscription/FileSource.java
+++ b/config/src/main/java/com/yahoo/config/subscription/FileSource.java
@@ -5,15 +5,15 @@ import java.io.File;
/**
* Source specifying config from one local file
- * @author vegardh
- * @since 5.1
*
+ * @author vegardh
*/
public class FileSource implements ConfigSource {
+
private final File file;
public FileSource(File file) {
- if (!file.isFile()) throw new IllegalArgumentException("Not an ordinary file: "+file);
+ if ( ! file.isFile()) throw new IllegalArgumentException("Not an ordinary file: "+file);
this.file = file;
}
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 9f277330401..64eee39af1f 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
@@ -324,6 +324,7 @@ public abstract class ConfigSubscription<T extends ConfigInstance> {
* True if someone has set the {@link #reloadedGeneration} number by calling {@link #reload(long)}
* and hence wants to force a given generation programmatically. If that is the case,
* sets the generation and flags it as changed accordingly.
+ *
* @return true if {@link #reload(long)} has been called, false otherwise
*/
protected boolean checkReloaded() {
diff --git a/config/src/main/java/com/yahoo/config/subscription/impl/GenericConfigHandle.java b/config/src/main/java/com/yahoo/config/subscription/impl/GenericConfigHandle.java
index 916aa6faeff..bf247dffb7c 100644
--- a/config/src/main/java/com/yahoo/config/subscription/impl/GenericConfigHandle.java
+++ b/config/src/main/java/com/yahoo/config/subscription/impl/GenericConfigHandle.java
@@ -12,14 +12,15 @@ import com.yahoo.vespa.config.RawConfig;
@SuppressWarnings({"rawtypes", "unchecked"})
public class GenericConfigHandle extends ConfigHandle {
- private final GenericJRTConfigSubscription genSub;
+ private final GenericJRTConfigSubscription subscription;
- public GenericConfigHandle(GenericJRTConfigSubscription sub) {
- super(sub);
- genSub = sub;
+ public GenericConfigHandle(GenericJRTConfigSubscription subscription) {
+ super(subscription);
+ this.subscription = subscription;
}
public RawConfig getRawConfig() {
- return genSub.getRawConfig();
+ return subscription.getRawConfig();
}
+
}
diff --git a/config/src/main/java/com/yahoo/config/subscription/impl/GenericConfigSubscriber.java b/config/src/main/java/com/yahoo/config/subscription/impl/GenericConfigSubscriber.java
index a33c1556dd3..82d4708f53b 100644
--- a/config/src/main/java/com/yahoo/config/subscription/impl/GenericConfigSubscriber.java
+++ b/config/src/main/java/com/yahoo/config/subscription/impl/GenericConfigSubscriber.java
@@ -19,6 +19,7 @@ import com.yahoo.vespa.config.TimingValues;
* @author vegardh
*/
public class GenericConfigSubscriber extends ConfigSubscriber {
+
/**
* Constructs a new subscriber using the given pool of requesters (JRTConfigRequester holds 1 connection which in
* turn is subject to failover across the elems in the source set.)
@@ -72,4 +73,5 @@ public class GenericConfigSubscriber extends ConfigSubscriber {
*/
public void closeRequesters() {
}
+
}
diff --git a/config/src/main/java/com/yahoo/config/subscription/impl/GenericJRTConfigSubscription.java b/config/src/main/java/com/yahoo/config/subscription/impl/GenericJRTConfigSubscription.java
index e8c7edd3943..c1f9ce02650 100644
--- a/config/src/main/java/com/yahoo/config/subscription/impl/GenericJRTConfigSubscription.java
+++ b/config/src/main/java/com/yahoo/config/subscription/impl/GenericJRTConfigSubscription.java
@@ -50,6 +50,17 @@ public class GenericJRTConfigSubscription extends JRTConfigSubscription<RawConfi
}
}
+ // Override to propagate internal redeploy into the config value in addition to the config state
+ @Override
+ void setInternalRedeploy(boolean internalRedeploy) {
+ super.setInternalRedeploy(internalRedeploy);
+ ConfigState<RawConfig> configState = getConfigState();
+
+ if (configState.getConfig() != null) {
+ configState.getConfig().setInternalRedeploy(internalRedeploy);
+ }
+ }
+
public RawConfig getRawConfig() {
return getConfigState().getConfig();
}
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 920c8c264e7..58f720b8cb6 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
@@ -172,6 +172,7 @@ public class JRTConfigSubscription<T extends ConfigInstance> extends ConfigSubsc
/**
* The timing values of this
+ *
* @return timing values
*/
public TimingValues timingValues() {
diff --git a/config/src/main/java/com/yahoo/vespa/config/ConfigCacheKey.java b/config/src/main/java/com/yahoo/vespa/config/ConfigCacheKey.java
index 79edeea99fc..e8e3fe9ed94 100644
--- a/config/src/main/java/com/yahoo/vespa/config/ConfigCacheKey.java
+++ b/config/src/main/java/com/yahoo/vespa/config/ConfigCacheKey.java
@@ -3,10 +3,11 @@ package com.yahoo.vespa.config;
/**
* A ConfigKey that also uses the def MD5 sum. Used for caching when def payload is user provided.
- * @author vegardh
*
+ * @author Vegard Havdal
*/
public class ConfigCacheKey {
+
private final ConfigKey<?> key;
private final String defMd5;
@@ -45,6 +46,7 @@ public class ConfigCacheKey {
/**
* The def md5 sum of this key
+ *
* @return md5 sum
*/
public String getDefMd5() {
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 da51df143d2..751f6578575 100644
--- a/config/src/main/java/com/yahoo/vespa/config/ConfigFileFormat.java
+++ b/config/src/main/java/com/yahoo/vespa/config/ConfigFileFormat.java
@@ -13,9 +13,9 @@ import java.util.Stack;
/**
* @author lulf
- * @since 5.1
*/
public class ConfigFileFormat implements SlimeFormat, ObjectTraverser {
+
private final InnerCNode root;
private DataOutputStream out = null;
private Stack<Node> nodeStack;
diff --git a/config/src/main/java/com/yahoo/vespa/config/ConfigKey.java b/config/src/main/java/com/yahoo/vespa/config/ConfigKey.java
index 5f068c4708a..930b74ea804 100755
--- a/config/src/main/java/com/yahoo/vespa/config/ConfigKey.java
+++ b/config/src/main/java/com/yahoo/vespa/config/ConfigKey.java
@@ -135,4 +135,5 @@ public class ConfigKey<CONFIGCLASS extends ConfigInstance> implements Comparable
public static ConfigKey<?> createFull(String name, String configId, String namespace, String md5) {
return new ConfigKey<>(name, configId, namespace, md5, null);
}
+
}
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 f5f570655da..44d0c5f0625 100644
--- a/config/src/main/java/com/yahoo/vespa/config/ConfigPayloadApplier.java
+++ b/config/src/main/java/com/yahoo/vespa/config/ConfigPayloadApplier.java
@@ -27,7 +27,7 @@ import java.util.logging.Logger;
*
* TODO: This can be refactored a lot, since many of the reflection methods are duplicated
*
- * @author lulf, hmusum, tonyv
+ * @author lulf, hmusum, Tony Vaagenes
* @since 5.1.6
*/
public class ConfigPayloadApplier<T extends ConfigInstance.Builder> {
diff --git a/config/src/main/java/com/yahoo/vespa/config/ConfigTransformer.java b/config/src/main/java/com/yahoo/vespa/config/ConfigTransformer.java
index 018b17a8b83..c0254561d3b 100644
--- a/config/src/main/java/com/yahoo/vespa/config/ConfigTransformer.java
+++ b/config/src/main/java/com/yahoo/vespa/config/ConfigTransformer.java
@@ -11,7 +11,7 @@ import static com.yahoo.vespa.config.ConfigPayloadApplier.IdentityPathAcquirer;
/**
* A utility class that can be used to transform config from one format to another.
*
- * @author lulf, hmusum, tonyv
+ * @author lulf, hmusum, Tony Vaagenes
* @since 5.1.6
*/
public class ConfigTransformer<T extends ConfigInstance> {
diff --git a/config/src/main/java/com/yahoo/vespa/config/GetConfigRequest.java b/config/src/main/java/com/yahoo/vespa/config/GetConfigRequest.java
index 8021fa95514..c5eeab74157 100644
--- a/config/src/main/java/com/yahoo/vespa/config/GetConfigRequest.java
+++ b/config/src/main/java/com/yahoo/vespa/config/GetConfigRequest.java
@@ -7,10 +7,8 @@ import java.util.Optional;
/**
* Interface for getConfig requests.
- * @author lulf
- * @since 5.3
+ * @author Ulf Lilleengen
*/
-
public interface GetConfigRequest {
/**
@@ -22,6 +20,7 @@ public interface GetConfigRequest {
/**
* The def file contents in the request, or empty array if not sent/not supported
+ *
* @return the contents (payload) of the def schema
*/
DefContent getDefContent();
@@ -33,7 +32,9 @@ public interface GetConfigRequest {
/**
* Whether or not the config can be retrieved from or stored in a cache.
+ *
* @return true if content should _not_ be cached, false if it should.
*/
boolean noCache();
+
}
diff --git a/config/src/main/java/com/yahoo/vespa/config/RawConfig.java b/config/src/main/java/com/yahoo/vespa/config/RawConfig.java
index ae4431e5195..96908f055f1 100755
--- a/config/src/main/java/com/yahoo/vespa/config/RawConfig.java
+++ b/config/src/main/java/com/yahoo/vespa/config/RawConfig.java
@@ -35,6 +35,7 @@ public class RawConfig extends ConfigInstance {
/**
* Constructor for an empty config (not yet resolved).
+ *
* @param key The ConfigKey
* @param defMd5 The md5 sum of the .def-file.
*/
@@ -69,6 +70,7 @@ public class RawConfig extends ConfigInstance {
/**
* Creates a new Config from the given request, with the values in the response parameters.
+ *
* @param req a {@link JRTClientConfigRequest}
*/
public static RawConfig createFromResponseParameters(JRTClientConfigRequest req) {
@@ -85,6 +87,7 @@ public class RawConfig extends ConfigInstance {
/**
* Creates a new Config from the given request, with the values in the response parameters.
+ *
* @param req a {@link JRTClientConfigRequest}
*/
public static RawConfig createFromServerRequest(JRTServerConfigRequest req) {
@@ -116,6 +119,8 @@ public class RawConfig extends ConfigInstance {
public void setGeneration(long generation) { this.generation = generation; }
+ public void setInternalRedeploy(boolean internalRedeploy) { this.internalRedeploy = internalRedeploy; }
+
/**
* Returns whether this config generation was created by a system internal redeploy, not an
* application package change.
@@ -133,7 +138,7 @@ public class RawConfig extends ConfigInstance {
/**
* Returns true if this config is equal to the config (same payload md5) in the given request.
*
- * @param req The request for which to compare config payload with this config.
+ * @param req the request for which to compare config payload with this config.
* @return true if this config is equal to the config in the given request.
*/
public boolean hasEqualConfig(JRTServerConfigRequest req) {
@@ -143,7 +148,7 @@ public class RawConfig extends ConfigInstance {
/**
* Returns true if this config has a more recent generation than the config in the given request.
*
- * @param req The request for which to compare generation with this config.
+ * @param req the request for which to compare generation with this config.
* @return true if this config has a more recent generation than the config in the given request.
*/
public boolean hasNewerGeneration(JRTServerConfigRequest req) {
@@ -158,6 +163,7 @@ public class RawConfig extends ConfigInstance {
return (errorCode() != 0);
}
+ @Override
public boolean equals(Object o) {
if (o == this) {
return true;
@@ -184,6 +190,7 @@ public class RawConfig extends ConfigInstance {
}
}
+ @Override
public int hashCode() {
int hash = 17;
if (key != null) {
@@ -203,6 +210,7 @@ public class RawConfig extends ConfigInstance {
return hash;
}
+ @Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(key.getNamespace()).append(".").append(key.getName());
@@ -222,4 +230,5 @@ public class RawConfig extends ConfigInstance {
public List<String> getDefContent() {
return defContent;
}
+
}
diff --git a/config/src/main/java/com/yahoo/vespa/config/protocol/JRTConfigRequest.java b/config/src/main/java/com/yahoo/vespa/config/protocol/JRTConfigRequest.java
index 46b3651afd7..26fa41c19c7 100644
--- a/config/src/main/java/com/yahoo/vespa/config/protocol/JRTConfigRequest.java
+++ b/config/src/main/java/com/yahoo/vespa/config/protocol/JRTConfigRequest.java
@@ -8,11 +8,14 @@ import java.util.Optional;
/**
* Common interface for jrt config requests available both at server and client.
+ *
* @author Ulf Lilleengen
*/
public interface JRTConfigRequest {
+
/**
* Get the config key of the config request.
+ *
* @return a {@link ConfigKey}.
*/
ConfigKey<?> getConfigKey();
@@ -20,18 +23,21 @@ public interface JRTConfigRequest {
/**
* Perform request parameter validation of this config request. This method should be called before fetching
* any kind of config protocol-specific parameter.
+ *
* @return true if valid, false if not.
*/
boolean validateParameters();
/**
* Get the config md5 of the config request. Return an empty string if no response has been returned.
+ *
* @return a config md5.
*/
String getRequestConfigMd5();
/**
* Get the generation of the requested config. If none has been given, 0 should be returned.
+ *
* @return the generation in the request.
*/
long getRequestGeneration();
@@ -39,42 +45,49 @@ public interface JRTConfigRequest {
/**
* Get the JRT request object for this config request.
* TODO: This method leaks the internal jrt stuff :(
+ *
* @return a {@link Request} object.
*/
Request getRequest();
/**
* Get a short hand description of this request.
+ *
* @return a short description
*/
String getShortDescription();
/**
* Get the error code of this request
+ *
* @return the error code as defined in {@link com.yahoo.vespa.config.ErrorCode}.
*/
int errorCode();
/**
* Return the error message of this request, mostly corresponding to the {@link com.yahoo.vespa.config.ErrorCode}.
+ *
* @return the error message.
*/
String errorMessage();
/**
* Get the server timeout of this request.
+ *
* @return the timeout given to the server
*/
long getTimeout();
/**
* Get the config protocol version
+ *
* @return a protocol version number.
*/
long getProtocolVersion();
/**
* Get the wanted generation for this request.
+ *
* @return a generation that client would like.
*/
long getWantedGeneration();
@@ -87,7 +100,9 @@ public interface JRTConfigRequest {
/**
* Get the Vespa version of the client that initiated the request
+ *
* @return Vespa version of the client
*/
Optional<VespaVersion> getVespaVersion();
+
}
diff --git a/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeRequestData.java b/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeRequestData.java
index 65e50acb72d..5e88c4cb146 100644
--- a/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeRequestData.java
+++ b/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeRequestData.java
@@ -12,11 +12,10 @@ import com.yahoo.vespa.config.ConfigKey;
import java.util.Optional;
/**
- * Contains slime request data objects. Provides methods for reading various fields from slime request data. All
- * data is read lazily.
+ * Contains slime request data objects. Provides methods for reading various fields from slime request data.
+ * All data is read lazily.
*
-* @author lulf
-* @since 5.18
+* @author Ulf Lilleengen
*/
class SlimeRequestData {
diff --git a/config/src/vespa/config/frt/frtconfigresponsev3.cpp b/config/src/vespa/config/frt/frtconfigresponsev3.cpp
index 379ebbd1803..405391d99b6 100644
--- a/config/src/vespa/config/frt/frtconfigresponsev3.cpp
+++ b/config/src/vespa/config/frt/frtconfigresponsev3.cpp
@@ -63,7 +63,7 @@ FRTConfigResponseV3::readConfigValue() const
if (consumedSize == 0) {
std::string json(make_json(*payloadData, true));
LOG(error, "Error decoding JSON. Consumed size: %lu, uncompressed size: %u, compression type: %s, assumed uncompressed size(%u), compressed size: %u, slime(%s)", consumedSize, data.size, compressionTypeToString(info.compressionType).c_str(), info.uncompressedSize, ((*_returnValues)[1]._data._len), json.c_str());
- assert(false);
+ LOG_ABORT("Error decoding JSON");
}
}
if (LOG_WOULD_LOG(spam)) {
diff --git a/config/src/vespa/config/print/fileconfigformatter.cpp b/config/src/vespa/config/print/fileconfigformatter.cpp
index 507e1440bae..e4071474760 100644
--- a/config/src/vespa/config/print/fileconfigformatter.cpp
+++ b/config/src/vespa/config/print/fileconfigformatter.cpp
@@ -7,6 +7,9 @@
#include <cmath>
#include <vector>
+#include <vespa/log/log.h>
+LOG_SETUP(".config.print.fileconfigformatter");
+
using namespace vespalib::slime::convenience;
using vespalib::slime::ArrayTraverser;
@@ -128,7 +131,7 @@ struct ConfigEncoder : public ArrayTraverser,
case vespalib::slime::OBJECT::ID: return encodeOBJECT(inspector);
case vespalib::slime::NIX::ID: return;
}
- abort(); // should not be reached
+ LOG_ABORT("should not be reached"); // should not be reached
}
void entry(size_t idx, const Inspector &inspector) override;
void field(const Memory &symbol_name, const Inspector &inspector) override;
diff --git a/configd/src/apps/sentinel/config-handler.cpp b/configd/src/apps/sentinel/config-handler.cpp
index 67216954f51..325de3c232c 100644
--- a/configd/src/apps/sentinel/config-handler.cpp
+++ b/configd/src/apps/sentinel/config-handler.cpp
@@ -133,6 +133,9 @@ ConfigHandler::terminate()
for (int retry = 0; retry < 10 && doWork(); ++retry) {
LOG(warning, "some services refuse to terminate cleanly, sending KILL");
terminateServices(false, true);
+ tv.tv_sec = 0;
+ tv.tv_usec = 200000;
+ select(0, nullptr, nullptr, nullptr, &tv);
}
return !doWork();
}
diff --git a/configd/src/apps/sentinel/service.cpp b/configd/src/apps/sentinel/service.cpp
index 5633c356bc7..e38328975dc 100644
--- a/configd/src/apps/sentinel/service.cpp
+++ b/configd/src/apps/sentinel/service.cpp
@@ -106,27 +106,36 @@ Service::terminate(bool catchable, bool dumpState)
runPreShutdownCommand();
LOG(debug, "%s: terminate(%s)", name().c_str(), catchable ? "cleanly" : "NOW");
resetRestartPenalty();
+ kill(_pid, SIGCONT); // if it was stopped for some reason
if (catchable) {
- setState(TERMINATING);
- int ret = kill(_pid, SIGTERM);
- LOG(debug, "%s: kill -SIGTERM %d: %s", name().c_str(), (int)_pid,
- ret == 0 ? "OK" : strerror(errno));
- return ret;
+ if (_state != TERMINATING) {
+ int ret = kill(_pid, SIGTERM);
+ if (ret == 0) {
+ setState(TERMINATING);
+ } else {
+ LOG(warning, "%s: kill -SIGTERM %d failed: %s",
+ name().c_str(), (int)_pid, strerror(errno));
+ }
+ return ret;
+ }
+ // already sent SIGTERM
+ return 0;
} else {
if (dumpState && _state != KILLING) {
- vespalib::string pstackCmd = make_string("pstack %d > %s/%s.pstack.%d",
+ vespalib::string pstackCmd = make_string("pstack %d > %s/%s.pstack.%d 2>&1",
_pid, getVespaTempDir().c_str(), name().c_str(), _pid);
- LOG(info, "%s:%d failed to stop. Stack dumped at %s", name().c_str(), _pid, pstackCmd.c_str());
+ LOG(info, "%s:%d failed to stop. Stack dumping with %s", name().c_str(), _pid, pstackCmd.c_str());
int pstackRet = system(pstackCmd.c_str());
if (pstackRet != 0) {
LOG(warning, "'%s' failed with return value %d", pstackCmd.c_str(), pstackRet);
}
}
setState(KILLING);
- kill(_pid, SIGCONT); // if it was stopped for some reason
int ret = kill(_pid, SIGKILL);
- LOG(debug, "%s: kill -SIGKILL %d: %s", name().c_str(), (int)_pid,
- ret == 0 ? "OK" : strerror(errno));
+ if (ret != 0) {
+ LOG(warning, "%s: kill -SIGKILL %d failed: %s",
+ name().c_str(), (int)_pid, strerror(errno));
+ }
return ret;
}
}
diff --git a/configdefinitions/src/vespa/configserver.def b/configdefinitions/src/vespa/configserver.def
index bf4c9599f4a..70df8ce5164 100644
--- a/configdefinitions/src/vespa/configserver.def
+++ b/configdefinitions/src/vespa/configserver.def
@@ -55,6 +55,16 @@ ztsUrl string default=""
nodeAdminInContainer bool default=true
# Maintainers
-maintainerIntervalMinutes int default=30
+maintainerIntervalMinutes int default=60
# TODO: Default set to a high value (1 year) => maintainer will not run, change when maintainer verified out in prod
tenantsMaintainerIntervalMinutes int default=525600
+
+# Bootstrapping
+# How long bootstrapping can take before giving up (in seconds)
+maxDurationOfBootstrap long default=7200
+# How long to sleep before redeploying again if it fails (in seconds)
+sleepTimeWhenRedeployingFails long default=30
+
+# Feature Flags (poor man's feature flags, to be overridden in configserver-config.xml if needed)
+deleteApplicationLegacy bool default=false
+buildMinimalSetOfConfigModels bool default=true
diff --git a/configserver/pom.xml b/configserver/pom.xml
index 97149d00746..2aeecb1ba2f 100644
--- a/configserver/pom.xml
+++ b/configserver/pom.xml
@@ -23,6 +23,11 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>com.github.tomakehurst</groupId>
+ <artifactId>wiremock-standalone</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>com.yahoo.vespa</groupId>
<artifactId>testutil</artifactId>
<version>${project.version}</version>
@@ -96,6 +101,13 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <!-- To get all necessary test deps. -->
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>container-test</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>com.yahoo.vespa</groupId>
<artifactId>vespa_jersey2</artifactId>
<version>${project.version}</version>
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 93e434c3b87..8a02383ad0b 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
@@ -6,7 +6,6 @@ import com.google.inject.Inject;
import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.component.Version;
import com.yahoo.component.Vtag;
-import com.yahoo.concurrent.DaemonThreadFactory;
import com.yahoo.config.FileReference;
import com.yahoo.config.application.api.ApplicationFile;
import com.yahoo.config.application.api.ApplicationMetaData;
@@ -15,7 +14,10 @@ import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.HostFilter;
import com.yahoo.config.provision.Provisioner;
+import com.yahoo.config.provision.RegionName;
+import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.TenantName;
+import com.yahoo.config.provision.Zone;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.io.IOUtils;
import com.yahoo.log.LogLevel;
@@ -41,6 +43,7 @@ import com.yahoo.vespa.config.server.session.LocalSession;
import com.yahoo.vespa.config.server.session.LocalSessionRepo;
import com.yahoo.vespa.config.server.session.PrepareParams;
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.SessionFactory;
import com.yahoo.vespa.config.server.session.SilentDeployLogger;
@@ -58,17 +61,10 @@ import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
@@ -93,7 +89,6 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
private final Clock clock;
private final DeployLogger logger = new SilentDeployLogger();
private final ConfigserverConfig configserverConfig;
- private final Environment environment;
private final FileDistributionStatus fileDistributionStatus;
@Inject
@@ -110,16 +105,16 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
public ApplicationRepository(TenantRepository tenantRepository,
Provisioner hostProvisioner,
Clock clock) {
- this(tenantRepository, new ConfigConvergenceChecker(), hostProvisioner, clock);
+ this(tenantRepository, hostProvisioner, clock, new ConfigserverConfig(new ConfigserverConfig.Builder()));
}
+ // For testing
public ApplicationRepository(TenantRepository tenantRepository,
- ConfigConvergenceChecker convergenceChecker,
Provisioner hostProvisioner,
- Clock clock) {
- this(tenantRepository, Optional.of(hostProvisioner),
- convergenceChecker, new HttpProxy(new SimpleHttpFetcher()),
- new ConfigserverConfig(new ConfigserverConfig.Builder()), clock, new FileDistributionStatus());
+ Clock clock,
+ ConfigserverConfig configserverConfig) {
+ this(tenantRepository, Optional.of(hostProvisioner), new ConfigConvergenceChecker(), new HttpProxy(new SimpleHttpFetcher()),
+ configserverConfig, clock, new FileDistributionStatus());
}
private ApplicationRepository(TenantRepository tenantRepository,
@@ -135,7 +130,6 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
this.httpProxy = httpProxy;
this.clock = clock;
this.configserverConfig = configserverConfig;
- this.environment = Environment.from(configserverConfig.environment());
this.fileDistributionStatus = fileDistributionStatus;
}
@@ -198,8 +192,27 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
* was not present for this id (meaning it either is not active or active on another
* node in the config server cluster)
*/
+ @Override
public Optional<com.yahoo.config.provision.Deployment> deployFromLocalActive(ApplicationId application) {
- return deployFromLocalActive(application, Duration.ofSeconds(configserverConfig.zookeeper().barrierTimeout()).plus(Duration.ofSeconds(5)));
+ return deployFromLocalActive(application, false);
+ }
+
+ /**
+ * Creates a new deployment from the active application, if available.
+ * This is used for system internal redeployments, not on application package changes.
+ *
+ * @param application the active application to be redeployed
+ * @param bootstrap the deployment is done when bootstrapping
+ * @return a new deployment from the local active, or empty if a local active application
+ * was not present for this id (meaning it either is not active or active on another
+ * node in the config server cluster)
+ */
+ @Override
+ public Optional<com.yahoo.config.provision.Deployment> deployFromLocalActive(ApplicationId application,
+ boolean bootstrap) {
+ return deployFromLocalActive(application,
+ Duration.ofSeconds(configserverConfig.zookeeper().barrierTimeout()).plus(Duration.ofSeconds(5)),
+ bootstrap);
}
/**
@@ -208,12 +221,15 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
*
* @param application the active application to be redeployed
* @param timeout the timeout to use for each individual deployment operation
+ * @param bootstrap the deployment is done when bootstrapping
* @return a new deployment from the local active, or empty if a local active application
* was not present for this id (meaning it either is not active or active on another
* node in the config server cluster)
*/
@Override
- public Optional<com.yahoo.config.provision.Deployment> deployFromLocalActive(ApplicationId application, Duration timeout) {
+ public Optional<com.yahoo.config.provision.Deployment> deployFromLocalActive(ApplicationId application,
+ Duration timeout,
+ boolean bootstrap) {
Tenant tenant = tenantRepository.getTenant(application.tenant());
if (tenant == null) return Optional.empty();
LocalSession activeSession = getActiveSession(tenant, application);
@@ -224,10 +240,20 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
// Keep manually deployed tenant applications on the latest version, don't change version otherwise
// TODO: Remove this and always use version from session once controller starts upgrading manual deployments
- Version version = decideVersion(application, environment, newSession.getVespaVersion());
+ Version version = decideVersion(application, zone().environment(), newSession.getVespaVersion(), bootstrap);
return Optional.of(Deployment.unprepared(newSession, this, hostProvisioner, tenant, timeout, clock,
- false /* don't validate as this is already deployed */, version));
+ false /* don't validate as this is already deployed */, version,
+ bootstrap));
+ }
+
+ @Override
+ public Optional<Instant> lastDeployTime(ApplicationId application) {
+ Tenant tenant = tenantRepository.getTenant(application.tenant());
+ if (tenant == null) return Optional.empty();
+ LocalSession activeSession = getActiveSession(tenant, application);
+ if (activeSession == null) return Optional.empty();
+ return Optional.of(Instant.ofEpochSecond(activeSession.getCreateTime()));
}
public ApplicationId activate(Tenant tenant,
@@ -244,23 +270,90 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
}
private Deployment deployFromPreparedSession(LocalSession session, Tenant tenant, Duration timeout) {
- return Deployment.prepared(session, this, hostProvisioner, tenant, timeout, clock);
+ return Deployment.prepared(session, this, hostProvisioner, tenant, timeout, clock, false);
}
// ---------------- Application operations ----------------------------------------------------------------
/**
- * Removes a previously deployed application
+ * Deletes an application
+ *
+ * @return true if the application was found and deleted, false if it was not present
+ * @throws RuntimeException if the delete transaction fails. This method is exception safe.
+ */
+ public boolean delete(ApplicationId applicationId) {
+ List<String> hostedZonesToUseDeleteApplication =
+ Arrays.asList("dev.us-east-1", "dev.corp-us-east-1", "test.us-east-1",
+ "prod.corp-us-east-1", "prod.aws-us-east-1a", "prod.aws-us-west-1b");
+ boolean useDeleteApplicationLegacyInThisZone = !hostedZonesToUseDeleteApplication.contains(zone().toString());
+
+ // TODO: Use deleteApplication() in all zones
+ if (configserverConfig.deleteApplicationLegacy() ||
+ (configserverConfig.hostedVespa() && zone().system() == SystemName.main &&
+ useDeleteApplicationLegacyInThisZone)) {
+ return deleteApplicationLegacy(applicationId);
+ } else {
+ return deleteApplication(applicationId);
+ }
+ }
+
+ /**
+ * Deletes an application
+ *
+ * @return true if the application was found and deleted, false if it was not present
+ * @throws RuntimeException if the delete transaction fails. This method is exception safe.
+ */
+ boolean deleteApplication(ApplicationId applicationId) {
+ Tenant tenant = tenantRepository.getTenant(applicationId.tenant());
+ if (tenant == null) return false;
+
+ TenantApplications tenantApplications = tenant.getApplicationRepo();
+ if (!tenantApplications.listApplications().contains(applicationId)) return false;
+
+ // Deleting an application is done by deleting the remote session and waiting
+ // until the config server where the deployment happened picks it up and deletes
+ // the local session
+ long sessionId = tenantApplications.getSessionIdForApplication(applicationId);
+ RemoteSession remoteSession = getRemoteSession(tenant, sessionId);
+ remoteSession.createDeleteTransaction().commit();
+
+ log.log(LogLevel.INFO, TenantRepository.logPre(applicationId) + "Waiting for session " + sessionId + " to be deleted");
+ // TODO: Add support for timeout in request
+ Duration waitTime = Duration.ofSeconds(60);
+ if (localSessionHasBeenDeleted(applicationId, sessionId, waitTime)) {
+ log.log(LogLevel.INFO, TenantRepository.logPre(applicationId) + "Session " + sessionId + " deleted");
+ } else {
+ log.log(LogLevel.ERROR, TenantRepository.logPre(applicationId) + "Session " + sessionId + " was not deleted (waited " + waitTime + ")");
+ return false;
+ }
+
+ NestedTransaction transaction = new NestedTransaction();
+ transaction.add(new Rotations(tenant.getCurator(), tenant.getPath()).delete(applicationId)); // TODO: Not unit tested
+ // (When rotations are updated in zk, we need to redeploy the zone app, on the right config server
+ // this is done asynchronously in application maintenance by the node repository)
+ transaction.add(tenantApplications.deleteApplication(applicationId));
+
+ hostProvisioner.ifPresent(provisioner -> provisioner.remove(transaction, applicationId));
+ transaction.onCommitted(() -> log.log(LogLevel.INFO, "Deleted " + applicationId));
+ transaction.commit();
+
+ return true;
+ }
+
+ /**
+ * Deletes an application the legacy way (if there is more than one config server, the call needs to be done
+ * on the config server the application was deployed)
*
- * @return true if the application was found and removed, false if it was not present
- * @throws RuntimeException if the remove transaction fails. This method is exception safe.
+ * @return true if the application was found and deleted, false if it was not present
+ * @throws RuntimeException if the delete transaction fails. This method is exception safe.
*/
- public boolean remove(ApplicationId applicationId) {
+ // TODO: Remove this method, use delete(ApplicationId) instead
+ boolean deleteApplicationLegacy(ApplicationId applicationId) {
Optional<Tenant> owner = Optional.ofNullable(tenantRepository.getTenant(applicationId.tenant()));
- if ( ! owner.isPresent()) return false;
+ if (!owner.isPresent()) return false;
TenantApplications tenantApplications = owner.get().getApplicationRepo();
- if ( ! tenantApplications.listApplications().contains(applicationId)) return false;
+ if (!tenantApplications.listApplications().contains(applicationId)) return false;
// TODO: Push lookup logic down
long sessionId = tenantApplications.getSessionIdForApplication(applicationId);
@@ -275,7 +368,6 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
transaction.add(new Rotations(owner.get().getCurator(), owner.get().getPath()).delete(applicationId)); // TODO: Not unit tested
// (When rotations are updated in zk, we need to redeploy the zone app, on the right config server
// this is done asynchronously in application maintenance by the node repository)
-
transaction.add(tenantApplications.deleteApplication(applicationId));
hostProvisioner.ifPresent(provisioner -> provisioner.remove(transaction, applicationId));
@@ -360,7 +452,7 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
}
}
- private Set<ApplicationId> listApplications() {
+ Set<ApplicationId> listApplications() {
return tenantRepository.getAllTenants().stream()
.flatMap(tenant -> tenant.getApplicationRepo().listApplications().stream())
.collect(Collectors.toSet());
@@ -376,14 +468,25 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
}
}
+ private boolean localSessionHasBeenDeleted(ApplicationId applicationId, long sessionId, Duration waitTime) {
+ RemoteSessionRepo remoteSessionRepo = tenantRepository.getTenant(applicationId.tenant()).getRemoteSessionRepo();
+ Instant end = Instant.now().plus(waitTime);
+ do {
+ if (remoteSessionRepo.getSession(sessionId) == null) return true;
+ try { Thread.sleep(10); } catch (InterruptedException e) { /* ignored */}
+ } while (Instant.now().isBefore(end));
+
+ return false;
+ }
+
// ---------------- Convergence ----------------------------------------------------------------
- public HttpResponse checkServiceForConfigConvergence(ApplicationId applicationId, String hostAndPort, URI uri) {
- return convergeChecker.checkService(getApplication(applicationId), hostAndPort, uri);
+ public HttpResponse checkServiceForConfigConvergence(ApplicationId applicationId, String hostAndPort, URI uri, Duration timeout) {
+ return convergeChecker.checkService(getApplication(applicationId), hostAndPort, uri, timeout);
}
- public HttpResponse servicesToCheckForConfigConvergence(ApplicationId applicationId, URI uri) {
- return convergeChecker.servicesToCheck(getApplication(applicationId), uri);
+ public HttpResponse servicesToCheckForConfigConvergence(ApplicationId applicationId, URI uri, Duration timeout) {
+ return convergeChecker.servicesToCheck(getApplication(applicationId), uri, timeout);
}
// ---------------- Session operations ----------------------------------------------------------------
@@ -410,7 +513,7 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
public void validateThatRemoteSessionIsPrepared(Tenant tenant, long sessionId) {
RemoteSession session = getRemoteSession(tenant, sessionId);
- if (!Session.Status.PREPARE.equals(session.getStatus()))
+ if ( ! Session.Status.PREPARE.equals(session.getStatus()))
throw new IllegalStateException("Session not prepared: " + sessionId);
}
@@ -447,6 +550,19 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
return session.getSessionId();
}
+ public void deleteExpiredLocalSessions() {
+ listApplications().forEach(app -> tenantRepository.getTenant(app.tenant()).getLocalSessionRepo().purgeOldSessions());
+ }
+
+ public int deleteExpiredRemoteSessions(Duration expiryTime) {
+ return listApplications()
+ .stream()
+ .map(app -> tenantRepository.getTenant(app.tenant()).getRemoteSessionRepo()
+ // TODO: Delete in all zones
+ .deleteExpiredSessions(expiryTime, zone().system() == SystemName.cd))
+ .mapToInt(i -> i)
+ .sum();
+ }
// ---------------- Tenant operations ----------------------------------------------------------------
@@ -491,6 +607,10 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
return getLocalSession(tenant, sessionId).getMetaData();
}
+ public ConfigserverConfig configserverConfig() {
+ return configserverConfig;
+ }
+
private void validateThatLocalSessionIsNotActive(Tenant tenant, long sessionId) {
LocalSession session = getLocalSession(tenant, sessionId);
if (Session.Status.ACTIVATE.equals(session.getStatus())) {
@@ -556,26 +676,6 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
}
}
- void redeployAllApplications() throws InterruptedException {
- ExecutorService executor = Executors.newFixedThreadPool(configserverConfig.numParallelTenantLoaders(),
- new DaemonThreadFactory("redeploy apps"));
- // Keep track of deployment per application
- Map<ApplicationId, Future<?>> futures = new HashMap<>();
- tenantRepository.getAllTenants()
- .forEach(tenant -> listApplicationIds(tenant)
- .forEach(appId -> deployFromLocalActive(appId).ifPresent(
- deployment -> futures.put(appId,executor.submit(deployment::activate)))));
- for (Map.Entry<ApplicationId, Future<?>> f : futures.entrySet()) {
- try {
- f.getValue().get();
- } catch (ExecutionException e) {
- throw new RuntimeException("Redeploying of " + f.getKey() + " failed", e);
- }
- }
- executor.shutdown();
- executor.awaitTermination(365, TimeUnit.DAYS); // Timeout should never happen
- }
-
private LocalSession getExistingSession(Tenant tenant, ApplicationId applicationId) {
TenantApplications applicationRepo = tenant.getApplicationRepo();
return getLocalSession(tenant, applicationRepo.getSessionIdForApplication(applicationId));
@@ -605,9 +705,10 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
}
/** Returns version to use when deploying application in given environment */
- static Version decideVersion(ApplicationId application, Environment environment, Version targetVersion) {
+ static Version decideVersion(ApplicationId application, Environment environment, Version targetVersion, boolean bootstrap) {
if (environment.isManuallyDeployed() &&
- !"hosted-vespa".equals(application.tenant().value())) { // Never change version of system applications
+ !"hosted-vespa".equals(application.tenant().value()) && // Never change version of system applications
+ !bootstrap) { // Do not use current version when bootstrapping config server
return Vtag.currentVersion;
}
return targetVersion;
@@ -619,4 +720,10 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
return deployLog;
}
+ public Zone zone() {
+ return new Zone(SystemName.from(configserverConfig.system()),
+ Environment.from(configserverConfig.environment()),
+ RegionName.from(configserverConfig.region()));
+ }
+
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ConfigServerBootstrap.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ConfigServerBootstrap.java
index 916fde97e35..46be61964ce 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/ConfigServerBootstrap.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ConfigServerBootstrap.java
@@ -2,8 +2,11 @@
package com.yahoo.vespa.config.server;
import com.google.inject.Inject;
+import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.component.AbstractComponent;
import com.yahoo.concurrent.DaemonThreadFactory;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.Deployment;
import com.yahoo.container.handler.VipStatus;
import com.yahoo.container.jdisc.state.StateMonitor;
import com.yahoo.log.LogLevel;
@@ -12,8 +15,16 @@ import com.yahoo.vespa.config.server.version.VersionState;
import java.time.Duration;
import java.time.Instant;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
/**
* Main component that bootstraps and starts config server threads.
@@ -28,15 +39,22 @@ import java.util.concurrent.Executors;
public class ConfigServerBootstrap extends AbstractComponent implements Runnable {
private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(ConfigServerBootstrap.class.getName());
- private static final ExecutorService rpcServerExecutor = Executors.newSingleThreadExecutor(new DaemonThreadFactory("config server RPC server"));
private static final String vipStatusClusterIdentifier = "configserver";
+ enum MainThread {START, DO_NOT_START}
+ enum RedeployingApplicationsFails {EXIT_JVM, CONTINUE}
+
private final ApplicationRepository applicationRepository;
private final RpcServer server;
private final Thread serverThread;
private final VersionState versionState;
private final StateMonitor stateMonitor;
private final VipStatus vipStatus;
+ private final ConfigserverConfig configserverConfig;
+ private final Duration maxDurationOfRedeployment;
+ private final Duration sleepTimeWhenRedeployingFails;
+ private final RedeployingApplicationsFails exitIfRedeployingApplicationsFails;
+ private final ExecutorService rpcServerExecutor;
// The tenants object is injected so that all initial requests handlers are
// added to the rpc server before it starts answering rpc requests.
@@ -44,20 +62,26 @@ public class ConfigServerBootstrap extends AbstractComponent implements Runnable
@Inject
public ConfigServerBootstrap(ApplicationRepository applicationRepository, RpcServer server,
VersionState versionState, StateMonitor stateMonitor, VipStatus vipStatus) {
- this(applicationRepository, server, versionState, stateMonitor, vipStatus, true);
+ this(applicationRepository, server, versionState, stateMonitor, vipStatus, MainThread.START, RedeployingApplicationsFails.EXIT_JVM);
}
// For testing only
ConfigServerBootstrap(ApplicationRepository applicationRepository, RpcServer server, VersionState versionState,
- StateMonitor stateMonitor, VipStatus vipStatus, boolean startMainThread) {
+ StateMonitor stateMonitor, VipStatus vipStatus, MainThread mainThread,
+ RedeployingApplicationsFails exitIfRedeployingApplicationsFails) {
this.applicationRepository = applicationRepository;
this.server = server;
this.versionState = versionState;
this.stateMonitor = stateMonitor;
this.serverThread = new Thread(this, "configserver main");
this.vipStatus = vipStatus;
+ this.configserverConfig = applicationRepository.configserverConfig();
+ this.maxDurationOfRedeployment = Duration.ofSeconds(configserverConfig.maxDurationOfBootstrap());
+ this.sleepTimeWhenRedeployingFails = Duration.ofSeconds(configserverConfig.sleepTimeWhenRedeployingFails());
+ this.exitIfRedeployingApplicationsFails = exitIfRedeployingApplicationsFails;
+ rpcServerExecutor = Executors.newSingleThreadExecutor(new DaemonThreadFactory("config server RPC server"));
initializing(); // Initially take server out of rotation
- if (startMainThread)
+ if (mainThread == MainThread.START)
start();
}
@@ -80,11 +104,15 @@ public class ConfigServerBootstrap extends AbstractComponent implements Runnable
log.log(LogLevel.INFO, "Configserver upgrading from " + versionState.storedVersion() + " to "
+ versionState.currentVersion() + ". Redeploying all applications");
try {
- applicationRepository.redeployAllApplications();
+ if ( ! redeployAllApplications()) {
+ redeployingApplicationsFailed();
+ return; // Status will not be set to 'up' since we return here
+ }
versionState.saveNewVersion();
log.log(LogLevel.INFO, "All applications redeployed successfully");
} catch (Exception e) {
log.log(LogLevel.ERROR, "Redeployment of applications failed", e);
+ redeployingApplicationsFailed();
return; // Status will not be set to 'up' since we return here
}
}
@@ -144,5 +172,56 @@ public class ConfigServerBootstrap extends AbstractComponent implements Runnable
log.log(LogLevel.INFO, "RPC server started");
}
+ private void redeployingApplicationsFailed() {
+ if (exitIfRedeployingApplicationsFails == RedeployingApplicationsFails.EXIT_JVM) System.exit(1);
+ }
+
+ private boolean redeployAllApplications() throws InterruptedException {
+ Instant end = Instant.now().plus(maxDurationOfRedeployment);
+ Set<ApplicationId> applicationsNotRedeployed = applicationRepository.listApplications();
+ do {
+ applicationsNotRedeployed = redeployApplications(applicationsNotRedeployed);
+ if ( ! applicationsNotRedeployed.isEmpty()) {
+ Thread.sleep(sleepTimeWhenRedeployingFails.toMillis());
+ }
+ } while ( ! applicationsNotRedeployed.isEmpty() && Instant.now().isBefore(end));
+
+ if ( ! applicationsNotRedeployed.isEmpty()) {
+ log.log(LogLevel.ERROR, "Redeploying applications not finished after " + maxDurationOfRedeployment +
+ ", exiting, applications that failed redeployment: " + applicationsNotRedeployed);
+ return false;
+ }
+ return true;
+ }
+
+ // Returns the set of applications that failed to redeploy
+ private Set<ApplicationId> redeployApplications(Set<ApplicationId> applicationIds) throws InterruptedException {
+ ExecutorService executor = Executors.newFixedThreadPool(configserverConfig.numParallelTenantLoaders(),
+ new DaemonThreadFactory("redeploy apps"));
+ // Keep track of deployment per application
+ Map<ApplicationId, Future<?>> futures = new HashMap<>();
+ Set<ApplicationId> failedDeployments = new HashSet<>();
+
+ for (ApplicationId appId : applicationIds) {
+ Optional<Deployment> deploymentOptional = applicationRepository.deployFromLocalActive(appId, true /* bootstrap */);
+ if ( ! deploymentOptional.isPresent()) continue;
+
+ futures.put(appId, executor.submit(deploymentOptional.get()::activate));
+ }
+
+ for (Map.Entry<ApplicationId, Future<?>> f : futures.entrySet()) {
+ try {
+ f.getValue().get();
+ } catch (ExecutionException e) {
+ ApplicationId app = f.getKey();
+ log.log(LogLevel.WARNING, "Redeploying " + app + " failed, will retry", e);
+ failedDeployments.add(app);
+ }
+ }
+ executor.shutdown();
+ executor.awaitTermination(365, TimeUnit.DAYS); // Timeout should never happen
+ return failedDeployments;
+ }
+
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ConfigServerDB.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ConfigServerDB.java
index 72a470cf937..152fc47d807 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/ConfigServerDB.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ConfigServerDB.java
@@ -20,6 +20,7 @@ import java.util.List;
* @author Ulf Lilleengen
*/
public class ConfigServerDB {
+
private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(ConfigServerDB.class.getName());
private final File serverDB;
private final ConfigserverConfig configserverConfig;
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ConfigServerSpec.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ConfigServerSpec.java
index 68db01ee0de..b1bcec73aa0 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/ConfigServerSpec.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ConfigServerSpec.java
@@ -7,7 +7,7 @@ import java.util.ArrayList;
import java.util.List;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ConfigServerSpec implements com.yahoo.config.model.api.ConfigServerSpec {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/GlobalComponentRegistry.java b/configserver/src/main/java/com/yahoo/vespa/config/server/GlobalComponentRegistry.java
index 6828204b17c..e7f048c28e8 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/GlobalComponentRegistry.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/GlobalComponentRegistry.java
@@ -39,5 +39,6 @@ public interface GlobalComponentRegistry {
Optional<Provisioner> getHostProvisioner();
Zone getZone();
Clock getClock();
+ ConfigServerDB getConfigServerDB();
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/InjectedGlobalComponentRegistry.java b/configserver/src/main/java/com/yahoo/vespa/config/server/InjectedGlobalComponentRegistry.java
index 88f54e569df..e7862f29399 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/InjectedGlobalComponentRegistry.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/InjectedGlobalComponentRegistry.java
@@ -40,7 +40,9 @@ public class InjectedGlobalComponentRegistry implements GlobalComponentRegistry
private final HostRegistries hostRegistries;
private final Optional<Provisioner> hostProvisioner;
private final Zone zone;
+ private final ConfigServerDB configServerDB;
+ @SuppressWarnings("WeakerAccess")
@Inject
public InjectedGlobalComponentRegistry(Curator curator,
ConfigCurator configCurator,
@@ -54,7 +56,8 @@ public class InjectedGlobalComponentRegistry implements GlobalComponentRegistry
PermanentApplicationPackage permanentApplicationPackage,
HostRegistries hostRegistries,
HostProvisionerProvider hostProvisionerProvider,
- Zone zone) {
+ Zone zone,
+ ConfigServerDB configServerDB) {
this.curator = curator;
this.configCurator = configCurator;
this.metrics = metrics;
@@ -68,6 +71,7 @@ public class InjectedGlobalComponentRegistry implements GlobalComponentRegistry
this.hostRegistries = hostRegistries;
this.hostProvisioner = hostProvisionerProvider.getHostProvisioner();
this.zone = zone;
+ this.configServerDB = configServerDB;
}
@Override
@@ -107,4 +111,7 @@ public class InjectedGlobalComponentRegistry implements GlobalComponentRegistry
@Override
public Clock getClock() {return Clock.systemUTC();}
+
+ @Override
+ public ConfigServerDB getConfigServerDB() { return configServerDB; }
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ReloadHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ReloadHandler.java
index b493e2c7bc6..93af4b1d593 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/ReloadHandler.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ReloadHandler.java
@@ -9,8 +9,7 @@ import java.util.Set;
/**
* Interface representing a reload handler.
*
- * @author lulf
- * @since 5.1.24
+ * @author Ulf Lilleengen
*/
public interface ReloadHandler {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ReloadListener.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ReloadListener.java
index 59783bf1af5..773a4862033 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/ReloadListener.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ReloadListener.java
@@ -12,10 +12,10 @@ import java.util.Collection;
* reloaded. It only exists because the RpcServer cannot distinguish between a
* successful reload of a new application and a reload of the same application.
*
- * @author lulf
- * @since 5.1
+ * @author Ulf Lilleengen
*/
public interface ReloadListener {
+
/**
* Signal the listener that hosts used by by a particular tenant.
*
@@ -47,4 +47,5 @@ public interface ReloadListener {
* Must be thread-safe.
*/
void applicationRemoved(ApplicationId applicationId);
+
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/SuperModelRequestHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/SuperModelRequestHandler.java
index 9999fb31e77..d5f3a237b9d 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/SuperModelRequestHandler.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/SuperModelRequestHandler.java
@@ -23,8 +23,7 @@ import java.util.Set;
/**
* Handles request for supermodel config.
*
- * @author lulf
- * @since 5.9
+ * @author Ulf Lilleengen
*/
public class SuperModelRequestHandler implements RequestHandler {
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 4978f5f274d..91cb5891e84 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
@@ -9,6 +9,7 @@ import com.yahoo.config.model.api.PortInfo;
import com.yahoo.config.model.api.ServiceInfo;
import com.yahoo.slime.Cursor;
import com.yahoo.vespa.config.server.http.JSONResponse;
+import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.client.proxy.WebResourceFactory;
import javax.ws.rs.GET;
@@ -18,6 +19,7 @@ import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import java.net.URI;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -33,6 +35,7 @@ import java.util.stream.Collectors;
* @author hmusum
*/
public class ConfigConvergenceChecker extends AbstractComponent {
+
private static final String statePath = "/state/v1/";
private static final String configSubPath = "config";
private final static Set<String> serviceTypesToCheck = new HashSet<>(Arrays.asList(
@@ -45,7 +48,6 @@ public class ConfigConvergenceChecker extends AbstractComponent {
));
private final StateApiFactory stateApiFactory;
- private final Client client = ClientBuilder.newClient();
@Inject
public ConfigConvergenceChecker() {
@@ -56,39 +58,36 @@ public class ConfigConvergenceChecker extends AbstractComponent {
this.stateApiFactory = stateApiFactory;
}
- public ServiceListResponse servicesToCheck(Application application, URI uri) {
+ /** Check all services in given application */
+ public ServiceListResponse servicesToCheck(Application application, URI requestUrl, Duration timeoutPerService) {
List<ServiceInfo> servicesToCheck = new ArrayList<>();
application.getModel().getHosts()
.forEach(host -> host.getServices().stream()
.filter(service -> serviceTypesToCheck.contains(service.getServiceType()))
.forEach(service -> getStatePort(service).ifPresent(port -> servicesToCheck.add(service))));
- long currentGeneration = getServiceGeneration(servicesToCheck);
- return new ServiceListResponse(200, servicesToCheck, uri, application.getApplicationGeneration(),
+ long currentGeneration = getServiceGeneration(servicesToCheck, timeoutPerService);
+ return new ServiceListResponse(200, servicesToCheck, requestUrl, application.getApplicationGeneration(),
currentGeneration);
}
- public ServiceResponse checkService(Application application, String hostAndPortToCheck, URI uri) {
+ /** Check service identified by host and port in given application */
+ public ServiceResponse checkService(Application application, String hostAndPortToCheck, URI requestUrl, Duration timeout) {
Long wantedGeneration = application.getApplicationGeneration();
try {
if (! hostInApplication(application, hostAndPortToCheck))
- return ServiceResponse.createHostNotFoundInAppResponse(uri, hostAndPortToCheck, wantedGeneration);
+ return ServiceResponse.createHostNotFoundInAppResponse(requestUrl, hostAndPortToCheck, wantedGeneration);
- long currentGeneration = getServiceGeneration(URI.create("http://" + hostAndPortToCheck));
+ long currentGeneration = getServiceGeneration(URI.create("http://" + hostAndPortToCheck), timeout);
boolean converged = currentGeneration >= wantedGeneration;
- return ServiceResponse.createOkResponse(uri, hostAndPortToCheck, wantedGeneration, currentGeneration, converged);
+ return ServiceResponse.createOkResponse(requestUrl, hostAndPortToCheck, wantedGeneration, currentGeneration, converged);
} catch (ProcessingException e) { // e.g. if we cannot connect to the service to find generation
- return ServiceResponse.createNotFoundResponse(uri, hostAndPortToCheck, wantedGeneration, e.getMessage());
+ return ServiceResponse.createNotFoundResponse(requestUrl, hostAndPortToCheck, wantedGeneration, e.getMessage());
} catch (Exception e) {
- return ServiceResponse.createErrorResponse(uri, hostAndPortToCheck, wantedGeneration, e.getMessage());
+ return ServiceResponse.createErrorResponse(requestUrl, hostAndPortToCheck, wantedGeneration, e.getMessage());
}
}
- @Override
- public void deconstruct() {
- client.close();
- }
-
@Path(statePath)
public interface StateApi {
@Path(configSubPath)
@@ -100,24 +99,8 @@ public class ConfigConvergenceChecker extends AbstractComponent {
StateApi createStateApi(Client client, URI serviceUri);
}
- private static Optional<Integer> getStatePort(ServiceInfo service) {
- return service.getPorts().stream()
- .filter(port -> port.getTags().contains("state"))
- .map(PortInfo::getPort)
- .findFirst();
- }
-
- private static long generationFromContainerState(JsonNode state) {
- return state.get("config").get("generation").asLong();
- }
-
- private static StateApi createStateApi(Client client, URI uri) {
- WebTarget target = client.target(uri);
- return WebResourceFactory.newResource(StateApi.class, target);
- }
-
/** Get service generation for a list of services. Returns the minimum generation of all services */
- private long getServiceGeneration(List<ServiceInfo> services) {
+ private long getServiceGeneration(List<ServiceInfo> services, Duration timeout) {
List<URI> serviceUris = services.stream()
.map(s -> "http://" + s.getHostName() + ":" + getStatePort(s).get())
.map(URI::create)
@@ -125,7 +108,7 @@ public class ConfigConvergenceChecker extends AbstractComponent {
long generation = -1;
for (URI uri : serviceUris) {
try {
- long serviceGeneration = getServiceGeneration(uri);
+ long serviceGeneration = getServiceGeneration(uri, timeout);
if (generation == -1 || serviceGeneration < generation) {
generation = serviceGeneration;
}
@@ -136,9 +119,15 @@ public class ConfigConvergenceChecker extends AbstractComponent {
return generation;
}
- private long getServiceGeneration(URI serviceUri) {
- StateApi state = stateApiFactory.createStateApi(client, serviceUri);
- return generationFromContainerState(state.config());
+ /** Get service generation of service at given URL */
+ private long getServiceGeneration(URI serviceUrl, Duration timeout) {
+ Client client = createClient(timeout);
+ try {
+ StateApi state = stateApiFactory.createStateApi(client, serviceUrl);
+ return generationFromContainerState(state.config());
+ } finally {
+ client.close();
+ }
}
private boolean hostInApplication(Application application, String hostPort) {
@@ -156,6 +145,29 @@ public class ConfigConvergenceChecker extends AbstractComponent {
return false;
}
+ private static Client createClient(Duration timeout) {
+ return ClientBuilder.newBuilder()
+ .property(ClientProperties.CONNECT_TIMEOUT, (int) timeout.toMillis())
+ .property(ClientProperties.READ_TIMEOUT, (int) timeout.toMillis())
+ .build();
+ }
+
+ private static Optional<Integer> getStatePort(ServiceInfo service) {
+ return service.getPorts().stream()
+ .filter(port -> port.getTags().contains("state"))
+ .map(PortInfo::getPort)
+ .findFirst();
+ }
+
+ private static long generationFromContainerState(JsonNode state) {
+ return state.get("config").get("generation").asLong();
+ }
+
+ private static StateApi createStateApi(Client client, URI uri) {
+ WebTarget target = client.target(uri);
+ return WebResourceFactory.newResource(StateApi.class, target);
+ }
+
private static class ServiceListResponse extends JSONResponse {
// Pre-condition: servicesToCheck has a state port
@@ -179,7 +191,7 @@ public class ConfigConvergenceChecker extends AbstractComponent {
}
}
- static class ServiceResponse extends JSONResponse {
+ private static class ServiceResponse extends JSONResponse {
private ServiceResponse(int status, URI uri, String hostname, Long wantedGeneration) {
super(status);
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/PermanentApplicationPackage.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/PermanentApplicationPackage.java
index 868e68d9c73..b64aa1c771c 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/application/PermanentApplicationPackage.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/PermanentApplicationPackage.java
@@ -14,8 +14,7 @@ import java.util.logging.Logger;
/**
* A global permanent application package containing configuration info that is always used during deploy.
*
- * @author lulf
- * @since 5.15
+ * @author Ulf Lilleengen
*/
public class PermanentApplicationPackage {
@@ -24,7 +23,7 @@ public class PermanentApplicationPackage {
public PermanentApplicationPackage(ConfigserverConfig config) {
File app = new File(getDefaults().underVespaHome(config.applicationDirectory()));
- applicationPackage = Optional.<ApplicationPackage>ofNullable(app.exists() ? FilesApplicationPackage.fromFile(app) : null);
+ applicationPackage = Optional.ofNullable(app.exists() ? FilesApplicationPackage.fromFile(app) : null);
if (applicationPackage.isPresent()) {
log.log(LogLevel.DEBUG, "Detected permanent application package in '" +
getDefaults().underVespaHome(config.applicationDirectory()) +
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java
index 74757032eaa..06b413a97ce 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java
@@ -48,6 +48,9 @@ public class Deployment implements com.yahoo.config.provision.Deployment {
/** The Vespa version this application should run on */
private final Version version;
+ /** True if this deployment is done to bootstrap the config server */
+ private final boolean isBootstrap;
+
private boolean prepared = false;
/** Whether this model should be validated (only takes effect if prepared=false) */
@@ -58,7 +61,8 @@ public class Deployment implements com.yahoo.config.provision.Deployment {
private Deployment(LocalSession session, ApplicationRepository applicationRepository,
Optional<Provisioner> hostProvisioner, Tenant tenant,
- Duration timeout, Clock clock, boolean prepared, boolean validate, Version version) {
+ Duration timeout, Clock clock, boolean prepared, boolean validate, Version version,
+ boolean isBootstrap) {
this.session = session;
this.applicationRepository = applicationRepository;
this.hostProvisioner = hostProvisioner;
@@ -68,20 +72,22 @@ public class Deployment implements com.yahoo.config.provision.Deployment {
this.prepared = prepared;
this.validate = validate;
this.version = version;
+ this.isBootstrap = isBootstrap;
}
public static Deployment unprepared(LocalSession session, ApplicationRepository applicationRepository,
Optional<Provisioner> hostProvisioner, Tenant tenant,
- Duration timeout, Clock clock, boolean validate, Version version) {
+ Duration timeout, Clock clock, boolean validate, Version version,
+ boolean isBootstrap) {
return new Deployment(session, applicationRepository, hostProvisioner, tenant,
- timeout, clock, false, validate, version);
+ timeout, clock, false, validate, version, isBootstrap);
}
public static Deployment prepared(LocalSession session, ApplicationRepository applicationRepository,
Optional<Provisioner> hostProvisioner, Tenant tenant,
- Duration timeout, Clock clock) {
+ Duration timeout, Clock clock, boolean isBootstrap) {
return new Deployment(session, applicationRepository, hostProvisioner, tenant,
- timeout, clock, true, true, session.getVespaVersion());
+ timeout, clock, true, true, session.getVespaVersion(), isBootstrap);
}
public Deployment setIgnoreLockFailure(boolean ignoreLockFailure) {
@@ -105,6 +111,7 @@ public class Deployment implements com.yahoo.config.provision.Deployment {
.timeoutBudget(timeoutBudget)
.ignoreValidationErrors( ! validate)
.vespaVersion(version.toString())
+ .isBootstrap(isBootstrap)
.build(),
Optional.empty(),
tenant.getPath(),
@@ -161,6 +168,7 @@ public class Deployment implements com.yahoo.config.provision.Deployment {
* This is sometimes needed after activation, but can also be requested without
* doing prepare and activate in the same session.
*/
+ @Override
public void restart(HostFilter filter) {
hostProvisioner.get().restart(session.getApplicationId(), filter);
}
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 cec879c6e14..914d6963ff0 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
@@ -123,6 +123,8 @@ public class ModelContextImpl implements ModelContext {
private final boolean hostedVespa;
private final Zone zone;
private final Set<Rotation> rotations;
+ private final boolean isBootstrap;
+ private final boolean isFirstTimeDeployment;
public Properties(ApplicationId applicationId,
boolean multitenant,
@@ -132,7 +134,9 @@ public class ModelContextImpl implements ModelContext {
String athenzDnsSuffix,
boolean hostedVespa,
Zone zone,
- Set<Rotation> rotations) {
+ Set<Rotation> rotations,
+ boolean isBootstrap,
+ boolean isFirstTimeDeployment) {
this.applicationId = applicationId;
this.multitenant = multitenant;
this.configServerSpecs = configServerSpecs;
@@ -142,6 +146,8 @@ public class ModelContextImpl implements ModelContext {
this.hostedVespa = hostedVespa;
this.zone = zone;
this.rotations = rotations;
+ this.isBootstrap = isBootstrap;
+ this.isFirstTimeDeployment = isFirstTimeDeployment;
}
@Override
@@ -175,6 +181,11 @@ public class ModelContextImpl implements ModelContext {
@Override
public Set<Rotation> rotations() { return rotations; }
+ @Override
+ public boolean isBootstrap() { return isBootstrap; }
+
+ @Override
+ public boolean isFirstTimeDeployment() { return isFirstTimeDeployment; }
}
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/TenantFileSystemDirs.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/TenantFileSystemDirs.java
index 293f35558cb..587015362c8 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/TenantFileSystemDirs.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/TenantFileSystemDirs.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.config.server.deploy;
-import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.config.provision.TenantName;
import com.yahoo.io.IOUtils;
import com.yahoo.path.Path;
@@ -19,8 +18,8 @@ public class TenantFileSystemDirs {
private final File serverDB;
private final TenantName tenant;
- public TenantFileSystemDirs(ConfigserverConfig configserverConfig, TenantName tenant) {
- this(new ConfigServerDB(configserverConfig).path(), tenant);
+ public TenantFileSystemDirs(ConfigServerDB configServerDB, TenantName tenant) {
+ this(configServerDB.path(), tenant);
}
// For testing
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 68704eeb66a..f9f84de1cfb 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
@@ -360,8 +360,8 @@ public class ZooKeeperClient {
}
}
- public void write(AllocatedHosts info) throws IOException {
- configCurator.putData(rootPath.append(ZKApplicationPackage.allocatedHostsNode).getAbsolute(), info.toJson());
+ public void write(AllocatedHosts hosts) throws IOException {
+ configCurator.putData(rootPath.append(ZKApplicationPackage.allocatedHostsNode).getAbsolute(), hosts.toJson());
}
public void write(Map<Version, FileRegistry> fileRegistryMap) {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java b/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java
index df2287c64cb..a8159405edd 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java
@@ -78,7 +78,7 @@ public class FileDirectory {
return Files.walk(file.toPath(), 100).map(path -> {
try {
log.log(LogLevel.DEBUG, "Calculating hash for '" + path + "'");
- return hash(file, hasher);
+ return hash(path.toFile(), hasher);
} catch (IOException e) {
log.log(LogLevel.WARNING, "Failed getting hash from '" + path + "'");
return 0;
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileServer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileServer.java
index 42bf269e9d2..060f92aa2f4 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileServer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileServer.java
@@ -28,6 +28,7 @@ import com.yahoo.yolean.Exceptions;
import java.io.File;
import java.io.IOException;
import java.util.List;
+import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
@@ -57,7 +58,7 @@ public class FileServer {
public static class ReplayStatus {
private final int code;
private final String description;
- public ReplayStatus(int code, String description) {
+ ReplayStatus(int code, String description) {
this.code = code;
this.description = description;
}
@@ -70,6 +71,7 @@ public class FileServer {
void receive(FileReferenceData fileData, ReplayStatus status);
}
+ @SuppressWarnings("WeakerAccess") // Created by dependency injection
@Inject
public FileServer(ConfigserverConfig configserverConfig) {
this(createConnectionPool(configserverConfig), new File(Defaults.getDefaults().underVespaHome(configserverConfig.fileReferencesDir())));
@@ -87,8 +89,8 @@ public class FileServer {
this.pullExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
}
- public boolean hasFile(String fileName) {
- return hasFile(new FileReference(fileName));
+ boolean hasFile(String fileReference) {
+ return hasFile(new FileReference(fileReference));
}
private boolean hasFile(FileReference reference) {
@@ -99,7 +101,8 @@ public class FileServer {
}
return false;
}
- public void startFileServing(String fileName, Receiver target) {
+
+ void startFileServing(String fileName, Receiver target) {
FileReference reference = new FileReference(fileName);
File file = root.getFile(reference);
@@ -143,40 +146,47 @@ public class FileServer {
return new LazyFileReferenceData(reference, file.getName(), FileReferenceData.Type.file, file);
}
}
- public void serveFile(Request request, Receiver receiver) {
- pullExecutor.execute(() -> serveFile(request.parameters().get(0).asString(), request, receiver));
+
+ public void serveFile(String fileReference, boolean downloadFromOtherSourceIfNotFound, Request request, Receiver receiver) {
+ pullExecutor.execute(() -> serveFileInternal(fileReference, downloadFromOtherSourceIfNotFound, request, receiver));
}
- private void serveFile(String fileReference, Request request, Receiver receiver) {
- FileApiErrorCodes result;
+
+ private void serveFileInternal(String fileReference, boolean downloadFromOtherSourceIfNotFound, Request request, Receiver receiver) {
+ log.log(LogLevel.DEBUG, () -> "Received request for reference '" + fileReference + "' from " + request.target());
+
+ boolean fileExists;
try {
- log.log(LogLevel.DEBUG, () -> "Received request for reference '" + fileReference + "' from " + request.target());
- result = hasFile(fileReference)
- ? FileApiErrorCodes.OK
- : FileApiErrorCodes.NOT_FOUND;
- if (result == FileApiErrorCodes.OK) {
- startFileServing(fileReference, receiver);
- } else {
- // Non-zero second parameter means that the request should never lead
- // to a new download typically because the request comes from another config server.
- // This is to avoid config servers asking each other for a file that does not exist
- if (request.parameters().size() == 1 || request.parameters().get(1).asInt32() == 0) {
- log.log(LogLevel.DEBUG, "File not found, downloading from another source");
- downloader.getFile(new FileReferenceDownload(new FileReference(fileReference), false /* downloadFromOtherSourceIfNotFound */));
- } else {
- log.log(LogLevel.DEBUG, "File not found, will not download from another source since request came from another config server");
- result = FileApiErrorCodes.NOT_FOUND;
- }
- }
+ fileExists = hasFile(fileReference) || download(fileReference, downloadFromOtherSourceIfNotFound);
+ if (fileExists) startFileServing(fileReference, receiver);
} catch (IllegalArgumentException e) {
- result = FileApiErrorCodes.NOT_FOUND;
+ fileExists = false;
log.warning("Failed serving file reference '" + fileReference + "', request was from " + request.target() + ", with error " + e.toString());
}
+
+ FileApiErrorCodes result = fileExists ? FileApiErrorCodes.OK : FileApiErrorCodes.NOT_FOUND;
request.returnValues()
.add(new Int32Value(result.getCode()))
.add(new StringValue(result.getDescription()));
request.returnRequest();
}
+ // downloadFromOtherSourceIfNotFound is true when the request comes from another config server.
+ // This is to avoid config servers asking each other for a file that does not exist
+ private boolean download(String fileReference, boolean downloadFromOtherSourceIfNotFound) {
+ if (downloadFromOtherSourceIfNotFound) {
+ log.log(LogLevel.DEBUG, "File not found, downloading from another source");
+ return download(fileReference).isPresent();
+ } else {
+ log.log(LogLevel.DEBUG, "File not found, will not download from another source since request came from another config server");
+ return false;
+ }
+ }
+
+ private Optional<File> download(String fileReference) {
+ /* downloadFromOtherSourceIfNotFound should be false here, since this request is from a config server */
+ return downloader.getFile(new FileReferenceDownload(new FileReference(fileReference), false));
+ }
+
public FileDownloader downloader() {
return downloader;
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpConfigResponse.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpConfigResponse.java
index 489424f8791..43fc0a2f418 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpConfigResponse.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpConfigResponse.java
@@ -14,9 +14,9 @@ import static com.yahoo.jdisc.http.HttpResponse.Status.OK;
* HTTP getConfig response
*
* @author lulf
- * @since 5.1
*/
public class HttpConfigResponse extends HttpResponse {
+
public static final String JSON_CONTENT_TYPE = "application/json";
private final ConfigResponse config;
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpGetConfigHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpGetConfigHandler.java
index 8ad7a85619c..7c962e5b6be 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpGetConfigHandler.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpGetConfigHandler.java
@@ -13,7 +13,7 @@ import com.yahoo.vespa.config.server.tenant.TenantRepository;
import java.util.Optional;
/**
- * HTTP handler for a v2 getConfig operation
+ * HTTP handler for a v1 getConfig operation
*
* @author Ulf Lilleengen
*/
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 473ec913f50..2004ab95144 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
@@ -46,8 +46,8 @@ public class ApplicationHandler extends HttpHandler {
@Override
public HttpResponse handleDELETE(HttpRequest request) {
ApplicationId applicationId = getApplicationIdFromRequest(request);
- boolean removed = applicationRepository.remove(applicationId);
- if ( ! removed)
+ boolean deleted = applicationRepository.delete(applicationId);
+ if ( ! deleted)
return HttpErrorResponse.notFoundError("Unable to delete " + applicationId + ": Not found");
return new DeleteApplicationResponse(Response.Status.OK, applicationId);
}
@@ -57,11 +57,12 @@ public class ApplicationHandler extends HttpHandler {
public HttpResponse handleGET(HttpRequest request) {
ApplicationId applicationId = getApplicationIdFromRequest(request);
Tenant tenant = verifyTenantAndApplication(applicationId);
+ Duration timeout = HttpHandler.getRequestTimeout(request, Duration.ofSeconds(5));
if (isServiceConvergeRequest(request)) {
// Expects both hostname and port in the request (hostname:port)
String hostAndPort = getHostNameFromRequest(request);
- return applicationRepository.checkServiceForConfigConvergence(applicationId, hostAndPort, request.getUri());
+ return applicationRepository.checkServiceForConfigConvergence(applicationId, hostAndPort, request.getUri(), timeout);
}
if (isClusterControllerStatusRequest(request)) {
@@ -88,11 +89,10 @@ public class ApplicationHandler extends HttpHandler {
}
if (isServiceConvergeListRequest(request)) {
- return applicationRepository.servicesToCheckForConfigConvergence(applicationId, request.getUri());
+ return applicationRepository.servicesToCheckForConfigConvergence(applicationId, request.getUri(), timeout);
}
if (isFiledistributionStatusRequest(request)) {
- Duration timeout = HttpHandler.getRequestTimeout(request, Duration.ofSeconds(5));
return applicationRepository.filedistributionStatus(applicationId, timeout);
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ListTenantsResponse.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ListTenantsResponse.java
index 131f5f6085b..4eefcc0ca75 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ListTenantsResponse.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ListTenantsResponse.java
@@ -1,14 +1,12 @@
// 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.v2;
-
+import com.google.common.collect.ImmutableSet;
import com.yahoo.config.provision.TenantName;
import com.yahoo.slime.Cursor;
import com.yahoo.vespa.config.server.http.HttpConfigResponse;
import com.yahoo.vespa.config.server.http.SessionResponse;
-import java.util.Collection;
-
/**
* Tenant list response
*
@@ -16,17 +14,11 @@ import java.util.Collection;
*
*/
public class ListTenantsResponse extends SessionResponse {
- private final Collection<TenantName> tenantNames;
-
- public ListTenantsResponse(final Collection<TenantName> tenants) {
+
+ ListTenantsResponse(ImmutableSet<TenantName> tenants) {
super();
- this.tenantNames = tenants;
Cursor tenantArray = this.root.setArray("tenants");
- synchronized (tenants) {
- for (final TenantName tenantName : tenants) {
- tenantArray.addString(tenantName.value());
- }
- }
+ tenants.forEach(tenantName -> tenantArray.addString(tenantName.value()));
}
@Override
@@ -34,7 +26,4 @@ public class ListTenantsResponse extends SessionResponse {
return HttpConfigResponse.JSON_CONTENT_TYPE;
}
- public Collection<TenantName> getTenantNames() {
- return tenantNames;
- }
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/TenantHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/TenantHandler.java
index c8e9da1265b..b79fd1c7a62 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/TenantHandler.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/TenantHandler.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.http.v2;
+import com.google.common.collect.ImmutableSet;
import com.google.inject.Inject;
import com.yahoo.config.provision.TenantName;
@@ -26,6 +27,7 @@ public class TenantHandler extends HttpHandler {
private final TenantRepository tenantRepository;
private final ApplicationRepository applicationRepository;
+ @SuppressWarnings("WeakerAccess") // instantiated by dependency injection
@Inject
public TenantHandler(Context ctx, TenantRepository tenantRepository, ApplicationRepository applicationRepository) {
super(ctx);
@@ -51,7 +53,7 @@ public class TenantHandler extends HttpHandler {
Utils.checkThatTenantExists(tenantRepository, tenantName);
return new TenantGetResponse(tenantName);
} else if (isListTenantsRequest(request)) {
- return new ListTenantsResponse(tenantRepository.getAllTenantNames());
+ return new ListTenantsResponse(ImmutableSet.copyOf(tenantRepository.getAllTenantNames()));
} else {
throw new BadRequestException(request.getUri().toString());
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ConfigServerMaintenance.java b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ConfigServerMaintenance.java
index 2a53f9ee45c..c1fc484e23c 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ConfigServerMaintenance.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ConfigServerMaintenance.java
@@ -3,7 +3,6 @@ package com.yahoo.vespa.config.server.maintenance;
import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.component.AbstractComponent;
-import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.SystemName;
import com.yahoo.vespa.config.server.ApplicationRepository;
import com.yahoo.vespa.config.server.session.FileDistributionFactory;
@@ -11,11 +10,19 @@ import com.yahoo.vespa.curator.Curator;
import java.time.Duration;
+/**
+ * Maintenance jobs of the config server.
+ * Each maintenance job is a singleton instance of its implementing class, created and owned by this,
+ * and running its own dedicated thread.
+ *
+ * @author hmusum
+ */
public class ConfigServerMaintenance extends AbstractComponent {
private final TenantsMaintainer tenantsMaintainer;
private final ZooKeeperDataMaintainer zooKeeperDataMaintainer;
private final FileDistributionMaintainer fileDistributionMaintainer;
+ private final SessionsMaintainer sessionsMaintainer;
@SuppressWarnings("unused") // instantiated by Dependency Injection
public ConfigServerMaintenance(ConfigserverConfig configserverConfig,
@@ -26,6 +33,7 @@ public class ConfigServerMaintenance extends AbstractComponent {
tenantsMaintainer = new TenantsMaintainer(applicationRepository, curator, defaults.tenantsMaintainerInterval);
zooKeeperDataMaintainer = new ZooKeeperDataMaintainer(applicationRepository, curator, defaults.defaultInterval);
fileDistributionMaintainer = new FileDistributionMaintainer(applicationRepository, curator, defaults.defaultInterval, configserverConfig);
+ sessionsMaintainer = new SessionsMaintainer(applicationRepository, curator, defaults.defaultInterval);
}
@Override
@@ -33,6 +41,7 @@ public class ConfigServerMaintenance extends AbstractComponent {
tenantsMaintainer.deconstruct();
zooKeeperDataMaintainer.deconstruct();
fileDistributionMaintainer.deconstruct();
+ sessionsMaintainer.deconstruct();
}
/*
@@ -45,13 +54,11 @@ public class ConfigServerMaintenance extends AbstractComponent {
private final Duration tenantsMaintainerInterval;
DefaultTimes(ConfigserverConfig configserverConfig) {
- boolean isCd = configserverConfig.system().equals(SystemName.cd.name());
- boolean isTest = Environment.from(configserverConfig.environment()).isTest();
-
this.defaultInterval = Duration.ofMinutes(configserverConfig.maintainerIntervalMinutes());
+ boolean isCd = configserverConfig.system().equals(SystemName.cd.name());
// TODO: Want job control or feature flag to control when to run this, for now use a very
- // long interval to avoid running the maintainer
- this.tenantsMaintainerInterval = isCd || isTest || configserverConfig.region().equals("us-central-1")
+ // long interval to avoid running the maintainer except in CD
+ this.tenantsMaintainerInterval = isCd
? defaultInterval
: Duration.ofMinutes(configserverConfig.tenantsMaintainerIntervalMinutes());
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/FileDistributionMaintainer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/FileDistributionMaintainer.java
index 4f45b200111..a5d4aa226a6 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/FileDistributionMaintainer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/FileDistributionMaintainer.java
@@ -9,7 +9,13 @@ import com.yahoo.vespa.defaults.Defaults;
import java.io.File;
import java.time.Duration;
-// Note: Unit test is in ApplicationRepositoryTest
+/**
+ * Removes unused file references from disk
+ * <p>
+ * Note: Unit test is in ApplicationRepositoryTest
+ *
+ * @author hmusum
+ */
public class FileDistributionMaintainer extends Maintainer {
private final ApplicationRepository applicationRepository;
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/Maintainer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/Maintainer.java
index ae000385dfd..513f70e4187 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/Maintainer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/Maintainer.java
@@ -16,6 +16,11 @@ import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
+/**
+ * A maintainer is some job which runs at a fixed interval to perform some maintenance task in the config server.
+ *
+ * @author hmusum
+ */
public abstract class Maintainer extends AbstractComponent implements Runnable {
protected static final Logger log = Logger.getLogger(Maintainer.class.getName());
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/SessionsMaintainer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/SessionsMaintainer.java
new file mode 100644
index 00000000000..a0cbf4e4845
--- /dev/null
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/SessionsMaintainer.java
@@ -0,0 +1,35 @@
+// 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.maintenance;
+
+import com.yahoo.vespa.config.server.ApplicationRepository;
+import com.yahoo.vespa.curator.Curator;
+
+import java.time.Duration;
+
+/**
+ * Removes inactive sessions
+ * <p>
+ * Note: Unit test is in ApplicationRepositoryTest
+ *
+ * @author hmusum
+ */
+public class SessionsMaintainer extends Maintainer {
+ private final boolean hostedVespa;
+
+ SessionsMaintainer(ApplicationRepository applicationRepository, Curator curator, Duration interval) {
+ super(applicationRepository, curator, interval);
+ this.hostedVespa = applicationRepository.configserverConfig().hostedVespa();
+ }
+
+ @Override
+ protected void maintain() {
+ applicationRepository.deleteExpiredLocalSessions();
+
+ // Expired remote sessions are not expected to exist, they should have been deleted when
+ // a deployment happened or when the application was deleted. We still see them from time to time,
+ // probably due to some race or another bug
+ Duration expiryTime = Duration.ofDays(30);
+ if (hostedVespa)
+ applicationRepository.deleteExpiredRemoteSessions(expiryTime);
+ }
+}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/TenantsMaintainer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/TenantsMaintainer.java
index ad3b1f9b1ea..86bd4e8db8e 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/TenantsMaintainer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/TenantsMaintainer.java
@@ -7,6 +7,11 @@ import com.yahoo.vespa.curator.Curator;
import java.time.Duration;
import java.time.Instant;
+/**
+ * Removes unused tenants (has no applications and was created more than 7 days ago)
+ *
+ * @author hmusum
+ */
public class TenantsMaintainer extends Maintainer {
private final Duration ttlForUnusedTenant;
@@ -21,7 +26,6 @@ public class TenantsMaintainer extends Maintainer {
}
@Override
- // Delete unused tenants that were created more than ttlForUnusedTenant ago
protected void maintain() {
applicationRepository.deleteUnusedTenants(ttlForUnusedTenant, Instant.now());
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ZooKeeperDataMaintainer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ZooKeeperDataMaintainer.java
index fd57732eb30..d01181638c6 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ZooKeeperDataMaintainer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ZooKeeperDataMaintainer.java
@@ -1,24 +1,32 @@
// 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.maintenance;
+package com.yahoo.vespa.config.server.maintenance;
import com.yahoo.path.Path;
import com.yahoo.vespa.config.server.ApplicationRepository;
import com.yahoo.vespa.curator.Curator;
import java.time.Duration;
+import java.util.Arrays;
+import java.util.List;
/**
- * Removes unused zookeeper data (for now only data used by old file distribution code is removed)
+ * Removes unused zookeeper data
+ *
+ * @author hmusum
*/
public class ZooKeeperDataMaintainer extends Maintainer {
+ private static final List<String> pathsToDelete = Arrays.asList(
+ "/vespa/filedistribution", // Path to file distribution data used before Vespa 6.213
+ "/vespa/config" // Path to config data used before Vespa 6
+ );
+
ZooKeeperDataMaintainer(ApplicationRepository applicationRepository, Curator curator, Duration interval) {
super(applicationRepository, curator, interval);
}
@Override
protected void maintain() {
- curator.delete(Path.fromString("/vespa/filedistribution"));
- curator.delete(Path.fromString("/vespa/config"));
+ pathsToDelete.forEach(path -> curator.delete(Path.fromString(path)));
}
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java
index 651e5a6bbb0..c18d3c7fe48 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java
@@ -10,9 +10,11 @@ import com.yahoo.config.model.api.ModelFactory;
import com.yahoo.config.model.application.provider.MockFileRegistry;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.AllocatedHosts;
+import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.Version;
import com.yahoo.log.LogLevel;
+import com.yahoo.vespa.config.server.ConfigServerSpec;
import com.yahoo.vespa.config.server.GlobalComponentRegistry;
import com.yahoo.vespa.config.server.tenant.Rotations;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
@@ -25,6 +27,7 @@ import com.yahoo.vespa.config.server.session.SessionZooKeeperClient;
import com.yahoo.vespa.config.server.session.SilentDeployLogger;
import com.yahoo.vespa.curator.Curator;
+import java.net.URI;
import java.time.Instant;
import java.util.Map;
import java.util.Optional;
@@ -42,22 +45,20 @@ public class ActivatedModelsBuilder extends ModelsBuilder<Application> {
private final TenantName tenant;
private final long appGeneration;
private final SessionZooKeeperClient zkClient;
- private final Optional<PermanentApplicationPackage> permanentApplicationPackage;
- private final ConfigserverConfig configserverConfig;
+ private final PermanentApplicationPackage permanentApplicationPackage;
private final ConfigDefinitionRepo configDefinitionRepo;
private final Metrics metrics;
private final Curator curator;
private final DeployLogger logger;
public ActivatedModelsBuilder(TenantName tenant, long appGeneration, SessionZooKeeperClient zkClient, GlobalComponentRegistry globalComponentRegistry) {
- super(globalComponentRegistry.getModelFactoryRegistry(),
- globalComponentRegistry.getHostProvisioner().isPresent(),
+ super(globalComponentRegistry.getModelFactoryRegistry(),
+ globalComponentRegistry.getConfigserverConfig(),
globalComponentRegistry.getZone());
this.tenant = tenant;
this.appGeneration = appGeneration;
this.zkClient = zkClient;
- this.permanentApplicationPackage = Optional.of(globalComponentRegistry.getPermanentApplicationPackage());
- this.configserverConfig = globalComponentRegistry.getConfigserverConfig();
+ this.permanentApplicationPackage = globalComponentRegistry.getPermanentApplicationPackage();
this.configDefinitionRepo = globalComponentRegistry.getConfigDefinitionRepo();
this.metrics = globalComponentRegistry.getMetrics();
this.curator = globalComponentRegistry.getCurator();
@@ -76,7 +77,7 @@ public class ActivatedModelsBuilder extends ModelsBuilder<Application> {
ModelContext modelContext = new ModelContextImpl(
applicationPackage,
Optional.empty(),
- permanentApplicationPackage.get().applicationPackage(),
+ permanentApplicationPackage.applicationPackage(),
logger,
configDefinitionRepo,
getForVersionOrLatest(applicationPackage.getFileRegistryMap(), modelFactory.getVersion()).orElse(new MockFileRegistry()),
@@ -104,11 +105,17 @@ public class ActivatedModelsBuilder extends ModelsBuilder<Application> {
}
private ModelContext.Properties createModelContextProperties(ApplicationId applicationId) {
- return createModelContextProperties(
- applicationId,
- configserverConfig,
- zone(),
- new Rotations(curator, TenantRepository.getTenantPath(tenant)).readRotationsFromZooKeeper(applicationId));
+ return new ModelContextImpl.Properties(applicationId,
+ configserverConfig.multitenant(),
+ ConfigServerSpec.fromConfig(configserverConfig),
+ HostName.from(configserverConfig.loadBalancerAddress()),
+ configserverConfig.ztsUrl() != null ? URI.create(configserverConfig.ztsUrl()) : null,
+ configserverConfig.athenzDnsSuffix(),
+ configserverConfig.hostedVespa(),
+ zone(),
+ new Rotations(curator, TenantRepository.getTenantPath(tenant)).readRotationsFromZooKeeper(applicationId),
+ false, // We may be bootstrapping, but we only know and care during prepare
+ false); // Always false, assume no one uses it when activating
}
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ModelsBuilder.java b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ModelsBuilder.java
index 5c5d5f55a4d..3eece4f5640 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ModelsBuilder.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ModelsBuilder.java
@@ -5,26 +5,21 @@ import com.google.common.util.concurrent.UncheckedTimeoutException;
import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.config.application.api.ApplicationPackage;
import com.yahoo.config.model.api.HostProvisioner;
-import com.yahoo.config.model.api.ModelContext;
import com.yahoo.config.model.api.ModelFactory;
import com.yahoo.config.provision.AllocatedHosts;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ApplicationLockException;
import com.yahoo.config.provision.Environment;
-import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.OutOfCapacityException;
-import com.yahoo.config.provision.Rotation;
+import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.Version;
import com.yahoo.config.provision.Zone;
import com.yahoo.lang.SettableOptional;
import com.yahoo.log.LogLevel;
-import com.yahoo.vespa.config.server.ConfigServerSpec;
-import com.yahoo.vespa.config.server.deploy.ModelContextImpl;
import com.yahoo.vespa.config.server.http.InternalServerException;
import com.yahoo.vespa.config.server.http.UnknownVespaVersionException;
import com.yahoo.vespa.config.server.provision.StaticProvisioner;
-import java.net.URI;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
@@ -49,15 +44,17 @@ public abstract class ModelsBuilder<MODELRESULT extends ModelResult> {
private static final Logger log = Logger.getLogger(ModelsBuilder.class.getName());
private final ModelFactoryRegistry modelFactoryRegistry;
+ protected final ConfigserverConfig configserverConfig;
/** True if we are running in hosted mode */
private final boolean hosted;
private final Zone zone;
- protected ModelsBuilder(ModelFactoryRegistry modelFactoryRegistry, boolean hosted, Zone zone) {
+ protected ModelsBuilder(ModelFactoryRegistry modelFactoryRegistry, ConfigserverConfig configserverConfig, Zone zone) {
this.modelFactoryRegistry = modelFactoryRegistry;
- this.hosted = hosted;
+ this.configserverConfig = configserverConfig;
+ this.hosted = configserverConfig.hostedVespa();
this.zone = zone;
}
@@ -94,9 +91,9 @@ public abstract class ModelsBuilder<MODELRESULT extends ModelResult> {
List<MODELRESULT> allApplicationModels = new ArrayList<>();
for (int i = 0; i < majorVersions.size(); i++) {
try {
- allApplicationModels.addAll(buildModelVersion(filterByMajorVersion(majorVersions.get(i), versions),
- applicationId, wantedNodeVespaVersion, applicationPackage,
- allocatedHosts, now));
+ allApplicationModels.addAll(buildModelVersions(filterByMajorVersion(majorVersions.get(i), versions),
+ applicationId, wantedNodeVespaVersion, applicationPackage,
+ allocatedHosts, now));
// skip old config models if requested after we have found a major version which works
if (allApplicationModels.size() > 0 && allApplicationModels.get(0).getModel().skipOldConfigModels(now))
@@ -125,11 +122,13 @@ public abstract class ModelsBuilder<MODELRESULT extends ModelResult> {
return allApplicationModels;
}
- private List<MODELRESULT> buildModelVersion(Set<Version> versions, ApplicationId applicationId,
- com.yahoo.component.Version wantedNodeVespaVersion,
- ApplicationPackage applicationPackage,
- SettableOptional<AllocatedHosts> allocatedHosts,
- Instant now) {
+ // versions is the set of versions for one particular major version
+ private List<MODELRESULT> buildModelVersions(Set<Version> versions,
+ ApplicationId applicationId,
+ com.yahoo.component.Version wantedNodeVespaVersion,
+ ApplicationPackage applicationPackage,
+ SettableOptional<AllocatedHosts> allocatedHosts,
+ Instant now) {
Version latest = findLatest(versions);
// load latest application version
MODELRESULT latestModelVersion = buildModelVersion(modelFactoryRegistry.getFactory(latest),
@@ -139,19 +138,13 @@ public abstract class ModelsBuilder<MODELRESULT extends ModelResult> {
allocatedHosts.asOptional(),
now);
allocatedHosts.set(latestModelVersion.getModel().allocatedHosts()); // Update with additional clusters allocated
-
+ List<MODELRESULT> allApplicationVersions = new ArrayList<>(Collections.singletonList(latestModelVersion));
+
if (latestModelVersion.getModel().skipOldConfigModels(now))
- return Collections.singletonList(latestModelVersion);
+ return allApplicationVersions;
// load old model versions
- List<MODELRESULT> allApplicationVersions = new ArrayList<>();
- allApplicationVersions.add(latestModelVersion);
-
- // TODO: Enable for all zones
- if (Arrays.asList(Environment.dev, Environment.test, Environment.staging).contains(zone().environment())
- || zone().region().value().equals("corp-us-east-1"))
- versions = keepThoseUsedOn(allocatedHosts.get(), versions);
-
+ versions = versionsToBuild(versions, wantedNodeVespaVersion, allocatedHosts.get());
// TODO: We use the allocated hosts from the newest version when building older model versions.
// This is correct except for the case where an old model specifies a cluster which the new version
// does not. In that case we really want to extend the set of allocated hosts to include those of that
@@ -172,6 +165,23 @@ public abstract class ModelsBuilder<MODELRESULT extends ModelResult> {
return allApplicationVersions;
}
+ private Set<Version> versionsToBuild(Set<Version> versions, com.yahoo.component.Version wantedVersion, AllocatedHosts allocatedHosts) {
+ // TODO: Enable for all zones
+ if (configserverConfig.buildMinimalSetOfConfigModels() &&
+ (SystemName.from(configserverConfig.system()) == SystemName.cd ||
+ Arrays.asList(Environment.dev, Environment.test, Environment.staging).contains(zone().environment()) ||
+ Arrays.asList("corp-us-east-1", "ap-southeast-1", "us-central-1").contains(zone().region().value())))
+ versions = keepThoseUsedOn(allocatedHosts, versions);
+
+ // Make sure we build wanted version if we are building models for this major version and we are on hosted vespa
+ // If not on hosted vespa, we do not want to try to build this version, since we have only one version (the latest)
+ Version wanted = Version.fromIntValues(wantedVersion.getMajor(), wantedVersion.getMinor(), wantedVersion.getMicro());
+ if (hosted && wantedVersion.getMajor() == findLatest(versions).getMajor())
+ versions.add(wanted);
+
+ return versions;
+ }
+
private Set<Version> filterByMajorVersion(int majorVersion, Set<Version> versions) {
Set<Version> filteredVersions = versions.stream().filter(v -> v.getMajor() == majorVersion).collect(Collectors.toSet());
if (filteredVersions.isEmpty())
@@ -202,22 +212,7 @@ public abstract class ModelsBuilder<MODELRESULT extends ModelResult> {
Optional<AllocatedHosts> allocatedHosts,
Instant now);
- protected ModelContext.Properties createModelContextProperties(ApplicationId applicationId,
- ConfigserverConfig configserverConfig,
- Zone zone,
- Set<Rotation> rotations) {
- return new ModelContextImpl.Properties(applicationId,
- configserverConfig.multitenant(),
- ConfigServerSpec.fromConfig(configserverConfig),
- HostName.from(configserverConfig.loadBalancerAddress()),
- configserverConfig.ztsUrl() != null ? URI.create(configserverConfig.ztsUrl()) : null,
- configserverConfig.athenzDnsSuffix(),
- configserverConfig.hostedVespa(),
- zone,
- rotations);
- }
-
- /**
+ /**
* Returns a host provisioner returning the previously allocated hosts if available and when on hosted Vespa,
* returns empty otherwise, which may either mean that no hosts are allocated or that we are running
* non-hosted and should default to use hosts defined in the application package, depending on context
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/PreparedModelsBuilder.java b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/PreparedModelsBuilder.java
index 0d9346101e9..56bdd432d90 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/PreparedModelsBuilder.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/PreparedModelsBuilder.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.modelfactory;
+import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.config.application.api.ApplicationPackage;
import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.config.model.api.ConfigChangeAction;
@@ -10,6 +11,8 @@ import com.yahoo.config.model.api.Model;
import com.yahoo.config.model.api.ModelContext;
import com.yahoo.config.model.api.ModelCreateResult;
import com.yahoo.config.model.api.ModelFactory;
+import com.yahoo.config.model.api.ValidationParameters;
+import com.yahoo.config.model.api.ValidationParameters.IgnoreValidationErrors;
import com.yahoo.config.model.application.provider.FilesApplicationPackage;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.AllocatedHosts;
@@ -62,8 +65,9 @@ public class PreparedModelsBuilder extends ModelsBuilder<PreparedModelsBuilder.P
DeployLogger logger,
PrepareParams params,
Optional<ApplicationSet> currentActiveApplicationSet,
- ModelContext.Properties properties) {
- super(modelFactoryRegistry, properties.hostedVespa(), properties.zone());
+ ModelContext.Properties properties,
+ ConfigserverConfig configserverConfig) {
+ super(modelFactoryRegistry, configserverConfig, properties.zone());
this.permanentApplicationPackage = permanentApplicationPackage;
this.configDefinitionRepo = configDefinitionRepo;
@@ -104,7 +108,9 @@ public class PreparedModelsBuilder extends ModelsBuilder<PreparedModelsBuilder.P
wantedNodeVespaVersion);
log.log(LogLevel.DEBUG, "Create and validate model " + modelVersion + " for " + applicationId);
- ModelCreateResult result = modelFactory.createAndValidateModel(modelContext, params.ignoreValidationErrors());
+ ValidationParameters validationParameters =
+ new ValidationParameters(params.ignoreValidationErrors() ? IgnoreValidationErrors.TRUE : IgnoreValidationErrors.FALSE);
+ ModelCreateResult result = modelFactory.createAndValidateModel(modelContext, validationParameters);
validateModelHosts(context.getHostValidator(), applicationId, result.getModel());
log.log(LogLevel.DEBUG, "Done building model " + modelVersion + " for " + applicationId);
return new PreparedModelsBuilder.PreparedModelResult(modelVersion, result.getModel(), fileDistributionProvider, result.getConfigChangeActions());
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessor.java b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessor.java
index 970fe9f169b..55d5321c93f 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessor.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessor.java
@@ -42,7 +42,7 @@ class GetConfigProcessor implements Runnable {
}
private void respond(JRTServerConfigRequest request) {
- final Request req = request.getRequest();
+ Request req = request.getRequest();
if (req.isError()) {
Level logLevel = (req.errorCode() == ErrorCode.APPLICATION_NOT_LOADED) ? LogLevel.DEBUG : LogLevel.INFO;
log.log(logLevel, logPre + req.errorMessage());
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java
index 94dc7d04da0..048000f31c4 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java
@@ -206,8 +206,7 @@ public class RpcServer implements Runnable, ReloadListener, TenantListener {
getSupervisor().addMethod(new Method("printStatistics", "", "s", this, "printStatistics")
.methodDesc("printStatistics")
.returnDesc(0, "statistics", "Statistics for server"));
- // TODO: Change parameters to "si" instead of "s*" when all clients have been updated
- getSupervisor().addMethod(new Method("filedistribution.serveFile", "s*", "is", this, "serveFile"));
+ getSupervisor().addMethod(new Method("filedistribution.serveFile", "si", "is", this, "serveFile"));
getSupervisor().addMethod(new Method("filedistribution.setFileReferencesToDownload", "S", "i",
this, "setFileReferencesToDownload")
.methodDesc("set which file references to download")
@@ -529,7 +528,7 @@ public class RpcServer implements Runnable, ReloadListener, TenantListener {
public final void serveFile(Request request) {
request.detach();
FileServer.Receiver receiver = new ChunkedFileReceiver(request.target());
- fileServer.serveFile(request, receiver);
+ fileServer.serveFile(request.parameters().get(0).asString(), request.parameters().get(1).asInt32() == 0, request, receiver);
}
@SuppressWarnings({"UnusedDeclaration"})
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 d294e462c44..0f9f8b72de1 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
@@ -120,14 +120,6 @@ public class LocalSession extends Session implements Comparable<LocalSession> {
return getSessionId() > sessionId;
}
- /** Delete this session */
- // TODO: Use transactional delete instead
- public void delete() {
- superModelGenerationCounter.increment();
- IOUtils.recursiveDeleteDir(serverDB);
- zooKeeperClient.delete();
- }
-
/** Add transactions to delete this session to the given nested transaction */
public void delete(NestedTransaction transaction) {
transaction.add(zooKeeperClient.deleteTransaction(), FileTransaction.class);
@@ -142,11 +134,6 @@ public class LocalSession extends Session implements Comparable<LocalSession> {
return lhsId.compareTo(rhsId);
}
- // in seconds
- public long getCreateTime() {
- return zooKeeperClient.readCreateTime();
- }
-
public void waitUntilActivated(TimeoutBudget timeoutBudget) {
zooKeeperClient.getActiveWaiter().awaitCompletion(timeoutBudget.timeLeft());
}
@@ -175,6 +162,8 @@ public class LocalSession extends Session implements Comparable<LocalSession> {
return zooKeeperClient.getAllocatedHosts();
}
+ public TenantName getTenantName() { return tenant; }
+
@Override
public String logPre() {
if (getApplicationId().equals(ApplicationId.defaultId())) {
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 550b08f3d5c..acfd81b33bf 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,57 +1,64 @@
// 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.ThreadFactoryFactory;
import com.yahoo.log.LogLevel;
-import com.yahoo.vespa.config.server.application.ZKTenantApplications;
+import com.yahoo.path.Path;
+import com.yahoo.transaction.NestedTransaction;
import com.yahoo.vespa.config.server.deploy.TenantFileSystemDirs;
+import com.yahoo.vespa.config.server.tenant.TenantRepository;
+import com.yahoo.vespa.config.server.zookeeper.ConfigCurator;
+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;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
/**
* File-based session repository for LocalSessions. Contains state for the local instance of the configserver.
*
- * @author lulf
+ * @author Ulf Lilleengen
*/
public class LocalSessionRepo extends SessionRepo<LocalSession> {
private static final Logger log = Logger.getLogger(LocalSessionRepo.class.getName());
private static final FilenameFilter sessionApplicationsFilter = (dir, name) -> name.matches("\\d+");
- private static final Duration delay = Duration.ofMinutes(5);
- // One executor for all instances of this class
- private static final ScheduledExecutorService purgeOldSessionsExecutor =
- new ScheduledThreadPoolExecutor(1, ThreadFactoryFactory.getDaemonThreadFactory("purge-old-sessions"));
+ private final Map<Long, LocalSessionStateWatcher> sessionStateWatchers = new HashMap<>();
private final long sessionLifetime; // in seconds
private final Clock clock;
+ private final Curator curator;
public LocalSessionRepo(TenantFileSystemDirs tenantFileSystemDirs, LocalSessionLoader loader,
- Clock clock, long sessionLifeTime) {
- this(clock, sessionLifeTime);
+ Clock clock, long sessionLifeTime, Curator curator) {
+ this(clock, curator, sessionLifeTime);
loadSessions(tenantFileSystemDirs.sessionsPath(), loader);
- purgeOldSessionsExecutor.scheduleWithFixedDelay(this::purgeOldSessions, delay.getSeconds(), delay.getSeconds(), TimeUnit.SECONDS);
}
// Constructor public only for testing
- public LocalSessionRepo(Clock clock) {
- this(clock, TimeUnit.DAYS.toMillis(1));
+ public LocalSessionRepo(Clock clock, Curator curator) {
+ this(clock, curator, TimeUnit.DAYS.toMillis(1));
}
// Constructor public only for testing
- private LocalSessionRepo(Clock clock, long sessionLifetime) {
- this.sessionLifetime = sessionLifetime;
+ private LocalSessionRepo(Clock clock, Curator curator, long sessionLifetime) {
this.clock = clock;
+ this.curator = curator;
+ this.sessionLifetime = sessionLifetime;
+ }
+
+ @Override
+ public synchronized void addSession(LocalSession session) {
+ super.addSession(session);
+ Path sessionsPath = TenantRepository.getSessionsPath(session.getTenantName());
+ long sessionId = session.getSessionId();
+ Curator.FileCache fileCache = curator.createFileCache(sessionsPath.append(String.valueOf(sessionId)).append(ConfigCurator.SESSIONSTATE_ZK_SUBPATH).getAbsolute(), false);
+ sessionStateWatchers.put(sessionId, new LocalSessionStateWatcher(fileCache, session, this));
}
private void loadSessions(File applicationsDir, LocalSessionLoader loader) {
@@ -69,7 +76,6 @@ public class LocalSessionRepo extends SessionRepo<LocalSession> {
}
}
- // public for testing
public void purgeOldSessions() {
log.log(LogLevel.DEBUG, "Purging old sessions");
try {
@@ -94,9 +100,15 @@ public class LocalSessionRepo extends SessionRepo<LocalSession> {
return candidate.getStatus() == Session.Status.ACTIVATE;
}
- private void deleteSession(LocalSession candidate) {
- removeSessionOrThrow(candidate.getSessionId());
- candidate.delete();
+ void deleteSession(LocalSession session) {
+ long sessionId = session.getSessionId();
+ log.log(LogLevel.DEBUG, "Deleting local session " + sessionId);
+ LocalSessionStateWatcher watcher = sessionStateWatchers.remove(sessionId);
+ if (watcher != null) watcher.close();
+ removeSession(sessionId);
+ NestedTransaction transaction = new NestedTransaction();
+ session.delete(transaction);
+ transaction.commit();
}
public void deleteAllSessions() {
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
new file mode 100644
index 00000000000..198f8e8e917
--- /dev/null
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionStateWatcher.java
@@ -0,0 +1,76 @@
+// 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.session;
+
+import com.yahoo.concurrent.ThreadFactoryFactory;
+import com.yahoo.log.LogLevel;
+import com.yahoo.text.Utf8;
+import com.yahoo.vespa.curator.Curator;
+import org.apache.curator.framework.recipes.cache.ChildData;
+import org.apache.curator.framework.recipes.cache.NodeCacheListener;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.logging.Logger;
+
+/**
+ * Watches one particular local session (/config/v2/tenants/&lt;tenantName&gt;/sessions/&lt;n&gt;/sessionState in ZooKeeper)
+ * to pick up when an application is deleted (the delete might be done on any config server in the cluster)
+ *
+ * @author Harald Musum
+ */
+public class LocalSessionStateWatcher implements NodeCacheListener {
+
+ private static final Logger log = Logger.getLogger(LocalSessionStateWatcher.class.getName());
+ // One thread pool for all instances of this class
+ private static final Executor executor = Executors.newCachedThreadPool(ThreadFactoryFactory.getDaemonThreadFactory(LocalSessionStateWatcher.class.getName()));
+
+ private final Curator.FileCache fileCache;
+ private final LocalSession session;
+ private final LocalSessionRepo localSessionRepo;
+
+ LocalSessionStateWatcher(Curator.FileCache fileCache, LocalSession session, LocalSessionRepo localSessionRepo) {
+ this.fileCache = fileCache;
+ this.session = session;
+ this.localSessionRepo = localSessionRepo;
+ this.fileCache.start();
+ this.fileCache.addListener(this);
+ }
+
+ // Will delete session if it exists in local session repo
+ private void sessionChanged(Session.Status status) {
+ long sessionId = session.getSessionId();
+ log.log(LogLevel.DEBUG, session.logPre() + "Session change: Local session " + sessionId + " changed status to " + status);
+
+ if (status.equals(Session.Status.DELETE) && localSessionRepo.getSession(sessionId) != null) {
+ log.log(LogLevel.DEBUG, session.logPre() + "Deleting session " + sessionId);
+ localSessionRepo.deleteSession(session);
+ }
+ }
+
+ public long getSessionId() {
+ return session.getSessionId();
+ }
+
+ public void close() {
+ try {
+ fileCache.close();
+ } catch (Exception e) {
+ log.log(LogLevel.WARNING, "Exception when closing watcher", e);
+ }
+ }
+
+ @Override
+ public void nodeChanged() {
+ executor.execute(() -> {
+ try {
+ ChildData data = fileCache.getCurrentData();
+ if (data != null) {
+ sessionChanged(Session.Status.parse(Utf8.toString(fileCache.getCurrentData().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/PrepareParams.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java
index b53ee9a5988..b44896ecb89 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java
@@ -35,16 +35,18 @@ public final class PrepareParams {
private final boolean ignoreValidationErrors;
private final boolean dryRun;
private final boolean verbose;
+ private final boolean isBootstrap;
private final Optional<Version> vespaVersion;
private final Set<Rotation> rotations;
private PrepareParams(ApplicationId applicationId, TimeoutBudget timeoutBudget, boolean ignoreValidationErrors,
- boolean dryRun, boolean verbose, Optional<Version> vespaVersion, Set<Rotation> rotations) {
+ boolean dryRun, boolean verbose, boolean isBootstrap, Optional<Version> vespaVersion, Set<Rotation> rotations) {
this.timeoutBudget = timeoutBudget;
this.applicationId = applicationId;
this.ignoreValidationErrors = ignoreValidationErrors;
this.dryRun = dryRun;
this.verbose = verbose;
+ this.isBootstrap = isBootstrap;
this.vespaVersion = vespaVersion;
this.rotations = rotations;
}
@@ -54,6 +56,7 @@ public final class PrepareParams {
private boolean ignoreValidationErrors = false;
private boolean dryRun = false;
private boolean verbose = false;
+ private boolean isBootstrap = false;
private ApplicationId applicationId = ApplicationId.defaultId();
private TimeoutBudget timeoutBudget = new TimeoutBudget(Clock.systemUTC(), Duration.ofSeconds(30));
private Optional<Version> vespaVersion = Optional.empty();
@@ -81,6 +84,11 @@ public final class PrepareParams {
return this;
}
+ public Builder isBootstrap(boolean isBootstrap) {
+ this.isBootstrap = isBootstrap;
+ return this;
+ }
+
public Builder timeoutBudget(TimeoutBudget timeoutBudget) {
this.timeoutBudget = timeoutBudget;
return this;
@@ -95,6 +103,11 @@ public final class PrepareParams {
return this;
}
+ public Builder vespaVersion(com.yahoo.config.provision.Version vespaVersion) {
+ this.vespaVersion = Optional.ofNullable(Version.fromString(vespaVersion.toSerializedForm()));
+ return this;
+ }
+
public Builder rotations(String rotationsString) {
this.rotations = new LinkedHashSet<>();
if (rotationsString != null && !rotationsString.isEmpty()) {
@@ -108,7 +121,7 @@ public final class PrepareParams {
public PrepareParams build() {
return new PrepareParams(applicationId, timeoutBudget, ignoreValidationErrors, dryRun,
- verbose, vespaVersion, rotations);
+ verbose, isBootstrap, vespaVersion, rotations);
}
}
@@ -165,6 +178,8 @@ public final class PrepareParams {
return verbose;
}
+ public boolean isBootstrap() { return isBootstrap; }
+
public TimeoutBudget getTimeoutBudget() {
return timeoutBudget;
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSession.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSession.java
index 454b98ddc05..dbccba395a2 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSession.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSession.java
@@ -1,10 +1,14 @@
// 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.config.provision.*;
+import com.yahoo.config.application.api.ApplicationPackage;
+import com.yahoo.config.provision.AllocatedHosts;
+import com.yahoo.config.provision.TenantName;
import com.yahoo.lang.SettableOptional;
-import com.yahoo.vespa.config.server.*;
+import com.yahoo.transaction.Transaction;
import com.yahoo.log.LogLevel;
+import com.yahoo.vespa.config.server.GlobalComponentRegistry;
+import com.yahoo.vespa.config.server.ReloadHandler;
import com.yahoo.vespa.config.server.application.ApplicationSet;
import com.yahoo.vespa.config.server.modelfactory.ActivatedModelsBuilder;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
@@ -13,7 +17,7 @@ import org.apache.zookeeper.KeeperException;
import java.time.Clock;
import java.time.Instant;
-import java.util.*;
+import java.util.Optional;
import java.util.logging.Logger;
/**
@@ -54,10 +58,15 @@ public class RemoteSession extends Session {
}
private ApplicationSet loadApplication() {
+ ApplicationPackage applicationPackage = zooKeeperClient.loadApplicationPackage();
+
+ // Read hosts allocated on the config server instance which created this
+ Optional<AllocatedHosts> allocatedHosts = applicationPackage.getAllocatedHosts();
+
return ApplicationSet.fromList(applicationLoader.buildModels(zooKeeperClient.readApplicationId(),
zooKeeperClient.readVespaVersion(),
- zooKeeperClient.loadApplicationPackage(),
- new SettableOptional<>(),
+ applicationPackage,
+ new SettableOptional<>(allocatedHosts),
clock.instant()));
}
@@ -76,9 +85,13 @@ public class RemoteSession extends Session {
applicationSet = null;
}
+ public Transaction createDeleteTransaction() {
+ return zooKeeperClient.createWriteStatusTransaction(Status.DELETE);
+ }
+
public void makeActive(ReloadHandler reloadHandler) {
Curator.CompletionWaiter waiter = zooKeeperClient.getActiveWaiter();
- log.log(LogLevel.DEBUG, logPre()+"Getting session from repo: " + getSessionId());
+ log.log(LogLevel.DEBUG, logPre() + "Getting session from repo: " + getSessionId());
ApplicationSet app = ensureApplicationLoaded();
log.log(LogLevel.DEBUG, logPre() + "Reloading config for " + app);
reloadHandler.reloadConfig(app);
@@ -100,7 +113,7 @@ public class RemoteSession extends Session {
Curator.CompletionWaiter waiter = zooKeeperClient.getUploadWaiter();
log.log(LogLevel.DEBUG, "Notifying upload waiter for session " + getSessionId());
notifyCompletion(waiter);
- log.log(LogLevel.DEBUG, "Done notifying for session " + getSessionId());
+ log.log(LogLevel.DEBUG, "Done notifying upload for session " + getSessionId());
}
private void notifyCompletion(Curator.CompletionWaiter completionWaiter) {
@@ -119,4 +132,9 @@ public class RemoteSession extends Session {
}
}
+ public void delete() {
+ Transaction transaction = zooKeeperClient.deleteTransaction();
+ transaction.commit();
+ }
+
}
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 12fa828f692..39e08a328e5 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
@@ -1,6 +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.config.server.session;
+import java.time.Duration;
+import java.time.Instant;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -14,7 +16,6 @@ import com.yahoo.concurrent.ThreadFactoryFactory;
import com.yahoo.config.provision.TenantName;
import com.yahoo.log.LogLevel;
import com.yahoo.path.Path;
-import com.yahoo.transaction.NestedTransaction;
import com.yahoo.vespa.config.server.application.ApplicationSet;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
import com.yahoo.vespa.curator.Curator;
@@ -47,8 +48,9 @@ public class RemoteSessionRepo extends SessionRepo<RemoteSession> implements Nod
private final Curator curator;
private final Path sessionsPath;
private final RemoteSessionFactory remoteSessionFactory;
- private final Map<Long, SessionStateWatcher> sessionStateWatchers = new HashMap<>();
+ private final Map<Long, RemoteSessionStateWatcher> sessionStateWatchers = new HashMap<>();
private final ReloadHandler reloadHandler;
+ private final TenantName tenantName;
private final MetricUpdater metrics;
private final Curator.DirectoryCache directoryCache;
private final TenantApplications applicationRepo;
@@ -57,20 +59,21 @@ public class RemoteSessionRepo extends SessionRepo<RemoteSession> implements Nod
* @param curator a {@link Curator} instance.
* @param remoteSessionFactory a {@link com.yahoo.vespa.config.server.session.RemoteSessionFactory}
* @param reloadHandler a {@link com.yahoo.vespa.config.server.ReloadHandler}
- * @param tenant a {@link TenantName} instance.
+ * @param tenantName a {@link TenantName} instance.
* @param applicationRepo a {@link TenantApplications} instance.
*/
public RemoteSessionRepo(Curator curator,
RemoteSessionFactory remoteSessionFactory,
ReloadHandler reloadHandler,
- TenantName tenant,
+ TenantName tenantName,
TenantApplications applicationRepo,
MetricUpdater metricUpdater) {
this.curator = curator;
- this.sessionsPath = TenantRepository.getSessionsPath(tenant);
+ this.sessionsPath = TenantRepository.getSessionsPath(tenantName);
this.applicationRepo = applicationRepo;
this.remoteSessionFactory = remoteSessionFactory;
this.reloadHandler = reloadHandler;
+ this.tenantName = tenantName;
this.metrics = metricUpdater;
initializeSessions();
this.directoryCache = curator.createDirectoryCache(sessionsPath.getAbsolute(), false, false, pathChildrenExecutor);
@@ -83,47 +86,37 @@ public class RemoteSessionRepo extends SessionRepo<RemoteSession> implements Nod
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;
}
- //---------- START overrides to keep sessions changed in sync
-
- @Override
- public synchronized void addSession(RemoteSession session) {
- super.addSession(session);
- sessionAdded(session.getSessionId());
- }
-
- @Override
- public synchronized void removeSessionOrThrow(long id) {
- super.removeSessionOrThrow(id);
- sessionRemoved(id);
+ public List<Long> getSessions() {
+ return getSessionList(curator.getChildren(sessionsPath));
}
- /**
- * Removes a session
- *
- * @param id the id of the session to remove
- * @return the removed session, or null if none was found
- */
- @Override
- public synchronized RemoteSession removeSession(long id) {
- RemoteSession session = super.removeSession(id);
- sessionRemoved(id);
- return session;
+ public int deleteExpiredSessions(Duration expiryTime, boolean deleteFromZooKeeper) {
+ int deleted = 0;
+ for (long sessionId : getSessions()) {
+ RemoteSession session = getSession(sessionId);
+ Instant created = Instant.ofEpochSecond(session.getCreateTime());
+ if (sessionHasExpired(created, expiryTime)) {
+ log.log(LogLevel.INFO, "Remote session " + sessionId + " for " + tenantName + " has expired");
+ if (deleteFromZooKeeper) {
+ session.delete();
+ deleted++;
+ }
+ }
+ }
+ return deleted;
}
- @Override
- public void removeSession(long id, NestedTransaction transaction) {
- super.removeSession(id, transaction);
- transaction.onCommitted(() -> sessionRemoved(id));
+ private boolean sessionHasExpired(Instant created, Duration expiryTime) {
+ return (created.plus(expiryTime).isBefore(Instant.now()));
}
- //---------- END overrides to keep sessions changed in sync
-
private void loadActiveSession(RemoteSession session) {
tryReload(session.ensureApplicationLoaded(), session.logPre());
}
@@ -131,9 +124,9 @@ public class RemoteSessionRepo extends SessionRepo<RemoteSession> implements Nod
private void tryReload(ApplicationSet applicationSet, String logPre) {
try {
reloadHandler.reloadConfig(applicationSet);
- log.log(LogLevel.INFO, logPre+"Application activated successfully: " + applicationSet.getId());
+ log.log(LogLevel.INFO, logPre + "Application activated successfully: " + applicationSet.getId());
} catch (Exception e) {
- log.log(LogLevel.WARNING, logPre+"Skipping loading of application '" + applicationSet.getId() + "': " + Exceptions.toMessageString(e));
+ log.log(LogLevel.WARNING, logPre + "Skipping loading of application '" + applicationSet.getId() + "': " + Exceptions.toMessageString(e));
}
}
@@ -149,7 +142,7 @@ public class RemoteSessionRepo extends SessionRepo<RemoteSession> implements Nod
// TODO: Add sessions in parallel
private void initializeSessions() throws NumberFormatException {
- getSessionList(curator.getChildren(sessionsPath)).forEach(this::sessionAdded);
+ getSessions().forEach(this::sessionAdded);
}
private synchronized void sessionsChanged() throws NumberFormatException {
@@ -183,8 +176,8 @@ public class RemoteSessionRepo extends SessionRepo<RemoteSession> implements Nod
Curator.FileCache fileCache = curator.createFileCache(sessionPath.append(ConfigCurator.SESSIONSTATE_ZK_SUBPATH).getAbsolute(), false);
fileCache.addListener(this);
loadSessionIfActive(session);
- sessionStateWatchers.put(sessionId, new SessionStateWatcher(fileCache, reloadHandler, session, metrics));
- internalAddSession(session);
+ sessionStateWatchers.put(sessionId, new RemoteSessionStateWatcher(fileCache, reloadHandler, session, metrics));
+ addSession(session);
metrics.incAddedSessions();
} catch (Exception e) {
log.log(Level.WARNING, "Failed loading session " + sessionId + ": No config for this session can be served", e);
@@ -192,9 +185,9 @@ public class RemoteSessionRepo extends SessionRepo<RemoteSession> implements Nod
}
private void sessionRemoved(long sessionId) {
- SessionStateWatcher watcher = sessionStateWatchers.remove(sessionId);
- watcher.close();
- internalRemoveSessionOrThrow(sessionId);
+ RemoteSessionStateWatcher watcher = sessionStateWatchers.remove(sessionId);
+ if (watcher != null) watcher.close();
+ removeSession(sessionId);
metrics.incRemovedSessions();
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionStateWatcher.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionStateWatcher.java
index 117930c6d7d..1a891c65c49 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionStateWatcher.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionStateWatcher.java
@@ -15,16 +15,16 @@ import java.util.concurrent.Executors;
import java.util.logging.Logger;
/**
- * Watches one particular session (/vespa/config/apps/n/sessionState in ZK)
+ * Watches one particular session (/config/v2/tenants/&lt;tenantName&gt;/sessions/&lt;n&gt;/sessionState in ZooKeeper)
* The session must be in the session repo.
*
- * @author vegardh
+ * @author Vegard Havdal
*/
-public class SessionStateWatcher implements NodeCacheListener {
+public class RemoteSessionStateWatcher implements NodeCacheListener {
- private static final Logger log = Logger.getLogger(SessionStateWatcher.class.getName());
+ private static final Logger log = Logger.getLogger(RemoteSessionStateWatcher.class.getName());
// One thread pool for all instances of this class
- private static final Executor executor = Executors.newCachedThreadPool(ThreadFactoryFactory.getDaemonThreadFactory(SessionStateWatcher.class.getName()));
+ private static final Executor executor = Executors.newCachedThreadPool(ThreadFactoryFactory.getDaemonThreadFactory(RemoteSessionStateWatcher.class.getName()));
private final Curator.FileCache fileCache;
private final ReloadHandler reloadHandler;
@@ -32,10 +32,10 @@ public class SessionStateWatcher implements NodeCacheListener {
private final MetricUpdater metrics;
- public SessionStateWatcher(Curator.FileCache fileCache,
- ReloadHandler reloadHandler,
- RemoteSession session,
- MetricUpdater metrics) {
+ RemoteSessionStateWatcher(Curator.FileCache fileCache,
+ ReloadHandler reloadHandler,
+ RemoteSession session,
+ MetricUpdater metrics) {
this.fileCache = fileCache;
this.reloadHandler = reloadHandler;
this.session = session;
@@ -45,7 +45,7 @@ public class SessionStateWatcher implements NodeCacheListener {
}
private void sessionChanged(Session.Status status) {
- log.log(LogLevel.DEBUG, session.logPre()+"Session change: Session " + session.getSessionId() + " changed status to " + status);
+ log.log(LogLevel.DEBUG, session.logPre() + "Session change: Remote session " + session.getSessionId() + " changed status to " + status);
// valid for NEW -> PREPARE transitions, not ACTIVATE -> PREPARE.
if (status.equals(Session.Status.PREPARE)) {
@@ -55,6 +55,8 @@ public class SessionStateWatcher implements NodeCacheListener {
session.makeActive(reloadHandler);
} else if (status.equals(Session.Status.DEACTIVATE)) {
session.deactivate();
+ } else if (status.equals(Session.Status.DELETE)) {
+ session.deactivate();
}
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java
index 3978a1f25f8..64ecc510fe9 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java
@@ -43,7 +43,7 @@ public abstract class Session {
* Represents the status of this session.
*/
public enum Status {
- NEW, PREPARE, ACTIVATE, DEACTIVATE, NONE;
+ NEW, PREPARE, ACTIVATE, DEACTIVATE, DELETE, NONE;
public static Status parse(String data) {
for (Status status : Status.values()) {
@@ -67,4 +67,9 @@ public abstract class Session {
return TenantRepository.logPre(getTenant());
}
+ // in seconds
+ public long getCreateTime() {
+ return zooKeeperClient.readCreateTime();
+ }
+
}
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 10590a26690..6e34511c62d 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
@@ -92,14 +92,14 @@ public class SessionFactoryImpl implements SessionFactory, LocalSessionLoader {
File configApplicationDir,
String applicationName,
long sessionId,
- long currentlyActiveSession,
+ long currentlyActiveSessionId,
boolean internalRedeploy) {
long deployTimestamp = System.currentTimeMillis();
String user = System.getenv("USER");
if (user == null) {
user = "unknown";
}
- DeployData deployData = new DeployData(user, userDir.getAbsolutePath(), applicationName, deployTimestamp, internalRedeploy, sessionId, currentlyActiveSession);
+ DeployData deployData = new DeployData(user, userDir.getAbsolutePath(), applicationName, deployTimestamp, internalRedeploy, sessionId, currentlyActiveSessionId);
return FilesApplicationPackage.fromFileWithDeployData(configApplicationDir, deployData);
}
@@ -128,15 +128,15 @@ public class SessionFactoryImpl implements SessionFactory, LocalSessionLoader {
File existingApp = getSessionAppDir(existingSession.getSessionId());
ApplicationId existingApplicationId = existingSession.getApplicationId();
- long liveApp = getLiveApp(existingApplicationId);
- logger.log(LogLevel.DEBUG, "Create from existing application id " + existingApplicationId + ", live app for it is " + liveApp);
- LocalSession session = create(existingApp, existingApplicationId, liveApp, internalRedeploy, timeoutBudget);
+ long activeSessionId = getActiveSessionId(existingApplicationId);
+ logger.log(LogLevel.DEBUG, "Create from existing application id " + existingApplicationId + ", active session id is " + activeSessionId);
+ LocalSession session = create(existingApp, existingApplicationId, activeSessionId, internalRedeploy, timeoutBudget);
session.setApplicationId(existingApplicationId);
session.setVespaVersion(existingSession.getVespaVersion());
return session;
}
- private LocalSession create(File applicationFile, ApplicationId applicationId, long currentlyActiveSession,
+ private LocalSession create(File applicationFile, ApplicationId applicationId, long currentlyActiveSessionId,
boolean internalRedeploy, TimeoutBudget timeoutBudget) {
long sessionId = sessionCounter.nextSessionId();
Path sessionIdPath = sessionsPath.append(String.valueOf(sessionId));
@@ -155,7 +155,7 @@ public class SessionFactoryImpl implements SessionFactory, LocalSessionLoader {
userApplicationDir,
applicationId.application().value(),
sessionId,
- currentlyActiveSession,
+ currentlyActiveSessionId,
internalRedeploy);
applicationPackage.writeMetaData();
return createSessionFromApplication(applicationPackage, sessionId, sessionZooKeeperClient, timeoutBudget, clock);
@@ -188,7 +188,7 @@ public class SessionFactoryImpl implements SessionFactory, LocalSessionLoader {
return new LocalSession(tenant, sessionId, sessionPreparer, context);
}
- private long getLiveApp(ApplicationId applicationId) {
+ private long getActiveSessionId(ApplicationId applicationId) {
List<ApplicationId> applicationIds = applicationRepo.listApplications();
if (applicationIds.contains(applicationId)) {
return applicationRepo.getSessionIdForApplication(applicationId);
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java
index 114ad936eda..49287669a06 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java
@@ -157,7 +157,9 @@ public class SessionPreparer {
configserverConfig.athenzDnsSuffix(),
configserverConfig.hostedVespa(),
zone,
- rotationsSet);
+ rotationsSet,
+ params.isBootstrap(),
+ ! currentActiveApplicationSet.isPresent());
this.preparedModelsBuilder = new PreparedModelsBuilder(modelFactoryRegistry,
permanentApplicationPackage,
configDefinitionRepo,
@@ -167,7 +169,8 @@ public class SessionPreparer {
logger,
params,
currentActiveApplicationSet,
- properties);
+ properties,
+ configserverConfig);
}
void checkTimeout(String step) {
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 645a2f19d89..415ff268309 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
@@ -16,8 +16,7 @@ import java.util.HashMap;
/**
* A generic session repository that can store any type of session that extends the abstract interface.
*
- * @author lulf
- * @since 5.1
+ * @author Ulf Lilleengen
*/
// TODO: This is a ZK cache. We should probably remove it, or make that explicit
public class SessionRepo<SESSIONTYPE extends Session> {
@@ -25,35 +24,17 @@ public class SessionRepo<SESSIONTYPE extends Session> {
private final HashMap<Long, SESSIONTYPE> sessions = new HashMap<>();
public synchronized void addSession(SESSIONTYPE session) {
- internalAddSession(session);
- }
-
- /** Why is this needed? Because of implementation inheritance - see RemoteSessionRepo */
- protected synchronized final void internalAddSession(SESSIONTYPE session) {
if (sessions.containsKey(session.getSessionId()))
throw new IllegalArgumentException("There already exists a session with id '" + session.getSessionId() + "'");
sessions.put(session.getSessionId(), session);
}
- public synchronized void removeSessionOrThrow(long id) {
- internalRemoveSessionOrThrow(id);
- }
-
- /** Why is this needed? Because of implementation inheritance - see RemoteSessionRepo */
- protected synchronized final void internalRemoveSessionOrThrow(long id) {
+ public synchronized SESSIONTYPE removeSession(long id) {
if ( ! sessions.containsKey(id))
- throw new IllegalArgumentException("No such session exists '" + id + "'");
- sessions.remove(id);
+ throw new IllegalArgumentException("No session with id '" + id + "' exists");
+ return sessions.remove(id);
}
- /**
- * Removes a session in a transaction
- *
- * @param id the id of the session to remove
- * @return the removed session, or null if none was found
- */
- public synchronized SESSIONTYPE removeSession(long id) { return sessions.remove(id); }
-
public void removeSession(long id, NestedTransaction nestedTransaction) {
SessionRepoTransaction transaction = new SessionRepoTransaction();
transaction.addRemoveOperation(id);
@@ -103,7 +84,7 @@ public class SessionRepo<SESSIONTYPE extends Session> {
public class SessionRepoTransaction extends AbstractTransaction {
- public void addRemoveOperation(long sessionIdToRemove) {
+ void addRemoveOperation(long sessionIdToRemove) {
add(new RemoveOperation(sessionIdToRemove));
}
@@ -124,7 +105,7 @@ public class SessionRepo<SESSIONTYPE extends Session> {
((SessionOperation)operation).rollback();
}
- public abstract class SessionOperation implements Transaction.Operation {
+ abstract class SessionOperation implements Transaction.Operation {
abstract void commit();
@@ -137,7 +118,7 @@ public class SessionRepo<SESSIONTYPE extends Session> {
private final long sessionIdToRemove;
private SESSIONTYPE removed = null;
- public RemoveOperation(long sessionIdToRemove) {
+ RemoveOperation(long sessionIdToRemove) {
this.sessionIdToRemove = sessionIdToRemove;
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionZooKeeperClient.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionZooKeeperClient.java
index de903b7f2dd..2049a7f9eb0 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionZooKeeperClient.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionZooKeeperClient.java
@@ -7,6 +7,7 @@ import com.yahoo.config.application.api.ApplicationPackage;
import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.config.provision.NodeFlavors;
import com.yahoo.config.provision.AllocatedHosts;
+import com.yahoo.transaction.NestedTransaction;
import com.yahoo.transaction.Transaction;
import com.yahoo.log.LogLevel;
import com.yahoo.path.Path;
@@ -103,9 +104,7 @@ public class SessionZooKeeperClient {
return getCompletionWaiter(getWaiterPath(ACTIVE_BARRIER));
}
- Curator.CompletionWaiter getUploadWaiter() {
- return getCompletionWaiter(getWaiterPath(UPLOAD_BARRIER));
- }
+ Curator.CompletionWaiter getUploadWaiter() { return getCompletionWaiter(getWaiterPath(UPLOAD_BARRIER)); }
private static final String PREPARE_BARRIER = "prepareBarrier";
private static final String ACTIVE_BARRIER = "activeBarrier";
@@ -128,12 +127,15 @@ public class SessionZooKeeperClient {
return curator.getCompletionWaiter(path, getNumberOfMembers(), serverId);
}
- public void delete() {
+ public void delete(NestedTransaction transaction ) {
try {
log.log(LogLevel.DEBUG, "Deleting " + sessionPath.getAbsolute());
- configCurator.deleteRecurse(sessionPath.getAbsolute());
+ CuratorTransaction curatorTransaction = new CuratorTransaction(curator);
+ CuratorOperations.deleteAll(sessionPath.getAbsolute(), curator).forEach(curatorTransaction::add);
+ transaction.add(curatorTransaction);
+ transaction.commit();
} catch (RuntimeException e) {
- log.log(LogLevel.INFO, "Error deleting session (" + sessionPath.getAbsolute() + ") from zookeeper");
+ log.log(LogLevel.INFO, "Error deleting session (" + sessionPath.getAbsolute() + ") from zookeeper", e);
}
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SilentDeployLogger.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SilentDeployLogger.java
index 445837104d4..92ab6b3fbf5 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SilentDeployLogger.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SilentDeployLogger.java
@@ -7,7 +7,7 @@ import java.util.logging.Logger;
import com.yahoo.config.application.api.DeployLogger;
/**
- * The purpose of this is to mute the log messages from model and application building in {@link RemoteSession} that is triggered by {@link SessionStateWatcher}, since those messages already
+ * The purpose of this is to mute the log messages from model and application building in {@link RemoteSession} that is triggered by {@link RemoteSessionStateWatcher}, since those messages already
* have been emitted by the prepare handler, for the same prepare operation.
*
* @author vegardh
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 ad967f49964..de44a0328f3 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
@@ -101,7 +101,9 @@ public class TenantBuilder {
private void createLocalSessionRepo() {
if (localSessionRepo == null) {
- localSessionRepo = new LocalSessionRepo(tenantFileSystemDirs, localSessionLoader, componentRegistry.getClock(), componentRegistry.getConfigserverConfig().sessionLifetime());
+ localSessionRepo = new LocalSessionRepo(tenantFileSystemDirs, localSessionLoader, componentRegistry.getClock(),
+ componentRegistry.getConfigserverConfig().sessionLifetime(),
+ componentRegistry.getCurator());
}
}
@@ -160,7 +162,7 @@ public class TenantBuilder {
private void createServerDbDirs() {
if (tenantFileSystemDirs == null) {
- tenantFileSystemDirs = new TenantFileSystemDirs(componentRegistry.getConfigserverConfig(), tenant);
+ tenantFileSystemDirs = new TenantFileSystemDirs(componentRegistry.getConfigServerDB(), tenant);
}
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandler.java
index f4c0a9fd47a..f30a58cffea 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandler.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandler.java
@@ -85,6 +85,7 @@ public class TenantRequestHandler implements RequestHandler, ReloadHandler, Host
/**
* Activates the config of the given app. Notifies listeners
+ *
* @param applicationSet the {@link ApplicationSet} to be reloaded
*/
public void reloadConfig(ApplicationSet applicationSet) {
@@ -122,7 +123,7 @@ public class TenantRequestHandler implements RequestHandler, ReloadHandler, Host
private void setLiveApp(ApplicationSet applicationSet) {
ApplicationId id = applicationSet.getId();
- final Collection<String> hostsForApp = applicationSet.getAllHosts();
+ Collection<String> hostsForApp = applicationSet.getAllHosts();
hostRegistry.update(id, hostsForApp);
applicationSet.updateHostMetrics();
tenantMetricUpdater.setApplications(applicationMapper.numApplications());
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/SessionCounter.java b/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/SessionCounter.java
index 2a25553d456..17d0f7e426e 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/SessionCounter.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/SessionCounter.java
@@ -25,4 +25,5 @@ public class SessionCounter extends InitializedCounter {
public long nextSessionId() {
return counter.next();
}
+
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ZKLiveApp.java b/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ZKLiveApp.java
index 8084be1cefa..d7d43dea022 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ZKLiveApp.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ZKLiveApp.java
@@ -14,9 +14,12 @@ import java.util.logging.Logger;
/**
* Responsible for providing data from the currently live application subtree in zookeeper.
- * (i.e. /vespa/config/apps/&lt;id of currently active app&gt;/)
+ * (i.e. /vespa/config/apps/&lt;id of currently active app&gt;/).
*
- * @author tonytv
+ * Note: The application revision ("session") stored in this tree is not necessarily live, just complete,
+ * preparable, prepared or active.
+ *
+ * @author Tony Vaagenes
*/
public class ZKLiveApp {
@@ -34,7 +37,8 @@ public class ZKLiveApp {
* Returns a list of the files (as readers) in the given path. The readers <b>must</b>
* be closed by the caller.
*
- * @param path a path relative to the currently active application (i.e. /vespa/config/apps/&lt;id of currently active app&gt;/).
+ * @param path a path relative to the currently active application
+ * (i.e. /vespa/config/apps/&lt;id of currently active app&gt;/).
* @param fileNameSuffix the suffix of files to return, or null to return all
* @param recursive if true, all files from all subdirectories of this will also be returned
* @return the files in the given path, or an empty list (never null) if the directory does not exist or is empty.
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/package-info.java b/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/package-info.java
index 51a43b5c94c..70671e5dca6 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/package-info.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/package-info.java
@@ -1,6 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
@ExportPackage
package com.yahoo.vespa.config.server.zookeeper;
diff --git a/configserver/src/main/resources/configserver-app/services.xml b/configserver/src/main/resources/configserver-app/services.xml
index 79797854689..8a99869e69a 100644
--- a/configserver/src/main/resources/configserver-app/services.xml
+++ b/configserver/src/main/resources/configserver-app/services.xml
@@ -93,6 +93,7 @@
</handler>
<handler id='com.yahoo.vespa.config.server.http.status.StatusHandler' bundle='configserver'>
<binding>http://*/status</binding>
+ <binding>https://*/status</binding>
</handler>
<handler id='com.yahoo.vespa.config.server.http.v2.TenantHandler' bundle='configserver'>
<binding>http://*/application/v2/tenant/</binding>
diff --git a/configserver/src/main/sh/start-configserver b/configserver/src/main/sh/start-configserver
index 03ce136e13c..8a1cbd0f0a8 100755
--- a/configserver/src/main/sh/start-configserver
+++ b/configserver/src/main/sh/start-configserver
@@ -161,7 +161,7 @@ vespa-run-as-vespa-user vespa-runserver -s configserver -r 30 -p $pidfile -- \
-Xms128m -Xmx2048m \
-XX:+PreserveFramePointer \
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${VESPA_HOME}/var/crash \
- -XX:OnOutOfMemoryError='kill -9 %p' \
+ -XX:+ExitOnOutOfMemoryError \
$jvmargs \
-Djava.library.path=${VESPA_HOME}/lib64 \
-Djava.awt.headless=true \
diff --git a/configserver/src/test/apps/hosted-no-write-access-control/searchdefinitions/music.sd b/configserver/src/test/apps/hosted-no-write-access-control/searchdefinitions/music.sd
new file mode 100644
index 00000000000..78d58b27d4a
--- /dev/null
+++ b/configserver/src/test/apps/hosted-no-write-access-control/searchdefinitions/music.sd
@@ -0,0 +1,10 @@
+# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+search music {
+ document music {
+ field title type string {
+ indexing: index | summary
+ # index-to: default
+ }
+ }
+}
+
diff --git a/configserver/src/test/apps/hosted-no-write-access-control/services.xml b/configserver/src/test/apps/hosted-no-write-access-control/services.xml
new file mode 100644
index 00000000000..c2257ab34f7
--- /dev/null
+++ b/configserver/src/test/apps/hosted-no-write-access-control/services.xml
@@ -0,0 +1,25 @@
+<?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. -->
+<services version="1.0">
+
+ <admin version="3.0">
+ <nodes count='1'/>
+ </admin>
+
+ <jdisc version="1.0">
+ <http>
+ <server id="foo" port="4080" />
+ </http>
+ <search/>
+ <nodes count='1'/>
+ </jdisc>
+
+ <content id="music" version="1.0">
+ <redundancy>1</redundancy>
+ <documents>
+ <document type="music" mode="index" />
+ </documents>
+ <nodes count="2" groups="2"/>
+ </content>
+
+</services>
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java
index 12a4ed7a4c0..6148c276088 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java
@@ -1,11 +1,14 @@
// 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;
+import com.google.common.io.Files;
+import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.component.Version;
import com.yahoo.component.Vtag;
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.Deployment;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.Provisioner;
@@ -13,9 +16,12 @@ import com.yahoo.config.provision.TenantName;
import com.yahoo.io.IOUtils;
import com.yahoo.test.ManualClock;
import com.yahoo.text.Utf8;
+import com.yahoo.vespa.config.server.deploy.DeployTester;
import com.yahoo.vespa.config.server.http.SessionHandlerTest;
import com.yahoo.vespa.config.server.http.v2.PrepareResult;
+import com.yahoo.vespa.config.server.session.LocalSession;
import com.yahoo.vespa.config.server.session.PrepareParams;
+import com.yahoo.vespa.config.server.tenant.Tenant;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
import com.yahoo.vespa.curator.Curator;
import com.yahoo.vespa.curator.mock.MockCurator;
@@ -29,11 +35,19 @@ import java.io.IOException;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
+import java.util.Optional;
import java.util.Set;
+import static org.hamcrest.CoreMatchers.is;
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.assertThat;
import static org.junit.Assert.assertTrue;
/**
@@ -52,6 +66,7 @@ public class ApplicationRepositoryTest {
private ApplicationRepository applicationRepository;
private TenantRepository tenantRepository;
+ private SessionHandlerTest.MockProvisioner provisioner;
private TimeoutBudget timeoutBudget;
@Rule
@@ -66,7 +81,7 @@ public class ApplicationRepositoryTest {
tenantRepository.addTenant(tenant1);
tenantRepository.addTenant(tenant2);
tenantRepository.addTenant(tenant3);
- Provisioner provisioner = new SessionHandlerTest.MockProvisioner();
+ provisioner = new SessionHandlerTest.MockProvisioner();
applicationRepository = new ApplicationRepository(tenantRepository, provisioner, clock);
timeoutBudget = new TimeoutBudget(clock, Duration.ofSeconds(60));
}
@@ -110,7 +125,7 @@ public class ApplicationRepositoryTest {
assertEquals(tenant3, applicationRepository.deleteUnusedTenants(ttlForUnusedTenant, now).iterator().next());
// Delete app used by tenant1, tenant2 still has an application
- applicationRepository.remove(applicationId());
+ applicationRepository.delete(applicationId());
Set<TenantName> tenantsDeleted = applicationRepository.deleteUnusedTenants(Duration.ofMillis(1), now);
assertTrue(tenantsDeleted.contains(tenant1));
assertFalse(tenantsDeleted.contains(tenant2));
@@ -123,14 +138,16 @@ public class ApplicationRepositoryTest {
Version targetVersion = Version.fromString("5.0");
// Always use target for system application
- assertEquals(targetVersion, ApplicationRepository.decideVersion(systemApp, Environment.prod, targetVersion));
- assertEquals(targetVersion, ApplicationRepository.decideVersion(systemApp, Environment.dev, targetVersion));
- assertEquals(targetVersion, ApplicationRepository.decideVersion(systemApp, Environment.perf, targetVersion));
+ assertEquals(targetVersion, ApplicationRepository.decideVersion(systemApp, Environment.prod, targetVersion, false));
+ assertEquals(targetVersion, ApplicationRepository.decideVersion(systemApp, Environment.dev, targetVersion, false));
+ assertEquals(targetVersion, ApplicationRepository.decideVersion(systemApp, Environment.perf, targetVersion, false));
// Target for regular application depends on environment
- assertEquals(targetVersion, ApplicationRepository.decideVersion(regularApp, Environment.prod, targetVersion));
- assertEquals(Vtag.currentVersion, ApplicationRepository.decideVersion(regularApp, Environment.dev, targetVersion));
- assertEquals(Vtag.currentVersion, ApplicationRepository.decideVersion(regularApp, Environment.perf, targetVersion));
+ assertEquals(targetVersion, ApplicationRepository.decideVersion(regularApp, Environment.prod, targetVersion, false));
+ assertEquals(Vtag.currentVersion, ApplicationRepository.decideVersion(regularApp, Environment.dev, targetVersion, false));
+ // If bootstrap, version should be target version
+ assertEquals(targetVersion, ApplicationRepository.decideVersion(regularApp, Environment.dev, targetVersion, true));
+ assertEquals(Vtag.currentVersion, ApplicationRepository.decideVersion(regularApp, Environment.perf, targetVersion, false));
}
@Test
@@ -172,6 +189,100 @@ public class ApplicationRepositoryTest {
return filereferenceDir;
}
+ @Test
+ public void delete() {
+ {
+ PrepareResult result = deployApp(testApp);
+ long sessionId = result.sessionId();
+ Tenant tenant = tenantRepository.getTenant(applicationId().tenant());
+ LocalSession applicationData = tenant.getLocalSessionRepo().getSession(sessionId);
+ assertNotNull(applicationData);
+ assertNotNull(applicationData.getApplicationId());
+ assertNotNull(tenant.getRemoteSessionRepo().getSession(sessionId));
+ assertNotNull(applicationRepository.getActiveSession(applicationId()));
+
+ // Delete app and verify that it has been deleted from repos and provisioner
+ assertTrue(applicationRepository.deleteApplication(applicationId()));
+ assertNull(applicationRepository.getActiveSession(applicationId()));
+ assertNull(tenant.getLocalSessionRepo().getSession(sessionId));
+ assertNull(tenant.getRemoteSessionRepo().getSession(sessionId));
+ assertTrue(provisioner.removed);
+ assertThat(provisioner.lastApplicationId.tenant(), is(tenant.getName()));
+ assertThat(provisioner.lastApplicationId, is(applicationId()));
+
+ assertFalse(applicationRepository.deleteApplication(applicationId()));
+ }
+
+ {
+ deployApp(testApp);
+ assertTrue(applicationRepository.deleteApplication(applicationId()));
+ deployApp(testApp);
+
+ // Deploy another app (with id fooId)
+ ApplicationId fooId = applicationId(tenant2);
+ PrepareParams prepareParams2 = new PrepareParams.Builder().applicationId(fooId).build();
+ deployApp(testApp, prepareParams2);
+ assertNotNull(applicationRepository.getActiveSession(fooId));
+
+ // Delete app with id fooId, should not affect original app
+ assertTrue(applicationRepository.deleteApplication(fooId));
+ assertThat(provisioner.lastApplicationId, is(fooId));
+ assertNotNull(applicationRepository.getActiveSession(applicationId()));
+
+ assertTrue(applicationRepository.deleteApplication(applicationId()));
+ }
+ }
+
+ @Test
+ public void deleteLegacy() {
+ deployApp(testApp);
+ assertNotNull(applicationRepository.getActiveSession(applicationId()));
+ assertTrue(applicationRepository.deleteApplicationLegacy(applicationId()));
+ assertNull(applicationRepository.getActiveSession(applicationId()));
+ assertFalse(applicationRepository.deleteApplicationLegacy(applicationId()));
+ }
+
+ @Test
+ public void testDeletingInactiveSessions() {
+ ManualClock clock = new ManualClock(Instant.now());
+ ConfigserverConfig configserverConfig =
+ new ConfigserverConfig(new ConfigserverConfig.Builder()
+ .configServerDBDir(Files.createTempDir().getAbsolutePath())
+ .configDefinitionsDir(Files.createTempDir().getAbsolutePath())
+ .sessionLifetime(60));
+ DeployTester tester = new DeployTester(configserverConfig, clock);
+ tester.deployApp("src/test/apps/app", "myapp", Instant.now()); // session 2 (numbering starts at 2)
+
+ clock.advance(Duration.ofSeconds(10));
+ Optional<Deployment> deployment2 = tester.redeployFromLocalActive();
+
+ assertTrue(deployment2.isPresent());
+ deployment2.get().activate(); // session 3
+ long activeSessionId = tester.tenant().getApplicationRepo().getSessionIdForApplication(tester.applicationId());
+
+ clock.advance(Duration.ofSeconds(10));
+ Optional<com.yahoo.config.provision.Deployment> deployment3 = tester.redeployFromLocalActive();
+ assertTrue(deployment3.isPresent());
+ deployment3.get().prepare(); // session 4 (not activated)
+
+ LocalSession deployment3session = ((com.yahoo.vespa.config.server.deploy.Deployment) deployment3.get()).session();
+ assertNotEquals(activeSessionId, deployment3session);
+ // No change to active session id
+ assertEquals(activeSessionId, tester.tenant().getApplicationRepo().getSessionIdForApplication(tester.applicationId()));
+ assertEquals(3, tester.tenant().getLocalSessionRepo().listSessions().size());
+
+ clock.advance(Duration.ofHours(1)); // longer than session lifetime
+
+ // All sessions except 3 should be removed after the call to deleteExpiredLocalSessions
+ tester.applicationRepository().deleteExpiredLocalSessions();
+ final Collection<LocalSession> sessions = tester.tenant().getLocalSessionRepo().listSessions();
+ assertEquals(1, sessions.size());
+ assertEquals(3, new ArrayList<>(sessions).get(0).getSessionId());
+
+ // There should be no expired remote sessions in the common case
+ assertEquals(0, applicationRepository.deleteExpiredRemoteSessions(Duration.ofSeconds(0)));
+ }
+
private PrepareResult prepareAndActivateApp(File application) throws IOException {
FilesApplicationPackage appDir = FilesApplicationPackage.fromFile(application);
ApplicationId applicationId = applicationId();
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/ConfigServerBootstrapTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/ConfigServerBootstrapTest.java
index 992d46d3115..293b8e1e8eb 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/ConfigServerBootstrapTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/ConfigServerBootstrapTest.java
@@ -2,13 +2,27 @@
package com.yahoo.vespa.config.server;
import com.yahoo.cloud.config.ConfigserverConfig;
+import com.yahoo.config.model.api.ModelFactory;
+import com.yahoo.config.model.provision.Host;
+import com.yahoo.config.model.provision.Hosts;
+import com.yahoo.config.model.provision.InMemoryProvisioner;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.Environment;
+import com.yahoo.config.provision.RegionName;
+import com.yahoo.config.provision.Version;
+import com.yahoo.config.provision.Zone;
import com.yahoo.container.handler.VipStatus;
import com.yahoo.container.jdisc.config.HealthMonitorConfig;
import com.yahoo.container.jdisc.state.StateMonitor;
import com.yahoo.jdisc.core.SystemTimer;
+import com.yahoo.path.Path;
+import com.yahoo.text.Utf8;
import com.yahoo.vespa.config.server.deploy.DeployTester;
import com.yahoo.vespa.config.server.rpc.RpcServer;
import com.yahoo.vespa.config.server.version.VersionState;
+import com.yahoo.vespa.curator.Curator;
+import com.yahoo.vespa.curator.mock.MockCurator;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -16,8 +30,13 @@ import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
+import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
import java.util.function.BooleanSupplier;
import static org.junit.Assert.assertEquals;
@@ -34,10 +53,11 @@ public class ConfigServerBootstrapTest {
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@Test
- public void testBootStrap() throws Exception {
+ public void testBootstrap() throws Exception {
ConfigserverConfig configserverConfig = createConfigserverConfig(temporaryFolder);
- DeployTester tester = new DeployTester("src/test/apps/hosted/", configserverConfig);
- tester.deployApp("myApp", "4.5.6", Instant.now());
+ InMemoryProvisioner provisioner = new InMemoryProvisioner(true, "host0", "host1", "host3");
+ DeployTester tester = new DeployTester(configserverConfig, provisioner);
+ tester.deployApp("src/test/apps/hosted/", "myApp", "4.5.6", Instant.now());
File versionFile = temporaryFolder.newFile();
VersionState versionState = new VersionState(versionFile);
@@ -45,6 +65,13 @@ public class ConfigServerBootstrapTest {
RpcServer rpcServer = createRpcServer(configserverConfig);
VipStatus vipStatus = new VipStatus();
+ // Take a host away so that there are too few for the application, to verify we can still bootstrap
+ ClusterSpec contentCluster = ClusterSpec.from(ClusterSpec.Type.content,
+ ClusterSpec.Id.from("music"),
+ ClusterSpec.Group.from(0),
+ new com.yahoo.component.Version(4, 5, 6),
+ false);
+ provisioner.allocations().get(contentCluster).remove(0);
ConfigServerBootstrap bootstrap = new ConfigServerBootstrap(tester.applicationRepository(), rpcServer, versionState, createStateMonitor(), vipStatus);
assertFalse(vipStatus.isInRotation());
waitUntil(rpcServer::isRunning, "failed waiting for Rpc server running");
@@ -58,10 +85,10 @@ public class ConfigServerBootstrapTest {
}
@Test
- public void testBootStrapWhenRedeploymentFails() throws Exception {
+ public void testBootstrapWhenRedeploymentFails() throws Exception {
ConfigserverConfig configserverConfig = createConfigserverConfig(temporaryFolder);
- DeployTester tester = new DeployTester("src/test/apps/hosted/", configserverConfig);
- tester.deployApp("myApp", "4.5.6", Instant.now());
+ DeployTester tester = new DeployTester(configserverConfig);
+ tester.deployApp("src/test/apps/hosted/", "myApp", "4.5.6", Instant.now());
File versionFile = temporaryFolder.newFile();
VersionState versionState = new VersionState(versionFile);
@@ -76,7 +103,9 @@ public class ConfigServerBootstrapTest {
RpcServer rpcServer = createRpcServer(configserverConfig);
VipStatus vipStatus = new VipStatus();
ConfigServerBootstrap bootstrap = new ConfigServerBootstrap(tester.applicationRepository(), rpcServer, versionState,
- createStateMonitor(), vipStatus, false /* do not call run method */);
+ createStateMonitor(), vipStatus,
+ ConfigServerBootstrap.MainThread.DO_NOT_START,
+ ConfigServerBootstrap.RedeployingApplicationsFails.CONTINUE);
assertFalse(vipStatus.isInRotation());
// Call method directly, to be sure that it is finished redeploying all applications and we can check status
bootstrap.run();
@@ -85,6 +114,41 @@ public class ConfigServerBootstrapTest {
assertEquals(StateMonitor.Status.initializing, bootstrap.status());
assertFalse(rpcServer.isRunning());
assertFalse(vipStatus.isInRotation());
+
+ bootstrap.deconstruct();
+ }
+
+ // Tests that we do not try to create the config model version stored in zookeeper when not on hosted vespa, since
+ // we are then only able to create the latest version
+ @Test
+ public void testBootstrapNonHostedOneConfigModel() throws Exception {
+ ConfigserverConfig configserverConfig = createConfigserverConfigNonHosted(temporaryFolder);
+ List<ModelFactory> modelFactories = Collections.singletonList(DeployTester.createModelFactory(Version.fromString("1.2.3")));
+ List<Host> hosts = Arrays.asList(createHost("host1", "1.2.3"), createHost("host2", "1.2.3"), createHost("host3", "1.2.3"));
+ InMemoryProvisioner provisioner = new InMemoryProvisioner(new Hosts(hosts), true);
+ Curator curator = new MockCurator();
+ DeployTester tester = new DeployTester(modelFactories, configserverConfig,
+ Clock.systemUTC(), new Zone(Environment.dev, RegionName.defaultName()),
+ provisioner, curator);
+ ApplicationId app = tester.deployApp("src/test/apps/app/", "myApp", "1.2.3", Instant.now());
+
+ File versionFile = temporaryFolder.newFile();
+ VersionState versionState = new VersionState(versionFile);
+ assertTrue(versionState.isUpgraded());
+
+ // Ugly hack, but I see no other way of doing it:
+ // Manipulate application version in zookeeper so that it is an older version than the model we know, which is
+ // the case when upgrading on non-hosted installations
+ curator.set(Path.fromString("/config/v2/tenants/" + app.tenant().value() + "/sessions/2/version"), Utf8.toBytes("1.2.2"));
+
+ RpcServer rpcServer = createRpcServer(configserverConfig);
+ VipStatus vipStatus = new VipStatus();
+ ConfigServerBootstrap bootstrap = new ConfigServerBootstrap(tester.applicationRepository(), rpcServer, versionState,
+ createStateMonitor(), vipStatus);
+
+ waitUntil(rpcServer::isRunning, "failed waiting for Rpc server running");
+ waitUntil(() -> bootstrap.status() == StateMonitor.Status.up, "failed waiting for status 'up'");
+ waitUntil(vipStatus::isInRotation, "failed waiting for server to be in rotation");
}
private void waitUntil(BooleanSupplier booleanSupplier, String messageIfWaitingFails) throws InterruptedException {
@@ -108,13 +172,28 @@ public class ConfigServerBootstrapTest {
}
private static ConfigserverConfig createConfigserverConfig(TemporaryFolder temporaryFolder) throws IOException {
+ return createConfigserverConfig(temporaryFolder, true);
+ }
+
+ private static ConfigserverConfig createConfigserverConfigNonHosted(TemporaryFolder temporaryFolder) throws IOException {
+ return createConfigserverConfig(temporaryFolder, false);
+ }
+
+ private static ConfigserverConfig createConfigserverConfig(TemporaryFolder temporaryFolder, boolean hosted) throws IOException {
return new ConfigserverConfig(new ConfigserverConfig.Builder()
.configServerDBDir(temporaryFolder.newFolder("serverdb").getAbsolutePath())
.configDefinitionsDir(temporaryFolder.newFolder("configdefinitions").getAbsolutePath())
- .hostedVespa(true)
- .multitenant(true));
+ .hostedVespa(hosted)
+ .multitenant(hosted)
+ .maxDurationOfBootstrap(1) /* seconds */
+ .sleepTimeWhenRedeployingFails(0)); /* seconds */
}
+ private Host createHost(String hostname, String version) {
+ return new Host(hostname, Collections.emptyList(), Optional.empty(), Optional.of(com.yahoo.component.Version.fromString(version)));
+ }
+
+
public static class MockRpc extends com.yahoo.vespa.config.server.rpc.MockRpc {
volatile boolean isRunning = false;
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 5ca3deab1fe..0c031556525 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
@@ -73,7 +73,8 @@ public class InjectedGlobalComponentRegistryTest {
zone = Zone.defaultZone();
globalComponentRegistry =
new InjectedGlobalComponentRegistry(curator, configCurator, metrics, modelFactoryRegistry, sessionPreparer, rpcServer, configserverConfig,
- generationCounter, defRepo, permanentApplicationPackage, hostRegistries, hostProvisionerProvider, zone);
+ generationCounter, defRepo, permanentApplicationPackage, hostRegistries, hostProvisionerProvider, zone,
+ new ConfigServerDB(configserverConfig));
}
@Test
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java
index 5d9a5f0fadc..28fc179770a 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java
@@ -50,7 +50,9 @@ public class ModelContextImplTest {
null,
false,
Zone.defaultZone(),
- rotations),
+ rotations,
+ false,
+ false),
Optional.empty(),
new Version(6),
new Version(6));
@@ -66,5 +68,6 @@ public class ModelContextImplTest {
assertTrue(context.properties().zone() instanceof Zone);
assertFalse(context.properties().hostedVespa());
assertThat(context.properties().rotations(), equalTo(rotations));
+ assertThat(context.properties().isFirstTimeDeployment(), equalTo(false));
}
}
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 761d78ee7e0..65cbe65f0d9 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
@@ -6,6 +6,7 @@ import com.yahoo.config.model.api.Model;
import com.yahoo.config.model.api.ModelContext;
import com.yahoo.config.model.api.ModelCreateResult;
import com.yahoo.config.model.api.ModelFactory;
+import com.yahoo.config.model.api.ValidationParameters;
import com.yahoo.config.provision.Version;
import com.yahoo.vespa.config.server.http.UnknownVespaVersionException;
import com.yahoo.vespa.config.server.modelfactory.ModelFactoryRegistry;
@@ -88,7 +89,7 @@ public class ModelFactoryRegistryTest {
}
@Override
- public ModelCreateResult createAndValidateModel(ModelContext modelContext, boolean ignoreValidationErrors) {
+ public ModelCreateResult createAndValidateModel(ModelContext modelContext, ValidationParameters validationParameters) {
return null;
}
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/TestComponentRegistry.java b/configserver/src/test/java/com/yahoo/vespa/config/server/TestComponentRegistry.java
index e4e45d3a014..5f00499598a 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/TestComponentRegistry.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/TestComponentRegistry.java
@@ -23,7 +23,6 @@ import com.yahoo.vespa.curator.mock.MockCurator;
import com.yahoo.vespa.config.server.zookeeper.ConfigCurator;
import com.yahoo.vespa.model.VespaModelFactory;
-import java.io.File;
import java.time.Clock;
import java.util.Collections;
import java.util.Optional;
@@ -51,6 +50,7 @@ public class TestComponentRegistry implements GlobalComponentRegistry {
private final Optional<Provisioner> hostProvisioner;
private final Zone zone;
private final Clock clock;
+ private final ConfigServerDB configServerDB;
private TestComponentRegistry(Curator curator, ConfigCurator configCurator, Metrics metrics,
ModelFactoryRegistry modelFactoryRegistry,
@@ -82,6 +82,7 @@ public class TestComponentRegistry implements GlobalComponentRegistry {
this.sessionPreparer = sessionPreparer;
this.zone = zone;
this.clock = clock;
+ this.configServerDB = new ConfigServerDB(configserverConfig);
}
public static class Builder {
@@ -153,7 +154,7 @@ public class TestComponentRegistry implements GlobalComponentRegistry {
final PermanentApplicationPackage permApp = this.permanentApplicationPackage
.orElse(new PermanentApplicationPackage(configserverConfig));
FileDistributionFactory fileDistributionFactory = this.fileDistributionFactory
- .orElse(new MockFileDistributionFactory(new File(configserverConfig.fileReferencesDir())));
+ .orElse(new MockFileDistributionFactory(configserverConfig));
HostProvisionerProvider hostProvisionerProvider = hostProvisioner.isPresent() ?
HostProvisionerProvider.withProvisioner(hostProvisioner.get()) :
HostProvisionerProvider.empty();
@@ -206,6 +207,9 @@ public class TestComponentRegistry implements GlobalComponentRegistry {
}
@Override
public Clock getClock() { return clock;}
+ @Override
+ public ConfigServerDB getConfigServerDB() { return configServerDB;}
+
public FileDistributionFactory getFileDistributionFactory() { return fileDistributionFactory; }
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/TestConfigDefinitionRepo.java b/configserver/src/test/java/com/yahoo/vespa/config/server/TestConfigDefinitionRepo.java
index 1ec4d20000f..d472f64d228 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/TestConfigDefinitionRepo.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/TestConfigDefinitionRepo.java
@@ -11,8 +11,7 @@ import java.util.LinkedHashMap;
import java.util.Map;
/**
- * @author lulf
- * @since 5.
+ * @author Ulf Lilleengen
*/
public class TestConfigDefinitionRepo implements ConfigDefinitionRepo {
private final Map<ConfigDefinitionKey, ConfigDefinition> repo = new LinkedHashMap<>();
@@ -27,4 +26,7 @@ public class TestConfigDefinitionRepo implements ConfigDefinitionRepo {
public Map<ConfigDefinitionKey, ConfigDefinition> getConfigDefinitions() {
return repo;
}
+
+ @Override
+ public ConfigDefinition get(ConfigDefinitionKey key) { return null; }
}
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 71052c8b463..487e96f17b2 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
@@ -1,8 +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.config.server.application;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import com.github.tomakehurst.wiremock.stubbing.Scenario;
import com.yahoo.config.model.api.Model;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ApplicationName;
@@ -10,151 +10,217 @@ import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.Version;
import com.yahoo.container.jdisc.HttpResponse;
+import com.yahoo.slime.Slime;
+import com.yahoo.vespa.config.SlimeUtils;
import com.yahoo.vespa.config.server.ServerCache;
-import com.yahoo.vespa.config.server.http.SessionHandlerTest;
import com.yahoo.vespa.config.server.monitoring.MetricUpdater;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
+import java.time.Duration;
import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.function.Consumer;
-import static org.hamcrest.CoreMatchers.is;
+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.okJson;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static com.yahoo.vespa.config.server.application.ConfigConvergenceChecker.ServiceResponse;
+import static org.junit.Assert.assertTrue;
/**
* @author Ulf Lilleengen
+ * @author mpolden
*/
public class ConfigConvergenceCheckerTest {
- private static final ObjectMapper mapper = new ObjectMapper();
-
private final TenantName tenant = TenantName.from("mytenant");
private final ApplicationId appId = ApplicationId.from(tenant, ApplicationName.from("myapp"), InstanceName.from("myinstance"));
+
private Application application;
private ConfigConvergenceChecker checker;
- private Map<URI, Long> currentGeneration;
+ private URI service;
@Rule
public TemporaryFolder folder = new TemporaryFolder();
+ @Rule
+ public final WireMockRule wireMock = new WireMockRule(options().dynamicPort(), true);
+
@Before
public void setup() {
- Model mockModel = MockModel.createContainer("localhost", 1337);
+ service = testServer();
+ Model mockModel = MockModel.createContainer(service.getHost(), service.getPort());
application = new Application(mockModel,
new ServerCache(),
3,
false,
Version.fromIntValues(0, 0, 0),
MetricUpdater.createTestUpdater(), appId);
- currentGeneration = new HashMap<>();
- checker = new ConfigConvergenceChecker(
- (client, serviceUri) -> () -> asJson("{\"config\":{\"generation\":"
- + currentGeneration.getOrDefault(serviceUri, 3L)
- + "}}"));
+ checker = new ConfigConvergenceChecker();
+ }
+
+ @Test
+ public void service_convergence() {
+ { // Known service
+ 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));
+ assertResponse("{\n" +
+ " \"url\": \"" + requestUrl.toString() + "\",\n" +
+ " \"host\": \"" + hostAndPort(this.service) + "\",\n" +
+ " \"wantedGeneration\": 3,\n" +
+ " \"converged\": true,\n" +
+ " \"currentGeneration\": 3\n" +
+ "}",
+ 200,
+ serviceResponse);
+ }
+
+ { // Missing service
+ String serviceName = "notPresent:1337";
+ URI requestUrl = testServer().resolve("/serviceconverge/" + serviceName);
+ HttpResponse response = checker.checkService(application, "notPresent:1337", requestUrl,
+ Duration.ofSeconds(5));
+ assertResponse("{\n" +
+ " \"url\": \"" + requestUrl.toString() + "\",\n" +
+ " \"host\": \"" + serviceName + "\",\n" +
+ " \"wantedGeneration\": 3,\n" +
+ " \"problem\": \"Host:port (service) no longer part of application, refetch list of services.\"\n" +
+ "}",
+ 410,
+ response);
+ }
}
@Test
- public void service_convergence() throws Exception {
- ServiceResponse serviceResponse = checker.checkService(application,
- "localhost:1337",
- URI.create("http://foo:234/serviceconverge/localhost:1337"));
- assertEquals(200, serviceResponse.getStatus());
- assertJsonEquals("{\n" +
- " \"url\": \"http://foo:234/serviceconverge/localhost:1337\",\n" +
- " \"host\": \"localhost:1337\",\n" +
- " \"wantedGeneration\": 3,\n" +
- " \"converged\": true,\n" +
- " \"currentGeneration\": 3\n" +
- "}",
- SessionHandlerTest.getRenderedString(serviceResponse));
-
- ServiceResponse hostMissingResponse = checker.checkService(application,
- "notPresent:1337",
- URI.create("http://foo:234/serviceconverge/notPresent:1337"));
- assertEquals(410, hostMissingResponse.getStatus());
- assertJsonEquals("{\n" +
- " \"url\": \"http://foo:234/serviceconverge/notPresent:1337\",\n" +
- " \"host\": \"notPresent:1337\",\n" +
- " \"wantedGeneration\": 3,\n" +
- " \"problem\": \"Host:port (service) no longer part of application, refetch list of services.\"\n" +
- "}",
- SessionHandlerTest.getRenderedString(hostMissingResponse));
+ public void service_list_convergence() {
+ {
+ String serviceName = hostAndPort(this.service);
+ 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));
+ assertResponse("{\n" +
+ " \"services\": [\n" +
+ " {\n" +
+ " \"host\": \"" + serviceUrl.getHost() + "\",\n" +
+ " \"port\": " + serviceUrl.getPort() + ",\n" +
+ " \"type\": \"container\",\n" +
+ " \"url\": \"" + serviceUrl.toString() + "\"\n" +
+ " }\n" +
+ " ],\n" +
+ " \"url\": \"" + requestUrl.toString() + "\",\n" +
+ " \"currentGeneration\": 3,\n" +
+ " \"wantedGeneration\": 3,\n" +
+ " \"converged\": true\n" +
+ "}",
+ 200,
+ response);
+ }
+
+
+ { // Model with two hosts on different generations
+ MockModel model = new MockModel(Arrays.asList(
+ // Reuse hostname and port to avoid the need for two WireMock servers
+ MockModel.createContainerHost(service.getHost(), service.getPort()),
+ MockModel.createContainerHost(service.getHost(), service.getPort()))
+ );
+ Application application = new Application(model, new ServerCache(), 4,
+ false,
+ Version.fromIntValues(0, 0, 0),
+ MetricUpdater.createTestUpdater(), appId);
+
+ String host2 = "host2";
+ wireMock.stubFor(get(urlEqualTo("/state/v1/config")).inScenario("config request")
+ .whenScenarioStateIs(Scenario.STARTED)
+ .willReturn(okJson("{\"config\":{\"generation\":4}}"))
+ .willSetStateTo(host2));
+ wireMock.stubFor(get(urlEqualTo("/state/v1/config")).inScenario("config request")
+ .whenScenarioStateIs(host2)
+ .willReturn(okJson("{\"config\":{\"generation\":3}}")));
+
+ URI requestUrl = testServer().resolve("/serviceconverge");
+ URI serviceUrl = testServer().resolve("/serviceconverge/" + hostAndPort(service));
+ HttpResponse response = checker.servicesToCheck(application, requestUrl, Duration.ofSeconds(5));
+ assertResponse("{\n" +
+ " \"services\": [\n" +
+ " {\n" +
+ " \"host\": \"" + service.getHost() + "\",\n" +
+ " \"port\": " + service.getPort() + ",\n" +
+ " \"type\": \"container\",\n" +
+ " \"url\": \"" + serviceUrl.toString() + "\"\n" +
+ " },\n" +
+ " {\n" +
+ " \"host\": \"" + service.getHost() + "\",\n" +
+ " \"port\": " + service.getPort() + ",\n" +
+ " \"type\": \"container\",\n" +
+ " \"url\": \"" + serviceUrl.toString() + "\"\n" +
+ " }\n" +
+ " ],\n" +
+ " \"url\": \"" + requestUrl.toString() + "\",\n" +
+ " \"currentGeneration\": 3,\n" +
+ " \"wantedGeneration\": 4,\n" +
+ " \"converged\": false\n" +
+ "}",
+ 200,
+ response);
+ }
}
@Test
- public void service_list_convergence() throws Exception {
- HttpResponse serviceListResponse = checker.servicesToCheck(application, URI.create("http://foo:234/serviceconverge"));
- assertEquals(200, serviceListResponse.getStatus());
- assertJsonEquals("{\n" +
- " \"services\": [\n" +
- " {\n" +
- " \"host\": \"localhost\",\n" +
- " \"port\": 1337,\n" +
- " \"type\": \"container\",\n" +
- " \"url\": \"http://foo:234/serviceconverge/localhost:1337\"\n" +
- " }\n" +
- " ],\n" +
- " \"url\": \"http://foo:234/serviceconverge\",\n" +
- " \"currentGeneration\": 3,\n" +
- " \"wantedGeneration\": 3,\n" +
- " \"converged\": true\n" +
- "}",
- SessionHandlerTest.getRenderedString(serviceListResponse));
-
- // Model with two hosts on different generations
- MockModel model = new MockModel(Arrays.asList(
- MockModel.createContainerHost("host1", 1234),
- MockModel.createContainerHost("host2", 1234))
- );
- Application application = new Application(model, new ServerCache(), 4,
- false,
- Version.fromIntValues(0, 0, 0),
- MetricUpdater.createTestUpdater(), appId);
- currentGeneration.put(URI.create("http://host2:1234"), 4L);
- serviceListResponse = checker.servicesToCheck(application, URI.create("http://foo:234/serviceconverge"));
- assertEquals(200, serviceListResponse.getStatus());
- assertJsonEquals("{\n" +
- " \"services\": [\n" +
- " {\n" +
- " \"host\": \"host1\",\n" +
- " \"port\": 1234,\n" +
- " \"type\": \"container\",\n" +
- " \"url\": \"http://foo:234/serviceconverge/host1:1234\"\n" +
- " },\n" +
- " {\n" +
- " \"host\": \"host2\",\n" +
- " \"port\": 1234,\n" +
- " \"type\": \"container\",\n" +
- " \"url\": \"http://foo:234/serviceconverge/host2:1234\"\n" +
- " }\n" +
- " ],\n" +
- " \"url\": \"http://foo:234/serviceconverge\",\n" +
- " \"currentGeneration\": 3,\n" +
- " \"wantedGeneration\": 4,\n" +
- " \"converged\": false\n" +
- "}",
- SessionHandlerTest.getRenderedString(serviceListResponse));
+ 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())
+ .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);
+ }
+
+ private URI testServer() {
+ return URI.create("http://127.0.0.1:" + wireMock.port());
+ }
+
+ private static String hostAndPort(URI uri) {
+ return uri.getHost() + ":" + uri.getPort();
}
- private static void assertJsonEquals(String expected, String actual) {
- assertEquals(asJson(expected), asJson(actual));
+ private static void assertResponse(String json, int status, HttpResponse response) {
+ assertResponse((responseBody) -> {
+ Slime expected = SlimeUtils.jsonToSlime(json.getBytes());
+ Slime actual = SlimeUtils.jsonToSlime(responseBody.getBytes());
+ try {
+ assertEquals(new String((SlimeUtils.toJsonBytes(expected))), new String(SlimeUtils.toJsonBytes(actual)));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }, status, response);
}
- private static JsonNode asJson(String data) {
+ private static void assertResponse(Consumer<String> assertFunc, int status, HttpResponse response) {
+ ByteArrayOutputStream responseBody = new ByteArrayOutputStream();
try {
- return mapper.readTree(data);
+ response.render(responseBody);
+ assertFunc.accept(responseBody.toString());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
+ assertEquals(status, response.getStatus());
}
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployTester.java b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployTester.java
index ce53451ae2e..b70947de0a6 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployTester.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployTester.java
@@ -10,6 +10,7 @@ import com.yahoo.config.model.api.Model;
import com.yahoo.config.model.api.ModelContext;
import com.yahoo.config.model.api.ModelCreateResult;
import com.yahoo.config.model.api.ModelFactory;
+import com.yahoo.config.model.api.ValidationParameters;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.provision.InMemoryProvisioner;
import com.yahoo.config.model.test.MockApplicationPackage;
@@ -62,57 +63,68 @@ public class DeployTester {
private final Clock clock;
private final TenantRepository tenantRepository;
- private final File testApp;
private final ApplicationRepository applicationRepository;
private ApplicationId id;
- public DeployTester(String appPath) {
- this(appPath, Collections.singletonList(createModelFactory(Clock.systemUTC())));
+ public DeployTester() {
+ this(Collections.singletonList(createModelFactory(Clock.systemUTC())));
}
- public DeployTester(String appPath, List<ModelFactory> modelFactories) {
- this(appPath, modelFactories,
+ public DeployTester(List<ModelFactory> modelFactories) {
+ this(modelFactories,
new ConfigserverConfig(new ConfigserverConfig.Builder()
.configServerDBDir(Files.createTempDir().getAbsolutePath())
.configDefinitionsDir(Files.createTempDir().getAbsolutePath())),
Clock.systemUTC());
}
- public DeployTester(String appPath, ConfigserverConfig configserverConfig) {
- this(appPath, Collections.singletonList(createModelFactory(Clock.systemUTC())), configserverConfig, Clock.systemUTC());
+ public DeployTester(ConfigserverConfig configserverConfig) {
+ this(Collections.singletonList(createModelFactory(Clock.systemUTC())), configserverConfig, Clock.systemUTC());
}
- public DeployTester(String appPath, ConfigserverConfig configserverConfig, Clock clock) {
- this(appPath, Collections.singletonList(createModelFactory(clock)), configserverConfig, clock);
+ public DeployTester(ConfigserverConfig configserverConfig, HostProvisioner provisioner) {
+ this(Collections.singletonList(createModelFactory(Clock.systemUTC())), configserverConfig, Clock.systemUTC(), provisioner);
}
- public DeployTester(String appPath, List<ModelFactory> modelFactories, ConfigserverConfig configserverConfig) {
- this(appPath, modelFactories, configserverConfig, Clock.systemUTC());
+ public DeployTester(ConfigserverConfig configserverConfig, Clock clock) {
+ this(Collections.singletonList(createModelFactory(clock)), configserverConfig, clock);
}
- public DeployTester(String appPath, List<ModelFactory> modelFactories, ConfigserverConfig configserverConfig, Clock clock) {
- this(appPath, modelFactories, configserverConfig, clock, Zone.defaultZone());
+ public DeployTester(List<ModelFactory> modelFactories, ConfigserverConfig configserverConfig) {
+ this(modelFactories, configserverConfig, Clock.systemUTC());
}
- public DeployTester(String appPath, List<ModelFactory> modelFactories, ConfigserverConfig configserverConfig, Clock clock, Zone zone) {
- this(appPath, modelFactories, configserverConfig, clock, Zone.defaultZone(), createProvisioner());
+ public DeployTester(List<ModelFactory> modelFactories, ConfigserverConfig configserverConfig, Clock clock) {
+ this(modelFactories, configserverConfig, clock, Zone.defaultZone());
}
- public DeployTester(String appPath, List<ModelFactory> modelFactories, ConfigserverConfig configserverConfig, Clock clock, Zone zone, HostProvisioner provisioner) {
+ public DeployTester(List<ModelFactory> modelFactories, ConfigserverConfig configserverConfig, Clock clock, HostProvisioner provisioner) {
+ this(modelFactories, configserverConfig, clock, Zone.defaultZone(), provisioner);
+ }
+
+ public DeployTester(List<ModelFactory> modelFactories, ConfigserverConfig configserverConfig, Clock clock, Zone zone) {
+ this(modelFactories, configserverConfig, clock, zone, createProvisioner());
+ }
+
+ public DeployTester(List<ModelFactory> modelFactories, ConfigserverConfig configserverConfig, Clock clock, Zone zone, HostProvisioner provisioner) {
+ this(modelFactories, configserverConfig, clock, zone, provisioner, new MockCurator());
+ }
+
+ public DeployTester(List<ModelFactory> modelFactories, ConfigserverConfig configserverConfig, Clock clock, Zone zone,
+ HostProvisioner provisioner, Curator curator) {
this.clock = clock;
- TestComponentRegistry componentRegistry = createComponentRegistry(new MockCurator(), Metrics.createTestMetrics(),
+ TestComponentRegistry componentRegistry = createComponentRegistry(curator, Metrics.createTestMetrics(),
modelFactories, configserverConfig, clock, zone,
provisioner);
try {
- this.testApp = new File(appPath);
this.tenantRepository = new TenantRepository(componentRegistry);
tenantRepository.addTenant(tenantName);
}
catch (Exception e) {
throw new IllegalArgumentException(e);
}
- applicationRepository = new ApplicationRepository(tenantRepository, new ProvisionerAdapter(provisioner), clock);
+ applicationRepository = new ApplicationRepository(tenantRepository, new ProvisionerAdapter(provisioner), clock, configserverConfig);
}
public Tenant tenant() {
@@ -125,6 +137,11 @@ public class DeployTester {
}
/** Create a model factory for a particular version */
+ public static CountingModelFactory createModelFactory(Version version) {
+ return new CountingModelFactory(version, Clock.systemUTC());
+ }
+
+ /** Create a model factory for a particular version */
public static CountingModelFactory createModelFactory(Version version, Clock clock) {
return new CountingModelFactory(version, clock);
}
@@ -135,14 +152,14 @@ public class DeployTester {
/**
* Do the initial "deploy" with the existing API-less code as the deploy API doesn't support first deploys yet.
*/
- public ApplicationId deployApp(String appName, Instant now) {
- return deployApp(appName, null, now);
+ public ApplicationId deployApp(String applicationPath, String appName, Instant now) {
+ return deployApp(applicationPath, appName, null, now);
}
/**
* Do the initial "deploy" with the existing API-less code as the deploy API doesn't support first deploys yet.
*/
- public ApplicationId deployApp(String appName, String vespaVersion, Instant now) {
+ public ApplicationId deployApp(String applicationPath, String appName, String vespaVersion, Instant now) {
Tenant tenant = tenant();
TimeoutBudget timeoutBudget = new TimeoutBudget(clock, Duration.ofSeconds(60));
ApplicationId id = ApplicationId.from(tenant.getName(), ApplicationName.from(appName), InstanceName.defaultName());
@@ -150,7 +167,7 @@ public class DeployTester {
if (vespaVersion != null)
paramsBuilder.vespaVersion(vespaVersion);
- long sessionId = applicationRepository.createSession(id, timeoutBudget, testApp);
+ long sessionId = applicationRepository.createSession(id, timeoutBudget, new File(applicationPath));
applicationRepository.prepare(tenant, sessionId, paramsBuilder.build(), now);
applicationRepository.activate(tenant, sessionId, timeoutBudget, false, false);
this.id = id;
@@ -248,7 +265,7 @@ public class DeployTester {
try {
Instant now = LocalDate.parse("2000-01-01", DateTimeFormatter.ISO_DATE).atStartOfDay().atZone(ZoneOffset.UTC).toInstant();
ApplicationPackage application = new MockApplicationPackage.Builder().withEmptyHosts().withEmptyServices().build();
- DeployState deployState = new DeployState.Builder().applicationPackage(application).now(now).build(true);
+ DeployState deployState = new DeployState.Builder().applicationPackage(application).now(now).build();
return new VespaModel(deployState);
} catch (Exception e) {
throw new RuntimeException(e);
@@ -256,8 +273,8 @@ public class DeployTester {
}
@Override
- public ModelCreateResult createAndValidateModel(ModelContext modelContext, boolean ignoreValidationErrors) {
- if ( ! ignoreValidationErrors)
+ public ModelCreateResult createAndValidateModel(ModelContext modelContext, ValidationParameters validationParameters) {
+ if ( ! validationParameters.ignoreValidationErrors())
throw new IllegalArgumentException("Validation fails");
return new ModelCreateResult(createModel(modelContext), Collections.emptyList());
}
@@ -292,8 +309,8 @@ public class DeployTester {
}
@Override
- public ModelCreateResult createAndValidateModel(ModelContext modelContext, boolean ignoreValidationErrors) {
- ModelCreateResult result = wrapped.createAndValidateModel(modelContext, ignoreValidationErrors);
+ public ModelCreateResult createAndValidateModel(ModelContext modelContext, ValidationParameters validationParameters) {
+ ModelCreateResult result = wrapped.createAndValidateModel(modelContext, validationParameters);
creationCount++;
return result;
}
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 b151ac57352..a184a461ce1 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
@@ -16,15 +16,18 @@ import com.yahoo.test.ManualClock;
import static com.yahoo.vespa.config.server.deploy.DeployTester.CountingModelFactory;
import com.yahoo.vespa.config.server.session.LocalSession;
-import org.junit.Ignore;
import org.junit.Test;
+import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -38,8 +41,9 @@ public class HostedDeployTest {
@Test
public void testRedeployWithVersion() {
- DeployTester tester = new DeployTester("src/test/apps/hosted/", createConfigserverConfig());
- tester.deployApp("myApp", "4.5.6", Instant.now());
+ CountingModelFactory modelFactory = DeployTester.createModelFactory(Version.fromString("4.5.6"), Clock.systemUTC());
+ DeployTester tester = new DeployTester(Collections.singletonList(modelFactory), createConfigserverConfig());
+ tester.deployApp("src/test/apps/hosted/", "myApp", "4.5.6", Instant.now());
Optional<com.yahoo.config.provision.Deployment> deployment = tester.redeployFromLocalActive();
assertTrue(deployment.isPresent());
@@ -50,8 +54,8 @@ public class HostedDeployTest {
@Test
public void testRedeploy() {
- DeployTester tester = new DeployTester("src/test/apps/hosted/", createConfigserverConfig());
- ApplicationId appId = tester.deployApp("myApp", Instant.now());
+ DeployTester tester = new DeployTester(createConfigserverConfig());
+ ApplicationId appId = tester.deployApp("src/test/apps/hosted/", "myApp", Instant.now());
LocalSession s1 = tester.applicationRepository().getActiveSession(appId);
System.out.println("First session: " + s1.getSessionId());
assertFalse(tester.applicationRepository().getActiveSession(appId).getMetaData().isInternalRedeploy());
@@ -72,15 +76,14 @@ public class HostedDeployTest {
modelFactories.add(DeployTester.createModelFactory(Version.fromString("6.1.0"), clock));
modelFactories.add(DeployTester.createModelFactory(Version.fromString("6.2.0"), clock));
modelFactories.add(DeployTester.createModelFactory(Version.fromString("7.0.0"), clock));
- DeployTester tester = new DeployTester("src/test/apps/hosted/", modelFactories, createConfigserverConfig(), clock, Zone.defaultZone());
- ApplicationId app = tester.deployApp("myApp", Instant.now());
+ DeployTester tester = new DeployTester(modelFactories, createConfigserverConfig(), clock, Zone.defaultZone());
+ ApplicationId app = tester.deployApp("src/test/apps/hosted/", "myApp", "6.2.0", Instant.now());
assertEquals(3, tester.getAllocatedHostsOf(app).getHosts().size());
-
}
- /** Test that unused versions are skipped in dev */
+ /** Test that only the minimal set of models are created (model versions used on hosts, the wanted version and the latest version) */
@Test
- public void testDeployMultipleVersionsInDev() {
+ public void testCreateOnlyNeededModelVersions() {
List<Host> hosts = new ArrayList<>();
hosts.add(createHost("host1", "6.0.0"));
hosts.add(createHost("host2", "6.0.2"));
@@ -102,29 +105,53 @@ public class HostedDeployTest {
modelFactories.add(factory710);
modelFactories.add(factory720);
- DeployTester tester = new DeployTester("src/test/apps/hosted/", modelFactories, createConfigserverConfig(),
+ DeployTester tester = new DeployTester(modelFactories, createConfigserverConfig(),
clock, new Zone(Environment.dev, RegionName.defaultName()), provisioner);
- ApplicationId app = tester.deployApp("myApp", Instant.now());
+ // Deploy with version that does not exist on hosts, the model for this version should also be created
+ ApplicationId app = tester.deployApp("src/test/apps/hosted/", "myApp", "7.0.0", Instant.now());
assertEquals(3, tester.getAllocatedHostsOf(app).getHosts().size());
// Check >0 not ==0 as the session watcher thread is running and will redeploy models in the background
assertTrue(factory600.creationCount() > 0);
assertFalse(factory610.creationCount() > 0);
assertTrue(factory620.creationCount() > 0);
- assertFalse(factory700.creationCount() > 0);
+ assertTrue(factory700.creationCount() > 0);
assertTrue(factory710.creationCount() > 0);
assertTrue("Newest is always included", factory720.creationCount() > 0);
}
@Test
+ public void testAccessControlIsOnlyCheckedWhenNoProdDeploymentExists() {
+ // Provisioner does not reuse hosts, so need twice as many hosts as app requires
+ List<Host> hosts = IntStream.rangeClosed(1,6).mapToObj(i -> createHost("host" + i, "6.0.0")).collect(Collectors.toList());
+ InMemoryProvisioner provisioner = new InMemoryProvisioner(new Hosts(hosts), true);
+
+ CountingModelFactory factory600 = DeployTester.createModelFactory(Version.fromString("6.0.0"));
+ CountingModelFactory factory610 = DeployTester.createModelFactory(Version.fromString("6.1.0"));
+ CountingModelFactory factory620 = DeployTester.createModelFactory(Version.fromString("6.2.0"));
+ List<ModelFactory> modelFactories = Arrays.asList(factory600, factory610, factory620);
+
+ DeployTester tester = new DeployTester(modelFactories, createConfigserverConfig(),
+ Clock.systemUTC(), new Zone(Environment.prod, RegionName.defaultName()), provisioner);
+ // Deploy with oldest version
+ ApplicationId app = tester.deployApp("src/test/apps/hosted/", "myApp", "6.0.0", Instant.now());
+ assertEquals(3, tester.getAllocatedHostsOf(app).getHosts().size());
+
+ // Deploy with version that does not exist on hosts and with app package that has no write access control,
+ // validation of access control should not be done, since the app is already deployed in prod
+ app = tester.deployApp("src/test/apps/hosted-no-write-access-control", "myApp", "6.1.0", Instant.now());
+ assertEquals(3, tester.getAllocatedHostsOf(app).getHosts().size());
+ }
+
+ @Test
public void testRedeployAfterExpiredValidationOverride() {
// Old version of model fails, but application disables loading old models until 2016-10-10, so deployment works
ManualClock clock = new ManualClock("2016-10-09T00:00:00");
List<ModelFactory> modelFactories = new ArrayList<>();
modelFactories.add(DeployTester.createModelFactory(clock));
modelFactories.add(DeployTester.createFailingModelFactory(Version.fromIntValues(1, 0, 0))); // older than default
- DeployTester tester = new DeployTester("src/test/apps/validationOverride/", modelFactories, createConfigserverConfig());
- tester.deployApp("myApp", clock.instant());
+ DeployTester tester = new DeployTester(modelFactories, createConfigserverConfig());
+ tester.deployApp("src/test/apps/validationOverride/", "myApp", clock.instant());
// Redeployment from local active works
{
@@ -147,7 +174,7 @@ public class HostedDeployTest {
// However, redeployment from the outside fails after this date
{
try {
- tester.deployApp("myApp", Instant.now());
+ tester.deployApp("src/test/apps/validationOverride/", "myApp", Instant.now());
fail("Expected redeployment to fail");
}
catch (Exception expected) {
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 051e7c9a8f9..da387eb569a 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
@@ -5,10 +5,11 @@ import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Deployment;
import java.time.Duration;
+import java.time.Instant;
import java.util.Optional;
/**
- * @author lulf
+ * @author Ulf Lilleengen
*/
public class MockDeployer implements com.yahoo.config.provision.Deployer {
@@ -20,9 +21,24 @@ public class MockDeployer implements com.yahoo.config.provision.Deployer {
}
@Override
+ public Optional<Deployment> deployFromLocalActive(ApplicationId application, boolean bootstrap) {
+ return Optional.empty();
+ }
+
+ @Override
public Optional<Deployment> deployFromLocalActive(ApplicationId application, Duration timeout) {
lastDeployed = application;
return Optional.empty();
}
+ @Override
+ public Optional<Deployment> deployFromLocalActive(ApplicationId application, Duration timeout, boolean bootstrap) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional<Instant> lastDeployTime(ApplicationId 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 fd023a19617..c6f4df74049 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
@@ -1,28 +1,21 @@
// 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.deploy;
-import com.google.common.io.Files;
-import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.config.model.api.ModelFactory;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ApplicationName;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.Version;
-import com.yahoo.test.ManualClock;
-import com.yahoo.vespa.config.server.session.LocalSession;
import org.junit.Test;
import java.time.Clock;
-import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
import java.util.Optional;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
/**
@@ -34,8 +27,8 @@ public class RedeployTest {
@Test
public void testRedeploy() {
- DeployTester tester = new DeployTester("src/test/apps/app");
- tester.deployApp("myapp", Instant.now());
+ DeployTester tester = new DeployTester();
+ tester.deployApp("src/test/apps/app", "myapp", Instant.now());
Optional<com.yahoo.config.provision.Deployment> deployment = tester.redeployFromLocalActive();
assertTrue(deployment.isPresent());
@@ -54,50 +47,11 @@ public class RedeployTest {
List<ModelFactory> modelFactories = new ArrayList<>();
modelFactories.add(DeployTester.createModelFactory(Clock.systemUTC()));
modelFactories.add(DeployTester.createFailingModelFactory(Version.fromIntValues(1, 0, 0)));
- DeployTester tester = new DeployTester("ignored/app/path", modelFactories);
+ DeployTester tester = new DeployTester(modelFactories);
ApplicationId id = ApplicationId.from(tester.tenant().getName(),
ApplicationName.from("default"),
InstanceName.from("default"));
assertFalse(tester.redeployFromLocalActive(id).isPresent());
}
- @Test
- public void testPurgingOfOldNonActiveDeployments() {
- ManualClock clock = new ManualClock(Instant.now());
- ConfigserverConfig configserverConfig = new ConfigserverConfig(new ConfigserverConfig.Builder()
- .configServerDBDir(Files.createTempDir()
- .getAbsolutePath())
- .configDefinitionsDir(Files.createTempDir()
- .getAbsolutePath())
- .sessionLifetime(60));
- DeployTester tester = new DeployTester("src/test/apps/app", configserverConfig, clock);
- tester.deployApp("myapp", Instant.now()); // session 2 (numbering starts at 2)
-
- clock.advance(Duration.ofSeconds(10));
- Optional<com.yahoo.config.provision.Deployment> deployment2 = tester.redeployFromLocalActive();
-
- assertTrue(deployment2.isPresent());
- deployment2.get().activate(); // session 3
- long activeSessionId = tester.tenant().getApplicationRepo().getSessionIdForApplication(tester.applicationId());
-
- clock.advance(Duration.ofSeconds(10));
- Optional<com.yahoo.config.provision.Deployment> deployment3 = tester.redeployFromLocalActive();
- assertTrue(deployment3.isPresent());
- deployment3.get().prepare(); // session 4 (not activated)
-
- LocalSession deployment3session = ((Deployment) deployment3.get()).session();
- assertNotEquals(activeSessionId, deployment3session);
- // No change to active session id
- assertEquals(activeSessionId, tester.tenant().getApplicationRepo().getSessionIdForApplication(tester.applicationId()));
- assertEquals(3, tester.tenant().getLocalSessionRepo().listSessions().size());
-
- clock.advance(Duration.ofHours(1)); // longer than session lifetime
-
- // All sessions except 3 should be removed after the call to purgeOldSessions
- tester.tenant().getLocalSessionRepo().purgeOldSessions();
- final Collection<LocalSession> sessions = tester.tenant().getLocalSessionRepo().listSessions();
- assertEquals(1, sessions.size());
- assertEquals(3, new ArrayList<>(sessions).get(0).getSessionId());
- }
-
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/filedistribution/FileDirectoryTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/filedistribution/FileDirectoryTest.java
index 88a6b1a0303..400ae4734af 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/filedistribution/FileDirectoryTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/filedistribution/FileDirectoryTest.java
@@ -18,6 +18,7 @@ import java.nio.file.attribute.FileTime;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
public class FileDirectoryTest {
@@ -49,22 +50,29 @@ public class FileDirectoryTest {
String subdirName = "subdir";
File subDirectory = new File(temporaryFolder.getRoot(), subdirName);
- createFileInSubDir(subDirectory, "foo");
+ createFileInSubDir(subDirectory, "foo", "some content");
FileReference fileReference = fileDirectory.addFile(subDirectory);
File dir = fileDirectory.getFile(fileReference);
assertTrue(dir.exists());
assertTrue(new File(dir, "foo").exists());
assertFalse(new File(dir, "doesnotexist").exists());
- assertEquals("1315a322fc323608", fileReference.value());
+ assertEquals("bebc5a1aee74223d", fileReference.value());
+ // Change contents of a file, file reference value should change
+ createFileInSubDir(subDirectory, "foo", "new content");
+ FileReference fileReference2 = fileDirectory.addFile(subDirectory);
+ dir = fileDirectory.getFile(fileReference2);
+ assertTrue(new File(dir, "foo").exists());
+ assertNotEquals(fileReference + " should not be equal to " + fileReference2, fileReference, fileReference2);
+ assertEquals("e5d4b3fe5ee3ede3", fileReference2.value());
// Add a file, should be available and file reference should have another value
- createFileInSubDir(subDirectory, "bar");
- fileReference = fileDirectory.addFile(subDirectory);
- dir = fileDirectory.getFile(fileReference);
+ createFileInSubDir(subDirectory, "bar", "some other content");
+ FileReference fileReference3 = fileDirectory.addFile(subDirectory);
+ dir = fileDirectory.getFile(fileReference3);
assertTrue(new File(dir, "foo").exists());
assertTrue(new File(dir, "bar").exists());
- assertEquals("9ca074b47a4b510c", fileReference.value());
+ assertEquals("894bced3fc9d199b", fileReference3.value());
}
@Test
@@ -73,7 +81,7 @@ public class FileDirectoryTest {
String subdirName = "subdir";
File subDirectory = new File(temporaryFolder.getRoot(), subdirName);
- createFileInSubDir(subDirectory, "foo");
+ createFileInSubDir(subDirectory, "foo", "some content");
FileReference fileReference = fileDirectory.addFile(subDirectory);
File dir = fileDirectory.getFile(fileReference);
assertTrue(dir.exists());
@@ -81,7 +89,7 @@ public class FileDirectoryTest {
assertTrue(foo.exists());
FileTime fooCreatedTimestamp = Files.readAttributes(foo.toPath(), BasicFileAttributes.class).creationTime();
assertFalse(new File(dir, "doesnotexist").exists());
- assertEquals("1315a322fc323608", fileReference.value());
+ assertEquals("bebc5a1aee74223d", fileReference.value());
// Remove a file, directory should be deleted before adding a new file
try { Thread.sleep(1000);} catch (InterruptedException e) {/*ignore */} // Needed since we have timestamp resolution of 1 second
@@ -95,7 +103,7 @@ public class FileDirectoryTest {
// Check that creation timestamp is newer than the old one to be sure that a new file was written
assertTrue(foo2CreatedTimestamp.compareTo(fooCreatedTimestamp) > 0);
assertFalse(new File(dir, "doesnotexist").exists());
- assertEquals("1315a322fc323608", fileReference.value());
+ assertEquals("bebc5a1aee74223d", fileReference.value());
}
// Content in created file is equal to the filename string
@@ -105,11 +113,11 @@ public class FileDirectoryTest {
return fileDirectory.addFile(file);
}
- private void createFileInSubDir(File subDirectory, String filename) throws IOException {
+ private void createFileInSubDir(File subDirectory, String filename, String fileContent) throws IOException {
if (!subDirectory.exists())
subDirectory.mkdirs();
File file = new File(subDirectory, filename);
- IOUtils.writeFile(file, filename, false);
+ IOUtils.writeFile(file, fileContent, false);
}
}
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 eb5dc7a2abf..c5faebe4a28 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
@@ -160,9 +160,6 @@ public class SessionHandlerTest {
}
@Override
- public void delete() { }
-
- @Override
public void delete(NestedTransaction transaction) { }
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java
index d8c5e33ca65..a5564d7ccf9 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java
@@ -19,7 +19,6 @@ import com.yahoo.vespa.config.server.http.HttpErrorResponse;
import com.yahoo.vespa.config.server.http.StaticResponse;
import com.yahoo.vespa.config.server.http.SessionHandlerTest;
import com.yahoo.vespa.config.server.provision.HostProvisionerProvider;
-import com.yahoo.vespa.config.server.session.LocalSession;
import com.yahoo.vespa.config.server.session.PrepareParams;
import com.yahoo.vespa.config.server.tenant.Tenant;
import com.yahoo.vespa.config.server.tenant.TenantBuilder;
@@ -33,12 +32,9 @@ import java.io.IOException;
import java.net.URI;
import java.time.Clock;
-import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
@@ -69,7 +65,6 @@ public class ApplicationHandlerTest {
tenantRepository.addTenant(TenantBuilder.create(componentRegistry, foobar));
provisioner = new SessionHandlerTest.MockProvisioner();
applicationRepository = new ApplicationRepository(tenantRepository,
- new ConfigConvergenceChecker(stateApiFactory),
provisioner, Clock.systemUTC());
listApplicationsHandler = new ListApplicationsHandler(ListApplicationsHandler.testOnlyContext(),
tenantRepository,
@@ -79,25 +74,9 @@ public class ApplicationHandlerTest {
@Test
public void testDelete() throws Exception {
{
- // This block is a real test of the interplay of (most of) the components of the config server
- // TODO: Extract it to ApplicationRepositoryTest, rewrite to bypass the HTTP layer and extend
- // as login is moved from the HTTP layer into ApplicationRepository
-
- PrepareResult result = applicationRepository.deploy(testApp, prepareParams(applicationId));
- long sessionId = result.sessionId();
+ applicationRepository.deploy(testApp, prepareParams(applicationId));
Tenant mytenant = tenantRepository.getTenant(applicationId.tenant());
- LocalSession applicationData = mytenant.getLocalSessionRepo().getSession(sessionId);
- assertNotNull(applicationData);
- assertNotNull(applicationData.getApplicationId());
- assertFalse(provisioner.removed);
-
deleteAndAssertOKResponse(mytenant, applicationId);
- assertTrue(provisioner.removed);
- assertThat(provisioner.lastApplicationId.tenant(), is(mytenantName));
- assertThat(provisioner.lastApplicationId, is(applicationId));
-
- assertNull(mytenant.getLocalSessionRepo().getSession(sessionId));
- assertNull(mytenant.getRemoteSessionRepo().getSession(sessionId));
}
{
@@ -115,7 +94,6 @@ public class ApplicationHandlerTest {
assertApplicationExists(fooId, Zone.defaultZone());
deleteAndAssertOKResponseMocked(fooId, true);
- assertThat(provisioner.lastApplicationId, is(fooId));
assertApplicationExists(applicationId, Zone.defaultZone());
deleteAndAssertOKResponseMocked(applicationId, true);
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 fba4e40000d..75a2d2f778d 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
@@ -100,7 +100,7 @@ public class SessionActiveHandlerTest extends SessionHandlerTest {
applicationRepo = new MemoryTenantApplications();
curator = new MockCurator();
configCurator = ConfigCurator.create(curator);
- localRepo = new LocalSessionRepo(clock);
+ localRepo = new LocalSessionRepo(clock, curator);
pathPrefix = "/application/v2/tenant/" + tenantName + "/session/";
hostProvisioner = new MockProvisioner();
modelFactory = new VespaModelFactory(new NullConfigModelRegistry());
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 7c0e410d244..640f5dafbed 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
@@ -17,6 +17,7 @@ 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.mock.MockCurator;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
@@ -69,7 +70,7 @@ public class SessionCreateHandlerTest extends SessionHandlerTest {
@Before
public void setupRepo() {
applicationRepo = new MemoryTenantApplications();
- localSessionRepo = new LocalSessionRepo(Clock.systemUTC());
+ localSessionRepo = new LocalSessionRepo(Clock.systemUTC(), new MockCurator());
tenantRepository = new TenantRepository(componentRegistry, false);
sessionFactory = new MockSessionFactory();
TenantBuilder tenantBuilder = TenantBuilder.create(componentRegistry, tenant)
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 149bec7ab79..e9b53c95c70 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
@@ -16,6 +16,7 @@ import com.yahoo.jdisc.http.HttpRequest;
import com.yahoo.path.Path;
import com.yahoo.slime.JsonDecoder;
import com.yahoo.slime.Slime;
+import com.yahoo.transaction.NestedTransaction;
import com.yahoo.transaction.Transaction;
import com.yahoo.vespa.config.server.ApplicationRepository;
import com.yahoo.vespa.config.server.TestComponentRegistry;
@@ -74,7 +75,7 @@ public class SessionPrepareHandlerTest extends SessionHandlerTest {
@Before
public void setupRepo() {
curator = new MockCurator();
- localRepo = new LocalSessionRepo(clock);
+ localRepo = new LocalSessionRepo(clock, curator);
pathPrefix = "/application/v2/tenant/" + tenant + "/session/";
preparedMessage = " for tenant '" + tenant + "' prepared.\"";
tenantMessage = ",\"tenant\":\"" + tenant + "\"";
@@ -243,7 +244,7 @@ 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);
+ LocalSessionRepo localRepoDefault = new LocalSessionRepo(clock, curator);
final TenantName defaultTenant = TenantName.from("test2");
TenantBuilder defaultTenantBuilder = TenantBuilder.create(componentRegistry, defaultTenant)
.withLocalSessionRepo(localRepoDefault)
@@ -444,6 +445,6 @@ public class SessionPrepareHandlerTest extends SessionHandlerTest {
}
@Override
- public void delete() { }
+ public void delete(NestedTransaction transaction) { }
}
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/maintenance/TenantsMaintainerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/maintenance/TenantsMaintainerTest.java
index 63ee9dfe3d9..a2f52bc5321 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/maintenance/TenantsMaintainerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/maintenance/TenantsMaintainerTest.java
@@ -23,6 +23,7 @@ public class TenantsMaintainerTest {
MaintainerTester tester = new MaintainerTester();
TenantRepository tenantRepository = tester.tenantRepository();
ApplicationRepository applicationRepository = tester.applicationRepository();
+ File applicationPackage = new File("src/test/apps/app");
TenantName shouldBeDeleted = TenantName.from("to-be-deleted");
TenantName shouldNotBeDeleted = TenantName.from("should-not-be-deleted");
@@ -30,10 +31,8 @@ public class TenantsMaintainerTest {
tenantRepository.addTenant(shouldBeDeleted);
tenantRepository.addTenant(shouldNotBeDeleted);
tenantRepository.addTenant(TenantRepository.HOSTED_VESPA_TENANT);
- applicationRepository.deploy(new File("src/test/apps/app"),
- new PrepareParams.Builder()
- .applicationId(ApplicationId.from(shouldNotBeDeleted, ApplicationName.from("foo"), InstanceName.defaultName()))
- .build());
+
+ applicationRepository.deploy(applicationPackage, prepareParams(shouldNotBeDeleted));
assertNotNull(tenantRepository.getTenant(shouldBeDeleted));
assertNotNull(tenantRepository.getTenant(shouldNotBeDeleted));
@@ -46,5 +45,17 @@ public class TenantsMaintainerTest {
// System tenants should not be deleted
assertNotNull(tenantRepository.getTenant(TenantName.defaultName()));
assertNotNull(tenantRepository.getTenant(TenantRepository.HOSTED_VESPA_TENANT));
+
+ // Add tenant again and deploy
+ tenantRepository.addTenant(shouldBeDeleted);
+ tester.applicationRepository().deploy(applicationPackage, prepareParams(shouldBeDeleted));
+ }
+
+ private PrepareParams prepareParams(TenantName tenantName) {
+ return new PrepareParams.Builder().applicationId(applicationId(tenantName)).build();
+ }
+
+ private ApplicationId applicationId(TenantName tenantName) {
+ return ApplicationId.from(tenantName, ApplicationName.from("foo"), InstanceName.defaultName());
}
}
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 8b89027e4a1..6fa36ba27bb 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
@@ -197,7 +197,7 @@ public class LbServicesProducerTest {
}
private Model createVespaModel(ApplicationPackage applicationPackage, DeployState.Builder deployStateBuilder) throws IOException, SAXException {
- return new VespaModel(new NullConfigModelRegistry(), deployStateBuilder.applicationPackage(applicationPackage).build(true));
+ return new VespaModel(new NullConfigModelRegistry(), deployStateBuilder.applicationPackage(applicationPackage).build());
}
private void assertConfig(LbServicesConfig expected, LbServicesConfig actual) {
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 8c48cfe4b99..8a6d772ff14 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
@@ -102,6 +102,6 @@ public class RoutingProducerTest {
}
private Model createVespaModel(ApplicationPackage applicationPackage, DeployState.Builder deployStateBuilder) throws IOException, SAXException {
- return new VespaModel(new NullConfigModelRegistry(), deployStateBuilder.applicationPackage(applicationPackage).build(true));
+ return new VespaModel(new NullConfigModelRegistry(), deployStateBuilder.applicationPackage(applicationPackage).build());
}
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/model/TestModelFactory.java b/configserver/src/test/java/com/yahoo/vespa/config/server/model/TestModelFactory.java
index 48caba2baef..d736611cdcd 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/model/TestModelFactory.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/model/TestModelFactory.java
@@ -4,6 +4,7 @@ package com.yahoo.vespa.config.server.model;
import com.yahoo.config.model.NullConfigModelRegistry;
import com.yahoo.config.model.api.ModelContext;
import com.yahoo.config.model.api.ModelCreateResult;
+import com.yahoo.config.model.api.ValidationParameters;
import com.yahoo.config.provision.Version;
import com.yahoo.vespa.model.VespaModelFactory;
@@ -21,9 +22,9 @@ public class TestModelFactory extends VespaModelFactory {
// Needed for testing (to get hold of ModelContext)
@Override
- public ModelCreateResult createAndValidateModel(ModelContext modelContext, boolean ignoreValidationErrors) {
+ public ModelCreateResult createAndValidateModel(ModelContext modelContext, ValidationParameters validationParameters) {
this.modelContext = modelContext;
- return super.createAndValidateModel(modelContext, ignoreValidationErrors);
+ return super.createAndValidateModel(modelContext, validationParameters);
}
@Override
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/provision/StaticProvisionerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/provision/StaticProvisionerTest.java
index 07ba1925bf6..badcdf53b77 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/provision/StaticProvisionerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/provision/StaticProvisionerTest.java
@@ -54,7 +54,7 @@ public class StaticProvisionerTest {
.multitenant(true)
.hostedVespa(true)
.build())
- .build(true);
+ .build();
return new VespaModel(new NullConfigModelRegistry(), deployState);
}
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 829dfb978b2..7b9389ada9b 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
@@ -57,7 +57,7 @@ public class LocalSessionRepoTest {
new MemoryTenantApplications(),
tenantFileSystemDirs, new HostRegistry<>(),
tenantName);
- repo = new LocalSessionRepo(tenantFileSystemDirs, loader, clock, 5);
+ repo = new LocalSessionRepo(tenantFileSystemDirs, loader, clock, 5, globalComponentRegistry.getCurator());
}
@Test
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 316c439a3cd..b2cfe3e7575 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
@@ -2,12 +2,12 @@
package com.yahoo.vespa.config.server.session;
import com.google.common.io.Files;
-import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.config.application.api.ApplicationFile;
import com.yahoo.config.provision.*;
import com.yahoo.path.Path;
import com.yahoo.config.model.application.provider.*;
import com.yahoo.slime.Slime;
+import com.yahoo.transaction.NestedTransaction;
import com.yahoo.vespa.config.server.*;
import com.yahoo.vespa.config.server.application.MemoryTenantApplications;
import com.yahoo.vespa.config.server.deploy.DeployHandlerLogger;
@@ -115,7 +115,9 @@ public class LocalSessionTest {
assertTrue(configCurator.exists(sessionNode));
assertTrue(new File(tenantFileSystemDirs.sessionsPath(), "3").exists());
long gen = superModelGenerationCounter.get();
- session.delete();
+ NestedTransaction transaction = new NestedTransaction();
+ session.delete(transaction);
+ transaction.commit();
assertThat(superModelGenerationCounter.get(), is(gen + 1));
assertFalse(configCurator.exists(sessionNode));
assertFalse(new File(tenantFileSystemDirs.sessionsPath(), "3").exists());
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/MockFileDistributionFactory.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/MockFileDistributionFactory.java
index 2c05017d449..e1874c622c2 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/MockFileDistributionFactory.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/MockFileDistributionFactory.java
@@ -14,9 +14,9 @@ public class MockFileDistributionFactory extends FileDistributionFactory {
public final MockFileDistributionProvider mockFileDistributionProvider;
- public MockFileDistributionFactory(File fileReferencesDir) {
- super(new ConfigserverConfig(new ConfigserverConfig.Builder()));
- mockFileDistributionProvider = new MockFileDistributionProvider(fileReferencesDir);
+ public MockFileDistributionFactory(ConfigserverConfig configserverConfig) {
+ super(configserverConfig);
+ mockFileDistributionProvider = new MockFileDistributionProvider(new File(configserverConfig.fileReferencesDir()));
}
@Override
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 b57d2d1a1a1..459604fa333 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
@@ -277,7 +277,7 @@ public class RemoteSessionTest {
public Model loadModel() {
try {
ApplicationPackage application = new MockApplicationPackage.Builder().withEmptyHosts().withEmptyServices().withValidationOverrides(validationOverrides).build();
- DeployState deployState = new DeployState.Builder().applicationPackage(application).now(clock.instant()).build(true);
+ DeployState deployState = new DeployState.Builder().applicationPackage(application).now(clock.instant()).build();
return new VespaModel(deployState);
} catch (Exception e) {
throw new RuntimeException(e);
@@ -285,7 +285,7 @@ public class RemoteSessionTest {
}
@Override
- public ModelCreateResult createAndValidateModel(ModelContext modelContext, boolean ignoreValidationErrors) {
+ public ModelCreateResult createAndValidateModel(ModelContext modelContext, ValidationParameters validationParameters) {
if (throwOnLoad) {
throw new IllegalArgumentException("Foo");
}
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 92fb67fdd54..a221a496e0c 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
@@ -7,6 +7,7 @@ import com.yahoo.config.model.api.ConfigChangeAction;
import com.yahoo.config.model.api.ModelContext;
import com.yahoo.config.model.api.ModelCreateResult;
import com.yahoo.config.model.api.ServiceInfo;
+import com.yahoo.config.model.api.ValidationParameters;
import com.yahoo.config.model.application.provider.*;
import com.yahoo.config.provision.ApplicationName;
import com.yahoo.config.provision.InstanceName;
@@ -21,6 +22,7 @@ import com.yahoo.vespa.config.server.*;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.vespa.config.server.application.MemoryTenantApplications;
import com.yahoo.vespa.config.server.application.PermanentApplicationPackage;
+import com.yahoo.vespa.config.server.configchange.ConfigChangeActions;
import com.yahoo.vespa.config.server.configchange.MockRestartAction;
import com.yahoo.vespa.config.server.configchange.RestartActions;
import com.yahoo.vespa.config.server.deploy.DeployHandlerLogger;
@@ -49,8 +51,7 @@ import static org.hamcrest.Matchers.contains;
import static org.junit.Assert.*;
/**
- * @author lulf
- * @since 5.1
+ * @author Ulf Lilleengen
*/
public class SessionPreparerTest {
@@ -58,6 +59,10 @@ public class SessionPreparerTest {
private static final Path sessionsPath = tenantPath.append("sessions").append("testapp");
private static final File testApp = new File("src/test/apps/app");
private static final File invalidTestApp = new File("src/test/apps/illegalApp");
+ private static final Version version123 = Version.fromIntValues(1, 2, 3);
+ private static final Version version124 = Version.fromIntValues(1, 2, 4);
+ private static final Version version321 = Version.fromIntValues(3, 2, 1);
+ private static final Version version323 = Version.fromIntValues(3, 2, 3);
private MockCurator curator;
private ConfigCurator configCurator;
@@ -83,9 +88,8 @@ public class SessionPreparerTest {
}
private SessionPreparer createPreparer(HostProvisionerProvider hostProvisionerProvider) {
- ModelFactoryRegistry modelFactoryRegistry = new ModelFactoryRegistry(Arrays.asList(
- new TestModelFactory(Version.fromIntValues(1, 2, 3)),
- new TestModelFactory(Version.fromIntValues(3, 2, 1))));
+ ModelFactoryRegistry modelFactoryRegistry =
+ new ModelFactoryRegistry(Arrays.asList(new TestModelFactory(version123), new TestModelFactory(version321)));
return createPreparer(modelFactoryRegistry, hostProvisionerProvider);
}
@@ -104,37 +108,29 @@ public class SessionPreparerTest {
@Test(expected = InvalidApplicationException.class)
public void require_that_application_validation_exception_is_not_caught() throws IOException {
- FilesApplicationPackage app = getApplicationPackage(invalidTestApp);
- preparer.prepare(getContext(app), getLogger(), new PrepareParams.Builder().build(), Optional.empty(), tenantPath, Instant.now());
+ prepare(invalidTestApp);
}
@Test
public void require_that_application_validation_exception_is_ignored_if_forced() throws IOException {
- FilesApplicationPackage app = getApplicationPackage(invalidTestApp);
- preparer.prepare(getContext(app), getLogger(),
- new PrepareParams.Builder().ignoreValidationErrors(true).timeoutBudget(TimeoutBudgetTest.day()).build(),
- Optional.empty(), tenantPath, Instant.now());
+ prepare(invalidTestApp, new PrepareParams.Builder().ignoreValidationErrors(true).timeoutBudget(TimeoutBudgetTest.day()).build());
}
@Test
public void require_that_zookeeper_is_not_written_to_if_dryrun() throws IOException {
- preparer.prepare(getContext(getApplicationPackage(testApp)), getLogger(),
- new PrepareParams.Builder().dryRun(true).timeoutBudget(TimeoutBudgetTest.day()).build(),
- Optional.empty(), tenantPath, Instant.now());
+ prepare(testApp, new PrepareParams.Builder().dryRun(true).timeoutBudget(TimeoutBudgetTest.day()).build());
assertFalse(configCurator.exists(sessionsPath.append(ConfigCurator.USERAPP_ZK_SUBPATH).append("services.xml").getAbsolute()));
}
@Test
public void require_that_filedistribution_is_ignored_on_dryrun() throws IOException {
- preparer.prepare(getContext(getApplicationPackage(testApp)), getLogger(),
- new PrepareParams.Builder().dryRun(true).timeoutBudget(TimeoutBudgetTest.day()).build(),
- Optional.empty(), tenantPath, Instant.now());
+ prepare(testApp, new PrepareParams.Builder().dryRun(true).timeoutBudget(TimeoutBudgetTest.day()).build());
assertThat(fileDistributionFactory.mockFileDistributionProvider.timesCalled, is(0));
}
@Test
public void require_that_application_is_prepared() throws Exception {
- preparer.prepare(getContext(getApplicationPackage(testApp)), getLogger(), new PrepareParams.Builder().build(), Optional.empty(), tenantPath, Instant.now());
+ prepare(testApp);
assertThat(fileDistributionFactory.mockFileDistributionProvider.timesCalled, is(2));
assertTrue(configCurator.exists(sessionsPath.append(ConfigCurator.USERAPP_ZK_SUBPATH).append("services.xml").getAbsolute()));
}
@@ -142,19 +138,19 @@ public class SessionPreparerTest {
@Test
public void require_that_prepare_succeeds_if_newer_version_fails() throws IOException {
ModelFactoryRegistry modelFactoryRegistry = new ModelFactoryRegistry(Arrays.asList(
- new TestModelFactory(Version.fromIntValues(1, 2, 3)),
- new FailingModelFactory(Version.fromIntValues(3, 2, 1), new IllegalArgumentException("BOOHOO"))));
+ new TestModelFactory(version123),
+ new FailingModelFactory(version321, new IllegalArgumentException("BOOHOO"))));
preparer = createPreparer(modelFactoryRegistry, HostProvisionerProvider.empty());
- preparer.prepare(getContext(getApplicationPackage(testApp)), getLogger(), new PrepareParams.Builder().build(), Optional.empty(), tenantPath, Instant.now());
+ prepare(testApp);
}
@Test(expected = InvalidApplicationException.class)
public void require_that_prepare_fails_if_older_version_fails() throws IOException {
ModelFactoryRegistry modelFactoryRegistry = new ModelFactoryRegistry(Arrays.asList(
- new TestModelFactory(Version.fromIntValues(3, 2, 3)),
- new FailingModelFactory(Version.fromIntValues(1, 2, 1), new IllegalArgumentException("BOOHOO"))));
+ new TestModelFactory(version323),
+ new FailingModelFactory(version123, new IllegalArgumentException("BOOHOO"))));
preparer = createPreparer(modelFactoryRegistry, HostProvisionerProvider.empty());
- preparer.prepare(getContext(getApplicationPackage(testApp)), getLogger(), new PrepareParams.Builder().build(), Optional.empty(), tenantPath, Instant.now());
+ prepare(testApp);
}
@Test(expected = InvalidApplicationException.class)
@@ -184,7 +180,7 @@ public class SessionPreparerTest {
.tenant(tenant)
.applicationName("foo").instanceName("quux").build();
PrepareParams params = new PrepareParams.Builder().applicationId(origId).build();
- preparer.prepare(getContext(getApplicationPackage(testApp)), getLogger(), params, Optional.empty(), tenantPath, Instant.now());
+ prepare(testApp, params);
SessionZooKeeperClient zkc = new SessionZooKeeperClient(curator, sessionsPath);
assertTrue(configCurator.exists(sessionsPath.append(SessionZooKeeperClient.APPLICATION_ID_PATH).getAbsolute()));
assertThat(zkc.readApplicationId(), is(origId));
@@ -194,15 +190,10 @@ public class SessionPreparerTest {
public void require_that_config_change_actions_are_collected_from_all_models() throws IOException {
ServiceInfo service = new ServiceInfo("serviceName", "serviceType", null, new HashMap<>(), "configId", "hostName");
ModelFactoryRegistry modelFactoryRegistry = new ModelFactoryRegistry(Arrays.asList(
- new ConfigChangeActionsModelFactory(Version.fromIntValues(1, 2, 3),
- new MockRestartAction("change", Arrays.asList(service))),
- new ConfigChangeActionsModelFactory(Version.fromIntValues(1, 2, 4),
- new MockRestartAction("other change", Arrays.asList(service)))));
+ new ConfigChangeActionsModelFactory(version123, new MockRestartAction("change", Arrays.asList(service))),
+ new ConfigChangeActionsModelFactory(version124, new MockRestartAction("other change", Arrays.asList(service)))));
preparer = createPreparer(modelFactoryRegistry, HostProvisionerProvider.empty());
- List<RestartActions.Entry> actions =
- preparer.prepare(getContext(getApplicationPackage(testApp)), getLogger(),
- new PrepareParams.Builder().build(), Optional.empty(), tenantPath, Instant.now())
- .getRestartActions().getEntries();
+ List<RestartActions.Entry> actions = prepare(testApp).getRestartActions().getEntries();
assertThat(actions.size(), is(1));
assertThat(actions.get(0).getMessages(), equalTo(ImmutableSet.of("change", "other change")));
}
@@ -216,15 +207,13 @@ public class SessionPreparerTest {
final String rotations = "mediasearch.msbe.global.vespa.yahooapis.com";
final ApplicationId applicationId = applicationId("test");
PrepareParams params = new PrepareParams.Builder().applicationId(applicationId).rotations(rotations).build();
- File app = new File("src/test/resources/deploy/app");
- preparer.prepare(getContext(getApplicationPackage(app)), getLogger(), params, Optional.empty(), tenantPath, Instant.now());
+ prepare(new File("src/test/resources/deploy/app"), params);
assertThat(readRotationsFromZK(applicationId), contains(new Rotation(rotations)));
}
@Test
public void require_that_rotations_are_read_from_zookeeper_and_used() throws IOException {
- final Version vespaVersion = Version.fromIntValues(1, 2, 3);
- final TestModelFactory modelFactory = new TestModelFactory(vespaVersion);
+ final TestModelFactory modelFactory = new TestModelFactory(version123);
preparer = createPreparer(new ModelFactoryRegistry(Arrays.asList(modelFactory)),
HostProvisionerProvider.empty());
@@ -233,7 +222,7 @@ public class SessionPreparerTest {
new Rotations(curator, tenantPath).writeRotationsToZooKeeper(applicationId, Collections.singleton(new Rotation(rotations)));
final PrepareParams params = new PrepareParams.Builder().applicationId(applicationId).build();
final File app = new File("src/test/resources/deploy/app");
- preparer.prepare(getContext(getApplicationPackage(app)), getLogger(), params, Optional.empty(), tenantPath, Instant.now());
+ prepare(app, params);
// check that the rotation from zookeeper were used
final ModelContext modelContext = modelFactory.getModelContext();
@@ -244,6 +233,14 @@ public class SessionPreparerTest {
assertThat(readRotationsFromZK(applicationId), contains(new Rotation(rotations)));
}
+ private ConfigChangeActions prepare(File app) throws IOException {
+ return prepare(app, new PrepareParams.Builder().build());
+ }
+
+ private ConfigChangeActions prepare(File app, PrepareParams params) throws IOException {
+ return preparer.prepare(getContext(getApplicationPackage(app)), getLogger(), params, Optional.empty(), tenantPath, Instant.now());
+ }
+
private SessionContext getContext(FilesApplicationPackage app) throws IOException {
return new SessionContext(app, new SessionZooKeeperClient(curator, sessionsPath), app.getAppDir(), new MemoryTenantApplications(), new HostRegistry<>(), new SuperModelGenerationCounter(curator));
}
@@ -271,7 +268,7 @@ public class SessionPreparerTest {
}
@Override
- public ModelCreateResult createAndValidateModel(ModelContext modelContext, boolean ignoreValidationErrors) {
+ public ModelCreateResult createAndValidateModel(ModelContext modelContext, ValidationParameters validationParameters) {
throw exception;
}
}
@@ -289,8 +286,8 @@ public class SessionPreparerTest {
}
@Override
- public ModelCreateResult createAndValidateModel(ModelContext modelContext, boolean ignoreValidationErrors) {
- ModelCreateResult result = super.createAndValidateModel(modelContext, ignoreValidationErrors);
+ public ModelCreateResult createAndValidateModel(ModelContext modelContext, ValidationParameters validationParameters) {
+ ModelCreateResult result = super.createAndValidateModel(modelContext, validationParameters);
return new ModelCreateResult(result.getModel(), Arrays.asList(action));
}
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/zookeeper/ZKApplicationPackageTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/zookeeper/ZKApplicationPackageTest.java
index 06908dbab51..22d5901a29a 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/zookeeper/ZKApplicationPackageTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/zookeeper/ZKApplicationPackageTest.java
@@ -55,7 +55,7 @@ public class ZKApplicationPackageTest {
assertTrue(Pattern.compile(".*<slobroks>.*",Pattern.MULTILINE+Pattern.DOTALL).matcher(IOUtils.readAll(zkApp.getServices())).matches());
assertTrue(Pattern.compile(".*<alias>.*",Pattern.MULTILINE+Pattern.DOTALL).matcher(IOUtils.readAll(zkApp.getHosts())).matches());
assertTrue(Pattern.compile(".*<slobroks>.*",Pattern.MULTILINE+Pattern.DOTALL).matcher(IOUtils.readAll(zkApp.getFile(Path.fromString("services.xml")).createReader())).matches());
- DeployState deployState = new DeployState.Builder().applicationPackage(zkApp).build(true);
+ DeployState deployState = new DeployState.Builder().applicationPackage(zkApp).build();
assertEquals(deployState.getSearchDefinitions().size(), 5);
assertEquals(zkApp.searchDefinitionContents().size(), 5);
assertEquals(IOUtils.readAll(zkApp.getRankingExpression("foo.expression")), "foo()+1\n");
diff --git a/container-accesslogging/src/main/java/com/yahoo/container/logging/AccessLog.java b/container-accesslogging/src/main/java/com/yahoo/container/logging/AccessLog.java
index c22345bad79..6f374d916b7 100644
--- a/container-accesslogging/src/main/java/com/yahoo/container/logging/AccessLog.java
+++ b/container-accesslogging/src/main/java/com/yahoo/container/logging/AccessLog.java
@@ -11,7 +11,7 @@ import java.net.URI;
/**
* Logs to all the configured access logs.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class AccessLog {
diff --git a/container-accesslogging/src/main/java/com/yahoo/container/logging/AccessLogEntry.java b/container-accesslogging/src/main/java/com/yahoo/container/logging/AccessLogEntry.java
index 9139cf8fb07..452466d7d05 100644
--- a/container-accesslogging/src/main/java/com/yahoo/container/logging/AccessLogEntry.java
+++ b/container-accesslogging/src/main/java/com/yahoo/container/logging/AccessLogEntry.java
@@ -31,7 +31,7 @@ import static java.util.stream.Collectors.toMap;
*
* This class is thread-safe, but the inner class {@link AdInfo} is not.
*
- * @author tonytv
+ * @author Tony Vaagenes
* @author bakksjo
* @author bjorncs
*/
diff --git a/container-accesslogging/src/main/java/com/yahoo/container/logging/AccessLogInterface.java b/container-accesslogging/src/main/java/com/yahoo/container/logging/AccessLogInterface.java
index 191eb39f2de..2523174abef 100644
--- a/container-accesslogging/src/main/java/com/yahoo/container/logging/AccessLogInterface.java
+++ b/container-accesslogging/src/main/java/com/yahoo/container/logging/AccessLogInterface.java
@@ -2,7 +2,7 @@
package com.yahoo.container.logging;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public interface AccessLogInterface {
void log(AccessLogEntry accessLogEntry);
diff --git a/container-accesslogging/src/main/java/com/yahoo/container/logging/JSONAccessLog.java b/container-accesslogging/src/main/java/com/yahoo/container/logging/JSONAccessLog.java
index 11da64a99a3..a84903467b6 100644
--- a/container-accesslogging/src/main/java/com/yahoo/container/logging/JSONAccessLog.java
+++ b/container-accesslogging/src/main/java/com/yahoo/container/logging/JSONAccessLog.java
@@ -9,7 +9,7 @@ import java.util.logging.Level;
* Log a message in Vespa JSON access log format.
*
* @author frodelu
- * @author tonytv
+ * @author Tony Vaagenes
*/
public final class JSONAccessLog implements AccessLogInterface {
diff --git a/container-accesslogging/src/main/java/com/yahoo/container/logging/YApacheFormatter.java b/container-accesslogging/src/main/java/com/yahoo/container/logging/YApacheFormatter.java
index db84d62131e..b3a3c0ffc05 100644
--- a/container-accesslogging/src/main/java/com/yahoo/container/logging/YApacheFormatter.java
+++ b/container-accesslogging/src/main/java/com/yahoo/container/logging/YApacheFormatter.java
@@ -16,7 +16,7 @@ import static com.yahoo.container.logging.AccessLogEntry.CookieType;
/**
* Formatting of an {@link AccessLogEntry} in the yapache access log format.
*
- * @author tonytv
+ * @author Tony Vaagenes
* @author bakksjo
*/
public class YApacheFormatter {
diff --git a/container-accesslogging/src/test/java/com/yahoo/container/logging/YApacheLogTestCase.java b/container-accesslogging/src/test/java/com/yahoo/container/logging/YApacheLogTestCase.java
index 53d501d1db7..03a0f85311e 100644
--- a/container-accesslogging/src/test/java/com/yahoo/container/logging/YApacheLogTestCase.java
+++ b/container-accesslogging/src/test/java/com/yahoo/container/logging/YApacheLogTestCase.java
@@ -23,7 +23,7 @@ import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class YApacheLogTestCase {
diff --git a/container-core/pom.xml b/container-core/pom.xml
index d8473c8b541..d51930b5059 100644
--- a/container-core/pom.xml
+++ b/container-core/pom.xml
@@ -95,9 +95,19 @@
<version>${project.version}</version>
<exclusions>
<exclusion>
+ <!-- Pulled in by language-detector in scope compile -->
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </exclusion>
+ <exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
+ <exclusion>
+ <!-- Pulled in by language-detector in scope compile -->
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
</exclusions>
</dependency>
<dependency>
diff --git a/container-core/src/main/java/com/yahoo/container/config/StatisticsRequestHandler.java b/container-core/src/main/java/com/yahoo/container/config/StatisticsRequestHandler.java
index 1e5c22f4023..e0b6392b64a 100644
--- a/container-core/src/main/java/com/yahoo/container/config/StatisticsRequestHandler.java
+++ b/container-core/src/main/java/com/yahoo/container/config/StatisticsRequestHandler.java
@@ -20,8 +20,8 @@ import java.util.concurrent.Executor;
* Handler of statistics http requests. Temporary hack as a step towards a more
* general network interface.
*
- * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
- * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ * @author Steinar Knutsen
+ * @author Einar M R Rosenvinge
*/
public class StatisticsRequestHandler extends ThreadedHttpRequestHandler {
diff --git a/container-core/src/main/java/com/yahoo/container/core/config/BundleLoader.java b/container-core/src/main/java/com/yahoo/container/core/config/BundleLoader.java
index 293771cb0bd..eceb41f9739 100644
--- a/container-core/src/main/java/com/yahoo/container/core/config/BundleLoader.java
+++ b/container-core/src/main/java/com/yahoo/container/core/config/BundleLoader.java
@@ -43,7 +43,7 @@ public class BundleLoader {
private List<Bundle> obtainBundles(FileReference reference, FileAcquirer fileAcquirer)
throws InterruptedException {
- File file = fileAcquirer.waitFor(reference, 15, TimeUnit.MINUTES);
+ File file = fileAcquirer.waitFor(reference, 7, TimeUnit.DAYS);
return osgi.install(file.getAbsolutePath());
}
diff --git a/container-core/src/main/java/com/yahoo/container/core/config/HandlersConfigurerDi.java b/container-core/src/main/java/com/yahoo/container/core/config/HandlersConfigurerDi.java
index cb4a21137a2..55d7de90f33 100644
--- a/container-core/src/main/java/com/yahoo/container/core/config/HandlersConfigurerDi.java
+++ b/container-core/src/main/java/com/yahoo/container/core/config/HandlersConfigurerDi.java
@@ -14,6 +14,7 @@ import com.yahoo.container.di.ComponentDeconstructor;
import com.yahoo.container.di.Container;
import com.yahoo.container.di.componentgraph.core.ComponentGraph;
import com.yahoo.container.di.config.SubscriberFactory;
+import com.yahoo.container.di.osgi.BundleClasses;
import com.yahoo.container.di.osgi.OsgiUtil;
import com.yahoo.container.logging.AccessLog;
import com.yahoo.jdisc.application.OsgiFramework;
@@ -27,13 +28,13 @@ import com.yahoo.osgi.OsgiImpl;
import com.yahoo.statistics.Statistics;
import org.osgi.framework.Bundle;
import org.osgi.framework.wiring.BundleWiring;
-import scala.collection.immutable.Set;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
+import java.util.Set;
import static com.yahoo.collections.CollectionUtil.first;
import static com.yahoo.container.util.Util.quote;
@@ -85,11 +86,7 @@ public class HandlersConfigurerDi {
osgiWrapper = new OsgiWrapper(osgiFramework, vespaContainer.getBundleLoader());
container = new Container(subscriberFactory, configId, deconstructor, osgiWrapper);
- try {
- getNewComponentGraph(discInjector, false);
- } catch (InterruptedException e) {
- throw new RuntimeException("Interrupted while setting up handlers for the first time.");
- }
+ getNewComponentGraph(discInjector, false);
}
private static class OsgiWrapper extends OsgiImpl implements com.yahoo.container.di.Osgi {
@@ -106,9 +103,6 @@ public class HandlersConfigurerDi {
@Override
public BundleClasses getBundleClasses(ComponentSpecification bundleSpec, Set<String> packagesToScan) {
- //Not written in an OO way since FelixFramework resides in JDisc core which for now is pure java,
- //and to load from classpath one needs classes from scalalib.
-
//Temporary hack: Using class name since ClassLoaderOsgiFramework is not available at compile time in this bundle.
if (osgiFramework.getClass().getName().equals("com.yahoo.application.container.impl.ClassLoaderOsgiFramework")) {
Bundle syntheticClassPathBundle = first(osgiFramework.bundles());
@@ -141,10 +135,10 @@ public class HandlersConfigurerDi {
/**
* Wait for new config to arrive and produce the new graph
*/
- public void getNewComponentGraph(Injector discInjector, boolean restartOnRedeploy) throws InterruptedException {
- currentGraph = container.getNewComponentGraph(currentGraph, createFallbackInjector(vespaContainer, discInjector), restartOnRedeploy);
-
- assert (currentGraph.getInstance(RegistriesHack.class) != null); // TODO: Remove, seems quite pointless?
+ public void getNewComponentGraph(Injector discInjector, boolean restartOnRedeploy) {
+ currentGraph = container.getNewComponentGraph(currentGraph,
+ createFallbackInjector(vespaContainer, discInjector),
+ restartOnRedeploy);
}
@SuppressWarnings("deprecation")
diff --git a/container-core/src/main/java/com/yahoo/container/core/config/testutil/HandlersConfigurerTestWrapper.java b/container-core/src/main/java/com/yahoo/container/core/config/testutil/HandlersConfigurerTestWrapper.java
index afbf163500f..42780f75a6c 100644
--- a/container-core/src/main/java/com/yahoo/container/core/config/testutil/HandlersConfigurerTestWrapper.java
+++ b/container-core/src/main/java/com/yahoo/container/core/config/testutil/HandlersConfigurerTestWrapper.java
@@ -23,7 +23,7 @@ import java.util.Set;
* Not for public use.
*
* If possible, please avoid using this class and HandlersConfigurer in your tests
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
*
*/
@@ -111,11 +111,7 @@ public class HandlersConfigurerTestWrapper {
public void reloadConfig() {
configurer.reloadConfig(++lastGeneration);
- try {
- configurer.getNewComponentGraph(Guice.createInjector(), false);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
+ configurer.getNewComponentGraph(Guice.createInjector(), false);
}
public void shutdown() {
diff --git a/container-core/src/main/java/com/yahoo/container/core/slobrok/SlobrokConfigurator.java b/container-core/src/main/java/com/yahoo/container/core/slobrok/SlobrokConfigurator.java
index f2cacb1b080..9472fa07bb5 100644
--- a/container-core/src/main/java/com/yahoo/container/core/slobrok/SlobrokConfigurator.java
+++ b/container-core/src/main/java/com/yahoo/container/core/slobrok/SlobrokConfigurator.java
@@ -10,7 +10,7 @@ import java.util.stream.Collectors;
/**
* Configures which slobrok nodes the container should register with.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class SlobrokConfigurator {
public SlobrokConfigurator(SlobroksConfig config) {
diff --git a/container-core/src/main/java/com/yahoo/container/handler/AccessLogRequestHandler.java b/container-core/src/main/java/com/yahoo/container/handler/AccessLogRequestHandler.java
index 241ae269fc9..c50dee43eaf 100644
--- a/container-core/src/main/java/com/yahoo/container/handler/AccessLogRequestHandler.java
+++ b/container-core/src/main/java/com/yahoo/container/handler/AccessLogRequestHandler.java
@@ -22,6 +22,7 @@ import java.util.concurrent.Executor;
* @author dybis
*/
public class AccessLogRequestHandler extends ThreadedHttpRequestHandler {
+
private final CircularArrayAccessLogKeeper circularArrayAccessLogKeeper;
private final JsonFactory jsonFactory = new JsonFactory();
@@ -53,4 +54,5 @@ public class AccessLogRequestHandler extends ThreadedHttpRequestHandler {
}
};
}
+
}
diff --git a/container-core/src/main/java/com/yahoo/container/handler/ThreadPoolProvider.java b/container-core/src/main/java/com/yahoo/container/handler/ThreadPoolProvider.java
index a9d6521e3eb..4cc3b48fd1a 100644
--- a/container-core/src/main/java/com/yahoo/container/handler/ThreadPoolProvider.java
+++ b/container-core/src/main/java/com/yahoo/container/handler/ThreadPoolProvider.java
@@ -1,6 +1,14 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.handler;
+import com.google.common.util.concurrent.ForwardingExecutorService;
+import com.google.inject.Inject;
+import com.yahoo.component.AbstractComponent;
+import com.yahoo.concurrent.ThreadFactoryFactory;
+import com.yahoo.container.di.componentgraph.Provider;
+import com.yahoo.container.protect.ProcessTerminator;
+import com.yahoo.jdisc.Metric;
+
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
@@ -12,14 +20,6 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
-import com.google.inject.Inject;
-import com.yahoo.container.protect.ProcessTerminator;
-import com.google.common.util.concurrent.ForwardingExecutorService;
-import com.yahoo.component.AbstractComponent;
-import com.yahoo.concurrent.ThreadFactoryFactory;
-import com.yahoo.container.di.componentgraph.Provider;
-import com.yahoo.jdisc.Metric;
-
/**
* A configurable thread pool provider. This provides the worker threads used for normal request processing.
* Request an Executor injected in your component constructor if you want to use it.
@@ -139,7 +139,7 @@ public class ThreadPoolProvider extends AbstractComponent implements Provider<Ex
super.execute(command);
} catch (RejectedExecutionException e) {
metric.add(MetricNames.REJECTED_REQUEST, 1, null);
- long timeSinceLastReturnedThreadMillis = System.currentTimeMillis() - wrapped.lastThreadReturnTimeMillis;
+ long timeSinceLastReturnedThreadMillis = System.currentTimeMillis() - wrapped.lastThreadAssignmentTimeMillis;
if (timeSinceLastReturnedThreadMillis > maxThreadExecutionTimeMillis)
processTerminator.logAndDie("No worker threads have been available for " +
timeSinceLastReturnedThreadMillis + " ms. Shutting down.", true);
@@ -161,7 +161,7 @@ public class ThreadPoolProvider extends AbstractComponent implements Provider<Ex
/** A thread pool executor which maintains the last time a worker completed */
private final static class WorkerCompletionTimingThreadPoolExecutor extends ThreadPoolExecutor {
- volatile long lastThreadReturnTimeMillis = System.currentTimeMillis();
+ volatile long lastThreadAssignmentTimeMillis = System.currentTimeMillis();
private final AtomicLong startedCount = new AtomicLong(0);
private final AtomicLong completedCount = new AtomicLong(0);
@@ -177,13 +177,13 @@ public class ThreadPoolProvider extends AbstractComponent implements Provider<Ex
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
+ lastThreadAssignmentTimeMillis = System.currentTimeMillis();
startedCount.incrementAndGet();
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
- lastThreadReturnTimeMillis = System.currentTimeMillis();
completedCount.incrementAndGet();
}
diff --git a/container-core/src/main/java/com/yahoo/container/handler/test/MockService.java b/container-core/src/main/java/com/yahoo/container/handler/test/MockService.java
index 99d28b9bcf1..7bd18c519eb 100644
--- a/container-core/src/main/java/com/yahoo/container/handler/test/MockService.java
+++ b/container-core/src/main/java/com/yahoo/container/handler/test/MockService.java
@@ -39,12 +39,10 @@ import java.util.logging.Logger;
* for descriptions of the format.
*
* @author lulf
- * @since 5.1.21
*/
@Beta
public class MockService extends LoggingRequestHandler {
- private final static Logger log = Logger.getLogger(MockService.class.getName());
private MockServiceHandler handler;
/**
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/RequestHandlerTestDriver.java b/container-core/src/main/java/com/yahoo/container/jdisc/RequestHandlerTestDriver.java
index 132b1153fc5..22933556d9f 100644
--- a/container-core/src/main/java/com/yahoo/container/jdisc/RequestHandlerTestDriver.java
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/RequestHandlerTestDriver.java
@@ -63,10 +63,16 @@ public class RequestHandlerTestDriver implements AutoCloseable {
return sendRequest(uri, method, "");
}
+ /** Send a POST request */
public MockResponseHandler sendRequest(String uri, HttpRequest.Method method, String body) {
return sendRequest(uri, method, ByteBuffer.wrap(body.getBytes(StandardCharsets.UTF_8)));
}
+ /** Send a POST request with defined content type */
+ public MockResponseHandler sendRequest(String uri, HttpRequest.Method method, String body, String contentType) {
+ return sendRequest(uri, method, ByteBuffer.wrap(body.getBytes(StandardCharsets.UTF_8)), contentType);
+ }
+
public MockResponseHandler sendRequest(String uri, HttpRequest.Method method, ByteBuffer body) {
responseHandler = new MockResponseHandler();
Request request = HttpRequest.newServerRequest(driver, URI.create(uri), method);
@@ -78,6 +84,18 @@ public class RequestHandlerTestDriver implements AutoCloseable {
return responseHandler;
}
+ public MockResponseHandler sendRequest(String uri, HttpRequest.Method method, ByteBuffer body, String contentType) {
+ responseHandler = new MockResponseHandler();
+ Request request = HttpRequest.newServerRequest(driver, URI.create(uri), method);
+ request.context().put("contextVariable", 37); // TODO: Add a method for accepting a Request instead
+ request.headers().put(com.yahoo.jdisc.http.HttpHeaders.Names.CONTENT_TYPE, contentType);
+ ContentChannel requestContent = request.connect(responseHandler);
+ requestContent.write(body, null);
+ requestContent.close(null);
+ request.release();
+ return responseHandler;
+ }
+
/** Replaces all occurrences of 0-9 digits by d's */
public String censorDigits(String s) {
return s.replaceAll("[0-9]","d");
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/ThreadedRequestHandler.java b/container-core/src/main/java/com/yahoo/container/jdisc/ThreadedRequestHandler.java
index 861ee40dbf5..c06aad9644d 100644
--- a/container-core/src/main/java/com/yahoo/container/jdisc/ThreadedRequestHandler.java
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/ThreadedRequestHandler.java
@@ -37,7 +37,7 @@ import javax.annotation.concurrent.GuardedBy;
* Note that this means that subclass handlers are synchronous - the request io can
* continue after completion of the worker thread.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public abstract class ThreadedRequestHandler extends AbstractRequestHandler {
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/VespaHeaders.java b/container-core/src/main/java/com/yahoo/container/jdisc/VespaHeaders.java
index 5fa1a57569b..715038470b9 100644
--- a/container-core/src/main/java/com/yahoo/container/jdisc/VespaHeaders.java
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/VespaHeaders.java
@@ -31,7 +31,7 @@ import com.yahoo.processing.request.ErrorMessage;
*
* @author Einar M R Rosenvinge
* @author Steinar Knutsen
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
* @author bratseth
*/
public final class VespaHeaders {
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/CountMetric.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/CountMetric.java
index 6d670b1795b..e3ee12a5137 100644
--- a/container-core/src/main/java/com/yahoo/container/jdisc/state/CountMetric.java
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/CountMetric.java
@@ -4,7 +4,7 @@ package com.yahoo.container.jdisc.state;
/**
* A metric which is counting an accumulative value
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public final class CountMetric extends MetricValue {
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/GaugeMetric.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/GaugeMetric.java
index 8ab0f5771dc..9b89b8abe52 100644
--- a/container-core/src/main/java/com/yahoo/container/jdisc/state/GaugeMetric.java
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/GaugeMetric.java
@@ -11,7 +11,7 @@ import com.yahoo.collections.Tuple2;
* measured at a point in time. This metric value contains some additional information about the distribution
* of this gauge value in the time interval this metric is for.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public final class GaugeMetric extends MetricValue {
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricDimensions.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricDimensions.java
index a85cba6628d..a03d1a60cfd 100644
--- a/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricDimensions.java
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricDimensions.java
@@ -6,7 +6,7 @@ import java.util.Map;
/**
* A set of metric dimensions, which are key-value string pairs.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public interface MetricDimensions extends Iterable<Map.Entry<String, String>> {
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricSet.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricSet.java
index 1cd106b37ff..e7304b35075 100644
--- a/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricSet.java
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricSet.java
@@ -11,7 +11,7 @@ import java.util.stream.Collectors;
/**
* A set of metrics.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public final class MetricSet implements Iterable<Map.Entry<String, MetricValue>> {
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricValue.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricValue.java
index 4a7b9e9941d..b6cf71343b3 100644
--- a/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricValue.java
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricValue.java
@@ -4,7 +4,7 @@ package com.yahoo.container.jdisc.state;
/**
* A metric value
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public abstract class MetricValue {
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/StateMetricConsumer.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/StateMetricConsumer.java
index 6642b06ff83..dfa791304e0 100644
--- a/container-core/src/main/java/com/yahoo/container/jdisc/state/StateMetricConsumer.java
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/StateMetricConsumer.java
@@ -7,7 +7,7 @@ import com.yahoo.jdisc.application.MetricConsumer;
import java.util.Map;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
final class StateMetricConsumer implements MetricConsumer {
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/StateMonitor.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/StateMonitor.java
index a5be5cb0b0a..c0dc7e8a9ae 100644
--- a/container-core/src/main/java/com/yahoo/container/jdisc/state/StateMonitor.java
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/StateMonitor.java
@@ -20,7 +20,7 @@ import java.util.logging.Logger;
* It is used by jDisc to hand out metric update API endpoints to workers through {@link #newMetricConsumer},
* and to inspect the current accumulated state of metrics through {@link #snapshot}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class StateMonitor extends AbstractComponent {
diff --git a/container-core/src/main/java/com/yahoo/osgi/MockOsgi.java b/container-core/src/main/java/com/yahoo/osgi/MockOsgi.java
index 1046289bb50..45ad02d2cef 100644
--- a/container-core/src/main/java/com/yahoo/osgi/MockOsgi.java
+++ b/container-core/src/main/java/com/yahoo/osgi/MockOsgi.java
@@ -11,7 +11,7 @@ import java.util.Collections;
import java.util.List;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class MockOsgi extends NonWorkingOsgiFramework implements Osgi {
diff --git a/container-core/src/main/java/com/yahoo/osgi/Osgi.java b/container-core/src/main/java/com/yahoo/osgi/Osgi.java
index 69a48bfb17e..31f1146c311 100644
--- a/container-core/src/main/java/com/yahoo/osgi/Osgi.java
+++ b/container-core/src/main/java/com/yahoo/osgi/Osgi.java
@@ -9,7 +9,7 @@ import org.osgi.framework.ServiceReference;
import java.util.List;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public interface Osgi {
diff --git a/container-core/src/main/java/com/yahoo/osgi/OsgiImpl.java b/container-core/src/main/java/com/yahoo/osgi/OsgiImpl.java
index 06ecd67ca6e..d9ccd5c590f 100644
--- a/container-core/src/main/java/com/yahoo/osgi/OsgiImpl.java
+++ b/container-core/src/main/java/com/yahoo/osgi/OsgiImpl.java
@@ -12,7 +12,7 @@ import java.util.List;
import java.util.logging.Logger;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class OsgiImpl implements Osgi {
diff --git a/container-core/src/main/java/com/yahoo/processing/handler/AbstractProcessingHandler.java b/container-core/src/main/java/com/yahoo/processing/handler/AbstractProcessingHandler.java
index 2425ac58c4c..2269bd1358b 100644
--- a/container-core/src/main/java/com/yahoo/processing/handler/AbstractProcessingHandler.java
+++ b/container-core/src/main/java/com/yahoo/processing/handler/AbstractProcessingHandler.java
@@ -48,7 +48,7 @@ import static com.yahoo.component.chain.ChainsConfigurer.prepareChainRegistry;
* COMPONENT: The type of the processing components of which this executes a chain
*
* @author bratseth
- * @author tonyv
+ * @author Tony Vaagenes
* @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
* @since 5.1.6
*/
diff --git a/container-core/src/main/java/com/yahoo/processing/handler/ProcessingHandler.java b/container-core/src/main/java/com/yahoo/processing/handler/ProcessingHandler.java
index cd03f7c45e1..1504a0e861b 100644
--- a/container-core/src/main/java/com/yahoo/processing/handler/ProcessingHandler.java
+++ b/container-core/src/main/java/com/yahoo/processing/handler/ProcessingHandler.java
@@ -15,7 +15,7 @@ import java.util.concurrent.Executor;
/**
* A jDisc request handler which invokes a processing chain to produce the response.
*
- * @author tonytv
+ * @author Tony Vaagenes
* @since 5.1.7
*/
public class ProcessingHandler extends AbstractProcessingHandler<Processor> {
diff --git a/container-core/src/main/java/com/yahoo/processing/rendering/Renderer.java b/container-core/src/main/java/com/yahoo/processing/rendering/Renderer.java
index 8644b7b9c98..d04eda943af 100644
--- a/container-core/src/main/java/com/yahoo/processing/rendering/Renderer.java
+++ b/container-core/src/main/java/com/yahoo/processing/rendering/Renderer.java
@@ -19,7 +19,7 @@ import java.io.OutputStream;
* <li>State mutated during rendering shall be initialized in the init method.</li>
* </ol>
*
- * @author tonytv
+ * @author Tony Vaagenes
* @author Steinar Knutsen
*/
public abstract class Renderer<RESPONSE extends Response> extends AbstractComponent implements Cloneable {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/Path.java b/container-core/src/main/java/com/yahoo/restapi/Path.java
index c6781657b8a..7f78572f2d7 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/Path.java
+++ b/container-core/src/main/java/com/yahoo/restapi/Path.java
@@ -1,5 +1,5 @@
// 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.controller.restapi;
+package com.yahoo.restapi;
import java.util.Arrays;
import java.util.HashMap;
@@ -19,7 +19,7 @@ import java.util.stream.Collectors;
* If the path spec ends with /{*}, it will match urls with any rest path.
* The rest path (not including the trailing slash) will be available as getRest().
*
- * Note that for convenience in common use this has state which is changes as a side effect of each matches
+ * Note that for convenience in common use this has state which changes as a side effect of each matches
* invocation. It is therefore for single thread use.
*
* @author bratseth
@@ -40,6 +40,8 @@ public class Path {
}
/**
+ * Parses the path according to pathSpec - must be called prior to {@link #get}
+ *
* Returns whether this path matches the given template string.
* If the given template has placeholders, their values (accessible by get) are reset by calling this,
* whether or not the path matches the given template.
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/restapi/impl/package-info.java b/container-core/src/main/java/com/yahoo/restapi/package-info.java
index dca8a22a313..57e14a6af34 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/restapi/impl/package-info.java
+++ b/container-core/src/main/java/com/yahoo/restapi/package-info.java
@@ -1,8 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-/**
- * @author Tony Vaagenes
- */
@ExportPackage
-package com.yahoo.vespa.hosted.restapi.impl;
+@PublicApi
+package com.yahoo.restapi;
+import com.yahoo.api.annotations.PublicApi;
import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/container-core/src/test/java/com/yahoo/container/jdisc/ThreadedRequestHandlerTestCase.java b/container-core/src/test/java/com/yahoo/container/jdisc/ThreadedRequestHandlerTestCase.java
index 5c2eee228a4..331c536a531 100644
--- a/container-core/src/test/java/com/yahoo/container/jdisc/ThreadedRequestHandlerTestCase.java
+++ b/container-core/src/test/java/com/yahoo/container/jdisc/ThreadedRequestHandlerTestCase.java
@@ -21,7 +21,7 @@ import java.util.concurrent.TimeUnit;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ThreadedRequestHandlerTestCase {
diff --git a/container-core/src/test/java/com/yahoo/container/jdisc/state/MetricConsumerProviders.java b/container-core/src/test/java/com/yahoo/container/jdisc/state/MetricConsumerProviders.java
index 335e5407e6f..777f43c5607 100644
--- a/container-core/src/test/java/com/yahoo/container/jdisc/state/MetricConsumerProviders.java
+++ b/container-core/src/test/java/com/yahoo/container/jdisc/state/MetricConsumerProviders.java
@@ -5,7 +5,7 @@ import com.google.inject.Provider;
import com.yahoo.jdisc.application.MetricConsumer;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
class MetricConsumerProviders {
diff --git a/container-core/src/test/java/com/yahoo/container/jdisc/state/StateMonitorBenchmarkTest.java b/container-core/src/test/java/com/yahoo/container/jdisc/state/StateMonitorBenchmarkTest.java
index 288270df975..3892f81b8b5 100644
--- a/container-core/src/test/java/com/yahoo/container/jdisc/state/StateMonitorBenchmarkTest.java
+++ b/container-core/src/test/java/com/yahoo/container/jdisc/state/StateMonitorBenchmarkTest.java
@@ -23,7 +23,7 @@ import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class StateMonitorBenchmarkTest {
diff --git a/container-core/src/test/java/com/yahoo/processing/handler/ProcessingHandlerTestCase.java b/container-core/src/test/java/com/yahoo/processing/handler/ProcessingHandlerTestCase.java
index 82ce8ca4c33..ce4d7f5d9cb 100644
--- a/container-core/src/test/java/com/yahoo/processing/handler/ProcessingHandlerTestCase.java
+++ b/container-core/src/test/java/com/yahoo/processing/handler/ProcessingHandlerTestCase.java
@@ -56,7 +56,7 @@ import static org.mockito.Mockito.times;
*
* @author bratseth
* @author gjoranv
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ProcessingHandlerTestCase {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/PathTest.java b/container-core/src/test/java/com/yahoo/restapi/PathTest.java
index 7b1d8d17a5c..9e77dc7b3f0 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/PathTest.java
+++ b/container-core/src/test/java/com/yahoo/restapi/PathTest.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.hosted.controller.restapi.application;
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.restapi;
-import com.yahoo.vespa.hosted.controller.restapi.Path;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
@@ -15,12 +14,12 @@ public class PathTest {
@Test
public void testPath() {
- assertFalse(new Path("").matches("/a/{foo}/bar/{b}"));;
- assertFalse(new Path("///").matches("/a/{foo}/bar/{b}"));;
- assertFalse(new Path("///foo").matches("/a/{foo}/bar/{b}"));;
- assertFalse(new Path("///bar/").matches("/a/{foo}/bar/{b}"));;
+ assertFalse(new Path("").matches("/a/{foo}/bar/{b}"));
+ assertFalse(new Path("///").matches("/a/{foo}/bar/{b}"));
+ assertFalse(new Path("///foo").matches("/a/{foo}/bar/{b}"));
+ assertFalse(new Path("///bar/").matches("/a/{foo}/bar/{b}"));
Path path = new Path("/a/1/bar/fuz");
- assertTrue(path.matches("/a/{foo}/bar/{b}"));;
+ assertTrue(path.matches("/a/{foo}/bar/{b}"));
assertEquals("1", path.get("foo"));
assertEquals("fuz", path.get("b"));
}
diff --git a/container-dependencies-enforcer/pom.xml b/container-dependencies-enforcer/pom.xml
index fa55908befe..bad78ce4182 100644
--- a/container-dependencies-enforcer/pom.xml
+++ b/container-dependencies-enforcer/pom.xml
@@ -51,7 +51,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
- <version>3.0.0-M1</version>
<executions>
<execution>
<!-- To allow running 'mvn enforcer:enforce' from the command line -->
@@ -88,6 +87,9 @@
<include>com.google.inject.extensions:guice-assistedinject:[${guice.version}]:jar:provided</include>
<include>com.google.inject.extensions:guice-multibindings:[${guice.version}]:jar:provided</include>
<include>com.google.inject:guice:[${guice.version}]:jar:provided:no_aop</include>
+ <include>com.sun.activation:javax.activation:[1.2.0]:jar:provided</include>
+ <include>com.sun.xml.bind:jaxb-core:[${jaxb.version}]:jar:provided</include>
+ <include>com.sun.xml.bind:jaxb-impl:[${jaxb.version}]:jar:provided</include>
<include>commons-codec:commons-codec:[1.4]:jar:provided</include>
<include>commons-daemon:commons-daemon:[1.0.3]:jar:provided</include>
<include>commons-logging:commons-logging:[1.1.1]:jar:provided</include>
@@ -96,13 +98,14 @@
<include>javax.servlet:javax.servlet-api:[3.1.0]:jar:provided</include>
<include>javax.validation:validation-api:[${javax.validation-api.version}]:jar:provided</include>
<include>javax.ws.rs:javax.ws.rs-api:[${javax.ws.rs-api.version}]:jar:provided</include>
+ <include>javax.xml.bind:jaxb-api:[${jaxb.version}]:jar:provided</include>
<include>net.jcip:jcip-annotations:[1.0]:jar:provided</include>
<include>net.jpountz.lz4:lz4:[1.3.0]:jar:provided</include>
<include>org.apache.felix:org.apache.felix.framework:[${felix.version}]:jar:provided</include>
<include>org.apache.felix:org.apache.felix.log:[1.0.1]:jar:provided</include>
<include>org.apache.felix:org.apache.felix.main:[${felix.version}]:jar:provided</include>
- <include>org.apache.httpcomponents:httpclient:[4.3.6]:jar:provided</include>
- <include>org.apache.httpcomponents:httpcore:[4.3.3]:jar:provided</include>
+ <include>org.apache.httpcomponents:httpclient:[${apache.httpclient.version}]:jar:provided</include>
+ <include>org.apache.httpcomponents:httpcore:[${apache.httpcore.version}]:jar:provided</include>
<include>org.bouncycastle:bcpkix-jdk15on:[${bouncycastle.version}]:jar:provided</include>
<include>org.bouncycastle:bcprov-jdk15on:[${bouncycastle.version}]:jar:provided</include>
<include>org.eclipse.jetty:jetty-http:[${jetty.version}]:jar:provided</include>
diff --git a/container-dependency-versions/pom.xml b/container-dependency-versions/pom.xml
index 90836b1c07f..ccb8a9c311c 100644
--- a/container-dependency-versions/pom.xml
+++ b/container-dependency-versions/pom.xml
@@ -186,7 +186,7 @@
<version>${javax.ws.rs-api.version}</version>
</dependency>
- <!-- TODO: upgrade jaxb-api artifacts to >=2.3.0. Note that from 2.3, these are OSGi bundles.
+ <!-- TODO: upgrade jaxb artifacts to >=2.3.0.
See https://stackoverflow.com/questions/50237516/proper-fix-for-java-10-complaining-about-illegal-reflection-access-by-jaxb-impl -->
<dependency>
<groupId>javax.xml.bind</groupId>
@@ -204,10 +204,11 @@
<version>${jaxb.version}</version>
</dependency>
<dependency>
- <groupId>javax.activation</groupId>
- <artifactId>javax.activation-api</artifactId>
+ <groupId>com.sun.activation</groupId>
+ <artifactId>javax.activation</artifactId>
<version>1.2.0</version>
</dependency>
+ <!-- jaxb end -->
<dependency>
<groupId>net.jcip</groupId>
@@ -237,12 +238,12 @@
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
- <version>4.3.6</version>
+ <version>${apache.httpclient.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
- <version>4.3.3</version>
+ <version>${apache.httpcore.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
@@ -370,13 +371,6 @@
<version>${mimepull.version}</version>
</dependency>
<dependency>
- <!-- NOT provided from Jdisc runtime, but any project that has unit tests based on
- the 'application' framework MUST use our version. -->
- <groupId>org.scala-lang</groupId>
- <artifactId>scala-library</artifactId>
- <version>${scala.version}</version>
- </dependency>
- <dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
@@ -423,6 +417,12 @@
</dependency>
<dependency>
<!-- NOT provided from jdisc runtime -->
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava-testlib</artifactId>
+ <version>${guava.version}</version>
+ </dependency>
+ <dependency>
+ <!-- NOT provided from jdisc runtime -->
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-continuation</artifactId>
<version>${jetty.version}</version>
@@ -458,14 +458,15 @@
</dependencyManagement>
<properties>
+ <apache.httpclient.version>4.4.1</apache.httpclient.version>
+ <apache.httpcore.version>4.4.1</apache.httpcore.version>
<bouncycastle.version>1.58</bouncycastle.version>
- <felix.version>5.4.0</felix.version>
+ <felix.version>5.6.10</felix.version>
<findbugs.version>1.3.9</findbugs.version>
<guava.version>18.0</guava.version>
<guice.version>3.0</guice.version>
- <jaxb.version>2.2.7</jaxb.version>
+ <jaxb.version>2.3.0</jaxb.version>
<jetty.version>9.4.10.v20180503</jetty.version>
- <scala.version>2.11.12</scala.version> <!-- When updating this, the scala.major-version in parent must also be updated! -->
<slf4j.version>1.7.5</slf4j.version>
<!-- These must be kept in sync with version used by current jersey2.version. -->
diff --git a/container-dev/pom.xml b/container-dev/pom.xml
index ff67e8db9fe..27e2435d825 100644
--- a/container-dev/pom.xml
+++ b/container-dev/pom.xml
@@ -96,10 +96,6 @@
<artifactId>commons-lang</artifactId>
</exclusion>
<exclusion>
- <groupId>org.scala-lang</groupId>
- <artifactId>scala-library</artifactId>
- </exclusion>
- <exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</exclusion>
@@ -121,12 +117,6 @@
<groupId>com.yahoo.vespa</groupId>
<artifactId>container-jersey2</artifactId>
<version>${project.version}</version>
- <exclusions>
- <exclusion>
- <groupId>org.scala-lang</groupId>
- <artifactId>scala-library</artifactId>
- </exclusion>
- </exclusions>
</dependency>
<dependency>
<groupId>com.yahoo.vespa</groupId>
@@ -197,20 +187,9 @@
<groupId>com.yahoo.vespa</groupId>
<artifactId>config-bundle</artifactId>
<version>${project.version}</version>
- <exclusions>
- <exclusion>
- <groupId>org.scala-lang</groupId>
- <artifactId>scala-library</artifactId>
- </exclusion>
- <exclusion>
- <!-- TODO: Remove exclusion when scala-xml is excluded in config-bundle pom -->
- <groupId>org.scala-lang.modules</groupId>
- <artifactId>scala-xml_${scala.major-version}</artifactId>
- </exclusion>
- </exclusions>
</dependency>
- <!-- Dependencies below are added explicitly to exclude transitive deps that are not provided runtime by the container,
+ <!-- NOTE: Dependencies below are added explicitly to exclude transitive deps that are not provided runtime by the container,
and hence make them invisible to user projects' build classpath.
Excluded artifacts should be added explicitly to the application module to make then visible in users' test classpath. -->
<dependency>
@@ -226,6 +205,21 @@
</dependency>
<dependency>
<groupId>com.yahoo.vespa</groupId>
+ <artifactId>linguistics</artifactId>
+ <version>${project.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>com.optimaize.languagedetector</groupId>
+ <artifactId>language-detector</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.opennlp</groupId>
+ <artifactId>opennlp-tools</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
<artifactId>predicate-search-core</artifactId>
<version>${project.version}</version>
<exclusions>
@@ -235,6 +229,6 @@
</exclusion>
</exclusions>
</dependency>
-
+ <!-- DO NOT add dependencies here, but above the NOTE! -->
</dependencies>
</project>
diff --git a/container-di/pom.xml b/container-di/pom.xml
index 97db8bc9b42..96ceedad352 100644
--- a/container-di/pom.xml
+++ b/container-di/pom.xml
@@ -51,15 +51,6 @@
<scope>provided</scope>
</dependency>
<dependency>
- <groupId>org.scala-lang</groupId>
- <artifactId>scala-library</artifactId>
- </dependency>
- <dependency>
- <groupId>org.scalatest</groupId>
- <artifactId>scalatest_${scala.major-version}</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
<groupId>com.yahoo.vespa</groupId>
<artifactId>vespajlib</artifactId>
<version>${project.version}</version>
@@ -122,45 +113,8 @@
</executions>
</plugin>
<plugin>
- <groupId>net.alchim31.maven</groupId>
- <artifactId>scala-maven-plugin</artifactId>
- <executions>
- <execution>
- <id>compile</id>
- <goals>
- <goal>compile</goal>
- </goals>
- <phase>compile</phase>
- </execution>
- <execution>
- <id>test-compile</id>
- <goals>
- <goal>testCompile</goal>
- </goals>
- <phase>test-compile</phase>
- </execution>
- <execution>
- <phase>process-resources</phase>
- <goals>
- <goal>compile</goal>
- </goals>
- </execution>
- <execution>
- <phase>process-test-resources</phase>
- <id>early-test-compile</id>
- <goals>
- <goal>testCompile</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
- <configuration>
- <!-- Exclude package with known scala-java interaction issue. -->
- <excludePackageNames>com.yahoo.container.di</excludePackageNames>
- </configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
diff --git a/container-di/src/main/java/com/yahoo/container/bundle/MockBundle.java b/container-di/src/main/java/com/yahoo/container/bundle/MockBundle.java
new file mode 100644
index 00000000000..a6524b41886
--- /dev/null
+++ b/container-di/src/main/java/com/yahoo/container/bundle/MockBundle.java
@@ -0,0 +1,264 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.bundle;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Version;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Wire;
+
+import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author gjoranv
+ * @author ollivir
+ */
+public class MockBundle implements Bundle, BundleWiring {
+ public static final String SymbolicName = "mock-bundle";
+ public static final Version BundleVersion = new Version(1, 0, 0);
+
+ private static final Class<BundleWiring> bundleWiringClass = BundleWiring.class;
+
+ @Override
+ public int getState() {
+ return Bundle.ACTIVE;
+ }
+
+ @Override
+ public void start(int options) {
+ }
+
+ @Override
+ public void start() {
+ }
+
+ @Override
+ public void stop(int options) {
+ }
+
+ @Override
+ public void stop() {
+ }
+
+ @Override
+ public void update(InputStream input) {
+ }
+
+ @Override
+ public void update() {
+ }
+
+ @Override
+ public void uninstall() {
+ }
+
+ @Override
+ public Dictionary<String, String> getHeaders(String locale) {
+ return getHeaders();
+ }
+
+ @Override
+ public String getSymbolicName() {
+ return SymbolicName;
+ }
+
+ @Override
+ public Version getVersion() {
+ return BundleVersion;
+ }
+
+ @Override
+ public String getLocation() {
+ return getSymbolicName();
+ }
+
+ @Override
+ public long getBundleId() {
+ return 0L;
+ }
+
+ @Override
+ public Dictionary<String, String> getHeaders() {
+ return new Hashtable<>();
+ }
+
+ @Override
+ public ServiceReference<?>[] getRegisteredServices() {
+ return new ServiceReference<?>[0];
+ }
+
+ @Override
+ public ServiceReference<?>[] getServicesInUse() {
+ return getRegisteredServices();
+ }
+
+ @Override
+ public boolean hasPermission(Object permission) {
+ return true;
+ }
+
+ @Override
+ public URL getResource(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Class<?> loadClass(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Enumeration<URL> getResources(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Enumeration<String> getEntryPaths(String path) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public URL getEntry(String path) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Enumeration<URL> findEntries(String path, String filePattern, boolean recurse) {
+ return Collections.emptyEnumeration();
+ }
+
+
+ @Override
+ public long getLastModified() {
+ return 1L;
+ }
+
+ @Override
+ public BundleContext getBundleContext() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Map<X509Certificate, List<X509Certificate>> getSignerCertificates(int signersType) {
+ return Collections.emptyMap();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> T adapt(Class<T> type) {
+ if (type.equals(bundleWiringClass)) {
+ return (T) this;
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @Override
+ public File getDataFile(String filename) {
+ return null;
+ }
+
+ @Override
+ public int compareTo(Bundle o) {
+ return Long.compare(getBundleId(), o.getBundleId());
+ }
+
+
+ //TODO: replace with mockito
+ @Override
+ public List<URL> findEntries(String p1, String p2, int p3) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<Wire> getRequiredResourceWires(String p1) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<Capability> getResourceCapabilities(String p1) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isCurrent() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<BundleWire> getRequiredWires(String p1) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<BundleCapability> getCapabilities(String p1) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<Wire> getProvidedResourceWires(String p1) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<BundleWire> getProvidedWires(String p1) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public BundleRevision getRevision() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<Requirement> getResourceRequirements(String p1) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isInUse() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Collection<String> listResources(String p1, String p2, int p3) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public ClassLoader getClassLoader() {
+ return MockBundle.class.getClassLoader();
+ }
+
+ @Override
+ public List<BundleRequirement> getRequirements(String p1) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public BundleRevision getResource() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Bundle getBundle() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/container-di/src/main/java/com/yahoo/container/di/CloudSubscriberFactory.java b/container-di/src/main/java/com/yahoo/container/di/CloudSubscriberFactory.java
new file mode 100644
index 00000000000..3f3991760e3
--- /dev/null
+++ b/container-di/src/main/java/com/yahoo/container/di/CloudSubscriberFactory.java
@@ -0,0 +1,148 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.di;
+
+import com.yahoo.config.ConfigInstance;
+import com.yahoo.config.subscription.ConfigHandle;
+import com.yahoo.config.subscription.ConfigSource;
+import com.yahoo.config.subscription.ConfigSourceSet;
+import com.yahoo.config.subscription.ConfigSubscriber;
+import com.yahoo.container.di.config.Subscriber;
+import com.yahoo.container.di.config.SubscriberFactory;
+import com.yahoo.vespa.config.ConfigKey;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class CloudSubscriberFactory implements SubscriberFactory {
+ private static final Logger log = Logger.getLogger(CloudSubscriberFactory.class.getName());
+
+ private final ConfigSource configSource;
+ private Optional<Long> testGeneration = Optional.empty();
+ private Map<CloudSubscriber, Integer> activeSubscribers = new WeakHashMap<>();
+
+ public CloudSubscriberFactory(ConfigSource configSource) {
+ this.configSource = configSource;
+ }
+
+ @Override
+ public Subscriber getSubscriber(Set<? extends ConfigKey<?>> configKeys) {
+ Set<ConfigKey<ConfigInstance>> subscriptionKeys = new HashSet<>();
+ for(ConfigKey<?> key: configKeys) {
+ @SuppressWarnings("unchecked") // ConfigKey is defined as <CONFIGCLASS extends ConfigInstance>
+ ConfigKey<ConfigInstance> invariant = (ConfigKey<ConfigInstance>) key;
+ subscriptionKeys.add(invariant);
+ }
+ CloudSubscriber subscriber = new CloudSubscriber(subscriptionKeys, configSource);
+
+ testGeneration.ifPresent(subscriber.subscriber::reload); //TODO: test specific code, remove
+ activeSubscribers.put(subscriber, 0);
+
+ return subscriber;
+ }
+
+ //TODO: test specific code, remove
+ @Override
+ public void reloadActiveSubscribers(long generation) {
+ testGeneration = Optional.of(generation);
+
+ List<CloudSubscriber> subscribers = new ArrayList<>(activeSubscribers.keySet());
+ subscribers.forEach(s -> s.subscriber.reload(generation));
+ }
+
+ private static class CloudSubscriber implements Subscriber {
+ private final ConfigSubscriber subscriber;
+ private final Map<ConfigKey<ConfigInstance>, ConfigHandle<ConfigInstance>> handles = new HashMap<>();
+
+ // if waitNextGeneration has not yet been called, -1 should be returned
+ private long generation = -1L;
+
+ // True if this reconfiguration was caused by a system-internal redeploy, not an external application change
+ private boolean internalRedeploy = false;
+
+ private CloudSubscriber(Set<ConfigKey<ConfigInstance>> keys, ConfigSource configSource) {
+ this.subscriber = new ConfigSubscriber(configSource);
+ keys.forEach(k -> handles.put(k, subscriber.subscribe(k.getConfigClass(), k.getConfigId())));
+ }
+
+ @Override
+ public boolean configChanged() {
+ return handles.values().stream().anyMatch(ConfigHandle::isChanged);
+ }
+
+ @Override
+ public long generation() {
+ return generation;
+ }
+
+ @Override
+ public boolean internalRedeploy() {
+ return internalRedeploy;
+ }
+
+ //mapValues returns a view,, so we need to force evaluation of it here to prevent deferred evaluation.
+ @Override
+ public Map<ConfigKey<ConfigInstance>, ConfigInstance> config() {
+ Map<ConfigKey<ConfigInstance>, ConfigInstance> ret = new HashMap<>();
+ handles.forEach((k, v) -> ret.put(k, v.getConfig()));
+ return ret;
+ }
+
+ @Override
+ public long waitNextGeneration() {
+ if (handles.isEmpty()) {
+ throw new IllegalStateException("No config keys registered");
+ }
+
+ /* Catch and just log config exceptions due to missing config values for parameters that do
+ * not have a default value. These exceptions occur when the user has removed a component
+ * from services.xml, and the component takes a config that has parameters without a
+ * default value in the def-file. There is a new 'components' config underway, where the
+ * component is removed, so this old config generation will soon be replaced by a new one. */
+ boolean gotNextGen = false;
+ int numExceptions = 0;
+ while (!gotNextGen) {
+ try {
+ if (subscriber.nextGeneration()) {
+ gotNextGen = true;
+ }
+ } catch (IllegalArgumentException e) {
+ numExceptions++;
+ log.log(Level.WARNING, "Got exception from the config system (please ignore the exception if you just removed "
+ + "a component from your application that used the mentioned config): ", e);
+ if (numExceptions >= 5) {
+ throw new IllegalArgumentException("Failed retrieving the next config generation.", e);
+ }
+ }
+ }
+
+ generation = subscriber.getGeneration();
+ internalRedeploy = subscriber.isInternalRedeploy();
+ return generation;
+ }
+
+ @Override
+ public void close() {
+ subscriber.close();
+ }
+ }
+
+
+ public static class Provider implements com.google.inject.Provider<SubscriberFactory> {
+ @Override
+ public SubscriberFactory get() {
+ return new CloudSubscriberFactory(ConfigSourceSet.createDefault());
+ }
+ }
+}
diff --git a/container-di/src/main/java/com/yahoo/container/di/ComponentDeconstructor.java b/container-di/src/main/java/com/yahoo/container/di/ComponentDeconstructor.java
index cd8bf61766d..dbfb842204d 100644
--- a/container-di/src/main/java/com/yahoo/container/di/ComponentDeconstructor.java
+++ b/container-di/src/main/java/com/yahoo/container/di/ComponentDeconstructor.java
@@ -3,7 +3,7 @@ package com.yahoo.container.di;
/**
* @author gjoranv
- * @author tonytv
+ * @author Tony Vaagenes
*/
public interface ComponentDeconstructor {
void deconstruct(Object component);
diff --git a/container-di/src/main/java/com/yahoo/container/di/ConfigRetriever.java b/container-di/src/main/java/com/yahoo/container/di/ConfigRetriever.java
new file mode 100644
index 00000000000..337997f8ac2
--- /dev/null
+++ b/container-di/src/main/java/com/yahoo/container/di/ConfigRetriever.java
@@ -0,0 +1,208 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.di;
+
+import com.google.common.collect.Sets;
+import com.yahoo.config.ConfigInstance;
+import com.yahoo.container.di.componentgraph.core.Keys;
+import com.yahoo.container.di.config.Subscriber;
+import com.yahoo.vespa.config.ConfigKey;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static com.yahoo.log.LogLevel.DEBUG;
+
+/**
+ * @author Tony Vaagenes
+ * @author gjoranv
+ * @author ollivir
+ */
+public final class ConfigRetriever {
+ private static final Logger log = Logger.getLogger(ConfigRetriever.class.getName());
+
+ private final Set<ConfigKey<? extends ConfigInstance>> bootstrapKeys;
+ private Set<ConfigKey<? extends ConfigInstance>> componentSubscriberKeys;
+ private final Subscriber bootstrapSubscriber;
+ private Subscriber componentSubscriber;
+ private final Function<Set<ConfigKey<? extends ConfigInstance>>, Subscriber> subscribe;
+
+ public ConfigRetriever(Set<ConfigKey<? extends ConfigInstance>> bootstrapKeys,
+ Function<Set<ConfigKey<? extends ConfigInstance>>, Subscriber> subscribe) {
+ this.bootstrapKeys = bootstrapKeys;
+ this.componentSubscriberKeys = new HashSet<>();
+ this.subscribe = subscribe;
+ if (bootstrapKeys.isEmpty()) {
+ throw new IllegalArgumentException("Bootstrap key set is empty");
+ }
+ this.bootstrapSubscriber = subscribe.apply(bootstrapKeys);
+ this.componentSubscriber = subscribe.apply(componentSubscriberKeys);
+ }
+
+ /**
+ * Loop forever until we get config
+ */
+ public ConfigSnapshot getConfigs(Set<ConfigKey<? extends ConfigInstance>> componentConfigKeys,
+ long leastGeneration,
+ boolean restartOnRedeploy) {
+ while (true) {
+ if (!Sets.intersection(componentConfigKeys, bootstrapKeys).isEmpty())
+ throw new IllegalArgumentException("Component config keys [" + componentConfigKeys +
+ "] overlaps with bootstrap config keys [" + bootstrapKeys + "]");
+
+ log.log(DEBUG, "getConfigs: " + componentConfigKeys);
+ Set<ConfigKey<? extends ConfigInstance>> allKeys = new HashSet<>(componentConfigKeys);
+ allKeys.addAll(bootstrapKeys);
+ setupComponentSubscriber(allKeys);
+
+ Optional<ConfigSnapshot> maybeSnapshot = getConfigsOptional(leastGeneration, restartOnRedeploy);
+ if (maybeSnapshot.isPresent()) {
+ ConfigSnapshot snapshot = maybeSnapshot.get();
+ resetComponentSubscriberIfBootstrap(snapshot);
+ return snapshot;
+ }
+ }
+ }
+
+ public ConfigSnapshot getConfigs(Set<ConfigKey<? extends ConfigInstance>> componentConfigKeys, long leastGeneration) {
+ return getConfigs(componentConfigKeys, leastGeneration, false);
+ }
+
+ /**
+ * Try to get config just once
+ */
+ Optional<ConfigSnapshot> getConfigsOnce(Set<ConfigKey<? extends ConfigInstance>> componentConfigKeys,
+ long leastGeneration,
+ boolean restartOnRedeploy) {
+ if (!Sets.intersection(componentConfigKeys, bootstrapKeys).isEmpty()) {
+ throw new IllegalArgumentException(
+ "Component config keys [" + componentConfigKeys + "] overlaps with bootstrap config keys [" + bootstrapKeys + "]");
+ }
+ log.log(DEBUG, "getConfigsOnce: " + componentConfigKeys);
+
+ Set<ConfigKey<? extends ConfigInstance>> allKeys = new HashSet<>(componentConfigKeys);
+ allKeys.addAll(bootstrapKeys);
+ setupComponentSubscriber(allKeys);
+
+ Optional<ConfigSnapshot> maybeSnapshot = getConfigsOptional(leastGeneration, restartOnRedeploy);
+ maybeSnapshot.ifPresent(this::resetComponentSubscriberIfBootstrap);
+ return maybeSnapshot;
+ }
+
+ private Optional<ConfigSnapshot> getConfigsOptional(long leastGeneration, boolean restartOnRedeploy) {
+ long newestComponentGeneration = componentSubscriber.waitNextGeneration();
+ log.log(DEBUG, "getConfigsOptional: new component generation: " + newestComponentGeneration);
+
+ // leastGeneration is only used to ensure newer generation when the previous generation was invalidated due to an exception
+ if (newestComponentGeneration < leastGeneration) {
+ return Optional.empty();
+ } else if (restartOnRedeploy && !componentSubscriber.internalRedeploy()) { // Don't reconfig - wait for restart
+ return Optional.empty();
+ } else if (bootstrapSubscriber.generation() < newestComponentGeneration) {
+ long newestBootstrapGeneration = bootstrapSubscriber.waitNextGeneration();
+ log.log(DEBUG, "getConfigsOptional: new bootstrap generation: " + bootstrapSubscriber.generation());
+ Optional<ConfigSnapshot> bootstrapConfig = bootstrapConfigIfChanged();
+ if (bootstrapConfig.isPresent()) {
+ return bootstrapConfig;
+ } else {
+ if (newestBootstrapGeneration == newestComponentGeneration) {
+ log.log(DEBUG, "Got new components configs with unchanged bootstrap configs.");
+ return componentsConfigIfChanged();
+ } else {
+ // This should not be a normal case, and hence a warning to allow investigation.
+ log.warning("Did not get same generation for bootstrap (" + newestBootstrapGeneration + ") and components configs ("
+ + newestComponentGeneration + ").");
+ return Optional.empty();
+ }
+ }
+ } else {
+ // bootstrapGen==componentGen (happens only when a new component subscriber returns first config after bootstrap)
+ return componentsConfigIfChanged();
+ }
+ }
+
+ private Optional<ConfigSnapshot> bootstrapConfigIfChanged() {
+ return configIfChanged(bootstrapSubscriber, BootstrapConfigs::new);
+ }
+
+ private Optional<ConfigSnapshot> componentsConfigIfChanged() {
+ return configIfChanged(componentSubscriber, ComponentsConfigs::new);
+ }
+
+ private Optional<ConfigSnapshot> configIfChanged(Subscriber subscriber,
+ Function<Map<ConfigKey<? extends ConfigInstance>, ConfigInstance>, ConfigSnapshot> constructor) {
+ if (subscriber.configChanged()) {
+ return Optional.of(constructor.apply(Keys.covariantCopy(subscriber.config())));
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ private void resetComponentSubscriberIfBootstrap(ConfigSnapshot snapshot) {
+ if (snapshot instanceof BootstrapConfigs) {
+ setupComponentSubscriber(Collections.emptySet());
+ }
+ }
+
+ private void setupComponentSubscriber(Set<ConfigKey<? extends ConfigInstance>> keys) {
+ if (! componentSubscriberKeys.equals(keys)) {
+ componentSubscriber.close();
+ componentSubscriberKeys = keys;
+ try {
+ log.log(DEBUG, "Setting up new component subscriber for keys: " + keys);
+ componentSubscriber = subscribe.apply(keys);
+ } catch (Throwable e) {
+ log.log(Level.WARNING, "Failed setting up subscriptions for component configs: " + e.getMessage());
+ log.log(Level.WARNING, "Config keys: " + keys);
+ throw e;
+ }
+ }
+ }
+
+ public void shutdown() {
+ bootstrapSubscriber.close();
+ componentSubscriber.close();
+ }
+
+ //TODO: check if these are really needed
+ public long getBootstrapGeneration() {
+ return bootstrapSubscriber.generation();
+ }
+
+ public long getComponentsGeneration() {
+ return componentSubscriber.generation();
+ }
+
+ public static class ConfigSnapshot {
+ private final Map<ConfigKey<? extends ConfigInstance>, ConfigInstance> configs;
+
+ ConfigSnapshot(Map<ConfigKey<? extends ConfigInstance>, ConfigInstance> configs) {
+ this.configs = configs;
+ }
+
+ public Map<ConfigKey<? extends ConfigInstance>, ConfigInstance> configs() {
+ return configs;
+ }
+
+ public int size() {
+ return configs.size();
+ }
+ }
+
+ public static class BootstrapConfigs extends ConfigSnapshot {
+ BootstrapConfigs(Map<ConfigKey<? extends ConfigInstance>, ConfigInstance> configs) {
+ super(configs);
+ }
+ }
+
+ public static class ComponentsConfigs extends ConfigSnapshot {
+ ComponentsConfigs(Map<ConfigKey<? extends ConfigInstance>, ConfigInstance> configs) {
+ super(configs);
+ }
+ }
+}
diff --git a/container-di/src/main/java/com/yahoo/container/di/Container.java b/container-di/src/main/java/com/yahoo/container/di/Container.java
new file mode 100644
index 00000000000..fb427bcf8ae
--- /dev/null
+++ b/container-di/src/main/java/com/yahoo/container/di/Container.java
@@ -0,0 +1,268 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.di;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.yahoo.config.ConfigInstance;
+import com.yahoo.config.ConfigurationRuntimeException;
+import com.yahoo.config.subscription.ConfigInterruptedException;
+import com.yahoo.container.BundlesConfig;
+import com.yahoo.container.ComponentsConfig;
+import com.yahoo.container.bundle.BundleInstantiationSpecification;
+import com.yahoo.container.di.ConfigRetriever.BootstrapConfigs;
+import com.yahoo.container.di.ConfigRetriever.ConfigSnapshot;
+import com.yahoo.container.di.componentgraph.core.ComponentGraph;
+import com.yahoo.container.di.componentgraph.core.ComponentNode;
+import com.yahoo.container.di.componentgraph.core.JerseyNode;
+import com.yahoo.container.di.componentgraph.core.Node;
+import com.yahoo.container.di.config.RestApiContext;
+import com.yahoo.container.di.config.SubscriberFactory;
+import com.yahoo.vespa.config.ConfigKey;
+
+import java.time.Duration;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static com.yahoo.log.LogLevel.DEBUG;
+
+/**
+ * @author gjoranv
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class Container {
+ private static final Logger log = Logger.getLogger(Container.class.getName());
+
+ private final SubscriberFactory subscriberFactory;
+ private ConfigKey<BundlesConfig> bundlesConfigKey;
+ private ConfigKey<ComponentsConfig> componentsConfigKey;
+ private final ComponentDeconstructor componentDeconstructor;
+ private final Osgi osgi;
+
+ private ConfigRetriever configurer;
+ private long previousConfigGeneration = -1L;
+ private long leastGeneration = -1L;
+
+ public Container(SubscriberFactory subscriberFactory, String configId, ComponentDeconstructor componentDeconstructor, Osgi osgi) {
+ this.subscriberFactory = subscriberFactory;
+ this.bundlesConfigKey = new ConfigKey<>(BundlesConfig.class, configId);
+ this.componentsConfigKey = new ConfigKey<>(ComponentsConfig.class, configId);
+ this.componentDeconstructor = componentDeconstructor;
+ this.osgi = osgi;
+
+ Set<ConfigKey<? extends ConfigInstance>> keySet = new HashSet<>();
+ keySet.add(bundlesConfigKey);
+ keySet.add(componentsConfigKey);
+ this.configurer = new ConfigRetriever(keySet, subscriberFactory::getSubscriber);
+ }
+
+ public Container(SubscriberFactory subscriberFactory, String configId, ComponentDeconstructor componentDeconstructor) {
+ this(subscriberFactory, configId, componentDeconstructor, new Osgi() {
+ });
+ }
+
+ private void deconstructObsoleteComponents(ComponentGraph oldGraph, ComponentGraph newGraph) {
+ IdentityHashMap<Object, Object> oldComponents = new IdentityHashMap<>();
+ oldGraph.allComponentsAndProviders().forEach(c -> oldComponents.put(c, null));
+ newGraph.allComponentsAndProviders().forEach(oldComponents::remove);
+ oldComponents.keySet().forEach(componentDeconstructor::deconstruct);
+ }
+
+ public ComponentGraph getNewComponentGraph(ComponentGraph oldGraph, Injector fallbackInjector, boolean restartOnRedeploy) {
+ try {
+ ComponentGraph newGraph = getConfigAndCreateGraph(oldGraph, fallbackInjector, restartOnRedeploy);
+ newGraph.reuseNodes(oldGraph);
+ constructComponents(newGraph);
+ deconstructObsoleteComponents(oldGraph, newGraph);
+ return newGraph;
+ } catch (Throwable t) {
+ // TODO: Wrap ComponentConstructorException in an Error when generation==0 (+ unit test that Error is thrown)
+ invalidateGeneration(oldGraph.generation(), t);
+ throw t;
+ }
+ }
+
+ ComponentGraph getNewComponentGraph(ComponentGraph oldGraph) {
+ return getNewComponentGraph(oldGraph, Guice.createInjector(), false);
+ }
+
+ ComponentGraph getNewComponentGraph() {
+ return getNewComponentGraph(new ComponentGraph(), Guice.createInjector(), false);
+ }
+
+ private static String newGraphErrorMessage(long generation, Throwable cause, Duration maxWaitToExit) {
+ String failedFirstMessage = "Failed to set up first component graph";
+ String failedNewMessage = "Failed to set up new component graph";
+ String constructMessage = " due to error when constructing one of the components";
+ String exitMessage = ". Exiting within " + maxWaitToExit.toString();
+ String retainMessage = ". Retaining previous component generation.";
+
+ if (generation == 0) {
+ if (cause instanceof ComponentNode.ComponentConstructorException) {
+ return failedFirstMessage + constructMessage + exitMessage;
+ } else {
+ return failedFirstMessage + exitMessage;
+ }
+ } else {
+ if (cause instanceof ComponentNode.ComponentConstructorException) {
+ return failedNewMessage + constructMessage + retainMessage;
+ } else {
+ return failedNewMessage + retainMessage;
+ }
+ }
+ }
+
+ private void invalidateGeneration(long generation, Throwable cause) {
+ Duration maxWaitToExit = Duration.ofSeconds(60);
+ leastGeneration = Math.max(configurer.getComponentsGeneration(), configurer.getBootstrapGeneration()) + 1;
+ if (!(cause instanceof InterruptedException) && !(cause instanceof ConfigInterruptedException)) {
+ log.log(Level.WARNING, newGraphErrorMessage(generation, cause, maxWaitToExit), cause);
+ }
+ }
+
+ private ComponentGraph getConfigAndCreateGraph(ComponentGraph graph, Injector fallbackInjector, boolean restartOnRedeploy) {
+ ConfigSnapshot snapshot;
+ while (true) {
+ snapshot = configurer.getConfigs(graph.configKeys(), leastGeneration, restartOnRedeploy);
+
+ log.log(DEBUG, String.format("createNewGraph:\n" + "graph.configKeys = %s\n" + "graph.generation = %s\n" + "snapshot = %s\n",
+ graph.configKeys(), graph.generation(), snapshot));
+
+ if (snapshot instanceof BootstrapConfigs) {
+ // TODO: remove require when proven unnecessary
+ if (getBootstrapGeneration() <= previousConfigGeneration) {
+ throw new IllegalStateException(String.format(
+ "Got bootstrap configs out of sequence for old config generation %d.\n" + "Previous config generation is %d",
+ getBootstrapGeneration(), previousConfigGeneration));
+ }
+ log.log(DEBUG,
+ String.format(
+ "Got new bootstrap generation\n" + "bootstrap generation = %d\n" + "components generation: %d\n"
+ + "previous generation: %d\n",
+ getBootstrapGeneration(), getComponentsGeneration(), previousConfigGeneration));
+ installBundles(snapshot.configs());
+ graph = createComponentsGraph(snapshot.configs(), getBootstrapGeneration(), fallbackInjector);
+ // Continues loop
+
+ } else if (snapshot instanceof ConfigRetriever.ComponentsConfigs) {
+ break;
+ }
+ }
+ log.log(DEBUG,
+ String.format(
+ "Got components configs,\n" + "bootstrap generation = %d\n" + "components generation: %d\n"
+ + "previous generation: %d",
+ getBootstrapGeneration(), getComponentsGeneration(), previousConfigGeneration));
+ return createAndConfigureComponentsGraph(snapshot.configs(), fallbackInjector);
+ }
+
+ private long getBootstrapGeneration() {
+ return configurer.getBootstrapGeneration();
+ }
+
+ private long getComponentsGeneration() {
+ return configurer.getComponentsGeneration();
+ }
+
+ private ComponentGraph createAndConfigureComponentsGraph(Map<ConfigKey<? extends ConfigInstance>, ConfigInstance> componentsConfigs,
+ Injector fallbackInjector) {
+ ComponentGraph componentGraph = createComponentsGraph(componentsConfigs, getComponentsGeneration(), fallbackInjector);
+ componentGraph.setAvailableConfigs(componentsConfigs);
+ return componentGraph;
+ }
+
+ private void injectNodes(ComponentsConfig config, ComponentGraph graph) {
+ for (ComponentsConfig.Components component : config.components()) {
+ Node componentNode = ComponentGraph.getNode(graph, component.id());
+
+ for (ComponentsConfig.Components.Inject inject : component.inject()) {
+ //TODO: Support inject.name()
+ componentNode.inject(ComponentGraph.getNode(graph, inject.id()));
+ }
+ }
+ }
+
+ public void installBundles(Map<ConfigKey<? extends ConfigInstance>, ConfigInstance> configsIncludingBootstrapConfigs) {
+ BundlesConfig bundlesConfig = getConfig(bundlesConfigKey, configsIncludingBootstrapConfigs);
+ osgi.useBundles(bundlesConfig.bundle());
+ }
+
+ private ComponentGraph createComponentsGraph(Map<ConfigKey<? extends ConfigInstance>, ConfigInstance> configsIncludingBootstrapConfigs,
+ long generation, Injector fallbackInjector) {
+
+ previousConfigGeneration = generation;
+
+ ComponentGraph graph = new ComponentGraph(generation);
+
+ ComponentsConfig componentsConfig = getConfig(componentsConfigKey, configsIncludingBootstrapConfigs);
+ if (componentsConfig == null) {
+ throw new ConfigurationRuntimeException("The set of all configs does not include a valid 'components' config. Config set: "
+ + configsIncludingBootstrapConfigs.keySet());
+ }
+ addNodes(componentsConfig, graph);
+ injectNodes(componentsConfig, graph);
+
+ graph.complete(fallbackInjector);
+ return graph;
+ }
+
+ private void addNodes(ComponentsConfig componentsConfig, ComponentGraph graph) {
+
+ for (ComponentsConfig.Components config : componentsConfig.components()) {
+ BundleInstantiationSpecification specification = bundleInstatiationSpecification(config);
+ Class<?> componentClass = osgi.resolveClass(specification);
+ Node componentNode;
+
+ if (RestApiContext.class.isAssignableFrom(componentClass)) {
+ Class<? extends RestApiContext> nodeClass = componentClass.asSubclass(RestApiContext.class);
+ componentNode = new JerseyNode(specification.id, config.configId(), nodeClass, osgi);
+ } else {
+ componentNode = new ComponentNode(specification.id, config.configId(), componentClass, null);
+ }
+ graph.add(componentNode);
+ }
+ }
+
+ private void constructComponents(ComponentGraph graph) {
+ graph.nodes().forEach(Node::newOrCachedInstance);
+ }
+
+ public void shutdown(ComponentGraph graph, ComponentDeconstructor deconstructor) {
+ shutdownConfigurer();
+ if (graph != null) {
+ deconstructAllComponents(graph, deconstructor);
+ }
+ }
+
+ public void shutdownConfigurer() {
+ configurer.shutdown();
+ }
+
+ // Reload config manually, when subscribing to non-configserver sources
+ public void reloadConfig(long generation) {
+ subscriberFactory.reloadActiveSubscribers(generation);
+ }
+
+ private void deconstructAllComponents(ComponentGraph graph, ComponentDeconstructor deconstructor) {
+ graph.allComponentsAndProviders().forEach(deconstructor::deconstruct);
+ }
+
+ public static <T extends ConfigInstance> T getConfig(ConfigKey<T> key,
+ Map<ConfigKey<? extends ConfigInstance>, ConfigInstance> configs) {
+ ConfigInstance inst = configs.get(key);
+
+ if (inst == null || key.getConfigClass() == null) {
+ throw new RuntimeException("Missing config " + key);
+ }
+
+ return key.getConfigClass().cast(inst);
+ }
+
+ public static BundleInstantiationSpecification bundleInstatiationSpecification(ComponentsConfig.Components config) {
+ return BundleInstantiationSpecification.getFromStrings(config.id(), config.classId(), config.bundle());
+ }
+}
diff --git a/container-di/src/main/java/com/yahoo/container/di/Osgi.java b/container-di/src/main/java/com/yahoo/container/di/Osgi.java
new file mode 100644
index 00000000000..7095180dfc5
--- /dev/null
+++ b/container-di/src/main/java/com/yahoo/container/di/Osgi.java
@@ -0,0 +1,43 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.di;
+
+import com.yahoo.component.ComponentSpecification;
+import com.yahoo.config.FileReference;
+import com.yahoo.container.bundle.BundleInstantiationSpecification;
+import com.yahoo.container.bundle.MockBundle;
+import com.yahoo.container.di.osgi.BundleClasses;
+import org.osgi.framework.Bundle;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author gjoranv
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public interface Osgi {
+ default BundleClasses getBundleClasses(ComponentSpecification bundle, Set<String> packagesToScan) {
+ return new BundleClasses(new MockBundle(), Collections.emptySet());
+ }
+
+ default void useBundles(Collection<FileReference> bundles) {
+ System.out.println("useBundles " + bundles.stream().map(Object::toString).collect(Collectors.joining(", ")));
+ }
+
+ default Class<?> resolveClass(BundleInstantiationSpecification spec) {
+ System.out.println("resolving class " + spec.classId);
+ try {
+ return Class.forName(spec.classId.getName());
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ default Bundle getBundle(ComponentSpecification spec) {
+ System.out.println("resolving bundle " + spec);
+ return new MockBundle();
+ }
+}
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
new file mode 100644
index 00000000000..463de0c089a
--- /dev/null
+++ b/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/ComponentGraph.java
@@ -0,0 +1,407 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.di.componentgraph.core;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import com.google.inject.BindingAnnotation;
+import com.google.inject.ConfigurationException;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.yahoo.collections.Pair;
+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.log.LogLevel;
+import com.yahoo.vespa.config.ConfigKey;
+import net.jcip.annotations.NotThreadSafe;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+import static com.yahoo.container.di.componentgraph.core.Exceptions.removeStackTrace;
+
+/**
+ * @author Tony Vaagenes
+ * @author gjoranv
+ * @author ollivir
+ */
+@NotThreadSafe
+public class ComponentGraph {
+ private static final Logger log = Logger.getLogger(ComponentGraph.class.getName());
+
+ private long generation;
+ private Map<ComponentId, Node> nodesById = new HashMap<>();
+
+ public ComponentGraph(long generation) {
+ this.generation = generation;
+ }
+
+ public ComponentGraph() {
+ this(0L);
+ }
+
+ public long generation() {
+ return generation;
+ }
+
+ public int size() {
+ return nodesById.size();
+ }
+
+ public Collection<Node> nodes() {
+ return nodesById.values();
+ }
+
+ public void add(Node component) {
+ if (nodesById.containsKey(component.componentId())) {
+ throw new IllegalStateException("Multiple components with the same id " + component.componentId());
+ }
+ nodesById.put(component.componentId(), component);
+ }
+
+ private Optional<Node> lookupGlobalComponent(Key<?> key) {
+ if (!(key.getTypeLiteral().getType() instanceof Class)) {
+
+ throw new RuntimeException("Type not supported " + key.getTypeLiteral());
+ }
+ Class<?> clazz = key.getTypeLiteral().getRawType();
+
+ Collection<ComponentNode> components = matchingComponentNodes(nodes(), key);
+ if (components.isEmpty()) {
+ return Optional.empty();
+ } else if (components.size() == 1) {
+ return Optional.ofNullable(Iterables.get(components, 0));
+ } else {
+
+ List<Node> nonProviderComponents = components.stream().filter(c -> !Provider.class.isAssignableFrom(c.instanceType()))
+ .collect(Collectors.toList());
+ if (nonProviderComponents.isEmpty()) {
+ throw new IllegalStateException("Multiple global component providers for class '" + clazz.getName() + "' found");
+ } else if (nonProviderComponents.size() == 1) {
+ return Optional.of(nonProviderComponents.get(0));
+ } else {
+ throw new IllegalStateException("Multiple global components with class '" + clazz.getName() + "' found");
+ }
+ }
+ }
+
+ public <T> T getInstance(Class<T> clazz) {
+ return getInstance(Key.get(clazz));
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T getInstance(Key<T> key) {
+ // TODO: Combine exception handling with lookupGlobalComponent.
+ Object ob = lookupGlobalComponent(key).map(Node::newOrCachedInstance)
+ .orElseThrow(() -> new IllegalStateException(String.format("No global component with key '%s' ", key)));
+ return (T) ob;
+ }
+
+ private Collection<ComponentNode> componentNodes() {
+ return nodesOfType(nodes(), ComponentNode.class);
+ }
+
+ private Collection<ComponentRegistryNode> componentRegistryNodes() {
+ return nodesOfType(nodes(), ComponentRegistryNode.class);
+ }
+
+ private Collection<ComponentNode> osgiComponentsOfClass(Class<?> clazz) {
+ return componentNodes().stream().filter(node -> clazz.isAssignableFrom(node.componentType())).collect(Collectors.toList());
+ }
+
+ public List<Node> complete(Injector fallbackInjector) {
+ componentNodes().forEach(node -> completeNode(node, fallbackInjector));
+ componentRegistryNodes().forEach(this::completeComponentRegistryNode);
+ return topologicalSort(nodes());
+ }
+
+ public List<Node> complete() {
+ return complete(Guice.createInjector());
+ }
+
+ public Set<ConfigKey<? extends ConfigInstance>> configKeys() {
+ return nodes().stream().flatMap(node -> node.configKeys().stream()).collect(Collectors.toSet());
+ }
+
+ public void setAvailableConfigs(Map<ConfigKey<? extends ConfigInstance>, ConfigInstance> configs) {
+ Map<ConfigKey<ConfigInstance>, ConfigInstance> invariantMap = Keys.invariantCopy(configs);
+ componentNodes().forEach(node -> node.setAvailableConfigs(invariantMap));
+ }
+
+ public void reuseNodes(ComponentGraph old) {
+ // copy instances if node equal
+ Set<ComponentId> commonComponentIds = Sets.intersection(nodesById.keySet(), old.nodesById.keySet());
+ for (ComponentId id : commonComponentIds) {
+ if (nodesById.get(id).equals(old.nodesById.get(id))) {
+ nodesById.get(id).instance = old.nodesById.get(id).instance;
+ }
+ }
+
+ // reset instances with modified dependencies
+ for (Node node : topologicalSort(nodes())) {
+ for (Node usedComponent : node.usedComponents()) {
+ if (!usedComponent.instance.isPresent()) {
+ node.instance = Optional.empty();
+ }
+ }
+ }
+ }
+
+ public Collection<?> allComponentsAndProviders() {
+ return nodes().stream().map(node -> node.instance().get()).collect(Collectors.toList());
+ }
+
+ private void completeComponentRegistryNode(ComponentRegistryNode registry) {
+ registry.injectAll(osgiComponentsOfClass(registry.componentClass()));
+ }
+
+ private void completeNode(ComponentNode node, Injector fallbackInjector) {
+ try {
+ Object[] arguments = node.getAnnotatedConstructorParams().stream().map(param -> handleParameter(node, fallbackInjector, param))
+ .toArray();
+
+ node.setArguments(arguments);
+ } catch (Exception e) {
+ throw removeStackTrace(new RuntimeException("When resolving dependencies of " + node.idAndType(), e));
+ }
+ }
+
+ private Object handleParameter(Node node, Injector fallbackInjector, Pair<Type, List<Annotation>> annotatedParameterType) {
+ Type parameterType = annotatedParameterType.getFirst();
+ List<Annotation> annotations = annotatedParameterType.getSecond();
+
+ if (parameterType instanceof Class && parameterType.equals(ComponentId.class)) {
+ return node.componentId();
+ } else if (parameterType instanceof Class && ConfigInstance.class.isAssignableFrom((Class<?>) parameterType)) {
+ return handleConfigParameter((ComponentNode) node, (Class<?>) parameterType);
+ } else if (parameterType instanceof ParameterizedType
+ && ((ParameterizedType) parameterType).getRawType().equals(ComponentRegistry.class)) {
+ ParameterizedType registry = (ParameterizedType) parameterType;
+ return getComponentRegistry(registry.getActualTypeArguments()[0]);
+ } else if (parameterType instanceof Class) {
+ return handleComponentParameter(node, fallbackInjector, (Class<?>) parameterType, annotations);
+ } else if (parameterType instanceof ParameterizedType) {
+ throw new RuntimeException("Injection of parameterized type " + parameterType + " is not supported.");
+ } else {
+ throw new RuntimeException("Injection of type " + parameterType + " is not supported");
+ }
+ }
+
+ private ComponentRegistryNode newComponentRegistryNode(Class<?> componentClass) {
+ ComponentRegistryNode registry = new ComponentRegistryNode(componentClass);
+ add(registry); //TODO: don't mutate nodes here.
+ return registry;
+ }
+
+ private ComponentRegistryNode getComponentRegistry(Type componentType) {
+ Class<?> componentClass;
+ if (componentType instanceof WildcardType) {
+ WildcardType wildcardType = (WildcardType) componentType;
+ if (wildcardType.getLowerBounds().length > 0 || wildcardType.getUpperBounds().length > 1) {
+ throw new RuntimeException("Can't create ComponentRegistry of unknown wildcard type" + wildcardType);
+ }
+ componentClass = (Class<?>) wildcardType.getUpperBounds()[0];
+ } else if (componentType instanceof Class) {
+ componentClass = (Class<?>) componentType;
+ } else if (componentType instanceof TypeVariable) {
+ throw new RuntimeException("Can't create ComponentRegistry of unknown type variable " + componentType);
+ } else {
+ throw new RuntimeException("Can't create ComponentRegistry of unknown type " + componentType);
+ }
+
+ for (ComponentRegistryNode node : componentRegistryNodes()) {
+ if (node.componentClass().equals(componentType)) {
+ return node;
+ }
+ }
+ return newComponentRegistryNode(componentClass);
+ }
+
+ @SuppressWarnings("unchecked")
+ private ConfigKey<ConfigInstance> handleConfigParameter(ComponentNode node, Class<?> clazz) {
+ Class<ConfigInstance> castClass = (Class<ConfigInstance>) clazz;
+ return new ConfigKey<>(castClass, node.configId());
+ }
+
+ private <T> Key<T> getKey(Class<T> clazz, Optional<Annotation> bindingAnnotation) {
+ return bindingAnnotation.map(annotation -> Key.get(clazz, annotation)).orElseGet(() -> Key.get(clazz));
+ }
+
+ private Optional<GuiceNode> matchingGuiceNode(Key<?> key, Object instance) {
+ return matchingNodes(nodes(), GuiceNode.class, key).stream().filter(node -> node.newOrCachedInstance() == instance). // TODO: assert that there is only one (after filter)
+ findFirst();
+ }
+
+ private Node lookupOrCreateGlobalComponent(Node node, Injector fallbackInjector, Class<?> clazz, Key<?> key) {
+ Optional<Node> component = lookupGlobalComponent(key);
+ if (!component.isPresent()) {
+ Object instance;
+ try {
+ log.log(LogLevel.DEBUG, "Trying the fallback injector to create" + messageForNoGlobalComponent(clazz, node));
+ instance = fallbackInjector.getInstance(key);
+ } catch (ConfigurationException e) {
+ throw removeStackTrace(new IllegalStateException(
+ (messageForMultipleClassLoaders(clazz).isEmpty()) ? "No global" + messageForNoGlobalComponent(clazz, node)
+ : messageForMultipleClassLoaders(clazz)));
+ }
+ component = Optional.of(matchingGuiceNode(key, instance).orElseGet(() -> {
+ GuiceNode guiceNode = new GuiceNode(instance, key.getAnnotation());
+ add(guiceNode);
+ return guiceNode;
+ }));
+ }
+ return component.get();
+ }
+
+ private Node handleComponentParameter(Node node, Injector fallbackInjector, Class<?> clazz, Collection<Annotation> annotations) {
+
+ List<Annotation> bindingAnnotations = annotations.stream().filter(ComponentGraph::isBindingAnnotation).collect(Collectors.toList());
+ Key<?> key = getKey(clazz, bindingAnnotations.stream().findFirst());
+
+ if (bindingAnnotations.size() > 1) {
+ throw new RuntimeException(String.format("More than one binding annotation used in class '%s'", node.instanceType()));
+ }
+
+ Collection<ComponentNode> injectedNodesOfCorrectType = matchingComponentNodes(node.componentsToInject, key);
+ if (injectedNodesOfCorrectType.size() == 0) {
+ return lookupOrCreateGlobalComponent(node, fallbackInjector, clazz, key);
+ } else if (injectedNodesOfCorrectType.size() == 1) {
+ return Iterables.get(injectedNodesOfCorrectType, 0);
+ } else {
+ //TODO: !className for last parameter
+ throw new RuntimeException(
+ String.format("Multiple components of type '%s' injected into component '%s'", clazz.getName(), node.instanceType()));
+ }
+ }
+
+ private static String messageForNoGlobalComponent(Class<?> clazz, Node node) {
+ return String.format(" component of class %s to inject into component %s.", clazz.getName(), node.idAndType());
+ }
+
+ private String messageForMultipleClassLoaders(Class<?> clazz) {
+ String errMsg = "Class " + clazz.getName() + " is provided by the framework, and cannot be embedded in a user bundle. "
+ + "To resolve this problem, please refer to osgi-classloading.html#multiple-implementations in the documentation";
+
+ try {
+ Class<?> resolvedClass = Class.forName(clazz.getName(), false, this.getClass().getClassLoader());
+ if (!resolvedClass.equals(clazz)) {
+ return errMsg;
+ }
+ } catch (ClassNotFoundException ignored) {
+
+ }
+ return "";
+ }
+
+ public static Node getNode(ComponentGraph graph, String componentId) {
+ return graph.nodesById.get(new ComponentId(componentId));
+ }
+
+ private static <T> Collection<T> nodesOfType(Collection<Node> nodes, Class<T> clazz) {
+ List<T> ret = new ArrayList<>();
+ for (Node node : nodes) {
+ if (clazz.isInstance(node)) {
+ ret.add(clazz.cast(node));
+ }
+ }
+ return ret;
+ }
+
+ private static Collection<ComponentNode> matchingComponentNodes(Collection<Node> nodes, Key<?> key) {
+ return matchingNodes(nodes, ComponentNode.class, key);
+ }
+
+ // Finds all nodes with a given nodeType and instance with given key
+ private static <T extends Node> Collection<T> matchingNodes(Collection<Node> nodes, Class<T> nodeType, Key<?> key) {
+ Class<?> clazz = key.getTypeLiteral().getRawType();
+ Annotation annotation = key.getAnnotation();
+
+ List<T> filteredByClass = nodesOfType(nodes, nodeType).stream().filter(node -> clazz.isAssignableFrom(node.componentType()))
+ .collect(Collectors.toList());
+
+ if (filteredByClass.size() == 1) {
+ return filteredByClass;
+ } else {
+ List<T> filteredByClassAndAnnotation = filteredByClass.stream()
+ .filter(node -> (annotation == null && node.instanceKey().getAnnotation() == null)
+ || annotation.equals(node.instanceKey().getAnnotation()))
+ .collect(Collectors.toList());
+ if (filteredByClassAndAnnotation.size() > 0) {
+ return filteredByClassAndAnnotation;
+ } else {
+ return filteredByClass;
+ }
+ }
+ }
+
+ // Returns true if annotation is a BindingAnnotation, e.g. com.google.inject.name.Named
+ public static boolean isBindingAnnotation(Annotation annotation) {
+ LinkedList<Class<?>> queue = new LinkedList<>();
+ queue.add(annotation.getClass());
+ queue.addAll(Arrays.asList(annotation.getClass().getInterfaces()));
+
+ while (!queue.isEmpty()) {
+ Class<?> clazz = queue.removeFirst();
+ if (clazz.getAnnotation(BindingAnnotation.class) != null) {
+ return true;
+ } else {
+ if (clazz.getSuperclass() != null) {
+ queue.addFirst(clazz.getSuperclass());
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * The returned list is the nodes from the graph bottom-up.
+ *
+ * @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)));
+ LinkedList<Node> sorted = new LinkedList<>();
+ List<Node> unsorted = new ArrayList<>(nodes);
+
+ while (!unsorted.isEmpty()) {
+ List<Node> ready = new ArrayList<>();
+ List<Node> notReady = new ArrayList<>();
+ unsorted.forEach(node -> {
+ if (numIncoming.getOrDefault(node.componentId(), 0) == 0) {
+ ready.add(node);
+ } else {
+ notReady.add(node);
+ }
+ });
+
+ if (ready.isEmpty()) {
+ throw new IllegalStateException("There is a cycle in the component injection graph.");
+ }
+
+ ready.forEach(node -> node.usedComponents()
+ .forEach(injectedNode -> numIncoming.merge(injectedNode.componentId(), -1, (a, b) -> a + b)));
+ sorted.addAll(0, ready);
+ unsorted = notReady;
+ }
+ return sorted;
+ }
+}
diff --git a/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/ComponentNode.java b/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/ComponentNode.java
new file mode 100644
index 00000000000..27298ce6c82
--- /dev/null
+++ b/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/ComponentNode.java
@@ -0,0 +1,304 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.di.componentgraph.core;
+
+import com.google.inject.Inject;
+import com.google.inject.Key;
+import com.yahoo.collections.Pair;
+import com.yahoo.component.AbstractComponent;
+import com.yahoo.component.ComponentId;
+import com.yahoo.config.ConfigInstance;
+import com.yahoo.container.di.componentgraph.Provider;
+import com.yahoo.vespa.config.ConfigKey;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+import static com.yahoo.container.di.componentgraph.core.Exceptions.cutStackTraceAtConstructor;
+import static com.yahoo.container.di.componentgraph.core.Exceptions.removeStackTrace;
+import static com.yahoo.container.di.componentgraph.core.Keys.createKey;
+
+/**
+ * @author Tony Vaagenes
+ * @author gjoranv
+ * @author ollivir
+ */
+public class ComponentNode extends Node {
+ private static final Logger log = Logger.getLogger(ComponentNode.class.getName());
+
+ private final Class<?> clazz;
+ private final Annotation key;
+ private Object[] arguments = null;
+ private final String configId;
+
+ private final Constructor<?> constructor;
+
+ private Map<ConfigKey<ConfigInstance>, ConfigInstance> availableConfigs = null;
+
+
+ public ComponentNode(ComponentId componentId,
+ String configId,
+ Class<?> clazz, Annotation XXX_key) // TODO expose key, not javaAnnotation
+ {
+ super(componentId);
+ if (isAbstract(clazz)) {
+ throw new IllegalArgumentException("Can't instantiate abstract class " + clazz.getName());
+ }
+ this.configId = configId;
+ this.clazz = clazz;
+ this.key = XXX_key;
+ this.constructor = bestConstructor(clazz);
+ }
+
+ public ComponentNode(ComponentId componentId, String configId, Class<?> clazz) {
+ this(componentId, configId, clazz, null);
+ }
+
+ public String configId() {
+ return configId;
+ }
+
+ @Override
+ public Key<?> instanceKey() {
+ return createKey(clazz, key);
+ }
+
+ @Override
+ public Class<?> instanceType() {
+ return clazz;
+ }
+
+ @Override
+ public List<Node> usedComponents() {
+ if (arguments == null) {
+ throw new IllegalStateException("Arguments must be set first.");
+ }
+ List<Node> ret = new ArrayList<>();
+ for (Object arg : arguments) {
+ if (arg instanceof Node) {
+ ret.add((Node) arg);
+ }
+ }
+ return ret;
+ }
+
+ private static List<Class<?>> allSuperClasses(Class<?> clazz) {
+ List<Class<?>> ret = new ArrayList<>();
+ while (clazz != null) {
+ ret.add(clazz);
+ clazz = clazz.getSuperclass();
+ }
+ return ret;
+ }
+
+ @Override
+ public Class<?> componentType() {
+ if (Provider.class.isAssignableFrom(clazz)) {
+ //TODO: Test what happens if you ask for something that isn't a class, e.g. a parameterized type.
+
+ List<Type> allGenericInterfaces = allSuperClasses(clazz).stream().flatMap(c -> Arrays.stream(c.getGenericInterfaces())).collect(Collectors.toList());
+ for (Type t : allGenericInterfaces) {
+ if (t instanceof ParameterizedType && ((ParameterizedType) t).getRawType().equals(Provider.class)) {
+ Type[] typeArgs = ((ParameterizedType) t).getActualTypeArguments();
+ if (typeArgs != null && typeArgs.length > 0) {
+ return (Class<?>) typeArgs[0];
+ }
+ }
+ }
+ throw new IllegalStateException("Component type cannot be resolved");
+ } else {
+ return clazz;
+ }
+ }
+
+ public void setArguments(Object[] arguments) {
+ this.arguments = arguments;
+ }
+
+ @Override
+ protected Object newInstance() {
+ if (arguments == null) {
+ throw new IllegalStateException("graph.complete must be called before retrieving instances.");
+ }
+
+ List<Object> actualArguments = new ArrayList<>();
+ for (Object ob : arguments) {
+ if (ob instanceof Node) {
+ actualArguments.add(((Node) ob).newOrCachedInstance());
+ } else if (ob instanceof ConfigKey) {
+ actualArguments.add(availableConfigs.get(ob));
+ } else {
+ actualArguments.add(ob);
+ }
+ }
+
+ Object instance;
+ try {
+ instance = constructor.newInstance(actualArguments.toArray());
+ } catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
+ StackTraceElement dependencyInjectorMarker = new StackTraceElement("============= Dependency Injection =============", "newInstance", null, -1);
+
+ throw removeStackTrace(new ComponentConstructorException("Error constructing " + idAndType(), cutStackTraceAtConstructor(e.getCause(), dependencyInjectorMarker)));
+ }
+
+ return initId(instance);
+ }
+
+ private Object initId(Object component) {
+ if (component instanceof AbstractComponent) {
+ AbstractComponent abstractComponent = (AbstractComponent) component;
+ if (abstractComponent.hasInitializedId() && !abstractComponent.getId().equals(componentId())) {
+ throw new IllegalStateException(
+ "Component with id '" + componentId() + "' is trying to set its component id explicitly: '" + abstractComponent.getId() + "'. " +
+ "This is not allowed, so please remove any call to super() in your component's constructor.");
+ }
+ abstractComponent.initId(componentId());
+ }
+ return component;
+ }
+
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + Arrays.hashCode(arguments);
+ result = prime * result + ((availableConfigs == null) ? 0 : availableConfigs.hashCode());
+ result = prime * result + ((configId == null) ? 0 : configId.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof ComponentNode) {
+ ComponentNode that = (ComponentNode) other;
+ return super.equals(that) && equalEdges(Arrays.asList(this.arguments), Arrays.asList(that.arguments)) && this.usedConfigs().equals(that.usedConfigs());
+ } else {
+ return false;
+ }
+ }
+
+ private List<ConfigInstance> usedConfigs() {
+ if (availableConfigs == null) {
+ throw new IllegalStateException("setAvailableConfigs must be called!");
+ }
+ List<ConfigInstance> ret = new ArrayList<>();
+ for (Object arg : arguments) {
+ if (arg instanceof ConfigKey) {
+ ret.add(availableConfigs.get(arg));
+ }
+ }
+ return ret;
+ }
+
+ protected List<Pair<Type, List<Annotation>>> getAnnotatedConstructorParams() {
+ Type[] types = constructor.getGenericParameterTypes();
+ Annotation[][] annotations = constructor.getParameterAnnotations();
+
+ List<Pair<Type, List<Annotation>>> ret = new ArrayList<>();
+
+ for (int i = 0; i < types.length; i++) {
+ ret.add(new Pair<>(types[i], Arrays.asList(annotations[i])));
+ }
+ return ret;
+ }
+
+ public void setAvailableConfigs(Map<ConfigKey<ConfigInstance>, ConfigInstance> configs) {
+ if (arguments == null) {
+ throw new IllegalStateException("graph.complete must be called before graph.setAvailableConfigs.");
+ }
+ this.availableConfigs = configs;
+ }
+
+ @Override
+ public Set<ConfigKey<ConfigInstance>> configKeys() {
+ return configParameterClasses().stream().map(par -> new ConfigKey<>(par, configId)).collect(Collectors.toSet());
+ }
+
+ @SuppressWarnings("unchecked")
+ private List<Class<ConfigInstance>> configParameterClasses() {
+ List<Class<ConfigInstance>> ret = new ArrayList<>();
+ for (Type type : constructor.getGenericParameterTypes()) {
+ if (type instanceof Class && ConfigInstance.class.isAssignableFrom((Class<?>) type)) {
+ ret.add((Class<ConfigInstance>) type);
+ }
+ }
+ return ret;
+ }
+
+ @Override
+ public String label() {
+ LinkedList<String> configNames = configKeys().stream().map(k -> k.getName() + ".def").collect(Collectors.toCollection(LinkedList::new));
+
+ configNames.addFirst(instanceType().getSimpleName());
+ configNames.addFirst(Node.packageName(instanceType()));
+
+ return "{" + String.join("|", configNames) + "}";
+ }
+
+ private static Constructor<?> bestConstructor(Class<?> clazz) {
+ Constructor<?>[] publicConstructors = clazz.getConstructors();
+
+ Constructor<?> annotated = null;
+ for (Constructor<?> ctor : publicConstructors) {
+ Annotation annotation = ctor.getAnnotation(Inject.class);
+ if (annotation != null) {
+ if (annotated == null) {
+ annotated = ctor;
+ } else {
+ throw componentConstructorException("Multiple constructor annotated with @Inject in class " + clazz.getName());
+ }
+ }
+ }
+ if (annotated != null) {
+ return annotated;
+ }
+
+ if (publicConstructors.length == 0) {
+ throw componentConstructorException("No public constructors in class " + clazz.getName());
+ } else if (publicConstructors.length == 1) {
+ return publicConstructors[0];
+ } else {
+ log.warning(String.format("Multiple public constructors found in class %s, there should only be one. "
+ + "If more than one public constructor is needed, the primary one must be annotated with @Inject.", clazz.getName()));
+ List<Pair<Constructor<?>, Integer>> withParameterCount = new ArrayList<>();
+ for (Constructor<?> ctor : publicConstructors) {
+ long count = Arrays.stream(ctor.getParameterTypes()).filter(ConfigInstance.class::isAssignableFrom).count();
+ withParameterCount.add(new Pair<>(ctor, (int) count));
+ }
+ withParameterCount.sort(Comparator.comparingInt(Pair::getSecond));
+ return withParameterCount.get(withParameterCount.size() - 1).getFirst();
+ }
+ }
+
+ private static ComponentConstructorException componentConstructorException(String message) {
+ return removeStackTrace(new ComponentConstructorException(message));
+ }
+
+ public static class ComponentConstructorException extends RuntimeException {
+ ComponentConstructorException(String message) {
+ super(message);
+ }
+
+ ComponentConstructorException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+
+
+ private static boolean isAbstract(Class<?> clazz) {
+ return Modifier.isAbstract(clazz.getModifiers());
+ }
+} \ No newline at end of file
diff --git a/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/ComponentRegistryNode.java b/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/ComponentRegistryNode.java
new file mode 100644
index 00000000000..8af1713c84f
--- /dev/null
+++ b/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/ComponentRegistryNode.java
@@ -0,0 +1,105 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.di.componentgraph.core;
+
+import com.google.inject.Key;
+import com.google.inject.util.Types;
+import com.yahoo.component.ComponentId;
+import com.yahoo.component.provider.ComponentRegistry;
+import com.yahoo.config.ConfigInstance;
+import com.yahoo.vespa.config.ConfigKey;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author Tony Vaagenes
+ * @author gjoranv
+ * @author ollivir
+ */
+public class ComponentRegistryNode extends Node {
+ private static ComponentId componentRegistryNamespace = ComponentId.fromString("ComponentRegistry");
+
+ private final Class<?> componentClass;
+
+ public ComponentRegistryNode(Class<?> componentClass) {
+ super(componentId(componentClass));
+ this.componentClass = componentClass;
+ }
+
+ @Override
+ public List<Node> usedComponents() {
+ return componentsToInject;
+ }
+
+ @Override
+ protected Object newInstance() {
+ ComponentRegistry<Object> registry = new ComponentRegistry<>();
+ componentsToInject.forEach(component -> registry.register(component.componentId(), component.newOrCachedInstance()));
+
+ return registry;
+ }
+
+ @Override
+ public Key<?> instanceKey() {
+ return Key.get(Types.newParameterizedType(ComponentRegistry.class, componentClass));
+ }
+
+ @Override
+ public Class<?> instanceType() {
+ return instanceKey().getTypeLiteral().getRawType();
+ }
+
+ @Override
+ public Class<?> componentType() {
+ return instanceType();
+ }
+
+ public Class<?> componentClass() {
+ return componentClass;
+ }
+
+ @Override
+ public Set<ConfigKey<ConfigInstance>> configKeys() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + ((componentClass == null) ? 0 : componentClass.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof ComponentRegistryNode) {
+ ComponentRegistryNode that = (ComponentRegistryNode) other;
+ return this.componentId().equals(that.componentId()) && this.instanceType().equals(that.instanceType())
+ && equalNodeEdges(this.usedComponents(), that.usedComponents());
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public String label() {
+ return String.format("{ComponentRegistry\\<%s\\>|%s}", componentClass.getSimpleName(), Node.packageName(componentClass));
+ }
+
+ private static ComponentId componentId(Class<?> componentClass) {
+ return syntheticComponentId(componentClass.getName(), componentClass, componentRegistryNamespace);
+ }
+
+ public static boolean equalNodeEdges(List<Node> edges, List<Node> otherEdges) {
+ if (edges.size() == otherEdges.size()) {
+ List<ComponentId> left = edges.stream().map(Node::componentId).sorted().collect(Collectors.toList());
+ List<ComponentId> right = otherEdges.stream().map(Node::componentId).sorted().collect(Collectors.toList());
+ return left.equals(right);
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/Exceptions.java b/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/Exceptions.java
new file mode 100644
index 00000000000..d84d771fef6
--- /dev/null
+++ b/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/Exceptions.java
@@ -0,0 +1,45 @@
+package com.yahoo.container.di.componentgraph.core;
+
+import java.util.Arrays;
+
+class Exceptions {
+ static <E extends Throwable> E removeStackTrace(E exception) {
+ if (preserveStackTrace()) {
+ return exception;
+ } else {
+ exception.setStackTrace(new StackTraceElement[0]);
+ return exception;
+ }
+ }
+
+ static boolean preserveStackTrace() {
+ String preserve = System.getProperty("jdisc.container.preserveStackTrace");
+ return (preserve != null && !preserve.isEmpty());
+ }
+
+ static Throwable cutStackTraceAtConstructor(Throwable throwable, StackTraceElement marker) {
+ if (throwable != null && !preserveStackTrace()) {
+ StackTraceElement[] stackTrace = throwable.getStackTrace();
+ int upTo = stackTrace.length - 1;
+
+ // take until ComponentNode is reached
+ while (upTo >= 0 && !stackTrace[upTo].getClassName().equals(ComponentNode.class.getName())) {
+ upTo--;
+ }
+
+ // then drop until <init> is reached
+ while (upTo >= 0 && !stackTrace[upTo].getMethodName().equals("<init>")) {
+ upTo--;
+ }
+ if (upTo < 0) {
+ throwable.setStackTrace(new StackTraceElement[0]);
+ } else {
+ throwable.setStackTrace(Arrays.copyOfRange(stackTrace, 0, upTo));
+ }
+
+ cutStackTraceAtConstructor(throwable.getCause(), marker);
+ }
+ return throwable;
+ }
+
+}
diff --git a/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/GuiceNode.java b/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/GuiceNode.java
new file mode 100644
index 00000000000..61d0d9bba8d
--- /dev/null
+++ b/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/GuiceNode.java
@@ -0,0 +1,78 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.di.componentgraph.core;
+
+import com.google.inject.Key;
+import com.yahoo.component.ComponentId;
+import com.yahoo.config.ConfigInstance;
+import com.yahoo.vespa.config.ConfigKey;
+
+import java.lang.annotation.Annotation;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import static com.yahoo.container.di.componentgraph.core.Keys.createKey;
+
+/**
+ * @author Tony Vaagenes
+ * @author gjoranv
+ * @author ollivir
+ */
+public final class GuiceNode extends Node {
+ private static final ComponentId guiceNamespace = ComponentId.fromString("Guice");
+
+ private final Object myInstance;
+ private final Annotation annotation;
+
+ public GuiceNode(Object myInstance,
+ Annotation annotation) {
+ super(componentId(myInstance));
+ this.myInstance = myInstance;
+ this.annotation = annotation;
+ }
+
+ @Override
+ public Set<ConfigKey<ConfigInstance>> configKeys() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Key<?> instanceKey() {
+ return createKey(myInstance.getClass(), annotation);
+ }
+
+ @Override
+ public Class<?> instanceType() {
+ return myInstance.getClass();
+ }
+
+ @Override
+ public Class<?> componentType() {
+ return instanceType();
+ }
+
+
+ @Override
+ public List<Node> usedComponents() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ protected Object newInstance() {
+ return myInstance;
+ }
+
+ @Override
+ public void inject(Node component) {
+ throw new UnsupportedOperationException("Illegal to inject components to a GuiceNode!");
+ }
+
+ @Override
+ public String label() {
+ return String.format("{{%s|Guice}|%s}", instanceType().getSimpleName(), Node.packageName(instanceType()));
+ }
+
+ private static ComponentId componentId(Object instance) {
+ return Node.syntheticComponentId(instance.getClass().getName(), instance, guiceNamespace);
+ }
+}
diff --git a/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/JerseyNode.java b/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/JerseyNode.java
new file mode 100644
index 00000000000..79b849bff8f
--- /dev/null
+++ b/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/JerseyNode.java
@@ -0,0 +1,92 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.di.componentgraph.core;
+
+import com.yahoo.component.ComponentId;
+import com.yahoo.component.ComponentSpecification;
+import com.yahoo.container.di.Osgi;
+import com.yahoo.container.di.config.JerseyBundlesConfig;
+import com.yahoo.container.di.config.RestApiContext;
+import com.yahoo.container.di.config.RestApiContext.BundleInfo;
+import com.yahoo.container.di.osgi.BundleClasses;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.wiring.BundleWiring;
+
+import java.net.URL;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Represents an instance of RestApiContext
+ *
+ * @author gjoranv
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class JerseyNode extends ComponentNode {
+ private static final String WEB_INF_URL = "WebInfUrl";
+
+ private final Osgi osgi;
+
+ public JerseyNode(ComponentId componentId, String configId, Class<?> clazz, Osgi osgi) {
+ super(componentId, configId, clazz, null);
+ this.osgi = osgi;
+ }
+
+ @Override
+ protected RestApiContext newInstance() {
+ Object instance = super.newInstance();
+ RestApiContext restApiContext = (RestApiContext) instance;
+
+ List<JerseyBundlesConfig.Bundles> bundles = restApiContext.bundlesConfig.bundles();
+ for (JerseyBundlesConfig.Bundles bundleConfig : bundles) {
+ BundleClasses bundleClasses = osgi.getBundleClasses(ComponentSpecification.fromString(bundleConfig.spec()),
+ new HashSet<>(bundleConfig.packages()));
+
+ restApiContext.addBundle(createBundleInfo(bundleClasses.bundle(), bundleClasses.classEntries()));
+ }
+
+ componentsToInject.forEach(component -> restApiContext.addInjectableComponent(component.instanceKey(), component.componentId(),
+ component.newOrCachedInstance()));
+
+ return restApiContext;
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return super.equals(other)
+ && (other instanceof JerseyNode && this.componentsToInject.equals(((JerseyNode) other).componentsToInject));
+ }
+
+ public static BundleInfo createBundleInfo(Bundle bundle, Collection<String> classEntries) {
+ BundleInfo bundleInfo = new BundleInfo(bundle.getSymbolicName(), bundle.getVersion(), bundle.getLocation(), webInfUrl(bundle),
+ bundle.adapt(BundleWiring.class).getClassLoader());
+
+ bundleInfo.setClassEntries(classEntries);
+ return bundleInfo;
+ }
+
+ public static Bundle getBundle(Osgi osgi, String bundleSpec) {
+ Bundle bundle = osgi.getBundle(ComponentSpecification.fromString(bundleSpec));
+ if (bundle == null) {
+ throw new IllegalArgumentException("Bundle not found: " + bundleSpec);
+ }
+ return bundle;
+ }
+
+ private static URL webInfUrl(Bundle bundle) {
+ String webInfUrlHeader = bundle.getHeaders().get(WEB_INF_URL);
+
+ if (webInfUrlHeader == null) {
+ return null;
+ } else {
+ return bundle.getEntry(webInfUrlHeader);
+ }
+ }
+
+}
diff --git a/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/Keys.java b/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/Keys.java
new file mode 100644
index 00000000000..005691721c4
--- /dev/null
+++ b/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/Keys.java
@@ -0,0 +1,37 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.di.componentgraph.core;
+
+import com.google.inject.Key;
+import com.yahoo.config.ConfigInstance;
+import com.yahoo.vespa.config.ConfigKey;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author ollivir
+ */
+public class Keys {
+ static Key<?> createKey(Type instanceType, Annotation annotation) {
+ if (annotation == null) {
+ return Key.get(instanceType);
+ } else {
+ return Key.get(instanceType, annotation);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public static Map<ConfigKey<ConfigInstance>, ConfigInstance> invariantCopy(Map<ConfigKey<? extends ConfigInstance>, ConfigInstance> configs) {
+ Map<ConfigKey<ConfigInstance>, ConfigInstance> ret = new HashMap<>();
+ configs.forEach((k, v) -> ret.put((ConfigKey<ConfigInstance>) k, v));
+ return ret;
+ }
+
+ public static Map<ConfigKey<? extends ConfigInstance>, ConfigInstance> covariantCopy(Map<ConfigKey<ConfigInstance>, ConfigInstance> configs) {
+ Map<ConfigKey<? extends ConfigInstance>, ConfigInstance> ret = new HashMap<>();
+ configs.forEach((k, v) -> ret.put(k, v));
+ return ret;
+ }
+}
diff --git a/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/Node.java b/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/Node.java
new file mode 100644
index 00000000000..6feac7a4078
--- /dev/null
+++ b/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/Node.java
@@ -0,0 +1,164 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.di.componentgraph.core;
+
+import com.google.inject.Key;
+import com.yahoo.component.ComponentId;
+import com.yahoo.config.ConfigInstance;
+import com.yahoo.container.di.componentgraph.Provider;
+import com.yahoo.vespa.config.ConfigKey;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import static com.yahoo.log.LogLevel.DEBUG;
+import static com.yahoo.log.LogLevel.SPAM;
+
+/**
+ * @author Tony Vaagenes
+ * @author gjoranv
+ * @author ollivir
+ */
+public abstract class Node {
+ private final static Logger log = Logger.getLogger(Node.class.getName());
+
+ private final ComponentId componentId;
+ protected Optional<Object> instance = Optional.empty();
+ List<Node> componentsToInject = new ArrayList<>();
+
+ public Node(ComponentId componentId) {
+ this.componentId = componentId;
+ }
+
+ public abstract Key<?> instanceKey();
+
+ /**
+ * The components actually used by this node. Consist of a subset of the injected nodes + subset of the global nodes.
+ */
+ public abstract List<Node> usedComponents();
+
+ protected abstract Object newInstance();
+
+ public Object newOrCachedInstance() {
+ Object inst;
+ if (instance.isPresent()) {
+ inst = instance.get();
+ log.log(SPAM, "Reusing instance for component with ID " + componentId);
+ } else {
+ log.log(DEBUG, "Creating new instance for component with ID " + componentId);
+ inst = newInstance();
+ instance = Optional.of(inst);
+ }
+ return component(inst);
+ }
+
+ private Object component(Object instance) {
+ if (instance instanceof Provider) {
+ Provider<?> provider = (Provider<?>) instance;
+ return provider.get();
+ } else {
+ return instance;
+ }
+ }
+
+ public abstract Set<ConfigKey<ConfigInstance>> configKeys();
+
+ public void inject(Node component) {
+ componentsToInject.add(component);
+ }
+
+ public void injectAll(Collection<ComponentNode> componentNodes) {
+ componentNodes.forEach(this::inject);
+ }
+
+ public abstract Class<?> instanceType();
+
+ public abstract Class<?> componentType();
+
+ public abstract String label();
+
+ public String idAndType() {
+ String className = instanceType().getName();
+
+ if (className.equals(componentId.getName())) {
+ return "'" + componentId + "'";
+ } else {
+ return "'" + componentId + "' of type '" + className + "'";
+ }
+ }
+
+ private static boolean equalNodes(Object a, Object b) {
+ if (a instanceof Node && b instanceof Node) {
+ Node l = (Node) a;
+ Node r = (Node) b;
+ return l.componentId.equals(r.componentId);
+ } else {
+ return a.equals(b);
+ }
+ }
+
+ public static boolean equalEdges(List<?> edges1, List<?> edges2) {
+ Iterator<?> right = edges2.iterator();
+ for (Object l : edges1) {
+ if (!right.hasNext()) {
+ return false;
+ }
+ Object r = right.next();
+ if (!equalNodes(l, r)) {
+ return false;
+ }
+ }
+ return !right.hasNext();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((componentId == null) ? 0 : componentId.hashCode());
+ result = prime * result + ((componentsToInject == null) ? 0 : componentsToInject.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof Node) {
+ Node that = (Node) other;
+ return getClass().equals(that.getClass()) && this.componentId.equals(that.componentId)
+ && this.instanceType().equals(that.instanceType()) && equalEdges(this.usedComponents(), that.usedComponents());
+ } else {
+ return false;
+ }
+ }
+
+ public ComponentId componentId() {
+ return componentId;
+ }
+
+ public Optional<?> instance() {
+ return instance;
+ }
+
+ /**
+ * @param identityObject
+ * The identifying object that makes the Node unique
+ */
+ protected static ComponentId syntheticComponentId(String className, Object identityObject, ComponentId namespace) {
+ String name = className + "_" + System.identityHashCode(identityObject);
+ return ComponentId.fromString(name).nestInNamespace(namespace);
+ }
+
+ public static String packageName(Class<?> componentClass) {
+ String fullClassName = componentClass.getName();
+ int index = fullClassName.lastIndexOf('.');
+ if (index < 0) {
+ return "";
+ } else {
+ return fullClassName.substring(0, index);
+ }
+ }
+}
diff --git a/container-di/src/main/java/com/yahoo/container/di/config/RestApiContext.java b/container-di/src/main/java/com/yahoo/container/di/config/RestApiContext.java
index 7b5f85778c6..bfb9a8f9160 100644
--- a/container-di/src/main/java/com/yahoo/container/di/config/RestApiContext.java
+++ b/container-di/src/main/java/com/yahoo/container/di/config/RestApiContext.java
@@ -1,4 +1,4 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.di.config;
import com.google.common.collect.ImmutableSet;
@@ -11,8 +11,6 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
diff --git a/container-di/src/main/java/com/yahoo/container/di/osgi/BundleClasses.java b/container-di/src/main/java/com/yahoo/container/di/osgi/BundleClasses.java
new file mode 100644
index 00000000000..bca3ed73d0b
--- /dev/null
+++ b/container-di/src/main/java/com/yahoo/container/di/osgi/BundleClasses.java
@@ -0,0 +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.container.di.osgi;
+
+import org.osgi.framework.Bundle;
+
+import java.util.Collection;
+
+/**
+ * @author ollivir
+ */
+public class BundleClasses {
+ private final Bundle bundle;
+ private final Collection<String> classEntries;
+
+ public BundleClasses(Bundle bundle, Collection<String> classEntries) {
+ this.bundle = bundle;
+ this.classEntries = classEntries;
+ }
+
+ public Bundle bundle() {
+ return bundle;
+ }
+
+ public Collection<String> classEntries() {
+ return classEntries;
+ }
+}
diff --git a/container-di/src/main/java/com/yahoo/container/di/osgi/OsgiUtil.java b/container-di/src/main/java/com/yahoo/container/di/osgi/OsgiUtil.java
new file mode 100644
index 00000000000..e1854155e5b
--- /dev/null
+++ b/container-di/src/main/java/com/yahoo/container/di/osgi/OsgiUtil.java
@@ -0,0 +1,168 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.di.osgi;
+
+import com.yahoo.component.ComponentSpecification;
+import com.yahoo.osgi.maven.ProjectBundleClassPaths;
+import com.yahoo.osgi.maven.ProjectBundleClassPaths.BundleClasspathMapping;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.wiring.BundleWiring;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+import static com.google.common.io.Files.fileTreeTraverser;
+
+/**
+ * Tested by com.yahoo.application.container.jersey.JerseyTest
+ *
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class OsgiUtil {
+ private static final Logger log = Logger.getLogger(OsgiUtil.class.getName());
+ private static final String CLASS_FILE_TYPE_SUFFIX = ".class";
+
+ public static Collection<String> getClassEntriesInBundleClassPath(Bundle bundle, Set<String> packagesToScan) {
+ BundleWiring bundleWiring = bundle.adapt(BundleWiring.class);
+
+ if (packagesToScan.isEmpty()) {
+ return bundleWiring.listResources("/", "*" + CLASS_FILE_TYPE_SUFFIX,
+ BundleWiring.LISTRESOURCES_LOCAL | BundleWiring.LISTRESOURCES_RECURSE);
+ } else {
+ List<String> ret = new ArrayList<>();
+ for (String pkg : packagesToScan) {
+ ret.addAll(bundleWiring.listResources(packageToPath(pkg), "*" + CLASS_FILE_TYPE_SUFFIX, BundleWiring.LISTRESOURCES_LOCAL));
+ }
+ return ret;
+ }
+ }
+
+ public static Collection<String> getClassEntriesForBundleUsingProjectClassPathMappings(ClassLoader classLoader,
+ ComponentSpecification bundleSpec, Set<String> packagesToScan) {
+ return classEntriesFrom(bundleClassPathMapping(bundleSpec, classLoader).classPathElements, packagesToScan);
+ }
+
+ private static BundleClasspathMapping bundleClassPathMapping(ComponentSpecification bundleSpec, ClassLoader classLoader) {
+ ProjectBundleClassPaths projectBundleClassPaths = loadProjectBundleClassPaths(classLoader);
+
+ if (projectBundleClassPaths.mainBundle.bundleSymbolicName.equals(bundleSpec.getName())) {
+ return projectBundleClassPaths.mainBundle;
+ } else {
+ log.log(Level.WARNING,
+ "Dependencies of the bundle " + bundleSpec + " will not be scanned. Please file a feature request if you need this");
+ return matchingBundleClassPathMapping(bundleSpec, projectBundleClassPaths.providedDependencies);
+ }
+ }
+
+ public static BundleClasspathMapping matchingBundleClassPathMapping(ComponentSpecification bundleSpec,
+ Collection<BundleClasspathMapping> providedBundlesClassPathMappings) {
+ for (BundleClasspathMapping mapping : providedBundlesClassPathMappings) {
+ if (mapping.bundleSymbolicName.equals(bundleSpec.getName())) {
+ return mapping;
+ }
+ }
+ throw new RuntimeException("No such bundle: " + bundleSpec);
+ }
+
+ private static ProjectBundleClassPaths loadProjectBundleClassPaths(ClassLoader classLoader) {
+ URL classPathMappingsFileLocation = classLoader.getResource(ProjectBundleClassPaths.CLASSPATH_MAPPINGS_FILENAME);
+ if (classPathMappingsFileLocation == null) {
+ throw new RuntimeException("Couldn't find " + ProjectBundleClassPaths.CLASSPATH_MAPPINGS_FILENAME + " in the class path.");
+ }
+
+ try {
+ return ProjectBundleClassPaths.load(Paths.get(classPathMappingsFileLocation.toURI()));
+ } catch (IOException | URISyntaxException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static Collection<String> classEntriesFrom(List<String> classPathEntries, Set<String> packagesToScan) {
+ Set<String> packagePathsToScan = packagesToScan.stream().map(OsgiUtil::packageToPath).collect(Collectors.toSet());
+ List<String> ret = new ArrayList<>();
+
+ for (String entry : classPathEntries) {
+ Path path = Paths.get(entry);
+ if (Files.isDirectory(path)) {
+ ret.addAll(classEntriesInPath(path, packagePathsToScan));
+ } else if (Files.isRegularFile(path) && path.getFileName().toString().endsWith(".jar")) {
+ ret.addAll(classEntriesInJar(path, packagePathsToScan));
+ } else {
+ throw new RuntimeException("Unsupported path " + path + " in the class path");
+ }
+ }
+ return ret;
+ }
+
+ private static String relativePathToClass(Path rootPath, Path pathToClass) {
+ Path relativePath = rootPath.relativize(pathToClass);
+ return relativePath.toString();
+ }
+
+ private static Collection<String> classEntriesInPath(Path rootPath, Collection<String> packagePathsToScan) {
+ Iterable<File> fileIterator;
+ if (packagePathsToScan.isEmpty()) {
+ fileIterator = fileTreeTraverser().preOrderTraversal(rootPath.toFile());
+ } else {
+ List<File> files = new ArrayList<>();
+ for (String packagePath : packagePathsToScan) {
+ for (File file : fileTreeTraverser().children(rootPath.resolve(packagePath).toFile())) {
+ files.add(file);
+ }
+ }
+ fileIterator = files;
+ }
+
+ List<String> ret = new ArrayList<>();
+ for (File file : fileIterator) {
+ if (file.isFile() && file.getName().endsWith(CLASS_FILE_TYPE_SUFFIX)) {
+ ret.add(relativePathToClass(rootPath, file.toPath()));
+ }
+ }
+ return ret;
+ }
+
+ private static String packagePath(String name) {
+ int index = name.lastIndexOf('/');
+ if (index < 0) {
+ return name;
+ } else {
+ return name.substring(0, index);
+ }
+ }
+
+ private static Collection<String> classEntriesInJar(Path jarPath, Set<String> packagePathsToScan) {
+ Predicate<String> acceptedPackage;
+ if (packagePathsToScan.isEmpty()) {
+ acceptedPackage = ign -> true;
+ } else {
+ acceptedPackage = name -> packagePathsToScan.contains(packagePath(name));
+ }
+
+ try (JarFile jarFile = new JarFile(jarPath.toFile())) {
+ return jarFile.stream().map(JarEntry::getName).filter(name -> name.endsWith(CLASS_FILE_TYPE_SUFFIX)).filter(acceptedPackage)
+ .collect(Collectors.toList());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static String packageToPath(String packageName) {
+ return packageName.replace('.', '/');
+ }
+}
diff --git a/container-di/src/main/java/com/yahoo/container/di/osgi/package-info.java b/container-di/src/main/java/com/yahoo/container/di/osgi/package-info.java
index 39cb2d8b0cd..9685cf571bd 100644
--- a/container-di/src/main/java/com/yahoo/container/di/osgi/package-info.java
+++ b/container-di/src/main/java/com/yahoo/container/di/osgi/package-info.java
@@ -1,6 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
@ExportPackage
package com.yahoo.container.di.osgi;
diff --git a/container-di/src/main/scala/com/yahoo/container/bundle/MockBundle.scala b/container-di/src/main/scala/com/yahoo/container/bundle/MockBundle.scala
deleted file mode 100644
index 5a28d9abe2a..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/bundle/MockBundle.scala
+++ /dev/null
@@ -1,94 +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.container.bundle
-
-
-import java.net.URL
-import java.util
-
-import org.osgi.framework.wiring._
-import org.osgi.framework.{ServiceReference, Version, Bundle}
-import java.io.InputStream
-import java.util.{Collections, Hashtable, Dictionary}
-import MockBundle._
-import org.osgi.resource.{Capability, Wire, Requirement}
-
-/**
- * @author gjoranv
- */
-class MockBundle extends Bundle with BundleWiring {
-
- override def getState = Bundle.ACTIVE
-
- override def start(options: Int) {}
- override def start() {}
- override def stop(options: Int) {}
- override def stop() {}
- override def update(input: InputStream) {}
- override def update() {}
- override def uninstall() {}
-
- override def getHeaders(locale: String) = getHeaders
-
- override def getSymbolicName = SymbolicName
- override def getVersion: Version = BundleVersion
- override def getLocation = getSymbolicName
- override def getBundleId: Long = 0L
-
- override def getHeaders: Dictionary[String, String] = new Hashtable[String, String]()
-
- override def getRegisteredServices = Array[ServiceReference[_]]()
- override def getServicesInUse = getRegisteredServices
-
- override def hasPermission(permission: Any) = true
-
- override def getResource(name: String) = throw new UnsupportedOperationException
- override def loadClass(name: String) = throw new UnsupportedOperationException
- override def getResources(name: String) = throw new UnsupportedOperationException
-
- override def getEntryPaths(path: String) = throw new UnsupportedOperationException
- override def getEntry(path: String) = throw new UnsupportedOperationException
- override def findEntries(path: String, filePattern: String, recurse: Boolean) = Collections.emptyEnumeration()
-
-
- override def getLastModified = 1L
-
- override def getBundleContext = throw new UnsupportedOperationException
- override def getSignerCertificates(signersType: Int) = Collections.emptyMap()
-
- override def adapt[A](`type`: Class[A]) =
- `type` match {
- case MockBundle.bundleWiringClass => this.asInstanceOf[A]
- case _ => ???
- }
-
- override def getDataFile(filename: String) = null
- override def compareTo(o: Bundle) = getBundleId compareTo o.getBundleId
-
-
- //TODO: replace with mockito
- override def findEntries(p1: String, p2: String, p3: Int): util.List[URL] = ???
- override def getRequiredResourceWires(p1: String): util.List[Wire] = ???
- override def getResourceCapabilities(p1: String): util.List[Capability] = ???
- override def isCurrent: Boolean = ???
- override def getRequiredWires(p1: String): util.List[BundleWire] = ???
- override def getCapabilities(p1: String): util.List[BundleCapability] = ???
- override def getProvidedResourceWires(p1: String): util.List[Wire] = ???
- override def getProvidedWires(p1: String): util.List[BundleWire] = ???
- override def getRevision: BundleRevision = ???
- override def getResourceRequirements(p1: String): util.List[Requirement] = ???
- override def isInUse: Boolean = ???
- override def listResources(p1: String, p2: String, p3: Int): util.Collection[String] = Collections.emptyList()
- override def getClassLoader: ClassLoader = MockBundle.getClass.getClassLoader
- override def getRequirements(p1: String): util.List[BundleRequirement] = ???
- override def getResource: BundleRevision = ???
- override def getBundle: Bundle = ???
-}
-
-object MockBundle {
- val SymbolicName = "mock-bundle"
- val BundleVersion = new Version(1, 0, 0)
-
- val bundleWiringClass = classOf[BundleWiring]
-
-
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/CloudSubscriberFactory.scala b/container-di/src/main/scala/com/yahoo/container/di/CloudSubscriberFactory.scala
deleted file mode 100644
index 0f3fab93e80..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/CloudSubscriberFactory.scala
+++ /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.container.di
-
-import java.util.logging.{Level, Logger}
-
-import com.yahoo.config.ConfigInstance
-import com.yahoo.config.subscription.{ConfigHandle, ConfigSource, ConfigSourceSet, ConfigSubscriber}
-import com.yahoo.container.di.CloudSubscriberFactory._
-import com.yahoo.container.di.config.{Subscriber, SubscriberFactory}
-import com.yahoo.vespa.config.ConfigKey
-
-import scala.collection.JavaConverters._
-import scala.language.existentials
-
-
-/**
- * @author Tony Vaagenes
- */
-class CloudSubscriberFactory(configSource: ConfigSource) extends SubscriberFactory {
-
- private var testGeneration: Option[Long] = None
-
- private val activeSubscribers = new java.util.WeakHashMap[CloudSubscriber, Int]()
-
- override def getSubscriber(configKeys: java.util.Set[_ <: ConfigKey[_]]): Subscriber = {
- val subscriber = new CloudSubscriber(configKeys.asScala.toSet.asInstanceOf[Set[ConfigKeyT]], configSource)
-
- testGeneration.foreach(subscriber.subscriber.reload(_)) //TODO: test specific code, remove
- activeSubscribers.put(subscriber, 0)
-
- subscriber
- }
-
- //TODO: test specific code, remove
- override def reloadActiveSubscribers(generation: Long) {
- testGeneration = Some(generation)
-
- val l = activeSubscribers.keySet().asScala.toSet
- l.foreach { _.subscriber.reload(generation) }
- }
-}
-
-object CloudSubscriberFactory {
- val log = Logger.getLogger(classOf[CloudSubscriberFactory].getName)
-
- private class CloudSubscriber(keys: Set[ConfigKeyT], configSource: ConfigSource) extends Subscriber
- {
- private[CloudSubscriberFactory] val subscriber = new ConfigSubscriber(configSource)
- private val handles: Map[ConfigKeyT, ConfigHandle[_ <: ConfigInstance]] = keys.map(subscribe).toMap
-
-
- // if waitNextGeneration has not yet been called, -1 should be returned
- var generation: Long = -1
-
- // True if this reconfiguration was caused by a system-internal redeploy, not an external application change
- var internalRedeploy: Boolean = false
-
- private def subscribe(key: ConfigKeyT) = (key, subscriber.subscribe(key.getConfigClass, key.getConfigId))
-
- override def configChanged = handles.values.exists(_.isChanged)
-
- //mapValues returns a view,, so we need to force evaluation of it here to prevent deferred evaluation.
- override def config = handles.mapValues(_.getConfig).toMap.view.force.
- asInstanceOf[Map[ConfigKey[ConfigInstance], ConfigInstance]].asJava
-
- override def waitNextGeneration() = {
- require(!handles.isEmpty)
-
- /* Catch and just log config exceptions due to missing config values for parameters that do
- * not have a default value. These exceptions occur when the user has removed a component
- * from services.xml, and the component takes a config that has parameters without a
- * default value in the def-file. There is a new 'components' config underway, where the
- * component is removed, so this old config generation will soon be replaced by a new one. */
- var gotNextGen = false
- var numExceptions = 0
- while (!gotNextGen) {
- try{
- if (subscriber.nextGeneration())
- gotNextGen = true
- } catch {
- case e: IllegalArgumentException =>
- numExceptions += 1
- log.log(Level.WARNING, "Got exception from the config system (please ignore the exception if you just removed "
- + "a component from your application that used the mentioned config): ", e)
- if (numExceptions >= 5)
- throw new IllegalArgumentException("Failed retrieving the next config generation.", e)
- }
- }
-
- generation = subscriber.getGeneration
- internalRedeploy = subscriber.isInternalRedeploy
- generation
- }
-
- override def close() {
- subscriber.close()
- }
- }
-
-
- class Provider extends com.google.inject.Provider[SubscriberFactory] {
- override def get() = new CloudSubscriberFactory(ConfigSourceSet.createDefault())
- }
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/ConfigRetriever.scala b/container-di/src/main/scala/com/yahoo/container/di/ConfigRetriever.scala
deleted file mode 100644
index aad9e17acb2..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/ConfigRetriever.scala
+++ /dev/null
@@ -1,135 +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.container.di
-
-
-import java.util.logging.{Level, Logger}
-
-import com.yahoo.config.ConfigInstance
-import com.yahoo.container.di.ConfigRetriever._
-import com.yahoo.container.di.config.Subscriber
-import com.yahoo.log.LogLevel.DEBUG
-
-import scala.annotation.tailrec
-import scala.collection.JavaConverters._
-import scala.language.postfixOps
-
-/**
- * @author Tony Vaagenes
- * @author gjoranv
- */
-final class ConfigRetriever(bootstrapKeys: Set[ConfigKeyT],
- subscribe: Set[ConfigKeyT] => Subscriber)
-{
- require(!bootstrapKeys.isEmpty)
-
- private val bootstrapSubscriber: Subscriber = subscribe(bootstrapKeys)
-
- private var componentSubscriber: Subscriber = subscribe(Set())
- private var componentSubscriberKeys: Set[ConfigKeyT] = Set()
-
- /** Loop forever until we get config */
- @tailrec
- final def getConfigs(componentConfigKeys: Set[ConfigKeyT], leastGeneration: Long, restartOnRedeploy: Boolean = false): ConfigSnapshot = {
- require(componentConfigKeys intersect bootstrapKeys isEmpty)
- log.log(DEBUG, "getConfigs: " + componentConfigKeys)
-
- setupComponentSubscriber(componentConfigKeys ++ bootstrapKeys)
-
- getConfigsOptional(leastGeneration, restartOnRedeploy) match {
- case Some(snapshot) => resetComponentSubscriberIfBootstrap(snapshot); snapshot
- case None => getConfigs(componentConfigKeys, leastGeneration, restartOnRedeploy)
- }
- }
-
-
- /** Try to get config just once */
- final def getConfigsOnce(componentConfigKeys: Set[ConfigKeyT], leastGeneration: Long, restartOnRedeploy: Boolean = false): Option[ConfigSnapshot] = {
- require(componentConfigKeys intersect bootstrapKeys isEmpty)
- log.log(DEBUG, "getConfigsOnce: " + componentConfigKeys)
-
- setupComponentSubscriber(componentConfigKeys ++ bootstrapKeys)
-
- getConfigsOptional(leastGeneration, restartOnRedeploy) match {
- case Some(snapshot) => resetComponentSubscriberIfBootstrap(snapshot); Some(snapshot)
- case None => None;
- }
- }
-
- private def getConfigsOptional(leastGeneration: Long, restartOnRedeploy: Boolean): Option[ConfigSnapshot] = {
- val newestComponentGeneration = componentSubscriber.waitNextGeneration()
- log.log(DEBUG, s"getConfigsOptional: new component generation: $newestComponentGeneration")
-
- // leastGeneration is only used to ensure newer generation when the previous generation was invalidated due to an exception
- if (newestComponentGeneration < leastGeneration) {
- None
- } else if (restartOnRedeploy && ! componentSubscriber.internalRedeploy()) { // Don't reconfig - wait for restart
- None
- } else if (bootstrapSubscriber.generation < newestComponentGeneration) {
- val newestBootstrapGeneration = bootstrapSubscriber.waitNextGeneration()
- log.log(DEBUG, s"getConfigsOptional: new bootstrap generation: ${bootstrapSubscriber.generation}")
- bootstrapConfigIfChanged() orElse {
- if (newestBootstrapGeneration == newestComponentGeneration){
- log.log(DEBUG, s"Got new components configs with unchanged bootstrap configs.")
- componentsConfigIfChanged()
- } else {
- // This should not be a normal case, and hence a warning to allow investigation.
- log.warning(s"Did not get same generation for bootstrap ($newestBootstrapGeneration) and components configs ($newestComponentGeneration).")
- None
- }
- }
- } else {
- // bootstrapGen==componentGen (happens only when a new component subscriber returns first config after bootstrap)
- componentsConfigIfChanged()
- }
- }
-
- private def bootstrapConfigIfChanged(): Option[BootstrapConfigs] = configIfChanged(bootstrapSubscriber, BootstrapConfigs)
- private def componentsConfigIfChanged(): Option[ComponentsConfigs] = configIfChanged(componentSubscriber, ComponentsConfigs)
-
- private def configIfChanged[T <: ConfigSnapshot](subscriber: Subscriber,
- constructor: Map[ConfigKeyT, ConfigInstance] => T ): Option[T] = {
- if (subscriber.configChanged) Some(constructor(subscriber.config.asScala.toMap))
- else None
- }
-
- private def resetComponentSubscriberIfBootstrap(snapshot: ConfigSnapshot) {
- snapshot match {
- case BootstrapConfigs(_) => setupComponentSubscriber(Set())
- case _ =>
- }
- }
-
- private def setupComponentSubscriber(keys: Set[ConfigKeyT]) {
- if (componentSubscriberKeys != keys) {
- componentSubscriber.close()
- componentSubscriberKeys = keys
- try {
- log.log(DEBUG, s"Setting up new component subscriber for keys: $keys")
- componentSubscriber = subscribe(keys)
- } catch {
- case e: Throwable =>
- log.log(Level.WARNING, s"Failed setting up subscriptions for component configs: ${e.getMessage}")
- log.log(Level.WARNING, s"Config keys: $keys")
- throw e
- }
- }
- }
-
- def shutdown() {
- bootstrapSubscriber.close()
- componentSubscriber.close()
- }
-
- //TODO: check if these are really needed
- final def getBootstrapGeneration = bootstrapSubscriber.generation
- final def getComponentsGeneration = componentSubscriber.generation
-}
-
-
-object ConfigRetriever {
- private val log = Logger.getLogger(classOf[ConfigRetriever].getName)
-
- sealed abstract class ConfigSnapshot
- case class BootstrapConfigs(configs: Map[ConfigKeyT, ConfigInstance]) extends ConfigSnapshot
- case class ComponentsConfigs(configs: Map[ConfigKeyT, ConfigInstance]) extends ConfigSnapshot
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/Container.scala b/container-di/src/main/scala/com/yahoo/container/di/Container.scala
deleted file mode 100644
index 2a185d41a6c..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/Container.scala
+++ /dev/null
@@ -1,265 +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.container.di
-
-import java.util.logging.{Level, Logger}
-import java.util.{IdentityHashMap, Random}
-
-import com.google.inject.{Guice, Injector}
-import com.yahoo.config._
-import com.yahoo.config.subscription.ConfigInterruptedException
-import com.yahoo.container.bundle.BundleInstantiationSpecification
-import com.yahoo.container.di.ConfigRetriever.{BootstrapConfigs, ComponentsConfigs}
-import com.yahoo.container.di.Container._
-import com.yahoo.container.di.componentgraph.core.ComponentNode.ComponentConstructorException
-import com.yahoo.container.di.componentgraph.core.{ComponentGraph, ComponentNode, JerseyNode}
-import com.yahoo.container.di.config.{RestApiContext, SubscriberFactory}
-import com.yahoo.container.{BundlesConfig, ComponentsConfig}
-import com.yahoo.log.LogLevel.DEBUG
-import com.yahoo.protect.Process
-import com.yahoo.vespa.config.ConfigKey
-
-import scala.collection.JavaConverters._
-import scala.concurrent.duration._
-import scala.language.postfixOps
-import scala.math.max
-
-
-/**
- *
- * @author gjoranv
- * @author Tony Vaagenes
- */
-class Container(
- subscriberFactory: SubscriberFactory,
- configId: String,
- componentDeconstructor: ComponentDeconstructor,
- osgi: Osgi = new Osgi {}
- )
-{
- val bundlesConfigKey = new ConfigKey(classOf[BundlesConfig], configId)
- val componentsConfigKey = new ConfigKey(classOf[ComponentsConfig], configId)
-
- var configurer = new ConfigRetriever(Set(bundlesConfigKey, componentsConfigKey), (keys) => subscriberFactory.getSubscriber(keys.asJava))
- var previousConfigGeneration = -1L
- var leastGeneration = -1L
-
- @throws(classOf[InterruptedException])
- def getNewComponentGraph(oldGraph: ComponentGraph = new ComponentGraph,
- fallbackInjector: GuiceInjector = Guice.createInjector(),
- restartOnRedeploy: Boolean = false): ComponentGraph = {
-
- def deconstructObsoleteComponents(oldGraph: ComponentGraph, newGraph: ComponentGraph) {
- val oldComponents = new IdentityHashMap[AnyRef, AnyRef]()
- oldGraph.allComponentsAndProviders foreach (oldComponents.put(_, null))
- newGraph.allComponentsAndProviders foreach (oldComponents.remove(_))
- oldComponents.keySet.asScala foreach (componentDeconstructor.deconstruct(_))
- }
-
- try {
- val newGraph = getConfigAndCreateGraph(oldGraph, fallbackInjector, restartOnRedeploy)
- newGraph.reuseNodes(oldGraph)
- constructComponents(newGraph)
- deconstructObsoleteComponents(oldGraph, newGraph)
- newGraph
- } catch {
- case userException: ComponentConstructorException =>
- invalidateGeneration(oldGraph.generation, userException)
- // TODO: Wrap userException in an Error when generation==0 (+ unit test that Error is thrown)
- throw userException
- case t: Throwable =>
- invalidateGeneration(oldGraph.generation, t)
- throw t
- }
- }
-
- private def invalidateGeneration(generation: Long, cause: Throwable) {
- val maxWaitToExit = 60 seconds
-
- def newGraphErrorMessage(generation: Long, cause: Throwable): String = {
- val failedFirstMessage = "Failed to set up first component graph"
- val failedNewMessage = "Failed to set up new component graph"
- val constructMessage = "due to error when constructing one of the components"
- val exitMessage = s"Exiting within $maxWaitToExit."
- val retainMessage = "Retaining previous component generation."
- generation match {
- case 0 =>
- cause match {
- case _: ComponentConstructorException => s"$failedFirstMessage $constructMessage. $exitMessage"
- case _ => s"$failedFirstMessage. $exitMessage"
- }
- case _ =>
- cause match {
- case _: ComponentConstructorException => s"$failedNewMessage $constructMessage. $retainMessage"
- case _ => s"$failedNewMessage. $retainMessage"
- }
- }
- }
-
- // TODO: move to ConfiguredApplication
- def logAndDie(message: String, cause: Throwable): Unit = {
- log.log(Level.SEVERE, message, cause)
- try {
- Thread.sleep((new Random(System.nanoTime).nextDouble * maxWaitToExit.toMillis).toLong)
- } catch {
- case _: InterruptedException => // Do nothing
- }
- Process.logAndDie("Exited for reason (repeated from above):", cause)
- }
-
- leastGeneration = max(configurer.getComponentsGeneration, configurer.getBootstrapGeneration) + 1
- cause match {
- case _: InterruptedException | _: ConfigInterruptedException => // Normal during shutdown, do not log anything.
- case _ => log.log(Level.WARNING, newGraphErrorMessage(generation, cause), cause)
- }
- }
-
- final def getConfigAndCreateGraph(graph: ComponentGraph = new ComponentGraph,
- fallbackInjector: Injector,
- restartOnRedeploy: Boolean): ComponentGraph = {
-
- val snapshot = configurer.getConfigs(graph.configKeys, leastGeneration, restartOnRedeploy)
-
- log.log(DEBUG,
- """createNewGraph:
- |graph.configKeys = %s
- |graph.generation = %s
- |snapshot = %s"""
- .format(graph.configKeys, graph.generation, snapshot).stripMargin)
-
- val preventTailRecursion =
- snapshot match {
- case BootstrapConfigs(configs) =>
- // TODO: remove require when proven unnecessary
- require(getBootstrapGeneration > previousConfigGeneration,
- """Got bootstrap configs out of sequence for old config generation %d.
- |Previous config generation is %d""".format(getBootstrapGeneration, previousConfigGeneration))
- log.log(DEBUG,
- """Got new bootstrap generation
- |bootstrap generation = %d
- |components generation: %d
- |previous generation: %d"""
- .format(getBootstrapGeneration, getComponentsGeneration, previousConfigGeneration).stripMargin)
- installBundles(configs)
- getConfigAndCreateGraph(
- createComponentsGraph(configs, getBootstrapGeneration,fallbackInjector), fallbackInjector, restartOnRedeploy)
- case ComponentsConfigs(configs) =>
- log.log(DEBUG,
- """Got components configs,
- |bootstrap generation = %d
- |components generation: %d
- |previous generation: %d"""
- .format(getBootstrapGeneration, getComponentsGeneration, previousConfigGeneration).stripMargin)
- createAndConfigureComponentsGraph(configs, fallbackInjector)
- }
-
- preventTailRecursion
- }
-
-
- def getBootstrapGeneration: Long = {
- configurer.getBootstrapGeneration
- }
-
- def getComponentsGeneration: Long = {
- configurer.getComponentsGeneration
- }
-
- private def createAndConfigureComponentsGraph[T](componentsConfigs: Map[ConfigKeyT, ConfigInstance],
- fallbackInjector: Injector): ComponentGraph = {
-
- val componentGraph = createComponentsGraph(componentsConfigs, getComponentsGeneration, fallbackInjector)
- componentGraph.setAvailableConfigs(componentsConfigs)
- componentGraph
- }
-
- def injectNodes(config: ComponentsConfig, graph: ComponentGraph) {
- for {
- component <- config.components().asScala
- inject <- component.inject().asScala
- } {
- def getNode = ComponentGraph.getNode(graph, _: String)
-
- //TODO: Support inject.name()
- getNode(component.id()).inject(getNode(inject.id()))
- }
-
- }
-
- def installBundles(configsIncludingBootstrapConfigs: Map[ConfigKeyT, ConfigInstance]) {
- val bundlesConfig = getConfig(bundlesConfigKey, configsIncludingBootstrapConfigs)
- osgi.useBundles(bundlesConfig.bundle())
- }
-
- private def createComponentsGraph[T](
- configsIncludingBootstrapConfigs: Map[ConfigKeyT, ConfigInstance],
- generation: Long,
- fallbackInjector: Injector): ComponentGraph = {
-
- previousConfigGeneration = generation
-
- val graph = new ComponentGraph(generation)
-
- val componentsConfig = getConfig(componentsConfigKey, configsIncludingBootstrapConfigs)
- if (componentsConfig == null)
- throw new ConfigurationRuntimeException(
- "The set of all configs does not include a valid 'components' config. Config set: " + configsIncludingBootstrapConfigs.keySet)
- addNodes(componentsConfig, graph)
- injectNodes(componentsConfig, graph)
-
- graph.complete(fallbackInjector)
- graph
- }
-
- def addNodes[T](componentsConfig: ComponentsConfig, graph: ComponentGraph) {
- def isRestApiContext(clazz: Class[_]) = classOf[RestApiContext].isAssignableFrom(clazz)
- def asRestApiContext(clazz: Class[_]) = clazz.asInstanceOf[Class[RestApiContext]]
-
- for (config : ComponentsConfig.Components <- componentsConfig.components.asScala) {
- val specification = bundleInstatiationSpecification(config)
- val componentClass = osgi.resolveClass(specification)
-
- val componentNode =
- if (isRestApiContext(componentClass))
- new JerseyNode(specification.id, config.configId(), asRestApiContext(componentClass), osgi)
- else
- new ComponentNode(specification.id, config.configId(), componentClass)
-
- graph.add(componentNode)
- }
- }
-
- private def constructComponents(graph: ComponentGraph) {
- graph.nodes foreach (_.newOrCachedInstance())
- }
-
- def shutdown(graph: ComponentGraph, deconstructor: ComponentDeconstructor) {
- shutdownConfigurer()
- if (graph != null)
- deconstructAllComponents(graph, deconstructor)
- }
-
- def shutdownConfigurer() {
- configurer.shutdown()
- }
-
- // Reload config manually, when subscribing to non-configserver sources
- def reloadConfig(generation: Long) {
- subscriberFactory.reloadActiveSubscribers(generation)
- }
-
- def deconstructAllComponents(graph: ComponentGraph, deconstructor: ComponentDeconstructor) {
- graph.allComponentsAndProviders foreach(deconstructor.deconstruct(_))
- }
-
-}
-
-object Container {
- val log = Logger.getLogger(classOf[Container].getName)
-
- def getConfig[T <: ConfigInstance](key: ConfigKey[T], configs: Map[ConfigKeyT, ConfigInstance]) : T = {
- key.getConfigClass.cast(configs.getOrElse(key.asInstanceOf[ConfigKeyT], sys.error("Missing config " + key)))
- }
-
- def bundleInstatiationSpecification(config: ComponentsConfig.Components) =
- BundleInstantiationSpecification.getFromStrings(config.id(), config.classId(), config.bundle())
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/Osgi.scala b/container-di/src/main/scala/com/yahoo/container/di/Osgi.scala
deleted file mode 100644
index 3407eceae3e..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/Osgi.scala
+++ /dev/null
@@ -1,40 +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.container.di
-
-import com.yahoo.config.FileReference
-import com.yahoo.container.bundle.{MockBundle, BundleInstantiationSpecification}
-import com.yahoo.container.di.Osgi.BundleClasses
-import org.osgi.framework.Bundle
-import com.yahoo.component.ComponentSpecification
-
-/**
- *
- * @author gjoranv
- * @author Tony Vaagenes
- */
-trait Osgi {
-
- def getBundleClasses(bundle: ComponentSpecification, packagesToScan: Set[String]): BundleClasses = {
- BundleClasses(new MockBundle, List())
- }
-
- def useBundles(bundles: java.util.Collection[FileReference]) {
- println("useBundles " + bundles.toArray.mkString(", "))
- }
-
- def resolveClass(spec: BundleInstantiationSpecification): Class[AnyRef] = {
- println("resolving class " + spec.classId)
- Class.forName(spec.classId.getName).asInstanceOf[Class[AnyRef]]
- }
-
- def getBundle(spec: ComponentSpecification): Bundle = {
- println("resolving bundle " + spec)
- new MockBundle()
- }
-
-}
-
-object Osgi {
- type RelativePath = String //e.g. "com/yahoo/MyClass.class"
- case class BundleClasses(bundle: Bundle, classEntries: Iterable[RelativePath])
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentGraph.scala b/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentGraph.scala
deleted file mode 100644
index 92f489798c9..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentGraph.scala
+++ /dev/null
@@ -1,334 +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.container.di.componentgraph.core
-
-import java.util.logging.Logger
-
-import com.yahoo.component.provider.ComponentRegistry
-import com.yahoo.config.ConfigInstance
-
-import java.lang.annotation.{Annotation => JavaAnnotation}
-import java.lang.IllegalStateException
-import com.yahoo.log.LogLevel
-
-import collection.mutable
-import annotation.tailrec
-
-import com.yahoo.container.di.{ConfigKeyT, GuiceInjector}
-import com.yahoo.container.di.componentgraph.Provider
-import com.yahoo.vespa.config.ConfigKey
-import com.google.inject.{Guice, ConfigurationException, Key, BindingAnnotation}
-import net.jcip.annotations.NotThreadSafe
-
-import com.yahoo.component.{AbstractComponent, ComponentId}
-import java.lang.reflect.{TypeVariable, WildcardType, Method, ParameterizedType, Type}
-import com.yahoo.container.di.removeStackTrace
-import scala.util.Try
-import scala.Some
-
-import scala.language.existentials
-
-/**
- * @author Tony Vaagenes
- * @author gjoranv
- */
-@NotThreadSafe
-class ComponentGraph(val generation: Long = 0) {
-
- import ComponentGraph._
-
- private var nodesById = Map[ComponentId, Node]()
-
- private[di] def size = nodesById.size
-
- def nodes = nodesById.values
-
- def add(component: Node) {
- require(!nodesById.isDefinedAt(component.componentId), "Multiple components with the same id " + component.componentId)
- nodesById += component.componentId -> component
- }
-
- def lookupGlobalComponent(key: Key[_]): Option[Node] = {
- require(key.getTypeLiteral.getType.isInstanceOf[Class[_]], "Type not supported " + key.getTypeLiteral)
- val clazz = key.getTypeLiteral.getRawType
-
-
- val components = matchingComponentNodes(nodesById.values, key)
-
- def singleNonProviderComponentOrThrow: Option[Node] = {
- val nonProviderComponents = components filter (c => !classOf[Provider[_]].isAssignableFrom(c.instanceType))
- nonProviderComponents.size match {
- case 0 => throw new IllegalStateException(s"Multiple global component providers for class '${clazz.getName}' found")
- case 1 => Some(nonProviderComponents.head.asInstanceOf[Node])
- case _ => throw new IllegalStateException(s"Multiple global components with class '${clazz.getName}' found")
- }
- }
-
- components.size match {
- case 0 => None
- case 1 => Some(components.head.asInstanceOf[Node])
- case _ => singleNonProviderComponentOrThrow
- }
- }
-
- def getInstance[T](clazz: Class[T]) : T = {
- getInstance(Key.get(clazz))
- }
-
- def getInstance[T](key: Key[T]) : T = {
- lookupGlobalComponent(key).
- // TODO: Combine exception handling with lookupGlobalComponent.
- getOrElse(throw new IllegalStateException("No global component with key '%s' ".format(key.toString))).
- newOrCachedInstance().asInstanceOf[T]
- }
-
- private def componentNodes: Traversable[ComponentNode] =
- nodesOfType(nodesById.values, classOf[ComponentNode])
-
- private def componentRegistryNodes: Traversable[ComponentRegistryNode] =
- nodesOfType(nodesById.values, classOf[ComponentRegistryNode])
-
- private def osgiComponentsOfClass(clazz: Class[_]): Traversable[ComponentNode] = {
- componentNodes.filter(node => clazz.isAssignableFrom(node.componentType))
- }
-
- def complete(fallbackInjector: GuiceInjector = Guice.createInjector()) {
- def detectCycles = topologicalSort(nodesById.values.toList)
-
- componentNodes foreach {completeNode(_, fallbackInjector)}
- componentRegistryNodes foreach completeComponentRegistryNode
- detectCycles
- }
-
- def configKeys: Set[ConfigKeyT] = {
- nodesById.values.flatMap(_.configKeys).toSet
- }
-
- def setAvailableConfigs(configs: Map[ConfigKeyT, ConfigInstance]) {
- componentNodes foreach { _.setAvailableConfigs(configs) }
- }
-
- def reuseNodes(old: ComponentGraph) {
- def copyInstancesIfNodeEqual() {
- val commonComponentIds = nodesById.keySet & old.nodesById.keySet
- for (id <- commonComponentIds) {
- if (nodesById(id) == old.nodesById(id)) {
- nodesById(id).instance = old.nodesById(id).instance
- }
- }
- }
- def resetInstancesWithModifiedDependencies() {
- for {
- node <- topologicalSort(nodesById.values.toList)
- usedComponent <- node.usedComponents
- } {
- if (usedComponent.instance == None) {
- node.instance = None
- }
- }
- }
-
- copyInstancesIfNodeEqual()
- resetInstancesWithModifiedDependencies()
- }
-
- def allComponentsAndProviders = nodes map {_.instance.get}
-
- private def completeComponentRegistryNode(registry: ComponentRegistryNode) {
- registry.injectAll(osgiComponentsOfClass(registry.componentClass))
- }
-
- private def completeNode(node: ComponentNode, fallbackInjector: GuiceInjector) {
- try {
- val arguments = node.getAnnotatedConstructorParams.map(handleParameter(node, fallbackInjector, _))
-
- node.setArguments(arguments)
- } catch {
- case e : Exception => throw removeStackTrace(new RuntimeException(s"When resolving dependencies of ${node.idAndType}", e))
- }
- }
-
- private def handleParameter(node : Node,
- fallbackInjector: GuiceInjector,
- annotatedParameterType: (Type, Array[JavaAnnotation])): AnyRef =
- {
- def isConfigParameter(clazz : Class[_]) = classOf[ConfigInstance].isAssignableFrom(clazz)
- def isComponentRegistry(t : Type) = t == classOf[ComponentRegistry[_]]
-
- val (parameterType, annotations) = annotatedParameterType
-
- (parameterType match {
- case componentIdClass: Class[_] if componentIdClass == classOf[ComponentId] => node.componentId
- case configClass : Class[_] if isConfigParameter(configClass) => handleConfigParameter(node.asInstanceOf[ComponentNode], configClass)
- case registry : ParameterizedType if isComponentRegistry(registry.getRawType) => getComponentRegistry(registry.getActualTypeArguments.head)
- case clazz : Class[_] => handleComponentParameter(node, fallbackInjector, clazz, annotations)
- case other: ParameterizedType => sys.error(s"Injection of parameterized type $other is not supported.")
- case other => sys.error(s"Injection of type $other is not supported.")
- }).asInstanceOf[AnyRef]
- }
-
-
- def newComponentRegistryNode(componentClass: Class[AnyRef]): ComponentRegistryNode = {
- val registry = new ComponentRegistryNode(componentClass)
- add(registry) //TODO: don't mutate nodes here.
- registry
- }
-
- private def getComponentRegistry(componentType : Type) : ComponentRegistryNode = {
- val componentClass = componentType match {
- case wildCardType: WildcardType =>
- assert(wildCardType.getLowerBounds.isEmpty)
- assert(wildCardType.getUpperBounds.size == 1)
- wildCardType.getUpperBounds.head.asInstanceOf[Class[AnyRef]]
- case clazz: Class[_] => clazz
- case typeVariable: TypeVariable[_] =>
- throw new RuntimeException("Can't create ComponentRegistry of unknown type variable " + typeVariable)
- }
-
- componentRegistryNodes.find(_.componentClass == componentType).
- getOrElse(newComponentRegistryNode(componentClass.asInstanceOf[Class[AnyRef]]))
- }
-
- def handleConfigParameter(node : ComponentNode, clazz: Class[_]) : ConfigKeyT = {
- new ConfigKey(clazz.asInstanceOf[Class[ConfigInstance]], node.configId)
- }
-
- def getKey(clazz: Class[_], bindingAnnotation: Option[JavaAnnotation]) =
- bindingAnnotation.map(Key.get(clazz, _)).getOrElse(Key.get(clazz))
-
- private def handleComponentParameter(node: Node,
- fallbackInjector: GuiceInjector,
- clazz: Class[_],
- annotations: Array[JavaAnnotation]) : Node = {
-
- val bindingAnnotations = annotations.filter(isBindingAnnotation)
- val key = getKey(clazz, bindingAnnotations.headOption)
-
- def matchingGuiceNode(key: Key[_], instance: AnyRef): Option[GuiceNode] = {
- matchingNodes(nodesById.values, classOf[GuiceNode], key).
- filter(node => node.newOrCachedInstance eq instance). // TODO: assert that there is only one (after filter)
- headOption
- }
-
- def lookupOrCreateGlobalComponent: Node = {
- lookupGlobalComponent(key).getOrElse {
- val instance =
- try {
- log.log(LogLevel.DEBUG, "Trying the fallback injector to create" + messageForNoGlobalComponent(clazz, node))
- fallbackInjector.getInstance(key).asInstanceOf[AnyRef]
- } catch {
- case e: ConfigurationException =>
- throw removeStackTrace(new IllegalStateException(
- if (messageForMultipleClassLoaders(clazz).isEmpty)
- "No global" + messageForNoGlobalComponent(clazz, node)
- else
- messageForMultipleClassLoaders(clazz)))
-
- }
- matchingGuiceNode(key, instance).getOrElse {
- val node = new GuiceNode(instance, key.getAnnotation)
- add(node)
- node
- }
- }
- }
-
- if (bindingAnnotations.size > 1)
- sys.error("More than one binding annotation used in class '%s'".format(node.instanceType))
-
- val injectedNodesOfCorrectType = matchingComponentNodes(node.componentsToInject, key)
- injectedNodesOfCorrectType.size match {
- case 0 => lookupOrCreateGlobalComponent
- case 1 => injectedNodesOfCorrectType.head.asInstanceOf[Node]
- case _ => sys.error("Multiple components of type '%s' injected into component '%s'".format(clazz.getName, node.instanceType)) //TODO: !className for last parameter
- }
- }
-
-}
-
-object ComponentGraph {
- val log = Logger.getLogger(classOf[ComponentGraph].getName)
-
- def messageForNoGlobalComponent(clazz: Class[_], node: Node) =
- s" component of class ${clazz.getName} to inject into component ${node.idAndType}."
-
- def messageForMultipleClassLoaders(clazz: Class[_]): String = {
- val errMsg = "Class " + clazz.getName + " is provided by the framework, and cannot be embedded in a user bundle. " +
- "To resolve this problem, please refer to osgi-classloading.html#multiple-implementations in the documentation"
-
- (for {
- resolvedClass <- Try {Class.forName(clazz.getName, false, this.getClass.getClassLoader)}
- if resolvedClass != clazz
- } yield errMsg)
- .getOrElse("")
- }
-
- // For unit testing
- def getNode(graph: ComponentGraph, componentId: String): Node = {
- graph.nodesById(new ComponentId(componentId))
- }
-
- private def nodesOfType[T <: Node](nodes: Traversable[Node], clazz : Class[T]) : Traversable[T] = {
- nodes.collect {
- case node if clazz.isInstance(node) => clazz.cast(node)
- }
- }
-
- private def matchingComponentNodes(nodes: Traversable[Node], key: Key[_]) : Traversable[ComponentNode] = {
- matchingNodes(nodes, classOf[ComponentNode], key)
- }
-
- // Finds all nodes with a given nodeType and instance with given key
- private def matchingNodes[T <: Node](nodes: Traversable[Node], nodeType: Class[T], key: Key[_]) : Traversable[T] = {
- val clazz = key.getTypeLiteral.getRawType
- val annotation = key.getAnnotation
-
- val filteredByClass = nodesOfType(nodes, nodeType) filter { node => clazz.isAssignableFrom(node.componentType) }
- val filteredByClassAndAnnotation = filteredByClass filter { node => annotation == node.instanceKey.getAnnotation }
-
- if (filteredByClass.size == 1) filteredByClass
- else if (filteredByClassAndAnnotation.size > 0) filteredByClassAndAnnotation
- else filteredByClass
- }
-
- // Returns true if annotation is a BindingAnnotation, e.g. com.google.inject.name.Named
- def isBindingAnnotation(annotation: JavaAnnotation) : Boolean = {
- def isBindingAnnotation(clazz: Class[_]) : Boolean = {
- val clazzOrSuperIsBindingAnnotation =
- (clazz.getAnnotation(classOf[BindingAnnotation]) != null) ||
- Option(clazz.getSuperclass).map(isBindingAnnotation).getOrElse(false)
-
- (clazzOrSuperIsBindingAnnotation /: clazz.getInterfaces.map(isBindingAnnotation))(_ || _)
- }
- isBindingAnnotation(annotation.getClass)
- }
-
- /**
- * The returned list is the nodes from the graph bottom-up.
- * @return A list where a earlier than b in the list implies that there is no path from a to b
- */
- def topologicalSort(nodes: List[Node]): List[Node] = {
- val numIncoming = mutable.Map[ComponentId, Int]().withDefaultValue(0)
-
- def forEachUsedComponent(nodes: Traversable[Node])(f: Node => Unit) {
- nodes.foreach(_.usedComponents.foreach(f))
- }
-
- def partitionByNoIncoming(nodes: List[Node]) =
- nodes.partition(node => numIncoming(node.componentId) == 0)
-
- @tailrec
- def sort(sorted: List[Node], unsorted: List[Node]) : List[Node] = {
- if (unsorted.isEmpty) {
- sorted
- } else {
- val (ready, notReady) = partitionByNoIncoming(unsorted)
- require(!ready.isEmpty, "There's a cycle in the graph.") //TODO: return cycle
- forEachUsedComponent(ready) { injectedNode => numIncoming(injectedNode.componentId) -= 1}
- sort(ready ::: sorted, notReady)
- }
- }
-
- forEachUsedComponent(nodes) { injectedNode => numIncoming(injectedNode.componentId) += 1 }
- sort(List(), nodes)
- }
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentNode.scala b/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentNode.scala
deleted file mode 100644
index cc1745d6e35..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentNode.scala
+++ /dev/null
@@ -1,213 +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.container.di.componentgraph.core
-
-import java.lang.reflect.{Constructor, InvocationTargetException, Modifier, ParameterizedType, Type}
-import java.util.logging.Logger
-
-import com.google.inject.Inject
-import com.yahoo.component.{AbstractComponent, ComponentId}
-import com.yahoo.config.ConfigInstance
-import com.yahoo.container.di.componentgraph.Provider
-import com.yahoo.container.di.componentgraph.core.ComponentNode._
-import com.yahoo.container.di.componentgraph.core.Node.equalEdges
-import com.yahoo.container.di.{ConfigKeyT, JavaAnnotation, createKey, makeClassCovariant, preserveStackTrace, removeStackTrace}
-import com.yahoo.vespa.config.ConfigKey
-
-import scala.language.postfixOps
-
-/**
- * @author Tony Vaagenes
- * @author gjoranv
- */
-class ComponentNode(componentId: ComponentId,
- val configId: String,
- clazz: Class[_ <: AnyRef],
- val XXX_key: JavaAnnotation = null) // TODO expose key, not javaAnnotation
- extends Node(componentId)
-{
- require(!isAbstract(clazz), "Can't instantiate abstract class " + clazz.getName)
-
- var arguments : Array[AnyRef] = _
-
- val constructor: Constructor[AnyRef] = bestConstructor(clazz)
-
- var availableConfigs: Map[ConfigKeyT, ConfigInstance] = null
-
- override val instanceKey = createKey(clazz, XXX_key)
-
- override val instanceType = clazz
-
- override def usedComponents: List[Node] = {
- require(arguments != null, "Arguments must be set first.")
- arguments.collect{case node: Node => node}.toList
- }
-
- override val componentType: Class[AnyRef] = {
- def allSuperClasses(clazz: Class[_], coll : List[Class[_]]) : List[Class[_]] = {
- if (clazz == null) coll
- else allSuperClasses(clazz.getSuperclass, clazz :: coll)
- }
-
- def allGenericInterfaces(clazz : Class[_]) = allSuperClasses(clazz, List()) flatMap (_.getGenericInterfaces)
-
- def isProvider = classOf[Provider[_]].isAssignableFrom(clazz)
- def providerComponentType = (allGenericInterfaces(clazz).collect {
- case t: ParameterizedType if t.getRawType == classOf[Provider[_]] => t.getActualTypeArguments.head
- }).head
-
- if (isProvider) providerComponentType.asInstanceOf[Class[AnyRef]] //TODO: Test what happens if you ask for something that isn't a class, e.g. a parametrized type.
- else clazz.asInstanceOf[Class[AnyRef]]
- }
-
- def setArguments(arguments: Array[AnyRef]) {
- this.arguments = arguments
- }
-
- def cutStackTraceAtConstructor(throwable: Throwable): Throwable = {
- def takeUntilComponentNode(elements: Array[StackTraceElement]) =
- elements.takeWhile(_.getClassName != classOf[ComponentNode].getName)
-
- def dropToInitAtEnd(elements: Array[StackTraceElement]) =
- elements.reverse.dropWhile(_.getMethodName != "<init>").reverse
-
- val modifyStackTrace = takeUntilComponentNode _ andThen dropToInitAtEnd
-
- val dependencyInjectorStackTraceMarker = new StackTraceElement("============= Dependency Injection =============", "newInstance", null, -1)
-
- if (throwable != null && !preserveStackTrace) {
- throwable.setStackTrace(modifyStackTrace(throwable.getStackTrace) :+
- dependencyInjectorStackTraceMarker)
-
- cutStackTraceAtConstructor(throwable.getCause)
- }
- throwable
- }
-
- override protected def newInstance() : AnyRef = {
- assert (arguments != null, "graph.complete must be called before retrieving instances.")
-
- val actualArguments = arguments.map {
- case node: Node => node.newOrCachedInstance()
- case config: ConfigKeyT => availableConfigs(config.asInstanceOf[ConfigKeyT])
- case other => other
- }
-
- val instance =
- try {
- constructor.newInstance(actualArguments: _*)
- } catch {
- case e: InvocationTargetException =>
- throw removeStackTrace(ErrorOrComponentConstructorException(cutStackTraceAtConstructor(e.getCause), s"Error constructing $idAndType"))
- }
-
- initId(instance)
- }
-
- private def ErrorOrComponentConstructorException(cause: Throwable, message: String) : Throwable = {
- if (cause != null && cause.isInstanceOf[Error]) // don't convert Errors to RuntimeExceptions
- new Error(message, cause)
- else
- new ComponentConstructorException(message, cause)
- }
-
- private def initId(component: AnyRef) = {
- def checkAndSetId(c: AbstractComponent) {
- if (c.hasInitializedId && c.getId != componentId )
- throw new IllegalStateException(
- s"Component with id '$componentId' is trying to set its component id explicitly: '${c.getId}'. " +
- "This is not allowed, so please remove any call to super() in your component's constructor.")
-
- c.initId(componentId)
- }
-
- component match {
- case component: AbstractComponent => checkAndSetId(component)
- case other => ()
- }
- component
- }
-
- override def equals(other: Any) = {
- other match {
- case that: ComponentNode =>
- super.equals(that) &&
- equalEdges(arguments.toList, that.arguments.toList) &&
- usedConfigs == that.usedConfigs
- }
- }
-
- private def usedConfigs = {
- require(availableConfigs != null, "setAvailableConfigs must be called!")
- ( arguments collect {case c: ConfigKeyT => c} map (availableConfigs) ).toList
- }
-
- def getAnnotatedConstructorParams: Array[(Type, Array[JavaAnnotation])] = {
- constructor.getGenericParameterTypes zip constructor.getParameterAnnotations
- }
-
- def setAvailableConfigs(configs: Map[ConfigKeyT, ConfigInstance]) {
- require (arguments != null, "graph.complete must be called before graph.setAvailableConfigs.")
- availableConfigs = configs
- }
-
- override def configKeys = {
- configParameterClasses.map(new ConfigKey(_, configId)).toSet
- }
-
-
- private def configParameterClasses: Array[Class[ConfigInstance]] = {
- constructor.getGenericParameterTypes.collect {
- case clazz: Class[_] if classOf[ConfigInstance].isAssignableFrom(clazz) => clazz.asInstanceOf[Class[ConfigInstance]]
- }
- }
-
- override def label = {
- val configNames = configKeys.map(_.getName + ".def").toList
-
- (List(instanceType.getSimpleName, Node.packageName(instanceType)) ::: configNames).
- mkString("{", "|", "}")
- }
-
-}
-
-object ComponentNode {
- val log = Logger.getLogger(classOf[ComponentNode].getName)
-
- private def bestConstructor(clazz: Class[AnyRef]) = {
- val publicConstructors = clazz.getConstructors.asInstanceOf[Array[Constructor[AnyRef]]]
-
- def constructorAnnotatedWithInject = {
- publicConstructors filter {_.getAnnotation(classOf[Inject]) != null} match {
- case Array() => None
- case Array(single) => Some(single)
- case _ => throwComponentConstructorException("Multiple constructors annotated with inject in class " + clazz.getName)
- }
- }
-
- def constructorWithMostConfigParameters = {
- def isConfigInstance(clazz: Class[_]) = classOf[ConfigInstance].isAssignableFrom(clazz)
-
- publicConstructors match {
- case Array() => throwComponentConstructorException("No public constructors in class " + clazz.getName)
- case Array(single) => single
- case _ =>
- log.warning("Multiple public constructors found in class %s, there should only be one. ".format(clazz.getName) +
- "If more than one public constructor is needed, the primary one must be annotated with @Inject.")
- publicConstructors.
- sortBy(_.getParameterTypes.filter(isConfigInstance).size).
- last
- }
- }
-
- constructorAnnotatedWithInject getOrElse constructorWithMostConfigParameters
- }
-
- private def throwComponentConstructorException(message: String) =
- throw removeStackTrace(new ComponentConstructorException(message))
-
- class ComponentConstructorException(message: String, cause: Throwable) extends RuntimeException(message, cause) {
- def this(message: String) = this(message, null)
- }
-
- def isAbstract(clazz: Class[_ <: AnyRef]) = Modifier.isAbstract(clazz.getModifiers)
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentRegistryNode.scala b/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentRegistryNode.scala
deleted file mode 100644
index 9c7e2ba322f..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentRegistryNode.scala
+++ /dev/null
@@ -1,71 +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.container.di.componentgraph.core
-
-import com.yahoo.component.provider.ComponentRegistry
-import com.yahoo.component.{ComponentId, Component}
-import ComponentRegistryNode._
-import com.google.inject.Key
-import com.google.inject.util.Types
-import Node.syntheticComponentId
-
-/**
- * @author Tony Vaagenes
- * @author gjoranv
- */
-class ComponentRegistryNode(val componentClass : Class[AnyRef])
- extends Node(componentId(componentClass)) {
-
- def usedComponents = componentsToInject
-
- protected def newInstance() = {
- val registry = new ComponentRegistry[AnyRef]
-
- componentsToInject foreach { component =>
- registry.register(component.componentId, component.newOrCachedInstance())
- }
-
- registry
- }
-
- override val instanceKey =
- Key.get(Types.newParameterizedType(classOf[ComponentRegistry[_]], componentClass)).asInstanceOf[Key[AnyRef]]
-
- override val instanceType: Class[AnyRef] = instanceKey.getTypeLiteral.getRawType.asInstanceOf[Class[AnyRef]]
- override val componentType: Class[AnyRef] = instanceType
-
- override def configKeys = Set()
-
- override def equals(other: Any) = {
- other match {
- case that: ComponentRegistryNode =>
- componentId == that.componentId && // includes componentClass
- instanceType == that.instanceType &&
- equalEdges(usedComponents, that.usedComponents)
- case _ => false
- }
- }
-
- override def label =
- "{ComponentRegistry\\<%s\\>|%s}".format(componentClass.getSimpleName, Node.packageName(componentClass))
-}
-
-object ComponentRegistryNode {
- val componentRegistryNamespace = ComponentId.fromString("ComponentRegistry")
-
- def componentId(componentClass: Class[_]) = {
- syntheticComponentId(componentClass.getName, componentClass, componentRegistryNamespace)
- }
-
- def equalEdges(edges: List[Node], otherEdges: List[Node]): Boolean = {
- def compareEdges = {
- (sortByComponentId(edges) zip sortByComponentId(otherEdges)).
- forall(equalEdge)
- }
-
- def sortByComponentId(in: List[Node]) = in.sortBy(_.componentId)
- def equalEdge(edgePair: (Node, Node)): Boolean = edgePair._1.componentId == edgePair._2.componentId
-
- edges.size == otherEdges.size &&
- compareEdges
- }
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/GuiceNode.scala b/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/GuiceNode.scala
deleted file mode 100644
index 26fb0e0d3d8..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/GuiceNode.scala
+++ /dev/null
@@ -1,41 +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.container.di.componentgraph.core
-
-import com.yahoo.container.di.{JavaAnnotation, createKey}
-import com.yahoo.component.ComponentId
-import Node.syntheticComponentId
-import GuiceNode._
-
-/**
- * @author Tony Vaagenes
- * @author gjoranv
- */
-final class GuiceNode(myInstance: AnyRef,
- annotation: JavaAnnotation) extends Node(componentId(myInstance)) {
-
- override def configKeys = Set()
-
- override val instanceKey = createKey(myInstance.getClass, annotation)
- override val instanceType = myInstance.getClass
- override val componentType = instanceType
-
-
- override def usedComponents = List()
-
- override protected def newInstance() = myInstance
-
- override def inject(component: Node) {
- throw new UnsupportedOperationException("Illegal to inject components to a GuiceNode!")
- }
-
- override def label =
- "{{%s|Guice}|%s}".format(instanceType.getSimpleName, Node.packageName(instanceType))
-}
-
-object GuiceNode {
- val guiceNamespace = ComponentId.fromString("Guice")
-
- def componentId(instance: AnyRef) = {
- syntheticComponentId(instance.getClass.getName, instance, guiceNamespace)
- }
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/JerseyNode.scala b/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/JerseyNode.scala
deleted file mode 100644
index 68353c47124..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/JerseyNode.scala
+++ /dev/null
@@ -1,92 +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.container.di.componentgraph.core
-
-import java.net.URL
-
-import com.yahoo.component.{ComponentId, ComponentSpecification}
-import com.yahoo.container.di.Osgi
-import com.yahoo.container.di.Osgi.RelativePath
-import com.yahoo.container.di.componentgraph.core.JerseyNode._
-import com.yahoo.container.di.config.RestApiContext
-import com.yahoo.container.di.config.RestApiContext.BundleInfo
-import org.osgi.framework.Bundle
-import org.osgi.framework.wiring.BundleWiring
-
-import scala.collection.JavaConverters._
-
-/**
- * Represents an instance of RestApiContext
- *
- * @author gjoranv
- * @author Tony Vaagenes
- */
-class JerseyNode(componentId: ComponentId,
- override val configId: String,
- clazz: Class[_ <: RestApiContext],
- osgi: Osgi)
- extends ComponentNode(componentId, configId, clazz) {
-
-
- override protected def newInstance(): RestApiContext = {
- val instance = super.newInstance()
- val restApiContext = instance.asInstanceOf[RestApiContext]
-
- val bundles = restApiContext.bundlesConfig.bundles().asScala
- bundles foreach ( bundleConfig => {
- val bundleClasses = osgi.getBundleClasses(
- ComponentSpecification.fromString(bundleConfig.spec()),
- bundleConfig.packages().asScala.toSet)
-
- restApiContext.addBundle(
- createBundleInfo(bundleClasses.bundle, bundleClasses.classEntries))
- })
-
- componentsToInject foreach (
- component =>
- restApiContext.addInjectableComponent(component.instanceKey, component.componentId, component.newOrCachedInstance()))
-
- restApiContext
- }
-
- override def equals(other: Any): Boolean = {
- super.equals(other) && (other match {
- case that: JerseyNode => componentsToInject == that.componentsToInject
- case _ => false
- })
- }
-
-}
-
-private[core]
-object JerseyNode {
- val WebInfUrl = "WebInfUrl"
-
- def createBundleInfo(bundle: Bundle, classEntries: Iterable[RelativePath]): BundleInfo = {
-
- val bundleInfo = new BundleInfo(bundle.getSymbolicName,
- bundle.getVersion,
- bundle.getLocation,
- webInfUrl(bundle),
- bundle.adapt(classOf[BundleWiring]).getClassLoader)
-
- bundleInfo.setClassEntries(classEntries.asJavaCollection)
- bundleInfo
- }
-
-
- private def getBundle(osgi: Osgi, bundleSpec: String): Bundle = {
-
- val bundle = osgi.getBundle(ComponentSpecification.fromString(bundleSpec))
- if (bundle == null)
- throw new IllegalArgumentException("Bundle not found: " + bundleSpec)
- bundle
- }
-
- private def webInfUrl(bundle: Bundle): URL = {
- val strWebInfUrl = bundle.getHeaders.get(WebInfUrl)
-
- if (strWebInfUrl == null) null
- else bundle.getEntry(strWebInfUrl)
- }
-
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/Node.scala b/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/Node.scala
deleted file mode 100644
index d2476904e39..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/Node.scala
+++ /dev/null
@@ -1,117 +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.container.di.componentgraph.core
-
-import java.util.logging.Logger
-
-import com.google.inject.Key
-import com.yahoo.component.ComponentId
-import com.yahoo.container.di.ConfigKeyT
-import com.yahoo.container.di.componentgraph.Provider
-import com.yahoo.container.di.componentgraph.core.Node._
-import com.yahoo.log.LogLevel.{DEBUG, SPAM}
-
-/**
- * @author Tony Vaagenes
- * @author gjoranv
- */
-abstract class Node(val componentId: ComponentId) {
-
- def instanceKey: Key[AnyRef]
-
- var instance : Option[AnyRef] = None
-
- var componentsToInject = List[Node]()
-
- /**
- * The components actually used by this node.
- * Consist of a subset of the injected nodes + subset of the global nodes.
- */
- def usedComponents: List[Node]
-
- protected def newInstance() : AnyRef
-
- def newOrCachedInstance() : AnyRef = {
- val inst = if (instance.isEmpty) {
- log.log(DEBUG, s"Creating new instance for component with ID $componentId")
- instance = Some(newInstance())
- instance.get
- } else {
- log.log(SPAM, s"Reusing instance for component with ID $componentId")
- instance.get
- }
- component(inst)
- }
-
- private def component(instance: AnyRef) = instance match {
- case provider: Provider[_] => provider.get().asInstanceOf[AnyRef]
- case other => other
- }
-
- def configKeys: Set[ConfigKeyT]
-
- def inject(component: Node) {
- componentsToInject ::= component
- }
-
- def injectAll(componentNodes: Traversable[ComponentNode]) {
- componentNodes.foreach(inject(_))
- }
-
- def instanceType: Class[_ <: AnyRef]
- def componentType: Class[_ <: AnyRef]
-
- override def equals(other: Any) = {
- other match {
- case that: Node =>
- getClass == that.getClass &&
- componentId == that.componentId &&
- instanceType == that.instanceType &&
- equalEdges(usedComponents, that.usedComponents)
- case _ => false
- }
- }
-
- def label: String
-
- def idAndType = {
- val className = instanceType.getName
-
- if (className == componentId.getName) s"'$componentId'"
- else s"'$componentId' of type '$className'"
- }
-
-}
-
-object Node {
- private val log = Logger.getLogger(classOf[Node].getName)
-
- def equalEdges(edges1: List[AnyRef], edges2: List[AnyRef]): Boolean = {
- def compare(objects: (AnyRef, AnyRef)): Boolean = {
- objects match {
- case (edge1: Node, edge2: Node) => equalEdge(edge1, edge2)
- case (o1, o2) => o1 == o2
- }
- }
-
- def equalEdge(e1: Node, e2: Node) = e1.componentId == e2.componentId
-
- (edges1 zip edges2).forall(compare)
- }
-
- /**
- * @param identityObject The identifying object that makes the Node unique
- */
- private[componentgraph]
- def syntheticComponentId(className: String, identityObject: AnyRef, namespace: ComponentId) = {
- val name = className + "_" + System.identityHashCode(identityObject)
- ComponentId.fromString(name).nestInNamespace(namespace)
- }
-
-
- def packageName(componentClass: Class[_]) = {
- def nullIfNotFound(index : Int) = if (index == -1) 0 else index
-
- val fullClassName = componentClass.getName
- fullClassName.substring(0, nullIfNotFound(fullClassName.lastIndexOf(".")))
- }
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/osgi/OsgiUtil.scala b/container-di/src/main/scala/com/yahoo/container/di/osgi/OsgiUtil.scala
deleted file mode 100644
index 3769eed6d2d..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/osgi/OsgiUtil.scala
+++ /dev/null
@@ -1,143 +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.container.di.osgi
-
-import java.nio.file.{Files, Path, Paths}
-import java.util.function.Predicate
-import java.util.jar.{JarEntry, JarFile}
-import java.util.logging.{Level, Logger}
-import java.util.stream.Collectors
-
-import com.google.common.io.Files.fileTreeTraverser
-import com.yahoo.component.ComponentSpecification
-import com.yahoo.container.di.Osgi.RelativePath
-import com.yahoo.osgi.maven.ProjectBundleClassPaths
-import com.yahoo.osgi.maven.ProjectBundleClassPaths.BundleClasspathMapping
-import org.osgi.framework.Bundle
-import org.osgi.framework.wiring.BundleWiring
-
-import scala.collection.JavaConverters._
-
-/**
- * Tested by com.yahoo.application.container.jersey.JerseyTest
- * @author Tony Vaagenes
- */
-object OsgiUtil {
- private val log = Logger.getLogger(getClass.getName)
- private val classFileTypeSuffix = ".class"
-
- def getClassEntriesInBundleClassPath(bundle: Bundle, packagesToScan: Set[String]) = {
- val bundleWiring = bundle.adapt(classOf[BundleWiring])
-
- def listClasses(path: String, recurse: Boolean): Iterable[RelativePath] = {
- val options =
- if (recurse) BundleWiring.LISTRESOURCES_LOCAL | BundleWiring.LISTRESOURCES_RECURSE
- else BundleWiring.LISTRESOURCES_LOCAL
-
- bundleWiring.listResources(path, "*" + classFileTypeSuffix, options).asScala
- }
-
- if (packagesToScan.isEmpty) listClasses("/", recurse = true)
- else packagesToScan flatMap { packageName => listClasses(packageToPath(packageName), recurse = false) }
- }
-
- def getClassEntriesForBundleUsingProjectClassPathMappings(classLoader: ClassLoader,
- bundleSpec: ComponentSpecification,
- packagesToScan: Set[String]) = {
- classEntriesFrom(
- bundleClassPathMapping(bundleSpec, classLoader).classPathElements.asScala.toList,
- packagesToScan)
- }
-
- private def bundleClassPathMapping(bundleSpec: ComponentSpecification,
- classLoader: ClassLoader): BundleClasspathMapping = {
-
- val projectBundleClassPaths = loadProjectBundleClassPaths(classLoader)
-
- if (projectBundleClassPaths.mainBundle.bundleSymbolicName == bundleSpec.getName) {
- projectBundleClassPaths.mainBundle
- } else {
- log.log(Level.WARNING, s"Dependencies of the bundle $bundleSpec will not be scanned. Please file a feature request if you need this" )
- matchingBundleClassPathMapping(bundleSpec, projectBundleClassPaths.providedDependencies.asScala.toList)
- }
- }
-
- def matchingBundleClassPathMapping(bundleSpec: ComponentSpecification,
- providedBundlesClassPathMappings: List[BundleClasspathMapping]): BundleClasspathMapping = {
- providedBundlesClassPathMappings.
- find(_.bundleSymbolicName == bundleSpec.getName).
- getOrElse(throw new RuntimeException("No such bundle: " + bundleSpec))
- }
-
- private def loadProjectBundleClassPaths(classLoader: ClassLoader): ProjectBundleClassPaths = {
- val classPathMappingsFileLocation = classLoader.getResource(ProjectBundleClassPaths.CLASSPATH_MAPPINGS_FILENAME)
- if (classPathMappingsFileLocation == null)
- throw new RuntimeException(s"Couldn't find ${ProjectBundleClassPaths.CLASSPATH_MAPPINGS_FILENAME} in the class path.")
-
- ProjectBundleClassPaths.load(Paths.get(classPathMappingsFileLocation.toURI))
- }
-
- private def classEntriesFrom(classPathEntries: List[String], packagesToScan: Set[String]): Iterable[RelativePath] = {
- val packagePathsToScan = packagesToScan map packageToPath
-
- classPathEntries.flatMap { entry =>
- val path = Paths.get(entry)
- if (Files.isDirectory(path)) classEntriesInPath(path, packagePathsToScan)
- else if (Files.isRegularFile(path) && path.getFileName.toString.endsWith(".jar")) classEntriesInJar(path, packagePathsToScan)
- else throw new RuntimeException("Unsupported path " + path + " in the class path")
- }
- }
-
- private def classEntriesInPath(rootPath: Path, packagePathsToScan: Traversable[String]): Traversable[RelativePath] = {
- def relativePathToClass(pathToClass: Path): RelativePath = {
- val relativePath = rootPath.relativize(pathToClass)
- relativePath.toString
- }
-
- val fileIterator =
- if (packagePathsToScan.isEmpty) fileTreeTraverser().preOrderTraversal(rootPath.toFile).asScala
- else packagePathsToScan.view flatMap { packagePath => fileTreeTraverser().children(rootPath.resolve(packagePath).toFile).asScala }
-
- for {
- file <- fileIterator
- if file.isFile
- if file.getName.endsWith(classFileTypeSuffix)
- } yield relativePathToClass(file.toPath)
- }
-
-
- private def classEntriesInJar(jarPath: Path, packagePathsToScan: Set[String]): Traversable[RelativePath] = {
- def packagePath(name: String) = {
- name.lastIndexOf('/') match {
- case -1 => name
- case n => name.substring(0, n)
- }
- }
-
- val acceptedPackage: Predicate[String] =
- if (packagePathsToScan.isEmpty) (name: String) => true
- else (name: String) => packagePathsToScan(packagePath(name))
-
- var jarFile: JarFile = null
- try {
- jarFile = new JarFile(jarPath.toFile)
- jarFile.stream().
- map[String] { entry: JarEntry => entry.getName}.
- filter { name: String => name.endsWith(classFileTypeSuffix)}.
- filter(acceptedPackage).
- collect(Collectors.toList()).
- asScala
- } finally {
- if (jarFile != null) jarFile.close()
- }
- }
-
- def packageToPath(packageName: String) = packageName.replaceAllLiterally(".", "/")
-
- implicit class JavaPredicate[T](f: T => Boolean) extends Predicate[T] {
- override def test(t: T): Boolean = f(t)
- }
-
- implicit class JavaFunction[T, R](f: T => R) extends java.util.function.Function[T, R] {
- override def apply(t: T): R = f(t)
- }
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/package.scala b/container-di/src/main/scala/com/yahoo/container/di/package.scala
deleted file mode 100644
index cccb0242e8b..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/package.scala
+++ /dev/null
@@ -1,44 +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.container
-
-import java.lang.reflect.Type
-
-import com.google.inject.Key
-import com.yahoo.config.ConfigInstance
-import com.yahoo.vespa.config.ConfigKey
-
-import scala.language.implicitConversions
-
-/**
- *
- * @author gjoranv
- * @author Tony Vaagenes
- */
-package object di {
- type ConfigKeyT = ConfigKey[_ <: ConfigInstance]
- type GuiceInjector = com.google.inject.Injector
- type JavaAnnotation = java.lang.annotation.Annotation
-
- def createKey(instanceType: Type, annotation: JavaAnnotation) = {
- {if (annotation == null)
- Key.get(instanceType)
- else
- Key.get(instanceType, annotation)
- }.asInstanceOf[Key[AnyRef]]
- }
-
- implicit def makeClassCovariant[SUB, SUPER >: SUB](clazz: Class[SUB]) : Class[SUPER] = {
- clazz.asInstanceOf[Class[SUPER]]
- }
-
- def removeStackTrace(exception: Throwable): Throwable = {
- if (preserveStackTrace) exception
- else {
- exception.setStackTrace(Array())
- exception
- }
- }
-
- //For debug purposes only
- val preserveStackTrace: Boolean = Option(System.getProperty("jdisc.container.preserveStackTrace")).filterNot(_.isEmpty).isDefined
-}
diff --git a/container-di/src/test/java/com/yahoo/component/provider/test/ComponentRegistryTestCase.java b/container-di/src/test/java/com/yahoo/component/provider/test/ComponentRegistryTestCase.java
index c02311e3b66..463a36af5d8 100644
--- a/container-di/src/test/java/com/yahoo/component/provider/test/ComponentRegistryTestCase.java
+++ b/container-di/src/test/java/com/yahoo/component/provider/test/ComponentRegistryTestCase.java
@@ -14,7 +14,7 @@ import com.yahoo.component.provider.ComponentRegistry;
/**
* Tests that ComponentRegistry handles namespaces correctly.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ComponentRegistryTestCase {
private static class TestComponent extends AbstractComponent {
diff --git a/container-di/src/test/java/com/yahoo/container/di/ConfigRetrieverTest.java b/container-di/src/test/java/com/yahoo/container/di/ConfigRetrieverTest.java
new file mode 100644
index 00000000000..e6b0309981a
--- /dev/null
+++ b/container-di/src/test/java/com/yahoo/container/di/ConfigRetrieverTest.java
@@ -0,0 +1,137 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.di;
+
+import com.yahoo.config.ConfigInstance;
+import com.yahoo.config.test.Bootstrap1Config;
+import com.yahoo.config.test.Bootstrap2Config;
+import com.yahoo.config.test.TestConfig;
+import com.yahoo.container.di.ConfigRetriever.BootstrapConfigs;
+import com.yahoo.container.di.ConfigRetriever.ComponentsConfigs;
+import com.yahoo.container.di.ConfigRetriever.ConfigSnapshot;
+import com.yahoo.vespa.config.ConfigKey;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+/**
+ *
+ * @author gjoranv
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class ConfigRetrieverTest {
+
+ private DirConfigSource dirConfigSource = null;
+
+ @Before
+ public void setup() {
+ dirConfigSource = new DirConfigSource("ConfigRetrieverTest-");
+ }
+
+ @After
+ public void cleanup() {
+ dirConfigSource.cleanup();
+ }
+
+ @Test
+ public void require_that_bootstrap_configs_come_first() {
+ writeConfigs();
+ ConfigRetriever retriever = createConfigRetriever();
+ ConfigSnapshot bootstrapConfigs = retriever.getConfigs(Collections.emptySet(), 0, false);
+
+ assertThat(bootstrapConfigs, Matchers.instanceOf(BootstrapConfigs.class));
+ }
+
+ @Test
+ @SuppressWarnings("unused")
+ public void require_that_components_comes_after_bootstrap() {
+ writeConfigs();
+ ConfigRetriever retriever = createConfigRetriever();
+ ConfigSnapshot bootstrapConfigs = retriever.getConfigs(Collections.emptySet(), 0, false);
+
+ ConfigKey<? extends ConfigInstance> testConfigKey = new ConfigKey<>(TestConfig.class, dirConfigSource.configId());
+ ConfigSnapshot componentsConfigs = retriever.getConfigs(Collections.singleton(testConfigKey), 0);
+
+ if (componentsConfigs instanceof ComponentsConfigs) {
+ assertThat(componentsConfigs.size(), is(3));
+ } else {
+ fail("ComponentsConfigs has unexpected type: " + componentsConfigs);
+ }
+ }
+
+ @Test
+ @SuppressWarnings("unused")
+ public void require_no_reconfig_when_restart_on_redeploy() {
+ // TODO
+ writeConfigs();
+ ConfigRetriever retriever = createConfigRetriever();
+ ConfigSnapshot bootstrapConfigs = retriever.getConfigs(Collections.emptySet(), 0, false);
+
+ ConfigKey<? extends ConfigInstance> testConfigKey = new ConfigKey<>(TestConfig.class, dirConfigSource.configId());
+ Optional<ConfigSnapshot> componentsConfigs = retriever.getConfigsOnce(Collections.singleton(testConfigKey), 0, true);
+
+ if (componentsConfigs.isPresent()) {
+ fail("Expected no configs");
+ }
+ }
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Ignore
+ @SuppressWarnings("unused")
+ public void require_exception_upon_modified_components_keys_without_bootstrap() {
+ expectedException.expect(IllegalArgumentException.class);
+
+ writeConfigs();
+ ConfigRetriever retriever = createConfigRetriever();
+ ConfigKey<? extends ConfigInstance> testConfigKey = new ConfigKey<>(TestConfig.class, dirConfigSource.configId());
+ ConfigSnapshot bootstrapConfigs = retriever.getConfigs(Collections.emptySet(), 0, false);
+ ConfigSnapshot componentsConfigs = retriever.getConfigs(Collections.singleton(testConfigKey), 0, false);
+ Set<ConfigKey<? extends ConfigInstance>> keys = new HashSet<>();
+ keys.add(testConfigKey);
+ keys.add(new ConfigKey<>(TestConfig.class, ""));
+ retriever.getConfigs(keys, 0);
+ }
+
+ @Test
+ public void require_that_empty_components_keys_after_bootstrap_returns_components_configs() {
+ writeConfigs();
+ ConfigRetriever retriever = createConfigRetriever();
+ assertThat(retriever.getConfigs(Collections.emptySet(), 0), instanceOf(BootstrapConfigs.class));
+ assertThat(retriever.getConfigs(Collections.emptySet(), 0), instanceOf(ComponentsConfigs.class));
+ }
+
+ public void writeConfigs() {
+ writeConfig("bootstrap1", "dummy \"ignored\"");
+ writeConfig("bootstrap2", "dummy \"ignored\"");
+ writeConfig("test", "stringVal \"ignored\"");
+ }
+
+ private ConfigRetriever createConfigRetriever() {
+ String configId = dirConfigSource.configId();
+ CloudSubscriberFactory subscriber = new CloudSubscriberFactory(dirConfigSource.configSource());
+ Set<ConfigKey<? extends ConfigInstance>> keys = new HashSet<>();
+ keys.add(new ConfigKey<>(Bootstrap1Config.class, configId));
+ keys.add(new ConfigKey<>(Bootstrap2Config.class, configId));
+ return new ConfigRetriever(keys, keySet -> subscriber.getSubscriber(keySet));
+ }
+
+ private void writeConfig(String name, String contents) {
+ dirConfigSource.writeConfig(name, contents);
+ }
+}
diff --git a/container-di/src/test/java/com/yahoo/container/di/ContainerTest.java b/container-di/src/test/java/com/yahoo/container/di/ContainerTest.java
new file mode 100644
index 00000000000..7e01505dc03
--- /dev/null
+++ b/container-di/src/test/java/com/yahoo/container/di/ContainerTest.java
@@ -0,0 +1,378 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.di;
+
+import com.yahoo.component.AbstractComponent;
+import com.yahoo.config.di.IntConfig;
+import com.yahoo.config.test.TestConfig;
+import com.yahoo.container.bundle.MockBundle;
+import com.yahoo.container.di.componentgraph.Provider;
+import com.yahoo.container.di.componentgraph.core.ComponentGraph;
+import com.yahoo.container.di.componentgraph.core.ComponentGraphTest.SimpleComponent;
+import com.yahoo.container.di.componentgraph.core.ComponentGraphTest.SimpleComponent2;
+import com.yahoo.container.di.componentgraph.core.ComponentNode.ComponentConstructorException;
+import com.yahoo.container.di.componentgraph.core.Node;
+import com.yahoo.container.di.config.RestApiContext;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @author Tony Vaagenes
+ * @author gjoranv
+ * @author ollivir
+ */
+public class ContainerTest extends ContainerTestBase {
+
+ @Test
+ public void components_can_be_created_from_config() {
+ writeBootstrapConfigs();
+ dirConfigSource.writeConfig("test", "stringVal \"myString\"");
+
+ Container container = newContainer(dirConfigSource);
+
+ ComponentTakingConfig component = createComponentTakingConfig(container.getNewComponentGraph());
+ assertThat(component.config.stringVal(), is("myString"));
+
+ container.shutdownConfigurer();
+ }
+
+ @Test
+ public void components_are_reconfigured_after_config_update_without_bootstrap_configs() {
+ writeBootstrapConfigs();
+ dirConfigSource.writeConfig("test", "stringVal \"original\"");
+
+ Container container = newContainer(dirConfigSource);
+
+ ComponentGraph componentGraph = container.getNewComponentGraph();
+ ComponentTakingConfig component = createComponentTakingConfig(componentGraph);
+
+ assertThat(component.config.stringVal(), is("original"));
+
+ // Reconfigure
+ dirConfigSource.writeConfig("test", "stringVal \"reconfigured\"");
+ container.reloadConfig(2);
+
+ ComponentGraph newComponentGraph = container.getNewComponentGraph(componentGraph);
+ ComponentTakingConfig component2 = createComponentTakingConfig(newComponentGraph);
+ assertThat(component2.config.stringVal(), is("reconfigured"));
+
+ container.shutdownConfigurer();
+ }
+
+ @Test
+ public void graph_is_updated_after_bootstrap_update() {
+ dirConfigSource.writeConfig("test", "stringVal \"original\"");
+ writeBootstrapConfigs("id1");
+
+ Container container = newContainer(dirConfigSource);
+
+ ComponentGraph graph = container.getNewComponentGraph();
+ ComponentTakingConfig component = createComponentTakingConfig(graph);
+ assertThat(component.getId().toString(), is("id1"));
+
+ writeBootstrapConfigs(
+ new ComponentEntry("id1", ComponentTakingConfig.class),
+ new ComponentEntry("id2", ComponentTakingConfig.class));
+
+ container.reloadConfig(2);
+ ComponentGraph newGraph = container.getNewComponentGraph(graph);
+
+ assertThat(ComponentGraph.getNode(newGraph, "id1"), notNullValue(Node.class));
+ assertThat(ComponentGraph.getNode(newGraph, "id2"), notNullValue(Node.class));
+
+ container.shutdownConfigurer();
+ }
+
+ //@Test TODO
+ public void deconstructor_is_given_guice_components() {
+ }
+
+ @Test
+ public void osgi_component_is_deconstructed_when_not_reused() {
+ writeBootstrapConfigs("id1", DestructableComponent.class);
+
+ Container container = newContainer(dirConfigSource);
+
+ ComponentGraph oldGraph = container.getNewComponentGraph();
+ DestructableComponent componentToDestruct = oldGraph.getInstance(DestructableComponent.class);
+
+ writeBootstrapConfigs("id2", DestructableComponent.class);
+ container.reloadConfig(2);
+ container.getNewComponentGraph(oldGraph);
+ assertTrue(componentToDestruct.deconstructed);
+ }
+
+ @Ignore // because logAndDie is impossible(?) to verify programmatically
+ @Test
+ public void manually_verify_what_happens_when_first_graph_contains_component_that_throws_exception_in_ctor() {
+ writeBootstrapConfigs("thrower", ComponentThrowingExceptionInConstructor.class);
+ Container container = newContainer(dirConfigSource);
+ try {
+ container.getNewComponentGraph();
+ fail("Expected to log and die.");
+ } catch (Throwable t) {
+ fail("Expected to log and die");
+ }
+ }
+
+ @Test
+ public void previous_graph_is_retained_when_new_graph_contains_component_that_throws_exception_in_ctor() {
+ ComponentEntry simpleComponentEntry = new ComponentEntry("simpleComponent", SimpleComponent.class);
+
+ writeBootstrapConfigs(simpleComponentEntry);
+ Container container = newContainer(dirConfigSource);
+ ComponentGraph currentGraph = container.getNewComponentGraph();
+
+ SimpleComponent simpleComponent = currentGraph.getInstance(SimpleComponent.class);
+
+ writeBootstrapConfigs("thrower", ComponentThrowingExceptionInConstructor.class);
+ container.reloadConfig(2);
+ try {
+ currentGraph = container.getNewComponentGraph(currentGraph);
+ fail("Expected exception");
+ } catch (ComponentConstructorException ignored) {
+ // Expected, do nothing
+ } catch (Throwable t) {
+ fail("Expected ComponentConstructorException");
+ }
+ assertEquals(1, currentGraph.generation());
+
+ // Also verify that next reconfig is successful
+ ComponentEntry componentTakingConfigEntry = new ComponentEntry("componentTakingConfig", ComponentTakingConfig.class);
+ dirConfigSource.writeConfig("test", "stringVal \"myString\"");
+ writeBootstrapConfigs(simpleComponentEntry, componentTakingConfigEntry);
+ container.reloadConfig(3);
+ currentGraph = container.getNewComponentGraph(currentGraph);
+
+ assertEquals(3, currentGraph.generation());
+ assertSame(simpleComponent, currentGraph.getInstance(SimpleComponent.class));
+ assertNotNull(currentGraph.getInstance(ComponentTakingConfig.class));
+ }
+
+ @Test
+ public void previous_graph_is_retained_when_new_graph_throws_exception_for_missing_config() {
+ ComponentEntry simpleComponentEntry = new ComponentEntry("simpleComponent", SimpleComponent.class);
+
+ writeBootstrapConfigs(simpleComponentEntry);
+ Container container = newContainer(dirConfigSource);
+ ComponentGraph currentGraph = container.getNewComponentGraph();
+
+ currentGraph.getInstance(SimpleComponent.class);
+
+ writeBootstrapConfigs("thrower", ComponentThrowingExceptionForMissingConfig.class);
+ dirConfigSource.writeConfig("test", "stringVal \"myString\"");
+ container.reloadConfig(2);
+ try {
+ currentGraph = container.getNewComponentGraph(currentGraph);
+ fail("Expected exception");
+ } catch (IllegalArgumentException ignored) {
+ // Expected, do nothing
+ } catch (Throwable t) {
+ fail("Expected IllegalArgumentException");
+ }
+ assertEquals(1, currentGraph.generation());
+ }
+
+ @Test
+ public void runOnce_hangs_waiting_for_valid_config_after_invalid_config() throws InterruptedException, ExecutionException, TimeoutException {
+ dirConfigSource.writeConfig("test", "stringVal \"original\"");
+ writeBootstrapConfigs("myId", ComponentTakingConfig.class);
+
+ Container container = newContainer(dirConfigSource);
+ final ComponentGraph currentGraph = container.getNewComponentGraph();
+
+ writeBootstrapConfigs("thrower", ComponentThrowingExceptionForMissingConfig.class);
+ container.reloadConfig(2);
+
+ try {
+ container.getNewComponentGraph(currentGraph);
+ fail("expected exception");
+ } catch (Exception ignored) {
+ }
+ ExecutorService exec = Executors.newFixedThreadPool(1);
+ Future<ComponentGraph> newGraph = exec.submit(() -> container.getNewComponentGraph(currentGraph));
+
+ try {
+ newGraph.get(1, TimeUnit.SECONDS);
+ fail("Expected waiting for new config.");
+ } catch (Exception ignored) {
+ // expect to time out
+ }
+
+ writeBootstrapConfigs("myId2", ComponentTakingConfig.class);
+ container.reloadConfig(3);
+
+ assertNotNull(newGraph.get(5, TimeUnit.MINUTES));
+ }
+
+
+ @Test
+ public void bundle_info_is_set_on_rest_api_context() {
+ Class<RestApiContext> clazz = RestApiContext.class;
+
+ writeBootstrapConfigs("restApiContext", clazz);
+ dirConfigSource.writeConfig("jersey-bundles", "bundles[0].spec \"mock-entry-to-enforce-a-MockBundle\"");
+ dirConfigSource.writeConfig("jersey-injection", "inject[0]");
+
+ Container container = newContainer(dirConfigSource);
+ ComponentGraph componentGraph = container.getNewComponentGraph();
+
+ RestApiContext restApiContext = componentGraph.getInstance(clazz);
+ assertNotNull(restApiContext);
+
+ assertThat(restApiContext.getBundles().size(), is(1));
+ assertThat(restApiContext.getBundles().get(0).symbolicName, is(MockBundle.SymbolicName));
+ assertThat(restApiContext.getBundles().get(0).version, is(MockBundle.BundleVersion));
+
+ container.shutdownConfigurer();
+ }
+
+ @Test
+ public void restApiContext_has_all_components_injected() {
+ Class<RestApiContext> restApiClass = RestApiContext.class;
+ Class<SimpleComponent> injectedClass = SimpleComponent.class;
+ String injectedComponentId = "injectedComponent";
+ Class<SimpleComponent2> anotherComponentClass = SimpleComponent2.class;
+ String anotherComponentId = "anotherComponent";
+
+ String componentsConfig =
+ new ComponentEntry(injectedComponentId, injectedClass).asConfig(0) + "\n" +
+ new ComponentEntry(anotherComponentId, anotherComponentClass).asConfig(1) + "\n" +
+ new ComponentEntry("restApiContext", restApiClass).asConfig(2) + "\n" +
+ "components[2].inject[0].id " + injectedComponentId + "\n" +
+ "components[2].inject[1].id " + anotherComponentId + "\n";
+
+ String injectionConfig = "inject[1]\n" +//
+ "inject[0].instance " + injectedComponentId + "\n" +//
+ "inject[0].forClass \"" + injectedClass.getName() + "\"\n";
+
+ dirConfigSource.writeConfig("components", componentsConfig);
+ dirConfigSource.writeConfig("bundles", "");
+ dirConfigSource.writeConfig("jersey-bundles", "bundles[0].spec \"mock-entry-to-enforce-a-MockBundle\"");
+ dirConfigSource.writeConfig("jersey-injection", injectionConfig);
+
+ Container container = newContainer(dirConfigSource);
+ ComponentGraph componentGraph = container.getNewComponentGraph();
+
+ RestApiContext restApiContext = componentGraph.getInstance(restApiClass);
+
+ assertFalse(restApiContext.getInjectableComponents().isEmpty());
+ assertThat(restApiContext.getInjectableComponents().size(), is(2));
+
+ container.shutdownConfigurer();
+ }
+
+ @Test
+ public void providers_are_destructed() {
+ writeBootstrapConfigs("id1", DestructableProvider.class);
+
+ ComponentDeconstructor deconstructor = new ComponentDeconstructor() {
+ @Override
+ public void deconstruct(Object component) {
+ if (component instanceof AbstractComponent) {
+ ((AbstractComponent) component).deconstruct();
+ ;
+ } else if (component instanceof Provider) {
+ ((Provider<?>) component).deconstruct();
+ }
+ }
+ };
+
+ Container container = newContainer(dirConfigSource, deconstructor);
+
+ ComponentGraph oldGraph = container.getNewComponentGraph();
+ DestructableEntity destructableEntity = oldGraph.getInstance(DestructableEntity.class);
+
+ writeBootstrapConfigs("id2", DestructableProvider.class);
+ container.reloadConfig(2);
+ container.getNewComponentGraph(oldGraph);
+
+ assertTrue(destructableEntity.deconstructed);
+ }
+
+ static class DestructableEntity {
+ private boolean deconstructed = false;
+ }
+
+ public static class DestructableProvider implements Provider<DestructableEntity> {
+ DestructableEntity instance = new DestructableEntity();
+
+ public DestructableEntity get() {
+ return instance;
+ }
+
+ public void deconstruct() {
+ assertFalse(instance.deconstructed);
+ instance.deconstructed = true;
+ }
+ }
+
+ public static class ComponentTakingConfig extends AbstractComponent {
+ private final TestConfig config;
+
+ public ComponentTakingConfig(TestConfig config) {
+ assertNotNull(config);
+ this.config = config;
+ }
+ }
+
+ public static class ComponentThrowingExceptionInConstructor {
+ public ComponentThrowingExceptionInConstructor() {
+ throw new RuntimeException("This component fails upon construction.");
+ }
+ }
+
+ public static class ComponentThrowingExceptionForMissingConfig extends AbstractComponent {
+ public ComponentThrowingExceptionForMissingConfig(IntConfig intConfig) {
+ fail("This component should never be created. Only used for tests where 'int' config is missing.");
+ }
+ }
+
+ public static class DestructableComponent extends AbstractComponent {
+ private boolean deconstructed = false;
+
+ @Override
+ public void deconstruct() {
+ deconstructed = true;
+ }
+ }
+
+ public static class TestDeconstructor implements ComponentDeconstructor {
+ @Override
+ public void deconstruct(Object component) {
+ if (component instanceof DestructableComponent) {
+ DestructableComponent vespaComponent = (DestructableComponent) component;
+ vespaComponent.deconstruct();
+ }
+ }
+ }
+
+ private static Container newContainer(DirConfigSource dirConfigSource,
+ ComponentDeconstructor deconstructor) {
+ return new Container(new CloudSubscriberFactory(dirConfigSource.configSource), dirConfigSource.configId(), deconstructor);
+ }
+
+ private static Container newContainer(DirConfigSource dirConfigSource) {
+ return newContainer(dirConfigSource, new TestDeconstructor());
+ }
+
+ private ComponentTakingConfig createComponentTakingConfig(ComponentGraph componentGraph) {
+ return componentGraph.getInstance(ComponentTakingConfig.class);
+ }
+}
diff --git a/container-di/src/test/java/com/yahoo/container/di/ContainerTestBase.java b/container-di/src/test/java/com/yahoo/container/di/ContainerTestBase.java
new file mode 100644
index 00000000000..79cb080dfa4
--- /dev/null
+++ b/container-di/src/test/java/com/yahoo/container/di/ContainerTestBase.java
@@ -0,0 +1,120 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.di;
+
+import com.google.inject.Guice;
+import com.yahoo.component.ComponentSpecification;
+import com.yahoo.config.FileReference;
+import com.yahoo.container.bundle.BundleInstantiationSpecification;
+import com.yahoo.container.di.CloudSubscriberFactory;
+import com.yahoo.container.di.ContainerTest.ComponentTakingConfig;
+import com.yahoo.container.di.componentgraph.core.ComponentGraph;
+import com.yahoo.container.di.osgi.BundleClasses;
+import org.junit.After;
+import org.junit.Before;
+import org.osgi.framework.Bundle;
+
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * @author Tony Vaagenes
+ * @author gjoranv
+ * @author ollivir
+ */
+public class ContainerTestBase {
+ private ComponentGraph componentGraph;
+ protected DirConfigSource dirConfigSource = null;
+
+ @Before
+ public void setup() {
+ dirConfigSource = new DirConfigSource("ContainerTest-");
+ }
+
+ @After
+ public void cleanup() {
+ dirConfigSource.cleanup();
+ }
+
+ @Before
+ public void createGraph() {
+ componentGraph = new ComponentGraph(0);
+ }
+
+ public void complete() {
+ try {
+ Container container = new Container(new CloudSubscriberFactory(dirConfigSource.configSource()), dirConfigSource.configId(),
+ new ContainerTest.TestDeconstructor(), new Osgi() {
+ @SuppressWarnings("unchecked")
+ @Override
+ public Class<Object> resolveClass(BundleInstantiationSpecification spec) {
+ try {
+ return (Class<Object>) Class.forName(spec.classId.getName());
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public BundleClasses getBundleClasses(ComponentSpecification bundle, Set<String> packagesToScan) {
+ throw new UnsupportedOperationException("getBundleClasses not supported");
+ }
+
+ @Override
+ public void useBundles(Collection<FileReference> bundles) {
+ }
+
+ @Override
+ public Bundle getBundle(ComponentSpecification spec) {
+ throw new UnsupportedOperationException("getBundle not supported.");
+ }
+ });
+ componentGraph = container.getNewComponentGraph(componentGraph, Guice.createInjector(), false);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public <T> T getInstance(Class<T> componentClass) {
+ return componentGraph.getInstance(componentClass);
+ }
+
+ protected void writeBootstrapConfigs(ComponentEntry... componentEntries) {
+ dirConfigSource.writeConfig("bundles", "");
+ StringBuilder components = new StringBuilder();
+ for (int i = 0; i < componentEntries.length; i++) {
+ components.append(componentEntries[i].asConfig(i));
+ components.append('\n');
+ }
+ dirConfigSource.writeConfig("components", String.format("components[%s]\n%s", componentEntries.length, components));
+ }
+
+ protected void writeBootstrapConfigs(String componentId, Class<?> classId) {
+ writeBootstrapConfigs(new ComponentEntry(componentId, classId));
+ }
+
+ protected void writeBootstrapConfigs(String componentId) {
+ writeBootstrapConfigs(componentId, ComponentTakingConfig.class);
+ }
+
+ protected void writeBootstrapConfigs() {
+ writeBootstrapConfigs(ComponentTakingConfig.class.getName(), ComponentTakingConfig.class);
+ }
+
+ protected class ComponentEntry {
+ private final String componentId;
+ private final Class<?> classId;
+
+ ComponentEntry(String componentId, Class<?> classId) {
+ this.componentId = componentId;
+ this.classId = classId;
+ }
+
+ String asConfig(int position) {
+ return "<config>\n" + //
+ "components[" + position + "].id \"" + componentId + "\"\n" + //
+ "components[" + position + "].classId \"" + classId.getName() + "\"\n" + //
+ "components[" + position + "].configId \"" + dirConfigSource.configId() + "\"\n" + //
+ "</config>";
+ }
+ }
+}
diff --git a/container-di/src/test/java/com/yahoo/container/di/DirConfigSource.java b/container-di/src/test/java/com/yahoo/container/di/DirConfigSource.java
new file mode 100644
index 00000000000..ec937a1a4ef
--- /dev/null
+++ b/container-di/src/test/java/com/yahoo/container/di/DirConfigSource.java
@@ -0,0 +1,69 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.di;
+
+import com.yahoo.config.subscription.ConfigSource;
+import com.yahoo.config.subscription.ConfigSourceSet;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Random;
+
+/**
+ * @author Tony Vaagenes
+ * @author gjoranv
+ * @author ollivir
+ */
+public class DirConfigSource {
+ private final TemporaryFolder tempFolder = createTemporaryFolder();
+ public final ConfigSource configSource;
+
+ public DirConfigSource(String testSourcePrefix) {
+ this.configSource = new ConfigSourceSet(testSourcePrefix + new Random().nextLong());
+ }
+
+ public void writeConfig(String name, String contents) {
+ File file = new File(tempFolder.getRoot(), name + ".cfg");
+ if (!file.exists()) {
+ try {
+ file.createNewFile();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ printFile(file, contents + "\n");
+ }
+
+ public String configId() {
+ return "dir:" + tempFolder.getRoot().getPath();
+ }
+
+ public ConfigSource configSource() {
+ return configSource;
+ }
+
+ public void cleanup() {
+ tempFolder.delete();
+ }
+
+ private static void printFile(File f, String content) {
+ try (OutputStream out = new FileOutputStream(f)) {
+ out.write(content.getBytes("UTF-8"));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static TemporaryFolder createTemporaryFolder() {
+ TemporaryFolder folder = new TemporaryFolder();
+ try {
+ folder.create();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return folder;
+ }
+}
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
new file mode 100644
index 00000000000..337c875b429
--- /dev/null
+++ b/container-di/src/test/java/com/yahoo/container/di/componentgraph/core/ComponentGraphTest.java
@@ -0,0 +1,674 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.di.componentgraph.core;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.name.Named;
+import com.google.inject.name.Names;
+import com.yahoo.collections.Pair;
+import com.yahoo.component.AbstractComponent;
+import com.yahoo.component.ComponentId;
+import com.yahoo.component.provider.ComponentRegistry;
+import com.yahoo.config.ConfigInstance;
+import com.yahoo.config.subscription.ConfigGetter;
+import com.yahoo.config.test.Test2Config;
+import com.yahoo.config.test.TestConfig;
+import com.yahoo.container.di.Osgi;
+import com.yahoo.container.di.componentgraph.Provider;
+import com.yahoo.container.di.config.JerseyBundlesConfig;
+import com.yahoo.container.di.config.JerseyInjectionConfig;
+import com.yahoo.container.di.config.RestApiContext;
+import com.yahoo.vespa.config.ConfigKey;
+import org.junit.Test;
+
+import java.lang.annotation.Annotation;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.function.Supplier;
+
+import static com.yahoo.container.di.componentgraph.core.ComponentGraph.isBindingAnnotation;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.sameInstance;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @author gjoranv
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class ComponentGraphTest {
+ public static class ConfigMap extends HashMap<ConfigKey<? extends ConfigInstance>, ConfigInstance> {
+ public ConfigMap() {
+ super();
+ }
+
+ public <T extends ConfigInstance> ConfigMap add(Class<T> clazz, String configId) {
+ ConfigKey<T> key = new ConfigKey<>(clazz, configId);
+ put(key, ConfigGetter.getConfig(key.getConfigClass(), key.getConfigId()));
+ return this;
+ }
+
+ public static <T extends ConfigInstance> ConfigMap newMap(Class<T> clazz, String configId) {
+ ConfigMap ret = new ConfigMap();
+ ret.add(clazz, configId);
+ return ret;
+ }
+ }
+
+ @Test
+ public void component_taking_config_can_be_instantiated() {
+ ComponentGraph componentGraph = new ComponentGraph();
+ String configId = "raw:stringVal \"test-value\"";
+ Node componentNode = mockComponentNode(ComponentTakingConfig.class, configId);
+
+ componentGraph.add(componentNode);
+ componentGraph.complete();
+ componentGraph.setAvailableConfigs(ConfigMap.newMap(TestConfig.class, configId));
+
+ ComponentTakingConfig instance = componentGraph.getInstance(ComponentTakingConfig.class);
+ assertNotNull(instance);
+ assertThat(instance.config.stringVal(), is("test-value"));
+ }
+
+ @Test
+ public void component_can_be_injected_into_another_component() {
+ Node injectedComponent = mockComponentNode(SimpleComponent.class);
+ Node targetComponent = mockComponentNode(ComponentTakingComponent.class);
+ targetComponent.inject(injectedComponent);
+
+ Node destroyGlobalLookupComponent = mockComponentNode(SimpleComponent.class);
+
+ ComponentGraph componentGraph = new ComponentGraph();
+ componentGraph.add(injectedComponent);
+ componentGraph.add(targetComponent);
+ componentGraph.add(destroyGlobalLookupComponent);
+ componentGraph.complete();
+
+ ComponentTakingComponent instance = componentGraph.getInstance(ComponentTakingComponent.class);
+ assertNotNull(instance);
+ }
+
+ @Test
+ public void all_components_of_a_type_can_be_injected() {
+ ComponentGraph componentGraph = new ComponentGraph();
+ componentGraph.add(mockComponentNode(SimpleComponent.class));
+ componentGraph.add(mockComponentNode(SimpleComponent.class));
+ componentGraph.add(mockComponentNode(SimpleDerivedComponent.class));
+ componentGraph.add(mockComponentNode(ComponentTakingAllSimpleComponents.class));
+ componentGraph.complete();
+
+ ComponentTakingAllSimpleComponents instance = componentGraph.getInstance(ComponentTakingAllSimpleComponents.class);
+ assertThat(instance.simpleComponents.allComponents().size(), is(3));
+ }
+
+ @Test
+ public void empty_component_registry_can_be_injected() {
+ ComponentGraph componentGraph = new ComponentGraph();
+ componentGraph.add(mockComponentNode(ComponentTakingAllSimpleComponents.class));
+ componentGraph.complete();
+
+ ComponentTakingAllSimpleComponents instance = componentGraph.getInstance(ComponentTakingAllSimpleComponents.class);
+ assertThat(instance.simpleComponents.allComponents().size(), is(0));
+ }
+
+ @Test
+ public void component_registry_with_wildcard_upper_bound_can_be_injected() {
+ ComponentGraph componentGraph = new ComponentGraph();
+ componentGraph.add(mockComponentNode(SimpleComponent.class));
+ componentGraph.add(mockComponentNode(SimpleDerivedComponent.class));
+ componentGraph.add(mockComponentNode(ComponentTakingAllSimpleComponentsUpperBound.class));
+ componentGraph.complete();
+
+ ComponentTakingAllSimpleComponentsUpperBound instance = componentGraph
+ .getInstance(ComponentTakingAllSimpleComponentsUpperBound.class);
+ assertThat(instance.simpleComponents.allComponents().size(), is(2));
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void require_exception_when_injecting_registry_with_unknown_type_variable() {
+ @SuppressWarnings("rawtypes")
+ Class<ComponentTakingAllComponentsWithTypeVariable> clazz = ComponentTakingAllComponentsWithTypeVariable.class;
+
+ ComponentGraph componentGraph = new ComponentGraph();
+ componentGraph.add(mockComponentNode(SimpleComponent.class));
+ componentGraph.add(mockComponentNode(SimpleDerivedComponent.class));
+ componentGraph.add(mockComponentNode(clazz));
+ componentGraph.complete();
+
+ componentGraph.getInstance(clazz);
+ }
+
+ @Test
+ public void components_are_shared() {
+ ComponentGraph componentGraph = new ComponentGraph();
+ componentGraph.add(mockComponentNode(SimpleComponent.class));
+ componentGraph.complete();
+
+ SimpleComponent instance1 = componentGraph.getInstance(SimpleComponent.class);
+ SimpleComponent instance2 = componentGraph.getInstance(SimpleComponent.class);
+ assertThat(instance1, sameInstance(instance2));
+ }
+
+ @Test
+ public void singleton_components_can_be_injected() {
+ ComponentGraph componentGraph = new ComponentGraph();
+ String configId = "raw:stringVal \"test-value\"";
+
+ componentGraph.add(mockComponentNode(ComponentTakingComponent.class));
+ componentGraph.add(mockComponentNode(ComponentTakingConfig.class, configId));
+ componentGraph.add(mockComponentNode(SimpleComponent2.class));
+ componentGraph.complete();
+ componentGraph.setAvailableConfigs(ConfigMap.newMap(TestConfig.class, configId));
+
+ ComponentTakingComponent instance = componentGraph.getInstance(ComponentTakingComponent.class);
+ ComponentTakingConfig injected = (ComponentTakingConfig) instance.injectedComponent;
+ assertThat(injected.config.stringVal(), is("test-value"));
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void require_error_when_multiple_components_match_a_singleton_dependency() {
+ ComponentGraph componentGraph = new ComponentGraph();
+ componentGraph.add(mockComponentNode(SimpleDerivedComponent.class));
+ componentGraph.add(mockComponentNode(SimpleComponent.class));
+ componentGraph.add(mockComponentNode(ComponentTakingComponent.class));
+ componentGraph.complete();
+ }
+
+ @Test
+ public void named_component_can_be_injected() {
+ ComponentGraph componentGraph = new ComponentGraph();
+ componentGraph.add(mockComponentNode(SimpleComponent.class));
+ componentGraph.add(mockComponentNode(SimpleComponent.class, Names.named("named-test")));
+ componentGraph.add(mockComponentNode(ComponentTakingNamedComponent.class));
+ componentGraph.complete();
+ }
+
+ @Test
+ public void config_keys_can_be_retrieved() {
+ ComponentGraph componentGraph = new ComponentGraph();
+ componentGraph.add(mockComponentNode(ComponentTakingConfig.class, "raw:stringVal \"component1\""));
+ componentGraph.add(mockComponentNode(ComponentTakingConfig.class, "raw:stringVal \"component2\""));
+ componentGraph.add(new ComponentRegistryNode(ComponentTakingConfig.class));
+ componentGraph.complete();
+
+ Set<ConfigKey<? extends ConfigInstance>> configKeys = componentGraph.configKeys();
+ assertThat(configKeys.size(), is(2));
+
+ configKeys.forEach(key -> {
+ assertThat(key.getConfigClass(), equalTo(TestConfig.class));
+ assertThat(key.getConfigId(), containsString("component"));
+ });
+ }
+
+ @Test
+ public void providers_can_be_instantiated() {
+ ComponentGraph componentGraph = new ComponentGraph();
+ componentGraph.add(mockComponentNode(ExecutorProvider.class));
+ componentGraph.complete();
+
+ assertNotNull(componentGraph.getInstance(Executor.class));
+ }
+
+ @Test
+ public void providers_can_be_inherited() {
+ ComponentGraph componentGraph = new ComponentGraph();
+ componentGraph.add(mockComponentNode(DerivedExecutorProvider.class));
+ componentGraph.complete();
+
+ assertNotNull(componentGraph.getInstance(Executor.class));
+ }
+
+ @Test
+ public void providers_can_deliver_a_new_instance_for_each_component() {
+ ComponentGraph componentGraph = new ComponentGraph();
+ componentGraph.add(mockComponentNode(NewIntProvider.class));
+ componentGraph.complete();
+
+ Integer instance1 = componentGraph.getInstance(Integer.class);
+ Integer instance2 = componentGraph.getInstance(Integer.class);
+ assertThat(instance1, not(equalTo(instance2)));
+ }
+
+ @Test
+ public void providers_can_be_injected_explicitly() {
+ ComponentGraph componentGraph = new ComponentGraph();
+
+ Node componentTakingExecutor = mockComponentNode(ComponentTakingExecutor.class);
+ Node executorProvider = mockComponentNode(ExecutorProvider.class);
+ componentTakingExecutor.inject(executorProvider);
+
+ componentGraph.add(executorProvider);
+ componentGraph.add(mockComponentNode(ExecutorProvider.class));
+
+ componentGraph.add(componentTakingExecutor);
+
+ componentGraph.complete();
+ assertNotNull(componentGraph.getInstance(ComponentTakingExecutor.class));
+ }
+
+ @Test
+ public void global_providers_can_be_injected() {
+ ComponentGraph componentGraph = new ComponentGraph();
+
+ componentGraph.add(mockComponentNode(ComponentTakingExecutor.class));
+ componentGraph.add(mockComponentNode(ExecutorProvider.class));
+ componentGraph.add(mockComponentNode(IntProvider.class));
+ componentGraph.complete();
+
+ assertNotNull(componentGraph.getInstance(ComponentTakingExecutor.class));
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void throw_if_multiple_global_providers_exist() {
+ ComponentGraph componentGraph = new ComponentGraph();
+
+ componentGraph.add(mockComponentNode(ExecutorProvider.class));
+ componentGraph.add(mockComponentNode(ExecutorProvider.class));
+ componentGraph.add(mockComponentNode(ComponentTakingExecutor.class));
+ componentGraph.complete();
+ }
+
+ @Test
+ public void provider_is_not_used_when_component_of_provided_class_exists() {
+ ComponentGraph componentGraph = new ComponentGraph();
+
+ componentGraph.add(mockComponentNode(SimpleComponent.class));
+ componentGraph.add(mockComponentNode(SimpleComponentProviderThatThrows.class));
+ componentGraph.add(mockComponentNode(ComponentTakingComponent.class));
+ componentGraph.complete();
+
+ SimpleComponent injectedComponent = componentGraph.getInstance(ComponentTakingComponent.class).injectedComponent;
+ assertNotNull(injectedComponent);
+ }
+
+ //TODO: move
+ @Test
+ public void check_if_annotation_is_a_binding_annotation() {
+ assertTrue(isBindingAnnotation(Names.named("name")));
+ assertFalse(isBindingAnnotation(Named.class.getAnnotations()[0]));
+ }
+
+ @Test
+ public void cycles_gives_exception() {
+ ComponentGraph componentGraph = new ComponentGraph();
+
+ Node node1 = mockComponentNode(ComponentCausingCycle.class);
+ Node node2 = mockComponentNode(ComponentCausingCycle.class);
+
+ node1.inject(node2);
+ node2.inject(node1);
+
+ componentGraph.add(node1);
+ componentGraph.add(node2);
+
+ try {
+ componentGraph.complete();
+ fail("Cycle exception expected.");
+ } catch (Throwable e) {
+ assertThat(e.getMessage(), containsString("cycle"));
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void abstract_classes_are_rejected() {
+ new ComponentNode(ComponentId.fromString("Test"), "", AbstractClass.class);
+ }
+
+ @Test
+ public void inject_constructor_is_preferred() {
+ assertThatComponentCanBeCreated(ComponentWithInjectConstructor.class);
+ }
+
+ @Test
+ public void constructor_with_most_parameters_is_preferred() {
+ assertThatComponentCanBeCreated(ComponentWithMultipleConstructors.class);
+ }
+
+ public void assertThatComponentCanBeCreated(Class<?> clazz) {
+ ComponentGraph componentGraph = new ComponentGraph();
+ String configId = "raw:stringVal \"dummy\"";
+
+ componentGraph.add(mockComponentNode(clazz, configId));
+ componentGraph.complete();
+
+ componentGraph.setAvailableConfigs(ConfigMap.newMap(TestConfig.class, configId).add(Test2Config.class, configId));
+
+ assertNotNull(componentGraph.getInstance(clazz));
+ }
+
+ @Test
+ public void require_fallback_to_child_injector() {
+ ComponentGraph componentGraph = new ComponentGraph();
+
+ componentGraph.add(mockComponentNode(ComponentTakingExecutor.class));
+
+ componentGraph.complete(singletonExecutorInjector);
+ assertNotNull(componentGraph.getInstance(ComponentTakingExecutor.class));
+ }
+
+ @Test
+ public void child_injector_can_inject_multiple_instances_for_same_key() {
+ Pair<Integer, Pair<Executor, Executor>> graph = buildGraphWithChildInjector(Executors::newSingleThreadExecutor);
+ int graphSize = graph.getFirst();
+ Executor executorA = graph.getSecond().getFirst();
+ Executor executorB = graph.getSecond().getSecond();
+
+ assertThat(graphSize, is(4));
+ assertThat(executorA, not(sameInstance(executorB)));
+ }
+
+ @Test
+ public void components_injected_via_child_injector_can_be_shared() {
+ Executor commonExecutor = Executors.newSingleThreadExecutor();
+ Pair<Integer, Pair<Executor, Executor>> graph = buildGraphWithChildInjector(() -> commonExecutor);
+ int graphSize = graph.getFirst();
+ Executor executorA = graph.getSecond().getFirst();
+ Executor executorB = graph.getSecond().getSecond();
+
+ assertThat(graphSize, is(3));
+ assertThat(executorA, sameInstance(executorB));
+ }
+
+ private Pair<Integer, Pair<Executor, Executor>> buildGraphWithChildInjector(Supplier<Executor> executorProvider) {
+ Injector childInjector = Guice.createInjector(new AbstractModule() {
+ @Override
+ public void configure() {
+ bind(Executor.class).toProvider(executorProvider::get);
+ }
+ });
+
+ ComponentGraph componentGraph = new ComponentGraph();
+
+ Key<ComponentTakingExecutor> keyA = Key.get(ComponentTakingExecutor.class, Names.named("A"));
+ Key<ComponentTakingExecutor> keyB = Key.get(ComponentTakingExecutor.class, Names.named("B"));
+
+ componentGraph.add(mockComponentNode(keyA));
+ componentGraph.add(mockComponentNode(keyB));
+
+ componentGraph.complete(childInjector);
+
+ return new Pair<>(componentGraph.size(),
+ new Pair<>(componentGraph.getInstance(keyA).executor, componentGraph.getInstance(keyB).executor));
+ }
+
+ @Test
+ public void providers_can_be_reused() {
+
+ ComponentGraph oldGraph = createReusingGraph();
+ Executor executor = oldGraph.getInstance(Executor.class);
+
+ ComponentGraph newGraph = createReusingGraph();
+ newGraph.reuseNodes(oldGraph);
+
+ Executor newExecutor = newGraph.getInstance(Executor.class);
+ assertThat(executor, sameInstance(newExecutor));
+ }
+
+ private ComponentGraph createReusingGraph() {
+ ComponentGraph graph = new ComponentGraph();
+ graph.add(mockComponentNodeWithId(ExecutorProvider.class, "dummyId"));
+ graph.complete();
+ graph.setAvailableConfigs(Collections.emptyMap());
+ return graph;
+ }
+
+ @Test
+ public void component_id_can_be_injected() {
+ String componentId = "myId:1.2@namespace";
+
+ ComponentGraph componentGraph = new ComponentGraph();
+ componentGraph.add(mockComponentNodeWithId(ComponentTakingComponentId.class, componentId));
+ componentGraph.complete();
+
+ assertThat(componentGraph.getInstance(ComponentTakingComponentId.class).componentId, is(ComponentId.fromString(componentId)));
+ }
+
+ @Test
+ public void rest_api_context_can_be_instantiated() {
+ String configId = "raw:\"\"";
+
+ Class<RestApiContext> clazz = RestApiContext.class;
+ JerseyNode jerseyNode = new JerseyNode(uniqueComponentId(clazz.getName()), configId, clazz, new Osgi() {
+ });
+
+ ComponentGraph componentGraph = new ComponentGraph();
+ componentGraph.add(jerseyNode);
+ componentGraph.complete();
+
+ componentGraph
+ .setAvailableConfigs(ConfigMap.newMap(JerseyBundlesConfig.class, configId).add(JerseyInjectionConfig.class, configId));
+
+ RestApiContext restApiContext = componentGraph.getInstance(clazz);
+ assertNotNull(restApiContext);
+ assertThat(restApiContext.getBundles().size(), is(0));
+ }
+
+ //Note that all Components must be defined in a static context,
+ //otherwise their constructor will take the outer class as the first parameter.
+ private static int counter = 0;
+
+ public static class SimpleComponent extends AbstractComponent {
+ }
+
+ public static class SimpleComponent2 extends AbstractComponent {
+ }
+
+ public static class SimpleDerivedComponent extends SimpleComponent {
+ }
+
+ public static class ComponentTakingConfig extends SimpleComponent {
+ private final TestConfig config;
+
+ public ComponentTakingConfig(TestConfig config) {
+ assertThat(config, notNullValue());
+ this.config = config;
+ }
+ }
+
+ public static class ComponentTakingComponent extends AbstractComponent {
+ private final SimpleComponent injectedComponent;
+
+ public ComponentTakingComponent(SimpleComponent injectedComponent) {
+ assertThat(injectedComponent, notNullValue());
+ this.injectedComponent = injectedComponent;
+ }
+ }
+
+ @SuppressWarnings("unused")
+ public static class ComponentTakingConfigAndComponent extends AbstractComponent {
+ private final TestConfig config;
+ private final SimpleComponent simpleComponent;
+
+ public ComponentTakingConfigAndComponent(TestConfig config, SimpleComponent injectedComponent) {
+ assertThat(config, notNullValue());
+ assertThat(injectedComponent, notNullValue());
+ this.config = config;
+ this.simpleComponent = injectedComponent;
+ }
+ }
+
+ public static class ComponentTakingAllSimpleComponents extends AbstractComponent {
+ public final ComponentRegistry<SimpleComponent> simpleComponents;
+
+ public ComponentTakingAllSimpleComponents(ComponentRegistry<SimpleComponent> simpleComponents) {
+ assertThat(simpleComponents, notNullValue());
+ this.simpleComponents = simpleComponents;
+ }
+ }
+
+ public static class ComponentTakingAllSimpleComponentsUpperBound extends AbstractComponent {
+ private final ComponentRegistry<? extends SimpleComponent> simpleComponents;
+
+ public ComponentTakingAllSimpleComponentsUpperBound(ComponentRegistry<? extends SimpleComponent> simpleComponents) {
+ assertThat(simpleComponents, notNullValue());
+ this.simpleComponents = simpleComponents;
+ }
+ }
+
+ public static class ComponentTakingAllComponentsWithTypeVariable<COMPONENT extends AbstractComponent> extends AbstractComponent {
+ public ComponentTakingAllComponentsWithTypeVariable(ComponentRegistry<COMPONENT> simpleComponents) {
+ assertThat(simpleComponents, notNullValue());
+ }
+ }
+
+ public static class ComponentTakingNamedComponent extends AbstractComponent {
+ public ComponentTakingNamedComponent(@Named("named-test") SimpleComponent injectedComponent) {
+ assertThat(injectedComponent, notNullValue());
+ }
+ }
+
+ public static class ComponentCausingCycle extends AbstractComponent {
+ public ComponentCausingCycle(ComponentCausingCycle component) {
+ }
+ }
+
+ public static class SimpleComponentProviderThatThrows implements Provider<SimpleComponent> {
+ public SimpleComponent get() {
+ throw new AssertionError("Should never be called.");
+ }
+
+ public void deconstruct() {
+ }
+ }
+
+ public static class ExecutorProvider implements Provider<Executor> {
+ private Executor executor = Executors.newSingleThreadExecutor();
+
+ public Executor get() {
+ return executor;
+ }
+
+ public void deconstruct() {
+ /*TODO */ }
+ }
+
+ public static class DerivedExecutorProvider extends ExecutorProvider {
+ }
+
+ public static class IntProvider implements Provider<Integer> {
+ public Integer get() {
+ throw new AssertionError("Should never be called.");
+ }
+
+ public void deconstruct() {
+ }
+ }
+
+ public static class NewIntProvider implements Provider<Integer> {
+ int i = 0;
+
+ public Integer get() {
+ i++;
+ return i;
+ }
+
+ public void deconstruct() {
+ }
+ }
+
+ public static class ComponentTakingExecutor extends AbstractComponent {
+ private final Executor executor;
+
+ public ComponentTakingExecutor(Executor executor) {
+ assertThat(executor, notNullValue());
+ this.executor = executor;
+ }
+ }
+
+ public static class ComponentWithInjectConstructor {
+ public ComponentWithInjectConstructor(TestConfig c, Test2Config c2) {
+ throw new RuntimeException("Should not be called");
+ }
+
+ @Inject
+ public ComponentWithInjectConstructor(Test2Config c) {
+ }
+ }
+
+ public static class ComponentWithMultipleConstructors {
+ private ComponentWithMultipleConstructors(int dummy) {
+ }
+
+ public ComponentWithMultipleConstructors() {
+ this(0);
+ throw new RuntimeException("Should not be called");
+ }
+
+ public ComponentWithMultipleConstructors(TestConfig c, Test2Config c2) {
+ this(0);
+ }
+
+ public ComponentWithMultipleConstructors(Test2Config c) {
+ this();
+ }
+ }
+
+ public static class ComponentTakingComponentId {
+ private final ComponentId componentId;
+
+ public ComponentTakingComponentId(ComponentId componentId) {
+ this.componentId = componentId;
+ }
+ }
+
+ public static ComponentId uniqueComponentId(String className) {
+ counter += 1;
+ return ComponentId.fromString(className + counter);
+ }
+
+ public static Node mockComponentNode(Key<?> key) {
+ return mockComponentNode(key.getTypeLiteral().getRawType(), "", key.getAnnotation());
+ }
+
+ public static Node mockComponentNode(Class<?> clazz, String configId, Annotation key) {
+ return new ComponentNode(uniqueComponentId(clazz.getName()), configId, clazz, key);
+ }
+
+ public static Node mockComponentNode(Class<?> clazz, String configId) {
+ return new ComponentNode(uniqueComponentId(clazz.getName()), configId, clazz, null);
+ }
+
+ public static Node mockComponentNode(Class<?> clazz, Annotation key) {
+ return new ComponentNode(uniqueComponentId(clazz.getName()), "", clazz, key);
+ }
+
+ public static Node mockComponentNode(Class<?> clazz) {
+ return new ComponentNode(uniqueComponentId(clazz.getName()), "", clazz, null);
+ }
+
+ public static Node mockComponentNodeWithId(Class<?> clazz, String componentId, String configId /*= ""*/, Annotation key /*= null*/) {
+ return new ComponentNode(ComponentId.fromString(componentId), configId, clazz, key);
+ }
+
+ public static Node mockComponentNodeWithId(Class<?> clazz, String componentId, String configId /*= ""*/) {
+ return new ComponentNode(ComponentId.fromString(componentId), configId, clazz, null);
+ }
+
+ public static Node mockComponentNodeWithId(Class<?> clazz, String componentId) {
+ return new ComponentNode(ComponentId.fromString(componentId), "", clazz, null);
+ }
+
+ public static Injector singletonExecutorInjector = Guice.createInjector(new AbstractModule() {
+ @Override
+ public void configure() {
+ bind(Executor.class).toInstance(Executors.newSingleThreadExecutor());
+ }
+ });
+
+ public static abstract class AbstractClass {
+ }
+}
diff --git a/container-di/src/test/java/com/yahoo/container/di/componentgraph/core/JerseyNodeTest.java b/container-di/src/test/java/com/yahoo/container/di/componentgraph/core/JerseyNodeTest.java
new file mode 100644
index 00000000000..f30f9260830
--- /dev/null
+++ b/container-di/src/test/java/com/yahoo/container/di/componentgraph/core/JerseyNodeTest.java
@@ -0,0 +1,68 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.di.componentgraph.core;
+
+import com.yahoo.container.bundle.MockBundle;
+import com.yahoo.container.di.config.RestApiContext;
+import com.yahoo.container.di.osgi.OsgiUtil;
+import org.junit.Test;
+import org.osgi.framework.wiring.BundleWiring;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author gjoranv
+ * @author ollivir
+ */
+
+public class JerseyNodeTest {
+ private MockBundle bundle;
+ private List<String> bundleClasses;
+ private final Map<String, String> resources;
+
+ public JerseyNodeTest() {
+ resources = new HashMap<>();
+ resources.put("com/foo", "com/foo/Foo.class");
+ resources.put("com/bar", "com/bar/Bar.class");
+ bundle = new MockBundle() {
+ @Override
+ public Collection<String> listResources(String path, String ignored, int options) {
+ if ((options & BundleWiring.LISTRESOURCES_RECURSE) != 0 && path.equals("/")) {
+ return resources.values();
+ } else {
+ return Collections.singleton(resources.get(path));
+ }
+ }
+ };
+ bundleClasses = new ArrayList<>(resources.values());
+ }
+
+ @Test
+ public void all_bundle_entries_are_returned_when_no_packages_are_given() {
+ Collection<String> entries = OsgiUtil.getClassEntriesInBundleClassPath(bundle, Collections.emptySet());
+ assertThat(entries, containsInAnyOrder(bundleClasses.toArray()));
+ }
+
+ @Test
+ public void only_bundle_entries_from_the_given_packages_are_returned() {
+ Collection<String> entries = OsgiUtil.getClassEntriesInBundleClassPath(bundle, Collections.singleton("com.foo"));
+ assertThat(entries, contains(resources.get("com/foo")));
+ }
+
+ @Test
+ public void bundle_info_is_initialized() {
+ RestApiContext.BundleInfo bundleInfo = JerseyNode.createBundleInfo(bundle, Collections.emptyList());
+ assertThat(bundleInfo.symbolicName, is(bundle.getSymbolicName()));
+ assertThat(bundleInfo.version, is(bundle.getVersion()));
+ assertThat(bundleInfo.fileLocation, is(bundle.getLocation()));
+ }
+}
diff --git a/container-di/src/test/java/com/yahoo/container/di/componentgraph/core/ReuseComponentsTest.java b/container-di/src/test/java/com/yahoo/container/di/componentgraph/core/ReuseComponentsTest.java
new file mode 100644
index 00000000000..e61e90cd718
--- /dev/null
+++ b/container-di/src/test/java/com/yahoo/container/di/componentgraph/core/ReuseComponentsTest.java
@@ -0,0 +1,254 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.di.componentgraph.core;
+
+import com.yahoo.component.ComponentId;
+import com.yahoo.component.provider.ComponentRegistry;
+import com.yahoo.config.subscription.ConfigGetter;
+import com.yahoo.config.test.TestConfig;
+import com.yahoo.container.di.componentgraph.core.ComponentGraphTest.ComponentTakingAllSimpleComponents;
+import com.yahoo.container.di.componentgraph.core.ComponentGraphTest.ComponentTakingComponent;
+import com.yahoo.container.di.componentgraph.core.ComponentGraphTest.ComponentTakingConfig;
+import com.yahoo.container.di.componentgraph.core.ComponentGraphTest.ComponentTakingConfigAndComponent;
+import com.yahoo.container.di.componentgraph.core.ComponentGraphTest.ComponentTakingExecutor;
+import com.yahoo.container.di.componentgraph.core.ComponentGraphTest.ExecutorProvider;
+import com.yahoo.container.di.componentgraph.core.ComponentGraphTest.SimpleComponent;
+import com.yahoo.container.di.componentgraph.core.ComponentGraphTest.SimpleComponent2;
+import com.yahoo.vespa.config.ConfigKey;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.concurrent.Executor;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.sameInstance;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author gjoranv
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class ReuseComponentsTest {
+ @Test
+ public void require_that_component_is_reused_when_componentNode_is_unmodified() {
+ reuseAndTest(SimpleComponent.class, SimpleComponent.class);
+ reuseAndTest(ExecutorProvider.class, Executor.class);
+ }
+
+ private <T> void reuseAndTest(Class<?> classToRegister, Class<T> classToLookup) {
+ ComponentGraph graph = buildGraphAndSetNoConfigs(classToRegister);
+ T instance = getComponent(graph, classToLookup);
+
+ ComponentGraph newGraph = buildGraphAndSetNoConfigs(classToRegister);
+ newGraph.reuseNodes(graph);
+ T instance2 = getComponent(newGraph, classToLookup);
+
+ assertThat(instance2, sameInstance(instance));
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void require_that_component_is_not_reused_when_class_is_changed() {
+ ComponentGraph graph = buildGraphAndSetNoConfigs(SimpleComponent.class);
+ SimpleComponent instance = getComponent(graph, SimpleComponent.class);
+
+ ComponentGraph newGraph = buildGraphAndSetNoConfigs(SimpleComponent2.class);
+ newGraph.reuseNodes(graph);
+ SimpleComponent2 instance2 = getComponent(newGraph, SimpleComponent2.class);
+
+ assertThat(instance2.getId(), is(instance.getId()));
+ @SuppressWarnings("unused")
+ SimpleComponent throwsException = getComponent(newGraph, SimpleComponent.class);
+ }
+
+ @Test
+ public void require_that_component_is_not_reused_when_config_is_changed() {
+ Class<ComponentTakingConfig> componentClass = ComponentTakingConfig.class;
+
+ ComponentGraph graph = buildGraph(componentClass);
+ graph.setAvailableConfigs(Collections.singletonMap(new ConfigKey<>(TestConfig.class, "component"),
+ ConfigGetter.getConfig(TestConfig.class, "raw: stringVal \"oldConfig\"")));
+ ComponentTakingConfig instance = getComponent(graph, componentClass);
+
+ ComponentGraph newGraph = buildGraph(componentClass);
+ newGraph.setAvailableConfigs(Collections.singletonMap(new ConfigKey<>(TestConfig.class, "component"),
+ ConfigGetter.getConfig(TestConfig.class, "raw: stringVal \"newConfig\"")));
+ newGraph.reuseNodes(graph);
+ ComponentTakingConfig instance2 = getComponent(newGraph, componentClass);
+
+ assertThat(instance2, not(sameInstance(instance)));
+ }
+
+ @Test
+ public void require_that_component_is_not_reused_when_injected_component_is_changed() {
+ Function<String, ComponentGraph> buildGraph = config -> {
+ ComponentGraph graph = new ComponentGraph();
+
+ ComponentNode rootComponent = mockComponentNode(ComponentTakingComponent.class, "root_component");
+
+ String configId = "componentTakingConfigId";
+ ComponentNode injectedComponent = mockComponentNode(ComponentTakingConfig.class, "injected_component", configId);
+
+ rootComponent.inject(injectedComponent);
+
+ graph.add(rootComponent);
+ graph.add(injectedComponent);
+
+ graph.complete();
+ graph.setAvailableConfigs(Collections.singletonMap(new ConfigKey<>(TestConfig.class, configId),
+ ConfigGetter.getConfig(TestConfig.class, "raw: stringVal \"" + config + "\"")));
+
+ return graph;
+ };
+
+ ComponentGraph oldGraph = buildGraph.apply("oldGraph");
+ ComponentTakingComponent oldInstance = getComponent(oldGraph, ComponentTakingComponent.class);
+
+ ComponentGraph newGraph = buildGraph.apply("newGraph");
+ newGraph.reuseNodes(oldGraph);
+ ComponentTakingComponent newInstance = getComponent(newGraph, ComponentTakingComponent.class);
+
+ assertThat(newInstance, not(sameInstance(oldInstance)));
+ }
+
+ @Test
+ public void require_that_component_is_not_reused_when_injected_component_registry_has_one_component_removed() {
+ Function<Boolean, ComponentGraph> buildGraph = useBothInjectedComponents -> {
+ ComponentGraph graph = new ComponentGraph();
+ graph.add(mockComponentNode(ComponentTakingAllSimpleComponents.class, "root_component"));
+
+ /* Below if-else has code duplication, but explicit ordering of the two components
+ * was necessary to reproduce erroneous behaviour in ComponentGraph.reuseNodes that
+ * occurred before ComponentRegistryNode got its own 'equals' implementation.
+ */
+ if (useBothInjectedComponents) {
+ graph.add(mockComponentNode(SimpleComponent.class, "injected_component2"));
+ graph.add(mockComponentNode(SimpleComponent.class, "injected_component1"));
+ } else {
+ graph.add(mockComponentNode(SimpleComponent.class, "injected_component1"));
+ }
+
+ graph.complete();
+ graph.setAvailableConfigs(Collections.emptyMap());
+ return graph;
+ };
+
+ ComponentGraph oldGraph = buildGraph.apply(true);
+ ComponentRegistry<SimpleComponent> oldSimpleComponentRegistry = getComponent(oldGraph, ComponentTakingAllSimpleComponents.class).simpleComponents;
+
+ ComponentGraph newGraph = buildGraph.apply(false);
+ newGraph.reuseNodes(oldGraph);
+ ComponentRegistry<SimpleComponent> newSimpleComponentRegistry = getComponent(newGraph, ComponentTakingAllSimpleComponents.class).simpleComponents;
+
+ assertThat(newSimpleComponentRegistry, not(sameInstance(oldSimpleComponentRegistry)));
+ }
+
+ @Test
+ public void require_that_injected_component_is_reused_even_when_dependent_component_is_changed() {
+ Function<String, ComponentGraph> buildGraph = config -> {
+ ComponentGraph graph = new ComponentGraph();
+
+ String configId = "componentTakingConfigAndComponent";
+ ComponentNode rootComponent = mockComponentNode(ComponentTakingConfigAndComponent.class, "root_component", configId);
+
+ ComponentNode injectedComponent = mockComponentNode(SimpleComponent.class, "injected_component");
+
+ rootComponent.inject(injectedComponent);
+
+ graph.add(rootComponent);
+ graph.add(injectedComponent);
+
+ graph.complete();
+ graph.setAvailableConfigs(Collections.singletonMap(new ConfigKey<>(TestConfig.class, configId),
+ ConfigGetter.getConfig(TestConfig.class, "raw: stringVal \"" + config + "\"")));
+
+ return graph;
+ };
+
+ ComponentGraph oldGraph = buildGraph.apply("oldGraph");
+ SimpleComponent oldInjectedComponent = getComponent(oldGraph, SimpleComponent.class);
+ ComponentTakingConfigAndComponent oldDependentComponent = getComponent(oldGraph, ComponentTakingConfigAndComponent.class);
+
+ ComponentGraph newGraph = buildGraph.apply("newGraph");
+ newGraph.reuseNodes(oldGraph);
+ SimpleComponent newInjectedComponent = getComponent(newGraph, SimpleComponent.class);
+ ComponentTakingConfigAndComponent newDependentComponent = getComponent(newGraph, ComponentTakingConfigAndComponent.class);
+
+ assertThat(newDependentComponent, not(sameInstance(oldDependentComponent)));
+ assertThat(newInjectedComponent, sameInstance(oldInjectedComponent));
+ }
+
+ @Test
+ public void require_that_node_depending_on_guice_node_is_reused() {
+ Supplier<ComponentGraph> makeGraph = () -> {
+ ComponentGraph graph = new ComponentGraph();
+ graph.add(mockComponentNode(ComponentTakingExecutor.class, "dummyId"));
+ graph.complete(ComponentGraphTest.singletonExecutorInjector);
+ graph.setAvailableConfigs(Collections.emptyMap());
+ return graph;
+ };
+
+ Function<ComponentGraph, ComponentTakingExecutor> componentRetriever = graph -> getComponent(graph, ComponentTakingExecutor.class);
+
+ ComponentGraph oldGraph = makeGraph.get();
+ componentRetriever.apply(oldGraph); // Ensure creation of GuiceNode
+ ComponentGraph newGraph = makeGraph.get();
+ newGraph.reuseNodes(oldGraph);
+ assertThat(componentRetriever.apply(oldGraph), sameInstance(componentRetriever.apply(newGraph)));
+ }
+
+ @Test
+ public void require_that_node_equals_only_checks_first_level_components_to_inject() {
+ Function<String, Node> createNodeWithInjectedNodeWithInjectedNode = indirectlyInjectedComponentId -> {
+ ComponentNode targetComponent = mockComponentNode(SimpleComponent.class, "target");
+ ComponentNode directlyInjectedComponent = mockComponentNode(SimpleComponent.class, "directlyInjected");
+ ComponentNode indirectlyInjectedComponent = mockComponentNode(SimpleComponent.class, indirectlyInjectedComponentId);
+ directlyInjectedComponent.inject(indirectlyInjectedComponent);
+ targetComponent.inject(directlyInjectedComponent);
+
+ completeNode(targetComponent);
+ completeNode(directlyInjectedComponent);
+ completeNode(indirectlyInjectedComponent);
+
+ return targetComponent;
+ };
+
+ Node targetNode1 = createNodeWithInjectedNodeWithInjectedNode.apply("indirectlyInjected_1");
+ Node targetNode2 = createNodeWithInjectedNodeWithInjectedNode.apply("indirectlyInjected_2");
+ assertThat(targetNode1, equalTo(targetNode2));
+ }
+
+ private void completeNode(ComponentNode node) {
+ node.setArguments(new Object[0]);
+ node.setAvailableConfigs(Collections.emptyMap());
+ }
+
+ private ComponentGraph buildGraph(Class<?> componentClass) {
+ String commonComponentId = "component";
+ ComponentGraph g = new ComponentGraph();
+ g.add(mockComponentNode(componentClass, commonComponentId, commonComponentId));
+ g.complete();
+ return g;
+ }
+
+ private ComponentGraph buildGraphAndSetNoConfigs(Class<?> componentClass) {
+ ComponentGraph g = buildGraph(componentClass);
+ g.setAvailableConfigs(Collections.emptyMap());
+ return g;
+ }
+
+ private static ComponentNode mockComponentNode(Class<?> clazz, String componentId, String configId) {
+ return new ComponentNode(new ComponentId(componentId), configId, clazz);
+ }
+
+ private static ComponentNode mockComponentNode(Class<?> clazz, String componentId) {
+ return mockComponentNode(clazz, componentId, "");
+ }
+
+ private static <T> T getComponent(ComponentGraph graph, Class<T> clazz) {
+ return graph.getInstance(clazz);
+ }
+}
diff --git a/container-di/src/test/java/demo/Base.java b/container-di/src/test/java/demo/Base.java
index b702bdcaddd..95ff2e14d53 100644
--- a/container-di/src/test/java/demo/Base.java
+++ b/container-di/src/test/java/demo/Base.java
@@ -5,7 +5,6 @@ import com.google.inject.Guice;
import com.google.inject.Injector;
import com.yahoo.component.ComponentId;
import com.yahoo.config.ConfigInstance;
-import com.yahoo.container.di.ContainerTest;
import com.yahoo.container.di.componentgraph.core.ComponentGraph;
import com.yahoo.container.di.componentgraph.core.ComponentNode;
import com.yahoo.container.di.componentgraph.core.Node;
@@ -16,7 +15,7 @@ import java.util.HashMap;
import java.util.Map;
/**
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
*/
public class Base {
@@ -48,10 +47,9 @@ public class Base {
return componentGraph.getInstance(componentClass);
}
- @SuppressWarnings("unchecked")
public void complete() {
componentGraph.complete(injector);
- componentGraph.setAvailableConfigs(ContainerTest.convertMap(configs));
+ componentGraph.setAvailableConfigs(configs);
}
public void setInjector(Injector injector) {
diff --git a/container-di/src/test/java/demo/ComponentConfigTest.java b/container-di/src/test/java/demo/ComponentConfigTest.java
index 4cd8856c2ec..02e98bbc325 100644
--- a/container-di/src/test/java/demo/ComponentConfigTest.java
+++ b/container-di/src/test/java/demo/ComponentConfigTest.java
@@ -13,7 +13,7 @@ import static org.junit.Assert.assertNotNull;
/**
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
*/
public class ComponentConfigTest extends Base {
diff --git a/container-di/src/test/java/demo/ComponentRegistryTest.java b/container-di/src/test/java/demo/ComponentRegistryTest.java
index fac6f89a9b9..26ef0a476d7 100644
--- a/container-di/src/test/java/demo/ComponentRegistryTest.java
+++ b/container-di/src/test/java/demo/ComponentRegistryTest.java
@@ -9,7 +9,7 @@ import static org.junit.Assert.assertNotNull;
/**
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
*/
public class ComponentRegistryTest extends Base {
diff --git a/container-di/src/test/java/demo/ContainerTestBase.java b/container-di/src/test/java/demo/ContainerTestBase.java
deleted file mode 100644
index 9c2415c3514..00000000000
--- a/container-di/src/test/java/demo/ContainerTestBase.java
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package demo;
-
-import com.google.inject.Guice;
-import com.yahoo.component.ComponentSpecification;
-import com.yahoo.container.bundle.BundleInstantiationSpecification;
-import com.yahoo.config.FileReference;
-import com.yahoo.container.di.CloudSubscriberFactory;
-import com.yahoo.container.di.Container;
-import com.yahoo.container.di.ContainerTest;
-import com.yahoo.container.di.Osgi;
-import com.yahoo.container.di.componentgraph.core.ComponentGraph;
-import org.junit.Before;
-import org.osgi.framework.Bundle;
-import scala.collection.immutable.Set;
-
-import java.util.Collection;
-
-/**
- * @author tonytv
- * @author gjoranv
- */
-public class ContainerTestBase extends ContainerTest {
- private ComponentGraph componentGraph;
-
- @Before
- public void createGraph() {
- componentGraph = new ComponentGraph(0);
- }
-
- public void complete() {
- try {
- Container container = new Container(
- new CloudSubscriberFactory(dirConfigSource().configSource()),
- dirConfigSource().configId(),
- new ContainerTest.TestDeconstructor(),
- new Osgi() {
- @SuppressWarnings("unchecked")
- @Override
- public Class<Object> resolveClass(BundleInstantiationSpecification spec) {
- try {
- return (Class<Object>) Class.forName(spec.classId.getName());
- } catch (ClassNotFoundException e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public BundleClasses getBundleClasses(ComponentSpecification bundle,
- Set<String> packagesToScan) {
- throw new UnsupportedOperationException("getBundleClasses not supported");
- }
-
- @Override
- public void useBundles(Collection<FileReference> bundles) {}
-
- @Override
- public Bundle getBundle(ComponentSpecification spec) {
- throw new UnsupportedOperationException("getBundle not supported.");
- }
- });
- componentGraph = container.getNewComponentGraph(componentGraph, Guice.createInjector(), false);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- public <T> T getInstance(Class<T> componentClass) {
- return componentGraph.getInstance(componentClass);
- }
-}
diff --git a/container-di/src/test/java/demo/DeconstructTest.java b/container-di/src/test/java/demo/DeconstructTest.java
index 1ec2fe17054..e3dc5e22416 100644
--- a/container-di/src/test/java/demo/DeconstructTest.java
+++ b/container-di/src/test/java/demo/DeconstructTest.java
@@ -2,12 +2,13 @@
package demo;
import com.yahoo.container.di.ContainerTest;
+import com.yahoo.container.di.ContainerTestBase;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
*/
public class DeconstructTest extends ContainerTestBase {
diff --git a/container-di/src/test/java/demo/FallbackToGuiceInjectorTest.java b/container-di/src/test/java/demo/FallbackToGuiceInjectorTest.java
index e1dc415de6d..4b7d9d54725 100644
--- a/container-di/src/test/java/demo/FallbackToGuiceInjectorTest.java
+++ b/container-di/src/test/java/demo/FallbackToGuiceInjectorTest.java
@@ -20,7 +20,7 @@ import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertNotNull;
/**
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
*/
@SuppressWarnings("unused")
diff --git a/container-di/src/test/scala/com/yahoo/container/di/ConfigRetrieverTest.scala b/container-di/src/test/scala/com/yahoo/container/di/ConfigRetrieverTest.scala
deleted file mode 100644
index 7f1d9a73a82..00000000000
--- a/container-di/src/test/scala/com/yahoo/container/di/ConfigRetrieverTest.scala
+++ /dev/null
@@ -1,107 +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.container.di
-
-import com.yahoo.config.test.{Bootstrap1Config, Bootstrap2Config, TestConfig}
-import com.yahoo.container.di.ConfigRetriever.{BootstrapConfigs, ComponentsConfigs}
-import com.yahoo.vespa.config.ConfigKey
-import org.hamcrest.CoreMatchers.{is, instanceOf => hamcrestInstanceOf}
-import org.hamcrest.Matcher
-import org.junit.Assert._
-import org.junit.{After, Before, Ignore, Test}
-
-import scala.reflect.ClassTag
-import scala.collection.JavaConverters._
-
-/**
- *
- * @author gjoranv
- * @author tonytv
- */
-class ConfigRetrieverTest {
-
- var dirConfigSource: DirConfigSource = null
-
- @Before def setup() {
- dirConfigSource = new DirConfigSource("ConfigRetrieverTest-")
- }
-
- @After def cleanup() { dirConfigSource.cleanup() }
-
- @Test
- def require_that_bootstrap_configs_come_first() {
- writeConfigs()
- val retriever = createConfigRetriever()
- val bootstrapConfigs = retriever.getConfigs(Set(), 0)
-
- assertThat(bootstrapConfigs, instanceOf[BootstrapConfigs])
- }
-
- @Test
- def require_that_components_comes_after_bootstrap() {
- writeConfigs()
- val retriever = createConfigRetriever()
- val bootstrapConfigs = retriever.getConfigs(Set(), 0)
-
- val testConfigKey = new ConfigKey(classOf[TestConfig], dirConfigSource.configId)
- val componentsConfigs = retriever.getConfigs(Set(testConfigKey), 0)
-
- componentsConfigs match {
- case ComponentsConfigs(configs) => assertThat(configs.size, is(3))
- case _ => fail("ComponentsConfigs has unexpected type: " + componentsConfigs)
- }
- }
-
- @Test
- def require_no_reconfig_when_restart_on_redeploy() {
- // TODO
- writeConfigs()
- val retriever = createConfigRetriever()
- val bootstrapConfigs = retriever.getConfigs(Set(), 0)
-
- val testConfigKey = new ConfigKey(classOf[TestConfig], dirConfigSource.configId)
- val componentsConfigs = retriever.getConfigsOnce(Set(testConfigKey), 0, true)
-
- componentsConfigs match {
- case Some(snapshot) => fail("Expected no configs")
- case _ => // ok
- }
- }
-
- @Test(expected = classOf[IllegalArgumentException])
- @Ignore
- def require_exception_upon_modified_components_keys_without_bootstrap() {
- writeConfigs()
- val retriever = createConfigRetriever()
- val testConfigKey = new ConfigKey(classOf[TestConfig], dirConfigSource.configId)
- val bootstrapConfigs = retriever.getConfigs(Set(), 0)
- val componentsConfigs = retriever.getConfigs(Set(testConfigKey), 0)
- retriever.getConfigs(Set(testConfigKey, new ConfigKey(classOf[TestConfig],"")), 0)
- }
-
- @Test
- def require_that_empty_components_keys_after_bootstrap_returns_components_configs() {
- writeConfigs()
- val retriever = createConfigRetriever()
- assertThat(retriever.getConfigs(Set(), 0), instanceOf[BootstrapConfigs])
- assertThat(retriever.getConfigs(Set(), 0), instanceOf[ComponentsConfigs])
- }
-
- def writeConfigs() {
- writeConfig("bootstrap1", """dummy "ignored" """")
- writeConfig("bootstrap2", """dummy "ignored" """")
- writeConfig("test", """stringVal "ignored" """")
- }
-
- def createConfigRetriever() = {
- val configId = dirConfigSource.configId
- val subscriber = new CloudSubscriberFactory(dirConfigSource.configSource)
- new ConfigRetriever(
- Set(new ConfigKey(classOf[Bootstrap1Config], configId),
- new ConfigKey(classOf[Bootstrap2Config], configId)),
- (keys) => subscriber.getSubscriber(keys.asJava))
- }
-
- def writeConfig = dirConfigSource.writeConfig _
-
- def instanceOf[T: ClassTag] = hamcrestInstanceOf(implicitly[ClassTag[T]].runtimeClass): Matcher[AnyRef]
-}
diff --git a/container-di/src/test/scala/com/yahoo/container/di/ContainerTest.scala b/container-di/src/test/scala/com/yahoo/container/di/ContainerTest.scala
deleted file mode 100644
index 9f07acc7dc9..00000000000
--- a/container-di/src/test/scala/com/yahoo/container/di/ContainerTest.scala
+++ /dev/null
@@ -1,398 +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.container.di
-
-import com.yahoo.component.AbstractComponent
-import com.yahoo.config.di.IntConfig
-import com.yahoo.config.test.TestConfig
-import com.yahoo.container.bundle.MockBundle
-import com.yahoo.container.di.ContainerTest._
-import com.yahoo.container.di.componentgraph.Provider
-import com.yahoo.container.di.componentgraph.core.ComponentGraphTest.{SimpleComponent, SimpleComponent2}
-import com.yahoo.container.di.componentgraph.core.ComponentNode.ComponentConstructorException
-import com.yahoo.container.di.componentgraph.core.{ComponentGraph, Node}
-import com.yahoo.container.di.config.RestApiContext
-import org.hamcrest.CoreMatchers._
-import org.junit.Assert._
-import org.junit.{After, Before, Ignore, Test}
-
-import scala.collection.JavaConverters._
-import scala.concurrent.ExecutionContext.Implicits.global
-import scala.concurrent.duration._
-import scala.concurrent.{Await, Future}
-import scala.language.{existentials, postfixOps}
-import scala.util.Try
-
-/**
- * @author tonytv
- * @author gjoranv
- */
-class ContainerTest {
- var dirConfigSource: DirConfigSource = _
-
- @Before def setup() {
- dirConfigSource = new DirConfigSource("ContainerTest-")
- }
-
- @After def cleanup() {
- dirConfigSource.cleanup()
- }
-
- @Test
- def components_can_be_created_from_config() {
- writeBootstrapConfigs()
- dirConfigSource.writeConfig("test", """stringVal "myString" """)
-
- val container = newContainer(dirConfigSource)
-
- val component = createComponentTakingConfig(container.getNewComponentGraph())
- assertThat(component.config.stringVal(), is("myString"))
-
- container.shutdownConfigurer()
- }
-
- @Test
- def components_are_reconfigured_after_config_update_without_bootstrap_configs() {
- writeBootstrapConfigs()
- dirConfigSource.writeConfig("test", """stringVal "original" """)
-
- val container = newContainer(dirConfigSource)
-
- val componentGraph = container.getNewComponentGraph()
- val component = createComponentTakingConfig(componentGraph)
-
- assertThat(component.config.stringVal(), is("original"))
-
- // Reconfigure
- dirConfigSource.writeConfig("test", """stringVal "reconfigured" """)
- container.reloadConfig(2)
-
- val newComponentGraph = container.getNewComponentGraph(componentGraph)
- val component2 = createComponentTakingConfig(newComponentGraph)
- assertThat(component2.config.stringVal(), is("reconfigured"))
-
- container.shutdownConfigurer()
- }
-
- @Test
- def graph_is_updated_after_bootstrap_update() {
- dirConfigSource.writeConfig("test", """stringVal "original" """)
- writeBootstrapConfigs("id1")
-
- val container = newContainer(dirConfigSource)
-
- val graph = container.getNewComponentGraph()
- val component = createComponentTakingConfig(graph)
- assertThat(component.getId.toString, is("id1"))
-
- writeBootstrapConfigsWithMultipleComponents(Array(
- ("id1", classOf[ComponentTakingConfig]),
- ("id2", classOf[ComponentTakingConfig])))
-
- container.reloadConfig(2)
- val newGraph = container.getNewComponentGraph(graph)
-
- assertThat(ComponentGraph.getNode(newGraph, "id1"), notNullValue(classOf[Node]))
- assertThat(ComponentGraph.getNode(newGraph, "id2"), notNullValue(classOf[Node]))
-
- container.shutdownConfigurer()
- }
-
- //@Test TODO
- def deconstructor_is_given_guice_components() {
- }
-
- @Test
- def osgi_component_is_deconstructed_when_not_reused() {
- writeBootstrapConfigs("id1", classOf[DestructableComponent])
-
- val container = newContainer(dirConfigSource)
-
- val oldGraph = container.getNewComponentGraph()
- val componentToDestruct = oldGraph.getInstance(classOf[DestructableComponent])
-
- writeBootstrapConfigs("id2", classOf[DestructableComponent])
- container.reloadConfig(2)
- container.getNewComponentGraph(oldGraph)
- assertTrue(componentToDestruct.deconstructed)
- }
-
- @Ignore // because logAndDie is impossible(?) to verify programmatically
- @Test
- def manually_verify_what_happens_when_first_graph_contains_component_that_throws_exception_in_ctor() {
- writeBootstrapConfigs("thrower", classOf[ComponentThrowingExceptionInConstructor])
- val container = newContainer(dirConfigSource)
- var currentGraph: ComponentGraph = null
- try {
- currentGraph = container.getNewComponentGraph()
- fail("Expected to log and die.")
- } catch {
- case _: Throwable => fail("Expected to log and die")
- }
- }
-
- @Test
- def previous_graph_is_retained_when_new_graph_contains_component_that_throws_exception_in_ctor() {
- val simpleComponentEntry = ComponentEntry("simpleComponent", classOf[SimpleComponent])
-
- writeBootstrapConfigs(Array(simpleComponentEntry))
- val container = newContainer(dirConfigSource)
- var currentGraph = container.getNewComponentGraph()
-
- val simpleComponent = currentGraph.getInstance(classOf[SimpleComponent])
-
- writeBootstrapConfigs("thrower", classOf[ComponentThrowingExceptionInConstructor])
- container.reloadConfig(2)
- try {
- currentGraph = container.getNewComponentGraph(currentGraph)
- fail("Expected exception")
- } catch {
- case _: ComponentConstructorException => // Expected, do nothing
- case _: Throwable => fail("Expected ComponentConstructorException")
- }
- assertEquals(1, currentGraph.generation)
-
- // Also verify that next reconfig is successful
- val componentTakingConfigEntry = ComponentEntry("componentTakingConfig", classOf[ComponentTakingConfig])
- dirConfigSource.writeConfig("test", """stringVal "myString" """)
- writeBootstrapConfigs(Array(simpleComponentEntry, componentTakingConfigEntry))
- container.reloadConfig(3)
- currentGraph = container.getNewComponentGraph(currentGraph)
-
- assertEquals(3, currentGraph.generation)
- assertSame(simpleComponent, currentGraph.getInstance(classOf[SimpleComponent]))
- assertNotNull(currentGraph.getInstance(classOf[ComponentTakingConfig]))
- }
-
- @Test
- def previous_graph_is_retained_when_new_graph_throws_exception_for_missing_config() {
- val simpleComponentEntry = ComponentEntry("simpleComponent", classOf[SimpleComponent])
-
- writeBootstrapConfigs(Array(simpleComponentEntry))
- val container = newContainer(dirConfigSource)
- var currentGraph = container.getNewComponentGraph()
-
- val simpleComponent = currentGraph.getInstance(classOf[SimpleComponent])
-
- writeBootstrapConfigs("thrower", classOf[ComponentThrowingExceptionForMissingConfig])
- dirConfigSource.writeConfig("test", """stringVal "myString" """)
- container.reloadConfig(2)
- try {
- currentGraph = container.getNewComponentGraph(currentGraph)
- fail("Expected exception")
- } catch {
- case _: IllegalArgumentException => // Expected, do nothing
- case _: Throwable => fail("Expected IllegalArgumentException")
- }
- assertEquals(1, currentGraph.generation)
- }
-
- @Test
- def runOnce_hangs_waiting_for_valid_config_after_invalid_config() {
- dirConfigSource.writeConfig("test", """stringVal "original" """)
- writeBootstrapConfigs("myId", classOf[ComponentTakingConfig])
-
- val container = newContainer(dirConfigSource)
- var currentGraph = container.getNewComponentGraph()
-
- writeBootstrapConfigs("thrower", classOf[ComponentThrowingExceptionForMissingConfig])
- container.reloadConfig(2)
-
- try {
- currentGraph = container.getNewComponentGraph(currentGraph)
- fail("expected exception")
- } catch {
- case e: Exception =>
- }
-
- val newGraph = Future {
- currentGraph = container.getNewComponentGraph(currentGraph)
- currentGraph
- }
-
- Try {
- Await.ready(newGraph, 1 second)
- } foreach { x => fail("Expected waiting for new config.") }
-
-
- writeBootstrapConfigs("myId2", classOf[ComponentTakingConfig])
- container.reloadConfig(3)
-
- assertNotNull(Await.result(newGraph, 5 minutes))
- }
-
-
- @Test
- def bundle_info_is_set_on_rest_api_context() {
- val clazz = classOf[RestApiContext]
-
- writeBootstrapConfigs("restApiContext", clazz)
- dirConfigSource.writeConfig("jersey-bundles", """bundles[0].spec "mock-entry-to-enforce-a-MockBundle" """)
- dirConfigSource.writeConfig("jersey-injection", """inject[0]" """)
-
- val container = newContainer(dirConfigSource)
- val componentGraph = container.getNewComponentGraph()
-
- val restApiContext = componentGraph.getInstance(clazz)
- assertNotNull(restApiContext)
-
- assertThat(restApiContext.getBundles.size, is(1))
- assertThat(restApiContext.getBundles.get(0).symbolicName, is(MockBundle.SymbolicName))
- assertThat(restApiContext.getBundles.get(0).version, is(MockBundle.BundleVersion))
-
- container.shutdownConfigurer()
- }
-
- @Test
- def restApiContext_has_all_components_injected() {
- new JerseyInjectionTest {
- assertFalse(restApiContext.getInjectableComponents.isEmpty)
- assertThat(restApiContext.getInjectableComponents.size(), is(2))
-
- container.shutdownConfigurer()
- }
- }
-
- // TODO: reuse injectedComponent as a named component when we support that
- trait JerseyInjectionTest {
- val restApiClass = classOf[RestApiContext]
- val injectedClass = classOf[SimpleComponent]
- val injectedComponentId = "injectedComponent"
- val anotherComponentClass = classOf[SimpleComponent2]
- val anotherComponentId = "anotherComponent"
-
- val componentsConfig: String =
- ComponentEntry(injectedComponentId, injectedClass).asConfig(0) + "\n" +
- ComponentEntry(anotherComponentId, anotherComponentClass).asConfig(1) + "\n" +
- ComponentEntry("restApiContext", restApiClass).asConfig(2) + "\n" +
- s"components[2].inject[0].id $injectedComponentId\n" +
- s"components[2].inject[1].id $anotherComponentId\n"
-
- val injectionConfig = s"""inject[1]
- |inject[0].instance $injectedComponentId
- |inject[0].forClass "${injectedClass.getName}"
- """.stripMargin
-
- dirConfigSource.writeConfig("components", componentsConfig)
- dirConfigSource.writeConfig("bundles", "")
- dirConfigSource.writeConfig("jersey-bundles", """bundles[0].spec "mock-entry-to-enforce-a-MockBundle" """)
- dirConfigSource.writeConfig("jersey-injection", injectionConfig)
-
- val container = newContainer(dirConfigSource)
- val componentGraph = container.getNewComponentGraph()
-
- val restApiContext = componentGraph.getInstance(restApiClass)
- }
-
- case class ComponentEntry(componentId: String, classId: Class[_]) {
- def asConfig(position: Int): String = {
- <config>
- |components[{position}].id "{componentId}"
- |components[{position}].classId "{classId.getName}"
- |components[{position}].configId "{dirConfigSource.configId}"
- </config>.text.stripMargin.trim
- }
- }
-
- def writeBootstrapConfigs(componentEntries: Array[ComponentEntry]) {
- dirConfigSource.writeConfig("bundles", "")
- dirConfigSource.writeConfig("components", """
- components[%s]
- %s
- """.format(componentEntries.length,
- componentEntries.zipWithIndex.map{ case (entry, index) => entry.asConfig(index) }.mkString("\n")))
- }
-
- def writeBootstrapConfigs(componentId: String = classOf[ComponentTakingConfig].getName,
- classId: Class[_] = classOf[ComponentTakingConfig]) {
-
- writeBootstrapConfigs(Array(ComponentEntry(componentId, classId)))
- }
-
- def writeBootstrapConfigsWithMultipleComponents(idAndClass: Array[(String, Class[_])]) {
- writeBootstrapConfigs(idAndClass.map{case(id, classId) => ComponentEntry(id, classId)})
- }
-
-
- @Test
- def providers_are_destructed() {
- writeBootstrapConfigs("id1", classOf[DestructableProvider])
-
- val deconstructor = new ComponentDeconstructor {
- def deconstruct(component: AnyRef) {
- component match {
- case c : AbstractComponent => c.deconstruct()
- case p : Provider[_] => p.deconstruct()
- }
- }
- }
-
- val container = newContainer(dirConfigSource, deconstructor)
-
- val oldGraph = container.getNewComponentGraph()
- val destructableEntity = oldGraph.getInstance(classOf[DestructableEntity])
-
- writeBootstrapConfigs("id2", classOf[DestructableProvider])
- container.reloadConfig(2)
- container.getNewComponentGraph(oldGraph)
-
- assertTrue(destructableEntity.deconstructed)
- }
-}
-
-
-object ContainerTest {
- class DestructableEntity {
- var deconstructed = false
- }
-
- class DestructableProvider extends Provider[DestructableEntity] {
- val instance = new DestructableEntity
-
- def get() = instance
-
- def deconstruct() {
- require(! instance.deconstructed)
- instance.deconstructed = true
- }
- }
-
- class ComponentTakingConfig(val config: TestConfig) extends AbstractComponent {
- require(config != null)
- }
-
- class ComponentThrowingExceptionInConstructor() {
- throw new RuntimeException("This component fails upon construction.")
- }
-
- class ComponentThrowingExceptionForMissingConfig(intConfig: IntConfig) extends AbstractComponent {
- fail("This component should never be created. Only used for tests where 'int' config is missing.")
- }
-
- class DestructableComponent extends AbstractComponent {
- var deconstructed = false
- override def deconstruct() {
- deconstructed = true
- }
- }
-
- class TestDeconstructor extends ComponentDeconstructor {
- def deconstruct(component: AnyRef) {
- component match {
- case vespaComponent: DestructableComponent => vespaComponent.deconstruct()
- case _ =>
- }
- }
- }
-
- private def newContainer(dirConfigSource: DirConfigSource,
- deconstructor: ComponentDeconstructor = new TestDeconstructor()):
- Container = {
- new Container(new CloudSubscriberFactory(dirConfigSource.configSource), dirConfigSource.configId, deconstructor)
- }
-
- def createComponentTakingConfig(componentGraph: ComponentGraph): ComponentTakingConfig = {
- componentGraph.getInstance(classOf[ComponentTakingConfig])
- }
-
- def convertMap[K, V](map: java.util.Map[K, V]): Map[K, V] = map.asScala.toMap
-}
diff --git a/container-di/src/test/scala/com/yahoo/container/di/DirConfigSource.scala b/container-di/src/test/scala/com/yahoo/container/di/DirConfigSource.scala
deleted file mode 100644
index 4f80b25a247..00000000000
--- a/container-di/src/test/scala/com/yahoo/container/di/DirConfigSource.scala
+++ /dev/null
@@ -1,50 +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.container.di
-
-import java.io.{FileOutputStream, OutputStream, File}
-import DirConfigSource._
-import java.util.Random
-import org.junit.rules.TemporaryFolder
-import com.yahoo.config.subscription.{ConfigSource, ConfigSourceSet}
-
-/**
- * @author tonytv
- * @author gjoranv
- */
-class DirConfigSource(val testSourcePrefix: String) {
-
- private val tempFolder = createTemporaryFolder()
-
- val configSource : ConfigSource = new ConfigSourceSet(testSourcePrefix + new Random().nextLong)
-
- def writeConfig(name: String, contents: String) {
- val file = new File(tempFolder.getRoot, name + ".cfg")
- if (!file.exists())
- file.createNewFile()
-
- printFile(file, contents + "\n")
- }
-
- def configId = "dir:" + tempFolder.getRoot.getPath
-
- def cleanup() {
- tempFolder.delete()
- }
-
-}
-
-private object DirConfigSource {
-
- def printFile(f: File, content: String) {
- var out: OutputStream = new FileOutputStream(f)
- out.write(content.getBytes("UTF-8"))
- out.close()
- }
-
- def createTemporaryFolder() = {
- val folder = new TemporaryFolder
- folder.create()
- folder
- }
-
-}
diff --git a/container-di/src/test/scala/com/yahoo/container/di/componentgraph/core/ComponentGraphTest.scala b/container-di/src/test/scala/com/yahoo/container/di/componentgraph/core/ComponentGraphTest.scala
deleted file mode 100644
index 05194cb911b..00000000000
--- a/container-di/src/test/scala/com/yahoo/container/di/componentgraph/core/ComponentGraphTest.scala
+++ /dev/null
@@ -1,540 +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.container.di.componentgraph.core
-
-import java.util.concurrent.{Executor, Executors}
-
-import com.google.inject.name.{Named, Names}
-import com.google.inject.{AbstractModule, Guice, Inject, Key, Provider => GuiceProvider}
-import com.yahoo.component.provider.ComponentRegistry
-import com.yahoo.component.{AbstractComponent, ComponentId}
-import com.yahoo.config.ConfigInstance
-import com.yahoo.config.subscription.ConfigGetter
-import com.yahoo.config.test.{Test2Config, TestConfig}
-import com.yahoo.container.di._
-import com.yahoo.container.di.componentgraph.Provider
-import com.yahoo.container.di.config.{JerseyBundlesConfig, JerseyInjectionConfig, RestApiContext}
-import com.yahoo.vespa.config.ConfigKey
-import org.hamcrest.CoreMatchers.{containsString, equalTo, is, not, sameInstance}
-import org.hamcrest.Matcher
-import org.junit.Assert._
-import org.junit.Test
-
-import scala.language.implicitConversions
-
-/**
- * @author gjoranv
- * @author tonytv
- */
-class ComponentGraphTest {
- import ComponentGraphTest._
-
- private def keyAndConfig[T <: ConfigInstance](clazz: Class[T], configId: String): (ConfigKey[T], T) = {
- val key = new ConfigKey(clazz, configId)
- key -> ConfigGetter.getConfig(key.getConfigClass, key.getConfigId.toString)
- }
-
- @Test
- def component_taking_config_can_be_instantiated() {
- val componentGraph = new ComponentGraph
- val configId = "raw:stringVal \"test-value\""
- val componentNode = mockComponentNode(classOf[ComponentTakingConfig], configId)
-
- componentGraph.add(componentNode)
- componentGraph.complete()
- componentGraph.setAvailableConfigs(Map(keyAndConfig(classOf[TestConfig], configId)))
-
- val instance = componentGraph.getInstance(classOf[ComponentTakingConfig])
- assertNotNull(instance)
- assertThat(instance.config.stringVal(), is("test-value"))
- }
-
- @Test
- def component_can_be_injected_into_another_component() {
- val injectedComponent = mockComponentNode(classOf[SimpleComponent])
- val targetComponent = mockComponentNode(classOf[ComponentTakingComponent])
- targetComponent.inject(injectedComponent)
-
- val destroyGlobalLookupComponent = mockComponentNode(classOf[SimpleComponent])
-
- val componentGraph = new ComponentGraph
- componentGraph.add(injectedComponent)
- componentGraph.add(targetComponent)
- componentGraph.add(destroyGlobalLookupComponent)
- componentGraph.complete()
-
-
- val instance = componentGraph.getInstance(classOf[ComponentTakingComponent])
- assertNotNull(instance)
- }
-
- @Test
- def all_components_of_a_type_can_be_injected() {
- val componentGraph = new ComponentGraph
- componentGraph.add(mockComponentNode(classOf[SimpleComponent]))
- componentGraph.add(mockComponentNode(classOf[SimpleComponent]))
- componentGraph.add(mockComponentNode(classOf[SimpleDerivedComponent]))
- componentGraph.add(mockComponentNode(classOf[ComponentTakingAllSimpleComponents]))
- componentGraph.complete()
-
- val instance = componentGraph.getInstance(classOf[ComponentTakingAllSimpleComponents])
- assertThat(instance.simpleComponents.allComponents().size(), is(3))
- }
-
- @Test
- def empty_component_registry_can_be_injected() {
- val componentGraph = new ComponentGraph
- componentGraph.add(mockComponentNode(classOf[ComponentTakingAllSimpleComponents]))
- componentGraph.complete()
-
- val instance = componentGraph.getInstance(classOf[ComponentTakingAllSimpleComponents])
- assertThat(instance.simpleComponents.allComponents().size(), is(0))
- }
-
- @Test
- def component_registry_with_wildcard_upper_bound_can_be_injected() {
- val componentGraph = new ComponentGraph
- componentGraph.add(mockComponentNode(classOf[SimpleComponent]))
- componentGraph.add(mockComponentNode(classOf[SimpleDerivedComponent]))
- componentGraph.add(mockComponentNode(classOf[ComponentTakingAllSimpleComponentsUpperBound]))
- componentGraph.complete()
-
- val instance = componentGraph.getInstance(classOf[ComponentTakingAllSimpleComponentsUpperBound])
- assertThat(instance.simpleComponents.allComponents().size(), is(2))
- }
-
- @Test(expected = classOf[RuntimeException])
- def require_exception_when_injecting_registry_with_unknown_type_variable() {
- val clazz = classOf[ComponentTakingAllComponentsWithTypeVariable[_]]
-
- val componentGraph = new ComponentGraph
- componentGraph.add(mockComponentNode(classOf[SimpleComponent]))
- componentGraph.add(mockComponentNode(classOf[SimpleDerivedComponent]))
- componentGraph.add(mockComponentNode(clazz))
- componentGraph.complete()
-
- componentGraph.getInstance(clazz)
- }
-
- @Test
- def components_are_shared() {
- val componentGraph = new ComponentGraph
- componentGraph.add(mockComponentNode(classOf[SimpleComponent]))
- componentGraph.complete()
-
- val instance1 = componentGraph.getInstance(classOf[SimpleComponent])
- val instance2 = componentGraph.getInstance(classOf[SimpleComponent])
- assertThat(instance1, sameInstance(instance2))
- }
-
- @Test
- def singleton_components_can_be_injected() {
- val componentGraph = new ComponentGraph
- val configId = """raw:stringVal "test-value" """
-
- componentGraph.add(mockComponentNode(classOf[ComponentTakingComponent]))
- componentGraph.add(mockComponentNode(classOf[ComponentTakingConfig], configId))
- componentGraph.add(mockComponentNode(classOf[SimpleComponent2]))
- componentGraph.complete()
- componentGraph.setAvailableConfigs(Map(keyAndConfig(classOf[TestConfig], configId)))
-
- val instance = componentGraph.getInstance(classOf[ComponentTakingComponent])
- assertThat(instance.injectedComponent.asInstanceOf[ComponentTakingConfig].config.stringVal(), is("test-value"))
- }
-
- @Test(expected = classOf[RuntimeException])
- def require_error_when_multiple_components_match_a_singleton_dependency() {
- val componentGraph = new ComponentGraph
- componentGraph.add(mockComponentNode(classOf[SimpleDerivedComponent]))
- componentGraph.add(mockComponentNode(classOf[SimpleComponent]))
- componentGraph.add(mockComponentNode(classOf[ComponentTakingComponent]))
- componentGraph.complete()
- }
-
- @Test
- def named_component_can_be_injected() {
- val componentGraph = new ComponentGraph
- componentGraph.add(mockComponentNode(classOf[SimpleComponent]))
- componentGraph.add(mockComponentNode(classOf[SimpleComponent], key = Names.named("named-test")))
- componentGraph.add(mockComponentNode(classOf[ComponentTakingNamedComponent]))
- componentGraph.complete()
- }
-
- @Test
- def config_keys_can_be_retrieved() {
- val componentGraph = new ComponentGraph
- componentGraph.add(mockComponentNode(classOf[ComponentTakingConfig], configId = """raw:stringVal "component1" """""))
- componentGraph.add(mockComponentNode(classOf[ComponentTakingConfig], configId = """raw:stringVal "component2" """""))
- componentGraph.add(new ComponentRegistryNode(classOf[ComponentTakingConfig]))
- componentGraph.complete()
-
- val configKeys = componentGraph.configKeys
- assertThat(configKeys.size, is(2))
-
- configKeys.foreach{ key =>
- assertThat(key.getConfigClass, equalTo(classOf[TestConfig]))
- assertThat(key.getConfigId.toString, containsString("component"))
- }
- }
-
- @Test
- def providers_can_be_instantiated() {
- val componentGraph = new ComponentGraph
- componentGraph.add(mockComponentNode(classOf[ExecutorProvider]))
- componentGraph.complete()
-
- assertNotNull(componentGraph.getInstance(classOf[Executor]))
- }
-
- @Test
- def providers_can_be_inherited() {
- val componentGraph = new ComponentGraph
- componentGraph.add(mockComponentNode(classOf[DerivedExecutorProvider]))
- componentGraph.complete()
-
- assertNotNull(componentGraph.getInstance(classOf[Executor]))
- }
-
- @Test
- def providers_can_deliver_a_new_instance_for_each_component() {
- val componentGraph = new ComponentGraph
- componentGraph.add(mockComponentNode(classOf[NewIntProvider]))
- componentGraph.complete()
-
- val instance1 = componentGraph.getInstance(classOf[Int])
- val instance2 = componentGraph.getInstance(classOf[Int])
- assertThat(instance1, not(equalTo(instance2)))
- }
-
- @Test
- def providers_can_be_injected_explicitly() {
- val componentGraph = new ComponentGraph
-
- val componentTakingExecutor = mockComponentNode(classOf[ComponentTakingExecutor])
- val executorProvider = mockComponentNode(classOf[ExecutorProvider])
- componentTakingExecutor.inject(executorProvider)
-
- componentGraph.add(executorProvider)
- componentGraph.add(mockComponentNode(classOf[ExecutorProvider]))
-
- componentGraph.add(componentTakingExecutor)
-
- componentGraph.complete()
- assertNotNull(componentGraph.getInstance(classOf[ComponentTakingExecutor]))
- }
-
- @Test
- def global_providers_can_be_injected() {
- val componentGraph = new ComponentGraph
-
- componentGraph.add(mockComponentNode(classOf[ComponentTakingExecutor]))
- componentGraph.add(mockComponentNode(classOf[ExecutorProvider]))
- componentGraph.add(mockComponentNode(classOf[IntProvider]))
- componentGraph.complete()
-
- assertNotNull(componentGraph.getInstance(classOf[ComponentTakingExecutor]))
- }
-
- @Test(expected = classOf[RuntimeException])
- def throw_if_multiple_global_providers_exist(): Unit = {
- val componentGraph = new ComponentGraph
-
- componentGraph.add(mockComponentNode(classOf[ExecutorProvider]))
- componentGraph.add(mockComponentNode(classOf[ExecutorProvider]))
- componentGraph.add(mockComponentNode(classOf[ComponentTakingExecutor]))
- componentGraph.complete()
- }
-
- @Test
- def provider_is_not_used_when_component_of_provided_class_exists() {
- val componentGraph = new ComponentGraph
-
- componentGraph.add(mockComponentNode(classOf[SimpleComponent]))
- componentGraph.add(mockComponentNode(classOf[SimpleComponentProviderThatThrows]))
- componentGraph.add(mockComponentNode(classOf[ComponentTakingComponent]))
- componentGraph.complete()
-
- val injectedComponent = componentGraph.getInstance(classOf[ComponentTakingComponent]).injectedComponent
- assertNotNull(injectedComponent)
- }
-
- //TODO: move
- @Test
- def check_if_annotation_is_a_binding_annotation() {
- import ComponentGraph.isBindingAnnotation
-
- assertTrue(isBindingAnnotation(Names.named("name")))
- assertFalse(isBindingAnnotation(classOf[Named].getAnnotations.head))
- }
-
- @Test
- def cycles_gives_exception() {
- val componentGraph = new ComponentGraph
-
- def mockNode = mockComponentNode(classOf[ComponentCausingCycle])
-
- val node1 = mockNode
- val node2 = mockNode
-
- node1.inject(node2)
- node2.inject(node1)
-
- componentGraph.add(node1)
- componentGraph.add(node2)
-
- try {
- componentGraph.complete()
- fail("Cycle exception expected.")
- } catch {
- case e : Throwable => assertThat(e.getMessage, containsString("cycle"))
- }
- }
-
- @Test(expected = classOf[IllegalArgumentException])
- def abstract_classes_are_rejected() {
- new ComponentNode(ComponentId.fromString("Test"), "", classOf[AbstractClass])
- }
-
- @Test
- def inject_constructor_is_preferred() {
- assertThatComponentCanBeCreated(classOf[ComponentWithInjectConstructor])
- }
-
- @Test
- def constructor_with_most_parameters_is_preferred() {
- assertThatComponentCanBeCreated(classOf[ComponentWithMultipleConstructors])
- }
-
- def assertThatComponentCanBeCreated(clazz: Class[AnyRef]) {
- val componentGraph = new ComponentGraph
- val configId = """raw:stringVal "dummy" """"
-
- componentGraph.add(mockComponentNode(clazz, configId))
- componentGraph.complete()
-
- componentGraph.setAvailableConfigs(Map(
- keyAndConfig(classOf[TestConfig], configId),
- keyAndConfig(classOf[Test2Config], configId)))
-
- assertNotNull(componentGraph.getInstance(clazz))
- }
-
- @Test
- def require_fallback_to_child_injector() {
- val componentGraph = new ComponentGraph
-
- componentGraph.add(mockComponentNode(classOf[ComponentTakingExecutor]))
-
- componentGraph.complete(singletonExecutorInjector)
- assertNotNull(componentGraph.getInstance(classOf[ComponentTakingExecutor]))
- }
-
- @Test
- def child_injector_can_inject_multiple_instances_for_same_key() {
- def executorProvider() = Executors.newSingleThreadExecutor()
-
- val (graphSize, executorA, executorB) = buildGraphWithChildInjector(() => executorProvider())
-
- assertThat(graphSize, is(4))
- assertThat(executorA, not(sameInstance(executorB)))
- }
-
- @Test
- def components_injected_via_child_injector_can_be_shared() {
- val commonExecutor = Executors.newSingleThreadExecutor()
- val (graphSize, executorA, executorB) = buildGraphWithChildInjector(() => commonExecutor)
-
- assertThat(graphSize, is(3))
- assertThat(executorA, sameInstance(executorB))
- }
-
- def buildGraphWithChildInjector(executorProvider: () => Executor) = {
- val childInjector = Guice.createInjector(new AbstractModule {
- override def configure() {
- bind(classOf[Executor]).toProvider(new GuiceProvider[Executor] {
- def get() = executorProvider()
- })
- }
- })
-
- val componentGraph = new ComponentGraph
-
- def key(name: String) = Key.get(classOf[ComponentTakingExecutor], Names.named(name))
- val keyA = key("A")
- val keyB = key("B")
-
- componentGraph.add(mockComponentNode(keyA))
- componentGraph.add(mockComponentNode(keyB))
-
- componentGraph.complete(childInjector)
-
- (componentGraph.size, componentGraph.getInstance(keyA).executor, componentGraph.getInstance(keyB).executor)
- }
-
- @Test
- def providers_can_be_reused() {
- def createGraph() = {
- val graph = new ComponentGraph()
- graph.add(mockComponentNodeWithId(classOf[ExecutorProvider], "dummyId"))
- graph.complete()
- graph.setAvailableConfigs(Map())
- graph
- }
-
- val oldGraph = createGraph()
- val executor = oldGraph.getInstance(classOf[Executor])
-
- val newGraph = createGraph()
- newGraph.reuseNodes(oldGraph)
-
- val newExecutor = newGraph.getInstance(classOf[Executor])
- assertThat(executor, sameInstance(newExecutor))
- }
-
- @Test
- def component_id_can_be_injected() {
- val componentId: String = "myId:1.2@namespace"
-
- val componentGraph = new ComponentGraph
- componentGraph.add(mockComponentNodeWithId(classOf[ComponentTakingComponentId], componentId))
- componentGraph.complete()
-
- assertThat(componentGraph.getInstance(classOf[ComponentTakingComponentId]).componentId,
- is(ComponentId.fromString(componentId)))
- }
-
- @Test
- def rest_api_context_can_be_instantiated() {
- val configId = """raw:"" """
-
- val clazz = classOf[RestApiContext]
- val jerseyNode = new JerseyNode(uniqueComponentId(clazz.getName), configId, clazz, new Osgi {})
-
- val componentGraph = new ComponentGraph
- componentGraph.add(jerseyNode)
- componentGraph.complete()
- componentGraph.setAvailableConfigs(Map(keyAndConfig(classOf[JerseyBundlesConfig], configId),
- keyAndConfig(classOf[JerseyInjectionConfig], configId)))
-
- val restApiContext = componentGraph.getInstance(clazz)
- assertNotNull(restApiContext)
- assertThat(restApiContext.getBundles.size, is(0))
- }
-
-}
-
-//Note that all Components must be defined in a static context,
-//otherwise their constructor will take the outer class as the first parameter.
-object ComponentGraphTest {
- var counter = 0
-
-
- class SimpleComponent extends AbstractComponent
- class SimpleComponent2 extends AbstractComponent
- class SimpleDerivedComponent extends SimpleComponent
-
- class ComponentTakingConfig(val config: TestConfig) extends SimpleComponent {
- require(config != null)
- }
-
- class ComponentTakingComponent(val injectedComponent: SimpleComponent) extends AbstractComponent {
- require(injectedComponent != null)
- }
-
- class ComponentTakingConfigAndComponent(val config: TestConfig, val injectedComponent: SimpleComponent) extends AbstractComponent {
- require(config != null)
- require(injectedComponent != null)
- }
-
- class ComponentTakingAllSimpleComponents(val simpleComponents: ComponentRegistry[SimpleComponent]) extends AbstractComponent {
- require(simpleComponents != null)
- }
-
- class ComponentTakingAllSimpleComponentsUpperBound(val simpleComponents: ComponentRegistry[_ <: SimpleComponent])
- extends AbstractComponent {
-
- require(simpleComponents != null)
- }
-
- class ComponentTakingAllComponentsWithTypeVariable[COMPONENT <: AbstractComponent](val simpleComponents: ComponentRegistry[COMPONENT])
- extends AbstractComponent {
-
- require(simpleComponents != null)
- }
-
- class ComponentTakingNamedComponent(@Named("named-test") injectedComponent: SimpleComponent) extends AbstractComponent {
- require(injectedComponent != null)
- }
-
- class ComponentCausingCycle(component: ComponentCausingCycle) extends AbstractComponent
-
- class SimpleComponentProviderThatThrows extends Provider[SimpleComponent] {
- def get() = throw new AssertionError("Should never be called.")
- def deconstruct() {}
- }
-
- class ExecutorProvider extends Provider[Executor] {
- val executor = Executors.newSingleThreadExecutor()
- def get() = executor
- def deconstruct() { /*TODO */ }
- }
-
- class DerivedExecutorProvider extends ExecutorProvider
-
- class IntProvider extends Provider[java.lang.Integer] {
- def get() = throw new AssertionError("Should never be called.")
- def deconstruct() {}
- }
-
- class NewIntProvider extends Provider[Integer] {
- var i: Int = 0
- def get() = {
- i += 1
- i
- }
- def deconstruct() {}
- }
-
- class ComponentTakingExecutor(val executor: Executor) extends AbstractComponent {
- require(executor != null)
- }
-
- class ComponentWithInjectConstructor private () {
- def this(c: TestConfig, c2: Test2Config) = { this(); sys.error("Should not be called") }
- @Inject
- def this(c: Test2Config) = { this() }
- }
-
- class ComponentWithMultipleConstructors private (dummy : Int) {
- def this(c: TestConfig, c2: Test2Config) = { this(0); }
-
- def this() = { this(0); sys.error("Should not be called") }
- def this(c: Test2Config) = { this() }
- }
-
- class ComponentTakingComponentId(val componentId: ComponentId)
-
- def uniqueComponentId(className: String): ComponentId = {
- counter += 1
- ComponentId.fromString(className + counter)
- }
-
- def mockComponentNode(key: Key[_ <: AnyRef]): Node =
- mockComponentNode(key.getTypeLiteral.getRawType.asInstanceOf[Class[AnyRef]], key=key.getAnnotation)
-
- def mockComponentNode(clazz: Class[_ <: AnyRef], configId: String = "", key: JavaAnnotation = null): Node =
- new ComponentNode(uniqueComponentId(clazz.getName), configId, clazz, key)
-
- def mockComponentNodeWithId(clazz: Class[_ <: AnyRef], componentId: String, configId: String = "", key: JavaAnnotation = null): Node =
- new ComponentNode(ComponentId.fromString(componentId), configId, clazz, key)
-
- val singletonExecutorInjector = Guice.createInjector(new AbstractModule {
- override def configure() {
- bind(classOf[Executor]).toInstance(Executors.newSingleThreadExecutor())
- }
- })
-
- implicit def makeMatcherCovariant[T, U >: T](matcher: Matcher[T]) : Matcher[U] = matcher.asInstanceOf[Matcher[U]]
-
- abstract class AbstractClass
-}
-
diff --git a/container-di/src/test/scala/com/yahoo/container/di/componentgraph/core/JerseyNodeTest.scala b/container-di/src/test/scala/com/yahoo/container/di/componentgraph/core/JerseyNodeTest.scala
deleted file mode 100644
index e55ac65680d..00000000000
--- a/container-di/src/test/scala/com/yahoo/container/di/componentgraph/core/JerseyNodeTest.scala
+++ /dev/null
@@ -1,66 +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.container.di.componentgraph.core
-
-import java.util
-import java.util.Collections
-import com.yahoo.container.di.osgi.OsgiUtil
-import org.junit.Test
-import org.junit.Assert._
-import org.hamcrest.CoreMatchers.is
-import org.hamcrest.Matchers.{contains, containsInAnyOrder}
-import org.osgi.framework.wiring.BundleWiring
-import scala.collection.JavaConverters._
-import com.yahoo.container.bundle.MockBundle
-
-/**
- *
- * @author gjoranv
- * @since 5.17
- */
-
-class JerseyNodeTest {
-
- trait WithMockBundle {
- object bundle extends MockBundle {
- val entry = Map(
- "com/foo" -> "Foo.class",
- "com/bar" -> "Bar.class)"
- ) map { case (packageName, className) => (packageName, packageName + "/" + className)}
-
-
- override def listResources(path: String, ignored: String, options: Int): util.Collection[String] = {
- if ((options & BundleWiring.LISTRESOURCES_RECURSE) != 0 && path == "/") entry.values.asJavaCollection
- else Collections.singleton(entry(path))
- }
- }
-
- val bundleClasses = bundle.entry.values.toList
- }
-
- @Test
- def all_bundle_entries_are_returned_when_no_packages_are_given() {
- new WithMockBundle {
- val entries = OsgiUtil.getClassEntriesInBundleClassPath(bundle, Set()).asJavaCollection
- assertThat(entries, containsInAnyOrder(bundleClasses: _*))
- }
- }
-
- @Test
- def only_bundle_entries_from_the_given_packages_are_returned() {
- new WithMockBundle {
- val entries = OsgiUtil.getClassEntriesInBundleClassPath(bundle, Set("com.foo")).asJavaCollection
- assertThat(entries, contains(bundle.entry("com/foo")))
- }
- }
-
- @Test
- def bundle_info_is_initialized() {
- new WithMockBundle {
- val bundleInfo = JerseyNode.createBundleInfo(bundle, List())
- assertThat(bundleInfo.symbolicName, is(bundle.getSymbolicName))
- assertThat(bundleInfo.version, is(bundle.getVersion))
- assertThat(bundleInfo.fileLocation, is(bundle.getLocation))
- }
- }
-
-}
diff --git a/container-di/src/test/scala/com/yahoo/container/di/componentgraph/core/ReuseComponentsTest.scala b/container-di/src/test/scala/com/yahoo/container/di/componentgraph/core/ReuseComponentsTest.scala
deleted file mode 100644
index 33c6d2a3e89..00000000000
--- a/container-di/src/test/scala/com/yahoo/container/di/componentgraph/core/ReuseComponentsTest.scala
+++ /dev/null
@@ -1,249 +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.container.di.componentgraph.core
-
-import com.yahoo.component.{ComponentId, AbstractComponent}
-import org.junit.Assert._
-import org.hamcrest.CoreMatchers.{is, not, sameInstance, equalTo}
-import com.yahoo.vespa.config.ConfigKey
-import java.util.concurrent.Executor
-import com.yahoo.container.di.componentgraph.core.ComponentGraphTest.{ExecutorProvider, SimpleComponent, SimpleComponent2}
-import com.yahoo.container.di.componentgraph.core.ComponentGraphTest.{ComponentTakingConfig, ComponentTakingExecutor, singletonExecutorInjector}
-import com.yahoo.container.di.makeClassCovariant
-import org.junit.Test
-import com.yahoo.config.subscription.ConfigGetter
-import com.yahoo.config.test.TestConfig
-
-/**
- * @author gjoranv
- * @author tonytv
- */
-class ReuseComponentsTest {
- import ReuseComponentsTest._
-
- @Test
- def require_that_component_is_reused_when_componentNode_is_unmodified() {
- def reuseAndTest(classToRegister: Class[AnyRef], classToLookup: Class[AnyRef]) {
- val graph = buildGraphAndSetNoConfigs(classToRegister)
- val instance = getComponent(graph, classToLookup)
-
- val newGraph = buildGraphAndSetNoConfigs(classToRegister)
- newGraph.reuseNodes(graph)
- val instance2 = getComponent(newGraph, classToLookup)
-
- assertThat(instance2, sameInstance(instance))
- }
-
- reuseAndTest(classOf[SimpleComponent], classOf[SimpleComponent])
- reuseAndTest(classOf[ExecutorProvider], classOf[Executor])
- }
-
-
- @Test(expected = classOf[IllegalStateException])
- def require_that_component_is_not_reused_when_class_is_changed() {
- val graph = buildGraphAndSetNoConfigs(classOf[SimpleComponent])
- val instance = getComponent(graph, classOf[SimpleComponent])
-
- val newGraph = buildGraphAndSetNoConfigs(classOf[SimpleComponent2])
- newGraph.reuseNodes(graph)
- val instance2 = getComponent(newGraph, classOf[SimpleComponent2])
-
- assertThat(instance2.getId, is(instance.getId))
- val throwsException = getComponent(newGraph, classOf[SimpleComponent])
- }
-
- @Test
- def require_that_component_is_not_reused_when_config_is_changed() {
- def setConfig(graph: ComponentGraph, config: String) {
- graph.setAvailableConfigs(
- Map(new ConfigKey(classOf[TestConfig], "component") ->
- ConfigGetter.getConfig(classOf[TestConfig], """raw: stringVal "%s" """.format(config))))
- }
-
- val componentClass = classOf[ComponentTakingConfig]
-
- val graph = buildGraph(componentClass)
- setConfig(graph, "oldConfig")
- val instance = getComponent(graph, componentClass)
-
- val newGraph = buildGraph(componentClass)
- setConfig(newGraph, "newConfig")
- newGraph.reuseNodes(graph)
- val instance2 = getComponent(newGraph, componentClass)
-
- assertThat(instance2, not(sameInstance(instance)))
- }
-
- @Test
- def require_that_component_is_not_reused_when_injected_component_is_changed() {
- import ComponentGraphTest.{ComponentTakingComponent, ComponentTakingConfig}
-
- def buildGraph(config: String) = {
- val graph = new ComponentGraph
-
- val rootComponent = mockComponentNode(classOf[ComponentTakingComponent], "root_component")
-
- val configId = "componentTakingConfigId"
- val injectedComponent = mockComponentNode(classOf[ComponentTakingConfig], "injected_component", configId)
-
- rootComponent.inject(injectedComponent)
-
- graph.add(rootComponent)
- graph.add(injectedComponent)
-
- graph.complete()
- graph.setAvailableConfigs(Map(new ConfigKey(classOf[TestConfig], configId) ->
- ConfigGetter.getConfig(classOf[TestConfig], """raw: stringVal "%s" """.format(config))))
-
- graph
- }
-
- val oldGraph = buildGraph(config="oldGraph")
- val oldInstance = getComponent(oldGraph, classOf[ComponentTakingComponent])
-
- val newGraph = buildGraph(config="newGraph")
- newGraph.reuseNodes(oldGraph)
- val newInstance = getComponent(newGraph, classOf[ComponentTakingComponent])
-
- assertThat(newInstance, not(sameInstance(oldInstance)))
- }
-
- @Test
- def require_that_component_is_not_reused_when_injected_component_registry_has_one_component_removed() {
- import ComponentGraphTest.ComponentTakingAllSimpleComponents
-
- def buildGraph(useBothInjectedComponents: Boolean) = {
- val graph = new ComponentGraph
- graph.add(mockComponentNode(classOf[ComponentTakingAllSimpleComponents], "root_component"))
-
- /* Below if-else has code duplication, but explicit ordering of the two components
- * was necessary to reproduce erroneous behaviour in ComponentGraph.reuseNodes that
- * occurred before ComponentRegistryNode got its own 'equals' implementation.
- */
- if (useBothInjectedComponents) {
- graph.add(mockComponentNode(classOf[SimpleComponent], "injected_component2"))
- graph.add(mockComponentNode(classOf[SimpleComponent], "injected_component1"))
- } else {
- graph.add(mockComponentNode(classOf[SimpleComponent], "injected_component1"))
- }
-
- graph.complete()
- graph.setAvailableConfigs(Map())
- graph
- }
-
- val oldGraph = buildGraph(useBothInjectedComponents = true)
- val oldSimpleComponentRegistry = getComponent(oldGraph, classOf[ComponentTakingAllSimpleComponents]).simpleComponents
-
- val newGraph = buildGraph(useBothInjectedComponents = false)
- newGraph.reuseNodes(oldGraph)
- val newSimpleComponentRegistry = getComponent(newGraph, classOf[ComponentTakingAllSimpleComponents]).simpleComponents
-
- assertThat(newSimpleComponentRegistry, not(sameInstance(oldSimpleComponentRegistry)))
- }
-
- @Test
- def require_that_injected_component_is_reused_even_when_dependent_component_is_changed() {
- import ComponentGraphTest.{ComponentTakingConfigAndComponent, SimpleComponent}
-
- def buildGraph(config: String) = {
- val graph = new ComponentGraph
-
- val configId = "componentTakingConfigAndComponent"
- val rootComponent = mockComponentNode(classOf[ComponentTakingConfigAndComponent], "root_component", configId)
-
- val injectedComponent = mockComponentNode(classOf[SimpleComponent], "injected_component")
-
- rootComponent.inject(injectedComponent)
-
- graph.add(rootComponent)
- graph.add(injectedComponent)
-
- graph.complete()
- graph.setAvailableConfigs(Map(new ConfigKey(classOf[TestConfig], configId) ->
- ConfigGetter.getConfig(classOf[TestConfig], """raw: stringVal "%s" """.format(config))))
-
- graph
- }
-
- val oldGraph = buildGraph(config="oldGraph")
- val oldInjectedComponent = getComponent(oldGraph, classOf[SimpleComponent])
- val oldDependentComponent = getComponent(oldGraph, classOf[ComponentTakingConfigAndComponent])
-
- val newGraph = buildGraph(config="newGraph")
- newGraph.reuseNodes(oldGraph)
- val newInjectedComponent = getComponent(newGraph, classOf[SimpleComponent])
- val newDependentComponent = getComponent(newGraph, classOf[ComponentTakingConfigAndComponent])
-
- assertThat(newDependentComponent, not(sameInstance(oldDependentComponent)))
- assertThat(newInjectedComponent, sameInstance(oldInjectedComponent))
- }
-
- @Test
- def require_that_node_depending_on_guice_node_is_reused() {
- def makeGraph = {
- val graph = new ComponentGraph
- graph.add(mockComponentNode(classOf[ComponentTakingExecutor], "dummyId"))
- graph.complete(singletonExecutorInjector)
- graph.setAvailableConfigs(Map())
- graph
- }
-
- val getComponentTakingExecutor = getComponent(_: ComponentGraph, classOf[ComponentTakingExecutor])
-
- val oldGraph = makeGraph
- getComponentTakingExecutor(oldGraph) // Ensure creation of GuiceNode
- val newGraph = makeGraph
- newGraph.reuseNodes(oldGraph)
- assertThat(getComponentTakingExecutor(oldGraph), sameInstance(getComponentTakingExecutor(newGraph)))
- }
-
- @Test
- def require_that_node_equals_only_checks_first_level_components_to_inject() {
-
- def createNodeWithInjectedNodeWithInjectedNode(indirectlyInjectedComponentId: String): Node = {
- val targetComponent = mockComponentNode(classOf[SimpleComponent], "target")
- val directlyInjectedComponent = mockComponentNode(classOf[SimpleComponent], "directlyInjected")
- val indirectlyInjectedComponent = mockComponentNode(classOf[SimpleComponent], indirectlyInjectedComponentId)
- directlyInjectedComponent.inject(indirectlyInjectedComponent)
- targetComponent.inject(directlyInjectedComponent)
-
- completeNode(targetComponent)
- completeNode(directlyInjectedComponent)
- completeNode(indirectlyInjectedComponent)
-
- targetComponent
- }
- val targetNode1 = createNodeWithInjectedNodeWithInjectedNode("indirectlyInjected_1")
- val targetNode2 = createNodeWithInjectedNodeWithInjectedNode("indirectlyInjected_2")
- assertThat(targetNode1, equalTo(targetNode2))
- }
-
- private def completeNode(node: ComponentNode) {
- node.setArguments(Array())
- node.setAvailableConfigs(Map())
- }
-
- private def buildGraph(componentClass: Class[_ <: AnyRef]) = {
- val commonComponentId = "component"
- val g = new ComponentGraph
- g.add(mockComponentNode(componentClass, commonComponentId, configId = commonComponentId))
- g.complete()
- g
- }
-
- private def buildGraphAndSetNoConfigs(componentClass: Class[_ <: AnyRef]) = {
- val g = buildGraph(componentClass)
- g.setAvailableConfigs(Map())
- g
- }
-}
-
-object ReuseComponentsTest {
-
- def mockComponentNode(clazz: Class[_ <: AnyRef], componentId: String = "", configId: String="") =
- new ComponentNode(new ComponentId(componentId), configId, clazz)
-
- def getComponent[T](graph: ComponentGraph, clazz: Class[T]) = {
- graph.getInstance(clazz)
- }
-}
diff --git a/container-disc/pom.xml b/container-disc/pom.xml
index c112466bbd4..ea3425d302d 100644
--- a/container-disc/pom.xml
+++ b/container-disc/pom.xml
@@ -169,6 +169,7 @@
configdefinitions-jar-with-dependencies.jar,
container-jersey2-jar-with-dependencies.jar,
container-search-and-docproc-jar-with-dependencies.jar,
+ container-search-gui-jar-with-dependencies.jar,
docprocs-jar-with-dependencies.jar,
jdisc-security-filters-jar-with-dependencies.jar,
jdisc_http_service-jar-with-dependencies.jar,
diff --git a/container-disc/src/main/java/com/yahoo/container/FilterConfigProvider.java b/container-disc/src/main/java/com/yahoo/container/FilterConfigProvider.java
index 5c2e893d2ee..c17b9d445a2 100644
--- a/container-disc/src/main/java/com/yahoo/container/FilterConfigProvider.java
+++ b/container-disc/src/main/java/com/yahoo/container/FilterConfigProvider.java
@@ -10,7 +10,7 @@ import java.util.HashMap;
import java.util.Map;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public final class FilterConfigProvider implements Provider<FilterConfig> {
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java b/container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java
index 932d31c0036..69a525a2c54 100644
--- a/container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java
@@ -203,7 +203,7 @@ public final class ConfiguredApplication implements Application {
// Block until new config arrives, and it should be applied
configurer.getNewComponentGraph(builder.guiceModules().activate(), qrConfig.restartOnDeploy());
intitializeAndActivateContainer(builder);
- } catch (ConfigInterruptedException | InterruptedException e) {
+ } catch (ConfigInterruptedException e) {
break;
} catch (Exception | LinkageError e) { // LinkageError: OSGi problems
log.log(Level.SEVERE,
@@ -256,13 +256,12 @@ public final class ConfiguredApplication implements Application {
}
private void configureComponents(Injector discInjector) {
- configurer = new HandlersConfigurerDi(
- subscriberFactory,
- Container.get(),
- configId,
- new Deconstructor(true),
- discInjector,
- osgiFramework);
+ configurer = new HandlersConfigurerDi(subscriberFactory,
+ Container.get(),
+ configId,
+ new Deconstructor(true),
+ discInjector,
+ osgiFramework);
}
private void setupGuiceBindings(GuiceRepository modules) {
@@ -284,14 +283,18 @@ public final class ConfiguredApplication implements Application {
startShutdownDeadlineExecutor();
shutdownReconfigurerThread();
- configurer.shutdown(new Deconstructor(false));
-
+ log.info("Stop: Closing servers");
for (ServerProvider server : Container.get().getServerProviderRegistry().allComponents()) {
if (startedServers.contains(server)) {
closeServer(server);
}
}
+
+ log.info("Stop: Shutting container down");
+ configurer.shutdown(new Deconstructor(false));
Container.get().shutdown();
+
+ log.info("Stop: Finished");
}
private void shutdownReconfigurerThread() {
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/DisableOsgiFramework.java b/container-disc/src/main/java/com/yahoo/container/jdisc/DisableOsgiFramework.java
index 07726f3356f..3978d39a89a 100644
--- a/container-disc/src/main/java/com/yahoo/container/jdisc/DisableOsgiFramework.java
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/DisableOsgiFramework.java
@@ -9,7 +9,7 @@ import org.osgi.framework.BundleException;
import java.util.List;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public final class DisableOsgiFramework implements OsgiFramework {
private final RestrictedBundleContext restrictedBundleContext;
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/component/Deconstructor.java b/container-disc/src/main/java/com/yahoo/container/jdisc/component/Deconstructor.java
index d39357fab46..4fe7a07e281 100644
--- a/container-disc/src/main/java/com/yahoo/container/jdisc/component/Deconstructor.java
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/component/Deconstructor.java
@@ -18,7 +18,7 @@ import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
/**
-* @author tonyv
+* @author Tony Vaagenes
* @author gv
*/
public class Deconstructor implements ComponentDeconstructor {
@@ -45,7 +45,7 @@ public class Deconstructor implements ComponentDeconstructor {
} else if (component instanceof Provider) {
// TODO Providers should most likely be deconstructed similarily to AbstractComponent
log.info("Starting deconstruction of provider " + component);
- ((Provider)component).deconstruct();
+ ((Provider<?>)component).deconstruct();
log.info("Finished deconstruction of provider " + component);
} else if (component instanceof SharedResource) {
log.info("Releasing container reference to resource " + component);
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/DisableGuiceMetric.java b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/DisableGuiceMetric.java
index 8ec21499e26..5c205cd5157 100644
--- a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/DisableGuiceMetric.java
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/DisableGuiceMetric.java
@@ -6,7 +6,7 @@ import com.yahoo.jdisc.Metric;
import java.util.Map;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class DisableGuiceMetric implements Metric {
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/ForwardingMetricConsumer.java b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/ForwardingMetricConsumer.java
index 8f71f13b4c2..7bdd2d9cc7b 100644
--- a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/ForwardingMetricConsumer.java
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/ForwardingMetricConsumer.java
@@ -12,7 +12,7 @@ import java.util.Map;
* forwarded to all the underlying <tt>MetricConsumers</tt>. That is the responsibility of this class. Instances of this
* class is created by the {@link MetricConsumerProvider} in those cases.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public final class ForwardingMetricConsumer implements MetricConsumer {
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProvider.java b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProvider.java
index 24e873a97f1..1e7a4e75385 100644
--- a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProvider.java
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProvider.java
@@ -20,7 +20,7 @@ import com.yahoo.metrics.MetricsPresentationConfig;
* this class, which means any component that uses <tt>Metric</tt> will be reconfigured. Any component that depends
* directly on <tt>MetricConsumer</tt> will also be reconfigured.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class MetricConsumerProvider {
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricProvider.java b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricProvider.java
index 8752fb15817..bcd75c0b36b 100644
--- a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricProvider.java
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricProvider.java
@@ -10,7 +10,7 @@ import com.yahoo.jdisc.application.MetricConsumer;
* MetricConsumerProvider}, any change to the consumer configuration will trigger reconfiguration of this component,
* which in turn triggers reconfiguration of any component that depends on <tt>Metric</tt>.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public final class MetricProvider implements Provider<Metric> {
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/state/StateMetricConsumerFactory.java b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/state/StateMetricConsumerFactory.java
index dc559fb5e9d..d1cf660f571 100644
--- a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/state/StateMetricConsumerFactory.java
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/state/StateMetricConsumerFactory.java
@@ -6,7 +6,7 @@ import com.yahoo.container.jdisc.state.StateMonitor;
import com.yahoo.jdisc.application.MetricConsumer;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class StateMetricConsumerFactory implements MetricConsumerFactory {
diff --git a/container-disc/src/main/sh/vespa-start-container-daemon.sh b/container-disc/src/main/sh/vespa-start-container-daemon.sh
index 6f0c97f6177..e6219ab0467 100755
--- a/container-disc/src/main/sh/vespa-start-container-daemon.sh
+++ b/container-disc/src/main/sh/vespa-start-container-daemon.sh
@@ -190,10 +190,10 @@ exec_jsvc () {
${jsvc_opts} \
${memory_options} \
${jvm_gcopts} \
- -XX:MaxJavaStackTraceDepth=-1 \
+ -XX:MaxJavaStackTraceDepth=1000000 \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath="${VESPA_HOME}/var/crash" \
- -XX:OnOutOfMemoryError='kill -9 %p' \
+ -XX:+ExitOnOutOfMemoryError \
-Djava.library.path="${VESPA_HOME}/lib64" \
-Djava.awt.headless=true \
-Djavax.net.ssl.keyStoreType=JKS \
@@ -262,10 +262,10 @@ exec $numactlcmd $envcmd java \
-XX:+PreserveFramePointer \
${memory_options} \
${jvm_gcopts} \
- -XX:MaxJavaStackTraceDepth=-1 \
+ -XX:MaxJavaStackTraceDepth=1000000 \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath="${VESPA_HOME}/var/crash" \
- -XX:OnOutOfMemoryError='kill -9 %p' \
+ -XX:+ExitOnOutOfMemoryError \
-Djava.library.path="${VESPA_HOME}/lib64" \
-Djava.awt.headless=true \
-Djavax.net.ssl.keyStoreType=JKS \
diff --git a/container-disc/src/test/java/com/yahoo/container/jdisc/ContainerThreadFactoryTest.java b/container-disc/src/test/java/com/yahoo/container/jdisc/ContainerThreadFactoryTest.java
index b2c644ce68f..65aff29e7ab 100644
--- a/container-disc/src/test/java/com/yahoo/container/jdisc/ContainerThreadFactoryTest.java
+++ b/container-disc/src/test/java/com/yahoo/container/jdisc/ContainerThreadFactoryTest.java
@@ -12,7 +12,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ContainerThreadFactoryTest {
diff --git a/container-disc/src/test/java/com/yahoo/container/jdisc/metric/ForwardingMetricConsumerTest.java b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/ForwardingMetricConsumerTest.java
index c8a90ba3cfe..37e6b13c49a 100644
--- a/container-disc/src/test/java/com/yahoo/container/jdisc/metric/ForwardingMetricConsumerTest.java
+++ b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/ForwardingMetricConsumerTest.java
@@ -12,7 +12,7 @@ import java.util.Map;
import static org.junit.Assert.assertNotNull;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ForwardingMetricConsumerTest {
diff --git a/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerFactories.java b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerFactories.java
index bb2b9706cbf..d6679a29015 100644
--- a/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerFactories.java
+++ b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerFactories.java
@@ -8,7 +8,7 @@ import org.mockito.Mockito;
import java.util.concurrent.atomic.AtomicInteger;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
class MetricConsumerFactories {
diff --git a/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderTest.java b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderTest.java
index 8ee51d403ae..5e3a49c2dda 100644
--- a/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderTest.java
+++ b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderTest.java
@@ -2,17 +2,15 @@
package com.yahoo.container.jdisc.metric;
import com.yahoo.jdisc.application.MetricConsumer;
-import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mockito;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
-import static org.junit.Assume.assumeTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class MetricConsumerProviderTest {
diff --git a/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviders.java b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviders.java
index 601c3961ef6..b77836774d7 100644
--- a/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviders.java
+++ b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviders.java
@@ -11,7 +11,7 @@ import com.yahoo.jdisc.application.MetricConsumer;
import com.yahoo.jdisc.core.SystemTimer;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
class MetricConsumerProviders {
diff --git a/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricProviderTest.java b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricProviderTest.java
index d7cdba9a150..19ec1390722 100644
--- a/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricProviderTest.java
+++ b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricProviderTest.java
@@ -18,7 +18,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class MetricProviderTest {
diff --git a/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricProviders.java b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricProviders.java
index bc4b99be707..5a8edcf2648 100644
--- a/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricProviders.java
+++ b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricProviders.java
@@ -5,7 +5,7 @@ import com.yahoo.container.jdisc.MetricConsumerFactory;
import com.yahoo.jdisc.application.MetricConsumer;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
class MetricProviders {
diff --git a/container-integration-test/.gitignore b/container-integration-test/.gitignore
new file mode 100644
index 00000000000..3cc25b51fc4
--- /dev/null
+++ b/container-integration-test/.gitignore
@@ -0,0 +1,2 @@
+/pom.xml.build
+/target
diff --git a/container-integration-test/OWNERS b/container-integration-test/OWNERS
new file mode 100644
index 00000000000..3b2ba1ede81
--- /dev/null
+++ b/container-integration-test/OWNERS
@@ -0,0 +1 @@
+gjoranv
diff --git a/container-integration-test/README.md b/container-integration-test/README.md
new file mode 100644
index 00000000000..8abe3a889ea
--- /dev/null
+++ b/container-integration-test/README.md
@@ -0,0 +1,9 @@
+<!-- Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
+
+# Integration tests for JDisc components
+
+This module contains integration tests for container components.
+
+Tests that use the `application` framework cannot be added to the same maven
+module as the component itself because that will usually create a cycle in the
+dependency graph.
diff --git a/container-integration-test/pom.xml b/container-integration-test/pom.xml
new file mode 100644
index 00000000000..4b4d5de21eb
--- /dev/null
+++ b/container-integration-test/pom.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<!-- Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+ http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>parent</artifactId>
+ <version>6-SNAPSHOT</version>
+ <relativePath>../parent/pom.xml</relativePath>
+ </parent>
+ <artifactId>container-integration-test</artifactId>
+ <packaging>jar</packaging>
+ <version>6-SNAPSHOT</version>
+ <dependencies>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>container-search-gui</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>application</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+</project> \ No newline at end of file
diff --git a/container-integration-test/src/test/java/com/yahoo/search/query/gui/GUIHandlerTest.java b/container-integration-test/src/test/java/com/yahoo/search/query/gui/GUIHandlerTest.java
new file mode 100644
index 00000000000..5fd73afe800
--- /dev/null
+++ b/container-integration-test/src/test/java/com/yahoo/search/query/gui/GUIHandlerTest.java
@@ -0,0 +1,84 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.query.gui;
+
+import com.yahoo.application.Networking;
+import com.yahoo.application.container.JDisc;
+import com.yahoo.application.container.handler.Request;
+import com.yahoo.application.container.handler.Response;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import java.io.IOException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Henrik Høiness
+ */
+
+public class GUIHandlerTest {
+
+ private JDisc container;
+
+ @Before
+ public void startContainer() {
+ container = JDisc.fromServicesXml(servicesXml(), Networking.disable);
+ }
+
+ @After
+ public void stopContainer() {
+ /*
+ try {
+ Thread.sleep(100_000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }*/
+ container.close();
+ }
+
+ @Test
+ public void testRequest() throws Exception {
+ assertResponse("/querybuilder/", "<!-- Copyright 2018 Yahoo Holdings.","text/html; charset=UTF-8", 200);
+ }
+
+ @Test
+ public void testContentTypes() throws Exception{
+ assertResponse("/querybuilder/_includes/css/vespa.css", "/**","text/css; charset=UTF-8", 200);
+ assertResponse("/querybuilder/js/agency.js", "/*!","application/javascript; charset=UTF-8", 200);
+ assertResponse("/querybuilder/img/reload.svg", "<?xml","image/svg+xml; charset=UTF-8", 200);
+ assertResponse("/querybuilder/img/Vespa-V2.png", null,"image/png; charset=UTF-8", 200);
+ }
+
+ @Test
+ public void testInvalidPath() throws Exception{
+ assertResponse("/querybuilder/invalid_filepath", "{\"error-code\":\"NOT_FOUND\",\"message\":\"Nothing at path","application/json; charset=UTF-8", 404);
+ }
+
+
+ private void assertResponse(String path, String expectedStartString, String expectedContentType, int expectedStatusCode) throws IOException {
+ assertResponse(Request.Method.GET, path, expectedStartString,expectedContentType, expectedStatusCode);
+ }
+
+ private void assertResponse(Request.Method method, String path, String expectedStartString, String expectedContentType, int expectedStatusCode) throws IOException {
+ Response response = container.handleRequest(new Request("http://localhost:8080" + path, new byte[0], method));
+ assertEquals("Status code", expectedStatusCode, response.getStatus());
+ assertEquals(expectedContentType, response.getHeaders().getFirst("Content-Type"));
+ if(expectedStartString != null){
+ assertTrue(response.getBodyAsString().startsWith(expectedStartString));
+ }
+ }
+
+ private String servicesXml() {
+ return "<jdisc version='1.0'>\n" +
+ " <handler id='com.yahoo.search.query.gui.GUIHandler'>\n" +
+ " <binding>http://*/querybuilder/*</binding>\n" +
+ " </handler>\n" +
+ " <http>\n" +
+ " <server id='default' port='8080'/>\n" +
+ " </http>\n" +
+ "</jdisc>";
+ }
+
+} \ No newline at end of file
diff --git a/container-jersey2/pom.xml b/container-jersey2/pom.xml
index c5ed7d872bf..70189e65f00 100644
--- a/container-jersey2/pom.xml
+++ b/container-jersey2/pom.xml
@@ -57,26 +57,6 @@
<build>
<plugins>
<plugin>
- <groupId>net.alchim31.maven</groupId>
- <artifactId>scala-maven-plugin</artifactId>
- <executions>
- <execution>
- <id>compile</id>
- <goals>
- <goal>compile</goal>
- </goals>
- <phase>compile</phase>
- </execution>
- <execution>
- <id>test-compile</id>
- <goals>
- <goal>testCompile</goal>
- </goals>
- <phase>test-compile</phase>
- </execution>
- </executions>
- </plugin>
- <plugin>
<groupId>com.yahoo.vespa</groupId>
<artifactId>bundle-plugin</artifactId>
<extensions>true</extensions>
diff --git a/container-jersey2/src/main/java/com/yahoo/container/jaxrs/annotation/Component.java b/container-jersey2/src/main/java/com/yahoo/container/jaxrs/annotation/Component.java
index 9dc86c89b3d..da8f35eaa00 100644
--- a/container-jersey2/src/main/java/com/yahoo/container/jaxrs/annotation/Component.java
+++ b/container-jersey2/src/main/java/com/yahoo/container/jaxrs/annotation/Component.java
@@ -8,7 +8,7 @@ import java.lang.annotation.Target;
/**
* Annotation for injecting jdisc container components into jaxrs resources and providers
- * @author tonytv
+ * @author Tony Vaagenes
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
diff --git a/container-jersey2/src/main/java/com/yahoo/container/jaxrs/annotation/package-info.java b/container-jersey2/src/main/java/com/yahoo/container/jaxrs/annotation/package-info.java
index 2e02ae56810..8628d52bbb8 100644
--- a/container-jersey2/src/main/java/com/yahoo/container/jaxrs/annotation/package-info.java
+++ b/container-jersey2/src/main/java/com/yahoo/container/jaxrs/annotation/package-info.java
@@ -1,6 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
@PublicApi
@ExportPackage
diff --git a/container-jersey2/src/main/java/com/yahoo/container/servlet/jersey/util/ResourceConfigUtil.java b/container-jersey2/src/main/java/com/yahoo/container/servlet/jersey/util/ResourceConfigUtil.java
index 28def86da27..fdfcf6565cd 100644
--- a/container-jersey2/src/main/java/com/yahoo/container/servlet/jersey/util/ResourceConfigUtil.java
+++ b/container-jersey2/src/main/java/com/yahoo/container/servlet/jersey/util/ResourceConfigUtil.java
@@ -4,7 +4,7 @@ package com.yahoo.container.servlet.jersey.util;
import org.glassfish.jersey.server.ResourceConfig;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ResourceConfigUtil {
/**
diff --git a/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/AbstractResource.java b/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/AbstractResource.java
index 6a1761ac669..dfa3354032a 100644
--- a/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/AbstractResource.java
+++ b/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/AbstractResource.java
@@ -4,7 +4,7 @@ package com.yahoo.container.servlet.jersey.classvisitor;
import javax.ws.rs.Path;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
@Path("ignored")
public abstract class AbstractResource {
diff --git a/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/DummyAnnotation.java b/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/DummyAnnotation.java
index 80131e3b092..ca6e20aba4d 100644
--- a/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/DummyAnnotation.java
+++ b/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/DummyAnnotation.java
@@ -5,7 +5,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface DummyAnnotation {}
diff --git a/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/InnerClass.java b/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/InnerClass.java
index c3eaf0bc04b..048ac4cdd9b 100644
--- a/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/InnerClass.java
+++ b/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/InnerClass.java
@@ -4,7 +4,7 @@ package com.yahoo.container.servlet.jersey.classvisitor;
import javax.ws.rs.Path;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class InnerClass {
@Path("ignored")
diff --git a/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/InterfaceResource.java b/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/InterfaceResource.java
index 8d56a1b90cf..d013028db19 100644
--- a/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/InterfaceResource.java
+++ b/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/InterfaceResource.java
@@ -4,7 +4,7 @@ package com.yahoo.container.servlet.jersey.classvisitor;
import javax.ws.rs.Path;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
@Path("ignored")
public interface InterfaceResource {
diff --git a/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/NestedClass.java b/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/NestedClass.java
index c0e87327020..11ff6364074 100644
--- a/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/NestedClass.java
+++ b/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/NestedClass.java
@@ -4,7 +4,7 @@ package com.yahoo.container.servlet.jersey.classvisitor;
import javax.ws.rs.Path;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class NestedClass {
@Path("ignored")
diff --git a/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/NonPublicNestedClass.java b/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/NonPublicNestedClass.java
index f2e69d01d62..8e4499fe6dc 100644
--- a/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/NonPublicNestedClass.java
+++ b/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/NonPublicNestedClass.java
@@ -4,7 +4,7 @@ package com.yahoo.container.servlet.jersey.classvisitor;
import javax.ws.rs.Path;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class NonPublicNestedClass {
@Path("ignored")
diff --git a/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/Provider.java b/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/Provider.java
index 0a81848ca94..c2c605db22c 100644
--- a/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/Provider.java
+++ b/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/Provider.java
@@ -2,7 +2,7 @@
package com.yahoo.container.servlet.jersey.classvisitor;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
@javax.ws.rs.ext.Provider
public class Provider {
diff --git a/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/Resource.java b/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/Resource.java
index 7459124c336..edfa2449fc3 100644
--- a/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/Resource.java
+++ b/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/Resource.java
@@ -4,7 +4,7 @@ package com.yahoo.container.servlet.jersey.classvisitor;
import javax.ws.rs.Path;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
@Path("ignored")
public class Resource {
diff --git a/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/ResourceOrProviderClassVisitorTest.java b/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/ResourceOrProviderClassVisitorTest.java
new file mode 100644
index 00000000000..394f1b858b2
--- /dev/null
+++ b/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/ResourceOrProviderClassVisitorTest.java
@@ -0,0 +1,78 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.servlet.jersey.classvisitor;
+
+import com.yahoo.container.servlet.jersey.ResourceOrProviderClassVisitor;
+import org.junit.Test;
+import org.objectweb.asm.ClassReader;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+public class ResourceOrProviderClassVisitorTest {
+ @Test
+ public void resource_is_detected() throws Exception {
+ assert_is_accepted(com.yahoo.container.servlet.jersey.classvisitor.Resource.class);
+ }
+
+ @Test
+ public void provider_is_detected() throws Exception {
+ assert_is_accepted(com.yahoo.container.servlet.jersey.classvisitor.Provider.class);
+ }
+
+ @Test
+ public void inner_class_is_ignored() throws Exception {
+ assert_is_ignored(com.yahoo.container.servlet.jersey.classvisitor.InnerClass.Inner.class);
+ }
+
+ @Test
+ public void nested_public_class_is_detected() throws Exception {
+ assert_is_accepted(com.yahoo.container.servlet.jersey.classvisitor.NestedClass.Nested.class);
+ }
+
+ @Test
+ public void nested_non_public_class_is_ignored() throws Exception {
+ assert_is_ignored(com.yahoo.container.servlet.jersey.classvisitor.NonPublicNestedClass.Nested.class);
+ }
+
+ @Test
+ public void resource_with_multiple_annotations_is_detected() throws Exception {
+ assert_is_accepted(com.yahoo.container.servlet.jersey.classvisitor.ResourceWithMultipleAnnotations.class);
+ }
+
+ @Test
+ public void interface_is_ignored() throws Exception {
+ assert_is_ignored(com.yahoo.container.servlet.jersey.classvisitor.InterfaceResource.class);
+ }
+
+ @Test
+ public void abstract_class_is_ignored() throws Exception {
+ assert_is_ignored(com.yahoo.container.servlet.jersey.classvisitor.AbstractResource.class);
+ }
+
+ @Test
+ public void className_is_equal_to_getName() throws Exception {
+ assertThat(analyzeClass(com.yahoo.container.servlet.jersey.classvisitor.Resource.class).getClassName(), is(com.yahoo.container.servlet.jersey.classvisitor.Resource.class.getName()));
+ }
+
+ public void assert_is_accepted(Class<?> clazz) throws Exception {
+ assertTrue(className(clazz) + " was not accepted",
+ analyzeClass(clazz).isJerseyClass());
+ }
+
+ public void assert_is_ignored(Class<?> clazz) throws Exception {
+ assertFalse(className(clazz) + " was not ignored",
+ analyzeClass(clazz).isJerseyClass());
+ }
+
+ public ResourceOrProviderClassVisitor analyzeClass(Class<?> clazz) throws Exception {
+ return ResourceOrProviderClassVisitor.visit(new ClassReader(className(clazz)));
+ }
+
+ public String className(Class<?> clazz) {
+ return clazz.getName();
+ }
+}
+
+
diff --git a/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/ResourceOrProviderClassVisitorTest.scala b/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/ResourceOrProviderClassVisitorTest.scala
deleted file mode 100644
index f20c5e02e62..00000000000
--- a/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/ResourceOrProviderClassVisitorTest.scala
+++ /dev/null
@@ -1,75 +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.container.servlet.jersey.classvisitor
-
-import com.yahoo.container.servlet.jersey.{ResourceOrProviderClassVisitor, classvisitor}
-import org.junit.{Assert, Test}
-import org.objectweb.asm.ClassReader
-
-import Assert.assertThat
-import org.hamcrest.CoreMatchers.is
-
-import scala.reflect.ClassTag
-
-class ResourceOrProviderClassVisitorTest {
- @Test
- def resource_is_detected() {
- assert_is_accepted[classvisitor.Resource]
- }
-
- @Test
- def provider_is_detected() {
- assert_is_accepted[classvisitor.Provider]
- }
-
- @Test
- def inner_class_is_ignored() {
- assert_is_ignored[classvisitor.InnerClass#Inner]
- }
-
- @Test
- def nested_public_class_is_detected() {
- assert_is_accepted[classvisitor.NestedClass.Nested]
- }
-
- @Test
- def nested_non_public_class_is_ignored() {
- assert_is_ignored[classvisitor.NonPublicNestedClass.Nested]
- }
-
- @Test
- def resource_with_multiple_annotations_is_detected() {
- assert_is_accepted[classvisitor.ResourceWithMultipleAnnotations]
- }
-
- def interface_is_ignored() {
- assert_is_ignored[classvisitor.InterfaceResource]
- }
-
- @Test
- def abstract_class_is_ignored() {
- assert_is_ignored[classvisitor.AbstractResource]
- }
-
- @Test
- def className_is_equal_to_getName() {
- assertThat(analyzeClass[classvisitor.Resource].getClassName, is(classOf[classvisitor.Resource].getName))
- }
-
- def assert_is_accepted[T: ClassTag] {
- Assert.assertTrue(className[T] + " was not accepted",
- analyzeClass[T].isJerseyClass)
- }
-
- def assert_is_ignored[T: ClassTag] {
- Assert.assertFalse(className[T] + " was not ignored",
- analyzeClass[T].isJerseyClass)
- }
-
- def analyzeClass[T: ClassTag] = {
- ResourceOrProviderClassVisitor.visit(new ClassReader(className[T]))
- }
-
- def className[T: ClassTag] = implicitly[ClassTag[T]].runtimeClass.getName
-}
-
-
diff --git a/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/ResourceWithMultipleAnnotations.java b/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/ResourceWithMultipleAnnotations.java
index 1e70dbb0f04..7047422736e 100644
--- a/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/ResourceWithMultipleAnnotations.java
+++ b/container-jersey2/src/test/java/com/yahoo/container/servlet/jersey/classvisitor/ResourceWithMultipleAnnotations.java
@@ -4,7 +4,7 @@ package com.yahoo.container.servlet.jersey.classvisitor;
import javax.ws.rs.Path;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
@Path("ignored")
@DummyAnnotation
diff --git a/container-messagebus/src/main/java/com/yahoo/container/jdisc/messagebus/MbusClientProvider.java b/container-messagebus/src/main/java/com/yahoo/container/jdisc/messagebus/MbusClientProvider.java
index cd267da659a..9e76f1bf651 100644
--- a/container-messagebus/src/main/java/com/yahoo/container/jdisc/messagebus/MbusClientProvider.java
+++ b/container-messagebus/src/main/java/com/yahoo/container/jdisc/messagebus/MbusClientProvider.java
@@ -13,7 +13,7 @@ import com.yahoo.messagebus.shared.SharedIntermediateSession;
import com.yahoo.messagebus.shared.SharedSourceSession;
/**
- * @author tonytv
+ * @author Tony Vaagenes
* @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
*/
public class MbusClientProvider implements Provider<MbusClient> {
diff --git a/container-messagebus/src/main/java/com/yahoo/container/jdisc/messagebus/MbusServerProvider.java b/container-messagebus/src/main/java/com/yahoo/container/jdisc/messagebus/MbusServerProvider.java
index 60df52f703e..58f0df6ddcf 100644
--- a/container-messagebus/src/main/java/com/yahoo/container/jdisc/messagebus/MbusServerProvider.java
+++ b/container-messagebus/src/main/java/com/yahoo/container/jdisc/messagebus/MbusServerProvider.java
@@ -15,7 +15,7 @@ import java.util.logging.Logger;
/**
* TODO: Javadoc
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class MbusServerProvider implements Provider<MbusServer> {
private static final Logger log = Logger.getLogger(MbusServerProvider.class.getName());
diff --git a/container-search-gui/.gitignore b/container-search-gui/.gitignore
new file mode 100644
index 00000000000..af784e084e1
--- /dev/null
+++ b/container-search-gui/.gitignore
@@ -0,0 +1,2 @@
+/pom.xml.build
+/target \ No newline at end of file
diff --git a/container-search-gui/CMakeLists.txt b/container-search-gui/CMakeLists.txt
new file mode 100644
index 00000000000..95476154443
--- /dev/null
+++ b/container-search-gui/CMakeLists.txt
@@ -0,0 +1,2 @@
+# Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+install_fat_java_artifact(container-search-gui) \ No newline at end of file
diff --git a/container-search-gui/OWNERS b/container-search-gui/OWNERS
new file mode 100644
index 00000000000..f889c2c48d0
--- /dev/null
+++ b/container-search-gui/OWNERS
@@ -0,0 +1 @@
+henrhoi \ No newline at end of file
diff --git a/container-search-gui/README b/container-search-gui/README
new file mode 100644
index 00000000000..af1a8818cbb
--- /dev/null
+++ b/container-search-gui/README
@@ -0,0 +1 @@
+Container layer providing gui for search functionality.
diff --git a/container-search-gui/pom.xml b/container-search-gui/pom.xml
new file mode 100644
index 00000000000..12f425f8b4b
--- /dev/null
+++ b/container-search-gui/pom.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0"?>
+<!-- Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+ http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>parent</artifactId>
+ <version>6-SNAPSHOT</version>
+ <relativePath>../parent/pom.xml</relativePath>
+ </parent>
+ <artifactId>container-search-gui</artifactId>
+ <packaging>container-plugin</packaging>
+ <version>6-SNAPSHOT</version>
+ <dependencies>
+ <dependency>
+ <groupId>com.google.inject</groupId>
+ <artifactId>guice</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>yolean</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>jdisc_http_service</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>jdisc_core</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>container-core</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>container-search-and-docproc</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>configdefinitions</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>com.googlecode.maven-download-plugin</groupId>
+ <artifactId>download-maven-plugin</artifactId>
+ <version>1.3.0</version>
+ <executions>
+ <execution>
+ <phase>prepare-package</phase>
+ <goals>
+ <goal>wget</goal>
+ </goals>
+ <configuration>
+ <url>https://docs.vespa.ai/documentation/reference/search-api-reference.html</url>
+ <outputFileName>search-api-reference.html</outputFileName>
+ <outputDirectory>${project.build.outputDirectory}/gui/_includes/</outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/container-search-gui/src/main/java/com/yahoo/search/query/gui/GUIHandler.java b/container-search-gui/src/main/java/com/yahoo/search/query/gui/GUIHandler.java
new file mode 100644
index 00000000000..45a616ce473
--- /dev/null
+++ b/container-search-gui/src/main/java/com/yahoo/search/query/gui/GUIHandler.java
@@ -0,0 +1,210 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.query.gui;
+
+import com.google.inject.Inject;
+
+import com.yahoo.container.QrSearchersConfig;
+import com.yahoo.container.jdisc.HttpRequest;
+import com.yahoo.container.jdisc.HttpResponse;
+import com.yahoo.container.jdisc.LoggingRequestHandler;
+import com.yahoo.prelude.IndexModel;
+import com.yahoo.prelude.fastsearch.CacheControl;
+import com.yahoo.prelude.querytransform.RecallSearcher;
+import com.yahoo.search.Query;
+import com.yahoo.search.query.Model;
+import com.yahoo.search.query.Presentation;
+import com.yahoo.search.query.Ranking;
+import com.yahoo.search.query.ranking.Diversity;
+import com.yahoo.search.query.ranking.MatchPhase;
+import com.yahoo.search.query.restapi.ErrorResponse;
+import com.yahoo.search.yql.MinimalQueryInserter;
+import com.yahoo.vespa.config.search.RankProfilesConfig;
+import com.yahoo.search.config.IndexInfoConfig;
+import com.yahoo.yolean.Exceptions;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.logging.Level;
+
+
+/**
+ * Takes requests on /querybuilder
+ *
+ * @author Henrik Høiness
+ */
+
+public class GUIHandler extends LoggingRequestHandler {
+
+ private final IndexModel indexModel;
+ private final RankProfilesConfig rankProfilesConfig;
+
+ @Inject
+ public GUIHandler(Context parentContext, IndexInfoConfig indexInfo, QrSearchersConfig clusters, RankProfilesConfig rankProfilesConfig) {
+ super(parentContext);
+ this.indexModel = new IndexModel(indexInfo, clusters);
+ this.rankProfilesConfig = rankProfilesConfig;
+
+ }
+
+ @Override
+ public HttpResponse handle(HttpRequest request) {
+ try {
+ switch (request.getMethod()) {
+ case GET: return handleGET(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(Level.WARNING, "Unexpected error handling '" + request.getUri() + "'", e);
+ return ErrorResponse.internalServerError(Exceptions.toMessageString(e));
+ }
+ }
+
+ private HttpResponse handleGET(HttpRequest request) {
+ com.yahoo.restapi.Path path = new com.yahoo.restapi.Path(request.getUri().getPath());
+ if (path.matches("/querybuilder/")) {
+ return new FileResponse("_includes/index.html", null, null);
+ }
+ if (!path.matches("/querybuilder/{*}") ) {
+ return ErrorResponse.notFoundError("Nothing at path:" + path);
+ }
+ String filepath = path.getRest();
+ if (!isValidPath(filepath) && !filepath.equals("config.json")){
+ return ErrorResponse.notFoundError("Nothing at path:" + filepath);
+ }
+ return new FileResponse(filepath, indexModel, rankProfilesConfig);
+ }
+
+ private static boolean isValidPath(String path) {
+ InputStream in = GUIHandler.class.getClassLoader().getResourceAsStream("gui/"+path);
+ boolean isValid = (in != null);
+ if(isValid){
+ try { in.close(); } catch (IOException e) {/* Problem with closing inputstream */}
+ }
+
+ return isValid;
+ }
+
+ private static class FileResponse extends HttpResponse {
+
+ private final String path;
+ private final IndexModel indexModel;
+ private final RankProfilesConfig rankProfilesConfig;
+
+ public FileResponse(String relativePath, IndexModel indexModel, RankProfilesConfig rankProfilesConfig) {
+ super(200);
+ this.path = relativePath;
+ this.indexModel = indexModel;
+ this.rankProfilesConfig = rankProfilesConfig;
+
+ }
+
+ @Override
+ public void render(OutputStream out) throws IOException {
+ InputStream is;
+ if (this.path.equals("config.json")){
+ String json = "{}";
+ try { json = getGUIConfig(); } catch (JSONException e) { /*Something happened while parsing JSON */ }
+ is = new ByteArrayInputStream(json.getBytes());
+ } else{
+ is = GUIHandler.class.getClassLoader().getResourceAsStream("gui/"+this.path);
+ }
+ byte[] buf = new byte[1024];
+ int numRead;
+ while ( (numRead = is.read(buf) ) >= 0) {
+ out.write(buf, 0, numRead);
+ }
+ }
+
+ @Override
+ public String getContentType() {
+ if (path.endsWith(".css")) {
+ return "text/css";
+ } else if (path.endsWith(".js")) {
+ return "application/javascript";
+ } else if (path.endsWith(".html")) {
+ return "text/html";
+ } else if (path.endsWith(".php")) {
+ return "text/php";
+ } else if (path.endsWith(".svg")) {
+ return "image/svg+xml";
+ } else if (path.endsWith(".eot")) {
+ return "application/vnd.ms-fontobject";
+ } else if (path.endsWith(".ttf")) {
+ return "font/ttf";
+ } else if (path.endsWith(".woff")) {
+ return "font/woff";
+ } else if (path.endsWith(".woff2")) {
+ return "font/woff2";
+ } else if (path.endsWith(".otf")) {
+ return "font/otf";
+ } else if (path.endsWith(".png")) {
+ return "image/png";
+ } else if (path.endsWith(".xml")) {
+ return "application/xml";
+ } else if (path.endsWith(".ico")) {
+ return "image/x-icon";
+ } else if (path.endsWith(".json")) {
+ return "application/json";
+ } else if (path.endsWith(".ttf")) {
+ return "font/ttf";
+ }
+ return "text/html";
+ }
+
+ private String getGUIConfig() throws JSONException {
+ JSONObject json = new JSONObject();
+ json.put("ranking_properties", Arrays.asList("propertyname"));
+ json.put("ranking_features", Arrays.asList("featurename"));
+
+ List<String> sources = new ArrayList<>();
+
+ try {
+ sources = new ArrayList<>(indexModel.getMasterClusters().keySet());
+ } catch (NullPointerException ex){ /* clusters are not set */ }
+ json.put("model_sources", sources);
+
+ List<String> rankProfiles = new ArrayList<>();
+ try {
+ rankProfilesConfig.rankprofile().forEach(rankProfile -> rankProfiles.add(rankProfile.name()));
+ } catch (NullPointerException ex){ /* rankprofiles are not set*/ }
+ json.put("ranking_profile", rankProfiles);
+
+
+ // Creating map from parent to children for GUI: parameter --> child-parameters
+ HashMap<String, List<String>> childMap = new HashMap<>();
+ childMap.put(Model.MODEL, Arrays.asList(Model.DEFAULT_INDEX, Model.ENCODING, Model.LANGUAGE, Model.QUERY_STRING, Model.RESTRICT, Model.SEARCH_PATH, Model.SOURCES, Model.TYPE));
+ childMap.put(Ranking.RANKING, Arrays.asList(Ranking.LOCATION, Ranking.FEATURES, Ranking.LIST_FEATURES, Ranking.PROFILE, Ranking.PROPERTIES, Ranking.SORTING, Ranking.FRESHNESS, Ranking.QUERYCACHE, Ranking.MATCH_PHASE));
+ childMap.put(Ranking.RANKING +"."+ Ranking.MATCH_PHASE, Arrays.asList(MatchPhase.MAX_HITS, MatchPhase.ATTRIBUTE, MatchPhase.ASCENDING, Ranking.DIVERSITY));
+ childMap.put(Ranking.RANKING +"."+ Ranking.MATCH_PHASE +"."+Ranking.DIVERSITY, Arrays.asList(Diversity.ATTRIBUTE, Diversity.MINGROUPS));
+ childMap.put(Presentation.PRESENTATION, Arrays.asList(Presentation.BOLDING, Presentation.FORMAT, Presentation.SUMMARY, "template", Presentation.TIMING ));
+ childMap.put("trace", Arrays.asList("timestamps"));
+ childMap.put("tracelevel", Arrays.asList("rules"));
+ childMap.put("metrics", Arrays.asList("ignore"));
+ childMap.put("collapse", Arrays.asList("summary"));
+ childMap.put("pos", Arrays.asList("ll", "radius", "bb", "attribute"));
+ childMap.put("streaming", Arrays.asList("userid", "groupname", "selection", "priority", "maxbucketspervisitor"));
+ childMap.put("rules", Arrays.asList("off", "rulebase"));
+ json.put("childMap", childMap);
+
+ List<String> levelZeroParameters = Arrays.asList(MinimalQueryInserter.YQL.toString(), Query.HITS.toString(), Query.OFFSET.toString(),
+ "queryProfile", Query.NO_CACHE.toString(), Query.GROUPING_SESSION_CACHE.toString(),
+ Query.SEARCH_CHAIN.toString(), Query.TIMEOUT.toString(), "trace", "tracelevel",
+ Query.TRACE_LEVEL.toString(), Model.MODEL, Ranking.RANKING, "collapse", "collapsesize","collapsefield",
+ Presentation.PRESENTATION, "pos", "streaming", "rules", RecallSearcher.recallName.toString(), "user",
+ CacheControl.nocachewrite.toString(), "metrics", "");
+ json.put("levelZeroParameters", levelZeroParameters);
+
+ return json.toString();
+ }
+ }
+} \ No newline at end of file
diff --git a/container-search-gui/src/main/java/com/yahoo/search/query/restapi/ErrorResponse.java b/container-search-gui/src/main/java/com/yahoo/search/query/restapi/ErrorResponse.java
new file mode 100644
index 00000000000..abb01388c1b
--- /dev/null
+++ b/container-search-gui/src/main/java/com/yahoo/search/query/restapi/ErrorResponse.java
@@ -0,0 +1,80 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.query.restapi;
+
+import com.yahoo.container.jdisc.HttpResponse;
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.JsonFormat;
+import com.yahoo.slime.Slime;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+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;
+
+/**
+ * Error responses with JSON bodies
+ *
+ * @author bratseth
+ */
+public class ErrorResponse extends HttpResponse {
+
+ private final Slime slime = new Slime();
+ private final String message;
+
+ public enum ErrorCode {
+ FORBIDDEN,
+ UNAUTHORIZED,
+ NOT_FOUND,
+ BAD_REQUEST,
+ METHOD_NOT_ALLOWED,
+ INTERNAL_SERVER_ERROR
+ }
+
+ private ErrorResponse(int code, ErrorCode errorCode, String message) {
+ super(code);
+ this.message = message;
+ Cursor root = slime.setObject();
+ root.setString("error-code", errorCode.name());
+ root.setString("message", message);
+ }
+
+ public String message() { return message; }
+
+ public static ErrorResponse notFoundError(String message) {
+ return new ErrorResponse(NOT_FOUND, ErrorCode.NOT_FOUND, message);
+ }
+
+ public static ErrorResponse internalServerError(String message) {
+ return new ErrorResponse(INTERNAL_SERVER_ERROR, ErrorCode.INTERNAL_SERVER_ERROR, message);
+ }
+
+ public static ErrorResponse badRequest(String message) {
+ return new ErrorResponse(BAD_REQUEST, ErrorCode.BAD_REQUEST, message);
+ }
+
+ public static ErrorResponse methodNotAllowed(String message) {
+ return new ErrorResponse(METHOD_NOT_ALLOWED, ErrorCode.METHOD_NOT_ALLOWED, message);
+ }
+
+ public static ErrorResponse unauthorized(String message) {
+ return new ErrorResponse(UNAUTHORIZED, ErrorCode.UNAUTHORIZED, message);
+ }
+
+ public static ErrorResponse forbidden(String message) {
+ return new ErrorResponse(FORBIDDEN, ErrorCode.FORBIDDEN, message);
+ }
+
+ @Override
+ public void render(OutputStream stream) throws IOException {
+ new JsonFormat(true).encode(stream, slime);
+ }
+
+ @Override
+ public String getContentType() { return "application/json"; }
+
+}
diff --git a/container-search-gui/src/main/resources/gui/_includes/css/agency.css b/container-search-gui/src/main/resources/gui/_includes/css/agency.css
new file mode 100644
index 00000000000..8a243ee5853
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/_includes/css/agency.css
@@ -0,0 +1,876 @@
+/*!
+ * Start Bootstrap - Agency Bootstrap Theme (http://startbootstrap.com)
+ * Code licensed under the Apache License v2.0.
+ * For details, see http://www.apache.org/licenses/LICENSE-2.0.
+ */
+ :root{
+ --primary: #188fff;
+ --secondary: #003abc;
+ --secondary-dark: #333;
+ --muted: #777;
+
+ --fontprimary: HelveticaNeue,Helvetica,Arial,sans-serif;
+ --fontsecondary: HelveticaNeue-Thin,Helvetica,Arial,sans-serif;
+ };
+
+body {
+ overflow-x: hidden;
+ font-family: HelveticaNeue,Helvetica,Arial,sans-serif;
+ color: var(--secondary-dark);
+}
+
+.text-muted {
+ color: var(--muted);
+}
+
+.text-primary {
+ color: HelveticaNeue,Helvetica,Arial,sans-serif
+}
+
+p {
+ font-size: 14px;
+ line-height: 1.75;
+}
+
+p.large {
+ font-size: 16px;
+}
+
+a,
+a:hover,
+a:focus,
+a:active,
+a.active {
+ outline: 0;
+}
+
+a {
+ color: HelveticaNeue,Helvetica,Arial,sans-serif
+}
+
+a:hover,
+a:focus,
+a:active,
+a.active {
+ color: HelveticaNeue-Thin,Helvetica,Arial,sans-serif
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ text-transform: uppercase;
+ font-family: HelveticaNeue,Helvetica,Arial,sans-serif;
+ font-weight: 700;
+}
+
+.img-centered {
+ margin: 0 auto;
+}
+
+.bg-light-gray {
+ background-color: #f7f7f7;
+}
+
+.bg-darkest-gray {
+ background-color: #222;
+}
+
+.btn-primary {
+ border-color: HelveticaNeue,Helvetica,Arial,sans-serif
+ text-transform: uppercase;
+ font-family: HelveticaNeue,Helvetica,Arial,sans-serif;
+ font-weight: 700;
+ color: #fff;
+ background-color: HelveticaNeue,Helvetica,Arial,sans-serif
+}
+
+.btn-primary:hover,
+.btn-primary:focus,
+.btn-primary:active,
+.btn-primary.active,
+.open .dropdown-toggle.btn-primary {
+ border-color: HelveticaNeue,Helvetica,Arial,sans-serif
+ color: #fff;
+ background-color: HelveticaNeue-Thin,Helvetica,Arial,sans-serif
+}
+
+.btn-primary:active,
+.btn-primary.active,
+.open .dropdown-toggle.btn-primary {
+ background-image: none;
+}
+
+.btn-primary.disabled,
+.btn-primary[disabled],
+fieldset[disabled] .btn-primary,
+.btn-primary.disabled:hover,
+.btn-primary[disabled]:hover,
+fieldset[disabled] .btn-primary:hover,
+.btn-primary.disabled:focus,
+.btn-primary[disabled]:focus,
+fieldset[disabled] .btn-primary:focus,
+.btn-primary.disabled:active,
+.btn-primary[disabled]:active,
+fieldset[disabled] .btn-primary:active,
+.btn-primary.disabled.active,
+.btn-primary[disabled].active,
+fieldset[disabled] .btn-primary.active {
+ border-color: HelveticaNeue,Helvetica,Arial,sans-serif
+ background-color: HelveticaNeue,Helvetica,Arial,sans-serif
+}
+
+.btn-primary .badge {
+ color: HelveticaNeue,Helvetica,Arial,sans-serif
+ background-color: #fff;
+}
+
+.btn-xl {
+ padding: 20px 40px;
+ border-color: HelveticaNeue,Helvetica,Arial,sans-serif
+ border-radius: 3px;
+ text-transform: uppercase;
+ font-family: HelveticaNeue,Helvetica,Arial,sans-serif;
+ font-size: 18px;
+ font-weight: 700;
+ color: #fff;
+ background-color: HelveticaNeue,Helvetica,Arial,sans-serif
+}
+
+.btn-xl:hover,
+.btn-xl:focus,
+.btn-xl:active,
+.btn-xl.active,
+.open .dropdown-toggle.btn-xl {
+ border-color: HelveticaNeue,Helvetica,Arial,sans-serif
+ color: #fff;
+ background-color: HelveticaNeue-Thin,Helvetica,Arial,sans-serif
+}
+
+.btn-xl:active,
+.btn-xl.active,
+.open .dropdown-toggle.btn-xl {
+ background-image: none;
+}
+
+.btn-xl.disabled,
+.btn-xl[disabled],
+fieldset[disabled] .btn-xl,
+.btn-xl.disabled:hover,
+.btn-xl[disabled]:hover,
+fieldset[disabled] .btn-xl:hover,
+.btn-xl.disabled:focus,
+.btn-xl[disabled]:focus,
+fieldset[disabled] .btn-xl:focus,
+.btn-xl.disabled:active,
+.btn-xl[disabled]:active,
+fieldset[disabled] .btn-xl:active,
+.btn-xl.disabled.active,
+.btn-xl[disabled].active,
+fieldset[disabled] .btn-xl.active {
+ border-color: HelveticaNeue,Helvetica,Arial,sans-serif
+ background-color: HelveticaNeue,Helvetica,Arial,sans-serif
+}
+
+.btn-xl .badge {
+ color: HelveticaNeue,Helvetica,Arial,sans-serif
+ background-color: #fff;
+}
+
+.navbar-default {
+ border-color: transparent;
+ background-color: #222;
+}
+
+.navbar-default .navbar-brand {
+ font-family: "Kaushan Script","Helvetica Neue",Helvetica,Arial,cursive;
+ color: HelveticaNeue,Helvetica,Arial,sans-serif
+}
+
+.navbar-default .navbar-brand:hover,
+.navbar-default .navbar-brand:focus,
+.navbar-default .navbar-brand:active,
+.navbar-default .navbar-brand.active {
+ color: HelveticaNeue-Thin,Helvetica,Arial,sans-serif
+}
+
+.navbar-default .navbar-collapse {
+ border-color: rgba(255,255,255,.02);
+}
+
+.navbar-default .navbar-toggle {
+ border-color: HelveticaNeue,Helvetica,Arial,sans-serif
+ background-color: HelveticaNeue,Helvetica,Arial,sans-serif
+}
+
+.navbar-default .navbar-toggle .icon-bar {
+ background-color: #fff;
+}
+
+.navbar-default .navbar-toggle:hover,
+.navbar-default .navbar-toggle:focus {
+ background-color: HelveticaNeue,Helvetica,Arial,sans-serif
+}
+
+.navbar-default .nav li a {
+ text-transform: uppercase;
+ font-family: HelveticaNeue,Helvetica,Arial,sans-serif;
+ font-weight: 400;
+ letter-spacing: 1px;
+ color: #fff;
+ -webkit-transition: all ease .35s;
+ -moz-transition: all ease .35s;
+ transition: all ease .35s;
+}
+
+.navbar-default .nav li a:hover,
+.navbar-default .nav li a:focus {
+ outline: 0;
+ color: HelveticaNeue,Helvetica,Arial,sans-serif
+}
+
+.navbar-default .navbar-nav>.active>a {
+ border-radius: 0;
+ color: #fff;
+ background-color: HelveticaNeue,Helvetica,Arial,sans-serif
+}
+
+.navbar-default .navbar-nav>.active>a:hover,
+.navbar-default .navbar-nav>.active>a:focus {
+ color: #fff;
+ background-color: HelveticaNeue-Thin,Helvetica,Arial,sans-serif
+}
+
+@media(min-width:768px) {
+ .navbar-default {
+ padding: 25px 0;
+ border: 0;
+ background-color: transparent;
+ -webkit-transition: padding .3s;
+ -moz-transition: padding .3s;
+ transition: padding .3s;
+ }
+
+ .navbar-default .navbar-brand {
+ font-size: 2em;
+ -webkit-transition: all .3s;
+ -moz-transition: all .3s;
+ transition: all .3s;
+ }
+
+ .navbar-default .navbar-nav>.active>a {
+ border-radius: 3px;
+ }
+
+ .navbar-default.navbar-shrink {
+ padding: 10px 0;
+ background-color: #222;
+ }
+
+ .navbar-default.navbar-shrink .navbar-brand {
+ font-size: 1.5em;
+ }
+}
+
+header {
+ text-align: center;
+ color: #fff;
+ background-attachment: scroll;
+ background-image: url('../img/header-bg.png');
+ background-position: center center;
+ background-repeat: none;
+ -webkit-background-size: cover;
+ -moz-background-size: cover;
+ background-size: cover;
+ -o-background-size: cover;
+}
+
+header .intro-text {
+ padding-top: 100px;
+ padding-bottom: 50px;
+}
+
+header .intro-text .intro-lead-in {
+ margin-bottom: 25px;
+ font-family: HelveticaNeue-Thin,Helvetica,Arial,sans-serif;
+ font-size: 22px;
+ font-style: italic;
+ line-height: 22px;
+}
+
+header .intro-text .intro-heading {
+ margin-bottom: 25px;
+ text-transform: uppercase;
+ font-family: HelveticaNeue,Helvetica,Arial,sans-serif;
+ font-size: 50px;
+ font-weight: 700;
+ line-height: 50px;
+}
+
+@media(min-width:768px) {
+ header .intro-text {
+ padding-top: 300px;
+ padding-bottom: 200px;
+ }
+
+ header .intro-text .intro-lead-in {
+ margin-bottom: 25px;
+ font-family: HelveticaNeue-Thin,Helvetica,Arial,sans-serif;
+ font-size: 40px;
+ font-style: italic;
+ line-height: 40px;
+ }
+
+ header .intro-text .intro-heading {
+ margin-bottom: 50px;
+ text-transform: uppercase;
+ font-family: HelveticaNeue,Helvetica,Arial,sans-serif;
+ font-size: 75px;
+ font-weight: 700;
+ line-height: 75px;
+ }
+}
+
+section {
+ padding: 100px 0;
+}
+
+section h2.section-heading {
+ margin-top: 0;
+ margin-bottom: 15px;
+ font-size: 40px;
+}
+
+section h3.section-subheading {
+ margin-bottom: 75px;
+ text-transform: none;
+ font-family: HelveticaNeue-Thin,Helvetica,Arial,sans-serif;
+ font-size: 16px;
+ font-style: italic;
+ font-weight: 400;
+}
+
+@media(min-width:768px) {
+ section {
+ padding: 150px 0;
+ }
+}
+
+.service-heading {
+ margin: 15px 0;
+ text-transform: none;
+}
+
+#portfolio .portfolio-item {
+ right: 0;
+ margin: 0 0 15px;
+}
+
+#portfolio .portfolio-item .portfolio-link {
+ display: block;
+ position: relative;
+ margin: 0 auto;
+ max-width: 400px;
+}
+
+#portfolio .portfolio-item .portfolio-link .portfolio-hover {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ opacity: 0;
+ -webkit-transition: all ease .5s;
+ -moz-transition: all ease .5s;
+ transition: all ease .5s;
+ background: rgba(254,209,54,.9); /* Fallback when no plugin support */
+ background: rgba( var(--primary) | hex_to_rgb | join: ',' }}, .9);
+}
+
+#portfolio .portfolio-item .portfolio-link .portfolio-hover:hover {
+ opacity: 1;
+}
+
+#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content {
+ position: absolute;
+ top: 50%;
+ width: 100%;
+ height: 20px;
+ margin-top: -12px;
+ text-align: center;
+ font-size: 20px;
+ color: #fff;
+}
+
+#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content i {
+ margin-top: -12px;
+}
+
+#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content h3,
+#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content h4 {
+ margin: 0;
+}
+
+#portfolio .portfolio-item .portfolio-caption {
+ margin: 0 auto;
+ padding: 25px;
+ max-width: 400px;
+ text-align: center;
+ background-color: #fff;
+}
+
+#portfolio .portfolio-item .portfolio-caption h4 {
+ margin: 0;
+ text-transform: none;
+}
+
+#portfolio .portfolio-item .portfolio-caption p {
+ margin: 0;
+ font-family: HelveticaNeue-Thin,Helvetica,Arial,sans-serif;
+ font-size: 16px;
+ font-style: italic;
+}
+
+#portfolio * {
+ z-index: 2;
+}
+
+@media(min-width:767px) {
+ #portfolio .portfolio-item {
+ margin: 0 0 30px;
+ }
+}
+
+.timeline {
+ position: relative;
+ padding: 0;
+ list-style: none;
+}
+
+.timeline:before {
+ content: "";
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 40px;
+ width: 2px;
+ margin-left: -1.5px;
+ background-color: #f1f1f1;
+}
+
+.timeline>li {
+ position: relative;
+ margin-bottom: 50px;
+ min-height: 50px;
+}
+
+.timeline>li:before,
+.timeline>li:after {
+ content: " ";
+ display: table;
+}
+
+.timeline>li:after {
+ clear: both;
+}
+
+.timeline>li .timeline-panel {
+ float: right;
+ position: relative;
+ width: 100%;
+ padding: 0 20px 0 100px;
+ text-align: left;
+}
+
+.timeline>li .timeline-panel:before {
+ right: auto;
+ left: -15px;
+ border-right-width: 15px;
+ border-left-width: 0;
+}
+
+.timeline>li .timeline-panel:after {
+ right: auto;
+ left: -14px;
+ border-right-width: 14px;
+ border-left-width: 0;
+}
+
+.timeline>li .timeline-image {
+ z-index: 100;
+ position: absolute;
+ left: 0;
+ width: 80px;
+ height: 80px;
+ margin-left: 0;
+ border: 7px solid #f1f1f1;
+ border-radius: 100%;
+ text-align: center;
+ color: #fff;
+ background-color: HelveticaNeue,Helvetica,Arial,sans-serif
+}
+
+.timeline>li .timeline-image h4 {
+ margin-top: 12px;
+ font-size: 10px;
+ line-height: 14px;
+}
+
+.timeline>li.timeline-inverted>.timeline-panel {
+ float: right;
+ padding: 0 20px 0 100px;
+ text-align: left;
+}
+
+.timeline>li.timeline-inverted>.timeline-panel:before {
+ right: auto;
+ left: -15px;
+ border-right-width: 15px;
+ border-left-width: 0;
+}
+
+.timeline>li.timeline-inverted>.timeline-panel:after {
+ right: auto;
+ left: -14px;
+ border-right-width: 14px;
+ border-left-width: 0;
+}
+
+.timeline>li:last-child {
+ margin-bottom: 0;
+}
+
+.timeline .timeline-heading h4 {
+ margin-top: 0;
+ color: inherit;
+}
+
+.timeline .timeline-heading h4.subheading {
+ text-transform: none;
+}
+
+.timeline .timeline-body>p,
+.timeline .timeline-body>ul {
+ margin-bottom: 0;
+}
+
+@media(min-width:768px) {
+ .timeline:before {
+ left: 50%;
+ }
+
+ .timeline>li {
+ margin-bottom: 100px;
+ min-height: 100px;
+ }
+
+ .timeline>li .timeline-panel {
+ float: left;
+ width: 41%;
+ padding: 0 20px 20px 30px;
+ text-align: right;
+ }
+
+ .timeline>li .timeline-image {
+ left: 50%;
+ width: 100px;
+ height: 100px;
+ margin-left: -50px;
+ }
+
+ .timeline>li .timeline-image h4 {
+ margin-top: 16px;
+ font-size: 13px;
+ line-height: 18px;
+ }
+
+ .timeline>li.timeline-inverted>.timeline-panel {
+ float: right;
+ padding: 0 30px 20px 20px;
+ text-align: left;
+ }
+}
+
+@media(min-width:992px) {
+ .timeline>li {
+ min-height: 150px;
+ }
+
+ .timeline>li .timeline-panel {
+ padding: 0 20px 20px;
+ }
+
+ .timeline>li .timeline-image {
+ width: 150px;
+ height: 150px;
+ margin-left: -75px;
+ }
+
+ .timeline>li .timeline-image h4 {
+ margin-top: 30px;
+ font-size: 18px;
+ line-height: 26px;
+ }
+
+ .timeline>li.timeline-inverted>.timeline-panel {
+ padding: 0 20px 20px;
+ }
+}
+
+@media(min-width:1200px) {
+ .timeline>li {
+ min-height: 170px;
+ }
+
+ .timeline>li .timeline-panel {
+ padding: 0 20px 20px 100px;
+ }
+
+ .timeline>li .timeline-image {
+ width: 170px;
+ height: 170px;
+ margin-left: -85px;
+ }
+
+ .timeline>li .timeline-image h4 {
+ margin-top: 40px;
+ }
+
+ .timeline>li.timeline-inverted>.timeline-panel {
+ padding: 0 100px 20px 20px;
+ }
+}
+
+.team-member {
+ margin-bottom: 50px;
+ text-align: center;
+}
+
+.team-member img {
+ margin: 0 auto;
+ border: 7px solid #fff;
+}
+
+.team-member h4 {
+ margin-top: 25px;
+ margin-bottom: 0;
+ text-transform: none;
+}
+
+.team-member p {
+ margin-top: 0;
+}
+
+aside.clients img {
+ margin: 50px auto;
+}
+
+section#contact {
+ background-color: #222;
+ background-image: url('../img/map-image.png');
+ background-position: center;
+ background-repeat: no-repeat;
+}
+
+section#contact .section-heading {
+ color: #fff;
+}
+
+section#contact .form-group {
+ margin-bottom: 25px;
+}
+
+section#contact .form-group input,
+section#contact .form-group textarea {
+ padding: 20px;
+}
+
+section#contact .form-group input.form-control {
+ height: auto;
+}
+
+section#contact .form-group textarea.form-control {
+ height: 236px;
+}
+
+section#contact .form-control:focus {
+ border-color: HelveticaNeue,Helvetica,Arial,sans-serif
+ box-shadow: none;
+}
+
+section#contact::-webkit-input-placeholder {
+ text-transform: uppercase;
+ font-family: HelveticaNeue,Helvetica,Arial,sans-serif;
+ font-weight: 700;
+ color: #bbb;
+}
+
+section#contact:-moz-placeholder {
+ text-transform: uppercase;
+ font-family: HelveticaNeue,Helvetica,Arial,sans-serif;
+ font-weight: 700;
+ color: #bbb;
+}
+
+section#contact::-moz-placeholder {
+ text-transform: uppercase;
+ font-family: HelveticaNeue,Helvetica,Arial,sans-serif;
+ font-weight: 700;
+ color: #bbb;
+}
+
+section#contact:-ms-input-placeholder {
+ text-transform: uppercase;
+ font-family: HelveticaNeue,Helvetica,Arial,sans-serif;
+ font-weight: 700;
+ color: #bbb;
+}
+
+section#contact .text-danger {
+ color: #e74c3c;
+}
+
+footer {
+ padding: 25px 0;
+ text-align: center;
+}
+
+footer span.copyright {
+ text-transform: uppercase;
+ text-transform: none;
+ font-family: HelveticaNeue,Helvetica,Arial,sans-serif;
+ line-height: 40px;
+}
+
+footer ul.quicklinks {
+ margin-bottom: 0;
+ text-transform: uppercase;
+ text-transform: none;
+ font-family: HelveticaNeue,Helvetica,Arial,sans-serif;
+ line-height: 40px;
+}
+
+ul.social-buttons {
+ margin-bottom: 0;
+}
+
+ul.social-buttons li a {
+ display: block;
+ width: 40px;
+ height: 40px;
+ border-radius: 100%;
+ font-size: 20px;
+ line-height: 40px;
+ outline: 0;
+ color: #fff;
+ background-color: #222;
+ -webkit-transition: all .3s;
+ -moz-transition: all .3s;
+ transition: all .3s;
+}
+
+ul.social-buttons li a:hover,
+ul.social-buttons li a:focus,
+ul.social-buttons li a:active {
+ background-color: HelveticaNeue,Helvetica,Arial,sans-serif
+}
+
+.btn:focus,
+.btn:active,
+.btn.active,
+.btn:active:focus {
+ outline: 0;
+}
+
+.portfolio-modal .modal-content {
+ padding: 100px 0;
+ min-height: 100%;
+ border: 0;
+ border-radius: 0;
+ text-align: center;
+ background-clip: border-box;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+
+.portfolio-modal .modal-content h2 {
+ margin-bottom: 15px;
+ font-size: 3em;
+}
+
+.portfolio-modal .modal-content p {
+ margin-bottom: 30px;
+}
+
+.portfolio-modal .modal-content p.item-intro {
+ margin: 20px 0 30px;
+ font-family: HelveticaNeue-Thin,Helvetica,Arial,sans-serif;
+ font-size: 16px;
+ font-style: italic;
+}
+
+.portfolio-modal .modal-content ul.list-inline {
+ margin-top: 0;
+ margin-bottom: 30px;
+}
+
+.portfolio-modal .modal-content img {
+ margin-bottom: 30px;
+}
+
+.portfolio-modal .close-modal {
+ position: absolute;
+ top: 25px;
+ right: 25px;
+ width: 75px;
+ height: 75px;
+ background-color: transparent;
+ cursor: pointer;
+}
+
+.portfolio-modal .close-modal:hover {
+ opacity: .3;
+}
+
+.portfolio-modal .close-modal .lr {
+ z-index: 1051;
+ width: 1px;
+ height: 75px;
+ margin-left: 35px;
+ background-color: #222;
+ -webkit-transform: rotate(45deg);
+ -ms-transform: rotate(45deg);
+ transform: rotate(45deg);
+}
+
+.portfolio-modal .close-modal .lr .rl {
+ z-index: 1052;
+ width: 1px;
+ height: 75px;
+ background-color: #222;
+ -webkit-transform: rotate(90deg);
+ -ms-transform: rotate(90deg);
+ transform: rotate(90deg);
+}
+
+::-moz-selection {
+ text-shadow: none;
+ background: HelveticaNeue,Helvetica,Arial,sans-serif
+}
+
+::selection {
+ text-shadow: none;
+ background: HelveticaNeue,Helvetica,Arial,sans-serif
+}
+
+img::selection {
+ background: 0 0;
+}
+
+img::-moz-selection {
+ background: 0 0;
+}
+
+body {
+ webkit-tap-highlight-color: HelveticaNeue,Helvetica,Arial,sans-serif
+}
diff --git a/container-search-gui/src/main/resources/gui/_includes/css/bootstrap.min.css b/container-search-gui/src/main/resources/gui/_includes/css/bootstrap.min.css
new file mode 100644
index 00000000000..4185df72627
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/_includes/css/bootstrap.min.css
@@ -0,0 +1,7 @@
+/*!
+ * Bootswatch v3.2.0
+ * Homepage: http://bootswatch.com
+ * Copyright 2012-2014 Thomas Park
+ * Licensed under MIT
+ * Based on Bootstrap
+*//*! normalize.css v3.0.1 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}@media print{*{text-shadow:none !important;color:#000 !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff !important}.navbar{display:none}.table td,.table th{background-color:#fff !important}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:'Glyphicons Halflings';src:url('../fonts/glyphicons-halflings-regular.eot');src:url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),url('../fonts/glyphicons-halflings-regular.woff') format('woff'),url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'),url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Lato","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:15px;line-height:1.42857143;color:var(--secondary);background-color:#ffffff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:var(--primary);text-decoration:none}a:hover,a:focus{color:var(--primary);text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;width:100% \9;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#ffffff;border:1px solid #ecf0f1;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;width:100% \9;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:21px;margin-bottom:21px;border:0;border-top:1px solid #ecf0f1}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Lato","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:400;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:normal;line-height:1;color:#b4bcc2}h1,.h1,h2,.h2,h3,.h3{margin-top:21px;margin-bottom:10.5px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10.5px;margin-bottom:10.5px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:39px}h2,.h2{font-size:32px}h3,.h3{font-size:26px}h4,.h4{font-size:19px}h5,.h5{font-size:15px}h6,.h6{font-size:13px}p{margin:0 0 10.5px}.lead{margin-bottom:21px;font-size:17px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:22.5px}}small,.small{font-size:86%}cite{font-style:normal}mark,.mark{background-color:#f39c12;padding:.2em}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#b4bcc2}.text-primary{color:var(--secondary)}a.text-primary:hover{color:#1a242f}.text-success{color:#ffffff}a.text-success:hover{color:#e6e6e6}.text-info{color:#ffffff}a.text-info:hover{color:#e6e6e6}.text-warning{color:#ffffff}a.text-warning:hover{color:#e6e6e6}.text-danger{color:#ffffff}a.text-danger:hover{color:#e6e6e6}.bg-primary{color:#fff;background-color:var(--secondary)}a.bg-primary:hover{background-color:#1a242f}.bg-success{background-color:var(--primary)}a.bg-success:hover{background-color:#128f76}.bg-info{background-color:#3498db}a.bg-info:hover{background-color:#217dbb}.bg-warning{background-color:#f39c12}a.bg-warning:hover{background-color:#c87f0a}.bg-danger{background-color:#e74c3c}a.bg-danger:hover{background-color:#d62c1a}.page-header{padding-bottom:9.5px;margin:42px 0 21px;border-bottom:1px solid transparent}ul,ol{margin-top:0;margin-bottom:10.5px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:21px}dt,dd{line-height:1.42857143}dt{font-weight:bold}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #b4bcc2}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10.5px 21px;margin:0 0 21px;font-size:18.75px;border-left:5px solid #ecf0f1}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#b4bcc2}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #ecf0f1;border-left:0;text-align:right}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}blockquote:before,blockquote:after{content:""}address{margin-bottom:21px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#ffffff;background-color:#333333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;box-shadow:none}pre{display:block;padding:10px;margin:0 0 10.5px;font-size:14px;line-height:1.42857143;word-break:break-all;word-wrap:break-word;color:#7b8a8b;background-color:#ecf0f1;border:1px solid #cccccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.row{margin-left:-15px;margin-right:-15px}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0%}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0%}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0%}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0%}}table{background-color:transparent}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:21px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ecf0f1}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ecf0f1}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ecf0f1}.table .table{background-color:#ffffff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ecf0f1}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ecf0f1}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#ecf0f1}table col[class*="col-"]{position:static;float:none;display:table-column}table td[class*="col-"],table th[class*="col-"]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#ecf0f1}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#dde4e6}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:var(--primary)}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#15a589}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#3498db}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#258cd1}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#f39c12}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#e08e0b}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#e74c3c}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#e43725}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15.75px;overflow-y:hidden;overflow-x:auto;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ecf0f1;-webkit-overflow-scrolling:touch}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:21px;font-size:22.5px;line-height:inherit;color:var(--secondary);border:0;border-bottom:1px solid transparent}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:bold}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:11px;font-size:15px;line-height:1.42857143;color:var(--secondary)}.form-control{display:block;width:100%;height:43px;padding:10px 15px;font-size:15px;line-height:1.42857143;color:var(--secondary);background-color:#ffffff;background-image:none;border:1px solid #dce4ec;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:var(--secondary);outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(44,62,80,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(44,62,80,0.6)}.form-control::-moz-placeholder{color:#acb6c0;opacity:1}.form-control:-ms-input-placeholder{color:#acb6c0}.form-control::-webkit-input-placeholder{color:#acb6c0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#ecf0f1;opacity:1}textarea.form-control{height:auto}input[type="search"]{-webkit-appearance:none}input[type="date"],input[type="time"],input[type="datetime-local"],input[type="month"]{line-height:43px;line-height:1.42857143 \0}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm{line-height:33px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg{line-height:64px}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;min-height:21px;margin-top:10px;margin-bottom:10px}.radio label,.checkbox label{padding-left:20px;margin-bottom:0;font-weight:normal;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-left:-20px;margin-top:4px \9}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:normal;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.form-control-static{padding-top:11px;padding-bottom:11px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-left:0;padding-right:0}.input-sm,.form-horizontal .form-group-sm .form-control{height:33px;padding:6px 9px;font-size:13px;line-height:1.5;border-radius:3px}select.input-sm{height:33px;line-height:33px}textarea.input-sm,select[multiple].input-sm{height:auto}.input-lg,.form-horizontal .form-group-lg .form-control{height:64px;padding:18px 27px;font-size:19px;line-height:1.33;border-radius:6px}select.input-lg{height:64px;line-height:64px}textarea.input-lg,select[multiple].input-lg{height:auto}.has-feedback{position:relative}.has-feedback .form-control{padding-right:53.75px}.form-control-feedback{position:absolute;top:26px;right:0;z-index:2;display:block;width:43px;height:43px;line-height:43px;text-align:center}.input-lg+.form-control-feedback{width:64px;height:64px;line-height:64px}.input-sm+.form-control-feedback{width:33px;height:33px;line-height:33px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline{color:#ffffff}.has-success .form-control{border-color:#ffffff;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#e6e6e6;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-success .input-group-addon{color:#ffffff;border-color:#ffffff;background-color:var(--primary)}.has-success .form-control-feedback{color:#ffffff}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline{color:#ffffff}.has-warning .form-control{border-color:#ffffff;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#e6e6e6;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-warning .input-group-addon{color:#ffffff;border-color:#ffffff;background-color:#f39c12}.has-warning .form-control-feedback{color:#ffffff}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline{color:#ffffff}.has-error .form-control{border-color:#ffffff;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#e6e6e6;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-error .input-group-addon{color:#ffffff;border-color:#ffffff;background-color:#e74c3c}.has-error .form-control-feedback{color:#ffffff}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#597ea2}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:0;margin-bottom:0;padding-top:11px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:32px}.form-horizontal .form-group{margin-left:-15px;margin-right:-15px}@media (min-width:768px){.form-horizontal .control-label{text-align:right;margin-bottom:0;padding-top:11px}}.form-horizontal .has-feedback .form-control-feedback{top:0;right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:24.94px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:7px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:10px 15px;font-size:15px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus{color:#ffffff;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;pointer-events:none;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#ffffff;background-color:#95a5a6;border-color:#95a5a6}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#ffffff;background-color:#798d8f;border-color:#74898a}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#95a5a6;border-color:#95a5a6}.btn-default .badge{color:#95a5a6;background-color:#ffffff}.btn-primary{color:#ffffff;background-color:var(--secondary);border-color:var(--secondary)}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#ffffff;background-color:#1a242f;border-color:#161f29}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:var(--secondary);border-color:var(--secondary)}.btn-primary .badge{color:var(--secondary);background-color:#ffffff}.btn-success{color:#ffffff;background-color:var(--primary);border-color:var(--primary)}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#ffffff;background-color:#128f76;border-color:#11866f}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:var(--primary);border-color:var(--primary)}.btn-success .badge{color:var(--primary);background-color:#ffffff}.btn-info{color:#ffffff;background-color:#3498db;border-color:#3498db}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#ffffff;background-color:#217dbb;border-color:#2077b2}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#3498db;border-color:#3498db}.btn-info .badge{color:#3498db;background-color:#ffffff}.btn-warning{color:#ffffff;background-color:#f39c12;border-color:#f39c12}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#ffffff;background-color:#c87f0a;border-color:#be780a}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f39c12;border-color:#f39c12}.btn-warning .badge{color:#f39c12;background-color:#ffffff}.btn-danger{color:#ffffff;background-color:#e74c3c;border-color:#e74c3c}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#ffffff;background-color:#d62c1a;border-color:#cd2a19}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#e74c3c;border-color:#e74c3c}.btn-danger .badge{color:#e74c3c;background-color:#ffffff}.btn-link{color:var(--primary);font-weight:normal;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:var(--primary);text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#b4bcc2;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:18px 27px;font-size:19px;line-height:1.33;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:6px 9px;font-size:13px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:13px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height 0.35s ease;-o-transition:height 0.35s ease;transition:height 0.35s ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:15px;text-align:left;background-color:#ffffff;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9.5px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:1.42857143;color:#7b8a8b;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{text-decoration:none;color:#ffffff;background-color:var(--secondary)}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;outline:0;background-color:var(--secondary)}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#b4bcc2}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);cursor:not-allowed}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:13px;line-height:1.42857143;color:#b4bcc2;white-space:nowrap}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}@media (min-width:768px){.navbar-right .dropdown-menu{left:auto;right:0}.navbar-right .dropdown-menu-left{left:0;right:auto}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group>.btn:focus,.btn-group-vertical>.btn:focus{outline:0}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-left:12px;padding-right:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-bottom-left-radius:4px;border-top-right-radius:0;border-top-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{float:none;display:table-cell;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn>input[type="radio"],[data-toggle="buttons"]>.btn>input[type="checkbox"]{position:absolute;z-index:-1;opacity:0;filter:alpha(opacity=0)}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-left:0;padding-right:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:64px;padding:18px 27px;font-size:19px;line-height:1.33;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:64px;line-height:64px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:33px;padding:6px 9px;font-size:13px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:33px;line-height:33px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:10px 15px;font-size:15px;font-weight:normal;line-height:1;color:var(--secondary);text-align:center;background-color:#ecf0f1;border:1px solid #dce4ec;border-radius:4px}.input-group-addon.input-sm{padding:6px 9px;font-size:13px;border-radius:3px}.input-group-addon.input-lg{padding:18px 27px;font-size:19px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{margin-bottom:0;padding-left:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#ecf0f1}.nav>li.disabled>a{color:#b4bcc2}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#b4bcc2;text-decoration:none;background-color:transparent;cursor:not-allowed}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#ecf0f1;border-color:var(--primary)}.nav .nav-divider{height:1px;margin:9.5px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ecf0f1}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#ecf0f1 #ecf0f1 #ecf0f1}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:var(--secondary);background-color:#ffffff;border:1px solid #ecf0f1;border-bottom-color:transparent;cursor:default}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ecf0f1}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ecf0f1;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#ffffff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#ffffff;background-color:var(--secondary)}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ecf0f1}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ecf0f1;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#ffffff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:60px;margin-bottom:21px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{overflow-x:visible;padding-right:15px;padding-left:15px;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-left:0;padding-right:0}}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0)}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;padding:19.5px 15px;font-size:19px;line-height:21px;height:60px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;margin-right:15px;padding:9px 10px;margin-top:13px;margin-bottom:13px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:9.75px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:21px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:21px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:19.5px;padding-bottom:19.5px}.navbar-nav.navbar-right:last-child{margin-right:-15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important}}.navbar-form{margin-left:-15px;margin-right:-15px;padding:10px 15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:8.5px;margin-bottom:8.5px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}}@media (min-width:768px){.navbar-form{width:auto;border:0;margin-left:0;margin-right:0;padding-top:0;padding-bottom:0;-webkit-box-shadow:none;box-shadow:none}.navbar-form.navbar-right:last-child{margin-right:-15px}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8.5px;margin-bottom:8.5px}.navbar-btn.btn-sm{margin-top:13.5px;margin-bottom:13.5px}.navbar-btn.btn-xs{margin-top:19px;margin-bottom:19px}.navbar-text{margin-top:19.5px;margin-bottom:19.5px}@media (min-width:768px){.navbar-text{float:left;margin-left:15px;margin-right:15px}.navbar-text.navbar-right:last-child{margin-right:0}}.navbar-default{background-color:var(--secondary);border-color:transparent}.navbar-default .navbar-brand{color:#ffffff}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:var(--primary);background-color:transparent}.navbar-default .navbar-text{color:#777777}.navbar-default .navbar-nav>li>a{color:#ffffff}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:var(--primary);background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#ffffff;background-color:#1a242f}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#1a242f}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#1a242f}.navbar-default .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:#1a242f;color:#ffffff}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#ffffff}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:var(--primary);background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#1a242f}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-default .navbar-link{color:#ffffff}.navbar-default .navbar-link:hover{color:var(--primary)}.navbar-default .btn-link{color:#ffffff}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:var(--primary)}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#cccccc}.navbar-inverse{background-color:var(--primary);border-color:transparent}.navbar-inverse .navbar-brand{color:#ffffff}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:var(--secondary);background-color:transparent}.navbar-inverse .navbar-text{color:#ffffff}.navbar-inverse .navbar-nav>li>a{color:#ffffff}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:var(--secondary);background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#ffffff;background-color:#15a589}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#128f76}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#128f76}.navbar-inverse .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#149c82}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{background-color:#15a589;color:#ffffff}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#ffffff}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:var(--secondary);background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#15a589}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-inverse .navbar-link{color:#ffffff}.navbar-inverse .navbar-link:hover{color:var(--secondary)}.navbar-inverse .btn-link{color:#ffffff}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:var(--secondary)}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#cccccc}.breadcrumb{padding:8px 15px;margin-bottom:21px;list-style:none;background-color:#ecf0f1;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{content:"/\00a0";padding:0 5px;color:#cccccc}.breadcrumb>.active{color:#95a5a6}.pagination{display:inline-block;padding-left:0;margin:21px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:10px 15px;line-height:1.42857143;text-decoration:none;color:#ffffff;background-color:var(--primary);border:1px solid transparent;margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:4px;border-top-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{color:#ffffff;background-color:#0f7864;border-color:transparent}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:2;color:#ffffff;background-color:#0f7864;border-color:transparent;cursor:default}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#ecf0f1;background-color:#3be6c4;border-color:transparent;cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:18px 27px;font-size:19px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:6px;border-top-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:6px 9px;font-size:13px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}.pager{padding-left:0;margin:21px 0;list-style:none;text-align:center}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:var(--primary);border:1px solid transparent;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#0f7864}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#ffffff;background-color:var(--primary);cursor:not-allowed}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:bold;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#ffffff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#95a5a6}.label-default[href]:hover,.label-default[href]:focus{background-color:#798d8f}.label-primary{background-color:var(--secondary)}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#1a242f}.label-success{background-color:var(--primary)}.label-success[href]:hover,.label-success[href]:focus{background-color:#128f76}.label-info{background-color:#3498db}.label-info[href]:hover,.label-info[href]:focus{background-color:#217dbb}.label-warning{background-color:#f39c12}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#c87f0a}.label-danger{background-color:#e74c3c}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#d62c1a}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:13px;font-weight:bold;color:#ffffff;line-height:1;vertical-align:baseline;white-space:nowrap;text-align:center;background-color:var(--secondary);border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer}a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:var(--secondary);background-color:#ffffff}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px;margin-bottom:30px;color:inherit;background-color:#ecf0f1}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:23px;font-weight:200}.jumbotron>hr{border-top-color:#cfd9db}.container .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron{padding-left:60px;padding-right:60px}.jumbotron h1,.jumbotron .h1{font-size:67.5px}}.thumbnail{display:block;padding:4px;margin-bottom:21px;line-height:1.42857143;background-color:#ffffff;border:1px solid #ecf0f1;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-left:auto;margin-right:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:var(--primary)}.thumbnail .caption{padding:9px;color:var(--secondary)}.alert{padding:15px;margin-bottom:21px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:var(--primary);border-color:var(--primary);color:#ffffff}.alert-success hr{border-top-color:#15a589}.alert-success .alert-link{color:#e6e6e6}.alert-info{background-color:#3498db;border-color:#3498db;color:#ffffff}.alert-info hr{border-top-color:#258cd1}.alert-info .alert-link{color:#e6e6e6}.alert-warning{background-color:#f39c12;border-color:#f39c12;color:#ffffff}.alert-warning hr{border-top-color:#e08e0b}.alert-warning .alert-link{color:#e6e6e6}.alert-danger{background-color:#e74c3c;border-color:#e74c3c;color:#ffffff}.alert-danger hr{border-top-color:#e43725}.alert-danger .alert-link{color:#e6e6e6}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{overflow:hidden;height:21px;margin-bottom:21px;background-color:#ecf0f1;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:13px;line-height:21px;color:#ffffff;text-align:center;background-color:var(--secondary);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar[aria-valuenow="1"],.progress-bar[aria-valuenow="2"]{min-width:30px}.progress-bar[aria-valuenow="0"]{color:#b4bcc2;min-width:30px;background-color:transparent;background-image:none;box-shadow:none}.progress-bar-success{background-color:var(--primary)}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#3498db}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#f39c12}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#e74c3c}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media,.media-body{overflow:hidden;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{padding-left:0;list-style:none}.list-group{margin-bottom:20px;padding-left:0}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#ffffff;border:1px solid #ecf0f1}.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}a.list-group-item{color:#555555}a.list-group-item .list-group-item-heading{color:#333333}a.list-group-item:hover,a.list-group-item:focus{text-decoration:none;color:#555555;background-color:#ecf0f1}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{background-color:#ecf0f1;color:#b4bcc2}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#b4bcc2}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#ffffff;background-color:var(--secondary);border-color:var(--secondary)}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#8aa4be}.list-group-item-success{color:#ffffff;background-color:var(--primary)}a.list-group-item-success{color:#ffffff}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,a.list-group-item-success:focus{color:#ffffff;background-color:#15a589}a.list-group-item-success.active,a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-info{color:#ffffff;background-color:#3498db}a.list-group-item-info{color:#ffffff}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,a.list-group-item-info:focus{color:#ffffff;background-color:#258cd1}a.list-group-item-info.active,a.list-group-item-info.active:hover,a.list-group-item-info.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-warning{color:#ffffff;background-color:#f39c12}a.list-group-item-warning{color:#ffffff}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,a.list-group-item-warning:focus{color:#ffffff;background-color:#e08e0b}a.list-group-item-warning.active,a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-danger{color:#ffffff;background-color:#e74c3c}a.list-group-item-danger{color:#ffffff}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,a.list-group-item-danger:focus{color:#ffffff;background-color:#e43725}a.list-group-item-danger.active,a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:21px;background-color:#ffffff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:17px;color:inherit}.panel-title>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#ecf0f1;border-top:1px solid #ecf0f1;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group{margin-bottom:0}.panel>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-right-radius:3px;border-top-left-radius:3px}.panel>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-right-radius:3px;border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive{border-top:1px solid #ecf0f1}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{border:0;margin-bottom:0}.panel-group{margin-bottom:21px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ecf0f1}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ecf0f1}.panel-default{border-color:#ecf0f1}.panel-default>.panel-heading{color:var(--secondary);background-color:#ecf0f1;border-color:#ecf0f1}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ecf0f1}.panel-default>.panel-heading .badge{color:#ecf0f1;background-color:var(--secondary)}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ecf0f1}.panel-primary{border-color:var(--secondary)}.panel-primary>.panel-heading{color:#ffffff;background-color:var(--secondary);border-color:var(--secondary)}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:var(--secondary)}.panel-primary>.panel-heading .badge{color:var(--secondary);background-color:#ffffff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:var(--secondary)}.panel-success{border-color:var(--primary)}.panel-success>.panel-heading{color:#ffffff;background-color:var(--primary);border-color:var(--primary)}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:var(--primary)}.panel-success>.panel-heading .badge{color:var(--primary);background-color:#ffffff}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:var(--primary)}.panel-info{border-color:#3498db}.panel-info>.panel-heading{color:#ffffff;background-color:#3498db;border-color:#3498db}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#3498db}.panel-info>.panel-heading .badge{color:#3498db;background-color:#ffffff}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#3498db}.panel-warning{border-color:#f39c12}.panel-warning>.panel-heading{color:#ffffff;background-color:#f39c12;border-color:#f39c12}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#f39c12}.panel-warning>.panel-heading .badge{color:#f39c12;background-color:#ffffff}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#f39c12}.panel-danger{border-color:#e74c3c}.panel-danger>.panel-heading{color:#ffffff;background-color:#e74c3c;border-color:#e74c3c}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#e74c3c}.panel-danger>.panel-heading .badge{color:#e74c3c;background-color:#ffffff}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#e74c3c}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object{position:absolute;top:0;left:0;bottom:0;height:100%;width:100%;border:0}.embed-responsive.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#ecf0f1;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:22.5px;font-weight:bold;line-height:1;color:#000000;text-shadow:none;opacity:0.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000000;text-decoration:none;cursor:pointer;opacity:0.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{display:none;overflow:hidden;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate3d(0, -25%, 0);transform:translate3d(0, -25%, 0);-webkit-transition:-webkit-transform .3s ease-out;-moz-transition:-moz-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#ffffff;border:1px solid #999999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);background-clip:padding-box;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:0.5;filter:alpha(opacity=50)}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5;min-height:16.42857143px}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{padding:20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;visibility:visible;font-size:13px;line-height:1.4;opacity:0;filter:alpha(opacity=0)}.tooltip.in{opacity:0.9;filter:alpha(opacity=90)}.tooltip.top{margin-top:-3px;padding:5px 0}.tooltip.right{margin-left:3px;padding:0 5px}.tooltip.bottom{margin-top:3px;padding:5px 0}.tooltip.left{margin-left:-3px;padding:0 5px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;text-decoration:none;background-color:rgba(0,0,0,0.9);border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:rgba(0,0,0,0.9)}.tooltip.top-left .tooltip-arrow{bottom:0;left:5px;border-width:5px 5px 0;border-top-color:rgba(0,0,0,0.9)}.tooltip.top-right .tooltip-arrow{bottom:0;right:5px;border-width:5px 5px 0;border-top-color:rgba(0,0,0,0.9)}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:rgba(0,0,0,0.9)}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:rgba(0,0,0,0.9)}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:rgba(0,0,0,0.9)}.tooltip.bottom-left .tooltip-arrow{top:0;left:5px;border-width:0 5px 5px;border-bottom-color:rgba(0,0,0,0.9)}.tooltip.bottom-right .tooltip-arrow{top:0;right:5px;border-width:0 5px 5px;border-bottom-color:rgba(0,0,0,0.9)}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;text-align:left;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);white-space:normal}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{margin:0;padding:8px 14px;font-size:15px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{border-width:10px;content:""}.popover.top>.arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#999999;border-top-color:rgba(0,0,0,0.25);bottom:-11px}.popover.top>.arrow:after{content:" ";bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#ffffff}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#999999;border-right-color:rgba(0,0,0,0.25)}.popover.right>.arrow:after{content:" ";left:1px;bottom:-10px;border-left-width:0;border-right-color:#ffffff}.popover.bottom>.arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999999;border-bottom-color:rgba(0,0,0,0.25);top:-11px}.popover.bottom>.arrow:after{content:" ";top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#ffffff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{content:" ";right:1px;border-right-width:0;border-left-color:#ffffff;bottom:-10px}.carousel{position:relative}.carousel-inner{position:relative;overflow:hidden;width:100%}.carousel-inner>.item{display:none;position:relative;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;left:0;bottom:0;width:15%;opacity:0.5;filter:alpha(opacity=50);font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-control.left{background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1)}.carousel-control.right{left:auto;right:0;background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1)}.carousel-control:hover,.carousel-control:focus{outline:0;color:#ffffff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90)}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;margin-top:-10px;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;margin-left:-30%;padding-left:0;list-style:none;text-align:center}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;border:1px solid #ffffff;border-radius:10px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0)}.carousel-indicators .active{margin:0;width:12px;height:12px;background-color:#ffffff}.carousel-caption{position:absolute;left:15%;right:15%;bottom:20px;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{left:20%;right:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-footer:before,.modal-footer:after{content:" ";display:table}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-footer:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important;visibility:hidden !important}.affix{position:fixed;-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0)}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.navbar{border-width:0}.navbar-default .badge{background-color:#fff;color:var(--secondary)}.navbar-inverse .badge{background-color:#fff;color:var(--primary)}.navbar-brand{padding:18.5px 15px 20.5px}.btn:active{-webkit-box-shadow:none;box-shadow:none}.btn-group.open .dropdown-toggle{-webkit-box-shadow:none;box-shadow:none}.text-primary,.text-primary:hover{color:var(--secondary)}.text-success,.text-success:hover{color:var(--primary)}.text-danger,.text-danger:hover{color:#e74c3c}.text-warning,.text-warning:hover{color:#f39c12}.text-info,.text-info:hover{color:#3498db}table a,.table a{text-decoration:underline}table .success,.table .success,table .warning,.table .warning,table .danger,.table .danger,table .info,.table .info{color:#fff}table .success a,.table .success a,table .warning a,.table .warning a,table .danger a,.table .danger a,table .info a,.table .info a{color:#fff}table>thead>tr>th,.table>thead>tr>th,table>tbody>tr>th,.table>tbody>tr>th,table>tfoot>tr>th,.table>tfoot>tr>th,table>thead>tr>td,.table>thead>tr>td,table>tbody>tr>td,.table>tbody>tr>td,table>tfoot>tr>td,.table>tfoot>tr>td{border:none}table-bordered>thead>tr>th,.table-bordered>thead>tr>th,table-bordered>tbody>tr>th,.table-bordered>tbody>tr>th,table-bordered>tfoot>tr>th,.table-bordered>tfoot>tr>th,table-bordered>thead>tr>td,.table-bordered>thead>tr>td,table-bordered>tbody>tr>td,.table-bordered>tbody>tr>td,table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ecf0f1}.form-control,input{border-width:2px;-webkit-box-shadow:none;box-shadow:none}.form-control:focus,input:focus{-webkit-box-shadow:none;box-shadow:none}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning .form-control-feedback{color:#f39c12}.has-warning .form-control,.has-warning .form-control:focus{border:2px solid #f39c12}.has-warning .input-group-addon{border-color:#f39c12}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error .form-control-feedback{color:#e74c3c}.has-error .form-control,.has-error .form-control:focus{border:2px solid #e74c3c}.has-error .input-group-addon{border-color:#e74c3c}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success .form-control-feedback{color:var(--primary)}.has-success .form-control,.has-success .form-control:focus{border:2px solid var(--primary)}.has-success .input-group-addon{border-color:var(--primary)}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{border-color:transparent}.pager a,.pager a:hover{color:#fff}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{background-color:#3be6c4}.alert a,.alert .alert-link{color:#fff;text-decoration:underline}.alert .close{text-decoration:none}.close{color:#fff;text-decoration:none;opacity:0.4}.close:hover,.close:focus{color:#fff;opacity:1}.progress{height:10px;-webkit-box-shadow:none;box-shadow:none}.progress .progress-bar{font-size:10px;line-height:10px}.well{-webkit-box-shadow:none;box-shadow:none}
diff --git a/container-search-gui/src/main/resources/gui/_includes/css/vespa.css b/container-search-gui/src/main/resources/gui/_includes/css/vespa.css
new file mode 100644
index 00000000000..c63690581c6
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/_includes/css/vespa.css
@@ -0,0 +1,653 @@
+/**
+* Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+* Based on vespa.css from https://github.com/vespa-engine/frontpage for header- and footer-elements
+*/
+:root{
+ --primary: #188fff;
+ --secondary: #003abc;
+ --secondary-dark: #333;
+ --muted: #777;
+
+ --fontprimary: HelveticaNeue,Helvetica,Arial,sans-serif;
+ --fontsecondary: HelveticaNeue-Thin,Helvetica,Arial,sans-serif;
+ };
+
+.bg-light-blue {
+ background-image: linear-gradient(-1deg, rgba(63,157,216,0.08) 0%, rgba(163, 195, 215, 0.08) 97%);
+}
+
+.yql {
+}
+
+.yql:focus {
+ outline: none;
+}
+
+
+.information {
+ margin-left: -40px;
+ margin-right: 28px;
+ margin-top: -2.5px;
+ opacity:0.6;
+ transition: transform .1s; /* Animation */
+}
+
+.information:hover{
+ transform: scale(1.05);
+}
+
+/*** Tooltips! ***/
+.tip {
+ visibility: visible;
+ border-bottom: 1px dotted [#FFFFFF];
+ position: relative;
+ cursor: help;
+ text-decoration: none;
+ color: #FFF !important;
+}
+.tip span {
+ display: none;
+ z-index: 100;
+ position: absolute;
+ padding: .6em;
+ padding-left: 1em;
+ top: 1.5em;
+ left: 2.4em;
+ width: 15em;
+ background-color: #4DA2D6;
+ border: 1px solid #FFFFFF;
+ border-radius: 0.5em;
+}
+
+.tip span td {
+text-align: left;
+vertical-align: top;
+padding-left: 5px;
+}
+
+.tip span a {
+text-decoration: none;
+color: white;
+}
+
+.tip:hover span {
+ display: inline-block;
+}
+.sg-question-set,
+.sg-type-radio ul.sg-list-vertical li,
+.sg-type-checkbox ul.sg-list-vertical li,
+.sg-question-options,
+.sg-type-radio-likert .sg-question-options,
+.sg-type-table .sg-question-options,
+.sg-instructions
+{
+ overflow: visible;
+}
+
+
+
+section h2.section-heading,
+section h2.section-subheading,
+section h3.section-heading,
+section h3.section-subheading {
+ text-transform: none;
+ font-family: HelveticaNeue-Thin,Helvetica,Arial,sans-serif;
+ font-weight: normal;
+ font-style: normal;
+}
+
+section h2.section-heading,
+section h3.section-heading {
+ font-size: 50px;
+ color: #3F9DD8;
+ margin-bottom: 5px;
+}
+
+section h2.section-subheading,
+section h3.section-subheading {
+ line-height: 26px;
+ margin-top: 0;
+}
+
+header .help-title {
+ color: #FFC43C;
+ text-transform: uppercase;
+ font-weight: bold;
+}
+
+
+.text-muted {
+ color: #303030;
+}
+
+#banner {
+ background-color: #336699;
+ color: #FFFFFF;
+ font-weight: bold;
+ padding-bottom: 2px;
+ padding-top: 2px;
+ width: 100%;
+}
+
+#banner a {
+ color: #FFFFFF;
+ text-decoration: underline;
+}
+
+.navbar-default {
+ background-color: #3F9DD8;
+ padding: 0;
+ border-bottom: 2px solid rgba(255, 255, 255, 0.2);
+}
+
+
+.navbar-default .navbar-header {
+ margin-left: 13px;
+}
+
+.navbar-default .navbar-brand {
+ background: transparent url("../../img/Vespa-V2.png") no-repeat;
+ background-size: contain;
+ direction: ltr;
+ text-indent: -9000px;
+ height: 28px;
+ width: 100px;
+ display: inline-block;
+ font-family: HelveticaNeue,Helvetica,Arial,sans-serif;
+ font-weight: bold;
+ color: var(--primary);
+ margin-top: 16px;
+ margin-left: 10px;
+}
+
+.navbar-default .nav li a {
+ font-weight: bold;
+ font-size: 14px;
+ letter-spacing: 1px;
+ color: #FFFFFF;
+ text-transform: none;
+ font-family: HelveticaNeue,Helvetica,Arial,sans-serif;
+}
+
+.navbar-default .navbar-nav > li > a:hover,
+.navbar-default .navbar-nav > .active > a,
+.navbar-default .navbar-nav > .active > a:hover {
+ color: #FFC43C;
+}
+
+.navbar-toggle {
+ margin-right: 45px;
+}
+
+/* Trick to prevent anchored links to hide heading behind navbar */
+section[id]:before {
+ display: block;
+ content: " ";
+ margin-top: -80px;
+ height: 100px;
+ visibility: hidden;
+}
+
+header {
+ background-image: linear-gradient(0deg, #98C1DB 7%, #3F9DD8 100%);
+ min-height: 1150px;
+}
+
+@media (max-height: 1150px) {
+ header {
+ min-height: 100vh;
+ }
+}
+
+header .intro {
+ margin-top: -80px;
+ max-width: 350px;
+ padding-top: 10px;
+ padding-bottom: 50px;
+}
+
+header .intro-logo {
+ max-width: 120px;
+ padding-bottom: 30px;
+}
+
+header .intro-button {
+ box-shadow: 0px 2px 4px 0px rgba(0,0,0,0.5);
+ padding-left: 50px;
+ padding-right: 50px;
+}
+
+header .intro-long {
+ line-height: 27px;
+ font-size: 16px;
+ padding-bottom: 20px;
+}
+
+header .intro-param {
+ line-height: 27px;
+ font-size: 16px;
+ padding-bottom: 0px;
+ margin-bottom: 5px;
+}
+header .response{
+ line-height: 27px;
+ font-size: 20px;
+ padding-top: 10px;
+ padding-bottom: 8px;
+}
+
+header .methodselector{
+ font-size: 15px;
+ color: #fff;;
+ background-color: #99C1DA;
+ width: 70px;
+ height: 25px;
+ border-width: 0px;
+ -o-transition:.3s;
+ -ms-transition:.3s;
+ -moz-transition:.3s;
+ -webkit-transition:.3s;
+ transition:.3s
+}
+header .intro-help:hover {background-color: #4EA2D6 !important;}
+header .methodselector:hover {background-color: #79B4D8;}
+header .button:hover {background-color: #79B4D8;}
+header .removeRow:hover {background-color: #79B4D8;}
+header .addRow:hover {background-color: #79B4D8;}
+header .showJSON:hover {background-color: #79B4D8;}
+header .copyURL:hover {background-color: #79B4D8;}
+header .copyJSON:hover {background-color: #79B4D8;}
+header .methodselector:focus { outline: none; }
+header .textbox:focus {outline: none; }
+header .input:focus {outline:none;}
+header .responsebox::selection {background-color:grey;}
+header .textbox::selection {background-color:grey;}
+header .input::selection {background-color:grey;}
+header .propvalue::selection {background-color:grey;}
+header .responsebox:focus {outline:none;}
+header .responseinfo:focus {outline:none;}
+header .propvalue:focus {outline:none;}
+header .addpropsbutton:focus {outline:none;}
+
+
+header .input {
+ color: #000;
+ width: 190px;
+ border-width: 0px;
+ margin-right:2px;
+}
+
+header .input2 {
+ color: #000;
+ width: 175px;
+ border-width: 0px;
+ margin-right:2px;
+}
+
+header .textbox {
+ color: #000;
+ padding-left: 2px;
+}
+
+header .responsebox {
+ color: #000;
+ background-color: #FFF;
+
+}
+
+header .responseinfo {
+ color: #d6e6f0;
+ text-align: center;
+ border-width: 0px;
+ background-color: transparent;
+}
+
+header .propvalue {
+ color: #000;
+ background-color: #FFF;
+ width: 175px;
+ border-width: 0px;
+ margin-bottom: 3px;
+}
+
+header .propvalue:-webkit-input-placeholder { /* Safari, Chrome(, Opera?) */
+ color:gray;
+ font-style:italic;
+}
+header .propvalue:-moz-placeholder { /* Firefox 18- */
+ color:gray;
+ font-style:italic;
+}
+header .propvalue:-moz-placeholder { /* Firefox 19+ */
+ color:gray;
+ font-style:italic;
+}
+header .propvalue-ms-input-placeholder { /* IE (10+?) */
+ color:gray;
+ font-style:italic;
+}
+
+
+header .button{
+ font-size: 15px;
+ color: #fff;;
+ background-color: #99C1DA;
+ width: 70px;
+ height: 25px;
+ border-width: 0px;
+ border-radius: 5px;
+ padding: 0px
+}
+header .button:focus { outline: none; }
+
+
+
+header .addpropsbutton{
+ font-size: 15px;
+ color: #fff;;
+ background-color: #99C1DA;
+ width: 175px;
+ height: 23px;
+ border-width: 0px;
+ border-radius: 5px;
+ padding: 0px;
+ margin-left: 2px;
+ margin-bottom: 3px;
+}
+header .addRow{
+ font-size: 15px;
+ color: #fff;;
+ background-color: #99C1DA;
+ width: 70px;
+ height: 25px;
+ border-width: 0px;
+ border-radius: 5px;
+ padding: 0px
+}
+header .addRow:focus { outline: none; }
+
+header .removeRow{
+ font-size: 16px;
+ color: #fff;;
+ background-color: #99C1DA;
+ width: 35px;
+ height: 23px;
+ border-width: 0px;
+ border-radius: 5px;
+ padding: 0px;
+ padding-bottom: 2px;
+ margin-left: 3px;
+ text-align: center;
+ line-height: normal;
+}
+header .removeRow:focus { outline: none; }
+
+header .json:focus {outline: none;}
+header .copyJSON:focus {outline: none;}
+header .showJSON:focus {outline: none;}
+header .copyURL:focus {outline: none;}
+header .pasteJSON:focus {outline: none;}
+
+header .copyJSON{
+ font-size: 15px;
+ color: #fff;;
+ background-color: #99C1DA;
+ width: 130px;
+ height: 25px;
+ border-width: 0px;
+ border-radius: 5px;
+ padding: 0px
+ padding-left: 1px;
+ margin-top: 10px;
+ margin-bottom: 20px;
+ margin-right: 2px;
+ display: none;
+}
+
+header .showJSON{
+ font-size: 15px;
+ color: #fff;;
+ background-color: #99C1DA;
+ width: 140px;
+ height: 25px;
+ border-width: 0px;
+ border-radius: 5px;
+ padding: 0px
+ padding-left: 1px;
+ margin-top: 5px;
+ margin-bottom: 10px;
+}
+
+header .pasteJSON{
+ font-size: 15px;
+ color: #fff;;
+ background-color: #99C1DA;
+ width: 140px;
+ height: 25px;
+ border-width: 0px;
+ border-radius: 5px;
+ padding: 0px;
+ padding-left: 0px;
+ padding-bottom: 2px;
+ margin-top: 35px;
+ margin-bottom: 10px;
+
+ align-content: 30%;
+}
+
+
+header .copyURL{
+ font-size: 15px;
+ color: #fff;;
+ background-color: #99C1DA;
+ width: 130px;
+ height: 25px;
+ border-width: 0px;
+ border-radius: 5px;
+ padding: 0px
+ padding-left: 1px;
+ margin-top: 25px;
+ margin-bottom: 10px;
+ margin-left: 2px;
+ display: none;
+}
+
+header .intro .intro-lead-in {
+ font-style: normal;
+ font-size: 22px;
+ line-height: 50px;
+ margin-bottom: 25px;
+ font-family: HelveticaNeue-Thin,Helvetica,Arial,sans-serif;
+}
+
+header .intro .intro-heading {
+ font-style: normal;
+}
+
+header .intro-button {
+ box-shadow: 0px 2px 4px 0px rgba(0,0,0,0.5);
+}
+
+header .intro .btn-xl {
+ background: #FFFFFF;
+ border: 1px solid #3F9DD8;
+ border-radius: 2px;
+ font-size: 20px;
+ color: #3F9DD8;
+ font-weight: normal;
+ text-transform: none;
+ font-family: HelveticaNeue,Helvetica,Arial,sans-serif;
+}
+
+
+header .intro-copy {
+ display:inline-block
+ position: inherit;
+ margin-top: 10px;
+ padding-left: 50px;
+ transform: translateX(-50%);
+}
+
+header .intro-help {
+ display: inline-block;
+ margin-top: 15px;
+ width: 134px;
+ height: 45px;
+
+ border-top-left-radius: 29%;
+ border-top-right-radius: 26%;
+ border-bottom-right-radius: 27%;
+ border-bottom-left-radius: 29%;
+
+}
+
+header .intro-refresh {
+ display: inline-block;
+ margin-top: 15px;
+ margin-left: 25px;
+ transform: translateX(-50%);
+}
+
+
+@media (min-height: 1150px) {
+ header .intro-down-arrow {
+ /* Hard code top: 1150 (height of top section) - 20 (bottom) - 36 (image height) = 1094 */
+ top: 1094px;
+ }
+}
+
+@media (max-height: 825px) {
+ header .intro-down-arrow {
+ display: none;
+ }
+}
+
+@media(min-width: 768px) {
+ header .intro {
+ padding-top: 200px;
+ padding-bottom: 200px;
+ max-width: 620px;
+ }
+
+ header .intro-logo {
+ max-width: 238px;
+ padding-bottom: 70px;
+ }
+
+ header .intro .intro-lead-in {
+ font-size: 51px;
+ font-style: normal;
+ line-height: 50px;
+ }
+
+ header .intro .intro-long {
+ margin-left: 80px;
+ margin-right: 80px;
+ }
+
+ header .intro .intro-heading {
+ margin-bottom: 25px;
+ font-size: 90px;
+ font-weight: 600;
+ }
+}
+
+section {
+ padding: 50px 0;
+}
+
+section h3.section-subheading {
+ margin-bottom: 50px;
+ font-weight: normal;
+}
+
+@media(min-width: 768px) {
+ section {
+ padding: 50px 0;
+ }
+}
+
+footer .row {
+ font-size: 12px;
+ text-align: left;
+}
+
+footer .row h4 {
+ font-size: 14px;
+ padding-left: 40px;
+}
+
+footer .row .quicklinks {
+ line-height: 25px;
+ list-style: none;
+ text-align: left;
+}
+
+footer .credits {
+ font-size: 10px;
+ text-align: center;
+}
+
+#description p {
+ font-weight: 300;
+ font-size: 25px;
+}
+
+footer {
+ font-size: 14px;
+ color: #FFFFFF;
+ background-color: #3F9DD8;
+ line-height: 22px;
+ letter-spacing: 0;
+}
+
+footer .footer-row {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ flex-wrap: wrap;
+}
+
+
+footer quicklinks {
+ font-size: 14px;
+ color: #FFFFFF;
+}
+
+
+footer ul.quicklinks {
+ margin: 0;
+ padding: 0;
+}
+
+footer .footer-title {
+ color: #FFC43C;
+ text-transform: uppercase;
+ font-weight: bold;
+}
+
+footer ul.quicklinks a {
+ font-size: 14px;
+ color: #FFFFFF;
+}
+
+footer .quicklink-section {
+ padding: 0;
+ min-width: 120px;
+}
+
+footer .credits {
+ font-size: 12px;
+ font-weight: normal;
+ text-align: right;
+ font-family: HelveticaNeue-Thin,Helvetica,Arial,sans-serif;
+}
+
+footer .credits a {
+ color: #FFFFFF;
+}
+
+footer .credits span {
+ color: #FFC43C;
+}
diff --git a/container-search-gui/src/main/resources/gui/_includes/index.html b/container-search-gui/src/main/resources/gui/_includes/index.html
new file mode 100644
index 00000000000..a441e302720
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/_includes/index.html
@@ -0,0 +1,987 @@
+<!-- Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <title>Vespa. Big data. Real time.</title>
+ <meta name="viewport" content="width=device-width">
+ <meta name="description" content=" Your big data processing and serving system - deep learning,
+ search applications, ad selection system, personalized
+ recommendation systems, and more...">
+
+
+ <!-- Site icons - generated using http://realfavicongenerator.net/ -->
+ <link rel="apple-touch-icon" sizes="180x180" href="/querybuilder/icons/apple-touch-icon.png">
+ <link rel="icon" type="image/png" sizes="32x32" href="/querybuilder/icons/favicon-32x32.png">
+ <link rel="icon" type="image/png" sizes="16x16" href="/querybuilder/icons/favicon-16x16.png">
+ <link rel="manifest" href="/querybuilder/icons/manifest.json">
+ <link rel="mask-icon" href="/querybuilder/icons/safari-pinned-tab.svg" color="#3f9dd8">
+ <link rel="shortcut icon" href="/querybuilder/icons/favicon.ico" type="image/x-icon">
+ <meta name="msapplication-config" content="/querybuilder/icons/browserconfig.xml">
+ <meta name="theme-color" content="#ffffff">
+
+ <!-- Custom Fonts -->
+ <link rel="stylesheet" href="/querybuilder/css/font-awesome/css/font-awesome.min.css">
+ <link href='https://fonts.googleapis.com/css?family=Kaushan+Script' rel='stylesheet' type='text/css'>
+ <link href="https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic" rel="stylesheet" type="text/css">
+ <link href='https://fonts.googleapis.com/css?family=Droid+Serif:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
+ <link href='https://fonts.googleapis.com/css?family=Roboto+Slab:400,100,300,700' rel='stylesheet' type='text/css'>
+
+ <title>Vespa - Big Data. Real time.</title>
+
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="">
+
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
+
+ </head>
+
+
+<body id="page-top" class="index">
+
+<style type="text/css"></style>
+
+
+<link href="/querybuilder/_includes/css/agency.css" rel="stylesheet" type="text/css">
+<link href="/querybuilder/_includes/css/bootstrap.min.css" rel="stylesheet" type="text/css">
+<link href="/querybuilder/_includes/css/vespa.css" rel="stylesheet" type="text/css">
+<nav class="navbar navbar-default navbar-fixed-top">
+ <div class="container">
+ <!-- Brand and toggle get grouped for better mobile display -->
+ <div class="navbar-header">
+ <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ <a class="navbar-brand" href="http://www.vespa.ai">Vespa. Big data. Real time.</a>
+ </div>
+
+ <!-- Collect the nav links, forms, and other content for toggling -->
+ <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
+ <ul class="nav navbar-nav navbar-right">
+ <li class="hidden"><a href="#page-top"></a>
+ <li><a href="http://blog.vespa.ai/">Blog</a>
+ <li><a href="https://twitter.com/vespaengine">Twitter</a>
+ <li><a href="http://docs.vespa.ai">Docs</a>
+ <li><a href="https://github.com/vespa-engine">GitHub</a>
+ <li><a href="http://docs.vespa.ai/documentation/vespa-quick-start.html">Get Started Now</a>
+ </ul>
+ </div>
+ </div>
+ </nav>
+
+ <!-- Header -->
+ <header>
+ <div class="layer">
+ <div class="intro container">
+ <div class="intro-lead-in">Vespa Search Engine</div>
+ <div class="intro-long">Select the method for sending requests and construct a query.</div>
+ <select class="methodselector" onchange="chooseMethod();" id="method">
+ <option class="options" value="POST">POST</option>
+ <option class="options" value="GET">GET</option>
+ </select>
+ <input type="text" class="textbox" name="value" value="http://localhost:8080/search/" id="url" size="30">
+ <button class="button" onclick="startSending();" id="send">Send</button>
+
+ <br/>
+ <div id="request">
+ </br>
+ <div class="intro-param">Construct a query by adding parameters or pasting a JSON.</div>
+ </div>
+ <br/>
+ <button class="addRow"onclick="addNewRow()" id="addRow">+</button>
+ <br/>
+ <button class="pasteJSON"onclick="pasteJSON();" id="pasteJSON"><img src="/querybuilder/img/paste.svg" height="16" width="16" style="margin-top:-2px; margin-right: 3px;"/> Paste JSON</button>
+ </br>
+ <button class="showJSON"onclick="showJSON();" id="showJSON">Show query JSON</button>
+ </br>
+ <textarea class="responsebox" id="jsonquery" cols=70 rows=15 style="display:none;"readonly></textarea>
+ </br>
+ <button class="copyJSON"onclick="copyToClipboard(jsonquery);" id="copyJSON"><img src="/querybuilder/img/copy.svg" height="17" width="17" /> Copy as JSON</button>
+ <button class="copyURL"onclick="copyURL();" id="copyURL"><img src="/querybuilder/img/copy.svg" height="17" width="17" /> Copy as URL</button>
+ <br/>
+
+ <div class="response">Response</div>
+ <input class="responseinfo" id="reponsestatus" value="" readonly></input></br>
+ <textarea class="responsebox" id="response" cols=70 rows=25 readonly></textarea>
+ <div id="div" >
+ <div >
+ <div class="intro-copy" onclick="copyToClipboard(response);" style="cursor:pointer;">
+ <img src="/querybuilder/img/copy.svg" height="30" width="30" />
+ </div>
+ <div class="intro-refresh" onclick="refresh();" style="cursor:pointer;">
+ <img src="/querybuilder/img/reload.svg" height="30" width="30" />
+ </div>
+ </br>
+ </br>
+ <div id="helpbutton" class="intro-help" onclick="toggleHelp();" style="cursor:pointer;">
+ <img src="/querybuilder/img/features-help.png" height="47" width="138" style="margin-left: -1px;"/>
+ </div>
+ <div id="help" style="display: none;">
+ <div class="intro-param" id="help">
+ </br>
+ <div class="help-title">Features</div>
+ <span> ○ Autocompletion of YQL-syntax</span> </br>
+ <span> ○ Drop-down lists of all valid parameters</span> </br>
+ <span> ○ Sending both POST and GET-requests to <i>Vespa</i></span> </br>
+ <span> ○ Easy access to the <a href="https://docs.vespa.ai/documentation/reference/search-api-reference.html">documentation</a> of each parameter</span> </br>
+ <span> ○ Conversion of POST- to GET-query</span> </br>
+ <span> ○ Pasting already built JSON-query</span> </br>
+ <span> ○ View and copy the response of queries</span> </br>
+ <span> ○ View the finished JSON-query as you build </span> </br>
+ </div>
+ </br>
+ <div class="intro-param" id="help">
+ <div class="help-title">Help</div>
+ <span> If you find errors, spelling mistakes, faulty pieces of code or want to improve the querybuilder, please submit a pull request or create an <a href="https://github.com/vespa-engine/vespa/issues">issue</a>.</span> </br>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </header>
+ <footer>
+ <div class="container">
+ <div class="row">
+ <div class="col-xs-2 quicklink-section">
+ <div class="footer-title">Resources</div>
+ <ul class="quicklinks">
+ <li><a href="http://docs.vespa.ai/documentation/vespa-quick-start.html">Getting Started</a>
+ <li><a href="http://docs.vespa.ai">Documentation</a>
+ <li><a href="https://github.com/vespa-engine/vespa">Open source</a>
+ </ul>
+ </div>
+ <div class="col-xs-2 quicklink-section">
+ <div class="footer-title">Contact</div>
+ <ul class="quicklinks">
+ <li><a href="https://twitter.com/vespaengine">Twitter</a>
+ <li><a href="mailto:info@vespa.ai">info@vespa.ai</a>
+ <li><a href="https://github.com/vespa-engine/vespa/issues">Issues</a>
+ </ul>
+ </div>
+ <div class="col-xs-2 quicklink-section">
+ <div class="footer-title">Community</div>
+ <ul class="quicklinks">
+ <li><a href="https://github.com/vespa-engine/vespa/blob/master/CONTRIBUTING.md">Contributing</a>
+ <li><a href="http://stackoverflow.com/questions/tagged/vespa">Stack Overflow</a>
+ <li><a href="https://gitter.im/vespa-engine/Lobby">Gitter</a>
+ </ul>
+ </div>
+ <div class="col-xs-6 quicklink-section">
+ <div class="credits">
+ <span>Copyright Yahoo Holdings 2018</span>
+ Licensed under <a href="https://github.com/vespa-engine/vespa/blob/master/LICENSE">Apache License 2.0</a>
+ , <a href="https://github.com/y7kim/agency-jekyll-theme">Theme</a> by Rick K.
+ </div>
+ </div>
+ </div>
+ </div>
+ </footer>
+
+ <!-- jQuery Version 1.11.0 -->
+ <script src="/querybuilder/js/jquery-1.11.0.js"></script>
+
+ <!-- Bootstrap Core JavaScript -->
+ <script src="/querybuilder/js/bootstrap.min.js"></script>
+
+ <!-- Plugin JavaScript -->
+ <script src="/querybuilder/js/jquery.easing.min.js"></script>
+ <script src="/querybuilder/js/classie.js"></script>
+
+ <!-- Custom Theme JavaScript -->
+ <script src="/querybuilder/js/agency.js"></script>
+
+ <script language="javascript" type="text/javascript" src="/querybuilder/editarea/edit_area/edit_area_full.js"></script>
+ <script language="javascript" type="text/javascript">
+ function fEALoaded() {
+ $('#frame_'+window.yqlID).contents().find('.area_toolbar').hide();
+ var iframe = document.getElementById("frame_"+window.yqlID);
+ iframe.style = "vertical-align: middle; height: 117px; width: 350px; margin-bottom: 5px; ";
+ }
+ </script>
+
+
+
+ <SCRIPT language="javascript">
+ const CONFIG = $.getJSON('/querybuilder/config.json', function(data){window.CONFIG = data;});
+ method = "POST";
+ var number = 0;
+ var childno = {};
+ var json = JSON.parse("{}");
+ var searchApiReference = null;
+ var possible = null;
+ var usedProps = [];
+ var removedIndexes = [0];
+ var childrenProps = null;
+ window.onload = function() {
+ setTimeout(function(){
+ possible = window.CONFIG.levelZeroParameters;
+ childrenProps = window.CONFIG.childMap;
+ if (window.CONFIG.hasOwnProperty("ranking_features")){
+ childrenProps["ranking.features"] = window.CONFIG.ranking_features;
+ }
+ if (window.CONFIG.hasOwnProperty("ranking_properties")){
+ childrenProps["ranking.properties"] = window.CONFIG.ranking_properties;
+ }
+ addNewRow();
+ getSearchApiReference();
+ }, 250);
+ };
+ var stringType = ["yql", "queryProfile", "searchChain", "model.defaultIndex", "model.encoding", "model.language",
+ "model.queryString", "model.searchPath", "model.type", "ranking.features", "ranking.profile", "ranking.properties", "ranking.sorting", "ranking.matchPhase.diversity.attribute",
+ "ranking.matchPhase.attribute","pos.attribute", "presentation.summary", "collapse.summary",
+ "presentation.template", "select", "collapsefield", "pos.ll", "pos.radius", "streaming.priority", "streaming.groupname", "streaming.selection", "rules.rulebase",
+ "user", "presentation.format","ranking.location","ranking.freshness"];
+ var booleanType = ["nocache", "groupingSessionCache", "trace.timestamps", "ranking.listFeatures", "ranking.queryCache", "ranking.matchPhase.ascending",
+ "presentation.bolding", "presentation.timing", "rules.off", "nocachewrite", "metrics.ignore"];
+ var listType = ["model.filter", "model.restrict", "model.sources", "pos.bb", "recall"];
+ var integerType = ["hits","offset", "traceLevel", "collapsesize", "streaming.userid", "streaming.maxbucketspervisitor", "tracelevel.rules"];
+ var floatType = ["timeout"];
+ var longType = ["ranking.matchPhase.maxHits", "ranking.matchPhase.diversity.minGroups"];
+ var latlongType = [""];
+ var yqlID = "v1";
+
+
+
+ function toggleHelp(){
+ var div = document.getElementById("help");
+ var buttonDiv = document.getElementById("helpbutton");
+ if (div.style.display === "none") {
+ div.style.display = "block";
+ buttonDiv.style.backgroundColor = '#4EA2D6';
+ } else {
+ div.style.display = "none";
+ buttonDiv.style.backgroundColor = 'transparent';
+ }
+ }
+
+ function updateFields(){
+ var temp = number;
+ while (temp > 0){
+ if (!contains(removedIndexes, temp)){
+ var datalist = document.getElementById("prop"+temp);
+ datalist.innerHTML = "";
+ possible.forEach(function(item){
+ if (!contains(usedProps, item)){
+ var option = document.createElement("option");
+ option.value = item;
+ datalist.appendChild(option);
+ }
+ });
+ }
+ temp -= 1;
+ }
+ }
+ function pasteJSON(){
+ var button = document.getElementById("pasteJSON");
+ if (button.innerHTML === "Press CMD+V"){
+ button.innerHTML = '<img src="/querybuilder/img/paste.svg" height="16" width="16" style="margin-top:-2px; margin-right: 3px;"/> Paste JSON';
+ button.style = "background-color: #99C1DA;"
+ }else{
+ var textarea = document.getElementById("response");
+ var button = document.getElementById("pasteJSON");
+ button.innerHTML = "Press CMD+V";
+ button.style = "background-color: #5791d7;"
+ textarea.focus();
+ document.getElementById('response').addEventListener('paste', handlePaste);
+ }
+ }
+ function handlePaste (e) {
+ var clipboardData, pastedData;
+ // Stop data actually being pasted into div
+ e.stopPropagation();
+ e.preventDefault();
+ // Get pasted data via clipboard API
+ clipboardData = e.clipboardData || window.clipboardData;
+ pastedData = clipboardData.getData('Text');
+ // Do whatever with pasteddata
+ alert("Converting JSON: \n\n "+pastedData);
+ //Removing eventlistener
+ var oldResponse = document.getElementById('response');
+ var newResponse = oldResponse.cloneNode(true);
+ oldResponse.parentNode.replaceChild(newResponse, oldResponse);
+ //Returning to old button and
+ pasteJSON();
+ convertPastedJSON(pastedData);
+ }
+ function convertPastedJSON(pastedData){
+ try {
+ document.getElementById("request").innerHTML = "</br> <div class=\"intro-param\">Construct a query by adding parameters or pasting a JSON.</div>";
+ number = 0;
+ var json = JSON.parse(pastedData);
+ buildFromJSON(json, 0);
+ }
+ catch (err) {
+ alert("Could not parse JSON, with error-message: \n\n"+err.message);
+ }
+ }
+ function buildFromJSON(obj,parentNo) {
+ Object.keys(obj).forEach(function(key) {
+ if ( typeof(eval('obj.'+key)) === "object" ) {
+ if (parentNo != 0){
+ var newNo = addChildProp(parentNo, key);
+ buildFromJSON(eval('obj.'+key), newNo);
+ }
+ if (parentNo === 0){
+ var newNo = addNewRow(key);
+ buildFromJSON(eval('obj.'+key), newNo);
+ }
+ }
+ else {
+ if (parentNo != 0){
+ var newNo = addChildProp(parentNo, key, eval('obj.'+key));
+ }
+ if (parentNo === 0){
+ var newNo = addNewRow(key, eval('obj.'+key));
+ }
+ }
+ });
+ }
+ function copyURL(){
+ generateJSON();
+ var urlMap = dotNotate(window.json);
+ var url = buildURL(urlMap);
+ var el = document.createElement("textarea");
+ el.value = url;
+ document.body.appendChild(el);
+ copyToClipboard(el);
+ document.body.removeChild(el);
+ return url;
+ }
+ function buildURL(map){
+ var url = document.getElementById("url").value + "?";
+ var parameters = "";
+ Object.keys(map).forEach(function(key) {
+ parameters += "&" + encodeToURI(key) + "=" + encodeToURI(map[key]);
+ });
+ parameters = parameters.substr(1)
+ return url+parameters
+ }
+ function encodeToURI(string){
+ string = encodeURIComponent(string);
+ string = string.replace(/%20/g, '+');
+ return string;
+ }
+ function dotNotate(obj,target,prefix) {
+ target = target || {},
+ prefix = prefix || "";
+ Object.keys(obj).forEach(function(key) {
+ if ( typeof(obj[key]) === "object" ) {
+ dotNotate(obj[key],target,prefix + key + ".");
+ } else {
+ return target[prefix + key] = obj[key];
+ }
+ });
+ return target;
+ }
+ function copyToClipboard(id){
+ id.select();
+ document.execCommand('copy');
+ clearSelection();
+ }
+ function clearSelection()
+ {
+ if (window.getSelection) {window.getSelection().removeAllRanges();}
+ else if (document.selection) {document.selection.empty();}
+ }
+ function chooseMethod(method) {
+ var selectBox = document.getElementById("method");
+ var selectedMethod = selectBox.options[selectBox.selectedIndex].value;
+ if (selectedMethod != method){
+ window.method = selectedMethod
+ changeDiv(selectedMethod);
+ }
+ }
+ function changeDiv(selectedMethod){
+ if (selectedMethod === "GET"){
+ document.getElementById("request").innerHTML = '</br><textarea class=\"responsebox\" cols=70 rows=4 id=\"url2\">'+copyURL();+'</textarea>';
+ changeVisibility();
+ }
+ else if (selectedMethod === "POST") {
+ document.getElementById("request").innerHTML = "</br> <div class=\"intro-param\">Construct a query by adding parameters or pasting a JSON.</div>";
+ number = 0;
+ changeVisibility();
+ let jsonString = JSON.stringify(window.json);
+ if (jsonString === "{}" || jsonString=== '{"":""}'){
+ addNewRow();
+ } else {
+ buildFromJSON(window.json, 0);
+ }
+ }
+ }
+ function stripMyHTML(html)
+ {
+ var tmp = document.createElement("DIV");
+ tmp.innerHTML = html
+ for (var i = 0; i < tmp.children.length; i++){
+ tmp.children[i].classList = [];
+ }
+ return tmp.innerHTML.replace('<code>','').replace('</code>','');
+ }
+ function addNewRow(key, value){
+ number += 1;
+ const temp = number;
+ childno[temp] = 0;
+ var newInput = document.createElement("input");
+ newInput.id = "i"+temp;
+ newInput.setAttribute("list", "prop"+temp);
+ newInput.onchange = function(){keySelected(temp);};
+ newInput.classList.add("input")
+ var newDatalist = document.createElement("datalist");
+ newDatalist.id = "prop"+temp;
+ newInputVal = document.createElement("input");
+ newInputVal.type = "text";
+ newInputVal.id = "v"+temp;
+ newInputVal.classList.add("propvalue");
+ newInputVal.setAttribute("list", "val"+temp);
+ var newDatalist2 = document.createElement("datalist");
+ newDatalist2.id = "val"+temp;
+ //newDatalist2.style = "display: none;";
+ var newButton = document.createElement("button");
+ newButton.id = "b"+temp;
+ newButton.innerHTML = "-";
+ newButton.onclick = function(){removeRow(temp);};
+ newButton.classList.add("removeRow");
+ var br = document.createElement("br");
+ br.id = "br"+temp
+ var img = document.createElement("img");
+ img.src = "/querybuilder/img/information.svg";
+ img.height = "15";
+ img.width = "15";
+ img.classList.add("information");
+ var span = document.createElement("span");
+ span.id = "span"+temp;
+ span.innerHTML = stripMyHTML('Choose a parameter for information');
+ var a = document.createElement("a");
+ a.href = "#";
+ a.classList.add("tip");
+ a.id = "inf"+temp
+ a.appendChild(img);
+ a.appendChild(span);
+ var div = document.createElement("div");
+ div.id = number;
+ div.appendChild(newInput);
+ div.appendChild(newDatalist);
+ div.appendChild(a);
+ div.appendChild(newInputVal);
+ div.appendChild(newDatalist2);
+ div.appendChild(newButton);
+ div.appendChild(br);
+ var bigdiv = document.getElementById("request");
+ bigdiv.appendChild(div);
+ updateFields();
+ value2 = ""+ value;
+ if (key){
+ newInput.value = key;
+ keySelected(temp, value)
+ }
+ if (value || value2==="false"){
+ if (key === "yql"){
+ editAreaLoader.setValue(window.yqlID, value);
+ } else {
+ newInputVal.value= value;
+ }
+ }
+ generateJSON();
+ return temp;
+ }
+ function showInformation(no, key){
+ var a = document.getElementById("inf"+no);
+ if(validKey(no, key)){
+ a.style = "visibility: visible;";
+ try {
+ document.getElementById("span"+no).innerHTML = changeInformation(no, key);
+ } catch (err) {
+ document.getElementById("span"+no).innerHTML = "Something went wrong loading search api reference. Please refresh page";
+ }
+ }
+ else{
+ a.style = "visibility: hidden;"
+ }
+ }
+ function validKey(no, possibleKey){
+ if (contains(possible, possibleKey)){return true;}
+ for (var key in childrenProps){
+ if (contains(childrenProps[key],possibleKey)){
+ return true;
+ }
+ }
+ return false;
+ }
+ function checkConfigOptions(key, no){
+ var jsonID = key.replace(/\./g , "_");
+ var datalist = document.getElementById("val"+no);
+ datalist.innerHTML = "";
+ if (window.CONFIG.hasOwnProperty(jsonID)){
+ var optionlist = eval("window.CONFIG."+jsonID);
+ optionlist.forEach(function(item){
+ var option = document.createElement("option");
+ option.value = item;
+ datalist.appendChild(option);
+ });
+ }
+ }
+ function keySelected(no, value){
+ var key = document.getElementById("i"+no).value;
+ showInformation(no, key);
+ findUsedProps();
+ var fullKey = getFullName(no, key);
+ if(fullKey in childrenProps){
+ var input = document.getElementById("v"+no);
+ if ($(input).is("textarea")){
+ editAreaLoader.delete_instance(window.yqlID);
+ }
+ var button = document.createElement("button");
+ //var datalist = document.getElementById("val"+no); //Hide value-datalist
+ //datalist.style = "display:none;";
+ button.id="propb"+no
+ button.innerHTML=" + Add property";
+ button.onclick = function(){addChildProp(no);};
+ button.classList.add("addpropsbutton");
+ var parent = input.parentNode;
+ parent.replaceChild(button, input);
+ } else {
+ if (document.getElementById("propb"+no) != null){
+ var button = document.getElementById("propb"+no)
+ var newInputVal = document.createElement("input");
+ newInputVal.type = "text";
+ newInputVal.id = "v"+no;
+ newInputVal.classList.add("propvalue");
+ newInputVal.setAttribute("list", "val"+no);
+ var parent = button.parentNode;
+ showType(newInputVal, no);
+ if (key === "yql"){
+ newInputVal = document.createElement("textarea");
+ newInputVal.id = "v"+no;
+ window.yqlID = "v"+no;
+ newInputVal.class = "yql";
+ newInputVal.name = "content";
+ newInputVal.cols = "80";
+ newInputVal.rows = "15";
+ newInputVal.innerHTML = "\n\n\n\n\n";
+ parent.replaceChild(newInputVal, button);
+ editAreaLoader.init({
+ id : "v"+no // textarea id
+ ,syntax: "yql" // syntax to be uses for highgliting
+ ,start_highlight: true // to display with highlight mode on start-up
+ ,autocompletion: true
+ ,plugins: 'autocompletion'
+ ,gecko_spellcheck: true
+ ,allow_toggle: false
+ ,browsers: "all"
+ ,replace_tab_by_spaces: 2
+ ,EA_load_callback: "fEALoaded"
+ });
+ }
+ if (key != "yql"){
+ parent.replaceChild(newInputVal, button);
+ }
+ }else if (true) {
+ var inputval = document.getElementById("v"+no);
+ showType(inputval, no);
+ if ($(inputval).is("textarea")){
+ var newInputVal = document.createElement("input");
+ editAreaLoader.delete_instance(window.yqlID);
+ newInputVal.type = "text";
+ newInputVal.id = "v"+no;
+ newInputVal.classList.add("propvalue");
+ newInputVal.setAttribute("list", "val"+no);
+ showType(newInputVal, no);
+ var parent = inputval.parentNode;
+ parent.replaceChild(newInputVal, inputval);
+ }
+ if (key === "yql"){
+ newInputVal = document.createElement("textarea");
+ newInputVal.id = "v"+no;
+ window.yqlID = "v"+no;
+ newInputVal.name = "content";
+ newInputVal.cols = "80";
+ newInputVal.rows = "15";
+ newInputVal.class = "yql";
+ newInputVal.innerHTML = "\n\n\n\n\n";
+ var parent = inputval.parentNode;
+ parent.replaceChild(newInputVal, inputval);
+ editAreaLoader.init({
+ id : "v"+no // textarea id
+ ,syntax: "yql" // syntax to be uses for highgliting
+ ,start_highlight: true // to display with highlight mode on start-up
+ ,autocompletion: true
+ ,plugins: 'autocompletion'
+ ,gecko_spellcheck: true
+ ,font_family: 'Tahoma'
+ ,allow_toggle: false
+ ,browsers: "all"
+ ,replace_tab_by_spaces: 2
+ ,EA_load_callback: "fEALoaded"
+ });
+ }
+ if(value){
+ inputval.value = value;
+ }
+ }
+ }
+ if (!validKey(no, key)){
+ //var keyInput = document.getElementById("i"+no);
+ //keyInput.style = "border-width: 1px; border-color: red;"
+ var valueInput = document.getElementById("v"+no);
+ valueInput.placeholder = "Possible invalid parameter";
+ //Removes possible options for for former parameter
+ var datalist = document.getElementById("val"+no);
+ datalist.innerHTML = "";
+ }
+ if (validKey(no, key)){
+ // Check if datalist should be visible and add options
+ checkConfigOptions(fullKey, no);
+ var keyInput = document.getElementById("i"+no);
+ //keyInput.style = "border-width: 0px;"
+ }
+ }
+ function showType(inputVal, no){
+ var key = document.getElementById("i"+no).value;
+ var key = getFullName(no, key);
+ if (contains(stringType, key)){
+ inputVal.placeholder = "String";
+ }
+ else if (contains(booleanType, key)){
+ inputVal.placeholder = "Boolean";
+ }
+ else if (contains(integerType, key)){
+ inputVal.placeholder = "Int";
+ }
+ else if (contains(listType, key)){
+ inputVal.placeholder = "Array";
+ }
+ else if (contains(longType, key)){
+ inputVal.placeholder = "Long";
+ }
+ else if (contains(latlongType, key)){
+ inputVal.placeholder = "Latlong";
+ }
+ else if (contains(floatType, key)){
+ inputVal.placeholder = "Float";
+ }
+ else {
+ inputVal.placeholder = "";
+ }
+ }
+ function removeRow(no){
+ var div = document.getElementById(no);
+ var key = document.getElementById("i"+no).value;
+ var bigdiv = document.getElementById("request");
+ setTimeout(function(){
+ bigdiv.removeChild(div);
+ }, 200);
+ removedIndexes.push(no);
+ findUsedProps();
+ updateFields();
+ generateJSON();
+ }
+ function removeChildRow(no){
+ var a = "" + no;
+ var parentNo = a.split(".").slice(0, -1).join(".");
+ var div = document.getElementById(no);
+ var bigdiv = document.getElementById(parentNo);
+ bigdiv.removeChild(div);
+ removedIndexes.push(no);
+ findUsedProps();
+ updateChildrenFields(parentNo);
+ generateJSON();
+ }
+ function countDots(s1) {
+ return ( s1.match( /\./g ) || [] ).length;
+ }
+ function addChildProp(no, key, value){
+ generateJSON();
+ childno[no] += 1;
+ var temp = ""+no+"."+childno[no];
+ childno[temp] = 0;
+ var parentNode = document.getElementById(no);
+ var newInput = document.createElement("input");
+ newInput.id = "i"+temp;
+ newInput.setAttribute("list", "prop"+temp);
+ newInput.onchange = function(){keySelected(temp);};
+ newInput.classList.add("input")
+ var newDatalist = document.createElement("datalist");
+ newDatalist.id = "prop"+temp;
+ newInputVal = document.createElement("input");
+ newInputVal.type = "text";
+ newInputVal.id = "v"+temp;
+ newInputVal.classList.add("propvalue");
+ newInputVal.setAttribute("list", "val"+temp);
+ var newDatalist2 = document.createElement("datalist");
+ newDatalist2.id = "val"+temp;
+ var newButton = document.createElement("button");
+ newButton.id = "b"+temp;
+ newButton.innerHTML = "-";
+ newButton.onclick = function(){removeChildRow(temp);};
+ newButton.classList.add("removeRow");
+ var br = document.createElement("br");
+ br.id = "br"+temp
+ var div = document.createElement("div");
+ div.id = temp;
+ var b = document.createElement('b');
+ b.innerHTML = ' ↳ ';
+ var margin = 20*(temp).length;
+ b.style = "padding-left:"+margin+"px;";
+ var img = document.createElement("img");
+ img.src = "/querybuilder/img/information.svg";
+ img.height = "15";
+ img.width = "15";
+ img.classList.add("information");
+ var span = document.createElement("span");
+ span.id = "span"+temp;
+ span.innerHTML = stripMyHTML('Choose a parameter for information');
+ var a = document.createElement("a");
+ a.href = "#";
+ a.classList.add("tip");
+ a.id = "inf"+temp
+ a.appendChild(img);
+ a.appendChild(span);
+ div.appendChild(b);
+ div.appendChild(newInput);
+ div.appendChild(newDatalist);
+ div.append(a);
+ div.appendChild(newInputVal);
+ div.appendChild(newDatalist2);
+ div.appendChild(newButton);
+ div.appendChild(br);
+ parentNode.appendChild(div);
+ updateChildrenFields(no);
+ let value2 = "" + value;
+ if (key){
+ newInput.value = key;
+ keySelected(temp, value);
+ }
+ if (value2==="false" || value){
+ newInputVal.value= value;
+ }
+ return temp;
+ }
+ function updateChildrenFields(parentNo){
+ parentKey = document.getElementById("i"+parentNo).value;
+ var temp = parseInt(childno[parentNo]);
+ while (temp > 0){
+ if (!contains(removedIndexes, parentNo+"."+temp)){
+ var datalist = document.getElementById("prop"+parentNo+"."+temp);
+ datalist.innerHTML = "";
+ var list = childrenProps[getFullName(parentNo,parentKey)];
+ list.forEach(function(item){
+ if(!contains(usedProps,getFullName(parentNo,parentKey)+"."+item)){
+ var option = document.createElement("option");
+ option.value = item;
+ datalist.appendChild(option);
+ }
+ });
+ }
+ temp -= 1;
+ }
+ }
+ function contains(a, obj) {
+ for (var i = 0; i < a.length; i++) {
+ if (a[i] === obj) {
+ return true;
+ }
+ }
+ return false;
+ }
+ function refresh(){
+ document.location.reload(true);
+ }
+ function generateJSON(){
+ json = JSON.parse("{}");
+ buildJSON(json, number, 0);
+ var textarea = document.getElementById("jsonquery");
+ textarea.innerHTML = JSON.stringify(window.json, undefined, 4);
+ }
+ function buildJSON(parent, no, thresh){
+ var temp = no;
+ if (countDots(""+temp) > 0){
+ var list = (""+no).split(".");
+ var child_id = list[list.length-1];
+ } else{
+ var child_id = (""+no)
+ }
+ while (child_id > thresh){
+ if (!contains(removedIndexes, temp)){
+ var key = document.getElementById("i"+temp).value;
+ if (document.getElementById("v"+temp) != null){
+ var value = document.getElementById("v"+temp).value;
+ if (key === "yql"){
+ value = $.trim(editAreaLoader.getValue(window.yqlID).replace(/\n/g, " "));
+ }
+ if (contains(booleanType, key)){
+ if (value === 'true'){
+ value = true;
+ } if (value === 'false'){
+ value = false;
+ }}
+ var fullKey = getFullName(temp, key);
+ if (contains(integerType, fullKey) || contains(longType, fullKey)){
+ value = parseInt(value);
+ value = isNaN(value) ? 0 : value;
+ }
+ if (contains(floatType, fullKey)){
+ value = parseFloat(value);
+ value = isNaN(value) ? 0 : value;
+ }
+ parent[key] = value
+ } else {
+ parent[key] = JSON.parse("{}");
+ const newParent = parent[key];
+ const tempJSON = json;
+ var newNo = temp+"."+childno[temp];
+ var newThresh = 0;
+ buildJSON(newParent, newNo, newThresh);
+ }
+ }
+ if (countDots(""+temp) > 0){
+ var temp2 = temp.split(".").slice(0, -1);
+ var newNo = temp.split(".")[temp2.length];
+ newNo = parseInt(newNo)-1;
+ temp2.push(newNo);
+ temp = temp2.join(".");
+ child_id--;
+ } else{
+ temp = parseInt(temp)-1;
+ child_id--;
+ }
+ }
+ }
+ function findUsedProps(){
+ usedProps = [];
+ addUsedProps(window.number, 0);
+ }
+ function addUsedProps(no, thresh){
+ var temp = no;
+ if (countDots(""+temp) > 0){
+ var list = (""+no).split(".");
+ var child_id = list[list.length-1];
+ } else{
+ var child_id = (""+no)
+ }
+ while (child_id > thresh){
+ if (!contains(removedIndexes, temp)){
+ var key = document.getElementById("i"+temp).value;
+ usedProps.push(getFullName(temp, key));
+ if (childno[temp] != -1 && childno[temp] != 0){
+ var temp2 = ""+temp+"."+childno[temp];
+ var thresh2 = 0;
+ addUsedProps(temp2, thresh2);
+ }
+ }
+ let dots = countDots(""+temp);
+ if (dots > 0){
+ var temp2 = temp.split(".").slice(0, -1);
+ var newNo = temp.split(".")[temp2.length];
+ newNo = parseInt(newNo)-1;
+ temp2.push(newNo);
+ temp = temp2.join(".");
+ child_id--;
+ }
+ if (dots == 0){
+ temp = parseInt(temp)-1;
+ child_id--;
+ }
+ }
+ }
+ function startSending(){
+ if (method === "POST"){
+ var url = document.getElementById("url").value;
+ generateJSON();
+ var jsonobj = JSON.stringify(window.json);
+ var xmlhttp = new XMLHttpRequest();
+ xmlhttp.open("POST", url, false);
+ xmlhttp.setRequestHeader("Content-Type", "application/json");
+ xmlhttp.send(jsonobj);
+ document.getElementById("reponsestatus").value = ""+xmlhttp.status+" "+xmlhttp.statusText;
+ document.getElementById("response").innerHTML = JSON.stringify(JSON.parse(xmlhttp.responseText), undefined, 4);
+ }else if (method === "GET") {
+ var url = document.getElementById("url2").value;
+ window.open(url);
+ }
+ }
+ function changeVisibility() {
+ var x = document.getElementById("url");
+ var y = document.getElementById("addRow");
+ var z = document.getElementById("showJSON");
+ var a = document.getElementById("pasteJSON");
+ if (x.style.display === "none") {
+ x.style.display = "inline-block";
+ y.style.display = "inline-block";
+ z.style.display = "inline-block";
+ a.style.display = "inline-block";
+ } else {
+ x.style.display = "none";
+ y.style.display = "none";
+ z.style.display = "none";
+ a.style.display = "none";
+ }
+ }
+ function showJSON(){
+ var textarea = document.getElementById("jsonquery");
+ var copyJSON = document.getElementById("copyJSON");
+ var showJSON = document.getElementById("showJSON");
+ var copyURL = document.getElementById("copyURL")
+ if (textarea.style.display === "none") {
+ textarea.style.display = "inline-block";
+ copyJSON.style.display = "inline-block";
+ showJSON.innerHTML = "Hide query JSON";
+ copyURL.style.display = "inline-block";
+ generateJSON();
+ } else {
+ copyJSON.style.display = "none";
+ textarea.style.display = "none";
+ showJSON.innerHTML = "Show query JSON";
+ copyURL.style.display = "none";
+ }
+ }
+ function getSearchApiReference(){
+ var div = document.getElementById("div");
+ var object = document.createElement("object");
+ object.data = "/querybuilder/_includes/search-api-reference.html";
+ object.type ="text/html";
+ object.style = "visibility: hidden; height: 1px; width: 200px;"
+ div.appendChild(object);
+ setTimeout(function(){
+ searchApiReference = object.contentDocument;
+ setTimeout(function(){
+ div.removeChild(object);
+ }, 20);
+ }, 150);
+ }
+ function changeInformation(no,key){
+ if (key===""){return "Choose a parameter for information"}
+ if (getFullName(no,key) in childrenProps){return "Add parameters under the parent ''" + getFullName(no,key) + "'"}
+ var refId = getFullName(no,key);
+ var ref = searchApiReference;
+ var tabletext = stripMyHTML(ref.getElementById(refId).nextElementSibling.outerHTML);
+ return tabletext;
+ }
+ function getFullName(no, key){
+ var name = key;
+ no = ""+no;
+ no = no.split(".").slice(0,-1).join(".");
+ while (no.length > 0){
+ var parentName = document.getElementById("i"+no).value;
+ name = parentName + "." + name;
+ no = no.split(".").slice(0,-1).join(".");
+ }
+ return name;
+ }
+ </script>
+
+ <!-- Global Site Tag (gtag.js) - Google Analytics -->
+ <script async src="https://www.googletagmanager.com/gtag/js?id=UA-107187180-1"></script>
+ <script>
+ window.dataLayer = window.dataLayer || [];
+ function gtag(){dataLayer.push(arguments)};
+ gtag('js', new Date());
+ gtag('config', 'UA-107187180-1');
+ </script>
+ </body>
+</html>
diff --git a/container-search-gui/src/main/resources/gui/css/font-awesome/css/font-awesome.css b/container-search-gui/src/main/resources/gui/css/font-awesome/css/font-awesome.css
new file mode 100644
index 00000000000..b2a5fe2f253
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/css/font-awesome/css/font-awesome.css
@@ -0,0 +1,2086 @@
+/*!
+ * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome
+ * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+ */
+/* FONT PATH
+ * -------------------------- */
+@font-face {
+ font-family: 'FontAwesome';
+ src: url('../fonts/fontawesome-webfont.eot?v=4.5.0');
+ src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.5.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.5.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.5.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.5.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular') format('svg');
+ font-weight: normal;
+ font-style: normal;
+}
+.fa {
+ display: inline-block;
+ font: normal normal normal 14px/1 FontAwesome;
+ font-size: inherit;
+ text-rendering: auto;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+/* makes the font 33% larger relative to the icon container */
+.fa-lg {
+ font-size: 1.33333333em;
+ line-height: 0.75em;
+ vertical-align: -15%;
+}
+.fa-2x {
+ font-size: 2em;
+}
+.fa-3x {
+ font-size: 3em;
+}
+.fa-4x {
+ font-size: 4em;
+}
+.fa-5x {
+ font-size: 5em;
+}
+.fa-fw {
+ width: 1.28571429em;
+ text-align: center;
+}
+.fa-ul {
+ padding-left: 0;
+ margin-left: 2.14285714em;
+ list-style-type: none;
+}
+.fa-ul > li {
+ position: relative;
+}
+.fa-li {
+ position: absolute;
+ left: -2.14285714em;
+ width: 2.14285714em;
+ top: 0.14285714em;
+ text-align: center;
+}
+.fa-li.fa-lg {
+ left: -1.85714286em;
+}
+.fa-border {
+ padding: .2em .25em .15em;
+ border: solid 0.08em #eeeeee;
+ border-radius: .1em;
+}
+.fa-pull-left {
+ float: left;
+}
+.fa-pull-right {
+ float: right;
+}
+.fa.fa-pull-left {
+ margin-right: .3em;
+}
+.fa.fa-pull-right {
+ margin-left: .3em;
+}
+/* Deprecated as of 4.4.0 */
+.pull-right {
+ float: right;
+}
+.pull-left {
+ float: left;
+}
+.fa.pull-left {
+ margin-right: .3em;
+}
+.fa.pull-right {
+ margin-left: .3em;
+}
+.fa-spin {
+ -webkit-animation: fa-spin 2s infinite linear;
+ animation: fa-spin 2s infinite linear;
+}
+.fa-pulse {
+ -webkit-animation: fa-spin 1s infinite steps(8);
+ animation: fa-spin 1s infinite steps(8);
+}
+@-webkit-keyframes fa-spin {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(359deg);
+ transform: rotate(359deg);
+ }
+}
+@keyframes fa-spin {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(359deg);
+ transform: rotate(359deg);
+ }
+}
+.fa-rotate-90 {
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
+ -webkit-transform: rotate(90deg);
+ -ms-transform: rotate(90deg);
+ transform: rotate(90deg);
+}
+.fa-rotate-180 {
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2);
+ -webkit-transform: rotate(180deg);
+ -ms-transform: rotate(180deg);
+ transform: rotate(180deg);
+}
+.fa-rotate-270 {
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
+ -webkit-transform: rotate(270deg);
+ -ms-transform: rotate(270deg);
+ transform: rotate(270deg);
+}
+.fa-flip-horizontal {
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);
+ -webkit-transform: scale(-1, 1);
+ -ms-transform: scale(-1, 1);
+ transform: scale(-1, 1);
+}
+.fa-flip-vertical {
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);
+ -webkit-transform: scale(1, -1);
+ -ms-transform: scale(1, -1);
+ transform: scale(1, -1);
+}
+:root .fa-rotate-90,
+:root .fa-rotate-180,
+:root .fa-rotate-270,
+:root .fa-flip-horizontal,
+:root .fa-flip-vertical {
+ filter: none;
+}
+.fa-stack {
+ position: relative;
+ display: inline-block;
+ width: 2em;
+ height: 2em;
+ line-height: 2em;
+ vertical-align: middle;
+}
+.fa-stack-1x,
+.fa-stack-2x {
+ position: absolute;
+ left: 0;
+ width: 100%;
+ text-align: center;
+}
+.fa-stack-1x {
+ line-height: inherit;
+}
+.fa-stack-2x {
+ font-size: 2em;
+}
+.fa-inverse {
+ color: #ffffff;
+}
+/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen
+ readers do not read off random characters that represent icons */
+.fa-glass:before {
+ content: "\f000";
+}
+.fa-music:before {
+ content: "\f001";
+}
+.fa-search:before {
+ content: "\f002";
+}
+.fa-envelope-o:before {
+ content: "\f003";
+}
+.fa-heart:before {
+ content: "\f004";
+}
+.fa-star:before {
+ content: "\f005";
+}
+.fa-star-o:before {
+ content: "\f006";
+}
+.fa-user:before {
+ content: "\f007";
+}
+.fa-film:before {
+ content: "\f008";
+}
+.fa-th-large:before {
+ content: "\f009";
+}
+.fa-th:before {
+ content: "\f00a";
+}
+.fa-th-list:before {
+ content: "\f00b";
+}
+.fa-check:before {
+ content: "\f00c";
+}
+.fa-remove:before,
+.fa-close:before,
+.fa-times:before {
+ content: "\f00d";
+}
+.fa-search-plus:before {
+ content: "\f00e";
+}
+.fa-search-minus:before {
+ content: "\f010";
+}
+.fa-power-off:before {
+ content: "\f011";
+}
+.fa-signal:before {
+ content: "\f012";
+}
+.fa-gear:before,
+.fa-cog:before {
+ content: "\f013";
+}
+.fa-trash-o:before {
+ content: "\f014";
+}
+.fa-home:before {
+ content: "\f015";
+}
+.fa-file-o:before {
+ content: "\f016";
+}
+.fa-clock-o:before {
+ content: "\f017";
+}
+.fa-road:before {
+ content: "\f018";
+}
+.fa-download:before {
+ content: "\f019";
+}
+.fa-arrow-circle-o-down:before {
+ content: "\f01a";
+}
+.fa-arrow-circle-o-up:before {
+ content: "\f01b";
+}
+.fa-inbox:before {
+ content: "\f01c";
+}
+.fa-play-circle-o:before {
+ content: "\f01d";
+}
+.fa-rotate-right:before,
+.fa-repeat:before {
+ content: "\f01e";
+}
+.fa-refresh:before {
+ content: "\f021";
+}
+.fa-list-alt:before {
+ content: "\f022";
+}
+.fa-lock:before {
+ content: "\f023";
+}
+.fa-flag:before {
+ content: "\f024";
+}
+.fa-headphones:before {
+ content: "\f025";
+}
+.fa-volume-off:before {
+ content: "\f026";
+}
+.fa-volume-down:before {
+ content: "\f027";
+}
+.fa-volume-up:before {
+ content: "\f028";
+}
+.fa-qrcode:before {
+ content: "\f029";
+}
+.fa-barcode:before {
+ content: "\f02a";
+}
+.fa-tag:before {
+ content: "\f02b";
+}
+.fa-tags:before {
+ content: "\f02c";
+}
+.fa-book:before {
+ content: "\f02d";
+}
+.fa-bookmark:before {
+ content: "\f02e";
+}
+.fa-print:before {
+ content: "\f02f";
+}
+.fa-camera:before {
+ content: "\f030";
+}
+.fa-font:before {
+ content: "\f031";
+}
+.fa-bold:before {
+ content: "\f032";
+}
+.fa-italic:before {
+ content: "\f033";
+}
+.fa-text-height:before {
+ content: "\f034";
+}
+.fa-text-width:before {
+ content: "\f035";
+}
+.fa-align-left:before {
+ content: "\f036";
+}
+.fa-align-center:before {
+ content: "\f037";
+}
+.fa-align-right:before {
+ content: "\f038";
+}
+.fa-align-justify:before {
+ content: "\f039";
+}
+.fa-list:before {
+ content: "\f03a";
+}
+.fa-dedent:before,
+.fa-outdent:before {
+ content: "\f03b";
+}
+.fa-indent:before {
+ content: "\f03c";
+}
+.fa-video-camera:before {
+ content: "\f03d";
+}
+.fa-photo:before,
+.fa-image:before,
+.fa-picture-o:before {
+ content: "\f03e";
+}
+.fa-pencil:before {
+ content: "\f040";
+}
+.fa-map-marker:before {
+ content: "\f041";
+}
+.fa-adjust:before {
+ content: "\f042";
+}
+.fa-tint:before {
+ content: "\f043";
+}
+.fa-edit:before,
+.fa-pencil-square-o:before {
+ content: "\f044";
+}
+.fa-share-square-o:before {
+ content: "\f045";
+}
+.fa-check-square-o:before {
+ content: "\f046";
+}
+.fa-arrows:before {
+ content: "\f047";
+}
+.fa-step-backward:before {
+ content: "\f048";
+}
+.fa-fast-backward:before {
+ content: "\f049";
+}
+.fa-backward:before {
+ content: "\f04a";
+}
+.fa-play:before {
+ content: "\f04b";
+}
+.fa-pause:before {
+ content: "\f04c";
+}
+.fa-stop:before {
+ content: "\f04d";
+}
+.fa-forward:before {
+ content: "\f04e";
+}
+.fa-fast-forward:before {
+ content: "\f050";
+}
+.fa-step-forward:before {
+ content: "\f051";
+}
+.fa-eject:before {
+ content: "\f052";
+}
+.fa-chevron-left:before {
+ content: "\f053";
+}
+.fa-chevron-right:before {
+ content: "\f054";
+}
+.fa-plus-circle:before {
+ content: "\f055";
+}
+.fa-minus-circle:before {
+ content: "\f056";
+}
+.fa-times-circle:before {
+ content: "\f057";
+}
+.fa-check-circle:before {
+ content: "\f058";
+}
+.fa-question-circle:before {
+ content: "\f059";
+}
+.fa-info-circle:before {
+ content: "\f05a";
+}
+.fa-crosshairs:before {
+ content: "\f05b";
+}
+.fa-times-circle-o:before {
+ content: "\f05c";
+}
+.fa-check-circle-o:before {
+ content: "\f05d";
+}
+.fa-ban:before {
+ content: "\f05e";
+}
+.fa-arrow-left:before {
+ content: "\f060";
+}
+.fa-arrow-right:before {
+ content: "\f061";
+}
+.fa-arrow-up:before {
+ content: "\f062";
+}
+.fa-arrow-down:before {
+ content: "\f063";
+}
+.fa-mail-forward:before,
+.fa-share:before {
+ content: "\f064";
+}
+.fa-expand:before {
+ content: "\f065";
+}
+.fa-compress:before {
+ content: "\f066";
+}
+.fa-plus:before {
+ content: "\f067";
+}
+.fa-minus:before {
+ content: "\f068";
+}
+.fa-asterisk:before {
+ content: "\f069";
+}
+.fa-exclamation-circle:before {
+ content: "\f06a";
+}
+.fa-gift:before {
+ content: "\f06b";
+}
+.fa-leaf:before {
+ content: "\f06c";
+}
+.fa-fire:before {
+ content: "\f06d";
+}
+.fa-eye:before {
+ content: "\f06e";
+}
+.fa-eye-slash:before {
+ content: "\f070";
+}
+.fa-warning:before,
+.fa-exclamation-triangle:before {
+ content: "\f071";
+}
+.fa-plane:before {
+ content: "\f072";
+}
+.fa-calendar:before {
+ content: "\f073";
+}
+.fa-random:before {
+ content: "\f074";
+}
+.fa-comment:before {
+ content: "\f075";
+}
+.fa-magnet:before {
+ content: "\f076";
+}
+.fa-chevron-up:before {
+ content: "\f077";
+}
+.fa-chevron-down:before {
+ content: "\f078";
+}
+.fa-retweet:before {
+ content: "\f079";
+}
+.fa-shopping-cart:before {
+ content: "\f07a";
+}
+.fa-folder:before {
+ content: "\f07b";
+}
+.fa-folder-open:before {
+ content: "\f07c";
+}
+.fa-arrows-v:before {
+ content: "\f07d";
+}
+.fa-arrows-h:before {
+ content: "\f07e";
+}
+.fa-bar-chart-o:before,
+.fa-bar-chart:before {
+ content: "\f080";
+}
+.fa-twitter-square:before {
+ content: "\f081";
+}
+.fa-facebook-square:before {
+ content: "\f082";
+}
+.fa-camera-retro:before {
+ content: "\f083";
+}
+.fa-key:before {
+ content: "\f084";
+}
+.fa-gears:before,
+.fa-cogs:before {
+ content: "\f085";
+}
+.fa-comments:before {
+ content: "\f086";
+}
+.fa-thumbs-o-up:before {
+ content: "\f087";
+}
+.fa-thumbs-o-down:before {
+ content: "\f088";
+}
+.fa-star-half:before {
+ content: "\f089";
+}
+.fa-heart-o:before {
+ content: "\f08a";
+}
+.fa-sign-out:before {
+ content: "\f08b";
+}
+.fa-linkedin-square:before {
+ content: "\f08c";
+}
+.fa-thumb-tack:before {
+ content: "\f08d";
+}
+.fa-external-link:before {
+ content: "\f08e";
+}
+.fa-sign-in:before {
+ content: "\f090";
+}
+.fa-trophy:before {
+ content: "\f091";
+}
+.fa-github-square:before {
+ content: "\f092";
+}
+.fa-upload:before {
+ content: "\f093";
+}
+.fa-lemon-o:before {
+ content: "\f094";
+}
+.fa-phone:before {
+ content: "\f095";
+}
+.fa-square-o:before {
+ content: "\f096";
+}
+.fa-bookmark-o:before {
+ content: "\f097";
+}
+.fa-phone-square:before {
+ content: "\f098";
+}
+.fa-twitter:before {
+ content: "\f099";
+}
+.fa-facebook-f:before,
+.fa-facebook:before {
+ content: "\f09a";
+}
+.fa-github:before {
+ content: "\f09b";
+}
+.fa-unlock:before {
+ content: "\f09c";
+}
+.fa-credit-card:before {
+ content: "\f09d";
+}
+.fa-feed:before,
+.fa-rss:before {
+ content: "\f09e";
+}
+.fa-hdd-o:before {
+ content: "\f0a0";
+}
+.fa-bullhorn:before {
+ content: "\f0a1";
+}
+.fa-bell:before {
+ content: "\f0f3";
+}
+.fa-certificate:before {
+ content: "\f0a3";
+}
+.fa-hand-o-right:before {
+ content: "\f0a4";
+}
+.fa-hand-o-left:before {
+ content: "\f0a5";
+}
+.fa-hand-o-up:before {
+ content: "\f0a6";
+}
+.fa-hand-o-down:before {
+ content: "\f0a7";
+}
+.fa-arrow-circle-left:before {
+ content: "\f0a8";
+}
+.fa-arrow-circle-right:before {
+ content: "\f0a9";
+}
+.fa-arrow-circle-up:before {
+ content: "\f0aa";
+}
+.fa-arrow-circle-down:before {
+ content: "\f0ab";
+}
+.fa-globe:before {
+ content: "\f0ac";
+}
+.fa-wrench:before {
+ content: "\f0ad";
+}
+.fa-tasks:before {
+ content: "\f0ae";
+}
+.fa-filter:before {
+ content: "\f0b0";
+}
+.fa-briefcase:before {
+ content: "\f0b1";
+}
+.fa-arrows-alt:before {
+ content: "\f0b2";
+}
+.fa-group:before,
+.fa-users:before {
+ content: "\f0c0";
+}
+.fa-chain:before,
+.fa-link:before {
+ content: "\f0c1";
+}
+.fa-cloud:before {
+ content: "\f0c2";
+}
+.fa-flask:before {
+ content: "\f0c3";
+}
+.fa-cut:before,
+.fa-scissors:before {
+ content: "\f0c4";
+}
+.fa-copy:before,
+.fa-files-o:before {
+ content: "\f0c5";
+}
+.fa-paperclip:before {
+ content: "\f0c6";
+}
+.fa-save:before,
+.fa-floppy-o:before {
+ content: "\f0c7";
+}
+.fa-square:before {
+ content: "\f0c8";
+}
+.fa-navicon:before,
+.fa-reorder:before,
+.fa-bars:before {
+ content: "\f0c9";
+}
+.fa-list-ul:before {
+ content: "\f0ca";
+}
+.fa-list-ol:before {
+ content: "\f0cb";
+}
+.fa-strikethrough:before {
+ content: "\f0cc";
+}
+.fa-underline:before {
+ content: "\f0cd";
+}
+.fa-table:before {
+ content: "\f0ce";
+}
+.fa-magic:before {
+ content: "\f0d0";
+}
+.fa-truck:before {
+ content: "\f0d1";
+}
+.fa-pinterest:before {
+ content: "\f0d2";
+}
+.fa-pinterest-square:before {
+ content: "\f0d3";
+}
+.fa-google-plus-square:before {
+ content: "\f0d4";
+}
+.fa-google-plus:before {
+ content: "\f0d5";
+}
+.fa-money:before {
+ content: "\f0d6";
+}
+.fa-caret-down:before {
+ content: "\f0d7";
+}
+.fa-caret-up:before {
+ content: "\f0d8";
+}
+.fa-caret-left:before {
+ content: "\f0d9";
+}
+.fa-caret-right:before {
+ content: "\f0da";
+}
+.fa-columns:before {
+ content: "\f0db";
+}
+.fa-unsorted:before,
+.fa-sort:before {
+ content: "\f0dc";
+}
+.fa-sort-down:before,
+.fa-sort-desc:before {
+ content: "\f0dd";
+}
+.fa-sort-up:before,
+.fa-sort-asc:before {
+ content: "\f0de";
+}
+.fa-envelope:before {
+ content: "\f0e0";
+}
+.fa-linkedin:before {
+ content: "\f0e1";
+}
+.fa-rotate-left:before,
+.fa-undo:before {
+ content: "\f0e2";
+}
+.fa-legal:before,
+.fa-gavel:before {
+ content: "\f0e3";
+}
+.fa-dashboard:before,
+.fa-tachometer:before {
+ content: "\f0e4";
+}
+.fa-comment-o:before {
+ content: "\f0e5";
+}
+.fa-comments-o:before {
+ content: "\f0e6";
+}
+.fa-flash:before,
+.fa-bolt:before {
+ content: "\f0e7";
+}
+.fa-sitemap:before {
+ content: "\f0e8";
+}
+.fa-umbrella:before {
+ content: "\f0e9";
+}
+.fa-paste:before,
+.fa-clipboard:before {
+ content: "\f0ea";
+}
+.fa-lightbulb-o:before {
+ content: "\f0eb";
+}
+.fa-exchange:before {
+ content: "\f0ec";
+}
+.fa-cloud-download:before {
+ content: "\f0ed";
+}
+.fa-cloud-upload:before {
+ content: "\f0ee";
+}
+.fa-user-md:before {
+ content: "\f0f0";
+}
+.fa-stethoscope:before {
+ content: "\f0f1";
+}
+.fa-suitcase:before {
+ content: "\f0f2";
+}
+.fa-bell-o:before {
+ content: "\f0a2";
+}
+.fa-coffee:before {
+ content: "\f0f4";
+}
+.fa-cutlery:before {
+ content: "\f0f5";
+}
+.fa-file-text-o:before {
+ content: "\f0f6";
+}
+.fa-building-o:before {
+ content: "\f0f7";
+}
+.fa-hospital-o:before {
+ content: "\f0f8";
+}
+.fa-ambulance:before {
+ content: "\f0f9";
+}
+.fa-medkit:before {
+ content: "\f0fa";
+}
+.fa-fighter-jet:before {
+ content: "\f0fb";
+}
+.fa-beer:before {
+ content: "\f0fc";
+}
+.fa-h-square:before {
+ content: "\f0fd";
+}
+.fa-plus-square:before {
+ content: "\f0fe";
+}
+.fa-angle-double-left:before {
+ content: "\f100";
+}
+.fa-angle-double-right:before {
+ content: "\f101";
+}
+.fa-angle-double-up:before {
+ content: "\f102";
+}
+.fa-angle-double-down:before {
+ content: "\f103";
+}
+.fa-angle-left:before {
+ content: "\f104";
+}
+.fa-angle-right:before {
+ content: "\f105";
+}
+.fa-angle-up:before {
+ content: "\f106";
+}
+.fa-angle-down:before {
+ content: "\f107";
+}
+.fa-desktop:before {
+ content: "\f108";
+}
+.fa-laptop:before {
+ content: "\f109";
+}
+.fa-tablet:before {
+ content: "\f10a";
+}
+.fa-mobile-phone:before,
+.fa-mobile:before {
+ content: "\f10b";
+}
+.fa-circle-o:before {
+ content: "\f10c";
+}
+.fa-quote-left:before {
+ content: "\f10d";
+}
+.fa-quote-right:before {
+ content: "\f10e";
+}
+.fa-spinner:before {
+ content: "\f110";
+}
+.fa-circle:before {
+ content: "\f111";
+}
+.fa-mail-reply:before,
+.fa-reply:before {
+ content: "\f112";
+}
+.fa-github-alt:before {
+ content: "\f113";
+}
+.fa-folder-o:before {
+ content: "\f114";
+}
+.fa-folder-open-o:before {
+ content: "\f115";
+}
+.fa-smile-o:before {
+ content: "\f118";
+}
+.fa-frown-o:before {
+ content: "\f119";
+}
+.fa-meh-o:before {
+ content: "\f11a";
+}
+.fa-gamepad:before {
+ content: "\f11b";
+}
+.fa-keyboard-o:before {
+ content: "\f11c";
+}
+.fa-flag-o:before {
+ content: "\f11d";
+}
+.fa-flag-checkered:before {
+ content: "\f11e";
+}
+.fa-terminal:before {
+ content: "\f120";
+}
+.fa-code:before {
+ content: "\f121";
+}
+.fa-mail-reply-all:before,
+.fa-reply-all:before {
+ content: "\f122";
+}
+.fa-star-half-empty:before,
+.fa-star-half-full:before,
+.fa-star-half-o:before {
+ content: "\f123";
+}
+.fa-location-arrow:before {
+ content: "\f124";
+}
+.fa-crop:before {
+ content: "\f125";
+}
+.fa-code-fork:before {
+ content: "\f126";
+}
+.fa-unlink:before,
+.fa-chain-broken:before {
+ content: "\f127";
+}
+.fa-question:before {
+ content: "\f128";
+}
+.fa-info:before {
+ content: "\f129";
+}
+.fa-exclamation:before {
+ content: "\f12a";
+}
+.fa-superscript:before {
+ content: "\f12b";
+}
+.fa-subscript:before {
+ content: "\f12c";
+}
+.fa-eraser:before {
+ content: "\f12d";
+}
+.fa-puzzle-piece:before {
+ content: "\f12e";
+}
+.fa-microphone:before {
+ content: "\f130";
+}
+.fa-microphone-slash:before {
+ content: "\f131";
+}
+.fa-shield:before {
+ content: "\f132";
+}
+.fa-calendar-o:before {
+ content: "\f133";
+}
+.fa-fire-extinguisher:before {
+ content: "\f134";
+}
+.fa-rocket:before {
+ content: "\f135";
+}
+.fa-maxcdn:before {
+ content: "\f136";
+}
+.fa-chevron-circle-left:before {
+ content: "\f137";
+}
+.fa-chevron-circle-right:before {
+ content: "\f138";
+}
+.fa-chevron-circle-up:before {
+ content: "\f139";
+}
+.fa-chevron-circle-down:before {
+ content: "\f13a";
+}
+.fa-html5:before {
+ content: "\f13b";
+}
+.fa-css3:before {
+ content: "\f13c";
+}
+.fa-anchor:before {
+ content: "\f13d";
+}
+.fa-unlock-alt:before {
+ content: "\f13e";
+}
+.fa-bullseye:before {
+ content: "\f140";
+}
+.fa-ellipsis-h:before {
+ content: "\f141";
+}
+.fa-ellipsis-v:before {
+ content: "\f142";
+}
+.fa-rss-square:before {
+ content: "\f143";
+}
+.fa-play-circle:before {
+ content: "\f144";
+}
+.fa-ticket:before {
+ content: "\f145";
+}
+.fa-minus-square:before {
+ content: "\f146";
+}
+.fa-minus-square-o:before {
+ content: "\f147";
+}
+.fa-level-up:before {
+ content: "\f148";
+}
+.fa-level-down:before {
+ content: "\f149";
+}
+.fa-check-square:before {
+ content: "\f14a";
+}
+.fa-pencil-square:before {
+ content: "\f14b";
+}
+.fa-external-link-square:before {
+ content: "\f14c";
+}
+.fa-share-square:before {
+ content: "\f14d";
+}
+.fa-compass:before {
+ content: "\f14e";
+}
+.fa-toggle-down:before,
+.fa-caret-square-o-down:before {
+ content: "\f150";
+}
+.fa-toggle-up:before,
+.fa-caret-square-o-up:before {
+ content: "\f151";
+}
+.fa-toggle-right:before,
+.fa-caret-square-o-right:before {
+ content: "\f152";
+}
+.fa-euro:before,
+.fa-eur:before {
+ content: "\f153";
+}
+.fa-gbp:before {
+ content: "\f154";
+}
+.fa-dollar:before,
+.fa-usd:before {
+ content: "\f155";
+}
+.fa-rupee:before,
+.fa-inr:before {
+ content: "\f156";
+}
+.fa-cny:before,
+.fa-rmb:before,
+.fa-yen:before,
+.fa-jpy:before {
+ content: "\f157";
+}
+.fa-ruble:before,
+.fa-rouble:before,
+.fa-rub:before {
+ content: "\f158";
+}
+.fa-won:before,
+.fa-krw:before {
+ content: "\f159";
+}
+.fa-bitcoin:before,
+.fa-btc:before {
+ content: "\f15a";
+}
+.fa-file:before {
+ content: "\f15b";
+}
+.fa-file-text:before {
+ content: "\f15c";
+}
+.fa-sort-alpha-asc:before {
+ content: "\f15d";
+}
+.fa-sort-alpha-desc:before {
+ content: "\f15e";
+}
+.fa-sort-amount-asc:before {
+ content: "\f160";
+}
+.fa-sort-amount-desc:before {
+ content: "\f161";
+}
+.fa-sort-numeric-asc:before {
+ content: "\f162";
+}
+.fa-sort-numeric-desc:before {
+ content: "\f163";
+}
+.fa-thumbs-up:before {
+ content: "\f164";
+}
+.fa-thumbs-down:before {
+ content: "\f165";
+}
+.fa-youtube-square:before {
+ content: "\f166";
+}
+.fa-youtube:before {
+ content: "\f167";
+}
+.fa-xing:before {
+ content: "\f168";
+}
+.fa-xing-square:before {
+ content: "\f169";
+}
+.fa-youtube-play:before {
+ content: "\f16a";
+}
+.fa-dropbox:before {
+ content: "\f16b";
+}
+.fa-stack-overflow:before {
+ content: "\f16c";
+}
+.fa-instagram:before {
+ content: "\f16d";
+}
+.fa-flickr:before {
+ content: "\f16e";
+}
+.fa-adn:before {
+ content: "\f170";
+}
+.fa-bitbucket:before {
+ content: "\f171";
+}
+.fa-bitbucket-square:before {
+ content: "\f172";
+}
+.fa-tumblr:before {
+ content: "\f173";
+}
+.fa-tumblr-square:before {
+ content: "\f174";
+}
+.fa-long-arrow-down:before {
+ content: "\f175";
+}
+.fa-long-arrow-up:before {
+ content: "\f176";
+}
+.fa-long-arrow-left:before {
+ content: "\f177";
+}
+.fa-long-arrow-right:before {
+ content: "\f178";
+}
+.fa-apple:before {
+ content: "\f179";
+}
+.fa-windows:before {
+ content: "\f17a";
+}
+.fa-android:before {
+ content: "\f17b";
+}
+.fa-linux:before {
+ content: "\f17c";
+}
+.fa-dribbble:before {
+ content: "\f17d";
+}
+.fa-skype:before {
+ content: "\f17e";
+}
+.fa-foursquare:before {
+ content: "\f180";
+}
+.fa-trello:before {
+ content: "\f181";
+}
+.fa-female:before {
+ content: "\f182";
+}
+.fa-male:before {
+ content: "\f183";
+}
+.fa-gittip:before,
+.fa-gratipay:before {
+ content: "\f184";
+}
+.fa-sun-o:before {
+ content: "\f185";
+}
+.fa-moon-o:before {
+ content: "\f186";
+}
+.fa-archive:before {
+ content: "\f187";
+}
+.fa-bug:before {
+ content: "\f188";
+}
+.fa-vk:before {
+ content: "\f189";
+}
+.fa-weibo:before {
+ content: "\f18a";
+}
+.fa-renren:before {
+ content: "\f18b";
+}
+.fa-pagelines:before {
+ content: "\f18c";
+}
+.fa-stack-exchange:before {
+ content: "\f18d";
+}
+.fa-arrow-circle-o-right:before {
+ content: "\f18e";
+}
+.fa-arrow-circle-o-left:before {
+ content: "\f190";
+}
+.fa-toggle-left:before,
+.fa-caret-square-o-left:before {
+ content: "\f191";
+}
+.fa-dot-circle-o:before {
+ content: "\f192";
+}
+.fa-wheelchair:before {
+ content: "\f193";
+}
+.fa-vimeo-square:before {
+ content: "\f194";
+}
+.fa-turkish-lira:before,
+.fa-try:before {
+ content: "\f195";
+}
+.fa-plus-square-o:before {
+ content: "\f196";
+}
+.fa-space-shuttle:before {
+ content: "\f197";
+}
+.fa-slack:before {
+ content: "\f198";
+}
+.fa-envelope-square:before {
+ content: "\f199";
+}
+.fa-wordpress:before {
+ content: "\f19a";
+}
+.fa-openid:before {
+ content: "\f19b";
+}
+.fa-institution:before,
+.fa-bank:before,
+.fa-university:before {
+ content: "\f19c";
+}
+.fa-mortar-board:before,
+.fa-graduation-cap:before {
+ content: "\f19d";
+}
+.fa-yahoo:before {
+ content: "\f19e";
+}
+.fa-google:before {
+ content: "\f1a0";
+}
+.fa-reddit:before {
+ content: "\f1a1";
+}
+.fa-reddit-square:before {
+ content: "\f1a2";
+}
+.fa-stumbleupon-circle:before {
+ content: "\f1a3";
+}
+.fa-stumbleupon:before {
+ content: "\f1a4";
+}
+.fa-delicious:before {
+ content: "\f1a5";
+}
+.fa-digg:before {
+ content: "\f1a6";
+}
+.fa-pied-piper:before {
+ content: "\f1a7";
+}
+.fa-pied-piper-alt:before {
+ content: "\f1a8";
+}
+.fa-drupal:before {
+ content: "\f1a9";
+}
+.fa-joomla:before {
+ content: "\f1aa";
+}
+.fa-language:before {
+ content: "\f1ab";
+}
+.fa-fax:before {
+ content: "\f1ac";
+}
+.fa-building:before {
+ content: "\f1ad";
+}
+.fa-child:before {
+ content: "\f1ae";
+}
+.fa-paw:before {
+ content: "\f1b0";
+}
+.fa-spoon:before {
+ content: "\f1b1";
+}
+.fa-cube:before {
+ content: "\f1b2";
+}
+.fa-cubes:before {
+ content: "\f1b3";
+}
+.fa-behance:before {
+ content: "\f1b4";
+}
+.fa-behance-square:before {
+ content: "\f1b5";
+}
+.fa-steam:before {
+ content: "\f1b6";
+}
+.fa-steam-square:before {
+ content: "\f1b7";
+}
+.fa-recycle:before {
+ content: "\f1b8";
+}
+.fa-automobile:before,
+.fa-car:before {
+ content: "\f1b9";
+}
+.fa-cab:before,
+.fa-taxi:before {
+ content: "\f1ba";
+}
+.fa-tree:before {
+ content: "\f1bb";
+}
+.fa-spotify:before {
+ content: "\f1bc";
+}
+.fa-deviantart:before {
+ content: "\f1bd";
+}
+.fa-soundcloud:before {
+ content: "\f1be";
+}
+.fa-database:before {
+ content: "\f1c0";
+}
+.fa-file-pdf-o:before {
+ content: "\f1c1";
+}
+.fa-file-word-o:before {
+ content: "\f1c2";
+}
+.fa-file-excel-o:before {
+ content: "\f1c3";
+}
+.fa-file-powerpoint-o:before {
+ content: "\f1c4";
+}
+.fa-file-photo-o:before,
+.fa-file-picture-o:before,
+.fa-file-image-o:before {
+ content: "\f1c5";
+}
+.fa-file-zip-o:before,
+.fa-file-archive-o:before {
+ content: "\f1c6";
+}
+.fa-file-sound-o:before,
+.fa-file-audio-o:before {
+ content: "\f1c7";
+}
+.fa-file-movie-o:before,
+.fa-file-video-o:before {
+ content: "\f1c8";
+}
+.fa-file-code-o:before {
+ content: "\f1c9";
+}
+.fa-vine:before {
+ content: "\f1ca";
+}
+.fa-codepen:before {
+ content: "\f1cb";
+}
+.fa-jsfiddle:before {
+ content: "\f1cc";
+}
+.fa-life-bouy:before,
+.fa-life-buoy:before,
+.fa-life-saver:before,
+.fa-support:before,
+.fa-life-ring:before {
+ content: "\f1cd";
+}
+.fa-circle-o-notch:before {
+ content: "\f1ce";
+}
+.fa-ra:before,
+.fa-rebel:before {
+ content: "\f1d0";
+}
+.fa-ge:before,
+.fa-empire:before {
+ content: "\f1d1";
+}
+.fa-git-square:before {
+ content: "\f1d2";
+}
+.fa-git:before {
+ content: "\f1d3";
+}
+.fa-y-combinator-square:before,
+.fa-yc-square:before,
+.fa-hacker-news:before {
+ content: "\f1d4";
+}
+.fa-tencent-weibo:before {
+ content: "\f1d5";
+}
+.fa-qq:before {
+ content: "\f1d6";
+}
+.fa-wechat:before,
+.fa-weixin:before {
+ content: "\f1d7";
+}
+.fa-send:before,
+.fa-paper-plane:before {
+ content: "\f1d8";
+}
+.fa-send-o:before,
+.fa-paper-plane-o:before {
+ content: "\f1d9";
+}
+.fa-history:before {
+ content: "\f1da";
+}
+.fa-circle-thin:before {
+ content: "\f1db";
+}
+.fa-header:before {
+ content: "\f1dc";
+}
+.fa-paragraph:before {
+ content: "\f1dd";
+}
+.fa-sliders:before {
+ content: "\f1de";
+}
+.fa-share-alt:before {
+ content: "\f1e0";
+}
+.fa-share-alt-square:before {
+ content: "\f1e1";
+}
+.fa-bomb:before {
+ content: "\f1e2";
+}
+.fa-soccer-ball-o:before,
+.fa-futbol-o:before {
+ content: "\f1e3";
+}
+.fa-tty:before {
+ content: "\f1e4";
+}
+.fa-binoculars:before {
+ content: "\f1e5";
+}
+.fa-plug:before {
+ content: "\f1e6";
+}
+.fa-slideshare:before {
+ content: "\f1e7";
+}
+.fa-twitch:before {
+ content: "\f1e8";
+}
+.fa-yelp:before {
+ content: "\f1e9";
+}
+.fa-newspaper-o:before {
+ content: "\f1ea";
+}
+.fa-wifi:before {
+ content: "\f1eb";
+}
+.fa-calculator:before {
+ content: "\f1ec";
+}
+.fa-paypal:before {
+ content: "\f1ed";
+}
+.fa-google-wallet:before {
+ content: "\f1ee";
+}
+.fa-cc-visa:before {
+ content: "\f1f0";
+}
+.fa-cc-mastercard:before {
+ content: "\f1f1";
+}
+.fa-cc-discover:before {
+ content: "\f1f2";
+}
+.fa-cc-amex:before {
+ content: "\f1f3";
+}
+.fa-cc-paypal:before {
+ content: "\f1f4";
+}
+.fa-cc-stripe:before {
+ content: "\f1f5";
+}
+.fa-bell-slash:before {
+ content: "\f1f6";
+}
+.fa-bell-slash-o:before {
+ content: "\f1f7";
+}
+.fa-trash:before {
+ content: "\f1f8";
+}
+.fa-copyright:before {
+ content: "\f1f9";
+}
+.fa-at:before {
+ content: "\f1fa";
+}
+.fa-eyedropper:before {
+ content: "\f1fb";
+}
+.fa-paint-brush:before {
+ content: "\f1fc";
+}
+.fa-birthday-cake:before {
+ content: "\f1fd";
+}
+.fa-area-chart:before {
+ content: "\f1fe";
+}
+.fa-pie-chart:before {
+ content: "\f200";
+}
+.fa-line-chart:before {
+ content: "\f201";
+}
+.fa-lastfm:before {
+ content: "\f202";
+}
+.fa-lastfm-square:before {
+ content: "\f203";
+}
+.fa-toggle-off:before {
+ content: "\f204";
+}
+.fa-toggle-on:before {
+ content: "\f205";
+}
+.fa-bicycle:before {
+ content: "\f206";
+}
+.fa-bus:before {
+ content: "\f207";
+}
+.fa-ioxhost:before {
+ content: "\f208";
+}
+.fa-angellist:before {
+ content: "\f209";
+}
+.fa-cc:before {
+ content: "\f20a";
+}
+.fa-shekel:before,
+.fa-sheqel:before,
+.fa-ils:before {
+ content: "\f20b";
+}
+.fa-meanpath:before {
+ content: "\f20c";
+}
+.fa-buysellads:before {
+ content: "\f20d";
+}
+.fa-connectdevelop:before {
+ content: "\f20e";
+}
+.fa-dashcube:before {
+ content: "\f210";
+}
+.fa-forumbee:before {
+ content: "\f211";
+}
+.fa-leanpub:before {
+ content: "\f212";
+}
+.fa-sellsy:before {
+ content: "\f213";
+}
+.fa-shirtsinbulk:before {
+ content: "\f214";
+}
+.fa-simplybuilt:before {
+ content: "\f215";
+}
+.fa-skyatlas:before {
+ content: "\f216";
+}
+.fa-cart-plus:before {
+ content: "\f217";
+}
+.fa-cart-arrow-down:before {
+ content: "\f218";
+}
+.fa-diamond:before {
+ content: "\f219";
+}
+.fa-ship:before {
+ content: "\f21a";
+}
+.fa-user-secret:before {
+ content: "\f21b";
+}
+.fa-motorcycle:before {
+ content: "\f21c";
+}
+.fa-street-view:before {
+ content: "\f21d";
+}
+.fa-heartbeat:before {
+ content: "\f21e";
+}
+.fa-venus:before {
+ content: "\f221";
+}
+.fa-mars:before {
+ content: "\f222";
+}
+.fa-mercury:before {
+ content: "\f223";
+}
+.fa-intersex:before,
+.fa-transgender:before {
+ content: "\f224";
+}
+.fa-transgender-alt:before {
+ content: "\f225";
+}
+.fa-venus-double:before {
+ content: "\f226";
+}
+.fa-mars-double:before {
+ content: "\f227";
+}
+.fa-venus-mars:before {
+ content: "\f228";
+}
+.fa-mars-stroke:before {
+ content: "\f229";
+}
+.fa-mars-stroke-v:before {
+ content: "\f22a";
+}
+.fa-mars-stroke-h:before {
+ content: "\f22b";
+}
+.fa-neuter:before {
+ content: "\f22c";
+}
+.fa-genderless:before {
+ content: "\f22d";
+}
+.fa-facebook-official:before {
+ content: "\f230";
+}
+.fa-pinterest-p:before {
+ content: "\f231";
+}
+.fa-whatsapp:before {
+ content: "\f232";
+}
+.fa-server:before {
+ content: "\f233";
+}
+.fa-user-plus:before {
+ content: "\f234";
+}
+.fa-user-times:before {
+ content: "\f235";
+}
+.fa-hotel:before,
+.fa-bed:before {
+ content: "\f236";
+}
+.fa-viacoin:before {
+ content: "\f237";
+}
+.fa-train:before {
+ content: "\f238";
+}
+.fa-subway:before {
+ content: "\f239";
+}
+.fa-medium:before {
+ content: "\f23a";
+}
+.fa-yc:before,
+.fa-y-combinator:before {
+ content: "\f23b";
+}
+.fa-optin-monster:before {
+ content: "\f23c";
+}
+.fa-opencart:before {
+ content: "\f23d";
+}
+.fa-expeditedssl:before {
+ content: "\f23e";
+}
+.fa-battery-4:before,
+.fa-battery-full:before {
+ content: "\f240";
+}
+.fa-battery-3:before,
+.fa-battery-three-quarters:before {
+ content: "\f241";
+}
+.fa-battery-2:before,
+.fa-battery-half:before {
+ content: "\f242";
+}
+.fa-battery-1:before,
+.fa-battery-quarter:before {
+ content: "\f243";
+}
+.fa-battery-0:before,
+.fa-battery-empty:before {
+ content: "\f244";
+}
+.fa-mouse-pointer:before {
+ content: "\f245";
+}
+.fa-i-cursor:before {
+ content: "\f246";
+}
+.fa-object-group:before {
+ content: "\f247";
+}
+.fa-object-ungroup:before {
+ content: "\f248";
+}
+.fa-sticky-note:before {
+ content: "\f249";
+}
+.fa-sticky-note-o:before {
+ content: "\f24a";
+}
+.fa-cc-jcb:before {
+ content: "\f24b";
+}
+.fa-cc-diners-club:before {
+ content: "\f24c";
+}
+.fa-clone:before {
+ content: "\f24d";
+}
+.fa-balance-scale:before {
+ content: "\f24e";
+}
+.fa-hourglass-o:before {
+ content: "\f250";
+}
+.fa-hourglass-1:before,
+.fa-hourglass-start:before {
+ content: "\f251";
+}
+.fa-hourglass-2:before,
+.fa-hourglass-half:before {
+ content: "\f252";
+}
+.fa-hourglass-3:before,
+.fa-hourglass-end:before {
+ content: "\f253";
+}
+.fa-hourglass:before {
+ content: "\f254";
+}
+.fa-hand-grab-o:before,
+.fa-hand-rock-o:before {
+ content: "\f255";
+}
+.fa-hand-stop-o:before,
+.fa-hand-paper-o:before {
+ content: "\f256";
+}
+.fa-hand-scissors-o:before {
+ content: "\f257";
+}
+.fa-hand-lizard-o:before {
+ content: "\f258";
+}
+.fa-hand-spock-o:before {
+ content: "\f259";
+}
+.fa-hand-pointer-o:before {
+ content: "\f25a";
+}
+.fa-hand-peace-o:before {
+ content: "\f25b";
+}
+.fa-trademark:before {
+ content: "\f25c";
+}
+.fa-registered:before {
+ content: "\f25d";
+}
+.fa-creative-commons:before {
+ content: "\f25e";
+}
+.fa-gg:before {
+ content: "\f260";
+}
+.fa-gg-circle:before {
+ content: "\f261";
+}
+.fa-tripadvisor:before {
+ content: "\f262";
+}
+.fa-odnoklassniki:before {
+ content: "\f263";
+}
+.fa-odnoklassniki-square:before {
+ content: "\f264";
+}
+.fa-get-pocket:before {
+ content: "\f265";
+}
+.fa-wikipedia-w:before {
+ content: "\f266";
+}
+.fa-safari:before {
+ content: "\f267";
+}
+.fa-chrome:before {
+ content: "\f268";
+}
+.fa-firefox:before {
+ content: "\f269";
+}
+.fa-opera:before {
+ content: "\f26a";
+}
+.fa-internet-explorer:before {
+ content: "\f26b";
+}
+.fa-tv:before,
+.fa-television:before {
+ content: "\f26c";
+}
+.fa-contao:before {
+ content: "\f26d";
+}
+.fa-500px:before {
+ content: "\f26e";
+}
+.fa-amazon:before {
+ content: "\f270";
+}
+.fa-calendar-plus-o:before {
+ content: "\f271";
+}
+.fa-calendar-minus-o:before {
+ content: "\f272";
+}
+.fa-calendar-times-o:before {
+ content: "\f273";
+}
+.fa-calendar-check-o:before {
+ content: "\f274";
+}
+.fa-industry:before {
+ content: "\f275";
+}
+.fa-map-pin:before {
+ content: "\f276";
+}
+.fa-map-signs:before {
+ content: "\f277";
+}
+.fa-map-o:before {
+ content: "\f278";
+}
+.fa-map:before {
+ content: "\f279";
+}
+.fa-commenting:before {
+ content: "\f27a";
+}
+.fa-commenting-o:before {
+ content: "\f27b";
+}
+.fa-houzz:before {
+ content: "\f27c";
+}
+.fa-vimeo:before {
+ content: "\f27d";
+}
+.fa-black-tie:before {
+ content: "\f27e";
+}
+.fa-fonticons:before {
+ content: "\f280";
+}
+.fa-reddit-alien:before {
+ content: "\f281";
+}
+.fa-edge:before {
+ content: "\f282";
+}
+.fa-credit-card-alt:before {
+ content: "\f283";
+}
+.fa-codiepie:before {
+ content: "\f284";
+}
+.fa-modx:before {
+ content: "\f285";
+}
+.fa-fort-awesome:before {
+ content: "\f286";
+}
+.fa-usb:before {
+ content: "\f287";
+}
+.fa-product-hunt:before {
+ content: "\f288";
+}
+.fa-mixcloud:before {
+ content: "\f289";
+}
+.fa-scribd:before {
+ content: "\f28a";
+}
+.fa-pause-circle:before {
+ content: "\f28b";
+}
+.fa-pause-circle-o:before {
+ content: "\f28c";
+}
+.fa-stop-circle:before {
+ content: "\f28d";
+}
+.fa-stop-circle-o:before {
+ content: "\f28e";
+}
+.fa-shopping-bag:before {
+ content: "\f290";
+}
+.fa-shopping-basket:before {
+ content: "\f291";
+}
+.fa-hashtag:before {
+ content: "\f292";
+}
+.fa-bluetooth:before {
+ content: "\f293";
+}
+.fa-bluetooth-b:before {
+ content: "\f294";
+}
+.fa-percent:before {
+ content: "\f295";
+}
diff --git a/container-search-gui/src/main/resources/gui/css/font-awesome/css/font-awesome.min.css b/container-search-gui/src/main/resources/gui/css/font-awesome/css/font-awesome.min.css
new file mode 100644
index 00000000000..d0603cb4b01
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/css/font-awesome/css/font-awesome.min.css
@@ -0,0 +1,4 @@
+/*!
+ * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome
+ * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+ */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.5.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.5.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.5.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.5.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.5.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}
diff --git a/container-search-gui/src/main/resources/gui/css/font-awesome/fonts/FontAwesome.otf b/container-search-gui/src/main/resources/gui/css/font-awesome/fonts/FontAwesome.otf
new file mode 100644
index 00000000000..3ed7f8b48ad
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/css/font-awesome/fonts/FontAwesome.otf
Binary files differ
diff --git a/container-search-gui/src/main/resources/gui/css/font-awesome/fonts/fontawesome-webfont.eot b/container-search-gui/src/main/resources/gui/css/font-awesome/fonts/fontawesome-webfont.eot
new file mode 100644
index 00000000000..9b6afaedc0f
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/css/font-awesome/fonts/fontawesome-webfont.eot
Binary files differ
diff --git a/container-search-gui/src/main/resources/gui/css/font-awesome/fonts/fontawesome-webfont.svg b/container-search-gui/src/main/resources/gui/css/font-awesome/fonts/fontawesome-webfont.svg
new file mode 100644
index 00000000000..d05688e9e28
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/css/font-awesome/fonts/fontawesome-webfont.svg
@@ -0,0 +1,655 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata></metadata>
+<defs>
+<font id="fontawesomeregular" horiz-adv-x="1536" >
+<font-face units-per-em="1792" ascent="1536" descent="-256" />
+<missing-glyph horiz-adv-x="448" />
+<glyph unicode=" " horiz-adv-x="448" />
+<glyph unicode="&#x09;" horiz-adv-x="448" />
+<glyph unicode="&#xa0;" horiz-adv-x="448" />
+<glyph unicode="&#xa8;" horiz-adv-x="1792" />
+<glyph unicode="&#xa9;" horiz-adv-x="1792" />
+<glyph unicode="&#xae;" horiz-adv-x="1792" />
+<glyph unicode="&#xb4;" horiz-adv-x="1792" />
+<glyph unicode="&#xc6;" horiz-adv-x="1792" />
+<glyph unicode="&#xd8;" horiz-adv-x="1792" />
+<glyph unicode="&#x2000;" horiz-adv-x="768" />
+<glyph unicode="&#x2001;" horiz-adv-x="1537" />
+<glyph unicode="&#x2002;" horiz-adv-x="768" />
+<glyph unicode="&#x2003;" horiz-adv-x="1537" />
+<glyph unicode="&#x2004;" horiz-adv-x="512" />
+<glyph unicode="&#x2005;" horiz-adv-x="384" />
+<glyph unicode="&#x2006;" horiz-adv-x="256" />
+<glyph unicode="&#x2007;" horiz-adv-x="256" />
+<glyph unicode="&#x2008;" horiz-adv-x="192" />
+<glyph unicode="&#x2009;" horiz-adv-x="307" />
+<glyph unicode="&#x200a;" horiz-adv-x="85" />
+<glyph unicode="&#x202f;" horiz-adv-x="307" />
+<glyph unicode="&#x205f;" horiz-adv-x="384" />
+<glyph unicode="&#x2122;" horiz-adv-x="1792" />
+<glyph unicode="&#x221e;" horiz-adv-x="1792" />
+<glyph unicode="&#x2260;" horiz-adv-x="1792" />
+<glyph unicode="&#x25fc;" horiz-adv-x="500" d="M0 0z" />
+<glyph unicode="&#xf000;" horiz-adv-x="1792" d="M1699 1350q0 -35 -43 -78l-632 -632v-768h320q26 0 45 -19t19 -45t-19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45t45 19h320v768l-632 632q-43 43 -43 78q0 23 18 36.5t38 17.5t43 4h1408q23 0 43 -4t38 -17.5t18 -36.5z" />
+<glyph unicode="&#xf001;" d="M1536 1312v-1120q0 -50 -34 -89t-86 -60.5t-103.5 -32t-96.5 -10.5t-96.5 10.5t-103.5 32t-86 60.5t-34 89t34 89t86 60.5t103.5 32t96.5 10.5q105 0 192 -39v537l-768 -237v-709q0 -50 -34 -89t-86 -60.5t-103.5 -32t-96.5 -10.5t-96.5 10.5t-103.5 32t-86 60.5t-34 89 t34 89t86 60.5t103.5 32t96.5 10.5q105 0 192 -39v967q0 31 19 56.5t49 35.5l832 256q12 4 28 4q40 0 68 -28t28 -68z" />
+<glyph unicode="&#xf002;" horiz-adv-x="1664" d="M1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1664 -128q0 -52 -38 -90t-90 -38q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5 t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z" />
+<glyph unicode="&#xf003;" horiz-adv-x="1792" d="M1664 32v768q-32 -36 -69 -66q-268 -206 -426 -338q-51 -43 -83 -67t-86.5 -48.5t-102.5 -24.5h-1h-1q-48 0 -102.5 24.5t-86.5 48.5t-83 67q-158 132 -426 338q-37 30 -69 66v-768q0 -13 9.5 -22.5t22.5 -9.5h1472q13 0 22.5 9.5t9.5 22.5zM1664 1083v11v13.5t-0.5 13 t-3 12.5t-5.5 9t-9 7.5t-14 2.5h-1472q-13 0 -22.5 -9.5t-9.5 -22.5q0 -168 147 -284q193 -152 401 -317q6 -5 35 -29.5t46 -37.5t44.5 -31.5t50.5 -27.5t43 -9h1h1q20 0 43 9t50.5 27.5t44.5 31.5t46 37.5t35 29.5q208 165 401 317q54 43 100.5 115.5t46.5 131.5z M1792 1120v-1088q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1472q66 0 113 -47t47 -113z" />
+<glyph unicode="&#xf004;" horiz-adv-x="1792" d="M896 -128q-26 0 -44 18l-624 602q-10 8 -27.5 26t-55.5 65.5t-68 97.5t-53.5 121t-23.5 138q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5q224 0 351 -124t127 -344q0 -221 -229 -450l-623 -600 q-18 -18 -44 -18z" />
+<glyph unicode="&#xf005;" horiz-adv-x="1664" d="M1664 889q0 -22 -26 -48l-363 -354l86 -500q1 -7 1 -20q0 -21 -10.5 -35.5t-30.5 -14.5q-19 0 -40 12l-449 236l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41t49 -41l225 -455 l502 -73q56 -9 56 -46z" />
+<glyph unicode="&#xf006;" horiz-adv-x="1664" d="M1137 532l306 297l-422 62l-189 382l-189 -382l-422 -62l306 -297l-73 -421l378 199l377 -199zM1664 889q0 -22 -26 -48l-363 -354l86 -500q1 -7 1 -20q0 -50 -41 -50q-19 0 -40 12l-449 236l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500 l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41t49 -41l225 -455l502 -73q56 -9 56 -46z" />
+<glyph unicode="&#xf007;" horiz-adv-x="1408" d="M1408 131q0 -120 -73 -189.5t-194 -69.5h-874q-121 0 -194 69.5t-73 189.5q0 53 3.5 103.5t14 109t26.5 108.5t43 97.5t62 81t85.5 53.5t111.5 20q9 0 42 -21.5t74.5 -48t108 -48t133.5 -21.5t133.5 21.5t108 48t74.5 48t42 21.5q61 0 111.5 -20t85.5 -53.5t62 -81 t43 -97.5t26.5 -108.5t14 -109t3.5 -103.5zM1088 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5z" />
+<glyph unicode="&#xf008;" horiz-adv-x="1920" d="M384 -64v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM384 320v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM384 704v128q0 26 -19 45t-45 19h-128 q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1408 -64v512q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-512q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM384 1088v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45 t45 -19h128q26 0 45 19t19 45zM1792 -64v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1408 704v512q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-512q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM1792 320v128 q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1792 704v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1792 1088v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19 t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1920 1248v-1344q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1344q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
+<glyph unicode="&#xf009;" horiz-adv-x="1664" d="M768 512v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM768 1280v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM1664 512v-384q0 -52 -38 -90t-90 -38 h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM1664 1280v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf00a;" horiz-adv-x="1792" d="M512 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 288v-192q0 -40 -28 -68t-68 -28h-320 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28 h320q40 0 68 -28t28 -68zM1792 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 800v-192 q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68z" />
+<glyph unicode="&#xf00b;" horiz-adv-x="1792" d="M512 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 288v-192q0 -40 -28 -68t-68 -28h-960 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h960q40 0 68 -28t28 -68zM512 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 800v-192q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v192q0 40 28 68t68 28 h960q40 0 68 -28t28 -68zM1792 1312v-192q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h960q40 0 68 -28t28 -68z" />
+<glyph unicode="&#xf00c;" horiz-adv-x="1792" d="M1671 970q0 -40 -28 -68l-724 -724l-136 -136q-28 -28 -68 -28t-68 28l-136 136l-362 362q-28 28 -28 68t28 68l136 136q28 28 68 28t68 -28l294 -295l656 657q28 28 68 28t68 -28l136 -136q28 -28 28 -68z" />
+<glyph unicode="&#xf00d;" horiz-adv-x="1408" d="M1298 214q0 -40 -28 -68l-136 -136q-28 -28 -68 -28t-68 28l-294 294l-294 -294q-28 -28 -68 -28t-68 28l-136 136q-28 28 -28 68t28 68l294 294l-294 294q-28 28 -28 68t28 68l136 136q28 28 68 28t68 -28l294 -294l294 294q28 28 68 28t68 -28l136 -136q28 -28 28 -68 t-28 -68l-294 -294l294 -294q28 -28 28 -68z" />
+<glyph unicode="&#xf00e;" horiz-adv-x="1664" d="M1024 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-224v-224q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v224h-224q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h224v224q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5v-224h224 q13 0 22.5 -9.5t9.5 -22.5zM1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1664 -128q0 -53 -37.5 -90.5t-90.5 -37.5q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5 t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z" />
+<glyph unicode="&#xf010;" horiz-adv-x="1664" d="M1024 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-576q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h576q13 0 22.5 -9.5t9.5 -22.5zM1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5z M1664 -128q0 -53 -37.5 -90.5t-90.5 -37.5q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z " />
+<glyph unicode="&#xf011;" d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61t-298 61t-245 164t-164 245t-61 298q0 182 80.5 343t226.5 270q43 32 95.5 25t83.5 -50q32 -42 24.5 -94.5t-49.5 -84.5q-98 -74 -151.5 -181t-53.5 -228q0 -104 40.5 -198.5t109.5 -163.5t163.5 -109.5 t198.5 -40.5t198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5q0 121 -53.5 228t-151.5 181q-42 32 -49.5 84.5t24.5 94.5q31 43 84 50t95 -25q146 -109 226.5 -270t80.5 -343zM896 1408v-640q0 -52 -38 -90t-90 -38t-90 38t-38 90v640q0 52 38 90t90 38t90 -38t38 -90z" />
+<glyph unicode="&#xf012;" horiz-adv-x="1792" d="M256 96v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM640 224v-320q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v320q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1024 480v-576q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23 v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1408 864v-960q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 1376v-1472q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v1472q0 14 9 23t23 9h192q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf013;" d="M1024 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1536 749v-222q0 -12 -8 -23t-20 -13l-185 -28q-19 -54 -39 -91q35 -50 107 -138q10 -12 10 -25t-9 -23q-27 -37 -99 -108t-94 -71q-12 0 -26 9l-138 108q-44 -23 -91 -38 q-16 -136 -29 -186q-7 -28 -36 -28h-222q-14 0 -24.5 8.5t-11.5 21.5l-28 184q-49 16 -90 37l-141 -107q-10 -9 -25 -9q-14 0 -25 11q-126 114 -165 168q-7 10 -7 23q0 12 8 23q15 21 51 66.5t54 70.5q-27 50 -41 99l-183 27q-13 2 -21 12.5t-8 23.5v222q0 12 8 23t19 13 l186 28q14 46 39 92q-40 57 -107 138q-10 12 -10 24q0 10 9 23q26 36 98.5 107.5t94.5 71.5q13 0 26 -10l138 -107q44 23 91 38q16 136 29 186q7 28 36 28h222q14 0 24.5 -8.5t11.5 -21.5l28 -184q49 -16 90 -37l142 107q9 9 24 9q13 0 25 -10q129 -119 165 -170q7 -8 7 -22 q0 -12 -8 -23q-15 -21 -51 -66.5t-54 -70.5q26 -50 41 -98l183 -28q13 -2 21 -12.5t8 -23.5z" />
+<glyph unicode="&#xf014;" horiz-adv-x="1408" d="M512 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM768 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1024 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576 q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1152 76v948h-896v-948q0 -22 7 -40.5t14.5 -27t10.5 -8.5h832q3 0 10.5 8.5t14.5 27t7 40.5zM480 1152h448l-48 117q-7 9 -17 11h-317q-10 -2 -17 -11zM1408 1120v-64q0 -14 -9 -23t-23 -9h-96v-948q0 -83 -47 -143.5t-113 -60.5h-832 q-66 0 -113 58.5t-47 141.5v952h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h309l70 167q15 37 54 63t79 26h320q40 0 79 -26t54 -63l70 -167h309q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf015;" horiz-adv-x="1664" d="M1408 544v-480q0 -26 -19 -45t-45 -19h-384v384h-256v-384h-384q-26 0 -45 19t-19 45v480q0 1 0.5 3t0.5 3l575 474l575 -474q1 -2 1 -6zM1631 613l-62 -74q-8 -9 -21 -11h-3q-13 0 -21 7l-692 577l-692 -577q-12 -8 -24 -7q-13 2 -21 11l-62 74q-8 10 -7 23.5t11 21.5 l719 599q32 26 76 26t76 -26l244 -204v195q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-408l219 -182q10 -8 11 -21.5t-7 -23.5z" />
+<glyph unicode="&#xf016;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z " />
+<glyph unicode="&#xf017;" d="M896 992v-448q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf018;" horiz-adv-x="1920" d="M1111 540v4l-24 320q-1 13 -11 22.5t-23 9.5h-186q-13 0 -23 -9.5t-11 -22.5l-24 -320v-4q-1 -12 8 -20t21 -8h244q12 0 21 8t8 20zM1870 73q0 -73 -46 -73h-704q13 0 22 9.5t8 22.5l-20 256q-1 13 -11 22.5t-23 9.5h-272q-13 0 -23 -9.5t-11 -22.5l-20 -256 q-1 -13 8 -22.5t22 -9.5h-704q-46 0 -46 73q0 54 26 116l417 1044q8 19 26 33t38 14h339q-13 0 -23 -9.5t-11 -22.5l-15 -192q-1 -14 8 -23t22 -9h166q13 0 22 9t8 23l-15 192q-1 13 -11 22.5t-23 9.5h339q20 0 38 -14t26 -33l417 -1044q26 -62 26 -116z" />
+<glyph unicode="&#xf019;" horiz-adv-x="1664" d="M1280 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 416v-320q0 -40 -28 -68t-68 -28h-1472q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h465l135 -136 q58 -56 136 -56t136 56l136 136h464q40 0 68 -28t28 -68zM1339 985q17 -41 -14 -70l-448 -448q-18 -19 -45 -19t-45 19l-448 448q-31 29 -14 70q17 39 59 39h256v448q0 26 19 45t45 19h256q26 0 45 -19t19 -45v-448h256q42 0 59 -39z" />
+<glyph unicode="&#xf01a;" d="M1120 608q0 -12 -10 -24l-319 -319q-11 -9 -23 -9t-23 9l-320 320q-15 16 -7 35q8 20 30 20h192v352q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-352h192q14 0 23 -9t9 -23zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273 t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf01b;" d="M1118 660q-8 -20 -30 -20h-192v-352q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v352h-192q-14 0 -23 9t-9 23q0 12 10 24l319 319q11 9 23 9t23 -9l320 -320q15 -16 7 -35zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198 t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf01c;" d="M1023 576h316q-1 3 -2.5 8t-2.5 8l-212 496h-708l-212 -496q-1 -2 -2.5 -8t-2.5 -8h316l95 -192h320zM1536 546v-482q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v482q0 62 25 123l238 552q10 25 36.5 42t52.5 17h832q26 0 52.5 -17t36.5 -42l238 -552 q25 -61 25 -123z" />
+<glyph unicode="&#xf01d;" d="M1184 640q0 -37 -32 -55l-544 -320q-15 -9 -32 -9q-16 0 -32 8q-32 19 -32 56v640q0 37 32 56q33 18 64 -1l544 -320q32 -18 32 -55zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf01e;" d="M1536 1280v-448q0 -26 -19 -45t-45 -19h-448q-42 0 -59 40q-17 39 14 69l138 138q-148 137 -349 137q-104 0 -198.5 -40.5t-163.5 -109.5t-109.5 -163.5t-40.5 -198.5t40.5 -198.5t109.5 -163.5t163.5 -109.5t198.5 -40.5q119 0 225 52t179 147q7 10 23 12q14 0 25 -9 l137 -138q9 -8 9.5 -20.5t-7.5 -22.5q-109 -132 -264 -204.5t-327 -72.5q-156 0 -298 61t-245 164t-164 245t-61 298t61 298t164 245t245 164t298 61q147 0 284.5 -55.5t244.5 -156.5l130 129q29 31 70 14q39 -17 39 -59z" />
+<glyph unicode="&#xf021;" d="M1511 480q0 -5 -1 -7q-64 -268 -268 -434.5t-478 -166.5q-146 0 -282.5 55t-243.5 157l-129 -129q-19 -19 -45 -19t-45 19t-19 45v448q0 26 19 45t45 19h448q26 0 45 -19t19 -45t-19 -45l-137 -137q71 -66 161 -102t187 -36q134 0 250 65t186 179q11 17 53 117 q8 23 30 23h192q13 0 22.5 -9.5t9.5 -22.5zM1536 1280v-448q0 -26 -19 -45t-45 -19h-448q-26 0 -45 19t-19 45t19 45l138 138q-148 137 -349 137q-134 0 -250 -65t-186 -179q-11 -17 -53 -117q-8 -23 -30 -23h-199q-13 0 -22.5 9.5t-9.5 22.5v7q65 268 270 434.5t480 166.5 q146 0 284 -55.5t245 -156.5l130 129q19 19 45 19t45 -19t19 -45z" />
+<glyph unicode="&#xf022;" horiz-adv-x="1792" d="M384 352v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 608v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M384 864v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1536 352v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5t9.5 -22.5z M1536 608v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5t9.5 -22.5zM1536 864v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5 t9.5 -22.5zM1664 160v832q0 13 -9.5 22.5t-22.5 9.5h-1472q-13 0 -22.5 -9.5t-9.5 -22.5v-832q0 -13 9.5 -22.5t22.5 -9.5h1472q13 0 22.5 9.5t9.5 22.5zM1792 1248v-1088q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1472q66 0 113 -47 t47 -113z" />
+<glyph unicode="&#xf023;" horiz-adv-x="1152" d="M320 768h512v192q0 106 -75 181t-181 75t-181 -75t-75 -181v-192zM1152 672v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h32v192q0 184 132 316t316 132t316 -132t132 -316v-192h32q40 0 68 -28t28 -68z" />
+<glyph unicode="&#xf024;" horiz-adv-x="1792" d="M320 1280q0 -72 -64 -110v-1266q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v1266q-64 38 -64 110q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -25 -12.5 -38.5t-39.5 -27.5q-215 -116 -369 -116q-61 0 -123.5 22t-108.5 48 t-115.5 48t-142.5 22q-192 0 -464 -146q-17 -9 -33 -9q-26 0 -45 19t-19 45v742q0 32 31 55q21 14 79 43q236 120 421 120q107 0 200 -29t219 -88q38 -19 88 -19q54 0 117.5 21t110 47t88 47t54.5 21q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf025;" horiz-adv-x="1664" d="M1664 650q0 -166 -60 -314l-20 -49l-185 -33q-22 -83 -90.5 -136.5t-156.5 -53.5v-32q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-32q71 0 130 -35.5t93 -95.5l68 12q29 95 29 193q0 148 -88 279t-236.5 209t-315.5 78 t-315.5 -78t-236.5 -209t-88 -279q0 -98 29 -193l68 -12q34 60 93 95.5t130 35.5v32q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v32q-88 0 -156.5 53.5t-90.5 136.5l-185 33l-20 49q-60 148 -60 314q0 151 67 291t179 242.5 t266 163.5t320 61t320 -61t266 -163.5t179 -242.5t67 -291z" />
+<glyph unicode="&#xf026;" horiz-adv-x="768" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45z" />
+<glyph unicode="&#xf027;" horiz-adv-x="1152" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45zM1152 640q0 -76 -42.5 -141.5t-112.5 -93.5q-10 -5 -25 -5q-26 0 -45 18.5t-19 45.5q0 21 12 35.5t29 25t34 23t29 35.5 t12 57t-12 57t-29 35.5t-34 23t-29 25t-12 35.5q0 27 19 45.5t45 18.5q15 0 25 -5q70 -27 112.5 -93t42.5 -142z" />
+<glyph unicode="&#xf028;" horiz-adv-x="1664" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45zM1152 640q0 -76 -42.5 -141.5t-112.5 -93.5q-10 -5 -25 -5q-26 0 -45 18.5t-19 45.5q0 21 12 35.5t29 25t34 23t29 35.5 t12 57t-12 57t-29 35.5t-34 23t-29 25t-12 35.5q0 27 19 45.5t45 18.5q15 0 25 -5q70 -27 112.5 -93t42.5 -142zM1408 640q0 -153 -85 -282.5t-225 -188.5q-13 -5 -25 -5q-27 0 -46 19t-19 45q0 39 39 59q56 29 76 44q74 54 115.5 135.5t41.5 173.5t-41.5 173.5 t-115.5 135.5q-20 15 -76 44q-39 20 -39 59q0 26 19 45t45 19q13 0 26 -5q140 -59 225 -188.5t85 -282.5zM1664 640q0 -230 -127 -422.5t-338 -283.5q-13 -5 -26 -5q-26 0 -45 19t-19 45q0 36 39 59q7 4 22.5 10.5t22.5 10.5q46 25 82 51q123 91 192 227t69 289t-69 289 t-192 227q-36 26 -82 51q-7 4 -22.5 10.5t-22.5 10.5q-39 23 -39 59q0 26 19 45t45 19q13 0 26 -5q211 -91 338 -283.5t127 -422.5z" />
+<glyph unicode="&#xf029;" horiz-adv-x="1408" d="M384 384v-128h-128v128h128zM384 1152v-128h-128v128h128zM1152 1152v-128h-128v128h128zM128 129h384v383h-384v-383zM128 896h384v384h-384v-384zM896 896h384v384h-384v-384zM640 640v-640h-640v640h640zM1152 128v-128h-128v128h128zM1408 128v-128h-128v128h128z M1408 640v-384h-384v128h-128v-384h-128v640h384v-128h128v128h128zM640 1408v-640h-640v640h640zM1408 1408v-640h-640v640h640z" />
+<glyph unicode="&#xf02a;" horiz-adv-x="1792" d="M63 0h-63v1408h63v-1408zM126 1h-32v1407h32v-1407zM220 1h-31v1407h31v-1407zM377 1h-31v1407h31v-1407zM534 1h-62v1407h62v-1407zM660 1h-31v1407h31v-1407zM723 1h-31v1407h31v-1407zM786 1h-31v1407h31v-1407zM943 1h-63v1407h63v-1407zM1100 1h-63v1407h63v-1407z M1226 1h-63v1407h63v-1407zM1352 1h-63v1407h63v-1407zM1446 1h-63v1407h63v-1407zM1635 1h-94v1407h94v-1407zM1698 1h-32v1407h32v-1407zM1792 0h-63v1408h63v-1408z" />
+<glyph unicode="&#xf02b;" d="M448 1088q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1515 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-53 0 -90 37l-715 716q-38 37 -64.5 101t-26.5 117v416q0 52 38 90t90 38h416q53 0 117 -26.5t102 -64.5 l715 -714q37 -39 37 -91z" />
+<glyph unicode="&#xf02c;" horiz-adv-x="1920" d="M448 1088q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1515 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-53 0 -90 37l-715 716q-38 37 -64.5 101t-26.5 117v416q0 52 38 90t90 38h416q53 0 117 -26.5t102 -64.5 l715 -714q37 -39 37 -91zM1899 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-36 0 -59 14t-53 45l470 470q37 37 37 90q0 52 -37 91l-715 714q-38 38 -102 64.5t-117 26.5h224q53 0 117 -26.5t102 -64.5l715 -714q37 -39 37 -91z" />
+<glyph unicode="&#xf02d;" horiz-adv-x="1664" d="M1639 1058q40 -57 18 -129l-275 -906q-19 -64 -76.5 -107.5t-122.5 -43.5h-923q-77 0 -148.5 53.5t-99.5 131.5q-24 67 -2 127q0 4 3 27t4 37q1 8 -3 21.5t-3 19.5q2 11 8 21t16.5 23.5t16.5 23.5q23 38 45 91.5t30 91.5q3 10 0.5 30t-0.5 28q3 11 17 28t17 23 q21 36 42 92t25 90q1 9 -2.5 32t0.5 28q4 13 22 30.5t22 22.5q19 26 42.5 84.5t27.5 96.5q1 8 -3 25.5t-2 26.5q2 8 9 18t18 23t17 21q8 12 16.5 30.5t15 35t16 36t19.5 32t26.5 23.5t36 11.5t47.5 -5.5l-1 -3q38 9 51 9h761q74 0 114 -56t18 -130l-274 -906 q-36 -119 -71.5 -153.5t-128.5 -34.5h-869q-27 0 -38 -15q-11 -16 -1 -43q24 -70 144 -70h923q29 0 56 15.5t35 41.5l300 987q7 22 5 57q38 -15 59 -43zM575 1056q-4 -13 2 -22.5t20 -9.5h608q13 0 25.5 9.5t16.5 22.5l21 64q4 13 -2 22.5t-20 9.5h-608q-13 0 -25.5 -9.5 t-16.5 -22.5zM492 800q-4 -13 2 -22.5t20 -9.5h608q13 0 25.5 9.5t16.5 22.5l21 64q4 13 -2 22.5t-20 9.5h-608q-13 0 -25.5 -9.5t-16.5 -22.5z" />
+<glyph unicode="&#xf02e;" horiz-adv-x="1280" d="M1164 1408q23 0 44 -9q33 -13 52.5 -41t19.5 -62v-1289q0 -34 -19.5 -62t-52.5 -41q-19 -8 -44 -8q-48 0 -83 32l-441 424l-441 -424q-36 -33 -83 -33q-23 0 -44 9q-33 13 -52.5 41t-19.5 62v1289q0 34 19.5 62t52.5 41q21 9 44 9h1048z" />
+<glyph unicode="&#xf02f;" horiz-adv-x="1664" d="M384 0h896v256h-896v-256zM384 640h896v384h-160q-40 0 -68 28t-28 68v160h-640v-640zM1536 576q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 576v-416q0 -13 -9.5 -22.5t-22.5 -9.5h-224v-160q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68 v160h-224q-13 0 -22.5 9.5t-9.5 22.5v416q0 79 56.5 135.5t135.5 56.5h64v544q0 40 28 68t68 28h672q40 0 88 -20t76 -48l152 -152q28 -28 48 -76t20 -88v-256h64q79 0 135.5 -56.5t56.5 -135.5z" />
+<glyph unicode="&#xf030;" horiz-adv-x="1920" d="M960 864q119 0 203.5 -84.5t84.5 -203.5t-84.5 -203.5t-203.5 -84.5t-203.5 84.5t-84.5 203.5t84.5 203.5t203.5 84.5zM1664 1280q106 0 181 -75t75 -181v-896q0 -106 -75 -181t-181 -75h-1408q-106 0 -181 75t-75 181v896q0 106 75 181t181 75h224l51 136 q19 49 69.5 84.5t103.5 35.5h512q53 0 103.5 -35.5t69.5 -84.5l51 -136h224zM960 128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf031;" horiz-adv-x="1664" d="M725 977l-170 -450q33 0 136.5 -2t160.5 -2q19 0 57 2q-87 253 -184 452zM0 -128l2 79q23 7 56 12.5t57 10.5t49.5 14.5t44.5 29t31 50.5l237 616l280 724h75h53q8 -14 11 -21l205 -480q33 -78 106 -257.5t114 -274.5q15 -34 58 -144.5t72 -168.5q20 -45 35 -57 q19 -15 88 -29.5t84 -20.5q6 -38 6 -57q0 -4 -0.5 -13t-0.5 -13q-63 0 -190 8t-191 8q-76 0 -215 -7t-178 -8q0 43 4 78l131 28q1 0 12.5 2.5t15.5 3.5t14.5 4.5t15 6.5t11 8t9 11t2.5 14q0 16 -31 96.5t-72 177.5t-42 100l-450 2q-26 -58 -76.5 -195.5t-50.5 -162.5 q0 -22 14 -37.5t43.5 -24.5t48.5 -13.5t57 -8.5t41 -4q1 -19 1 -58q0 -9 -2 -27q-58 0 -174.5 10t-174.5 10q-8 0 -26.5 -4t-21.5 -4q-80 -14 -188 -14z" />
+<glyph unicode="&#xf032;" horiz-adv-x="1408" d="M555 15q74 -32 140 -32q376 0 376 335q0 114 -41 180q-27 44 -61.5 74t-67.5 46.5t-80.5 25t-84 10.5t-94.5 2q-73 0 -101 -10q0 -53 -0.5 -159t-0.5 -158q0 -8 -1 -67.5t-0.5 -96.5t4.5 -83.5t12 -66.5zM541 761q42 -7 109 -7q82 0 143 13t110 44.5t74.5 89.5t25.5 142 q0 70 -29 122.5t-79 82t-108 43.5t-124 14q-50 0 -130 -13q0 -50 4 -151t4 -152q0 -27 -0.5 -80t-0.5 -79q0 -46 1 -69zM0 -128l2 94q15 4 85 16t106 27q7 12 12.5 27t8.5 33.5t5.5 32.5t3 37.5t0.5 34v35.5v30q0 982 -22 1025q-4 8 -22 14.5t-44.5 11t-49.5 7t-48.5 4.5 t-30.5 3l-4 83q98 2 340 11.5t373 9.5q23 0 68.5 -0.5t67.5 -0.5q70 0 136.5 -13t128.5 -42t108 -71t74 -104.5t28 -137.5q0 -52 -16.5 -95.5t-39 -72t-64.5 -57.5t-73 -45t-84 -40q154 -35 256.5 -134t102.5 -248q0 -100 -35 -179.5t-93.5 -130.5t-138 -85.5t-163.5 -48.5 t-176 -14q-44 0 -132 3t-132 3q-106 0 -307 -11t-231 -12z" />
+<glyph unicode="&#xf033;" horiz-adv-x="1024" d="M0 -126l17 85q6 2 81.5 21.5t111.5 37.5q28 35 41 101q1 7 62 289t114 543.5t52 296.5v25q-24 13 -54.5 18.5t-69.5 8t-58 5.5l19 103q33 -2 120 -6.5t149.5 -7t120.5 -2.5q48 0 98.5 2.5t121 7t98.5 6.5q-5 -39 -19 -89q-30 -10 -101.5 -28.5t-108.5 -33.5 q-8 -19 -14 -42.5t-9 -40t-7.5 -45.5t-6.5 -42q-27 -148 -87.5 -419.5t-77.5 -355.5q-2 -9 -13 -58t-20 -90t-16 -83.5t-6 -57.5l1 -18q17 -4 185 -31q-3 -44 -16 -99q-11 0 -32.5 -1.5t-32.5 -1.5q-29 0 -87 10t-86 10q-138 2 -206 2q-51 0 -143 -9t-121 -11z" />
+<glyph unicode="&#xf034;" horiz-adv-x="1792" d="M1744 128q33 0 42 -18.5t-11 -44.5l-126 -162q-20 -26 -49 -26t-49 26l-126 162q-20 26 -11 44.5t42 18.5h80v1024h-80q-33 0 -42 18.5t11 44.5l126 162q20 26 49 26t49 -26l126 -162q20 -26 11 -44.5t-42 -18.5h-80v-1024h80zM81 1407l54 -27q12 -5 211 -5q44 0 132 2 t132 2q36 0 107.5 -0.5t107.5 -0.5h293q6 0 21 -0.5t20.5 0t16 3t17.5 9t15 17.5l42 1q4 0 14 -0.5t14 -0.5q2 -112 2 -336q0 -80 -5 -109q-39 -14 -68 -18q-25 44 -54 128q-3 9 -11 48t-14.5 73.5t-7.5 35.5q-6 8 -12 12.5t-15.5 6t-13 2.5t-18 0.5t-16.5 -0.5 q-17 0 -66.5 0.5t-74.5 0.5t-64 -2t-71 -6q-9 -81 -8 -136q0 -94 2 -388t2 -455q0 -16 -2.5 -71.5t0 -91.5t12.5 -69q40 -21 124 -42.5t120 -37.5q5 -40 5 -50q0 -14 -3 -29l-34 -1q-76 -2 -218 8t-207 10q-50 0 -151 -9t-152 -9q-3 51 -3 52v9q17 27 61.5 43t98.5 29t78 27 q19 42 19 383q0 101 -3 303t-3 303v117q0 2 0.5 15.5t0.5 25t-1 25.5t-3 24t-5 14q-11 12 -162 12q-33 0 -93 -12t-80 -26q-19 -13 -34 -72.5t-31.5 -111t-42.5 -53.5q-42 26 -56 44v383z" />
+<glyph unicode="&#xf035;" d="M81 1407l54 -27q12 -5 211 -5q44 0 132 2t132 2q70 0 246.5 1t304.5 0.5t247 -4.5q33 -1 56 31l42 1q4 0 14 -0.5t14 -0.5q2 -112 2 -336q0 -80 -5 -109q-39 -14 -68 -18q-25 44 -54 128q-3 9 -11 47.5t-15 73.5t-7 36q-10 13 -27 19q-5 2 -66 2q-30 0 -93 1t-103 1 t-94 -2t-96 -7q-9 -81 -8 -136l1 -152v52q0 -55 1 -154t1.5 -180t0.5 -153q0 -16 -2.5 -71.5t0 -91.5t12.5 -69q40 -21 124 -42.5t120 -37.5q5 -40 5 -50q0 -14 -3 -29l-34 -1q-76 -2 -218 8t-207 10q-50 0 -151 -9t-152 -9q-3 51 -3 52v9q17 27 61.5 43t98.5 29t78 27 q7 16 11.5 74t6 145.5t1.5 155t-0.5 153.5t-0.5 89q0 7 -2.5 21.5t-2.5 22.5q0 7 0.5 44t1 73t0 76.5t-3 67.5t-6.5 32q-11 12 -162 12q-41 0 -163 -13.5t-138 -24.5q-19 -12 -34 -71.5t-31.5 -111.5t-42.5 -54q-42 26 -56 44v383zM1310 125q12 0 42 -19.5t57.5 -41.5 t59.5 -49t36 -30q26 -21 26 -49t-26 -49q-4 -3 -36 -30t-59.5 -49t-57.5 -41.5t-42 -19.5q-13 0 -20.5 10.5t-10 28.5t-2.5 33.5t1.5 33t1.5 19.5h-1024q0 -2 1.5 -19.5t1.5 -33t-2.5 -33.5t-10 -28.5t-20.5 -10.5q-12 0 -42 19.5t-57.5 41.5t-59.5 49t-36 30q-26 21 -26 49 t26 49q4 3 36 30t59.5 49t57.5 41.5t42 19.5q13 0 20.5 -10.5t10 -28.5t2.5 -33.5t-1.5 -33t-1.5 -19.5h1024q0 2 -1.5 19.5t-1.5 33t2.5 33.5t10 28.5t20.5 10.5z" />
+<glyph unicode="&#xf036;" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1408 576v-128q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1280q26 0 45 -19t19 -45zM1664 960v-128q0 -26 -19 -45 t-45 -19h-1536q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1536q26 0 45 -19t19 -45zM1280 1344v-128q0 -26 -19 -45t-45 -19h-1152q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf037;" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1408 576v-128q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h896q26 0 45 -19t19 -45zM1664 960v-128q0 -26 -19 -45t-45 -19 h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1280 1344v-128q0 -26 -19 -45t-45 -19h-640q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h640q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf038;" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 576v-128q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1280q26 0 45 -19t19 -45zM1792 960v-128q0 -26 -19 -45 t-45 -19h-1536q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1536q26 0 45 -19t19 -45zM1792 1344v-128q0 -26 -19 -45t-45 -19h-1152q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf039;" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 576v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 960v-128q0 -26 -19 -45 t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 1344v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf03a;" horiz-adv-x="1792" d="M256 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM256 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5 t9.5 -22.5zM256 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1344 q13 0 22.5 -9.5t9.5 -22.5zM256 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5 t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192 q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5z" />
+<glyph unicode="&#xf03b;" horiz-adv-x="1792" d="M384 992v-576q0 -13 -9.5 -22.5t-22.5 -9.5q-14 0 -23 9l-288 288q-9 9 -9 23t9 23l288 288q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5 t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088 q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5t9.5 -22.5z" />
+<glyph unicode="&#xf03c;" horiz-adv-x="1792" d="M352 704q0 -14 -9 -23l-288 -288q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v576q0 13 9.5 22.5t22.5 9.5q14 0 23 -9l288 -288q9 -9 9 -23zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5 t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088 q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5t9.5 -22.5z" />
+<glyph unicode="&#xf03d;" horiz-adv-x="1792" d="M1792 1184v-1088q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-403 403v-166q0 -119 -84.5 -203.5t-203.5 -84.5h-704q-119 0 -203.5 84.5t-84.5 203.5v704q0 119 84.5 203.5t203.5 84.5h704q119 0 203.5 -84.5t84.5 -203.5v-165l403 402q18 19 45 19q12 0 25 -5 q39 -17 39 -59z" />
+<glyph unicode="&#xf03e;" horiz-adv-x="1920" d="M640 960q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1664 576v-448h-1408v192l320 320l160 -160l512 512zM1760 1280h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-1216q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5v1216 q0 13 -9.5 22.5t-22.5 9.5zM1920 1248v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
+<glyph unicode="&#xf040;" d="M363 0l91 91l-235 235l-91 -91v-107h128v-128h107zM886 928q0 22 -22 22q-10 0 -17 -7l-542 -542q-7 -7 -7 -17q0 -22 22 -22q10 0 17 7l542 542q7 7 7 17zM832 1120l416 -416l-832 -832h-416v416zM1515 1024q0 -53 -37 -90l-166 -166l-416 416l166 165q36 38 90 38 q53 0 91 -38l235 -234q37 -39 37 -91z" />
+<glyph unicode="&#xf041;" horiz-adv-x="1024" d="M768 896q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1024 896q0 -109 -33 -179l-364 -774q-16 -33 -47.5 -52t-67.5 -19t-67.5 19t-46.5 52l-365 774q-33 70 -33 179q0 212 150 362t362 150t362 -150t150 -362z" />
+<glyph unicode="&#xf042;" d="M768 96v1088q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf043;" horiz-adv-x="1024" d="M512 384q0 36 -20 69q-1 1 -15.5 22.5t-25.5 38t-25 44t-21 50.5q-4 16 -21 16t-21 -16q-7 -23 -21 -50.5t-25 -44t-25.5 -38t-15.5 -22.5q-20 -33 -20 -69q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 512q0 -212 -150 -362t-362 -150t-362 150t-150 362 q0 145 81 275q6 9 62.5 90.5t101 151t99.5 178t83 201.5q9 30 34 47t51 17t51.5 -17t33.5 -47q28 -93 83 -201.5t99.5 -178t101 -151t62.5 -90.5q81 -127 81 -275z" />
+<glyph unicode="&#xf044;" horiz-adv-x="1792" d="M888 352l116 116l-152 152l-116 -116v-56h96v-96h56zM1328 1072q-16 16 -33 -1l-350 -350q-17 -17 -1 -33t33 1l350 350q17 17 1 33zM1408 478v-190q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832 q63 0 117 -25q15 -7 18 -23q3 -17 -9 -29l-49 -49q-14 -14 -32 -8q-23 6 -45 6h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v126q0 13 9 22l64 64q15 15 35 7t20 -29zM1312 1216l288 -288l-672 -672h-288v288zM1756 1084l-92 -92 l-288 288l92 92q28 28 68 28t68 -28l152 -152q28 -28 28 -68t-28 -68z" />
+<glyph unicode="&#xf045;" horiz-adv-x="1664" d="M1408 547v-259q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h255v0q13 0 22.5 -9.5t9.5 -22.5q0 -27 -26 -32q-77 -26 -133 -60q-10 -4 -16 -4h-112q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832 q66 0 113 47t47 113v214q0 19 18 29q28 13 54 37q16 16 35 8q21 -9 21 -29zM1645 1043l-384 -384q-18 -19 -45 -19q-12 0 -25 5q-39 17 -39 59v192h-160q-323 0 -438 -131q-119 -137 -74 -473q3 -23 -20 -34q-8 -2 -12 -2q-16 0 -26 13q-10 14 -21 31t-39.5 68.5t-49.5 99.5 t-38.5 114t-17.5 122q0 49 3.5 91t14 90t28 88t47 81.5t68.5 74t94.5 61.5t124.5 48.5t159.5 30.5t196.5 11h160v192q0 42 39 59q13 5 25 5q26 0 45 -19l384 -384q19 -19 19 -45t-19 -45z" />
+<glyph unicode="&#xf046;" horiz-adv-x="1664" d="M1408 606v-318q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q63 0 117 -25q15 -7 18 -23q3 -17 -9 -29l-49 -49q-10 -10 -23 -10q-3 0 -9 2q-23 6 -45 6h-832q-66 0 -113 -47t-47 -113v-832 q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v254q0 13 9 22l64 64q10 10 23 10q6 0 12 -3q20 -8 20 -29zM1639 1095l-814 -814q-24 -24 -57 -24t-57 24l-430 430q-24 24 -24 57t24 57l110 110q24 24 57 24t57 -24l263 -263l647 647q24 24 57 24t57 -24l110 -110 q24 -24 24 -57t-24 -57z" />
+<glyph unicode="&#xf047;" horiz-adv-x="1792" d="M1792 640q0 -26 -19 -45l-256 -256q-19 -19 -45 -19t-45 19t-19 45v128h-384v-384h128q26 0 45 -19t19 -45t-19 -45l-256 -256q-19 -19 -45 -19t-45 19l-256 256q-19 19 -19 45t19 45t45 19h128v384h-384v-128q0 -26 -19 -45t-45 -19t-45 19l-256 256q-19 19 -19 45 t19 45l256 256q19 19 45 19t45 -19t19 -45v-128h384v384h-128q-26 0 -45 19t-19 45t19 45l256 256q19 19 45 19t45 -19l256 -256q19 -19 19 -45t-19 -45t-45 -19h-128v-384h384v128q0 26 19 45t45 19t45 -19l256 -256q19 -19 19 -45z" />
+<glyph unicode="&#xf048;" horiz-adv-x="1024" d="M979 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-678q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-678q4 11 13 19z" />
+<glyph unicode="&#xf049;" horiz-adv-x="1792" d="M1747 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-710q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-678q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-678q4 11 13 19l710 710 q19 19 32 13t13 -32v-710q4 11 13 19z" />
+<glyph unicode="&#xf04a;" horiz-adv-x="1664" d="M1619 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-8 9 -13 19v-710q0 -26 -13 -32t-32 13l-710 710q-19 19 -19 45t19 45l710 710q19 19 32 13t13 -32v-710q5 11 13 19z" />
+<glyph unicode="&#xf04b;" horiz-adv-x="1408" d="M1384 609l-1328 -738q-23 -13 -39.5 -3t-16.5 36v1472q0 26 16.5 36t39.5 -3l1328 -738q23 -13 23 -31t-23 -31z" />
+<glyph unicode="&#xf04c;" d="M1536 1344v-1408q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h512q26 0 45 -19t19 -45zM640 1344v-1408q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h512q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf04d;" d="M1536 1344v-1408q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf04e;" horiz-adv-x="1664" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v710q0 26 13 32t32 -13l710 -710q19 -19 19 -45t-19 -45l-710 -710q-19 -19 -32 -13t-13 32v710q-5 -10 -13 -19z" />
+<glyph unicode="&#xf050;" horiz-adv-x="1792" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v710q0 26 13 32t32 -13l710 -710q8 -8 13 -19v678q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-1408q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v678q-5 -10 -13 -19l-710 -710 q-19 -19 -32 -13t-13 32v710q-5 -10 -13 -19z" />
+<glyph unicode="&#xf051;" horiz-adv-x="1024" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v678q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-1408q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v678q-5 -10 -13 -19z" />
+<glyph unicode="&#xf052;" horiz-adv-x="1538" d="M14 557l710 710q19 19 45 19t45 -19l710 -710q19 -19 13 -32t-32 -13h-1472q-26 0 -32 13t13 32zM1473 0h-1408q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1408q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19z" />
+<glyph unicode="&#xf053;" horiz-adv-x="1280" d="M1171 1235l-531 -531l531 -531q19 -19 19 -45t-19 -45l-166 -166q-19 -19 -45 -19t-45 19l-742 742q-19 19 -19 45t19 45l742 742q19 19 45 19t45 -19l166 -166q19 -19 19 -45t-19 -45z" />
+<glyph unicode="&#xf054;" horiz-adv-x="1280" d="M1107 659l-742 -742q-19 -19 -45 -19t-45 19l-166 166q-19 19 -19 45t19 45l531 531l-531 531q-19 19 -19 45t19 45l166 166q19 19 45 19t45 -19l742 -742q19 -19 19 -45t-19 -45z" />
+<glyph unicode="&#xf055;" d="M1216 576v128q0 26 -19 45t-45 19h-256v256q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-256h-256q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h256v-256q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v256h256q26 0 45 19t19 45zM1536 640q0 -209 -103 -385.5 t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf056;" d="M1216 576v128q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5 t103 -385.5z" />
+<glyph unicode="&#xf057;" d="M1149 414q0 26 -19 45l-181 181l181 181q19 19 19 45q0 27 -19 46l-90 90q-19 19 -46 19q-26 0 -45 -19l-181 -181l-181 181q-19 19 -45 19q-27 0 -46 -19l-90 -90q-19 -19 -19 -46q0 -26 19 -45l181 -181l-181 -181q-19 -19 -19 -45q0 -27 19 -46l90 -90q19 -19 46 -19 q26 0 45 19l181 181l181 -181q19 -19 45 -19q27 0 46 19l90 90q19 19 19 46zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf058;" d="M1284 802q0 28 -18 46l-91 90q-19 19 -45 19t-45 -19l-408 -407l-226 226q-19 19 -45 19t-45 -19l-91 -90q-18 -18 -18 -46q0 -27 18 -45l362 -362q19 -19 45 -19q27 0 46 19l543 543q18 18 18 45zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf059;" d="M896 160v192q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h192q14 0 23 9t9 23zM1152 832q0 88 -55.5 163t-138.5 116t-170 41q-243 0 -371 -213q-15 -24 8 -42l132 -100q7 -6 19 -6q16 0 25 12q53 68 86 92q34 24 86 24q48 0 85.5 -26t37.5 -59 q0 -38 -20 -61t-68 -45q-63 -28 -115.5 -86.5t-52.5 -125.5v-36q0 -14 9 -23t23 -9h192q14 0 23 9t9 23q0 19 21.5 49.5t54.5 49.5q32 18 49 28.5t46 35t44.5 48t28 60.5t12.5 81zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf05a;" d="M1024 160v160q0 14 -9 23t-23 9h-96v512q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23t23 -9h96v-320h-96q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23t23 -9h448q14 0 23 9t9 23zM896 1056v160q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23 t23 -9h192q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf05b;" d="M1197 512h-109q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h109q-32 108 -112.5 188.5t-188.5 112.5v-109q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v109q-108 -32 -188.5 -112.5t-112.5 -188.5h109q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-109 q32 -108 112.5 -188.5t188.5 -112.5v109q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-109q108 32 188.5 112.5t112.5 188.5zM1536 704v-128q0 -26 -19 -45t-45 -19h-143q-37 -161 -154.5 -278.5t-278.5 -154.5v-143q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v143 q-161 37 -278.5 154.5t-154.5 278.5h-143q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h143q37 161 154.5 278.5t278.5 154.5v143q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-143q161 -37 278.5 -154.5t154.5 -278.5h143q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf05c;" d="M1097 457l-146 -146q-10 -10 -23 -10t-23 10l-137 137l-137 -137q-10 -10 -23 -10t-23 10l-146 146q-10 10 -10 23t10 23l137 137l-137 137q-10 10 -10 23t10 23l146 146q10 10 23 10t23 -10l137 -137l137 137q10 10 23 10t23 -10l146 -146q10 -10 10 -23t-10 -23 l-137 -137l137 -137q10 -10 10 -23t-10 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5 t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf05d;" d="M1171 723l-422 -422q-19 -19 -45 -19t-45 19l-294 294q-19 19 -19 45t19 45l102 102q19 19 45 19t45 -19l147 -147l275 275q19 19 45 19t45 -19l102 -102q19 -19 19 -45t-19 -45zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198 t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf05e;" d="M1312 643q0 161 -87 295l-754 -753q137 -89 297 -89q111 0 211.5 43.5t173.5 116.5t116 174.5t43 212.5zM313 344l755 754q-135 91 -300 91q-148 0 -273 -73t-198 -199t-73 -274q0 -162 89 -299zM1536 643q0 -157 -61 -300t-163.5 -246t-245 -164t-298.5 -61t-298.5 61 t-245 164t-163.5 246t-61 300t61 299.5t163.5 245.5t245 164t298.5 61t298.5 -61t245 -164t163.5 -245.5t61 -299.5z" />
+<glyph unicode="&#xf060;" d="M1536 640v-128q0 -53 -32.5 -90.5t-84.5 -37.5h-704l293 -294q38 -36 38 -90t-38 -90l-75 -76q-37 -37 -90 -37q-52 0 -91 37l-651 652q-37 37 -37 90q0 52 37 91l651 650q38 38 91 38q52 0 90 -38l75 -74q38 -38 38 -91t-38 -91l-293 -293h704q52 0 84.5 -37.5 t32.5 -90.5z" />
+<glyph unicode="&#xf061;" d="M1472 576q0 -54 -37 -91l-651 -651q-39 -37 -91 -37q-51 0 -90 37l-75 75q-38 38 -38 91t38 91l293 293h-704q-52 0 -84.5 37.5t-32.5 90.5v128q0 53 32.5 90.5t84.5 37.5h704l-293 294q-38 36 -38 90t38 90l75 75q38 38 90 38q53 0 91 -38l651 -651q37 -35 37 -90z" />
+<glyph unicode="&#xf062;" horiz-adv-x="1664" d="M1611 565q0 -51 -37 -90l-75 -75q-38 -38 -91 -38q-54 0 -90 38l-294 293v-704q0 -52 -37.5 -84.5t-90.5 -32.5h-128q-53 0 -90.5 32.5t-37.5 84.5v704l-294 -293q-36 -38 -90 -38t-90 38l-75 75q-38 38 -38 90q0 53 38 91l651 651q35 37 90 37q54 0 91 -37l651 -651 q37 -39 37 -91z" />
+<glyph unicode="&#xf063;" horiz-adv-x="1664" d="M1611 704q0 -53 -37 -90l-651 -652q-39 -37 -91 -37q-53 0 -90 37l-651 652q-38 36 -38 90q0 53 38 91l74 75q39 37 91 37q53 0 90 -37l294 -294v704q0 52 38 90t90 38h128q52 0 90 -38t38 -90v-704l294 294q37 37 90 37q52 0 91 -37l75 -75q37 -39 37 -91z" />
+<glyph unicode="&#xf064;" horiz-adv-x="1792" d="M1792 896q0 -26 -19 -45l-512 -512q-19 -19 -45 -19t-45 19t-19 45v256h-224q-98 0 -175.5 -6t-154 -21.5t-133 -42.5t-105.5 -69.5t-80 -101t-48.5 -138.5t-17.5 -181q0 -55 5 -123q0 -6 2.5 -23.5t2.5 -26.5q0 -15 -8.5 -25t-23.5 -10q-16 0 -28 17q-7 9 -13 22 t-13.5 30t-10.5 24q-127 285 -127 451q0 199 53 333q162 403 875 403h224v256q0 26 19 45t45 19t45 -19l512 -512q19 -19 19 -45z" />
+<glyph unicode="&#xf065;" d="M755 480q0 -13 -10 -23l-332 -332l144 -144q19 -19 19 -45t-19 -45t-45 -19h-448q-26 0 -45 19t-19 45v448q0 26 19 45t45 19t45 -19l144 -144l332 332q10 10 23 10t23 -10l114 -114q10 -10 10 -23zM1536 1344v-448q0 -26 -19 -45t-45 -19t-45 19l-144 144l-332 -332 q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l332 332l-144 144q-19 19 -19 45t19 45t45 19h448q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf066;" d="M768 576v-448q0 -26 -19 -45t-45 -19t-45 19l-144 144l-332 -332q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l332 332l-144 144q-19 19 -19 45t19 45t45 19h448q26 0 45 -19t19 -45zM1523 1248q0 -13 -10 -23l-332 -332l144 -144q19 -19 19 -45t-19 -45 t-45 -19h-448q-26 0 -45 19t-19 45v448q0 26 19 45t45 19t45 -19l144 -144l332 332q10 10 23 10t23 -10l114 -114q10 -10 10 -23z" />
+<glyph unicode="&#xf067;" horiz-adv-x="1408" d="M1408 800v-192q0 -40 -28 -68t-68 -28h-416v-416q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v416h-416q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h416v416q0 40 28 68t68 28h192q40 0 68 -28t28 -68v-416h416q40 0 68 -28t28 -68z" />
+<glyph unicode="&#xf068;" horiz-adv-x="1408" d="M1408 800v-192q0 -40 -28 -68t-68 -28h-1216q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h1216q40 0 68 -28t28 -68z" />
+<glyph unicode="&#xf069;" horiz-adv-x="1664" d="M1482 486q46 -26 59.5 -77.5t-12.5 -97.5l-64 -110q-26 -46 -77.5 -59.5t-97.5 12.5l-266 153v-307q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v307l-266 -153q-46 -26 -97.5 -12.5t-77.5 59.5l-64 110q-26 46 -12.5 97.5t59.5 77.5l266 154l-266 154 q-46 26 -59.5 77.5t12.5 97.5l64 110q26 46 77.5 59.5t97.5 -12.5l266 -153v307q0 52 38 90t90 38h128q52 0 90 -38t38 -90v-307l266 153q46 26 97.5 12.5t77.5 -59.5l64 -110q26 -46 12.5 -97.5t-59.5 -77.5l-266 -154z" />
+<glyph unicode="&#xf06a;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM896 161v190q0 14 -9 23.5t-22 9.5h-192q-13 0 -23 -10t-10 -23v-190q0 -13 10 -23t23 -10h192 q13 0 22 9.5t9 23.5zM894 505l18 621q0 12 -10 18q-10 8 -24 8h-220q-14 0 -24 -8q-10 -6 -10 -18l17 -621q0 -10 10 -17.5t24 -7.5h185q14 0 23.5 7.5t10.5 17.5z" />
+<glyph unicode="&#xf06b;" d="M928 180v56v468v192h-320v-192v-468v-56q0 -25 18 -38.5t46 -13.5h192q28 0 46 13.5t18 38.5zM472 1024h195l-126 161q-26 31 -69 31q-40 0 -68 -28t-28 -68t28 -68t68 -28zM1160 1120q0 40 -28 68t-68 28q-43 0 -69 -31l-125 -161h194q40 0 68 28t28 68zM1536 864v-320 q0 -14 -9 -23t-23 -9h-96v-416q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28t-28 68v416h-96q-14 0 -23 9t-9 23v320q0 14 9 23t23 9h440q-93 0 -158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5q107 0 168 -77l128 -165l128 165q61 77 168 77q93 0 158.5 -65.5t65.5 -158.5 t-65.5 -158.5t-158.5 -65.5h440q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf06c;" horiz-adv-x="1792" d="M1280 832q0 26 -19 45t-45 19q-172 0 -318 -49.5t-259.5 -134t-235.5 -219.5q-19 -21 -19 -45q0 -26 19 -45t45 -19q24 0 45 19q27 24 74 71t67 66q137 124 268.5 176t313.5 52q26 0 45 19t19 45zM1792 1030q0 -95 -20 -193q-46 -224 -184.5 -383t-357.5 -268 q-214 -108 -438 -108q-148 0 -286 47q-15 5 -88 42t-96 37q-16 0 -39.5 -32t-45 -70t-52.5 -70t-60 -32q-30 0 -51 11t-31 24t-27 42q-2 4 -6 11t-5.5 10t-3 9.5t-1.5 13.5q0 35 31 73.5t68 65.5t68 56t31 48q0 4 -14 38t-16 44q-9 51 -9 104q0 115 43.5 220t119 184.5 t170.5 139t204 95.5q55 18 145 25.5t179.5 9t178.5 6t163.5 24t113.5 56.5l29.5 29.5t29.5 28t27 20t36.5 16t43.5 4.5q39 0 70.5 -46t47.5 -112t24 -124t8 -96z" />
+<glyph unicode="&#xf06d;" horiz-adv-x="1408" d="M1408 -160v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1152 896q0 -78 -24.5 -144t-64 -112.5t-87.5 -88t-96 -77.5t-87.5 -72t-64 -81.5t-24.5 -96.5q0 -96 67 -224l-4 1l1 -1 q-90 41 -160 83t-138.5 100t-113.5 122.5t-72.5 150.5t-27.5 184q0 78 24.5 144t64 112.5t87.5 88t96 77.5t87.5 72t64 81.5t24.5 96.5q0 94 -66 224l3 -1l-1 1q90 -41 160 -83t138.5 -100t113.5 -122.5t72.5 -150.5t27.5 -184z" />
+<glyph unicode="&#xf06e;" horiz-adv-x="1792" d="M1664 576q-152 236 -381 353q61 -104 61 -225q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 121 61 225q-229 -117 -381 -353q133 -205 333.5 -326.5t434.5 -121.5t434.5 121.5t333.5 326.5zM944 960q0 20 -14 34t-34 14q-125 0 -214.5 -89.5 t-89.5 -214.5q0 -20 14 -34t34 -14t34 14t14 34q0 86 61 147t147 61q20 0 34 14t14 34zM1792 576q0 -34 -20 -69q-140 -230 -376.5 -368.5t-499.5 -138.5t-499.5 139t-376.5 368q-20 35 -20 69t20 69q140 229 376.5 368t499.5 139t499.5 -139t376.5 -368q20 -35 20 -69z" />
+<glyph unicode="&#xf070;" horiz-adv-x="1792" d="M555 201l78 141q-87 63 -136 159t-49 203q0 121 61 225q-229 -117 -381 -353q167 -258 427 -375zM944 960q0 20 -14 34t-34 14q-125 0 -214.5 -89.5t-89.5 -214.5q0 -20 14 -34t34 -14t34 14t14 34q0 86 61 147t147 61q20 0 34 14t14 34zM1307 1151q0 -7 -1 -9 q-105 -188 -315 -566t-316 -567l-49 -89q-10 -16 -28 -16q-12 0 -134 70q-16 10 -16 28q0 12 44 87q-143 65 -263.5 173t-208.5 245q-20 31 -20 69t20 69q153 235 380 371t496 136q89 0 180 -17l54 97q10 16 28 16q5 0 18 -6t31 -15.5t33 -18.5t31.5 -18.5t19.5 -11.5 q16 -10 16 -27zM1344 704q0 -139 -79 -253.5t-209 -164.5l280 502q8 -45 8 -84zM1792 576q0 -35 -20 -69q-39 -64 -109 -145q-150 -172 -347.5 -267t-419.5 -95l74 132q212 18 392.5 137t301.5 307q-115 179 -282 294l63 112q95 -64 182.5 -153t144.5 -184q20 -34 20 -69z " />
+<glyph unicode="&#xf071;" horiz-adv-x="1792" d="M1024 161v190q0 14 -9.5 23.5t-22.5 9.5h-192q-13 0 -22.5 -9.5t-9.5 -23.5v-190q0 -14 9.5 -23.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 23.5zM1022 535l18 459q0 12 -10 19q-13 11 -24 11h-220q-11 0 -24 -11q-10 -7 -10 -21l17 -457q0 -10 10 -16.5t24 -6.5h185 q14 0 23.5 6.5t10.5 16.5zM1008 1469l768 -1408q35 -63 -2 -126q-17 -29 -46.5 -46t-63.5 -17h-1536q-34 0 -63.5 17t-46.5 46q-37 63 -2 126l768 1408q17 31 47 49t65 18t65 -18t47 -49z" />
+<glyph unicode="&#xf072;" horiz-adv-x="1408" d="M1376 1376q44 -52 12 -148t-108 -172l-161 -161l160 -696q5 -19 -12 -33l-128 -96q-7 -6 -19 -6q-4 0 -7 1q-15 3 -21 16l-279 508l-259 -259l53 -194q5 -17 -8 -31l-96 -96q-9 -9 -23 -9h-2q-15 2 -24 13l-189 252l-252 189q-11 7 -13 23q-1 13 9 25l96 97q9 9 23 9 q6 0 8 -1l194 -53l259 259l-508 279q-14 8 -17 24q-2 16 9 27l128 128q14 13 30 8l665 -159l160 160q76 76 172 108t148 -12z" />
+<glyph unicode="&#xf073;" horiz-adv-x="1664" d="M128 -128h288v288h-288v-288zM480 -128h320v288h-320v-288zM128 224h288v320h-288v-320zM480 224h320v320h-320v-320zM128 608h288v288h-288v-288zM864 -128h320v288h-320v-288zM480 608h320v288h-320v-288zM1248 -128h288v288h-288v-288zM864 224h320v320h-320v-320z M512 1088v288q0 13 -9.5 22.5t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-288q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1248 224h288v320h-288v-320zM864 608h320v288h-320v-288zM1248 608h288v288h-288v-288zM1280 1088v288q0 13 -9.5 22.5t-22.5 9.5h-64 q-13 0 -22.5 -9.5t-9.5 -22.5v-288q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47 h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf074;" horiz-adv-x="1792" d="M666 1055q-60 -92 -137 -273q-22 45 -37 72.5t-40.5 63.5t-51 56.5t-63 35t-81.5 14.5h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224q250 0 410 -225zM1792 256q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v192q-32 0 -85 -0.5t-81 -1t-73 1 t-71 5t-64 10.5t-63 18.5t-58 28.5t-59 40t-55 53.5t-56 69.5q59 93 136 273q22 -45 37 -72.5t40.5 -63.5t51 -56.5t63 -35t81.5 -14.5h256v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23zM1792 1152q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5 v192h-256q-48 0 -87 -15t-69 -45t-51 -61.5t-45 -77.5q-32 -62 -78 -171q-29 -66 -49.5 -111t-54 -105t-64 -100t-74 -83t-90 -68.5t-106.5 -42t-128 -16.5h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224q48 0 87 15t69 45t51 61.5t45 77.5q32 62 78 171q29 66 49.5 111 t54 105t64 100t74 83t90 68.5t106.5 42t128 16.5h256v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23z" />
+<glyph unicode="&#xf075;" horiz-adv-x="1792" d="M1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22q-17 -2 -30.5 9t-17.5 29v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51t27 59t26 76q-157 89 -247.5 220t-90.5 281 q0 130 71 248.5t191 204.5t286 136.5t348 50.5q244 0 450 -85.5t326 -233t120 -321.5z" />
+<glyph unicode="&#xf076;" d="M1536 704v-128q0 -201 -98.5 -362t-274 -251.5t-395.5 -90.5t-395.5 90.5t-274 251.5t-98.5 362v128q0 26 19 45t45 19h384q26 0 45 -19t19 -45v-128q0 -52 23.5 -90t53.5 -57t71 -30t64 -13t44 -2t44 2t64 13t71 30t53.5 57t23.5 90v128q0 26 19 45t45 19h384 q26 0 45 -19t19 -45zM512 1344v-384q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h384q26 0 45 -19t19 -45zM1536 1344v-384q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h384q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf077;" horiz-adv-x="1792" d="M1683 205l-166 -165q-19 -19 -45 -19t-45 19l-531 531l-531 -531q-19 -19 -45 -19t-45 19l-166 165q-19 19 -19 45.5t19 45.5l742 741q19 19 45 19t45 -19l742 -741q19 -19 19 -45.5t-19 -45.5z" />
+<glyph unicode="&#xf078;" horiz-adv-x="1792" d="M1683 728l-742 -741q-19 -19 -45 -19t-45 19l-742 741q-19 19 -19 45.5t19 45.5l166 165q19 19 45 19t45 -19l531 -531l531 531q19 19 45 19t45 -19l166 -165q19 -19 19 -45.5t-19 -45.5z" />
+<glyph unicode="&#xf079;" horiz-adv-x="1920" d="M1280 32q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-8 0 -13.5 2t-9 7t-5.5 8t-3 11.5t-1 11.5v13v11v160v416h-192q-26 0 -45 19t-19 45q0 24 15 41l320 384q19 22 49 22t49 -22l320 -384q15 -17 15 -41q0 -26 -19 -45t-45 -19h-192v-384h576q16 0 25 -11l160 -192q7 -11 7 -21 zM1920 448q0 -24 -15 -41l-320 -384q-20 -23 -49 -23t-49 23l-320 384q-15 17 -15 41q0 26 19 45t45 19h192v384h-576q-16 0 -25 12l-160 192q-7 9 -7 20q0 13 9.5 22.5t22.5 9.5h960q8 0 13.5 -2t9 -7t5.5 -8t3 -11.5t1 -11.5v-13v-11v-160v-416h192q26 0 45 -19t19 -45z " />
+<glyph unicode="&#xf07a;" horiz-adv-x="1664" d="M640 0q0 -52 -38 -90t-90 -38t-90 38t-38 90t38 90t90 38t90 -38t38 -90zM1536 0q0 -52 -38 -90t-90 -38t-90 38t-38 90t38 90t90 38t90 -38t38 -90zM1664 1088v-512q0 -24 -16.5 -42.5t-40.5 -21.5l-1044 -122q13 -60 13 -70q0 -16 -24 -64h920q26 0 45 -19t19 -45 t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 11 8 31.5t16 36t21.5 40t15.5 29.5l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t19.5 -15.5t13 -24.5t8 -26t5.5 -29.5t4.5 -26h1201q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf07b;" horiz-adv-x="1664" d="M1664 928v-704q0 -92 -66 -158t-158 -66h-1216q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h672q92 0 158 -66t66 -158z" />
+<glyph unicode="&#xf07c;" horiz-adv-x="1920" d="M1879 584q0 -31 -31 -66l-336 -396q-43 -51 -120.5 -86.5t-143.5 -35.5h-1088q-34 0 -60.5 13t-26.5 43q0 31 31 66l336 396q43 51 120.5 86.5t143.5 35.5h1088q34 0 60.5 -13t26.5 -43zM1536 928v-160h-832q-94 0 -197 -47.5t-164 -119.5l-337 -396l-5 -6q0 4 -0.5 12.5 t-0.5 12.5v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h544q92 0 158 -66t66 -158z" />
+<glyph unicode="&#xf07d;" horiz-adv-x="768" d="M704 1216q0 -26 -19 -45t-45 -19h-128v-1024h128q26 0 45 -19t19 -45t-19 -45l-256 -256q-19 -19 -45 -19t-45 19l-256 256q-19 19 -19 45t19 45t45 19h128v1024h-128q-26 0 -45 19t-19 45t19 45l256 256q19 19 45 19t45 -19l256 -256q19 -19 19 -45z" />
+<glyph unicode="&#xf07e;" horiz-adv-x="1792" d="M1792 640q0 -26 -19 -45l-256 -256q-19 -19 -45 -19t-45 19t-19 45v128h-1024v-128q0 -26 -19 -45t-45 -19t-45 19l-256 256q-19 19 -19 45t19 45l256 256q19 19 45 19t45 -19t19 -45v-128h1024v128q0 26 19 45t45 19t45 -19l256 -256q19 -19 19 -45z" />
+<glyph unicode="&#xf080;" horiz-adv-x="2048" d="M640 640v-512h-256v512h256zM1024 1152v-1024h-256v1024h256zM2048 0v-128h-2048v1536h128v-1408h1920zM1408 896v-768h-256v768h256zM1792 1280v-1152h-256v1152h256z" />
+<glyph unicode="&#xf081;" d="M1280 926q-56 -25 -121 -34q68 40 93 117q-65 -38 -134 -51q-61 66 -153 66q-87 0 -148.5 -61.5t-61.5 -148.5q0 -29 5 -48q-129 7 -242 65t-192 155q-29 -50 -29 -106q0 -114 91 -175q-47 1 -100 26v-2q0 -75 50 -133.5t123 -72.5q-29 -8 -51 -8q-13 0 -39 4 q21 -63 74.5 -104t121.5 -42q-116 -90 -261 -90q-26 0 -50 3q148 -94 322 -94q112 0 210 35.5t168 95t120.5 137t75 162t24.5 168.5q0 18 -1 27q63 45 105 109zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5 t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf082;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-188v595h199l30 232h-229v148q0 56 23.5 84t91.5 28l122 1v207q-63 9 -178 9q-136 0 -217.5 -80t-81.5 -226v-171h-200v-232h200v-595h-532q-119 0 -203.5 84.5t-84.5 203.5v960 q0 119 84.5 203.5t203.5 84.5h960z" />
+<glyph unicode="&#xf083;" horiz-adv-x="1792" d="M928 704q0 14 -9 23t-23 9q-66 0 -113 -47t-47 -113q0 -14 9 -23t23 -9t23 9t9 23q0 40 28 68t68 28q14 0 23 9t9 23zM1152 574q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM128 0h1536v128h-1536v-128zM1280 574q0 159 -112.5 271.5 t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM256 1216h384v128h-384v-128zM128 1024h1536v118v138h-828l-64 -128h-644v-128zM1792 1280v-1280q0 -53 -37.5 -90.5t-90.5 -37.5h-1536q-53 0 -90.5 37.5t-37.5 90.5v1280 q0 53 37.5 90.5t90.5 37.5h1536q53 0 90.5 -37.5t37.5 -90.5z" />
+<glyph unicode="&#xf084;" horiz-adv-x="1792" d="M832 1024q0 80 -56 136t-136 56t-136 -56t-56 -136q0 -42 19 -83q-41 19 -83 19q-80 0 -136 -56t-56 -136t56 -136t136 -56t136 56t56 136q0 42 -19 83q41 -19 83 -19q80 0 136 56t56 136zM1683 320q0 -17 -49 -66t-66 -49q-9 0 -28.5 16t-36.5 33t-38.5 40t-24.5 26 l-96 -96l220 -220q28 -28 28 -68q0 -42 -39 -81t-81 -39q-40 0 -68 28l-671 671q-176 -131 -365 -131q-163 0 -265.5 102.5t-102.5 265.5q0 160 95 313t248 248t313 95q163 0 265.5 -102.5t102.5 -265.5q0 -189 -131 -365l355 -355l96 96q-3 3 -26 24.5t-40 38.5t-33 36.5 t-16 28.5q0 17 49 66t66 49q13 0 23 -10q6 -6 46 -44.5t82 -79.5t86.5 -86t73 -78t28.5 -41z" />
+<glyph unicode="&#xf085;" horiz-adv-x="1920" d="M896 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1664 128q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 1152q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5 t90.5 37.5t37.5 90.5zM1280 731v-185q0 -10 -7 -19.5t-16 -10.5l-155 -24q-11 -35 -32 -76q34 -48 90 -115q7 -10 7 -20q0 -12 -7 -19q-23 -30 -82.5 -89.5t-78.5 -59.5q-11 0 -21 7l-115 90q-37 -19 -77 -31q-11 -108 -23 -155q-7 -24 -30 -24h-186q-11 0 -20 7.5t-10 17.5 l-23 153q-34 10 -75 31l-118 -89q-7 -7 -20 -7q-11 0 -21 8q-144 133 -144 160q0 9 7 19q10 14 41 53t47 61q-23 44 -35 82l-152 24q-10 1 -17 9.5t-7 19.5v185q0 10 7 19.5t16 10.5l155 24q11 35 32 76q-34 48 -90 115q-7 11 -7 20q0 12 7 20q22 30 82 89t79 59q11 0 21 -7 l115 -90q34 18 77 32q11 108 23 154q7 24 30 24h186q11 0 20 -7.5t10 -17.5l23 -153q34 -10 75 -31l118 89q8 7 20 7q11 0 21 -8q144 -133 144 -160q0 -9 -7 -19q-12 -16 -42 -54t-45 -60q23 -48 34 -82l152 -23q10 -2 17 -10.5t7 -19.5zM1920 198v-140q0 -16 -149 -31 q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20 t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31zM1920 1222v-140q0 -16 -149 -31q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68 q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70 q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31z" />
+<glyph unicode="&#xf086;" horiz-adv-x="1792" d="M1408 768q0 -139 -94 -257t-256.5 -186.5t-353.5 -68.5q-86 0 -176 16q-124 -88 -278 -128q-36 -9 -86 -16h-3q-11 0 -20.5 8t-11.5 21q-1 3 -1 6.5t0.5 6.5t2 6l2.5 5t3.5 5.5t4 5t4.5 5t4 4.5q5 6 23 25t26 29.5t22.5 29t25 38.5t20.5 44q-124 72 -195 177t-71 224 q0 139 94 257t256.5 186.5t353.5 68.5t353.5 -68.5t256.5 -186.5t94 -257zM1792 512q0 -120 -71 -224.5t-195 -176.5q10 -24 20.5 -44t25 -38.5t22.5 -29t26 -29.5t23 -25q1 -1 4 -4.5t4.5 -5t4 -5t3.5 -5.5l2.5 -5t2 -6t0.5 -6.5t-1 -6.5q-3 -14 -13 -22t-22 -7 q-50 7 -86 16q-154 40 -278 128q-90 -16 -176 -16q-271 0 -472 132q58 -4 88 -4q161 0 309 45t264 129q125 92 192 212t67 254q0 77 -23 152q129 -71 204 -178t75 -230z" />
+<glyph unicode="&#xf087;" d="M256 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 768q0 51 -39 89.5t-89 38.5h-352q0 58 48 159.5t48 160.5q0 98 -32 145t-128 47q-26 -26 -38 -85t-30.5 -125.5t-59.5 -109.5q-22 -23 -77 -91q-4 -5 -23 -30t-31.5 -41t-34.5 -42.5 t-40 -44t-38.5 -35.5t-40 -27t-35.5 -9h-32v-640h32q13 0 31.5 -3t33 -6.5t38 -11t35 -11.5t35.5 -12.5t29 -10.5q211 -73 342 -73h121q192 0 192 167q0 26 -5 56q30 16 47.5 52.5t17.5 73.5t-18 69q53 50 53 119q0 25 -10 55.5t-25 47.5q32 1 53.5 47t21.5 81zM1536 769 q0 -89 -49 -163q9 -33 9 -69q0 -77 -38 -144q3 -21 3 -43q0 -101 -60 -178q1 -139 -85 -219.5t-227 -80.5h-36h-93q-96 0 -189.5 22.5t-216.5 65.5q-116 40 -138 40h-288q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5h274q36 24 137 155q58 75 107 128 q24 25 35.5 85.5t30.5 126.5t62 108q39 37 90 37q84 0 151 -32.5t102 -101.5t35 -186q0 -93 -48 -192h176q104 0 180 -76t76 -179z" />
+<glyph unicode="&#xf088;" d="M256 1088q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 512q0 35 -21.5 81t-53.5 47q15 17 25 47.5t10 55.5q0 69 -53 119q18 32 18 69t-17.5 73.5t-47.5 52.5q5 30 5 56q0 85 -49 126t-136 41h-128q-131 0 -342 -73q-5 -2 -29 -10.5 t-35.5 -12.5t-35 -11.5t-38 -11t-33 -6.5t-31.5 -3h-32v-640h32q16 0 35.5 -9t40 -27t38.5 -35.5t40 -44t34.5 -42.5t31.5 -41t23 -30q55 -68 77 -91q41 -43 59.5 -109.5t30.5 -125.5t38 -85q96 0 128 47t32 145q0 59 -48 160.5t-48 159.5h352q50 0 89 38.5t39 89.5z M1536 511q0 -103 -76 -179t-180 -76h-176q48 -99 48 -192q0 -118 -35 -186q-35 -69 -102 -101.5t-151 -32.5q-51 0 -90 37q-34 33 -54 82t-25.5 90.5t-17.5 84.5t-31 64q-48 50 -107 127q-101 131 -137 155h-274q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5 h288q22 0 138 40q128 44 223 66t200 22h112q140 0 226.5 -79t85.5 -216v-5q60 -77 60 -178q0 -22 -3 -43q38 -67 38 -144q0 -36 -9 -69q49 -74 49 -163z" />
+<glyph unicode="&#xf089;" horiz-adv-x="896" d="M832 1504v-1339l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41z" />
+<glyph unicode="&#xf08a;" horiz-adv-x="1792" d="M1664 940q0 81 -21.5 143t-55 98.5t-81.5 59.5t-94 31t-98 8t-112 -25.5t-110.5 -64t-86.5 -72t-60 -61.5q-18 -22 -49 -22t-49 22q-24 28 -60 61.5t-86.5 72t-110.5 64t-112 25.5t-98 -8t-94 -31t-81.5 -59.5t-55 -98.5t-21.5 -143q0 -168 187 -355l581 -560l580 559 q188 188 188 356zM1792 940q0 -221 -229 -450l-623 -600q-18 -18 -44 -18t-44 18l-624 602q-10 8 -27.5 26t-55.5 65.5t-68 97.5t-53.5 121t-23.5 138q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5 q224 0 351 -124t127 -344z" />
+<glyph unicode="&#xf08b;" horiz-adv-x="1664" d="M640 96q0 -4 1 -20t0.5 -26.5t-3 -23.5t-10 -19.5t-20.5 -6.5h-320q-119 0 -203.5 84.5t-84.5 203.5v704q0 119 84.5 203.5t203.5 84.5h320q13 0 22.5 -9.5t9.5 -22.5q0 -4 1 -20t0.5 -26.5t-3 -23.5t-10 -19.5t-20.5 -6.5h-320q-66 0 -113 -47t-47 -113v-704 q0 -66 47 -113t113 -47h288h11h13t11.5 -1t11.5 -3t8 -5.5t7 -9t2 -13.5zM1568 640q0 -26 -19 -45l-544 -544q-19 -19 -45 -19t-45 19t-19 45v288h-448q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h448v288q0 26 19 45t45 19t45 -19l544 -544q19 -19 19 -45z" />
+<glyph unicode="&#xf08c;" d="M237 122h231v694h-231v-694zM483 1030q-1 52 -36 86t-93 34t-94.5 -34t-36.5 -86q0 -51 35.5 -85.5t92.5 -34.5h1q59 0 95 34.5t36 85.5zM1068 122h231v398q0 154 -73 233t-193 79q-136 0 -209 -117h2v101h-231q3 -66 0 -694h231v388q0 38 7 56q15 35 45 59.5t74 24.5 q116 0 116 -157v-371zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf08d;" horiz-adv-x="1152" d="M480 672v448q0 14 -9 23t-23 9t-23 -9t-9 -23v-448q0 -14 9 -23t23 -9t23 9t9 23zM1152 320q0 -26 -19 -45t-45 -19h-429l-51 -483q-2 -12 -10.5 -20.5t-20.5 -8.5h-1q-27 0 -32 27l-76 485h-404q-26 0 -45 19t-19 45q0 123 78.5 221.5t177.5 98.5v512q-52 0 -90 38 t-38 90t38 90t90 38h640q52 0 90 -38t38 -90t-38 -90t-90 -38v-512q99 0 177.5 -98.5t78.5 -221.5z" />
+<glyph unicode="&#xf08e;" horiz-adv-x="1792" d="M1408 608v-320q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h704q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v320 q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1792 1472v-512q0 -26 -19 -45t-45 -19t-45 19l-176 176l-652 -652q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l652 652l-176 176q-19 19 -19 45t19 45t45 19h512q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf090;" d="M1184 640q0 -26 -19 -45l-544 -544q-19 -19 -45 -19t-45 19t-19 45v288h-448q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h448v288q0 26 19 45t45 19t45 -19l544 -544q19 -19 19 -45zM1536 992v-704q0 -119 -84.5 -203.5t-203.5 -84.5h-320q-13 0 -22.5 9.5t-9.5 22.5 q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q66 0 113 47t47 113v704q0 66 -47 113t-113 47h-288h-11h-13t-11.5 1t-11.5 3t-8 5.5t-7 9t-2 13.5q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf091;" horiz-adv-x="1664" d="M458 653q-74 162 -74 371h-256v-96q0 -78 94.5 -162t235.5 -113zM1536 928v96h-256q0 -209 -74 -371q141 29 235.5 113t94.5 162zM1664 1056v-128q0 -71 -41.5 -143t-112 -130t-173 -97.5t-215.5 -44.5q-42 -54 -95 -95q-38 -34 -52.5 -72.5t-14.5 -89.5q0 -54 30.5 -91 t97.5 -37q75 0 133.5 -45.5t58.5 -114.5v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v64q0 69 58.5 114.5t133.5 45.5q67 0 97.5 37t30.5 91q0 51 -14.5 89.5t-52.5 72.5q-53 41 -95 95q-113 5 -215.5 44.5t-173 97.5t-112 130t-41.5 143v128q0 40 28 68t68 28h288v96 q0 66 47 113t113 47h576q66 0 113 -47t47 -113v-96h288q40 0 68 -28t28 -68z" />
+<glyph unicode="&#xf092;" d="M394 184q-8 -9 -20 3q-13 11 -4 19q8 9 20 -3q12 -11 4 -19zM352 245q9 -12 0 -19q-8 -6 -17 7t0 18q9 7 17 -6zM291 305q-5 -7 -13 -2q-10 5 -7 12q3 5 13 2q10 -5 7 -12zM322 271q-6 -7 -16 3q-9 11 -2 16q6 6 16 -3q9 -11 2 -16zM451 159q-4 -12 -19 -6q-17 4 -13 15 t19 7q16 -5 13 -16zM514 154q0 -11 -16 -11q-17 -2 -17 11q0 11 16 11q17 2 17 -11zM572 164q2 -10 -14 -14t-18 8t14 15q16 2 18 -9zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-224q-16 0 -24.5 1t-19.5 5t-16 14.5t-5 27.5v239q0 97 -52 142q57 6 102.5 18t94 39 t81 66.5t53 105t20.5 150.5q0 121 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-86 13.5q-44 -113 -7 -204q-79 -85 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-40 -36 -49 -103 q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -103t0.5 -68q0 -22 -11 -33.5t-22 -13t-33 -1.5 h-224q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf093;" horiz-adv-x="1664" d="M1280 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 288v-320q0 -40 -28 -68t-68 -28h-1472q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h427q21 -56 70.5 -92 t110.5 -36h256q61 0 110.5 36t70.5 92h427q40 0 68 -28t28 -68zM1339 936q-17 -40 -59 -40h-256v-448q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v448h-256q-42 0 -59 40q-17 39 14 69l448 448q18 19 45 19t45 -19l448 -448q31 -30 14 -69z" />
+<glyph unicode="&#xf094;" d="M1407 710q0 44 -7 113.5t-18 96.5q-12 30 -17 44t-9 36.5t-4 48.5q0 23 5 68.5t5 67.5q0 37 -10 55q-4 1 -13 1q-19 0 -58 -4.5t-59 -4.5q-60 0 -176 24t-175 24q-43 0 -94.5 -11.5t-85 -23.5t-89.5 -34q-137 -54 -202 -103q-96 -73 -159.5 -189.5t-88 -236t-24.5 -248.5 q0 -40 12.5 -120t12.5 -121q0 -23 -11 -66.5t-11 -65.5t12 -36.5t34 -14.5q24 0 72.5 11t73.5 11q57 0 169.5 -15.5t169.5 -15.5q181 0 284 36q129 45 235.5 152.5t166 245.5t59.5 275zM1535 712q0 -165 -70 -327.5t-196 -288t-281 -180.5q-124 -44 -326 -44 q-57 0 -170 14.5t-169 14.5q-24 0 -72.5 -14.5t-73.5 -14.5q-73 0 -123.5 55.5t-50.5 128.5q0 24 11 68t11 67q0 40 -12.5 120.5t-12.5 121.5q0 111 18 217.5t54.5 209.5t100.5 194t150 156q78 59 232 120q194 78 316 78q60 0 175.5 -24t173.5 -24q19 0 57 5t58 5 q81 0 118 -50.5t37 -134.5q0 -23 -5 -68t-5 -68q0 -10 1 -18.5t3 -17t4 -13.5t6.5 -16t6.5 -17q16 -40 25 -118.5t9 -136.5z" />
+<glyph unicode="&#xf095;" horiz-adv-x="1408" d="M1408 296q0 -27 -10 -70.5t-21 -68.5q-21 -50 -122 -106q-94 -51 -186 -51q-27 0 -52.5 3.5t-57.5 12.5t-47.5 14.5t-55.5 20.5t-49 18q-98 35 -175 83q-128 79 -264.5 215.5t-215.5 264.5q-48 77 -83 175q-3 9 -18 49t-20.5 55.5t-14.5 47.5t-12.5 57.5t-3.5 52.5 q0 92 51 186q56 101 106 122q25 11 68.5 21t70.5 10q14 0 21 -3q18 -6 53 -76q11 -19 30 -54t35 -63.5t31 -53.5q3 -4 17.5 -25t21.5 -35.5t7 -28.5q0 -20 -28.5 -50t-62 -55t-62 -53t-28.5 -46q0 -9 5 -22.5t8.5 -20.5t14 -24t11.5 -19q76 -137 174 -235t235 -174 q2 -1 19 -11.5t24 -14t20.5 -8.5t22.5 -5q18 0 46 28.5t53 62t55 62t50 28.5q14 0 28.5 -7t35.5 -21.5t25 -17.5q25 -15 53.5 -31t63.5 -35t54 -30q70 -35 76 -53q3 -7 3 -21z" />
+<glyph unicode="&#xf096;" horiz-adv-x="1408" d="M1120 1280h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v832q0 66 -47 113t-113 47zM1408 1120v-832q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832 q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf097;" horiz-adv-x="1280" d="M1152 1280h-1024v-1242l423 406l89 85l89 -85l423 -406v1242zM1164 1408q23 0 44 -9q33 -13 52.5 -41t19.5 -62v-1289q0 -34 -19.5 -62t-52.5 -41q-19 -8 -44 -8q-48 0 -83 32l-441 424l-441 -424q-36 -33 -83 -33q-23 0 -44 9q-33 13 -52.5 41t-19.5 62v1289 q0 34 19.5 62t52.5 41q21 9 44 9h1048z" />
+<glyph unicode="&#xf098;" d="M1280 343q0 11 -2 16q-3 8 -38.5 29.5t-88.5 49.5l-53 29q-5 3 -19 13t-25 15t-21 5q-18 0 -47 -32.5t-57 -65.5t-44 -33q-7 0 -16.5 3.5t-15.5 6.5t-17 9.5t-14 8.5q-99 55 -170.5 126.5t-126.5 170.5q-2 3 -8.5 14t-9.5 17t-6.5 15.5t-3.5 16.5q0 13 20.5 33.5t45 38.5 t45 39.5t20.5 36.5q0 10 -5 21t-15 25t-13 19q-3 6 -15 28.5t-25 45.5t-26.5 47.5t-25 40.5t-16.5 18t-16 2q-48 0 -101 -22q-46 -21 -80 -94.5t-34 -130.5q0 -16 2.5 -34t5 -30.5t9 -33t10 -29.5t12.5 -33t11 -30q60 -164 216.5 -320.5t320.5 -216.5q6 -2 30 -11t33 -12.5 t29.5 -10t33 -9t30.5 -5t34 -2.5q57 0 130.5 34t94.5 80q22 53 22 101zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf099;" horiz-adv-x="1664" d="M1620 1128q-67 -98 -162 -167q1 -14 1 -42q0 -130 -38 -259.5t-115.5 -248.5t-184.5 -210.5t-258 -146t-323 -54.5q-271 0 -496 145q35 -4 78 -4q225 0 401 138q-105 2 -188 64.5t-114 159.5q33 -5 61 -5q43 0 85 11q-112 23 -185.5 111.5t-73.5 205.5v4q68 -38 146 -41 q-66 44 -105 115t-39 154q0 88 44 163q121 -149 294.5 -238.5t371.5 -99.5q-8 38 -8 74q0 134 94.5 228.5t228.5 94.5q140 0 236 -102q109 21 205 78q-37 -115 -142 -178q93 10 186 50z" />
+<glyph unicode="&#xf09a;" horiz-adv-x="1024" d="M959 1524v-264h-157q-86 0 -116 -36t-30 -108v-189h293l-39 -296h-254v-759h-306v759h-255v296h255v218q0 186 104 288.5t277 102.5q147 0 228 -12z" />
+<glyph unicode="&#xf09b;" d="M1536 640q0 -251 -146.5 -451.5t-378.5 -277.5q-27 -5 -39.5 7t-12.5 30v211q0 97 -52 142q57 6 102.5 18t94 39t81 66.5t53 105t20.5 150.5q0 121 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-86 13.5 q-44 -113 -7 -204q-79 -85 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-40 -36 -49 -103q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23 q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -89t0.5 -54q0 -18 -13 -30t-40 -7q-232 77 -378.5 277.5t-146.5 451.5q0 209 103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf09c;" horiz-adv-x="1664" d="M1664 960v-256q0 -26 -19 -45t-45 -19h-64q-26 0 -45 19t-19 45v256q0 106 -75 181t-181 75t-181 -75t-75 -181v-192h96q40 0 68 -28t28 -68v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h672v192q0 185 131.5 316.5t316.5 131.5 t316.5 -131.5t131.5 -316.5z" />
+<glyph unicode="&#xf09d;" horiz-adv-x="1920" d="M1760 1408q66 0 113 -47t47 -113v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600zM160 1280q-13 0 -22.5 -9.5t-9.5 -22.5v-224h1664v224q0 13 -9.5 22.5t-22.5 9.5h-1600zM1760 0q13 0 22.5 9.5t9.5 22.5v608h-1664v-608 q0 -13 9.5 -22.5t22.5 -9.5h1600zM256 128v128h256v-128h-256zM640 128v128h384v-128h-384z" />
+<glyph unicode="&#xf09e;" horiz-adv-x="1408" d="M384 192q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 69q2 -28 -17 -48q-18 -21 -47 -21h-135q-25 0 -43 16.5t-20 41.5q-22 229 -184.5 391.5t-391.5 184.5q-25 2 -41.5 20t-16.5 43v135q0 29 21 47q17 17 43 17h5q160 -13 306 -80.5 t259 -181.5q114 -113 181.5 -259t80.5 -306zM1408 67q2 -27 -18 -47q-18 -20 -46 -20h-143q-26 0 -44.5 17.5t-19.5 42.5q-12 215 -101 408.5t-231.5 336t-336 231.5t-408.5 102q-25 1 -42.5 19.5t-17.5 43.5v143q0 28 20 46q18 18 44 18h3q262 -13 501.5 -120t425.5 -294 q187 -186 294 -425.5t120 -501.5z" />
+<glyph unicode="&#xf0a0;" d="M1040 320q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5zM1296 320q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5zM1408 160v320q0 13 -9.5 22.5t-22.5 9.5 h-1216q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h1216q13 0 22.5 9.5t9.5 22.5zM178 640h1180l-157 482q-4 13 -16 21.5t-26 8.5h-782q-14 0 -26 -8.5t-16 -21.5zM1536 480v-320q0 -66 -47 -113t-113 -47h-1216q-66 0 -113 47t-47 113v320q0 25 16 75 l197 606q17 53 63 86t101 33h782q55 0 101 -33t63 -86l197 -606q16 -50 16 -75z" />
+<glyph unicode="&#xf0a1;" horiz-adv-x="1792" d="M1664 896q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5v-384q0 -52 -38 -90t-90 -38q-417 347 -812 380q-58 -19 -91 -66t-31 -100.5t40 -92.5q-20 -33 -23 -65.5t6 -58t33.5 -55t48 -50t61.5 -50.5q-29 -58 -111.5 -83t-168.5 -11.5t-132 55.5q-7 23 -29.5 87.5 t-32 94.5t-23 89t-15 101t3.5 98.5t22 110.5h-122q-66 0 -113 47t-47 113v192q0 66 47 113t113 47h480q435 0 896 384q52 0 90 -38t38 -90v-384zM1536 292v954q-394 -302 -768 -343v-270q377 -42 768 -341z" />
+<glyph unicode="&#xf0a2;" horiz-adv-x="1792" d="M912 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM246 128h1300q-266 300 -266 832q0 51 -24 105t-69 103t-121.5 80.5t-169.5 31.5t-169.5 -31.5t-121.5 -80.5t-69 -103t-24 -105q0 -532 -266 -832z M1728 128q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-181 75t-75 181h-448q-52 0 -90 38t-38 90q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q190 -28 307 -158.5 t117 -282.5q0 -139 19.5 -260t50 -206t74.5 -158.5t85 -119.5t91 -88z" />
+<glyph unicode="&#xf0a3;" d="M1376 640l138 -135q30 -28 20 -70q-12 -41 -52 -51l-188 -48l53 -186q12 -41 -19 -70q-29 -31 -70 -19l-186 53l-48 -188q-10 -40 -51 -52q-12 -2 -19 -2q-31 0 -51 22l-135 138l-135 -138q-28 -30 -70 -20q-41 11 -51 52l-48 188l-186 -53q-41 -12 -70 19q-31 29 -19 70 l53 186l-188 48q-40 10 -52 51q-10 42 20 70l138 135l-138 135q-30 28 -20 70q12 41 52 51l188 48l-53 186q-12 41 19 70q29 31 70 19l186 -53l48 188q10 41 51 51q41 12 70 -19l135 -139l135 139q29 30 70 19q41 -10 51 -51l48 -188l186 53q41 12 70 -19q31 -29 19 -70 l-53 -186l188 -48q40 -10 52 -51q10 -42 -20 -70z" />
+<glyph unicode="&#xf0a4;" horiz-adv-x="1792" d="M256 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 768q0 51 -39 89.5t-89 38.5h-576q0 20 15 48.5t33 55t33 68t15 84.5q0 67 -44.5 97.5t-115.5 30.5q-24 0 -90 -139q-24 -44 -37 -65q-40 -64 -112 -145q-71 -81 -101 -106 q-69 -57 -140 -57h-32v-640h32q72 0 167 -32t193.5 -64t179.5 -32q189 0 189 167q0 26 -5 56q30 16 47.5 52.5t17.5 73.5t-18 69q53 50 53 119q0 25 -10 55.5t-25 47.5h331q52 0 90 38t38 90zM1792 769q0 -105 -75.5 -181t-180.5 -76h-169q-4 -62 -37 -119q3 -21 3 -43 q0 -101 -60 -178q1 -139 -85 -219.5t-227 -80.5q-133 0 -322 69q-164 59 -223 59h-288q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5h288q10 0 21.5 4.5t23.5 14t22.5 18t24 22.5t20.5 21.5t19 21.5t14 17q65 74 100 129q13 21 33 62t37 72t40.5 63t55 49.5 t69.5 17.5q125 0 206.5 -67t81.5 -189q0 -68 -22 -128h374q104 0 180 -76t76 -179z" />
+<glyph unicode="&#xf0a5;" horiz-adv-x="1792" d="M1376 128h32v640h-32q-35 0 -67.5 12t-62.5 37t-50 46t-49 54q-2 3 -3.5 4.5t-4 4.5t-4.5 5q-72 81 -112 145q-14 22 -38 68q-1 3 -10.5 22.5t-18.5 36t-20 35.5t-21.5 30.5t-18.5 11.5q-71 0 -115.5 -30.5t-44.5 -97.5q0 -43 15 -84.5t33 -68t33 -55t15 -48.5h-576 q-50 0 -89 -38.5t-39 -89.5q0 -52 38 -90t90 -38h331q-15 -17 -25 -47.5t-10 -55.5q0 -69 53 -119q-18 -32 -18 -69t17.5 -73.5t47.5 -52.5q-4 -24 -4 -56q0 -85 48.5 -126t135.5 -41q84 0 183 32t194 64t167 32zM1664 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45 t45 -19t45 19t19 45zM1792 768v-640q0 -53 -37.5 -90.5t-90.5 -37.5h-288q-59 0 -223 -59q-190 -69 -317 -69q-142 0 -230 77.5t-87 217.5l1 5q-61 76 -61 178q0 22 3 43q-33 57 -37 119h-169q-105 0 -180.5 76t-75.5 181q0 103 76 179t180 76h374q-22 60 -22 128 q0 122 81.5 189t206.5 67q38 0 69.5 -17.5t55 -49.5t40.5 -63t37 -72t33 -62q35 -55 100 -129q2 -3 14 -17t19 -21.5t20.5 -21.5t24 -22.5t22.5 -18t23.5 -14t21.5 -4.5h288q53 0 90.5 -37.5t37.5 -90.5z" />
+<glyph unicode="&#xf0a6;" d="M1280 -64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 700q0 189 -167 189q-26 0 -56 -5q-16 30 -52.5 47.5t-73.5 17.5t-69 -18q-50 53 -119 53q-25 0 -55.5 -10t-47.5 -25v331q0 52 -38 90t-90 38q-51 0 -89.5 -39t-38.5 -89v-576 q-20 0 -48.5 15t-55 33t-68 33t-84.5 15q-67 0 -97.5 -44.5t-30.5 -115.5q0 -24 139 -90q44 -24 65 -37q64 -40 145 -112q81 -71 106 -101q57 -69 57 -140v-32h640v32q0 72 32 167t64 193.5t32 179.5zM1536 705q0 -133 -69 -322q-59 -164 -59 -223v-288q0 -53 -37.5 -90.5 t-90.5 -37.5h-640q-53 0 -90.5 37.5t-37.5 90.5v288q0 10 -4.5 21.5t-14 23.5t-18 22.5t-22.5 24t-21.5 20.5t-21.5 19t-17 14q-74 65 -129 100q-21 13 -62 33t-72 37t-63 40.5t-49.5 55t-17.5 69.5q0 125 67 206.5t189 81.5q68 0 128 -22v374q0 104 76 180t179 76 q105 0 181 -75.5t76 -180.5v-169q62 -4 119 -37q21 3 43 3q101 0 178 -60q139 1 219.5 -85t80.5 -227z" />
+<glyph unicode="&#xf0a7;" d="M1408 576q0 84 -32 183t-64 194t-32 167v32h-640v-32q0 -35 -12 -67.5t-37 -62.5t-46 -50t-54 -49q-9 -8 -14 -12q-81 -72 -145 -112q-22 -14 -68 -38q-3 -1 -22.5 -10.5t-36 -18.5t-35.5 -20t-30.5 -21.5t-11.5 -18.5q0 -71 30.5 -115.5t97.5 -44.5q43 0 84.5 15t68 33 t55 33t48.5 15v-576q0 -50 38.5 -89t89.5 -39q52 0 90 38t38 90v331q46 -35 103 -35q69 0 119 53q32 -18 69 -18t73.5 17.5t52.5 47.5q24 -4 56 -4q85 0 126 48.5t41 135.5zM1280 1344q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 580 q0 -142 -77.5 -230t-217.5 -87l-5 1q-76 -61 -178 -61q-22 0 -43 3q-54 -30 -119 -37v-169q0 -105 -76 -180.5t-181 -75.5q-103 0 -179 76t-76 180v374q-54 -22 -128 -22q-121 0 -188.5 81.5t-67.5 206.5q0 38 17.5 69.5t49.5 55t63 40.5t72 37t62 33q55 35 129 100 q3 2 17 14t21.5 19t21.5 20.5t22.5 24t18 22.5t14 23.5t4.5 21.5v288q0 53 37.5 90.5t90.5 37.5h640q53 0 90.5 -37.5t37.5 -90.5v-288q0 -59 59 -223q69 -190 69 -317z" />
+<glyph unicode="&#xf0a8;" d="M1280 576v128q0 26 -19 45t-45 19h-502l189 189q19 19 19 45t-19 45l-91 91q-18 18 -45 18t-45 -18l-362 -362l-91 -91q-18 -18 -18 -45t18 -45l91 -91l362 -362q18 -18 45 -18t45 18l91 91q18 18 18 45t-18 45l-189 189h502q26 0 45 19t19 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf0a9;" d="M1285 640q0 27 -18 45l-91 91l-362 362q-18 18 -45 18t-45 -18l-91 -91q-18 -18 -18 -45t18 -45l189 -189h-502q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h502l-189 -189q-19 -19 -19 -45t19 -45l91 -91q18 -18 45 -18t45 18l362 362l91 91q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf0aa;" d="M1284 641q0 27 -18 45l-362 362l-91 91q-18 18 -45 18t-45 -18l-91 -91l-362 -362q-18 -18 -18 -45t18 -45l91 -91q18 -18 45 -18t45 18l189 189v-502q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v502l189 -189q19 -19 45 -19t45 19l91 91q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf0ab;" d="M1284 639q0 27 -18 45l-91 91q-18 18 -45 18t-45 -18l-189 -189v502q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-502l-189 189q-19 19 -45 19t-45 -19l-91 -91q-18 -18 -18 -45t18 -45l362 -362l91 -91q18 -18 45 -18t45 18l91 91l362 362q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf0ac;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM1042 887q-2 -1 -9.5 -9.5t-13.5 -9.5q2 0 4.5 5t5 11t3.5 7q6 7 22 15q14 6 52 12q34 8 51 -11 q-2 2 9.5 13t14.5 12q3 2 15 4.5t15 7.5l2 22q-12 -1 -17.5 7t-6.5 21q0 -2 -6 -8q0 7 -4.5 8t-11.5 -1t-9 -1q-10 3 -15 7.5t-8 16.5t-4 15q-2 5 -9.5 10.5t-9.5 10.5q-1 2 -2.5 5.5t-3 6.5t-4 5.5t-5.5 2.5t-7 -5t-7.5 -10t-4.5 -5q-3 2 -6 1.5t-4.5 -1t-4.5 -3t-5 -3.5 q-3 -2 -8.5 -3t-8.5 -2q15 5 -1 11q-10 4 -16 3q9 4 7.5 12t-8.5 14h5q-1 4 -8.5 8.5t-17.5 8.5t-13 6q-8 5 -34 9.5t-33 0.5q-5 -6 -4.5 -10.5t4 -14t3.5 -12.5q1 -6 -5.5 -13t-6.5 -12q0 -7 14 -15.5t10 -21.5q-3 -8 -16 -16t-16 -12q-5 -8 -1.5 -18.5t10.5 -16.5 q2 -2 1.5 -4t-3.5 -4.5t-5.5 -4t-6.5 -3.5l-3 -2q-11 -5 -20.5 6t-13.5 26q-7 25 -16 30q-23 8 -29 -1q-5 13 -41 26q-25 9 -58 4q6 1 0 15q-7 15 -19 12q3 6 4 17.5t1 13.5q3 13 12 23q1 1 7 8.5t9.5 13.5t0.5 6q35 -4 50 11q5 5 11.5 17t10.5 17q9 6 14 5.5t14.5 -5.5 t14.5 -5q14 -1 15.5 11t-7.5 20q12 -1 3 17q-5 7 -8 9q-12 4 -27 -5q-8 -4 2 -8q-1 1 -9.5 -10.5t-16.5 -17.5t-16 5q-1 1 -5.5 13.5t-9.5 13.5q-8 0 -16 -15q3 8 -11 15t-24 8q19 12 -8 27q-7 4 -20.5 5t-19.5 -4q-5 -7 -5.5 -11.5t5 -8t10.5 -5.5t11.5 -4t8.5 -3 q14 -10 8 -14q-2 -1 -8.5 -3.5t-11.5 -4.5t-6 -4q-3 -4 0 -14t-2 -14q-5 5 -9 17.5t-7 16.5q7 -9 -25 -6l-10 1q-4 0 -16 -2t-20.5 -1t-13.5 8q-4 8 0 20q1 4 4 2q-4 3 -11 9.5t-10 8.5q-46 -15 -94 -41q6 -1 12 1q5 2 13 6.5t10 5.5q34 14 42 7l5 5q14 -16 20 -25 q-7 4 -30 1q-20 -6 -22 -12q7 -12 5 -18q-4 3 -11.5 10t-14.5 11t-15 5q-16 0 -22 -1q-146 -80 -235 -222q7 -7 12 -8q4 -1 5 -9t2.5 -11t11.5 3q9 -8 3 -19q1 1 44 -27q19 -17 21 -21q3 -11 -10 -18q-1 2 -9 9t-9 4q-3 -5 0.5 -18.5t10.5 -12.5q-7 0 -9.5 -16t-2.5 -35.5 t-1 -23.5l2 -1q-3 -12 5.5 -34.5t21.5 -19.5q-13 -3 20 -43q6 -8 8 -9q3 -2 12 -7.5t15 -10t10 -10.5q4 -5 10 -22.5t14 -23.5q-2 -6 9.5 -20t10.5 -23q-1 0 -2.5 -1t-2.5 -1q3 -7 15.5 -14t15.5 -13q1 -3 2 -10t3 -11t8 -2q2 20 -24 62q-15 25 -17 29q-3 5 -5.5 15.5 t-4.5 14.5q2 0 6 -1.5t8.5 -3.5t7.5 -4t2 -3q-3 -7 2 -17.5t12 -18.5t17 -19t12 -13q6 -6 14 -19.5t0 -13.5q9 0 20 -10t17 -20q5 -8 8 -26t5 -24q2 -7 8.5 -13.5t12.5 -9.5l16 -8t13 -7q5 -2 18.5 -10.5t21.5 -11.5q10 -4 16 -4t14.5 2.5t13.5 3.5q15 2 29 -15t21 -21 q36 -19 55 -11q-2 -1 0.5 -7.5t8 -15.5t9 -14.5t5.5 -8.5q5 -6 18 -15t18 -15q6 4 7 9q-3 -8 7 -20t18 -10q14 3 14 32q-31 -15 -49 18q0 1 -2.5 5.5t-4 8.5t-2.5 8.5t0 7.5t5 3q9 0 10 3.5t-2 12.5t-4 13q-1 8 -11 20t-12 15q-5 -9 -16 -8t-16 9q0 -1 -1.5 -5.5t-1.5 -6.5 q-13 0 -15 1q1 3 2.5 17.5t3.5 22.5q1 4 5.5 12t7.5 14.5t4 12.5t-4.5 9.5t-17.5 2.5q-19 -1 -26 -20q-1 -3 -3 -10.5t-5 -11.5t-9 -7q-7 -3 -24 -2t-24 5q-13 8 -22.5 29t-9.5 37q0 10 2.5 26.5t3 25t-5.5 24.5q3 2 9 9.5t10 10.5q2 1 4.5 1.5t4.5 0t4 1.5t3 6q-1 1 -4 3 q-3 3 -4 3q7 -3 28.5 1.5t27.5 -1.5q15 -11 22 2q0 1 -2.5 9.5t-0.5 13.5q5 -27 29 -9q3 -3 15.5 -5t17.5 -5q3 -2 7 -5.5t5.5 -4.5t5 0.5t8.5 6.5q10 -14 12 -24q11 -40 19 -44q7 -3 11 -2t4.5 9.5t0 14t-1.5 12.5l-1 8v18l-1 8q-15 3 -18.5 12t1.5 18.5t15 18.5q1 1 8 3.5 t15.5 6.5t12.5 8q21 19 15 35q7 0 11 9q-1 0 -5 3t-7.5 5t-4.5 2q9 5 2 16q5 3 7.5 11t7.5 10q9 -12 21 -2q7 8 1 16q5 7 20.5 10.5t18.5 9.5q7 -2 8 2t1 12t3 12q4 5 15 9t13 5l17 11q3 4 0 4q18 -2 31 11q10 11 -6 20q3 6 -3 9.5t-15 5.5q3 1 11.5 0.5t10.5 1.5 q15 10 -7 16q-17 5 -43 -12zM879 10q206 36 351 189q-3 3 -12.5 4.5t-12.5 3.5q-18 7 -24 8q1 7 -2.5 13t-8 9t-12.5 8t-11 7q-2 2 -7 6t-7 5.5t-7.5 4.5t-8.5 2t-10 -1l-3 -1q-3 -1 -5.5 -2.5t-5.5 -3t-4 -3t0 -2.5q-21 17 -36 22q-5 1 -11 5.5t-10.5 7t-10 1.5t-11.5 -7 q-5 -5 -6 -15t-2 -13q-7 5 0 17.5t2 18.5q-3 6 -10.5 4.5t-12 -4.5t-11.5 -8.5t-9 -6.5t-8.5 -5.5t-8.5 -7.5q-3 -4 -6 -12t-5 -11q-2 4 -11.5 6.5t-9.5 5.5q2 -10 4 -35t5 -38q7 -31 -12 -48q-27 -25 -29 -40q-4 -22 12 -26q0 -7 -8 -20.5t-7 -21.5q0 -6 2 -16z" />
+<glyph unicode="&#xf0ad;" horiz-adv-x="1664" d="M384 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1028 484l-682 -682q-37 -37 -90 -37q-52 0 -91 37l-106 108q-38 36 -38 90q0 53 38 91l681 681q39 -98 114.5 -173.5t173.5 -114.5zM1662 919q0 -39 -23 -106q-47 -134 -164.5 -217.5 t-258.5 -83.5q-185 0 -316.5 131.5t-131.5 316.5t131.5 316.5t316.5 131.5q58 0 121.5 -16.5t107.5 -46.5q16 -11 16 -28t-16 -28l-293 -169v-224l193 -107q5 3 79 48.5t135.5 81t70.5 35.5q15 0 23.5 -10t8.5 -25z" />
+<glyph unicode="&#xf0ae;" horiz-adv-x="1792" d="M1024 128h640v128h-640v-128zM640 640h1024v128h-1024v-128zM1280 1152h384v128h-384v-128zM1792 320v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 832v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19 t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 1344v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf0b0;" horiz-adv-x="1408" d="M1403 1241q17 -41 -14 -70l-493 -493v-742q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-256 256q-19 19 -19 45v486l-493 493q-31 29 -14 70q17 39 59 39h1280q42 0 59 -39z" />
+<glyph unicode="&#xf0b1;" horiz-adv-x="1792" d="M640 1280h512v128h-512v-128zM1792 640v-480q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v480h672v-160q0 -26 19 -45t45 -19h320q26 0 45 19t19 45v160h672zM1024 640v-128h-256v128h256zM1792 1120v-384h-1792v384q0 66 47 113t113 47h352v160q0 40 28 68 t68 28h576q40 0 68 -28t28 -68v-160h352q66 0 113 -47t47 -113z" />
+<glyph unicode="&#xf0b2;" d="M1283 995l-355 -355l355 -355l144 144q29 31 70 14q39 -17 39 -59v-448q0 -26 -19 -45t-45 -19h-448q-42 0 -59 40q-17 39 14 69l144 144l-355 355l-355 -355l144 -144q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l144 -144 l355 355l-355 355l-144 -144q-19 -19 -45 -19q-12 0 -24 5q-40 17 -40 59v448q0 26 19 45t45 19h448q42 0 59 -40q17 -39 -14 -69l-144 -144l355 -355l355 355l-144 144q-31 30 -14 69q17 40 59 40h448q26 0 45 -19t19 -45v-448q0 -42 -39 -59q-13 -5 -25 -5q-26 0 -45 19z " />
+<glyph unicode="&#xf0c0;" horiz-adv-x="1920" d="M593 640q-162 -5 -265 -128h-134q-82 0 -138 40.5t-56 118.5q0 353 124 353q6 0 43.5 -21t97.5 -42.5t119 -21.5q67 0 133 23q-5 -37 -5 -66q0 -139 81 -256zM1664 3q0 -120 -73 -189.5t-194 -69.5h-874q-121 0 -194 69.5t-73 189.5q0 53 3.5 103.5t14 109t26.5 108.5 t43 97.5t62 81t85.5 53.5t111.5 20q10 0 43 -21.5t73 -48t107 -48t135 -21.5t135 21.5t107 48t73 48t43 21.5q61 0 111.5 -20t85.5 -53.5t62 -81t43 -97.5t26.5 -108.5t14 -109t3.5 -103.5zM640 1280q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75 t75 -181zM1344 896q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5zM1920 671q0 -78 -56 -118.5t-138 -40.5h-134q-103 123 -265 128q81 117 81 256q0 29 -5 66q66 -23 133 -23q59 0 119 21.5t97.5 42.5 t43.5 21q124 0 124 -353zM1792 1280q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181z" />
+<glyph unicode="&#xf0c1;" horiz-adv-x="1664" d="M1456 320q0 40 -28 68l-208 208q-28 28 -68 28q-42 0 -72 -32q3 -3 19 -18.5t21.5 -21.5t15 -19t13 -25.5t3.5 -27.5q0 -40 -28 -68t-68 -28q-15 0 -27.5 3.5t-25.5 13t-19 15t-21.5 21.5t-18.5 19q-33 -31 -33 -73q0 -40 28 -68l206 -207q27 -27 68 -27q40 0 68 26 l147 146q28 28 28 67zM753 1025q0 40 -28 68l-206 207q-28 28 -68 28q-39 0 -68 -27l-147 -146q-28 -28 -28 -67q0 -40 28 -68l208 -208q27 -27 68 -27q42 0 72 31q-3 3 -19 18.5t-21.5 21.5t-15 19t-13 25.5t-3.5 27.5q0 40 28 68t68 28q15 0 27.5 -3.5t25.5 -13t19 -15 t21.5 -21.5t18.5 -19q33 31 33 73zM1648 320q0 -120 -85 -203l-147 -146q-83 -83 -203 -83q-121 0 -204 85l-206 207q-83 83 -83 203q0 123 88 209l-88 88q-86 -88 -208 -88q-120 0 -204 84l-208 208q-84 84 -84 204t85 203l147 146q83 83 203 83q121 0 204 -85l206 -207 q83 -83 83 -203q0 -123 -88 -209l88 -88q86 88 208 88q120 0 204 -84l208 -208q84 -84 84 -204z" />
+<glyph unicode="&#xf0c2;" horiz-adv-x="1920" d="M1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088q-185 0 -316.5 131.5t-131.5 316.5q0 132 71 241.5t187 163.5q-2 28 -2 43q0 212 150 362t362 150q158 0 286.5 -88t187.5 -230q70 62 166 62q106 0 181 -75t75 -181q0 -75 -41 -138q129 -30 213 -134.5t84 -239.5z " />
+<glyph unicode="&#xf0c3;" horiz-adv-x="1664" d="M1527 88q56 -89 21.5 -152.5t-140.5 -63.5h-1152q-106 0 -140.5 63.5t21.5 152.5l503 793v399h-64q-26 0 -45 19t-19 45t19 45t45 19h512q26 0 45 -19t19 -45t-19 -45t-45 -19h-64v-399zM748 813l-272 -429h712l-272 429l-20 31v37v399h-128v-399v-37z" />
+<glyph unicode="&#xf0c4;" horiz-adv-x="1792" d="M960 640q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1260 576l507 -398q28 -20 25 -56q-5 -35 -35 -51l-128 -64q-13 -7 -29 -7q-17 0 -31 8l-690 387l-110 -66q-8 -4 -12 -5q14 -49 10 -97q-7 -77 -56 -147.5t-132 -123.5q-132 -84 -277 -84 q-136 0 -222 78q-90 84 -79 207q7 76 56 147t131 124q132 84 278 84q83 0 151 -31q9 13 22 22l122 73l-122 73q-13 9 -22 22q-68 -31 -151 -31q-146 0 -278 84q-82 53 -131 124t-56 147q-5 59 15.5 113t63.5 93q85 79 222 79q145 0 277 -84q83 -52 132 -123t56 -148 q4 -48 -10 -97q4 -1 12 -5l110 -66l690 387q14 8 31 8q16 0 29 -7l128 -64q30 -16 35 -51q3 -36 -25 -56zM579 836q46 42 21 108t-106 117q-92 59 -192 59q-74 0 -113 -36q-46 -42 -21 -108t106 -117q92 -59 192 -59q74 0 113 36zM494 91q81 51 106 117t-21 108 q-39 36 -113 36q-100 0 -192 -59q-81 -51 -106 -117t21 -108q39 -36 113 -36q100 0 192 59zM672 704l96 -58v11q0 36 33 56l14 8l-79 47l-26 -26q-3 -3 -10 -11t-12 -12q-2 -2 -4 -3.5t-3 -2.5zM896 480l96 -32l736 576l-128 64l-768 -431v-113l-160 -96l9 -8q2 -2 7 -6 q4 -4 11 -12t11 -12l26 -26zM1600 64l128 64l-520 408l-177 -138q-2 -3 -13 -7z" />
+<glyph unicode="&#xf0c5;" horiz-adv-x="1792" d="M1696 1152q40 0 68 -28t28 -68v-1216q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v288h-544q-40 0 -68 28t-28 68v672q0 40 20 88t48 76l408 408q28 28 76 48t88 20h416q40 0 68 -28t28 -68v-328q68 40 128 40h416zM1152 939l-299 -299h299v299zM512 1323l-299 -299 h299v299zM708 676l316 316v416h-384v-416q0 -40 -28 -68t-68 -28h-416v-640h512v256q0 40 20 88t48 76zM1664 -128v1152h-384v-416q0 -40 -28 -68t-68 -28h-416v-640h896z" />
+<glyph unicode="&#xf0c6;" horiz-adv-x="1408" d="M1404 151q0 -117 -79 -196t-196 -79q-135 0 -235 100l-777 776q-113 115 -113 271q0 159 110 270t269 111q158 0 273 -113l605 -606q10 -10 10 -22q0 -16 -30.5 -46.5t-46.5 -30.5q-13 0 -23 10l-606 607q-79 77 -181 77q-106 0 -179 -75t-73 -181q0 -105 76 -181 l776 -777q63 -63 145 -63q64 0 106 42t42 106q0 82 -63 145l-581 581q-26 24 -60 24q-29 0 -48 -19t-19 -48q0 -32 25 -59l410 -410q10 -10 10 -22q0 -16 -31 -47t-47 -31q-12 0 -22 10l-410 410q-63 61 -63 149q0 82 57 139t139 57q88 0 149 -63l581 -581q100 -98 100 -235 z" />
+<glyph unicode="&#xf0c7;" d="M384 0h768v384h-768v-384zM1280 0h128v896q0 14 -10 38.5t-20 34.5l-281 281q-10 10 -34 20t-39 10v-416q0 -40 -28 -68t-68 -28h-576q-40 0 -68 28t-28 68v416h-128v-1280h128v416q0 40 28 68t68 28h832q40 0 68 -28t28 -68v-416zM896 928v320q0 13 -9.5 22.5t-22.5 9.5 h-192q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 22.5zM1536 896v-928q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h928q40 0 88 -20t76 -48l280 -280q28 -28 48 -76t20 -88z" />
+<glyph unicode="&#xf0c8;" d="M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf0c9;" d="M1536 192v-128q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1536 704v-128q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1536 1216v-128q0 -26 -19 -45 t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf0ca;" horiz-adv-x="1792" d="M384 128q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM384 640q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5 t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5zM384 1152q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1792 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z M1792 1248v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z" />
+<glyph unicode="&#xf0cb;" horiz-adv-x="1792" d="M381 -84q0 -80 -54.5 -126t-135.5 -46q-106 0 -172 66l57 88q49 -45 106 -45q29 0 50.5 14.5t21.5 42.5q0 64 -105 56l-26 56q8 10 32.5 43.5t42.5 54t37 38.5v1q-16 0 -48.5 -1t-48.5 -1v-53h-106v152h333v-88l-95 -115q51 -12 81 -49t30 -88zM383 543v-159h-362 q-6 36 -6 54q0 51 23.5 93t56.5 68t66 47.5t56.5 43.5t23.5 45q0 25 -14.5 38.5t-39.5 13.5q-46 0 -81 -58l-85 59q24 51 71.5 79.5t105.5 28.5q73 0 123 -41.5t50 -112.5q0 -50 -34 -91.5t-75 -64.5t-75.5 -50.5t-35.5 -52.5h127v60h105zM1792 224v-192q0 -13 -9.5 -22.5 t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 14 9 23t23 9h1216q13 0 22.5 -9.5t9.5 -22.5zM384 1123v-99h-335v99h107q0 41 0.5 122t0.5 121v12h-2q-8 -17 -50 -54l-71 76l136 127h106v-404h108zM1792 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5 t-9.5 22.5v192q0 14 9 23t23 9h1216q13 0 22.5 -9.5t9.5 -22.5zM1792 1248v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z" />
+<glyph unicode="&#xf0cc;" horiz-adv-x="1792" d="M1760 640q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1728q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h1728zM483 704q-28 35 -51 80q-48 97 -48 188q0 181 134 309q133 127 393 127q50 0 167 -19q66 -12 177 -48q10 -38 21 -118q14 -123 14 -183q0 -18 -5 -45l-12 -3l-84 6 l-14 2q-50 149 -103 205q-88 91 -210 91q-114 0 -182 -59q-67 -58 -67 -146q0 -73 66 -140t279 -129q69 -20 173 -66q58 -28 95 -52h-743zM990 448h411q7 -39 7 -92q0 -111 -41 -212q-23 -55 -71 -104q-37 -35 -109 -81q-80 -48 -153 -66q-80 -21 -203 -21q-114 0 -195 23 l-140 40q-57 16 -72 28q-8 8 -8 22v13q0 108 -2 156q-1 30 0 68l2 37v44l102 2q15 -34 30 -71t22.5 -56t12.5 -27q35 -57 80 -94q43 -36 105 -57q59 -22 132 -22q64 0 139 27q77 26 122 86q47 61 47 129q0 84 -81 157q-34 29 -137 71z" />
+<glyph unicode="&#xf0cd;" d="M48 1313q-37 2 -45 4l-3 88q13 1 40 1q60 0 112 -4q132 -7 166 -7q86 0 168 3q116 4 146 5q56 0 86 2l-1 -14l2 -64v-9q-60 -9 -124 -9q-60 0 -79 -25q-13 -14 -13 -132q0 -13 0.5 -32.5t0.5 -25.5l1 -229l14 -280q6 -124 51 -202q35 -59 96 -92q88 -47 177 -47 q104 0 191 28q56 18 99 51q48 36 65 64q36 56 53 114q21 73 21 229q0 79 -3.5 128t-11 122.5t-13.5 159.5l-4 59q-5 67 -24 88q-34 35 -77 34l-100 -2l-14 3l2 86h84l205 -10q76 -3 196 10l18 -2q6 -38 6 -51q0 -7 -4 -31q-45 -12 -84 -13q-73 -11 -79 -17q-15 -15 -15 -41 q0 -7 1.5 -27t1.5 -31q8 -19 22 -396q6 -195 -15 -304q-15 -76 -41 -122q-38 -65 -112 -123q-75 -57 -182 -89q-109 -33 -255 -33q-167 0 -284 46q-119 47 -179 122q-61 76 -83 195q-16 80 -16 237v333q0 188 -17 213q-25 36 -147 39zM1536 -96v64q0 14 -9 23t-23 9h-1472 q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h1472q14 0 23 9t9 23z" />
+<glyph unicode="&#xf0ce;" horiz-adv-x="1664" d="M512 160v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM512 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 160v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23 v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM512 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 160v192 q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192 q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1664 1248v-1088q0 -66 -47 -113t-113 -47h-1344q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1344q66 0 113 -47t47 -113 z" />
+<glyph unicode="&#xf0d0;" horiz-adv-x="1664" d="M1190 955l293 293l-107 107l-293 -293zM1637 1248q0 -27 -18 -45l-1286 -1286q-18 -18 -45 -18t-45 18l-198 198q-18 18 -18 45t18 45l1286 1286q18 18 45 18t45 -18l198 -198q18 -18 18 -45zM286 1438l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98zM636 1276 l196 -60l-196 -60l-60 -196l-60 196l-196 60l196 60l60 196zM1566 798l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98zM926 1438l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98z" />
+<glyph unicode="&#xf0d1;" horiz-adv-x="1792" d="M640 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM256 640h384v256h-158q-13 0 -22 -9l-195 -195q-9 -9 -9 -22v-30zM1536 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM1792 1216v-1024q0 -15 -4 -26.5t-13.5 -18.5 t-16.5 -11.5t-23.5 -6t-22.5 -2t-25.5 0t-22.5 0.5q0 -106 -75 -181t-181 -75t-181 75t-75 181h-384q0 -106 -75 -181t-181 -75t-181 75t-75 181h-64q-3 0 -22.5 -0.5t-25.5 0t-22.5 2t-23.5 6t-16.5 11.5t-13.5 18.5t-4 26.5q0 26 19 45t45 19v320q0 8 -0.5 35t0 38 t2.5 34.5t6.5 37t14 30.5t22.5 30l198 198q19 19 50.5 32t58.5 13h160v192q0 26 19 45t45 19h1024q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf0d2;" d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103q-111 0 -218 32q59 93 78 164q9 34 54 211q20 -39 73 -67.5t114 -28.5q121 0 216 68.5t147 188.5t52 270q0 114 -59.5 214t-172.5 163t-255 63q-105 0 -196 -29t-154.5 -77t-109 -110.5t-67 -129.5t-21.5 -134 q0 -104 40 -183t117 -111q30 -12 38 20q2 7 8 31t8 30q6 23 -11 43q-51 61 -51 151q0 151 104.5 259.5t273.5 108.5q151 0 235.5 -82t84.5 -213q0 -170 -68.5 -289t-175.5 -119q-61 0 -98 43.5t-23 104.5q8 35 26.5 93.5t30 103t11.5 75.5q0 50 -27 83t-77 33 q-62 0 -105 -57t-43 -142q0 -73 25 -122l-99 -418q-17 -70 -13 -177q-206 91 -333 281t-127 423q0 209 103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf0d3;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-725q85 122 108 210q9 34 53 209q21 -39 73.5 -67t112.5 -28q181 0 295.5 147.5t114.5 373.5q0 84 -35 162.5t-96.5 139t-152.5 97t-197 36.5q-104 0 -194.5 -28.5t-153 -76.5 t-107.5 -109.5t-66.5 -128t-21.5 -132.5q0 -102 39.5 -180t116.5 -110q13 -5 23.5 0t14.5 19q10 44 15 61q6 23 -11 42q-50 62 -50 150q0 150 103.5 256.5t270.5 106.5q149 0 232.5 -81t83.5 -210q0 -168 -67.5 -286t-173.5 -118q-60 0 -97 43.5t-23 103.5q8 34 26.5 92.5 t29.5 102t11 74.5q0 49 -26.5 81.5t-75.5 32.5q-61 0 -103.5 -56.5t-42.5 -139.5q0 -72 24 -121l-98 -414q-24 -100 -7 -254h-183q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960z" />
+<glyph unicode="&#xf0d4;" d="M917 631q0 26 -6 64h-362v-132h217q-3 -24 -16.5 -50t-37.5 -53t-66.5 -44.5t-96.5 -17.5q-99 0 -169 71t-70 171t70 171t169 71q92 0 153 -59l104 101q-108 100 -257 100q-160 0 -272 -112.5t-112 -271.5t112 -271.5t272 -112.5q165 0 266.5 105t101.5 270zM1262 585 h109v110h-109v110h-110v-110h-110v-110h110v-110h110v110zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf0d5;" horiz-adv-x="2304" d="M1437 623q0 -208 -87 -370.5t-248 -254t-369 -91.5q-149 0 -285 58t-234 156t-156 234t-58 285t58 285t156 234t234 156t285 58q286 0 491 -192l-199 -191q-117 113 -292 113q-123 0 -227.5 -62t-165.5 -168.5t-61 -232.5t61 -232.5t165.5 -168.5t227.5 -62 q83 0 152.5 23t114.5 57.5t78.5 78.5t49 83t21.5 74h-416v252h692q12 -63 12 -122zM2304 745v-210h-209v-209h-210v209h-209v210h209v209h210v-209h209z" />
+<glyph unicode="&#xf0d6;" horiz-adv-x="1920" d="M768 384h384v96h-128v448h-114l-148 -137l77 -80q42 37 55 57h2v-288h-128v-96zM1280 640q0 -70 -21 -142t-59.5 -134t-101.5 -101t-138 -39t-138 39t-101.5 101t-59.5 134t-21 142t21 142t59.5 134t101.5 101t138 39t138 -39t101.5 -101t59.5 -134t21 -142zM1792 384 v512q-106 0 -181 75t-75 181h-1152q0 -106 -75 -181t-181 -75v-512q106 0 181 -75t75 -181h1152q0 106 75 181t181 75zM1920 1216v-1152q0 -26 -19 -45t-45 -19h-1792q-26 0 -45 19t-19 45v1152q0 26 19 45t45 19h1792q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf0d7;" horiz-adv-x="1024" d="M1024 832q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf0d8;" horiz-adv-x="1024" d="M1024 320q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
+<glyph unicode="&#xf0d9;" horiz-adv-x="640" d="M640 1088v-896q0 -26 -19 -45t-45 -19t-45 19l-448 448q-19 19 -19 45t19 45l448 448q19 19 45 19t45 -19t19 -45z" />
+<glyph unicode="&#xf0da;" horiz-adv-x="640" d="M576 640q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19t-19 45v896q0 26 19 45t45 19t45 -19l448 -448q19 -19 19 -45z" />
+<glyph unicode="&#xf0db;" horiz-adv-x="1664" d="M160 0h608v1152h-640v-1120q0 -13 9.5 -22.5t22.5 -9.5zM1536 32v1120h-640v-1152h608q13 0 22.5 9.5t9.5 22.5zM1664 1248v-1216q0 -66 -47 -113t-113 -47h-1344q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1344q66 0 113 -47t47 -113z" />
+<glyph unicode="&#xf0dc;" horiz-adv-x="1024" d="M1024 448q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45zM1024 832q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
+<glyph unicode="&#xf0dd;" horiz-adv-x="1024" d="M1024 448q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf0de;" horiz-adv-x="1024" d="M1024 832q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
+<glyph unicode="&#xf0e0;" horiz-adv-x="1792" d="M1792 826v-794q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v794q44 -49 101 -87q362 -246 497 -345q57 -42 92.5 -65.5t94.5 -48t110 -24.5h1h1q51 0 110 24.5t94.5 48t92.5 65.5q170 123 498 345q57 39 100 87zM1792 1120q0 -79 -49 -151t-122 -123 q-376 -261 -468 -325q-10 -7 -42.5 -30.5t-54 -38t-52 -32.5t-57.5 -27t-50 -9h-1h-1q-23 0 -50 9t-57.5 27t-52 32.5t-54 38t-42.5 30.5q-91 64 -262 182.5t-205 142.5q-62 42 -117 115.5t-55 136.5q0 78 41.5 130t118.5 52h1472q65 0 112.5 -47t47.5 -113z" />
+<glyph unicode="&#xf0e1;" d="M349 911v-991h-330v991h330zM370 1217q1 -73 -50.5 -122t-135.5 -49h-2q-82 0 -132 49t-50 122q0 74 51.5 122.5t134.5 48.5t133 -48.5t51 -122.5zM1536 488v-568h-329v530q0 105 -40.5 164.5t-126.5 59.5q-63 0 -105.5 -34.5t-63.5 -85.5q-11 -30 -11 -81v-553h-329 q2 399 2 647t-1 296l-1 48h329v-144h-2q20 32 41 56t56.5 52t87 43.5t114.5 15.5q171 0 275 -113.5t104 -332.5z" />
+<glyph unicode="&#xf0e2;" d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61q-172 0 -327 72.5t-264 204.5q-7 10 -6.5 22.5t8.5 20.5l137 138q10 9 25 9q16 -2 23 -12q73 -95 179 -147t225 -52q104 0 198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5t-40.5 198.5t-109.5 163.5 t-163.5 109.5t-198.5 40.5q-98 0 -188 -35.5t-160 -101.5l137 -138q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l130 -129q107 101 244.5 156.5t284.5 55.5q156 0 298 -61t245 -164t164 -245t61 -298z" />
+<glyph unicode="&#xf0e3;" horiz-adv-x="1792" d="M1771 0q0 -53 -37 -90l-107 -108q-39 -37 -91 -37q-53 0 -90 37l-363 364q-38 36 -38 90q0 53 43 96l-256 256l-126 -126q-14 -14 -34 -14t-34 14q2 -2 12.5 -12t12.5 -13t10 -11.5t10 -13.5t6 -13.5t5.5 -16.5t1.5 -18q0 -38 -28 -68q-3 -3 -16.5 -18t-19 -20.5 t-18.5 -16.5t-22 -15.5t-22 -9t-26 -4.5q-40 0 -68 28l-408 408q-28 28 -28 68q0 13 4.5 26t9 22t15.5 22t16.5 18.5t20.5 19t18 16.5q30 28 68 28q10 0 18 -1.5t16.5 -5.5t13.5 -6t13.5 -10t11.5 -10t13 -12.5t12 -12.5q-14 14 -14 34t14 34l348 348q14 14 34 14t34 -14 q-2 2 -12.5 12t-12.5 13t-10 11.5t-10 13.5t-6 13.5t-5.5 16.5t-1.5 18q0 38 28 68q3 3 16.5 18t19 20.5t18.5 16.5t22 15.5t22 9t26 4.5q40 0 68 -28l408 -408q28 -28 28 -68q0 -13 -4.5 -26t-9 -22t-15.5 -22t-16.5 -18.5t-20.5 -19t-18 -16.5q-30 -28 -68 -28 q-10 0 -18 1.5t-16.5 5.5t-13.5 6t-13.5 10t-11.5 10t-13 12.5t-12 12.5q14 -14 14 -34t-14 -34l-126 -126l256 -256q43 43 96 43q52 0 91 -37l363 -363q37 -39 37 -91z" />
+<glyph unicode="&#xf0e4;" horiz-adv-x="1792" d="M384 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM576 832q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1004 351l101 382q6 26 -7.5 48.5t-38.5 29.5 t-48 -6.5t-30 -39.5l-101 -382q-60 -5 -107 -43.5t-63 -98.5q-20 -77 20 -146t117 -89t146 20t89 117q16 60 -6 117t-72 91zM1664 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 1024q0 53 -37.5 90.5 t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1472 832q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1792 384q0 -261 -141 -483q-19 -29 -54 -29h-1402q-35 0 -54 29 q-141 221 -141 483q0 182 71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
+<glyph unicode="&#xf0e5;" horiz-adv-x="1792" d="M896 1152q-204 0 -381.5 -69.5t-282 -187.5t-104.5 -255q0 -112 71.5 -213.5t201.5 -175.5l87 -50l-27 -96q-24 -91 -70 -172q152 63 275 171l43 38l57 -6q69 -8 130 -8q204 0 381.5 69.5t282 187.5t104.5 255t-104.5 255t-282 187.5t-381.5 69.5zM1792 640 q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22h-5q-15 0 -27 10.5t-16 27.5v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51t27 59t26 76q-157 89 -247.5 220t-90.5 281q0 174 120 321.5 t326 233t450 85.5t450 -85.5t326 -233t120 -321.5z" />
+<glyph unicode="&#xf0e6;" horiz-adv-x="1792" d="M704 1152q-153 0 -286 -52t-211.5 -141t-78.5 -191q0 -82 53 -158t149 -132l97 -56l-35 -84q34 20 62 39l44 31l53 -10q78 -14 153 -14q153 0 286 52t211.5 141t78.5 191t-78.5 191t-211.5 141t-286 52zM704 1280q191 0 353.5 -68.5t256.5 -186.5t94 -257t-94 -257 t-256.5 -186.5t-353.5 -68.5q-86 0 -176 16q-124 -88 -278 -128q-36 -9 -86 -16h-3q-11 0 -20.5 8t-11.5 21q-1 3 -1 6.5t0.5 6.5t2 6l2.5 5t3.5 5.5t4 5t4.5 5t4 4.5q5 6 23 25t26 29.5t22.5 29t25 38.5t20.5 44q-124 72 -195 177t-71 224q0 139 94 257t256.5 186.5 t353.5 68.5zM1526 111q10 -24 20.5 -44t25 -38.5t22.5 -29t26 -29.5t23 -25q1 -1 4 -4.5t4.5 -5t4 -5t3.5 -5.5l2.5 -5t2 -6t0.5 -6.5t-1 -6.5q-3 -14 -13 -22t-22 -7q-50 7 -86 16q-154 40 -278 128q-90 -16 -176 -16q-271 0 -472 132q58 -4 88 -4q161 0 309 45t264 129 q125 92 192 212t67 254q0 77 -23 152q129 -71 204 -178t75 -230q0 -120 -71 -224.5t-195 -176.5z" />
+<glyph unicode="&#xf0e7;" horiz-adv-x="896" d="M885 970q18 -20 7 -44l-540 -1157q-13 -25 -42 -25q-4 0 -14 2q-17 5 -25.5 19t-4.5 30l197 808l-406 -101q-4 -1 -12 -1q-18 0 -31 11q-18 15 -13 39l201 825q4 14 16 23t28 9h328q19 0 32 -12.5t13 -29.5q0 -8 -5 -18l-171 -463l396 98q8 2 12 2q19 0 34 -15z" />
+<glyph unicode="&#xf0e8;" horiz-adv-x="1792" d="M1792 288v-320q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192h-512v-192h96q40 0 68 -28t28 -68v-320q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192h-512v-192h96q40 0 68 -28t28 -68v-320 q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192q0 52 38 90t90 38h512v192h-96q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h320q40 0 68 -28t28 -68v-320q0 -40 -28 -68t-68 -28h-96v-192h512q52 0 90 -38t38 -90v-192h96q40 0 68 -28t28 -68 z" />
+<glyph unicode="&#xf0e9;" horiz-adv-x="1664" d="M896 708v-580q0 -104 -76 -180t-180 -76t-180 76t-76 180q0 26 19 45t45 19t45 -19t19 -45q0 -50 39 -89t89 -39t89 39t39 89v580q33 11 64 11t64 -11zM1664 681q0 -13 -9.5 -22.5t-22.5 -9.5q-11 0 -23 10q-49 46 -93 69t-102 23q-68 0 -128 -37t-103 -97 q-7 -10 -17.5 -28t-14.5 -24q-11 -17 -28 -17q-18 0 -29 17q-4 6 -14.5 24t-17.5 28q-43 60 -102.5 97t-127.5 37t-127.5 -37t-102.5 -97q-7 -10 -17.5 -28t-14.5 -24q-11 -17 -29 -17q-17 0 -28 17q-4 6 -14.5 24t-17.5 28q-43 60 -103 97t-128 37q-58 0 -102 -23t-93 -69 q-12 -10 -23 -10q-13 0 -22.5 9.5t-9.5 22.5q0 5 1 7q45 183 172.5 319.5t298 204.5t360.5 68q140 0 274.5 -40t246.5 -113.5t194.5 -187t115.5 -251.5q1 -2 1 -7zM896 1408v-98q-42 2 -64 2t-64 -2v98q0 26 19 45t45 19t45 -19t19 -45z" />
+<glyph unicode="&#xf0ea;" horiz-adv-x="1792" d="M768 -128h896v640h-416q-40 0 -68 28t-28 68v416h-384v-1152zM1024 1312v64q0 13 -9.5 22.5t-22.5 9.5h-704q-13 0 -22.5 -9.5t-9.5 -22.5v-64q0 -13 9.5 -22.5t22.5 -9.5h704q13 0 22.5 9.5t9.5 22.5zM1280 640h299l-299 299v-299zM1792 512v-672q0 -40 -28 -68t-68 -28 h-960q-40 0 -68 28t-28 68v160h-544q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1088q40 0 68 -28t28 -68v-328q21 -13 36 -28l408 -408q28 -28 48 -76t20 -88z" />
+<glyph unicode="&#xf0eb;" horiz-adv-x="1024" d="M736 960q0 -13 -9.5 -22.5t-22.5 -9.5t-22.5 9.5t-9.5 22.5q0 46 -54 71t-106 25q-13 0 -22.5 9.5t-9.5 22.5t9.5 22.5t22.5 9.5q50 0 99.5 -16t87 -54t37.5 -90zM896 960q0 72 -34.5 134t-90 101.5t-123 62t-136.5 22.5t-136.5 -22.5t-123 -62t-90 -101.5t-34.5 -134 q0 -101 68 -180q10 -11 30.5 -33t30.5 -33q128 -153 141 -298h228q13 145 141 298q10 11 30.5 33t30.5 33q68 79 68 180zM1024 960q0 -155 -103 -268q-45 -49 -74.5 -87t-59.5 -95.5t-34 -107.5q47 -28 47 -82q0 -37 -25 -64q25 -27 25 -64q0 -52 -45 -81q13 -23 13 -47 q0 -46 -31.5 -71t-77.5 -25q-20 -44 -60 -70t-87 -26t-87 26t-60 70q-46 0 -77.5 25t-31.5 71q0 24 13 47q-45 29 -45 81q0 37 25 64q-25 27 -25 64q0 54 47 82q-4 50 -34 107.5t-59.5 95.5t-74.5 87q-103 113 -103 268q0 99 44.5 184.5t117 142t164 89t186.5 32.5 t186.5 -32.5t164 -89t117 -142t44.5 -184.5z" />
+<glyph unicode="&#xf0ec;" horiz-adv-x="1792" d="M1792 352v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5q-12 0 -24 10l-319 320q-9 9 -9 22q0 14 9 23l320 320q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5v-192h1376q13 0 22.5 -9.5t9.5 -22.5zM1792 896q0 -14 -9 -23l-320 -320q-9 -9 -23 -9 q-13 0 -22.5 9.5t-9.5 22.5v192h-1376q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1376v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23z" />
+<glyph unicode="&#xf0ed;" horiz-adv-x="1920" d="M1280 608q0 14 -9 23t-23 9h-224v352q0 13 -9.5 22.5t-22.5 9.5h-192q-13 0 -22.5 -9.5t-9.5 -22.5v-352h-224q-13 0 -22.5 -9.5t-9.5 -22.5q0 -14 9 -23l352 -352q9 -9 23 -9t23 9l351 351q10 12 10 24zM1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088 q-185 0 -316.5 131.5t-131.5 316.5q0 130 70 240t188 165q-2 30 -2 43q0 212 150 362t362 150q156 0 285.5 -87t188.5 -231q71 62 166 62q106 0 181 -75t75 -181q0 -76 -41 -138q130 -31 213.5 -135.5t83.5 -238.5z" />
+<glyph unicode="&#xf0ee;" horiz-adv-x="1920" d="M1280 672q0 14 -9 23l-352 352q-9 9 -23 9t-23 -9l-351 -351q-10 -12 -10 -24q0 -14 9 -23t23 -9h224v-352q0 -13 9.5 -22.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 22.5v352h224q13 0 22.5 9.5t9.5 22.5zM1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088 q-185 0 -316.5 131.5t-131.5 316.5q0 130 70 240t188 165q-2 30 -2 43q0 212 150 362t362 150q156 0 285.5 -87t188.5 -231q71 62 166 62q106 0 181 -75t75 -181q0 -76 -41 -138q130 -31 213.5 -135.5t83.5 -238.5z" />
+<glyph unicode="&#xf0f0;" horiz-adv-x="1408" d="M384 192q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45t45 19t45 -19t19 -45zM1408 131q0 -121 -73 -190t-194 -69h-874q-121 0 -194 69t-73 190q0 68 5.5 131t24 138t47.5 132.5t81 103t120 60.5q-22 -52 -22 -120v-203q-58 -20 -93 -70t-35 -111q0 -80 56 -136t136 -56 t136 56t56 136q0 61 -35.5 111t-92.5 70v203q0 62 25 93q132 -104 295 -104t295 104q25 -31 25 -93v-64q-106 0 -181 -75t-75 -181v-89q-32 -29 -32 -71q0 -40 28 -68t68 -28t68 28t28 68q0 42 -32 71v89q0 52 38 90t90 38t90 -38t38 -90v-89q-32 -29 -32 -71q0 -40 28 -68 t68 -28t68 28t28 68q0 42 -32 71v89q0 68 -34.5 127.5t-93.5 93.5q0 10 0.5 42.5t0 48t-2.5 41.5t-7 47t-13 40q68 -15 120 -60.5t81 -103t47.5 -132.5t24 -138t5.5 -131zM1088 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5 t271.5 -112.5t112.5 -271.5z" />
+<glyph unicode="&#xf0f1;" horiz-adv-x="1408" d="M1280 832q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 832q0 -62 -35.5 -111t-92.5 -70v-395q0 -159 -131.5 -271.5t-316.5 -112.5t-316.5 112.5t-131.5 271.5v132q-164 20 -274 128t-110 252v512q0 26 19 45t45 19q6 0 16 -2q17 30 47 48 t65 18q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5q-33 0 -64 18v-402q0 -106 94 -181t226 -75t226 75t94 181v402q-31 -18 -64 -18q-53 0 -90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5q35 0 65 -18t47 -48q10 2 16 2q26 0 45 -19t19 -45v-512q0 -144 -110 -252 t-274 -128v-132q0 -106 94 -181t226 -75t226 75t94 181v395q-57 21 -92.5 70t-35.5 111q0 80 56 136t136 56t136 -56t56 -136z" />
+<glyph unicode="&#xf0f2;" horiz-adv-x="1792" d="M640 1152h512v128h-512v-128zM288 1152v-1280h-64q-92 0 -158 66t-66 158v832q0 92 66 158t158 66h64zM1408 1152v-1280h-1024v1280h128v160q0 40 28 68t68 28h576q40 0 68 -28t28 -68v-160h128zM1792 928v-832q0 -92 -66 -158t-158 -66h-64v1280h64q92 0 158 -66 t66 -158z" />
+<glyph unicode="&#xf0f3;" horiz-adv-x="1792" d="M912 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM1728 128q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-181 75t-75 181h-448q-52 0 -90 38t-38 90q50 42 91 88t85 119.5t74.5 158.5 t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q190 -28 307 -158.5t117 -282.5q0 -139 19.5 -260t50 -206t74.5 -158.5t85 -119.5t91 -88z" />
+<glyph unicode="&#xf0f4;" horiz-adv-x="1920" d="M1664 896q0 80 -56 136t-136 56h-64v-384h64q80 0 136 56t56 136zM0 128h1792q0 -106 -75 -181t-181 -75h-1280q-106 0 -181 75t-75 181zM1856 896q0 -159 -112.5 -271.5t-271.5 -112.5h-64v-32q0 -92 -66 -158t-158 -66h-704q-92 0 -158 66t-66 158v736q0 26 19 45 t45 19h1152q159 0 271.5 -112.5t112.5 -271.5z" />
+<glyph unicode="&#xf0f5;" horiz-adv-x="1408" d="M640 1472v-640q0 -61 -35.5 -111t-92.5 -70v-779q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v779q-57 20 -92.5 70t-35.5 111v640q0 26 19 45t45 19t45 -19t19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45v416q0 26 19 45t45 19t45 -19t19 -45v-416q0 -26 19 -45 t45 -19t45 19t19 45v416q0 26 19 45t45 19t45 -19t19 -45zM1408 1472v-1600q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v512h-224q-13 0 -22.5 9.5t-9.5 22.5v800q0 132 94 226t226 94h256q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf0f6;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M384 736q0 14 9 23t23 9h704q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64zM1120 512q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704zM1120 256q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704 q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704z" />
+<glyph unicode="&#xf0f7;" horiz-adv-x="1408" d="M384 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 -128h384v1536h-1152v-1536h384v224q0 13 9.5 22.5t22.5 9.5h320q13 0 22.5 -9.5t9.5 -22.5v-224zM1408 1472v-1664q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1664q0 26 19 45t45 19h1280q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf0f8;" horiz-adv-x="1408" d="M384 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 -128h384v1152h-256v-32q0 -40 -28 -68t-68 -28h-448q-40 0 -68 28t-28 68v32h-256v-1152h384v224q0 13 9.5 22.5t22.5 9.5h320q13 0 22.5 -9.5t9.5 -22.5v-224zM896 1056v320q0 13 -9.5 22.5t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-96h-128v96q0 13 -9.5 22.5 t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5v96h128v-96q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1408 1088v-1280q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1280q0 26 19 45t45 19h320 v288q0 40 28 68t68 28h448q40 0 68 -28t28 -68v-288h320q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf0f9;" horiz-adv-x="1920" d="M640 128q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM256 640h384v256h-158q-14 -2 -22 -9l-195 -195q-7 -12 -9 -22v-30zM1536 128q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5 t90.5 37.5t37.5 90.5zM1664 800v192q0 14 -9 23t-23 9h-224v224q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-224h-224q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h224v-224q0 -14 9 -23t23 -9h192q14 0 23 9t9 23v224h224q14 0 23 9t9 23zM1920 1344v-1152 q0 -26 -19 -45t-45 -19h-192q0 -106 -75 -181t-181 -75t-181 75t-75 181h-384q0 -106 -75 -181t-181 -75t-181 75t-75 181h-128q-26 0 -45 19t-19 45t19 45t45 19v416q0 26 13 58t32 51l198 198q19 19 51 32t58 13h160v320q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf0fa;" horiz-adv-x="1792" d="M1280 416v192q0 14 -9 23t-23 9h-224v224q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-224h-224q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h224v-224q0 -14 9 -23t23 -9h192q14 0 23 9t9 23v224h224q14 0 23 9t9 23zM640 1152h512v128h-512v-128zM256 1152v-1280h-32 q-92 0 -158 66t-66 158v832q0 92 66 158t158 66h32zM1440 1152v-1280h-1088v1280h160v160q0 40 28 68t68 28h576q40 0 68 -28t28 -68v-160h160zM1792 928v-832q0 -92 -66 -158t-158 -66h-32v1280h32q92 0 158 -66t66 -158z" />
+<glyph unicode="&#xf0fb;" horiz-adv-x="1920" d="M1920 576q-1 -32 -288 -96l-352 -32l-224 -64h-64l-293 -352h69q26 0 45 -4.5t19 -11.5t-19 -11.5t-45 -4.5h-96h-160h-64v32h64v416h-160l-192 -224h-96l-32 32v192h32v32h128v8l-192 24v128l192 24v8h-128v32h-32v192l32 32h96l192 -224h160v416h-64v32h64h160h96 q26 0 45 -4.5t19 -11.5t-19 -11.5t-45 -4.5h-69l293 -352h64l224 -64l352 -32q261 -58 287 -93z" />
+<glyph unicode="&#xf0fc;" horiz-adv-x="1664" d="M640 640v384h-256v-256q0 -53 37.5 -90.5t90.5 -37.5h128zM1664 192v-192h-1152v192l128 192h-128q-159 0 -271.5 112.5t-112.5 271.5v320l-64 64l32 128h480l32 128h960l32 -192l-64 -32v-800z" />
+<glyph unicode="&#xf0fd;" d="M1280 192v896q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-512v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-896q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h512v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf0fe;" d="M1280 576v128q0 26 -19 45t-45 19h-320v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-320q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h320v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h320q26 0 45 19t19 45zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf100;" horiz-adv-x="1024" d="M627 160q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23zM1011 160q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23 t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23z" />
+<glyph unicode="&#xf101;" horiz-adv-x="1024" d="M595 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23zM979 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23 l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
+<glyph unicode="&#xf102;" horiz-adv-x="1152" d="M1075 224q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23zM1075 608q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393 q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
+<glyph unicode="&#xf103;" horiz-adv-x="1152" d="M1075 672q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23zM1075 1056q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23 t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
+<glyph unicode="&#xf104;" horiz-adv-x="640" d="M627 992q0 -13 -10 -23l-393 -393l393 -393q10 -10 10 -23t-10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
+<glyph unicode="&#xf105;" horiz-adv-x="640" d="M595 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
+<glyph unicode="&#xf106;" horiz-adv-x="1152" d="M1075 352q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
+<glyph unicode="&#xf107;" horiz-adv-x="1152" d="M1075 800q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
+<glyph unicode="&#xf108;" horiz-adv-x="1920" d="M1792 544v832q0 13 -9.5 22.5t-22.5 9.5h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-832q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5zM1920 1376v-1088q0 -66 -47 -113t-113 -47h-544q0 -37 16 -77.5t32 -71t16 -43.5q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19 t-19 45q0 14 16 44t32 70t16 78h-544q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
+<glyph unicode="&#xf109;" horiz-adv-x="1920" d="M416 256q-66 0 -113 47t-47 113v704q0 66 47 113t113 47h1088q66 0 113 -47t47 -113v-704q0 -66 -47 -113t-113 -47h-1088zM384 1120v-704q0 -13 9.5 -22.5t22.5 -9.5h1088q13 0 22.5 9.5t9.5 22.5v704q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5z M1760 192h160v-96q0 -40 -47 -68t-113 -28h-1600q-66 0 -113 28t-47 68v96h160h1600zM1040 96q16 0 16 16t-16 16h-160q-16 0 -16 -16t16 -16h160z" />
+<glyph unicode="&#xf10a;" horiz-adv-x="1152" d="M640 128q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1024 288v960q0 13 -9.5 22.5t-22.5 9.5h-832q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h832q13 0 22.5 9.5t9.5 22.5zM1152 1248v-1088q0 -66 -47 -113t-113 -47h-832 q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h832q66 0 113 -47t47 -113z" />
+<glyph unicode="&#xf10b;" horiz-adv-x="768" d="M464 128q0 33 -23.5 56.5t-56.5 23.5t-56.5 -23.5t-23.5 -56.5t23.5 -56.5t56.5 -23.5t56.5 23.5t23.5 56.5zM672 288v704q0 13 -9.5 22.5t-22.5 9.5h-512q-13 0 -22.5 -9.5t-9.5 -22.5v-704q0 -13 9.5 -22.5t22.5 -9.5h512q13 0 22.5 9.5t9.5 22.5zM480 1136 q0 16 -16 16h-160q-16 0 -16 -16t16 -16h160q16 0 16 16zM768 1152v-1024q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v1024q0 52 38 90t90 38h512q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf10c;" d="M768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103 t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf10d;" horiz-adv-x="1664" d="M768 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z M1664 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z" />
+<glyph unicode="&#xf10e;" horiz-adv-x="1664" d="M768 1216v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136zM1664 1216 v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136z" />
+<glyph unicode="&#xf110;" horiz-adv-x="1792" d="M526 142q0 -53 -37.5 -90.5t-90.5 -37.5q-52 0 -90 38t-38 90q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1024 -64q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM320 640q0 -53 -37.5 -90.5t-90.5 -37.5 t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1522 142q0 -52 -38 -90t-90 -38q-53 0 -90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM558 1138q0 -66 -47 -113t-113 -47t-113 47t-47 113t47 113t113 47t113 -47t47 -113z M1728 640q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1088 1344q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1618 1138q0 -93 -66 -158.5t-158 -65.5q-93 0 -158.5 65.5t-65.5 158.5 q0 92 65.5 158t158.5 66q92 0 158 -66t66 -158z" />
+<glyph unicode="&#xf111;" d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf112;" horiz-adv-x="1792" d="M1792 416q0 -166 -127 -451q-3 -7 -10.5 -24t-13.5 -30t-13 -22q-12 -17 -28 -17q-15 0 -23.5 10t-8.5 25q0 9 2.5 26.5t2.5 23.5q5 68 5 123q0 101 -17.5 181t-48.5 138.5t-80 101t-105.5 69.5t-133 42.5t-154 21.5t-175.5 6h-224v-256q0 -26 -19 -45t-45 -19t-45 19 l-512 512q-19 19 -19 45t19 45l512 512q19 19 45 19t45 -19t19 -45v-256h224q713 0 875 -403q53 -134 53 -333z" />
+<glyph unicode="&#xf113;" horiz-adv-x="1664" d="M640 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1280 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1440 320 q0 120 -69 204t-187 84q-41 0 -195 -21q-71 -11 -157 -11t-157 11q-152 21 -195 21q-118 0 -187 -84t-69 -204q0 -88 32 -153.5t81 -103t122 -60t140 -29.5t149 -7h168q82 0 149 7t140 29.5t122 60t81 103t32 153.5zM1664 496q0 -207 -61 -331q-38 -77 -105.5 -133t-141 -86 t-170 -47.5t-171.5 -22t-167 -4.5q-78 0 -142 3t-147.5 12.5t-152.5 30t-137 51.5t-121 81t-86 115q-62 123 -62 331q0 237 136 396q-27 82 -27 170q0 116 51 218q108 0 190 -39.5t189 -123.5q147 35 309 35q148 0 280 -32q105 82 187 121t189 39q51 -102 51 -218 q0 -87 -27 -168q136 -160 136 -398z" />
+<glyph unicode="&#xf114;" horiz-adv-x="1664" d="M1536 224v704q0 40 -28 68t-68 28h-704q-40 0 -68 28t-28 68v64q0 40 -28 68t-68 28h-320q-40 0 -68 -28t-28 -68v-960q0 -40 28 -68t68 -28h1216q40 0 68 28t28 68zM1664 928v-704q0 -92 -66 -158t-158 -66h-1216q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320 q92 0 158 -66t66 -158v-32h672q92 0 158 -66t66 -158z" />
+<glyph unicode="&#xf115;" horiz-adv-x="1920" d="M1781 605q0 35 -53 35h-1088q-40 0 -85.5 -21.5t-71.5 -52.5l-294 -363q-18 -24 -18 -40q0 -35 53 -35h1088q40 0 86 22t71 53l294 363q18 22 18 39zM640 768h768v160q0 40 -28 68t-68 28h-576q-40 0 -68 28t-28 68v64q0 40 -28 68t-68 28h-320q-40 0 -68 -28t-28 -68 v-853l256 315q44 53 116 87.5t140 34.5zM1909 605q0 -62 -46 -120l-295 -363q-43 -53 -116 -87.5t-140 -34.5h-1088q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h544q92 0 158 -66t66 -158v-160h192q54 0 99 -24.5t67 -70.5q15 -32 15 -68z " />
+<glyph unicode="&#xf116;" horiz-adv-x="1792" />
+<glyph unicode="&#xf117;" horiz-adv-x="1792" />
+<glyph unicode="&#xf118;" d="M1134 461q-37 -121 -138 -195t-228 -74t-228 74t-138 195q-8 25 4 48.5t38 31.5q25 8 48.5 -4t31.5 -38q25 -80 92.5 -129.5t151.5 -49.5t151.5 49.5t92.5 129.5q8 26 32 38t49 4t37 -31.5t4 -48.5zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5 t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5 t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf119;" d="M1134 307q8 -25 -4 -48.5t-37 -31.5t-49 4t-32 38q-25 80 -92.5 129.5t-151.5 49.5t-151.5 -49.5t-92.5 -129.5q-8 -26 -31.5 -38t-48.5 -4q-26 8 -38 31.5t-4 48.5q37 121 138 195t228 74t228 -74t138 -195zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5 t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204 t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf11a;" d="M1152 448q0 -26 -19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h640q26 0 45 -19t19 -45zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5 t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf11b;" horiz-adv-x="1920" d="M832 448v128q0 14 -9 23t-23 9h-192v192q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-192h-192q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h192v-192q0 -14 9 -23t23 -9h128q14 0 23 9t9 23v192h192q14 0 23 9t9 23zM1408 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5 t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1920 512q0 -212 -150 -362t-362 -150q-192 0 -338 128h-220q-146 -128 -338 -128q-212 0 -362 150 t-150 362t150 362t362 150h896q212 0 362 -150t150 -362z" />
+<glyph unicode="&#xf11c;" horiz-adv-x="1920" d="M384 368v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM512 624v-96q0 -16 -16 -16h-224q-16 0 -16 16v96q0 16 16 16h224q16 0 16 -16zM384 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1408 368v-96q0 -16 -16 -16 h-864q-16 0 -16 16v96q0 16 16 16h864q16 0 16 -16zM768 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM640 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1024 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16 h96q16 0 16 -16zM896 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1280 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1664 368v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1152 880v-96 q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1408 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1664 880v-352q0 -16 -16 -16h-224q-16 0 -16 16v96q0 16 16 16h112v240q0 16 16 16h96q16 0 16 -16zM1792 128v896h-1664v-896 h1664zM1920 1024v-896q0 -53 -37.5 -90.5t-90.5 -37.5h-1664q-53 0 -90.5 37.5t-37.5 90.5v896q0 53 37.5 90.5t90.5 37.5h1664q53 0 90.5 -37.5t37.5 -90.5z" />
+<glyph unicode="&#xf11d;" horiz-adv-x="1792" d="M1664 491v616q-169 -91 -306 -91q-82 0 -145 32q-100 49 -184 76.5t-178 27.5q-173 0 -403 -127v-599q245 113 433 113q55 0 103.5 -7.5t98 -26t77 -31t82.5 -39.5l28 -14q44 -22 101 -22q120 0 293 92zM320 1280q0 -35 -17.5 -64t-46.5 -46v-1266q0 -14 -9 -23t-23 -9 h-64q-14 0 -23 9t-9 23v1266q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -39 -35 -57q-10 -5 -17 -9q-218 -116 -369 -116q-88 0 -158 35l-28 14q-64 33 -99 48t-91 29t-114 14q-102 0 -235.5 -44t-228.5 -102 q-15 -9 -33 -9q-16 0 -32 8q-32 19 -32 56v742q0 35 31 55q35 21 78.5 42.5t114 52t152.5 49.5t155 19q112 0 209 -31t209 -86q38 -19 89 -19q122 0 310 112q22 12 31 17q31 16 62 -2q31 -20 31 -55z" />
+<glyph unicode="&#xf11e;" horiz-adv-x="1792" d="M832 536v192q-181 -16 -384 -117v-185q205 96 384 110zM832 954v197q-172 -8 -384 -126v-189q215 111 384 118zM1664 491v184q-235 -116 -384 -71v224q-20 6 -39 15q-5 3 -33 17t-34.5 17t-31.5 15t-34.5 15.5t-32.5 13t-36 12.5t-35 8.5t-39.5 7.5t-39.5 4t-44 2 q-23 0 -49 -3v-222h19q102 0 192.5 -29t197.5 -82q19 -9 39 -15v-188q42 -17 91 -17q120 0 293 92zM1664 918v189q-169 -91 -306 -91q-45 0 -78 8v-196q148 -42 384 90zM320 1280q0 -35 -17.5 -64t-46.5 -46v-1266q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v1266 q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -39 -35 -57q-10 -5 -17 -9q-218 -116 -369 -116q-88 0 -158 35l-28 14q-64 33 -99 48t-91 29t-114 14q-102 0 -235.5 -44t-228.5 -102q-15 -9 -33 -9q-16 0 -32 8 q-32 19 -32 56v742q0 35 31 55q35 21 78.5 42.5t114 52t152.5 49.5t155 19q112 0 209 -31t209 -86q38 -19 89 -19q122 0 310 112q22 12 31 17q31 16 62 -2q31 -20 31 -55z" />
+<glyph unicode="&#xf120;" horiz-adv-x="1664" d="M585 553l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23t-10 -23zM1664 96v-64q0 -14 -9 -23t-23 -9h-960q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h960q14 0 23 -9 t9 -23z" />
+<glyph unicode="&#xf121;" horiz-adv-x="1920" d="M617 137l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23t-10 -23zM1208 1204l-373 -1291q-4 -13 -15.5 -19.5t-23.5 -2.5l-62 17q-13 4 -19.5 15.5t-2.5 24.5 l373 1291q4 13 15.5 19.5t23.5 2.5l62 -17q13 -4 19.5 -15.5t2.5 -24.5zM1865 553l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23t-10 -23z" />
+<glyph unicode="&#xf122;" horiz-adv-x="1792" d="M640 454v-70q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-512 512q-19 19 -19 45t19 45l512 512q29 31 70 14q39 -17 39 -59v-69l-397 -398q-19 -19 -19 -45t19 -45zM1792 416q0 -58 -17 -133.5t-38.5 -138t-48 -125t-40.5 -90.5l-20 -40q-8 -17 -28 -17q-6 0 -9 1 q-25 8 -23 34q43 400 -106 565q-64 71 -170.5 110.5t-267.5 52.5v-251q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-512 512q-19 19 -19 45t19 45l512 512q29 31 70 14q39 -17 39 -59v-262q411 -28 599 -221q169 -173 169 -509z" />
+<glyph unicode="&#xf123;" horiz-adv-x="1664" d="M1186 579l257 250l-356 52l-66 10l-30 60l-159 322v-963l59 -31l318 -168l-60 355l-12 66zM1638 841l-363 -354l86 -500q5 -33 -6 -51.5t-34 -18.5q-17 0 -40 12l-449 236l-449 -236q-23 -12 -40 -12q-23 0 -34 18.5t-6 51.5l86 500l-364 354q-32 32 -23 59.5t54 34.5 l502 73l225 455q20 41 49 41q28 0 49 -41l225 -455l502 -73q45 -7 54 -34.5t-24 -59.5z" />
+<glyph unicode="&#xf124;" horiz-adv-x="1408" d="M1401 1187l-640 -1280q-17 -35 -57 -35q-5 0 -15 2q-22 5 -35.5 22.5t-13.5 39.5v576h-576q-22 0 -39.5 13.5t-22.5 35.5t4 42t29 30l1280 640q13 7 29 7q27 0 45 -19q15 -14 18.5 -34.5t-6.5 -39.5z" />
+<glyph unicode="&#xf125;" horiz-adv-x="1664" d="M557 256h595v595zM512 301l595 595h-595v-595zM1664 224v-192q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v224h-864q-14 0 -23 9t-9 23v864h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224v224q0 14 9 23t23 9h192q14 0 23 -9t9 -23 v-224h851l246 247q10 9 23 9t23 -9q9 -10 9 -23t-9 -23l-247 -246v-851h224q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf126;" horiz-adv-x="1024" d="M288 64q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM288 1216q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM928 1088q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1024 1088q0 -52 -26 -96.5t-70 -69.5 q-2 -287 -226 -414q-68 -38 -203 -81q-128 -40 -169.5 -71t-41.5 -100v-26q44 -25 70 -69.5t26 -96.5q0 -80 -56 -136t-136 -56t-136 56t-56 136q0 52 26 96.5t70 69.5v820q-44 25 -70 69.5t-26 96.5q0 80 56 136t136 56t136 -56t56 -136q0 -52 -26 -96.5t-70 -69.5v-497 q54 26 154 57q55 17 87.5 29.5t70.5 31t59 39.5t40.5 51t28 69.5t8.5 91.5q-44 25 -70 69.5t-26 96.5q0 80 56 136t136 56t136 -56t56 -136z" />
+<glyph unicode="&#xf127;" horiz-adv-x="1664" d="M439 265l-256 -256q-10 -9 -23 -9q-12 0 -23 9q-9 10 -9 23t9 23l256 256q10 9 23 9t23 -9q9 -10 9 -23t-9 -23zM608 224v-320q0 -14 -9 -23t-23 -9t-23 9t-9 23v320q0 14 9 23t23 9t23 -9t9 -23zM384 448q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23t9 23t23 9h320 q14 0 23 -9t9 -23zM1648 320q0 -120 -85 -203l-147 -146q-83 -83 -203 -83q-121 0 -204 85l-334 335q-21 21 -42 56l239 18l273 -274q27 -27 68 -27.5t68 26.5l147 146q28 28 28 67q0 40 -28 68l-274 275l18 239q35 -21 56 -42l336 -336q84 -86 84 -204zM1031 1044l-239 -18 l-273 274q-28 28 -68 28q-39 0 -68 -27l-147 -146q-28 -28 -28 -67q0 -40 28 -68l274 -274l-18 -240q-35 21 -56 42l-336 336q-84 86 -84 204q0 120 85 203l147 146q83 83 203 83q121 0 204 -85l334 -335q21 -21 42 -56zM1664 960q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9 t-9 23t9 23t23 9h320q14 0 23 -9t9 -23zM1120 1504v-320q0 -14 -9 -23t-23 -9t-23 9t-9 23v320q0 14 9 23t23 9t23 -9t9 -23zM1527 1353l-256 -256q-11 -9 -23 -9t-23 9q-9 10 -9 23t9 23l256 256q10 9 23 9t23 -9q9 -10 9 -23t-9 -23z" />
+<glyph unicode="&#xf128;" horiz-adv-x="1024" d="M704 280v-240q0 -16 -12 -28t-28 -12h-240q-16 0 -28 12t-12 28v240q0 16 12 28t28 12h240q16 0 28 -12t12 -28zM1020 880q0 -54 -15.5 -101t-35 -76.5t-55 -59.5t-57.5 -43.5t-61 -35.5q-41 -23 -68.5 -65t-27.5 -67q0 -17 -12 -32.5t-28 -15.5h-240q-15 0 -25.5 18.5 t-10.5 37.5v45q0 83 65 156.5t143 108.5q59 27 84 56t25 76q0 42 -46.5 74t-107.5 32q-65 0 -108 -29q-35 -25 -107 -115q-13 -16 -31 -16q-12 0 -25 8l-164 125q-13 10 -15.5 25t5.5 28q160 266 464 266q80 0 161 -31t146 -83t106 -127.5t41 -158.5z" />
+<glyph unicode="&#xf129;" horiz-adv-x="640" d="M640 192v-128q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64v384h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h384q26 0 45 -19t19 -45v-576h64q26 0 45 -19t19 -45zM512 1344v-192q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v192 q0 26 19 45t45 19h256q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf12a;" horiz-adv-x="640" d="M512 288v-224q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v224q0 26 19 45t45 19h256q26 0 45 -19t19 -45zM542 1344l-28 -768q-1 -26 -20.5 -45t-45.5 -19h-256q-26 0 -45.5 19t-20.5 45l-28 768q-1 26 17.5 45t44.5 19h320q26 0 44.5 -19t17.5 -45z" />
+<glyph unicode="&#xf12b;" d="M897 167v-167h-248l-159 252l-24 42q-8 9 -11 21h-3l-9 -21q-10 -20 -25 -44l-155 -250h-258v167h128l197 291l-185 272h-137v168h276l139 -228q2 -4 23 -42q8 -9 11 -21h3q3 9 11 21l25 42l140 228h257v-168h-125l-184 -267l204 -296h109zM1534 846v-206h-514l-3 27 q-4 28 -4 46q0 64 26 117t65 86.5t84 65t84 54.5t65 54t26 64q0 38 -29.5 62.5t-70.5 24.5q-51 0 -97 -39q-14 -11 -36 -38l-105 92q26 37 63 66q83 65 188 65q110 0 178 -59.5t68 -158.5q0 -56 -24.5 -103t-62 -76.5t-81.5 -58.5t-82 -50.5t-65.5 -51.5t-30.5 -63h232v80 h126z" />
+<glyph unicode="&#xf12c;" d="M897 167v-167h-248l-159 252l-24 42q-8 9 -11 21h-3l-9 -21q-10 -20 -25 -44l-155 -250h-258v167h128l197 291l-185 272h-137v168h276l139 -228q2 -4 23 -42q8 -9 11 -21h3q3 9 11 21l25 42l140 228h257v-168h-125l-184 -267l204 -296h109zM1536 -50v-206h-514l-4 27 q-3 45 -3 46q0 64 26 117t65 86.5t84 65t84 54.5t65 54t26 64q0 38 -29.5 62.5t-70.5 24.5q-51 0 -97 -39q-14 -11 -36 -38l-105 92q26 37 63 66q80 65 188 65q110 0 178 -59.5t68 -158.5q0 -66 -34.5 -118.5t-84 -86t-99.5 -62.5t-87 -63t-41 -73h232v80h126z" />
+<glyph unicode="&#xf12d;" horiz-adv-x="1920" d="M896 128l336 384h-768l-336 -384h768zM1909 1205q15 -34 9.5 -71.5t-30.5 -65.5l-896 -1024q-38 -44 -96 -44h-768q-38 0 -69.5 20.5t-47.5 54.5q-15 34 -9.5 71.5t30.5 65.5l896 1024q38 44 96 44h768q38 0 69.5 -20.5t47.5 -54.5z" />
+<glyph unicode="&#xf12e;" horiz-adv-x="1664" d="M1664 438q0 -81 -44.5 -135t-123.5 -54q-41 0 -77.5 17.5t-59 38t-56.5 38t-71 17.5q-110 0 -110 -124q0 -39 16 -115t15 -115v-5q-22 0 -33 -1q-34 -3 -97.5 -11.5t-115.5 -13.5t-98 -5q-61 0 -103 26.5t-42 83.5q0 37 17.5 71t38 56.5t38 59t17.5 77.5q0 79 -54 123.5 t-135 44.5q-84 0 -143 -45.5t-59 -127.5q0 -43 15 -83t33.5 -64.5t33.5 -53t15 -50.5q0 -45 -46 -89q-37 -35 -117 -35q-95 0 -245 24q-9 2 -27.5 4t-27.5 4l-13 2q-1 0 -3 1q-2 0 -2 1v1024q2 -1 17.5 -3.5t34 -5t21.5 -3.5q150 -24 245 -24q80 0 117 35q46 44 46 89 q0 22 -15 50.5t-33.5 53t-33.5 64.5t-15 83q0 82 59 127.5t144 45.5q80 0 134 -44.5t54 -123.5q0 -41 -17.5 -77.5t-38 -59t-38 -56.5t-17.5 -71q0 -57 42 -83.5t103 -26.5q64 0 180 15t163 17v-2q-1 -2 -3.5 -17.5t-5 -34t-3.5 -21.5q-24 -150 -24 -245q0 -80 35 -117 q44 -46 89 -46q22 0 50.5 15t53 33.5t64.5 33.5t83 15q82 0 127.5 -59t45.5 -143z" />
+<glyph unicode="&#xf130;" horiz-adv-x="1152" d="M1152 832v-128q0 -221 -147.5 -384.5t-364.5 -187.5v-132h256q26 0 45 -19t19 -45t-19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h256v132q-217 24 -364.5 187.5t-147.5 384.5v128q0 26 19 45t45 19t45 -19t19 -45v-128q0 -185 131.5 -316.5t316.5 -131.5 t316.5 131.5t131.5 316.5v128q0 26 19 45t45 19t45 -19t19 -45zM896 1216v-512q0 -132 -94 -226t-226 -94t-226 94t-94 226v512q0 132 94 226t226 94t226 -94t94 -226z" />
+<glyph unicode="&#xf131;" horiz-adv-x="1408" d="M271 591l-101 -101q-42 103 -42 214v128q0 26 19 45t45 19t45 -19t19 -45v-128q0 -53 15 -113zM1385 1193l-361 -361v-128q0 -132 -94 -226t-226 -94q-55 0 -109 19l-96 -96q97 -51 205 -51q185 0 316.5 131.5t131.5 316.5v128q0 26 19 45t45 19t45 -19t19 -45v-128 q0 -221 -147.5 -384.5t-364.5 -187.5v-132h256q26 0 45 -19t19 -45t-19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h256v132q-125 13 -235 81l-254 -254q-10 -10 -23 -10t-23 10l-82 82q-10 10 -10 23t10 23l1234 1234q10 10 23 10t23 -10l82 -82q10 -10 10 -23 t-10 -23zM1005 1325l-621 -621v512q0 132 94 226t226 94q102 0 184.5 -59t116.5 -152z" />
+<glyph unicode="&#xf132;" horiz-adv-x="1280" d="M1088 576v640h-448v-1137q119 63 213 137q235 184 235 360zM1280 1344v-768q0 -86 -33.5 -170.5t-83 -150t-118 -127.5t-126.5 -103t-121 -77.5t-89.5 -49.5t-42.5 -20q-12 -6 -26 -6t-26 6q-16 7 -42.5 20t-89.5 49.5t-121 77.5t-126.5 103t-118 127.5t-83 150 t-33.5 170.5v768q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf133;" horiz-adv-x="1664" d="M128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280 q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf134;" horiz-adv-x="1408" d="M512 1344q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 1376v-320q0 -16 -12 -25q-8 -7 -20 -7q-4 0 -7 1l-448 96q-11 2 -18 11t-7 20h-256v-102q111 -23 183.5 -111t72.5 -203v-800q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v800 q0 106 62.5 190.5t161.5 114.5v111h-32q-59 0 -115 -23.5t-91.5 -53t-66 -66.5t-40.5 -53.5t-14 -24.5q-17 -35 -57 -35q-16 0 -29 7q-23 12 -31.5 37t3.5 49q5 10 14.5 26t37.5 53.5t60.5 70t85 67t108.5 52.5q-25 42 -25 86q0 66 47 113t113 47t113 -47t47 -113 q0 -33 -14 -64h302q0 11 7 20t18 11l448 96q3 1 7 1q12 0 20 -7q12 -9 12 -25z" />
+<glyph unicode="&#xf135;" horiz-adv-x="1664" d="M1440 1088q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1664 1376q0 -249 -75.5 -430.5t-253.5 -360.5q-81 -80 -195 -176l-20 -379q-2 -16 -16 -26l-384 -224q-7 -4 -16 -4q-12 0 -23 9l-64 64q-13 14 -8 32l85 276l-281 281l-276 -85q-3 -1 -9 -1 q-14 0 -23 9l-64 64q-17 19 -5 39l224 384q10 14 26 16l379 20q96 114 176 195q188 187 358 258t431 71q14 0 24 -9.5t10 -22.5z" />
+<glyph unicode="&#xf136;" horiz-adv-x="1792" d="M1745 763l-164 -763h-334l178 832q13 56 -15 88q-27 33 -83 33h-169l-204 -953h-334l204 953h-286l-204 -953h-334l204 953l-153 327h1276q101 0 189.5 -40.5t147.5 -113.5q60 -73 81 -168.5t0 -194.5z" />
+<glyph unicode="&#xf137;" d="M909 141l102 102q19 19 19 45t-19 45l-307 307l307 307q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-454 -454q-19 -19 -19 -45t19 -45l454 -454q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf138;" d="M717 141l454 454q19 19 19 45t-19 45l-454 454q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l307 -307l-307 -307q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf139;" d="M1165 397l102 102q19 19 19 45t-19 45l-454 454q-19 19 -45 19t-45 -19l-454 -454q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19l307 307l307 -307q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf13a;" d="M813 237l454 454q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-307 -307l-307 307q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l454 -454q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf13b;" horiz-adv-x="1408" d="M1130 939l16 175h-884l47 -534h612l-22 -228l-197 -53l-196 53l-13 140h-175l22 -278l362 -100h4v1l359 99l50 544h-644l-15 181h674zM0 1408h1408l-128 -1438l-578 -162l-574 162z" />
+<glyph unicode="&#xf13c;" horiz-adv-x="1792" d="M275 1408h1505l-266 -1333l-804 -267l-698 267l71 356h297l-29 -147l422 -161l486 161l68 339h-1208l58 297h1209l38 191h-1208z" />
+<glyph unicode="&#xf13d;" horiz-adv-x="1792" d="M960 1280q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1792 352v-352q0 -22 -20 -30q-8 -2 -12 -2q-13 0 -23 9l-93 93q-119 -143 -318.5 -226.5t-429.5 -83.5t-429.5 83.5t-318.5 226.5l-93 -93q-9 -9 -23 -9q-4 0 -12 2q-20 8 -20 30v352 q0 14 9 23t23 9h352q22 0 30 -20q8 -19 -7 -35l-100 -100q67 -91 189.5 -153.5t271.5 -82.5v647h-192q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h192v163q-58 34 -93 92.5t-35 128.5q0 106 75 181t181 75t181 -75t75 -181q0 -70 -35 -128.5t-93 -92.5v-163h192q26 0 45 -19 t19 -45v-128q0 -26 -19 -45t-45 -19h-192v-647q149 20 271.5 82.5t189.5 153.5l-100 100q-15 16 -7 35q8 20 30 20h352q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf13e;" horiz-adv-x="1152" d="M1056 768q40 0 68 -28t28 -68v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h32v320q0 185 131.5 316.5t316.5 131.5t316.5 -131.5t131.5 -316.5q0 -26 -19 -45t-45 -19h-64q-26 0 -45 19t-19 45q0 106 -75 181t-181 75t-181 -75t-75 -181 v-320h736z" />
+<glyph unicode="&#xf140;" d="M1024 640q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM1152 640q0 159 -112.5 271.5t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM1280 640q0 -212 -150 -362t-362 -150t-362 150 t-150 362t150 362t362 150t362 -150t150 -362zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf141;" horiz-adv-x="1408" d="M384 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM896 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM1408 800v-192q0 -40 -28 -68t-68 -28h-192 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68z" />
+<glyph unicode="&#xf142;" horiz-adv-x="384" d="M384 288v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM384 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM384 1312v-192q0 -40 -28 -68t-68 -28h-192 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68z" />
+<glyph unicode="&#xf143;" d="M512 256q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM863 162q-13 232 -177 396t-396 177q-14 1 -24 -9t-10 -23v-128q0 -13 8.5 -22t21.5 -10q154 -11 264 -121t121 -264q1 -13 10 -21.5t22 -8.5h128q13 0 23 10 t9 24zM1247 161q-5 154 -56 297.5t-139.5 260t-205 205t-260 139.5t-297.5 56q-14 1 -23 -9q-10 -10 -10 -23v-128q0 -13 9 -22t22 -10q204 -7 378 -111.5t278.5 -278.5t111.5 -378q1 -13 10 -22t22 -9h128q13 0 23 10q11 9 9 23zM1536 1120v-960q0 -119 -84.5 -203.5 t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf144;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM1152 585q32 18 32 55t-32 55l-544 320q-31 19 -64 1q-32 -19 -32 -56v-640q0 -37 32 -56 q16 -8 32 -8q17 0 32 9z" />
+<glyph unicode="&#xf145;" horiz-adv-x="1792" d="M1024 1084l316 -316l-572 -572l-316 316zM813 105l618 618q19 19 19 45t-19 45l-362 362q-18 18 -45 18t-45 -18l-618 -618q-19 -19 -19 -45t19 -45l362 -362q18 -18 45 -18t45 18zM1702 742l-907 -908q-37 -37 -90.5 -37t-90.5 37l-126 126q56 56 56 136t-56 136 t-136 56t-136 -56l-125 126q-37 37 -37 90.5t37 90.5l907 906q37 37 90.5 37t90.5 -37l125 -125q-56 -56 -56 -136t56 -136t136 -56t136 56l126 -125q37 -37 37 -90.5t-37 -90.5z" />
+<glyph unicode="&#xf146;" d="M1280 576v128q0 26 -19 45t-45 19h-896q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h896q26 0 45 19t19 45zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 t84.5 -203.5z" />
+<glyph unicode="&#xf147;" horiz-adv-x="1408" d="M1152 736v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h832q14 0 23 -9t9 -23zM1280 288v832q0 66 -47 113t-113 47h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113zM1408 1120v-832q0 -119 -84.5 -203.5 t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf148;" horiz-adv-x="1024" d="M1018 933q-18 -37 -58 -37h-192v-864q0 -14 -9 -23t-23 -9h-704q-21 0 -29 18q-8 20 4 35l160 192q9 11 25 11h320v640h-192q-40 0 -58 37q-17 37 9 68l320 384q18 22 49 22t49 -22l320 -384q27 -32 9 -68z" />
+<glyph unicode="&#xf149;" horiz-adv-x="1024" d="M32 1280h704q13 0 22.5 -9.5t9.5 -23.5v-863h192q40 0 58 -37t-9 -69l-320 -384q-18 -22 -49 -22t-49 22l-320 384q-26 31 -9 69q18 37 58 37h192v640h-320q-14 0 -25 11l-160 192q-13 14 -4 34q9 19 29 19z" />
+<glyph unicode="&#xf14a;" d="M685 237l614 614q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-467 -467l-211 211q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l358 -358q19 -19 45 -19t45 19zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5 t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf14b;" d="M404 428l152 -152l-52 -52h-56v96h-96v56zM818 818q14 -13 -3 -30l-291 -291q-17 -17 -30 -3q-14 13 3 30l291 291q17 17 30 3zM544 128l544 544l-288 288l-544 -544v-288h288zM1152 736l92 92q28 28 28 68t-28 68l-152 152q-28 28 -68 28t-68 -28l-92 -92zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf14c;" d="M1280 608v480q0 26 -19 45t-45 19h-480q-42 0 -59 -39q-17 -41 14 -70l144 -144l-534 -534q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19l534 534l144 -144q18 -19 45 -19q12 0 25 5q39 17 39 59zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960 q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf14d;" d="M1005 435l352 352q19 19 19 45t-19 45l-352 352q-30 31 -69 14q-40 -17 -40 -59v-160q-119 0 -216 -19.5t-162.5 -51t-114 -79t-76.5 -95.5t-44.5 -109t-21.5 -111.5t-5 -110.5q0 -181 167 -404q10 -12 25 -12q7 0 13 3q22 9 19 33q-44 354 62 473q46 52 130 75.5 t224 23.5v-160q0 -42 40 -59q12 -5 24 -5q26 0 45 19zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf14e;" d="M640 448l256 128l-256 128v-256zM1024 1039v-542l-512 -256v542zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf150;" d="M1145 861q18 -35 -5 -66l-320 -448q-19 -27 -52 -27t-52 27l-320 448q-23 31 -5 66q17 35 57 35h640q40 0 57 -35zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf151;" d="M1145 419q-17 -35 -57 -35h-640q-40 0 -57 35q-18 35 5 66l320 448q19 27 52 27t52 -27l320 -448q23 -31 5 -66zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf152;" d="M1088 640q0 -33 -27 -52l-448 -320q-31 -23 -66 -5q-35 17 -35 57v640q0 40 35 57q35 18 66 -5l448 -320q27 -19 27 -52zM1280 160v960q0 14 -9 23t-23 9h-960q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h960q14 0 23 9t9 23zM1536 1120v-960q0 -119 -84.5 -203.5 t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf153;" horiz-adv-x="1024" d="M976 229l35 -159q3 -12 -3 -22.5t-17 -14.5l-5 -1q-4 -2 -10.5 -3.5t-16 -4.5t-21.5 -5.5t-25.5 -5t-30 -5t-33.5 -4.5t-36.5 -3t-38.5 -1q-234 0 -409 130.5t-238 351.5h-95q-13 0 -22.5 9.5t-9.5 22.5v113q0 13 9.5 22.5t22.5 9.5h66q-2 57 1 105h-67q-14 0 -23 9 t-9 23v114q0 14 9 23t23 9h98q67 210 243.5 338t400.5 128q102 0 194 -23q11 -3 20 -15q6 -11 3 -24l-43 -159q-3 -13 -14 -19.5t-24 -2.5l-4 1q-4 1 -11.5 2.5l-17.5 3.5t-22.5 3.5t-26 3t-29 2.5t-29.5 1q-126 0 -226 -64t-150 -176h468q16 0 25 -12q10 -12 7 -26 l-24 -114q-5 -26 -32 -26h-488q-3 -37 0 -105h459q15 0 25 -12q9 -12 6 -27l-24 -112q-2 -11 -11 -18.5t-20 -7.5h-387q48 -117 149.5 -185.5t228.5 -68.5q18 0 36 1.5t33.5 3.5t29.5 4.5t24.5 5t18.5 4.5l12 3l5 2q13 5 26 -2q12 -7 15 -21z" />
+<glyph unicode="&#xf154;" horiz-adv-x="1024" d="M1020 399v-367q0 -14 -9 -23t-23 -9h-956q-14 0 -23 9t-9 23v150q0 13 9.5 22.5t22.5 9.5h97v383h-95q-14 0 -23 9.5t-9 22.5v131q0 14 9 23t23 9h95v223q0 171 123.5 282t314.5 111q185 0 335 -125q9 -8 10 -20.5t-7 -22.5l-103 -127q-9 -11 -22 -12q-13 -2 -23 7 q-5 5 -26 19t-69 32t-93 18q-85 0 -137 -47t-52 -123v-215h305q13 0 22.5 -9t9.5 -23v-131q0 -13 -9.5 -22.5t-22.5 -9.5h-305v-379h414v181q0 13 9 22.5t23 9.5h162q14 0 23 -9.5t9 -22.5z" />
+<glyph unicode="&#xf155;" horiz-adv-x="1024" d="M978 351q0 -153 -99.5 -263.5t-258.5 -136.5v-175q0 -14 -9 -23t-23 -9h-135q-13 0 -22.5 9.5t-9.5 22.5v175q-66 9 -127.5 31t-101.5 44.5t-74 48t-46.5 37.5t-17.5 18q-17 21 -2 41l103 135q7 10 23 12q15 2 24 -9l2 -2q113 -99 243 -125q37 -8 74 -8q81 0 142.5 43 t61.5 122q0 28 -15 53t-33.5 42t-58.5 37.5t-66 32t-80 32.5q-39 16 -61.5 25t-61.5 26.5t-62.5 31t-56.5 35.5t-53.5 42.5t-43.5 49t-35.5 58t-21 66.5t-8.5 78q0 138 98 242t255 134v180q0 13 9.5 22.5t22.5 9.5h135q14 0 23 -9t9 -23v-176q57 -6 110.5 -23t87 -33.5 t63.5 -37.5t39 -29t15 -14q17 -18 5 -38l-81 -146q-8 -15 -23 -16q-14 -3 -27 7q-3 3 -14.5 12t-39 26.5t-58.5 32t-74.5 26t-85.5 11.5q-95 0 -155 -43t-60 -111q0 -26 8.5 -48t29.5 -41.5t39.5 -33t56 -31t60.5 -27t70 -27.5q53 -20 81 -31.5t76 -35t75.5 -42.5t62 -50 t53 -63.5t31.5 -76.5t13 -94z" />
+<glyph unicode="&#xf156;" horiz-adv-x="898" d="M898 1066v-102q0 -14 -9 -23t-23 -9h-168q-23 -144 -129 -234t-276 -110q167 -178 459 -536q14 -16 4 -34q-8 -18 -29 -18h-195q-16 0 -25 12q-306 367 -498 571q-9 9 -9 22v127q0 13 9.5 22.5t22.5 9.5h112q132 0 212.5 43t102.5 125h-427q-14 0 -23 9t-9 23v102 q0 14 9 23t23 9h413q-57 113 -268 113h-145q-13 0 -22.5 9.5t-9.5 22.5v133q0 14 9 23t23 9h832q14 0 23 -9t9 -23v-102q0 -14 -9 -23t-23 -9h-233q47 -61 64 -144h171q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf157;" horiz-adv-x="1027" d="M603 0h-172q-13 0 -22.5 9t-9.5 23v330h-288q-13 0 -22.5 9t-9.5 23v103q0 13 9.5 22.5t22.5 9.5h288v85h-288q-13 0 -22.5 9t-9.5 23v104q0 13 9.5 22.5t22.5 9.5h214l-321 578q-8 16 0 32q10 16 28 16h194q19 0 29 -18l215 -425q19 -38 56 -125q10 24 30.5 68t27.5 61 l191 420q8 19 29 19h191q17 0 27 -16q9 -14 1 -31l-313 -579h215q13 0 22.5 -9.5t9.5 -22.5v-104q0 -14 -9.5 -23t-22.5 -9h-290v-85h290q13 0 22.5 -9.5t9.5 -22.5v-103q0 -14 -9.5 -23t-22.5 -9h-290v-330q0 -13 -9.5 -22.5t-22.5 -9.5z" />
+<glyph unicode="&#xf158;" horiz-adv-x="1280" d="M1043 971q0 100 -65 162t-171 62h-320v-448h320q106 0 171 62t65 162zM1280 971q0 -193 -126.5 -315t-326.5 -122h-340v-118h505q14 0 23 -9t9 -23v-128q0 -14 -9 -23t-23 -9h-505v-192q0 -14 -9.5 -23t-22.5 -9h-167q-14 0 -23 9t-9 23v192h-224q-14 0 -23 9t-9 23v128 q0 14 9 23t23 9h224v118h-224q-14 0 -23 9t-9 23v149q0 13 9 22.5t23 9.5h224v629q0 14 9 23t23 9h539q200 0 326.5 -122t126.5 -315z" />
+<glyph unicode="&#xf159;" horiz-adv-x="1792" d="M514 341l81 299h-159l75 -300q1 -1 1 -3t1 -3q0 1 0.5 3.5t0.5 3.5zM630 768l35 128h-292l32 -128h225zM822 768h139l-35 128h-70zM1271 340l78 300h-162l81 -299q0 -1 0.5 -3.5t1.5 -3.5q0 1 0.5 3t0.5 3zM1382 768l33 128h-297l34 -128h230zM1792 736v-64q0 -14 -9 -23 t-23 -9h-213l-164 -616q-7 -24 -31 -24h-159q-24 0 -31 24l-166 616h-209l-167 -616q-7 -24 -31 -24h-159q-11 0 -19.5 7t-10.5 17l-160 616h-208q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h175l-33 128h-142q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h109l-89 344q-5 15 5 28 q10 12 26 12h137q26 0 31 -24l90 -360h359l97 360q7 24 31 24h126q24 0 31 -24l98 -360h365l93 360q5 24 31 24h137q16 0 26 -12q10 -13 5 -28l-91 -344h111q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-145l-34 -128h179q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf15a;" horiz-adv-x="1280" d="M1167 896q18 -182 -131 -258q117 -28 175 -103t45 -214q-7 -71 -32.5 -125t-64.5 -89t-97 -58.5t-121.5 -34.5t-145.5 -15v-255h-154v251q-80 0 -122 1v-252h-154v255q-18 0 -54 0.5t-55 0.5h-200l31 183h111q50 0 58 51v402h16q-6 1 -16 1v287q-13 68 -89 68h-111v164 l212 -1q64 0 97 1v252h154v-247q82 2 122 2v245h154v-252q79 -7 140 -22.5t113 -45t82.5 -78t36.5 -114.5zM952 351q0 36 -15 64t-37 46t-57.5 30.5t-65.5 18.5t-74 9t-69 3t-64.5 -1t-47.5 -1v-338q8 0 37 -0.5t48 -0.5t53 1.5t58.5 4t57 8.5t55.5 14t47.5 21t39.5 30 t24.5 40t9.5 51zM881 827q0 33 -12.5 58.5t-30.5 42t-48 28t-55 16.5t-61.5 8t-58 2.5t-54 -1t-39.5 -0.5v-307q5 0 34.5 -0.5t46.5 0t50 2t55 5.5t51.5 11t48.5 18.5t37 27t27 38.5t9 51z" />
+<glyph unicode="&#xf15b;" d="M1024 1024v472q22 -14 36 -28l408 -408q14 -14 28 -36h-472zM896 992q0 -40 28 -68t68 -28h544v-1056q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h800v-544z" />
+<glyph unicode="&#xf15c;" d="M1468 1060q14 -14 28 -36h-472v472q22 -14 36 -28zM992 896h544v-1056q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h800v-544q0 -40 28 -68t68 -28zM1152 160v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704 q14 0 23 9t9 23zM1152 416v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23zM1152 672v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23z" />
+<glyph unicode="&#xf15d;" horiz-adv-x="1664" d="M1191 1128h177l-72 218l-12 47q-2 16 -2 20h-4l-3 -20q0 -1 -3.5 -18t-7.5 -29zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1572 -23 v-233h-584v90l369 529q12 18 21 27l11 9v3q-2 0 -6.5 -0.5t-7.5 -0.5q-12 -3 -30 -3h-232v-115h-120v229h567v-89l-369 -530q-6 -8 -21 -26l-11 -11v-2l14 2q9 2 30 2h248v119h121zM1661 874v-106h-288v106h75l-47 144h-243l-47 -144h75v-106h-287v106h70l230 662h162 l230 -662h70z" />
+<glyph unicode="&#xf15e;" horiz-adv-x="1664" d="M1191 104h177l-72 218l-12 47q-2 16 -2 20h-4l-3 -20q0 -1 -3.5 -18t-7.5 -29zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1661 -150 v-106h-288v106h75l-47 144h-243l-47 -144h75v-106h-287v106h70l230 662h162l230 -662h70zM1572 1001v-233h-584v90l369 529q12 18 21 27l11 9v3q-2 0 -6.5 -0.5t-7.5 -0.5q-12 -3 -30 -3h-232v-115h-120v229h567v-89l-369 -530q-6 -8 -21 -26l-11 -10v-3l14 3q9 1 30 1h248 v119h121z" />
+<glyph unicode="&#xf160;" horiz-adv-x="1792" d="M736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1792 -32v-192q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h832 q14 0 23 -9t9 -23zM1600 480v-192q0 -14 -9 -23t-23 -9h-640q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h640q14 0 23 -9t9 -23zM1408 992v-192q0 -14 -9 -23t-23 -9h-448q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h448q14 0 23 -9t9 -23zM1216 1504v-192q0 -14 -9 -23t-23 -9h-256 q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h256q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf161;" horiz-adv-x="1792" d="M1216 -32v-192q0 -14 -9 -23t-23 -9h-256q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h256q14 0 23 -9t9 -23zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192 q14 0 23 -9t9 -23zM1408 480v-192q0 -14 -9 -23t-23 -9h-448q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h448q14 0 23 -9t9 -23zM1600 992v-192q0 -14 -9 -23t-23 -9h-640q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h640q14 0 23 -9t9 -23zM1792 1504v-192q0 -14 -9 -23t-23 -9h-832 q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h832q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf162;" d="M1346 223q0 63 -44 116t-103 53q-52 0 -83 -37t-31 -94t36.5 -95t104.5 -38q50 0 85 27t35 68zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23 zM1486 165q0 -62 -13 -121.5t-41 -114t-68 -95.5t-98.5 -65.5t-127.5 -24.5q-62 0 -108 16q-24 8 -42 15l39 113q15 -7 31 -11q37 -13 75 -13q84 0 134.5 58.5t66.5 145.5h-2q-21 -23 -61.5 -37t-84.5 -14q-106 0 -173 71.5t-67 172.5q0 105 72 178t181 73q123 0 205 -94.5 t82 -252.5zM1456 882v-114h-469v114h167v432q0 7 0.5 19t0.5 17v16h-2l-7 -12q-8 -13 -26 -31l-62 -58l-82 86l192 185h123v-654h165z" />
+<glyph unicode="&#xf163;" d="M1346 1247q0 63 -44 116t-103 53q-52 0 -83 -37t-31 -94t36.5 -95t104.5 -38q50 0 85 27t35 68zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9 t9 -23zM1456 -142v-114h-469v114h167v432q0 7 0.5 19t0.5 17v16h-2l-7 -12q-8 -13 -26 -31l-62 -58l-82 86l192 185h123v-654h165zM1486 1189q0 -62 -13 -121.5t-41 -114t-68 -95.5t-98.5 -65.5t-127.5 -24.5q-62 0 -108 16q-24 8 -42 15l39 113q15 -7 31 -11q37 -13 75 -13 q84 0 134.5 58.5t66.5 145.5h-2q-21 -23 -61.5 -37t-84.5 -14q-106 0 -173 71.5t-67 172.5q0 105 72 178t181 73q123 0 205 -94.5t82 -252.5z" />
+<glyph unicode="&#xf164;" horiz-adv-x="1664" d="M256 192q0 26 -19 45t-45 19q-27 0 -45.5 -19t-18.5 -45q0 -27 18.5 -45.5t45.5 -18.5q26 0 45 18.5t19 45.5zM416 704v-640q0 -26 -19 -45t-45 -19h-288q-26 0 -45 19t-19 45v640q0 26 19 45t45 19h288q26 0 45 -19t19 -45zM1600 704q0 -86 -55 -149q15 -44 15 -76 q3 -76 -43 -137q17 -56 0 -117q-15 -57 -54 -94q9 -112 -49 -181q-64 -76 -197 -78h-36h-76h-17q-66 0 -144 15.5t-121.5 29t-120.5 39.5q-123 43 -158 44q-26 1 -45 19.5t-19 44.5v641q0 25 18 43.5t43 20.5q24 2 76 59t101 121q68 87 101 120q18 18 31 48t17.5 48.5 t13.5 60.5q7 39 12.5 61t19.5 52t34 50q19 19 45 19q46 0 82.5 -10.5t60 -26t40 -40.5t24 -45t12 -50t5 -45t0.5 -39q0 -38 -9.5 -76t-19 -60t-27.5 -56q-3 -6 -10 -18t-11 -22t-8 -24h277q78 0 135 -57t57 -135z" />
+<glyph unicode="&#xf165;" horiz-adv-x="1664" d="M256 960q0 -26 -19 -45t-45 -19q-27 0 -45.5 19t-18.5 45q0 27 18.5 45.5t45.5 18.5q26 0 45 -18.5t19 -45.5zM416 448v640q0 26 -19 45t-45 19h-288q-26 0 -45 -19t-19 -45v-640q0 -26 19 -45t45 -19h288q26 0 45 19t19 45zM1545 597q55 -61 55 -149q-1 -78 -57.5 -135 t-134.5 -57h-277q4 -14 8 -24t11 -22t10 -18q18 -37 27 -57t19 -58.5t10 -76.5q0 -24 -0.5 -39t-5 -45t-12 -50t-24 -45t-40 -40.5t-60 -26t-82.5 -10.5q-26 0 -45 19q-20 20 -34 50t-19.5 52t-12.5 61q-9 42 -13.5 60.5t-17.5 48.5t-31 48q-33 33 -101 120q-49 64 -101 121 t-76 59q-25 2 -43 20.5t-18 43.5v641q0 26 19 44.5t45 19.5q35 1 158 44q77 26 120.5 39.5t121.5 29t144 15.5h17h76h36q133 -2 197 -78q58 -69 49 -181q39 -37 54 -94q17 -61 0 -117q46 -61 43 -137q0 -32 -15 -76z" />
+<glyph unicode="&#xf166;" d="M919 233v157q0 50 -29 50q-17 0 -33 -16v-224q16 -16 33 -16q29 0 29 49zM1103 355h66v34q0 51 -33 51t-33 -51v-34zM532 621v-70h-80v-423h-74v423h-78v70h232zM733 495v-367h-67v40q-39 -45 -76 -45q-33 0 -42 28q-6 16 -6 54v290h66v-270q0 -24 1 -26q1 -15 15 -15 q20 0 42 31v280h67zM985 384v-146q0 -52 -7 -73q-12 -42 -53 -42q-35 0 -68 41v-36h-67v493h67v-161q32 40 68 40q41 0 53 -42q7 -21 7 -74zM1236 255v-9q0 -29 -2 -43q-3 -22 -15 -40q-27 -40 -80 -40q-52 0 -81 38q-21 27 -21 86v129q0 59 20 86q29 38 80 38t78 -38 q21 -28 21 -86v-76h-133v-65q0 -51 34 -51q24 0 30 26q0 1 0.5 7t0.5 16.5v21.5h68zM785 1079v-156q0 -51 -32 -51t-32 51v156q0 52 32 52t32 -52zM1318 366q0 177 -19 260q-10 44 -43 73.5t-76 34.5q-136 15 -412 15q-275 0 -411 -15q-44 -5 -76.5 -34.5t-42.5 -73.5 q-20 -87 -20 -260q0 -176 20 -260q10 -43 42.5 -73t75.5 -35q137 -15 412 -15t412 15q43 5 75.5 35t42.5 73q20 84 20 260zM563 1017l90 296h-75l-51 -195l-53 195h-78l24 -69t23 -69q35 -103 46 -158v-201h74v201zM852 936v130q0 58 -21 87q-29 38 -78 38q-51 0 -78 -38 q-21 -29 -21 -87v-130q0 -58 21 -87q27 -38 78 -38q49 0 78 38q21 27 21 87zM1033 816h67v370h-67v-283q-22 -31 -42 -31q-15 0 -16 16q-1 2 -1 26v272h-67v-293q0 -37 6 -55q11 -27 43 -27q36 0 77 45v-40zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960 q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf167;" d="M971 292v-211q0 -67 -39 -67q-23 0 -45 22v301q22 22 45 22q39 0 39 -67zM1309 291v-46h-90v46q0 68 45 68t45 -68zM343 509h107v94h-312v-94h105v-569h100v569zM631 -60h89v494h-89v-378q-30 -42 -57 -42q-18 0 -21 21q-1 3 -1 35v364h-89v-391q0 -49 8 -73 q12 -37 58 -37q48 0 102 61v-54zM1060 88v197q0 73 -9 99q-17 56 -71 56q-50 0 -93 -54v217h-89v-663h89v48q45 -55 93 -55q54 0 71 55q9 27 9 100zM1398 98v13h-91q0 -51 -2 -61q-7 -36 -40 -36q-46 0 -46 69v87h179v103q0 79 -27 116q-39 51 -106 51q-68 0 -107 -51 q-28 -37 -28 -116v-173q0 -79 29 -116q39 -51 108 -51q72 0 108 53q18 27 21 54q2 9 2 58zM790 1011v210q0 69 -43 69t-43 -69v-210q0 -70 43 -70t43 70zM1509 260q0 -234 -26 -350q-14 -59 -58 -99t-102 -46q-184 -21 -555 -21t-555 21q-58 6 -102.5 46t-57.5 99 q-26 112 -26 350q0 234 26 350q14 59 58 99t103 47q183 20 554 20t555 -20q58 -7 102.5 -47t57.5 -99q26 -112 26 -350zM511 1536h102l-121 -399v-271h-100v271q-14 74 -61 212q-37 103 -65 187h106l71 -263zM881 1203v-175q0 -81 -28 -118q-37 -51 -106 -51q-67 0 -105 51 q-28 38 -28 118v175q0 80 28 117q38 51 105 51q69 0 106 -51q28 -37 28 -117zM1216 1365v-499h-91v55q-53 -62 -103 -62q-46 0 -59 37q-8 24 -8 75v394h91v-367q0 -33 1 -35q3 -22 21 -22q27 0 57 43v381h91z" />
+<glyph unicode="&#xf168;" horiz-adv-x="1408" d="M597 869q-10 -18 -257 -456q-27 -46 -65 -46h-239q-21 0 -31 17t0 36l253 448q1 0 0 1l-161 279q-12 22 -1 37q9 15 32 15h239q40 0 66 -45zM1403 1511q11 -16 0 -37l-528 -934v-1l336 -615q11 -20 1 -37q-10 -15 -32 -15h-239q-42 0 -66 45l-339 622q18 32 531 942 q25 45 64 45h241q22 0 31 -15z" />
+<glyph unicode="&#xf169;" d="M685 771q0 1 -126 222q-21 34 -52 34h-184q-18 0 -26 -11q-7 -12 1 -29l125 -216v-1l-196 -346q-9 -14 0 -28q8 -13 24 -13h185q31 0 50 36zM1309 1268q-7 12 -24 12h-187q-30 0 -49 -35l-411 -729q1 -2 262 -481q20 -35 52 -35h184q18 0 25 12q8 13 -1 28l-260 476v1 l409 723q8 16 0 28zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf16a;" horiz-adv-x="1792" d="M1280 640q0 37 -30 54l-512 320q-31 20 -65 2q-33 -18 -33 -56v-640q0 -38 33 -56q16 -8 31 -8q20 0 34 10l512 320q30 17 30 54zM1792 640q0 -96 -1 -150t-8.5 -136.5t-22.5 -147.5q-16 -73 -69 -123t-124 -58q-222 -25 -671 -25t-671 25q-71 8 -124.5 58t-69.5 123 q-14 65 -21.5 147.5t-8.5 136.5t-1 150t1 150t8.5 136.5t22.5 147.5q16 73 69 123t124 58q222 25 671 25t671 -25q71 -8 124.5 -58t69.5 -123q14 -65 21.5 -147.5t8.5 -136.5t1 -150z" />
+<glyph unicode="&#xf16b;" horiz-adv-x="1792" d="M402 829l494 -305l-342 -285l-490 319zM1388 274v-108l-490 -293v-1l-1 1l-1 -1v1l-489 293v108l147 -96l342 284v2l1 -1l1 1v-2l343 -284zM554 1418l342 -285l-494 -304l-338 270zM1390 829l338 -271l-489 -319l-343 285zM1239 1418l489 -319l-338 -270l-494 304z" />
+<glyph unicode="&#xf16c;" d="M1289 -96h-1118v480h-160v-640h1438v640h-160v-480zM347 428l33 157l783 -165l-33 -156zM450 802l67 146l725 -339l-67 -145zM651 1158l102 123l614 -513l-102 -123zM1048 1536l477 -641l-128 -96l-477 641zM330 65v159h800v-159h-800z" />
+<glyph unicode="&#xf16d;" d="M1362 110v648h-135q20 -63 20 -131q0 -126 -64 -232.5t-174 -168.5t-240 -62q-197 0 -337 135.5t-140 327.5q0 68 20 131h-141v-648q0 -26 17.5 -43.5t43.5 -17.5h1069q25 0 43 17.5t18 43.5zM1078 643q0 124 -90.5 211.5t-218.5 87.5q-127 0 -217.5 -87.5t-90.5 -211.5 t90.5 -211.5t217.5 -87.5q128 0 218.5 87.5t90.5 211.5zM1362 1003v165q0 28 -20 48.5t-49 20.5h-174q-29 0 -49 -20.5t-20 -48.5v-165q0 -29 20 -49t49 -20h174q29 0 49 20t20 49zM1536 1211v-1142q0 -81 -58 -139t-139 -58h-1142q-81 0 -139 58t-58 139v1142q0 81 58 139 t139 58h1142q81 0 139 -58t58 -139z" />
+<glyph unicode="&#xf16e;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM698 640q0 88 -62 150t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150zM1262 640q0 88 -62 150 t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150z" />
+<glyph unicode="&#xf170;" d="M768 914l201 -306h-402zM1133 384h94l-459 691l-459 -691h94l104 160h522zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf171;" horiz-adv-x="1408" d="M815 677q8 -63 -50.5 -101t-111.5 -6q-39 17 -53.5 58t-0.5 82t52 58q36 18 72.5 12t64 -35.5t27.5 -67.5zM926 698q-14 107 -113 164t-197 13q-63 -28 -100.5 -88.5t-34.5 -129.5q4 -91 77.5 -155t165.5 -56q91 8 152 84t50 168zM1165 1240q-20 27 -56 44.5t-58 22 t-71 12.5q-291 47 -566 -2q-43 -7 -66 -12t-55 -22t-50 -43q30 -28 76 -45.5t73.5 -22t87.5 -11.5q228 -29 448 -1q63 8 89.5 12t72.5 21.5t75 46.5zM1222 205q-8 -26 -15.5 -76.5t-14 -84t-28.5 -70t-58 -56.5q-86 -48 -189.5 -71.5t-202 -22t-201.5 18.5q-46 8 -81.5 18 t-76.5 27t-73 43.5t-52 61.5q-25 96 -57 292l6 16l18 9q223 -148 506.5 -148t507.5 148q21 -6 24 -23t-5 -45t-8 -37zM1403 1166q-26 -167 -111 -655q-5 -30 -27 -56t-43.5 -40t-54.5 -31q-252 -126 -610 -88q-248 27 -394 139q-15 12 -25.5 26.5t-17 35t-9 34t-6 39.5 t-5.5 35q-9 50 -26.5 150t-28 161.5t-23.5 147.5t-22 158q3 26 17.5 48.5t31.5 37.5t45 30t46 22.5t48 18.5q125 46 313 64q379 37 676 -50q155 -46 215 -122q16 -20 16.5 -51t-5.5 -54z" />
+<glyph unicode="&#xf172;" d="M848 666q0 43 -41 66t-77 1q-43 -20 -42.5 -72.5t43.5 -70.5q39 -23 81 4t36 72zM928 682q8 -66 -36 -121t-110 -61t-119 40t-56 113q-2 49 25.5 93t72.5 64q70 31 141.5 -10t81.5 -118zM1100 1073q-20 -21 -53.5 -34t-53 -16t-63.5 -8q-155 -20 -324 0q-44 6 -63 9.5 t-52.5 16t-54.5 32.5q13 19 36 31t40 15.5t47 8.5q198 35 408 1q33 -5 51 -8.5t43 -16t39 -31.5zM1142 327q0 7 5.5 26.5t3 32t-17.5 16.5q-161 -106 -365 -106t-366 106l-12 -6l-5 -12q26 -154 41 -210q47 -81 204 -108q249 -46 428 53q34 19 49 51.5t22.5 85.5t12.5 71z M1272 1020q9 53 -8 75q-43 55 -155 88q-216 63 -487 36q-132 -12 -226 -46q-38 -15 -59.5 -25t-47 -34t-29.5 -54q8 -68 19 -138t29 -171t24 -137q1 -5 5 -31t7 -36t12 -27t22 -28q105 -80 284 -100q259 -28 440 63q24 13 39.5 23t31 29t19.5 40q48 267 80 473zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf173;" horiz-adv-x="1024" d="M944 207l80 -237q-23 -35 -111 -66t-177 -32q-104 -2 -190.5 26t-142.5 74t-95 106t-55.5 120t-16.5 118v544h-168v215q72 26 129 69.5t91 90t58 102t34 99t15 88.5q1 5 4.5 8.5t7.5 3.5h244v-424h333v-252h-334v-518q0 -30 6.5 -56t22.5 -52.5t49.5 -41.5t81.5 -14 q78 2 134 29z" />
+<glyph unicode="&#xf174;" d="M1136 75l-62 183q-44 -22 -103 -22q-36 -1 -62 10.5t-38.5 31.5t-17.5 40.5t-5 43.5v398h257v194h-256v326h-188q-8 0 -9 -10q-5 -44 -17.5 -87t-39 -95t-77 -95t-118.5 -68v-165h130v-418q0 -57 21.5 -115t65 -111t121 -85.5t176.5 -30.5q69 1 136.5 25t85.5 50z M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf175;" horiz-adv-x="768" d="M765 237q8 -19 -5 -35l-350 -384q-10 -10 -23 -10q-14 0 -24 10l-355 384q-13 16 -5 35q9 19 29 19h224v1248q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1248h224q21 0 29 -19z" />
+<glyph unicode="&#xf176;" horiz-adv-x="768" d="M765 1043q-9 -19 -29 -19h-224v-1248q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v1248h-224q-21 0 -29 19t5 35l350 384q10 10 23 10q14 0 24 -10l355 -384q13 -16 5 -35z" />
+<glyph unicode="&#xf177;" horiz-adv-x="1792" d="M1792 736v-192q0 -14 -9 -23t-23 -9h-1248v-224q0 -21 -19 -29t-35 5l-384 350q-10 10 -10 23q0 14 10 24l384 354q16 14 35 6q19 -9 19 -29v-224h1248q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf178;" horiz-adv-x="1792" d="M1728 643q0 -14 -10 -24l-384 -354q-16 -14 -35 -6q-19 9 -19 29v224h-1248q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h1248v224q0 21 19 29t35 -5l384 -350q10 -10 10 -23z" />
+<glyph unicode="&#xf179;" horiz-adv-x="1408" d="M1393 321q-39 -125 -123 -250q-129 -196 -257 -196q-49 0 -140 32q-86 32 -151 32q-61 0 -142 -33q-81 -34 -132 -34q-152 0 -301 259q-147 261 -147 503q0 228 113 374q112 144 284 144q72 0 177 -30q104 -30 138 -30q45 0 143 34q102 34 173 34q119 0 213 -65 q52 -36 104 -100q-79 -67 -114 -118q-65 -94 -65 -207q0 -124 69 -223t158 -126zM1017 1494q0 -61 -29 -136q-30 -75 -93 -138q-54 -54 -108 -72q-37 -11 -104 -17q3 149 78 257q74 107 250 148q1 -3 2.5 -11t2.5 -11q0 -4 0.5 -10t0.5 -10z" />
+<glyph unicode="&#xf17a;" horiz-adv-x="1664" d="M682 530v-651l-682 94v557h682zM682 1273v-659h-682v565zM1664 530v-786l-907 125v661h907zM1664 1408v-794h-907v669z" />
+<glyph unicode="&#xf17b;" horiz-adv-x="1408" d="M493 1053q16 0 27.5 11.5t11.5 27.5t-11.5 27.5t-27.5 11.5t-27 -11.5t-11 -27.5t11 -27.5t27 -11.5zM915 1053q16 0 27 11.5t11 27.5t-11 27.5t-27 11.5t-27.5 -11.5t-11.5 -27.5t11.5 -27.5t27.5 -11.5zM103 869q42 0 72 -30t30 -72v-430q0 -43 -29.5 -73t-72.5 -30 t-73 30t-30 73v430q0 42 30 72t73 30zM1163 850v-666q0 -46 -32 -78t-77 -32h-75v-227q0 -43 -30 -73t-73 -30t-73 30t-30 73v227h-138v-227q0 -43 -30 -73t-73 -30q-42 0 -72 30t-30 73l-1 227h-74q-46 0 -78 32t-32 78v666h918zM931 1255q107 -55 171 -153.5t64 -215.5 h-925q0 117 64 215.5t172 153.5l-71 131q-7 13 5 20q13 6 20 -6l72 -132q95 42 201 42t201 -42l72 132q7 12 20 6q12 -7 5 -20zM1408 767v-430q0 -43 -30 -73t-73 -30q-42 0 -72 30t-30 73v430q0 43 30 72.5t72 29.5q43 0 73 -29.5t30 -72.5z" />
+<glyph unicode="&#xf17c;" d="M663 1125q-11 -1 -15.5 -10.5t-8.5 -9.5q-5 -1 -5 5q0 12 19 15h10zM750 1111q-4 -1 -11.5 6.5t-17.5 4.5q24 11 32 -2q3 -6 -3 -9zM399 684q-4 1 -6 -3t-4.5 -12.5t-5.5 -13.5t-10 -13q-7 -10 -1 -12q4 -1 12.5 7t12.5 18q1 3 2 7t2 6t1.5 4.5t0.5 4v3t-1 2.5t-3 2z M1254 325q0 18 -55 42q4 15 7.5 27.5t5 26t3 21.5t0.5 22.5t-1 19.5t-3.5 22t-4 20.5t-5 25t-5.5 26.5q-10 48 -47 103t-72 75q24 -20 57 -83q87 -162 54 -278q-11 -40 -50 -42q-31 -4 -38.5 18.5t-8 83.5t-11.5 107q-9 39 -19.5 69t-19.5 45.5t-15.5 24.5t-13 15t-7.5 7 q-14 62 -31 103t-29.5 56t-23.5 33t-15 40q-4 21 6 53.5t4.5 49.5t-44.5 25q-15 3 -44.5 18t-35.5 16q-8 1 -11 26t8 51t36 27q37 3 51 -30t4 -58q-11 -19 -2 -26.5t30 -0.5q13 4 13 36v37q-5 30 -13.5 50t-21 30.5t-23.5 15t-27 7.5q-107 -8 -89 -134q0 -15 -1 -15 q-9 9 -29.5 10.5t-33 -0.5t-15.5 5q1 57 -16 90t-45 34q-27 1 -41.5 -27.5t-16.5 -59.5q-1 -15 3.5 -37t13 -37.5t15.5 -13.5q10 3 16 14q4 9 -7 8q-7 0 -15.5 14.5t-9.5 33.5q-1 22 9 37t34 14q17 0 27 -21t9.5 -39t-1.5 -22q-22 -15 -31 -29q-8 -12 -27.5 -23.5 t-20.5 -12.5q-13 -14 -15.5 -27t7.5 -18q14 -8 25 -19.5t16 -19t18.5 -13t35.5 -6.5q47 -2 102 15q2 1 23 7t34.5 10.5t29.5 13t21 17.5q9 14 20 8q5 -3 6.5 -8.5t-3 -12t-16.5 -9.5q-20 -6 -56.5 -21.5t-45.5 -19.5q-44 -19 -70 -23q-25 -5 -79 2q-10 2 -9 -2t17 -19 q25 -23 67 -22q17 1 36 7t36 14t33.5 17.5t30 17t24.5 12t17.5 2.5t8.5 -11q0 -2 -1 -4.5t-4 -5t-6 -4.5t-8.5 -5t-9 -4.5t-10 -5t-9.5 -4.5q-28 -14 -67.5 -44t-66.5 -43t-49 -1q-21 11 -63 73q-22 31 -25 22q-1 -3 -1 -10q0 -25 -15 -56.5t-29.5 -55.5t-21 -58t11.5 -63 q-23 -6 -62.5 -90t-47.5 -141q-2 -18 -1.5 -69t-5.5 -59q-8 -24 -29 -3q-32 31 -36 94q-2 28 4 56q4 19 -1 18l-4 -5q-36 -65 10 -166q5 -12 25 -28t24 -20q20 -23 104 -90.5t93 -76.5q16 -15 17.5 -38t-14 -43t-45.5 -23q8 -15 29 -44.5t28 -54t7 -70.5q46 24 7 92 q-4 8 -10.5 16t-9.5 12t-2 6q3 5 13 9.5t20 -2.5q46 -52 166 -36q133 15 177 87q23 38 34 30q12 -6 10 -52q-1 -25 -23 -92q-9 -23 -6 -37.5t24 -15.5q3 19 14.5 77t13.5 90q2 21 -6.5 73.5t-7.5 97t23 70.5q15 18 51 18q1 37 34.5 53t72.5 10.5t60 -22.5zM626 1152 q3 17 -2.5 30t-11.5 15q-9 2 -9 -7q2 -5 5 -6q10 0 7 -15q-3 -20 8 -20q3 0 3 3zM1045 955q-2 8 -6.5 11.5t-13 5t-14.5 5.5q-5 3 -9.5 8t-7 8t-5.5 6.5t-4 4t-4 -1.5q-14 -16 7 -43.5t39 -31.5q9 -1 14.5 8t3.5 20zM867 1168q0 11 -5 19.5t-11 12.5t-9 3q-14 -1 -7 -7l4 -2 q14 -4 18 -31q0 -3 8 2zM921 1401q0 2 -2.5 5t-9 7t-9.5 6q-15 15 -24 15q-9 -1 -11.5 -7.5t-1 -13t-0.5 -12.5q-1 -4 -6 -10.5t-6 -9t3 -8.5q4 -3 8 0t11 9t15 9q1 1 9 1t15 2t9 7zM1486 60q20 -12 31 -24.5t12 -24t-2.5 -22.5t-15.5 -22t-23.5 -19.5t-30 -18.5 t-31.5 -16.5t-32 -15.5t-27 -13q-38 -19 -85.5 -56t-75.5 -64q-17 -16 -68 -19.5t-89 14.5q-18 9 -29.5 23.5t-16.5 25.5t-22 19.5t-47 9.5q-44 1 -130 1q-19 0 -57 -1.5t-58 -2.5q-44 -1 -79.5 -15t-53.5 -30t-43.5 -28.5t-53.5 -11.5q-29 1 -111 31t-146 43q-19 4 -51 9.5 t-50 9t-39.5 9.5t-33.5 14.5t-17 19.5q-10 23 7 66.5t18 54.5q1 16 -4 40t-10 42.5t-4.5 36.5t10.5 27q14 12 57 14t60 12q30 18 42 35t12 51q21 -73 -32 -106q-32 -20 -83 -15q-34 3 -43 -10q-13 -15 5 -57q2 -6 8 -18t8.5 -18t4.5 -17t1 -22q0 -15 -17 -49t-14 -48 q3 -17 37 -26q20 -6 84.5 -18.5t99.5 -20.5q24 -6 74 -22t82.5 -23t55.5 -4q43 6 64.5 28t23 48t-7.5 58.5t-19 52t-20 36.5q-121 190 -169 242q-68 74 -113 40q-11 -9 -15 15q-3 16 -2 38q1 29 10 52t24 47t22 42q8 21 26.5 72t29.5 78t30 61t39 54q110 143 124 195 q-12 112 -16 310q-2 90 24 151.5t106 104.5q39 21 104 21q53 1 106 -13.5t89 -41.5q57 -42 91.5 -121.5t29.5 -147.5q-5 -95 30 -214q34 -113 133 -218q55 -59 99.5 -163t59.5 -191q8 -49 5 -84.5t-12 -55.5t-20 -22q-10 -2 -23.5 -19t-27 -35.5t-40.5 -33.5t-61 -14 q-18 1 -31.5 5t-22.5 13.5t-13.5 15.5t-11.5 20.5t-9 19.5q-22 37 -41 30t-28 -49t7 -97q20 -70 1 -195q-10 -65 18 -100.5t73 -33t85 35.5q59 49 89.5 66.5t103.5 42.5q53 18 77 36.5t18.5 34.5t-25 28.5t-51.5 23.5q-33 11 -49.5 48t-15 72.5t15.5 47.5q1 -31 8 -56.5 t14.5 -40.5t20.5 -28.5t21 -19t21.5 -13t16.5 -9.5z" />
+<glyph unicode="&#xf17d;" d="M1024 36q-42 241 -140 498h-2l-2 -1q-16 -6 -43 -16.5t-101 -49t-137 -82t-131 -114.5t-103 -148l-15 11q184 -150 418 -150q132 0 256 52zM839 643q-21 49 -53 111q-311 -93 -673 -93q-1 -7 -1 -21q0 -124 44 -236.5t124 -201.5q50 89 123.5 166.5t142.5 124.5t130.5 81 t99.5 48l37 13q4 1 13 3.5t13 4.5zM732 855q-120 213 -244 378q-138 -65 -234 -186t-128 -272q302 0 606 80zM1416 536q-210 60 -409 29q87 -239 128 -469q111 75 185 189.5t96 250.5zM611 1277q-1 0 -2 -1q1 1 2 1zM1201 1132q-185 164 -433 164q-76 0 -155 -19 q131 -170 246 -382q69 26 130 60.5t96.5 61.5t65.5 57t37.5 40.5zM1424 647q-3 232 -149 410l-1 -1q-9 -12 -19 -24.5t-43.5 -44.5t-71 -60.5t-100 -65t-131.5 -64.5q25 -53 44 -95q2 -6 6.5 -17.5t7.5 -16.5q36 5 74.5 7t73.5 2t69 -1.5t64 -4t56.5 -5.5t48 -6.5t36.5 -6 t25 -4.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf17e;" d="M1173 473q0 50 -19.5 91.5t-48.5 68.5t-73 49t-82.5 34t-87.5 23l-104 24q-30 7 -44 10.5t-35 11.5t-30 16t-16.5 21t-7.5 30q0 77 144 77q43 0 77 -12t54 -28.5t38 -33.5t40 -29t48 -12q47 0 75.5 32t28.5 77q0 55 -56 99.5t-142 67.5t-182 23q-68 0 -132 -15.5 t-119.5 -47t-89 -87t-33.5 -128.5q0 -61 19 -106.5t56 -75.5t80 -48.5t103 -32.5l146 -36q90 -22 112 -36q32 -20 32 -60q0 -39 -40 -64.5t-105 -25.5q-51 0 -91.5 16t-65 38.5t-45.5 45t-46 38.5t-54 16q-50 0 -75.5 -30t-25.5 -75q0 -92 122 -157.5t291 -65.5 q73 0 140 18.5t122.5 53.5t88.5 93.5t33 131.5zM1536 256q0 -159 -112.5 -271.5t-271.5 -112.5q-130 0 -234 80q-77 -16 -150 -16q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5q0 73 16 150q-80 104 -80 234q0 159 112.5 271.5t271.5 112.5q130 0 234 -80 q77 16 150 16q143 0 273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -73 -16 -150q80 -104 80 -234z" />
+<glyph unicode="&#xf180;" horiz-adv-x="1280" d="M1000 1102l37 194q5 23 -9 40t-35 17h-712q-23 0 -38.5 -17t-15.5 -37v-1101q0 -7 6 -1l291 352q23 26 38 33.5t48 7.5h239q22 0 37 14.5t18 29.5q24 130 37 191q4 21 -11.5 40t-36.5 19h-294q-29 0 -48 19t-19 48v42q0 29 19 47.5t48 18.5h346q18 0 35 13.5t20 29.5z M1227 1324q-15 -73 -53.5 -266.5t-69.5 -350t-35 -173.5q-6 -22 -9 -32.5t-14 -32.5t-24.5 -33t-38.5 -21t-58 -10h-271q-13 0 -22 -10q-8 -9 -426 -494q-22 -25 -58.5 -28.5t-48.5 5.5q-55 22 -55 98v1410q0 55 38 102.5t120 47.5h888q95 0 127 -53t10 -159zM1227 1324 l-158 -790q4 17 35 173.5t69.5 350t53.5 266.5z" />
+<glyph unicode="&#xf181;" d="M704 192v1024q0 14 -9 23t-23 9h-480q-14 0 -23 -9t-9 -23v-1024q0 -14 9 -23t23 -9h480q14 0 23 9t9 23zM1376 576v640q0 14 -9 23t-23 9h-480q-14 0 -23 -9t-9 -23v-640q0 -14 9 -23t23 -9h480q14 0 23 9t9 23zM1536 1344v-1408q0 -26 -19 -45t-45 -19h-1408 q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf182;" horiz-adv-x="1280" d="M1280 480q0 -40 -28 -68t-68 -28q-51 0 -80 43l-227 341h-45v-132l247 -411q9 -15 9 -33q0 -26 -19 -45t-45 -19h-192v-272q0 -46 -33 -79t-79 -33h-160q-46 0 -79 33t-33 79v272h-192q-26 0 -45 19t-19 45q0 18 9 33l247 411v132h-45l-227 -341q-29 -43 -80 -43 q-40 0 -68 28t-28 68q0 29 16 53l256 384q73 107 176 107h384q103 0 176 -107l256 -384q16 -24 16 -53zM864 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" />
+<glyph unicode="&#xf183;" horiz-adv-x="1024" d="M1024 832v-416q0 -40 -28 -68t-68 -28t-68 28t-28 68v352h-64v-912q0 -46 -33 -79t-79 -33t-79 33t-33 79v464h-64v-464q0 -46 -33 -79t-79 -33t-79 33t-33 79v912h-64v-352q0 -40 -28 -68t-68 -28t-68 28t-28 68v416q0 80 56 136t136 56h640q80 0 136 -56t56 -136z M736 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" />
+<glyph unicode="&#xf184;" d="M773 234l350 473q16 22 24.5 59t-6 85t-61.5 79q-40 26 -83 25.5t-73.5 -17.5t-54.5 -45q-36 -40 -96 -40q-59 0 -95 40q-24 28 -54.5 45t-73.5 17.5t-84 -25.5q-46 -31 -60.5 -79t-6 -85t24.5 -59zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf185;" horiz-adv-x="1792" d="M1472 640q0 117 -45.5 223.5t-123 184t-184 123t-223.5 45.5t-223.5 -45.5t-184 -123t-123 -184t-45.5 -223.5t45.5 -223.5t123 -184t184 -123t223.5 -45.5t223.5 45.5t184 123t123 184t45.5 223.5zM1748 363q-4 -15 -20 -20l-292 -96v-306q0 -16 -13 -26q-15 -10 -29 -4 l-292 94l-180 -248q-10 -13 -26 -13t-26 13l-180 248l-292 -94q-14 -6 -29 4q-13 10 -13 26v306l-292 96q-16 5 -20 20q-5 17 4 29l180 248l-180 248q-9 13 -4 29q4 15 20 20l292 96v306q0 16 13 26q15 10 29 4l292 -94l180 248q9 12 26 12t26 -12l180 -248l292 94 q14 6 29 -4q13 -10 13 -26v-306l292 -96q16 -5 20 -20q5 -16 -4 -29l-180 -248l180 -248q9 -12 4 -29z" />
+<glyph unicode="&#xf186;" d="M1262 233q-54 -9 -110 -9q-182 0 -337 90t-245 245t-90 337q0 192 104 357q-201 -60 -328.5 -229t-127.5 -384q0 -130 51 -248.5t136.5 -204t204 -136.5t248.5 -51q144 0 273.5 61.5t220.5 171.5zM1465 318q-94 -203 -283.5 -324.5t-413.5 -121.5q-156 0 -298 61 t-245 164t-164 245t-61 298q0 153 57.5 292.5t156 241.5t235.5 164.5t290 68.5q44 2 61 -39q18 -41 -15 -72q-86 -78 -131.5 -181.5t-45.5 -218.5q0 -148 73 -273t198 -198t273 -73q118 0 228 51q41 18 72 -13q14 -14 17.5 -34t-4.5 -38z" />
+<glyph unicode="&#xf187;" horiz-adv-x="1792" d="M1088 704q0 26 -19 45t-45 19h-256q-26 0 -45 -19t-19 -45t19 -45t45 -19h256q26 0 45 19t19 45zM1664 896v-960q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v960q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1728 1344v-256q0 -26 -19 -45t-45 -19h-1536 q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1536q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf188;" horiz-adv-x="1664" d="M1632 576q0 -26 -19 -45t-45 -19h-224q0 -171 -67 -290l208 -209q19 -19 19 -45t-19 -45q-18 -19 -45 -19t-45 19l-198 197q-5 -5 -15 -13t-42 -28.5t-65 -36.5t-82 -29t-97 -13v896h-128v-896q-51 0 -101.5 13.5t-87 33t-66 39t-43.5 32.5l-15 14l-183 -207 q-20 -21 -48 -21q-24 0 -43 16q-19 18 -20.5 44.5t15.5 46.5l202 227q-58 114 -58 274h-224q-26 0 -45 19t-19 45t19 45t45 19h224v294l-173 173q-19 19 -19 45t19 45t45 19t45 -19l173 -173h844l173 173q19 19 45 19t45 -19t19 -45t-19 -45l-173 -173v-294h224q26 0 45 -19 t19 -45zM1152 1152h-640q0 133 93.5 226.5t226.5 93.5t226.5 -93.5t93.5 -226.5z" />
+<glyph unicode="&#xf189;" horiz-adv-x="1920" d="M1917 1016q23 -64 -150 -294q-24 -32 -65 -85q-78 -100 -90 -131q-17 -41 14 -81q17 -21 81 -82h1l1 -1l1 -1l2 -2q141 -131 191 -221q3 -5 6.5 -12.5t7 -26.5t-0.5 -34t-25 -27.5t-59 -12.5l-256 -4q-24 -5 -56 5t-52 22l-20 12q-30 21 -70 64t-68.5 77.5t-61 58 t-56.5 15.5q-3 -1 -8 -3.5t-17 -14.5t-21.5 -29.5t-17 -52t-6.5 -77.5q0 -15 -3.5 -27.5t-7.5 -18.5l-4 -5q-18 -19 -53 -22h-115q-71 -4 -146 16.5t-131.5 53t-103 66t-70.5 57.5l-25 24q-10 10 -27.5 30t-71.5 91t-106 151t-122.5 211t-130.5 272q-6 16 -6 27t3 16l4 6 q15 19 57 19l274 2q12 -2 23 -6.5t16 -8.5l5 -3q16 -11 24 -32q20 -50 46 -103.5t41 -81.5l16 -29q29 -60 56 -104t48.5 -68.5t41.5 -38.5t34 -14t27 5q2 1 5 5t12 22t13.5 47t9.5 81t0 125q-2 40 -9 73t-14 46l-6 12q-25 34 -85 43q-13 2 5 24q17 19 38 30q53 26 239 24 q82 -1 135 -13q20 -5 33.5 -13.5t20.5 -24t10.5 -32t3.5 -45.5t-1 -55t-2.5 -70.5t-1.5 -82.5q0 -11 -1 -42t-0.5 -48t3.5 -40.5t11.5 -39t22.5 -24.5q8 -2 17 -4t26 11t38 34.5t52 67t68 107.5q60 104 107 225q4 10 10 17.5t11 10.5l4 3l5 2.5t13 3t20 0.5l288 2 q39 5 64 -2.5t31 -16.5z" />
+<glyph unicode="&#xf18a;" horiz-adv-x="1792" d="M675 252q21 34 11 69t-45 50q-34 14 -73 1t-60 -46q-22 -34 -13 -68.5t43 -50.5t74.5 -2.5t62.5 47.5zM769 373q8 13 3.5 26.5t-17.5 18.5q-14 5 -28.5 -0.5t-21.5 -18.5q-17 -31 13 -45q14 -5 29 0.5t22 18.5zM943 266q-45 -102 -158 -150t-224 -12 q-107 34 -147.5 126.5t6.5 187.5q47 93 151.5 139t210.5 19q111 -29 158.5 -119.5t2.5 -190.5zM1255 426q-9 96 -89 170t-208.5 109t-274.5 21q-223 -23 -369.5 -141.5t-132.5 -264.5q9 -96 89 -170t208.5 -109t274.5 -21q223 23 369.5 141.5t132.5 264.5zM1563 422 q0 -68 -37 -139.5t-109 -137t-168.5 -117.5t-226 -83t-270.5 -31t-275 33.5t-240.5 93t-171.5 151t-65 199.5q0 115 69.5 245t197.5 258q169 169 341.5 236t246.5 -7q65 -64 20 -209q-4 -14 -1 -20t10 -7t14.5 0.5t13.5 3.5l6 2q139 59 246 59t153 -61q45 -63 0 -178 q-2 -13 -4.5 -20t4.5 -12.5t12 -7.5t17 -6q57 -18 103 -47t80 -81.5t34 -116.5zM1489 1046q42 -47 54.5 -108.5t-6.5 -117.5q-8 -23 -29.5 -34t-44.5 -4q-23 8 -34 29.5t-4 44.5q20 63 -24 111t-107 35q-24 -5 -45 8t-25 37q-5 24 8 44.5t37 25.5q60 13 119 -5.5t101 -65.5z M1670 1209q87 -96 112.5 -222.5t-13.5 -241.5q-9 -27 -34 -40t-52 -4t-40 34t-5 52q28 82 10 172t-80 158q-62 69 -148 95.5t-173 8.5q-28 -6 -52 9.5t-30 43.5t9.5 51.5t43.5 29.5q123 26 244 -11.5t208 -134.5z" />
+<glyph unicode="&#xf18b;" d="M1133 -34q-171 -94 -368 -94q-196 0 -367 94q138 87 235.5 211t131.5 268q35 -144 132.5 -268t235.5 -211zM638 1394v-485q0 -252 -126.5 -459.5t-330.5 -306.5q-181 215 -181 495q0 187 83.5 349.5t229.5 269.5t325 137zM1536 638q0 -280 -181 -495 q-204 99 -330.5 306.5t-126.5 459.5v485q179 -30 325 -137t229.5 -269.5t83.5 -349.5z" />
+<glyph unicode="&#xf18c;" horiz-adv-x="1408" d="M1402 433q-32 -80 -76 -138t-91 -88.5t-99 -46.5t-101.5 -14.5t-96.5 8.5t-86.5 22t-69.5 27.5t-46 22.5l-17 10q-113 -228 -289.5 -359.5t-384.5 -132.5q-19 0 -32 13t-13 32t13 31.5t32 12.5q173 1 322.5 107.5t251.5 294.5q-36 -14 -72 -23t-83 -13t-91 2.5t-93 28.5 t-92 59t-84.5 100t-74.5 146q114 47 214 57t167.5 -7.5t124.5 -56.5t88.5 -77t56.5 -82q53 131 79 291q-7 -1 -18 -2.5t-46.5 -2.5t-69.5 0.5t-81.5 10t-88.5 23t-84 42.5t-75 65t-54.5 94.5t-28.5 127.5q70 28 133.5 36.5t112.5 -1t92 -30t73.5 -50t56 -61t42 -63t27.5 -56 t16 -39.5l4 -16q12 122 12 195q-8 6 -21.5 16t-49 44.5t-63.5 71.5t-54 93t-33 112.5t12 127t70 138.5q73 -25 127.5 -61.5t84.5 -76.5t48 -85t20.5 -89t-0.5 -85.5t-13 -76.5t-19 -62t-17 -42l-7 -15q1 -5 1 -50.5t-1 -71.5q3 7 10 18.5t30.5 43t50.5 58t71 55.5t91.5 44.5 t112 14.5t132.5 -24q-2 -78 -21.5 -141.5t-50 -104.5t-69.5 -71.5t-81.5 -45.5t-84.5 -24t-80 -9.5t-67.5 1t-46.5 4.5l-17 3q-23 -147 -73 -283q6 7 18 18.5t49.5 41t77.5 52.5t99.5 42t117.5 20t129 -23.5t137 -77.5z" />
+<glyph unicode="&#xf18d;" horiz-adv-x="1280" d="M1259 283v-66q0 -85 -57.5 -144.5t-138.5 -59.5h-57l-260 -269v269h-529q-81 0 -138.5 59.5t-57.5 144.5v66h1238zM1259 609v-255h-1238v255h1238zM1259 937v-255h-1238v255h1238zM1259 1077v-67h-1238v67q0 84 57.5 143.5t138.5 59.5h846q81 0 138.5 -59.5t57.5 -143.5z " />
+<glyph unicode="&#xf18e;" d="M1152 640q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v192h-352q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h352v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198 t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf190;" d="M1152 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-352v-192q0 -14 -9 -23t-23 -9q-12 0 -24 10l-319 319q-9 9 -9 23t9 23l320 320q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5v-192h352q13 0 22.5 -9.5t9.5 -22.5zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198 t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf191;" d="M1024 960v-640q0 -26 -19 -45t-45 -19q-20 0 -37 12l-448 320q-27 19 -27 52t27 52l448 320q17 12 37 12q26 0 45 -19t19 -45zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5z M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf192;" d="M1024 640q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5 t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf193;" horiz-adv-x="1664" d="M1023 349l102 -204q-58 -179 -210 -290t-339 -111q-156 0 -288.5 77.5t-210 210t-77.5 288.5q0 181 104.5 330t274.5 211l17 -131q-122 -54 -195 -165.5t-73 -244.5q0 -185 131.5 -316.5t316.5 -131.5q126 0 232.5 65t165 175.5t49.5 236.5zM1571 249l58 -114l-256 -128 q-13 -7 -29 -7q-40 0 -57 35l-239 477h-472q-24 0 -42.5 16.5t-21.5 40.5l-96 779q-2 16 6 42q14 51 57 82.5t97 31.5q66 0 113 -47t47 -113q0 -69 -52 -117.5t-120 -41.5l37 -289h423v-128h-407l16 -128h455q40 0 57 -35l228 -455z" />
+<glyph unicode="&#xf194;" d="M1292 898q10 216 -161 222q-231 8 -312 -261q44 19 82 19q85 0 74 -96q-4 -57 -74 -167t-105 -110q-43 0 -82 169q-13 54 -45 255q-30 189 -160 177q-59 -7 -164 -100l-81 -72l-81 -72l52 -67q76 52 87 52q57 0 107 -179q15 -55 45 -164.5t45 -164.5q68 -179 164 -179 q157 0 383 294q220 283 226 444zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf195;" horiz-adv-x="1152" d="M1152 704q0 -191 -94.5 -353t-256.5 -256.5t-353 -94.5h-160q-14 0 -23 9t-9 23v611l-215 -66q-3 -1 -9 -1q-10 0 -19 6q-13 10 -13 26v128q0 23 23 31l233 71v93l-215 -66q-3 -1 -9 -1q-10 0 -19 6q-13 10 -13 26v128q0 23 23 31l233 71v250q0 14 9 23t23 9h160 q14 0 23 -9t9 -23v-181l375 116q15 5 28 -5t13 -26v-128q0 -23 -23 -31l-393 -121v-93l375 116q15 5 28 -5t13 -26v-128q0 -23 -23 -31l-393 -121v-487q188 13 318 151t130 328q0 14 9 23t23 9h160q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf196;" horiz-adv-x="1408" d="M1152 736v-64q0 -14 -9 -23t-23 -9h-352v-352q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v352h-352q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h352v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-352h352q14 0 23 -9t9 -23zM1280 288v832q0 66 -47 113t-113 47h-832 q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113zM1408 1120v-832q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf197;" horiz-adv-x="2176" d="M620 416q-110 -64 -268 -64h-128v64h-64q-13 0 -22.5 23.5t-9.5 56.5q0 24 7 49q-58 2 -96.5 10.5t-38.5 20.5t38.5 20.5t96.5 10.5q-7 25 -7 49q0 33 9.5 56.5t22.5 23.5h64v64h128q158 0 268 -64h1113q42 -7 106.5 -18t80.5 -14q89 -15 150 -40.5t83.5 -47.5t22.5 -40 t-22.5 -40t-83.5 -47.5t-150 -40.5q-16 -3 -80.5 -14t-106.5 -18h-1113zM1739 668q53 -36 53 -92t-53 -92l81 -30q68 48 68 122t-68 122zM625 400h1015q-217 -38 -456 -80q-57 0 -113 -24t-83 -48l-28 -24l-288 -288q-26 -26 -70.5 -45t-89.5 -19h-96l-93 464h29 q157 0 273 64zM352 816h-29l93 464h96q46 0 90 -19t70 -45l288 -288q4 -4 11 -10.5t30.5 -23t48.5 -29t61.5 -23t72.5 -10.5l456 -80h-1015q-116 64 -273 64z" />
+<glyph unicode="&#xf198;" horiz-adv-x="1664" d="M1519 760q62 0 103.5 -40.5t41.5 -101.5q0 -97 -93 -130l-172 -59l56 -167q7 -21 7 -47q0 -59 -42 -102t-101 -43q-47 0 -85.5 27t-53.5 72l-55 165l-310 -106l55 -164q8 -24 8 -47q0 -59 -42 -102t-102 -43q-47 0 -85 27t-53 72l-55 163l-153 -53q-29 -9 -50 -9 q-61 0 -101.5 40t-40.5 101q0 47 27.5 85t71.5 53l156 53l-105 313l-156 -54q-26 -8 -48 -8q-60 0 -101 40.5t-41 100.5q0 47 27.5 85t71.5 53l157 53l-53 159q-8 24 -8 47q0 60 42 102.5t102 42.5q47 0 85 -27t53 -72l54 -160l310 105l-54 160q-8 24 -8 47q0 59 42.5 102 t101.5 43q47 0 85.5 -27.5t53.5 -71.5l53 -161l162 55q21 6 43 6q60 0 102.5 -39.5t42.5 -98.5q0 -45 -30 -81.5t-74 -51.5l-157 -54l105 -316l164 56q24 8 46 8zM725 498l310 105l-105 315l-310 -107z" />
+<glyph unicode="&#xf199;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM1280 352v436q-31 -35 -64 -55q-34 -22 -132.5 -85t-151.5 -99q-98 -69 -164 -69v0v0q-66 0 -164 69 q-46 32 -141.5 92.5t-142.5 92.5q-12 8 -33 27t-31 27v-436q0 -40 28 -68t68 -28h832q40 0 68 28t28 68zM1280 925q0 41 -27.5 70t-68.5 29h-832q-40 0 -68 -28t-28 -68q0 -37 30.5 -76.5t67.5 -64.5q47 -32 137.5 -89t129.5 -83q3 -2 17 -11.5t21 -14t21 -13t23.5 -13 t21.5 -9.5t22.5 -7.5t20.5 -2.5t20.5 2.5t22.5 7.5t21.5 9.5t23.5 13t21 13t21 14t17 11.5l267 174q35 23 66.5 62.5t31.5 73.5z" />
+<glyph unicode="&#xf19a;" horiz-adv-x="1792" d="M127 640q0 163 67 313l367 -1005q-196 95 -315 281t-119 411zM1415 679q0 -19 -2.5 -38.5t-10 -49.5t-11.5 -44t-17.5 -59t-17.5 -58l-76 -256l-278 826q46 3 88 8q19 2 26 18.5t-2.5 31t-28.5 13.5l-205 -10q-75 1 -202 10q-12 1 -20.5 -5t-11.5 -15t-1.5 -18.5t9 -16.5 t19.5 -8l80 -8l120 -328l-168 -504l-280 832q46 3 88 8q19 2 26 18.5t-2.5 31t-28.5 13.5l-205 -10q-7 0 -23 0.5t-26 0.5q105 160 274.5 253.5t367.5 93.5q147 0 280.5 -53t238.5 -149h-10q-55 0 -92 -40.5t-37 -95.5q0 -12 2 -24t4 -21.5t8 -23t9 -21t12 -22.5t12.5 -21 t14.5 -24t14 -23q63 -107 63 -212zM909 573l237 -647q1 -6 5 -11q-126 -44 -255 -44q-112 0 -217 32zM1570 1009q95 -174 95 -369q0 -209 -104 -385.5t-279 -278.5l235 678q59 169 59 276q0 42 -6 79zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286 t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM896 -215q173 0 331.5 68t273 182.5t182.5 273t68 331.5t-68 331.5t-182.5 273t-273 182.5t-331.5 68t-331.5 -68t-273 -182.5t-182.5 -273t-68 -331.5t68 -331.5t182.5 -273 t273 -182.5t331.5 -68z" />
+<glyph unicode="&#xf19b;" horiz-adv-x="1792" d="M1086 1536v-1536l-272 -128q-228 20 -414 102t-293 208.5t-107 272.5q0 140 100.5 263.5t275 205.5t391.5 108v-172q-217 -38 -356.5 -150t-139.5 -255q0 -152 154.5 -267t388.5 -145v1360zM1755 954l37 -390l-525 114l147 83q-119 70 -280 99v172q277 -33 481 -157z" />
+<glyph unicode="&#xf19c;" horiz-adv-x="2048" d="M960 1536l960 -384v-128h-128q0 -26 -20.5 -45t-48.5 -19h-1526q-28 0 -48.5 19t-20.5 45h-128v128zM256 896h256v-768h128v768h256v-768h128v768h256v-768h128v768h256v-768h59q28 0 48.5 -19t20.5 -45v-64h-1664v64q0 26 20.5 45t48.5 19h59v768zM1851 -64 q28 0 48.5 -19t20.5 -45v-128h-1920v128q0 26 20.5 45t48.5 19h1782z" />
+<glyph unicode="&#xf19d;" horiz-adv-x="2304" d="M1774 700l18 -316q4 -69 -82 -128t-235 -93.5t-323 -34.5t-323 34.5t-235 93.5t-82 128l18 316l574 -181q22 -7 48 -7t48 7zM2304 1024q0 -23 -22 -31l-1120 -352q-4 -1 -10 -1t-10 1l-652 206q-43 -34 -71 -111.5t-34 -178.5q63 -36 63 -109q0 -69 -58 -107l58 -433 q2 -14 -8 -25q-9 -11 -24 -11h-192q-15 0 -24 11q-10 11 -8 25l58 433q-58 38 -58 107q0 73 65 111q11 207 98 330l-333 104q-22 8 -22 31t22 31l1120 352q4 1 10 1t10 -1l1120 -352q22 -8 22 -31z" />
+<glyph unicode="&#xf19e;" d="M859 579l13 -707q-62 11 -105 11q-41 0 -105 -11l13 707q-40 69 -168.5 295.5t-216.5 374.5t-181 287q58 -15 108 -15q43 0 111 15q63 -111 133.5 -229.5t167 -276.5t138.5 -227q37 61 109.5 177.5t117.5 190t105 176t107 189.5q54 -14 107 -14q56 0 114 14v0 q-28 -39 -60 -88.5t-49.5 -78.5t-56.5 -96t-49 -84q-146 -248 -353 -610z" />
+<glyph unicode="&#xf1a0;" d="M768 750h725q12 -67 12 -128q0 -217 -91 -387.5t-259.5 -266.5t-386.5 -96q-157 0 -299 60.5t-245 163.5t-163.5 245t-60.5 299t60.5 299t163.5 245t245 163.5t299 60.5q300 0 515 -201l-209 -201q-123 119 -306 119q-129 0 -238.5 -65t-173.5 -176.5t-64 -243.5 t64 -243.5t173.5 -176.5t238.5 -65q87 0 160 24t120 60t82 82t51.5 87t22.5 78h-436v264z" />
+<glyph unicode="&#xf1a1;" horiz-adv-x="1792" d="M1095 369q16 -16 0 -31q-62 -62 -199 -62t-199 62q-16 15 0 31q6 6 15 6t15 -6q48 -49 169 -49q120 0 169 49q6 6 15 6t15 -6zM788 550q0 -37 -26 -63t-63 -26t-63.5 26t-26.5 63q0 38 26.5 64t63.5 26t63 -26.5t26 -63.5zM1183 550q0 -37 -26.5 -63t-63.5 -26t-63 26 t-26 63t26 63.5t63 26.5t63.5 -26t26.5 -64zM1434 670q0 49 -35 84t-85 35t-86 -36q-130 90 -311 96l63 283l200 -45q0 -37 26 -63t63 -26t63.5 26.5t26.5 63.5t-26.5 63.5t-63.5 26.5q-54 0 -80 -50l-221 49q-19 5 -25 -16l-69 -312q-180 -7 -309 -97q-35 37 -87 37 q-50 0 -85 -35t-35 -84q0 -35 18.5 -64t49.5 -44q-6 -27 -6 -56q0 -142 140 -243t337 -101q198 0 338 101t140 243q0 32 -7 57q30 15 48 43.5t18 63.5zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191 t348 71t348 -71t286 -191t191 -286t71 -348z" />
+<glyph unicode="&#xf1a2;" d="M939 407q13 -13 0 -26q-53 -53 -171 -53t-171 53q-13 13 0 26q5 6 13 6t13 -6q42 -42 145 -42t145 42q5 6 13 6t13 -6zM676 563q0 -31 -23 -54t-54 -23t-54 23t-23 54q0 32 22.5 54.5t54.5 22.5t54.5 -22.5t22.5 -54.5zM1014 563q0 -31 -23 -54t-54 -23t-54 23t-23 54 q0 32 22.5 54.5t54.5 22.5t54.5 -22.5t22.5 -54.5zM1229 666q0 42 -30 72t-73 30q-42 0 -73 -31q-113 78 -267 82l54 243l171 -39q1 -32 23.5 -54t53.5 -22q32 0 54.5 22.5t22.5 54.5t-22.5 54.5t-54.5 22.5q-48 0 -69 -43l-189 42q-17 5 -21 -13l-60 -268q-154 -6 -265 -83 q-30 32 -74 32q-43 0 -73 -30t-30 -72q0 -30 16 -55t42 -38q-5 -25 -5 -48q0 -122 120 -208.5t289 -86.5q170 0 290 86.5t120 208.5q0 25 -6 49q25 13 40.5 37.5t15.5 54.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960 q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf1a3;" d="M866 697l90 27v62q0 79 -58 135t-138 56t-138 -55.5t-58 -134.5v-283q0 -20 -14 -33.5t-33 -13.5t-32.5 13.5t-13.5 33.5v120h-151v-122q0 -82 57.5 -139t139.5 -57q81 0 138.5 56.5t57.5 136.5v280q0 19 13.5 33t33.5 14q19 0 32.5 -14t13.5 -33v-54zM1199 502v122h-150 v-126q0 -20 -13.5 -33.5t-33.5 -13.5q-19 0 -32.5 14t-13.5 33v123l-90 -26l-60 28v-123q0 -80 58 -137t139 -57t138.5 57t57.5 139zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103 t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf1a4;" horiz-adv-x="1920" d="M1062 824v118q0 42 -30 72t-72 30t-72 -30t-30 -72v-612q0 -175 -126 -299t-303 -124q-178 0 -303.5 125.5t-125.5 303.5v266h328v-262q0 -43 30 -72.5t72 -29.5t72 29.5t30 72.5v620q0 171 126.5 292t301.5 121q176 0 302 -122t126 -294v-136l-195 -58zM1592 602h328 v-266q0 -178 -125.5 -303.5t-303.5 -125.5q-177 0 -303 124.5t-126 300.5v268l131 -61l195 58v-270q0 -42 30 -71.5t72 -29.5t72 29.5t30 71.5v275z" />
+<glyph unicode="&#xf1a5;" d="M1472 160v480h-704v704h-480q-93 0 -158.5 -65.5t-65.5 -158.5v-480h704v-704h480q93 0 158.5 65.5t65.5 158.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 t84.5 -203.5z" />
+<glyph unicode="&#xf1a6;" horiz-adv-x="2048" d="M328 1254h204v-983h-532v697h328v286zM328 435v369h-123v-369h123zM614 968v-697h205v697h-205zM614 1254v-204h205v204h-205zM901 968h533v-942h-533v163h328v82h-328v697zM1229 435v369h-123v-369h123zM1516 968h532v-942h-532v163h327v82h-327v697zM1843 435v369h-123 v-369h123z" />
+<glyph unicode="&#xf1a7;" d="M1046 516q0 -64 -38 -109t-91 -45q-43 0 -70 15v277q28 17 70 17q53 0 91 -45.5t38 -109.5zM703 944q0 -64 -38 -109.5t-91 -45.5q-43 0 -70 15v277q28 17 70 17q53 0 91 -45t38 -109zM1265 513q0 134 -88 229t-213 95q-20 0 -39 -3q-23 -78 -78 -136q-87 -95 -211 -101 v-636l211 41v206q51 -19 117 -19q125 0 213 95t88 229zM922 940q0 134 -88.5 229t-213.5 95q-74 0 -141 -36h-186v-840l211 41v206q55 -19 116 -19q125 0 213.5 95t88.5 229zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960 q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf1a8;" horiz-adv-x="2038" d="M1222 607q75 3 143.5 -20.5t118 -58.5t101 -94.5t84 -108t75.5 -120.5q33 -56 78.5 -109t75.5 -80.5t99 -88.5q-48 -30 -108.5 -57.5t-138.5 -59t-114 -47.5q-44 37 -74 115t-43.5 164.5t-33 180.5t-42.5 168.5t-72.5 123t-122.5 48.5l-10 -2l-6 -4q4 -5 13 -14 q6 -5 28 -23.5t25.5 -22t19 -18t18 -20.5t11.5 -21t10.5 -27.5t4.5 -31t4 -40.5l1 -33q1 -26 -2.5 -57.5t-7.5 -52t-12.5 -58.5t-11.5 -53q-35 1 -101 -9.5t-98 -10.5q-39 0 -72 10q-2 16 -2 47q0 74 3 96q2 13 31.5 41.5t57 59t26.5 51.5q-24 2 -43 -24 q-36 -53 -111.5 -99.5t-136.5 -46.5q-25 0 -75.5 63t-106.5 139.5t-84 96.5q-6 4 -27 30q-482 -112 -513 -112q-16 0 -28 11t-12 27q0 15 8.5 26.5t22.5 14.5l486 106q-8 14 -8 25t5.5 17.5t16 11.5t20 7t23 4.5t18.5 4.5q4 1 15.5 7.5t17.5 6.5q15 0 28 -16t20 -33 q163 37 172 37q17 0 29.5 -11t12.5 -28q0 -15 -8.5 -26t-23.5 -14l-182 -40l-1 -16q-1 -26 81.5 -117.5t104.5 -91.5q47 0 119 80t72 129q0 36 -23.5 53t-51 18.5t-51 11.5t-23.5 34q0 16 10 34l-68 19q43 44 43 117q0 26 -5 58q82 16 144 16q44 0 71.5 -1.5t48.5 -8.5 t31 -13.5t20.5 -24.5t15.5 -33.5t17 -47.5t24 -60l50 25q-3 -40 -23 -60t-42.5 -21t-40 -6.5t-16.5 -20.5zM1282 842q-5 5 -13.5 15.5t-12 14.5t-10.5 11.5t-10 10.5l-8 8t-8.5 7.5t-8 5t-8.5 4.5q-7 3 -14.5 5t-20.5 2.5t-22 0.5h-32.5h-37.5q-126 0 -217 -43 q16 30 36 46.5t54 29.5t65.5 36t46 36.5t50 55t43.5 50.5q12 -9 28 -31.5t32 -36.5t38 -13l12 1v-76l22 -1q247 95 371 190q28 21 50 39t42.5 37.5t33 31t29.5 34t24 31t24.5 37t23 38t27 47.5t29.5 53l7 9q-2 -53 -43 -139q-79 -165 -205 -264t-306 -142q-14 -3 -42 -7.5 t-50 -9.5t-39 -14q3 -19 24.5 -46t21.5 -34q0 -11 -26 -30zM1061 -79q39 26 131.5 47.5t146.5 21.5q9 0 22.5 -15.5t28 -42.5t26 -50t24 -51t14.5 -33q-121 -45 -244 -45q-61 0 -125 11zM822 568l48 12l109 -177l-73 -48zM1323 51q3 -15 3 -16q0 -7 -17.5 -14.5t-46 -13 t-54 -9.5t-53.5 -7.5t-32 -4.5l-7 43q21 2 60.5 8.5t72 10t60.5 3.5h14zM866 679l-96 -20l-6 17q10 1 32.5 7t34.5 6q19 0 35 -10zM1061 45h31l10 -83l-41 -12v95zM1950 1535v1v-1zM1950 1535l-1 -5l-2 -2l1 3zM1950 1535l1 1z" />
+<glyph unicode="&#xf1a9;" d="M1167 -50q-5 19 -24 5q-30 -22 -87 -39t-131 -17q-129 0 -193 49q-5 4 -13 4q-11 0 -26 -12q-7 -6 -7.5 -16t7.5 -20q34 -32 87.5 -46t102.5 -12.5t99 4.5q41 4 84.5 20.5t65 30t28.5 20.5q12 12 7 29zM1128 65q-19 47 -39 61q-23 15 -76 15q-47 0 -71 -10 q-29 -12 -78 -56q-26 -24 -12 -44q9 -8 17.5 -4.5t31.5 23.5q3 2 10.5 8.5t10.5 8.5t10 7t11.5 7t12.5 5t15 4.5t16.5 2.5t20.5 1q27 0 44.5 -7.5t23 -14.5t13.5 -22q10 -17 12.5 -20t12.5 1q23 12 14 34zM1483 346q0 22 -5 44.5t-16.5 45t-34 36.5t-52.5 14 q-33 0 -97 -41.5t-129 -83.5t-101 -42q-27 -1 -63.5 19t-76 49t-83.5 58t-100 49t-111 19q-115 -1 -197 -78.5t-84 -178.5q-2 -112 74 -164q29 -20 62.5 -28.5t103.5 -8.5q57 0 132 32.5t134 71t120 70.5t93 31q26 -1 65 -31.5t71.5 -67t68 -67.5t55.5 -32q35 -3 58.5 14 t55.5 63q28 41 42.5 101t14.5 106zM1536 506q0 -164 -62 -304.5t-166 -236t-242.5 -149.5t-290.5 -54t-293 57.5t-247.5 157t-170.5 241.5t-64 302q0 89 19.5 172.5t49 145.5t70.5 118.5t78.5 94t78.5 69.5t64.5 46.5t42.5 24.5q14 8 51 26.5t54.5 28.5t48 30t60.5 44 q36 28 58 72.5t30 125.5q129 -155 186 -193q44 -29 130 -68t129 -66q21 -13 39 -25t60.5 -46.5t76 -70.5t75 -95t69 -122t47 -148.5t19.5 -177.5z" />
+<glyph unicode="&#xf1aa;" d="M1070 463l-160 -160l-151 -152l-30 -30q-65 -64 -151.5 -87t-171.5 -2q-16 -70 -72 -115t-129 -45q-85 0 -145 60.5t-60 145.5q0 72 44.5 128t113.5 72q-22 86 1 173t88 152l12 12l151 -152l-11 -11q-37 -37 -37 -89t37 -90q37 -37 89 -37t89 37l30 30l151 152l161 160z M729 1145l12 -12l-152 -152l-12 12q-37 37 -89 37t-89 -37t-37 -89.5t37 -89.5l29 -29l152 -152l160 -160l-151 -152l-161 160l-151 152l-30 30q-68 67 -90 159.5t5 179.5q-70 15 -115 71t-45 129q0 85 60 145.5t145 60.5q76 0 133.5 -49t69.5 -123q84 20 169.5 -3.5 t149.5 -87.5zM1536 78q0 -85 -60 -145.5t-145 -60.5q-74 0 -131 47t-71 118q-86 -28 -179.5 -6t-161.5 90l-11 12l151 152l12 -12q37 -37 89 -37t89 37t37 89t-37 89l-30 30l-152 152l-160 160l152 152l160 -160l152 -152l29 -30q64 -64 87.5 -150.5t2.5 -171.5 q76 -11 126.5 -68.5t50.5 -134.5zM1534 1202q0 -77 -51 -135t-127 -69q26 -85 3 -176.5t-90 -158.5l-12 -12l-151 152l12 12q37 37 37 89t-37 89t-89 37t-89 -37l-30 -30l-152 -152l-160 -160l-152 152l161 160l152 152l29 30q67 67 159 89.5t178 -3.5q11 75 68.5 126 t135.5 51q85 0 145 -60.5t60 -145.5z" />
+<glyph unicode="&#xf1ab;" d="M654 458q-1 -3 -12.5 0.5t-31.5 11.5l-20 9q-44 20 -87 49q-7 5 -41 31.5t-38 28.5q-67 -103 -134 -181q-81 -95 -105 -110q-4 -2 -19.5 -4t-18.5 0q6 4 82 92q21 24 85.5 115t78.5 118q17 30 51 98.5t36 77.5q-8 1 -110 -33q-8 -2 -27.5 -7.5t-34.5 -9.5t-17 -5 q-2 -2 -2 -10.5t-1 -9.5q-5 -10 -31 -15q-23 -7 -47 0q-18 4 -28 21q-4 6 -5 23q6 2 24.5 5t29.5 6q58 16 105 32q100 35 102 35q10 2 43 19.5t44 21.5q9 3 21.5 8t14.5 5.5t6 -0.5q2 -12 -1 -33q0 -2 -12.5 -27t-26.5 -53.5t-17 -33.5q-25 -50 -77 -131l64 -28 q12 -6 74.5 -32t67.5 -28q4 -1 10.5 -25.5t4.5 -30.5zM449 944q3 -15 -4 -28q-12 -23 -50 -38q-30 -12 -60 -12q-26 3 -49 26q-14 15 -18 41l1 3q3 -3 19.5 -5t26.5 0t58 16q36 12 55 14q17 0 21 -17zM1147 815l63 -227l-139 42zM39 15l694 232v1032l-694 -233v-1031z M1280 332l102 -31l-181 657l-100 31l-216 -536l102 -31l45 110l211 -65zM777 1294l573 -184v380zM1088 -29l158 -13l-54 -160l-40 66q-130 -83 -276 -108q-58 -12 -91 -12h-84q-79 0 -199.5 39t-183.5 85q-8 7 -8 16q0 8 5 13.5t13 5.5q4 0 18 -7.5t30.5 -16.5t20.5 -11 q73 -37 159.5 -61.5t157.5 -24.5q95 0 167 14.5t157 50.5q15 7 30.5 15.5t34 19t28.5 16.5zM1536 1050v-1079l-774 246q-14 -6 -375 -127.5t-368 -121.5q-13 0 -18 13q0 1 -1 3v1078q3 9 4 10q5 6 20 11q106 35 149 50v384l558 -198q2 0 160.5 55t316 108.5t161.5 53.5 q20 0 20 -21v-418z" />
+<glyph unicode="&#xf1ac;" horiz-adv-x="1792" d="M288 1152q66 0 113 -47t47 -113v-1088q0 -66 -47 -113t-113 -47h-128q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h128zM1664 989q58 -34 93 -93t35 -128v-768q0 -106 -75 -181t-181 -75h-864q-66 0 -113 47t-47 113v1536q0 40 28 68t68 28h672q40 0 88 -20t76 -48 l152 -152q28 -28 48 -76t20 -88v-163zM928 0v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM928 256v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM928 512v128q0 14 -9 23 t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1184 0v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1184 256v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128 q14 0 23 9t9 23zM1184 512v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1440 0v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1440 256v128q0 14 -9 23t-23 9h-128 q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1440 512v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1536 896v256h-160q-40 0 -68 28t-28 68v160h-640v-512h896z" />
+<glyph unicode="&#xf1ad;" d="M1344 1536q26 0 45 -19t19 -45v-1664q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1664q0 26 19 45t45 19h1280zM512 1248v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM512 992v-64q0 -14 9 -23t23 -9h64q14 0 23 9 t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM512 736v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM512 480v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM384 160v64 q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM384 416v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM384 672v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64 q14 0 23 9t9 23zM384 928v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM384 1184v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 -96v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9 t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM896 416v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 672v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 928v64 q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 1184v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 160v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64 q14 0 23 9t9 23zM1152 416v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 672v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 928v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9 t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 1184v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23z" />
+<glyph unicode="&#xf1ae;" horiz-adv-x="1280" d="M1188 988l-292 -292v-824q0 -46 -33 -79t-79 -33t-79 33t-33 79v384h-64v-384q0 -46 -33 -79t-79 -33t-79 33t-33 79v824l-292 292q-28 28 -28 68t28 68t68 28t68 -28l228 -228h368l228 228q28 28 68 28t68 -28t28 -68t-28 -68zM864 1152q0 -93 -65.5 -158.5 t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" />
+<glyph unicode="&#xf1b0;" horiz-adv-x="1664" d="M780 1064q0 -60 -19 -113.5t-63 -92.5t-105 -39q-76 0 -138 57.5t-92 135.5t-30 151q0 60 19 113.5t63 92.5t105 39q77 0 138.5 -57.5t91.5 -135t30 -151.5zM438 581q0 -80 -42 -139t-119 -59q-76 0 -141.5 55.5t-100.5 133.5t-35 152q0 80 42 139.5t119 59.5 q76 0 141.5 -55.5t100.5 -134t35 -152.5zM832 608q118 0 255 -97.5t229 -237t92 -254.5q0 -46 -17 -76.5t-48.5 -45t-64.5 -20t-76 -5.5q-68 0 -187.5 45t-182.5 45q-66 0 -192.5 -44.5t-200.5 -44.5q-183 0 -183 146q0 86 56 191.5t139.5 192.5t187.5 146t193 59zM1071 819 q-61 0 -105 39t-63 92.5t-19 113.5q0 74 30 151.5t91.5 135t138.5 57.5q61 0 105 -39t63 -92.5t19 -113.5q0 -73 -30 -151t-92 -135.5t-138 -57.5zM1503 923q77 0 119 -59.5t42 -139.5q0 -74 -35 -152t-100.5 -133.5t-141.5 -55.5q-77 0 -119 59t-42 139q0 74 35 152.5 t100.5 134t141.5 55.5z" />
+<glyph unicode="&#xf1b1;" horiz-adv-x="768" d="M704 1008q0 -145 -57 -243.5t-152 -135.5l45 -821q2 -26 -16 -45t-44 -19h-192q-26 0 -44 19t-16 45l45 821q-95 37 -152 135.5t-57 243.5q0 128 42.5 249.5t117.5 200t160 78.5t160 -78.5t117.5 -200t42.5 -249.5z" />
+<glyph unicode="&#xf1b2;" horiz-adv-x="1792" d="M896 -93l640 349v636l-640 -233v-752zM832 772l698 254l-698 254l-698 -254zM1664 1024v-768q0 -35 -18 -65t-49 -47l-704 -384q-28 -16 -61 -16t-61 16l-704 384q-31 17 -49 47t-18 65v768q0 40 23 73t61 47l704 256q22 8 44 8t44 -8l704 -256q38 -14 61 -47t23 -73z " />
+<glyph unicode="&#xf1b3;" horiz-adv-x="2304" d="M640 -96l384 192v314l-384 -164v-342zM576 358l404 173l-404 173l-404 -173zM1664 -96l384 192v314l-384 -164v-342zM1600 358l404 173l-404 173l-404 -173zM1152 651l384 165v266l-384 -164v-267zM1088 1030l441 189l-441 189l-441 -189zM2176 512v-416q0 -36 -19 -67 t-52 -47l-448 -224q-25 -14 -57 -14t-57 14l-448 224q-5 2 -7 4q-2 -2 -7 -4l-448 -224q-25 -14 -57 -14t-57 14l-448 224q-33 16 -52 47t-19 67v416q0 38 21.5 70t56.5 48l434 186v400q0 38 21.5 70t56.5 48l448 192q23 10 50 10t50 -10l448 -192q35 -16 56.5 -48t21.5 -70 v-400l434 -186q36 -16 57 -48t21 -70z" />
+<glyph unicode="&#xf1b4;" horiz-adv-x="2048" d="M1848 1197h-511v-124h511v124zM1596 771q-90 0 -146 -52.5t-62 -142.5h408q-18 195 -200 195zM1612 186q63 0 122 32t76 87h221q-100 -307 -427 -307q-214 0 -340.5 132t-126.5 347q0 208 130.5 345.5t336.5 137.5q138 0 240.5 -68t153 -179t50.5 -248q0 -17 -2 -47h-658 q0 -111 57.5 -171.5t166.5 -60.5zM277 236h296q205 0 205 167q0 180 -199 180h-302v-347zM277 773h281q78 0 123.5 36.5t45.5 113.5q0 144 -190 144h-260v-294zM0 1282h594q87 0 155 -14t126.5 -47.5t90 -96.5t31.5 -154q0 -181 -172 -263q114 -32 172 -115t58 -204 q0 -75 -24.5 -136.5t-66 -103.5t-98.5 -71t-121 -42t-134 -13h-611v1260z" />
+<glyph unicode="&#xf1b5;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM499 1041h-371v-787h382q117 0 197 57.5t80 170.5q0 158 -143 200q107 52 107 164q0 57 -19.5 96.5 t-56.5 60.5t-79 29.5t-97 8.5zM477 723h-176v184h163q119 0 119 -90q0 -94 -106 -94zM486 388h-185v217h189q124 0 124 -113q0 -104 -128 -104zM1136 356q-68 0 -104 38t-36 107h411q1 10 1 30q0 132 -74.5 220.5t-203.5 88.5q-128 0 -210 -86t-82 -216q0 -135 79 -217 t213 -82q205 0 267 191h-138q-11 -34 -47.5 -54t-75.5 -20zM1126 722q113 0 124 -122h-254q4 56 39 89t91 33zM964 988h319v-77h-319v77z" />
+<glyph unicode="&#xf1b6;" horiz-adv-x="1792" d="M1582 954q0 -101 -71.5 -172.5t-172.5 -71.5t-172.5 71.5t-71.5 172.5t71.5 172.5t172.5 71.5t172.5 -71.5t71.5 -172.5zM812 212q0 104 -73 177t-177 73q-27 0 -54 -6l104 -42q77 -31 109.5 -106.5t1.5 -151.5q-31 -77 -107 -109t-152 -1q-21 8 -62 24.5t-61 24.5 q32 -60 91 -96.5t130 -36.5q104 0 177 73t73 177zM1642 953q0 126 -89.5 215.5t-215.5 89.5q-127 0 -216.5 -89.5t-89.5 -215.5q0 -127 89.5 -216t216.5 -89q126 0 215.5 89t89.5 216zM1792 953q0 -189 -133.5 -322t-321.5 -133l-437 -319q-12 -129 -109 -218t-229 -89 q-121 0 -214 76t-118 192l-230 92v429l389 -157q79 48 173 48q13 0 35 -2l284 407q2 187 135.5 319t320.5 132q188 0 321.5 -133.5t133.5 -321.5z" />
+<glyph unicode="&#xf1b7;" d="M1242 889q0 80 -57 136.5t-137 56.5t-136.5 -57t-56.5 -136q0 -80 56.5 -136.5t136.5 -56.5t137 56.5t57 136.5zM632 301q0 -83 -58 -140.5t-140 -57.5q-56 0 -103 29t-72 77q52 -20 98 -40q60 -24 120 1.5t85 86.5q24 60 -1.5 120t-86.5 84l-82 33q22 5 42 5 q82 0 140 -57.5t58 -140.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v153l172 -69q20 -92 93.5 -152t168.5 -60q104 0 181 70t87 173l345 252q150 0 255.5 105.5t105.5 254.5q0 150 -105.5 255.5t-255.5 105.5 q-148 0 -253 -104.5t-107 -252.5l-225 -322q-9 1 -28 1q-75 0 -137 -37l-297 119v468q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5zM1289 887q0 -100 -71 -170.5t-171 -70.5t-170.5 70.5t-70.5 170.5t70.5 171t170.5 71q101 0 171.5 -70.5t70.5 -171.5z " />
+<glyph unicode="&#xf1b8;" horiz-adv-x="1792" d="M836 367l-15 -368l-2 -22l-420 29q-36 3 -67 31.5t-47 65.5q-11 27 -14.5 55t4 65t12 55t21.5 64t19 53q78 -12 509 -28zM449 953l180 -379l-147 92q-63 -72 -111.5 -144.5t-72.5 -125t-39.5 -94.5t-18.5 -63l-4 -21l-190 357q-17 26 -18 56t6 47l8 18q35 63 114 188 l-140 86zM1680 436l-188 -359q-12 -29 -36.5 -46.5t-43.5 -20.5l-18 -4q-71 -7 -219 -12l8 -164l-230 367l211 362l7 -173q170 -16 283 -5t170 33zM895 1360q-47 -63 -265 -435l-317 187l-19 12l225 356q20 31 60 45t80 10q24 -2 48.5 -12t42 -21t41.5 -33t36 -34.5 t36 -39.5t32 -35zM1550 1053l212 -363q18 -37 12.5 -76t-27.5 -74q-13 -20 -33 -37t-38 -28t-48.5 -22t-47 -16t-51.5 -14t-46 -12q-34 72 -265 436l313 195zM1407 1279l142 83l-220 -373l-419 20l151 86q-34 89 -75 166t-75.5 123.5t-64.5 80t-47 46.5l-17 13l405 -1 q31 3 58 -10.5t39 -28.5l11 -15q39 -61 112 -190z" />
+<glyph unicode="&#xf1b9;" horiz-adv-x="2048" d="M480 448q0 66 -47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47t113 47t47 113zM516 768h1016l-89 357q-2 8 -14 17.5t-21 9.5h-768q-9 0 -21 -9.5t-14 -17.5zM1888 448q0 66 -47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47t113 47t47 113zM2048 544v-384 q0 -14 -9 -23t-23 -9h-96v-128q0 -80 -56 -136t-136 -56t-136 56t-56 136v128h-1024v-128q0 -80 -56 -136t-136 -56t-136 56t-56 136v128h-96q-14 0 -23 9t-9 23v384q0 93 65.5 158.5t158.5 65.5h28l105 419q23 94 104 157.5t179 63.5h768q98 0 179 -63.5t104 -157.5 l105 -419h28q93 0 158.5 -65.5t65.5 -158.5z" />
+<glyph unicode="&#xf1ba;" horiz-adv-x="2048" d="M1824 640q93 0 158.5 -65.5t65.5 -158.5v-384q0 -14 -9 -23t-23 -9h-96v-64q0 -80 -56 -136t-136 -56t-136 56t-56 136v64h-1024v-64q0 -80 -56 -136t-136 -56t-136 56t-56 136v64h-96q-14 0 -23 9t-9 23v384q0 93 65.5 158.5t158.5 65.5h28l105 419q23 94 104 157.5 t179 63.5h128v224q0 14 9 23t23 9h448q14 0 23 -9t9 -23v-224h128q98 0 179 -63.5t104 -157.5l105 -419h28zM320 160q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47zM516 640h1016l-89 357q-2 8 -14 17.5t-21 9.5h-768q-9 0 -21 -9.5t-14 -17.5z M1728 160q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47z" />
+<glyph unicode="&#xf1bb;" d="M1504 64q0 -26 -19 -45t-45 -19h-462q1 -17 6 -87.5t5 -108.5q0 -25 -18 -42.5t-43 -17.5h-320q-25 0 -43 17.5t-18 42.5q0 38 5 108.5t6 87.5h-462q-26 0 -45 19t-19 45t19 45l402 403h-229q-26 0 -45 19t-19 45t19 45l402 403h-197q-26 0 -45 19t-19 45t19 45l384 384 q19 19 45 19t45 -19l384 -384q19 -19 19 -45t-19 -45t-45 -19h-197l402 -403q19 -19 19 -45t-19 -45t-45 -19h-229l402 -403q19 -19 19 -45z" />
+<glyph unicode="&#xf1bc;" d="M1127 326q0 32 -30 51q-193 115 -447 115q-133 0 -287 -34q-42 -9 -42 -52q0 -20 13.5 -34.5t35.5 -14.5q5 0 37 8q132 27 243 27q226 0 397 -103q19 -11 33 -11q19 0 33 13.5t14 34.5zM1223 541q0 40 -35 61q-237 141 -548 141q-153 0 -303 -42q-48 -13 -48 -64 q0 -25 17.5 -42.5t42.5 -17.5q7 0 37 8q122 33 251 33q279 0 488 -124q24 -13 38 -13q25 0 42.5 17.5t17.5 42.5zM1331 789q0 47 -40 70q-126 73 -293 110.5t-343 37.5q-204 0 -364 -47q-23 -7 -38.5 -25.5t-15.5 -48.5q0 -31 20.5 -52t51.5 -21q11 0 40 8q133 37 307 37 q159 0 309.5 -34t253.5 -95q21 -12 40 -12q29 0 50.5 20.5t21.5 51.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf1bd;" horiz-adv-x="1024" d="M1024 1233l-303 -582l24 -31h279v-415h-507l-44 -30l-142 -273l-30 -30h-301v303l303 583l-24 30h-279v415h507l44 30l142 273l30 30h301v-303z" />
+<glyph unicode="&#xf1be;" horiz-adv-x="2304" d="M784 164l16 241l-16 523q-1 10 -7.5 17t-16.5 7q-9 0 -16 -7t-7 -17l-14 -523l14 -241q1 -10 7.5 -16.5t15.5 -6.5q22 0 24 23zM1080 193l11 211l-12 586q0 16 -13 24q-8 5 -16 5t-16 -5q-13 -8 -13 -24l-1 -6l-10 -579q0 -1 11 -236v-1q0 -10 6 -17q9 -11 23 -11 q11 0 20 9q9 7 9 20zM35 533l20 -128l-20 -126q-2 -9 -9 -9t-9 9l-17 126l17 128q2 9 9 9t9 -9zM121 612l26 -207l-26 -203q-2 -9 -10 -9q-9 0 -9 10l-23 202l23 207q0 9 9 9q8 0 10 -9zM401 159zM213 650l25 -245l-25 -237q0 -11 -11 -11q-10 0 -12 11l-21 237l21 245 q2 12 12 12q11 0 11 -12zM307 657l23 -252l-23 -244q-2 -13 -14 -13q-13 0 -13 13l-21 244l21 252q0 13 13 13q12 0 14 -13zM401 639l21 -234l-21 -246q-2 -16 -16 -16q-6 0 -10.5 4.5t-4.5 11.5l-20 246l20 234q0 6 4.5 10.5t10.5 4.5q14 0 16 -15zM784 164zM495 785 l21 -380l-21 -246q0 -7 -5 -12.5t-12 -5.5q-16 0 -18 18l-18 246l18 380q2 18 18 18q7 0 12 -5.5t5 -12.5zM589 871l19 -468l-19 -244q0 -8 -5.5 -13.5t-13.5 -5.5q-18 0 -20 19l-16 244l16 468q2 19 20 19q8 0 13.5 -5.5t5.5 -13.5zM687 911l18 -506l-18 -242 q-2 -21 -22 -21q-19 0 -21 21l-16 242l16 506q0 9 6.5 15.5t14.5 6.5q9 0 15 -6.5t7 -15.5zM1079 169v0v0zM881 915l15 -510l-15 -239q0 -10 -7.5 -17.5t-17.5 -7.5t-17 7t-8 18l-14 239l14 510q0 11 7.5 18t17.5 7t17.5 -7t7.5 -18zM980 896l14 -492l-14 -236q0 -11 -8 -19 t-19 -8t-19 8t-9 19l-12 236l12 492q1 12 9 20t19 8t18.5 -8t8.5 -20zM1192 404l-14 -231v0q0 -13 -9 -22t-22 -9t-22 9t-10 22l-6 114l-6 117l12 636v3q2 15 12 24q9 7 20 7q8 0 15 -5q14 -8 16 -26zM2304 423q0 -117 -83 -199.5t-200 -82.5h-786q-13 2 -22 11t-9 22v899 q0 23 28 33q85 34 181 34q195 0 338 -131.5t160 -323.5q53 22 110 22q117 0 200 -83t83 -201z" />
+<glyph unicode="&#xf1c0;" d="M768 768q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127t443 -43zM768 0q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127 t443 -43zM768 384q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127t443 -43zM768 1536q208 0 385 -34.5t280 -93.5t103 -128v-128q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5 t-103 128v128q0 69 103 128t280 93.5t385 34.5z" />
+<glyph unicode="&#xf1c1;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M894 465q33 -26 84 -56q59 7 117 7q147 0 177 -49q16 -22 2 -52q0 -1 -1 -2l-2 -2v-1q-6 -38 -71 -38q-48 0 -115 20t-130 53q-221 -24 -392 -83q-153 -262 -242 -262q-15 0 -28 7l-24 12q-1 1 -6 5q-10 10 -6 36q9 40 56 91.5t132 96.5q14 9 23 -6q2 -2 2 -4q52 85 107 197 q68 136 104 262q-24 82 -30.5 159.5t6.5 127.5q11 40 42 40h21h1q23 0 35 -15q18 -21 9 -68q-2 -6 -4 -8q1 -3 1 -8v-30q-2 -123 -14 -192q55 -164 146 -238zM318 54q52 24 137 158q-51 -40 -87.5 -84t-49.5 -74zM716 974q-15 -42 -2 -132q1 7 7 44q0 3 7 43q1 4 4 8 q-1 1 -1 2t-0.5 1.5t-0.5 1.5q-1 22 -13 36q0 -1 -1 -2v-2zM592 313q135 54 284 81q-2 1 -13 9.5t-16 13.5q-76 67 -127 176q-27 -86 -83 -197q-30 -56 -45 -83zM1238 329q-24 24 -140 24q76 -28 124 -28q14 0 18 1q0 1 -2 3z" />
+<glyph unicode="&#xf1c2;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M233 768v-107h70l164 -661h159l128 485q7 20 10 46q2 16 2 24h4l3 -24q1 -3 3.5 -20t5.5 -26l128 -485h159l164 661h70v107h-300v-107h90l-99 -438q-5 -20 -7 -46l-2 -21h-4l-3 21q-1 5 -4 21t-5 25l-144 545h-114l-144 -545q-2 -9 -4.5 -24.5t-3.5 -21.5l-4 -21h-4l-2 21 q-2 26 -7 46l-99 438h90v107h-300z" />
+<glyph unicode="&#xf1c3;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M429 106v-106h281v106h-75l103 161q5 7 10 16.5t7.5 13.5t3.5 4h2q1 -4 5 -10q2 -4 4.5 -7.5t6 -8t6.5 -8.5l107 -161h-76v-106h291v106h-68l-192 273l195 282h67v107h-279v-107h74l-103 -159q-4 -7 -10 -16.5t-9 -13.5l-2 -3h-2q-1 4 -5 10q-6 11 -17 23l-106 159h76v107 h-290v-107h68l189 -272l-194 -283h-68z" />
+<glyph unicode="&#xf1c4;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M416 106v-106h327v106h-93v167h137q76 0 118 15q67 23 106.5 87t39.5 146q0 81 -37 141t-100 87q-48 19 -130 19h-368v-107h92v-555h-92zM769 386h-119v268h120q52 0 83 -18q56 -33 56 -115q0 -89 -62 -120q-31 -15 -78 -15z" />
+<glyph unicode="&#xf1c5;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M1280 320v-320h-1024v192l192 192l128 -128l384 384zM448 512q-80 0 -136 56t-56 136t56 136t136 56t136 -56t56 -136t-56 -136t-136 -56z" />
+<glyph unicode="&#xf1c6;" d="M640 1152v128h-128v-128h128zM768 1024v128h-128v-128h128zM640 896v128h-128v-128h128zM768 768v128h-128v-128h128zM1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400 v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-128v-128h-128v128h-512v-1536h1280zM781 593l107 -349q8 -27 8 -52q0 -83 -72.5 -137.5t-183.5 -54.5t-183.5 54.5t-72.5 137.5q0 25 8 52q21 63 120 396v128h128v-128h79 q22 0 39 -13t23 -34zM640 128q53 0 90.5 19t37.5 45t-37.5 45t-90.5 19t-90.5 -19t-37.5 -45t37.5 -45t90.5 -19z" />
+<glyph unicode="&#xf1c7;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M620 686q20 -8 20 -30v-544q0 -22 -20 -30q-8 -2 -12 -2q-12 0 -23 9l-166 167h-131q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h131l166 167q16 15 35 7zM1037 -3q31 0 50 24q129 159 129 363t-129 363q-16 21 -43 24t-47 -14q-21 -17 -23.5 -43.5t14.5 -47.5 q100 -123 100 -282t-100 -282q-17 -21 -14.5 -47.5t23.5 -42.5q18 -15 40 -15zM826 145q27 0 47 20q87 93 87 219t-87 219q-18 19 -45 20t-46 -17t-20 -44.5t18 -46.5q52 -57 52 -131t-52 -131q-19 -20 -18 -46.5t20 -44.5q20 -17 44 -17z" />
+<glyph unicode="&#xf1c8;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M768 768q52 0 90 -38t38 -90v-384q0 -52 -38 -90t-90 -38h-384q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h384zM1260 766q20 -8 20 -30v-576q0 -22 -20 -30q-8 -2 -12 -2q-14 0 -23 9l-265 266v90l265 266q9 9 23 9q4 0 12 -2z" />
+<glyph unicode="&#xf1c9;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M480 768q8 11 21 12.5t24 -6.5l51 -38q11 -8 12.5 -21t-6.5 -24l-182 -243l182 -243q8 -11 6.5 -24t-12.5 -21l-51 -38q-11 -8 -24 -6.5t-21 12.5l-226 301q-14 19 0 38zM1282 467q14 -19 0 -38l-226 -301q-8 -11 -21 -12.5t-24 6.5l-51 38q-11 8 -12.5 21t6.5 24l182 243 l-182 243q-8 11 -6.5 24t12.5 21l51 38q11 8 24 6.5t21 -12.5zM662 6q-13 2 -20.5 13t-5.5 24l138 831q2 13 13 20.5t24 5.5l63 -10q13 -2 20.5 -13t5.5 -24l-138 -831q-2 -13 -13 -20.5t-24 -5.5z" />
+<glyph unicode="&#xf1ca;" d="M1497 709v-198q-101 -23 -198 -23q-65 -136 -165.5 -271t-181.5 -215.5t-128 -106.5q-80 -45 -162 3q-28 17 -60.5 43.5t-85 83.5t-102.5 128.5t-107.5 184t-105.5 244t-91.5 314.5t-70.5 390h283q26 -218 70 -398.5t104.5 -317t121.5 -235.5t140 -195q169 169 287 406 q-142 72 -223 220t-81 333q0 192 104 314.5t284 122.5q178 0 273 -105.5t95 -297.5q0 -159 -58 -286q-7 -1 -19.5 -3t-46 -2t-63 6t-62 25.5t-50.5 51.5q31 103 31 184q0 87 -29 132t-79 45q-53 0 -85 -49.5t-32 -140.5q0 -186 105 -293.5t267 -107.5q62 0 121 14z" />
+<glyph unicode="&#xf1cb;" horiz-adv-x="1792" d="M216 367l603 -402v359l-334 223zM154 511l193 129l-193 129v-258zM973 -35l603 402l-269 180l-334 -223v-359zM896 458l272 182l-272 182l-272 -182zM485 733l334 223v359l-603 -402zM1445 640l193 -129v258zM1307 733l269 180l-603 402v-359zM1792 913v-546 q0 -41 -34 -64l-819 -546q-21 -13 -43 -13t-43 13l-819 546q-34 23 -34 64v546q0 41 34 64l819 546q21 13 43 13t43 -13l819 -546q34 -23 34 -64z" />
+<glyph unicode="&#xf1cc;" horiz-adv-x="2048" d="M1800 764q111 -46 179.5 -145.5t68.5 -221.5q0 -164 -118 -280.5t-285 -116.5q-4 0 -11.5 0.5t-10.5 0.5h-1209h-1h-2h-5q-170 10 -288 125.5t-118 280.5q0 110 55 203t147 147q-12 39 -12 82q0 115 82 196t199 81q95 0 172 -58q75 154 222.5 248t326.5 94 q166 0 306 -80.5t221.5 -218.5t81.5 -301q0 -6 -0.5 -18t-0.5 -18zM468 498q0 -122 84 -193t208 -71q137 0 240 99q-16 20 -47.5 56.5t-43.5 50.5q-67 -65 -144 -65q-55 0 -93.5 33.5t-38.5 87.5q0 53 38.5 87t91.5 34q44 0 84.5 -21t73 -55t65 -75t69 -82t77 -75t97 -55 t121.5 -21q121 0 204.5 71.5t83.5 190.5q0 121 -84 192t-207 71q-143 0 -241 -97q14 -16 29.5 -34t34.5 -40t29 -34q66 64 142 64q52 0 92 -33t40 -84q0 -57 -37 -91.5t-94 -34.5q-43 0 -82.5 21t-72 55t-65.5 75t-69.5 82t-77.5 75t-96.5 55t-118.5 21q-122 0 -207 -70.5 t-85 -189.5z" />
+<glyph unicode="&#xf1cd;" horiz-adv-x="1792" d="M896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM896 1408q-190 0 -361 -90l194 -194q82 28 167 28t167 -28l194 194q-171 90 -361 90zM218 279l194 194 q-28 82 -28 167t28 167l-194 194q-90 -171 -90 -361t90 -361zM896 -128q190 0 361 90l-194 194q-82 -28 -167 -28t-167 28l-194 -194q171 -90 361 -90zM896 256q159 0 271.5 112.5t112.5 271.5t-112.5 271.5t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5 t271.5 -112.5zM1380 473l194 -194q90 171 90 361t-90 361l-194 -194q28 -82 28 -167t-28 -167z" />
+<glyph unicode="&#xf1ce;" horiz-adv-x="1792" d="M1760 640q0 -176 -68.5 -336t-184 -275.5t-275.5 -184t-336 -68.5t-336 68.5t-275.5 184t-184 275.5t-68.5 336q0 213 97 398.5t265 305.5t374 151v-228q-221 -45 -366.5 -221t-145.5 -406q0 -130 51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5 t136.5 204t51 248.5q0 230 -145.5 406t-366.5 221v228q206 -31 374 -151t265 -305.5t97 -398.5z" />
+<glyph unicode="&#xf1d0;" horiz-adv-x="1792" d="M19 662q8 217 116 406t305 318h5q0 -1 -1 -3q-8 -8 -28 -33.5t-52 -76.5t-60 -110.5t-44.5 -135.5t-14 -150.5t39 -157.5t108.5 -154q50 -50 102 -69.5t90.5 -11.5t69.5 23.5t47 32.5l16 16q39 51 53 116.5t6.5 122.5t-21 107t-26.5 80l-14 29q-10 25 -30.5 49.5t-43 41 t-43.5 29.5t-35 19l-13 6l104 115q39 -17 78 -52t59 -61l19 -27q1 48 -18.5 103.5t-40.5 87.5l-20 31l161 183l160 -181q-33 -46 -52.5 -102.5t-22.5 -90.5l-4 -33q22 37 61.5 72.5t67.5 52.5l28 17l103 -115q-44 -14 -85 -50t-60 -65l-19 -29q-31 -56 -48 -133.5t-7 -170 t57 -156.5q33 -45 77.5 -60.5t85 -5.5t76 26.5t57.5 33.5l21 16q60 53 96.5 115t48.5 121.5t10 121.5t-18 118t-37 107.5t-45.5 93t-45 72t-34.5 47.5l-13 17q-14 13 -7 13l10 -3q40 -29 62.5 -46t62 -50t64 -58t58.5 -65t55.5 -77t45.5 -88t38 -103t23.5 -117t10.5 -136 q3 -259 -108 -465t-312 -321t-456 -115q-185 0 -351 74t-283.5 198t-184 293t-60.5 353z" />
+<glyph unicode="&#xf1d1;" horiz-adv-x="1792" d="M874 -102v-66q-208 6 -385 109.5t-283 275.5l58 34q29 -49 73 -99l65 57q148 -168 368 -212l-17 -86q65 -12 121 -13zM276 428l-83 -28q22 -60 49 -112l-57 -33q-98 180 -98 385t98 385l57 -33q-30 -56 -49 -112l82 -28q-35 -100 -35 -212q0 -109 36 -212zM1528 251 l58 -34q-106 -172 -283 -275.5t-385 -109.5v66q56 1 121 13l-17 86q220 44 368 212l65 -57q44 50 73 99zM1377 805l-233 -80q14 -42 14 -85t-14 -85l232 -80q-31 -92 -98 -169l-185 162q-57 -67 -147 -85l48 -241q-52 -10 -98 -10t-98 10l48 241q-90 18 -147 85l-185 -162 q-67 77 -98 169l232 80q-14 42 -14 85t14 85l-233 80q33 93 99 169l185 -162q59 68 147 86l-48 240q44 10 98 10t98 -10l-48 -240q88 -18 147 -86l185 162q66 -76 99 -169zM874 1448v-66q-65 -2 -121 -13l17 -86q-220 -42 -368 -211l-65 56q-38 -42 -73 -98l-57 33 q106 172 282 275.5t385 109.5zM1705 640q0 -205 -98 -385l-57 33q27 52 49 112l-83 28q36 103 36 212q0 112 -35 212l82 28q-19 56 -49 112l57 33q98 -180 98 -385zM1585 1063l-57 -33q-35 56 -73 98l-65 -56q-148 169 -368 211l17 86q-56 11 -121 13v66q209 -6 385 -109.5 t282 -275.5zM1748 640q0 173 -67.5 331t-181.5 272t-272 181.5t-331 67.5t-331 -67.5t-272 -181.5t-181.5 -272t-67.5 -331t67.5 -331t181.5 -272t272 -181.5t331 -67.5t331 67.5t272 181.5t181.5 272t67.5 331zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71 t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
+<glyph unicode="&#xf1d2;" d="M582 228q0 -66 -93 -66q-107 0 -107 63q0 64 98 64q102 0 102 -61zM546 694q0 -85 -74 -85q-77 0 -77 84q0 90 77 90q36 0 55 -25.5t19 -63.5zM712 769v125q-78 -29 -135 -29q-50 29 -110 29q-86 0 -145 -57t-59 -143q0 -50 29.5 -102t73.5 -67v-3q-38 -17 -38 -85 q0 -53 41 -77v-3q-113 -37 -113 -139q0 -45 20 -78.5t54 -51t72 -25.5t81 -8q224 0 224 188q0 67 -48 99t-126 46q-27 5 -51.5 20.5t-24.5 39.5q0 44 49 52q77 15 122 70t45 134q0 24 -10 52q37 9 49 13zM771 350h137q-2 27 -2 82v387q0 46 2 69h-137q3 -23 3 -71v-392 q0 -50 -3 -75zM1280 366v121q-30 -21 -68 -21q-53 0 -53 82v225h52q9 0 26.5 -1t26.5 -1v117h-105q0 82 3 102h-140q4 -24 4 -55v-47h-60v-117q36 3 37 3q3 0 11 -0.5t12 -0.5v-2h-2v-217q0 -37 2.5 -64t11.5 -56.5t24.5 -48.5t43.5 -31t66 -12q64 0 108 24zM924 1072 q0 36 -24 63.5t-60 27.5t-60.5 -27t-24.5 -64q0 -36 25 -62.5t60 -26.5t59.5 27t24.5 62zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf1d3;" horiz-adv-x="1792" d="M595 22q0 100 -165 100q-158 0 -158 -104q0 -101 172 -101q151 0 151 105zM536 777q0 61 -30 102t-89 41q-124 0 -124 -145q0 -135 124 -135q119 0 119 137zM805 1101v-202q-36 -12 -79 -22q16 -43 16 -84q0 -127 -73 -216.5t-197 -112.5q-40 -8 -59.5 -27t-19.5 -58 q0 -31 22.5 -51.5t58 -32t78.5 -22t86 -25.5t78.5 -37.5t58 -64t22.5 -98.5q0 -304 -363 -304q-69 0 -130 12.5t-116 41t-87.5 82t-32.5 127.5q0 165 182 225v4q-67 41 -67 126q0 109 63 137v4q-72 24 -119.5 108.5t-47.5 165.5q0 139 95 231.5t235 92.5q96 0 178 -47 q98 0 218 47zM1123 220h-222q4 45 4 134v609q0 94 -4 128h222q-4 -33 -4 -124v-613q0 -89 4 -134zM1724 442v-196q-71 -39 -174 -39q-62 0 -107 20t-70 50t-39.5 78t-18.5 92t-4 103v351h2v4q-7 0 -19 1t-18 1q-21 0 -59 -6v190h96v76q0 54 -6 89h227q-6 -41 -6 -165h171 v-190q-15 0 -43.5 2t-42.5 2h-85v-365q0 -131 87 -131q61 0 109 33zM1148 1389q0 -58 -39 -101.5t-96 -43.5q-58 0 -98 43.5t-40 101.5q0 59 39.5 103t98.5 44q58 0 96.5 -44.5t38.5 -102.5z" />
+<glyph unicode="&#xf1d4;" d="M809 532l266 499h-112l-157 -312q-24 -48 -44 -92l-42 92l-155 312h-120l263 -493v-324h101v318zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf1d5;" horiz-adv-x="1280" d="M842 964q0 -80 -57 -136.5t-136 -56.5q-60 0 -111 35q-62 -67 -115 -146q-247 -371 -202 -859q1 -22 -12.5 -38.5t-34.5 -18.5h-5q-20 0 -35 13.5t-17 33.5q-14 126 -3.5 247.5t29.5 217t54 186t69 155.5t74 125q61 90 132 165q-16 35 -16 77q0 80 56.5 136.5t136.5 56.5 t136.5 -56.5t56.5 -136.5zM1223 953q0 -158 -78 -292t-212.5 -212t-292.5 -78q-64 0 -131 14q-21 5 -32.5 23.5t-6.5 39.5q5 20 23 31.5t39 7.5q51 -13 108 -13q97 0 186 38t153 102t102 153t38 186t-38 186t-102 153t-153 102t-186 38t-186 -38t-153 -102t-102 -153 t-38 -186q0 -114 52 -218q10 -20 3.5 -40t-25.5 -30t-39.5 -3t-30.5 26q-64 123 -64 265q0 119 46.5 227t124.5 186t186 124t226 46q158 0 292.5 -78t212.5 -212.5t78 -292.5z" />
+<glyph unicode="&#xf1d6;" horiz-adv-x="1792" d="M270 730q-8 19 -8 52q0 20 11 49t24 45q-1 22 7.5 53t22.5 43q0 139 92.5 288.5t217.5 209.5q139 66 324 66q133 0 266 -55q49 -21 90 -48t71 -56t55 -68t42 -74t32.5 -84.5t25.5 -89.5t22 -98l1 -5q55 -83 55 -150q0 -14 -9 -40t-9 -38q0 -1 1.5 -3.5t3.5 -5t2 -3.5 q77 -114 120.5 -214.5t43.5 -208.5q0 -43 -19.5 -100t-55.5 -57q-9 0 -19.5 7.5t-19 17.5t-19 26t-16 26.5t-13.5 26t-9 17.5q-1 1 -3 1l-5 -4q-59 -154 -132 -223q20 -20 61.5 -38.5t69 -41.5t35.5 -65q-2 -4 -4 -16t-7 -18q-64 -97 -302 -97q-53 0 -110.5 9t-98 20 t-104.5 30q-15 5 -23 7q-14 4 -46 4.5t-40 1.5q-41 -45 -127.5 -65t-168.5 -20q-35 0 -69 1.5t-93 9t-101 20.5t-74.5 40t-32.5 64q0 40 10 59.5t41 48.5q11 2 40.5 13t49.5 12q4 0 14 2q2 2 2 4l-2 3q-48 11 -108 105.5t-73 156.5l-5 3q-4 0 -12 -20q-18 -41 -54.5 -74.5 t-77.5 -37.5h-1q-4 0 -6 4.5t-5 5.5q-23 54 -23 100q0 275 252 466z" />
+<glyph unicode="&#xf1d7;" horiz-adv-x="2048" d="M580 1075q0 41 -25 66t-66 25q-43 0 -76 -25.5t-33 -65.5q0 -39 33 -64.5t76 -25.5q41 0 66 24.5t25 65.5zM1323 568q0 28 -25.5 50t-65.5 22q-27 0 -49.5 -22.5t-22.5 -49.5q0 -28 22.5 -50.5t49.5 -22.5q40 0 65.5 22t25.5 51zM1087 1075q0 41 -24.5 66t-65.5 25 q-43 0 -76 -25.5t-33 -65.5q0 -39 33 -64.5t76 -25.5q41 0 65.5 24.5t24.5 65.5zM1722 568q0 28 -26 50t-65 22q-27 0 -49.5 -22.5t-22.5 -49.5q0 -28 22.5 -50.5t49.5 -22.5q39 0 65 22t26 51zM1456 965q-31 4 -70 4q-169 0 -311 -77t-223.5 -208.5t-81.5 -287.5 q0 -78 23 -152q-35 -3 -68 -3q-26 0 -50 1.5t-55 6.5t-44.5 7t-54.5 10.5t-50 10.5l-253 -127l72 218q-290 203 -290 490q0 169 97.5 311t264 223.5t363.5 81.5q176 0 332.5 -66t262 -182.5t136.5 -260.5zM2048 404q0 -117 -68.5 -223.5t-185.5 -193.5l55 -181l-199 109 q-150 -37 -218 -37q-169 0 -311 70.5t-223.5 191.5t-81.5 264t81.5 264t223.5 191.5t311 70.5q161 0 303 -70.5t227.5 -192t85.5 -263.5z" />
+<glyph unicode="&#xf1d8;" horiz-adv-x="1792" d="M1764 1525q33 -24 27 -64l-256 -1536q-5 -29 -32 -45q-14 -8 -31 -8q-11 0 -24 5l-453 185l-242 -295q-18 -23 -49 -23q-13 0 -22 4q-19 7 -30.5 23.5t-11.5 36.5v349l864 1059l-1069 -925l-395 162q-37 14 -40 55q-2 40 32 59l1664 960q15 9 32 9q20 0 36 -11z" />
+<glyph unicode="&#xf1d9;" horiz-adv-x="1792" d="M1764 1525q33 -24 27 -64l-256 -1536q-5 -29 -32 -45q-14 -8 -31 -8q-11 0 -24 5l-527 215l-298 -327q-18 -21 -47 -21q-14 0 -23 4q-19 7 -30 23.5t-11 36.5v452l-472 193q-37 14 -40 55q-3 39 32 59l1664 960q35 21 68 -2zM1422 26l221 1323l-1434 -827l336 -137 l863 639l-478 -797z" />
+<glyph unicode="&#xf1da;" d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61q-172 0 -327 72.5t-264 204.5q-7 10 -6.5 22.5t8.5 20.5l137 138q10 9 25 9q16 -2 23 -12q73 -95 179 -147t225 -52q104 0 198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5t-40.5 198.5t-109.5 163.5 t-163.5 109.5t-198.5 40.5q-98 0 -188 -35.5t-160 -101.5l137 -138q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l130 -129q107 101 244.5 156.5t284.5 55.5q156 0 298 -61t245 -164t164 -245t61 -298zM896 928v-448q0 -14 -9 -23 t-23 -9h-320q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf1db;" d="M768 1280q-130 0 -248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5t-51 248.5t-136.5 204t-204 136.5t-248.5 51zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf1dc;" horiz-adv-x="1792" d="M1682 -128q-44 0 -132.5 3.5t-133.5 3.5q-44 0 -132 -3.5t-132 -3.5q-24 0 -37 20.5t-13 45.5q0 31 17 46t39 17t51 7t45 15q33 21 33 140l-1 391q0 21 -1 31q-13 4 -50 4h-675q-38 0 -51 -4q-1 -10 -1 -31l-1 -371q0 -142 37 -164q16 -10 48 -13t57 -3.5t45 -15 t20 -45.5q0 -26 -12.5 -48t-36.5 -22q-47 0 -139.5 3.5t-138.5 3.5q-43 0 -128 -3.5t-127 -3.5q-23 0 -35.5 21t-12.5 45q0 30 15.5 45t36 17.5t47.5 7.5t42 15q33 23 33 143l-1 57v813q0 3 0.5 26t0 36.5t-1.5 38.5t-3.5 42t-6.5 36.5t-11 31.5t-16 18q-15 10 -45 12t-53 2 t-41 14t-18 45q0 26 12 48t36 22q46 0 138.5 -3.5t138.5 -3.5q42 0 126.5 3.5t126.5 3.5q25 0 37.5 -22t12.5 -48q0 -30 -17 -43.5t-38.5 -14.5t-49.5 -4t-43 -13q-35 -21 -35 -160l1 -320q0 -21 1 -32q13 -3 39 -3h699q25 0 38 3q1 11 1 32l1 320q0 139 -35 160 q-18 11 -58.5 12.5t-66 13t-25.5 49.5q0 26 12.5 48t37.5 22q44 0 132 -3.5t132 -3.5q43 0 129 3.5t129 3.5q25 0 37.5 -22t12.5 -48q0 -30 -17.5 -44t-40 -14.5t-51.5 -3t-44 -12.5q-35 -23 -35 -161l1 -943q0 -119 34 -140q16 -10 46 -13.5t53.5 -4.5t41.5 -15.5t18 -44.5 q0 -26 -12 -48t-36 -22z" />
+<glyph unicode="&#xf1dd;" horiz-adv-x="1280" d="M1278 1347v-73q0 -29 -18.5 -61t-42.5 -32q-50 0 -54 -1q-26 -6 -32 -31q-3 -11 -3 -64v-1152q0 -25 -18 -43t-43 -18h-108q-25 0 -43 18t-18 43v1218h-143v-1218q0 -25 -17.5 -43t-43.5 -18h-108q-26 0 -43.5 18t-17.5 43v496q-147 12 -245 59q-126 58 -192 179 q-64 117 -64 259q0 166 88 286q88 118 209 159q111 37 417 37h479q25 0 43 -18t18 -43z" />
+<glyph unicode="&#xf1de;" d="M352 128v-128h-352v128h352zM704 256q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h256zM864 640v-128h-864v128h864zM224 1152v-128h-224v128h224zM1536 128v-128h-736v128h736zM576 1280q26 0 45 -19t19 -45v-256 q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h256zM1216 768q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h256zM1536 640v-128h-224v128h224zM1536 1152v-128h-864v128h864z" />
+<glyph unicode="&#xf1e0;" d="M1216 512q133 0 226.5 -93.5t93.5 -226.5t-93.5 -226.5t-226.5 -93.5t-226.5 93.5t-93.5 226.5q0 12 2 34l-360 180q-92 -86 -218 -86q-133 0 -226.5 93.5t-93.5 226.5t93.5 226.5t226.5 93.5q126 0 218 -86l360 180q-2 22 -2 34q0 133 93.5 226.5t226.5 93.5 t226.5 -93.5t93.5 -226.5t-93.5 -226.5t-226.5 -93.5q-126 0 -218 86l-360 -180q2 -22 2 -34t-2 -34l360 -180q92 86 218 86z" />
+<glyph unicode="&#xf1e1;" d="M1280 341q0 88 -62.5 151t-150.5 63q-84 0 -145 -58l-241 120q2 16 2 23t-2 23l241 120q61 -58 145 -58q88 0 150.5 63t62.5 151t-62.5 150.5t-150.5 62.5t-151 -62.5t-63 -150.5q0 -7 2 -23l-241 -120q-62 57 -145 57q-88 0 -150.5 -62.5t-62.5 -150.5t62.5 -150.5 t150.5 -62.5q83 0 145 57l241 -120q-2 -16 -2 -23q0 -88 63 -150.5t151 -62.5t150.5 62.5t62.5 150.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf1e2;" horiz-adv-x="1792" d="M571 947q-10 25 -34 35t-49 0q-108 -44 -191 -127t-127 -191q-10 -25 0 -49t35 -34q13 -5 24 -5q42 0 60 40q34 84 98.5 148.5t148.5 98.5q25 11 35 35t0 49zM1513 1303l46 -46l-244 -243l68 -68q19 -19 19 -45.5t-19 -45.5l-64 -64q89 -161 89 -343q0 -143 -55.5 -273.5 t-150 -225t-225 -150t-273.5 -55.5t-273.5 55.5t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5q182 0 343 -89l64 64q19 19 45.5 19t45.5 -19l68 -68zM1521 1359q-10 -10 -22 -10q-13 0 -23 10l-91 90q-9 10 -9 23t9 23q10 9 23 9t23 -9l90 -91 q10 -9 10 -22.5t-10 -22.5zM1751 1129q-11 -9 -23 -9t-23 9l-90 91q-10 9 -10 22.5t10 22.5q9 10 22.5 10t22.5 -10l91 -90q9 -10 9 -23t-9 -23zM1792 1312q0 -14 -9 -23t-23 -9h-96q-14 0 -23 9t-9 23t9 23t23 9h96q14 0 23 -9t9 -23zM1600 1504v-96q0 -14 -9 -23t-23 -9 t-23 9t-9 23v96q0 14 9 23t23 9t23 -9t9 -23zM1751 1449l-91 -90q-10 -10 -22 -10q-13 0 -23 10q-10 9 -10 22.5t10 22.5l90 91q10 9 23 9t23 -9q9 -10 9 -23t-9 -23z" />
+<glyph unicode="&#xf1e3;" horiz-adv-x="1792" d="M609 720l287 208l287 -208l-109 -336h-355zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM1515 186q149 203 149 454v3l-102 -89l-240 224l63 323 l134 -12q-150 206 -389 282l53 -124l-287 -159l-287 159l53 124q-239 -76 -389 -282l135 12l62 -323l-240 -224l-102 89v-3q0 -251 149 -454l30 132l326 -40l139 -298l-116 -69q117 -39 240 -39t240 39l-116 69l139 298l326 40z" />
+<glyph unicode="&#xf1e4;" horiz-adv-x="1792" d="M448 224v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM256 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM832 224v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23 v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM640 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM66 768q-28 0 -47 19t-19 46v129h514v-129q0 -27 -19 -46t-46 -19h-383zM1216 224v-192q0 -14 -9 -23t-23 -9h-192 q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1024 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1600 224v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23 zM1408 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 1016v-13h-514v10q0 104 -382 102q-382 -1 -382 -102v-10h-514v13q0 17 8.5 43t34 64t65.5 75.5t110.5 76t160 67.5t224 47.5t293.5 18.5t293 -18.5t224 -47.5 t160.5 -67.5t110.5 -76t65.5 -75.5t34 -64t8.5 -43zM1792 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 962v-129q0 -27 -19 -46t-46 -19h-384q-27 0 -46 19t-19 46v129h514z" />
+<glyph unicode="&#xf1e5;" horiz-adv-x="1792" d="M704 1216v-768q0 -26 -19 -45t-45 -19v-576q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v512l249 873q7 23 31 23h424zM1024 1216v-704h-256v704h256zM1792 320v-512q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v576q-26 0 -45 19t-19 45v768h424q24 0 31 -23z M736 1504v-224h-352v224q0 14 9 23t23 9h288q14 0 23 -9t9 -23zM1408 1504v-224h-352v224q0 14 9 23t23 9h288q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf1e6;" horiz-adv-x="1792" d="M1755 1083q37 -37 37 -90t-37 -91l-401 -400l150 -150l-160 -160q-163 -163 -389.5 -186.5t-411.5 100.5l-362 -362h-181v181l362 362q-124 185 -100.5 411.5t186.5 389.5l160 160l150 -150l400 401q38 37 91 37t90 -37t37 -90.5t-37 -90.5l-400 -401l234 -234l401 400 q38 37 91 37t90 -37z" />
+<glyph unicode="&#xf1e7;" horiz-adv-x="1792" d="M873 796q0 -83 -63.5 -142.5t-152.5 -59.5t-152.5 59.5t-63.5 142.5q0 84 63.5 143t152.5 59t152.5 -59t63.5 -143zM1375 796q0 -83 -63 -142.5t-153 -59.5q-89 0 -152.5 59.5t-63.5 142.5q0 84 63.5 143t152.5 59q90 0 153 -59t63 -143zM1600 616v667q0 87 -32 123.5 t-111 36.5h-1112q-83 0 -112.5 -34t-29.5 -126v-673q43 -23 88.5 -40t81 -28t81 -18.5t71 -11t70 -4t58.5 -0.5t56.5 2t44.5 2q68 1 95 -27q6 -6 10 -9q26 -25 61 -51q7 91 118 87q5 0 36.5 -1.5t43 -2t45.5 -1t53 1t54.5 4.5t61 8.5t62 13.5t67 19.5t67.5 27t72 34.5z M1763 621q-121 -149 -372 -252q84 -285 -23 -465q-66 -113 -183 -148q-104 -32 -182 15q-86 51 -82 164l-1 326v1q-8 2 -24.5 6t-23.5 5l-1 -338q4 -114 -83 -164q-79 -47 -183 -15q-117 36 -182 150q-105 180 -22 463q-251 103 -372 252q-25 37 -4 63t60 -1q3 -2 11 -7 t11 -8v694q0 72 47 123t114 51h1257q67 0 114 -51t47 -123v-694l21 15q39 27 60 1t-4 -63z" />
+<glyph unicode="&#xf1e8;" horiz-adv-x="1792" d="M896 1102v-434h-145v434h145zM1294 1102v-434h-145v434h145zM1294 342l253 254v795h-1194v-1049h326v-217l217 217h398zM1692 1536v-1013l-434 -434h-326l-217 -217h-217v217h-398v1158l109 289h1483z" />
+<glyph unicode="&#xf1e9;" d="M773 217v-127q-1 -292 -6 -305q-12 -32 -51 -40q-54 -9 -181.5 38t-162.5 89q-13 15 -17 36q-1 12 4 26q4 10 34 47t181 216q1 0 60 70q15 19 39.5 24.5t49.5 -3.5q24 -10 37.5 -29t12.5 -42zM624 468q-3 -55 -52 -70l-120 -39q-275 -88 -292 -88q-35 2 -54 36 q-12 25 -17 75q-8 76 1 166.5t30 124.5t56 32q13 0 202 -77q70 -29 115 -47l84 -34q23 -9 35.5 -30.5t11.5 -48.5zM1450 171q-7 -54 -91.5 -161t-135.5 -127q-37 -14 -63 7q-14 10 -184 287l-47 77q-14 21 -11.5 46t19.5 46q35 43 83 26q1 -1 119 -40q203 -66 242 -79.5 t47 -20.5q28 -22 22 -61zM778 803q5 -102 -54 -122q-58 -17 -114 71l-378 598q-8 35 19 62q41 43 207.5 89.5t224.5 31.5q40 -10 49 -45q3 -18 22 -305.5t24 -379.5zM1440 695q3 -39 -26 -59q-15 -10 -329 -86q-67 -15 -91 -23l1 2q-23 -6 -46 4t-37 32q-30 47 0 87 q1 1 75 102q125 171 150 204t34 39q28 19 65 2q48 -23 123 -133.5t81 -167.5v-3z" />
+<glyph unicode="&#xf1ea;" horiz-adv-x="2048" d="M1024 1024h-384v-384h384v384zM1152 384v-128h-640v128h640zM1152 1152v-640h-640v640h640zM1792 384v-128h-512v128h512zM1792 640v-128h-512v128h512zM1792 896v-128h-512v128h512zM1792 1152v-128h-512v128h512zM256 192v960h-128v-960q0 -26 19 -45t45 -19t45 19 t19 45zM1920 192v1088h-1536v-1088q0 -33 -11 -64h1483q26 0 45 19t19 45zM2048 1408v-1216q0 -80 -56 -136t-136 -56h-1664q-80 0 -136 56t-56 136v1088h256v128h1792z" />
+<glyph unicode="&#xf1eb;" horiz-adv-x="2048" d="M1024 13q-20 0 -93 73.5t-73 93.5q0 32 62.5 54t103.5 22t103.5 -22t62.5 -54q0 -20 -73 -93.5t-93 -73.5zM1294 284q-2 0 -40 25t-101.5 50t-128.5 25t-128.5 -25t-101 -50t-40.5 -25q-18 0 -93.5 75t-75.5 93q0 13 10 23q78 77 196 121t233 44t233 -44t196 -121 q10 -10 10 -23q0 -18 -75.5 -93t-93.5 -75zM1567 556q-11 0 -23 8q-136 105 -252 154.5t-268 49.5q-85 0 -170.5 -22t-149 -53t-113.5 -62t-79 -53t-31 -22q-17 0 -92 75t-75 93q0 12 10 22q132 132 320 205t380 73t380 -73t320 -205q10 -10 10 -22q0 -18 -75 -93t-92 -75z M1838 827q-11 0 -22 9q-179 157 -371.5 236.5t-420.5 79.5t-420.5 -79.5t-371.5 -236.5q-11 -9 -22 -9q-17 0 -92.5 75t-75.5 93q0 13 10 23q187 186 445 288t527 102t527 -102t445 -288q10 -10 10 -23q0 -18 -75.5 -93t-92.5 -75z" />
+<glyph unicode="&#xf1ec;" horiz-adv-x="1792" d="M384 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM384 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5 t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1152 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5 t37.5 90.5zM384 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1152 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 768q0 53 -37.5 90.5t-90.5 37.5 t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1536 0v384q0 52 -38 90t-90 38t-90 -38t-38 -90v-384q0 -52 38 -90t90 -38t90 38t38 90zM1152 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5z M1536 1088v256q0 26 -19 45t-45 19h-1280q-26 0 -45 -19t-19 -45v-256q0 -26 19 -45t45 -19h1280q26 0 45 19t19 45zM1536 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 1408v-1536q0 -52 -38 -90t-90 -38 h-1408q-52 0 -90 38t-38 90v1536q0 52 38 90t90 38h1408q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf1ed;" d="M1519 890q18 -84 -4 -204q-87 -444 -565 -444h-44q-25 0 -44 -16.5t-24 -42.5l-4 -19l-55 -346l-2 -15q-5 -26 -24.5 -42.5t-44.5 -16.5h-251q-21 0 -33 15t-9 36q9 56 26.5 168t26.5 168t27 167.5t27 167.5q5 37 43 37h131q133 -2 236 21q175 39 287 144q102 95 155 246 q24 70 35 133q1 6 2.5 7.5t3.5 1t6 -3.5q79 -59 98 -162zM1347 1172q0 -107 -46 -236q-80 -233 -302 -315q-113 -40 -252 -42q0 -1 -90 -1l-90 1q-100 0 -118 -96q-2 -8 -85 -530q-1 -10 -12 -10h-295q-22 0 -36.5 16.5t-11.5 38.5l232 1471q5 29 27.5 48t51.5 19h598 q34 0 97.5 -13t111.5 -32q107 -41 163.5 -123t56.5 -196z" />
+<glyph unicode="&#xf1ee;" horiz-adv-x="1792" d="M602 949q19 -61 31 -123.5t17 -141.5t-14 -159t-62 -145q-21 81 -67 157t-95.5 127t-99 90.5t-78.5 57.5t-33 19q-62 34 -81.5 100t14.5 128t101 81.5t129 -14.5q138 -83 238 -177zM927 1236q11 -25 20.5 -46t36.5 -100.5t42.5 -150.5t25.5 -179.5t0 -205.5t-47.5 -209.5 t-105.5 -208.5q-51 -72 -138 -72q-54 0 -98 31q-57 40 -69 109t28 127q60 85 81 195t13 199.5t-32 180.5t-39 128t-22 52q-31 63 -8.5 129.5t85.5 97.5q34 17 75 17q47 0 88.5 -25t63.5 -69zM1248 567q-17 -160 -72 -311q-17 131 -63 246q25 174 -5 361q-27 178 -94 342 q114 -90 212 -211q9 -37 15 -80q26 -179 7 -347zM1520 1440q9 -17 23.5 -49.5t43.5 -117.5t50.5 -178t34 -227.5t5 -269t-47 -300t-112.5 -323.5q-22 -48 -66 -75.5t-95 -27.5q-39 0 -74 16q-67 31 -92.5 100t4.5 136q58 126 90 257.5t37.5 239.5t-3.5 213.5t-26.5 180.5 t-38.5 138.5t-32.5 90t-15.5 32.5q-34 65 -11.5 135.5t87.5 104.5q37 20 81 20q49 0 91.5 -25.5t66.5 -70.5z" />
+<glyph unicode="&#xf1f0;" horiz-adv-x="2304" d="M1975 546h-138q14 37 66 179l3 9q4 10 10 26t9 26l12 -55zM531 611l-58 295q-11 54 -75 54h-268l-2 -13q311 -79 403 -336zM710 960l-162 -438l-17 89q-26 70 -85 129.5t-131 88.5l135 -510h175l261 641h-176zM849 318h166l104 642h-166zM1617 944q-69 27 -149 27 q-123 0 -201 -59t-79 -153q-1 -102 145 -174q48 -23 67 -41t19 -39q0 -30 -30 -46t-69 -16q-86 0 -156 33l-22 11l-23 -144q74 -34 185 -34q130 -1 208.5 59t80.5 160q0 106 -140 174q-49 25 -71 42t-22 38q0 22 24.5 38.5t70.5 16.5q70 1 124 -24l15 -8zM2042 960h-128 q-65 0 -87 -54l-246 -588h174l35 96h212q5 -22 20 -96h154zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf1f1;" horiz-adv-x="2304" d="M671 603h-13q-47 0 -47 -32q0 -22 20 -22q17 0 28 15t12 39zM1066 639h62v3q1 4 0.5 6.5t-1 7t-2 8t-4.5 6.5t-7.5 5t-11.5 2q-28 0 -36 -38zM1606 603h-12q-48 0 -48 -32q0 -22 20 -22q17 0 28 15t12 39zM1925 629q0 41 -30 41q-19 0 -31 -20t-12 -51q0 -42 28 -42 q20 0 32.5 20t12.5 52zM480 770h87l-44 -262h-56l32 201l-71 -201h-39l-4 200l-34 -200h-53l44 262h81l2 -163zM733 663q0 -6 -4 -42q-16 -101 -17 -113h-47l1 22q-20 -26 -58 -26q-23 0 -37.5 16t-14.5 42q0 39 26 60.5t73 21.5q14 0 23 -1q0 3 0.5 5.5t1 4.5t0.5 3 q0 20 -36 20q-29 0 -59 -10q0 4 7 48q38 11 67 11q74 0 74 -62zM889 721l-8 -49q-22 3 -41 3q-27 0 -27 -17q0 -8 4.5 -12t21.5 -11q40 -19 40 -60q0 -72 -87 -71q-34 0 -58 6q0 2 7 49q29 -8 51 -8q32 0 32 19q0 7 -4.5 11.5t-21.5 12.5q-43 20 -43 59q0 72 84 72 q30 0 50 -4zM977 721h28l-7 -52h-29q-2 -17 -6.5 -40.5t-7 -38.5t-2.5 -18q0 -16 19 -16q8 0 16 2l-8 -47q-21 -7 -40 -7q-43 0 -45 47q0 12 8 56q3 20 25 146h55zM1180 648q0 -23 -7 -52h-111q-3 -22 10 -33t38 -11q30 0 58 14l-9 -54q-30 -8 -57 -8q-95 0 -95 95 q0 55 27.5 90.5t69.5 35.5q35 0 55.5 -21t20.5 -56zM1319 722q-13 -23 -22 -62q-22 2 -31 -24t-25 -128h-56l3 14q22 130 29 199h51l-3 -33q14 21 25.5 29.5t28.5 4.5zM1506 763l-9 -57q-28 14 -50 14q-31 0 -51 -27.5t-20 -70.5q0 -30 13.5 -47t38.5 -17q21 0 48 13 l-10 -59q-28 -8 -50 -8q-45 0 -71.5 30.5t-26.5 82.5q0 70 35.5 114.5t91.5 44.5q26 0 61 -13zM1668 663q0 -18 -4 -42q-13 -79 -17 -113h-46l1 22q-20 -26 -59 -26q-23 0 -37 16t-14 42q0 39 25.5 60.5t72.5 21.5q15 0 23 -1q2 7 2 13q0 20 -36 20q-29 0 -59 -10q0 4 8 48 q38 11 67 11q73 0 73 -62zM1809 722q-14 -24 -21 -62q-23 2 -31.5 -23t-25.5 -129h-56l3 14q19 104 29 199h52q0 -11 -4 -33q15 21 26.5 29.5t27.5 4.5zM1950 770h56l-43 -262h-53l3 19q-23 -23 -52 -23q-31 0 -49.5 24t-18.5 64q0 53 27.5 92t64.5 39q31 0 53 -29z M2061 640q0 148 -72.5 273t-198 198t-273.5 73q-181 0 -328 -110q127 -116 171 -284h-50q-44 150 -158 253q-114 -103 -158 -253h-50q44 168 171 284q-147 110 -328 110q-148 0 -273.5 -73t-198 -198t-72.5 -273t72.5 -273t198 -198t273.5 -73q181 0 328 110 q-120 111 -165 264h50q46 -138 152 -233q106 95 152 233h50q-45 -153 -165 -264q147 -110 328 -110q148 0 273.5 73t198 198t72.5 273zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf1f2;" horiz-adv-x="2304" d="M313 759q0 -51 -36 -84q-29 -26 -89 -26h-17v220h17q61 0 89 -27q36 -31 36 -83zM2089 824q0 -52 -64 -52h-19v101h20q63 0 63 -49zM380 759q0 74 -50 120.5t-129 46.5h-95v-333h95q74 0 119 38q60 51 60 128zM410 593h65v333h-65v-333zM730 694q0 40 -20.5 62t-75.5 42 q-29 10 -39.5 19t-10.5 23q0 16 13.5 26.5t34.5 10.5q29 0 53 -27l34 44q-41 37 -98 37q-44 0 -74 -27.5t-30 -67.5q0 -35 18 -55.5t64 -36.5q37 -13 45 -19q19 -12 19 -34q0 -20 -14 -33.5t-36 -13.5q-48 0 -71 44l-42 -40q44 -64 115 -64q51 0 83 30.5t32 79.5zM1008 604 v77q-37 -37 -78 -37q-49 0 -80.5 32.5t-31.5 82.5q0 48 31.5 81.5t77.5 33.5q43 0 81 -38v77q-40 20 -80 20q-74 0 -125.5 -50.5t-51.5 -123.5t51 -123.5t125 -50.5q42 0 81 19zM2240 0v527q-65 -40 -144.5 -84t-237.5 -117t-329.5 -137.5t-417.5 -134.5t-504 -118h1569 q26 0 45 19t19 45zM1389 757q0 75 -53 128t-128 53t-128 -53t-53 -128t53 -128t128 -53t128 53t53 128zM1541 584l144 342h-71l-90 -224l-89 224h-71l142 -342h35zM1714 593h184v56h-119v90h115v56h-115v74h119v57h-184v-333zM2105 593h80l-105 140q76 16 76 94q0 47 -31 73 t-87 26h-97v-333h65v133h9zM2304 1274v-1268q0 -56 -38.5 -95t-93.5 -39h-2040q-55 0 -93.5 39t-38.5 95v1268q0 56 38.5 95t93.5 39h2040q55 0 93.5 -39t38.5 -95z" />
+<glyph unicode="&#xf1f3;" horiz-adv-x="2304" d="M119 854h89l-45 108zM740 328l74 79l-70 79h-163v-49h142v-55h-142v-54h159zM898 406l99 -110v217zM1186 453q0 33 -40 33h-84v-69h83q41 0 41 36zM1475 457q0 29 -42 29h-82v-61h81q43 0 43 32zM1197 923q0 29 -42 29h-82v-60h81q43 0 43 31zM1656 854h89l-44 108z M699 1009v-271h-66v212l-94 -212h-57l-94 212v-212h-132l-25 60h-135l-25 -60h-70l116 271h96l110 -257v257h106l85 -184l77 184h108zM1255 453q0 -20 -5.5 -35t-14 -25t-22.5 -16.5t-26 -10t-31.5 -4.5t-31.5 -1t-32.5 0.5t-29.5 0.5v-91h-126l-80 90l-83 -90h-256v271h260 l80 -89l82 89h207q109 0 109 -89zM964 794v-56h-217v271h217v-57h-152v-49h148v-55h-148v-54h152zM2304 235v-229q0 -55 -38.5 -94.5t-93.5 -39.5h-2040q-55 0 -93.5 39.5t-38.5 94.5v678h111l25 61h55l25 -61h218v46l19 -46h113l20 47v-47h541v99l10 1q10 0 10 -14v-86h279 v23q23 -12 55 -18t52.5 -6.5t63 0.5t51.5 1l25 61h56l25 -61h227v58l34 -58h182v378h-180v-44l-25 44h-185v-44l-23 44h-249q-69 0 -109 -22v22h-172v-22q-24 22 -73 22h-628l-43 -97l-43 97h-198v-44l-22 44h-169l-78 -179v391q0 55 38.5 94.5t93.5 39.5h2040 q55 0 93.5 -39.5t38.5 -94.5v-678h-120q-51 0 -81 -22v22h-177q-55 0 -78 -22v22h-316v-22q-31 22 -87 22h-209v-22q-23 22 -91 22h-234l-54 -58l-50 58h-349v-378h343l55 59l52 -59h211v89h21q59 0 90 13v-102h174v99h8q8 0 10 -2t2 -10v-87h529q57 0 88 24v-24h168 q60 0 95 17zM1546 469q0 -23 -12 -43t-34 -29q25 -9 34 -26t9 -46v-54h-65v45q0 33 -12 43.5t-46 10.5h-69v-99h-65v271h154q48 0 77 -15t29 -58zM1269 936q0 -24 -12.5 -44t-33.5 -29q26 -9 34.5 -25.5t8.5 -46.5v-53h-65q0 9 0.5 26.5t0 25t-3 18.5t-8.5 16t-17.5 8.5 t-29.5 3.5h-70v-98h-64v271l153 -1q49 0 78 -14.5t29 -57.5zM1798 327v-56h-216v271h216v-56h-151v-49h148v-55h-148v-54zM1372 1009v-271h-66v271h66zM2065 357q0 -86 -102 -86h-126v58h126q34 0 34 25q0 16 -17 21t-41.5 5t-49.5 3.5t-42 22.5t-17 55q0 39 26 60t66 21 h130v-57h-119q-36 0 -36 -25q0 -16 17.5 -20.5t42 -4t49 -2.5t42 -21.5t17.5 -54.5zM2304 407v-101q-24 -35 -88 -35h-125v58h125q33 0 33 25q0 13 -12.5 19t-31 5.5t-40 2t-40 8t-31 24t-12.5 48.5q0 39 26.5 60t66.5 21h129v-57h-118q-36 0 -36 -25q0 -20 29 -22t68.5 -5 t56.5 -26zM2139 1008v-270h-92l-122 203v-203h-132l-26 60h-134l-25 -60h-75q-129 0 -129 133q0 138 133 138h63v-59q-7 0 -28 1t-28.5 0.5t-23 -2t-21.5 -6.5t-14.5 -13.5t-11.5 -23t-3 -33.5q0 -38 13.5 -58t49.5 -20h29l92 213h97l109 -256v256h99l114 -188v188h66z" />
+<glyph unicode="&#xf1f4;" horiz-adv-x="2304" d="M745 630q0 -37 -25.5 -61.5t-62.5 -24.5q-29 0 -46.5 16t-17.5 44q0 37 25 62.5t62 25.5q28 0 46.5 -16.5t18.5 -45.5zM1530 779q0 -42 -22 -57t-66 -15l-32 -1l17 107q2 11 13 11h18q22 0 35 -2t25 -12.5t12 -30.5zM1881 630q0 -36 -25.5 -61t-61.5 -25q-29 0 -47 16 t-18 44q0 37 25 62.5t62 25.5q28 0 46.5 -16.5t18.5 -45.5zM513 801q0 59 -38.5 85.5t-100.5 26.5h-160q-19 0 -21 -19l-65 -408q-1 -6 3 -11t10 -5h76q20 0 22 19l18 110q1 8 7 13t15 6.5t17 1.5t19 -1t14 -1q86 0 135 48.5t49 134.5zM822 489l41 261q1 6 -3 11t-10 5h-76 q-14 0 -17 -33q-27 40 -95 40q-72 0 -122.5 -54t-50.5 -127q0 -59 34.5 -94t92.5 -35q28 0 58 12t48 32q-4 -12 -4 -21q0 -16 13 -16h69q19 0 22 19zM1269 752q0 5 -4 9.5t-9 4.5h-77q-11 0 -18 -10l-106 -156l-44 150q-5 16 -22 16h-75q-5 0 -9 -4.5t-4 -9.5q0 -2 19.5 -59 t42 -123t23.5 -70q-82 -112 -82 -120q0 -13 13 -13h77q11 0 18 10l255 368q2 2 2 7zM1649 801q0 59 -38.5 85.5t-100.5 26.5h-159q-20 0 -22 -19l-65 -408q-1 -6 3 -11t10 -5h82q12 0 16 13l18 116q1 8 7 13t15 6.5t17 1.5t19 -1t14 -1q86 0 135 48.5t49 134.5zM1958 489 l41 261q1 6 -3 11t-10 5h-76q-14 0 -17 -33q-26 40 -95 40q-72 0 -122.5 -54t-50.5 -127q0 -59 34.5 -94t92.5 -35q29 0 59 12t47 32q0 -1 -2 -9t-2 -12q0 -16 13 -16h69q19 0 22 19zM2176 898v1q0 14 -13 14h-74q-11 0 -13 -11l-65 -416l-1 -2q0 -5 4 -9.5t10 -4.5h66 q19 0 21 19zM392 764q-5 -35 -26 -46t-60 -11l-33 -1l17 107q2 11 13 11h19q40 0 58 -11.5t12 -48.5zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf1f5;" horiz-adv-x="2304" d="M1597 633q0 -69 -21 -106q-19 -35 -52 -35q-23 0 -41 9v224q29 30 57 30q57 0 57 -122zM2035 669h-110q6 98 56 98q51 0 54 -98zM476 534q0 59 -33 91.5t-101 57.5q-36 13 -52 24t-16 25q0 26 38 26q58 0 124 -33l18 112q-67 32 -149 32q-77 0 -123 -38q-48 -39 -48 -109 q0 -58 32.5 -90.5t99.5 -56.5q39 -14 54.5 -25.5t15.5 -27.5q0 -31 -48 -31q-29 0 -70 12.5t-72 30.5l-18 -113q72 -41 168 -41q81 0 129 37q51 41 51 117zM771 749l19 111h-96v135l-129 -21l-18 -114l-46 -8l-17 -103h62v-219q0 -84 44 -120q38 -30 111 -30q32 0 79 11v118 q-32 -7 -44 -7q-42 0 -42 50v197h77zM1087 724v139q-15 3 -28 3q-32 0 -55.5 -16t-33.5 -46l-10 56h-131v-471h150v306q26 31 82 31q16 0 26 -2zM1124 389h150v471h-150v-471zM1746 638q0 122 -45 179q-40 52 -111 52q-64 0 -117 -56l-8 47h-132v-645l150 25v151 q36 -11 68 -11q83 0 134 56q61 65 61 202zM1278 986q0 33 -23 56t-56 23t-56 -23t-23 -56t23 -56.5t56 -23.5t56 23.5t23 56.5zM2176 629q0 113 -48 176q-50 64 -144 64q-96 0 -151.5 -66t-55.5 -180q0 -128 63 -188q55 -55 161 -55q101 0 160 40l-16 103q-57 -31 -128 -31 q-43 0 -63 19q-23 19 -28 66h248q2 14 2 52zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf1f6;" horiz-adv-x="2048" d="M1558 684q61 -356 298 -556q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-180.5 74.5t-75.5 180.5zM1024 -176q16 0 16 16t-16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5zM2026 1424q8 -10 7.5 -23.5t-10.5 -22.5 l-1872 -1622q-10 -8 -23.5 -7t-21.5 11l-84 96q-8 10 -7.5 23.5t10.5 21.5l186 161q-19 32 -19 66q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q124 -18 219 -82.5t148 -157.5 l418 363q10 8 23.5 7t21.5 -11z" />
+<glyph unicode="&#xf1f7;" horiz-adv-x="2048" d="M1040 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM503 315l877 760q-42 88 -132.5 146.5t-223.5 58.5q-93 0 -169.5 -31.5t-121.5 -80.5t-69 -103t-24 -105q0 -384 -137 -645zM1856 128 q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-180.5 74.5t-75.5 180.5l149 129h757q-166 187 -227 459l111 97q61 -356 298 -556zM1942 1520l84 -96q8 -10 7.5 -23.5t-10.5 -22.5l-1872 -1622q-10 -8 -23.5 -7t-21.5 11l-84 96q-8 10 -7.5 23.5t10.5 21.5l186 161 q-19 32 -19 66q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q124 -18 219 -82.5t148 -157.5l418 363q10 8 23.5 7t21.5 -11z" />
+<glyph unicode="&#xf1f8;" horiz-adv-x="1408" d="M512 160v704q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-704q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM768 160v704q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-704q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1024 160v704q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-704 q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM480 1152h448l-48 117q-7 9 -17 11h-317q-10 -2 -17 -11zM1408 1120v-64q0 -14 -9 -23t-23 -9h-96v-948q0 -83 -47 -143.5t-113 -60.5h-832q-66 0 -113 58.5t-47 141.5v952h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h309l70 167 q15 37 54 63t79 26h320q40 0 79 -26t54 -63l70 -167h309q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf1f9;" d="M1150 462v-109q0 -50 -36.5 -89t-94 -60.5t-118 -32.5t-117.5 -11q-205 0 -342.5 139t-137.5 346q0 203 136 339t339 136q34 0 75.5 -4.5t93 -18t92.5 -34t69 -56.5t28 -81v-109q0 -16 -16 -16h-118q-16 0 -16 16v70q0 43 -65.5 67.5t-137.5 24.5q-140 0 -228.5 -91.5 t-88.5 -237.5q0 -151 91.5 -249.5t233.5 -98.5q68 0 138 24t70 66v70q0 7 4.5 11.5t10.5 4.5h119q6 0 11 -4.5t5 -11.5zM768 1280q-130 0 -248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5 t-51 248.5t-136.5 204t-204 136.5t-248.5 51zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf1fa;" d="M972 761q0 108 -53.5 169t-147.5 61q-63 0 -124 -30.5t-110 -84.5t-79.5 -137t-30.5 -180q0 -112 53.5 -173t150.5 -61q96 0 176 66.5t122.5 166t42.5 203.5zM1536 640q0 -111 -37 -197t-98.5 -135t-131.5 -74.5t-145 -27.5q-6 0 -15.5 -0.5t-16.5 -0.5q-95 0 -142 53 q-28 33 -33 83q-52 -66 -131.5 -110t-173.5 -44q-161 0 -249.5 95.5t-88.5 269.5q0 157 66 290t179 210.5t246 77.5q87 0 155 -35.5t106 -99.5l2 19l11 56q1 6 5.5 12t9.5 6h118q5 0 13 -11q5 -5 3 -16l-120 -614q-5 -24 -5 -48q0 -39 12.5 -52t44.5 -13q28 1 57 5.5t73 24 t77 50t57 89.5t24 137q0 292 -174 466t-466 174q-130 0 -248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51q228 0 405 144q11 9 24 8t21 -12l41 -49q8 -12 7 -24q-2 -13 -12 -22q-102 -83 -227.5 -128t-258.5 -45q-156 0 -298 61 t-245 164t-164 245t-61 298t61 298t164 245t245 164t298 61q344 0 556 -212t212 -556z" />
+<glyph unicode="&#xf1fb;" horiz-adv-x="1792" d="M1698 1442q94 -94 94 -226.5t-94 -225.5l-225 -223l104 -104q10 -10 10 -23t-10 -23l-210 -210q-10 -10 -23 -10t-23 10l-105 105l-603 -603q-37 -37 -90 -37h-203l-256 -128l-64 64l128 256v203q0 53 37 90l603 603l-105 105q-10 10 -10 23t10 23l210 210q10 10 23 10 t23 -10l104 -104l223 225q93 94 225.5 94t226.5 -94zM512 64l576 576l-192 192l-576 -576v-192h192z" />
+<glyph unicode="&#xf1fc;" horiz-adv-x="1792" d="M1615 1536q70 0 122.5 -46.5t52.5 -116.5q0 -63 -45 -151q-332 -629 -465 -752q-97 -91 -218 -91q-126 0 -216.5 92.5t-90.5 219.5q0 128 92 212l638 579q59 54 130 54zM706 502q39 -76 106.5 -130t150.5 -76l1 -71q4 -213 -129.5 -347t-348.5 -134q-123 0 -218 46.5 t-152.5 127.5t-86.5 183t-29 220q7 -5 41 -30t62 -44.5t59 -36.5t46 -17q41 0 55 37q25 66 57.5 112.5t69.5 76t88 47.5t103 25.5t125 10.5z" />
+<glyph unicode="&#xf1fd;" horiz-adv-x="1792" d="M1792 128v-384h-1792v384q45 0 85 14t59 27.5t47 37.5q30 27 51.5 38t56.5 11t55.5 -11t52.5 -38q29 -25 47 -38t58 -27t86 -14q45 0 85 14.5t58 27t48 37.5q21 19 32.5 27t31 15t43.5 7q35 0 56.5 -11t51.5 -38q28 -24 47 -37.5t59 -27.5t85 -14t85 14t59 27.5t47 37.5 q30 27 51.5 38t56.5 11q34 0 55.5 -11t51.5 -38q28 -24 47 -37.5t59 -27.5t85 -14zM1792 448v-192q-35 0 -55.5 11t-52.5 38q-29 25 -47 38t-58 27t-85 14q-46 0 -86 -14t-58 -27t-47 -38q-22 -19 -33 -27t-31 -15t-44 -7q-35 0 -56.5 11t-51.5 38q-29 25 -47 38t-58 27 t-86 14q-45 0 -85 -14.5t-58 -27t-48 -37.5q-21 -19 -32.5 -27t-31 -15t-43.5 -7q-35 0 -56.5 11t-51.5 38q-28 24 -47 37.5t-59 27.5t-85 14q-46 0 -86 -14t-58 -27t-47 -38q-30 -27 -51.5 -38t-56.5 -11v192q0 80 56 136t136 56h64v448h256v-448h256v448h256v-448h256v448 h256v-448h64q80 0 136 -56t56 -136zM512 1312q0 -77 -36 -118.5t-92 -41.5q-53 0 -90.5 37.5t-37.5 90.5q0 29 9.5 51t23.5 34t31 28t31 31.5t23.5 44.5t9.5 67q38 0 83 -74t45 -150zM1024 1312q0 -77 -36 -118.5t-92 -41.5q-53 0 -90.5 37.5t-37.5 90.5q0 29 9.5 51 t23.5 34t31 28t31 31.5t23.5 44.5t9.5 67q38 0 83 -74t45 -150zM1536 1312q0 -77 -36 -118.5t-92 -41.5q-53 0 -90.5 37.5t-37.5 90.5q0 29 9.5 51t23.5 34t31 28t31 31.5t23.5 44.5t9.5 67q38 0 83 -74t45 -150z" />
+<glyph unicode="&#xf1fe;" horiz-adv-x="2048" d="M2048 0v-128h-2048v1536h128v-1408h1920zM1664 1024l256 -896h-1664v576l448 576l576 -576z" />
+<glyph unicode="&#xf200;" horiz-adv-x="1792" d="M768 646l546 -546q-106 -108 -247.5 -168t-298.5 -60q-209 0 -385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103v-762zM955 640h773q0 -157 -60 -298.5t-168 -247.5zM1664 768h-768v768q209 0 385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf201;" horiz-adv-x="2048" d="M2048 0v-128h-2048v1536h128v-1408h1920zM1920 1248v-435q0 -21 -19.5 -29.5t-35.5 7.5l-121 121l-633 -633q-10 -10 -23 -10t-23 10l-233 233l-416 -416l-192 192l585 585q10 10 23 10t23 -10l233 -233l464 464l-121 121q-16 16 -7.5 35.5t29.5 19.5h435q14 0 23 -9 t9 -23z" />
+<glyph unicode="&#xf202;" horiz-adv-x="1792" d="M1292 832q0 -6 10 -41q10 -29 25 -49.5t41 -34t44 -20t55 -16.5q325 -91 325 -332q0 -146 -105.5 -242.5t-254.5 -96.5q-59 0 -111.5 18.5t-91.5 45.5t-77 74.5t-63 87.5t-53.5 103.5t-43.5 103t-39.5 106.5t-35.5 95q-32 81 -61.5 133.5t-73.5 96.5t-104 64t-142 20 q-96 0 -183 -55.5t-138 -144.5t-51 -185q0 -160 106.5 -279.5t263.5 -119.5q177 0 258 95q56 63 83 116l84 -152q-15 -34 -44 -70l1 -1q-131 -152 -388 -152q-147 0 -269.5 79t-190.5 207.5t-68 274.5q0 105 43.5 206t116 176.5t172 121.5t204.5 46q87 0 159 -19t123.5 -50 t95 -80t72.5 -99t58.5 -117t50.5 -124.5t50 -130.5t55 -127q96 -200 233 -200q81 0 138.5 48.5t57.5 128.5q0 42 -19 72t-50.5 46t-72.5 31.5t-84.5 27t-87.5 34t-81 52t-65 82t-39 122.5q-3 16 -3 33q0 110 87.5 192t198.5 78q78 -3 120.5 -14.5t90.5 -53.5h-1 q12 -11 23 -24.5t26 -36t19 -27.5l-129 -99q-26 49 -54 70v1q-23 21 -97 21q-49 0 -84 -33t-35 -83z" />
+<glyph unicode="&#xf203;" d="M1432 484q0 173 -234 239q-35 10 -53 16.5t-38 25t-29 46.5q0 2 -2 8.5t-3 12t-1 7.5q0 36 24.5 59.5t60.5 23.5q54 0 71 -15h-1q20 -15 39 -51l93 71q-39 54 -49 64q-33 29 -67.5 39t-85.5 10q-80 0 -142 -57.5t-62 -137.5q0 -7 2 -23q16 -96 64.5 -140t148.5 -73 q29 -8 49 -15.5t45 -21.5t38.5 -34.5t13.5 -46.5v-5q1 -58 -40.5 -93t-100.5 -35q-97 0 -167 144q-23 47 -51.5 121.5t-48 125.5t-54 110.5t-74 95.5t-103.5 60.5t-147 24.5q-101 0 -192 -56t-144 -148t-50 -192v-1q4 -108 50.5 -199t133.5 -147.5t196 -56.5q186 0 279 110 q20 27 31 51l-60 109q-42 -80 -99 -116t-146 -36q-115 0 -191 87t-76 204q0 105 82 189t186 84q112 0 170 -53.5t104 -172.5q8 -21 25.5 -68.5t28.5 -76.5t31.5 -74.5t38.5 -74t45.5 -62.5t55.5 -53.5t66 -33t80 -13.5q107 0 183 69.5t76 174.5zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf204;" horiz-adv-x="2048" d="M1152 640q0 104 -40.5 198.5t-109.5 163.5t-163.5 109.5t-198.5 40.5t-198.5 -40.5t-163.5 -109.5t-109.5 -163.5t-40.5 -198.5t40.5 -198.5t109.5 -163.5t163.5 -109.5t198.5 -40.5t198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5zM1920 640q0 104 -40.5 198.5 t-109.5 163.5t-163.5 109.5t-198.5 40.5h-386q119 -90 188.5 -224t69.5 -288t-69.5 -288t-188.5 -224h386q104 0 198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5zM2048 640q0 -130 -51 -248.5t-136.5 -204t-204 -136.5t-248.5 -51h-768q-130 0 -248.5 51t-204 136.5 t-136.5 204t-51 248.5t51 248.5t136.5 204t204 136.5t248.5 51h768q130 0 248.5 -51t204 -136.5t136.5 -204t51 -248.5z" />
+<glyph unicode="&#xf205;" horiz-adv-x="2048" d="M0 640q0 130 51 248.5t136.5 204t204 136.5t248.5 51h768q130 0 248.5 -51t204 -136.5t136.5 -204t51 -248.5t-51 -248.5t-136.5 -204t-204 -136.5t-248.5 -51h-768q-130 0 -248.5 51t-204 136.5t-136.5 204t-51 248.5zM1408 128q104 0 198.5 40.5t163.5 109.5 t109.5 163.5t40.5 198.5t-40.5 198.5t-109.5 163.5t-163.5 109.5t-198.5 40.5t-198.5 -40.5t-163.5 -109.5t-109.5 -163.5t-40.5 -198.5t40.5 -198.5t109.5 -163.5t163.5 -109.5t198.5 -40.5z" />
+<glyph unicode="&#xf206;" horiz-adv-x="2304" d="M762 384h-314q-40 0 -57.5 35t6.5 67l188 251q-65 31 -137 31q-132 0 -226 -94t-94 -226t94 -226t226 -94q115 0 203 72.5t111 183.5zM576 512h186q-18 85 -75 148zM1056 512l288 384h-480l-99 -132q105 -103 126 -252h165zM2176 448q0 132 -94 226t-226 94 q-60 0 -121 -24l174 -260q15 -23 10 -49t-27 -40q-15 -11 -36 -11q-35 0 -53 29l-174 260q-93 -95 -93 -225q0 -132 94 -226t226 -94t226 94t94 226zM2304 448q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 97 39.5 183.5t109.5 149.5l-65 98l-353 -469 q-18 -26 -51 -26h-197q-23 -164 -149 -274t-294 -110q-185 0 -316.5 131.5t-131.5 316.5t131.5 316.5t316.5 131.5q114 0 215 -55l137 183h-224q-26 0 -45 19t-19 45t19 45t45 19h384v-128h435l-85 128h-222q-26 0 -45 19t-19 45t19 45t45 19h256q33 0 53 -28l267 -400 q91 44 192 44q185 0 316.5 -131.5t131.5 -316.5z" />
+<glyph unicode="&#xf207;" d="M384 320q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1408 320q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1362 716l-72 384q-5 23 -22.5 37.5t-40.5 14.5 h-918q-23 0 -40.5 -14.5t-22.5 -37.5l-72 -384q-5 -30 14 -53t49 -23h1062q30 0 49 23t14 53zM1136 1328q0 20 -14 34t-34 14h-640q-20 0 -34 -14t-14 -34t14 -34t34 -14h640q20 0 34 14t14 34zM1536 603v-603h-128v-128q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5 t-37.5 90.5v128h-768v-128q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5v128h-128v603q0 112 25 223l103 454q9 78 97.5 137t230 89t312.5 30t312.5 -30t230 -89t97.5 -137l105 -454q23 -102 23 -223z" />
+<glyph unicode="&#xf208;" horiz-adv-x="2048" d="M1463 704q0 -35 -25 -60.5t-61 -25.5h-702q-36 0 -61 25.5t-25 60.5t25 60.5t61 25.5h702q36 0 61 -25.5t25 -60.5zM1677 704q0 86 -23 170h-982q-36 0 -61 25t-25 60q0 36 25 61t61 25h908q-88 143 -235 227t-320 84q-177 0 -327.5 -87.5t-238 -237.5t-87.5 -327 q0 -86 23 -170h982q36 0 61 -25t25 -60q0 -36 -25 -61t-61 -25h-908q88 -143 235.5 -227t320.5 -84q132 0 253 51.5t208 139t139 208t52 253.5zM2048 959q0 -35 -25 -60t-61 -25h-131q17 -85 17 -170q0 -167 -65.5 -319.5t-175.5 -263t-262.5 -176t-319.5 -65.5 q-246 0 -448.5 133t-301.5 350h-189q-36 0 -61 25t-25 61q0 35 25 60t61 25h132q-17 85 -17 170q0 167 65.5 319.5t175.5 263t262.5 176t320.5 65.5q245 0 447.5 -133t301.5 -350h188q36 0 61 -25t25 -61z" />
+<glyph unicode="&#xf209;" horiz-adv-x="1280" d="M953 1158l-114 -328l117 -21q165 451 165 518q0 56 -38 56q-57 0 -130 -225zM654 471l33 -88q37 42 71 67l-33 5.5t-38.5 7t-32.5 8.5zM362 1367q0 -98 159 -521q18 10 49 10q15 0 75 -5l-121 351q-75 220 -123 220q-19 0 -29 -17.5t-10 -37.5zM283 608q0 -36 51.5 -119 t117.5 -153t100 -70q14 0 25.5 13t11.5 27q0 24 -32 102q-13 32 -32 72t-47.5 89t-61.5 81t-62 32q-20 0 -45.5 -27t-25.5 -47zM125 273q0 -41 25 -104q59 -145 183.5 -227t281.5 -82q227 0 382 170q152 169 152 427q0 43 -1 67t-11.5 62t-30.5 56q-56 49 -211.5 75.5 t-270.5 26.5q-37 0 -49 -11q-12 -5 -12 -35q0 -34 21.5 -60t55.5 -40t77.5 -23.5t87.5 -11.5t85 -4t70 0h23q24 0 40 -19q15 -19 19 -55q-28 -28 -96 -54q-61 -22 -93 -46q-64 -46 -108.5 -114t-44.5 -137q0 -31 18.5 -88.5t18.5 -87.5l-3 -12q-4 -12 -4 -14 q-137 10 -146 216q-8 -2 -41 -2q2 -7 2 -21q0 -53 -40.5 -89.5t-94.5 -36.5q-82 0 -166.5 78t-84.5 159q0 34 33 67q52 -64 60 -76q77 -104 133 -104q12 0 26.5 8.5t14.5 20.5q0 34 -87.5 145t-116.5 111q-43 0 -70 -44.5t-27 -90.5zM11 264q0 101 42.5 163t136.5 88 q-28 74 -28 104q0 62 61 123t122 61q29 0 70 -15q-163 462 -163 567q0 80 41 130.5t119 50.5q131 0 325 -581q6 -17 8 -23q6 16 29 79.5t43.5 118.5t54 127.5t64.5 123t70.5 86.5t76.5 36q71 0 112 -49t41 -122q0 -108 -159 -550q61 -15 100.5 -46t58.5 -78t26 -93.5 t7 -110.5q0 -150 -47 -280t-132 -225t-211 -150t-278 -55q-111 0 -223 42q-149 57 -258 191.5t-109 286.5z" />
+<glyph unicode="&#xf20a;" horiz-adv-x="2048" d="M785 528h207q-14 -158 -98.5 -248.5t-214.5 -90.5q-162 0 -254.5 116t-92.5 316q0 194 93 311.5t233 117.5q148 0 232 -87t97 -247h-203q-5 64 -35.5 99t-81.5 35q-57 0 -88.5 -60.5t-31.5 -177.5q0 -48 5 -84t18 -69.5t40 -51.5t66 -18q95 0 109 139zM1497 528h206 q-14 -158 -98 -248.5t-214 -90.5q-162 0 -254.5 116t-92.5 316q0 194 93 311.5t233 117.5q148 0 232 -87t97 -247h-204q-4 64 -35 99t-81 35q-57 0 -88.5 -60.5t-31.5 -177.5q0 -48 5 -84t18 -69.5t39.5 -51.5t65.5 -18q49 0 76.5 38t33.5 101zM1856 647q0 207 -15.5 307 t-60.5 161q-6 8 -13.5 14t-21.5 15t-16 11q-86 63 -697 63q-625 0 -710 -63q-5 -4 -17.5 -11.5t-21 -14t-14.5 -14.5q-45 -60 -60 -159.5t-15 -308.5q0 -208 15 -307.5t60 -160.5q6 -8 15 -15t20.5 -14t17.5 -12q44 -33 239.5 -49t470.5 -16q610 0 697 65q5 4 17 11t20.5 14 t13.5 16q46 60 61 159t15 309zM2048 1408v-1536h-2048v1536h2048z" />
+<glyph unicode="&#xf20b;" d="M992 912v-496q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v496q0 112 -80 192t-192 80h-272v-1152q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v1344q0 14 9 23t23 9h464q135 0 249 -66.5t180.5 -180.5t66.5 -249zM1376 1376v-880q0 -135 -66.5 -249t-180.5 -180.5 t-249 -66.5h-464q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h160q14 0 23 -9t9 -23v-768h272q112 0 192 80t80 192v880q0 14 9 23t23 9h160q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf20c;" d="M1311 694v-114q0 -24 -13.5 -38t-37.5 -14h-202q-24 0 -38 14t-14 38v114q0 24 14 38t38 14h202q24 0 37.5 -14t13.5 -38zM821 464v250q0 53 -32.5 85.5t-85.5 32.5h-133q-68 0 -96 -52q-28 52 -96 52h-130q-53 0 -85.5 -32.5t-32.5 -85.5v-250q0 -22 21 -22h55 q22 0 22 22v230q0 24 13.5 38t38.5 14h94q24 0 38 -14t14 -38v-230q0 -22 21 -22h54q22 0 22 22v230q0 24 14 38t38 14h97q24 0 37.5 -14t13.5 -38v-230q0 -22 22 -22h55q21 0 21 22zM1410 560v154q0 53 -33 85.5t-86 32.5h-264q-53 0 -86 -32.5t-33 -85.5v-410 q0 -21 22 -21h55q21 0 21 21v180q31 -42 94 -42h191q53 0 86 32.5t33 85.5zM1536 1176v-1072q0 -96 -68 -164t-164 -68h-1072q-96 0 -164 68t-68 164v1072q0 96 68 164t164 68h1072q96 0 164 -68t68 -164z" />
+<glyph unicode="&#xf20d;" d="M915 450h-294l147 551zM1001 128h311l-324 1024h-440l-324 -1024h311l383 314zM1536 1120v-960q0 -118 -85 -203t-203 -85h-960q-118 0 -203 85t-85 203v960q0 118 85 203t203 85h960q118 0 203 -85t85 -203z" />
+<glyph unicode="&#xf20e;" horiz-adv-x="2048" d="M2048 641q0 -21 -13 -36.5t-33 -19.5l-205 -356q3 -9 3 -18q0 -20 -12.5 -35.5t-32.5 -19.5l-193 -337q3 -8 3 -16q0 -23 -16.5 -40t-40.5 -17q-25 0 -41 18h-400q-17 -20 -43 -20t-43 20h-399q-17 -20 -43 -20q-23 0 -40 16.5t-17 40.5q0 8 4 20l-193 335 q-20 4 -32.5 19.5t-12.5 35.5q0 9 3 18l-206 356q-20 5 -32.5 20.5t-12.5 35.5q0 21 13.5 36.5t33.5 19.5l199 344q0 1 -0.5 3t-0.5 3q0 36 34 51l209 363q-4 10 -4 18q0 24 17 40.5t40 16.5q26 0 44 -21h396q16 21 43 21t43 -21h398q18 21 44 21q23 0 40 -16.5t17 -40.5 q0 -6 -4 -18l207 -358q23 -1 39 -17.5t16 -38.5q0 -13 -7 -27l187 -324q19 -4 31.5 -19.5t12.5 -35.5zM1063 -158h389l-342 354h-143l-342 -354h360q18 16 39 16t39 -16zM112 654q1 -4 1 -13q0 -10 -2 -15l208 -360q2 0 4.5 -1t5.5 -2.5l5 -2.5l188 199v347l-187 194 q-13 -8 -29 -10zM986 1438h-388l190 -200l554 200h-280q-16 -16 -38 -16t-38 16zM1689 226q1 6 5 11l-64 68l-17 -79h76zM1583 226l22 105l-252 266l-296 -307l63 -64h463zM1495 -142l16 28l65 310h-427l333 -343q8 4 13 5zM578 -158h5l342 354h-373v-335l4 -6q14 -5 22 -13 zM552 226h402l64 66l-309 321l-157 -166v-221zM359 226h163v189l-168 -177q4 -8 5 -12zM358 1051q0 -1 0.5 -2t0.5 -2q0 -16 -8 -29l171 -177v269zM552 1121v-311l153 -157l297 314l-223 236zM556 1425l-4 -8v-264l205 74l-191 201q-6 -2 -10 -3zM1447 1438h-16l-621 -224 l213 -225zM1023 946l-297 -315l311 -319l296 307zM688 634l-136 141v-284zM1038 270l-42 -44h85zM1374 618l238 -251l132 624l-3 5l-1 1zM1718 1018q-8 13 -8 29v2l-216 376q-5 1 -13 5l-437 -463l310 -327zM522 1142v223l-163 -282zM522 196h-163l163 -283v283zM1607 196 l-48 -227l130 227h-82zM1729 266l207 361q-2 10 -2 14q0 1 3 16l-171 296l-129 -612l77 -82q5 3 15 7z" />
+<glyph unicode="&#xf210;" d="M0 856q0 131 91.5 226.5t222.5 95.5h742l352 358v-1470q0 -132 -91.5 -227t-222.5 -95h-780q-131 0 -222.5 95t-91.5 227v790zM1232 102l-176 180v425q0 46 -32 79t-78 33h-484q-46 0 -78 -33t-32 -79v-492q0 -46 32.5 -79.5t77.5 -33.5h770z" />
+<glyph unicode="&#xf211;" d="M934 1386q-317 -121 -556 -362.5t-358 -560.5q-20 89 -20 176q0 208 102.5 384.5t278.5 279t384 102.5q82 0 169 -19zM1203 1267q93 -65 164 -155q-389 -113 -674.5 -400.5t-396.5 -676.5q-93 72 -155 162q112 386 395 671t667 399zM470 -67q115 356 379.5 622t619.5 384 q40 -92 54 -195q-292 -120 -516 -345t-343 -518q-103 14 -194 52zM1536 -125q-193 50 -367 115q-135 -84 -290 -107q109 205 274 370.5t369 275.5q-21 -152 -101 -284q65 -175 115 -370z" />
+<glyph unicode="&#xf212;" horiz-adv-x="2048" d="M1893 1144l155 -1272q-131 0 -257 57q-200 91 -393 91q-226 0 -374 -148q-148 148 -374 148q-193 0 -393 -91q-128 -57 -252 -57h-5l155 1272q224 127 482 127q233 0 387 -106q154 106 387 106q258 0 482 -127zM1398 157q129 0 232 -28.5t260 -93.5l-124 1021 q-171 78 -368 78q-224 0 -374 -141q-150 141 -374 141q-197 0 -368 -78l-124 -1021q105 43 165.5 65t148.5 39.5t178 17.5q202 0 374 -108q172 108 374 108zM1438 191l-55 907q-211 -4 -359 -155q-152 155 -374 155q-176 0 -336 -66l-114 -941q124 51 228.5 76t221.5 25 q209 0 374 -102q172 107 374 102z" />
+<glyph unicode="&#xf213;" horiz-adv-x="2048" d="M1500 165v733q0 21 -15 36t-35 15h-93q-20 0 -35 -15t-15 -36v-733q0 -20 15 -35t35 -15h93q20 0 35 15t15 35zM1216 165v531q0 20 -15 35t-35 15h-101q-20 0 -35 -15t-15 -35v-531q0 -20 15 -35t35 -15h101q20 0 35 15t15 35zM924 165v429q0 20 -15 35t-35 15h-101 q-20 0 -35 -15t-15 -35v-429q0 -20 15 -35t35 -15h101q20 0 35 15t15 35zM632 165v362q0 20 -15 35t-35 15h-101q-20 0 -35 -15t-15 -35v-362q0 -20 15 -35t35 -15h101q20 0 35 15t15 35zM2048 311q0 -166 -118 -284t-284 -118h-1244q-166 0 -284 118t-118 284 q0 116 63 214.5t168 148.5q-10 34 -10 73q0 113 80.5 193.5t193.5 80.5q102 0 180 -67q45 183 194 300t338 117q149 0 275 -73.5t199.5 -199.5t73.5 -275q0 -66 -14 -122q135 -33 221 -142.5t86 -247.5z" />
+<glyph unicode="&#xf214;" d="M0 1536h1536v-1392l-776 -338l-760 338v1392zM1436 209v926h-1336v-926l661 -294zM1436 1235v201h-1336v-201h1336zM181 937v-115h-37v115h37zM181 789v-115h-37v115h37zM181 641v-115h-37v115h37zM181 493v-115h-37v115h37zM181 345v-115h-37v115h37zM207 202l15 34 l105 -47l-15 -33zM343 142l15 34l105 -46l-15 -34zM478 82l15 34l105 -46l-15 -34zM614 23l15 33l104 -46l-15 -34zM797 10l105 46l15 -33l-105 -47zM932 70l105 46l15 -34l-105 -46zM1068 130l105 46l15 -34l-105 -46zM1203 189l105 47l15 -34l-105 -46zM259 1389v-36h-114 v36h114zM421 1389v-36h-115v36h115zM583 1389v-36h-115v36h115zM744 1389v-36h-114v36h114zM906 1389v-36h-114v36h114zM1068 1389v-36h-115v36h115zM1230 1389v-36h-115v36h115zM1391 1389v-36h-114v36h114zM181 1049v-79h-37v115h115v-36h-78zM421 1085v-36h-115v36h115z M583 1085v-36h-115v36h115zM744 1085v-36h-114v36h114zM906 1085v-36h-114v36h114zM1068 1085v-36h-115v36h115zM1230 1085v-36h-115v36h115zM1355 970v79h-78v36h115v-115h-37zM1355 822v115h37v-115h-37zM1355 674v115h37v-115h-37zM1355 526v115h37v-115h-37zM1355 378 v115h37v-115h-37zM1355 230v115h37v-115h-37zM760 265q-129 0 -221 91.5t-92 221.5q0 129 92 221t221 92q130 0 221.5 -92t91.5 -221q0 -130 -91.5 -221.5t-221.5 -91.5zM595 646q0 -36 19.5 -56.5t49.5 -25t64 -7t64 -2t49.5 -9t19.5 -30.5q0 -49 -112 -49q-97 0 -123 51 h-3l-31 -63q67 -42 162 -42q29 0 56.5 5t55.5 16t45.5 33t17.5 53q0 46 -27.5 69.5t-67.5 27t-79.5 3t-67 5t-27.5 25.5q0 21 20.5 33t40.5 15t41 3q34 0 70.5 -11t51.5 -34h3l30 58q-3 1 -21 8.5t-22.5 9t-19.5 7t-22 7t-20 4.5t-24 4t-23 1q-29 0 -56.5 -5t-54 -16.5 t-43 -34t-16.5 -53.5z" />
+<glyph unicode="&#xf215;" horiz-adv-x="2048" d="M863 504q0 112 -79.5 191.5t-191.5 79.5t-191 -79.5t-79 -191.5t79 -191t191 -79t191.5 79t79.5 191zM1726 505q0 112 -79 191t-191 79t-191.5 -79t-79.5 -191q0 -113 79.5 -192t191.5 -79t191 79.5t79 191.5zM2048 1314v-1348q0 -44 -31.5 -75.5t-76.5 -31.5h-1832 q-45 0 -76.5 31.5t-31.5 75.5v1348q0 44 31.5 75.5t76.5 31.5h431q44 0 76 -31.5t32 -75.5v-161h754v161q0 44 32 75.5t76 31.5h431q45 0 76.5 -31.5t31.5 -75.5z" />
+<glyph unicode="&#xf216;" horiz-adv-x="2048" d="M1430 953zM1690 749q148 0 253 -98.5t105 -244.5q0 -157 -109 -261.5t-267 -104.5q-85 0 -162 27.5t-138 73.5t-118 106t-109 126.5t-103.5 132.5t-108.5 126t-117 106t-136 73.5t-159 27.5q-154 0 -251.5 -91.5t-97.5 -244.5q0 -157 104 -250t263 -93q100 0 208 37.5 t193 98.5q5 4 21 18.5t30 24t22 9.5q14 0 24.5 -10.5t10.5 -24.5q0 -24 -60 -77q-101 -88 -234.5 -142t-260.5 -54q-133 0 -245.5 58t-180 165t-67.5 241q0 205 141.5 341t347.5 136q120 0 226.5 -43.5t185.5 -113t151.5 -153t139 -167.5t133.5 -153.5t149.5 -113 t172.5 -43.5q102 0 168.5 61.5t66.5 162.5q0 95 -64.5 159t-159.5 64q-30 0 -81.5 -18.5t-68.5 -18.5q-20 0 -35.5 15t-15.5 35q0 18 8.5 57t8.5 59q0 159 -107.5 263t-266.5 104q-58 0 -111.5 -18.5t-84 -40.5t-55.5 -40.5t-33 -18.5q-15 0 -25.5 10.5t-10.5 25.5 q0 19 25 46q59 67 147 103.5t182 36.5q191 0 318 -125.5t127 -315.5q0 -37 -4 -66q57 15 115 15z" />
+<glyph unicode="&#xf217;" horiz-adv-x="1664" d="M1216 832q0 26 -19 45t-45 19h-128v128q0 26 -19 45t-45 19t-45 -19t-19 -45v-128h-128q-26 0 -45 -19t-19 -45t19 -45t45 -19h128v-128q0 -26 19 -45t45 -19t45 19t19 45v128h128q26 0 45 19t19 45zM640 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5 t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1536 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1664 1088v-512q0 -24 -16 -42.5t-41 -21.5l-1044 -122q1 -7 4.5 -21.5t6 -26.5t2.5 -22q0 -16 -24 -64h920 q26 0 45 -19t19 -45t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 14 11 39.5t29.5 59.5t20.5 38l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t20 -15.5t13 -24.5t7.5 -26.5t5.5 -29.5t4.5 -25.5h1201q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf218;" horiz-adv-x="1664" d="M1280 832q0 26 -19 45t-45 19t-45 -19l-147 -146v293q0 26 -19 45t-45 19t-45 -19t-19 -45v-293l-147 146q-19 19 -45 19t-45 -19t-19 -45t19 -45l256 -256q19 -19 45 -19t45 19l256 256q19 19 19 45zM640 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5 t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1536 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1664 1088v-512q0 -24 -16 -42.5t-41 -21.5l-1044 -122q1 -7 4.5 -21.5t6 -26.5t2.5 -22q0 -16 -24 -64h920 q26 0 45 -19t19 -45t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 14 11 39.5t29.5 59.5t20.5 38l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t20 -15.5t13 -24.5t7.5 -26.5t5.5 -29.5t4.5 -25.5h1201q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf219;" horiz-adv-x="2048" d="M212 768l623 -665l-300 665h-323zM1024 -4l349 772h-698zM538 896l204 384h-262l-288 -384h346zM1213 103l623 665h-323zM683 896h682l-204 384h-274zM1510 896h346l-288 384h-262zM1651 1382l384 -512q14 -18 13 -41.5t-17 -40.5l-960 -1024q-18 -20 -47 -20t-47 20 l-960 1024q-16 17 -17 40.5t13 41.5l384 512q18 26 51 26h1152q33 0 51 -26z" />
+<glyph unicode="&#xf21a;" horiz-adv-x="2048" d="M1811 -19q19 19 45 19t45 -19l128 -128l-90 -90l-83 83l-83 -83q-18 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83 q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-128 128l90 90l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83 q19 19 45 19t45 -19l83 -83zM237 19q-19 -19 -45 -19t-45 19l-128 128l90 90l83 -82l83 82q19 19 45 19t45 -19l83 -82l64 64v293l-210 314q-17 26 -7 56.5t40 40.5l177 58v299h128v128h256v128h256v-128h256v-128h128v-299l177 -58q30 -10 40 -40.5t-7 -56.5l-210 -314 v-293l19 18q19 19 45 19t45 -19l83 -82l83 82q19 19 45 19t45 -19l128 -128l-90 -90l-83 83l-83 -83q-18 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83 q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83zM640 1152v-128l384 128l384 -128v128h-128v128h-512v-128h-128z" />
+<glyph unicode="&#xf21b;" d="M576 0l96 448l-96 128l-128 64zM832 0l128 640l-128 -64l-96 -128zM992 1010q-2 4 -4 6q-10 8 -96 8q-70 0 -167 -19q-7 -2 -21 -2t-21 2q-97 19 -167 19q-86 0 -96 -8q-2 -2 -4 -6q2 -18 4 -27q2 -3 7.5 -6.5t7.5 -10.5q2 -4 7.5 -20.5t7 -20.5t7.5 -17t8.5 -17t9 -14 t12 -13.5t14 -9.5t17.5 -8t20.5 -4t24.5 -2q36 0 59 12.5t32.5 30t14.5 34.5t11.5 29.5t17.5 12.5h12q11 0 17.5 -12.5t11.5 -29.5t14.5 -34.5t32.5 -30t59 -12.5q13 0 24.5 2t20.5 4t17.5 8t14 9.5t12 13.5t9 14t8.5 17t7.5 17t7 20.5t7.5 20.5q2 7 7.5 10.5t7.5 6.5 q2 9 4 27zM1408 131q0 -121 -73 -190t-194 -69h-874q-121 0 -194 69t-73 190q0 61 4.5 118t19 125.5t37.5 123.5t63.5 103.5t93.5 74.5l-90 220h214q-22 64 -22 128q0 12 2 32q-194 40 -194 96q0 57 210 99q17 62 51.5 134t70.5 114q32 37 76 37q30 0 84 -31t84 -31t84 31 t84 31q44 0 76 -37q36 -42 70.5 -114t51.5 -134q210 -42 210 -99q0 -56 -194 -96q7 -81 -20 -160h214l-82 -225q63 -33 107.5 -96.5t65.5 -143.5t29 -151.5t8 -148.5z" />
+<glyph unicode="&#xf21c;" horiz-adv-x="2304" d="M2301 500q12 -103 -22 -198.5t-99 -163.5t-158.5 -106t-196.5 -31q-161 11 -279.5 125t-134.5 274q-12 111 27.5 210.5t118.5 170.5l-71 107q-96 -80 -151 -194t-55 -244q0 -27 -18.5 -46.5t-45.5 -19.5h-256h-69q-23 -164 -149 -274t-294 -110q-185 0 -316.5 131.5 t-131.5 316.5t131.5 316.5t316.5 131.5q76 0 152 -27l24 45q-123 110 -304 110h-64q-26 0 -45 19t-19 45t19 45t45 19h128q78 0 145 -13.5t116.5 -38.5t71.5 -39.5t51 -36.5h512h115l-85 128h-222q-30 0 -49 22.5t-14 52.5q4 23 23 38t43 15h253q33 0 53 -28l70 -105 l114 114q19 19 46 19h101q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-179l115 -172q131 63 275 36q143 -26 244 -134.5t118 -253.5zM448 128q115 0 203 72.5t111 183.5h-314q-35 0 -55 31q-18 32 -1 63l147 277q-47 13 -91 13q-132 0 -226 -94t-94 -226t94 -226 t226 -94zM1856 128q132 0 226 94t94 226t-94 226t-226 94q-60 0 -121 -24l174 -260q15 -23 10 -49t-27 -40q-15 -11 -36 -11q-35 0 -53 29l-174 260q-93 -95 -93 -225q0 -132 94 -226t226 -94z" />
+<glyph unicode="&#xf21d;" d="M1408 0q0 -63 -61.5 -113.5t-164 -81t-225 -46t-253.5 -15.5t-253.5 15.5t-225 46t-164 81t-61.5 113.5q0 49 33 88.5t91 66.5t118 44.5t131 29.5q26 5 48 -10.5t26 -41.5q5 -26 -10.5 -48t-41.5 -26q-58 -10 -106 -23.5t-76.5 -25.5t-48.5 -23.5t-27.5 -19.5t-8.5 -12 q3 -11 27 -26.5t73 -33t114 -32.5t160.5 -25t201.5 -10t201.5 10t160.5 25t114 33t73 33.5t27 27.5q-1 4 -8.5 11t-27.5 19t-48.5 23.5t-76.5 25t-106 23.5q-26 4 -41.5 26t-10.5 48q4 26 26 41.5t48 10.5q71 -12 131 -29.5t118 -44.5t91 -66.5t33 -88.5zM1024 896v-384 q0 -26 -19 -45t-45 -19h-64v-384q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v384h-64q-26 0 -45 19t-19 45v384q0 53 37.5 90.5t90.5 37.5h384q53 0 90.5 -37.5t37.5 -90.5zM928 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5 t158.5 -65.5t65.5 -158.5z" />
+<glyph unicode="&#xf21e;" horiz-adv-x="1792" d="M1280 512h305q-5 -6 -10 -10.5t-9 -7.5l-3 -4l-623 -600q-18 -18 -44 -18t-44 18l-624 602q-5 2 -21 20h369q22 0 39.5 13.5t22.5 34.5l70 281l190 -667q6 -20 23 -33t39 -13q21 0 38 13t23 33l146 485l56 -112q18 -35 57 -35zM1792 940q0 -145 -103 -300h-369l-111 221 q-8 17 -25.5 27t-36.5 8q-45 -5 -56 -46l-129 -430l-196 686q-6 20 -23.5 33t-39.5 13t-39 -13.5t-22 -34.5l-116 -464h-423q-103 155 -103 300q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5q224 0 351 -124 t127 -344z" />
+<glyph unicode="&#xf221;" horiz-adv-x="1280" d="M1152 960q0 -221 -147.5 -384.5t-364.5 -187.5v-260h224q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v260q-150 16 -271.5 103t-186 224t-52.5 292 q11 134 80.5 249t182 188t245.5 88q170 19 319 -54t236 -212t87 -306zM128 960q0 -185 131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5z" />
+<glyph unicode="&#xf222;" d="M1472 1408q26 0 45 -19t19 -45v-416q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v262l-382 -383q126 -156 126 -359q0 -117 -45.5 -223.5t-123 -184t-184 -123t-223.5 -45.5t-223.5 45.5t-184 123t-123 184t-45.5 223.5t45.5 223.5t123 184t184 123t223.5 45.5 q203 0 359 -126l382 382h-261q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h416zM576 0q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf223;" horiz-adv-x="1280" d="M830 1220q145 -72 233.5 -210.5t88.5 -305.5q0 -221 -147.5 -384.5t-364.5 -187.5v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-217 24 -364.5 187.5 t-147.5 384.5q0 167 88.5 305.5t233.5 210.5q-165 96 -228 273q-6 16 3.5 29.5t26.5 13.5h69q21 0 29 -20q44 -106 140 -171t214 -65t214 65t140 171q8 20 37 20h61q17 0 26.5 -13.5t3.5 -29.5q-63 -177 -228 -273zM576 256q185 0 316.5 131.5t131.5 316.5t-131.5 316.5 t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf224;" d="M1024 1504q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q126 -158 126 -359q0 -221 -147.5 -384.5t-364.5 -187.5v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64 q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-149 16 -270.5 103t-186.5 223.5t-53 291.5q16 204 160 353.5t347 172.5q118 14 228 -19t198 -103l255 254h-134q-14 0 -23 9t-9 23v64zM576 256q185 0 316.5 131.5t131.5 316.5t-131.5 316.5 t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf225;" horiz-adv-x="1792" d="M1280 1504q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q126 -158 126 -359q0 -221 -147.5 -384.5t-364.5 -187.5v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64 q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-217 24 -364.5 187.5t-147.5 384.5q0 201 126 359l-52 53l-101 -111q-9 -10 -22 -10.5t-23 7.5l-48 44q-10 8 -10.5 21.5t8.5 23.5l105 115l-111 112v-134q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9 t-9 23v288q0 26 19 45t45 19h288q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-133l106 -107l86 94q9 10 22 10.5t23 -7.5l48 -44q10 -8 10.5 -21.5t-8.5 -23.5l-90 -99l57 -56q158 126 359 126t359 -126l255 254h-134q-14 0 -23 9t-9 23v64zM832 256q185 0 316.5 131.5 t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf226;" horiz-adv-x="1792" d="M1790 1007q12 -155 -52.5 -292t-186 -224t-271.5 -103v-260h224q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-512v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23 t23 9h224v260q-150 16 -271.5 103t-186 224t-52.5 292q17 206 164.5 356.5t352.5 169.5q206 21 377 -94q171 115 377 94q205 -19 352.5 -169.5t164.5 -356.5zM896 647q128 131 128 313t-128 313q-128 -131 -128 -313t128 -313zM576 512q115 0 218 57q-154 165 -154 391 q0 224 154 391q-103 57 -218 57q-185 0 -316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5zM1152 128v260q-137 15 -256 94q-119 -79 -256 -94v-260h512zM1216 512q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5q-115 0 -218 -57q154 -167 154 -391 q0 -226 -154 -391q103 -57 218 -57z" />
+<glyph unicode="&#xf227;" horiz-adv-x="1920" d="M1536 1120q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q76 -95 107.5 -214t9.5 -247q-31 -182 -166 -312t-318 -156q-210 -29 -384.5 80t-241.5 300q-117 6 -221 57.5t-177.5 133t-113.5 192.5t-32 230 q9 135 78 252t182 191.5t248 89.5q118 14 227.5 -19t198.5 -103l255 254h-134q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q59 -74 93 -169q182 -9 328 -124l255 254h-134q-14 0 -23 9 t-9 23v64zM1024 704q0 20 -4 58q-162 -25 -271 -150t-109 -292q0 -20 4 -58q162 25 271 150t109 292zM128 704q0 -168 111 -294t276 -149q-3 29 -3 59q0 210 135 369.5t338 196.5q-53 120 -163.5 193t-245.5 73q-185 0 -316.5 -131.5t-131.5 -316.5zM1088 -128 q185 0 316.5 131.5t131.5 316.5q0 168 -111 294t-276 149q3 -29 3 -59q0 -210 -135 -369.5t-338 -196.5q53 -120 163.5 -193t245.5 -73z" />
+<glyph unicode="&#xf228;" horiz-adv-x="2048" d="M1664 1504q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q76 -95 107.5 -214t9.5 -247q-32 -180 -164.5 -310t-313.5 -157q-223 -34 -409 90q-117 -78 -256 -93v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23 t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-155 17 -279.5 109.5t-187 237.5t-39.5 307q25 187 159.5 322.5t320.5 164.5q224 34 410 -90q146 97 320 97q201 0 359 -126l255 254h-134q-14 0 -23 9 t-9 23v64zM896 391q128 131 128 313t-128 313q-128 -131 -128 -313t128 -313zM128 704q0 -185 131.5 -316.5t316.5 -131.5q117 0 218 57q-154 167 -154 391t154 391q-101 57 -218 57q-185 0 -316.5 -131.5t-131.5 -316.5zM1216 256q185 0 316.5 131.5t131.5 316.5 t-131.5 316.5t-316.5 131.5q-117 0 -218 -57q154 -167 154 -391t-154 -391q101 -57 218 -57z" />
+<glyph unicode="&#xf229;" d="M1472 1408q26 0 45 -19t19 -45v-416q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v262l-213 -214l140 -140q9 -10 9 -23t-9 -22l-46 -46q-9 -9 -22 -9t-23 9l-140 141l-78 -79q126 -156 126 -359q0 -117 -45.5 -223.5t-123 -184t-184 -123t-223.5 -45.5t-223.5 45.5 t-184 123t-123 184t-45.5 223.5t45.5 223.5t123 184t184 123t223.5 45.5q203 0 359 -126l78 78l-172 172q-9 10 -9 23t9 22l46 46q9 9 22 9t23 -9l172 -172l213 213h-261q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h416zM576 0q185 0 316.5 131.5t131.5 316.5t-131.5 316.5 t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf22a;" horiz-adv-x="1280" d="M640 892q217 -24 364.5 -187.5t147.5 -384.5q0 -167 -87 -306t-236 -212t-319 -54q-133 15 -245.5 88t-182 188t-80.5 249q-12 155 52.5 292t186 224t271.5 103v132h-160q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h160v165l-92 -92q-10 -9 -23 -9t-22 9l-46 46q-9 9 -9 22 t9 23l202 201q19 19 45 19t45 -19l202 -201q9 -10 9 -23t-9 -22l-46 -46q-9 -9 -22 -9t-23 9l-92 92v-165h160q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-160v-132zM576 -128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5 t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf22b;" horiz-adv-x="2048" d="M1901 621q19 -19 19 -45t-19 -45l-294 -294q-9 -10 -22.5 -10t-22.5 10l-45 45q-10 9 -10 22.5t10 22.5l185 185h-294v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-132q-24 -217 -187.5 -364.5t-384.5 -147.5q-167 0 -306 87t-212 236t-54 319q15 133 88 245.5 t188 182t249 80.5q155 12 292 -52.5t224 -186t103 -271.5h132v224q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-224h294l-185 185q-10 9 -10 22.5t10 22.5l45 45q9 10 22.5 10t22.5 -10zM576 128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5 t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf22c;" horiz-adv-x="1280" d="M1152 960q0 -221 -147.5 -384.5t-364.5 -187.5v-612q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v612q-217 24 -364.5 187.5t-147.5 384.5q0 117 45.5 223.5t123 184t184 123t223.5 45.5t223.5 -45.5t184 -123t123 -184t45.5 -223.5zM576 512q185 0 316.5 131.5 t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf22d;" horiz-adv-x="1280" d="M1024 576q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1152 576q0 -117 -45.5 -223.5t-123 -184t-184 -123t-223.5 -45.5t-223.5 45.5t-184 123t-123 184t-45.5 223.5t45.5 223.5t123 184t184 123 t223.5 45.5t223.5 -45.5t184 -123t123 -184t45.5 -223.5z" />
+<glyph unicode="&#xf22e;" horiz-adv-x="1792" />
+<glyph unicode="&#xf22f;" horiz-adv-x="1792" />
+<glyph unicode="&#xf230;" d="M1451 1408q35 0 60 -25t25 -60v-1366q0 -35 -25 -60t-60 -25h-391v595h199l30 232h-229v148q0 56 23.5 84t91.5 28l122 1v207q-63 9 -178 9q-136 0 -217.5 -80t-81.5 -226v-171h-200v-232h200v-595h-735q-35 0 -60 25t-25 60v1366q0 35 25 60t60 25h1366z" />
+<glyph unicode="&#xf231;" horiz-adv-x="1280" d="M0 939q0 108 37.5 203.5t103.5 166.5t152 123t185 78t202 26q158 0 294 -66.5t221 -193.5t85 -287q0 -96 -19 -188t-60 -177t-100 -149.5t-145 -103t-189 -38.5q-68 0 -135 32t-96 88q-10 -39 -28 -112.5t-23.5 -95t-20.5 -71t-26 -71t-32 -62.5t-46 -77.5t-62 -86.5 l-14 -5l-9 10q-15 157 -15 188q0 92 21.5 206.5t66.5 287.5t52 203q-32 65 -32 169q0 83 52 156t132 73q61 0 95 -40.5t34 -102.5q0 -66 -44 -191t-44 -187q0 -63 45 -104.5t109 -41.5q55 0 102 25t78.5 68t56 95t38 110.5t20 111t6.5 99.5q0 173 -109.5 269.5t-285.5 96.5 q-200 0 -334 -129.5t-134 -328.5q0 -44 12.5 -85t27 -65t27 -45.5t12.5 -30.5q0 -28 -15 -73t-37 -45q-2 0 -17 3q-51 15 -90.5 56t-61 94.5t-32.5 108t-11 106.5z" />
+<glyph unicode="&#xf232;" d="M985 562q13 0 97.5 -44t89.5 -53q2 -5 2 -15q0 -33 -17 -76q-16 -39 -71 -65.5t-102 -26.5q-57 0 -190 62q-98 45 -170 118t-148 185q-72 107 -71 194v8q3 91 74 158q24 22 52 22q6 0 18 -1.5t19 -1.5q19 0 26.5 -6.5t15.5 -27.5q8 -20 33 -88t25 -75q0 -21 -34.5 -57.5 t-34.5 -46.5q0 -7 5 -15q34 -73 102 -137q56 -53 151 -101q12 -7 22 -7q15 0 54 48.5t52 48.5zM782 32q127 0 243.5 50t200.5 134t134 200.5t50 243.5t-50 243.5t-134 200.5t-200.5 134t-243.5 50t-243.5 -50t-200.5 -134t-134 -200.5t-50 -243.5q0 -203 120 -368l-79 -233 l242 77q158 -104 345 -104zM782 1414q153 0 292.5 -60t240.5 -161t161 -240.5t60 -292.5t-60 -292.5t-161 -240.5t-240.5 -161t-292.5 -60q-195 0 -365 94l-417 -134l136 405q-108 178 -108 389q0 153 60 292.5t161 240.5t240.5 161t292.5 60z" />
+<glyph unicode="&#xf233;" horiz-adv-x="1792" d="M128 128h1024v128h-1024v-128zM128 640h1024v128h-1024v-128zM1696 192q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM128 1152h1024v128h-1024v-128zM1696 704q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1696 1216 q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1792 384v-384h-1792v384h1792zM1792 896v-384h-1792v384h1792zM1792 1408v-384h-1792v384h1792z" />
+<glyph unicode="&#xf234;" horiz-adv-x="2048" d="M704 640q-159 0 -271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5t-112.5 -271.5t-271.5 -112.5zM1664 512h352q13 0 22.5 -9.5t9.5 -22.5v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-352v-352q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5 t-9.5 22.5v352h-352q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h352v352q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5v-352zM928 288q0 -52 38 -90t90 -38h256v-238q-68 -50 -171 -50h-874q-121 0 -194 69t-73 190q0 53 3.5 103.5t14 109t26.5 108.5 t43 97.5t62 81t85.5 53.5t111.5 20q19 0 39 -17q79 -61 154.5 -91.5t164.5 -30.5t164.5 30.5t154.5 91.5q20 17 39 17q132 0 217 -96h-223q-52 0 -90 -38t-38 -90v-192z" />
+<glyph unicode="&#xf235;" horiz-adv-x="2048" d="M704 640q-159 0 -271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5t-112.5 -271.5t-271.5 -112.5zM1781 320l249 -249q9 -9 9 -23q0 -13 -9 -22l-136 -136q-9 -9 -22 -9q-14 0 -23 9l-249 249l-249 -249q-9 -9 -23 -9q-13 0 -22 9l-136 136 q-9 9 -9 22q0 14 9 23l249 249l-249 249q-9 9 -9 23q0 13 9 22l136 136q9 9 22 9q14 0 23 -9l249 -249l249 249q9 9 23 9q13 0 22 -9l136 -136q9 -9 9 -22q0 -14 -9 -23zM1283 320l-181 -181q-37 -37 -37 -91q0 -53 37 -90l83 -83q-21 -3 -44 -3h-874q-121 0 -194 69 t-73 190q0 53 3.5 103.5t14 109t26.5 108.5t43 97.5t62 81t85.5 53.5t111.5 20q19 0 39 -17q154 -122 319 -122t319 122q20 17 39 17q28 0 57 -6q-28 -27 -41 -50t-13 -56q0 -54 37 -91z" />
+<glyph unicode="&#xf236;" horiz-adv-x="2048" d="M256 512h1728q26 0 45 -19t19 -45v-448h-256v256h-1536v-256h-256v1216q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-704zM832 832q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM2048 576v64q0 159 -112.5 271.5t-271.5 112.5h-704 q-26 0 -45 -19t-19 -45v-384h1152z" />
+<glyph unicode="&#xf237;" d="M1536 1536l-192 -448h192v-192h-274l-55 -128h329v-192h-411l-357 -832l-357 832h-411v192h329l-55 128h-274v192h192l-192 448h256l323 -768h378l323 768h256zM768 320l108 256h-216z" />
+<glyph unicode="&#xf238;" d="M1088 1536q185 0 316.5 -93.5t131.5 -226.5v-896q0 -130 -125.5 -222t-305.5 -97l213 -202q16 -15 8 -35t-30 -20h-1056q-22 0 -30 20t8 35l213 202q-180 5 -305.5 97t-125.5 222v896q0 133 131.5 226.5t316.5 93.5h640zM768 192q80 0 136 56t56 136t-56 136t-136 56 t-136 -56t-56 -136t56 -136t136 -56zM1344 768v512h-1152v-512h1152z" />
+<glyph unicode="&#xf239;" d="M1088 1536q185 0 316.5 -93.5t131.5 -226.5v-896q0 -130 -125.5 -222t-305.5 -97l213 -202q16 -15 8 -35t-30 -20h-1056q-22 0 -30 20t8 35l213 202q-180 5 -305.5 97t-125.5 222v896q0 133 131.5 226.5t316.5 93.5h640zM288 224q66 0 113 47t47 113t-47 113t-113 47 t-113 -47t-47 -113t47 -113t113 -47zM704 768v512h-544v-512h544zM1248 224q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47zM1408 768v512h-576v-512h576z" />
+<glyph unicode="&#xf23a;" horiz-adv-x="1792" d="M597 1115v-1173q0 -25 -12.5 -42.5t-36.5 -17.5q-17 0 -33 8l-465 233q-21 10 -35.5 33.5t-14.5 46.5v1140q0 20 10 34t29 14q14 0 44 -15l511 -256q3 -3 3 -5zM661 1014l534 -866l-534 266v600zM1792 996v-1054q0 -25 -14 -40.5t-38 -15.5t-47 13l-441 220zM1789 1116 q0 -3 -256.5 -419.5t-300.5 -487.5l-390 634l324 527q17 28 52 28q14 0 26 -6l541 -270q4 -2 4 -6z" />
+<glyph unicode="&#xf23b;" d="M809 532l266 499h-112l-157 -312q-24 -48 -44 -92l-42 92l-155 312h-120l263 -493v-324h101v318zM1536 1408v-1536h-1536v1536h1536z" />
+<glyph unicode="&#xf23c;" horiz-adv-x="2296" d="M478 -139q-8 -16 -27 -34.5t-37 -25.5q-25 -9 -51.5 3.5t-28.5 31.5q-1 22 40 55t68 38q23 4 34 -21.5t2 -46.5zM1819 -139q7 -16 26 -34.5t38 -25.5q25 -9 51.5 3.5t27.5 31.5q2 22 -39.5 55t-68.5 38q-22 4 -33 -21.5t-2 -46.5zM1867 -30q13 -27 56.5 -59.5t77.5 -41.5 q45 -13 82 4.5t37 50.5q0 46 -67.5 100.5t-115.5 59.5q-40 5 -63.5 -37.5t-6.5 -76.5zM428 -30q-13 -27 -56 -59.5t-77 -41.5q-45 -13 -82 4.5t-37 50.5q0 46 67.5 100.5t115.5 59.5q40 5 63 -37.5t6 -76.5zM1158 1094h1q-41 0 -76 -15q27 -8 44 -30.5t17 -49.5 q0 -35 -27 -60t-65 -25q-52 0 -80 43q-5 -23 -5 -42q0 -74 56 -126.5t135 -52.5q80 0 136 52.5t56 126.5t-56 126.5t-136 52.5zM1462 1312q-99 109 -220.5 131.5t-245.5 -44.5q27 60 82.5 96.5t118 39.5t121.5 -17t99.5 -74.5t44.5 -131.5zM2212 73q8 -11 -11 -42 q7 -23 7 -40q1 -56 -44.5 -112.5t-109.5 -91.5t-118 -37q-48 -2 -92 21.5t-66 65.5q-687 -25 -1259 0q-23 -41 -66.5 -65t-92.5 -22q-86 3 -179.5 80.5t-92.5 160.5q2 22 7 40q-19 31 -11 42q6 10 31 1q14 22 41 51q-7 29 2 38q11 10 39 -4q29 20 59 34q0 29 13 37 q23 12 51 -16q35 5 61 -2q18 -4 38 -19v73q-11 0 -18 2q-53 10 -97 44.5t-55 87.5q-9 38 0 81q15 62 93 95q2 17 19 35.5t36 23.5t33 -7.5t19 -30.5h13q46 -5 60 -23q3 -3 5 -7q10 1 30.5 3.5t30.5 3.5q-15 11 -30 17q-23 40 -91 43q0 6 1 10q-62 2 -118.5 18.5t-84.5 47.5 q-32 36 -42.5 92t-2.5 112q16 126 90 179q23 16 52 4.5t32 -40.5q0 -1 1.5 -14t2.5 -21t3 -20t5.5 -19t8.5 -10q27 -14 76 -12q48 46 98 74q-40 4 -162 -14l47 46q61 58 163 111q145 73 282 86q-20 8 -41 15.5t-47 14t-42.5 10.5t-47.5 11t-43 10q595 126 904 -139 q98 -84 158 -222q85 -10 121 9h1q5 3 8.5 10t5.5 19t3 19.5t3 21.5l1 14q3 28 32 40t52 -5q73 -52 91 -178q7 -57 -3.5 -113t-42.5 -91q-28 -32 -83.5 -48.5t-115.5 -18.5v-10q-71 -2 -95 -43q-14 -5 -31 -17q11 -1 32 -3.5t30 -3.5q1 4 5 8q16 18 60 23h13q5 18 19 30t33 8 t36 -23t19 -36q79 -32 93 -95q9 -40 1 -81q-12 -53 -56 -88t-97 -44q-10 -2 -17 -2q0 -49 -1 -73q20 15 38 19q26 7 61 2q28 28 51 16q14 -9 14 -37q33 -16 59 -34q27 13 38 4q10 -10 2 -38q28 -30 41 -51q23 8 31 -1zM1937 1025q0 -29 -9 -54q82 -32 112 -132 q4 37 -9.5 98.5t-41.5 90.5q-20 19 -36 17t-16 -20zM1859 925q35 -42 47.5 -108.5t-0.5 -124.5q67 13 97 45q13 14 18 28q-3 64 -31 114.5t-79 66.5q-15 -15 -52 -21zM1822 921q-30 0 -44 1q42 -115 53 -239q21 0 43 3q16 68 1 135t-53 100zM258 839q30 100 112 132 q-9 25 -9 54q0 18 -16.5 20t-35.5 -17q-28 -29 -41.5 -90.5t-9.5 -98.5zM294 737q29 -31 97 -45q-13 58 -0.5 124.5t47.5 108.5v0q-37 6 -52 21q-51 -16 -78.5 -66t-31.5 -115q9 -17 18 -28zM471 683q14 124 73 235q-19 -4 -55 -18l-45 -19v1q-46 -89 -20 -196q25 -3 47 -3z M1434 644q8 -38 16.5 -108.5t11.5 -89.5q3 -18 9.5 -21.5t23.5 4.5q40 20 62 85.5t23 125.5q-24 2 -146 4zM1152 1285q-116 0 -199 -82.5t-83 -198.5q0 -117 83 -199.5t199 -82.5t199 82.5t83 199.5q0 116 -83 198.5t-199 82.5zM1380 646q-106 2 -211 0v1q-1 -27 2.5 -86 t13.5 -66q29 -14 93.5 -14.5t95.5 10.5q9 3 11 39t-0.5 69.5t-4.5 46.5zM1112 447q8 4 9.5 48t-0.5 88t-4 63v1q-212 -3 -214 -3q-4 -20 -7 -62t0 -83t14 -46q34 -15 101 -16t101 10zM718 636q-16 -59 4.5 -118.5t77.5 -84.5q15 -8 24 -5t12 21q3 16 8 90t10 103 q-69 -2 -136 -6zM591 510q3 -23 -34 -36q132 -141 271.5 -240t305.5 -154q172 49 310.5 146t293.5 250q-33 13 -30 34l3 9v1v-1q-17 2 -50 5.5t-48 4.5q-26 -90 -82 -132q-51 -38 -82 1q-5 6 -9 14q-7 13 -17 62q-2 -5 -5 -9t-7.5 -7t-8 -5.5t-9.5 -4l-10 -2.5t-12 -2 l-12 -1.5t-13.5 -1t-13.5 -0.5q-106 -9 -163 11q-4 -17 -10 -26.5t-21 -15t-23 -7t-36 -3.5q-2 0 -3 -0.5t-3 -0.5h-3q-179 -17 -203 40q-2 -63 -56 -54q-47 8 -91 54q-12 13 -20 26q-17 29 -26 65q-58 -6 -87 -10q1 -2 4 -10zM507 -118q3 14 3 30q-17 71 -51 130t-73 70 q-41 12 -101.5 -14.5t-104.5 -80t-39 -107.5q35 -53 100 -93t119 -42q51 -2 94 28t53 79zM510 53q23 -63 27 -119q195 113 392 174q-98 52 -180.5 120t-179.5 165q-6 -4 -29 -13q0 -2 -1 -5t-1 -4q31 -18 22 -37q-12 -23 -56 -34q-10 -13 -29 -24h-1q-2 -83 1 -150 q19 -34 35 -73zM579 -113q532 -21 1145 0q-254 147 -428 196q-76 -35 -156 -57q-8 -3 -16 0q-65 21 -129 49q-208 -60 -416 -188h-1v-1q1 0 1 1zM1763 -67q4 54 28 120q14 38 33 71l-1 -1q3 77 3 153q-15 8 -30 25q-42 9 -56 33q-9 20 22 38q-2 4 -2 9q-16 4 -28 12 q-204 -190 -383 -284q198 -59 414 -176zM2155 -90q5 54 -39 107.5t-104 80t-102 14.5q-38 -11 -72.5 -70.5t-51.5 -129.5q0 -16 3 -30q10 -49 53 -79t94 -28q54 2 119 42t100 93z" />
+<glyph unicode="&#xf23d;" horiz-adv-x="2304" d="M1524 -25q0 -68 -48 -116t-116 -48t-116.5 48t-48.5 116t48.5 116.5t116.5 48.5t116 -48.5t48 -116.5zM775 -25q0 -68 -48.5 -116t-116.5 -48t-116 48t-48 116t48 116.5t116 48.5t116.5 -48.5t48.5 -116.5zM0 1469q57 -60 110.5 -104.5t121 -82t136 -63t166 -45.5 t200 -31.5t250 -18.5t304 -9.5t372.5 -2.5q139 0 244.5 -5t181 -16.5t124 -27.5t71 -39.5t24 -51.5t-19.5 -64t-56.5 -76.5t-89.5 -91t-116 -104.5t-139 -119q-185 -157 -286 -247q29 51 76.5 109t94 105.5t94.5 98.5t83 91.5t54 80.5t13 70t-45.5 55.5t-116.5 41t-204 23.5 t-304 5q-168 -2 -314 6t-256 23t-204.5 41t-159.5 51.5t-122.5 62.5t-91.5 66.5t-68 71.5t-50.5 69.5t-40 68t-36.5 59.5z" />
+<glyph unicode="&#xf23e;" horiz-adv-x="1792" d="M896 1472q-169 0 -323 -66t-265.5 -177.5t-177.5 -265.5t-66 -323t66 -323t177.5 -265.5t265.5 -177.5t323 -66t323 66t265.5 177.5t177.5 265.5t66 323t-66 323t-177.5 265.5t-265.5 177.5t-323 66zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348 t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM496 704q16 0 16 -16v-480q0 -16 -16 -16h-32q-16 0 -16 16v480q0 16 16 16h32zM896 640q53 0 90.5 -37.5t37.5 -90.5q0 -35 -17.5 -64t-46.5 -46v-114q0 -14 -9 -23 t-23 -9h-64q-14 0 -23 9t-9 23v114q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5zM896 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM544 928v-96 q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v96q0 93 65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5v-96q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v96q0 146 -103 249t-249 103t-249 -103t-103 -249zM1408 192v512q0 26 -19 45t-45 19h-896q-26 0 -45 -19t-19 -45v-512 q0 -26 19 -45t45 -19h896q26 0 45 19t19 45z" />
+<glyph unicode="&#xf240;" horiz-adv-x="2304" d="M1920 1024v-768h-1664v768h1664zM2048 448h128v384h-128v288q0 14 -9 23t-23 9h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288zM2304 832v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113 v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160q53 0 90.5 -37.5t37.5 -90.5z" />
+<glyph unicode="&#xf241;" horiz-adv-x="2304" d="M256 256v768h1280v-768h-1280zM2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9 h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
+<glyph unicode="&#xf242;" horiz-adv-x="2304" d="M256 256v768h896v-768h-896zM2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9 h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
+<glyph unicode="&#xf243;" horiz-adv-x="2304" d="M256 256v768h512v-768h-512zM2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9 h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
+<glyph unicode="&#xf244;" horiz-adv-x="2304" d="M2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9h-1856q-14 0 -23 -9t-9 -23 v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
+<glyph unicode="&#xf245;" horiz-adv-x="1280" d="M1133 493q31 -30 14 -69q-17 -40 -59 -40h-382l201 -476q10 -25 0 -49t-34 -35l-177 -75q-25 -10 -49 0t-35 34l-191 452l-312 -312q-19 -19 -45 -19q-12 0 -24 5q-40 17 -40 59v1504q0 42 40 59q12 5 24 5q27 0 45 -19z" />
+<glyph unicode="&#xf246;" horiz-adv-x="1024" d="M832 1408q-320 0 -320 -224v-416h128v-128h-128v-544q0 -224 320 -224h64v-128h-64q-272 0 -384 146q-112 -146 -384 -146h-64v128h64q320 0 320 224v544h-128v128h128v416q0 224 -320 224h-64v128h64q272 0 384 -146q112 146 384 146h64v-128h-64z" />
+<glyph unicode="&#xf247;" horiz-adv-x="2048" d="M2048 1152h-128v-1024h128v-384h-384v128h-1280v-128h-384v384h128v1024h-128v384h384v-128h1280v128h384v-384zM1792 1408v-128h128v128h-128zM128 1408v-128h128v128h-128zM256 -128v128h-128v-128h128zM1664 0v128h128v1024h-128v128h-1280v-128h-128v-1024h128v-128 h1280zM1920 -128v128h-128v-128h128zM1280 896h384v-768h-896v256h-384v768h896v-256zM512 512h640v512h-640v-512zM1536 256v512h-256v-384h-384v-128h640z" />
+<glyph unicode="&#xf248;" horiz-adv-x="2304" d="M2304 768h-128v-640h128v-384h-384v128h-896v-128h-384v384h128v128h-384v-128h-384v384h128v640h-128v384h384v-128h896v128h384v-384h-128v-128h384v128h384v-384zM2048 1024v-128h128v128h-128zM1408 1408v-128h128v128h-128zM128 1408v-128h128v128h-128zM256 256 v128h-128v-128h128zM1536 384h-128v-128h128v128zM384 384h896v128h128v640h-128v128h-896v-128h-128v-640h128v-128zM896 -128v128h-128v-128h128zM2176 -128v128h-128v-128h128zM2048 128v640h-128v128h-384v-384h128v-384h-384v128h-384v-128h128v-128h896v128h128z" />
+<glyph unicode="&#xf249;" d="M1024 288v-416h-928q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1344q40 0 68 -28t28 -68v-928h-416q-40 0 -68 -28t-28 -68zM1152 256h381q-15 -82 -65 -132l-184 -184q-50 -50 -132 -65v381z" />
+<glyph unicode="&#xf24a;" d="M1400 256h-248v-248q29 10 41 22l185 185q12 12 22 41zM1120 384h288v896h-1280v-1280h896v288q0 40 28 68t68 28zM1536 1312v-1024q0 -40 -20 -88t-48 -76l-184 -184q-28 -28 -76 -48t-88 -20h-1024q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1344q40 0 68 -28t28 -68 z" />
+<glyph unicode="&#xf24b;" horiz-adv-x="2304" d="M1951 538q0 -26 -15.5 -44.5t-38.5 -23.5q-8 -2 -18 -2h-153v140h153q10 0 18 -2q23 -5 38.5 -23.5t15.5 -44.5zM1933 751q0 -25 -15 -42t-38 -21q-3 -1 -15 -1h-139v129h139q3 0 8.5 -0.5t6.5 -0.5q23 -4 38 -21.5t15 -42.5zM728 587v308h-228v-308q0 -58 -38 -94.5 t-105 -36.5q-108 0 -229 59v-112q53 -15 121 -23t109 -9l42 -1q328 0 328 217zM1442 403v113q-99 -52 -200 -59q-108 -8 -169 41t-61 142t61 142t169 41q101 -7 200 -58v112q-48 12 -100 19.5t-80 9.5l-28 2q-127 6 -218.5 -14t-140.5 -60t-71 -88t-22 -106t22 -106t71 -88 t140.5 -60t218.5 -14q101 4 208 31zM2176 518q0 54 -43 88.5t-109 39.5v3q57 8 89 41.5t32 79.5q0 55 -41 88t-107 36q-3 0 -12 0.5t-14 0.5h-455v-510h491q74 0 121.5 36.5t47.5 96.5zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90 t90 38h2048q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf24c;" horiz-adv-x="2304" d="M858 295v693q-106 -41 -172 -135.5t-66 -211.5t66 -211.5t172 -134.5zM1362 641q0 117 -66 211.5t-172 135.5v-694q106 41 172 135.5t66 211.5zM1577 641q0 -159 -78.5 -294t-213.5 -213.5t-294 -78.5q-119 0 -227.5 46.5t-187 125t-125 187t-46.5 227.5q0 159 78.5 294 t213.5 213.5t294 78.5t294 -78.5t213.5 -213.5t78.5 -294zM1960 634q0 139 -55.5 261.5t-147.5 205.5t-213.5 131t-252.5 48h-301q-176 0 -323.5 -81t-235 -230t-87.5 -335q0 -171 87 -317.5t236 -231.5t323 -85h301q129 0 251.5 50.5t214.5 135t147.5 202.5t55.5 246z M2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf24d;" horiz-adv-x="1792" d="M1664 -96v1088q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5v-1088q0 -13 9.5 -22.5t22.5 -9.5h1088q13 0 22.5 9.5t9.5 22.5zM1792 992v-1088q0 -66 -47 -113t-113 -47h-1088q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1088q66 0 113 -47t47 -113 zM1408 1376v-160h-128v160q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5v-1088q0 -13 9.5 -22.5t22.5 -9.5h160v-128h-160q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1088q66 0 113 -47t47 -113z" />
+<glyph unicode="&#xf24e;" horiz-adv-x="2304" d="M1728 1088l-384 -704h768zM448 1088l-384 -704h768zM1269 1280q-14 -40 -45.5 -71.5t-71.5 -45.5v-1291h608q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1344q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h608v1291q-40 14 -71.5 45.5t-45.5 71.5h-491q-14 0 -23 9t-9 23v64 q0 14 9 23t23 9h491q21 57 70 92.5t111 35.5t111 -35.5t70 -92.5h491q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-491zM1088 1264q33 0 56.5 23.5t23.5 56.5t-23.5 56.5t-56.5 23.5t-56.5 -23.5t-23.5 -56.5t23.5 -56.5t56.5 -23.5zM2176 384q0 -73 -46.5 -131t-117.5 -91 t-144.5 -49.5t-139.5 -16.5t-139.5 16.5t-144.5 49.5t-117.5 91t-46.5 131q0 11 35 81t92 174.5t107 195.5t102 184t56 100q18 33 56 33t56 -33q4 -7 56 -100t102 -184t107 -195.5t92 -174.5t35 -81zM896 384q0 -73 -46.5 -131t-117.5 -91t-144.5 -49.5t-139.5 -16.5 t-139.5 16.5t-144.5 49.5t-117.5 91t-46.5 131q0 11 35 81t92 174.5t107 195.5t102 184t56 100q18 33 56 33t56 -33q4 -7 56 -100t102 -184t107 -195.5t92 -174.5t35 -81z" />
+<glyph unicode="&#xf250;" d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM874 700q77 29 149 92.5t129.5 152.5t92.5 210t35 253h-1024q0 -132 35 -253t92.5 -210t129.5 -152.5t149 -92.5q19 -7 30.5 -23.5t11.5 -36.5t-11.5 -36.5t-30.5 -23.5q-77 -29 -149 -92.5 t-129.5 -152.5t-92.5 -210t-35 -253h1024q0 132 -35 253t-92.5 210t-129.5 152.5t-149 92.5q-19 7 -30.5 23.5t-11.5 36.5t11.5 36.5t30.5 23.5z" />
+<glyph unicode="&#xf251;" d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM1280 1408h-1024q0 -66 9 -128h1006q9 61 9 128zM1280 -128q0 130 -34 249.5t-90.5 208t-126.5 152t-146 94.5h-230q-76 -31 -146 -94.5t-126.5 -152t-90.5 -208t-34 -249.5h1024z" />
+<glyph unicode="&#xf252;" d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM1280 1408h-1024q0 -206 85 -384h854q85 178 85 384zM1223 192q-54 141 -145.5 241.5t-194.5 142.5h-230q-103 -42 -194.5 -142.5t-145.5 -241.5h910z" />
+<glyph unicode="&#xf253;" d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM874 700q77 29 149 92.5t129.5 152.5t92.5 210t35 253h-1024q0 -132 35 -253t92.5 -210t129.5 -152.5t149 -92.5q19 -7 30.5 -23.5t11.5 -36.5t-11.5 -36.5t-30.5 -23.5q-137 -51 -244 -196 h700q-107 145 -244 196q-19 7 -30.5 23.5t-11.5 36.5t11.5 36.5t30.5 23.5z" />
+<glyph unicode="&#xf254;" d="M1504 -64q14 0 23 -9t9 -23v-128q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v128q0 14 9 23t23 9h1472zM130 0q3 55 16 107t30 95t46 87t53.5 76t64.5 69.5t66 60t70.5 55t66.5 47.5t65 43q-43 28 -65 43t-66.5 47.5t-70.5 55t-66 60t-64.5 69.5t-53.5 76t-46 87 t-30 95t-16 107h1276q-3 -55 -16 -107t-30 -95t-46 -87t-53.5 -76t-64.5 -69.5t-66 -60t-70.5 -55t-66.5 -47.5t-65 -43q43 -28 65 -43t66.5 -47.5t70.5 -55t66 -60t64.5 -69.5t53.5 -76t46 -87t30 -95t16 -107h-1276zM1504 1536q14 0 23 -9t9 -23v-128q0 -14 -9 -23t-23 -9 h-1472q-14 0 -23 9t-9 23v128q0 14 9 23t23 9h1472z" />
+<glyph unicode="&#xf255;" d="M768 1152q-53 0 -90.5 -37.5t-37.5 -90.5v-128h-32v93q0 48 -32 81.5t-80 33.5q-46 0 -79 -33t-33 -79v-429l-32 30v172q0 48 -32 81.5t-80 33.5q-46 0 -79 -33t-33 -79v-224q0 -47 35 -82l310 -296q39 -39 39 -102q0 -26 19 -45t45 -19h640q26 0 45 19t19 45v25 q0 41 10 77l108 436q10 36 10 77v246q0 48 -32 81.5t-80 33.5q-46 0 -79 -33t-33 -79v-32h-32v125q0 40 -25 72.5t-64 40.5q-14 2 -23 2q-46 0 -79 -33t-33 -79v-128h-32v122q0 51 -32.5 89.5t-82.5 43.5q-5 1 -13 1zM768 1280q84 0 149 -50q57 34 123 34q59 0 111 -27 t86 -76q27 7 59 7q100 0 170 -71.5t70 -171.5v-246q0 -51 -13 -108l-109 -436q-6 -24 -6 -71q0 -80 -56 -136t-136 -56h-640q-84 0 -138 58.5t-54 142.5l-308 296q-76 73 -76 175v224q0 99 70.5 169.5t169.5 70.5q11 0 16 -1q6 95 75.5 160t164.5 65q52 0 98 -21 q72 69 174 69z" />
+<glyph unicode="&#xf256;" horiz-adv-x="1792" d="M880 1408q-46 0 -79 -33t-33 -79v-656h-32v528q0 46 -33 79t-79 33t-79 -33t-33 -79v-528v-256l-154 205q-38 51 -102 51q-53 0 -90.5 -37.5t-37.5 -90.5q0 -43 26 -77l384 -512q38 -51 102 -51h688q34 0 61 22t34 56l76 405q5 32 5 59v498q0 46 -33 79t-79 33t-79 -33 t-33 -79v-272h-32v528q0 46 -33 79t-79 33t-79 -33t-33 -79v-528h-32v656q0 46 -33 79t-79 33zM880 1536q68 0 125.5 -35.5t88.5 -96.5q19 4 42 4q99 0 169.5 -70.5t70.5 -169.5v-17q105 6 180.5 -64t75.5 -175v-498q0 -40 -8 -83l-76 -404q-14 -79 -76.5 -131t-143.5 -52 h-688q-60 0 -114.5 27.5t-90.5 74.5l-384 512q-51 68 -51 154q0 106 75 181t181 75q78 0 128 -34v434q0 99 70.5 169.5t169.5 70.5q23 0 42 -4q31 61 88.5 96.5t125.5 35.5z" />
+<glyph unicode="&#xf257;" horiz-adv-x="1792" d="M1073 -128h-177q-163 0 -226 141q-23 49 -23 102v5q-62 30 -98.5 88.5t-36.5 127.5q0 38 5 48h-261q-106 0 -181 75t-75 181t75 181t181 75h113l-44 17q-74 28 -119.5 93.5t-45.5 145.5q0 106 75 181t181 75q46 0 91 -17l628 -239h401q106 0 181 -75t75 -181v-668 q0 -88 -54 -157.5t-140 -90.5l-339 -85q-92 -23 -186 -23zM1024 583l-155 -71l-163 -74q-30 -14 -48 -41.5t-18 -60.5q0 -46 33 -79t79 -33q26 0 46 10l338 154q-49 10 -80.5 50t-31.5 90v55zM1344 272q0 46 -33 79t-79 33q-26 0 -46 -10l-290 -132q-28 -13 -37 -17 t-30.5 -17t-29.5 -23.5t-16 -29t-8 -40.5q0 -50 31.5 -82t81.5 -32q20 0 38 9l352 160q30 14 48 41.5t18 60.5zM1112 1024l-650 248q-24 8 -46 8q-53 0 -90.5 -37.5t-37.5 -90.5q0 -40 22.5 -73t59.5 -47l526 -200v-64h-640q-53 0 -90.5 -37.5t-37.5 -90.5t37.5 -90.5 t90.5 -37.5h535l233 106v198q0 63 46 106l111 102h-69zM1073 0q82 0 155 19l339 85q43 11 70 45.5t27 78.5v668q0 53 -37.5 90.5t-90.5 37.5h-308l-136 -126q-36 -33 -36 -82v-296q0 -46 33 -77t79 -31t79 35t33 81v208h32v-208q0 -70 -57 -114q52 -8 86.5 -48.5t34.5 -93.5 q0 -42 -23 -78t-61 -53l-310 -141h91z" />
+<glyph unicode="&#xf258;" horiz-adv-x="2048" d="M1151 1536q61 0 116 -28t91 -77l572 -781q118 -159 118 -359v-355q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v177l-286 143h-546q-80 0 -136 56t-56 136v32q0 119 84.5 203.5t203.5 84.5h420l42 128h-686q-100 0 -173.5 67.5t-81.5 166.5q-65 79 -65 182v32 q0 80 56 136t136 56h959zM1920 -64v355q0 157 -93 284l-573 781q-39 52 -103 52h-959q-26 0 -45 -19t-19 -45q0 -32 1.5 -49.5t9.5 -40.5t25 -43q10 31 35.5 50t56.5 19h832v-32h-832q-26 0 -45 -19t-19 -45q0 -44 3 -58q8 -44 44 -73t81 -29h640h91q40 0 68 -28t28 -68 q0 -15 -5 -30l-64 -192q-10 -29 -35 -47.5t-56 -18.5h-443q-66 0 -113 -47t-47 -113v-32q0 -26 19 -45t45 -19h561q16 0 29 -7l317 -158q24 -13 38.5 -36t14.5 -50v-197q0 -26 19 -45t45 -19h384q26 0 45 19t19 45z" />
+<glyph unicode="&#xf259;" horiz-adv-x="2048" d="M816 1408q-48 0 -79.5 -34t-31.5 -82q0 -14 3 -28l150 -624h-26l-116 482q-9 38 -39.5 62t-69.5 24q-47 0 -79 -34t-32 -81q0 -11 4 -29q3 -13 39 -161t68 -282t32 -138v-227l-307 230q-34 26 -77 26q-52 0 -89.5 -36.5t-37.5 -88.5q0 -67 56 -110l507 -379 q34 -26 76 -26h694q33 0 59 20.5t34 52.5l100 401q8 30 10 88t9 86l116 478q3 12 3 26q0 46 -33 79t-80 33q-38 0 -69 -25.5t-40 -62.5l-99 -408h-26l132 547q3 14 3 28q0 47 -32 80t-80 33q-38 0 -68.5 -24t-39.5 -62l-145 -602h-127l-164 682q-9 38 -39.5 62t-68.5 24z M1461 -256h-694q-85 0 -153 51l-507 380q-50 38 -78.5 94t-28.5 118q0 105 75 179t180 74q25 0 49.5 -5.5t41.5 -11t41 -20.5t35 -23t38.5 -29.5t37.5 -28.5l-123 512q-7 35 -7 59q0 93 60 162t152 79q14 87 80.5 144.5t155.5 57.5q83 0 148 -51.5t85 -132.5l103 -428 l83 348q20 81 85 132.5t148 51.5q87 0 152.5 -54t82.5 -139q93 -10 155 -78t62 -161q0 -30 -7 -57l-116 -477q-5 -22 -5 -67q0 -51 -13 -108l-101 -401q-19 -75 -79.5 -122.5t-137.5 -47.5z" />
+<glyph unicode="&#xf25a;" horiz-adv-x="1792" d="M640 1408q-53 0 -90.5 -37.5t-37.5 -90.5v-512v-384l-151 202q-41 54 -107 54q-52 0 -89 -38t-37 -90q0 -43 26 -77l384 -512q38 -51 102 -51h718q22 0 39.5 13.5t22.5 34.5l92 368q24 96 24 194v217q0 41 -28 71t-68 30t-68 -28t-28 -68h-32v61q0 48 -32 81.5t-80 33.5 q-46 0 -79 -33t-33 -79v-64h-32v90q0 55 -37 94.5t-91 39.5q-53 0 -90.5 -37.5t-37.5 -90.5v-96h-32v570q0 55 -37 94.5t-91 39.5zM640 1536q107 0 181.5 -77.5t74.5 -184.5v-220q22 2 32 2q99 0 173 -69q47 21 99 21q113 0 184 -87q27 7 56 7q94 0 159 -67.5t65 -161.5 v-217q0 -116 -28 -225l-92 -368q-16 -64 -68 -104.5t-118 -40.5h-718q-60 0 -114.5 27.5t-90.5 74.5l-384 512q-51 68 -51 154q0 105 74.5 180.5t179.5 75.5q71 0 130 -35v547q0 106 75 181t181 75zM768 128v384h-32v-384h32zM1024 128v384h-32v-384h32zM1280 128v384h-32 v-384h32z" />
+<glyph unicode="&#xf25b;" d="M1288 889q60 0 107 -23q141 -63 141 -226v-177q0 -94 -23 -186l-85 -339q-21 -86 -90.5 -140t-157.5 -54h-668q-106 0 -181 75t-75 181v401l-239 628q-17 45 -17 91q0 106 75 181t181 75q80 0 145.5 -45.5t93.5 -119.5l17 -44v113q0 106 75 181t181 75t181 -75t75 -181 v-261q27 5 48 5q69 0 127.5 -36.5t88.5 -98.5zM1072 896q-33 0 -60.5 -18t-41.5 -48l-74 -163l-71 -155h55q50 0 90 -31.5t50 -80.5l154 338q10 20 10 46q0 46 -33 79t-79 33zM1293 761q-22 0 -40.5 -8t-29 -16t-23.5 -29.5t-17 -30.5t-17 -37l-132 -290q-10 -20 -10 -46 q0 -46 33 -79t79 -33q33 0 60.5 18t41.5 48l160 352q9 18 9 38q0 50 -32 81.5t-82 31.5zM128 1120q0 -22 8 -46l248 -650v-69l102 111q43 46 106 46h198l106 233v535q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5v-640h-64l-200 526q-14 37 -47 59.5t-73 22.5 q-53 0 -90.5 -37.5t-37.5 -90.5zM1180 -128q44 0 78.5 27t45.5 70l85 339q19 73 19 155v91l-141 -310q-17 -38 -53 -61t-78 -23q-53 0 -93.5 34.5t-48.5 86.5q-44 -57 -114 -57h-208v32h208q46 0 81 33t35 79t-31 79t-77 33h-296q-49 0 -82 -36l-126 -136v-308 q0 -53 37.5 -90.5t90.5 -37.5h668z" />
+<glyph unicode="&#xf25c;" horiz-adv-x="1973" d="M857 992v-117q0 -13 -9.5 -22t-22.5 -9h-298v-812q0 -13 -9 -22.5t-22 -9.5h-135q-13 0 -22.5 9t-9.5 23v812h-297q-13 0 -22.5 9t-9.5 22v117q0 14 9 23t23 9h793q13 0 22.5 -9.5t9.5 -22.5zM1895 995l77 -961q1 -13 -8 -24q-10 -10 -23 -10h-134q-12 0 -21 8.5 t-10 20.5l-46 588l-189 -425q-8 -19 -29 -19h-120q-20 0 -29 19l-188 427l-45 -590q-1 -12 -10 -20.5t-21 -8.5h-135q-13 0 -23 10q-9 10 -9 24l78 961q1 12 10 20.5t21 8.5h142q20 0 29 -19l220 -520q10 -24 20 -51q3 7 9.5 24.5t10.5 26.5l221 520q9 19 29 19h141 q13 0 22 -8.5t10 -20.5z" />
+<glyph unicode="&#xf25d;" horiz-adv-x="1792" d="M1042 833q0 88 -60 121q-33 18 -117 18h-123v-281h162q66 0 102 37t36 105zM1094 548l205 -373q8 -17 -1 -31q-8 -16 -27 -16h-152q-20 0 -28 17l-194 365h-155v-350q0 -14 -9 -23t-23 -9h-134q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h294q128 0 190 -24q85 -31 134 -109 t49 -180q0 -92 -42.5 -165.5t-115.5 -109.5q6 -10 9 -16zM896 1376q-150 0 -286 -58.5t-234.5 -157t-157 -234.5t-58.5 -286t58.5 -286t157 -234.5t234.5 -157t286 -58.5t286 58.5t234.5 157t157 234.5t58.5 286t-58.5 286t-157 234.5t-234.5 157t-286 58.5zM1792 640 q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
+<glyph unicode="&#xf25e;" horiz-adv-x="1792" d="M605 303q153 0 257 104q14 18 3 36l-45 82q-6 13 -24 17q-16 2 -27 -11l-4 -3q-4 -4 -11.5 -10t-17.5 -13t-23.5 -14.5t-28.5 -13.5t-33.5 -9.5t-37.5 -3.5q-76 0 -125 50t-49 127q0 76 48 125.5t122 49.5q37 0 71.5 -14t50.5 -28l16 -14q11 -11 26 -10q16 2 24 14l53 78 q13 20 -2 39q-3 4 -11 12t-30 23.5t-48.5 28t-67.5 22.5t-86 10q-148 0 -246 -96.5t-98 -240.5q0 -146 97 -241.5t247 -95.5zM1235 303q153 0 257 104q14 18 4 36l-45 82q-8 14 -25 17q-16 2 -27 -11l-4 -3q-4 -4 -11.5 -10t-17.5 -13t-23.5 -14.5t-28.5 -13.5t-33.5 -9.5 t-37.5 -3.5q-76 0 -125 50t-49 127q0 76 48 125.5t122 49.5q37 0 71.5 -14t50.5 -28l16 -14q11 -11 26 -10q16 2 24 14l53 78q13 20 -2 39q-3 4 -11 12t-30 23.5t-48.5 28t-67.5 22.5t-86 10q-147 0 -245.5 -96.5t-98.5 -240.5q0 -146 97 -241.5t247 -95.5zM896 1376 q-150 0 -286 -58.5t-234.5 -157t-157 -234.5t-58.5 -286t58.5 -286t157 -234.5t234.5 -157t286 -58.5t286 58.5t234.5 157t157 234.5t58.5 286t-58.5 286t-157 234.5t-234.5 157t-286 58.5zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286t-286 -191 t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71z" />
+<glyph unicode="&#xf260;" horiz-adv-x="2048" d="M736 736l384 -384l-384 -384l-672 672l672 672l168 -168l-96 -96l-72 72l-480 -480l480 -480l193 193l-289 287zM1312 1312l672 -672l-672 -672l-168 168l96 96l72 -72l480 480l-480 480l-193 -193l289 -287l-96 -96l-384 384z" />
+<glyph unicode="&#xf261;" horiz-adv-x="1792" d="M717 182l271 271l-279 279l-88 -88l192 -191l-96 -96l-279 279l279 279l40 -40l87 87l-127 128l-454 -454zM1075 190l454 454l-454 454l-271 -271l279 -279l88 88l-192 191l96 96l279 -279l-279 -279l-40 40l-87 -88zM1792 640q0 -182 -71 -348t-191 -286t-286 -191 t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
+<glyph unicode="&#xf262;" horiz-adv-x="2304" d="M651 539q0 -39 -27.5 -66.5t-65.5 -27.5q-39 0 -66.5 27.5t-27.5 66.5q0 38 27.5 65.5t66.5 27.5q38 0 65.5 -27.5t27.5 -65.5zM1805 540q0 -39 -27.5 -66.5t-66.5 -27.5t-66.5 27.5t-27.5 66.5t27.5 66t66.5 27t66.5 -27t27.5 -66zM765 539q0 79 -56.5 136t-136.5 57 t-136.5 -56.5t-56.5 -136.5t56.5 -136.5t136.5 -56.5t136.5 56.5t56.5 136.5zM1918 540q0 80 -56.5 136.5t-136.5 56.5q-79 0 -136 -56.5t-57 -136.5t56.5 -136.5t136.5 -56.5t136.5 56.5t56.5 136.5zM850 539q0 -116 -81.5 -197.5t-196.5 -81.5q-116 0 -197.5 82t-81.5 197 t82 196.5t197 81.5t196.5 -81.5t81.5 -196.5zM2004 540q0 -115 -81.5 -196.5t-197.5 -81.5q-115 0 -196.5 81.5t-81.5 196.5t81.5 196.5t196.5 81.5q116 0 197.5 -81.5t81.5 -196.5zM1040 537q0 191 -135.5 326.5t-326.5 135.5q-125 0 -231 -62t-168 -168.5t-62 -231.5 t62 -231.5t168 -168.5t231 -62q191 0 326.5 135.5t135.5 326.5zM1708 1110q-254 111 -556 111q-319 0 -573 -110q117 0 223 -45.5t182.5 -122.5t122 -183t45.5 -223q0 115 43.5 219.5t118 180.5t177.5 123t217 50zM2187 537q0 191 -135 326.5t-326 135.5t-326.5 -135.5 t-135.5 -326.5t135.5 -326.5t326.5 -135.5t326 135.5t135 326.5zM1921 1103h383q-44 -51 -75 -114.5t-40 -114.5q110 -151 110 -337q0 -156 -77 -288t-209 -208.5t-287 -76.5q-133 0 -249 56t-196 155q-47 -56 -129 -179q-11 22 -53.5 82.5t-74.5 97.5 q-80 -99 -196.5 -155.5t-249.5 -56.5q-155 0 -287 76.5t-209 208.5t-77 288q0 186 110 337q-9 51 -40 114.5t-75 114.5h365q149 100 355 156.5t432 56.5q224 0 421 -56t348 -157z" />
+<glyph unicode="&#xf263;" horiz-adv-x="1280" d="M640 629q-188 0 -321 133t-133 320q0 188 133 321t321 133t321 -133t133 -321q0 -187 -133 -320t-321 -133zM640 1306q-92 0 -157.5 -65.5t-65.5 -158.5q0 -92 65.5 -157.5t157.5 -65.5t157.5 65.5t65.5 157.5q0 93 -65.5 158.5t-157.5 65.5zM1163 574q13 -27 15 -49.5 t-4.5 -40.5t-26.5 -38.5t-42.5 -37t-61.5 -41.5q-115 -73 -315 -94l73 -72l267 -267q30 -31 30 -74t-30 -73l-12 -13q-31 -30 -74 -30t-74 30q-67 68 -267 268l-267 -268q-31 -30 -74 -30t-73 30l-12 13q-31 30 -31 73t31 74l267 267l72 72q-203 21 -317 94 q-39 25 -61.5 41.5t-42.5 37t-26.5 38.5t-4.5 40.5t15 49.5q10 20 28 35t42 22t56 -2t65 -35q5 -4 15 -11t43 -24.5t69 -30.5t92 -24t113 -11q91 0 174 25.5t120 50.5l38 25q33 26 65 35t56 2t42 -22t28 -35z" />
+<glyph unicode="&#xf264;" d="M927 956q0 -66 -46.5 -112.5t-112.5 -46.5t-112.5 46.5t-46.5 112.5t46.5 112.5t112.5 46.5t112.5 -46.5t46.5 -112.5zM1141 593q-10 20 -28 32t-47.5 9.5t-60.5 -27.5q-10 -8 -29 -20t-81 -32t-127 -20t-124 18t-86 36l-27 18q-31 25 -60.5 27.5t-47.5 -9.5t-28 -32 q-22 -45 -2 -74.5t87 -73.5q83 -53 226 -67l-51 -52q-142 -142 -191 -190q-22 -22 -22 -52.5t22 -52.5l9 -9q22 -22 52.5 -22t52.5 22l191 191q114 -115 191 -191q22 -22 52.5 -22t52.5 22l9 9q22 22 22 52.5t-22 52.5l-191 190l-52 52q141 14 225 67q67 44 87 73.5t-2 74.5 zM1092 956q0 134 -95 229t-229 95t-229 -95t-95 -229t95 -229t229 -95t229 95t95 229zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf265;" horiz-adv-x="1720" d="M1565 1408q65 0 110 -45.5t45 -110.5v-519q0 -176 -68 -336t-182.5 -275t-274 -182.5t-334.5 -67.5q-176 0 -335.5 67.5t-274.5 182.5t-183 275t-68 336v519q0 64 46 110t110 46h1409zM861 344q47 0 82 33l404 388q37 35 37 85q0 49 -34.5 83.5t-83.5 34.5q-47 0 -82 -33 l-323 -310l-323 310q-35 33 -81 33q-49 0 -83.5 -34.5t-34.5 -83.5q0 -51 36 -85l405 -388q33 -33 81 -33z" />
+<glyph unicode="&#xf266;" horiz-adv-x="2304" d="M1494 -103l-295 695q-25 -49 -158.5 -305.5t-198.5 -389.5q-1 -1 -27.5 -0.5t-26.5 1.5q-82 193 -255.5 587t-259.5 596q-21 50 -66.5 107.5t-103.5 100.5t-102 43q0 5 -0.5 24t-0.5 27h583v-50q-39 -2 -79.5 -16t-66.5 -43t-10 -64q26 -59 216.5 -499t235.5 -540 q31 61 140 266.5t131 247.5q-19 39 -126 281t-136 295q-38 69 -201 71v50l513 -1v-47q-60 -2 -93.5 -25t-12.5 -69q33 -70 87 -189.5t86 -187.5q110 214 173 363q24 55 -10 79.5t-129 26.5q1 7 1 25v24q64 0 170.5 0.5t180 1t92.5 0.5v-49q-62 -2 -119 -33t-90 -81 l-213 -442q13 -33 127.5 -290t121.5 -274l441 1017q-14 38 -49.5 62.5t-65 31.5t-55.5 8v50l460 -4l1 -2l-1 -44q-139 -4 -201 -145q-526 -1216 -559 -1291h-49z" />
+<glyph unicode="&#xf267;" horiz-adv-x="1792" d="M949 643q0 -26 -16.5 -45t-41.5 -19q-26 0 -45 16.5t-19 41.5q0 26 17 45t42 19t44 -16.5t19 -41.5zM964 585l350 581q-9 -8 -67.5 -62.5t-125.5 -116.5t-136.5 -127t-117 -110.5t-50.5 -51.5l-349 -580q7 7 67 62t126 116.5t136 127t117 111t50 50.5zM1611 640 q0 -201 -104 -371q-3 2 -17 11t-26.5 16.5t-16.5 7.5q-13 0 -13 -13q0 -10 59 -44q-74 -112 -184.5 -190.5t-241.5 -110.5l-16 67q-1 10 -15 10q-5 0 -8 -5.5t-2 -9.5l16 -68q-72 -15 -146 -15q-199 0 -372 105q1 2 13 20.5t21.5 33.5t9.5 19q0 13 -13 13q-6 0 -17 -14.5 t-22.5 -34.5t-13.5 -23q-113 75 -192 187.5t-110 244.5l69 15q10 3 10 15q0 5 -5.5 8t-10.5 2l-68 -15q-14 72 -14 139q0 206 109 379q2 -1 18.5 -12t30 -19t17.5 -8q13 0 13 12q0 6 -12.5 15.5t-32.5 21.5l-20 12q77 112 189 189t244 107l15 -67q2 -10 15 -10q5 0 8 5.5 t2 10.5l-15 66q71 13 134 13q204 0 379 -109q-39 -56 -39 -65q0 -13 12 -13q11 0 48 64q111 -75 187.5 -186t107.5 -241l-56 -12q-10 -2 -10 -16q0 -5 5.5 -8t9.5 -2l57 13q14 -72 14 -140zM1696 640q0 163 -63.5 311t-170.5 255t-255 170.5t-311 63.5t-311 -63.5 t-255 -170.5t-170.5 -255t-63.5 -311t63.5 -311t170.5 -255t255 -170.5t311 -63.5t311 63.5t255 170.5t170.5 255t63.5 311zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191 t191 -286t71 -348z" />
+<glyph unicode="&#xf268;" horiz-adv-x="1792" d="M893 1536q240 2 451 -120q232 -134 352 -372l-742 39q-160 9 -294 -74.5t-185 -229.5l-276 424q128 159 311 245.5t383 87.5zM146 1131l337 -663q72 -143 211 -217t293 -45l-230 -451q-212 33 -385 157.5t-272.5 316t-99.5 411.5q0 267 146 491zM1732 962 q58 -150 59.5 -310.5t-48.5 -306t-153 -272t-246 -209.5q-230 -133 -498 -119l405 623q88 131 82.5 290.5t-106.5 277.5zM896 942q125 0 213.5 -88.5t88.5 -213.5t-88.5 -213.5t-213.5 -88.5t-213.5 88.5t-88.5 213.5t88.5 213.5t213.5 88.5z" />
+<glyph unicode="&#xf269;" horiz-adv-x="1792" d="M903 -256q-283 0 -504.5 150.5t-329.5 398.5q-58 131 -67 301t26 332.5t111 312t179 242.5l-11 -281q11 14 68 15.5t70 -15.5q42 81 160.5 138t234.5 59q-54 -45 -119.5 -148.5t-58.5 -163.5q25 -8 62.5 -13.5t63 -7.5t68 -4t50.5 -3q15 -5 9.5 -45.5t-30.5 -75.5 q-5 -7 -16.5 -18.5t-56.5 -35.5t-101 -34l15 -189l-139 67q-18 -43 -7.5 -81.5t36 -66.5t65.5 -41.5t81 -6.5q51 9 98 34.5t83.5 45t73.5 17.5q61 -4 89.5 -33t19.5 -65q-1 -2 -2.5 -5.5t-8.5 -12.5t-18 -15.5t-31.5 -10.5t-46.5 -1q-60 -95 -144.5 -135.5t-209.5 -29.5 q74 -61 162.5 -82.5t168.5 -6t154.5 52t128 87.5t80.5 104q43 91 39 192.5t-37.5 188.5t-78.5 125q87 -38 137 -79.5t77 -112.5q15 170 -57.5 343t-209.5 284q265 -77 412 -279.5t151 -517.5q2 -127 -40.5 -255t-123.5 -238t-189 -196t-247.5 -135.5t-288.5 -49.5z" />
+<glyph unicode="&#xf26a;" horiz-adv-x="1792" d="M1493 1308q-165 110 -359 110q-155 0 -293 -73t-240 -200q-75 -93 -119.5 -218t-48.5 -266v-42q4 -141 48.5 -266t119.5 -218q102 -127 240 -200t293 -73q194 0 359 110q-121 -108 -274.5 -168t-322.5 -60q-29 0 -43 1q-175 8 -333 82t-272 193t-181 281t-67 339 q0 182 71 348t191 286t286 191t348 71h3q168 -1 320.5 -60.5t273.5 -167.5zM1792 640q0 -192 -77 -362.5t-213 -296.5q-104 -63 -222 -63q-137 0 -255 84q154 56 253.5 233t99.5 405q0 227 -99 404t-253 234q119 83 254 83q119 0 226 -65q135 -125 210.5 -295t75.5 -361z " />
+<glyph unicode="&#xf26b;" horiz-adv-x="1792" d="M1792 599q0 -56 -7 -104h-1151q0 -146 109.5 -244.5t257.5 -98.5q99 0 185.5 46.5t136.5 130.5h423q-56 -159 -170.5 -281t-267.5 -188.5t-321 -66.5q-187 0 -356 83q-228 -116 -394 -116q-237 0 -237 263q0 115 45 275q17 60 109 229q199 360 475 606 q-184 -79 -427 -354q63 274 283.5 449.5t501.5 175.5q30 0 45 -1q255 117 433 117q64 0 116 -13t94.5 -40.5t66.5 -76.5t24 -115q0 -116 -75 -286q101 -182 101 -390zM1722 1239q0 83 -53 132t-137 49q-108 0 -254 -70q121 -47 222.5 -131.5t170.5 -195.5q51 135 51 216z M128 2q0 -86 48.5 -132.5t134.5 -46.5q115 0 266 83q-122 72 -213.5 183t-137.5 245q-98 -205 -98 -332zM632 715h728q-5 142 -113 237t-251 95q-144 0 -251.5 -95t-112.5 -237z" />
+<glyph unicode="&#xf26c;" horiz-adv-x="2048" d="M1792 288v960q0 13 -9.5 22.5t-22.5 9.5h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5zM1920 1248v-960q0 -66 -47 -113t-113 -47h-736v-128h352q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23 v64q0 14 9 23t23 9h352v128h-736q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
+<glyph unicode="&#xf26d;" horiz-adv-x="1792" d="M138 1408h197q-70 -64 -126 -149q-36 -56 -59 -115t-30 -125.5t-8.5 -120t10.5 -132t21 -126t28 -136.5q4 -19 6 -28q51 -238 81 -329q57 -171 152 -275h-272q-48 0 -82 34t-34 82v1304q0 48 34 82t82 34zM1346 1408h308q48 0 82 -34t34 -82v-1304q0 -48 -34 -82t-82 -34 h-178q212 210 196 565l-469 -101q-2 -45 -12 -82t-31 -72t-59.5 -59.5t-93.5 -36.5q-123 -26 -199 40q-32 27 -53 61t-51.5 129t-64.5 258q-35 163 -45.5 263t-5.5 139t23 77q20 41 62.5 73t102.5 45q45 12 83.5 6.5t67 -17t54 -35t43 -48t34.5 -56.5l468 100 q-68 175 -180 287z" />
+<glyph unicode="&#xf26e;" d="M1401 -11l-6 -6q-113 -114 -259 -175q-154 -64 -317 -64q-165 0 -317 64q-148 63 -259 175q-113 112 -175 258q-42 103 -54 189q-4 28 48 36q51 8 56 -20q1 -1 1 -4q18 -90 46 -159q50 -124 152 -226q98 -98 226 -152q132 -56 276 -56q143 0 276 56q128 55 225 152l6 6 q10 10 25 6q12 -3 33 -22q36 -37 17 -58zM929 604l-66 -66l63 -63q21 -21 -7 -49q-17 -17 -32 -17q-10 0 -19 10l-62 61l-66 -66q-5 -5 -15 -5q-15 0 -31 16l-2 2q-18 15 -18 29q0 7 8 17l66 65l-66 66q-16 16 14 45q18 18 31 18q6 0 13 -5l65 -66l65 65q18 17 48 -13 q27 -27 11 -44zM1400 547q0 -118 -46 -228q-45 -105 -126 -186q-80 -80 -187 -126t-228 -46t-228 46t-187 126q-82 82 -125 186q-15 32 -15 40h-1q-9 27 43 44q50 16 60 -12q37 -99 97 -167h1v339v2q3 136 102 232q105 103 253 103q147 0 251 -103t104 -249 q0 -147 -104.5 -251t-250.5 -104q-58 0 -112 16q-28 11 -13 61q16 51 44 43l14 -3q14 -3 32.5 -6t30.5 -3q104 0 176 71.5t72 174.5q0 101 -72 171q-71 71 -175 71q-107 0 -178 -80q-64 -72 -64 -160v-413q110 -67 242 -67q96 0 185 36.5t156 103.5t103.5 155t36.5 183 q0 198 -141 339q-140 140 -339 140q-200 0 -340 -140q-53 -53 -77 -87l-2 -2q-8 -11 -13 -15.5t-21.5 -9.5t-38.5 3q-21 5 -36.5 16.5t-15.5 26.5v680q0 15 10.5 26.5t27.5 11.5h877q30 0 30 -55t-30 -55h-811v-483h1q40 42 102 84t108 61q109 46 231 46q121 0 228 -46 t187 -126q81 -81 126 -186q46 -112 46 -229zM1369 1128q9 -8 9 -18t-5.5 -18t-16.5 -21q-26 -26 -39 -26q-9 0 -16 7q-106 91 -207 133q-128 56 -276 56q-133 0 -262 -49q-27 -10 -45 37q-9 25 -8 38q3 16 16 20q130 57 299 57q164 0 316 -64q137 -58 235 -152z" />
+<glyph unicode="&#xf270;" horiz-adv-x="1792" d="M1551 60q15 6 26 3t11 -17.5t-15 -33.5q-13 -16 -44 -43.5t-95.5 -68t-141 -74t-188 -58t-229.5 -24.5q-119 0 -238 31t-209 76.5t-172.5 104t-132.5 105t-84 87.5q-8 9 -10 16.5t1 12t8 7t11.5 2t11.5 -4.5q192 -117 300 -166q389 -176 799 -90q190 40 391 135z M1758 175q11 -16 2.5 -69.5t-28.5 -102.5q-34 -83 -85 -124q-17 -14 -26 -9t0 24q21 45 44.5 121.5t6.5 98.5q-5 7 -15.5 11.5t-27 6t-29.5 2.5t-35 0t-31.5 -2t-31 -3t-22.5 -2q-6 -1 -13 -1.5t-11 -1t-8.5 -1t-7 -0.5h-5.5h-4.5t-3 0.5t-2 1.5l-1.5 3q-6 16 47 40t103 30 q46 7 108 1t76 -24zM1364 618q0 -31 13.5 -64t32 -58t37.5 -46t33 -32l13 -11l-227 -224q-40 37 -79 75.5t-58 58.5l-19 20q-11 11 -25 33q-38 -59 -97.5 -102.5t-127.5 -63.5t-140 -23t-137.5 21t-117.5 65.5t-83 113t-31 162.5q0 84 28 154t72 116.5t106.5 83t122.5 57 t130 34.5t119.5 18.5t99.5 6.5v127q0 65 -21 97q-34 53 -121 53q-6 0 -16.5 -1t-40.5 -12t-56 -29.5t-56 -59.5t-48 -96l-294 27q0 60 22 119t67 113t108 95t151.5 65.5t190.5 24.5q100 0 181 -25t129.5 -61.5t81 -83t45 -86t12.5 -73.5v-589zM692 597q0 -86 70 -133 q66 -44 139 -22q84 25 114 123q14 45 14 101v162q-59 -2 -111 -12t-106.5 -33.5t-87 -71t-32.5 -114.5z" />
+<glyph unicode="&#xf271;" horiz-adv-x="1792" d="M1536 1280q52 0 90 -38t38 -90v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128zM1152 1376v-288q0 -14 9 -23t23 -9 h64q14 0 23 9t9 23v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM384 1376v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM1536 -128v1024h-1408v-1024h1408zM896 448h224q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-224 v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v224q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-224z" />
+<glyph unicode="&#xf272;" horiz-adv-x="1792" d="M1152 416v-64q0 -14 -9 -23t-23 -9h-576q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h576q14 0 23 -9t9 -23zM128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23 t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47 t47 -113v-96h128q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf273;" horiz-adv-x="1792" d="M1111 151l-46 -46q-9 -9 -22 -9t-23 9l-188 189l-188 -189q-10 -9 -23 -9t-22 9l-46 46q-9 9 -9 22t9 23l189 188l-189 188q-9 10 -9 23t9 22l46 46q9 9 22 9t23 -9l188 -188l188 188q10 9 23 9t22 -9l46 -46q9 -9 9 -22t-9 -23l-188 -188l188 -188q9 -10 9 -23t-9 -22z M128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280 q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf274;" horiz-adv-x="1792" d="M1303 572l-512 -512q-10 -9 -23 -9t-23 9l-288 288q-9 10 -9 23t9 22l46 46q9 9 22 9t23 -9l220 -220l444 444q10 9 23 9t22 -9l46 -46q9 -9 9 -22t-9 -23zM128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23 t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47 t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf275;" horiz-adv-x="1792" d="M448 1536q26 0 45 -19t19 -45v-891l536 429q17 14 40 14q26 0 45 -19t19 -45v-379l536 429q17 14 40 14q26 0 45 -19t19 -45v-1152q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v1664q0 26 19 45t45 19h384z" />
+<glyph unicode="&#xf276;" horiz-adv-x="1024" d="M512 448q66 0 128 15v-655q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v655q61 -15 128 -15zM512 1536q212 0 362 -150t150 -362t-150 -362t-362 -150t-362 150t-150 362t150 362t362 150zM512 1312q14 0 23 9t9 23t-9 23t-23 9q-146 0 -249 -103t-103 -249 q0 -14 9 -23t23 -9t23 9t9 23q0 119 84.5 203.5t203.5 84.5z" />
+<glyph unicode="&#xf277;" horiz-adv-x="1792" d="M1745 1239q10 -10 10 -23t-10 -23l-141 -141q-28 -28 -68 -28h-1344q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h576v64q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-64h512q40 0 68 -28zM768 320h256v-512q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v512zM1600 768 q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19h-1344q-40 0 -68 28l-141 141q-10 10 -10 23t10 23l141 141q28 28 68 28h512v192h256v-192h576z" />
+<glyph unicode="&#xf278;" horiz-adv-x="2048" d="M2020 1525q28 -20 28 -53v-1408q0 -20 -11 -36t-29 -23l-640 -256q-24 -11 -48 0l-616 246l-616 -246q-10 -5 -24 -5q-19 0 -36 11q-28 20 -28 53v1408q0 20 11 36t29 23l640 256q24 11 48 0l616 -246l616 246q32 13 60 -6zM736 1390v-1270l576 -230v1270zM128 1173 v-1270l544 217v1270zM1920 107v1270l-544 -217v-1270z" />
+<glyph unicode="&#xf279;" horiz-adv-x="1792" d="M512 1536q13 0 22.5 -9.5t9.5 -22.5v-1472q0 -20 -17 -28l-480 -256q-7 -4 -15 -4q-13 0 -22.5 9.5t-9.5 22.5v1472q0 20 17 28l480 256q7 4 15 4zM1760 1536q13 0 22.5 -9.5t9.5 -22.5v-1472q0 -20 -17 -28l-480 -256q-7 -4 -15 -4q-13 0 -22.5 9.5t-9.5 22.5v1472 q0 20 17 28l480 256q7 4 15 4zM640 1536q8 0 14 -3l512 -256q18 -10 18 -29v-1472q0 -13 -9.5 -22.5t-22.5 -9.5q-8 0 -14 3l-512 256q-18 10 -18 29v1472q0 13 9.5 22.5t22.5 9.5z" />
+<glyph unicode="&#xf27a;" horiz-adv-x="1792" d="M640 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1408 640q0 53 -37.5 90.5t-90.5 37.5 t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-110 0 -211 18q-173 -173 -435 -229q-52 -10 -86 -13q-12 -1 -22 6t-13 18q-4 15 20 37q5 5 23.5 21.5t25.5 23.5t23.5 25.5t24 31.5t20.5 37 t20 48t14.5 57.5t12.5 72.5q-146 90 -229.5 216.5t-83.5 269.5q0 174 120 321.5t326 233t450 85.5t450 -85.5t326 -233t120 -321.5z" />
+<glyph unicode="&#xf27b;" horiz-adv-x="1792" d="M640 640q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1024 640q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 -53 -37.5 -90.5t-90.5 -37.5 t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM896 1152q-204 0 -381.5 -69.5t-282 -187.5t-104.5 -255q0 -112 71.5 -213.5t201.5 -175.5l87 -50l-27 -96q-24 -91 -70 -172q152 63 275 171l43 38l57 -6q69 -8 130 -8q204 0 381.5 69.5t282 187.5 t104.5 255t-104.5 255t-282 187.5t-381.5 69.5zM1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22h-5q-15 0 -27 10.5t-16 27.5v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51 t27 59t26 76q-157 89 -247.5 220t-90.5 281q0 130 71 248.5t191 204.5t286 136.5t348 50.5t348 -50.5t286 -136.5t191 -204.5t71 -248.5z" />
+<glyph unicode="&#xf27c;" horiz-adv-x="1024" d="M512 345l512 295v-591l-512 -296v592zM0 640v-591l512 296zM512 1527v-591l-512 -296v591zM512 936l512 295v-591z" />
+<glyph unicode="&#xf27d;" horiz-adv-x="1792" d="M1709 1018q-10 -236 -332 -651q-333 -431 -562 -431q-142 0 -240 263q-44 160 -132 482q-72 262 -157 262q-18 0 -127 -76l-77 98q24 21 108 96.5t130 115.5q156 138 241 146q95 9 153 -55.5t81 -203.5q44 -287 66 -373q55 -249 120 -249q51 0 154 161q101 161 109 246 q13 139 -109 139q-57 0 -121 -26q120 393 459 382q251 -8 236 -326z" />
+<glyph unicode="&#xf27e;" d="M0 1408h1536v-1536h-1536v1536zM1085 293l-221 631l221 297h-634l221 -297l-221 -631l317 -304z" />
+<glyph unicode="&#xf280;" d="M0 1408h1536v-1536h-1536v1536zM908 1088l-12 -33l75 -83l-31 -114l25 -25l107 57l107 -57l25 25l-31 114l75 83l-12 33h-95l-53 96h-32l-53 -96h-95zM641 925q32 0 44.5 -16t11.5 -63l174 21q0 55 -17.5 92.5t-50.5 56t-69 25.5t-85 7q-133 0 -199 -57.5t-66 -182.5v-72 h-96v-128h76q20 0 20 -8v-382q0 -14 -5 -20t-18 -7l-73 -7v-88h448v86l-149 14q-6 1 -8.5 1.5t-3.5 2.5t-0.5 4t1 7t0.5 10v387h191l38 128h-231q-6 0 -2 6t4 9v80q0 27 1.5 40.5t7.5 28t19.5 20t36.5 5.5zM1248 96v86l-54 9q-7 1 -9.5 2.5t-2.5 3t1 7.5t1 12v520h-275 l-23 -101l83 -22q23 -7 23 -27v-370q0 -14 -6 -18.5t-20 -6.5l-70 -9v-86h352z" />
+<glyph unicode="&#xf281;" horiz-adv-x="1792" d="M1792 690q0 -58 -29.5 -105.5t-79.5 -72.5q12 -46 12 -96q0 -155 -106.5 -287t-290.5 -208.5t-400 -76.5t-399.5 76.5t-290 208.5t-106.5 287q0 47 11 94q-51 25 -82 73.5t-31 106.5q0 82 58 140.5t141 58.5q85 0 145 -63q218 152 515 162l116 521q3 13 15 21t26 5 l369 -81q18 37 54 59.5t79 22.5q62 0 106 -43.5t44 -105.5t-44 -106t-106 -44t-105.5 43.5t-43.5 105.5l-334 74l-104 -472q300 -9 519 -160q58 61 143 61q83 0 141 -58.5t58 -140.5zM418 491q0 -62 43.5 -106t105.5 -44t106 44t44 106t-44 105.5t-106 43.5q-61 0 -105 -44 t-44 -105zM1228 136q11 11 11 26t-11 26q-10 10 -25 10t-26 -10q-41 -42 -121 -62t-160 -20t-160 20t-121 62q-11 10 -26 10t-25 -10q-11 -10 -11 -25.5t11 -26.5q43 -43 118.5 -68t122.5 -29.5t91 -4.5t91 4.5t122.5 29.5t118.5 68zM1225 341q62 0 105.5 44t43.5 106 q0 61 -44 105t-105 44q-62 0 -106 -43.5t-44 -105.5t44 -106t106 -44z" />
+<glyph unicode="&#xf282;" horiz-adv-x="1792" d="M69 741h1q16 126 58.5 241.5t115 217t167.5 176t223.5 117.5t276.5 43q231 0 414 -105.5t294 -303.5q104 -187 104 -442v-188h-1125q1 -111 53.5 -192.5t136.5 -122.5t189.5 -57t213 -3t208 46.5t173.5 84.5v-377q-92 -55 -229.5 -92t-312.5 -38t-316 53 q-189 73 -311.5 249t-124.5 372q-3 242 111 412t325 268q-48 -60 -78 -125.5t-46 -159.5h635q8 77 -8 140t-47 101.5t-70.5 66.5t-80.5 41t-75 20.5t-56 8.5l-22 1q-135 -5 -259.5 -44.5t-223.5 -104.5t-176 -140.5t-138 -163.5z" />
+<glyph unicode="&#xf283;" horiz-adv-x="2304" d="M0 32v608h2304v-608q0 -66 -47 -113t-113 -47h-1984q-66 0 -113 47t-47 113zM640 256v-128h384v128h-384zM256 256v-128h256v128h-256zM2144 1408q66 0 113 -47t47 -113v-224h-2304v224q0 66 47 113t113 47h1984z" />
+<glyph unicode="&#xf284;" horiz-adv-x="1792" d="M1549 857q55 0 85.5 -28.5t30.5 -83.5t-34 -82t-91 -27h-136v-177h-25v398h170zM1710 267l-4 -11l-5 -10q-113 -230 -330.5 -366t-474.5 -136q-182 0 -348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71q244 0 454.5 -124t329.5 -338l2 -4l8 -16 q-30 -15 -136.5 -68.5t-163.5 -84.5q-6 -3 -479 -268q384 -183 799 -366zM896 -234q250 0 462.5 132.5t322.5 357.5l-287 129q-72 -140 -206 -222t-292 -82q-151 0 -280 75t-204 204t-75 280t75 280t204 204t280 75t280 -73.5t204 -204.5l280 143q-116 208 -321 329 t-443 121q-119 0 -232.5 -31.5t-209 -87.5t-176.5 -137t-137 -176.5t-87.5 -209t-31.5 -232.5t31.5 -232.5t87.5 -209t137 -176.5t176.5 -137t209 -87.5t232.5 -31.5z" />
+<glyph unicode="&#xf285;" horiz-adv-x="1792" d="M1427 827l-614 386l92 151h855zM405 562l-184 116v858l1183 -743zM1424 697l147 -95v-858l-532 335zM1387 718l-500 -802h-855l356 571z" />
+<glyph unicode="&#xf286;" horiz-adv-x="1792" d="M640 528v224q0 16 -16 16h-96q-16 0 -16 -16v-224q0 -16 16 -16h96q16 0 16 16zM1152 528v224q0 16 -16 16h-96q-16 0 -16 -16v-224q0 -16 16 -16h96q16 0 16 16zM1664 496v-752h-640v320q0 80 -56 136t-136 56t-136 -56t-56 -136v-320h-640v752q0 16 16 16h96 q16 0 16 -16v-112h128v624q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h16v393q-32 19 -32 55q0 26 19 45t45 19t45 -19t19 -45q0 -36 -32 -55v-9h272q16 0 16 -16v-224q0 -16 -16 -16h-272v-128h16q16 0 16 -16v-112h128 v112q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h96q16 0 16 -16v-624h128v112q0 16 16 16h96q16 0 16 -16z" />
+<glyph unicode="&#xf287;" horiz-adv-x="2304" d="M2288 731q16 -8 16 -27t-16 -27l-320 -192q-8 -5 -16 -5q-9 0 -16 4q-16 10 -16 28v128h-858q37 -58 83 -165q16 -37 24.5 -55t24 -49t27 -47t27 -34t31.5 -26t33 -8h96v96q0 14 9 23t23 9h320q14 0 23 -9t9 -23v-320q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23v96h-96 q-32 0 -61 10t-51 23.5t-45 40.5t-37 46t-33.5 57t-28.5 57.5t-28 60.5q-23 53 -37 81.5t-36 65t-44.5 53.5t-46.5 17h-360q-22 -84 -91 -138t-157 -54q-106 0 -181 75t-75 181t75 181t181 75q88 0 157 -54t91 -138h104q24 0 46.5 17t44.5 53.5t36 65t37 81.5q19 41 28 60.5 t28.5 57.5t33.5 57t37 46t45 40.5t51 23.5t61 10h107q21 57 70 92.5t111 35.5q80 0 136 -56t56 -136t-56 -136t-136 -56q-62 0 -111 35.5t-70 92.5h-107q-17 0 -33 -8t-31.5 -26t-27 -34t-27 -47t-24 -49t-24.5 -55q-46 -107 -83 -165h1114v128q0 18 16 28t32 -1z" />
+<glyph unicode="&#xf288;" horiz-adv-x="1792" d="M1150 774q0 -56 -39.5 -95t-95.5 -39h-253v269h253q56 0 95.5 -39.5t39.5 -95.5zM1329 774q0 130 -91.5 222t-222.5 92h-433v-896h180v269h253q130 0 222 91.5t92 221.5zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348 t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
+<glyph unicode="&#xf289;" horiz-adv-x="2304" d="M1645 438q0 59 -34 106.5t-87 68.5q-7 -45 -23 -92q-7 -24 -27.5 -38t-44.5 -14q-12 0 -24 3q-31 10 -45 38.5t-4 58.5q23 71 23 143q0 123 -61 227.5t-166 165.5t-228 61q-134 0 -247 -73t-167 -194q108 -28 188 -106q22 -23 22 -55t-22 -54t-54 -22t-55 22 q-75 75 -180 75q-106 0 -181 -74.5t-75 -180.5t75 -180.5t181 -74.5h1046q79 0 134.5 55.5t55.5 133.5zM1798 438q0 -142 -100.5 -242t-242.5 -100h-1046q-169 0 -289 119.5t-120 288.5q0 153 100 267t249 136q62 184 221 298t354 114q235 0 408.5 -158.5t196.5 -389.5 q116 -25 192.5 -118.5t76.5 -214.5zM2048 438q0 -175 -97 -319q-23 -33 -64 -33q-24 0 -43 13q-26 17 -32 48.5t12 57.5q71 104 71 233t-71 233q-18 26 -12 57t32 49t57.5 11.5t49.5 -32.5q97 -142 97 -318zM2304 438q0 -244 -134 -443q-23 -34 -64 -34q-23 0 -42 13 q-26 18 -32.5 49t11.5 57q108 164 108 358q0 195 -108 357q-18 26 -11.5 57.5t32.5 48.5q26 18 57 12t49 -33q134 -198 134 -442z" />
+<glyph unicode="&#xf28a;" d="M1500 -13q0 -89 -63 -152.5t-153 -63.5t-153.5 63.5t-63.5 152.5q0 90 63.5 153.5t153.5 63.5t153 -63.5t63 -153.5zM1267 268q-115 -15 -192.5 -102.5t-77.5 -205.5q0 -74 33 -138q-146 -78 -379 -78q-109 0 -201 21t-153.5 54.5t-110.5 76.5t-76 85t-44.5 83 t-23.5 66.5t-6 39.5q0 19 4.5 42.5t18.5 56t36.5 58t64 43.5t94.5 18t94 -17.5t63 -41t35.5 -53t17.5 -49t4 -33.5q0 -34 -23 -81q28 -27 82 -42t93 -17l40 -1q115 0 190 51t75 133q0 26 -9 48.5t-31.5 44.5t-49.5 41t-74 44t-93.5 47.5t-119.5 56.5q-28 13 -43 20 q-116 55 -187 100t-122.5 102t-72 125.5t-20.5 162.5q0 78 20.5 150t66 137.5t112.5 114t166.5 77t221.5 28.5q120 0 220 -26t164.5 -67t109.5 -94t64 -105.5t19 -103.5q0 -46 -15 -82.5t-36.5 -58t-48.5 -36t-49 -19.5t-39 -5h-8h-32t-39 5t-44 14t-41 28t-37 46t-24 70.5 t-10 97.5q-15 16 -59 25.5t-81 10.5l-37 1q-68 0 -117.5 -31t-70.5 -70t-21 -76q0 -24 5 -43t24 -46t53 -51t97 -53.5t150 -58.5q76 -25 138.5 -53.5t109 -55.5t83 -59t60.5 -59.5t41 -62.5t26.5 -62t14.5 -63.5t6 -62t1 -62.5z" />
+<glyph unicode="&#xf28b;" d="M704 352v576q0 14 -9 23t-23 9h-256q-14 0 -23 -9t-9 -23v-576q0 -14 9 -23t23 -9h256q14 0 23 9t9 23zM1152 352v576q0 14 -9 23t-23 9h-256q-14 0 -23 -9t-9 -23v-576q0 -14 9 -23t23 -9h256q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103 t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf28c;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM768 96q148 0 273 73t198 198t73 273t-73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273 t73 -273t198 -198t273 -73zM864 320q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-192zM480 320q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-192z" />
+<glyph unicode="&#xf28d;" d="M1088 352v576q0 14 -9 23t-23 9h-576q-14 0 -23 -9t-9 -23v-576q0 -14 9 -23t23 -9h576q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5 t103 -385.5z" />
+<glyph unicode="&#xf28e;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM768 96q148 0 273 73t198 198t73 273t-73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273 t73 -273t198 -198t273 -73zM480 320q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h576q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-576z" />
+<glyph unicode="&#xf290;" horiz-adv-x="1792" d="M1757 128l35 -313q3 -28 -16 -50q-19 -21 -48 -21h-1664q-29 0 -48 21q-19 22 -16 50l35 313h1722zM1664 967l86 -775h-1708l86 775q3 24 21 40.5t43 16.5h256v-128q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5v128h384v-128q0 -53 37.5 -90.5t90.5 -37.5 t90.5 37.5t37.5 90.5v128h256q25 0 43 -16.5t21 -40.5zM1280 1152v-256q0 -26 -19 -45t-45 -19t-45 19t-19 45v256q0 106 -75 181t-181 75t-181 -75t-75 -181v-256q0 -26 -19 -45t-45 -19t-45 19t-19 45v256q0 159 112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5z" />
+<glyph unicode="&#xf291;" horiz-adv-x="2048" d="M1920 768q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5h-15l-115 -662q-8 -46 -44 -76t-82 -30h-1280q-46 0 -82 30t-44 76l-115 662h-15q-53 0 -90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5h1792zM485 -32q26 2 43.5 22.5t15.5 46.5l-32 416q-2 26 -22.5 43.5 t-46.5 15.5t-43.5 -22.5t-15.5 -46.5l32 -416q2 -25 20.5 -42t43.5 -17h5zM896 32v416q0 26 -19 45t-45 19t-45 -19t-19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45zM1280 32v416q0 26 -19 45t-45 19t-45 -19t-19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45zM1632 27l32 416 q2 26 -15.5 46.5t-43.5 22.5t-46.5 -15.5t-22.5 -43.5l-32 -416q-2 -26 15.5 -46.5t43.5 -22.5h5q25 0 43.5 17t20.5 42zM476 1244l-93 -412h-132l101 441q19 88 89 143.5t160 55.5h167q0 26 19 45t45 19h384q26 0 45 -19t19 -45h167q90 0 160 -55.5t89 -143.5l101 -441 h-132l-93 412q-11 44 -45.5 72t-79.5 28h-167q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45h-167q-45 0 -79.5 -28t-45.5 -72z" />
+<glyph unicode="&#xf292;" horiz-adv-x="1792" d="M991 512l64 256h-254l-64 -256h254zM1759 1016l-56 -224q-7 -24 -31 -24h-327l-64 -256h311q15 0 25 -12q10 -14 6 -28l-56 -224q-5 -24 -31 -24h-327l-81 -328q-7 -24 -31 -24h-224q-16 0 -26 12q-9 12 -6 28l78 312h-254l-81 -328q-7 -24 -31 -24h-225q-15 0 -25 12 q-9 12 -6 28l78 312h-311q-15 0 -25 12q-9 12 -6 28l56 224q7 24 31 24h327l64 256h-311q-15 0 -25 12q-10 14 -6 28l56 224q5 24 31 24h327l81 328q7 24 32 24h224q15 0 25 -12q9 -12 6 -28l-78 -312h254l81 328q7 24 32 24h224q15 0 25 -12q9 -12 6 -28l-78 -312h311 q15 0 25 -12q9 -12 6 -28z" />
+<glyph unicode="&#xf293;" d="M841 483l148 -148l-149 -149zM840 1094l149 -149l-148 -148zM710 -130l464 464l-306 306l306 306l-464 464v-611l-255 255l-93 -93l320 -321l-320 -321l93 -93l255 255v-611zM1429 640q0 -209 -32 -365.5t-87.5 -257t-140.5 -162.5t-181.5 -86.5t-219.5 -24.5 t-219.5 24.5t-181.5 86.5t-140.5 162.5t-87.5 257t-32 365.5t32 365.5t87.5 257t140.5 162.5t181.5 86.5t219.5 24.5t219.5 -24.5t181.5 -86.5t140.5 -162.5t87.5 -257t32 -365.5z" />
+<glyph unicode="&#xf294;" horiz-adv-x="1024" d="M596 113l173 172l-173 172v-344zM596 823l173 172l-173 172v-344zM628 640l356 -356l-539 -540v711l-297 -296l-108 108l372 373l-372 373l108 108l297 -296v711l539 -540z" />
+<glyph unicode="&#xf295;" d="M1280 256q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM512 1024q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM1536 256q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5 t112.5 -271.5zM1440 1344q0 -20 -13 -38l-1056 -1408q-19 -26 -51 -26h-160q-26 0 -45 19t-19 45q0 20 13 38l1056 1408q19 26 51 26h160q26 0 45 -19t19 -45zM768 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5 t271.5 -112.5t112.5 -271.5z" />
+<glyph unicode="&#xf296;" horiz-adv-x="1792" />
+<glyph unicode="&#xf297;" horiz-adv-x="1792" />
+<glyph unicode="&#xf298;" horiz-adv-x="1792" />
+<glyph unicode="&#xf299;" horiz-adv-x="1792" />
+<glyph unicode="&#xf29a;" horiz-adv-x="1792" />
+<glyph unicode="&#xf29b;" horiz-adv-x="1792" />
+<glyph unicode="&#xf29c;" horiz-adv-x="1792" />
+<glyph unicode="&#xf29d;" horiz-adv-x="1792" />
+<glyph unicode="&#xf29e;" horiz-adv-x="1792" />
+<glyph unicode="&#xf500;" horiz-adv-x="1792" />
+</font>
+</defs></svg> \ No newline at end of file
diff --git a/container-search-gui/src/main/resources/gui/css/font-awesome/fonts/fontawesome-webfont.ttf b/container-search-gui/src/main/resources/gui/css/font-awesome/fonts/fontawesome-webfont.ttf
new file mode 100644
index 00000000000..26dea7951a7
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/css/font-awesome/fonts/fontawesome-webfont.ttf
Binary files differ
diff --git a/container-search-gui/src/main/resources/gui/css/font-awesome/fonts/fontawesome-webfont.woff b/container-search-gui/src/main/resources/gui/css/font-awesome/fonts/fontawesome-webfont.woff
new file mode 100644
index 00000000000..dc35ce3c2cf
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/css/font-awesome/fonts/fontawesome-webfont.woff
Binary files differ
diff --git a/container-search-gui/src/main/resources/gui/css/font-awesome/fonts/fontawesome-webfont.woff2 b/container-search-gui/src/main/resources/gui/css/font-awesome/fonts/fontawesome-webfont.woff2
new file mode 100644
index 00000000000..500e5172534
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/css/font-awesome/fonts/fontawesome-webfont.woff2
Binary files differ
diff --git a/container-search-gui/src/main/resources/gui/editarea/edit_area/edit_area.css b/container-search-gui/src/main/resources/gui/editarea/edit_area/edit_area.css
new file mode 100755
index 00000000000..3ba22307e56
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/editarea/edit_area/edit_area.css
@@ -0,0 +1,545 @@
+/*
+* Copyright (c) 2008, Christophe Dolivet
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+*
+* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+* Neither the name of EditArea nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+* Open source under the BSD License.
+*/
+
+body, html{
+ margin: 0;
+ padding: 0;
+ height: 100%;
+ border: none;
+ overflow: hidden;
+ background-color: #FFF;
+}
+
+body, html, table, form, textarea{
+ font: 12px monospace, sans-serif;
+}
+
+#editor{
+ border: solid #888 1px;
+ overflow: hidden;
+}
+
+#result{
+ z-index: 4;
+ overflow-x: auto;
+ overflow-y: scroll;
+ border-top: solid #888 1px;
+ border-bottom: solid #888 1px;
+ position: relative;
+ clear: both;
+}
+
+#result.empty{
+ overflow: hidden;
+}
+
+#container{
+ overflow: hidden;
+ border: solid blue 0;
+ position: relative;
+ z-index: 10;
+ padding: 0 5px 0 45px;
+ /*padding-right: 5px;*/
+}
+
+#textarea{
+ position: relative;
+ top: 0;
+ left: 0;
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ z-index: 7;
+ border-width: 0;
+ background-color: transparent;
+ resize: none;
+}
+
+#textarea, #textarea:hover{
+ outline: none; /* safari outline fix */
+}
+
+#content_highlight{
+ white-space: pre;
+ margin: 0;
+ padding: 0;
+ position : absolute;
+ z-index: 4;
+ overflow: visible;
+}
+
+
+#selection_field, #selection_field_text{
+ margin: 0;
+ background-color: #E1F2F9;
+/* height: 1px; */
+ position: absolute;
+ z-index: 5;
+ top: -100px;
+ padding: 0;
+ white-space: pre;
+ overflow: hidden;
+}
+
+#selection_field.show_colors {
+ z-index: 3;
+ background-color:#EDF9FC;
+
+}
+
+#selection_field strong{
+ font-weight:normal;
+}
+
+#selection_field.show_colors *, #selection_field_text * {
+ visibility: hidden;
+}
+
+#selection_field_text{
+ background-color:transparent;
+}
+
+#selection_field_text strong{
+ font-weight:normal;
+ background-color:#3399FE;
+ color: #FFF;
+ visibility:visible;
+}
+
+#container.word_wrap #content_highlight,
+#container.word_wrap #selection_field,
+#container.word_wrap #selection_field_text,
+#container.word_wrap #test_font_size{
+ white-space: pre-wrap; /* css-3 */
+ white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
+ white-space: -pre-wrap; /* Opera 4-6 */
+ white-space: -o-pre-wrap; /* Opera 7 */
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
+ width: 99%;
+}
+
+#line_number{
+ position: absolute;
+ overflow: hidden;
+ border-right: solid black 1px;
+ z-index:8;
+ width: 30px;
+ padding: 0 5px 0 0;
+ margin: 0 0 0 -45px;
+ text-align: right;
+ color: #AAAAAA;
+}
+
+#test_font_size{
+ padding: 0;
+ margin: 0;
+ visibility: hidden;
+ position: absolute;
+ white-space: pre;
+}
+
+pre{
+ margin: 0;
+ padding: 0;
+}
+
+.hidden{
+ opacity: 0.2;
+ filter:alpha(opacity=20);
+}
+
+#result .edit_area_cursor{
+ position: absolute;
+ z-index:6;
+ background-color: #FF6633;
+ top: -100px;
+ margin: 0;
+}
+
+#result .edit_area_selection_field .overline{
+ background-color: #996600;
+}
+
+
+/* area popup */
+.editarea_popup{
+ border: solid 1px #888888;
+ background-color: #ECE9D8;
+ width: 250px;
+ padding: 4px;
+ position: absolute;
+ visibility: hidden;
+ z-index: 15;
+ top: -500px;
+}
+
+.editarea_popup, .editarea_popup table{
+ font-family: sans-serif;
+ font-size: 10pt;
+}
+
+.editarea_popup img{
+ border: 0;
+}
+
+.editarea_popup .close_popup{
+ float: right;
+ line-height: 16px;
+ border: 0;
+ padding: 0;
+}
+
+.editarea_popup h1,.editarea_popup h2,.editarea_popup h3,.editarea_popup h4,.editarea_popup h5,.editarea_popup h6{
+ margin: 0;
+ padding: 0;
+}
+
+.editarea_popup . {
+ text-align: right;
+}
+
+/* Area_search */
+div#area_search_replace{
+ /*width: 250px;*/
+}
+
+div#area_search_replace img{
+ border: 0;
+}
+
+div#area_search_replace div.button{
+ text-align: center;
+ line-height: 1.7em;
+}
+
+div#area_search_replace .button a{
+ cursor: pointer;
+ border: solid 1px #888888;
+ background-color: #DEDEDE;
+ text-decoration: none;
+ padding: 0 2px;
+ color: #000000;
+ white-space: nowrap;
+}
+
+div#area_search_replace a:hover{
+ /*border: solid 1px #888888;*/
+ background-color: #EDEDED;
+}
+
+div#area_search_replace #move_area_search_replace{
+ cursor: move;
+ border: solid 1px #888;
+}
+
+div#area_search_replace #close_area_search_replace{
+ text-align: right;
+ vertical-align: top;
+ white-space: nowrap;
+}
+
+div#area_search_replace #area_search_msg{
+ height: 18px;
+ overflow: hidden;
+ border-top: solid 1px #888;
+ margin-top: 3px;
+}
+
+/* area help */
+#edit_area_help{
+ width: 350px;
+}
+
+#edit_area_help div.close_popup{
+ float: right;
+}
+
+/* area_toolbar */
+.area_toolbar{
+ /*font: 11px sans-serif;*/
+ width: 100%;
+ /*height: 21px; */
+ margin: 0;
+ padding: 0;
+ background-color: #ECE9D8;
+ text-align: center;
+}
+
+.area_toolbar, .area_toolbar table{
+ font: 11px sans-serif;
+}
+
+.area_toolbar img{
+ border: 0;
+ vertical-align: middle;
+}
+
+.area_toolbar input{
+ margin: 0;
+ padding: 0;
+}
+
+.area_toolbar select{
+ font-family: 'MS Sans Serif',sans-serif,Verdana,Arial;
+ font-size: 7pt;
+ font-weight: normal;
+ margin: 2px 0 0 0 ;
+ padding: 0;
+ vertical-align: top;
+ background-color: #F0F0EE;
+}
+
+table.statusbar{
+ width: 100%;
+}
+
+.area_toolbar td.infos{
+ text-align: center;
+ width: 130px;
+ border-right: solid 1px #888;
+ border-width: 0 1px 0 0;
+ padding: 0;
+}
+
+.area_toolbar td.total{
+ text-align: right;
+ width: 50px;
+ padding: 0;
+}
+
+.area_toolbar td.resize{
+ text-align: right;
+}
+/*
+.area_toolbar span{
+ line-height: 1px;
+ padding: 0;
+ margin: 0;
+}*/
+
+.area_toolbar span#resize_area{
+ cursor: nw-resize;
+ visibility: hidden;
+}
+
+/* toolbar buttons */
+.editAreaButtonNormal, .editAreaButtonOver, .editAreaButtonDown, .editAreaSeparator, .editAreaSeparatorLine, .editAreaButtonDisabled, .editAreaButtonSelected {
+ border: 0; margin: 0; padding: 0; background: transparent;
+ margin-top: 0;
+ margin-left: 1px;
+ padding: 0;
+}
+
+.editAreaButtonNormal {
+ border: 1px solid #ECE9D8 !important;
+ cursor: pointer;
+}
+
+.editAreaButtonOver {
+ border: 1px solid #0A246A !important;
+ cursor: pointer;
+ background-color: #B6BDD2;
+}
+
+.editAreaButtonDown {
+ cursor: pointer;
+ border: 1px solid #0A246A !important;
+ background-color: #8592B5;
+}
+
+.editAreaButtonSelected {
+ border: 1px solid #C0C0BB !important;
+ cursor: pointer;
+ background-color: #F4F2E8;
+}
+
+.editAreaButtonDisabled {
+ filter:progid:DXImageTransform.Microsoft.Alpha(opacity=30);
+ -moz-opacity:0.3;
+ opacity: 0.3;
+ border: 1px solid #F0F0EE !important;
+ cursor: pointer;
+}
+
+.editAreaSeparatorLine {
+ margin: 1px 2px;
+ background-color: #C0C0BB;
+ width: 2px;
+ height: 18px;
+}
+
+/* waiting screen */
+#processing{
+ display: none;
+ background-color:#ECE9D8;
+ border: solid #888 1px;
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 100;
+ text-align: center;
+}
+
+#processing_text{
+ position:absolute;
+ left: 50%;
+ top: 50%;
+ width: 200px;
+ height: 20px;
+ margin-left: -100px;
+ margin-top: -10px;
+ text-align: center;
+}
+/* end */
+
+
+/**** tab browsing area ****/
+#tab_browsing_area{
+ display: none;
+ background-color: #CCC9A8;
+ border-top: 1px solid #888;
+ text-align: left;
+ margin: 0;
+}
+
+#tab_browsing_list {
+ padding: 0;
+ margin: 0;
+ list-style-type: none;
+ white-space: nowrap;
+}
+#tab_browsing_list li {
+ float: left;
+ margin: -1px;
+}
+#tab_browsing_list a {
+ position: relative;
+ display: block;
+ text-decoration: none;
+ float: left;
+ cursor: pointer;
+ line-height:14px;
+}
+
+#tab_browsing_list a span {
+ display: block;
+ color: #000;
+ background: #ECE9D8;
+ border: 1px solid #888;
+ border-width: 1px 1px 0;
+ text-align: center;
+ padding: 2px 2px 1px 4px;
+ position: relative; /*IE 6 hack */
+}
+
+#tab_browsing_list a b {
+ display: block;
+ border-bottom: 2px solid #617994;
+}
+
+#tab_browsing_list a .edited {
+ display: none;
+}
+
+#tab_browsing_list a.edited .edited {
+ display: inline;
+}
+
+#tab_browsing_list a img{
+ margin-left: 7px;
+}
+
+#tab_browsing_list a.edited img{
+ margin-left: 3px;
+}
+
+#tab_browsing_list a:hover span {
+ background: #F4F2E8;
+ border-color: #0A246A;
+}
+
+#tab_browsing_list .selected a span{
+ background: #046380;
+ color: #FFF;
+}
+
+
+#no_file_selected{
+ height: 100%;
+ width: 150%; /* Opera need more than 100% */
+ background: #CCC;
+ display: none;
+ z-index: 20;
+ position: absolute;
+}
+
+
+/*** Non-editable mode ***/
+.non_editable #editor
+{
+ border-width: 0 1px;
+}
+
+.non_editable .area_toolbar
+{
+ display: none;
+}
+
+/*** Auto completion ***/
+#auto_completion_area
+{
+ background: #FFF;
+ border: solid 1px #888;
+ position: absolute;
+ z-index: 15;
+ width: 280px;
+ height: 180px;
+ overflow: auto;
+ display:none;
+}
+
+#auto_completion_area a, #auto_completion_area a:visited
+{
+ display: block;
+ padding: 0 2px 1px;
+ color: #000;
+ text-decoration:none;
+}
+
+#auto_completion_area a:hover, #auto_completion_area a:focus, #auto_completion_area a.focus
+{
+ background: #D6E1FE;
+ text-decoration:none;
+}
+
+#auto_completion_area ul
+{
+ margin: 0;
+ padding: 0;
+ list-style: none inside;
+}
+#auto_completion_area li
+{
+ padding: 0;
+}
+#auto_completion_area .prefix
+{
+ font-style: italic;
+ padding: 0 3px;
+}
diff --git a/container-search-gui/src/main/resources/gui/editarea/edit_area/edit_area_full.js b/container-search-gui/src/main/resources/gui/editarea/edit_area/edit_area_full.js
new file mode 100644
index 00000000000..61a50b4241e
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/editarea/edit_area/edit_area_full.js
@@ -0,0 +1,52 @@
+/*
+* Copyright (c) 2008, Christophe Dolivet
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+*
+* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+* Neither the name of EditArea nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+* Open source under the BSD License.
+*/
+ function EAL(){var t=this;t.version="0.8.2";date=new Date();t.start_time=date.getTime();t.win="loading";t.error=false;t.baseURL="";t.template="";t.lang={};t.load_syntax={};t.syntax={};t.loadedFiles=[];t.waiting_loading={};t.scripts_to_load=[];t.sub_scripts_to_load=[];t.syntax_display_name={'basic':'Basic','brainfuck':'Brainfuck','c':'C','coldfusion':'Coldfusion','cpp':'CPP','css':'CSS','html':'HTML','java':'Java','js':'Javascript','pas':'Pascal','perl':'Perl','php':'Php','python':'Python','robotstxt':'Robots txt','ruby':'Ruby','sql':'SQL','tsql':'T-SQL','vb':'Visual Basic','xml':'XML'};t.resize=[];t.hidden={};t.default_settings={debug:false,smooth_selection:true,font_size:"10",font_family:"monospace",start_highlight:false,toolbar:"search,go_to_line,fullscreen,|,undo,redo,|,select_font,|,change_smooth_selection,highlight,reset_highlight,word_wrap,|,help",begin_toolbar:"",end_toolbar:"",is_multi_files:false,allow_resize:"both",show_line_colors:false,min_width:400,min_height:125,replace_tab_by_spaces:false,allow_toggle:true,language:"en",syntax:"",syntax_selection_allow:"basic,brainfuck,c,coldfusion,cpp,css,html,java,js,pas,perl,php,python,ruby,robotstxt,sql,tsql,vb,xml",display:"onload",max_undo:30,browsers:"known",plugins:"",gecko_spellcheck:false,fullscreen:false,is_editable:true,cursor_position:"begin",word_wrap:false,autocompletion:false,load_callback:"",save_callback:"",change_callback:"",submit_callback:"",EA_init_callback:"",EA_delete_callback:"",EA_load_callback:"",EA_unload_callback:"",EA_toggle_on_callback:"",EA_toggle_off_callback:"",EA_file_switch_on_callback:"",EA_file_switch_off_callback:"",EA_file_close_callback:""};t.advanced_buttons=[ ['new_document','','new_document',false],['search','','show_search',false],['go_to_line','','go_to_line',false],['undo','','undo',true],['redo','','redo',true],['change_smooth_selection','','change_smooth_selection_mode',true],['reset_highlight','','resync_highlight',true],['highlight','','change_highlight',true],['help','','show_help',false],['save','','save',false],['load','','load',false],['fullscreen','','toggle_full_screen',false],['word_wrap','','toggle_word_wrap',true],['autocompletion','','toggle_autocompletion'] ];t.set_browser_infos(t);if(t.isIE>=6||t.isGecko||(t.isWebKit&&!t.isSafari<3)||t.isOpera>=9||t.isCamino)t.isValidBrowser=true;
+else t.isValidBrowser=false;t.set_base_url();for(var i=0;i<t.scripts_to_load.length;i++){setTimeout("eAL.load_script('"+t.baseURL+t.scripts_to_load[i]+".js');",1);t.waiting_loading[t.scripts_to_load[i]+".js"]=false;}t.add_event(window,"load",EAL.prototype.window_loaded);};EAL.prototype={has_error:function(){this.error=true;for(var i in EAL.prototype){EAL.prototype[i]=function(){};}},set_browser_infos:function(o){ua=navigator.userAgent;o.isWebKit=/WebKit/.test(ua);o.isGecko=!o.isWebKit&&/Gecko/.test(ua);o.isMac=/Mac/.test(ua);o.isIE=(navigator.appName=="Microsoft Internet Explorer");if(o.isIE){o.isIE=ua.replace(/^.*?MSIE\s+([0-9\.]+).*$/,"$1");if(o.isIE<6)o.has_error();}if(o.isOpera=(ua.indexOf('Opera')!=-1)){o.isOpera=ua.replace(/^.*?Opera.*?([0-9\.]+).*$/i,"$1");if(o.isOpera<9)o.has_error();o.isIE=false;}if(o.isFirefox=(ua.indexOf('Firefox')!=-1))o.isFirefox=ua.replace(/^.*?Firefox.*?([0-9\.]+).*$/i,"$1");if(ua.indexOf('Iceweasel')!=-1)o.isFirefox=ua.replace(/^.*?Iceweasel.*?([0-9\.]+).*$/i,"$1");if(ua.indexOf('GranParadiso')!=-1)o.isFirefox=ua.replace(/^.*?GranParadiso.*?([0-9\.]+).*$/i,"$1");if(ua.indexOf('BonEcho')!=-1)o.isFirefox=ua.replace(/^.*?BonEcho.*?([0-9\.]+).*$/i,"$1");if(ua.indexOf('SeaMonkey')!=-1)o.isFirefox=(ua.replace(/^.*?SeaMonkey.*?([0-9\.]+).*$/i,"$1"))+1;if(o.isCamino=(ua.indexOf('Camino')!=-1))o.isCamino=ua.replace(/^.*?Camino.*?([0-9\.]+).*$/i,"$1");if(o.isSafari=(ua.indexOf('Safari')!=-1))o.isSafari=ua.replace(/^.*?Version\/([0-9]+\.[0-9]+).*$/i,"$1");if(o.isChrome=(ua.indexOf('Chrome')!=-1)){o.isChrome=ua.replace(/^.*?Chrome.*?([0-9\.]+).*$/i,"$1");o.isSafari=false;}},window_loaded:function(){eAL.win="loaded";if(document.forms){for(var i=0;i<document.forms.length;i++){var form=document.forms[i];form.edit_area_replaced_submit=null;try{form.edit_area_replaced_submit=form.onsubmit;form.onsubmit="";}catch(e){}eAL.add_event(form,"submit",EAL.prototype.submit);eAL.add_event(form,"reset",EAL.prototype.reset);}}eAL.add_event(window,"unload",function(){for(var i in eAs){eAL.delete_instance(i);}});},init_ie_textarea:function(id){var a=document.getElementById(id);try{if(a&&typeof(a.focused)=="undefined"){a.focus();a.focused=true;a.selectionStart=a.selectionEnd=0;get_IE_selection(a);eAL.add_event(a,"focus",IE_textarea_focus);eAL.add_event(a,"blur",IE_textarea_blur);}}catch(ex){}},init:function(settings){var t=this,s=settings,i;if(!s["id"])t.has_error();if(t.error)return;if(eAs[s["id"]])t.delete_instance(s["id"]);for(i in t.default_settings){if(typeof(s[i])=="undefined")s[i]=t.default_settings[i];}if(s["browsers"]=="known"&&t.isValidBrowser==false){return;}if(s["begin_toolbar"].length>0)s["toolbar"]=s["begin_toolbar"]+","+s["toolbar"];if(s["end_toolbar"].length>0)s["toolbar"]=s["toolbar"]+","+s["end_toolbar"];s["tab_toolbar"]=s["toolbar"].replace(/ /g,"").split(",");s["plugins"]=s["plugins"].replace(/ /g,"").split(",");for(i=0;i<s["plugins"].length;i++){if(s["plugins"][i].length==0)s["plugins"].splice(i,1);}t.get_template();t.load_script(t.baseURL+"langs/"+s["language"]+".js");if(s["syntax"].length>0){s["syntax"]=s["syntax"].toLowerCase();t.load_script(t.baseURL+"reg_syntax/"+s["syntax"]+".js");}eAs[s["id"]]={"settings":s};eAs[s["id"]]["displayed"]=false;eAs[s["id"]]["hidden"]=false;t.start(s["id"]);},delete_instance:function(id){var d=document,fs=window.frames,span,iframe;eAL.execCommand(id,"EA_delete");if(fs["frame_"+id]&&fs["frame_"+id].editArea){if(eAs[id]["displayed"])eAL.toggle(id,"off");fs["frame_"+id].editArea.execCommand("EA_unload");}span=d.getElementById("EditAreaArroundInfos_"+id);if(span)span.parentNode.removeChild(span);iframe=d.getElementById("frame_"+id);if(iframe){iframe.parentNode.removeChild(iframe);try{delete fs["frame_"+id];}catch(e){}}delete eAs[id];},start:function(id){var t=this,d=document,f,span,father,next,html='',html_toolbar_content='',template,content,i;if(t.win!="loaded"){setTimeout("eAL.start('"+id+"');",50);return;}for(i in t.waiting_loading){if(t.waiting_loading[i]!="loaded"&&typeof(t.waiting_loading[i])!="function"){setTimeout("eAL.start('"+id+"');",50);return;}}if(!t.lang[eAs[id]["settings"]["language"]]||(eAs[id]["settings"]["syntax"].length>0&&!t.load_syntax[eAs[id]["settings"]["syntax"]])){setTimeout("eAL.start('"+id+"');",50);return;}if(eAs[id]["settings"]["syntax"].length>0)t.init_syntax_regexp();if(!d.getElementById("EditAreaArroundInfos_"+id)&&(eAs[id]["settings"]["debug"]||eAs[id]["settings"]["allow_toggle"])){span=d.createElement("span");span.id="EditAreaArroundInfos_"+id;if(eAs[id]["settings"]["allow_toggle"]){checked=(eAs[id]["settings"]["display"]=="onload")?"checked='checked'":"";html+="<div id='edit_area_toggle_"+i+"'>";html+="<input id='edit_area_toggle_checkbox_"+id+"' class='toggle_"+id+"' type='checkbox' onclick='eAL.toggle(\""+id+"\");' accesskey='e' "+checked+" />";html+="<label for='edit_area_toggle_checkbox_"+id+"'>{$toggle}</label></div>";}if(eAs[id]["settings"]["debug"])html+="<textarea id='edit_area_debug_"+id+"' spellcheck='off' style='z-index:20;width:100%;height:120px;overflow:auto;border:solid black 1px;'></textarea><br />";html=t.translate(html,eAs[id]["settings"]["language"]);span.innerHTML=html;father=d.getElementById(id).parentNode;next=d.getElementById(id).nextSibling;if(next==null)father.appendChild(span);
+else father.insertBefore(span,next);}if(!eAs[id]["initialized"]){t.execCommand(id,"EA_init");if(eAs[id]["settings"]["display"]=="later"){eAs[id]["initialized"]=true;return;}}if(t.isIE){t.init_ie_textarea(id);}var area=eAs[id];for(i=0;i<area["settings"]["tab_toolbar"].length;i++){html_toolbar_content+=t.get_control_html(area["settings"]["tab_toolbar"][i],area["settings"]["language"]);}html_toolbar_content=t.translate(html_toolbar_content,area["settings"]["language"],"template");if(!t.iframe_script){t.iframe_script="";for(i=0;i<t.sub_scripts_to_load.length;i++)t.iframe_script+='<script language="javascript" type="text/javascript" src="'+t.baseURL+t.sub_scripts_to_load[i]+'.js"></script>';}for(i=0;i<area["settings"]["plugins"].length;i++){if(!t.all_plugins_loaded)t.iframe_script+='<script language="javascript" type="text/javascript" src="'+t.baseURL+'plugins/'+area["settings"]["plugins"][i]+'/'+area["settings"]["plugins"][i]+'.js"></script>';t.iframe_script+='<script language="javascript" type="text/javascript" src="'+t.baseURL+'plugins/'+area["settings"]["plugins"][i]+'/langs/'+area["settings"]["language"]+'.js"></script>';}if(!t.iframe_css){t.iframe_css="<link href='"+t.baseURL+"edit_area.css' rel='stylesheet' type='text/css' />";}template=t.template.replace(/\[__BASEURL__\]/g,t.baseURL);template=template.replace("[__TOOLBAR__]",html_toolbar_content);template=t.translate(template,area["settings"]["language"],"template");template=template.replace("[__CSSRULES__]",t.iframe_css);template=template.replace("[__JSCODE__]",t.iframe_script);template=template.replace("[__EA_VERSION__]",t.version);area.textarea=d.getElementById(area["settings"]["id"]);eAs[area["settings"]["id"]]["textarea"]=area.textarea;if(typeof(window.frames["frame_"+area["settings"]["id"]])!='undefined')delete window.frames["frame_"+area["settings"]["id"]];father=area.textarea.parentNode;content=d.createElement("iframe");content.name="frame_"+area["settings"]["id"];content.id="frame_"+area["settings"]["id"];content.style.borderWidth="0px";setAttribute(content,"frameBorder","0");content.style.overflow="hidden";content.style.display="none";next=area.textarea.nextSibling;if(next==null)father.appendChild(content);
+else father.insertBefore(content,next);f=window.frames["frame_"+area["settings"]["id"]];f.document.open();f.eAs=eAs;f.area_id=area["settings"]["id"];f.document.area_id=area["settings"]["id"];f.document.write(template);f.document.close();},toggle:function(id,toggle_to){if(!toggle_to)toggle_to=(eAs[id]["displayed"]==true)?"off":"on";if(eAs[id]["displayed"]==true&&toggle_to=="off"){this.toggle_off(id);}
+else if(eAs[id]["displayed"]==false&&toggle_to=="on"){this.toggle_on(id);}return false;},toggle_off:function(id){var fs=window.frames,f,t,parNod,nxtSib,selStart,selEnd,scrollTop,scrollLeft;if(fs["frame_"+id]){f=fs["frame_"+id];t=eAs[id]["textarea"];if(f.editArea.fullscreen['isFull'])f.editArea.toggle_full_screen(false);eAs[id]["displayed"]=false;t.wrap="off";setAttribute(t,"wrap","off");parNod=t.parentNode;nxtSib=t.nextSibling;parNod.removeChild(t);parNod.insertBefore(t,nxtSib);t.value=f.editArea.textarea.value;selStart=f.editArea.last_selection["selectionStart"];selEnd=f.editArea.last_selection["selectionEnd"];scrollTop=f.document.getElementById("result").scrollTop;scrollLeft=f.document.getElementById("result").scrollLeft;document.getElementById("frame_"+id).style.display='none';t.style.display="inline";try{t.focus();}catch(e){};if(this.isIE){t.selectionStart=selStart;t.selectionEnd=selEnd;t.focused=true;set_IE_selection(t);}
+else{if(this.isOpera&&this.isOpera < 9.6){t.setSelectionRange(0,0);}try{t.setSelectionRange(selStart,selEnd);}catch(e){};}t.scrollTop=scrollTop;t.scrollLeft=scrollLeft;f.editArea.execCommand("toggle_off");}},toggle_on:function(id){var fs=window.frames,f,t,selStart=0,selEnd=0,scrollTop=0,scrollLeft=0,curPos,elem;if(fs["frame_"+id]){f=fs["frame_"+id];t=eAs[id]["textarea"];area=f.editArea;area.textarea.value=t.value;curPos=eAs[id]["settings"]["cursor_position"];if(t.use_last==true){selStart=t.last_selectionStart;selEnd=t.last_selectionEnd;scrollTop=t.last_scrollTop;scrollLeft=t.last_scrollLeft;t.use_last=false;}
+else if(curPos=="auto"){try{selStart=t.selectionStart;selEnd=t.selectionEnd;scrollTop=t.scrollTop;scrollLeft=t.scrollLeft;}catch(ex){}}this.set_editarea_size_from_textarea(id,document.getElementById("frame_"+id));t.style.display="none";document.getElementById("frame_"+id).style.display="inline";area.execCommand("focus");eAs[id]["displayed"]=true;area.execCommand("update_size");f.document.getElementById("result").scrollTop=scrollTop;f.document.getElementById("result").scrollLeft=scrollLeft;area.area_select(selStart,selEnd-selStart);area.execCommand("toggle_on");}
+else{elem=document.getElementById(id);elem.last_selectionStart=elem.selectionStart;elem.last_selectionEnd=elem.selectionEnd;elem.last_scrollTop=elem.scrollTop;elem.last_scrollLeft=elem.scrollLeft;elem.use_last=true;eAL.start(id);}},set_editarea_size_from_textarea:function(id,frame){var elem,width,height;elem=document.getElementById(id);width=Math.max(eAs[id]["settings"]["min_width"],elem.offsetWidth)+"px";height=Math.max(eAs[id]["settings"]["min_height"],elem.offsetHeight)+"px";if(elem.style.width.indexOf("%")!=-1)width=elem.style.width;if(elem.style.height.indexOf("%")!=-1)height=elem.style.height;frame.style.width=width;frame.style.height=height;},set_base_url:function(){var t=this,elems,i,docBasePath;if(!this.baseURL){elems=document.getElementsByTagName('script');for(i=0;i<elems.length;i++){if(elems[i].src&&elems[i].src.match(/edit_area_[^\\\/]*$/i)){var src=unescape(elems[i].src);src=src.substring(0,src.lastIndexOf('/'));this.baseURL=src;this.file_name=elems[i].src.substr(elems[i].src.lastIndexOf("/")+1);break;}}}docBasePath=document.location.href;if(docBasePath.indexOf('?')!=-1)docBasePath=docBasePath.substring(0,docBasePath.indexOf('?'));docBasePath=docBasePath.substring(0,docBasePath.lastIndexOf('/'));if(t.baseURL.indexOf('://')==-1&&t.baseURL.charAt(0)!='/'){t.baseURL=docBasePath+"/"+t.baseURL;}t.baseURL+="/";},get_button_html:function(id,img,exec,isFileSpecific,baseURL){var cmd,html;if(!baseURL)baseURL=this.baseURL;cmd='editArea.execCommand(\''+exec+'\')';html='<a id="a_'+id+'" href="javascript:'+cmd+'" onclick="'+cmd+';return false;" onmousedown="return false;" target="_self" fileSpecific="'+(isFileSpecific?'yes':'no')+'"></a>';return html;},get_control_html:function(button_name,lang){var t=this,i,but,html,si;for(i=0;i<t.advanced_buttons.length;i++){but=t.advanced_buttons[i];if(but[0]==button_name){return t.get_button_html(but[0],but[1],but[2],but[3]);}}switch(button_name){case "*":case "return":return "<br />";case "|":case "separator":return '';case "select_font":html="<select id='area_font_size' onchange='javascript:editArea.execCommand(\"change_font_size\")' fileSpecific='yes'>";html+="<option value='-1'>{$font_size}</option>";si=[8,9,10,11,12,14];for(i=0;i<si.length;i++){html+="<option value='"+si[i]+"'>"+si[i]+" pt</option>";}html+="</select>";return html;case "syntax_selection":html="<select id='syntax_selection' onchange='javascript:editArea.execCommand(\"change_syntax\",this.value)' fileSpecific='yes'>";html+="<option value='-1'>{$syntax_selection}</option>";html+="</select>";return html;}return "<span id='tmp_tool_"+button_name+"'>["+button_name+"]</span>";},get_template:function(){if(this.template==""){var xhr_object=null;if(window.XMLHttpRequest)xhr_object=new XMLHttpRequest();
+else if(window.ActiveXObject)xhr_object=new ActiveXObject("Microsoft.XMLHTTP");
+else{alert("XMLHTTPRequest not supported. EditArea not loaded");return;}xhr_object.open("GET",this.baseURL+"template.html",false);xhr_object.send(null);if(xhr_object.readyState==4)this.template=xhr_object.responseText;
+else this.has_error();}},translate:function(text,lang,mode){if(mode=="word")text=eAL.get_word_translation(text,lang);
+else if(mode="template"){eAL.current_language=lang;text=text.replace(/\{\$([^\}]+)\}/gm,eAL.translate_template);}return text;},translate_template:function(){return eAL.get_word_translation(EAL.prototype.translate_template.arguments[1],eAL.current_language);},get_word_translation:function(val,lang){var i;for(i in eAL.lang[lang]){if(i==val)return eAL.lang[lang][i];}return "_"+val;},load_script:function(url){var t=this,d=document,script,head;if(t.loadedFiles[url])return;try{script=d.createElement("script");script.type="text/javascript";script.src=url;script.charset="UTF-8";d.getElementsByTagName("head")[0].appendChild(script);}catch(e){d.write('<sc'+'ript language="javascript" type="text/javascript" src="'+url+'" charset="UTF-8"></sc'+'ript>');}t.loadedFiles[url]=true;},add_event:function(obj,name,handler){try{if(obj.attachEvent){obj.attachEvent("on"+name,handler);}
+else{obj.addEventListener(name,handler,false);}}catch(e){}},remove_event:function(obj,name,handler){try{if(obj.detachEvent)obj.detachEvent("on"+name,handler);
+else obj.removeEventListener(name,handler,false);}catch(e){}},reset:function(e){var formObj,is_child,i,x;formObj=eAL.isIE ? window.event.srcElement:e.target;if(formObj.tagName!='FORM')formObj=formObj.form;for(i in eAs){is_child=false;for(x=0;x<formObj.elements.length;x++){if(formObj.elements[x].id==i)is_child=true;}if(window.frames["frame_"+i]&&is_child&&eAs[i]["displayed"]==true){var exec='window.frames["frame_'+i+'"].editArea.textarea.value=document.getElementById("'+i+'").value;';exec+='window.frames["frame_'+i+'"].editArea.execCommand("focus");';exec+='window.frames["frame_'+i+'"].editArea.check_line_selection();';exec+='window.frames["frame_'+i+'"].editArea.execCommand("reset");';window.setTimeout(exec,10);}}return;},submit:function(e){var formObj,is_child,fs=window.frames,i,x;formObj=eAL.isIE ? window.event.srcElement:e.target;if(formObj.tagName!='FORM')formObj=formObj.form;for(i in eAs){is_child=false;for(x=0;x<formObj.elements.length;x++){if(formObj.elements[x].id==i)is_child=true;}if(is_child){if(fs["frame_"+i]&&eAs[i]["displayed"]==true)document.getElementById(i).value=fs["frame_"+i].editArea.textarea.value;eAL.execCommand(i,"EA_submit");}}if(typeof(formObj.edit_area_replaced_submit)=="function"){res=formObj.edit_area_replaced_submit();if(res==false){if(eAL.isIE)return false;
+else e.preventDefault();}}return;},getValue:function(id){if(window.frames["frame_"+id]&&eAs[id]["displayed"]==true){return window.frames["frame_"+id].editArea.textarea.value;}
+else if(elem=document.getElementById(id)){return elem.value;}return false;},setValue:function(id,new_val){var fs=window.frames;if((f=fs["frame_"+id])&&eAs[id]["displayed"]==true){f.editArea.textarea.value=new_val;f.editArea.execCommand("focus");f.editArea.check_line_selection(false);f.editArea.execCommand("onchange");}
+else if(elem=document.getElementById(id)){elem.value=new_val;}},getSelectionRange:function(id){var sel,eA,fs=window.frames;sel={"start":0,"end":0};if(fs["frame_"+id]&&eAs[id]["displayed"]==true){eA=fs["frame_"+id].editArea;sel["start"]=eA.textarea.selectionStart;sel["end"]=eA.textarea.selectionEnd;}
+else if(elem=document.getElementById(id)){sel=getSelectionRange(elem);}return sel;},setSelectionRange:function(id,new_start,new_end){var fs=window.frames;if(fs["frame_"+id]&&eAs[id]["displayed"]==true){fs["frame_"+id].editArea.area_select(new_start,new_end-new_start);if(!this.isIE){fs["frame_"+id].editArea.check_line_selection(false);fs["frame_"+id].editArea.scroll_to_view();}}
+else if(elem=document.getElementById(id)){setSelectionRange(elem,new_start,new_end);}},getSelectedText:function(id){var sel=this.getSelectionRange(id);return this.getValue(id).substring(sel["start"],sel["end"]);},setSelectedText:function(id,new_val){var fs=window.frames,d=document,sel,text,scrollTop,scrollLeft,new_sel_end;new_val=new_val.replace(/\r/g,"");sel=this.getSelectionRange(id);text=this.getValue(id);if(fs["frame_"+id]&&eAs[id]["displayed"]==true){scrollTop=fs["frame_"+id].document.getElementById("result").scrollTop;scrollLeft=fs["frame_"+id].document.getElementById("result").scrollLeft;}
+else{scrollTop=d.getElementById(id).scrollTop;scrollLeft=d.getElementById(id).scrollLeft;}text=text.substring(0,sel["start"])+new_val+text.substring(sel["end"]);this.setValue(id,text);new_sel_end=sel["start"]+new_val.length;this.setSelectionRange(id,sel["start"],new_sel_end);if(new_val !=this.getSelectedText(id).replace(/\r/g,"")){this.setSelectionRange(id,sel["start"],new_sel_end+new_val.split("\n").length-1);}if(fs["frame_"+id]&&eAs[id]["displayed"]==true){fs["frame_"+id].document.getElementById("result").scrollTop=scrollTop;fs["frame_"+id].document.getElementById("result").scrollLeft=scrollLeft;fs["frame_"+id].editArea.execCommand("onchange");}
+else{d.getElementById(id).scrollTop=scrollTop;d.getElementById(id).scrollLeft=scrollLeft;}},insertTags:function(id,open_tag,close_tag){var old_sel,new_sel;old_sel=this.getSelectionRange(id);text=open_tag+this.getSelectedText(id)+close_tag;eAL.setSelectedText(id,text);new_sel=this.getSelectionRange(id);if(old_sel["end"] > old_sel["start"])this.setSelectionRange(id,new_sel["end"],new_sel["end"]);
+else this.setSelectionRange(id,old_sel["start"]+open_tag.length,old_sel["start"]+open_tag.length);},hide:function(id){var fs=window.frames,d=document,t=this,scrollTop,scrollLeft,span;if(d.getElementById(id)&&!t.hidden[id]){t.hidden[id]={};t.hidden[id]["selectionRange"]=t.getSelectionRange(id);if(d.getElementById(id).style.display!="none"){t.hidden[id]["scrollTop"]=d.getElementById(id).scrollTop;t.hidden[id]["scrollLeft"]=d.getElementById(id).scrollLeft;}if(fs["frame_"+id]){t.hidden[id]["toggle"]=eAs[id]["displayed"];if(fs["frame_"+id]&&eAs[id]["displayed"]==true){scrollTop=fs["frame_"+id].document.getElementById("result").scrollTop;scrollLeft=fs["frame_"+id].document.getElementById("result").scrollLeft;}
+else{scrollTop=d.getElementById(id).scrollTop;scrollLeft=d.getElementById(id).scrollLeft;}t.hidden[id]["scrollTop"]=scrollTop;t.hidden[id]["scrollLeft"]=scrollLeft;if(eAs[id]["displayed"]==true)eAL.toggle_off(id);}span=d.getElementById("EditAreaArroundInfos_"+id);if(span){span.style.display='none';}d.getElementById(id).style.display="none";}},show:function(id){var fs=window.frames,d=document,t=this,span;if((elem=d.getElementById(id))&&t.hidden[id]){elem.style.display="inline";elem.scrollTop=t.hidden[id]["scrollTop"];elem.scrollLeft=t.hidden[id]["scrollLeft"];span=d.getElementById("EditAreaArroundInfos_"+id);if(span){span.style.display='inline';}if(fs["frame_"+id]){elem.style.display="inline";if(t.hidden[id]["toggle"]==true)eAL.toggle_on(id);scrollTop=t.hidden[id]["scrollTop"];scrollLeft=t.hidden[id]["scrollLeft"];if(fs["frame_"+id]&&eAs[id]["displayed"]==true){fs["frame_"+id].document.getElementById("result").scrollTop=scrollTop;fs["frame_"+id].document.getElementById("result").scrollLeft=scrollLeft;}
+else{elem.scrollTop=scrollTop;elem.scrollLeft=scrollLeft;}}sel=t.hidden[id]["selectionRange"];t.setSelectionRange(id,sel["start"],sel["end"]);delete t.hidden[id];}},getCurrentFile:function(id){return this.execCommand(id,'get_file',this.execCommand(id,'curr_file'));},getFile:function(id,file_id){return this.execCommand(id,'get_file',file_id);},getAllFiles:function(id){return this.execCommand(id,'get_all_files()');},openFile:function(id,file_infos){return this.execCommand(id,'open_file',file_infos);},closeFile:function(id,file_id){return this.execCommand(id,'close_file',file_id);},setFileEditedMode:function(id,file_id,to){var reg1,reg2;reg1=new RegExp('\\\\','g');reg2=new RegExp('"','g');return this.execCommand(id,'set_file_edited_mode("'+file_id.replace(reg1,'\\\\').replace(reg2,'\\"')+'",'+to+')');},execCommand:function(id,cmd,fct_param){switch(cmd){case "EA_init":if(eAs[id]['settings']["EA_init_callback"].length>0)eval(eAs[id]['settings']["EA_init_callback"]+"('"+id+"');");break;case "EA_delete":if(eAs[id]['settings']["EA_delete_callback"].length>0)eval(eAs[id]['settings']["EA_delete_callback"]+"('"+id+"');");break;case "EA_submit":if(eAs[id]['settings']["submit_callback"].length>0)eval(eAs[id]['settings']["submit_callback"]+"('"+id+"');");break;}if(window.frames["frame_"+id]&&window.frames["frame_"+id].editArea){if(fct_param!=undefined)return eval('window.frames["frame_'+id+'"].editArea.'+cmd+'(fct_param);');
+else return eval('window.frames["frame_'+id+'"].editArea.'+cmd+';');}return false;}};var eAL=new EAL();var eAs={}; function getAttribute(elm,aName){var aValue,taName,i;try{aValue=elm.getAttribute(aName);}catch(exept){}if(! aValue){for(i=0;i < elm.attributes.length;i++){taName=elm.attributes[i] .name.toLowerCase();if(taName==aName){aValue=elm.attributes[i] .value;return aValue;}}}return aValue;};function setAttribute(elm,attr,val){if(attr=="class"){elm.setAttribute("className",val);elm.setAttribute("class",val);}
+else{elm.setAttribute(attr,val);}};function getChildren(elem,elem_type,elem_attribute,elem_attribute_match,option,depth){if(!option)var option="single";if(!depth)var depth=-1;if(elem){var children=elem.childNodes;var result=null;var results=[];for(var x=0;x<children.length;x++){strTagName=new String(children[x].tagName);children_class="?";if(strTagName!="undefined"){child_attribute=getAttribute(children[x],elem_attribute);if((strTagName.toLowerCase()==elem_type.toLowerCase()||elem_type=="")&&(elem_attribute==""||child_attribute==elem_attribute_match)){if(option=="all"){results.push(children[x]);}
+else{return children[x];}}if(depth!=0){result=getChildren(children[x],elem_type,elem_attribute,elem_attribute_match,option,depth-1);if(option=="all"){if(result.length>0){results=results.concat(result);}}
+else if(result!=null){return result;}}}}if(option=="all")return results;}return null;};function isChildOf(elem,parent){if(elem){if(elem==parent)return true;while(elem.parentNode !='undefined'){return isChildOf(elem.parentNode,parent);}}return false;};function getMouseX(e){if(e!=null&&typeof(e.pageX)!="undefined"){return e.pageX;}
+else{return(e!=null?e.x:event.x)+document.documentElement.scrollLeft;}};function getMouseY(e){if(e!=null&&typeof(e.pageY)!="undefined"){return e.pageY;}
+else{return(e!=null?e.y:event.y)+document.documentElement.scrollTop;}};function calculeOffsetLeft(r){return calculeOffset(r,"offsetLeft")};function calculeOffsetTop(r){return calculeOffset(r,"offsetTop")};function calculeOffset(element,attr){var offset=0;while(element){offset+=element[attr];element=element.offsetParent}return offset;};function get_css_property(elem,prop){if(document.defaultView){return document.defaultView.getComputedStyle(elem,null).getPropertyValue(prop);}
+else if(elem.currentStyle){var prop=prop.replace(/-\D/gi,function(sMatch){return sMatch.charAt(sMatch.length-1).toUpperCase();});return elem.currentStyle[prop];}
+else return null;}var _mCE;function start_move_element(e,id,frame){var elem_id=(e.target||e.srcElement).id;if(id)elem_id=id;if(!frame)frame=window;if(frame.event)e=frame.event;_mCE=frame.document.getElementById(elem_id);_mCE.frame=frame;frame.document.onmousemove=move_element;frame.document.onmouseup=end_move_element;mouse_x=getMouseX(e);mouse_y=getMouseY(e);_mCE.start_pos_x=mouse_x-(_mCE.style.left.replace("px","")||calculeOffsetLeft(_mCE));_mCE.start_pos_y=mouse_y-(_mCE.style.top.replace("px","")||calculeOffsetTop(_mCE));return false;};function end_move_element(e){_mCE.frame.document.onmousemove="";_mCE.frame.document.onmouseup="";_mCE=null;};function move_element(e){var newTop,newLeft,maxLeft;if(_mCE.frame&&_mCE.frame.event)e=_mCE.frame.event;newTop=getMouseY(e)-_mCE.start_pos_y;newLeft=getMouseX(e)-_mCE.start_pos_x;maxLeft=_mCE.frame.document.body.offsetWidth-_mCE.offsetWidth;max_top=_mCE.frame.document.body.offsetHeight-_mCE.offsetHeight;newTop=Math.min(Math.max(0,newTop),max_top);newLeft=Math.min(Math.max(0,newLeft),maxLeft);_mCE.style.top=newTop+"px";_mCE.style.left=newLeft+"px";return false;};var nav=eAL.nav;function getSelectionRange(textarea){return{"start":textarea.selectionStart,"end":textarea.selectionEnd};};function setSelectionRange(t,start,end){t.focus();start=Math.max(0,Math.min(t.value.length,start));end=Math.max(start,Math.min(t.value.length,end));if(nav.isOpera&&nav.isOpera < 9.6){t.selectionEnd=1;t.selectionStart=0;t.selectionEnd=1;t.selectionStart=0;}t.selectionStart=start;t.selectionEnd=end;if(nav.isIE)set_IE_selection(t);};function get_IE_selection(t){var d=document,div,range,stored_range,elem,scrollTop,relative_top,line_start,line_nb,range_start,range_end,tab;if(t&&t.focused){if(!t.ea_line_height){div=d.createElement("div");div.style.fontFamily=get_css_property(t,"font-family");div.style.fontSize=get_css_property(t,"font-size");div.style.visibility="hidden";div.innerHTML="0";d.body.appendChild(div);t.ea_line_height=div.offsetHeight;d.body.removeChild(div);}range=d.selection.createRange();try{stored_range=range.duplicate();stored_range.moveToElementText(t);stored_range.setEndPoint('EndToEnd',range);if(stored_range.parentElement()==t){elem=t;scrollTop=0;while(elem.parentNode){scrollTop+=elem.scrollTop;elem=elem.parentNode;}relative_top=range.offsetTop-calculeOffsetTop(t)+scrollTop;line_start=Math.round((relative_top / t.ea_line_height)+1);line_nb=Math.round(range.boundingHeight / t.ea_line_height);range_start=stored_range.text.length-range.text.length;tab=t.value.substr(0,range_start).split("\n");range_start+=(line_start-tab.length)*2;t.selectionStart=range_start;range_end=t.selectionStart+range.text.length;tab=t.value.substr(0,range_start+range.text.length).split("\n");range_end+=(line_start+line_nb-1-tab.length)*2;t.selectionEnd=range_end;}}catch(e){}}if(t&&t.id){setTimeout("get_IE_selection(document.getElementById('"+t.id+"'));",50);}};function IE_textarea_focus(){event.srcElement.focused=true;}function IE_textarea_blur(){event.srcElement.focused=false;}function set_IE_selection(t){var nbLineStart,nbLineStart,nbLineEnd,range;if(!window.closed){nbLineStart=t.value.substr(0,t.selectionStart).split("\n").length-1;nbLineEnd=t.value.substr(0,t.selectionEnd).split("\n").length-1;try{range=document.selection.createRange();range.moveToElementText(t);range.setEndPoint('EndToStart',range);range.moveStart('character',t.selectionStart-nbLineStart);range.moveEnd('character',t.selectionEnd-nbLineEnd-(t.selectionStart-nbLineStart));range.select();}catch(e){}}};eAL.waiting_loading["elements_functions.js"]="loaded";
+ EAL.prototype.start_resize_area=function(){var d=document,a,div,width,height,father;d.onmouseup=eAL.end_resize_area;d.onmousemove=eAL.resize_area;eAL.toggle(eAL.resize["id"]);a=eAs[eAL.resize["id"]]["textarea"];div=d.getElementById("edit_area_resize");if(!div){div=d.createElement("div");div.id="edit_area_resize";div.style.border="dashed #888888 1px";}width=a.offsetWidth-2;height=a.offsetHeight-2;div.style.display="block";div.style.width=width+"px";div.style.height=height+"px";father=a.parentNode;father.insertBefore(div,a);a.style.display="none";eAL.resize["start_top"]=calculeOffsetTop(div);eAL.resize["start_left"]=calculeOffsetLeft(div);};EAL.prototype.end_resize_area=function(e){var d=document,div,a,width,height;d.onmouseup="";d.onmousemove="";div=d.getElementById("edit_area_resize");a=eAs[eAL.resize["id"]]["textarea"];width=Math.max(eAs[eAL.resize["id"]]["settings"]["min_width"],div.offsetWidth-4);height=Math.max(eAs[eAL.resize["id"]]["settings"]["min_height"],div.offsetHeight-4);if(eAL.isIE==6){width-=2;height-=2;}a.style.width=width+"px";a.style.height=height+"px";div.style.display="none";a.style.display="inline";a.selectionStart=eAL.resize["selectionStart"];a.selectionEnd=eAL.resize["selectionEnd"];eAL.toggle(eAL.resize["id"]);return false;};EAL.prototype.resize_area=function(e){var allow,newHeight,newWidth;allow=eAs[eAL.resize["id"]]["settings"]["allow_resize"];if(allow=="both"||allow=="y"){newHeight=Math.max(20,getMouseY(e)-eAL.resize["start_top"]);document.getElementById("edit_area_resize").style.height=newHeight+"px";}if(allow=="both"||allow=="x"){newWidth=Math.max(20,getMouseX(e)-eAL.resize["start_left"]);document.getElementById("edit_area_resize").style.width=newWidth+"px";}return false;};eAL.waiting_loading["resize_area.js"]="loaded";
+ EAL.prototype.get_regexp=function(text_array){res="(\\b)(";for(i=0;i<text_array.length;i++){if(i>0)res+="|";res+=this.get_escaped_regexp(text_array[i]);}res+=")(\\b)";reg=new RegExp(res);return res;};EAL.prototype.get_escaped_regexp=function(str){return str.toString().replace(/(\.|\?|\*|\+|\\|\(|\)|\[|\]|\}|\{|\$|\^|\|)/g,"\\$1");};EAL.prototype.init_syntax_regexp=function(){var lang_style={};for(var lang in this.load_syntax){if(!this.syntax[lang]){this.syntax[lang]={};this.syntax[lang]["keywords_reg_exp"]={};this.keywords_reg_exp_nb=0;if(this.load_syntax[lang]['KEYWORDS']){param="g";if(this.load_syntax[lang]['KEYWORD_CASE_SENSITIVE']===false)param+="i";for(var i in this.load_syntax[lang]['KEYWORDS']){if(typeof(this.load_syntax[lang]['KEYWORDS'][i])=="function")continue;this.syntax[lang]["keywords_reg_exp"][i]=new RegExp(this.get_regexp(this.load_syntax[lang]['KEYWORDS'][i]),param);this.keywords_reg_exp_nb++;}}if(this.load_syntax[lang]['OPERATORS']){var str="";var nb=0;for(var i in this.load_syntax[lang]['OPERATORS']){if(typeof(this.load_syntax[lang]['OPERATORS'][i])=="function")continue;if(nb>0)str+="|";str+=this.get_escaped_regexp(this.load_syntax[lang]['OPERATORS'][i]);nb++;}if(str.length>0)this.syntax[lang]["operators_reg_exp"]=new RegExp("("+str+")","g");}if(this.load_syntax[lang]['DELIMITERS']){var str="";var nb=0;for(var i in this.load_syntax[lang]['DELIMITERS']){if(typeof(this.load_syntax[lang]['DELIMITERS'][i])=="function")continue;if(nb>0)str+="|";str+=this.get_escaped_regexp(this.load_syntax[lang]['DELIMITERS'][i]);nb++;}if(str.length>0)this.syntax[lang]["delimiters_reg_exp"]=new RegExp("("+str+")","g");}var syntax_trace=[];this.syntax[lang]["quotes"]={};var quote_tab=[];if(this.load_syntax[lang]['QUOTEMARKS']){for(var i in this.load_syntax[lang]['QUOTEMARKS']){if(typeof(this.load_syntax[lang]['QUOTEMARKS'][i])=="function")continue;var x=this.get_escaped_regexp(this.load_syntax[lang]['QUOTEMARKS'][i]);this.syntax[lang]["quotes"][x]=x;quote_tab[quote_tab.length]="("+x+"(\\\\.|[^"+x+"])*(?:"+x+"|$))";syntax_trace.push(x);}}this.syntax[lang]["comments"]={};if(this.load_syntax[lang]['COMMENT_SINGLE']){for(var i in this.load_syntax[lang]['COMMENT_SINGLE']){if(typeof(this.load_syntax[lang]['COMMENT_SINGLE'][i])=="function")continue;var x=this.get_escaped_regexp(this.load_syntax[lang]['COMMENT_SINGLE'][i]);quote_tab[quote_tab.length]="("+x+"(.|\\r|\\t)*(\\n|$))";syntax_trace.push(x);this.syntax[lang]["comments"][x]="\n";}}if(this.load_syntax[lang]['COMMENT_MULTI']){for(var i in this.load_syntax[lang]['COMMENT_MULTI']){if(typeof(this.load_syntax[lang]['COMMENT_MULTI'][i])=="function")continue;var start=this.get_escaped_regexp(i);var end=this.get_escaped_regexp(this.load_syntax[lang]['COMMENT_MULTI'][i]);quote_tab[quote_tab.length]="("+start+"(.|\\n|\\r)*?("+end+"|$))";syntax_trace.push(start);syntax_trace.push(end);this.syntax[lang]["comments"][i]=this.load_syntax[lang]['COMMENT_MULTI'][i];}}if(quote_tab.length>0)this.syntax[lang]["comment_or_quote_reg_exp"]=new RegExp("("+quote_tab.join("|")+")","gi");if(syntax_trace.length>0)this.syntax[lang]["syntax_trace_regexp"]=new RegExp("((.|\n)*?)(\\\\*("+syntax_trace.join("|")+"|$))","gmi");if(this.load_syntax[lang]['SCRIPT_DELIMITERS']){this.syntax[lang]["script_delimiters"]={};for(var i in this.load_syntax[lang]['SCRIPT_DELIMITERS']){if(typeof(this.load_syntax[lang]['SCRIPT_DELIMITERS'][i])=="function")continue;this.syntax[lang]["script_delimiters"][i]=this.load_syntax[lang]['SCRIPT_DELIMITERS'];}}this.syntax[lang]["custom_regexp"]={};if(this.load_syntax[lang]['REGEXPS']){for(var i in this.load_syntax[lang]['REGEXPS']){if(typeof(this.load_syntax[lang]['REGEXPS'][i])=="function")continue;var val=this.load_syntax[lang]['REGEXPS'][i];if(!this.syntax[lang]["custom_regexp"][val['execute']])this.syntax[lang]["custom_regexp"][val['execute']]={};this.syntax[lang]["custom_regexp"][val['execute']][i]={'regexp':new RegExp(val['search'],val['modifiers']),'class':val['class']};}}if(this.load_syntax[lang]['STYLES']){lang_style[lang]={};for(var i in this.load_syntax[lang]['STYLES']){if(typeof(this.load_syntax[lang]['STYLES'][i])=="function")continue;if(typeof(this.load_syntax[lang]['STYLES'][i])!="string"){for(var j in this.load_syntax[lang]['STYLES'][i]){lang_style[lang][j]=this.load_syntax[lang]['STYLES'][i][j];}}
+else{lang_style[lang][i]=this.load_syntax[lang]['STYLES'][i];}}}var style="";for(var i in lang_style[lang]){if(lang_style[lang][i].length>0){style+="."+lang+" ."+i.toLowerCase()+" span{"+lang_style[lang][i]+"}\n";style+="."+lang+" ."+i.toLowerCase()+"{"+lang_style[lang][i]+"}\n";}}this.syntax[lang]["styles"]=style;}}};eAL.waiting_loading["reg_syntax.js"]="loaded";
+var editAreaLoader= eAL;var editAreas=eAs;EditAreaLoader=EAL;editAreaLoader.iframe_script= "<script type='text/javascript'> Ã EA(){var t=Á;t.error=Ì;t.inlinePopup=[{popup_id:\"area_search_replace\",icon_id:\"search\"},{popup_id:\"edit_area_help\",icon_id:\"help\"}];t.plugins={};t.line_number=0;È.eAL.set_browser_infos(t);if(t.isIE >=8)t.isIE=7;t.É={};t.last_text_to_highlight=\"\";t.last_hightlighted_text=\"\";t.syntax_list=[];t.allready_used_syntax={};t.check_line_selection_timer=50;t.ÂFocused=Ì;t.highlight_selection_line=null;t.previous=[];t.next=[];t.last_undo=\"\";t.files={};t.filesIdAssoc={};t.curr_file='';t.assocBracket={};t.revertAssocBracket={};t.assocBracket[\"(\"]=\")\";t.assocBracket[\"{\"]=\"}\";t.assocBracket[\"[\"]=\"]\";for(var index in t.assocBracket){t.revertAssocBracket[t.assocBracket[index]]=index;}t.is_editable=Ë;t.lineHeight=16;t.tab_nb_char=8;if(t.isOpera)t.tab_nb_char=6;t.is_tabbing=Ì;t.fullscreen={'isFull':Ì};t.isResizing=Ì;t.id=area_id;t.Å=eAs[t.id][\"Å\"];if((\"\"+t.Å['replace_tab_by_spaces']).match(/^[0-9]+$/)){t.tab_nb_char=t.Å['replace_tab_by_spaces'];t.tabulation=\"\";for(var i=0;i<t.tab_nb_char;i++)t.tabulation+=\" \";}\nelse{t.tabulation=\"\t\";}if(t.Å[\"syntax_selection_allow\"]&&t.Å[\"syntax_selection_allow\"].Æ>0)t.syntax_list=t.Å[\"syntax_selection_allow\"].replace(/ /g,\"\").split(\",\");if(t.Å['syntax'])t.allready_used_syntax[t.Å['syntax']]=Ë;};EA.Ä.init=Ã(){var t=Á,a,s=t.Å;t.Â=_$(\"Â\");t.container=_$(\"container\");t.result=_$(\"result\");t.content_highlight=_$(\"content_highlight\");t.selection_field=_$(\"selection_field\");t.selection_field_text=_$(\"selection_field_text\");t.processing_screen=_$(\"processing\");t.editor_area=_$(\"editor\");t.tab_browsing_area=_$(\"tab_browsing_area\");t.test_font_size=_$(\"test_font_size\");a=t.Â;if(!s['is_editable'])t.set_editable(Ì);t.set_show_line_colors(s['show_line_colors']);if(syntax_selec=_$(\"syntax_selection\")){for(var i=0;i<t.syntax_list.Æ;i++){var syntax=t.syntax_list[i];var option=document.createElement(\"option\");option.Ê=syntax;if(syntax==s['syntax'])option.selected=\"selected\";dispSyntax=È.eAL.syntax_display_name[ syntax ];option.innerHTML=typeof(dispSyntax)=='undefined' ? syntax.substring(0,1).toUpperCase()+syntax.substring(1):dispSyntax;syntax_selec.appendChild(option);}}spans=È.getChildren(_$(\"toolbar_1\"),\"span\",\"\",\"\",\"all\",-1);for(var i=0;i<spans.Æ;i++){id=spans[i].id.replace(/tmp_tool_(.*)/,\"$1\");if(id!=spans[i].id){for(var j in t.plugins){if(typeof(t.plugins[j].get_control_html)==\"Ã\"){html=t.plugins[j].get_control_html(id);if(html!=Ì){html=t.get_translation(html,\"template\");var new_span=document.createElement(\"span\");new_span.innerHTML=html;var father=spans[i].ÈNode;spans[i].ÈNode.replaceChild(new_span,spans[i]);break;}}}}}if(s[\"debug\"]){t.debug=È.document.getElementById(\"edit_area_debug_\"+t.id);}if(_$(\"redo\")!=null)t.switchClassSticky(_$(\"redo\"),'editAreaButtonDisabled',Ë);if(typeof(È.eAL.syntax[s[\"syntax\"]])!=\"undefined\"){for(var i in È.eAL.syntax){if(typeof(È.eAL.syntax[i][\"Çs\"])!=\"undefined\"){t.add_Ç(È.eAL.syntax[i][\"Çs\"]);}}}if(t.isOpera)_$(\"editor\").onkeypress=keyDown;\nelse _$(\"editor\").onkeydown=keyDown;for(var i=0;i<t.inlinePopup.Æ;i++){if(t.isOpera)_$(t.inlinePopup[i][\"popup_id\"]).onkeypress=keyDown;\nelse _$(t.inlinePopup[i][\"popup_id\"]).onkeydown=keyDown;}if(s[\"allow_resize\"]==\"both\"||s[\"allow_resize\"]==\"x\"||s[\"allow_resize\"]==\"y\")t.allow_resize(Ë);È.eAL.toggle(t.id,\"on\");t.change_smooth_selection_mode(eA.smooth_selection);t.execCommand(\"change_highlight\",s[\"start_highlight\"]);t.set_font(eA.Å[\"font_family\"],eA.Å[\"font_size\"]);children=È.getChildren(document.body,\"\",\"selec\",\"none\",\"all\",-1);for(var i=0;i<children.Æ;i++){if(t.isIE)children[i].unselectable=Ë;\nelse children[i].onmousedown=Ã(){return Ì};}a.spellcheck=s[\"gecko_spellcheck\"];if(t.isFirefox >='3'){t.content_highlight.Ç.paddingLeft=\"1px\";t.selection_field.Ç.paddingLeft=\"1px\";t.selection_field_text.Ç.paddingLeft=\"1px\";}if(t.isIE&&t.isIE < 8){a.Ç.marginTop=\"-1px\";}if(t.isSafari){t.editor_area.Ç.position=\"absolute\";a.Ç.marginLeft=\"-3px\";if(t.isSafari < 3.2)a.Ç.marginTop=\"1px\";}È.eAL.add_event(t.result,\"click\",Ã(e){if((e.target||e.srcElement)==eA.result){eA.area_select(eA.Â.Ê.Æ,0);}});if(s['is_multi_files']!=Ì)t.open_file({'id':t.curr_file,'text':''});t.set_word_wrap(s['word_wrap']);setTimeout(\"eA.focus();eA.manage_size();eA.execCommand('EA_load');\",10);t.check_undo();t.check_line_selection(Ë);t.scroll_to_view();for(var i in t.plugins){if(typeof(t.plugins[i].onload)==\"Ã\")t.plugins[i].onload();}if(s['fullscreen']==Ë)t.toggle_full_screen(Ë);È.eAL.add_event(window,\"resize\",eA.update_size);È.eAL.add_event(È.window,\"resize\",eA.update_size);È.eAL.add_event(top.window,\"resize\",eA.update_size);È.eAL.add_event(window,\"unload\",Ã(){if(È.eAL){È.eAL.remove_event(È.window,\"resize\",eA.update_size);È.eAL.remove_event(top.window,\"resize\",eA.update_size);}if(eAs[eA.id]&&eAs[eA.id][\"displayed\"]){eA.execCommand(\"EA_unload\");}});};EA.Ä.update_size=Ã(){var d=document,pd=È.document,height,width,popup,maxLeft,maxTop;if(typeof eAs !='undefined'&&eAs[eA.id]&&eAs[eA.id][\"displayed\"]==Ë){if(eA.fullscreen['isFull']){pd.getElementById(\"frame_\"+eA.id).Ç.width=pd.getElementsByTagName(\"html\")[0].clientWidth+\"px\";pd.getElementById(\"frame_\"+eA.id).Ç.height=pd.getElementsByTagName(\"html\")[0].clientHeight+\"px\";}if(eA.tab_browsing_area.Ç.display=='block'&&(!eA.isIE||eA.isIE >=8)){eA.tab_browsing_area.Ç.height=\"0px\";eA.tab_browsing_area.Ç.height=(eA.result.offsetTop-eA.tab_browsing_area.offsetTop-1)+\"px\";}height=d.body.offsetHeight-eA.get_all_toolbar_height()-4;eA.result.Ç.height=height+\"px\";width=d.body.offsetWidth-2;eA.result.Ç.width=width+\"px\";for(i=0;i < eA.inlinePopup.Æ;i++){popup=_$(eA.inlinePopup[i][\"popup_id\"]);maxLeft=d.body.offsetWidth-popup.offsetWidth;maxTop=d.body.offsetHeight-popup.offsetHeight;if(popup.offsetTop > maxTop)popup.Ç.top=maxTop+\"px\";if(popup.offsetLeft > maxLeft)popup.Ç.left=maxLeft+\"px\";}eA.manage_size(Ë);eA.fixLinesHeight(eA.Â.Ê,0,-1);}};EA.Ä.manage_size=Ã(onlyOneTime){if(!eAs[Á.id])return Ì;if(eAs[Á.id][\"displayed\"]==Ë&&Á.ÂFocused){var area_height,resized=Ì;if(!Á.Å['word_wrap']){var area_width=Á.Â.scrollWidth;area_height=Á.Â.scrollHeight;if(Á.isOpera&&Á.isOpera < 9.6){area_width=10000;}if(Á.Â.previous_scrollWidth!=area_width){Á.container.Ç.width=area_width+\"px\";Á.Â.Ç.width=area_width+\"px\";Á.content_highlight.Ç.width=area_width+\"px\";Á.Â.previous_scrollWidth=area_width;resized=Ë;}}if(Á.Å['word_wrap']){newW=Á.Â.offsetWidth;if(Á.isFirefox||Á.isIE)newW-=2;if(Á.isSafari)newW-=6;Á.content_highlight.Ç.width=Á.selection_field_text.Ç.width=Á.selection_field.Ç.width=Á.test_font_size.Ç.width=newW+\"px\";}if(Á.isOpera||Á.isFirefox||Á.isSafari){area_height=Á.getLinePosTop(Á.É[\"nb_line\"]+1);}\nelse{area_height=Á.Â.scrollHeight;}if(Á.Â.previous_scrollHeight!=area_height){Á.container.Ç.height=(area_height+2)+\"px\";Á.Â.Ç.height=area_height+\"px\";Á.content_highlight.Ç.height=area_height+\"px\";Á.Â.previous_scrollHeight=area_height;resized=Ë;}if(Á.É[\"nb_line\"] >=Á.line_number){var newLines='',destDiv=_$(\"line_number\"),start=Á.line_number,end=Á.É[\"nb_line\"]+100;for(i=start+1;i < end;i++){newLines+='<div id=\"line_'+i+'\">'+i+\"</div>\";Á.line_number++;}destDiv.innerHTML=destDiv.innerHTML+newLines;if(Á.Å['word_wrap']){Á.fixLinesHeight(Á.Â.Ê,start,-1);}}Á.Â.scrollTop=\"0px\";Á.Â.scrollLeft=\"0px\";if(resized==Ë){Á.scroll_to_view();}}if(!onlyOneTime)setTimeout(\"eA.manage_size();\",100);};EA.Ä.execCommand=Ã(cmd,param){for(var i in Á.plugins){if(typeof(Á.plugins[i].execCommand)==\"Ã\"){if(!Á.plugins[i].execCommand(cmd,param))return;}}switch(cmd){case \"save\":if(Á.Å[\"save_callback\"].Æ>0)eval(\"È.\"+Á.Å[\"save_callback\"]+\"('\"+Á.id+\"',eA.Â.Ê);\");break;case \"load\":if(Á.Å[\"load_callback\"].Æ>0)eval(\"È.\"+Á.Å[\"load_callback\"]+\"('\"+Á.id+\"');\");break;case \"onchange\":if(Á.Å[\"change_callback\"].Æ>0)eval(\"È.\"+Á.Å[\"change_callback\"]+\"('\"+Á.id+\"');\");break;case \"EA_load\":if(Á.Å[\"EA_load_callback\"].Æ>0)eval(\"È.\"+Á.Å[\"EA_load_callback\"]+\"('\"+Á.id+\"');\");break;case \"EA_unload\":if(Á.Å[\"EA_unload_callback\"].Æ>0)eval(\"È.\"+Á.Å[\"EA_unload_callback\"]+\"('\"+Á.id+\"');\");break;case \"toggle_on\":if(Á.Å[\"EA_toggle_on_callback\"].Æ>0)eval(\"È.\"+Á.Å[\"EA_toggle_on_callback\"]+\"('\"+Á.id+\"');\");break;case \"toggle_off\":if(Á.Å[\"EA_toggle_off_callback\"].Æ>0)eval(\"È.\"+Á.Å[\"EA_toggle_off_callback\"]+\"('\"+Á.id+\"');\");break;case \"re_sync\":if(!Á.do_highlight)break;case \"file_switch_on\":if(Á.Å[\"EA_file_switch_on_callback\"].Æ>0)eval(\"È.\"+Á.Å[\"EA_file_switch_on_callback\"]+\"(param);\");break;case \"file_switch_off\":if(Á.Å[\"EA_file_switch_off_callback\"].Æ>0)eval(\"È.\"+Á.Å[\"EA_file_switch_off_callback\"]+\"(param);\");break;case \"file_close\":if(Á.Å[\"EA_file_close_callback\"].Æ>0)return eval(\"È.\"+Á.Å[\"EA_file_close_callback\"]+\"(param);\");break;default:if(typeof(eval(\"eA.\"+cmd))==\"Ã\"){if(Á.Å[\"debug\"])eval(\"eA.\"+cmd+\"(param);\");\nelse try{eval(\"eA.\"+cmd+\"(param);\");}catch(e){};}}};EA.Ä.get_translation=Ã(word,mode){if(mode==\"template\")return È.eAL.translate(word,Á.Å[\"language\"],mode);\nelse return È.eAL.get_word_translation(word,Á.Å[\"language\"]);};EA.Ä.add_plugin=Ã(plug_name,plug_obj){for(var i=0;i<Á.Å[\"plugins\"].Æ;i++){if(Á.Å[\"plugins\"][i]==plug_name){Á.plugins[plug_name]=plug_obj;plug_obj.baseURL=È.eAL.baseURL+\"plugins/\"+plug_name+\"/\";if(typeof(plug_obj.init)==\"Ã\")plug_obj.init();}}};EA.Ä.load_css=Ã(url){try{link=document.createElement(\"link\");link.type=\"text/css\";link.rel=\"Çsheet\";link.media=\"all\";link.href=url;head=document.getElementsByTagName(\"head\");head[0].appendChild(link);}catch(e){document.write(\"<link href='\"+url+\"' rel='Çsheet' type='text/css' />\");}};EA.Ä.load_script=Ã(url){try{script=document.createElement(\"script\");script.type=\"text/javascript\";script.src=url;script.charset=\"UTF-8\";head=document.getElementsByTagName(\"head\");head[0].appendChild(script);}catch(e){document.write(\"<script type='text/javascript' src='\"+url+\"' charset=\\\"UTF-8\\\"><\"+\"/script>\");}};EA.Ä.add_lang=Ã(language,Ês){if(!È.eAL.lang[language])È.eAL.lang[language]={};for(var i in Ês)È.eAL.lang[language][i]=Ês[i];};Ã _$(id){return document.getElementById(id);};var eA=new EA();È.eAL.add_event(window,\"load\",init);Ã init(){setTimeout(\"eA.init();\",10);}; EA.Ä.focus=Ã(){Á.Â.focus();Á.ÂFocused=Ë;};EA.Ä.check_line_selection=Ã(timer_checkup){var changes,infos,new_top,new_width,i;var t1=t2=t2_1=t3=tLines=tend=new Date().getTime();if(!eAs[Á.id])return Ì;if(!Á.smooth_selection&&!Á.do_highlight){}\nelse if(Á.ÂFocused&&eAs[Á.id][\"displayed\"]==Ë&&Á.isResizing==Ì){infos=Á.get_selection_infos();changes=Á.checkTextEvolution(typeof(Á.É['full_text'])=='undefined' ? '':Á.É['full_text'],infos['full_text']);t2=new Date().getTime();if(Á.É[\"line_start\"] !=infos[\"line_start\"]||Á.É[\"line_nb\"] !=infos[\"line_nb\"]||infos[\"full_text\"] !=Á.É[\"full_text\"]||Á.reload_highlight||Á.É[\"selectionStart\"] !=infos[\"selectionStart\"]||Á.É[\"selectionEnd\"] !=infos[\"selectionEnd\"]||!timer_checkup){new_top=Á.getLinePosTop(infos[\"line_start\"]);new_width=Math.max(Á.Â.scrollWidth,Á.container.clientWidth-50);Á.selection_field.Ç.top=Á.selection_field_text.Ç.top=new_top+\"px\";if(!Á.Å['word_wrap']){Á.selection_field.Ç.width=Á.selection_field_text.Ç.width=Á.test_font_size.Ç.width=new_width+\"px\";}if(Á.do_highlight==Ë){var curr_text=infos[\"full_text\"].split(\"\\n\");var content=\"\";var start=Math.max(0,infos[\"line_start\"]-1);var end=Math.min(curr_text.Æ,infos[\"line_start\"]+infos[\"line_nb\"]-1);for(i=start;i< end;i++){content+=curr_text[i]+\"\\n\";}selLength=infos['selectionEnd']-infos['selectionStart'];content=content.substr(0,infos[\"curr_pos\"]-1)+\"\\r\\r\"+content.substr(infos[\"curr_pos\"]-1,selLength)+\"\\r\\r\"+content.substr(infos[\"curr_pos\"]-1+selLength);content='<span>'+content.replace(/&/g,\"&amp;\").replace(/</g,\"&lt;\").replace(/>/g,\"&gt;\").replace(\"\\r\\r\",'</span><strong>').replace(\"\\r\\r\",'</strong><span>')+'</span>';if(Á.isIE||(Á.isOpera&&Á.isOpera < 9.6)){Á.selection_field.innerHTML=\"<pre>\"+content.replace(/^\\r?\\n/,\"<br>\")+\"</pre>\";}\nelse{Á.selection_field.innerHTML=content;}Á.selection_field_text.innerHTML=Á.selection_field.innerHTML;t2_1=new Date().getTime();if(Á.reload_highlight||(infos[\"full_text\"] !=Á.last_text_to_highlight&&(Á.É[\"line_start\"]!=infos[\"line_start\"]||Á.show_line_colors||Á.Å['word_wrap']||Á.É[\"line_nb\"]!=infos[\"line_nb\"]||Á.É[\"nb_line\"]!=infos[\"nb_line\"]))){Á.maj_highlight(infos);}}}t3=new Date().getTime();if(Á.Å['word_wrap']&&infos[\"full_text\"] !=Á.É[\"full_text\"]){if(changes.newText.split(\"\\n\").Æ==1&&Á.É['nb_line']&&infos['nb_line']==Á.É['nb_line']){Á.fixLinesHeight(infos['full_text'],changes.lineStart,changes.lineStart);}\nelse{Á.fixLinesHeight(infos['full_text'],changes.lineStart,-1);}}tLines=new Date().getTime();if(infos[\"line_start\"] !=Á.É[\"line_start\"]||infos[\"curr_pos\"] !=Á.É[\"curr_pos\"]||infos[\"full_text\"].Æ!=Á.É[\"full_text\"].Æ||Á.reload_highlight||!timer_checkup){var selec_char=infos[\"curr_line\"].charAt(infos[\"curr_pos\"]-1);var no_real_move=Ë;if(infos[\"line_nb\"]==1&&(Á.assocBracket[selec_char]||Á.revertAssocBracket[selec_char])){no_real_move=Ì;if(Á.findEndBracket(infos,selec_char)===Ë){_$(\"end_bracket\").Ç.visibility=\"visible\";_$(\"cursor_pos\").Ç.visibility=\"visible\";_$(\"cursor_pos\").innerHTML=selec_char;_$(\"end_bracket\").innerHTML=(Á.assocBracket[selec_char]||Á.revertAssocBracket[selec_char]);}\nelse{_$(\"end_bracket\").Ç.visibility=\"hidden\";_$(\"cursor_pos\").Ç.visibility=\"hidden\";}}\nelse{_$(\"cursor_pos\").Ç.visibility=\"hidden\";_$(\"end_bracket\").Ç.visibility=\"hidden\";}Á.displayToCursorPosition(\"cursor_pos\",infos[\"line_start\"],infos[\"curr_pos\"]-1,infos[\"curr_line\"],no_real_move);if(infos[\"line_nb\"]==1&&infos[\"line_start\"]!=Á.É[\"line_start\"])Á.scroll_to_view();}Á.É=infos;}tend=new Date().getTime();if(timer_checkup){setTimeout(\"eA.check_line_selection(Ë)\",Á.check_line_selection_timer);}};EA.Ä.get_selection_infos=Ã(){var sel={},start,end,len,str;Á.getIESelection();start=Á.Â.selectionStart;end=Á.Â.selectionEnd;if(Á.É[\"selectionStart\"]==start&&Á.É[\"selectionEnd\"]==end&&Á.É[\"full_text\"]==Á.Â.Ê){return Á.É;}if(Á.tabulation!=\"\t\"&&Á.Â.Ê.indexOf(\"\t\")!=-1){len=Á.Â.Ê.Æ;Á.Â.Ê=Á.replace_tab(Á.Â.Ê);start=end=start+(Á.Â.Ê.Æ-len);Á.area_select(start,0);}sel[\"selectionStart\"]=start;sel[\"selectionEnd\"]=end;sel[\"full_text\"]=Á.Â.Ê;sel[\"line_start\"]=1;sel[\"line_nb\"]=1;sel[\"curr_pos\"]=0;sel[\"curr_line\"]=\"\";sel[\"indexOfCursor\"]=0;sel[\"selec_direction\"]=Á.É[\"selec_direction\"];var splitTab=sel[\"full_text\"].split(\"\\n\");var nbLine=Math.max(0,splitTab.Æ);var nbChar=Math.max(0,sel[\"full_text\"].Æ-(nbLine-1));if(sel[\"full_text\"].indexOf(\"\\r\")!=-1)nbChar=nbChar-(nbLine-1);sel[\"nb_line\"]=nbLine;sel[\"nb_char\"]=nbChar;if(start>0){str=sel[\"full_text\"].substr(0,start);sel[\"curr_pos\"]=start-str.lastIndexOf(\"\\n\");sel[\"line_start\"]=Math.max(1,str.split(\"\\n\").Æ);}\nelse{sel[\"curr_pos\"]=1;}if(end>start){sel[\"line_nb\"]=sel[\"full_text\"].substring(start,end).split(\"\\n\").Æ;}sel[\"indexOfCursor\"]=start;sel[\"curr_line\"]=splitTab[Math.max(0,sel[\"line_start\"]-1)];if(sel[\"selectionStart\"]==Á.É[\"selectionStart\"]){if(sel[\"selectionEnd\"]>Á.É[\"selectionEnd\"])sel[\"selec_direction\"]=\"down\";\nelse if(sel[\"selectionEnd\"]==Á.É[\"selectionStart\"])sel[\"selec_direction\"]=Á.É[\"selec_direction\"];}\nelse if(sel[\"selectionStart\"]==Á.É[\"selectionEnd\"]&&sel[\"selectionEnd\"]>Á.É[\"selectionEnd\"]){sel[\"selec_direction\"]=\"down\";}\nelse{sel[\"selec_direction\"]=\"up\";}_$(\"nbLine\").innerHTML=nbLine;_$(\"nbChar\").innerHTML=nbChar;_$(\"linePos\").innerHTML=sel[\"line_start\"];_$(\"currPos\").innerHTML=sel[\"curr_pos\"];return sel;};EA.Ä.getIESelection=Ã(){var selectionStart,selectionEnd,range,stored_range;if(!Á.isIE)return Ì;if(Á.Å['word_wrap'])Á.Â.wrap='off';try{range=document.selection.createRange();stored_range=range.duplicate();stored_range.moveToElementText(Á.Â);stored_range.setEndPoint('EndToEnd',range);if(stored_range.ÈElement()!=Á.Â)throw \"invalid focus\";var scrollTop=Á.result.scrollTop+document.body.scrollTop;var relative_top=range.offsetTop-È.calculeOffsetTop(Á.Â)+scrollTop;var line_start=Math.round((relative_top / Á.lineHeight)+1);var line_nb=Math.round(range.boundingHeight / Á.lineHeight);selectionStart=stored_range.text.Æ-range.text.Æ;selectionStart+=(line_start-Á.Â.Ê.substr(0,selectionStart).split(\"\\n\").Æ)*2;selectionStart-=(line_start-Á.Â.Ê.substr(0,selectionStart).split(\"\\n\").Æ)* 2;selectionEnd=selectionStart+range.text.Æ;selectionEnd+=(line_start+line_nb-1-Á.Â.Ê.substr(0,selectionEnd).split(\"\\n\").Æ)*2;Á.Â.selectionStart=selectionStart;Á.Â.selectionEnd=selectionEnd;}catch(e){}if(Á.Å['word_wrap'])Á.Â.wrap='soft';};EA.Ä.setIESelection=Ã(){var a=Á.Â,nbLineStart,nbLineEnd,range;if(!Á.isIE)return Ì;nbLineStart=a.Ê.substr(0,a.selectionStart).split(\"\\n\").Æ-1;nbLineEnd=a.Ê.substr(0,a.selectionEnd).split(\"\\n\").Æ-1;range=document.selection.createRange();range.moveToElementText(a);range.setEndPoint('EndToStart',range);range.moveStart('character',a.selectionStart-nbLineStart);range.moveEnd('character',a.selectionEnd-nbLineEnd-(a.selectionStart-nbLineStart));range.select();};EA.Ä.checkTextEvolution=Ã(lastText,newText){var ch={},baseStep=200,cpt=0,end,step,tStart=new Date().getTime();end=Math.min(newText.Æ,lastText.Æ);step=baseStep;while(cpt<end&&step>=1){if(lastText.substr(cpt,step)==newText.substr(cpt,step)){cpt+=step;}\nelse{step=Math.floor(step/2);}}ch.posStart=cpt;ch.lineStart=newText.substr(0,ch.posStart).split(\"\\n\").Æ-1;cpt_last=lastText.Æ;cpt=newText.Æ;step=baseStep;while(cpt>=0&&cpt_last>=0&&step>=1){if(lastText.substr(cpt_last-step,step)==newText.substr(cpt-step,step)){cpt-=step;cpt_last-=step;}\nelse{step=Math.floor(step/2);}}ch.posNewEnd=cpt;ch.posLastEnd=cpt_last;if(ch.posNewEnd<=ch.posStart){if(lastText.Æ < newText.Æ){ch.posNewEnd=ch.posStart+newText.Æ-lastText.Æ;ch.posLastEnd=ch.posStart;}\nelse{ch.posLastEnd=ch.posStart+lastText.Æ-newText.Æ;ch.posNewEnd=ch.posStart;}}ch.newText=newText.substring(ch.posStart,ch.posNewEnd);ch.lastText=lastText.substring(ch.posStart,ch.posLastEnd);ch.lineNewEnd=newText.substr(0,ch.posNewEnd).split(\"\\n\").Æ-1;ch.lineLastEnd=lastText.substr(0,ch.posLastEnd).split(\"\\n\").Æ-1;ch.newTextLine=newText.split(\"\\n\").slice(ch.lineStart,ch.lineNewEnd+1).join(\"\\n\");ch.lastTextLine=lastText.split(\"\\n\").slice(ch.lineStart,ch.lineLastEnd+1).join(\"\\n\");return ch;};EA.Ä.tab_selection=Ã(){if(Á.is_tabbing)return;Á.is_tabbing=Ë;Á.getIESelection();var start=Á.Â.selectionStart;var end=Á.Â.selectionEnd;var insText=Á.Â.Ê.substring(start,end);var pos_start=start;var pos_end=end;if(insText.Æ==0){Á.Â.Ê=Á.Â.Ê.substr(0,start)+Á.tabulation+Á.Â.Ê.substr(end);pos_start=start+Á.tabulation.Æ;pos_end=pos_start;}\nelse{start=Math.max(0,Á.Â.Ê.substr(0,start).lastIndexOf(\"\\n\")+1);endText=Á.Â.Ê.substr(end);startText=Á.Â.Ê.substr(0,start);tmp=Á.Â.Ê.substring(start,end).split(\"\\n\");insText=Á.tabulation+tmp.join(\"\\n\"+Á.tabulation);Á.Â.Ê=startText+insText+endText;pos_start=start;pos_end=Á.Â.Ê.indexOf(\"\\n\",startText.Æ+insText.Æ);if(pos_end==-1)pos_end=Á.Â.Ê.Æ;}Á.Â.selectionStart=pos_start;Á.Â.selectionEnd=pos_end;if(Á.isIE){Á.setIESelection();setTimeout(\"eA.is_tabbing=Ì;\",100);}\nelse{Á.is_tabbing=Ì;}};EA.Ä.invert_tab_selection=Ã(){var t=Á,a=Á.Â;if(t.is_tabbing)return;t.is_tabbing=Ë;t.getIESelection();var start=a.selectionStart;var end=a.selectionEnd;var insText=a.Ê.substring(start,end);var pos_start=start;var pos_end=end;if(insText.Æ==0){if(a.Ê.substring(start-t.tabulation.Æ,start)==t.tabulation){a.Ê=a.Ê.substr(0,start-t.tabulation.Æ)+a.Ê.substr(end);pos_start=Math.max(0,start-t.tabulation.Æ);pos_end=pos_start;}}\nelse{start=a.Ê.substr(0,start).lastIndexOf(\"\\n\")+1;endText=a.Ê.substr(end);startText=a.Ê.substr(0,start);tmp=a.Ê.substring(start,end).split(\"\\n\");insText=\"\";for(i=0;i<tmp.Æ;i++){for(j=0;j<t.tab_nb_char;j++){if(tmp[i].charAt(0)==\"\t\"){tmp[i]=tmp[i].substr(1);j=t.tab_nb_char;}\nelse if(tmp[i].charAt(0)==\" \")tmp[i]=tmp[i].substr(1);}insText+=tmp[i];if(i<tmp.Æ-1)insText+=\"\\n\";}a.Ê=startText+insText+endText;pos_start=start;pos_end=a.Ê.indexOf(\"\\n\",startText.Æ+insText.Æ);if(pos_end==-1)pos_end=a.Ê.Æ;}a.selectionStart=pos_start;a.selectionEnd=pos_end;if(t.isIE){t.setIESelection();setTimeout(\"eA.is_tabbing=Ì;\",100);}\nelse t.is_tabbing=Ì;};EA.Ä.press_enter=Ã(){if(!Á.smooth_selection)return Ì;Á.getIESelection();var scrollTop=Á.result.scrollTop;var scrollLeft=Á.result.scrollLeft;var start=Á.Â.selectionStart;var end=Á.Â.selectionEnd;var start_last_line=Math.max(0,Á.Â.Ê.substring(0,start).lastIndexOf(\"\\n\")+1);var begin_line=Á.Â.Ê.substring(start_last_line,start).replace(/^([ \t]*).*/gm,\"$1\");var lineStart=Á.Â.Ê.substring(0,start).split(\"\\n\").Æ;if(begin_line==\"\\n\"||begin_line==\"\\r\"||begin_line.Æ==0){return Ì;}if(Á.isIE||(Á.isOpera&&Á.isOpera < 9.6)){begin_line=\"\\r\\n\"+begin_line;}\nelse{begin_line=\"\\n\"+begin_line;}Á.Â.Ê=Á.Â.Ê.substring(0,start)+begin_line+Á.Â.Ê.substring(end);Á.area_select(start+begin_line.Æ,0);if(Á.isIE){Á.result.scrollTop=scrollTop;Á.result.scrollLeft=scrollLeft;}return Ë;};EA.Ä.findEndBracket=Ã(infos,bracket){var start=infos[\"indexOfCursor\"];var normal_order=Ë;if(Á.assocBracket[bracket])endBracket=Á.assocBracket[bracket];\nelse if(Á.revertAssocBracket[bracket]){endBracket=Á.revertAssocBracket[bracket];normal_order=Ì;}var end=-1;var nbBracketOpen=0;for(var i=start;i<infos[\"full_text\"].Æ&&i>=0;){if(infos[\"full_text\"].charAt(i)==endBracket){nbBracketOpen--;if(nbBracketOpen<=0){end=i;break;}}\nelse if(infos[\"full_text\"].charAt(i)==bracket)nbBracketOpen++;if(normal_order)i++;\nelse i--;}if(end==-1)return Ì;var endLastLine=infos[\"full_text\"].substr(0,end).lastIndexOf(\"\\n\");if(endLastLine==-1)line=1;\nelse line=infos[\"full_text\"].substr(0,endLastLine).split(\"\\n\").Æ+1;var curPos=end-endLastLine-1;var endLineLength=infos[\"full_text\"].substring(end).split(\"\\n\")[0].Æ;Á.displayToCursorPosition(\"end_bracket\",line,curPos,infos[\"full_text\"].substring(endLastLine+1,end+endLineLength));return Ë;};EA.Ä.displayToCursorPosition=Ã(id,start_line,cur_pos,lineContent,no_real_move){var elem,dest,content,posLeft=0,posTop,fixPadding,topOffset,endElem;elem=Á.test_font_size;dest=_$(id);content=\"<span id='test_font_size_inner'>\"+lineContent.substr(0,cur_pos).replace(/&/g,\"&amp;\").replace(/</g,\"&lt;\")+\"</span><span id='endTestFont'>\"+lineContent.substr(cur_pos).replace(/&/g,\"&amp;\").replace(/</g,\"&lt;\")+\"</span>\";if(Á.isIE||(Á.isOpera&&Á.isOpera < 9.6)){elem.innerHTML=\"<pre>\"+content.replace(/^\\r?\\n/,\"<br>\")+\"</pre>\";}\nelse{elem.innerHTML=content;}endElem=_$('endTestFont');topOffset=endElem.offsetTop;fixPadding=parseInt(Á.content_highlight.Ç.paddingLeft.replace(\"px\",\"\"));posLeft=45+endElem.offsetLeft+(!isNaN(fixPadding)&&topOffset > 0 ? fixPadding:0);posTop=Á.getLinePosTop(start_line)+topOffset;if(Á.isIE&&cur_pos > 0&&endElem.offsetLeft==0){posTop+=Á.lineHeight;}if(no_real_move!=Ë){dest.Ç.top=posTop+\"px\";dest.Ç.left=posLeft+\"px\";}dest.cursor_top=posTop;dest.cursor_left=posLeft;};EA.Ä.getLinePosTop=Ã(start_line){var elem=_$('line_'+start_line),posTop=0;if(elem)posTop=elem.offsetTop;\nelse posTop=Á.lineHeight *(start_line-1);return posTop;};EA.Ä.getTextHeight=Ã(text){var t=Á,elem,height;elem=t.test_font_size;content=text.replace(/&/g,\"&amp;\").replace(/</g,\"&lt;\");if(t.isIE||(Á.isOpera&&Á.isOpera < 9.6)){elem.innerHTML=\"<pre>\"+content.replace(/^\\r?\\n/,\"<br>\")+\"</pre>\";}\nelse{elem.innerHTML=content;}height=elem.offsetHeight;height=Math.max(1,Math.floor(elem.offsetHeight / Á.lineHeight))* Á.lineHeight;return height;};EA.Ä.fixLinesHeight=Ã(textValue,lineStart,lineEnd){var aText=textValue.split(\"\\n\");if(lineEnd==-1)lineEnd=aText.Æ-1;for(var i=Math.max(0,lineStart);i <=lineEnd;i++){if(elem=_$('line_'+(i+1))){elem.Ç.height=typeof(aText[i])!=\"undefined\" ? Á.getTextHeight(aText[i])+\"px\":Á.lineHeight;}}};EA.Ä.area_select=Ã(start,Æ){Á.Â.focus();start=Math.max(0,Math.min(Á.Â.Ê.Æ,start));end=Math.max(start,Math.min(Á.Â.Ê.Æ,start+Æ));if(Á.isIE){Á.Â.selectionStart=start;Á.Â.selectionEnd=end;Á.setIESelection();}\nelse{if(Á.isOpera&&Á.isOpera < 9.6){Á.Â.setSelectionRange(0,0);}Á.Â.setSelectionRange(start,end);}Á.check_line_selection();};EA.Ä.area_get_selection=Ã(){var text=\"\";if(document.selection){var range=document.selection.createRange();text=range.text;}\nelse{text=Á.Â.Ê.substring(Á.Â.selectionStart,Á.Â.selectionEnd);}return text;}; EA.Ä.replace_tab=Ã(text){return text.replace(/((\\n?)([^\t\\n]*)\t)/gi,eA.smartTab);};EA.Ä.smartTab=Ã(){val=\" \";return EA.Ä.smartTab.arguments[2]+EA.Ä.smartTab.arguments[3]+val.substr(0,eA.tab_nb_char-(EA.Ä.smartTab.arguments[3].Æ)%eA.tab_nb_char);};EA.Ä.show_waiting_screen=Ã(){width=Á.editor_area.offsetWidth;height=Á.editor_area.offsetHeight;if(!(Á.isIE&&Á.isIE<6)){width-=2;height-=2;}Á.processing_screen.Ç.display=\"block\";Á.processing_screen.Ç.width=width+\"px\";Á.processing_screen.Ç.height=height+\"px\";Á.waiting_screen_displayed=Ë;};EA.Ä.hide_waiting_screen=Ã(){Á.processing_screen.Ç.display=\"none\";Á.waiting_screen_displayed=Ì;};EA.Ä.add_Ç=Ã(Çs){if(Çs.Æ>0){newcss=document.createElement(\"Ç\");newcss.type=\"text/css\";newcss.media=\"all\";if(newcss.ÇSheet){newcss.ÇSheet.cssText=Çs;}\nelse{newcss.appendChild(document.createTextNode(Çs));}document.getElementsByTagName(\"head\")[0].appendChild(newcss);}};EA.Ä.set_font=Ã(family,size){var t=Á,a=Á.Â,s=Á.Å,elem_font,i,elem;var elems=[\"Â\",\"content_highlight\",\"cursor_pos\",\"end_bracket\",\"selection_field\",\"selection_field_text\",\"line_number\"];if(family&&family!=\"\")s[\"font_family\"]=family;if(size&&size>0)s[\"font_size\"]=size;if(t.isOpera&&t.isOpera < 9.6)s['font_family']=\"monospace\";if(elem_font=_$(\"area_font_size\")){for(i=0;i < elem_font.Æ;i++){if(elem_font.options[i].Ê&&elem_font.options[i].Ê==s[\"font_size\"])elem_font.options[i].selected=Ë;}}if(t.isFirefox){var nbTry=3;do{var div1=document.createElement('div'),text1=document.createElement('Â');var Çs={width:'40px',overflow:'scroll',zIndex:50,visibility:'hidden',fontFamily:s[\"font_family\"],fontSize:s[\"font_size\"]+\"pt\",lineHeight:t.lineHeight+\"px\",padding:'0',margin:'0',border:'none',whiteSpace:'nowrap'};var diff,changed=Ì;for(i in Çs){div1.Ç[ i ]=Çs[i];text1.Ç[ i ]=Çs[i];}text1.wrap='off';text1.setAttribute('wrap','off');t.container.appendChild(div1);t.container.appendChild(text1);div1.innerHTML=text1.Ê='azertyuiopqsdfghjklm';div1.innerHTML=text1.Ê=text1.Ê+'wxcvbn^p*ù$!:;,,';diff=text1.scrollWidth-div1.scrollWidth;if(Math.abs(diff)>=2){s[\"font_size\"]++;changed=Ë;}t.container.removeChild(div1);t.container.removeChild(text1);nbTry--;}while(changed&&nbTry > 0);}elem=t.test_font_size;elem.Ç.fontFamily=\"\"+s[\"font_family\"];elem.Ç.fontSize=s[\"font_size\"]+\"pt\";elem.innerHTML=\"0\";t.lineHeight=elem.offsetHeight;for(i=0;i<elems.Æ;i++){elem=_$(elems[i]);elem.Ç.fontFamily=s[\"font_family\"];elem.Ç.fontSize=s[\"font_size\"]+\"pt\";elem.Ç.lineHeight=t.lineHeight+\"px\";}t.add_Ç(\"pre{font-family:\"+s[\"font_family\"]+\"}\");if((t.isOpera&&t.isOpera < 9.6)||t.isIE >=8){var parNod=a.ÈNode,nxtSib=a.nextSibling,start=a.selectionStart,end=a.selectionEnd;parNod.removeChild(a);parNod.insertBefore(a,nxtSib);t.area_select(start,end-start);}Á.focus();Á.update_size();Á.check_line_selection();};EA.Ä.change_font_size=Ã(){var size=_$(\"area_font_size\").Ê;if(size>0)Á.set_font(\"\",size);};EA.Ä.open_inline_popup=Ã(popup_id){Á.close_all_inline_popup();var popup=_$(popup_id);var editor=_$(\"editor\");for(var i=0;i<Á.inlinePopup.Æ;i++){if(Á.inlinePopup[i][\"popup_id\"]==popup_id){var icon=_$(Á.inlinePopup[i][\"icon_id\"]);if(icon){Á.switchClassSticky(icon,'editAreaButtonSelected',Ë);break;}}}popup.Ç.height=\"auto\";popup.Ç.overflow=\"visible\";if(document.body.offsetHeight< popup.offsetHeight){popup.Ç.height=(document.body.offsetHeight-10)+\"px\";popup.Ç.overflow=\"auto\";}if(!popup.positionned){var new_left=editor.offsetWidth /2-popup.offsetWidth /2;var new_top=editor.offsetHeight /2-popup.offsetHeight /2;popup.Ç.left=new_left+\"px\";popup.Ç.top=new_top+\"px\";popup.positionned=Ë;}popup.Ç.visibility=\"visible\";};EA.Ä.close_inline_popup=Ã(popup_id){var popup=_$(popup_id);for(var i=0;i<Á.inlinePopup.Æ;i++){if(Á.inlinePopup[i][\"popup_id\"]==popup_id){var icon=_$(Á.inlinePopup[i][\"icon_id\"]);if(icon){Á.switchClassSticky(icon,'editAreaButtonNormal',Ì);break;}}}popup.Ç.visibility=\"hidden\";};EA.Ä.close_all_inline_popup=Ã(e){for(var i=0;i<Á.inlinePopup.Æ;i++){Á.close_inline_popup(Á.inlinePopup[i][\"popup_id\"]);}Á.Â.focus();};EA.Ä.show_help=Ã(){Á.open_inline_popup(\"edit_area_help\");};EA.Ä.new_document=Ã(){Á.Â.Ê=\"\";Á.area_select(0,0);};EA.Ä.get_all_toolbar_height=Ã(){var area=_$(\"editor\");var results=È.getChildren(area,\"div\",\"class\",\"area_toolbar\",\"all\",\"0\");var height=0;for(var i=0;i<results.Æ;i++){height+=results[i].offsetHeight;}return height;};EA.Ä.go_to_line=Ã(line){if(!line){var icon=_$(\"go_to_line\");if(icon !=null){Á.restoreClass(icon);Á.switchClassSticky(icon,'editAreaButtonSelected',Ë);}line=prompt(Á.get_translation(\"go_to_line_prompt\"));if(icon !=null)Á.switchClassSticky(icon,'editAreaButtonNormal',Ì);}if(line&&line!=null&&line.search(/^[0-9]+$/)!=-1){var start=0;var lines=Á.Â.Ê.split(\"\\n\");if(line > lines.Æ)start=Á.Â.Ê.Æ;\nelse{for(var i=0;i<Math.min(line-1,lines.Æ);i++)start+=lines[i].Æ+1;}Á.area_select(start,0);}};EA.Ä.change_smooth_selection_mode=Ã(setTo){if(Á.do_highlight)return;if(setTo !=null){if(setTo===Ì)Á.smooth_selection=Ë;\nelse Á.smooth_selection=Ì;}var icon=_$(\"change_smooth_selection\");Á.Â.focus();if(Á.smooth_selection===Ë){Á.switchClassSticky(icon,'editAreaButtonNormal',Ì);Á.smooth_selection=Ì;Á.selection_field.Ç.display=\"none\";_$(\"cursor_pos\").Ç.display=\"none\";_$(\"end_bracket\").Ç.display=\"none\";}\nelse{Á.switchClassSticky(icon,'editAreaButtonSelected',Ì);Á.smooth_selection=Ë;Á.selection_field.Ç.display=\"block\";_$(\"cursor_pos\").Ç.display=\"block\";_$(\"end_bracket\").Ç.display=\"block\";}};EA.Ä.scroll_to_view=Ã(show){var zone,lineElem;if(!Á.smooth_selection)return;zone=_$(\"result\");var cursor_pos_top=_$(\"cursor_pos\").cursor_top;if(show==\"bottom\"){cursor_pos_top+=Á.getLinePosTop(Á.É['line_start']+Á.É['line_nb']-1);}var max_height_visible=zone.clientHeight+zone.scrollTop;var miss_top=cursor_pos_top+Á.lineHeight-max_height_visible;if(miss_top>0){zone.scrollTop=zone.scrollTop+miss_top;}\nelse if(zone.scrollTop > cursor_pos_top){zone.scrollTop=cursor_pos_top;}var cursor_pos_left=_$(\"cursor_pos\").cursor_left;var max_width_visible=zone.clientWidth+zone.scrollLeft;var miss_left=cursor_pos_left+10-max_width_visible;if(miss_left>0){zone.scrollLeft=zone.scrollLeft+miss_left+50;}\nelse if(zone.scrollLeft > cursor_pos_left){zone.scrollLeft=cursor_pos_left;}\nelse if(zone.scrollLeft==45){zone.scrollLeft=0;}};EA.Ä.check_undo=Ã(only_once){if(!eAs[Á.id])return Ì;if(Á.ÂFocused&&eAs[Á.id][\"displayed\"]==Ë){var text=Á.Â.Ê;if(Á.previous.Æ<=1)Á.switchClassSticky(_$(\"undo\"),'editAreaButtonDisabled',Ë);if(!Á.previous[Á.previous.Æ-1]||Á.previous[Á.previous.Æ-1][\"text\"] !=text){Á.previous.push({\"text\":text,\"selStart\":Á.Â.selectionStart,\"selEnd\":Á.Â.selectionEnd});if(Á.previous.Æ > Á.Å[\"max_undo\"]+1)Á.previous.shift();}if(Á.previous.Æ >=2)Á.switchClassSticky(_$(\"undo\"),'editAreaButtonNormal',Ì);}if(!only_once)setTimeout(\"eA.check_undo()\",3000);};EA.Ä.undo=Ã(){if(Á.previous.Æ > 0){Á.getIESelection();Á.next.push({\"text\":Á.Â.Ê,\"selStart\":Á.Â.selectionStart,\"selEnd\":Á.Â.selectionEnd});var prev=Á.previous.pop();if(prev[\"text\"]==Á.Â.Ê&&Á.previous.Æ > 0)prev=Á.previous.pop();Á.Â.Ê=prev[\"text\"];Á.last_undo=prev[\"text\"];Á.area_select(prev[\"selStart\"],prev[\"selEnd\"]-prev[\"selStart\"]);Á.switchClassSticky(_$(\"redo\"),'editAreaButtonNormal',Ì);Á.resync_highlight(Ë);Á.check_file_changes();}};EA.Ä.redo=Ã(){if(Á.next.Æ > 0){var next=Á.next.pop();Á.previous.push(next);Á.Â.Ê=next[\"text\"];Á.last_undo=next[\"text\"];Á.area_select(next[\"selStart\"],next[\"selEnd\"]-next[\"selStart\"]);Á.switchClassSticky(_$(\"undo\"),'editAreaButtonNormal',Ì);Á.resync_highlight(Ë);Á.check_file_changes();}if(Á.next.Æ==0)Á.switchClassSticky(_$(\"redo\"),'editAreaButtonDisabled',Ë);};EA.Ä.check_redo=Ã(){if(eA.next.Æ==0||eA.Â.Ê!=eA.last_undo){eA.next=[];eA.switchClassSticky(_$(\"redo\"),'editAreaButtonDisabled',Ë);}\nelse{Á.switchClassSticky(_$(\"redo\"),'editAreaButtonNormal',Ì);}};EA.Ä.switchClass=Ã(element,class_name,lock_state){var lockChanged=Ì;if(typeof(lock_state)!=\"undefined\"&&element !=null){element.classLock=lock_state;lockChanged=Ë;}if(element !=null&&(lockChanged||!element.classLock)){element.oldClassName=element.className;element.className=class_name;}};EA.Ä.restoreAndSwitchClass=Ã(element,class_name){if(element !=null&&!element.classLock){Á.restoreClass(element);Á.switchClass(element,class_name);}};EA.Ä.restoreClass=Ã(element){if(element !=null&&element.oldClassName&&!element.classLock){element.className=element.oldClassName;element.oldClassName=null;}};EA.Ä.setClassLock=Ã(element,lock_state){if(element !=null)element.classLock=lock_state;};EA.Ä.switchClassSticky=Ã(element,class_name,lock_state){var lockChanged=Ì;if(typeof(lock_state)!=\"undefined\"&&element !=null){element.classLock=lock_state;lockChanged=Ë;}if(element !=null&&(lockChanged||!element.classLock)){element.className=class_name;element.oldClassName=class_name;}};EA.Ä.scroll_page=Ã(params){var dir=params[\"dir\"],shift_pressed=params[\"shift\"];var lines=Á.Â.Ê.split(\"\\n\");var new_pos=0,Æ=0,char_left=0,line_nb=0,curLine=0;var toScrollAmount=_$(\"result\").clientHeight-30;var nbLineToScroll=0,diff=0;if(dir==\"up\"){nbLineToScroll=Math.ceil(toScrollAmount / Á.lineHeight);for(i=Á.É[\"line_start\"];i-diff > Á.É[\"line_start\"]-nbLineToScroll;i--){if(elem=_$('line_'+i)){diff+=Math.floor((elem.offsetHeight-1)/ Á.lineHeight);}}nbLineToScroll-=diff;if(Á.É[\"selec_direction\"]==\"up\"){for(line_nb=0;line_nb< Math.min(Á.É[\"line_start\"]-nbLineToScroll,lines.Æ);line_nb++){new_pos+=lines[line_nb].Æ+1;}char_left=Math.min(lines[Math.min(lines.Æ-1,line_nb)].Æ,Á.É[\"curr_pos\"]-1);if(shift_pressed)Æ=Á.É[\"selectionEnd\"]-new_pos-char_left;Á.area_select(new_pos+char_left,Æ);view=\"top\";}\nelse{view=\"bottom\";for(line_nb=0;line_nb< Math.min(Á.É[\"line_start\"]+Á.É[\"line_nb\"]-1-nbLineToScroll,lines.Æ);line_nb++){new_pos+=lines[line_nb].Æ+1;}char_left=Math.min(lines[Math.min(lines.Æ-1,line_nb)].Æ,Á.É[\"curr_pos\"]-1);if(shift_pressed){start=Math.min(Á.É[\"selectionStart\"],new_pos+char_left);Æ=Math.max(new_pos+char_left,Á.É[\"selectionStart\"])-start;if(new_pos+char_left < Á.É[\"selectionStart\"])view=\"top\";}\nelse start=new_pos+char_left;Á.area_select(start,Æ);}}\nelse{var nbLineToScroll=Math.floor(toScrollAmount / Á.lineHeight);for(i=Á.É[\"line_start\"];i+diff < Á.É[\"line_start\"]+nbLineToScroll;i++){if(elem=_$('line_'+i)){diff+=Math.floor((elem.offsetHeight-1)/ Á.lineHeight);}}nbLineToScroll-=diff;if(Á.É[\"selec_direction\"]==\"down\"){view=\"bottom\";for(line_nb=0;line_nb< Math.min(Á.É[\"line_start\"]+Á.É[\"line_nb\"]-2+nbLineToScroll,lines.Æ);line_nb++){if(line_nb==Á.É[\"line_start\"]-1)char_left=Á.É[\"selectionStart\"]-new_pos;new_pos+=lines[line_nb].Æ+1;}if(shift_pressed){Æ=Math.abs(Á.É[\"selectionStart\"]-new_pos);Æ+=Math.min(lines[Math.min(lines.Æ-1,line_nb)].Æ,Á.É[\"curr_pos\"]);Á.area_select(Math.min(Á.É[\"selectionStart\"],new_pos),Æ);}\nelse{Á.area_select(new_pos+char_left,0);}}\nelse{view=\"top\";for(line_nb=0;line_nb< Math.min(Á.É[\"line_start\"]+nbLineToScroll-1,lines.Æ,lines.Æ);line_nb++){if(line_nb==Á.É[\"line_start\"]-1)char_left=Á.É[\"selectionStart\"]-new_pos;new_pos+=lines[line_nb].Æ+1;}if(shift_pressed){Æ=Math.abs(Á.É[\"selectionEnd\"]-new_pos-char_left);Æ+=Math.min(lines[Math.min(lines.Æ-1,line_nb)].Æ,Á.É[\"curr_pos\"])-char_left-1;Á.area_select(Math.min(Á.É[\"selectionEnd\"],new_pos+char_left),Æ);if(new_pos+char_left > Á.É[\"selectionEnd\"])view=\"bottom\";}\nelse{Á.area_select(new_pos+char_left,0);}}}Á.check_line_selection();Á.scroll_to_view(view);};EA.Ä.start_resize=Ã(e){È.eAL.resize[\"id\"]=eA.id;È.eAL.resize[\"start_x\"]=(e)? e.pageX:event.x+document.body.scrollLeft;È.eAL.resize[\"start_y\"]=(e)? e.pageY:event.y+document.body.scrollTop;if(eA.isIE){eA.Â.focus();eA.getIESelection();}È.eAL.resize[\"selectionStart\"]=eA.Â.selectionStart;È.eAL.resize[\"selectionEnd\"]=eA.Â.selectionEnd;È.eAL.start_resize_area();};EA.Ä.toggle_full_screen=Ã(to){var t=Á,p=È,a=t.Â,html,frame,selStart,selEnd,old,icon;if(typeof(to)==\"undefined\")to=!t.fullscreen['isFull'];old=t.fullscreen['isFull'];t.fullscreen['isFull']=to;icon=_$(\"fullscreen\");selStart=t.Â.selectionStart;selEnd=t.Â.selectionEnd;html=p.document.getElementsByTagName(\"html\")[0];frame=p.document.getElementById(\"frame_\"+t.id);if(to&&to!=old){t.fullscreen['old_overflow']=p.get_css_property(html,\"overflow\");t.fullscreen['old_height']=p.get_css_property(html,\"height\");t.fullscreen['old_width']=p.get_css_property(html,\"width\");t.fullscreen['old_scrollTop']=html.scrollTop;t.fullscreen['old_scrollLeft']=html.scrollLeft;t.fullscreen['old_zIndex']=p.get_css_property(frame,\"z-index\");if(t.isOpera){html.Ç.height=\"100%\";html.Ç.width=\"100%\";}html.Ç.overflow=\"hidden\";html.scrollTop=0;html.scrollLeft=0;frame.Ç.position=\"absolute\";frame.Ç.width=html.clientWidth+\"px\";frame.Ç.height=html.clientHeight+\"px\";frame.Ç.display=\"block\";frame.Ç.zIndex=\"999999\";frame.Ç.top=\"0px\";frame.Ç.left=\"0px\";frame.Ç.top=\"-\"+p.calculeOffsetTop(frame)+\"px\";frame.Ç.left=\"-\"+p.calculeOffsetLeft(frame)+\"px\";t.switchClassSticky(icon,'editAreaButtonSelected',Ì);t.fullscreen['allow_resize']=t.resize_allowed;t.allow_resize(Ì);if(t.isFirefox){p.eAL.execCommand(t.id,\"update_size();\");t.area_select(selStart,selEnd-selStart);t.scroll_to_view();t.focus();}\nelse{setTimeout(\"È.eAL.execCommand('\"+t.id+\"','update_size();');eA.focus();\",10);}}\nelse if(to!=old){frame.Ç.position=\"static\";frame.Ç.zIndex=t.fullscreen['old_zIndex'];if(t.isOpera){html.Ç.height=\"auto\";html.Ç.width=\"auto\";html.Ç.overflow=\"auto\";}\nelse if(t.isIE&&p!=top){html.Ç.overflow=\"auto\";}\nelse{html.Ç.overflow=t.fullscreen['old_overflow'];}html.scrollTop=t.fullscreen['old_scrollTop'];html.scrollLeft=t.fullscreen['old_scrollLeft'];p.eAL.hide(t.id);p.eAL.show(t.id);t.switchClassSticky(icon,'editAreaButtonNormal',Ì);if(t.fullscreen['allow_resize'])t.allow_resize(t.fullscreen['allow_resize']);if(t.isFirefox){t.area_select(selStart,selEnd-selStart);setTimeout(\"eA.scroll_to_view();\",10);}}};EA.Ä.allow_resize=Ã(allow){var resize=_$(\"resize_area\");if(allow){resize.Ç.visibility=\"visible\";È.eAL.add_event(resize,\"mouseup\",eA.start_resize);}\nelse{resize.Ç.visibility=\"hidden\";È.eAL.remove_event(resize,\"mouseup\",eA.start_resize);}Á.resize_allowed=allow;};EA.Ä.change_syntax=Ã(new_syntax,is_waiting){if(new_syntax==Á.Å['syntax'])return Ë;var founded=Ì;for(var i=0;i<Á.syntax_list.Æ;i++){if(Á.syntax_list[i]==new_syntax)founded=Ë;}if(founded==Ë){if(!È.eAL.load_syntax[new_syntax]){if(!is_waiting)È.eAL.load_script(È.eAL.baseURL+\"reg_syntax/\"+new_syntax+\".js\");setTimeout(\"eA.change_syntax('\"+new_syntax+\"',Ë);\",100);Á.show_waiting_screen();}\nelse{if(!Á.allready_used_syntax[new_syntax]){È.eAL.init_syntax_regexp();Á.add_Ç(È.eAL.syntax[new_syntax][\"Çs\"]);Á.allready_used_syntax[new_syntax]=Ë;}var sel=_$(\"syntax_selection\");if(sel&&sel.Ê!=new_syntax){for(var i=0;i<sel.Æ;i++){if(sel.options[i].Ê&&sel.options[i].Ê==new_syntax)sel.options[i].selected=Ë;}}Á.Å['syntax']=new_syntax;Á.resync_highlight(Ë);Á.hide_waiting_screen();return Ë;}}return Ì;};EA.Ä.set_editable=Ã(is_editable){if(is_editable){document.body.className=\"\";Á.Â.readOnly=Ì;Á.is_editable=Ë;}\nelse{document.body.className=\"non_editable\";Á.Â.readOnly=Ë;Á.is_editable=Ì;}if(eAs[Á.id][\"displayed\"]==Ë)Á.update_size();};EA.Ä.toggle_word_wrap=Ã(){Á.set_word_wrap(!Á.Å['word_wrap']);};EA.Ä.set_word_wrap=Ã(to){var t=Á,a=t.Â;if(t.isOpera&&t.isOpera < 9.8){Á.Å['word_wrap']=Ì;t.switchClassSticky(_$(\"word_wrap\"),'editAreaButtonDisabled',Ë);return Ì;}if(to){wrap_mode='soft';Á.container.className+=' word_wrap';Á.container.Ç.width=\"\";Á.content_highlight.Ç.width=\"\";a.Ç.width=\"100%\";if(t.isIE&&t.isIE < 7){a.Ç.width=(a.offsetWidth-5)+\"px\";}t.switchClassSticky(_$(\"word_wrap\"),'editAreaButtonSelected',Ì);}\nelse{wrap_mode='off';Á.container.className=Á.container.className.replace(/word_wrap/g,'');t.switchClassSticky(_$(\"word_wrap\"),'editAreaButtonNormal',Ë);}Á.Â.previous_scrollWidth='';Á.Â.previous_scrollHeight='';a.wrap=wrap_mode;a.setAttribute('wrap',wrap_mode);if(!Á.isIE){var start=a.selectionStart,end=a.selectionEnd;var parNod=a.ÈNode,nxtSib=a.nextSibling;parNod.removeChild(a);parNod.insertBefore(a,nxtSib);Á.area_select(start,end-start);}Á.Å['word_wrap']=to;Á.focus();Á.update_size();Á.check_line_selection();};EA.Ä.open_file=Ã(Å){if(Å['id']!=\"undefined\"){var id=Å['id'];var new_file={};new_file['id']=id;new_file['title']=id;new_file['text']=\"\";new_file['É']=\"\";new_file['last_text_to_highlight']=\"\";new_file['last_hightlighted_text']=\"\";new_file['previous']=[];new_file['next']=[];new_file['last_undo']=\"\";new_file['smooth_selection']=Á.Å['smooth_selection'];new_file['do_highlight']=Á.Å['start_highlight'];new_file['syntax']=Á.Å['syntax'];new_file['scroll_top']=0;new_file['scroll_left']=0;new_file['selection_start']=0;new_file['selection_end']=0;new_file['edited']=Ì;new_file['font_size']=Á.Å[\"font_size\"];new_file['font_family']=Á.Å[\"font_family\"];new_file['word_wrap']=Á.Å[\"word_wrap\"];new_file['toolbar']={'links':{},'selects':{}};new_file['compare_edited_text']=new_file['text'];Á.files[id]=new_file;Á.update_file(id,Å);Á.files[id]['compare_edited_text']=Á.files[id]['text'];var html_id='tab_file_'+encodeURIComponent(id);Á.filesIdAssoc[html_id]=id;Á.files[id]['html_id']=html_id;if(!_$(Á.files[id]['html_id'])&&id!=\"\"){Á.tab_browsing_area.Ç.display=\"block\";var elem=document.createElement('li');elem.id=Á.files[id]['html_id'];var close=\"<b><span><strong class=\\\"edited\\\">*</strong>\"+Á.files[id]['title']+close+\"</span></b></a>\";_$('tab_browsing_list').appendChild(elem);var elem=document.createElement('text');Á.update_size();}if(id!=\"\")Á.execCommand('file_open',Á.files[id]);Á.switch_to_file(id,Ë);return Ë;}\nelse return Ì;};EA.Ä.close_file=Ã(id){if(Á.files[id]){Á.save_file(id);if(Á.execCommand('file_close',Á.files[id])!==Ì){var li=_$(Á.files[id]['html_id']);li.ÈNode.removeChild(li);if(id==Á.curr_file){var next_file=\"\";var is_next=Ì;for(var i in Á.files){if(is_next){next_file=i;break;}\nelse if(i==id)is_next=Ë;\nelse next_file=i;}Á.switch_to_file(next_file);}delete(Á.files[id]);Á.update_size();}}};EA.Ä.save_file=Ã(id){var t=Á,save,a_links,a_selects,save_butt,img,i;if(t.files[id]){var save=t.files[id];save['É']=t.É;save['last_text_to_highlight']=t.last_text_to_highlight;save['last_hightlighted_text']=t.last_hightlighted_text;save['previous']=t.previous;save['next']=t.next;save['last_undo']=t.last_undo;save['smooth_selection']=t.smooth_selection;save['do_highlight']=t.do_highlight;save['syntax']=t.Å['syntax'];save['text']=t.Â.Ê;save['scroll_top']=t.result.scrollTop;save['scroll_left']=t.result.scrollLeft;save['selection_start']=t.É[\"selectionStart\"];save['selection_end']=t.É[\"selectionEnd\"];save['font_size']=t.Å[\"font_size\"];save['font_family']=t.Å[\"font_family\"];save['word_wrap']=t.Å[\"word_wrap\"];save['toolbar']={'links':{},'selects':{}};a_links=_$(\"toolbar_1\").getElementsByTagName(\"a\");for(i=0;i<a_links.Æ;i++){if(a_links[i].getAttribute('fileSpecific')=='yes'){save_butt={};img=a_links[i].getElementsByTagName('img')[0];save_butt['classLock']=img.classLock;save_butt['className']=img.className;save_butt['oldClassName']=img.oldClassName;save['toolbar']['links'][a_links[i].id]=save_butt;}}a_selects=_$(\"toolbar_1\").getElementsByTagName(\"select\");for(i=0;i<a_selects.Æ;i++){if(a_selects[i].getAttribute('fileSpecific')=='yes'){save['toolbar']['selects'][a_selects[i].id]=a_selects[i].Ê;}}t.files[id]=save;return save;}return Ì;};EA.Ä.update_file=Ã(id,new_Ês){for(var i in new_Ês){Á.files[id][i]=new_Ês[i];}};EA.Ä.display_file=Ã(id){var t=Á,a=t.Â,new_file,a_lis,a_selects,a_links,a_options,i,j;if(id==''){a.readOnly=Ë;t.tab_browsing_area.Ç.display=\"none\";_$(\"no_file_selected\").Ç.display=\"block\";t.result.className=\"empty\";if(!t.files['']){t.open_file({id:''});}}\nelse if(typeof(t.files[id])=='undefined'){return Ì;}\nelse{t.result.className=\"\";a.readOnly=!t.is_editable;_$(\"no_file_selected\").Ç.display=\"none\";t.tab_browsing_area.Ç.display=\"block\";}t.check_redo(Ë);t.check_undo(Ë);t.curr_file=id;a_lis=t.tab_browsing_area.getElementsByTagName('li');for(i=0;i<a_lis.Æ;i++){if(a_lis[i].id==t.files[id]['html_id'])a_lis[i].className='selected';\nelse a_lis[i].className='';}new_file=t.files[id];a.Ê=new_file['text'];t.set_font(new_file['font_family'],new_file['font_size']);t.area_select(new_file['selection_start'],new_file['selection_end']-new_file['selection_start']);t.manage_size(Ë);t.result.scrollTop=new_file['scroll_top'];t.result.scrollLeft=new_file['scroll_left'];t.previous=new_file['previous'];t.next=new_file['next'];t.last_undo=new_file['last_undo'];t.check_redo(Ë);t.check_undo(Ë);t.execCommand(\"change_highlight\",new_file['do_highlight']);t.execCommand(\"change_syntax\",new_file['syntax']);t.execCommand(\"change_smooth_selection_mode\",new_file['smooth_selection']);t.execCommand(\"set_word_wrap\",new_file['word_wrap']);a_links=new_file['toolbar']['links'];for(i in a_links){if(img=_$(i).getElementsByTagName('img')[0]){img.classLock=a_links[i]['classLock'];img.className=a_links[i]['className'];img.oldClassName=a_links[i]['oldClassName'];}}a_selects=new_file['toolbar']['selects'];for(i in a_selects){a_options=_$(i).options;for(j=0;j<a_options.Æ;j++){if(a_options[j].Ê==a_selects[i])_$(i).options[j].selected=Ë;}}};EA.Ä.switch_to_file=Ã(file_to_show,force_refresh){if(file_to_show!=Á.curr_file||force_refresh){Á.save_file(Á.curr_file);if(Á.curr_file!='')Á.execCommand('file_switch_off',Á.files[Á.curr_file]);Á.display_file(file_to_show);if(file_to_show!='')Á.execCommand('file_switch_on',Á.files[file_to_show]);}};EA.Ä.get_file=Ã(id){if(id==Á.curr_file)Á.save_file(id);return Á.files[id];};EA.Ä.get_all_files=Ã(){tmp_files=Á.files;Á.save_file(Á.curr_file);if(tmp_files[''])delete(Á.files['']);return tmp_files;};EA.Ä.check_file_changes=Ã(){var id=Á.curr_file;if(Á.files[id]&&Á.files[id]['compare_edited_text']!=undefined){if(Á.files[id]['compare_edited_text'].Æ==Á.Â.Ê.Æ&&Á.files[id]['compare_edited_text']==Á.Â.Ê){if(Á.files[id]['edited']!=Ì)Á.set_file_edited_mode(id,Ì);}\nelse{if(Á.files[id]['edited']!=Ë)Á.set_file_edited_mode(id,Ë);}}};EA.Ä.set_file_edited_mode=Ã(id,to){if(Á.files[id]&&_$(Á.files[id]['html_id'])){var link=_$(Á.files[id]['html_id']).getElementsByTagName('a')[0];if(to==Ë){link.className='edited';}\nelse{link.className='';if(id==Á.curr_file)text=Á.Â.Ê;\nelse text=Á.files[id]['text'];Á.files[id]['compare_edited_text']=text;}Á.files[id]['edited']=to;}};EA.Ä.set_show_line_colors=Ã(new_Ê){Á.show_line_colors=new_Ê;if(new_Ê)Á.selection_field.className+=' show_colors';\nelse Á.selection_field.className=Á.selection_field.className.replace(/ show_colors/g,'');};var EA_keys={8:\"Retour arriere\",9:\"Tabulation\",12:\"Milieu(pave numerique)\",13:\"Entrer\",16:\"Shift\",17:\"Ctrl\",18:\"Alt\",19:\"Pause\",20:\"Verr Maj\",27:\"Esc\",32:\"Space\",33:\"Page up\",34:\"Page down\",35:\"End\",36:\"Begin\",37:\"Left\",38:\"Up\",39:\"Right\",40:\"Down\",44:\"Impr ecran\",45:\"Inser\",46:\"Suppr\",91:\"Menu Demarrer Windows / touche pomme Mac\",92:\"Menu Demarrer Windows\",93:\"Menu contextuel Windows\",112:\"F1\",113:\"F2\",114:\"F3\",115:\"F4\",116:\"F5\",117:\"F6\",118:\"F7\",119:\"F8\",120:\"F9\",121:\"F10\",122:\"F11\",123:\"F12\",144:\"Verr Num\",145:\"Arret defil\"};Ã keyDown(e){if(!e){e=event;}for(var i in eA.plugins){if(typeof(eA.plugins[i].onkeydown)==\"Ã\"){if(eA.plugins[i].onkeydown(e)===Ì){if(eA.isIE)e.keyCode=0;return Ì;}}}var target_id=(e.target||e.srcElement).id;var use=Ì;if(EA_keys[e.keyCode])letter=EA_keys[e.keyCode];\nelse letter=String.fromCharCode(e.keyCode);var low_letter=letter.toLowerCase();if(letter==\"Page up\"&&!AltPressed(e)&&!eA.isOpera){eA.execCommand(\"scroll_page\",{\"dir\":\"up\",\"shift\":ShiftPressed(e)});use=Ë;}\nelse if(letter==\"Page down\"&&!AltPressed(e)&&!eA.isOpera){eA.execCommand(\"scroll_page\",{\"dir\":\"down\",\"shift\":ShiftPressed(e)});use=Ë;}\nelse if(eA.is_editable==Ì){return Ë;}\nelse if(letter==\"Tabulation\"&&target_id==\"Â\"&&!CtrlPressed(e)&&!AltPressed(e)){if(ShiftPressed(e))eA.execCommand(\"invert_tab_selection\");\nelse eA.execCommand(\"tab_selection\");use=Ë;if(eA.isOpera||(eA.isFirefox&&eA.isMac))setTimeout(\"eA.execCommand('focus');\",1);}\nelse if(letter==\"Entrer\"&&target_id==\"Â\"){if(eA.press_enter())use=Ë;}\nelse if(letter==\"Entrer\"&&target_id==\"area_search\"){eA.execCommand(\"area_search\");use=Ë;}\nelse if(letter==\"Esc\"){eA.execCommand(\"close_all_inline_popup\",e);use=Ë;}\nelse if(CtrlPressed(e)&&!AltPressed(e)&&!ShiftPressed(e)){switch(low_letter){case \"f\":eA.execCommand(\"area_search\");use=Ë;break;case \"r\":eA.execCommand(\"area_replace\");use=Ë;break;case \"q\":eA.execCommand(\"close_all_inline_popup\",e);use=Ë;break;case \"h\":eA.execCommand(\"change_highlight\");use=Ë;break;case \"g\":setTimeout(\"eA.execCommand('go_to_line');\",5);use=Ë;break;case \"e\":eA.execCommand(\"show_help\");use=Ë;break;case \"z\":use=Ë;eA.execCommand(\"undo\");break;case \"y\":use=Ë;eA.execCommand(\"redo\");break;default:break;}}if(eA.next.Æ > 0){setTimeout(\"eA.check_redo();\",10);}setTimeout(\"eA.check_file_changes();\",10);if(use){if(eA.isIE)e.keyCode=0;return Ì;}return Ë;};Ã AltPressed(e){if(window.event){return(window.event.altKey);}\nelse{if(e.modifiers)return(e.altKey||(e.modifiers % 2));\nelse return e.altKey;}};Ã CtrlPressed(e){if(window.event){return(window.event.ctrlKey);}\nelse{return(e.ctrlKey||(e.modifiers==2)||(e.modifiers==3)||(e.modifiers>5));}};Ã ShiftPressed(e){if(window.event){return(window.event.shiftKey);}\nelse{return(e.shiftKey||(e.modifiers>3));}}; EA.Ä.show_search=Ã(){if(_$(\"area_search_replace\").Ç.visibility==\"visible\"){Á.hidden_search();}\nelse{Á.open_inline_popup(\"area_search_replace\");var text=Á.area_get_selection();var search=text.split(\"\\n\")[0];_$(\"area_search\").Ê=search;_$(\"area_search\").focus();}};EA.Ä.hidden_search=Ã(){Á.close_inline_popup(\"area_search_replace\");};EA.Ä.area_search=Ã(mode){if(!mode)mode=\"search\";_$(\"area_search_msg\").innerHTML=\"\";var search=_$(\"area_search\").Ê;Á.Â.focus();Á.Â.ÂFocused=Ë;var infos=Á.get_selection_infos();var start=infos[\"selectionStart\"];var pos=-1;var pos_begin=-1;var Æ=search.Æ;if(_$(\"area_search_replace\").Ç.visibility!=\"visible\"){Á.show_search();return;}if(search.Æ==0){_$(\"area_search_msg\").innerHTML=Á.get_translation(\"search_field_empty\");return;}if(mode!=\"replace\"){if(_$(\"area_search_reg_exp\").checked)start++;\nelse start+=search.Æ;}if(_$(\"area_search_reg_exp\").checked){var opt=\"m\";if(!_$(\"area_search_match_case\").checked)opt+=\"i\";var reg=new RegExp(search,opt);pos=infos[\"full_text\"].substr(start).search(reg);pos_begin=infos[\"full_text\"].search(reg);if(pos!=-1){pos+=start;Æ=infos[\"full_text\"].substr(start).match(reg)[0].Æ;}\nelse if(pos_begin!=-1){Æ=infos[\"full_text\"].match(reg)[0].Æ;}}\nelse{if(_$(\"area_search_match_case\").checked){pos=infos[\"full_text\"].indexOf(search,start);pos_begin=infos[\"full_text\"].indexOf(search);}\nelse{pos=infos[\"full_text\"].toLowerCase().indexOf(search.toLowerCase(),start);pos_begin=infos[\"full_text\"].toLowerCase().indexOf(search.toLowerCase());}}if(pos==-1&&pos_begin==-1){_$(\"area_search_msg\").innerHTML=\"<strong>\"+search+\"</strong> \"+Á.get_translation(\"not_found\");return;}\nelse if(pos==-1&&pos_begin !=-1){begin=pos_begin;_$(\"area_search_msg\").innerHTML=Á.get_translation(\"restart_search_at_begin\");}\nelse begin=pos;if(mode==\"replace\"&&pos==infos[\"indexOfCursor\"]){var replace=_$(\"area_replace\").Ê;var new_text=\"\";if(_$(\"area_search_reg_exp\").checked){var opt=\"m\";if(!_$(\"area_search_match_case\").checked)opt+=\"i\";var reg=new RegExp(search,opt);new_text=infos[\"full_text\"].substr(0,begin)+infos[\"full_text\"].substr(start).replace(reg,replace);}\nelse{new_text=infos[\"full_text\"].substr(0,begin)+replace+infos[\"full_text\"].substr(begin+Æ);}Á.Â.Ê=new_text;Á.area_select(begin,Æ);Á.area_search();}\nelse Á.area_select(begin,Æ);};EA.Ä.area_replace=Ã(){Á.area_search(\"replace\");};EA.Ä.area_replace_all=Ã(){var base_text=Á.Â.Ê;var search=_$(\"area_search\").Ê;var replace=_$(\"area_replace\").Ê;if(search.Æ==0){_$(\"area_search_msg\").innerHTML=Á.get_translation(\"search_field_empty\");return;}var new_text=\"\";var nb_change=0;if(_$(\"area_search_reg_exp\").checked){var opt=\"mg\";if(!_$(\"area_search_match_case\").checked)opt+=\"i\";var reg=new RegExp(search,opt);nb_change=infos[\"full_text\"].match(reg).Æ;new_text=infos[\"full_text\"].replace(reg,replace);}\nelse{if(_$(\"area_search_match_case\").checked){var tmp_tab=base_text.split(search);nb_change=tmp_tab.Æ-1;new_text=tmp_tab.join(replace);}\nelse{var lower_Ê=base_text.toLowerCase();var lower_search=search.toLowerCase();var start=0;var pos=lower_Ê.indexOf(lower_search);while(pos!=-1){nb_change++;new_text+=Á.Â.Ê.substring(start,pos)+replace;start=pos+search.Æ;pos=lower_Ê.indexOf(lower_search,pos+1);}new_text+=Á.Â.Ê.substring(start);}}if(new_text==base_text){_$(\"area_search_msg\").innerHTML=\"<strong>\"+search+\"</strong> \"+Á.get_translation(\"not_found\");}\nelse{Á.Â.Ê=new_text;_$(\"area_search_msg\").innerHTML=\"<strong>\"+nb_change+\"</strong> \"+Á.get_translation(\"occurrence_replaced\");setTimeout(\"eA.Â.focus();eA.Â.ÂFocused=Ë;\",100);}}; EA.Ä.change_highlight=Ã(change_to){if(Á.Å[\"syntax\"].Æ==0&&change_to==Ì){Á.switchClassSticky(_$(\"highlight\"),'editAreaButtonDisabled',Ë);Á.switchClassSticky(_$(\"reset_highlight\"),'editAreaButtonDisabled',Ë);return Ì;}if(Á.do_highlight==change_to)return Ì;Á.getIESelection();var pos_start=Á.Â.selectionStart;var pos_end=Á.Â.selectionEnd;if(Á.do_highlight===Ë||change_to==Ì)Á.disable_highlight();\nelse Á.enable_highlight();Á.Â.focus();Á.Â.selectionStart=pos_start;Á.Â.selectionEnd=pos_end;Á.setIESelection();};EA.Ä.disable_highlight=Ã(displayOnly){var t=Á,a=t.Â,new_Obj,old_class,new_class;t.selection_field.innerHTML=\"\";t.selection_field_text.innerHTML=\"\";t.content_highlight.Ç.visibility=\"hidden\";new_Obj=t.content_highlight.cloneNode(Ì);new_Obj.innerHTML=\"\";t.content_highlight.ÈNode.insertBefore(new_Obj,t.content_highlight);t.content_highlight.ÈNode.removeChild(t.content_highlight);t.content_highlight=new_Obj;old_class=È.getAttribute(a,\"class\");if(old_class){new_class=old_class.replace(\"hidden\",\"\");È.setAttribute(a,\"class\",new_class);}a.Ç.backgroundColor=\"transÈ\";t.switchClassSticky(_$(\"highlight\"),'editAreaButtonNormal',Ë);t.switchClassSticky(_$(\"reset_highlight\"),'editAreaButtonDisabled',Ë);t.do_highlight=Ì;t.switchClassSticky(_$(\"change_smooth_selection\"),'editAreaButtonSelected',Ë);if(typeof(t.smooth_selection_before_highlight)!=\"undefined\"&&t.smooth_selection_before_highlight===Ì){t.change_smooth_selection_mode(Ì);}};EA.Ä.enable_highlight=Ã(){var t=Á,a=t.Â,new_class;t.show_waiting_screen();t.content_highlight.Ç.visibility=\"visible\";new_class=È.getAttribute(a,\"class\")+\" hidden\";È.setAttribute(a,\"class\",new_class);if(t.isIE)a.Ç.backgroundColor=\"#FFFFFF\";t.switchClassSticky(_$(\"highlight\"),'editAreaButtonSelected',Ì);t.switchClassSticky(_$(\"reset_highlight\"),'editAreaButtonNormal',Ì);t.smooth_selection_before_highlight=t.smooth_selection;if(!t.smooth_selection)t.change_smooth_selection_mode(Ë);t.switchClassSticky(_$(\"change_smooth_selection\"),'editAreaButtonDisabled',Ë);t.do_highlight=Ë;t.resync_highlight();t.hide_waiting_screen();};EA.Ä.maj_highlight=Ã(infos){var debug_opti=\"\",tps_start=new Date().getTime(),tps_middle_opti=new Date().getTime();var t=Á,hightlighted_text,updated_highlight;var textToHighlight=infos[\"full_text\"],doSyntaxOpti=Ì,doHtmlOpti=Ì,stay_begin=\"\",stay_end=\"\",trace_new,trace_last;if(t.last_text_to_highlight==infos[\"full_text\"]&&t.resync_highlight!==Ë)return;if(t.reload_highlight===Ë){t.reload_highlight=Ì;}\nelse if(textToHighlight.Æ==0){textToHighlight=\"\\n \";}\nelse{changes=t.checkTextEvolution(t.last_text_to_highlight,textToHighlight);trace_new=t.get_syntax_trace(changes.newTextLine).replace(/\\r/g,'');trace_last=t.get_syntax_trace(changes.lastTextLine).replace(/\\r/g,'');doSyntaxOpti=(trace_new==trace_last);if(!doSyntaxOpti&&trace_new==\"\\n\"+trace_last&&/^[ \t\s]*\\n[ \t\s]*$/.test(changes.newText.replace(/\\r/g,''))&&changes.lastText==\"\"){doSyntaxOpti=Ë;}if(doSyntaxOpti){tps_middle_opti=new Date().getTime();stay_begin=t.last_hightlighted_text.split(\"\\n\").slice(0,changes.lineStart).join(\"\\n\");if(changes.lineStart>0)stay_begin+=\"\\n\";stay_end=t.last_hightlighted_text.split(\"\\n\").slice(changes.lineLastEnd+1).join(\"\\n\");if(stay_end.Æ>0)stay_end=\"\\n\"+stay_end;if(stay_begin.split('<span').Æ !=stay_begin.split('</span').Æ||stay_end.split('<span').Æ !=stay_end.split('</span').Æ){doSyntaxOpti=Ì;stay_end='';stay_begin='';}\nelse{if(stay_begin.Æ==0&&changes.posLastEnd==-1)changes.newTextLine+=\"\\n\";textToHighlight=changes.newTextLine;}}if(t.Å[\"debug\"]){var ch=changes;debug_opti=(doSyntaxOpti?\"Optimisation\":\"No optimisation\")+\" start:\"+ch.posStart+\"(\"+ch.lineStart+\")\"+\" end_new:\"+ch.posNewEnd+\"(\"+ch.lineNewEnd+\")\"+\" end_last:\"+ch.posLastEnd+\"(\"+ch.lineLastEnd+\")\"+\"\\nchanged_text:\"+ch.newText+\"=> trace:\"+trace_new+\"\\nchanged_last_text:\"+ch.lastText+\"=> trace:\"+trace_last+\"\\nchanged_line:\"+ch.newTextLine+\"\\nlast_changed_line:\"+ch.lastTextLine+\"\\nstay_begin:\"+stay_begin.slice(-100)+\"\\nstay_end:\"+stay_end.substr(0,100);+\"\\n\";}}tps_end_opti=new Date().getTime();updated_highlight=t.colorize_text(textToHighlight);tpsAfterReg=new Date().getTime();doSyntaxOpti=doHtmlOpti=Ì;if(doSyntaxOpti){try{var replacedBloc,i,nbStart='',nbEnd='',newHtml,ÆOld,ÆNew;replacedBloc=t.last_hightlighted_text.substring(stay_begin.Æ,t.last_hightlighted_text.Æ-stay_end.Æ);ÆOld=replacedBloc.Æ;ÆNew=updated_highlight.Æ;for(i=0;i < ÆOld&&i < ÆNew&&replacedBloc.charAt(i)==updated_highlight.charAt(i);i++){}nbStart=i;for(i=0;i+nbStart < ÆOld&&i+nbStart < ÆNew&&replacedBloc.charAt(ÆOld-i-1)==updated_highlight.charAt(ÆNew-i-1);i++){}nbEnd=i;lastHtml=replacedBloc.substring(nbStart,ÆOld-nbEnd);newHtml=updated_highlight.substring(nbStart,ÆNew-nbEnd);if(newHtml.indexOf('<span')==-1&&newHtml.indexOf('</span')==-1&&lastHtml.indexOf('<span')==-1&&lastHtml.indexOf('</span')==-1){var beginStr,nbOpendedSpan,nbClosedSpan,nbUnchangedChars,span,textNode;doHtmlOpti=Ë;beginStr=t.last_hightlighted_text.substr(0,stay_begin.Æ+nbStart);newHtml=newHtml.replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');nbOpendedSpan=beginStr.split('<span').Æ-1;nbClosedSpan=beginStr.split('</span').Æ-1;span=t.content_highlight.getElementsByTagName('span')[ nbOpendedSpan ];ÈSpan=span;maxStartOffset=maxEndOffset=0;if(nbOpendedSpan==nbClosedSpan){while(ÈSpan.ÈNode !=t.content_highlight&&ÈSpan.ÈNode.tagName !='PRE'){ÈSpan=ÈSpan.ÈNode;}}\nelse{maxStartOffset=maxEndOffset=beginStr.Æ+1;nbClosed=beginStr.substr(Math.max(0,beginStr.lastIndexOf('<span',maxStartOffset-1))).split('</span').Æ-1;while(nbClosed > 0){nbClosed--;ÈSpan=ÈSpan.ÈNode;}while(ÈSpan.ÈNode !=t.content_highlight&&ÈSpan.ÈNode.tagName !='PRE'&&(tmpMaxStartOffset=Math.max(0,beginStr.lastIndexOf('<span',maxStartOffset-1)))<(tmpMaxEndOffset=Math.max(0,beginStr.lastIndexOf('</span',maxEndOffset-1)))){maxStartOffset=tmpMaxStartOffset;maxEndOffset=tmpMaxEndOffset;}}if(ÈSpan.ÈNode==t.content_highlight||ÈSpan.ÈNode.tagName=='PRE'){maxStartOffset=Math.max(0,beginStr.indexOf('<span'));}if(maxStartOffset==beginStr.Æ){nbSubSpanBefore=0;}\nelse{lastEndPos=Math.max(0,beginStr.lastIndexOf('>',maxStartOffset));nbSubSpanBefore=beginStr.substr(lastEndPos).split('<span').Æ-1;}if(nbSubSpanBefore==0){textNode=ÈSpan.firstChild;}\nelse{lastSubSpan=ÈSpan.getElementsByTagName('span')[ nbSubSpanBefore-1 ];while(lastSubSpan.ÈNode !=ÈSpan){lastSubSpan=lastSubSpan.ÈNode;}if(lastSubSpan.nextSibling==null||lastSubSpan.nextSibling.nodeType !=3){textNode=document.createTextNode('');lastSubSpan.ÈNode.insertBefore(textNode,lastSubSpan.nextSibling);}\nelse{textNode=lastSubSpan.nextSibling;}}if((lastIndex=beginStr.lastIndexOf('>'))==-1){nbUnchangedChars=beginStr.Æ;}\nelse{nbUnchangedChars=beginStr.substr(lastIndex+1).replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&').Æ;}if(t.isIE){nbUnchangedChars-=(beginStr.substr(beginStr.Æ-nbUnchangedChars).split(\"\\n\").Æ-1);textNode.replaceData(nbUnchangedChars,lastHtml.replace(/\\n/g,'').Æ,newHtml.replace(/\\n/g,''));}\nelse{textNode.replaceData(nbUnchangedChars,lastHtml.Æ,newHtml);}}}catch(e){doHtmlOpti=Ì;}}tpsAfterOpti2=new Date().getTime();hightlighted_text=stay_begin+updated_highlight+stay_end;if(!doHtmlOpti){var new_Obj=t.content_highlight.cloneNode(Ì);if((t.isIE&&t.isIE < 8)||(t.isOpera&&t.isOpera < 9.6))new_Obj.innerHTML=\"<pre><span class='\"+t.Å[\"syntax\"]+\"'>\"+hightlighted_text+\"</span></pre>\";\nelse new_Obj.innerHTML=\"<span class='\"+t.Å[\"syntax\"]+\"'>\"+hightlighted_text+\"</span>\";t.content_highlight.ÈNode.replaceChild(new_Obj,t.content_highlight);t.content_highlight=new_Obj;}t.last_text_to_highlight=infos[\"full_text\"];t.last_hightlighted_text=hightlighted_text;tps3=new Date().getTime();if(t.Å[\"debug\"]){t.debug.Ê=\"Tps optimisation \"+(tps_end_opti-tps_start)+\" | tps reg exp:\"+(tpsAfterReg-tps_end_opti)+\" | tps opti HTML:\"+(tpsAfterOpti2-tpsAfterReg)+' '+(doHtmlOpti ? 'yes':'no')+\" | tps update highlight content:\"+(tps3-tpsAfterOpti2)+\" | tpsTotal:\"+(tps3-tps_start)+\"(\"+tps3+\")\\n\"+debug_opti;}};EA.Ä.resync_highlight=Ã(reload_now){Á.reload_highlight=Ë;Á.last_text_to_highlight=\"\";Á.focus();if(reload_now)Á.check_line_selection(Ì);}; EA.Ä.comment_or_quote=Ã(){var new_class=\"\",close_tag=\"\",sy,arg,i;sy=È.eAL.syntax[eA.current_code_lang];arg=EA.Ä.comment_or_quote.arguments[0];for(i in sy[\"quotes\"]){if(arg.indexOf(i)==0){new_class=\"quotesmarks\";close_tag=sy[\"quotes\"][i];}}if(new_class.Æ==0){for(var i in sy[\"comments\"]){if(arg.indexOf(i)==0){new_class=\"comments\";close_tag=sy[\"comments\"][i];}}}if(close_tag==\"\\n\"){return \"µ__\"+new_class+\"__µ\"+arg.replace(/(\\r?\\n)?$/m,\"µ_END_µ$1\");}\nelse{reg=new RegExp(È.eAL.get_escaped_regexp(close_tag)+\"$\",\"m\");if(arg.search(reg)!=-1)return \"µ__\"+new_class+\"__µ\"+arg+\"µ_END_µ\";\nelse return \"µ__\"+new_class+\"__µ\"+arg;}};EA.Ä.get_syntax_trace=Ã(text){if(Á.Å[\"syntax\"].Æ>0&&È.eAL.syntax[Á.Å[\"syntax\"]][\"syntax_trace_regexp\"])return text.replace(È.eAL.syntax[Á.Å[\"syntax\"]][\"syntax_trace_regexp\"],\"$3\");};EA.Ä.colorize_text=Ã(text){text=\" \"+text;if(Á.Å[\"syntax\"].Æ>0)text=Á.apply_syntax(text,Á.Å[\"syntax\"]);return text.substr(1).replace(/&/g,\"&amp;\").replace(/</g,\"&lt;\").replace(/>/g,\"&gt;\").replace(/µ_END_µ/g,\"</span>\").replace(/µ__([a-zA-Z0-9]+)__µ/g,\"<span class='$1'>\");};EA.Ä.apply_syntax=Ã(text,lang){var sy;Á.current_code_lang=lang;if(!È.eAL.syntax[lang])return text;sy=È.eAL.syntax[lang];if(sy[\"custom_regexp\"]['before']){for(var i in sy[\"custom_regexp\"]['before']){var convert=\"$1µ__\"+sy[\"custom_regexp\"]['before'][i]['class']+\"__µ$2µ_END_µ$3\";text=text.replace(sy[\"custom_regexp\"]['before'][i]['regexp'],convert);}}if(sy[\"comment_or_quote_reg_exp\"]){text=text.replace(sy[\"comment_or_quote_reg_exp\"],Á.comment_or_quote);}if(sy[\"keywords_reg_exp\"]){for(var i in sy[\"keywords_reg_exp\"]){text=text.replace(sy[\"keywords_reg_exp\"][i],'µ__'+i+'__µ$2µ_END_µ');}}if(sy[\"delimiters_reg_exp\"]){text=text.replace(sy[\"delimiters_reg_exp\"],'µ__delimiters__µ$1µ_END_µ');}if(sy[\"operators_reg_exp\"]){text=text.replace(sy[\"operators_reg_exp\"],'µ__operators__µ$1µ_END_µ');}if(sy[\"custom_regexp\"]['after']){for(var i in sy[\"custom_regexp\"]['after']){var convert=\"$1µ__\"+sy[\"custom_regexp\"]['after'][i]['class']+\"__µ$2µ_END_µ$3\";text=text.replace(sy[\"custom_regexp\"]['after'][i]['regexp'],convert);}}return text;};var editArea= eA;EditArea=EA;</script>".replace(/Á/g,'this').replace(/Â/g,'textarea').replace(/Ã/g,'function').replace(/Ä/g,'prototype').replace(/Å/g,'settings').replace(/Æ/g,'length').replace(/Ç/g,'style').replace(/È/g,'parent').replace(/É/g,'last_selection').replace(/Ê/g,'value').replace(/Ë/g,'true').replace(/Ì/g,'false');
+editAreaLoader.template= "<?xml version=\"1.0\" encoding=\"UTF-8\"?> <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\"> <html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" > <head> <title>EditArea</title> <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /> <meta http-equiv=\"X-UA-Compatible\" content=\"IE=EmulateIE7\"/> [__CSSRULES__] [__JSCODE__] </head> <body> <div id='editor'> <div class='area_toolbar' id='toolbar_1'>[__TOOLBAR__]</div> <div class='area_toolbar' id='tab_browsing_area'><ul id='tab_browsing_list' class='menu'> <li> </li> </ul></div> <div id='result'> <div id='no_file_selected'></div> <div id='container'> <div id='cursor_pos' class='edit_area_cursor'>&nbsp;</div> <div id='end_bracket' class='edit_area_cursor'>&nbsp;</div> <div id='selection_field'></div> <div id='line_number' selec='none'></div> <div id='content_highlight'></div> <div id='test_font_size'></div> <div id='selection_field_text'></div> <textarea id='textarea' wrap='off' onchange='editArea.execCommand(\"onchange\");' onfocus='javascript:editArea.textareaFocused=true;' onblur='javascript:editArea.textareaFocused=false;'> </textarea> </div> </div> <div class='area_toolbar' id='toolbar_2'> <table class='statusbar' cellspacing='0' cellpadding='0'> <tr> <td class='total' selec='none'>{$position}:</td> <td class='infos' selec='none'> {$line_abbr} <span id='linePos'>0</span>, {$char_abbr} <span id='currPos'>0</span> </td> <td class='total' selec='none'>{$total}:</td> <td class='infos' selec='none'> {$line_abbr} <span id='nbLine'>0</span>, {$char_abbr} <span id='nbChar'>0</span> </td> <td class='resize'> <span id='resize_area'></span> </td> </tr> </table> </div> </div> <div id='processing'> <div id='processing_text'> {$processing} </div> </div> <div id='area_search_replace' class='editarea_popup'> <table cellspacing='2' cellpadding='0' style='width: 100%'> <tr> <td selec='none'>{$search}</td> <td><input type='text' id='area_search' /></td> <td id='close_area_search_replace'> <a onclick='Javascript:editArea.execCommand(\"hidden_search\")'></td> </tr> </table> <div class='button'> <input type='checkbox' id='area_search_match_case' /><label for='area_search_match_case' selec='none'>{$match_case}</label> <input type='checkbox' id='area_search_reg_exp' /><label for='area_search_reg_exp' selec='none'>{$reg_exp}</label> <br /> <a onclick='Javascript:editArea.execCommand(\"area_search\")' selec='none'>{$find_next}</a> <a onclick='Javascript:editArea.execCommand(\"area_replace\")' selec='none'>{$replace}</a> <a onclick='Javascript:editArea.execCommand(\"area_replace_all\")' selec='none'>{$replace_all}</a><br /> </div> <div id='area_search_msg' selec='none'></div> </div> <div id='edit_area_help' class='editarea_popup'> <div class='close_popup'> <a onclick='Javascript:editArea.execCommand(\"close_all_inline_popup\")'></a> </div> <div><h2>Editarea [__EA_VERSION__]</h2><br /> <h3>{$shortcuts}:</h3> {$tab}: {$add_tab}<br /> {$shift}+{$tab}: {$remove_tab}<br /> {$ctrl}+f: {$search_command}<br /> {$ctrl}+r: {$replace_command}<br /> {$ctrl}+h: {$highlight}<br /> {$ctrl}+g: {$go_to_line}<br /> {$ctrl}+z: {$undo}<br /> {$ctrl}+y: {$redo}<br /> {$ctrl}+e: {$help}<br /> {$ctrl}+q, {$esc}: {$close_popup}<br /> {$accesskey} E: {$toggle}<br /> <br /> <em>{$about_notice}</em> <br /><div class=' '>&copy; 2007-2010</div> </div> </div> </body> </html> ";
+editAreaLoader.iframe_css= "<style>body,html{margin:0;padding:0;height:100%;border:none;overflow:hidden;background-color:#FFF;}body,html,table,form,textarea{font:12px monospace,sans-serif;}#editor{border:solid #888 1px;overflow:hidden;}#result{z-index:4;overflow-x:auto;overflow-y:scroll;border-top:solid #888 1px;border-bottom:solid #888 1px;position:relative;clear:both;}#result.empty{overflow:hidden;}#container{overflow:hidden;border:solid blue 0;position:relative;z-index:10;padding:0 5px 0 45px;}#textarea{position:relative;top:0;left:0;margin:0;padding:0;width:100%;height:100%;overflow:hidden;z-index:7;border-width:0;background-color:transparent;resize:none;}#textarea,#textarea:hover{outline:none;}#content_highlight{white-space:pre;margin:0;padding:0;position:absolute;z-index:4;overflow:visible;}#selection_field,#selection_field_text{margin:0;background-color:#E1F2F9;position:absolute;z-index:5;top:-100px;padding:0;white-space:pre;overflow:hidden;}#selection_field.show_colors {z-index:3;background-color:#EDF9FC;}#selection_field strong{font-weight:normal;}#selection_field.show_colors *,#selection_field_text * {visibility:hidden;}#selection_field_text{background-color:transparent;}#selection_field_text strong{font-weight:normal;background-color:#3399FE;color:#FFF;visibility:visible;}#container.word_wrap #content_highlight,#container.word_wrap #selection_field,#container.word_wrap #selection_field_text,#container.word_wrap #test_font_size{white-space:pre-wrap;white-space:-moz-pre-wrap !important;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;width:99%;}#line_number{position:absolute;overflow:hidden;border-right:solid black 1px;z-index:8;width:38px;padding:0 5px 0 0;margin:0 0 0 -45px;text-align:right;color:#AAAAAA;}#test_font_size{padding:0;margin:0;visibility:hidden;position:absolute;white-space:pre;}pre{margin:0;padding:0;}.hidden{opacity:0.2;filter:alpha(opacity=20);}#result .edit_area_cursor{position:absolute;z-index:6;background-color:#FF6633;top:-100px;margin:0;}#result .edit_area_selection_field .overline{background-color:#996600;}.editarea_popup{border:solid 1px #888888;background-color:#ECE9D8;width:250px;padding:4px;position:absolute;visibility:hidden;z-index:15;top:-500px;}.editarea_popup,.editarea_popup table{font-family:sans-serif;font-size:10pt;}.editarea_popup img{border:0;}.editarea_popup .close_popup{float:right;line-height:16px;border:0;padding:0;}.editarea_popup h1,.editarea_popup h2,.editarea_popup h3,.editarea_popup h4,.editarea_popup h5,.editarea_popup h6{margin:0;padding:0;}.editarea_popup . {text-align:right;}div#area_search_replace{}div#area_search_replace img{border:0;}div#area_search_replace div.button{text-align:center;line-height:1.7em;}div#area_search_replace .button a{cursor:pointer;border:solid 1px #888888;background-color:#DEDEDE;text-decoration:none;padding:0 2px;color:#000000;white-space:nowrap;}div#area_search_replace a:hover{background-color:#EDEDED;}div#area_search_replace #move_area_search_replace{cursor:move;border:solid 1px #888;}div#area_search_replace #close_area_search_replace{text-align:right;vertical-align:top;white-space:nowrap;}div#area_search_replace #area_search_msg{height:18px;overflow:hidden;border-top:solid 1px #888;margin-top:3px;}#edit_area_help{width:350px;}#edit_area_help div.close_popup{float:right;}.area_toolbar{width:100%;margin:0;padding:0;background-color:#ECE9D8;text-align:center;}.area_toolbar,.area_toolbar table{font:11px sans-serif;}.area_toolbar img{border:0;vertical-align:middle;}.area_toolbar input{margin:0;padding:0;}.area_toolbar select{font-family:'MS Sans Serif',sans-serif,Verdana,Arial;font-size:7pt;font-weight:normal;margin:2px 0 0 0 ;padding:0;vertical-align:top;background-color:#F0F0EE;}table.statusbar{width:100%;}.area_toolbar td.infos{text-align:center;width:130px;border-right:solid 1px #888;border-width:0 1px 0 0;padding:0;}.area_toolbar td.total{text-align:right;width:50px;padding:0;}.area_toolbar td.resize{text-align:right;}.area_toolbar span#resize_area{cursor:nw-resize;visibility:hidden;}.editAreaButtonNormal,.editAreaButtonOver,.editAreaButtonDown,.editAreaSeparator,.editAreaSeparatorLine,.editAreaButtonDisabled,.editAreaButtonSelected {border:0; margin:0; padding:0; background:transparent;margin-top:0;margin-left:1px;padding:0;}.editAreaButtonNormal {border:1px solid #ECE9D8 !important;cursor:pointer;}.editAreaButtonOver {border:1px solid #0A246A !important;cursor:pointer;background-color:#B6BDD2;}.editAreaButtonDown {cursor:pointer;border:1px solid #0A246A !important;background-color:#8592B5;}.editAreaButtonSelected {border:1px solid #C0C0BB !important;cursor:pointer;background-color:#F4F2E8;}.editAreaButtonDisabled {filter:progid:DXImageTransform.Microsoft.Alpha(opacity=30);-moz-opacity:0.3;opacity:0.3;border:1px solid #F0F0EE !important;cursor:pointer;}.editAreaSeparatorLine {margin:1px 2px;background-color:#C0C0BB;width:2px;height:18px;}#processing{display:none;background-color:#ECE9D8;border:solid #888 1px;position:absolute;top:0;left:0;width:100%;height:100%;z-index:100;text-align:center;}#processing_text{position:absolute;left:50%;top:50%;width:200px;height:20px;margin-left:-100px;margin-top:-10px;text-align:center;}#tab_browsing_area{display:none;background-color:#CCC9A8;border-top:1px solid #888;text-align:left;margin:0;}#tab_browsing_list {padding:0;margin:0;list-style-type:none;white-space:nowrap;}#tab_browsing_list li {float:left;margin:-1px;}#tab_browsing_list a {position:relative;display:block;text-decoration:none;float:left;cursor:pointer;line-height:14px;}#tab_browsing_list a span {display:block;color:#000;background:#ECE9D8;border:1px solid #888;border-width:1px 1px 0;text-align:center;padding:2px 2px 1px 4px;position:relative;}#tab_browsing_list a b {display:block;border-bottom:2px solid #617994;}#tab_browsing_list a .edited {display:none;}#tab_browsing_list a.edited .edited {display:inline;}#tab_browsing_list a img{margin-left:7px;}#tab_browsing_list a.edited img{margin-left:3px;}#tab_browsing_list a:hover span {background:#F4F2E8;border-color:#0A246A;}#tab_browsing_list .selected a span{background:#046380;color:#FFF;}#no_file_selected{height:100%;width:150%;background:#CCC;display:none;z-index:20;position:absolute;}.non_editable #editor{border-width:0 1px;}.non_editable .area_toolbar{display:none;}#auto_completion_area{background:#FFF;border:solid 1px #888;position:absolute;z-index:15;width:280px;height:180px;overflow:auto;display:none;}#auto_completion_area a,#auto_completion_area a:visited{display:block;padding:0 2px 1px;color:#000;text-decoration:none;}#auto_completion_area a:hover,#auto_completion_area a:focus,#auto_completion_area a.focus{background:#D6E1FE;text-decoration:none;}#auto_completion_area ul{margin:0;padding:0;list-style:none inside;}#auto_completion_area li{padding:0;}#auto_completion_area .prefix{font-style:italic;padding:0 3px;}</style>";
diff --git a/container-search-gui/src/main/resources/gui/editarea/edit_area/langs/en.js b/container-search-gui/src/main/resources/gui/editarea/edit_area/langs/en.js
new file mode 100755
index 00000000000..ec5ec1c3724
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/editarea/edit_area/langs/en.js
@@ -0,0 +1,62 @@
+/*
+* Copyright (c) 2008, Christophe Dolivet
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+*
+* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+* Neither the name of EditArea nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+* Open source under the BSD License.
+*/
+editAreaLoader.lang["en"]={
+new_document: "new empty document",
+search_button: "search and replace",
+search_command: "search next / open search area",
+search: "search",
+replace: "replace",
+replace_command: "replace / open search area",
+find_next: "find next",
+replace_all: "replace all",
+reg_exp: "regular expressions",
+match_case: "match case",
+not_found: "not found.",
+occurrence_replaced: "occurences replaced.",
+search_field_empty: "Search field empty",
+restart_search_at_begin: "End of area reached. Restart at begin.",
+move_popup: "move search popup",
+font_size: "--Font size--",
+go_to_line: "go to line",
+go_to_line_prompt: "go to line number:",
+undo: "undo",
+redo: "redo",
+change_smooth_selection: "enable/disable some display features (smarter display but more CPU charge)",
+highlight: "toggle syntax highlight on/off",
+reset_highlight: "reset highlight (if desyncronized from text)",
+word_wrap: "toggle word wrapping mode",
+help: "about",
+save: "save",
+load: "load",
+line_abbr: "Ln",
+char_abbr: "Ch",
+position: "Position",
+total: "Total",
+close_popup: "close popup",
+shortcuts: "Shortcuts",
+add_tab: "add tabulation to text",
+remove_tab: "remove tabulation to text",
+about_notice: "Notice: syntax highlight function is only for small text",
+toggle: "Toggle editor",
+accesskey: "Accesskey",
+tab: "Tab",
+shift: "Shift",
+ctrl: "Ctrl",
+esc: "Esc",
+processing: "Processing...",
+fullscreen: "fullscreen",
+syntax_selection: "--Syntax--",
+close_tab: "Close file"
+};
diff --git a/container-search-gui/src/main/resources/gui/editarea/edit_area/plugins/autocompletion/autocompletion.js b/container-search-gui/src/main/resources/gui/editarea/edit_area/plugins/autocompletion/autocompletion.js
new file mode 100644
index 00000000000..5531a2a4f69
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/editarea/edit_area/plugins/autocompletion/autocompletion.js
@@ -0,0 +1,510 @@
+/**
+* Copyright (c) 2008, Christophe Dolivet
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+*
+* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+* Neither the name of EditArea nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+* Open source under the BSD License.
+*
+*
+*
+* Autocompletion class
+*
+* An auto completion box appear while you're writing. It's possible to force it to appear with Ctrl+Space short cut
+*
+* Loaded as a plugin inside editArea (everything made here could have been made in the plugin directory)
+* But is definitly linked to syntax selection (no need to do 2 different files for color and auto complete for each syntax language)
+* and add a too important feature that many people would miss if included as a plugin
+*
+* - init param: autocompletion_start
+* - Button name: "autocompletion"
+*/
+
+var EditArea_autocompletion= {
+
+ /**
+ * Get called once this file is loaded (editArea still not initialized)
+ *
+ * @return nothing
+ */
+ init: function(){
+ // alert("test init: "+ this._someInternalFunction(2, 3));
+
+ if(editArea.settings["autocompletion"])
+ this.enabled= true;
+ else
+ this.enabled= false;
+ this.current_word = false;
+ this.shown = false;
+ this.selectIndex = -1;
+ this.forceDisplay = false;
+ this.isInMiddleWord = false;
+ this.autoSelectIfOneResult = false;
+ this.delayBeforeDisplay = 100;
+ this.checkDelayTimer = false;
+ this.curr_syntax_str = '';
+
+ this.file_syntax_datas = {};
+ }
+ /**
+ * Returns the HTML code for a specific control string or false if this plugin doesn't have that control.
+ * A control can be a button, select list or any other HTML item to present in the EditArea user interface.
+ * Language variables such as {$lang_somekey} will also be replaced with contents from
+ * the language packs.
+ *
+ * @param {string} ctrl_name: the name of the control to add
+ * @return HTML code for a specific control or false.
+ * @type string or boolean
+ */
+ /*,get_control_html: function(ctrl_name){
+ switch( ctrl_name ){
+ case 'autocompletion':
+ // Control id, button img, command
+ return parent.editAreaLoader.get_button_html('autocompletion_but', 'autocompletion.gif', 'toggle_autocompletion', false, this.baseURL);
+ break;
+ }
+ return false;
+ }*/
+ /**
+ * Get called once EditArea is fully loaded and initialised
+ *
+ * @return nothing
+ */
+ ,onload: function(){
+ if(this.enabled)
+ {
+ var icon= document.getElementById("autocompletion");
+ if(icon)
+ editArea.switchClassSticky(icon, 'editAreaButtonSelected', true);
+ }
+
+ this.container = document.createElement('div');
+ this.container.style = "width: 150px; height: 65px; font-family: Tahoma";
+ this.container.id = "auto_completion_area";
+ editArea.container.insertBefore( this.container, editArea.container.firstChild );
+
+ // add event detection for hiding suggestion box
+ parent.editAreaLoader.add_event( document, "click", function(){ editArea.plugins['autocompletion']._hide();} );
+ parent.editAreaLoader.add_event( editArea.textarea, "blur", function(){ editArea.plugins['autocompletion']._hide();} );
+
+ }
+
+ /**
+ * Is called each time the user touch a keyboard key.
+ *
+ * @param (event) e: the keydown event
+ * @return true - pass to next handler in chain, false - stop chain execution
+ * @type boolean
+ */
+ ,onkeydown: function(e){
+ if(!this.enabled)
+ return true;
+
+ if (EA_keys[e.keyCode])
+ letter=EA_keys[e.keyCode];
+ else
+ letter=String.fromCharCode(e.keyCode);
+ // shown
+ if( this._isShown() )
+ {
+ // if escape, hide the box
+ if(letter=="Esc")
+ {
+ this._hide();
+ return false;
+ }
+ // Enter
+ else if( letter=="Entrer")
+ {
+ var as = this.container.getElementsByTagName('A');
+ // select a suggested entry
+ if( this.selectIndex >= 0 && this.selectIndex < as.length )
+ {
+ as[ this.selectIndex ].onmousedown();
+ return false
+ }
+ // simply add an enter in the code
+ else
+ {
+ this._hide();
+ return true;
+ }
+ }
+ else if( letter=="Tab" || letter=="Down")
+ {
+ this._selectNext();
+ return false;
+ }
+ else if( letter=="Up")
+ {
+ this._selectBefore();
+ return false;
+ }
+ }
+ // hidden
+ else
+ {
+
+ }
+
+ // show current suggestion list and do autoSelect if possible (no matter it's shown or hidden)
+ if( letter=="Space" && CtrlPressed(e) )
+ {
+ //parent.console.log('SHOW SUGGEST');
+ this.forceDisplay = true;
+ this.autoSelectIfOneResult = true;
+ this._checkLetter();
+ return false;
+ }
+
+ // wait a short period for check that the cursor isn't moving
+ setTimeout("editArea.plugins['autocompletion']._checkDelayAndCursorBeforeDisplay();", editArea.check_line_selection_timer +5 );
+ this.checkDelayTimer = false;
+ return true;
+ }
+ /**
+ * Executes a specific command, this function handles plugin commands.
+ *
+ * @param {string} cmd: the name of the command being executed
+ * @param {unknown} param: the parameter of the command
+ * @return true - pass to next handler in chain, false - stop chain execution
+ * @type boolean
+ */
+ ,execCommand: function(cmd, param){
+ switch( cmd ){
+ case 'toggle_autocompletion':
+ var icon= document.getElementById("autocompletion");
+ if(!this.enabled)
+ {
+ if(icon != null){
+ editArea.restoreClass(icon);
+ editArea.switchClassSticky(icon, 'editAreaButtonSelected', true);
+ }
+ this.enabled= true;
+ }
+ else
+ {
+ this.enabled= false;
+ if(icon != null)
+ editArea.switchClassSticky(icon, 'editAreaButtonNormal', false);
+ }
+ return true;
+ }
+ return true;
+ }
+ ,_checkDelayAndCursorBeforeDisplay: function()
+ {
+ this.checkDelayTimer = setTimeout("if(editArea.textarea.selectionStart == "+ editArea.textarea.selectionStart +") EditArea_autocompletion._checkLetter();", this.delayBeforeDisplay - editArea.check_line_selection_timer - 5 );
+ }
+ // hide the suggested box
+ ,_hide: function(){
+ this.container.style.display="none";
+ this.selectIndex = -1;
+ this.shown = false;
+ this.forceDisplay = false;
+ this.autoSelectIfOneResult = false;
+ }
+ // display the suggested box
+ ,_show: function(){
+ if( !this._isShown() )
+ {
+ this.container.style.display="block";
+ this.selectIndex = -1;
+ this.shown = true;
+ }
+ }
+ // is the suggested box displayed?
+ ,_isShown: function(){
+ return this.shown;
+ }
+ // setter and getter
+ ,_isInMiddleWord: function( new_value ){
+ if( typeof( new_value ) == "undefined" )
+ return this.isInMiddleWord;
+ else
+ this.isInMiddleWord = new_value;
+ }
+ // select the next element in the suggested box
+ ,_selectNext: function()
+ {
+ var as = this.container.getElementsByTagName('A');
+
+ // clean existing elements
+ for( var i=0; i<as.length; i++ )
+ {
+ if( as[i].className )
+ as[i].className = as[i].className.replace(/ focus/g, '');
+ }
+
+ this.selectIndex++;
+ this.selectIndex = ( this.selectIndex >= as.length || this.selectIndex < 0 ) ? 0 : this.selectIndex;
+ as[ this.selectIndex ].className += " focus";
+ }
+ // select the previous element in the suggested box
+ ,_selectBefore: function()
+ {
+ var as = this.container.getElementsByTagName('A');
+
+ // clean existing elements
+ for( var i=0; i<as.length; i++ )
+ {
+ if( as[i].className )
+ as[i].className = as[ i ].className.replace(/ focus/g, '');
+ }
+
+ this.selectIndex--;
+
+ this.selectIndex = ( this.selectIndex >= as.length || this.selectIndex < 0 ) ? as.length-1 : this.selectIndex;
+ as[ this.selectIndex ].className += " focus";
+ }
+ ,_select: function( content )
+ {
+ cursor_forced_position = content.indexOf( '{@}' );
+ content = content.replace(/{@}/g, '' );
+ editArea.getIESelection();
+
+ // retrive the number of matching characters
+ var start_index = Math.max( 0, editArea.textarea.selectionEnd - content.length );
+
+ line_string = editArea.textarea.value.substring( start_index, editArea.textarea.selectionEnd + 1);
+ limit = line_string.length -1;
+ nbMatch = 0;
+ for( i =0; i<limit ; i++ )
+ {
+ if( line_string.substring( limit - i - 1, limit ) == content.substring( 0, i + 1 ) )
+ nbMatch = i + 1;
+ }
+ // if characters match, we should include them in the selection that will be replaced
+ if( nbMatch > 0 )
+ parent.editAreaLoader.setSelectionRange(editArea.id, editArea.textarea.selectionStart - nbMatch , editArea.textarea.selectionEnd);
+
+ parent.editAreaLoader.setSelectedText(editArea.id, content );
+ range= parent.editAreaLoader.getSelectionRange(editArea.id);
+
+ if( cursor_forced_position != -1 )
+ new_pos = range["end"] - ( content.length-cursor_forced_position );
+ else
+ new_pos = range["end"];
+ parent.editAreaLoader.setSelectionRange(editArea.id, new_pos, new_pos);
+ this._hide();
+ }
+
+
+ /**
+ * Parse the AUTO_COMPLETION part of syntax definition files
+ */
+ ,_parseSyntaxAutoCompletionDatas: function(){
+ //foreach syntax loaded
+ for(var lang in parent.editAreaLoader.load_syntax)
+ {
+ if(!parent.editAreaLoader.syntax[lang]['autocompletion']) // init the regexp if not already initialized
+ {
+ parent.editAreaLoader.syntax[lang]['autocompletion']= {};
+ // the file has auto completion datas
+ if(parent.editAreaLoader.load_syntax[lang]['AUTO_COMPLETION'])
+ {
+ // parse them
+ for(var i in parent.editAreaLoader.load_syntax[lang]['AUTO_COMPLETION'])
+ {
+ datas = parent.editAreaLoader.load_syntax[lang]['AUTO_COMPLETION'][i];
+ tmp = {};
+ if(datas["CASE_SENSITIVE"]!="undefined" && datas["CASE_SENSITIVE"]==false)
+ tmp["modifiers"]="i";
+ else
+ tmp["modifiers"]="";
+ tmp["prefix_separator"]= datas["REGEXP"]["prefix_separator"];
+ tmp["match_prefix_separator"]= new RegExp( datas["REGEXP"]["prefix_separator"] +"$", tmp["modifiers"]);
+ tmp["match_word"]= new RegExp("(?:"+ datas["REGEXP"]["before_word"] +")("+ datas["REGEXP"]["possible_words_letters"] +")$", tmp["modifiers"]);
+ tmp["match_next_letter"]= new RegExp("^("+ datas["REGEXP"]["letter_after_word_must_match"] +")$", tmp["modifiers"]);
+ tmp["keywords"]= {};
+ //console.log( datas["KEYWORDS"] );
+ for( var prefix in datas["KEYWORDS"] )
+ {
+ tmp["keywords"][prefix]= {
+ prefix: prefix,
+ prefix_name: prefix,
+ prefix_reg: new RegExp("(?:"+ parent.editAreaLoader.get_escaped_regexp( prefix ) +")(?:"+ tmp["prefix_separator"] +")$", tmp["modifiers"] ),
+ datas: []
+ };
+ for( var j=0; j<datas["KEYWORDS"][prefix].length; j++ )
+ {
+ tmp["keywords"][prefix]['datas'][j]= {
+ is_typing: datas["KEYWORDS"][prefix][j][0],
+ // if replace with is empty, replace with the is_typing value
+ replace_with: datas["KEYWORDS"][prefix][j][1] ? datas["KEYWORDS"][prefix][j][1].replace('�', datas["KEYWORDS"][prefix][j][0] ) : '',
+ comment: datas["KEYWORDS"][prefix][j][2] ? datas["KEYWORDS"][prefix][j][2] : ''
+ };
+
+ // the replace with shouldn't be empty
+ if( tmp["keywords"][prefix]['datas'][j]['replace_with'].length == 0 )
+ tmp["keywords"][prefix]['datas'][j]['replace_with'] = tmp["keywords"][prefix]['datas'][j]['is_typing'];
+
+ // if the comment is empty, display the replace_with value
+ if( tmp["keywords"][prefix]['datas'][j]['comment'].length == 0 )
+ tmp["keywords"][prefix]['datas'][j]['comment'] = tmp["keywords"][prefix]['datas'][j]['replace_with'].replace(/{@}/g, '' );
+ }
+
+ }
+ tmp["max_text_length"]= datas["MAX_TEXT_LENGTH"];
+ parent.editAreaLoader.syntax[lang]['autocompletion'][i] = tmp;
+ }
+ }
+ }
+ }
+ }
+
+ ,_checkLetter: function(){
+ // check that syntax hasn't changed
+ if( this.curr_syntax_str != editArea.settings['syntax'] )
+ {
+ if( !parent.editAreaLoader.syntax[editArea.settings['syntax']]['autocompletion'] )
+ this._parseSyntaxAutoCompletionDatas();
+ this.curr_syntax= parent.editAreaLoader.syntax[editArea.settings['syntax']]['autocompletion'];
+ this.curr_syntax_str = editArea.settings['syntax'];
+ //console.log( this.curr_syntax );
+ }
+
+ if( editArea.is_editable )
+ {
+ time=new Date;
+ t1= time.getTime();
+ editArea.getIESelection();
+ this.selectIndex = -1;
+ start=editArea.textarea.selectionStart;
+ var str = editArea.textarea.value;
+ var results= [];
+
+
+ for(var i in this.curr_syntax)
+ {
+ var last_chars = str.substring(Math.max(0, start-this.curr_syntax[i]["max_text_length"]), start);
+ var matchNextletter = str.substring(start, start+1).match( this.curr_syntax[i]["match_next_letter"]);
+ // if not writting in the middle of a word or if forcing display
+ if( matchNextletter || this.forceDisplay )
+ {
+ // check if the last chars match a separator
+ var match_prefix_separator = last_chars.match(this.curr_syntax[i]["match_prefix_separator"]);
+
+ // check if it match a possible word
+ var match_word= last_chars.match(this.curr_syntax[i]["match_word"]);
+
+ //console.log( match_word );
+ if( match_word )
+ {
+ var begin_word= match_word[1];
+ var match_curr_word= new RegExp("^"+ parent.editAreaLoader.get_escaped_regexp( begin_word ), this.curr_syntax[i]["modifiers"]);
+ //console.log( match_curr_word );
+ for(var prefix in this.curr_syntax[i]["keywords"])
+ {
+ // parent.console.log( this.curr_syntax[i]["keywords"][prefix] );
+ for(var j=0; j<this.curr_syntax[i]["keywords"][prefix]['datas'].length; j++)
+ {
+ // parent.console.log( this.curr_syntax[i]["keywords"][prefix]['datas'][j]['is_typing'] );
+ // the key word match or force display
+ if( this.curr_syntax[i]["keywords"][prefix]['datas'][j]['is_typing'].match(match_curr_word) )
+ {
+ // parent.console.log('match');
+ hasMatch = false;
+ var before = last_chars.substr( 0, last_chars.length - begin_word.length );
+
+ // no prefix to match => it's valid
+ if( !match_prefix_separator && this.curr_syntax[i]["keywords"][prefix]['prefix'].length == 0 )
+ {
+ if( ! before.match( this.curr_syntax[i]["keywords"][prefix]['prefix_reg'] ) )
+ hasMatch = true;
+ }
+ // we still need to check the prefix if there is one
+ else if( this.curr_syntax[i]["keywords"][prefix]['prefix'].length > 0 )
+ {
+ if( before.match( this.curr_syntax[i]["keywords"][prefix]['prefix_reg'] ) )
+ hasMatch = true;
+ }
+
+ if( hasMatch )
+ results[results.length]= [ this.curr_syntax[i]["keywords"][prefix], this.curr_syntax[i]["keywords"][prefix]['datas'][j] ];
+ }
+ }
+ }
+ }
+ // it doesn't match any possible word but we want to display something
+ // we'll display to list of all available words
+ else if( this.forceDisplay || match_prefix_separator )
+ {
+ for(var prefix in this.curr_syntax[i]["keywords"])
+ {
+ for(var j=0; j<this.curr_syntax[i]["keywords"][prefix]['datas'].length; j++)
+ {
+ hasMatch = false;
+ // no prefix to match => it's valid
+ if( !match_prefix_separator && this.curr_syntax[i]["keywords"][prefix]['prefix'].length == 0 )
+ {
+ hasMatch = true;
+ }
+ // we still need to check the prefix if there is one
+ else if( match_prefix_separator && this.curr_syntax[i]["keywords"][prefix]['prefix'].length > 0 )
+ {
+ var before = last_chars; //.substr( 0, last_chars.length );
+ if( before.match( this.curr_syntax[i]["keywords"][prefix]['prefix_reg'] ) )
+ hasMatch = true;
+ }
+
+ if( hasMatch )
+ results[results.length]= [ this.curr_syntax[i]["keywords"][prefix], this.curr_syntax[i]["keywords"][prefix]['datas'][j] ];
+ }
+ }
+ }
+ }
+ }
+
+ // there is only one result, and we can select it automatically
+ if( results.length == 1 && this.autoSelectIfOneResult )
+ {
+ // console.log( results );
+ this._select( results[0][1]['replace_with'] );
+ }
+ else if( results.length == 0 )
+ {
+ this._hide();
+ }
+ else
+ {
+ // build the suggestion box content
+ var lines=[];
+ for(var i=0; i<results.length; i++)
+ {
+ var line= "<li><a href=\"#\" class=\"entry\" onmousedown=\"EditArea_autocompletion._select('"+ results[i][1]['replace_with'].replace(new RegExp('"', "g"), "&quot;") +"');return false;\">"+ results[i][1]['comment'];
+ if(results[i][0]['prefix_name'].length>0)
+ var value = results[i][0]['prefix_name'];
+ value = (value == undefined) ? "" : value;
+ if (value == 'SOURCES' || value == ','){value = 'Source'}
+ line+='<span class="prefix">'+ value +'</span>';
+ line+='</a></li>';
+ lines[lines.length]=line;
+ }
+ // sort results
+ this.container.innerHTML = '<ul>'+ lines.sort().join('') +'</ul>';
+
+ var cursor = _$("cursor_pos");
+ this.container.style.top = ( cursor.cursor_top + editArea.lineHeight ) +"px";
+ this.container.style.left = ( cursor.cursor_left + 8 ) +"px";
+ this._show();
+ }
+
+ this.autoSelectIfOneResult = false;
+ time=new Date;
+ t2= time.getTime();
+
+ //parent.console.log( begin_word +"\n"+ (t2-t1) +"\n"+ html );
+ }
+ }
+};
+
+// Load as a plugin
+editArea.settings['plugins'][ editArea.settings['plugins'].length ] = 'autocompletion';
+editArea.add_plugin('autocompletion', EditArea_autocompletion);
diff --git a/container-search-gui/src/main/resources/gui/editarea/edit_area/plugins/autocompletion/langs/en.js b/container-search-gui/src/main/resources/gui/editarea/edit_area/plugins/autocompletion/langs/en.js
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/editarea/edit_area/plugins/autocompletion/langs/en.js
diff --git a/container-search-gui/src/main/resources/gui/editarea/edit_area/reg_syntax/yql.js b/container-search-gui/src/main/resources/gui/editarea/edit_area/reg_syntax/yql.js
new file mode 100755
index 00000000000..0a7dae862f3
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/editarea/edit_area/reg_syntax/yql.js
@@ -0,0 +1,109 @@
+/**
+* Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+*/
+function getSources(bool){
+ var options = [];
+ if (window.CONFIG.hasOwnProperty("model_sources")){
+ var sources = window.CONFIG.model_sources;
+ for (i in sources){
+ option = [sources[i]];
+ options.push(option);
+ }
+ if (bool == true){options.push(["*"])}
+ }
+ return options;
+}
+
+editAreaLoader.load_syntax["yql"] = {
+ 'DISPLAY_NAME' : 'YQL'
+ ,'QUOTEMARKS' : {1: "'", 2: '"', 3: '`'}
+ ,'KEYWORD_CASE_SENSITIVE' : false
+ ,'OPERATOR_CASE_SENSITIVE' : false
+ ,'KEYWORDS' : {
+ 'statements' : [
+ 'SELECT', 'FROM','SOURCES', 'CONTAINS',
+ 'NOT', 'ORDER',
+ 'BY', 'WHERE'
+ ]
+ ,'reserved' : [
+ 'null'
+
+ ]
+ ,'functions' : [
+ 'DESC', 'ASC', 'ALL', 'GROUP', 'RANGE', 'EACH', 'OUTPUT', 'SUM', 'LIMIT', 'OFFSET', 'TIMEOUT'
+ ]
+ }
+ ,'OPERATORS' :[
+ 'COUNT','AND','and','OR','or','BETWEEN','between','&&','&','|','^','/','<=>','=','>=','>','<<','>>','<=','<','-','%','!=','<>','!','||','+','~','*'
+ ]
+ ,'DELIMITERS' :[
+ '(', ')', '[', ']', '{', '}'
+ ]
+ ,'REGEXPS' : {
+ // highlight all variables (@...)
+ 'variables' : {
+ 'search' : '()(\\@\\w+)()'
+ ,'class' : 'variables'
+ ,'modifiers' : 'g'
+ ,'execute' : 'before' // before or after
+ }
+ }
+ ,'STYLES' : {
+ 'COMMENTS': 'color: #AAAAAA;'
+ ,'QUOTESMARKS': 'color: #879EFA;'
+ ,'KEYWORDS' : {
+ 'reserved' : 'color: #48BDDF;'
+ ,'functions' : 'color: #0040FD;'
+ ,'statements' : 'color: #60CA00;'
+ }
+ ,'OPERATORS' : 'color: #FF00FF;'
+ ,'DELIMITERS' : 'color: #d1421b;'
+ ,'REGEXPS' : {
+ 'variables' : 'color: #E0BD54;'
+ }
+ },
+ 'AUTO_COMPLETION' : {
+ "default": { // the name of this definition group. It's posisble to have different rules inside the same definition file
+ "REGEXP": { "before_word": "[^a-zA-Z0-9_]|^" // \\s|\\.|
+ ,"possible_words_letters": "[a-zA-Z0-9_]+"
+ ,"letter_after_word_must_match": "[^a-zA-Z0-9_]|$"
+ ,"prefix_separator": "\\."
+ }
+ ,"CASE_SENSITIVE": false
+ ,"MAX_TEXT_LENGTH": 50 // the maximum length of the text being analyzed before the cursor position
+ ,"KEYWORDS": {
+ '': [ // the prefix of thoses items
+ /**
+ * 0 : the keyword the user is typing
+ * 1 : (optionnal) the string inserted in code ("{@}" being the new position of the cursor, "§" beeing the equivalent to the value the typed string indicated if the previous )
+ * If empty the keyword will be displayed
+ * 2 : (optionnal) the text that appear in the suggestion box (if empty, the string to insert will be displayed)
+ */
+ ['SELECT','SELECT'],
+ ['FROM','FROM'],
+ ['SOURCES','SOURCES'],
+ ['CONTAINS','CONTAINS'],
+ ['NOT','NOT'], ['ORDER','ORDER'], ['BY','BY'],['WHERE','WHERE'],
+ ['DESC','DESC'],['ASC','ASC'],['ALL','ALL'], ['GROUP','GROUP'],
+ ['RANGE','RANGE'],['EACH','EACH'],['OUTPUT','OUTPUT'],
+ ['SUM','SUM'],['LIMIT','LIMIT'],['OFFSET','OFFSET'],
+ ['TIMEOUT','TIMEOUT']
+ ]
+ }
+ },
+ "sources": { // the name of this definition group. It's posisble to have different rules inside the same definition file
+ "REGEXP": { "before_word": "[^a-zA-Z0-9_]|^" // \\s|\\.|
+ ,"possible_words_letters": "[a-zA-Z0-9_]+"
+ ,"letter_after_word_must_match": "[^a-zA-Z0-9_]|$"
+ ,"prefix_separator": " "
+ }
+ ,"CASE_SENSITIVE": false
+ ,"MAX_TEXT_LENGTH": 50 // the maximum length of the text being analyzed before the cursor position
+ ,"KEYWORDS": {
+ 'SOURCES' : getSources(true) ,
+ ',' : getSources(false)
+ }
+ }
+
+ }
+};
diff --git a/container-search-gui/src/main/resources/gui/icons/android-chrome-192x192.png b/container-search-gui/src/main/resources/gui/icons/android-chrome-192x192.png
new file mode 100644
index 00000000000..af80e645b77
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/icons/android-chrome-192x192.png
Binary files differ
diff --git a/container-search-gui/src/main/resources/gui/icons/android-chrome-384x384.png b/container-search-gui/src/main/resources/gui/icons/android-chrome-384x384.png
new file mode 100644
index 00000000000..7094e2e232d
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/icons/android-chrome-384x384.png
Binary files differ
diff --git a/container-search-gui/src/main/resources/gui/icons/apple-touch-icon.png b/container-search-gui/src/main/resources/gui/icons/apple-touch-icon.png
new file mode 100644
index 00000000000..4dfc9a85a2a
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/icons/apple-touch-icon.png
Binary files differ
diff --git a/container-search-gui/src/main/resources/gui/icons/browserconfig.xml b/container-search-gui/src/main/resources/gui/icons/browserconfig.xml
new file mode 100644
index 00000000000..5fe3c6a3ff9
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/icons/browserconfig.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
+<browserconfig>
+ <msapplication>
+ <tile>
+ <square150x150logo src="/icons/mstile-150x150.png"/>
+ <TileColor>#2b5797</TileColor>
+ </tile>
+ </msapplication>
+</browserconfig>
diff --git a/container-search-gui/src/main/resources/gui/icons/favicon-16x16.png b/container-search-gui/src/main/resources/gui/icons/favicon-16x16.png
new file mode 100644
index 00000000000..cce56472694
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/icons/favicon-16x16.png
Binary files differ
diff --git a/container-search-gui/src/main/resources/gui/icons/favicon-32x32.png b/container-search-gui/src/main/resources/gui/icons/favicon-32x32.png
new file mode 100644
index 00000000000..5f9f3e1b85b
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/icons/favicon-32x32.png
Binary files differ
diff --git a/container-search-gui/src/main/resources/gui/icons/favicon.ico b/container-search-gui/src/main/resources/gui/icons/favicon.ico
new file mode 100644
index 00000000000..885fb77541d
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/icons/favicon.ico
Binary files differ
diff --git a/container-search-gui/src/main/resources/gui/icons/manifest.json b/container-search-gui/src/main/resources/gui/icons/manifest.json
new file mode 100644
index 00000000000..f71a0007cf4
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/icons/manifest.json
@@ -0,0 +1,18 @@
+{
+ "name": "",
+ "icons": [
+ {
+ "src": "/icons/android-chrome-192x192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "/icons/android-chrome-384x384.png",
+ "sizes": "384x384",
+ "type": "image/png"
+ }
+ ],
+ "theme_color": "#ffffff",
+ "background_color": "#ffffff",
+ "display": "standalone"
+} \ No newline at end of file
diff --git a/container-search-gui/src/main/resources/gui/icons/mstile-150x150.png b/container-search-gui/src/main/resources/gui/icons/mstile-150x150.png
new file mode 100644
index 00000000000..15ba5c1404e
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/icons/mstile-150x150.png
Binary files differ
diff --git a/container-search-gui/src/main/resources/gui/icons/safari-pinned-tab.svg b/container-search-gui/src/main/resources/gui/icons/safari-pinned-tab.svg
new file mode 100644
index 00000000000..7b775fcdbde
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/icons/safari-pinned-tab.svg
@@ -0,0 +1,22 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+ "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
+ width="464.000000pt" height="464.000000pt" viewBox="0 0 464.000000 464.000000"
+ preserveAspectRatio="xMidYMid meet">
+<metadata>
+Created by potrace 1.11, written by Peter Selinger 2001-2013
+</metadata>
+<g transform="translate(0.000000,464.000000) scale(0.100000,-0.100000)"
+fill="#000000" stroke="none">
+<path d="M2559 4542 c-129 -88 -531 -360 -599 -406 -25 -17 -49 -34 -55 -38
+-5 -5 -26 -19 -45 -31 -35 -23 -183 -124 -761 -517 -68 -46 -144 -97 -169
+-114 -25 -16 -46 -31 -48 -32 -2 -1 -4 -126 -4 -278 l1 -276 -27 18 c-15 11
+-148 104 -297 209 -148 104 -294 206 -323 226 l-52 37 0 -1052 1 -1053 627
+-425 c919 -624 937 -636 1056 -717 l109 -75 196 133 c603 409 710 482 1061
+721 146 99 327 221 402 271 l137 92 3 277 3 277 285 -201 c157 -110 313 -220
+348 -245 l62 -44 0 1053 -1 1053 -92 62 c-50 34 -144 97 -207 140 -63 43 -214
+146 -335 228 -121 82 -431 292 -688 467 -258 175 -469 318 -471 318 -1 0 -54
+-35 -117 -78z"/>
+</g>
+</svg>
diff --git a/container-search-gui/src/main/resources/gui/img/Vespa-V2.png b/container-search-gui/src/main/resources/gui/img/Vespa-V2.png
new file mode 100644
index 00000000000..ac87f8e94d0
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/img/Vespa-V2.png
Binary files differ
diff --git a/container-search-gui/src/main/resources/gui/img/VespaIcon.png b/container-search-gui/src/main/resources/gui/img/VespaIcon.png
new file mode 100644
index 00000000000..33063432c20
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/img/VespaIcon.png
Binary files differ
diff --git a/container-search-gui/src/main/resources/gui/img/copy.svg b/container-search-gui/src/main/resources/gui/img/copy.svg
new file mode 100644
index 00000000000..eada154413a
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/img/copy.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 488.3 488.3" style="enable-background:new 0 0 488.3 488.3;" xml:space="preserve" width="512px" height="512px" class=""><g><g>
+ <g>
+ <path d="M314.25,85.4h-227c-21.3,0-38.6,17.3-38.6,38.6v325.7c0,21.3,17.3,38.6,38.6,38.6h227c21.3,0,38.6-17.3,38.6-38.6V124 C352.75,102.7,335.45,85.4,314.25,85.4z M325.75,449.6c0,6.4-5.2,11.6-11.6,11.6h-227c-6.4,0-11.6-5.2-11.6-11.6V124 c0-6.4,5.2-11.6,11.6-11.6h227c6.4,0,11.6,5.2,11.6,11.6V449.6z" data-original="#000000" class="active-path" data-old_color="#F8F5F5" fill="#FCFCFC"/>
+ <path d="M401.05,0h-227c-21.3,0-38.6,17.3-38.6,38.6c0,7.5,6,13.5,13.5,13.5s13.5-6,13.5-13.5c0-6.4,5.2-11.6,11.6-11.6h227 c6.4,0,11.6,5.2,11.6,11.6v325.7c0,6.4-5.2,11.6-11.6,11.6c-7.5,0-13.5,6-13.5,13.5s6,13.5,13.5,13.5c21.3,0,38.6-17.3,38.6-38.6 V38.6C439.65,17.3,422.35,0,401.05,0z" data-original="#000000" class="active-path" data-old_color="#F8F5F5" fill="#FCFCFC"/>
+ </g>
+</g></g> </svg>
diff --git a/container-search-gui/src/main/resources/gui/img/down-arrow.svg b/container-search-gui/src/main/resources/gui/img/down-arrow.svg
new file mode 100644
index 00000000000..d78d2f9c17f
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/img/down-arrow.svg
@@ -0,0 +1 @@
+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 36 36"><defs><style>.cls-1{clip-path:url(#clip-path);}.cls-2{clip-path:url(#clip-path-2);}.cls-3{clip-path:url(#clip-path-3);}</style><clipPath id="clip-path"><path d="M18,4.09A13.91,13.91,0,1,1,4.09,18,13.91,13.91,0,0,1,18,4.09ZM18,35A17,17,0,1,0,6,30,17,17,0,0,0,18,35Z"/></clipPath><clipPath id="clip-path-2"><rect x="-723.82" y="-837.15" width="1483.64" height="3802.85" fill="#FFFFFF"/></clipPath><clipPath id="clip-path-3"><path d="M26,13.83a1.55,1.55,0,0,0-2.16,0L18,19.75l-5.88-6A1.54,1.54,0,0,0,9.93,16L18,24.18,26.07,16a1.57,1.57,0,0,0,0-2.19Z"/></clipPath></defs><title>down-arrow</title><path d="M18,4.09A13.91,13.91,0,1,1,4.09,18,13.91,13.91,0,0,1,18,4.09ZM18,35A17,17,0,1,0,6,30,17,17,0,0,0,18,35Z"/><g class="cls-1"><rect x="-723.82" y="-837.15" width="1483.64" height="3802.85" fill="#FFFFFF"/><g class="cls-2"><rect x="-4.15" y="-4.15" width="44.3" height="44.3" fill="#FFFFFF"/></g></g><path d="M26,13.83a1.55,1.55,0,0,0-2.16,0L18,19.75l-5.88-6A1.54,1.54,0,0,0,9.93,16L18,24.18,26.07,16a1.57,1.57,0,0,0,0-2.19Z"/><g class="cls-3"><rect x="-723.82" y="-837.15" width="1483.64" height="3802.85" fill="#FFFFFF"/><g class="cls-2"><rect x="4.35" y="8.21" width="27.3" height="21.12" fill="#FFFFFF"/></g></g></svg>
diff --git a/container-search-gui/src/main/resources/gui/img/features-help.png b/container-search-gui/src/main/resources/gui/img/features-help.png
new file mode 100644
index 00000000000..65702f8b91f
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/img/features-help.png
Binary files differ
diff --git a/container-search-gui/src/main/resources/gui/img/information.svg b/container-search-gui/src/main/resources/gui/img/information.svg
new file mode 100644
index 00000000000..da42cf2caf6
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/img/information.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve" width="512px" height="512px"><g><g>
+ <g>
+ <g>
+ <circle cx="256" cy="378.5" r="25" data-original="#000000" class="active-path" data-old_color="#898989" fill="#767474"/>
+ <path d="M256,0C114.516,0,0,114.497,0,256c0,141.484,114.497,256,256,256c141.484,0,256-114.497,256-256 C512,114.516,397.503,0,256,0z M256,472c-119.377,0-216-96.607-216-216c0-119.377,96.607-216,216-216 c119.377,0,216,96.607,216,216C472,375.377,375.393,472,256,472z" data-original="#000000" class="active-path" data-old_color="#898989" fill="#767474"/>
+ <path d="M256,128.5c-44.112,0-80,35.888-80,80c0,11.046,8.954,20,20,20s20-8.954,20-20c0-22.056,17.944-40,40-40 c22.056,0,40,17.944,40,40c0,22.056-17.944,40-40,40c-11.046,0-20,8.954-20,20v50c0,11.046,8.954,20,20,20 c11.046,0,20-8.954,20-20v-32.531c34.466-8.903,60-40.26,60-77.469C336,164.388,300.112,128.5,256,128.5z" data-original="#000000" class="active-path" data-old_color="#898989" fill="#767474"/>
+ </g>
+ </g>
+</g></g> </svg>
diff --git a/container-search-gui/src/main/resources/gui/img/paste.svg b/container-search-gui/src/main/resources/gui/img/paste.svg
new file mode 100644
index 00000000000..b2edac680bf
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/img/paste.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" width="512px" height="512px" viewBox="0 0 561 561" style="enable-background:new 0 0 561 561;" xml:space="preserve" class=""><g><g>
+ <g id="content-paste">
+ <path d="M459,51H351.9c-10.2-30.6-38.25-51-71.4-51c-33.15,0-61.2,20.4-71.4,51H102c-28.05,0-51,22.95-51,51v408 c0,28.05,22.95,51,51,51h357c28.05,0,51-22.95,51-51V102C510,73.95,487.05,51,459,51z M280.5,51c15.3,0,25.5,10.2,25.5,25.5 S295.8,102,280.5,102S255,91.8,255,76.5S265.2,51,280.5,51z M459,510H102V102h51v76.5h255V102h51V510z" data-original="#000000" class="active-path" data-old_color="#F9F6F6" fill="#FBF9F9"/>
+ </g>
+</g></g> </svg>
diff --git a/container-search-gui/src/main/resources/gui/img/reload.svg b/container-search-gui/src/main/resources/gui/img/reload.svg
new file mode 100644
index 00000000000..c5381f9f232
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/img/reload.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve" width="512px" height="512px" class=""><g><g>
+ <g>
+ <path d="M482.195,226.196C482.195,101.471,380.725,0,256.001,0S29.805,101.471,29.805,226.196c0,7.409,6.007,13.416,13.416,13.416 s13.416-6.008,13.416-13.416c0-109.93,89.434-199.363,199.363-199.363s199.363,89.434,199.363,199.363 c0,109.928-89.434,199.362-199.363,199.362h-23.276l33.282-37.255c4.937-5.525,4.458-14.007-1.067-18.944 c-5.525-4.937-14.008-4.457-18.944,1.068l-47.576,53.255c-7.788,8.718-7.788,21.866,0,30.584l47.576,53.255 c2.651,2.968,6.322,4.478,10.01,4.478c3.181,0,6.375-1.126,8.934-3.41c5.526-4.937,6.004-13.419,1.067-18.944l-33.282-37.255 h23.276C380.725,452.39,482.195,350.919,482.195,226.196z" data-original="#000000" class="active-path" data-old_color="#F0EDED" fill="#F3F2F2"/>
+ </g>
+</g></g> </svg>
diff --git a/container-search-gui/src/main/resources/gui/js/agency.js b/container-search-gui/src/main/resources/gui/js/agency.js
new file mode 100644
index 00000000000..553f3eba651
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/js/agency.js
@@ -0,0 +1,39 @@
+/*!
+ * Start Bootstrap - Agnecy Bootstrap Theme (http://startbootstrap.com)
+ * Code licensed under the Apache License v2.0.
+ * For details, see http://www.apache.org/licenses/LICENSE-2.0.
+ */
+
+// jQuery for page scrolling feature - requires jQuery Easing plugin
+$(function() {
+ var navbarHeight = $('.navbar').outerHeight();
+
+ $('a.page-scroll').bind('click', function(event) {
+ var $anchor = $(this);
+ $('html, body').stop().animate({
+ scrollTop: $($anchor.attr('href')).offset().top - navbarHeight
+ }, 1500, 'easeInOutExpo');
+ event.preventDefault();
+ });
+});
+
+// Highlight the top nav as scrolling occurs
+$('body').scrollspy({
+ target: '.navbar-fixed-top'
+})
+
+// Closes the Responsive Menu on Menu Item Click
+$('.navbar-collapse ul li a').click(function() {
+ $('.navbar-toggle:visible').click();
+});
+
+$('div.modal').on('show.bs.modal', function() {
+ var modal = this;
+ var hash = modal.id;
+ window.location.hash = hash;
+ window.onhashchange = function() {
+ if (!location.hash){
+ $(modal).modal('hide');
+ }
+ }
+});
diff --git a/container-search-gui/src/main/resources/gui/js/bootstrap.js b/container-search-gui/src/main/resources/gui/js/bootstrap.js
new file mode 100644
index 00000000000..53da1c77c08
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/js/bootstrap.js
@@ -0,0 +1,2114 @@
+/*!
+ * Bootstrap v3.2.0 (http://getbootstrap.com)
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+
+if (typeof jQuery === 'undefined') { throw new Error('Bootstrap\'s JavaScript requires jQuery') }
+
+/* ========================================================================
+ * Bootstrap: transition.js v3.2.0
+ * http://getbootstrap.com/javascript/#transitions
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
+ // ============================================================
+
+ function transitionEnd() {
+ var el = document.createElement('bootstrap')
+
+ var transEndEventNames = {
+ WebkitTransition : 'webkitTransitionEnd',
+ MozTransition : 'transitionend',
+ OTransition : 'oTransitionEnd otransitionend',
+ transition : 'transitionend'
+ }
+
+ for (var name in transEndEventNames) {
+ if (el.style[name] !== undefined) {
+ return { end: transEndEventNames[name] }
+ }
+ }
+
+ return false // explicit for ie8 ( ._.)
+ }
+
+ // http://blog.alexmaccaw.com/css-transitions
+ $.fn.emulateTransitionEnd = function (duration) {
+ var called = false
+ var $el = this
+ $(this).one('bsTransitionEnd', function () { called = true })
+ var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
+ setTimeout(callback, duration)
+ return this
+ }
+
+ $(function () {
+ $.support.transition = transitionEnd()
+
+ if (!$.support.transition) return
+
+ $.event.special.bsTransitionEnd = {
+ bindType: $.support.transition.end,
+ delegateType: $.support.transition.end,
+ handle: function (e) {
+ if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments)
+ }
+ }
+ })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: alert.js v3.2.0
+ * http://getbootstrap.com/javascript/#alerts
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // ALERT CLASS DEFINITION
+ // ======================
+
+ var dismiss = '[data-dismiss="alert"]'
+ var Alert = function (el) {
+ $(el).on('click', dismiss, this.close)
+ }
+
+ Alert.VERSION = '3.2.0'
+
+ Alert.prototype.close = function (e) {
+ var $this = $(this)
+ var selector = $this.attr('data-target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
+ }
+
+ var $parent = $(selector)
+
+ if (e) e.preventDefault()
+
+ if (!$parent.length) {
+ $parent = $this.hasClass('alert') ? $this : $this.parent()
+ }
+
+ $parent.trigger(e = $.Event('close.bs.alert'))
+
+ if (e.isDefaultPrevented()) return
+
+ $parent.removeClass('in')
+
+ function removeElement() {
+ // detach from parent, fire event then clean up data
+ $parent.detach().trigger('closed.bs.alert').remove()
+ }
+
+ $.support.transition && $parent.hasClass('fade') ?
+ $parent
+ .one('bsTransitionEnd', removeElement)
+ .emulateTransitionEnd(150) :
+ removeElement()
+ }
+
+
+ // ALERT PLUGIN DEFINITION
+ // =======================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.alert')
+
+ if (!data) $this.data('bs.alert', (data = new Alert(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ var old = $.fn.alert
+
+ $.fn.alert = Plugin
+ $.fn.alert.Constructor = Alert
+
+
+ // ALERT NO CONFLICT
+ // =================
+
+ $.fn.alert.noConflict = function () {
+ $.fn.alert = old
+ return this
+ }
+
+
+ // ALERT DATA-API
+ // ==============
+
+ $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: button.js v3.2.0
+ * http://getbootstrap.com/javascript/#buttons
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // BUTTON PUBLIC CLASS DEFINITION
+ // ==============================
+
+ var Button = function (element, options) {
+ this.$element = $(element)
+ this.options = $.extend({}, Button.DEFAULTS, options)
+ this.isLoading = false
+ }
+
+ Button.VERSION = '3.2.0'
+
+ Button.DEFAULTS = {
+ loadingText: 'loading...'
+ }
+
+ Button.prototype.setState = function (state) {
+ var d = 'disabled'
+ var $el = this.$element
+ var val = $el.is('input') ? 'val' : 'html'
+ var data = $el.data()
+
+ state = state + 'Text'
+
+ if (data.resetText == null) $el.data('resetText', $el[val]())
+
+ $el[val](data[state] == null ? this.options[state] : data[state])
+
+ // push to event loop to allow forms to submit
+ setTimeout($.proxy(function () {
+ if (state == 'loadingText') {
+ this.isLoading = true
+ $el.addClass(d).attr(d, d)
+ } else if (this.isLoading) {
+ this.isLoading = false
+ $el.removeClass(d).removeAttr(d)
+ }
+ }, this), 0)
+ }
+
+ Button.prototype.toggle = function () {
+ var changed = true
+ var $parent = this.$element.closest('[data-toggle="buttons"]')
+
+ if ($parent.length) {
+ var $input = this.$element.find('input')
+ if ($input.prop('type') == 'radio') {
+ if ($input.prop('checked') && this.$element.hasClass('active')) changed = false
+ else $parent.find('.active').removeClass('active')
+ }
+ if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change')
+ }
+
+ if (changed) this.$element.toggleClass('active')
+ }
+
+
+ // BUTTON PLUGIN DEFINITION
+ // ========================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.button')
+ var options = typeof option == 'object' && option
+
+ if (!data) $this.data('bs.button', (data = new Button(this, options)))
+
+ if (option == 'toggle') data.toggle()
+ else if (option) data.setState(option)
+ })
+ }
+
+ var old = $.fn.button
+
+ $.fn.button = Plugin
+ $.fn.button.Constructor = Button
+
+
+ // BUTTON NO CONFLICT
+ // ==================
+
+ $.fn.button.noConflict = function () {
+ $.fn.button = old
+ return this
+ }
+
+
+ // BUTTON DATA-API
+ // ===============
+
+ $(document).on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
+ var $btn = $(e.target)
+ if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
+ Plugin.call($btn, 'toggle')
+ e.preventDefault()
+ })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: carousel.js v3.2.0
+ * http://getbootstrap.com/javascript/#carousel
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // CAROUSEL CLASS DEFINITION
+ // =========================
+
+ var Carousel = function (element, options) {
+ this.$element = $(element).on('keydown.bs.carousel', $.proxy(this.keydown, this))
+ this.$indicators = this.$element.find('.carousel-indicators')
+ this.options = options
+ this.paused =
+ this.sliding =
+ this.interval =
+ this.$active =
+ this.$items = null
+
+ this.options.pause == 'hover' && this.$element
+ .on('mouseenter.bs.carousel', $.proxy(this.pause, this))
+ .on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
+ }
+
+ Carousel.VERSION = '3.2.0'
+
+ Carousel.DEFAULTS = {
+ interval: 5000,
+ pause: 'hover',
+ wrap: true
+ }
+
+ Carousel.prototype.keydown = function (e) {
+ switch (e.which) {
+ case 37: this.prev(); break
+ case 39: this.next(); break
+ default: return
+ }
+
+ e.preventDefault()
+ }
+
+ Carousel.prototype.cycle = function (e) {
+ e || (this.paused = false)
+
+ this.interval && clearInterval(this.interval)
+
+ this.options.interval
+ && !this.paused
+ && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
+
+ return this
+ }
+
+ Carousel.prototype.getItemIndex = function (item) {
+ this.$items = item.parent().children('.item')
+ return this.$items.index(item || this.$active)
+ }
+
+ Carousel.prototype.to = function (pos) {
+ var that = this
+ var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active'))
+
+ if (pos > (this.$items.length - 1) || pos < 0) return
+
+ if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid"
+ if (activeIndex == pos) return this.pause().cycle()
+
+ return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))
+ }
+
+ Carousel.prototype.pause = function (e) {
+ e || (this.paused = true)
+
+ if (this.$element.find('.next, .prev').length && $.support.transition) {
+ this.$element.trigger($.support.transition.end)
+ this.cycle(true)
+ }
+
+ this.interval = clearInterval(this.interval)
+
+ return this
+ }
+
+ Carousel.prototype.next = function () {
+ if (this.sliding) return
+ return this.slide('next')
+ }
+
+ Carousel.prototype.prev = function () {
+ if (this.sliding) return
+ return this.slide('prev')
+ }
+
+ Carousel.prototype.slide = function (type, next) {
+ var $active = this.$element.find('.item.active')
+ var $next = next || $active[type]()
+ var isCycling = this.interval
+ var direction = type == 'next' ? 'left' : 'right'
+ var fallback = type == 'next' ? 'first' : 'last'
+ var that = this
+
+ if (!$next.length) {
+ if (!this.options.wrap) return
+ $next = this.$element.find('.item')[fallback]()
+ }
+
+ if ($next.hasClass('active')) return (this.sliding = false)
+
+ var relatedTarget = $next[0]
+ var slideEvent = $.Event('slide.bs.carousel', {
+ relatedTarget: relatedTarget,
+ direction: direction
+ })
+ this.$element.trigger(slideEvent)
+ if (slideEvent.isDefaultPrevented()) return
+
+ this.sliding = true
+
+ isCycling && this.pause()
+
+ if (this.$indicators.length) {
+ this.$indicators.find('.active').removeClass('active')
+ var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)])
+ $nextIndicator && $nextIndicator.addClass('active')
+ }
+
+ var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid"
+ if ($.support.transition && this.$element.hasClass('slide')) {
+ $next.addClass(type)
+ $next[0].offsetWidth // force reflow
+ $active.addClass(direction)
+ $next.addClass(direction)
+ $active
+ .one('bsTransitionEnd', function () {
+ $next.removeClass([type, direction].join(' ')).addClass('active')
+ $active.removeClass(['active', direction].join(' '))
+ that.sliding = false
+ setTimeout(function () {
+ that.$element.trigger(slidEvent)
+ }, 0)
+ })
+ .emulateTransitionEnd($active.css('transition-duration').slice(0, -1) * 1000)
+ } else {
+ $active.removeClass('active')
+ $next.addClass('active')
+ this.sliding = false
+ this.$element.trigger(slidEvent)
+ }
+
+ isCycling && this.cycle()
+
+ return this
+ }
+
+
+ // CAROUSEL PLUGIN DEFINITION
+ // ==========================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.carousel')
+ var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)
+ var action = typeof option == 'string' ? option : options.slide
+
+ if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))
+ if (typeof option == 'number') data.to(option)
+ else if (action) data[action]()
+ else if (options.interval) data.pause().cycle()
+ })
+ }
+
+ var old = $.fn.carousel
+
+ $.fn.carousel = Plugin
+ $.fn.carousel.Constructor = Carousel
+
+
+ // CAROUSEL NO CONFLICT
+ // ====================
+
+ $.fn.carousel.noConflict = function () {
+ $.fn.carousel = old
+ return this
+ }
+
+
+ // CAROUSEL DATA-API
+ // =================
+
+ $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {
+ var href
+ var $this = $(this)
+ var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
+ if (!$target.hasClass('carousel')) return
+ var options = $.extend({}, $target.data(), $this.data())
+ var slideIndex = $this.attr('data-slide-to')
+ if (slideIndex) options.interval = false
+
+ Plugin.call($target, options)
+
+ if (slideIndex) {
+ $target.data('bs.carousel').to(slideIndex)
+ }
+
+ e.preventDefault()
+ })
+
+ $(window).on('load', function () {
+ $('[data-ride="carousel"]').each(function () {
+ var $carousel = $(this)
+ Plugin.call($carousel, $carousel.data())
+ })
+ })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: collapse.js v3.2.0
+ * http://getbootstrap.com/javascript/#collapse
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // COLLAPSE PUBLIC CLASS DEFINITION
+ // ================================
+
+ var Collapse = function (element, options) {
+ this.$element = $(element)
+ this.options = $.extend({}, Collapse.DEFAULTS, options)
+ this.transitioning = null
+
+ if (this.options.parent) this.$parent = $(this.options.parent)
+ if (this.options.toggle) this.toggle()
+ }
+
+ Collapse.VERSION = '3.2.0'
+
+ Collapse.DEFAULTS = {
+ toggle: true
+ }
+
+ Collapse.prototype.dimension = function () {
+ var hasWidth = this.$element.hasClass('width')
+ return hasWidth ? 'width' : 'height'
+ }
+
+ Collapse.prototype.show = function () {
+ if (this.transitioning || this.$element.hasClass('in')) return
+
+ var startEvent = $.Event('show.bs.collapse')
+ this.$element.trigger(startEvent)
+ if (startEvent.isDefaultPrevented()) return
+
+ var actives = this.$parent && this.$parent.find('> .panel > .in')
+
+ if (actives && actives.length) {
+ var hasData = actives.data('bs.collapse')
+ if (hasData && hasData.transitioning) return
+ Plugin.call(actives, 'hide')
+ hasData || actives.data('bs.collapse', null)
+ }
+
+ var dimension = this.dimension()
+
+ this.$element
+ .removeClass('collapse')
+ .addClass('collapsing')[dimension](0)
+
+ this.transitioning = 1
+
+ var complete = function () {
+ this.$element
+ .removeClass('collapsing')
+ .addClass('collapse in')[dimension]('')
+ this.transitioning = 0
+ this.$element
+ .trigger('shown.bs.collapse')
+ }
+
+ if (!$.support.transition) return complete.call(this)
+
+ var scrollSize = $.camelCase(['scroll', dimension].join('-'))
+
+ this.$element
+ .one('bsTransitionEnd', $.proxy(complete, this))
+ .emulateTransitionEnd(350)[dimension](this.$element[0][scrollSize])
+ }
+
+ Collapse.prototype.hide = function () {
+ if (this.transitioning || !this.$element.hasClass('in')) return
+
+ var startEvent = $.Event('hide.bs.collapse')
+ this.$element.trigger(startEvent)
+ if (startEvent.isDefaultPrevented()) return
+
+ var dimension = this.dimension()
+
+ this.$element[dimension](this.$element[dimension]())[0].offsetHeight
+
+ this.$element
+ .addClass('collapsing')
+ .removeClass('collapse')
+ .removeClass('in')
+
+ this.transitioning = 1
+
+ var complete = function () {
+ this.transitioning = 0
+ this.$element
+ .trigger('hidden.bs.collapse')
+ .removeClass('collapsing')
+ .addClass('collapse')
+ }
+
+ if (!$.support.transition) return complete.call(this)
+
+ this.$element
+ [dimension](0)
+ .one('bsTransitionEnd', $.proxy(complete, this))
+ .emulateTransitionEnd(350)
+ }
+
+ Collapse.prototype.toggle = function () {
+ this[this.$element.hasClass('in') ? 'hide' : 'show']()
+ }
+
+
+ // COLLAPSE PLUGIN DEFINITION
+ // ==========================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.collapse')
+ var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
+
+ if (!data && options.toggle && option == 'show') option = !option
+ if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ var old = $.fn.collapse
+
+ $.fn.collapse = Plugin
+ $.fn.collapse.Constructor = Collapse
+
+
+ // COLLAPSE NO CONFLICT
+ // ====================
+
+ $.fn.collapse.noConflict = function () {
+ $.fn.collapse = old
+ return this
+ }
+
+
+ // COLLAPSE DATA-API
+ // =================
+
+ $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
+ var href
+ var $this = $(this)
+ var target = $this.attr('data-target')
+ || e.preventDefault()
+ || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
+ var $target = $(target)
+ var data = $target.data('bs.collapse')
+ var option = data ? 'toggle' : $this.data()
+ var parent = $this.attr('data-parent')
+ var $parent = parent && $(parent)
+
+ if (!data || !data.transitioning) {
+ if ($parent) $parent.find('[data-toggle="collapse"][data-parent="' + parent + '"]').not($this).addClass('collapsed')
+ $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
+ }
+
+ Plugin.call($target, option)
+ })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: dropdown.js v3.2.0
+ * http://getbootstrap.com/javascript/#dropdowns
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // DROPDOWN CLASS DEFINITION
+ // =========================
+
+ var backdrop = '.dropdown-backdrop'
+ var toggle = '[data-toggle="dropdown"]'
+ var Dropdown = function (element) {
+ $(element).on('click.bs.dropdown', this.toggle)
+ }
+
+ Dropdown.VERSION = '3.2.0'
+
+ Dropdown.prototype.toggle = function (e) {
+ var $this = $(this)
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ clearMenus()
+
+ if (!isActive) {
+ if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
+ // if mobile we use a backdrop because click events don't delegate
+ $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
+ }
+
+ var relatedTarget = { relatedTarget: this }
+ $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
+
+ if (e.isDefaultPrevented()) return
+
+ $this.trigger('focus')
+
+ $parent
+ .toggleClass('open')
+ .trigger('shown.bs.dropdown', relatedTarget)
+ }
+
+ return false
+ }
+
+ Dropdown.prototype.keydown = function (e) {
+ if (!/(38|40|27)/.test(e.keyCode)) return
+
+ var $this = $(this)
+
+ e.preventDefault()
+ e.stopPropagation()
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ if (!isActive || (isActive && e.keyCode == 27)) {
+ if (e.which == 27) $parent.find(toggle).trigger('focus')
+ return $this.trigger('click')
+ }
+
+ var desc = ' li:not(.divider):visible a'
+ var $items = $parent.find('[role="menu"]' + desc + ', [role="listbox"]' + desc)
+
+ if (!$items.length) return
+
+ var index = $items.index($items.filter(':focus'))
+
+ if (e.keyCode == 38 && index > 0) index-- // up
+ if (e.keyCode == 40 && index < $items.length - 1) index++ // down
+ if (!~index) index = 0
+
+ $items.eq(index).trigger('focus')
+ }
+
+ function clearMenus(e) {
+ if (e && e.which === 3) return
+ $(backdrop).remove()
+ $(toggle).each(function () {
+ var $parent = getParent($(this))
+ var relatedTarget = { relatedTarget: this }
+ if (!$parent.hasClass('open')) return
+ $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
+ if (e.isDefaultPrevented()) return
+ $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
+ })
+ }
+
+ function getParent($this) {
+ var selector = $this.attr('data-target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
+ }
+
+ var $parent = selector && $(selector)
+
+ return $parent && $parent.length ? $parent : $this.parent()
+ }
+
+
+ // DROPDOWN PLUGIN DEFINITION
+ // ==========================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.dropdown')
+
+ if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ var old = $.fn.dropdown
+
+ $.fn.dropdown = Plugin
+ $.fn.dropdown.Constructor = Dropdown
+
+
+ // DROPDOWN NO CONFLICT
+ // ====================
+
+ $.fn.dropdown.noConflict = function () {
+ $.fn.dropdown = old
+ return this
+ }
+
+
+ // APPLY TO STANDARD DROPDOWN ELEMENTS
+ // ===================================
+
+ $(document)
+ .on('click.bs.dropdown.data-api', clearMenus)
+ .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+ .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
+ .on('keydown.bs.dropdown.data-api', toggle + ', [role="menu"], [role="listbox"]', Dropdown.prototype.keydown)
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: modal.js v3.2.0
+ * http://getbootstrap.com/javascript/#modals
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // MODAL CLASS DEFINITION
+ // ======================
+
+ var Modal = function (element, options) {
+ this.options = options
+ this.$body = $(document.body)
+ this.$element = $(element)
+ this.$backdrop =
+ this.isShown = null
+ this.scrollbarWidth = 0
+
+ if (this.options.remote) {
+ this.$element
+ .find('.modal-content')
+ .load(this.options.remote, $.proxy(function () {
+ this.$element.trigger('loaded.bs.modal')
+ }, this))
+ }
+ }
+
+ Modal.VERSION = '3.2.0'
+
+ Modal.DEFAULTS = {
+ backdrop: true,
+ keyboard: true,
+ show: true
+ }
+
+ Modal.prototype.toggle = function (_relatedTarget) {
+ return this.isShown ? this.hide() : this.show(_relatedTarget)
+ }
+
+ Modal.prototype.show = function (_relatedTarget) {
+ var that = this
+ var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
+
+ this.$element.trigger(e)
+
+ if (this.isShown || e.isDefaultPrevented()) return
+
+ this.isShown = true
+
+ this.checkScrollbar()
+ this.$body.addClass('modal-open')
+
+ this.setScrollbar()
+ this.escape()
+
+ this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
+
+ this.backdrop(function () {
+ var transition = $.support.transition && that.$element.hasClass('fade')
+
+ if (!that.$element.parent().length) {
+ that.$element.appendTo(that.$body) // don't move modals dom position
+ }
+
+ that.$element
+ .show()
+ .scrollTop(0)
+
+ if (transition) {
+ that.$element[0].offsetWidth // force reflow
+ }
+
+ that.$element
+ .addClass('in')
+ .attr('aria-hidden', false)
+
+ that.enforceFocus()
+
+ var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
+
+ transition ?
+ that.$element.find('.modal-dialog') // wait for modal to slide in
+ .one('bsTransitionEnd', function () {
+ that.$element.trigger('focus').trigger(e)
+ })
+ .emulateTransitionEnd(300) :
+ that.$element.trigger('focus').trigger(e)
+ })
+ }
+
+ Modal.prototype.hide = function (e) {
+ if (e) e.preventDefault()
+
+ e = $.Event('hide.bs.modal')
+
+ this.$element.trigger(e)
+
+ if (!this.isShown || e.isDefaultPrevented()) return
+
+ this.isShown = false
+
+ this.$body.removeClass('modal-open')
+
+ this.resetScrollbar()
+ this.escape()
+
+ $(document).off('focusin.bs.modal')
+
+ this.$element
+ .removeClass('in')
+ .attr('aria-hidden', true)
+ .off('click.dismiss.bs.modal')
+
+ $.support.transition && this.$element.hasClass('fade') ?
+ this.$element
+ .one('bsTransitionEnd', $.proxy(this.hideModal, this))
+ .emulateTransitionEnd(300) :
+ this.hideModal()
+ }
+
+ Modal.prototype.enforceFocus = function () {
+ $(document)
+ .off('focusin.bs.modal') // guard against infinite focus loop
+ .on('focusin.bs.modal', $.proxy(function (e) {
+ if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
+ this.$element.trigger('focus')
+ }
+ }, this))
+ }
+
+ Modal.prototype.escape = function () {
+ if (this.isShown && this.options.keyboard) {
+ this.$element.on('keyup.dismiss.bs.modal', $.proxy(function (e) {
+ e.which == 27 && this.hide()
+ }, this))
+ } else if (!this.isShown) {
+ this.$element.off('keyup.dismiss.bs.modal')
+ }
+ }
+
+ Modal.prototype.hideModal = function () {
+ var that = this
+ this.$element.hide()
+ this.backdrop(function () {
+ that.$element.trigger('hidden.bs.modal')
+ })
+ }
+
+ Modal.prototype.removeBackdrop = function () {
+ this.$backdrop && this.$backdrop.remove()
+ this.$backdrop = null
+ }
+
+ Modal.prototype.backdrop = function (callback) {
+ var that = this
+ var animate = this.$element.hasClass('fade') ? 'fade' : ''
+
+ if (this.isShown && this.options.backdrop) {
+ var doAnimate = $.support.transition && animate
+
+ this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
+ .appendTo(this.$body)
+
+ this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
+ if (e.target !== e.currentTarget) return
+ this.options.backdrop == 'static'
+ ? this.$element[0].focus.call(this.$element[0])
+ : this.hide.call(this)
+ }, this))
+
+ if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
+
+ this.$backdrop.addClass('in')
+
+ if (!callback) return
+
+ doAnimate ?
+ this.$backdrop
+ .one('bsTransitionEnd', callback)
+ .emulateTransitionEnd(150) :
+ callback()
+
+ } else if (!this.isShown && this.$backdrop) {
+ this.$backdrop.removeClass('in')
+
+ var callbackRemove = function () {
+ that.removeBackdrop()
+ callback && callback()
+ }
+ $.support.transition && this.$element.hasClass('fade') ?
+ this.$backdrop
+ .one('bsTransitionEnd', callbackRemove)
+ .emulateTransitionEnd(150) :
+ callbackRemove()
+
+ } else if (callback) {
+ callback()
+ }
+ }
+
+ Modal.prototype.checkScrollbar = function () {
+ if (document.body.clientWidth >= window.innerWidth) return
+ this.scrollbarWidth = this.scrollbarWidth || this.measureScrollbar()
+ }
+
+ Modal.prototype.setScrollbar = function () {
+ var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
+ if (this.scrollbarWidth) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
+ }
+
+ Modal.prototype.resetScrollbar = function () {
+ this.$body.css('padding-right', '')
+ }
+
+ Modal.prototype.measureScrollbar = function () { // thx walsh
+ var scrollDiv = document.createElement('div')
+ scrollDiv.className = 'modal-scrollbar-measure'
+ this.$body.append(scrollDiv)
+ var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
+ this.$body[0].removeChild(scrollDiv)
+ return scrollbarWidth
+ }
+
+
+ // MODAL PLUGIN DEFINITION
+ // =======================
+
+ function Plugin(option, _relatedTarget) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.modal')
+ var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
+
+ if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
+ if (typeof option == 'string') data[option](_relatedTarget)
+ else if (options.show) data.show(_relatedTarget)
+ })
+ }
+
+ var old = $.fn.modal
+
+ $.fn.modal = Plugin
+ $.fn.modal.Constructor = Modal
+
+
+ // MODAL NO CONFLICT
+ // =================
+
+ $.fn.modal.noConflict = function () {
+ $.fn.modal = old
+ return this
+ }
+
+
+ // MODAL DATA-API
+ // ==============
+
+ $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
+ var $this = $(this)
+ var href = $this.attr('href')
+ var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
+ var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
+
+ if ($this.is('a')) e.preventDefault()
+
+ $target.one('show.bs.modal', function (showEvent) {
+ if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
+ $target.one('hidden.bs.modal', function () {
+ $this.is(':visible') && $this.trigger('focus')
+ })
+ })
+ Plugin.call($target, option, this)
+ })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: tooltip.js v3.2.0
+ * http://getbootstrap.com/javascript/#tooltip
+ * Inspired by the original jQuery.tipsy by Jason Frame
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // TOOLTIP PUBLIC CLASS DEFINITION
+ // ===============================
+
+ var Tooltip = function (element, options) {
+ this.type =
+ this.options =
+ this.enabled =
+ this.timeout =
+ this.hoverState =
+ this.$element = null
+
+ this.init('tooltip', element, options)
+ }
+
+ Tooltip.VERSION = '3.2.0'
+
+ Tooltip.DEFAULTS = {
+ animation: true,
+ placement: 'top',
+ selector: false,
+ template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
+ trigger: 'hover focus',
+ title: '',
+ delay: 0,
+ html: false,
+ container: false,
+ viewport: {
+ selector: 'body',
+ padding: 0
+ }
+ }
+
+ Tooltip.prototype.init = function (type, element, options) {
+ this.enabled = true
+ this.type = type
+ this.$element = $(element)
+ this.options = this.getOptions(options)
+ this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport)
+
+ var triggers = this.options.trigger.split(' ')
+
+ for (var i = triggers.length; i--;) {
+ var trigger = triggers[i]
+
+ if (trigger == 'click') {
+ this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
+ } else if (trigger != 'manual') {
+ var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
+ var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
+
+ this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
+ this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
+ }
+ }
+
+ this.options.selector ?
+ (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
+ this.fixTitle()
+ }
+
+ Tooltip.prototype.getDefaults = function () {
+ return Tooltip.DEFAULTS
+ }
+
+ Tooltip.prototype.getOptions = function (options) {
+ options = $.extend({}, this.getDefaults(), this.$element.data(), options)
+
+ if (options.delay && typeof options.delay == 'number') {
+ options.delay = {
+ show: options.delay,
+ hide: options.delay
+ }
+ }
+
+ return options
+ }
+
+ Tooltip.prototype.getDelegateOptions = function () {
+ var options = {}
+ var defaults = this.getDefaults()
+
+ this._options && $.each(this._options, function (key, value) {
+ if (defaults[key] != value) options[key] = value
+ })
+
+ return options
+ }
+
+ Tooltip.prototype.enter = function (obj) {
+ var self = obj instanceof this.constructor ?
+ obj : $(obj.currentTarget).data('bs.' + this.type)
+
+ if (!self) {
+ self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
+ $(obj.currentTarget).data('bs.' + this.type, self)
+ }
+
+ clearTimeout(self.timeout)
+
+ self.hoverState = 'in'
+
+ if (!self.options.delay || !self.options.delay.show) return self.show()
+
+ self.timeout = setTimeout(function () {
+ if (self.hoverState == 'in') self.show()
+ }, self.options.delay.show)
+ }
+
+ Tooltip.prototype.leave = function (obj) {
+ var self = obj instanceof this.constructor ?
+ obj : $(obj.currentTarget).data('bs.' + this.type)
+
+ if (!self) {
+ self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
+ $(obj.currentTarget).data('bs.' + this.type, self)
+ }
+
+ clearTimeout(self.timeout)
+
+ self.hoverState = 'out'
+
+ if (!self.options.delay || !self.options.delay.hide) return self.hide()
+
+ self.timeout = setTimeout(function () {
+ if (self.hoverState == 'out') self.hide()
+ }, self.options.delay.hide)
+ }
+
+ Tooltip.prototype.show = function () {
+ var e = $.Event('show.bs.' + this.type)
+
+ if (this.hasContent() && this.enabled) {
+ this.$element.trigger(e)
+
+ var inDom = $.contains(document.documentElement, this.$element[0])
+ if (e.isDefaultPrevented() || !inDom) return
+ var that = this
+
+ var $tip = this.tip()
+
+ var tipId = this.getUID(this.type)
+
+ this.setContent()
+ $tip.attr('id', tipId)
+ this.$element.attr('aria-describedby', tipId)
+
+ if (this.options.animation) $tip.addClass('fade')
+
+ var placement = typeof this.options.placement == 'function' ?
+ this.options.placement.call(this, $tip[0], this.$element[0]) :
+ this.options.placement
+
+ var autoToken = /\s?auto?\s?/i
+ var autoPlace = autoToken.test(placement)
+ if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
+
+ $tip
+ .detach()
+ .css({ top: 0, left: 0, display: 'block' })
+ .addClass(placement)
+ .data('bs.' + this.type, this)
+
+ this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
+
+ var pos = this.getPosition()
+ var actualWidth = $tip[0].offsetWidth
+ var actualHeight = $tip[0].offsetHeight
+
+ if (autoPlace) {
+ var orgPlacement = placement
+ var $parent = this.$element.parent()
+ var parentDim = this.getPosition($parent)
+
+ placement = placement == 'bottom' && pos.top + pos.height + actualHeight - parentDim.scroll > parentDim.height ? 'top' :
+ placement == 'top' && pos.top - parentDim.scroll - actualHeight < 0 ? 'bottom' :
+ placement == 'right' && pos.right + actualWidth > parentDim.width ? 'left' :
+ placement == 'left' && pos.left - actualWidth < parentDim.left ? 'right' :
+ placement
+
+ $tip
+ .removeClass(orgPlacement)
+ .addClass(placement)
+ }
+
+ var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
+
+ this.applyPlacement(calculatedOffset, placement)
+
+ var complete = function () {
+ that.$element.trigger('shown.bs.' + that.type)
+ that.hoverState = null
+ }
+
+ $.support.transition && this.$tip.hasClass('fade') ?
+ $tip
+ .one('bsTransitionEnd', complete)
+ .emulateTransitionEnd(150) :
+ complete()
+ }
+ }
+
+ Tooltip.prototype.applyPlacement = function (offset, placement) {
+ var $tip = this.tip()
+ var width = $tip[0].offsetWidth
+ var height = $tip[0].offsetHeight
+
+ // manually read margins because getBoundingClientRect includes difference
+ var marginTop = parseInt($tip.css('margin-top'), 10)
+ var marginLeft = parseInt($tip.css('margin-left'), 10)
+
+ // we must check for NaN for ie 8/9
+ if (isNaN(marginTop)) marginTop = 0
+ if (isNaN(marginLeft)) marginLeft = 0
+
+ offset.top = offset.top + marginTop
+ offset.left = offset.left + marginLeft
+
+ // $.fn.offset doesn't round pixel values
+ // so we use setOffset directly with our own function B-0
+ $.offset.setOffset($tip[0], $.extend({
+ using: function (props) {
+ $tip.css({
+ top: Math.round(props.top),
+ left: Math.round(props.left)
+ })
+ }
+ }, offset), 0)
+
+ $tip.addClass('in')
+
+ // check to see if placing tip in new offset caused the tip to resize itself
+ var actualWidth = $tip[0].offsetWidth
+ var actualHeight = $tip[0].offsetHeight
+
+ if (placement == 'top' && actualHeight != height) {
+ offset.top = offset.top + height - actualHeight
+ }
+
+ var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
+
+ if (delta.left) offset.left += delta.left
+ else offset.top += delta.top
+
+ var arrowDelta = delta.left ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
+ var arrowPosition = delta.left ? 'left' : 'top'
+ var arrowOffsetPosition = delta.left ? 'offsetWidth' : 'offsetHeight'
+
+ $tip.offset(offset)
+ this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], arrowPosition)
+ }
+
+ Tooltip.prototype.replaceArrow = function (delta, dimension, position) {
+ this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + '%') : '')
+ }
+
+ Tooltip.prototype.setContent = function () {
+ var $tip = this.tip()
+ var title = this.getTitle()
+
+ $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
+ $tip.removeClass('fade in top bottom left right')
+ }
+
+ Tooltip.prototype.hide = function () {
+ var that = this
+ var $tip = this.tip()
+ var e = $.Event('hide.bs.' + this.type)
+
+ this.$element.removeAttr('aria-describedby')
+
+ function complete() {
+ if (that.hoverState != 'in') $tip.detach()
+ that.$element.trigger('hidden.bs.' + that.type)
+ }
+
+ this.$element.trigger(e)
+
+ if (e.isDefaultPrevented()) return
+
+ $tip.removeClass('in')
+
+ $.support.transition && this.$tip.hasClass('fade') ?
+ $tip
+ .one('bsTransitionEnd', complete)
+ .emulateTransitionEnd(150) :
+ complete()
+
+ this.hoverState = null
+
+ return this
+ }
+
+ Tooltip.prototype.fixTitle = function () {
+ var $e = this.$element
+ if ($e.attr('title') || typeof ($e.attr('data-original-title')) != 'string') {
+ $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
+ }
+ }
+
+ Tooltip.prototype.hasContent = function () {
+ return this.getTitle()
+ }
+
+ Tooltip.prototype.getPosition = function ($element) {
+ $element = $element || this.$element
+ var el = $element[0]
+ var isBody = el.tagName == 'BODY'
+ return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : null, {
+ scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop(),
+ width: isBody ? $(window).width() : $element.outerWidth(),
+ height: isBody ? $(window).height() : $element.outerHeight()
+ }, isBody ? { top: 0, left: 0 } : $element.offset())
+ }
+
+ Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
+ return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
+ placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
+ placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
+ /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
+
+ }
+
+ Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
+ var delta = { top: 0, left: 0 }
+ if (!this.$viewport) return delta
+
+ var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
+ var viewportDimensions = this.getPosition(this.$viewport)
+
+ if (/right|left/.test(placement)) {
+ var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll
+ var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
+ if (topEdgeOffset < viewportDimensions.top) { // top overflow
+ delta.top = viewportDimensions.top - topEdgeOffset
+ } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
+ delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
+ }
+ } else {
+ var leftEdgeOffset = pos.left - viewportPadding
+ var rightEdgeOffset = pos.left + viewportPadding + actualWidth
+ if (leftEdgeOffset < viewportDimensions.left) { // left overflow
+ delta.left = viewportDimensions.left - leftEdgeOffset
+ } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
+ delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
+ }
+ }
+
+ return delta
+ }
+
+ Tooltip.prototype.getTitle = function () {
+ var title
+ var $e = this.$element
+ var o = this.options
+
+ title = $e.attr('data-original-title')
+ || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
+
+ return title
+ }
+
+ Tooltip.prototype.getUID = function (prefix) {
+ do prefix += ~~(Math.random() * 1000000)
+ while (document.getElementById(prefix))
+ return prefix
+ }
+
+ Tooltip.prototype.tip = function () {
+ return (this.$tip = this.$tip || $(this.options.template))
+ }
+
+ Tooltip.prototype.arrow = function () {
+ return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
+ }
+
+ Tooltip.prototype.validate = function () {
+ if (!this.$element[0].parentNode) {
+ this.hide()
+ this.$element = null
+ this.options = null
+ }
+ }
+
+ Tooltip.prototype.enable = function () {
+ this.enabled = true
+ }
+
+ Tooltip.prototype.disable = function () {
+ this.enabled = false
+ }
+
+ Tooltip.prototype.toggleEnabled = function () {
+ this.enabled = !this.enabled
+ }
+
+ Tooltip.prototype.toggle = function (e) {
+ var self = this
+ if (e) {
+ self = $(e.currentTarget).data('bs.' + this.type)
+ if (!self) {
+ self = new this.constructor(e.currentTarget, this.getDelegateOptions())
+ $(e.currentTarget).data('bs.' + this.type, self)
+ }
+ }
+
+ self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
+ }
+
+ Tooltip.prototype.destroy = function () {
+ clearTimeout(this.timeout)
+ this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)
+ }
+
+
+ // TOOLTIP PLUGIN DEFINITION
+ // =========================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.tooltip')
+ var options = typeof option == 'object' && option
+
+ if (!data && option == 'destroy') return
+ if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ var old = $.fn.tooltip
+
+ $.fn.tooltip = Plugin
+ $.fn.tooltip.Constructor = Tooltip
+
+
+ // TOOLTIP NO CONFLICT
+ // ===================
+
+ $.fn.tooltip.noConflict = function () {
+ $.fn.tooltip = old
+ return this
+ }
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: popover.js v3.2.0
+ * http://getbootstrap.com/javascript/#popovers
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // POPOVER PUBLIC CLASS DEFINITION
+ // ===============================
+
+ var Popover = function (element, options) {
+ this.init('popover', element, options)
+ }
+
+ if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
+
+ Popover.VERSION = '3.2.0'
+
+ Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
+ placement: 'right',
+ trigger: 'click',
+ content: '',
+ template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
+ })
+
+
+ // NOTE: POPOVER EXTENDS tooltip.js
+ // ================================
+
+ Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
+
+ Popover.prototype.constructor = Popover
+
+ Popover.prototype.getDefaults = function () {
+ return Popover.DEFAULTS
+ }
+
+ Popover.prototype.setContent = function () {
+ var $tip = this.tip()
+ var title = this.getTitle()
+ var content = this.getContent()
+
+ $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
+ $tip.find('.popover-content').empty()[ // we use append for html objects to maintain js events
+ this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
+ ](content)
+
+ $tip.removeClass('fade top bottom left right in')
+
+ // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
+ // this manually by checking the contents.
+ if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
+ }
+
+ Popover.prototype.hasContent = function () {
+ return this.getTitle() || this.getContent()
+ }
+
+ Popover.prototype.getContent = function () {
+ var $e = this.$element
+ var o = this.options
+
+ return $e.attr('data-content')
+ || (typeof o.content == 'function' ?
+ o.content.call($e[0]) :
+ o.content)
+ }
+
+ Popover.prototype.arrow = function () {
+ return (this.$arrow = this.$arrow || this.tip().find('.arrow'))
+ }
+
+ Popover.prototype.tip = function () {
+ if (!this.$tip) this.$tip = $(this.options.template)
+ return this.$tip
+ }
+
+
+ // POPOVER PLUGIN DEFINITION
+ // =========================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.popover')
+ var options = typeof option == 'object' && option
+
+ if (!data && option == 'destroy') return
+ if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ var old = $.fn.popover
+
+ $.fn.popover = Plugin
+ $.fn.popover.Constructor = Popover
+
+
+ // POPOVER NO CONFLICT
+ // ===================
+
+ $.fn.popover.noConflict = function () {
+ $.fn.popover = old
+ return this
+ }
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: scrollspy.js v3.2.0
+ * http://getbootstrap.com/javascript/#scrollspy
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // SCROLLSPY CLASS DEFINITION
+ // ==========================
+
+ function ScrollSpy(element, options) {
+ var process = $.proxy(this.process, this)
+
+ this.$body = $('body')
+ this.$scrollElement = $(element).is('body') ? $(window) : $(element)
+ this.options = $.extend({}, ScrollSpy.DEFAULTS, options)
+ this.selector = (this.options.target || '') + ' .nav li > a'
+ this.offsets = []
+ this.targets = []
+ this.activeTarget = null
+ this.scrollHeight = 0
+
+ this.$scrollElement.on('scroll.bs.scrollspy', process)
+ this.refresh()
+ this.process()
+ }
+
+ ScrollSpy.VERSION = '3.2.0'
+
+ ScrollSpy.DEFAULTS = {
+ offset: 10
+ }
+
+ ScrollSpy.prototype.getScrollHeight = function () {
+ return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
+ }
+
+ ScrollSpy.prototype.refresh = function () {
+ var offsetMethod = 'offset'
+ var offsetBase = 0
+
+ if (!$.isWindow(this.$scrollElement[0])) {
+ offsetMethod = 'position'
+ offsetBase = this.$scrollElement.scrollTop()
+ }
+
+ this.offsets = []
+ this.targets = []
+ this.scrollHeight = this.getScrollHeight()
+
+ var self = this
+
+ this.$body
+ .find(this.selector)
+ .map(function () {
+ var $el = $(this)
+ var href = $el.data('target') || $el.attr('href')
+ var $href = /^#./.test(href) && $(href)
+
+ return ($href
+ && $href.length
+ && $href.is(':visible')
+ && [[$href[offsetMethod]().top + offsetBase, href]]) || null
+ })
+ .sort(function (a, b) { return a[0] - b[0] })
+ .each(function () {
+ self.offsets.push(this[0])
+ self.targets.push(this[1])
+ })
+ }
+
+ ScrollSpy.prototype.process = function () {
+ var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
+ var scrollHeight = this.getScrollHeight()
+ var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height()
+ var offsets = this.offsets
+ var targets = this.targets
+ var activeTarget = this.activeTarget
+ var i
+
+ if (this.scrollHeight != scrollHeight) {
+ this.refresh()
+ }
+
+ if (scrollTop >= maxScroll) {
+ return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
+ }
+
+ if (activeTarget && scrollTop <= offsets[0]) {
+ return activeTarget != (i = targets[0]) && this.activate(i)
+ }
+
+ for (i = offsets.length; i--;) {
+ activeTarget != targets[i]
+ && scrollTop >= offsets[i]
+ && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
+ && this.activate(targets[i])
+ }
+ }
+
+ ScrollSpy.prototype.activate = function (target) {
+ this.activeTarget = target
+
+ $(this.selector)
+ .parentsUntil(this.options.target, '.active')
+ .removeClass('active')
+
+ var selector = this.selector +
+ '[data-target="' + target + '"],' +
+ this.selector + '[href="' + target + '"]'
+
+ var active = $(selector)
+ .parents('li')
+ .addClass('active')
+
+ if (active.parent('.dropdown-menu').length) {
+ active = active
+ .closest('li.dropdown')
+ .addClass('active')
+ }
+
+ active.trigger('activate.bs.scrollspy')
+ }
+
+
+ // SCROLLSPY PLUGIN DEFINITION
+ // ===========================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.scrollspy')
+ var options = typeof option == 'object' && option
+
+ if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ var old = $.fn.scrollspy
+
+ $.fn.scrollspy = Plugin
+ $.fn.scrollspy.Constructor = ScrollSpy
+
+
+ // SCROLLSPY NO CONFLICT
+ // =====================
+
+ $.fn.scrollspy.noConflict = function () {
+ $.fn.scrollspy = old
+ return this
+ }
+
+
+ // SCROLLSPY DATA-API
+ // ==================
+
+ $(window).on('load.bs.scrollspy.data-api', function () {
+ $('[data-spy="scroll"]').each(function () {
+ var $spy = $(this)
+ Plugin.call($spy, $spy.data())
+ })
+ })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: tab.js v3.2.0
+ * http://getbootstrap.com/javascript/#tabs
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // TAB CLASS DEFINITION
+ // ====================
+
+ var Tab = function (element) {
+ this.element = $(element)
+ }
+
+ Tab.VERSION = '3.2.0'
+
+ Tab.prototype.show = function () {
+ var $this = this.element
+ var $ul = $this.closest('ul:not(.dropdown-menu)')
+ var selector = $this.data('target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
+ }
+
+ if ($this.parent('li').hasClass('active')) return
+
+ var previous = $ul.find('.active:last a')[0]
+ var e = $.Event('show.bs.tab', {
+ relatedTarget: previous
+ })
+
+ $this.trigger(e)
+
+ if (e.isDefaultPrevented()) return
+
+ var $target = $(selector)
+
+ this.activate($this.closest('li'), $ul)
+ this.activate($target, $target.parent(), function () {
+ $this.trigger({
+ type: 'shown.bs.tab',
+ relatedTarget: previous
+ })
+ })
+ }
+
+ Tab.prototype.activate = function (element, container, callback) {
+ var $active = container.find('> .active')
+ var transition = callback
+ && $.support.transition
+ && $active.hasClass('fade')
+
+ function next() {
+ $active
+ .removeClass('active')
+ .find('> .dropdown-menu > .active')
+ .removeClass('active')
+
+ element.addClass('active')
+
+ if (transition) {
+ element[0].offsetWidth // reflow for transition
+ element.addClass('in')
+ } else {
+ element.removeClass('fade')
+ }
+
+ if (element.parent('.dropdown-menu')) {
+ element.closest('li.dropdown').addClass('active')
+ }
+
+ callback && callback()
+ }
+
+ transition ?
+ $active
+ .one('bsTransitionEnd', next)
+ .emulateTransitionEnd(150) :
+ next()
+
+ $active.removeClass('in')
+ }
+
+
+ // TAB PLUGIN DEFINITION
+ // =====================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.tab')
+
+ if (!data) $this.data('bs.tab', (data = new Tab(this)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ var old = $.fn.tab
+
+ $.fn.tab = Plugin
+ $.fn.tab.Constructor = Tab
+
+
+ // TAB NO CONFLICT
+ // ===============
+
+ $.fn.tab.noConflict = function () {
+ $.fn.tab = old
+ return this
+ }
+
+
+ // TAB DATA-API
+ // ============
+
+ $(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
+ e.preventDefault()
+ Plugin.call($(this), 'show')
+ })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: affix.js v3.2.0
+ * http://getbootstrap.com/javascript/#affix
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // AFFIX CLASS DEFINITION
+ // ======================
+
+ var Affix = function (element, options) {
+ this.options = $.extend({}, Affix.DEFAULTS, options)
+
+ this.$target = $(this.options.target)
+ .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
+ .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
+
+ this.$element = $(element)
+ this.affixed =
+ this.unpin =
+ this.pinnedOffset = null
+
+ this.checkPosition()
+ }
+
+ Affix.VERSION = '3.2.0'
+
+ Affix.RESET = 'affix affix-top affix-bottom'
+
+ Affix.DEFAULTS = {
+ offset: 0,
+ target: window
+ }
+
+ Affix.prototype.getPinnedOffset = function () {
+ if (this.pinnedOffset) return this.pinnedOffset
+ this.$element.removeClass(Affix.RESET).addClass('affix')
+ var scrollTop = this.$target.scrollTop()
+ var position = this.$element.offset()
+ return (this.pinnedOffset = position.top - scrollTop)
+ }
+
+ Affix.prototype.checkPositionWithEventLoop = function () {
+ setTimeout($.proxy(this.checkPosition, this), 1)
+ }
+
+ Affix.prototype.checkPosition = function () {
+ if (!this.$element.is(':visible')) return
+
+ var scrollHeight = $(document).height()
+ var scrollTop = this.$target.scrollTop()
+ var position = this.$element.offset()
+ var offset = this.options.offset
+ var offsetTop = offset.top
+ var offsetBottom = offset.bottom
+
+ if (typeof offset != 'object') offsetBottom = offsetTop = offset
+ if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element)
+ if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)
+
+ var affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ? false :
+ offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' :
+ offsetTop != null && (scrollTop <= offsetTop) ? 'top' : false
+
+ if (this.affixed === affix) return
+ if (this.unpin != null) this.$element.css('top', '')
+
+ var affixType = 'affix' + (affix ? '-' + affix : '')
+ var e = $.Event(affixType + '.bs.affix')
+
+ this.$element.trigger(e)
+
+ if (e.isDefaultPrevented()) return
+
+ this.affixed = affix
+ this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
+
+ this.$element
+ .removeClass(Affix.RESET)
+ .addClass(affixType)
+ .trigger($.Event(affixType.replace('affix', 'affixed')))
+
+ if (affix == 'bottom') {
+ this.$element.offset({
+ top: scrollHeight - this.$element.height() - offsetBottom
+ })
+ }
+ }
+
+
+ // AFFIX PLUGIN DEFINITION
+ // =======================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.affix')
+ var options = typeof option == 'object' && option
+
+ if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ var old = $.fn.affix
+
+ $.fn.affix = Plugin
+ $.fn.affix.Constructor = Affix
+
+
+ // AFFIX NO CONFLICT
+ // =================
+
+ $.fn.affix.noConflict = function () {
+ $.fn.affix = old
+ return this
+ }
+
+
+ // AFFIX DATA-API
+ // ==============
+
+ $(window).on('load', function () {
+ $('[data-spy="affix"]').each(function () {
+ var $spy = $(this)
+ var data = $spy.data()
+
+ data.offset = data.offset || {}
+
+ if (data.offsetBottom) data.offset.bottom = data.offsetBottom
+ if (data.offsetTop) data.offset.top = data.offsetTop
+
+ Plugin.call($spy, data)
+ })
+ })
+
+}(jQuery);
diff --git a/container-search-gui/src/main/resources/gui/js/bootstrap.min.js b/container-search-gui/src/main/resources/gui/js/bootstrap.min.js
new file mode 100644
index 00000000000..7c1561a8b96
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/js/bootstrap.min.js
@@ -0,0 +1,6 @@
+/*!
+ * Bootstrap v3.2.0 (http://getbootstrap.com)
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.2.0",d.prototype.close=function(b){function c(){f.detach().trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one("bsTransitionEnd",c).emulateTransitionEnd(150):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.2.0",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),d[e](null==f[b]?this.options[b]:f[b]),setTimeout(a.proxy(function(){"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b).on("keydown.bs.carousel",a.proxy(this.keydown,this)),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.2.0",c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},c.prototype.keydown=function(a){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.to=function(b){var c=this,d=this.getItemIndex(this.$active=this.$element.find(".item.active"));return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}if(e.hasClass("active"))return this.sliding=!1;var j=e[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:g});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,f&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(e)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:g});return a.support.transition&&this.$element.hasClass("slide")?(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one("bsTransitionEnd",function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(1e3*d.css("transition-duration").slice(0,-1))):(d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger(m)),f&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b);!e&&f.toggle&&"show"==b&&(b=!b),e||d.data("bs.collapse",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};c.VERSION="3.2.0",c.DEFAULTS={toggle:!0},c.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},c.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var c=a.Event("show.bs.collapse");if(this.$element.trigger(c),!c.isDefaultPrevented()){var d=this.$parent&&this.$parent.find("> .panel > .in");if(d&&d.length){var e=d.data("bs.collapse");if(e&&e.transitioning)return;b.call(d,"hide"),e||d.data("bs.collapse",null)}var f=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[f](0),this.transitioning=1;var g=function(){this.$element.removeClass("collapsing").addClass("collapse in")[f](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return g.call(this);var h=a.camelCase(["scroll",f].join("-"));this.$element.one("bsTransitionEnd",a.proxy(g,this)).emulateTransitionEnd(350)[f](this.$element[0][h])}}},c.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(d,this)).emulateTransitionEnd(350):d.call(this)}}},c.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var d=a.fn.collapse;a.fn.collapse=b,a.fn.collapse.Constructor=c,a.fn.collapse.noConflict=function(){return a.fn.collapse=d,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(c){var d,e=a(this),f=e.attr("data-target")||c.preventDefault()||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),g=a(f),h=g.data("bs.collapse"),i=h?"toggle":e.data(),j=e.attr("data-parent"),k=j&&a(j);h&&h.transitioning||(k&&k.find('[data-toggle="collapse"][data-parent="'+j+'"]').not(e).addClass("collapsed"),e[g.hasClass("in")?"addClass":"removeClass"]("collapsed")),b.call(g,i)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=c(a(this)),e={relatedTarget:this};d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown",e)),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown",e))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.2.0",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('<div class="dropdown-backdrop"/>').insertAfter(a(this)).on("click",b);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(b){if(/(38|40|27)/.test(b.keyCode)){var d=a(this);if(b.preventDefault(),b.stopPropagation(),!d.is(".disabled, :disabled")){var e=c(d),g=e.hasClass("open");if(!g||g&&27==b.keyCode)return 27==b.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.divider):visible a",i=e.find('[role="menu"]'+h+', [role="listbox"]'+h);if(i.length){var j=i.index(i.filter(":focus"));38==b.keyCode&&j>0&&j--,40==b.keyCode&&j<i.length-1&&j++,~j||(j=0),i.eq(j).trigger("focus")}}}};var h=a.fn.dropdown;a.fn.dropdown=d,a.fn.dropdown.Constructor=g,a.fn.dropdown.noConflict=function(){return a.fn.dropdown=h,this},a(document).on("click.bs.dropdown.data-api",b).on("click.bs.dropdown.data-api",".dropdown form",function(a){a.stopPropagation()}).on("click.bs.dropdown.data-api",f,g.prototype.toggle).on("keydown.bs.dropdown.data-api",f+', [role="menu"], [role="listbox"]',g.prototype.keydown)}(jQuery),+function(a){"use strict";function b(b,d){return this.each(function(){var e=a(this),f=e.data("bs.modal"),g=a.extend({},c.DEFAULTS,e.data(),"object"==typeof b&&b);f||e.data("bs.modal",f=new c(this,g)),"string"==typeof b?f[b](d):g.show&&f.show(d)})}var c=function(b,c){this.options=c,this.$body=a(document.body),this.$element=a(b),this.$backdrop=this.isShown=null,this.scrollbarWidth=0,this.options.remote&&this.$element.find(".modal-content").load(this.options.remote,a.proxy(function(){this.$element.trigger("loaded.bs.modal")},this))};c.VERSION="3.2.0",c.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},c.prototype.toggle=function(a){return this.isShown?this.hide():this.show(a)},c.prototype.show=function(b){var c=this,d=a.Event("show.bs.modal",{relatedTarget:b});this.$element.trigger(d),this.isShown||d.isDefaultPrevented()||(this.isShown=!0,this.checkScrollbar(),this.$body.addClass("modal-open"),this.setScrollbar(),this.escape(),this.$element.on("click.dismiss.bs.modal",'[data-dismiss="modal"]',a.proxy(this.hide,this)),this.backdrop(function(){var d=a.support.transition&&c.$element.hasClass("fade");c.$element.parent().length||c.$element.appendTo(c.$body),c.$element.show().scrollTop(0),d&&c.$element[0].offsetWidth,c.$element.addClass("in").attr("aria-hidden",!1),c.enforceFocus();var e=a.Event("shown.bs.modal",{relatedTarget:b});d?c.$element.find(".modal-dialog").one("bsTransitionEnd",function(){c.$element.trigger("focus").trigger(e)}).emulateTransitionEnd(300):c.$element.trigger("focus").trigger(e)}))},c.prototype.hide=function(b){b&&b.preventDefault(),b=a.Event("hide.bs.modal"),this.$element.trigger(b),this.isShown&&!b.isDefaultPrevented()&&(this.isShown=!1,this.$body.removeClass("modal-open"),this.resetScrollbar(),this.escape(),a(document).off("focusin.bs.modal"),this.$element.removeClass("in").attr("aria-hidden",!0).off("click.dismiss.bs.modal"),a.support.transition&&this.$element.hasClass("fade")?this.$element.one("bsTransitionEnd",a.proxy(this.hideModal,this)).emulateTransitionEnd(300):this.hideModal())},c.prototype.enforceFocus=function(){a(document).off("focusin.bs.modal").on("focusin.bs.modal",a.proxy(function(a){this.$element[0]===a.target||this.$element.has(a.target).length||this.$element.trigger("focus")},this))},c.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.bs.modal",a.proxy(function(a){27==a.which&&this.hide()},this)):this.isShown||this.$element.off("keyup.dismiss.bs.modal")},c.prototype.hideModal=function(){var a=this;this.$element.hide(),this.backdrop(function(){a.$element.trigger("hidden.bs.modal")})},c.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},c.prototype.backdrop=function(b){var c=this,d=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var e=a.support.transition&&d;if(this.$backdrop=a('<div class="modal-backdrop '+d+'" />').appendTo(this.$body),this.$element.on("click.dismiss.bs.modal",a.proxy(function(a){a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus.call(this.$element[0]):this.hide.call(this))},this)),e&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;e?this.$backdrop.one("bsTransitionEnd",b).emulateTransitionEnd(150):b()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var f=function(){c.removeBackdrop(),b&&b()};a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",f).emulateTransitionEnd(150):f()}else b&&b()},c.prototype.checkScrollbar=function(){document.body.clientWidth>=window.innerWidth||(this.scrollbarWidth=this.scrollbarWidth||this.measureScrollbar())},c.prototype.setScrollbar=function(){var a=parseInt(this.$body.css("padding-right")||0,10);this.scrollbarWidth&&this.$body.css("padding-right",a+this.scrollbarWidth)},c.prototype.resetScrollbar=function(){this.$body.css("padding-right","")},c.prototype.measureScrollbar=function(){var a=document.createElement("div");a.className="modal-scrollbar-measure",this.$body.append(a);var b=a.offsetWidth-a.clientWidth;return this.$body[0].removeChild(a),b};var d=a.fn.modal;a.fn.modal=b,a.fn.modal.Constructor=c,a.fn.modal.noConflict=function(){return a.fn.modal=d,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(c){var d=a(this),e=d.attr("href"),f=a(d.attr("data-target")||e&&e.replace(/.*(?=#[^\s]+$)/,"")),g=f.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(e)&&e},f.data(),d.data());d.is("a")&&c.preventDefault(),f.one("show.bs.modal",function(a){a.isDefaultPrevented()||f.one("hidden.bs.modal",function(){d.is(":visible")&&d.trigger("focus")})}),b.call(f,g,this)})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tooltip"),f="object"==typeof b&&b;(e||"destroy"!=b)&&(e||d.data("bs.tooltip",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.type=this.options=this.enabled=this.timeout=this.hoverState=this.$element=null,this.init("tooltip",a,b)};c.VERSION="3.2.0",c.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(this.options.viewport.selector||this.options.viewport);for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show()},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var c=a.contains(document.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!c)return;var d=this,e=this.tip(),f=this.getUID(this.type);this.setContent(),e.attr("id",f),this.$element.attr("aria-describedby",f),this.options.animation&&e.addClass("fade");var g="function"==typeof this.options.placement?this.options.placement.call(this,e[0],this.$element[0]):this.options.placement,h=/\s?auto?\s?/i,i=h.test(g);i&&(g=g.replace(h,"")||"top"),e.detach().css({top:0,left:0,display:"block"}).addClass(g).data("bs."+this.type,this),this.options.container?e.appendTo(this.options.container):e.insertAfter(this.$element);var j=this.getPosition(),k=e[0].offsetWidth,l=e[0].offsetHeight;if(i){var m=g,n=this.$element.parent(),o=this.getPosition(n);g="bottom"==g&&j.top+j.height+l-o.scroll>o.height?"top":"top"==g&&j.top-o.scroll-l<0?"bottom":"right"==g&&j.right+k>o.width?"left":"left"==g&&j.left-k<o.left?"right":g,e.removeClass(m).addClass(g)}var p=this.getCalculatedOffset(g,j,k,l);this.applyPlacement(p,g);var q=function(){d.$element.trigger("shown.bs."+d.type),d.hoverState=null};a.support.transition&&this.$tip.hasClass("fade")?e.one("bsTransitionEnd",q).emulateTransitionEnd(150):q()}},c.prototype.applyPlacement=function(b,c){var d=this.tip(),e=d[0].offsetWidth,f=d[0].offsetHeight,g=parseInt(d.css("margin-top"),10),h=parseInt(d.css("margin-left"),10);isNaN(g)&&(g=0),isNaN(h)&&(h=0),b.top=b.top+g,b.left=b.left+h,a.offset.setOffset(d[0],a.extend({using:function(a){d.css({top:Math.round(a.top),left:Math.round(a.left)})}},b),0),d.addClass("in");var i=d[0].offsetWidth,j=d[0].offsetHeight;"top"==c&&j!=f&&(b.top=b.top+f-j);var k=this.getViewportAdjustedDelta(c,b,i,j);k.left?b.left+=k.left:b.top+=k.top;var l=k.left?2*k.left-e+i:2*k.top-f+j,m=k.left?"left":"top",n=k.left?"offsetWidth":"offsetHeight";d.offset(b),this.replaceArrow(l,d[0][n],m)},c.prototype.replaceArrow=function(a,b,c){this.arrow().css(c,a?50*(1-a/b)+"%":"")},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle();a.find(".tooltip-inner")[this.options.html?"html":"text"](b),a.removeClass("fade in top bottom left right")},c.prototype.hide=function(){function b(){"in"!=c.hoverState&&d.detach(),c.$element.trigger("hidden.bs."+c.type)}var c=this,d=this.tip(),e=a.Event("hide.bs."+this.type);return this.$element.removeAttr("aria-describedby"),this.$element.trigger(e),e.isDefaultPrevented()?void 0:(d.removeClass("in"),a.support.transition&&this.$tip.hasClass("fade")?d.one("bsTransitionEnd",b).emulateTransitionEnd(150):b(),this.hoverState=null,this)},c.prototype.fixTitle=function(){var a=this.$element;(a.attr("title")||"string"!=typeof a.attr("data-original-title"))&&a.attr("data-original-title",a.attr("title")||"").attr("title","")},c.prototype.hasContent=function(){return this.getTitle()},c.prototype.getPosition=function(b){b=b||this.$element;var c=b[0],d="BODY"==c.tagName;return a.extend({},"function"==typeof c.getBoundingClientRect?c.getBoundingClientRect():null,{scroll:d?document.documentElement.scrollTop||document.body.scrollTop:b.scrollTop(),width:d?a(window).width():b.outerWidth(),height:d?a(window).height():b.outerHeight()},d?{top:0,left:0}:b.offset())},c.prototype.getCalculatedOffset=function(a,b,c,d){return"bottom"==a?{top:b.top+b.height,left:b.left+b.width/2-c/2}:"top"==a?{top:b.top-d,left:b.left+b.width/2-c/2}:"left"==a?{top:b.top+b.height/2-d/2,left:b.left-c}:{top:b.top+b.height/2-d/2,left:b.left+b.width}},c.prototype.getViewportAdjustedDelta=function(a,b,c,d){var e={top:0,left:0};if(!this.$viewport)return e;var f=this.options.viewport&&this.options.viewport.padding||0,g=this.getPosition(this.$viewport);if(/right|left/.test(a)){var h=b.top-f-g.scroll,i=b.top+f-g.scroll+d;h<g.top?e.top=g.top-h:i>g.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;j<g.left?e.left=g.left-j:k>g.width&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){return this.$tip=this.$tip||a(this.options.template)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.validate=function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){clearTimeout(this.timeout),this.hide().$element.off("."+this.type).removeData("bs."+this.type)};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||"destroy"!=b)&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.2.0",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").empty()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},c.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){var e=a.proxy(this.process,this);this.$body=a("body"),this.$scrollElement=a(a(c).is("body")?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",e),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.2.0",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b="offset",c=0;a.isWindow(this.$scrollElement[0])||(b="position",c=this.$scrollElement.scrollTop()),this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight();var d=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[b]().top+c,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){d.offsets.push(this[0]),d.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b<=e[0])return g!=(a=f[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parentsUntil(this.options.target,".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")};var d=a.fn.scrollspy;a.fn.scrollspy=c,a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=d,this},a(window).on("load.bs.scrollspy.data-api",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new c(this)),"string"==typeof b&&e[b]()})}var c=function(b){this.element=a(b)};c.VERSION="3.2.0",c.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.closest("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},c.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one("bsTransitionEnd",e).emulateTransitionEnd(150):e(),f.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(c){c.preventDefault(),b.call(a(this),"show")})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=this.unpin=this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.2.0",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=a(document).height(),d=this.$target.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top(this.$element)),"function"==typeof h&&(h=f.bottom(this.$element));var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=b-h?"bottom":null!=g&&g>=d?"top":!1;if(this.affixed!==i){null!=this.unpin&&this.$element.css("top","");var j="affix"+(i?"-"+i:""),k=a.Event(j+".bs.affix");this.$element.trigger(k),k.isDefaultPrevented()||(this.affixed=i,this.unpin="bottom"==i?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(j).trigger(a.Event(j.replace("affix","affixed"))),"bottom"==i&&this.$element.offset({top:b-this.$element.height()-h}))}}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},d.offsetBottom&&(d.offset.bottom=d.offsetBottom),d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); \ No newline at end of file
diff --git a/container-search-gui/src/main/resources/gui/js/classie.js b/container-search-gui/src/main/resources/gui/js/classie.js
new file mode 100644
index 00000000000..2cbf994382b
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/js/classie.js
@@ -0,0 +1,83 @@
+/*!
+* Copyright © 2014 Dustin Diaz
+* Licensed under the terms of the MIT license
+*
+*
+* classie - class helper functions
+* from bonzo https://github.com/ded/bonzo
+*
+* classie.has( elem, 'my-class' ) -> true/false
+* classie.add( elem, 'my-new-class' )
+* classie.remove( elem, 'my-unwanted-class' )
+* classie.toggle( elem, 'my-class' )
+*/
+/*jshint browser: true, strict: true, undef: true */
+/*global define: false */
+
+( function( window ) {
+
+'use strict';
+
+// class helper functions from bonzo https://github.com/ded/bonzo
+
+function classReg( className ) {
+ return new RegExp("(^|\\s+)" + className + "(\\s+|$)");
+}
+
+// classList support for class management
+// altho to be fair, the api sucks because it won't accept multiple classes at once
+var hasClass, addClass, removeClass;
+
+if ( 'classList' in document.documentElement ) {
+ hasClass = function( elem, c ) {
+ return elem.classList.contains( c );
+ };
+ addClass = function( elem, c ) {
+ elem.classList.add( c );
+ };
+ removeClass = function( elem, c ) {
+ elem.classList.remove( c );
+ };
+}
+else {
+ hasClass = function( elem, c ) {
+ return classReg( c ).test( elem.className );
+ };
+ addClass = function( elem, c ) {
+ if ( !hasClass( elem, c ) ) {
+ elem.className = elem.className + ' ' + c;
+ }
+ };
+ removeClass = function( elem, c ) {
+ elem.className = elem.className.replace( classReg( c ), ' ' );
+ };
+}
+
+function toggleClass( elem, c ) {
+ var fn = hasClass( elem, c ) ? removeClass : addClass;
+ fn( elem, c );
+}
+
+var classie = {
+ // full names
+ hasClass: hasClass,
+ addClass: addClass,
+ removeClass: removeClass,
+ toggleClass: toggleClass,
+ // short names
+ has: hasClass,
+ add: addClass,
+ remove: removeClass,
+ toggle: toggleClass
+};
+
+// transport
+if ( typeof define === 'function' && define.amd ) {
+ // AMD
+ define( classie );
+} else {
+ // browser global
+ window.classie = classie;
+}
+
+})( window );
diff --git a/container-search-gui/src/main/resources/gui/js/jquery-1.11.0.js b/container-search-gui/src/main/resources/gui/js/jquery-1.11.0.js
new file mode 100644
index 00000000000..046e93aa15e
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/js/jquery-1.11.0.js
@@ -0,0 +1,4 @@
+/*! jQuery v1.11.0 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */
+!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k="".trim,l={},m="1.11.0",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(n.isPlainObject(c)||(b=n.isArray(c)))?(b?(b=!1,f=a&&n.isArray(a)?a:[]):f=a&&n.isPlainObject(a)?a:{},g[d]=n.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray||function(a){return"array"===n.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return a-parseFloat(a)>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(l.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&n.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:k&&!k.call("\ufeff\xa0")?function(a){return null==a?"":k.call(a)}:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),n.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||n.guid++,e):void 0},now:function(){return+new Date},support:l}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s="sizzle"+-new Date,t=a.document,u=0,v=0,w=eb(),x=eb(),y=eb(),z=function(a,b){return a===b&&(j=!0),0},A="undefined",B=1<<31,C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=D.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},J="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",K="[\\x20\\t\\r\\n\\f]",L="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",M=L.replace("w","w#"),N="\\["+K+"*("+L+")"+K+"*(?:([*^$|!~]?=)"+K+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+M+")|)|)"+K+"*\\]",O=":("+L+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+N.replace(3,8)+")*)|.*)\\)|)",P=new RegExp("^"+K+"+|((?:^|[^\\\\])(?:\\\\.)*)"+K+"+$","g"),Q=new RegExp("^"+K+"*,"+K+"*"),R=new RegExp("^"+K+"*([>+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(O),U=new RegExp("^"+M+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L.replace("w","w*")+")"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=/'|\\/g,ab=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),bb=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{G.apply(D=H.call(t.childNodes),t.childNodes),D[t.childNodes.length].nodeType}catch(cb){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function db(a,b,d,e){var f,g,h,i,j,m,p,q,u,v;if((b?b.ownerDocument||b:t)!==l&&k(b),b=b||l,d=d||[],!a||"string"!=typeof a)return d;if(1!==(i=b.nodeType)&&9!==i)return[];if(n&&!e){if(f=Z.exec(a))if(h=f[1]){if(9===i){if(g=b.getElementById(h),!g||!g.parentNode)return d;if(g.id===h)return d.push(g),d}else if(b.ownerDocument&&(g=b.ownerDocument.getElementById(h))&&r(b,g)&&g.id===h)return d.push(g),d}else{if(f[2])return G.apply(d,b.getElementsByTagName(a)),d;if((h=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(h)),d}if(c.qsa&&(!o||!o.test(a))){if(q=p=s,u=b,v=9===i&&a,1===i&&"object"!==b.nodeName.toLowerCase()){m=ob(a),(p=b.getAttribute("id"))?q=p.replace(_,"\\$&"):b.setAttribute("id",q),q="[id='"+q+"'] ",j=m.length;while(j--)m[j]=q+pb(m[j]);u=$.test(a)&&mb(b.parentNode)||b,v=m.join(",")}if(v)try{return G.apply(d,u.querySelectorAll(v)),d}catch(w){}finally{p||b.removeAttribute("id")}}}return xb(a.replace(P,"$1"),b,d,e)}function eb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function fb(a){return a[s]=!0,a}function gb(a){var b=l.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function hb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function ib(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||B)-(~a.sourceIndex||B);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function jb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function kb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function lb(a){return fb(function(b){return b=+b,fb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function mb(a){return a&&typeof a.getElementsByTagName!==A&&a}c=db.support={},f=db.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},k=db.setDocument=function(a){var b,e=a?a.ownerDocument||a:t,g=e.defaultView;return e!==l&&9===e.nodeType&&e.documentElement?(l=e,m=e.documentElement,n=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){k()},!1):g.attachEvent&&g.attachEvent("onunload",function(){k()})),c.attributes=gb(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=gb(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(e.getElementsByClassName)&&gb(function(a){return a.innerHTML="<div class='a'></div><div class='a i'></div>",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=gb(function(a){return m.appendChild(a).id=s,!e.getElementsByName||!e.getElementsByName(s).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==A&&n){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ab,bb);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ab,bb);return function(a){var c=typeof a.getAttributeNode!==A&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==A?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==A&&n?b.getElementsByClassName(a):void 0},p=[],o=[],(c.qsa=Y.test(e.querySelectorAll))&&(gb(function(a){a.innerHTML="<select t=''><option selected=''></option></select>",a.querySelectorAll("[t^='']").length&&o.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||o.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll(":checked").length||o.push(":checked")}),gb(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&o.push("name"+K+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||o.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),o.push(",.*:")})),(c.matchesSelector=Y.test(q=m.webkitMatchesSelector||m.mozMatchesSelector||m.oMatchesSelector||m.msMatchesSelector))&&gb(function(a){c.disconnectedMatch=q.call(a,"div"),q.call(a,"[s!='']:x"),p.push("!=",O)}),o=o.length&&new RegExp(o.join("|")),p=p.length&&new RegExp(p.join("|")),b=Y.test(m.compareDocumentPosition),r=b||Y.test(m.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},z=b?function(a,b){if(a===b)return j=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===t&&r(t,a)?-1:b===e||b.ownerDocument===t&&r(t,b)?1:i?I.call(i,a)-I.call(i,b):0:4&d?-1:1)}:function(a,b){if(a===b)return j=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],k=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:i?I.call(i,a)-I.call(i,b):0;if(f===g)return ib(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)k.unshift(c);while(h[d]===k[d])d++;return d?ib(h[d],k[d]):h[d]===t?-1:k[d]===t?1:0},e):l},db.matches=function(a,b){return db(a,null,null,b)},db.matchesSelector=function(a,b){if((a.ownerDocument||a)!==l&&k(a),b=b.replace(S,"='$1']"),!(!c.matchesSelector||!n||p&&p.test(b)||o&&o.test(b)))try{var d=q.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return db(b,l,null,[a]).length>0},db.contains=function(a,b){return(a.ownerDocument||a)!==l&&k(a),r(a,b)},db.attr=function(a,b){(a.ownerDocument||a)!==l&&k(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!n):void 0;return void 0!==f?f:c.attributes||!n?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},db.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},db.uniqueSort=function(a){var b,d=[],e=0,f=0;if(j=!c.detectDuplicates,i=!c.sortStable&&a.slice(0),a.sort(z),j){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return i=null,a},e=db.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=db.selectors={cacheLength:50,createPseudo:fb,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ab,bb),a[3]=(a[4]||a[5]||"").replace(ab,bb),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||db.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&db.error(a[0]),a},PSEUDO:function(a){var b,c=!a[5]&&a[2];return V.CHILD.test(a[0])?null:(a[3]&&void 0!==a[4]?a[2]=a[4]:c&&T.test(c)&&(b=ob(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ab,bb).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=w[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&w(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==A&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=db.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),t=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&t){k=q[s]||(q[s]={}),j=k[a]||[],n=j[0]===u&&j[1],m=j[0]===u&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[u,n,m];break}}else if(t&&(j=(b[s]||(b[s]={}))[a])&&j[0]===u)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(t&&((l[s]||(l[s]={}))[a]=[u,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||db.error("unsupported pseudo: "+a);return e[s]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?fb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:fb(function(a){var b=[],c=[],d=g(a.replace(P,"$1"));return d[s]?fb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:fb(function(a){return function(b){return db(a,b).length>0}}),contains:fb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:fb(function(a){return U.test(a||"")||db.error("unsupported lang: "+a),a=a.replace(ab,bb).toLowerCase(),function(b){var c;do if(c=n?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===m},focus:function(a){return a===l.activeElement&&(!l.hasFocus||l.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:lb(function(){return[0]}),last:lb(function(a,b){return[b-1]}),eq:lb(function(a,b,c){return[0>c?c+b:c]}),even:lb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:lb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:lb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:lb(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=jb(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=kb(b);function nb(){}nb.prototype=d.filters=d.pseudos,d.setFilters=new nb;function ob(a,b){var c,e,f,g,h,i,j,k=x[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){(!c||(e=Q.exec(h)))&&(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=R.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(P," ")}),h=h.slice(c.length));for(g in d.filter)!(e=V[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?db.error(a):x(a,i).slice(0)}function pb(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function qb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=v++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[u,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[s]||(b[s]={}),(h=i[d])&&h[0]===u&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function rb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function sb(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function tb(a,b,c,d,e,f){return d&&!d[s]&&(d=tb(d)),e&&!e[s]&&(e=tb(e,f)),fb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||wb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:sb(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=sb(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?I.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=sb(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ub(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],i=g||d.relative[" "],j=g?1:0,k=qb(function(a){return a===b},i,!0),l=qb(function(a){return I.call(b,a)>-1},i,!0),m=[function(a,c,d){return!g&&(d||c!==h)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>j;j++)if(c=d.relative[a[j].type])m=[qb(rb(m),c)];else{if(c=d.filter[a[j].type].apply(null,a[j].matches),c[s]){for(e=++j;f>e;e++)if(d.relative[a[e].type])break;return tb(j>1&&rb(m),j>1&&pb(a.slice(0,j-1).concat({value:" "===a[j-2].type?"*":""})).replace(P,"$1"),c,e>j&&ub(a.slice(j,e)),f>e&&ub(a=a.slice(e)),f>e&&pb(a))}m.push(c)}return rb(m)}function vb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,i,j,k){var m,n,o,p=0,q="0",r=f&&[],s=[],t=h,v=f||e&&d.find.TAG("*",k),w=u+=null==t?1:Math.random()||.1,x=v.length;for(k&&(h=g!==l&&g);q!==x&&null!=(m=v[q]);q++){if(e&&m){n=0;while(o=a[n++])if(o(m,g,i)){j.push(m);break}k&&(u=w)}c&&((m=!o&&m)&&p--,f&&r.push(m))}if(p+=q,c&&q!==p){n=0;while(o=b[n++])o(r,s,g,i);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=E.call(j));s=sb(s)}G.apply(j,s),k&&!f&&s.length>0&&p+b.length>1&&db.uniqueSort(j)}return k&&(u=w,h=t),r};return c?fb(f):f}g=db.compile=function(a,b){var c,d=[],e=[],f=y[a+" "];if(!f){b||(b=ob(a)),c=b.length;while(c--)f=ub(b[c]),f[s]?d.push(f):e.push(f);f=y(a,vb(e,d))}return f};function wb(a,b,c){for(var d=0,e=b.length;e>d;d++)db(a,b[d],c);return c}function xb(a,b,e,f){var h,i,j,k,l,m=ob(a);if(!f&&1===m.length){if(i=m[0]=m[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&c.getById&&9===b.nodeType&&n&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(ab,bb),b)||[])[0],!b)return e;a=a.slice(i.shift().value.length)}h=V.needsContext.test(a)?0:i.length;while(h--){if(j=i[h],d.relative[k=j.type])break;if((l=d.find[k])&&(f=l(j.matches[0].replace(ab,bb),$.test(i[0].type)&&mb(b.parentNode)||b))){if(i.splice(h,1),a=f.length&&pb(i),!a)return G.apply(e,f),e;break}}}return g(a,m)(f,b,!n,e,$.test(a)&&mb(b.parentNode)||b),e}return c.sortStable=s.split("").sort(z).join("")===s,c.detectDuplicates=!!j,k(),c.sortDetached=gb(function(a){return 1&a.compareDocumentPosition(l.createElement("div"))}),gb(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||hb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&gb(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||hb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),gb(function(a){return null==a.getAttribute("disabled")})||hb(J,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),db}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return n.inArray(a,b)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;e>b;b++)if(n.contains(d[b],this))return!0}));for(b=0;e>b;b++)n.find(a,d[b],c);return c=this.pushStack(e>1?n.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=a.document,A=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,B=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:A.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:z,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=z.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return y.find(a);this.length=1,this[0]=d}return this.context=z,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};B.prototype=n.fn,y=n(z);var C=/^(?:parents|prev(?:Until|All))/,D={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!n(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b,c=n(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(n.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?n.inArray(this[0],n(a)):n.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function E(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return E(a,"nextSibling")},prev:function(a){return E(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return n.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(D[a]||(e=n.unique(e)),C.test(a)&&(e=e.reverse())),this.pushStack(e)}});var F=/\S+/g,G={};function H(a){var b=G[a]={};return n.each(a.match(F)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?G[a]||H(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&n.each(arguments,function(a,c){var d;while((d=n.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){if(a===!0?!--n.readyWait:!n.isReady){if(!z.body)return setTimeout(n.ready);n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(z,[n]),n.fn.trigger&&n(z).trigger("ready").off("ready"))}}});function J(){z.addEventListener?(z.removeEventListener("DOMContentLoaded",K,!1),a.removeEventListener("load",K,!1)):(z.detachEvent("onreadystatechange",K),a.detachEvent("onload",K))}function K(){(z.addEventListener||"load"===event.type||"complete"===z.readyState)&&(J(),n.ready())}n.ready.promise=function(b){if(!I)if(I=n.Deferred(),"complete"===z.readyState)setTimeout(n.ready);else if(z.addEventListener)z.addEventListener("DOMContentLoaded",K,!1),a.addEventListener("load",K,!1);else{z.attachEvent("onreadystatechange",K),a.attachEvent("onload",K);var c=!1;try{c=null==a.frameElement&&z.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!n.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}J(),n.ready()}}()}return I.promise(b)};var L="undefined",M;for(M in n(l))break;l.ownLast="0"!==M,l.inlineBlockNeedsLayout=!1,n(function(){var a,b,c=z.getElementsByTagName("body")[0];c&&(a=z.createElement("div"),a.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",b=z.createElement("div"),c.appendChild(a).appendChild(b),typeof b.style.zoom!==L&&(b.style.cssText="border:0;margin:0;width:1px;padding:1px;display:inline;zoom:1",(l.inlineBlockNeedsLayout=3===b.offsetWidth)&&(c.style.zoom=1)),c.removeChild(a),a=b=null)}),function(){var a=z.createElement("div");if(null==l.deleteExpando){l.deleteExpando=!0;try{delete a.test}catch(b){l.deleteExpando=!1}}a=null}(),n.acceptData=function(a){var b=n.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(O,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}n.data(a,b,c)}else c=void 0}return c}function Q(a){var b;for(b in a)if(("data"!==b||!n.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function R(a,b,d,e){if(n.acceptData(a)){var f,g,h=n.expando,i=a.nodeType,j=i?n.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||n.guid++:h),j[k]||(j[k]=i?{}:{toJSON:n.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=n.extend(j[k],b):j[k].data=n.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[n.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[n.camelCase(b)])):f=g,f
+}}function S(a,b,c){if(n.acceptData(a)){var d,e,f=a.nodeType,g=f?n.cache:a,h=f?a[n.expando]:n.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){n.isArray(b)?b=b.concat(n.map(b,n.camelCase)):b in d?b=[b]:(b=n.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!Q(d):!n.isEmptyObject(d))return}(c||(delete g[h].data,Q(g[h])))&&(f?n.cleanData([a],!0):l.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}n.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?n.cache[a[n.expando]]:a[n.expando],!!a&&!Q(a)},data:function(a,b,c){return R(a,b,c)},removeData:function(a,b){return S(a,b)},_data:function(a,b,c){return R(a,b,c,!0)},_removeData:function(a,b){return S(a,b,!0)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=n.data(f),1===f.nodeType&&!n._data(f,"parsedAttrs"))){c=g.length;while(c--)d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d]));n._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){n.data(this,a)}):arguments.length>1?this.each(function(){n.data(this,a,b)}):f?P(f,a,n.data(f,a)):void 0},removeData:function(a){return this.each(function(){n.removeData(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=n._data(a,b),c&&(!d||n.isArray(c)?d=n._data(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return n._data(a,c)||n._data(a,c,{empty:n.Callbacks("once memory").add(function(){n._removeData(a,b+"queue"),n._removeData(a,c)})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?n.queue(this[0],a):void 0===b?this:this.each(function(){var c=n.queue(this,a,b);n._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&n.dequeue(this,a)})},dequeue:function(a){return this.each(function(){n.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=n.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=n._data(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var T=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,U=["Top","Right","Bottom","Left"],V=function(a,b){return a=b||a,"none"===n.css(a,"display")||!n.contains(a.ownerDocument,a)},W=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},X=/^(?:checkbox|radio)$/i;!function(){var a=z.createDocumentFragment(),b=z.createElement("div"),c=z.createElement("input");if(b.setAttribute("className","t"),b.innerHTML=" <link/><table></table><a href='/a'>a</a>",l.leadingWhitespace=3===b.firstChild.nodeType,l.tbody=!b.getElementsByTagName("tbody").length,l.htmlSerialize=!!b.getElementsByTagName("link").length,l.html5Clone="<:nav></:nav>"!==z.createElement("nav").cloneNode(!0).outerHTML,c.type="checkbox",c.checked=!0,a.appendChild(c),l.appendChecked=c.checked,b.innerHTML="<textarea>x</textarea>",l.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,a.appendChild(b),b.innerHTML="<input type='radio' checked='checked' name='t'/>",l.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,l.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){l.noCloneEvent=!1}),b.cloneNode(!0).click()),null==l.deleteExpando){l.deleteExpando=!0;try{delete b.test}catch(d){l.deleteExpando=!1}}a=b=c=null}(),function(){var b,c,d=z.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(l[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),l[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var Y=/^(?:input|select|textarea)$/i,Z=/^key/,$=/^(?:mouse|contextmenu)|click/,_=/^(?:focusinfocus|focusoutblur)$/,ab=/^([^.]*)(?:\.(.+)|)$/;function bb(){return!0}function cb(){return!1}function db(){try{return z.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=n.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof n===L||a&&n.event.triggered===a.type?void 0:n.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(F)||[""],h=b.length;while(h--)f=ab.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=n.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=n.event.special[o]||{},l=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},i),(m=g[o])||(m=g[o]=[],m.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,l):m.push(l),n.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n.hasData(a)&&n._data(a);if(r&&(k=r.events)){b=(b||"").match(F)||[""],j=b.length;while(j--)if(h=ab.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=m.length;while(f--)g=m[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(m.splice(f,1),g.selector&&m.delegateCount--,l.remove&&l.remove.call(a,g));i&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(k)&&(delete r.handle,n._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,m,o=[d||z],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||z,3!==d.nodeType&&8!==d.nodeType&&!_.test(p+n.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[n.expando]?b:new n.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),k=n.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!n.isWindow(d)){for(i=k.delegateType||p,_.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||z)&&o.push(l.defaultView||l.parentWindow||a)}m=0;while((h=o[m++])&&!b.isPropagationStopped())b.type=m>1?i:k.bindType||p,f=(n._data(h,"events")||{})[b.type]&&n._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&n.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&n.acceptData(d)&&g&&d[p]&&!n.isWindow(d)){l=d[g],l&&(d[g]=null),n.event.triggered=p;try{d[p]()}catch(r){}n.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(n._data(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((n.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?n(c,this).index(i)>=0:n.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},fix:function(a){if(a[n.expando])return a;var b,c,d,e=a.type,f=a,g=this.fixHooks[e];g||(this.fixHooks[e]=g=$.test(e)?this.mouseHooks:Z.test(e)?this.keyHooks:{}),d=g.props?this.props.concat(g.props):this.props,a=new n.Event(f),b=d.length;while(b--)c=d[b],a[c]=f[c];return a.target||(a.target=f.srcElement||z),3===a.target.nodeType&&(a.target=a.target.parentNode),a.metaKey=!!a.metaKey,g.filter?g.filter(a,f):a},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,d,e,f=b.button,g=b.fromElement;return null==a.pageX&&null!=b.clientX&&(d=a.target.ownerDocument||z,e=d.documentElement,c=d.body,a.pageX=b.clientX+(e&&e.scrollLeft||c&&c.scrollLeft||0)-(e&&e.clientLeft||c&&c.clientLeft||0),a.pageY=b.clientY+(e&&e.scrollTop||c&&c.scrollTop||0)-(e&&e.clientTop||c&&c.clientTop||0)),!a.relatedTarget&&g&&(a.relatedTarget=g===a.target?b.toElement:g),a.which||void 0===f||(a.which=1&f?1:2&f?3:4&f?2:0),a}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==db()&&this.focus)try{return this.focus(),!1}catch(a){}},delegateType:"focusin"},blur:{trigger:function(){return this===db()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return n.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):void 0},_default:function(a){return n.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c,d){var e=n.extend(new n.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?n.event.trigger(e,null,b):n.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},n.removeEvent=z.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){var d="on"+b;a.detachEvent&&(typeof a[d]===L&&(a[d]=null),a.detachEvent(d,c))},n.Event=function(a,b){return this instanceof n.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&(a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault())?bb:cb):this.type=a,b&&n.extend(this,b),this.timeStamp=a&&a.timeStamp||n.now(),void(this[n.expando]=!0)):new n.Event(a,b)},n.Event.prototype={isDefaultPrevented:cb,isPropagationStopped:cb,isImmediatePropagationStopped:cb,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=bb,a&&(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=bb,a&&(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=bb,this.stopPropagation()}},n.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){n.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!n.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),l.submitBubbles||(n.event.special.submit={setup:function(){return n.nodeName(this,"form")?!1:void n.event.add(this,"click._submit keypress._submit",function(a){var b=a.target,c=n.nodeName(b,"input")||n.nodeName(b,"button")?b.form:void 0;c&&!n._data(c,"submitBubbles")&&(n.event.add(c,"submit._submit",function(a){a._submit_bubble=!0}),n._data(c,"submitBubbles",!0))})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&n.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){return n.nodeName(this,"form")?!1:void n.event.remove(this,"._submit")}}),l.changeBubbles||(n.event.special.change={setup:function(){return Y.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(n.event.add(this,"propertychange._change",function(a){"checked"===a.originalEvent.propertyName&&(this._just_changed=!0)}),n.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1),n.event.simulate("change",this,a,!0)})),!1):void n.event.add(this,"beforeactivate._change",function(a){var b=a.target;Y.test(b.nodeName)&&!n._data(b,"changeBubbles")&&(n.event.add(b,"change._change",function(a){!this.parentNode||a.isSimulated||a.isTrigger||n.event.simulate("change",this.parentNode,a,!0)}),n._data(b,"changeBubbles",!0))})},handle:function(a){var b=a.target;return this!==b||a.isSimulated||a.isTrigger||"radio"!==b.type&&"checkbox"!==b.type?a.handleObj.handler.apply(this,arguments):void 0},teardown:function(){return n.event.remove(this,"._change"),!Y.test(this.nodeName)}}),l.focusinBubbles||n.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){n.event.simulate(b,a.target,n.event.fix(a),!0)};n.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=n._data(d,b);e||d.addEventListener(a,c,!0),n._data(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=n._data(d,b)-1;e?n._data(d,b,e):(d.removeEventListener(a,c,!0),n._removeData(d,b))}}}),n.fn.extend({on:function(a,b,c,d,e){var f,g;if("object"==typeof a){"string"!=typeof b&&(c=c||b,b=void 0);for(f in a)this.on(f,b,c,a[f],e);return this}if(null==c&&null==d?(d=b,c=b=void 0):null==d&&("string"==typeof b?(d=c,c=void 0):(d=c,c=b,b=void 0)),d===!1)d=cb;else if(!d)return this;return 1===e&&(g=d,d=function(a){return n().off(a),g.apply(this,arguments)},d.guid=g.guid||(g.guid=n.guid++)),this.each(function(){n.event.add(this,a,d,c,b)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,n(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return(b===!1||"function"==typeof b)&&(c=b,b=void 0),c===!1&&(c=cb),this.each(function(){n.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){n.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?n.event.trigger(a,b,c,!0):void 0}});function eb(a){var b=fb.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}var fb="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gb=/ jQuery\d+="(?:null|\d+)"/g,hb=new RegExp("<(?:"+fb+")[\\s/>]","i"),ib=/^\s+/,jb=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,kb=/<([\w:]+)/,lb=/<tbody/i,mb=/<|&#?\w+;/,nb=/<(?:script|style|link)/i,ob=/checked\s*(?:[^=]|=\s*.checked.)/i,pb=/^$|\/(?:java|ecma)script/i,qb=/^true\/(.*)/,rb=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,sb={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:l.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},tb=eb(z),ub=tb.appendChild(z.createElement("div"));sb.optgroup=sb.option,sb.tbody=sb.tfoot=sb.colgroup=sb.caption=sb.thead,sb.th=sb.td;function vb(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==L?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==L?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||n.nodeName(d,b)?f.push(d):n.merge(f,vb(d,b));return void 0===b||b&&n.nodeName(a,b)?n.merge([a],f):f}function wb(a){X.test(a.type)&&(a.defaultChecked=a.checked)}function xb(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function yb(a){return a.type=(null!==n.find.attr(a,"type"))+"/"+a.type,a}function zb(a){var b=qb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ab(a,b){for(var c,d=0;null!=(c=a[d]);d++)n._data(c,"globalEval",!b||n._data(b[d],"globalEval"))}function Bb(a,b){if(1===b.nodeType&&n.hasData(a)){var c,d,e,f=n._data(a),g=n._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)n.event.add(b,c,h[c][d])}g.data&&(g.data=n.extend({},g.data))}}function Cb(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!l.noCloneEvent&&b[n.expando]){e=n._data(b);for(d in e.events)n.removeEvent(b,d,e.handle);b.removeAttribute(n.expando)}"script"===c&&b.text!==a.text?(yb(b).text=a.text,zb(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),l.html5Clone&&a.innerHTML&&!n.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&X.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}n.extend({clone:function(a,b,c){var d,e,f,g,h,i=n.contains(a.ownerDocument,a);if(l.html5Clone||n.isXMLDoc(a)||!hb.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(ub.innerHTML=a.outerHTML,ub.removeChild(f=ub.firstChild)),!(l.noCloneEvent&&l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(d=vb(f),h=vb(a),g=0;null!=(e=h[g]);++g)d[g]&&Cb(e,d[g]);if(b)if(c)for(h=h||vb(a),d=d||vb(f),g=0;null!=(e=h[g]);g++)Bb(e,d[g]);else Bb(a,f);return d=vb(f,"script"),d.length>0&&Ab(d,!i&&vb(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k,m=a.length,o=eb(b),p=[],q=0;m>q;q++)if(f=a[q],f||0===f)if("object"===n.type(f))n.merge(p,f.nodeType?[f]:f);else if(mb.test(f)){h=h||o.appendChild(b.createElement("div")),i=(kb.exec(f)||["",""])[1].toLowerCase(),k=sb[i]||sb._default,h.innerHTML=k[1]+f.replace(jb,"<$1></$2>")+k[2],e=k[0];while(e--)h=h.lastChild;if(!l.leadingWhitespace&&ib.test(f)&&p.push(b.createTextNode(ib.exec(f)[0])),!l.tbody){f="table"!==i||lb.test(f)?"<table>"!==k[1]||lb.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)n.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}n.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),l.appendChecked||n.grep(vb(p,"input"),wb),q=0;while(f=p[q++])if((!d||-1===n.inArray(f,d))&&(g=n.contains(f.ownerDocument,f),h=vb(o.appendChild(f),"script"),g&&Ab(h),c)){e=0;while(f=h[e++])pb.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=n.expando,j=n.cache,k=l.deleteExpando,m=n.event.special;null!=(d=a[h]);h++)if((b||n.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)m[e]?n.event.remove(d,e):n.removeEvent(d,e,g.handle);j[f]&&(delete j[f],k?delete d[i]:typeof d.removeAttribute!==L?d.removeAttribute(i):d[i]=null,c.push(f))}}}),n.fn.extend({text:function(a){return W(this,function(a){return void 0===a?n.text(this):this.empty().append((this[0]&&this[0].ownerDocument||z).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=xb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=xb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(vb(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&Ab(vb(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&n.cleanData(vb(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&n.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return W(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(gb,""):void 0;if(!("string"!=typeof a||nb.test(a)||!l.htmlSerialize&&hb.test(a)||!l.leadingWhitespace&&ib.test(a)||sb[(kb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(jb,"<$1></$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(vb(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(vb(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,k=this.length,m=this,o=k-1,p=a[0],q=n.isFunction(p);if(q||k>1&&"string"==typeof p&&!l.checkClone&&ob.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(k&&(i=n.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=n.map(vb(i,"script"),yb),f=g.length;k>j;j++)d=i,j!==o&&(d=n.clone(d,!0,!0),f&&n.merge(g,vb(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,n.map(g,zb),j=0;f>j;j++)d=g[j],pb.test(d.type||"")&&!n._data(d,"globalEval")&&n.contains(h,d)&&(d.src?n._evalUrl&&n._evalUrl(d.src):n.globalEval((d.text||d.textContent||d.innerHTML||"").replace(rb,"")));i=c=null}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=0,e=[],g=n(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),n(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Db,Eb={};function Fb(b,c){var d=n(c.createElement(b)).appendTo(c.body),e=a.getDefaultComputedStyle?a.getDefaultComputedStyle(d[0]).display:n.css(d[0],"display");return d.detach(),e}function Gb(a){var b=z,c=Eb[a];return c||(c=Fb(a,b),"none"!==c&&c||(Db=(Db||n("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=(Db[0].contentWindow||Db[0].contentDocument).document,b.write(),b.close(),c=Fb(a,b),Db.detach()),Eb[a]=c),c}!function(){var a,b,c=z.createElement("div"),d="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;padding:0;margin:0;border:0";c.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",a=c.getElementsByTagName("a")[0],a.style.cssText="float:left;opacity:.5",l.opacity=/^0.5/.test(a.style.opacity),l.cssFloat=!!a.style.cssFloat,c.style.backgroundClip="content-box",c.cloneNode(!0).style.backgroundClip="",l.clearCloneStyle="content-box"===c.style.backgroundClip,a=c=null,l.shrinkWrapBlocks=function(){var a,c,e,f;if(null==b){if(a=z.getElementsByTagName("body")[0],!a)return;f="border:0;width:0;height:0;position:absolute;top:0;left:-9999px",c=z.createElement("div"),e=z.createElement("div"),a.appendChild(c).appendChild(e),b=!1,typeof e.style.zoom!==L&&(e.style.cssText=d+";width:1px;padding:1px;zoom:1",e.innerHTML="<div></div>",e.firstChild.style.width="5px",b=3!==e.offsetWidth),a.removeChild(c),a=c=e=null}return b}}();var Hb=/^margin/,Ib=new RegExp("^("+T+")(?!px)[a-z%]+$","i"),Jb,Kb,Lb=/^(top|right|bottom|left)$/;a.getComputedStyle?(Jb=function(a){return a.ownerDocument.defaultView.getComputedStyle(a,null)},Kb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Jb(a),g=c?c.getPropertyValue(b)||c[b]:void 0,c&&(""!==g||n.contains(a.ownerDocument,a)||(g=n.style(a,b)),Ib.test(g)&&Hb.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0===g?g:g+""}):z.documentElement.currentStyle&&(Jb=function(a){return a.currentStyle},Kb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Jb(a),g=c?c[b]:void 0,null==g&&h&&h[b]&&(g=h[b]),Ib.test(g)&&!Lb.test(b)&&(d=h.left,e=a.runtimeStyle,f=e&&e.left,f&&(e.left=a.currentStyle.left),h.left="fontSize"===b?"1em":g,g=h.pixelLeft+"px",h.left=d,f&&(e.left=f)),void 0===g?g:g+""||"auto"});function Mb(a,b){return{get:function(){var c=a();if(null!=c)return c?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d,e,f,g,h=z.createElement("div"),i="border:0;width:0;height:0;position:absolute;top:0;left:-9999px",j="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;padding:0;margin:0;border:0";h.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",b=h.getElementsByTagName("a")[0],b.style.cssText="float:left;opacity:.5",l.opacity=/^0.5/.test(b.style.opacity),l.cssFloat=!!b.style.cssFloat,h.style.backgroundClip="content-box",h.cloneNode(!0).style.backgroundClip="",l.clearCloneStyle="content-box"===h.style.backgroundClip,b=h=null,n.extend(l,{reliableHiddenOffsets:function(){if(null!=c)return c;var a,b,d,e=z.createElement("div"),f=z.getElementsByTagName("body")[0];if(f)return e.setAttribute("className","t"),e.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",a=z.createElement("div"),a.style.cssText=i,f.appendChild(a).appendChild(e),e.innerHTML="<table><tr><td></td><td>t</td></tr></table>",b=e.getElementsByTagName("td"),b[0].style.cssText="padding:0;margin:0;border:0;display:none",d=0===b[0].offsetHeight,b[0].style.display="",b[1].style.display="none",c=d&&0===b[0].offsetHeight,f.removeChild(a),e=f=null,c},boxSizing:function(){return null==d&&k(),d},boxSizingReliable:function(){return null==e&&k(),e},pixelPosition:function(){return null==f&&k(),f},reliableMarginRight:function(){var b,c,d,e;if(null==g&&a.getComputedStyle){if(b=z.getElementsByTagName("body")[0],!b)return;c=z.createElement("div"),d=z.createElement("div"),c.style.cssText=i,b.appendChild(c).appendChild(d),e=d.appendChild(z.createElement("div")),e.style.cssText=d.style.cssText=j,e.style.marginRight=e.style.width="0",d.style.width="1px",g=!parseFloat((a.getComputedStyle(e,null)||{}).marginRight),b.removeChild(c)}return g}});function k(){var b,c,h=z.getElementsByTagName("body")[0];h&&(b=z.createElement("div"),c=z.createElement("div"),b.style.cssText=i,h.appendChild(b).appendChild(c),c.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;position:absolute;display:block;padding:1px;border:1px;width:4px;margin-top:1%;top:1%",n.swap(h,null!=h.style.zoom?{zoom:1}:{},function(){d=4===c.offsetWidth}),e=!0,f=!1,g=!0,a.getComputedStyle&&(f="1%"!==(a.getComputedStyle(c,null)||{}).top,e="4px"===(a.getComputedStyle(c,null)||{width:"4px"}).width),h.removeChild(b),c=h=null)}}(),n.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var Nb=/alpha\([^)]*\)/i,Ob=/opacity\s*=\s*([^)]*)/,Pb=/^(none|table(?!-c[ea]).+)/,Qb=new RegExp("^("+T+")(.*)$","i"),Rb=new RegExp("^([+-])=("+T+")","i"),Sb={position:"absolute",visibility:"hidden",display:"block"},Tb={letterSpacing:0,fontWeight:400},Ub=["Webkit","O","Moz","ms"];function Vb(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=Ub.length;while(e--)if(b=Ub[e]+c,b in a)return b;return d}function Wb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=n._data(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&V(d)&&(f[g]=n._data(d,"olddisplay",Gb(d.nodeName)))):f[g]||(e=V(d),(c&&"none"!==c||!e)&&n._data(d,"olddisplay",e?c:n.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}function Xb(a,b,c){var d=Qb.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Yb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=n.css(a,c+U[f],!0,e)),d?("content"===c&&(g-=n.css(a,"padding"+U[f],!0,e)),"margin"!==c&&(g-=n.css(a,"border"+U[f]+"Width",!0,e))):(g+=n.css(a,"padding"+U[f],!0,e),"padding"!==c&&(g+=n.css(a,"border"+U[f]+"Width",!0,e)));return g}function Zb(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=Jb(a),g=l.boxSizing()&&"border-box"===n.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=Kb(a,b,f),(0>e||null==e)&&(e=a.style[b]),Ib.test(e))return e;d=g&&(l.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Yb(a,b,c||(g?"border":"content"),d,f)+"px"}n.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Kb(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":l.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=n.camelCase(b),i=a.style;if(b=n.cssProps[h]||(n.cssProps[h]=Vb(i,h)),g=n.cssHooks[b]||n.cssHooks[h],void 0===c)return g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b];if(f=typeof c,"string"===f&&(e=Rb.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(n.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||n.cssNumber[h]||(c+="px"),l.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),!(g&&"set"in g&&void 0===(c=g.set(a,c,d)))))try{i[b]="",i[b]=c}catch(j){}}},css:function(a,b,c,d){var e,f,g,h=n.camelCase(b);return b=n.cssProps[h]||(n.cssProps[h]=Vb(a.style,h)),g=n.cssHooks[b]||n.cssHooks[h],g&&"get"in g&&(f=g.get(a,!0,c)),void 0===f&&(f=Kb(a,b,d)),"normal"===f&&b in Tb&&(f=Tb[b]),""===c||c?(e=parseFloat(f),c===!0||n.isNumeric(e)?e||0:f):f}}),n.each(["height","width"],function(a,b){n.cssHooks[b]={get:function(a,c,d){return c?0===a.offsetWidth&&Pb.test(n.css(a,"display"))?n.swap(a,Sb,function(){return Zb(a,b,d)}):Zb(a,b,d):void 0},set:function(a,c,d){var e=d&&Jb(a);return Xb(a,c,d?Yb(a,b,d,l.boxSizing()&&"border-box"===n.css(a,"boxSizing",!1,e),e):0)}}}),l.opacity||(n.cssHooks.opacity={get:function(a,b){return Ob.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=n.isNumeric(b)?"alpha(opacity="+100*b+")":"",f=d&&d.filter||c.filter||"";c.zoom=1,(b>=1||""===b)&&""===n.trim(f.replace(Nb,""))&&c.removeAttribute&&(c.removeAttribute("filter"),""===b||d&&!d.filter)||(c.filter=Nb.test(f)?f.replace(Nb,e):f+" "+e)}}),n.cssHooks.marginRight=Mb(l.reliableMarginRight,function(a,b){return b?n.swap(a,{display:"inline-block"},Kb,[a,"marginRight"]):void 0}),n.each({margin:"",padding:"",border:"Width"},function(a,b){n.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+U[d]+b]=f[d]||f[d-2]||f[0];return e}},Hb.test(a)||(n.cssHooks[a+b].set=Xb)}),n.fn.extend({css:function(a,b){return W(this,function(a,b,c){var d,e,f={},g=0;if(n.isArray(b)){for(d=Jb(a),e=b.length;e>g;g++)f[b[g]]=n.css(a,b[g],!1,d);return f}return void 0!==c?n.style(a,b,c):n.css(a,b)
+},a,b,arguments.length>1)},show:function(){return Wb(this,!0)},hide:function(){return Wb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){V(this)?n(this).show():n(this).hide()})}});function $b(a,b,c,d,e){return new $b.prototype.init(a,b,c,d,e)}n.Tween=$b,$b.prototype={constructor:$b,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(n.cssNumber[c]?"":"px")},cur:function(){var a=$b.propHooks[this.prop];return a&&a.get?a.get(this):$b.propHooks._default.get(this)},run:function(a){var b,c=$b.propHooks[this.prop];return this.pos=b=this.options.duration?n.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):$b.propHooks._default.set(this),this}},$b.prototype.init.prototype=$b.prototype,$b.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=n.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){n.fx.step[a.prop]?n.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[n.cssProps[a.prop]]||n.cssHooks[a.prop])?n.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},$b.propHooks.scrollTop=$b.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},n.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},n.fx=$b.prototype.init,n.fx.step={};var _b,ac,bc=/^(?:toggle|show|hide)$/,cc=new RegExp("^(?:([+-])=|)("+T+")([a-z%]*)$","i"),dc=/queueHooks$/,ec=[jc],fc={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=cc.exec(b),f=e&&e[3]||(n.cssNumber[a]?"":"px"),g=(n.cssNumber[a]||"px"!==f&&+d)&&cc.exec(n.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,n.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function gc(){return setTimeout(function(){_b=void 0}),_b=n.now()}function hc(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=U[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function ic(a,b,c){for(var d,e=(fc[b]||[]).concat(fc["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function jc(a,b,c){var d,e,f,g,h,i,j,k,m=this,o={},p=a.style,q=a.nodeType&&V(a),r=n._data(a,"fxshow");c.queue||(h=n._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,m.always(function(){m.always(function(){h.unqueued--,n.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=n.css(a,"display"),k=Gb(a.nodeName),"none"===j&&(j=k),"inline"===j&&"none"===n.css(a,"float")&&(l.inlineBlockNeedsLayout&&"inline"!==k?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",l.shrinkWrapBlocks()||m.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],bc.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||n.style(a,d)}if(!n.isEmptyObject(o)){r?"hidden"in r&&(q=r.hidden):r=n._data(a,"fxshow",{}),f&&(r.hidden=!q),q?n(a).show():m.done(function(){n(a).hide()}),m.done(function(){var b;n._removeData(a,"fxshow");for(b in o)n.style(a,b,o[b])});for(d in o)g=ic(q?r[d]:0,d,m),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function kc(a,b){var c,d,e,f,g;for(c in a)if(d=n.camelCase(c),e=b[d],f=a[c],n.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=n.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function lc(a,b,c){var d,e,f=0,g=ec.length,h=n.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=_b||gc(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:n.extend({},b),opts:n.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:_b||gc(),duration:c.duration,tweens:[],createTween:function(b,c){var d=n.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(kc(k,j.opts.specialEasing);g>f;f++)if(d=ec[f].call(j,a,k,j.opts))return d;return n.map(k,ic,j),n.isFunction(j.opts.start)&&j.opts.start.call(a,j),n.fx.timer(n.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}n.Animation=n.extend(lc,{tweener:function(a,b){n.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],fc[c]=fc[c]||[],fc[c].unshift(b)},prefilter:function(a,b){b?ec.unshift(a):ec.push(a)}}),n.speed=function(a,b,c){var d=a&&"object"==typeof a?n.extend({},a):{complete:c||!c&&b||n.isFunction(a)&&a,duration:a,easing:c&&b||b&&!n.isFunction(b)&&b};return d.duration=n.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in n.fx.speeds?n.fx.speeds[d.duration]:n.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){n.isFunction(d.old)&&d.old.call(this),d.queue&&n.dequeue(this,d.queue)},d},n.fn.extend({fadeTo:function(a,b,c,d){return this.filter(V).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=n.isEmptyObject(a),f=n.speed(b,c,d),g=function(){var b=lc(this,n.extend({},a),f);(e||n._data(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=n.timers,g=n._data(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&dc.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&n.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=n._data(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=n.timers,g=d?d.length:0;for(c.finish=!0,n.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),n.each(["toggle","show","hide"],function(a,b){var c=n.fn[b];n.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(hc(b,!0),a,d,e)}}),n.each({slideDown:hc("show"),slideUp:hc("hide"),slideToggle:hc("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){n.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),n.timers=[],n.fx.tick=function(){var a,b=n.timers,c=0;for(_b=n.now();c<b.length;c++)a=b[c],a()||b[c]!==a||b.splice(c--,1);b.length||n.fx.stop(),_b=void 0},n.fx.timer=function(a){n.timers.push(a),a()?n.fx.start():n.timers.pop()},n.fx.interval=13,n.fx.start=function(){ac||(ac=setInterval(n.fx.tick,n.fx.interval))},n.fx.stop=function(){clearInterval(ac),ac=null},n.fx.speeds={slow:600,fast:200,_default:400},n.fn.delay=function(a,b){return a=n.fx?n.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},function(){var a,b,c,d,e=z.createElement("div");e.setAttribute("className","t"),e.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",a=e.getElementsByTagName("a")[0],c=z.createElement("select"),d=c.appendChild(z.createElement("option")),b=e.getElementsByTagName("input")[0],a.style.cssText="top:1px",l.getSetAttribute="t"!==e.className,l.style=/top/.test(a.getAttribute("style")),l.hrefNormalized="/a"===a.getAttribute("href"),l.checkOn=!!b.value,l.optSelected=d.selected,l.enctype=!!z.createElement("form").enctype,c.disabled=!0,l.optDisabled=!d.disabled,b=z.createElement("input"),b.setAttribute("value",""),l.input=""===b.getAttribute("value"),b.value="t",b.setAttribute("type","radio"),l.radioValue="t"===b.value,a=b=c=d=e=null}();var mc=/\r/g;n.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=n.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,n(this).val()):a,null==e?e="":"number"==typeof e?e+="":n.isArray(e)&&(e=n.map(e,function(a){return null==a?"":a+""})),b=n.valHooks[this.type]||n.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=n.valHooks[e.type]||n.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(mc,""):null==c?"":c)}}}),n.extend({valHooks:{option:{get:function(a){var b=n.find.attr(a,"value");return null!=b?b:n.text(a)}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(l.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&n.nodeName(c.parentNode,"optgroup"))){if(b=n(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=n.makeArray(b),g=e.length;while(g--)if(d=e[g],n.inArray(n.valHooks.option.get(d),f)>=0)try{d.selected=c=!0}catch(h){d.scrollHeight}else d.selected=!1;return c||(a.selectedIndex=-1),e}}}}),n.each(["radio","checkbox"],function(){n.valHooks[this]={set:function(a,b){return n.isArray(b)?a.checked=n.inArray(n(a).val(),b)>=0:void 0}},l.checkOn||(n.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var nc,oc,pc=n.expr.attrHandle,qc=/^(?:checked|selected)$/i,rc=l.getSetAttribute,sc=l.input;n.fn.extend({attr:function(a,b){return W(this,n.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){n.removeAttr(this,a)})}}),n.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===L?n.prop(a,b,c):(1===f&&n.isXMLDoc(a)||(b=b.toLowerCase(),d=n.attrHooks[b]||(n.expr.match.bool.test(b)?oc:nc)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=n.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void n.removeAttr(a,b))},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(F);if(f&&1===a.nodeType)while(c=f[e++])d=n.propFix[c]||c,n.expr.match.bool.test(c)?sc&&rc||!qc.test(c)?a[d]=!1:a[n.camelCase("default-"+c)]=a[d]=!1:n.attr(a,c,""),a.removeAttribute(rc?c:d)},attrHooks:{type:{set:function(a,b){if(!l.radioValue&&"radio"===b&&n.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),oc={set:function(a,b,c){return b===!1?n.removeAttr(a,c):sc&&rc||!qc.test(c)?a.setAttribute(!rc&&n.propFix[c]||c,c):a[n.camelCase("default-"+c)]=a[c]=!0,c}},n.each(n.expr.match.bool.source.match(/\w+/g),function(a,b){var c=pc[b]||n.find.attr;pc[b]=sc&&rc||!qc.test(b)?function(a,b,d){var e,f;return d||(f=pc[b],pc[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,pc[b]=f),e}:function(a,b,c){return c?void 0:a[n.camelCase("default-"+b)]?b.toLowerCase():null}}),sc&&rc||(n.attrHooks.value={set:function(a,b,c){return n.nodeName(a,"input")?void(a.defaultValue=b):nc&&nc.set(a,b,c)}}),rc||(nc={set:function(a,b,c){var d=a.getAttributeNode(c);return d||a.setAttributeNode(d=a.ownerDocument.createAttribute(c)),d.value=b+="","value"===c||b===a.getAttribute(c)?b:void 0}},pc.id=pc.name=pc.coords=function(a,b,c){var d;return c?void 0:(d=a.getAttributeNode(b))&&""!==d.value?d.value:null},n.valHooks.button={get:function(a,b){var c=a.getAttributeNode(b);return c&&c.specified?c.value:void 0},set:nc.set},n.attrHooks.contenteditable={set:function(a,b,c){nc.set(a,""===b?!1:b,c)}},n.each(["width","height"],function(a,b){n.attrHooks[b]={set:function(a,c){return""===c?(a.setAttribute(b,"auto"),c):void 0}}})),l.style||(n.attrHooks.style={get:function(a){return a.style.cssText||void 0},set:function(a,b){return a.style.cssText=b+""}});var tc=/^(?:input|select|textarea|button|object)$/i,uc=/^(?:a|area)$/i;n.fn.extend({prop:function(a,b){return W(this,n.prop,a,b,arguments.length>1)},removeProp:function(a){return a=n.propFix[a]||a,this.each(function(){try{this[a]=void 0,delete this[a]}catch(b){}})}}),n.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!n.isXMLDoc(a),f&&(b=n.propFix[b]||b,e=n.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=n.find.attr(a,"tabindex");return b?parseInt(b,10):tc.test(a.nodeName)||uc.test(a.nodeName)&&a.href?0:-1}}}}),l.hrefNormalized||n.each(["href","src"],function(a,b){n.propHooks[b]={get:function(a){return a.getAttribute(b,4)}}}),l.optSelected||(n.propHooks.selected={get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}}),n.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){n.propFix[this.toLowerCase()]=this}),l.enctype||(n.propFix.enctype="encoding");var vc=/[\t\r\n\f]/g;n.fn.extend({addClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j="string"==typeof a&&a;if(n.isFunction(a))return this.each(function(b){n(this).addClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(F)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(vc," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=n.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j=0===arguments.length||"string"==typeof a&&a;if(n.isFunction(a))return this.each(function(b){n(this).removeClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(F)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(vc," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?n.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(n.isFunction(a)?function(c){n(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=n(this),f=a.match(F)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===L||"boolean"===c)&&(this.className&&n._data(this,"__className__",this.className),this.className=this.className||a===!1?"":n._data(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(vc," ").indexOf(b)>=0)return!0;return!1}}),n.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){n.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),n.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var wc=n.now(),xc=/\?/,yc=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;n.parseJSON=function(b){if(a.JSON&&a.JSON.parse)return a.JSON.parse(b+"");var c,d=null,e=n.trim(b+"");return e&&!n.trim(e.replace(yc,function(a,b,e,f){return c&&b&&(d=0),0===d?a:(c=e||b,d+=!f-!e,"")}))?Function("return "+e)():n.error("Invalid JSON: "+b)},n.parseXML=function(b){var c,d;if(!b||"string"!=typeof b)return null;try{a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b))}catch(e){c=void 0}return c&&c.documentElement&&!c.getElementsByTagName("parsererror").length||n.error("Invalid XML: "+b),c};var zc,Ac,Bc=/#.*$/,Cc=/([?&])_=[^&]*/,Dc=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Ec=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Fc=/^(?:GET|HEAD)$/,Gc=/^\/\//,Hc=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Ic={},Jc={},Kc="*/".concat("*");try{Ac=location.href}catch(Lc){Ac=z.createElement("a"),Ac.href="",Ac=Ac.href}zc=Hc.exec(Ac.toLowerCase())||[];function Mc(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(F)||[];if(n.isFunction(c))while(d=f[e++])"+"===d.charAt(0)?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Nc(a,b,c,d){var e={},f=a===Jc;function g(h){var i;return e[h]=!0,n.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Oc(a,b){var c,d,e=n.ajaxSettings.flatOptions||{};for(d in b)void 0!==b[d]&&((e[d]?a:c||(c={}))[d]=b[d]);return c&&n.extend(!0,a,c),a}function Pc(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===e&&(e=a.mimeType||b.getResponseHeader("Content-Type"));if(e)for(g in h)if(h[g]&&h[g].test(e)){i.unshift(g);break}if(i[0]in c)f=i[0];else{for(g in c){if(!i[0]||a.converters[g+" "+i[0]]){f=g;break}d||(d=g)}f=f||d}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function Qc(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}n.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ac,type:"GET",isLocal:Ec.test(zc[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Kc,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":n.parseJSON,"text xml":n.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Oc(Oc(a,n.ajaxSettings),b):Oc(n.ajaxSettings,a)},ajaxPrefilter:Mc(Ic),ajaxTransport:Mc(Jc),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=n.ajaxSetup({},b),l=k.context||k,m=k.context&&(l.nodeType||l.jquery)?n(l):n.event,o=n.Deferred(),p=n.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!j){j={};while(b=Dc.exec(f))j[b[1].toLowerCase()]=b[2]}b=j[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?f:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return i&&i.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||Ac)+"").replace(Bc,"").replace(Gc,zc[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=n.trim(k.dataType||"*").toLowerCase().match(F)||[""],null==k.crossDomain&&(c=Hc.exec(k.url.toLowerCase()),k.crossDomain=!(!c||c[1]===zc[1]&&c[2]===zc[2]&&(c[3]||("http:"===c[1]?"80":"443"))===(zc[3]||("http:"===zc[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=n.param(k.data,k.traditional)),Nc(Ic,k,b,v),2===t)return v;h=k.global,h&&0===n.active++&&n.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!Fc.test(k.type),e=k.url,k.hasContent||(k.data&&(e=k.url+=(xc.test(e)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=Cc.test(e)?e.replace(Cc,"$1_="+wc++):e+(xc.test(e)?"&":"?")+"_="+wc++)),k.ifModified&&(n.lastModified[e]&&v.setRequestHeader("If-Modified-Since",n.lastModified[e]),n.etag[e]&&v.setRequestHeader("If-None-Match",n.etag[e])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+Kc+"; q=0.01":""):k.accepts["*"]);for(d in k.headers)v.setRequestHeader(d,k.headers[d]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(d in{success:1,error:1,complete:1})v[d](k[d]);if(i=Nc(Jc,k,b,v)){v.readyState=1,h&&m.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,i.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,c,d){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),i=void 0,f=d||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,c&&(u=Pc(k,v,c)),u=Qc(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(n.lastModified[e]=w),w=v.getResponseHeader("etag"),w&&(n.etag[e]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,h&&m.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),h&&(m.trigger("ajaxComplete",[v,k]),--n.active||n.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return n.get(a,b,c,"json")},getScript:function(a,b){return n.get(a,void 0,b,"script")}}),n.each(["get","post"],function(a,b){n[b]=function(a,c,d,e){return n.isFunction(c)&&(e=e||d,d=c,c=void 0),n.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),n.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){n.fn[b]=function(a){return this.on(b,a)}}),n._evalUrl=function(a){return n.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},n.fn.extend({wrapAll:function(a){if(n.isFunction(a))return this.each(function(b){n(this).wrapAll(a.call(this,b))});if(this[0]){var b=n(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&1===a.firstChild.nodeType)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return this.each(n.isFunction(a)?function(b){n(this).wrapInner(a.call(this,b))}:function(){var b=n(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=n.isFunction(a);return this.each(function(c){n(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){n.nodeName(this,"body")||n(this).replaceWith(this.childNodes)}).end()}}),n.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0||!l.reliableHiddenOffsets()&&"none"===(a.style&&a.style.display||n.css(a,"display"))},n.expr.filters.visible=function(a){return!n.expr.filters.hidden(a)};var Rc=/%20/g,Sc=/\[\]$/,Tc=/\r?\n/g,Uc=/^(?:submit|button|image|reset|file)$/i,Vc=/^(?:input|select|textarea|keygen)/i;function Wc(a,b,c,d){var e;if(n.isArray(b))n.each(b,function(b,e){c||Sc.test(a)?d(a,e):Wc(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==n.type(b))d(a,b);else for(e in b)Wc(a+"["+e+"]",b[e],c,d)}n.param=function(a,b){var c,d=[],e=function(a,b){b=n.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=n.ajaxSettings&&n.ajaxSettings.traditional),n.isArray(a)||a.jquery&&!n.isPlainObject(a))n.each(a,function(){e(this.name,this.value)});else for(c in a)Wc(c,a[c],b,e);return d.join("&").replace(Rc,"+")},n.fn.extend({serialize:function(){return n.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=n.prop(this,"elements");return a?n.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!n(this).is(":disabled")&&Vc.test(this.nodeName)&&!Uc.test(a)&&(this.checked||!X.test(a))}).map(function(a,b){var c=n(this).val();return null==c?null:n.isArray(c)?n.map(c,function(a){return{name:b.name,value:a.replace(Tc,"\r\n")}}):{name:b.name,value:c.replace(Tc,"\r\n")}}).get()}}),n.ajaxSettings.xhr=void 0!==a.ActiveXObject?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&$c()||_c()}:$c;var Xc=0,Yc={},Zc=n.ajaxSettings.xhr();a.ActiveXObject&&n(a).on("unload",function(){for(var a in Yc)Yc[a](void 0,!0)}),l.cors=!!Zc&&"withCredentials"in Zc,Zc=l.ajax=!!Zc,Zc&&n.ajaxTransport(function(a){if(!a.crossDomain||l.cors){var b;return{send:function(c,d){var e,f=a.xhr(),g=++Xc;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)void 0!==c[e]&&f.setRequestHeader(e,c[e]+"");f.send(a.hasContent&&a.data||null),b=function(c,e){var h,i,j;if(b&&(e||4===f.readyState))if(delete Yc[g],b=void 0,f.onreadystatechange=n.noop,e)4!==f.readyState&&f.abort();else{j={},h=f.status,"string"==typeof f.responseText&&(j.text=f.responseText);try{i=f.statusText}catch(k){i=""}h||!a.isLocal||a.crossDomain?1223===h&&(h=204):h=j.text?200:404}j&&d(h,i,j,f.getAllResponseHeaders())},a.async?4===f.readyState?setTimeout(b):f.onreadystatechange=Yc[g]=b:b()},abort:function(){b&&b(void 0,!0)}}}});function $c(){try{return new a.XMLHttpRequest}catch(b){}}function _c(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}n.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return n.globalEval(a),a}}}),n.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),n.ajaxTransport("script",function(a){if(a.crossDomain){var b,c=z.head||n("head")[0]||z.documentElement;return{send:function(d,e){b=z.createElement("script"),b.async=!0,a.scriptCharset&&(b.charset=a.scriptCharset),b.src=a.url,b.onload=b.onreadystatechange=function(a,c){(c||!b.readyState||/loaded|complete/.test(b.readyState))&&(b.onload=b.onreadystatechange=null,b.parentNode&&b.parentNode.removeChild(b),b=null,c||e(200,"success"))},c.insertBefore(b,c.firstChild)},abort:function(){b&&b.onload(void 0,!0)}}}});var ad=[],bd=/(=)\?(?=&|$)|\?\?/;n.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=ad.pop()||n.expando+"_"+wc++;return this[a]=!0,a}}),n.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(bd.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&bd.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=n.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(bd,"$1"+e):b.jsonp!==!1&&(b.url+=(xc.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||n.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,ad.push(e)),g&&n.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),n.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||z;var d=v.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=n.buildFragment([a],b,e),e&&e.length&&n(e).remove(),n.merge([],d.childNodes))};var cd=n.fn.load;n.fn.load=function(a,b,c){if("string"!=typeof a&&cd)return cd.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=a.slice(h,a.length),a=a.slice(0,h)),n.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(f="POST"),g.length>0&&n.ajax({url:a,type:f,dataType:"html",data:b}).done(function(a){e=arguments,g.html(d?n("<div>").append(n.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,e||[a.responseText,b,a])}),this},n.expr.filters.animated=function(a){return n.grep(n.timers,function(b){return a===b.elem}).length};var dd=a.document.documentElement;function ed(a){return n.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}n.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=n.css(a,"position"),l=n(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=n.css(a,"top"),i=n.css(a,"left"),j=("absolute"===k||"fixed"===k)&&n.inArray("auto",[f,i])>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),n.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},n.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){n.offset.setOffset(this,a,b)});var b,c,d={top:0,left:0},e=this[0],f=e&&e.ownerDocument;if(f)return b=f.documentElement,n.contains(b,e)?(typeof e.getBoundingClientRect!==L&&(d=e.getBoundingClientRect()),c=ed(f),{top:d.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:d.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):d},position:function(){if(this[0]){var a,b,c={top:0,left:0},d=this[0];return"fixed"===n.css(d,"position")?b=d.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),n.nodeName(a[0],"html")||(c=a.offset()),c.top+=n.css(a[0],"borderTopWidth",!0),c.left+=n.css(a[0],"borderLeftWidth",!0)),{top:b.top-c.top-n.css(d,"marginTop",!0),left:b.left-c.left-n.css(d,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||dd;while(a&&!n.nodeName(a,"html")&&"static"===n.css(a,"position"))a=a.offsetParent;return a||dd})}}),n.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c=/Y/.test(b);n.fn[a]=function(d){return W(this,function(a,d,e){var f=ed(a);return void 0===e?f?b in f?f[b]:f.document.documentElement[d]:a[d]:void(f?f.scrollTo(c?n(f).scrollLeft():e,c?e:n(f).scrollTop()):a[d]=e)},a,d,arguments.length,null)}}),n.each(["top","left"],function(a,b){n.cssHooks[b]=Mb(l.pixelPosition,function(a,c){return c?(c=Kb(a,b),Ib.test(c)?n(a).position()[b]+"px":c):void 0})}),n.each({Height:"height",Width:"width"},function(a,b){n.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){n.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return W(this,function(b,c,d){var e;return n.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?n.css(b,c,g):n.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),n.fn.size=function(){return this.length},n.fn.andSelf=n.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return n});var fd=a.jQuery,gd=a.$;return n.noConflict=function(b){return a.$===n&&(a.$=gd),b&&a.jQuery===n&&(a.jQuery=fd),n},typeof b===L&&(a.jQuery=a.$=n),n}); \ No newline at end of file
diff --git a/container-search-gui/src/main/resources/gui/js/jquery.easing.min.js b/container-search-gui/src/main/resources/gui/js/jquery.easing.min.js
new file mode 100644
index 00000000000..4a54fcd4300
--- /dev/null
+++ b/container-search-gui/src/main/resources/gui/js/jquery.easing.min.js
@@ -0,0 +1,44 @@
+/*
+ * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
+ *
+ * Uses the built in easing capabilities added In jQuery 1.1
+ * to offer multiple easing options
+ *
+ * TERMS OF USE - EASING EQUATIONS
+ *
+ * Open source under the BSD License.
+ *
+ * Copyright © 2001 Robert Penner
+ * All rights reserved.
+ *
+ * TERMS OF USE - jQuery Easing
+ *
+ * Open source under the BSD License.
+ *
+ * Copyright © 2008 George McGinley Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the author nor the names of contributors may be used to endorse
+ * or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+*/
+jQuery.easing.jswing=jQuery.easing.swing;jQuery.extend(jQuery.easing,{def:"easeOutQuad",swing:function(e,f,a,h,g){return jQuery.easing[jQuery.easing.def](e,f,a,h,g)},easeInQuad:function(e,f,a,h,g){return h*(f/=g)*f+a},easeOutQuad:function(e,f,a,h,g){return -h*(f/=g)*(f-2)+a},easeInOutQuad:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f+a}return -h/2*((--f)*(f-2)-1)+a},easeInCubic:function(e,f,a,h,g){return h*(f/=g)*f*f+a},easeOutCubic:function(e,f,a,h,g){return h*((f=f/g-1)*f*f+1)+a},easeInOutCubic:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f+a}return h/2*((f-=2)*f*f+2)+a},easeInQuart:function(e,f,a,h,g){return h*(f/=g)*f*f*f+a},easeOutQuart:function(e,f,a,h,g){return -h*((f=f/g-1)*f*f*f-1)+a},easeInOutQuart:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f+a}return -h/2*((f-=2)*f*f*f-2)+a},easeInQuint:function(e,f,a,h,g){return h*(f/=g)*f*f*f*f+a},easeOutQuint:function(e,f,a,h,g){return h*((f=f/g-1)*f*f*f*f+1)+a},easeInOutQuint:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f*f+a}return h/2*((f-=2)*f*f*f*f+2)+a},easeInSine:function(e,f,a,h,g){return -h*Math.cos(f/g*(Math.PI/2))+h+a},easeOutSine:function(e,f,a,h,g){return h*Math.sin(f/g*(Math.PI/2))+a},easeInOutSine:function(e,f,a,h,g){return -h/2*(Math.cos(Math.PI*f/g)-1)+a},easeInExpo:function(e,f,a,h,g){return(f==0)?a:h*Math.pow(2,10*(f/g-1))+a},easeOutExpo:function(e,f,a,h,g){return(f==g)?a+h:h*(-Math.pow(2,-10*f/g)+1)+a},easeInOutExpo:function(e,f,a,h,g){if(f==0){return a}if(f==g){return a+h}if((f/=g/2)<1){return h/2*Math.pow(2,10*(f-1))+a}return h/2*(-Math.pow(2,-10*--f)+2)+a},easeInCirc:function(e,f,a,h,g){return -h*(Math.sqrt(1-(f/=g)*f)-1)+a},easeOutCirc:function(e,f,a,h,g){return h*Math.sqrt(1-(f=f/g-1)*f)+a},easeInOutCirc:function(e,f,a,h,g){if((f/=g/2)<1){return -h/2*(Math.sqrt(1-f*f)-1)+a}return h/2*(Math.sqrt(1-(f-=2)*f)+1)+a},easeInElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k)==1){return e+l}if(!j){j=k*0.3}if(g<Math.abs(l)){g=l;var i=j/4}else{var i=j/(2*Math.PI)*Math.asin(l/g)}return -(g*Math.pow(2,10*(h-=1))*Math.sin((h*k-i)*(2*Math.PI)/j))+e},easeOutElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k)==1){return e+l}if(!j){j=k*0.3}if(g<Math.abs(l)){g=l;var i=j/4}else{var i=j/(2*Math.PI)*Math.asin(l/g)}return g*Math.pow(2,-10*h)*Math.sin((h*k-i)*(2*Math.PI)/j)+l+e},easeInOutElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k/2)==2){return e+l}if(!j){j=k*(0.3*1.5)}if(g<Math.abs(l)){g=l;var i=j/4}else{var i=j/(2*Math.PI)*Math.asin(l/g)}if(h<1){return -0.5*(g*Math.pow(2,10*(h-=1))*Math.sin((h*k-i)*(2*Math.PI)/j))+e}return g*Math.pow(2,-10*(h-=1))*Math.sin((h*k-i)*(2*Math.PI)/j)*0.5+l+e},easeInBack:function(e,f,a,i,h,g){if(g==undefined){g=1.70158}return i*(f/=h)*f*((g+1)*f-g)+a},easeOutBack:function(e,f,a,i,h,g){if(g==undefined){g=1.70158}return i*((f=f/h-1)*f*((g+1)*f+g)+1)+a},easeInOutBack:function(e,f,a,i,h,g){if(g==undefined){g=1.70158}if((f/=h/2)<1){return i/2*(f*f*(((g*=(1.525))+1)*f-g))+a}return i/2*((f-=2)*f*(((g*=(1.525))+1)*f+g)+2)+a},easeInBounce:function(e,f,a,h,g){return h-jQuery.easing.easeOutBounce(e,g-f,0,h,g)+a},easeOutBounce:function(e,f,a,h,g){if((f/=g)<(1/2.75)){return h*(7.5625*f*f)+a}else{if(f<(2/2.75)){return h*(7.5625*(f-=(1.5/2.75))*f+0.75)+a}else{if(f<(2.5/2.75)){return h*(7.5625*(f-=(2.25/2.75))*f+0.9375)+a}else{return h*(7.5625*(f-=(2.625/2.75))*f+0.984375)+a}}}},easeInOutBounce:function(e,f,a,h,g){if(f<g/2){return jQuery.easing.easeInBounce(e,f*2,0,h,g)*0.5+a}return jQuery.easing.easeOutBounce(e,f*2-g,0,h,g)*0.5+h*0.5+a}}); \ No newline at end of file
diff --git a/container-search/src/main/java/com/yahoo/fs4/DocumentInfo.java b/container-search/src/main/java/com/yahoo/fs4/DocumentInfo.java
index 075ec60c099..e5ab00fb139 100644
--- a/container-search/src/main/java/com/yahoo/fs4/DocumentInfo.java
+++ b/container-search/src/main/java/com/yahoo/fs4/DocumentInfo.java
@@ -28,9 +28,9 @@ public class DocumentInfo implements Cloneable {
}
public DocumentInfo(GlobalId globalId, int metric, int partId, int distributionKey) {
- this.globalId=globalId;
- this.metric=metric;
- this.partId=partId;
+ this.globalId = globalId;
+ this.metric = metric;
+ this.partId = partId;
this.distributionKey = distributionKey;
}
diff --git a/container-search/src/main/java/com/yahoo/fs4/HexByteIterator.java b/container-search/src/main/java/com/yahoo/fs4/HexByteIterator.java
index 5df9e260c15..78ba857c475 100644
--- a/container-search/src/main/java/com/yahoo/fs4/HexByteIterator.java
+++ b/container-search/src/main/java/com/yahoo/fs4/HexByteIterator.java
@@ -8,7 +8,7 @@ import java.util.Iterator;
* Provides sequential access to each byte of a buffer
* as a hexadecimal string of length 2.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public final class HexByteIterator implements Iterator<String> {
private final ByteBuffer buffer;
diff --git a/container-search/src/main/java/com/yahoo/fs4/PacketDumper.java b/container-search/src/main/java/com/yahoo/fs4/PacketDumper.java
index 123403a1c8d..6b2c792837a 100644
--- a/container-search/src/main/java/com/yahoo/fs4/PacketDumper.java
+++ b/container-search/src/main/java/com/yahoo/fs4/PacketDumper.java
@@ -20,7 +20,7 @@ import com.yahoo.search.Query;
/**
* Responsible for dumping query &amp; query result packets
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class PacketDumper implements PacketListener {
/** High level representation of packet types (e.g. query, result, ...) */
diff --git a/container-search/src/main/java/com/yahoo/fs4/PacketListener.java b/container-search/src/main/java/com/yahoo/fs4/PacketListener.java
index a876caf9699..113da03b420 100644
--- a/container-search/src/main/java/com/yahoo/fs4/PacketListener.java
+++ b/container-search/src/main/java/com/yahoo/fs4/PacketListener.java
@@ -8,7 +8,7 @@ import com.yahoo.fs4.mplex.FS4Channel;
/**
* Interface for recieving notifications of packets sent or recieved.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public interface PacketListener {
void packetSent(FS4Channel channel, BasicPacket packet, ByteBuffer serializedForm);
diff --git a/container-search/src/main/java/com/yahoo/fs4/PacketNotificationsBroadcaster.java b/container-search/src/main/java/com/yahoo/fs4/PacketNotificationsBroadcaster.java
index 06c908cac4b..1be79031d1b 100644
--- a/container-search/src/main/java/com/yahoo/fs4/PacketNotificationsBroadcaster.java
+++ b/container-search/src/main/java/com/yahoo/fs4/PacketNotificationsBroadcaster.java
@@ -8,7 +8,7 @@ import com.yahoo.fs4.mplex.FS4Channel;
/**
* Broadcasts packet notifications to a list of listeners.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class PacketNotificationsBroadcaster implements PacketListener {
diff --git a/container-search/src/main/java/com/yahoo/fs4/PacketQueryTracer.java b/container-search/src/main/java/com/yahoo/fs4/PacketQueryTracer.java
index ded5db99594..b577ef31ad8 100644
--- a/container-search/src/main/java/com/yahoo/fs4/PacketQueryTracer.java
+++ b/container-search/src/main/java/com/yahoo/fs4/PacketQueryTracer.java
@@ -9,7 +9,7 @@ import com.yahoo.search.Query;
/**
* Adds packets to the query context
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class PacketQueryTracer implements PacketListener {
diff --git a/container-search/src/main/java/com/yahoo/fs4/QueryResultPacket.java b/container-search/src/main/java/com/yahoo/fs4/QueryResultPacket.java
index b18d1e88a4b..239101184ea 100644
--- a/container-search/src/main/java/com/yahoo/fs4/QueryResultPacket.java
+++ b/container-search/src/main/java/com/yahoo/fs4/QueryResultPacket.java
@@ -125,7 +125,7 @@ public class QueryResultPacket extends Packet {
soonActiveDocs = buffer.getLong();
degradedReason = buffer.getInt();
- decodeDocuments(buffer,documentCount);
+ decodeDocuments(buffer, documentCount);
if (propsFeature) {
int numMaps = buffer.getInt();
propsArray = new FS4Properties[numMaps];
diff --git a/container-search/src/main/java/com/yahoo/fs4/mplex/Backend.java b/container-search/src/main/java/com/yahoo/fs4/mplex/Backend.java
index 81192be035a..68a13c06e32 100644
--- a/container-search/src/main/java/com/yahoo/fs4/mplex/Backend.java
+++ b/container-search/src/main/java/com/yahoo/fs4/mplex/Backend.java
@@ -61,7 +61,7 @@ public class Backend implements ConnectionFactory {
private final ConnectionPool connectionPool;
private final PacketDumper packetDumper;
private final AtomicInteger connectionCount = new AtomicInteger(0);
-
+ private final Optional<Integer> distributionKey;
/**
* For unit testing. do not use
@@ -74,9 +74,15 @@ public class Backend implements ConnectionFactory {
packetDumper = null;
address = null;
connectionPool = new ConnectionPool();
+ distributionKey = Optional.empty();
}
- public Backend(String host, int port, String serverDiscriminator, ListenerPool listenerPool, ConnectionPool connectionPool) {
+ public Backend(String host,
+ int port,
+ String serverDiscriminator,
+ ListenerPool listenerPool,
+ ConnectionPool connectionPool,
+ Optional<Integer> distributionKey) {
String fileNamePattern = "qrs." + serverDiscriminator + '.' + host + ":" + port + ".%s" + ".dump";
packetDumper = new PacketDumper(new File(Defaults.getDefaults().underVespaHome("logs/vespa/qrs/")),
fileNamePattern);
@@ -86,6 +92,7 @@ public class Backend implements ConnectionFactory {
this.port = port;
address = new InetSocketAddress(host, port);
this.connectionPool = connectionPool;
+ this.distributionKey = distributionKey;
}
private void logWarning(String attemptDescription, Exception e) {
@@ -96,11 +103,13 @@ public class Backend implements ConnectionFactory {
log.log(Level.INFO, "Exception on " + attemptDescription + " '" + host + ":" + port + "': " + Exceptions.toMessageString(e));
}
+ /** Returns the distribution key of the content node this represents, or empty if it is a dispatch node */
+ public Optional<Integer> distributionKey() { return distributionKey; }
+
// ============================================================
// ==== connection pool stuff
// ============================================================
-
/**
* Fetch a connection from the connection pool. If the pool
* is empty we create a connection.
@@ -187,10 +196,7 @@ public class Backend implements ConnectionFactory {
//==== channel management
//============================================================
- /**
- * Open a new channel to fdispatch. Analogous to the "Channel"
- * concept as used in FS4.
- */
+ /** Opens a new channel to fdispatch. Analogous to the "Channel" concept as used in FS4. */
public FS4Channel openChannel () {
int cachedChannelId;
synchronized (this) {
diff --git a/container-search/src/main/java/com/yahoo/fs4/mplex/FS4Channel.java b/container-search/src/main/java/com/yahoo/fs4/mplex/FS4Channel.java
index b0fce14e73f..237b0cdb8e2 100644
--- a/container-search/src/main/java/com/yahoo/fs4/mplex/FS4Channel.java
+++ b/container-search/src/main/java/com/yahoo/fs4/mplex/FS4Channel.java
@@ -1,10 +1,10 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
package com.yahoo.fs4.mplex;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -16,23 +16,18 @@ import com.yahoo.fs4.ChannelTimeoutException;
import com.yahoo.fs4.Packet;
import com.yahoo.search.Query;
-
-
/**
- *
* This class is used to represent a "channel" in the FS4 protocol.
* A channel represents a session between a client and the fdispatch.
* Internally this class has a response queue used by the backend
* for queueing up FS4 packets that belong to this channel (or
* <em>session</em>, which might be a more appropriate name for it).
- *
- * <P>
* Outbound packets are handed off to the FS4Connection.
*
* @author Bjorn Borud
*/
-public class FS4Channel
-{
+public class FS4Channel {
+
private static Logger log = Logger.getLogger(FS4Channel.class.getName());
private Integer channelId;
@@ -67,13 +62,14 @@ public class FS4Channel
return query;
}
- /**
- * @return returns an Integer representing the (fs4) channel id
- */
+ /** Returns the (fs4) channel id */
public Integer getChannelId () {
return channelId;
}
+ /** Returns the distribution key of the content node this represents, or empty if it is a dispatch node */
+ public Optional<Integer> distributionKey() { return backend == null ? Optional.empty() : backend.distributionKey(); }
+
/**
* Closes the channel
*/
diff --git a/container-search/src/main/java/com/yahoo/prelude/cluster/ClusterSearcher.java b/container-search/src/main/java/com/yahoo/prelude/cluster/ClusterSearcher.java
index b1399c6cc8d..0d9534457e9 100644
--- a/container-search/src/main/java/com/yahoo/prelude/cluster/ClusterSearcher.java
+++ b/container-search/src/main/java/com/yahoo/prelude/cluster/ClusterSearcher.java
@@ -192,8 +192,7 @@ public class ClusterSearcher extends Searcher {
private static ClusterParams makeClusterParams(int searchclusterIndex,
LegacyEmulationConfig emulConfig,
int dispatchIndex) {
- return new ClusterParams(searchclusterIndex,
- "sc" + searchclusterIndex + ".num" + dispatchIndex,
+ return new ClusterParams("sc" + searchclusterIndex + ".num" + dispatchIndex,
emulConfig);
}
diff --git a/container-search/src/main/java/com/yahoo/prelude/fastsearch/CacheControl.java b/container-search/src/main/java/com/yahoo/prelude/fastsearch/CacheControl.java
index b7e16b6c082..dd5b3caf0c5 100644
--- a/container-search/src/main/java/com/yahoo/prelude/fastsearch/CacheControl.java
+++ b/container-search/src/main/java/com/yahoo/prelude/fastsearch/CacheControl.java
@@ -8,6 +8,8 @@ import com.yahoo.fs4.QueryResultPacket;
import com.yahoo.search.Query;
import com.yahoo.processing.request.CompoundName;
+import java.util.Optional;
+
/**
* The cache control logic for FastSearcher
@@ -16,7 +18,7 @@ import com.yahoo.processing.request.CompoundName;
*/
public class CacheControl {
- private static final CompoundName nocachewrite=new CompoundName("nocachewrite");
+ public static final CompoundName nocachewrite=new CompoundName("nocachewrite");
/** Whether this CacheControl actually should cache hits at all. */
private final boolean activeCache;
@@ -84,7 +86,7 @@ public class CacheControl {
}
}
- void cache(CacheKey key, Query query, DocsumPacketKey[] packetKeys, Packet[] packets) {
+ void cache(CacheKey key, Query query, DocsumPacketKey[] packetKeys, Packet[] packets, Optional<Integer> distributionKey) {
if ( ! activeCache) return;
if (query.getNoCache()) return;
@@ -92,7 +94,7 @@ public class CacheControl {
PacketWrapper wrapper = lookup(key, query);
if (wrapper == null) {
- wrapper = new PacketWrapper(key, packetKeys,packets);
+ wrapper = new PacketWrapper(key, packetKeys, packets, distributionKey);
long now = System.currentTimeMillis();
synchronized (packetCache) {
packetCache.put(key, wrapper, now);
diff --git a/container-search/src/main/java/com/yahoo/prelude/fastsearch/ClusterParams.java b/container-search/src/main/java/com/yahoo/prelude/fastsearch/ClusterParams.java
index f95f303e87c..3dfa506a967 100644
--- a/container-search/src/main/java/com/yahoo/prelude/fastsearch/ClusterParams.java
+++ b/container-search/src/main/java/com/yahoo/prelude/fastsearch/ClusterParams.java
@@ -11,29 +11,20 @@ import com.yahoo.container.search.LegacyEmulationConfig;
*/
public class ClusterParams {
- public final int clusterNumber;
public final String searcherName;
public final LegacyEmulationConfig emulation;
/**
- * For compatibility
- */
- public ClusterParams(int number, String name) {
- this(number, name, new LegacyEmulationConfig(new LegacyEmulationConfig.Builder()));
- }
-
- /**
* For testcases only
*/
public ClusterParams(String name) {
- this(0, name);
+ this(name, new LegacyEmulationConfig(new LegacyEmulationConfig.Builder()));
}
/**
* Make up full ClusterParams
*/
- public ClusterParams(int number, String name, LegacyEmulationConfig cfg) {
- this.clusterNumber = number;
+ public ClusterParams(String name, LegacyEmulationConfig cfg) {
this.searcherName = name;
this.emulation = cfg;
}
diff --git a/container-search/src/main/java/com/yahoo/prelude/fastsearch/FS4ResourcePool.java b/container-search/src/main/java/com/yahoo/prelude/fastsearch/FS4ResourcePool.java
index 8d4e9418c1c..e933f4857b3 100644
--- a/container-search/src/main/java/com/yahoo/prelude/fastsearch/FS4ResourcePool.java
+++ b/container-search/src/main/java/com/yahoo/prelude/fastsearch/FS4ResourcePool.java
@@ -14,6 +14,7 @@ import com.yahoo.io.Connection;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
+import java.util.Optional;
import java.util.Timer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -60,11 +61,14 @@ public class FS4ResourcePool extends AbstractComponent {
}
public Backend getBackend(String host, int port) {
+ return getBackend(host, port, Optional.empty());
+ }
+ public Backend getBackend(String host, int port, Optional<Integer> distributionKey) {
String key = host + ":" + port;
synchronized (connectionPoolMap) {
Backend pool = connectionPoolMap.get(key);
if (pool == null) {
- pool = new Backend(host, port, Server.get().getServerDiscriminator(), listeners, new ConnectionPool(timer));
+ pool = new Backend(host, port, Server.get().getServerDiscriminator(), listeners, new ConnectionPool(timer), distributionKey);
connectionPoolMap.put(key, pool);
}
return pool;
diff --git a/container-search/src/main/java/com/yahoo/prelude/fastsearch/FastHit.java b/container-search/src/main/java/com/yahoo/prelude/fastsearch/FastHit.java
index 1160ea0a204..f6894439ddc 100644
--- a/container-search/src/main/java/com/yahoo/prelude/fastsearch/FastHit.java
+++ b/container-search/src/main/java/com/yahoo/prelude/fastsearch/FastHit.java
@@ -50,9 +50,9 @@ public class FastHit extends Hit {
* Summaries added to this hit which are not yet decoded into fields.
* Fields are resolved by returning the first non-null value found by
* 1) the field value from the Map of fields in the Hit supertype, and
- * 2) each of the summaries, in the order of the list (which is the add order).
+ * 2) each of the summaries, reverse add order
* This ensures that values set from code overwrites any value received as
- * summary data.
+ * summary data, and fetching a new summary overrides previous summaries.
*
* The reason we keep this rather than eagerly decoding into a the field map
* is to reduce garbage collection and decoding cost, with the assumption
@@ -131,7 +131,7 @@ public class FastHit extends Hit {
/** Returns the index of the node this hit originated at */
public int getDistributionKey() { return distributionKey; }
- /** Returns the index of the node this hit originated at */
+ /** Sets the index of the node this hit originated at */
public void setDistributionKey(int distributionKey) { this.distributionKey = distributionKey; }
/**
@@ -163,7 +163,7 @@ public class FastHit extends Hit {
public void addSummary(DocsumDefinition docsumDef, Inspector value) {
if (removedFields != null)
removedFields.removeAll(docsumDef.fieldNames());
- summaries.add(new SummaryData(this, docsumDef, value, summaries.size()));
+ summaries.add(0, new SummaryData(this, docsumDef, value, 1 + summaries.size()));
}
/**
@@ -331,6 +331,7 @@ public class FastHit extends Hit {
private Object getSummaryValue(String name) {
if (removedFields != null && removedFields.contains(name))
return null;
+ // fetch from last added summary with the field
for (SummaryData summaryData : summaries) {
Object value = summaryData.getField(name);
if (value != null) return value;
@@ -520,7 +521,7 @@ public class FastHit extends Hit {
private final DocsumDefinition type;
private final Inspector data;
- /** The index of this summary in the list of summaries added to this */
+ /** The index from the end of this summary in the list of summaries */
private final int index;
SummaryData(FastHit hit, DocsumDefinition type, Inspector data, int index) {
@@ -577,11 +578,11 @@ public class FastHit extends Hit {
/**
* Returns whether this field is present in the map properties
- * or an earlier (lower index) summary in this hit
+ * or a summary added later in this hit
*/
private boolean shadowed(String name) {
if (hit.hasField(name)) return true;
- for (int i = 0; i < index; i++) {
+ for (int i = 0; i < hit.summaries.size() - index; i++) {
if (hit.summaries.get(i).type.fieldNames().contains(name))
return true;
}
diff --git a/container-search/src/main/java/com/yahoo/prelude/fastsearch/FastSearcher.java b/container-search/src/main/java/com/yahoo/prelude/fastsearch/FastSearcher.java
index 30e2adab182..90410699748 100644
--- a/container-search/src/main/java/com/yahoo/prelude/fastsearch/FastSearcher.java
+++ b/container-search/src/main/java/com/yahoo/prelude/fastsearch/FastSearcher.java
@@ -229,8 +229,9 @@ public class FastSearcher extends VespaBackEndSearcher {
// Dispatch directly to the single, local search node
query.trace(false, 2, "Dispatching directly to ", directDispatchRecipient.get());
- return fs4ResourcePool.getBackend(directDispatchRecipient.get().hostname(),
- directDispatchRecipient.get().fs4port());
+ return fs4ResourcePool.getBackend(directDispatchRecipient.get().hostname(),
+ directDispatchRecipient.get().fs4port(),
+ Optional.of(directDispatchRecipient.get().key()));
}
/**
@@ -373,7 +374,6 @@ public class FastSearcher extends VespaBackEndSearcher {
}
private Result searchTwoPhase(FS4Channel channel, Query query, QueryPacket queryPacket, CacheKey cacheKey) throws IOException {
-
if (isLoggingFine())
getLogger().finest("sending query packet");
@@ -417,7 +417,8 @@ public class FastSearcher extends VespaBackEndSearcher {
addMetaInfo(query, queryPacket.getQueryPacketData(), resultPacket, result, false);
- addUnfilledHits(result, resultPacket.getDocuments(), false, queryPacket.getQueryPacketData(), cacheKey);
+ addUnfilledHits(result, resultPacket.getDocuments(), false,
+ queryPacket.getQueryPacketData(), cacheKey, channel.distributionKey());
Packet[] packets;
PacketWrapper packetWrapper = cacheControl.lookup(cacheKey, query);
@@ -432,7 +433,7 @@ public class FastSearcher extends VespaBackEndSearcher {
} else {
packets = new Packet[1];
packets[0] = resultPacket;
- cacheControl.cache(cacheKey, query, new DocsumPacketKey[0], packets);
+ cacheControl.cache(cacheKey, query, new DocsumPacketKey[0], packets, channel.distributionKey());
}
}
return result;
diff --git a/container-search/src/main/java/com/yahoo/prelude/fastsearch/PacketWrapper.java b/container-search/src/main/java/com/yahoo/prelude/fastsearch/PacketWrapper.java
index 2b1b7dd24cc..8e22bae430a 100644
--- a/container-search/src/main/java/com/yahoo/prelude/fastsearch/PacketWrapper.java
+++ b/container-search/src/main/java/com/yahoo/prelude/fastsearch/PacketWrapper.java
@@ -7,6 +7,7 @@ import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
import java.util.logging.Logger;
import com.yahoo.fs4.BasicPacket;
@@ -31,10 +32,13 @@ public class PacketWrapper implements Cloneable {
private static Logger log = Logger.getLogger(PacketWrapper.class.getName());
- final int keySize;
+ private final int keySize;
// associated result packets, sorted in regard to offset
private ArrayList<BasicPacket> resultPackets = new ArrayList<>(3); // length = "some small number"
- LinkedHashMap<DocsumPacketKey, BasicPacket> packets;
+
+ private LinkedHashMap<DocsumPacketKey, BasicPacket> packets;
+
+ private final Optional<Integer> distributionKey;
private static class ResultPacketComparator<T extends BasicPacket> implements Comparator<T> {
@Override
@@ -47,11 +51,12 @@ public class PacketWrapper implements Cloneable {
private static ResultPacketComparator<BasicPacket> resultPacketComparator = new ResultPacketComparator<>();
- public PacketWrapper(CacheKey key, DocsumPacketKey[] packetKeys, BasicPacket[] bpackets) {
+ public PacketWrapper(CacheKey key, DocsumPacketKey[] packetKeys, BasicPacket[] bpackets, Optional<Integer> distributionKey) {
// Should not support key == null
this.keySize = key.byteSize();
resultPackets.add(bpackets[0]);
this.packets = new LinkedHashMap<>();
+ this.distributionKey = distributionKey;
Packet[] ppackets = new Packet[packetKeys.length];
for (int i = 0; i < packetKeys.length; i++) {
@@ -72,6 +77,7 @@ public class PacketWrapper implements Cloneable {
}
resultPackets.add(packets[0]);
this.packets = new LinkedHashMap<>();
+ this.distributionKey = Optional.empty();
for (int i = 0; i < packets.length - 1; i++) {
this.packets.put(new DocsumPacketKey(new GlobalId(new DocumentId("doc:test:" + i).getGlobalId()), i, null), packets[i + 1]);
}
@@ -87,7 +93,13 @@ public class PacketWrapper implements Cloneable {
}
/**
- * @return list of documents, null if not all are available
+ * Returns the distribution key of the content node producing these hits,
+ * or empty if the hits were returned through dispatch
+ */
+ public Optional<Integer> distributionKey() { return distributionKey; }
+
+ /**
+ * Returns the list of documents, null if not all are available
*/
public List<DocumentInfo> getDocuments(int offset, int hits) {
// speculatively allocate list for the hits
diff --git a/container-search/src/main/java/com/yahoo/prelude/fastsearch/VespaBackEndSearcher.java b/container-search/src/main/java/com/yahoo/prelude/fastsearch/VespaBackEndSearcher.java
index 3abd97d814e..3e9a92ea0f7 100644
--- a/container-search/src/main/java/com/yahoo/prelude/fastsearch/VespaBackEndSearcher.java
+++ b/container-search/src/main/java/com/yahoo/prelude/fastsearch/VespaBackEndSearcher.java
@@ -75,9 +75,6 @@ public abstract class VespaBackEndSearcher extends PingableSearcher {
/** Cache wrapper */
protected CacheControl cacheControl = null;
- /** Searchcluster number */
- private int sourceNumber;
-
protected final String getName() { return name; }
protected final String getDefaultDocsumClass() { return defaultDocsumClass; }
@@ -140,7 +137,7 @@ public abstract class VespaBackEndSearcher extends PingableSearcher {
addMetaInfo(query, queryPacketData, resultPacket, result, true);
if (packetWrapper.getNumPackets() == 0)
- addUnfilledHits(result, documents, true, queryPacketData, key);
+ addUnfilledHits(result, documents, true, queryPacketData, key, packetWrapper.distributionKey());
else
addCachedHits(result, packetWrapper, summaryClass, documents);
return result;
@@ -168,7 +165,6 @@ public abstract class VespaBackEndSearcher extends PingableSearcher {
public final void init(SummaryParameters docSumParams, ClusterParams clusterParams, CacheParams cacheParams,
DocumentdbInfoConfig documentdbInfoConfig) {
this.name = clusterParams.searcherName;
- this.sourceNumber = clusterParams.clusterNumber;
Validator.ensureNotNull("Name of Vespa backend integration", getName());
@@ -620,8 +616,16 @@ public abstract class VespaBackEndSearcher extends PingableSearcher {
*
* @param queryPacketData binary data from first phase of search, or null
* @param cacheKey the key this hit should match in the packet cache, or null
+ * @param channelDistributionKey distribution key of the node producing these hits.
+ * Only set if produced directly by a search node, not dispatch
+ * (in which case it is not set in the received packets.)
*/
- boolean addUnfilledHits(Result result, List<DocumentInfo> documents, boolean fromCache, QueryPacketData queryPacketData, CacheKey cacheKey) {
+ boolean addUnfilledHits(Result result,
+ List<DocumentInfo> documents,
+ boolean fromCache,
+ QueryPacketData queryPacketData,
+ CacheKey cacheKey,
+ Optional<Integer> channelDistributionKey) {
boolean allHitsOK = true;
Query myQuery = result.getQuery();
@@ -638,6 +642,7 @@ public abstract class VespaBackEndSearcher extends PingableSearcher {
hit.setCached(fromCache);
extractDocumentInfo(hit, document);
+ channelDistributionKey.ifPresent(hit::setDistributionKey);
result.hits().add(hit);
} catch (ConfigurationException e) {
diff --git a/container-search/src/main/java/com/yahoo/prelude/query/WeakAndItem.java b/container-search/src/main/java/com/yahoo/prelude/query/WeakAndItem.java
index 708c48f0954..902fefae9be 100644
--- a/container-search/src/main/java/com/yahoo/prelude/query/WeakAndItem.java
+++ b/container-search/src/main/java/com/yahoo/prelude/query/WeakAndItem.java
@@ -35,6 +35,7 @@ public final class WeakAndItem extends NonReducibleCompositeItem {
/**
* Make a WAND item with no children. You can mention a common index or you can mention it on each child.
+ *
* @param index The index it shall search.
* @param N the target for minimum number of hits to produce;
* a backend will not suppress any hits in the operator
@@ -94,6 +95,7 @@ public final class WeakAndItem extends NonReducibleCompositeItem {
* This threshold is currently only used if the WeakAndItem is searching a RISE index field.
* The score threshold then specifies the minimum dot product score a match needs to be part of the result set.
* Default value is 0.
+ *
* @param scoreThreshold the score threshold.
*/
public void setScoreThreshold(int scoreThreshold) {
diff --git a/container-search/src/main/java/com/yahoo/prelude/query/parser/CustomParser.java b/container-search/src/main/java/com/yahoo/prelude/query/parser/CustomParser.java
index db9c5b8f726..e57e06f6b12 100644
--- a/container-search/src/main/java/com/yahoo/prelude/query/parser/CustomParser.java
+++ b/container-search/src/main/java/com/yahoo/prelude/query/parser/CustomParser.java
@@ -11,7 +11,7 @@ import java.util.Objects;
import java.util.Set;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
* @since 5.1.4
*/
public interface CustomParser extends Parser {
diff --git a/container-search/src/main/java/com/yahoo/prelude/query/parser/ProgrammaticParser.java b/container-search/src/main/java/com/yahoo/prelude/query/parser/ProgrammaticParser.java
index e55da49b921..eab001042d3 100644
--- a/container-search/src/main/java/com/yahoo/prelude/query/parser/ProgrammaticParser.java
+++ b/container-search/src/main/java/com/yahoo/prelude/query/parser/ProgrammaticParser.java
@@ -12,7 +12,7 @@ import com.yahoo.search.query.textserialize.TextSerialize;
import java.util.Set;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @since 5.1.4
*/
public final class ProgrammaticParser implements CustomParser {
diff --git a/container-search/src/main/java/com/yahoo/prelude/query/textualrepresentation/Discloser.java b/container-search/src/main/java/com/yahoo/prelude/query/textualrepresentation/Discloser.java
index 7bced128ac5..1d7372a2497 100644
--- a/container-search/src/main/java/com/yahoo/prelude/query/textualrepresentation/Discloser.java
+++ b/container-search/src/main/java/com/yahoo/prelude/query/textualrepresentation/Discloser.java
@@ -6,7 +6,7 @@ import com.yahoo.prelude.query.Item;
/**
* Allows an item to disclose its properties and children/value.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public interface Discloser {
void addProperty(String key, Object value);
diff --git a/container-search/src/main/java/com/yahoo/prelude/query/textualrepresentation/TextualQueryRepresentation.java b/container-search/src/main/java/com/yahoo/prelude/query/textualrepresentation/TextualQueryRepresentation.java
index f00219122c4..56f106a43f4 100644
--- a/container-search/src/main/java/com/yahoo/prelude/query/textualrepresentation/TextualQueryRepresentation.java
+++ b/container-search/src/main/java/com/yahoo/prelude/query/textualrepresentation/TextualQueryRepresentation.java
@@ -10,7 +10,7 @@ import java.util.regex.Pattern;
/**
* Creates a detailed, QED inspired representation of a query tree.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class TextualQueryRepresentation {
private Map<Item, Integer> itemReferences = new IdentityHashMap<>();
diff --git a/container-search/src/main/java/com/yahoo/prelude/querytransform/RecallSearcher.java b/container-search/src/main/java/com/yahoo/prelude/querytransform/RecallSearcher.java
index 69331a196a2..060fd598245 100644
--- a/container-search/src/main/java/com/yahoo/prelude/querytransform/RecallSearcher.java
+++ b/container-search/src/main/java/com/yahoo/prelude/querytransform/RecallSearcher.java
@@ -29,13 +29,13 @@ import static com.yahoo.prelude.querytransform.StemmingSearcher.STEMMING;
*
* If the "recall" property is unset, this searcher does nothing.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
@After("com.yahoo.search.querytransform.WandSearcher")
@Before({STEMMING, ACCENT_REMOVAL})
public class RecallSearcher extends Searcher {
- private static final CompoundName recallName=new CompoundName("recall");
+ public static final CompoundName recallName=new CompoundName("recall");
@Override
public Result search(Query query, Execution execution) {
diff --git a/container-search/src/main/java/com/yahoo/prelude/searcher/MultipleResultsSearcher.java b/container-search/src/main/java/com/yahoo/prelude/searcher/MultipleResultsSearcher.java
index 3b2fd596cfa..75066a424fc 100644
--- a/container-search/src/main/java/com/yahoo/prelude/searcher/MultipleResultsSearcher.java
+++ b/container-search/src/main/java/com/yahoo/prelude/searcher/MultipleResultsSearcher.java
@@ -17,7 +17,7 @@ import java.util.*;
*
* <p> For each group, the desired number of hits can be specified. </p>
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class MultipleResultsSearcher extends Searcher {
diff --git a/container-search/src/main/java/com/yahoo/prelude/templates/HitContext.java b/container-search/src/main/java/com/yahoo/prelude/templates/HitContext.java
index 4d1daa97306..14b05f5e2a6 100644
--- a/container-search/src/main/java/com/yahoo/prelude/templates/HitContext.java
+++ b/container-search/src/main/java/com/yahoo/prelude/templates/HitContext.java
@@ -16,7 +16,7 @@ import java.util.Set;
/**
* A context providing all the fields of a hit, and falls back to MapContext behavior for all other keys.
*
- * @author tonytv
+ * @author Tony Vaagenes
* @deprecated use a Renderer instead
*/
@SuppressWarnings("deprecation")
diff --git a/container-search/src/main/java/com/yahoo/prelude/templates/LogExceptionUserTemplateDelegator.java b/container-search/src/main/java/com/yahoo/prelude/templates/LogExceptionUserTemplateDelegator.java
index b9272ac4c3d..f9ad3ebf86e 100644
--- a/container-search/src/main/java/com/yahoo/prelude/templates/LogExceptionUserTemplateDelegator.java
+++ b/container-search/src/main/java/com/yahoo/prelude/templates/LogExceptionUserTemplateDelegator.java
@@ -12,7 +12,7 @@ import java.util.logging.Logger;
/**
* Delegates to another UserTemplate, but handles any exceptions(except IOException) by logging them.
*
- * @author tonytv
+ * @author Tony Vaagenes
* @deprecated use a renderer instead
*/
@SuppressWarnings("deprecation")
diff --git a/container-search/src/main/java/com/yahoo/prelude/templates/SearchRendererAdaptor.java b/container-search/src/main/java/com/yahoo/prelude/templates/SearchRendererAdaptor.java
index a639a6b97ec..8439dd105a8 100644
--- a/container-search/src/main/java/com/yahoo/prelude/templates/SearchRendererAdaptor.java
+++ b/container-search/src/main/java/com/yahoo/prelude/templates/SearchRendererAdaptor.java
@@ -20,7 +20,7 @@ import java.util.Iterator;
/**
* Renders a search result using the old templates API.
*
- * @author tonytv
+ * @author Tony Vaagenes
* @deprecated do not use
*/
@SuppressWarnings({ "rawtypes", "deprecation", "unchecked" })
diff --git a/container-search/src/main/java/com/yahoo/search/Query.java b/container-search/src/main/java/com/yahoo/search/Query.java
index ab6976e29d9..cbc8b15eff9 100644
--- a/container-search/src/main/java/com/yahoo/search/Query.java
+++ b/container-search/src/main/java/com/yahoo/search/Query.java
@@ -46,7 +46,6 @@ import com.yahoo.search.yql.VespaSerializer;
import com.yahoo.search.yql.YqlParser;
import com.yahoo.yolean.Exceptions;
import edu.umd.cs.findbugs.annotations.Nullable;
-
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
@@ -270,6 +269,7 @@ public class Query extends com.yahoo.processing.Request implements Cloneable {
this("");
}
+
/**
* Construct a query from a string formatted in the http style, e.g <code>?query=test&amp;offset=10&amp;hits=13</code>
* The query must be uri encoded.
@@ -278,6 +278,16 @@ public class Query extends com.yahoo.processing.Request implements Cloneable {
this(query, null);
}
+
+ /**
+ * Creates a query from a request
+ *
+ * @param request the HTTP request from which this is created
+ */
+ public Query(HttpRequest request) {
+ this(request, null);
+ }
+
/**
* Construct a query from a string formatted in the http style, e.g <code>?query=test&amp;offset=10&amp;hits=13</code>
* The query must be uri encoded.
@@ -293,20 +303,24 @@ public class Query extends com.yahoo.processing.Request implements Cloneable {
* @param queryProfile the query profile to use for this query, or null if none.
*/
public Query(HttpRequest request, CompiledQueryProfile queryProfile) {
- super(new QueryPropertyAliases(propertyAliases));
- this.httpRequest = request;
- init(request.propertyMap(), queryProfile);
+ this(request, request.propertyMap(), queryProfile);
}
/**
* Creates a query from a request
*
- * @param request the HTTP request from which this is created
+ * @param request the HTTP request from which this is created.
+ * @param requestMap the property map of the query.
+ * @param queryProfile the query profile to use for this query, or null if none.
*/
- public Query(HttpRequest request) {
- this(request, null);
+ public Query(HttpRequest request, Map<String, String> requestMap, CompiledQueryProfile queryProfile) {
+ super(new QueryPropertyAliases(propertyAliases));
+ this.httpRequest = request;
+ init(requestMap, queryProfile);
}
+
+
private void init(Map<String, String> requestMap, CompiledQueryProfile queryProfile) {
startTime = System.currentTimeMillis();
if (queryProfile != null) {
@@ -497,11 +511,15 @@ public class Query extends com.yahoo.processing.Request implements Cloneable {
* Get the appropriate timeout for the query.
*
* @return timeout in milliseconds
- **/
+ */
public long getTimeLeft() {
return getTimeout() - getDurationTime();
}
+ /**
+ * @deprecated do not use
+ */
+ @Deprecated // TODO: Remove on Vespa 7
public boolean requestHasProperty(String name) {
return httpRequest.hasProperty(name);
}
diff --git a/container-search/src/main/java/com/yahoo/search/config/dispatchprototype/package-info.java b/container-search/src/main/java/com/yahoo/search/config/dispatchprototype/package-info.java
index 60bd4fd71ab..3b1a3b3bd05 100644
--- a/container-search/src/main/java/com/yahoo/search/config/dispatchprototype/package-info.java
+++ b/container-search/src/main/java/com/yahoo/search/config/dispatchprototype/package-info.java
@@ -1,7 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
/**
* Package for dispatchprototype config.
- * @author tonytv
+ * @author Tony Vaagenes
*/
@ExportPackage
package com.yahoo.search.config.dispatchprototype;
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/Dispatcher.java b/container-search/src/main/java/com/yahoo/search/dispatch/Dispatcher.java
index 654fd6cc68f..3eb010da555 100644
--- a/container-search/src/main/java/com/yahoo/search/dispatch/Dispatcher.java
+++ b/container-search/src/main/java/com/yahoo/search/dispatch/Dispatcher.java
@@ -62,8 +62,7 @@ public class Dispatcher extends AbstractComponent {
this.client = new RpcClient();
this.searchCluster = new SearchCluster(dispatchConfig, fs4ResourcePool, containerClusterSize, vipStatus);
- // Create node rpc connections, indexed by the legacy "partid", which allows us to bridge
- // between fs4 calls (for search) and rpc calls (for summary fetch)
+ // Create node rpc connections, indexed by the node distribution key
ImmutableMap.Builder<Integer, Client.NodeConnection> nodeConnectionsBuilder = new ImmutableMap.Builder<>();
for (DispatchConfig.Node node : dispatchConfig.node()) {
nodeConnectionsBuilder.put(node.key(), client.createConnection(node.host(), node.port()));
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/SearchCluster.java b/container-search/src/main/java/com/yahoo/search/dispatch/SearchCluster.java
index 9b2a24cd01f..efce2fdac9c 100644
--- a/container-search/src/main/java/com/yahoo/search/dispatch/SearchCluster.java
+++ b/container-search/src/main/java/com/yahoo/search/dispatch/SearchCluster.java
@@ -137,7 +137,7 @@ public class SearchCluster implements NodeManager<SearchCluster.Node> {
private static ImmutableList<Node> toNodes(DispatchConfig dispatchConfig) {
ImmutableList.Builder<Node> nodesBuilder = new ImmutableList.Builder<>();
for (DispatchConfig.Node node : dispatchConfig.node())
- nodesBuilder.add(new Node(node.host(), node.fs4port(), node.group()));
+ nodesBuilder.add(new Node(node.key(), node.host(), node.fs4port(), node.group()));
return nodesBuilder.build();
}
@@ -360,6 +360,7 @@ public class SearchCluster implements NodeManager<SearchCluster.Node> {
/** A node in a search cluster. This class is multithread safe. */
public static class Node {
+ private final int key;
private final String hostname;
private final int fs4port;
private final int group;
@@ -367,12 +368,16 @@ public class SearchCluster implements NodeManager<SearchCluster.Node> {
private final AtomicBoolean working = new AtomicBoolean(true);
private final AtomicLong activeDocuments = new AtomicLong(0);
- public Node(String hostname, int fs4port, int group) {
+ public Node(int key, String hostname, int fs4port, int group) {
+ this.key = key;
this.hostname = hostname;
this.fs4port = fs4port;
this.group = group;
}
+ /** Returns the unique and stable distribution key of this node */
+ public int key() { return key; }
+
public String hostname() { return hostname; }
public int fs4port() { return fs4port; }
diff --git a/container-search/src/main/java/com/yahoo/search/federation/http/ConfiguredHTTPClientSearcher.java b/container-search/src/main/java/com/yahoo/search/federation/http/ConfiguredHTTPClientSearcher.java
index f201861bc0c..8abd6deb5f9 100644
--- a/container-search/src/main/java/com/yahoo/search/federation/http/ConfiguredHTTPClientSearcher.java
+++ b/container-search/src/main/java/com/yahoo/search/federation/http/ConfiguredHTTPClientSearcher.java
@@ -16,6 +16,7 @@ import com.yahoo.statistics.Statistics;
*
* @author bratseth
*/
+@Deprecated
public abstract class ConfiguredHTTPClientSearcher extends HTTPClientSearcher {
/** Create this from a configuraton */
diff --git a/container-search/src/main/java/com/yahoo/search/federation/http/ConfiguredHTTPProviderSearcher.java b/container-search/src/main/java/com/yahoo/search/federation/http/ConfiguredHTTPProviderSearcher.java
index c2a7e6fa640..22c2802c9e8 100644
--- a/container-search/src/main/java/com/yahoo/search/federation/http/ConfiguredHTTPProviderSearcher.java
+++ b/container-search/src/main/java/com/yahoo/search/federation/http/ConfiguredHTTPProviderSearcher.java
@@ -19,6 +19,7 @@ import java.util.Collections;
* @author <a href="mailto:arnebef@yahoo-inc.com">Arne Bergene Fossaa</a>
* @author bratseth
*/
+@Deprecated
public abstract class ConfiguredHTTPProviderSearcher extends HTTPProviderSearcher {
/** Create this from a configuraton */
diff --git a/container-search/src/main/java/com/yahoo/search/federation/http/HTTPClientSearcher.java b/container-search/src/main/java/com/yahoo/search/federation/http/HTTPClientSearcher.java
index 3d36994984d..dc0b5981e63 100644
--- a/container-search/src/main/java/com/yahoo/search/federation/http/HTTPClientSearcher.java
+++ b/container-search/src/main/java/com/yahoo/search/federation/http/HTTPClientSearcher.java
@@ -91,6 +91,7 @@ import java.util.logging.Logger;
* @author <a href="mailto:arnebef@yahoo-inc.com">Arne Bergene Fossaa</a>
* @author bratseth
*/
+@Deprecated
public abstract class HTTPClientSearcher extends HTTPSearcher {
static final CompoundName REQUEST_META_CARRIER = new CompoundName("com.yahoo.search.federation.http.HTTPClientSearcher_requestMeta");
diff --git a/container-search/src/main/java/com/yahoo/search/federation/http/HTTPProviderSearcher.java b/container-search/src/main/java/com/yahoo/search/federation/http/HTTPProviderSearcher.java
index 5348a687253..d0f76b815dc 100644
--- a/container-search/src/main/java/com/yahoo/search/federation/http/HTTPProviderSearcher.java
+++ b/container-search/src/main/java/com/yahoo/search/federation/http/HTTPProviderSearcher.java
@@ -39,6 +39,7 @@ import java.util.logging.Logger;
* @author Arne Bergene Fossaa
* @author bratseth
*/
+@Deprecated
public abstract class HTTPProviderSearcher extends HTTPSearcher {
private final Counter emptyResults;
diff --git a/container-search/src/main/java/com/yahoo/search/federation/http/HTTPSearcher.java b/container-search/src/main/java/com/yahoo/search/federation/http/HTTPSearcher.java
index 3707d7fa77a..80e785f0175 100644
--- a/container-search/src/main/java/com/yahoo/search/federation/http/HTTPSearcher.java
+++ b/container-search/src/main/java/com/yahoo/search/federation/http/HTTPSearcher.java
@@ -136,6 +136,7 @@ import java.util.logging.Logger;
*
* @author Arne Bergene Fossaa
*/
+@Deprecated
public abstract class HTTPSearcher extends ClusterSearcher<Connection> {
protected static final String YCA_HTTP_HEADER = "Yahoo-App-Auth";
diff --git a/container-search/src/main/java/com/yahoo/search/federation/selection/FederationTarget.java b/container-search/src/main/java/com/yahoo/search/federation/selection/FederationTarget.java
index 32a5644f29f..7ade9a0eaf9 100644
--- a/container-search/src/main/java/com/yahoo/search/federation/selection/FederationTarget.java
+++ b/container-search/src/main/java/com/yahoo/search/federation/selection/FederationTarget.java
@@ -13,7 +13,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
* along with a timeout and
* custom data reserved for use by the TargetSelector.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public final class FederationTarget<T> {
diff --git a/container-search/src/main/java/com/yahoo/search/federation/selection/TargetSelector.java b/container-search/src/main/java/com/yahoo/search/federation/selection/TargetSelector.java
index d4ae147eae7..465ddedbecf 100644
--- a/container-search/src/main/java/com/yahoo/search/federation/selection/TargetSelector.java
+++ b/container-search/src/main/java/com/yahoo/search/federation/selection/TargetSelector.java
@@ -18,7 +18,7 @@ import java.util.Collection;
* 1) call modifyTargetQuery(target, query)
* 2) call modifyTargetResult(target, result)
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public interface TargetSelector<T> {
diff --git a/container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainInvocationSpec.java b/container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainInvocationSpec.java
index 4fe41d16d35..6cb8d2ef174 100644
--- a/container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainInvocationSpec.java
+++ b/container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainInvocationSpec.java
@@ -12,7 +12,7 @@ import java.util.Objects;
* Specifices which search chain should be run and how it should be run.
* This is a value object.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class SearchChainInvocationSpec implements Cloneable {
diff --git a/container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainResolver.java b/container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainResolver.java
index 66874f1a2f1..97ceee96dfc 100644
--- a/container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainResolver.java
+++ b/container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainResolver.java
@@ -37,7 +37,7 @@ import java.util.TreeSet;
* o: SearchChainInvocationSpec
* </pre>
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class SearchChainResolver {
diff --git a/container-search/src/main/java/com/yahoo/search/federation/sourceref/SingleTarget.java b/container-search/src/main/java/com/yahoo/search/federation/sourceref/SingleTarget.java
index 6e08fa2f966..f4c98075a5a 100644
--- a/container-search/src/main/java/com/yahoo/search/federation/sourceref/SingleTarget.java
+++ b/container-search/src/main/java/com/yahoo/search/federation/sourceref/SingleTarget.java
@@ -7,7 +7,7 @@ import com.yahoo.processing.request.Properties;
/**
* TODO: What is this?
*
-* @author tonytv
+* @author Tony Vaagenes
*/
public class SingleTarget extends Target {
private final SearchChainInvocationSpec searchChainInvocationSpec;
diff --git a/container-search/src/main/java/com/yahoo/search/federation/sourceref/SourceRefResolver.java b/container-search/src/main/java/com/yahoo/search/federation/sourceref/SourceRefResolver.java
index e14c46056b7..ee98d033440 100644
--- a/container-search/src/main/java/com/yahoo/search/federation/sourceref/SourceRefResolver.java
+++ b/container-search/src/main/java/com/yahoo/search/federation/sourceref/SourceRefResolver.java
@@ -15,7 +15,7 @@ import com.yahoo.processing.request.Properties;
/**
* Maps a source reference to search chain invocation specs.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class SourceRefResolver {
diff --git a/container-search/src/main/java/com/yahoo/search/federation/sourceref/Target.java b/container-search/src/main/java/com/yahoo/search/federation/sourceref/Target.java
index cf7276d767e..f23e24525bb 100644
--- a/container-search/src/main/java/com/yahoo/search/federation/sourceref/Target.java
+++ b/container-search/src/main/java/com/yahoo/search/federation/sourceref/Target.java
@@ -8,7 +8,7 @@ import com.yahoo.processing.request.Properties;
/**
* TODO: What's this?
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public abstract class Target extends AbstractComponent {
diff --git a/container-search/src/main/java/com/yahoo/search/federation/sourceref/UnresolvedProviderException.java b/container-search/src/main/java/com/yahoo/search/federation/sourceref/UnresolvedProviderException.java
index 087fa0825ff..5075b05454b 100644
--- a/container-search/src/main/java/com/yahoo/search/federation/sourceref/UnresolvedProviderException.java
+++ b/container-search/src/main/java/com/yahoo/search/federation/sourceref/UnresolvedProviderException.java
@@ -7,7 +7,7 @@ import com.yahoo.component.ComponentSpecification;
import static com.yahoo.container.util.Util.quote;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
@SuppressWarnings("serial")
class UnresolvedProviderException extends UnresolvedSearchChainException {
diff --git a/container-search/src/main/java/com/yahoo/search/federation/sourceref/UnresolvedSearchChainException.java b/container-search/src/main/java/com/yahoo/search/federation/sourceref/UnresolvedSearchChainException.java
index 2763bf52b22..dceae0318b4 100644
--- a/container-search/src/main/java/com/yahoo/search/federation/sourceref/UnresolvedSearchChainException.java
+++ b/container-search/src/main/java/com/yahoo/search/federation/sourceref/UnresolvedSearchChainException.java
@@ -3,7 +3,7 @@ package com.yahoo.search.federation.sourceref;
/**
* Thrown if a search chain can not be resolved from one or more ids.
- * @author tonytv
+ * @author Tony Vaagenes
*/
@SuppressWarnings("serial")
public class UnresolvedSearchChainException extends Exception {
diff --git a/container-search/src/main/java/com/yahoo/search/federation/sourceref/UnresolvedSourceRefException.java b/container-search/src/main/java/com/yahoo/search/federation/sourceref/UnresolvedSourceRefException.java
index 44569a55e22..233e92c1699 100644
--- a/container-search/src/main/java/com/yahoo/search/federation/sourceref/UnresolvedSourceRefException.java
+++ b/container-search/src/main/java/com/yahoo/search/federation/sourceref/UnresolvedSourceRefException.java
@@ -6,7 +6,7 @@ import com.yahoo.component.ComponentSpecification;
import static com.yahoo.container.util.Util.quote;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
@SuppressWarnings("serial")
class UnresolvedSourceRefException extends UnresolvedSearchChainException {
diff --git a/container-search/src/main/java/com/yahoo/search/federation/vespa/ResultBuilder.java b/container-search/src/main/java/com/yahoo/search/federation/vespa/ResultBuilder.java
index 71971b56ef1..dd20472474d 100644
--- a/container-search/src/main/java/com/yahoo/search/federation/vespa/ResultBuilder.java
+++ b/container-search/src/main/java/com/yahoo/search/federation/vespa/ResultBuilder.java
@@ -10,7 +10,6 @@ import com.yahoo.search.result.Hit;
import com.yahoo.search.result.HitGroup;
import com.yahoo.search.result.Relevance;
import com.yahoo.text.XML;
-import com.yahoo.text.DoubleParser;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;
@@ -472,7 +471,7 @@ public class ResultBuilder extends DefaultHandler {
//We try to get either uri or documentID and use that as id
Object docId = extractDocumentID();
Hit newHit = new Hit(docId.toString());
- if (hitRelevance != null) newHit.setRelevance(new Relevance(DoubleParser.parse(hitRelevance)));
+ if (hitRelevance != null) newHit.setRelevance(new Relevance(Double.parseDouble(hitRelevance)));
if(hitSource != null) newHit.setSource(hitSource);
if(hitType != null) {
for(String type: hitType.split(" ")) {
diff --git a/container-search/src/main/java/com/yahoo/search/federation/vespa/VespaSearcher.java b/container-search/src/main/java/com/yahoo/search/federation/vespa/VespaSearcher.java
index dfa4f9ad9bb..b59578ab6a3 100644
--- a/container-search/src/main/java/com/yahoo/search/federation/vespa/VespaSearcher.java
+++ b/container-search/src/main/java/com/yahoo/search/federation/vespa/VespaSearcher.java
@@ -9,6 +9,7 @@ import java.net.URL;
import java.util.Map;
import java.util.Set;
+import com.yahoo.search.federation.http.ConfiguredHTTPProviderSearcher;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
@@ -28,7 +29,6 @@ import com.yahoo.search.Result;
import com.yahoo.search.cache.QrBinaryCacheConfig;
import com.yahoo.search.cache.QrBinaryCacheRegionConfig;
import com.yahoo.search.federation.ProviderConfig;
-import com.yahoo.search.federation.http.ConfiguredHTTPProviderSearcher;
import com.yahoo.search.federation.http.Connection;
import com.yahoo.search.query.QueryTree;
import com.yahoo.search.query.textserialize.TextSerialize;
@@ -46,6 +46,7 @@ import edu.umd.cs.findbugs.annotations.Nullable;
* @author Arne Bergene Fossaa
* @author Steinar Knutsen
*/
+@Deprecated
@Provides("Vespa")
@After("*")
public class VespaSearcher extends ConfiguredHTTPProviderSearcher {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/Continuation.java b/container-search/src/main/java/com/yahoo/search/grouping/Continuation.java
index e7f40b8b447..eda2b449fda 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/Continuation.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/Continuation.java
@@ -10,7 +10,7 @@ import com.yahoo.search.grouping.vespa.ContinuationDecoder;
*
* <p>To render a Cookie within a result set, you simply need to call {@link #toString()}.</p>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public abstract class Continuation {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/GroupingQueryParser.java b/container-search/src/main/java/com/yahoo/search/grouping/GroupingQueryParser.java
index f3af9cf1fc7..94d2340ebee 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/GroupingQueryParser.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/GroupingQueryParser.java
@@ -19,7 +19,7 @@ import java.util.*;
* also parse any "timezone" parameter as the timezone for time expressions such as {@link
* com.yahoo.search.grouping.request.DayOfMonthFunction} and {@link com.yahoo.search.grouping.request.HourOfDayFunction}.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
@After(PhaseNames.RAW_QUERY)
@Before(PhaseNames.TRANSFORMED_QUERY)
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/GroupingRequest.java b/container-search/src/main/java/com/yahoo/search/grouping/GroupingRequest.java
index 52d08cc204f..8ce0d90dfc5 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/GroupingRequest.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/GroupingRequest.java
@@ -18,7 +18,7 @@ import java.util.*;
* {@link GroupingOperation} using {@link #setRootOperation(GroupingOperation)}. Once the search returns, access the
* result {@link Group} using the {@link #getResultGroup(Result)} method.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class GroupingRequest {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/GroupingValidator.java b/container-search/src/main/java/com/yahoo/search/grouping/GroupingValidator.java
index b321915359f..6379c43bc87 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/GroupingValidator.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/GroupingValidator.java
@@ -28,7 +28,7 @@ import static com.yahoo.search.grouping.GroupingQueryParser.SELECT_PARAMETER_PAR
* cluster for which this searcher has been deployed. This searcher uses exceptions to signal invalid grouping
* requests.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
@Before(PhaseNames.BACKEND)
@After(SELECT_PARAMETER_PARSING)
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/UnavailableAttributeException.java b/container-search/src/main/java/com/yahoo/search/grouping/UnavailableAttributeException.java
index 0669bcb3d60..5750f1c78e3 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/UnavailableAttributeException.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/UnavailableAttributeException.java
@@ -5,7 +5,7 @@ package com.yahoo.search.grouping;
* This exception is thrown by the {@link GroupingValidator} if it a {@link GroupingRequest} contains a reference to an
* unavailable attribute.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings("serial")
public class UnavailableAttributeException extends RuntimeException {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/AddFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/AddFunction.java
index 76e49bc975b..8cb27569e67 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/AddFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/AddFunction.java
@@ -7,7 +7,7 @@ import java.util.List;
* This class represents an add-function in a {@link GroupingExpression}. It evaluates to a number that equals the
* result of adding the results of all arguments together in the order they were given to the constructor.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class AddFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/AggregatorNode.java b/container-search/src/main/java/com/yahoo/search/grouping/request/AggregatorNode.java
index ef41b18d21d..323f1ac769a 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/AggregatorNode.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/AggregatorNode.java
@@ -6,7 +6,7 @@ package com.yahoo.search.grouping.request;
* can not be used as a document-level expression (i.e. level 0, see {@link GroupingExpression#resolveLevel(int)}). The
* contained expression is evaluated at the level of the aggregator minus 1.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class AggregatorNode extends GroupingExpression {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/AllOperation.java b/container-search/src/main/java/com/yahoo/search/grouping/request/AllOperation.java
index 9d26c4e281d..b809706331f 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/AllOperation.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/AllOperation.java
@@ -5,7 +5,7 @@ package com.yahoo.search.grouping.request;
* This is a grouping operation that processes the input list as a whole, as opposed to {@link EachOperation} which
* processes each element of that list separately.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class AllOperation extends GroupingOperation {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/AndFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/AndFunction.java
index ec4a71c3d24..5f2617acf16 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/AndFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/AndFunction.java
@@ -7,7 +7,7 @@ import java.util.List;
* This class represents an and-function in a {@link GroupingExpression}. It evaluates to a long that equals the result
* of and'ing the results of all arguments together in the order they were given to the constructor.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class AndFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/ArrayAtLookup.java b/container-search/src/main/java/com/yahoo/search/grouping/request/ArrayAtLookup.java
index 094cfae6b35..ed69e8aaee7 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/ArrayAtLookup.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/ArrayAtLookup.java
@@ -12,6 +12,7 @@ import com.google.common.annotations.Beta;
* If the index argument is less than 0 returns the first array element;
* if the index is greater than or equal to size(array) returns the last array element;
* if the array is empty returns 0 (or NaN?).
+ *
* @author arnej27959
*/
@Beta
@@ -28,7 +29,7 @@ public class ArrayAtLookup extends DocumentValue {
public ArrayAtLookup(String attributeName, GroupingExpression indexArg) {
super("array.at(" + attributeName + ", " + indexArg + ")");
this.attributeName = attributeName;
- this.arg2 = indexArg;
+ this.arg2 = indexArg;
}
/**
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/AttributeFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/AttributeFunction.java
index dfb49000ff1..faf565647c7 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/AttributeFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/AttributeFunction.java
@@ -5,7 +5,7 @@ package com.yahoo.search.grouping.request;
* This class represents a document attribute function in a {@link GroupingExpression}. It evaluates to the value of the
* named attribute in the input {@link com.yahoo.search.result.Hit}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class AttributeFunction extends DocumentValue {
@@ -14,7 +14,7 @@ public class AttributeFunction extends DocumentValue {
/**
* Constructs a new instance of this class.
*
- * @param attributeName The attribute name to assign to this.
+ * @param attributeName the attribute name to assign to this.
*/
public AttributeFunction(String attributeName) {
super("attribute(" + attributeName + ")");
@@ -24,9 +24,10 @@ public class AttributeFunction extends DocumentValue {
/**
* Returns the name of the attribute to retrieve from the input hit.
*
- * @return The attribute name.
+ * @return the attribute name.
*/
public String getAttributeName() {
return name;
}
+
}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/AttributeValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/AttributeValue.java
index 080aa3deb3d..d83888b3107 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/AttributeValue.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/AttributeValue.java
@@ -5,7 +5,7 @@ package com.yahoo.search.grouping.request;
* This class represents a document attribute value in a {@link GroupingExpression}. It evaluates to the value of the
* named attribute in the input {@link com.yahoo.search.result.Hit}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class AttributeValue extends DocumentValue {
@@ -14,7 +14,7 @@ public class AttributeValue extends DocumentValue {
/**
* Constructs a new instance of this class.
*
- * @param attributeName The attribute name to assign to this.
+ * @param attributeName the attribute name to assign to this.
*/
public AttributeValue(String attributeName) {
super(attributeName);
@@ -24,9 +24,10 @@ public class AttributeValue extends DocumentValue {
/**
* Returns the name of the attribute to retrieve from the input hit.
*
- * @return The attribute name.
+ * @return the attribute name.
*/
public String getAttributeName() {
return name;
}
+
}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/AvgAggregator.java b/container-search/src/main/java/com/yahoo/search/grouping/request/AvgAggregator.java
index 4ac64ef35fc..d7e3232ea5f 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/AvgAggregator.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/AvgAggregator.java
@@ -5,7 +5,7 @@ package com.yahoo.search.grouping.request;
* This class represents an average-aggregator in a {@link GroupingExpression}. It evaluates to the average value that
* the contained expression evaluated to over all the inputs.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class AvgAggregator extends AggregatorNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/AvgFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/AvgFunction.java
index aed409361b3..55c4d355589 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/AvgFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/AvgFunction.java
@@ -7,7 +7,7 @@ import java.util.List;
* This class represents a min-function in a {@link GroupingExpression}. It evaluates to a number that equals the
* average of the results of all arguments.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class AvgFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/BucketResolver.java b/container-search/src/main/java/com/yahoo/search/grouping/request/BucketResolver.java
index bb54b0e5a02..c36c8af5c34 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/BucketResolver.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/BucketResolver.java
@@ -11,7 +11,7 @@ import java.util.List;
* {@link #resolve(GroupingExpression)} to retrieve the list of corresponding
* grouping expression object.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class BucketResolver {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/BucketValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/BucketValue.java
index b053f7ede43..c73b7199394 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/BucketValue.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/BucketValue.java
@@ -6,7 +6,7 @@ package com.yahoo.search.grouping.request;
* 'from' and 'to'. The range is inclusive-from and exclusive-to. All supported data types are represented as subclasses
* of this.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class BucketValue extends GroupingExpression implements Comparable<BucketValue> {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/CatFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/CatFunction.java
index ae6161ec9d9..5e51c8e35a0 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/CatFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/CatFunction.java
@@ -7,7 +7,7 @@ import java.util.List;
* This class represents a cat-function in a {@link GroupingExpression}. It evaluates to a byte array that equals the
* concatenation of the binary result of all arguments in the order they were given to the constructor.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CatFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/ConstantValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/ConstantValue.java
index b986cf2efb9..ad8c1ef1cc9 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/ConstantValue.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/ConstantValue.java
@@ -6,7 +6,7 @@ package com.yahoo.search.grouping.request;
* this expression type can be used at any input level (see {@link GroupingExpression#resolveLevel(int)}). All supported
* data types are represented as subclasses of this.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings("rawtypes")
public abstract class ConstantValue<T extends Comparable> extends GroupingExpression {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/CountAggregator.java b/container-search/src/main/java/com/yahoo/search/grouping/request/CountAggregator.java
index 39f9c31766b..19a6c939087 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/CountAggregator.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/CountAggregator.java
@@ -5,7 +5,7 @@ package com.yahoo.search.grouping.request;
* This class represents an count-aggregator in a {@link GroupingExpression}. It evaluates to the number of elements
* there are in the input.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CountAggregator extends AggregatorNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/DateFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/DateFunction.java
index bcf81f1fbe6..b3a4c451b6b 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/DateFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/DateFunction.java
@@ -7,7 +7,7 @@ import java.util.Arrays;
* This class represents a timestamp-formatter function in a {@link GroupingExpression}. It evaluates to a string on the
* form "YYYY-MM-DD" of the result of the argument.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DateFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/DayOfMonthFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/DayOfMonthFunction.java
index 55b99c26ad7..5d82a80352b 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/DayOfMonthFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/DayOfMonthFunction.java
@@ -7,7 +7,7 @@ import java.util.Arrays;
* This class represents a day-of-month timestamp-function in a {@link GroupingExpression}. It evaluates to a long that
* equals the day of month (1-31) of the result of the argument.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DayOfMonthFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/DayOfWeekFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/DayOfWeekFunction.java
index 2b30968456e..f2135427bf5 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/DayOfWeekFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/DayOfWeekFunction.java
@@ -7,7 +7,7 @@ import java.util.Arrays;
* This class represents a day-of-week timestamp-function in a {@link GroupingExpression}. It evaluates to a long that
* equals the day of week (0 - 6) of the result of the argument, Monday being 0.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DayOfWeekFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/DayOfYearFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/DayOfYearFunction.java
index 0589b960b98..15f158ef2f1 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/DayOfYearFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/DayOfYearFunction.java
@@ -7,7 +7,7 @@ import java.util.Arrays;
* This class represents a day-of-year timestamp-function in a {@link GroupingExpression}. It evaluates to a long that
* equals the day of year (0-365) of the result of the argument.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DayOfYearFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/DivFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/DivFunction.java
index ad5e339bae6..f50dcde2d56 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/DivFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/DivFunction.java
@@ -8,7 +8,7 @@ import java.util.List;
* of dividing the results of all arguments in the order they were given to the constructor (divide first argument by
* second, result by third, ...).
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DivFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/DocIdNsSpecificValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/DocIdNsSpecificValue.java
index d2049b60fc8..188e642e7d7 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/DocIdNsSpecificValue.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/DocIdNsSpecificValue.java
@@ -5,7 +5,7 @@ package com.yahoo.search.grouping.request;
* This class represents a document id specific value in a {@link GroupingExpression}. It evaluates to the namespace-
* specific value of the document id of the input {@link com.yahoo.search.result.Hit}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DocIdNsSpecificValue extends DocumentValue {
@@ -15,5 +15,6 @@ public class DocIdNsSpecificValue extends DocumentValue {
public DocIdNsSpecificValue() {
super("docidnsspecific()");
}
+
}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/DocumentValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/DocumentValue.java
index 50d50edc865..f8756b81163 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/DocumentValue.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/DocumentValue.java
@@ -5,7 +5,7 @@ package com.yahoo.search.grouping.request;
* This class represents a document value in a {@link GroupingExpression}. As such, the subclasses of this can only be
* used as document-level expressions (i.e. level 0, see {@link GroupingExpression#resolveLevel(int)}).
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class DocumentValue extends GroupingExpression {
@@ -21,4 +21,5 @@ public abstract class DocumentValue extends GroupingExpression {
}
super.resolveLevel(level);
}
+
}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/DoubleBucket.java b/container-search/src/main/java/com/yahoo/search/grouping/request/DoubleBucket.java
index 5cbfd2b4ee4..d71ec8093fd 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/DoubleBucket.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/DoubleBucket.java
@@ -5,7 +5,7 @@ import java.text.ChoiceFormat;
/**
* This class represents a {@link Double} bucket in a {@link PredefinedFunction}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DoubleBucket extends BucketValue {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/DoublePredefined.java b/container-search/src/main/java/com/yahoo/search/grouping/request/DoublePredefined.java
index 227a95b1d3f..d43e8412623 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/DoublePredefined.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/DoublePredefined.java
@@ -7,7 +7,7 @@ import java.util.List;
* This class represents a predefined bucket-function in a {@link GroupingExpression} for expressions that evaluate to a
* double.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DoublePredefined extends PredefinedFunction {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/DoubleValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/DoubleValue.java
index 867683c94ef..5dad65aaabf 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/DoubleValue.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/DoubleValue.java
@@ -4,7 +4,7 @@ package com.yahoo.search.grouping.request;
/**
* This class represents a constant {@link Double} value in a {@link GroupingExpression}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DoubleValue extends ConstantValue<Double> {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/EachOperation.java b/container-search/src/main/java/com/yahoo/search/grouping/request/EachOperation.java
index db501f2325a..1a7074a2c6e 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/EachOperation.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/EachOperation.java
@@ -5,7 +5,7 @@ package com.yahoo.search.grouping.request;
* This is a grouping operation that processes each element of the input list separately, as opposed to {@link
* AllOperation} which processes that list as a whole.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class EachOperation extends GroupingOperation {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/ExpressionVisitor.java b/container-search/src/main/java/com/yahoo/search/grouping/request/ExpressionVisitor.java
index 84626061a3f..a341bee2568 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/ExpressionVisitor.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/ExpressionVisitor.java
@@ -6,7 +6,7 @@ package com.yahoo.search.grouping.request;
* GroupingOperation}. It is used by the {@link com.yahoo.search.grouping.GroupingValidator} to ensure that all
* referenced attributes are valid for the cluster being queried.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface ExpressionVisitor {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/FixedWidthFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/FixedWidthFunction.java
index 0898439da87..f44394c6193 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/FixedWidthFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/FixedWidthFunction.java
@@ -7,7 +7,7 @@ import java.util.Arrays;
* This class represents a fixed-width bucket-function in a {@link GroupingExpression}. It maps the input into the given
* number of buckets by the result of the argument expression.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class FixedWidthFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/FunctionNode.java b/container-search/src/main/java/com/yahoo/search/grouping/request/FunctionNode.java
index ce4442fa781..1a059d79b1a 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/FunctionNode.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/FunctionNode.java
@@ -8,7 +8,7 @@ import java.util.*;
* to {@link AggregatorNode} and {@link DocumentValue} that operate on inputs), this expression type can be used at any
* input level (see {@link GroupingExpression#resolveLevel(int)}).
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class FunctionNode extends GroupingExpression implements Iterable<GroupingExpression> {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/GroupingExpression.java b/container-search/src/main/java/com/yahoo/search/grouping/request/GroupingExpression.java
index d15608fcbeb..2f7691156e5 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/GroupingExpression.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/GroupingExpression.java
@@ -9,7 +9,7 @@ import java.util.List;
* This class represents an expression in a {@link GroupingOperation}. You may manually construct this expression, or
* you may use the {@link com.yahoo.search.grouping.request.parser.GroupingParser} to generate one from a query-string.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class GroupingExpression extends GroupingNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/GroupingNode.java b/container-search/src/main/java/com/yahoo/search/grouping/request/GroupingNode.java
index 918bff3c218..148b4243a4b 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/GroupingNode.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/GroupingNode.java
@@ -5,7 +5,7 @@ package com.yahoo.search.grouping.request;
* This is the abstract super class of both {@link GroupingOperation} and {@link GroupingExpression}. All nodes can be
* assigned a {@link String} label which in turn can be used to identify the corresponding result objects.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class GroupingNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/GroupingOperation.java b/container-search/src/main/java/com/yahoo/search/grouping/request/GroupingOperation.java
index ccce5fa8241..4c29ca7d98d 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/GroupingOperation.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/GroupingOperation.java
@@ -16,7 +16,7 @@ import java.util.*;
* com.yahoo.search.grouping.GroupingRequest} using the {@link com.yahoo.search.grouping.GroupingRequest#setRootOperation(GroupingOperation)}
* method.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public abstract class GroupingOperation extends GroupingNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/HourOfDayFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/HourOfDayFunction.java
index 42982df637d..faf1c1e0289 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/HourOfDayFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/HourOfDayFunction.java
@@ -7,7 +7,7 @@ import java.util.Arrays;
* This class represents an hour-of-day timestamp-function in a {@link GroupingExpression}. It evaluates to a long that
* equals the hour of day (0-23) of the result of the argument.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class HourOfDayFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/LongBucket.java b/container-search/src/main/java/com/yahoo/search/grouping/request/LongBucket.java
index cd8e807b1a6..fd363320033 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/LongBucket.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/LongBucket.java
@@ -4,7 +4,7 @@ package com.yahoo.search.grouping.request;
/**
* This class represents a {@link Long} bucket in a {@link PredefinedFunction}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class LongBucket extends BucketValue {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/LongPredefined.java b/container-search/src/main/java/com/yahoo/search/grouping/request/LongPredefined.java
index ee9d57b5b30..98acc800ed7 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/LongPredefined.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/LongPredefined.java
@@ -7,7 +7,7 @@ import java.util.List;
* This class represents a predefined bucket-function in a {@link GroupingExpression} for expressions that evaluate to a
* long.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class LongPredefined extends PredefinedFunction {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/LongValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/LongValue.java
index 2f5d833a5be..4c4eb1c409d 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/LongValue.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/LongValue.java
@@ -4,7 +4,7 @@ package com.yahoo.search.grouping.request;
/**
* This class represents a constant {@link Long} value in a {@link GroupingExpression}.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class LongValue extends ConstantValue<Long> {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathResolver.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathResolver.java
index 75b897a60c1..8aa841930e3 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/MathResolver.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathResolver.java
@@ -10,7 +10,7 @@ import java.util.Stack;
* operation simply push operator-expression pairs onto it, before calling {@link #resolve()} to retrieve the single
* corresponding grouping expression object.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MathResolver {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MaxAggregator.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MaxAggregator.java
index 7de8dd62f27..54dc7c6b3bd 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/MaxAggregator.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MaxAggregator.java
@@ -5,7 +5,7 @@ package com.yahoo.search.grouping.request;
* This class represents an maximum-aggregator in a {@link GroupingExpression}. It evaluates to the maximum value that
* the contained expression evaluated to over all the inputs.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MaxAggregator extends AggregatorNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MaxFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MaxFunction.java
index 570379afe66..731c1c4eba4 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/MaxFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MaxFunction.java
@@ -7,7 +7,7 @@ import java.util.List;
* This class represents a max-function in a {@link GroupingExpression}. It evaluates to a number that equals the
* largest of the results of all arguments.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MaxFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/Md5Function.java b/container-search/src/main/java/com/yahoo/search/grouping/request/Md5Function.java
index 701a2d1cc38..6ae33b804e9 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/Md5Function.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/Md5Function.java
@@ -7,7 +7,7 @@ import java.util.Arrays;
* This class represents an md5-function in a {@link GroupingExpression}. It evaluates to a long that equals the md5 of
* the result of the argument.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Md5Function extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MinAggregator.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MinAggregator.java
index c15fa4cb954..1a24527dcfc 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/MinAggregator.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MinAggregator.java
@@ -5,7 +5,7 @@ package com.yahoo.search.grouping.request;
* This class represents an minimum-aggregator in a {@link GroupingExpression}. It evaluates to the minimum value that
* the contained expression evaluated to over all the inputs.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MinAggregator extends AggregatorNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MinFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MinFunction.java
index 05f6f9d9abf..478684dd73e 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/MinFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MinFunction.java
@@ -7,7 +7,7 @@ import java.util.List;
* This class represents a min-function in a {@link GroupingExpression}. It evaluates to a number that equals the
* smallest of the results of all arguments.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MinFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MinuteOfHourFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MinuteOfHourFunction.java
index 81815490bf1..d2442b8455e 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/MinuteOfHourFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MinuteOfHourFunction.java
@@ -7,7 +7,7 @@ import java.util.Arrays;
* This class represents a minute-of-hour timestamp-function in a {@link GroupingExpression}. It evaluates to a long
* that equals the minute of hour (0-59) of the result of the argument.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MinuteOfHourFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/ModFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/ModFunction.java
index ac6bee5a04e..a3fd1041e6e 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/ModFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/ModFunction.java
@@ -8,7 +8,7 @@ import java.util.List;
* of mod'ing the results of all arguments in the order they were given to the constructor (modulo first argument by
* second, result by third, ...).
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ModFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MonthOfYearFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MonthOfYearFunction.java
index 4540efff23e..96e08562e42 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/MonthOfYearFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MonthOfYearFunction.java
@@ -7,7 +7,7 @@ import java.util.Arrays;
* This class represents a month-of-year timestamp-function in a {@link GroupingExpression}. It evaluates to a long that
* equals the month of year (1-12) of the result of the argument.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MonthOfYearFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MulFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MulFunction.java
index 60b4729ce33..84584270f58 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/MulFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MulFunction.java
@@ -7,7 +7,7 @@ import java.util.List;
* This class represents a mul-function in a {@link GroupingExpression}. It evaluates to a number that equals the result
* of multiplying the results of all arguments together in the order they were given to the constructor.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MulFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/NegFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/NegFunction.java
index 1429f3a8605..7e05b02b59e 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/NegFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/NegFunction.java
@@ -7,7 +7,7 @@ import java.util.Arrays;
* This class represents a negate-function in a {@link GroupingExpression}. It evaluates to a number that equals the
* negative of the results of the argument.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NegFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/NowFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/NowFunction.java
index ae1cd4f247d..24dd888e98d 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/NowFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/NowFunction.java
@@ -7,7 +7,7 @@ import java.util.Collections;
* This class represents a now-function in a {@link GroupingExpression}. It evaluates to a long that equals the number
* of seconds since midnight, January 1, 1970 UTC.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NowFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/OrFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/OrFunction.java
index 592709c4c91..ad1809f312f 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/OrFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/OrFunction.java
@@ -7,7 +7,7 @@ import java.util.List;
* This class represents an or-function in a {@link GroupingExpression}. It evaluates to a long that equals the result
* of or'ing the results of all arguments together in the order they were given to the constructor.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class OrFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/PredefinedFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/PredefinedFunction.java
index 7f880ffc2fc..ebae4b238b4 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/PredefinedFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/PredefinedFunction.java
@@ -9,7 +9,7 @@ import java.util.List;
* This class represents a predefined bucket-function in a {@link GroupingExpression}. It maps the input into one of the
* given buckets by the result of the argument expression.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class PredefinedFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/RelevanceValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/RelevanceValue.java
index 869304e117d..01735c0bdab 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/RelevanceValue.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/RelevanceValue.java
@@ -5,7 +5,7 @@ package com.yahoo.search.grouping.request;
* This class represents a document relevance score in a {@link GroupingExpression}. It evaluates to the relevance of
* the input {@link com.yahoo.search.result.Hit}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RelevanceValue extends DocumentValue {
@@ -15,5 +15,6 @@ public class RelevanceValue extends DocumentValue {
public RelevanceValue() {
super("relevance()");
}
+
}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/SecondOfMinuteFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/SecondOfMinuteFunction.java
index 8402f607a33..b83065af629 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/SecondOfMinuteFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/SecondOfMinuteFunction.java
@@ -7,7 +7,7 @@ import java.util.Arrays;
* This class represents a second-of-minute timestamp-function in a {@link GroupingExpression}. It evaluates to a long
* that equals the second of minute (0-59) of the result of the argument.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SecondOfMinuteFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/SizeFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/SizeFunction.java
index 4366446f84e..4b7055ffb89 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/SizeFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/SizeFunction.java
@@ -7,7 +7,7 @@ import java.util.Arrays;
* This class represents a size-function in a {@link GroupingExpression}. It evaluates to a number that equals the
* number of elements in the result of the argument (e.g. the number of elements in an array).
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SizeFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/StrCatFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/StrCatFunction.java
index d0fd495cc42..97dcc909254 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/StrCatFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/StrCatFunction.java
@@ -7,7 +7,7 @@ import java.util.List;
* This class represents a strcat-function in a {@link GroupingExpression}. It evaluates to a string that equals the
* contatenation of the string results of all arguments in the order they were given to the constructor.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class StrCatFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/StrLenFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/StrLenFunction.java
index 2aaefa8e99a..e9ca48b3db3 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/StrLenFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/StrLenFunction.java
@@ -7,7 +7,7 @@ import java.util.Arrays;
* This class represents a strcat-function in a {@link GroupingExpression}. It evaluates to a long that equals the
* number of bytes in the string result of the argument.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class StrLenFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/StringBucket.java b/container-search/src/main/java/com/yahoo/search/grouping/request/StringBucket.java
index fbbe08dbcf4..80e85ed9e78 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/StringBucket.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/StringBucket.java
@@ -4,7 +4,7 @@ package com.yahoo.search.grouping.request;
/**
* This class represents a {@link String} bucket in a {@link PredefinedFunction}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class StringBucket extends BucketValue {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/StringPredefined.java b/container-search/src/main/java/com/yahoo/search/grouping/request/StringPredefined.java
index 618f5e3bb25..465ff96157d 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/StringPredefined.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/StringPredefined.java
@@ -7,7 +7,7 @@ import java.util.List;
* This class represents a predefined bucket-function in a {@link GroupingExpression} for expressions that evaluate to a
* string.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class StringPredefined extends PredefinedFunction {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/StringValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/StringValue.java
index 78bc8960fc7..4fc630c7b1b 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/StringValue.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/StringValue.java
@@ -4,7 +4,7 @@ package com.yahoo.search.grouping.request;
/**
* This class represents a constant {@link String} value in a {@link GroupingExpression}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class StringValue extends ConstantValue<String> {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/SubFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/SubFunction.java
index e28f6f3b412..1d0456ffcf2 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/SubFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/SubFunction.java
@@ -8,7 +8,7 @@ import java.util.List;
* of subtracting the results of all arguments in the order they were given to the constructor (subtract second argument
* from first, third from result, ...).
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SubFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/SumAggregator.java b/container-search/src/main/java/com/yahoo/search/grouping/request/SumAggregator.java
index 369f5637e8f..b50dd84e27e 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/SumAggregator.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/SumAggregator.java
@@ -5,7 +5,7 @@ package com.yahoo.search.grouping.request;
* This class represents an sum-aggregator in a {@link GroupingExpression}. It evaluates to the sum of the values that
* the contained expression evaluated to over all the inputs.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SumAggregator extends AggregatorNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/SummaryValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/SummaryValue.java
index 165d8a2b8e3..6cdaa3b2e4a 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/SummaryValue.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/SummaryValue.java
@@ -5,7 +5,7 @@ package com.yahoo.search.grouping.request;
* This class represents a document summary in a {@link GroupingExpression}. It evaluates to the summary of the input
* {@link com.yahoo.search.result.Hit} that corresponds to the named summary class.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SummaryValue extends DocumentValue {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/TimeFunctions.java b/container-search/src/main/java/com/yahoo/search/grouping/request/TimeFunctions.java
index 3b1040a0b2e..c12d32044da 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/TimeFunctions.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/TimeFunctions.java
@@ -6,7 +6,7 @@ package com.yahoo.search.grouping.request;
* per-function factory methods, this class also contains a {@link #newInstance(com.yahoo.search.grouping.request.TimeFunctions.Type,
* GroupingExpression)} method which is useful for runtime construction of grouping requests.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class TimeFunctions {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/XorAggregator.java b/container-search/src/main/java/com/yahoo/search/grouping/request/XorAggregator.java
index 31cbe46e0cb..18ce7abd96c 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/XorAggregator.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/XorAggregator.java
@@ -5,7 +5,7 @@ package com.yahoo.search.grouping.request;
* This class represents an xor-aggregator in a {@link GroupingExpression}. It evaluates to the xor of the values that
* the contained expression evaluated to over all the inputs.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class XorAggregator extends AggregatorNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/XorBitFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/XorBitFunction.java
index 21928a2f060..2f6061a3aa2 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/XorBitFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/XorBitFunction.java
@@ -7,7 +7,7 @@ import java.util.Arrays;
* This class represents an xor-function in a {@link GroupingExpression}. It evaluates to a long that equals the xor of
* 'width' bits over the binary representation of the result of the argument.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class XorBitFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/XorFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/XorFunction.java
index d0fbac7e055..c3a5f5d88ad 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/XorFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/XorFunction.java
@@ -7,7 +7,7 @@ import java.util.List;
* This class represents an xor-function in a {@link GroupingExpression}. It evaluates to a long that equals the result
* of and'ing the results of all arguments together in the order they were given to the constructor.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class XorFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/YearFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/YearFunction.java
index 9d880939892..5bda8231929 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/YearFunction.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/YearFunction.java
@@ -7,7 +7,7 @@ import java.util.Arrays;
* This class represents a year timestamp-function in a {@link GroupingExpression}. It evaluates to a long that equals
* the full year (e.g. 2010) of the result of the argument.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class YearFunction extends FunctionNode {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/YmumValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/YmumValue.java
index 7bec89f6218..c5749c4673d 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/YmumValue.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/YmumValue.java
@@ -5,7 +5,7 @@ package com.yahoo.search.grouping.request;
* This class represents a document checksum in a {@link GroupingExpression}. It evaluates to the YMUM checksum of the
* input {@link com.yahoo.search.result.Hit}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class YmumValue extends DocumentValue {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/parser/GroupingParserInput.java b/container-search/src/main/java/com/yahoo/search/grouping/request/parser/GroupingParserInput.java
index 0c71348d368..189281485da 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/request/parser/GroupingParserInput.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/parser/GroupingParserInput.java
@@ -4,7 +4,7 @@ package com.yahoo.search.grouping.request.parser;
import com.yahoo.javacc.FastCharStream;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GroupingParserInput extends FastCharStream implements CharStream {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/result/AbstractList.java b/container-search/src/main/java/com/yahoo/search/grouping/result/AbstractList.java
index e4c351149f6..baa6c4c4253 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/result/AbstractList.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/result/AbstractList.java
@@ -9,7 +9,7 @@ import java.util.HashMap;
import java.util.Map;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public abstract class AbstractList extends HitGroup {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/result/BucketGroupId.java b/container-search/src/main/java/com/yahoo/search/grouping/result/BucketGroupId.java
index 7ee55e85479..07729b4524a 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/result/BucketGroupId.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/result/BucketGroupId.java
@@ -7,7 +7,7 @@ import static com.yahoo.text.Lowercase.toLowerCase;
* This abstract class is used in {@link Group} instances where the identifying expression evaluated to a {@link
* com.yahoo.search.grouping.request.BucketValue}. The range is inclusive-from and exclusive-to.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public abstract class BucketGroupId<T> extends GroupId {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/result/DoubleBucketId.java b/container-search/src/main/java/com/yahoo/search/grouping/result/DoubleBucketId.java
index 5b9ae94b3a2..3a85f32c790 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/result/DoubleBucketId.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/result/DoubleBucketId.java
@@ -5,7 +5,7 @@ package com.yahoo.search.grouping.result;
* This class is used in {@link Group} instances where the identifying expression evaluated to a {@link
* com.yahoo.search.grouping.request.DoubleBucket}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DoubleBucketId extends BucketGroupId<Double> {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/result/DoubleId.java b/container-search/src/main/java/com/yahoo/search/grouping/result/DoubleId.java
index d74140d3778..114d0c62cb9 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/result/DoubleId.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/result/DoubleId.java
@@ -4,7 +4,7 @@ package com.yahoo.search.grouping.result;
/**
* This class is used in {@link Group} instances where the identifying expression evaluated to a {@link Double}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DoubleId extends ValueGroupId<Double> {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/result/Group.java b/container-search/src/main/java/com/yahoo/search/grouping/result/Group.java
index c0c6c67e463..2a1e3199d7d 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/result/Group.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/result/Group.java
@@ -10,7 +10,7 @@ import com.yahoo.search.result.Relevance;
* as fields, use {@link #getField(String)} to access), {@link GroupList} and {@link HitList}. Use the {@link
* com.yahoo.search.grouping.GroupingRequest#getResultGroup(com.yahoo.search.Result)} to retrieve an instance of this.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class Group extends HitGroup {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/result/GroupId.java b/container-search/src/main/java/com/yahoo/search/grouping/result/GroupId.java
index 81ce8b7573b..27be1209bbc 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/result/GroupId.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/result/GroupId.java
@@ -10,7 +10,7 @@ package com.yahoo.search.grouping.result;
* The {@link #toString()} method of this class generates a URI-compatible string on the form
* "group:&lt;typeName&gt;:&lt;subclassSpecific&gt;".
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class GroupId {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/result/GroupList.java b/container-search/src/main/java/com/yahoo/search/grouping/result/GroupList.java
index ca51be8702f..2788ece2c36 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/result/GroupList.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/result/GroupList.java
@@ -9,7 +9,7 @@ import com.yahoo.search.grouping.GroupingRequest;
* contains one or more {@link Group groups} itself, allowing for a hierarchy of grouping results. Use the {@link
* GroupingRequest#getResultGroup(Result)} to retrieve grouping results.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GroupList extends AbstractList {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/result/HitList.java b/container-search/src/main/java/com/yahoo/search/grouping/result/HitList.java
index 8f4d72012a6..03e0f7e6d59 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/result/HitList.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/result/HitList.java
@@ -10,7 +10,7 @@ import com.yahoo.search.result.Hit;
* contains one or more {@link Hit hits} itself, making this the parent of leaf nodes in the hierarchy of grouping
* results. Use the {@link GroupingRequest#getResultGroup(Result)} to retrieve grouping results.</p>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class HitList extends AbstractList {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/result/HitRenderer.java b/container-search/src/main/java/com/yahoo/search/grouping/result/HitRenderer.java
index 37906c8012f..3907f87a276 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/result/HitRenderer.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/result/HitRenderer.java
@@ -13,7 +13,7 @@ import java.util.Map;
/**
* This is a helper class for rendering grouping results.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public abstract class HitRenderer {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/result/LongBucketId.java b/container-search/src/main/java/com/yahoo/search/grouping/result/LongBucketId.java
index 219356d6799..4a9d403c33d 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/result/LongBucketId.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/result/LongBucketId.java
@@ -5,7 +5,7 @@ package com.yahoo.search.grouping.result;
* This class is used in {@link Group} instances where the identifying expression evaluated to a {@link
* com.yahoo.search.grouping.request.LongBucket}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class LongBucketId extends BucketGroupId<Long> {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/result/LongId.java b/container-search/src/main/java/com/yahoo/search/grouping/result/LongId.java
index 4c6fe465eb6..7254e488deb 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/result/LongId.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/result/LongId.java
@@ -4,7 +4,7 @@ package com.yahoo.search.grouping.result;
/**
* This class is used in {@link Group} instances where the identifying expression evaluated to a {@link Long}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class LongId extends ValueGroupId<Long> {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/result/NullId.java b/container-search/src/main/java/com/yahoo/search/grouping/result/NullId.java
index 98e74fbf1b1..6edf1cabca9 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/result/NullId.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/result/NullId.java
@@ -6,7 +6,7 @@ package com.yahoo.search.grouping.result;
* fall outside the buckets of a {@link com.yahoo.search.grouping.request.PredefinedFunction} are added to an
* auto-generated group with this id.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NullId extends GroupId {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/result/RawId.java b/container-search/src/main/java/com/yahoo/search/grouping/result/RawId.java
index 9b347bb950d..11720756754 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/result/RawId.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/result/RawId.java
@@ -6,7 +6,7 @@ import java.util.Arrays;
/**
* This class is used in {@link Group} instances where the identifying expression evaluated to a {@link Byte} array.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RawId extends ValueGroupId<byte[]> {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/result/RootGroup.java b/container-search/src/main/java/com/yahoo/search/grouping/result/RootGroup.java
index 360daa7a0b1..571ad2d3365 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/result/RootGroup.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/result/RootGroup.java
@@ -8,7 +8,7 @@ import com.yahoo.search.result.Relevance;
* This class represents the root {@link Group} in the grouping result model. This class adds a {@link Continuation}
* object that can be used to paginate the result.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RootGroup extends Group {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/result/RootId.java b/container-search/src/main/java/com/yahoo/search/grouping/result/RootId.java
index e4650de610b..d5667dd0383 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/result/RootId.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/result/RootId.java
@@ -4,7 +4,7 @@ package com.yahoo.search.grouping.result;
/**
* This class is used in {@link RootGroup} instances.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RootId extends GroupId {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/result/StringBucketId.java b/container-search/src/main/java/com/yahoo/search/grouping/result/StringBucketId.java
index 9eebb110aab..e34b50e6df8 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/result/StringBucketId.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/result/StringBucketId.java
@@ -5,7 +5,7 @@ package com.yahoo.search.grouping.result;
* This class is used in {@link Group} instances where the identifying expression evaluated to a {@link
* com.yahoo.search.grouping.request.StringBucket}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class StringBucketId extends BucketGroupId<String> {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/result/StringId.java b/container-search/src/main/java/com/yahoo/search/grouping/result/StringId.java
index 3eea3220c83..4448d587a03 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/result/StringId.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/result/StringId.java
@@ -4,7 +4,7 @@ package com.yahoo.search.grouping.result;
/**
* This class is used in {@link Group} instances where the identifying expression evaluated to a {@link String}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class StringId extends ValueGroupId<String> {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/result/ValueGroupId.java b/container-search/src/main/java/com/yahoo/search/grouping/result/ValueGroupId.java
index 17945b8b5bd..bebd3fd85b0 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/result/ValueGroupId.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/result/ValueGroupId.java
@@ -6,7 +6,7 @@ import static com.yahoo.text.Lowercase.toLowerCase;
/**
* This abstract class is used in {@link Group} instances where the identifying expression evaluated to a singe value.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class ValueGroupId<T> extends GroupId {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/vespa/CompositeContinuation.java b/container-search/src/main/java/com/yahoo/search/grouping/vespa/CompositeContinuation.java
index 5d83ff813c7..a02b80db16f 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/vespa/CompositeContinuation.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/vespa/CompositeContinuation.java
@@ -8,7 +8,7 @@ import java.util.Iterator;
import java.util.List;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
class CompositeContinuation extends EncodableContinuation implements Iterable<EncodableContinuation> {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/vespa/ContinuationDecoder.java b/container-search/src/main/java/com/yahoo/search/grouping/vespa/ContinuationDecoder.java
index b3dab1cf527..151524e0350 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/vespa/ContinuationDecoder.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/vespa/ContinuationDecoder.java
@@ -4,7 +4,7 @@ package com.yahoo.search.grouping.vespa;
import com.yahoo.search.grouping.Continuation;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ContinuationDecoder {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/vespa/EncodableContinuation.java b/container-search/src/main/java/com/yahoo/search/grouping/vespa/EncodableContinuation.java
index 47c9f34ffbe..2ec371475c2 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/vespa/EncodableContinuation.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/vespa/EncodableContinuation.java
@@ -4,7 +4,7 @@ package com.yahoo.search.grouping.vespa;
import com.yahoo.search.grouping.Continuation;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
abstract class EncodableContinuation extends Continuation {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/vespa/ExpressionConverter.java b/container-search/src/main/java/com/yahoo/search/grouping/vespa/ExpressionConverter.java
index 4ce50335b2f..d2dfb3c0ee7 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/vespa/ExpressionConverter.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/vespa/ExpressionConverter.java
@@ -156,7 +156,7 @@ import com.yahoo.searchlib.expression.ZCurveFunctionNode;
* This is a helper class for {@link RequestBuilder} that offloads the code to convert {@link GroupingExpression} type
* objects to back-end specific expressions. This is a straightforward one-to-one conversion.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
class ExpressionConverter {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/vespa/GroupingExecutor.java b/container-search/src/main/java/com/yahoo/search/grouping/vespa/GroupingExecutor.java
index ae128e70f20..bf7eb8dc12e 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/vespa/GroupingExecutor.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/vespa/GroupingExecutor.java
@@ -37,7 +37,7 @@ import com.yahoo.vespa.objects.ObjectPredicate;
* transformation from the abstract request to Vespa grouping expressions (using {@link RequestBuilder}), and the
* corresponding transformation of results (using {@link ResultBuilder}).
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
@After({ GroupingValidator.GROUPING_VALIDATED,
"com.yahoo.search.querytransform.WandSearcher",
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/vespa/GroupingTransform.java b/container-search/src/main/java/com/yahoo/search/grouping/vespa/GroupingTransform.java
index a0a3f1b4e55..e58448ea2ef 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/vespa/GroupingTransform.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/vespa/GroupingTransform.java
@@ -13,7 +13,7 @@ import java.util.Set;
* transformed into a list {@link com.yahoo.searchlib.aggregation.Grouping} objects, so that the results of those
* queries can be transformed into something that corresponds to the original request.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
class GroupingTransform {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/vespa/HitConverter.java b/container-search/src/main/java/com/yahoo/search/grouping/vespa/HitConverter.java
index 617c037d2a7..1d7d380f4db 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/vespa/HitConverter.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/vespa/HitConverter.java
@@ -13,7 +13,7 @@ import com.yahoo.searchlib.aggregation.VdsHit;
/**
* Implementation of the {@link ResultBuilder.HitConverter} interface for {@link GroupingExecutor}.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
class HitConverter implements ResultBuilder.HitConverter {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/vespa/IntegerDecoder.java b/container-search/src/main/java/com/yahoo/search/grouping/vespa/IntegerDecoder.java
index a92238fa0a0..e1a222d6bc0 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/vespa/IntegerDecoder.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/vespa/IntegerDecoder.java
@@ -2,7 +2,7 @@
package com.yahoo.search.grouping.vespa;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
class IntegerDecoder {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/vespa/IntegerEncoder.java b/container-search/src/main/java/com/yahoo/search/grouping/vespa/IntegerEncoder.java
index 5cd1ee1fc13..b17991b22f1 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/vespa/IntegerEncoder.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/vespa/IntegerEncoder.java
@@ -2,7 +2,7 @@
package com.yahoo.search.grouping.vespa;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
class IntegerEncoder {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/vespa/OffsetContinuation.java b/container-search/src/main/java/com/yahoo/search/grouping/vespa/OffsetContinuation.java
index 7aff32a5847..6adec94132e 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/vespa/OffsetContinuation.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/vespa/OffsetContinuation.java
@@ -2,7 +2,7 @@
package com.yahoo.search.grouping.vespa;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
class OffsetContinuation extends EncodableContinuation {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/vespa/RequestBuilder.java b/container-search/src/main/java/com/yahoo/search/grouping/vespa/RequestBuilder.java
index 6a35e599eba..46b1fecd7de 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/vespa/RequestBuilder.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/vespa/RequestBuilder.java
@@ -17,7 +17,7 @@ import java.util.*;
* This class implements the necessary logic to build a list of {@link Grouping} objects from an instance of {@link
* GroupingOperation}. It is used by the {@link GroupingExecutor}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
class RequestBuilder {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/vespa/ResultBuilder.java b/container-search/src/main/java/com/yahoo/search/grouping/vespa/ResultBuilder.java
index 2bb752122ba..7e2404c34e8 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/vespa/ResultBuilder.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/vespa/ResultBuilder.java
@@ -52,7 +52,7 @@ import java.util.Map;
* This class implements the necessary logic to build a {@link RootGroup} from a list of {@link Grouping} objects. It is
* used by the {@link GroupingExecutor}.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
class ResultBuilder {
@@ -383,7 +383,7 @@ class ResultBuilder {
* Defines a helper interface to convert Vespa style grouping hits into corresponding instances of {@link Hit}. It
* is an interface to simplify testing.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public interface HitConverter {
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/vespa/ResultId.java b/container-search/src/main/java/com/yahoo/search/grouping/vespa/ResultId.java
index 21fa8b2f553..0d09e7ab69d 100644
--- a/container-search/src/main/java/com/yahoo/search/grouping/vespa/ResultId.java
+++ b/container-search/src/main/java/com/yahoo/search/grouping/vespa/ResultId.java
@@ -4,7 +4,7 @@ package com.yahoo.search.grouping.vespa;
import java.util.Arrays;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
class ResultId {
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 e9e4e34727c..3bfaee658f9 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
@@ -20,8 +20,8 @@ import com.yahoo.container.jdisc.LoggingRequestHandler;
import com.yahoo.container.jdisc.VespaHeaders;
import com.yahoo.container.logging.AccessLog;
import com.yahoo.container.protect.FreezeDetector;
+import com.yahoo.io.IOUtils;
import com.yahoo.jdisc.Metric;
-import com.yahoo.jdisc.Response;
import com.yahoo.language.Linguistics;
import com.yahoo.log.LogLevel;
import com.yahoo.net.HostName;
@@ -33,6 +33,9 @@ import com.yahoo.prelude.query.parser.ParseException;
import com.yahoo.prelude.query.parser.SpecialTokenRegistry;
import com.yahoo.processing.rendering.Renderer;
import com.yahoo.processing.request.CompoundName;
+import com.yahoo.slime.Inspector;
+import com.yahoo.slime.ObjectTraverser;
+import com.yahoo.vespa.config.SlimeUtils;
import com.yahoo.yolean.Exceptions;
import com.yahoo.search.Query;
import com.yahoo.search.Result;
@@ -55,9 +58,10 @@ import com.yahoo.statistics.Statistics;
import com.yahoo.statistics.Value;
import com.yahoo.vespa.configdefinition.SpecialtokensConfig;
import edu.umd.cs.findbugs.annotations.NonNull;
-
-import java.util.ArrayList;
-import java.util.Iterator;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
@@ -82,6 +86,8 @@ public class SearchHandler extends LoggingRequestHandler {
/** Event name for number of connections to the search subsystem */
private static final String SEARCH_CONNECTIONS = "search_connections";
+ private static final String JSON_CONTENT_TYPE = "application/json";
+
private static Logger log = Logger.getLogger(SearchHandler.class.getName());
private Value searchConnections;
@@ -275,14 +281,14 @@ public class SearchHandler extends LoggingRequestHandler {
return errorResponse(request, ErrorMessage.createInternalServerError(Exceptions.toMessageString(e)));
}
- private HttpSearchResponse handleBody(HttpRequest request) {
+
+ private HttpSearchResponse handleBody(HttpRequest request){
// Find query profile
String queryProfileName = request.getProperty("queryProfile");
CompiledQueryProfile queryProfile = queryProfileRegistry.findQueryProfile(queryProfileName);
boolean benchmarkOutput = VespaHeaders.benchmarkOutput(request);
- // Create query
- Query query = new Query(request, queryProfile);
+ Query query = queryFromRequest(request, queryProfile);
boolean benchmarkCoverage = VespaHeaders.benchmarkCoverage(benchmarkOutput, request.getJDiscRequest().headers());
@@ -552,4 +558,60 @@ public class SearchHandler extends LoggingRequestHandler {
return searchChainRegistry;
}
+ private Query queryFromRequest(HttpRequest request, CompiledQueryProfile queryProfile){
+ if (request.getMethod() == com.yahoo.jdisc.http.HttpRequest.Method.POST
+ && JSON_CONTENT_TYPE.equals(request.getHeader(com.yahoo.jdisc.http.HttpHeaders.Names.CONTENT_TYPE))) {
+ Inspector inspector;
+ try {
+ byte[] byteArray = IOUtils.readBytes(request.getData(), 1 << 20);
+ inspector = SlimeUtils.jsonToSlime(byteArray).get();
+ if (inspector.field("error_message").valid()){
+ throw new QueryException("Illegal query: "+inspector.field("error_message").asString() + ", at: "+ new String(inspector.field("offending_input").asData(), StandardCharsets.UTF_8));
+ }
+
+ } catch (IOException e) {
+ throw new RuntimeException("Problem with reading from input-stream", e);
+ }
+
+ // Create request-mapping
+ Map<String, String> requestMap = new HashMap<>();
+ createRequestMapping(inspector, requestMap, "");
+ return new Query(request, requestMap, queryProfile);
+
+
+ } else {
+ return new Query(request, queryProfile);
+
+ }
+ }
+
+ public void createRequestMapping(Inspector inspector, Map<String, String> map, String parent) {
+ inspector.traverse((ObjectTraverser) (key, value) -> {
+ String qualifiedKey = parent + key;
+ switch (value.type()) {
+ case BOOL:
+ map.put(qualifiedKey, Boolean.toString(value.asBool()));
+ break;
+ case DOUBLE:
+ map.put(qualifiedKey, Double.toString(value.asDouble()));
+ break;
+ case LONG:
+ map.put(qualifiedKey, Long.toString(value.asLong()));
+ break;
+ case STRING:
+ map.put(qualifiedKey , value.asString());
+ break;
+ case ARRAY:
+ map.put(qualifiedKey, value.asString());
+ break;
+ case OBJECT:
+ createRequestMapping(value, map, qualifiedKey+".");
+ break;
+ }
+
+ });
+ }
+
}
+
+
diff --git a/container-search/src/main/java/com/yahoo/search/query/Model.java b/container-search/src/main/java/com/yahoo/search/query/Model.java
index dc7a61344cb..167bb312f61 100644
--- a/container-search/src/main/java/com/yahoo/search/query/Model.java
+++ b/container-search/src/main/java/com/yahoo/search/query/Model.java
@@ -78,11 +78,11 @@ public class Model implements Cloneable {
private String defaultIndex = null;
private Query.Type type = Query.Type.ALL;
private Query parent;
- private Set<String> sources=new LinkedHashSet<>();
- private Set<String> restrict=new LinkedHashSet<>();
+ private Set<String> sources = new LinkedHashSet<>();
+ private Set<String> restrict = new LinkedHashSet<>();
private String searchPath;
private String documentDbName = null;
- private Execution execution=new Execution(new Execution.Context(null, null, null, null, null));
+ private Execution execution = new Execution(new Execution.Context(null, null, null, null, null));
public Model(Query query) {
setParent(query);
@@ -101,7 +101,7 @@ public class Model implements Cloneable {
*/
@Deprecated
public void traceLanguage() {
- if (getParent().getTraceLevel()<2) return;
+ if (getParent().getTraceLevel() < 2) return;
if (language != null) {
getParent().trace("Language " + getLanguage() + " specified directly as a parameter", false, 2);
}
diff --git a/container-search/src/main/java/com/yahoo/search/query/parser/Parsable.java b/container-search/src/main/java/com/yahoo/search/query/parser/Parsable.java
index 55e5c7ae07d..e5941a90b83 100644
--- a/container-search/src/main/java/com/yahoo/search/query/parser/Parsable.java
+++ b/container-search/src/main/java/com/yahoo/search/query/parser/Parsable.java
@@ -25,7 +25,7 @@ import java.util.Set;
* <p>In case you are parsing the content of a {@link Model}, you can use the {@link #fromQueryModel(Model)} factory for
* convenience.</p>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public final class Parsable {
diff --git a/container-search/src/main/java/com/yahoo/search/query/parser/Parser.java b/container-search/src/main/java/com/yahoo/search/query/parser/Parser.java
index 2d050426b00..32c386f0e32 100644
--- a/container-search/src/main/java/com/yahoo/search/query/parser/Parser.java
+++ b/container-search/src/main/java/com/yahoo/search/query/parser/Parser.java
@@ -6,7 +6,7 @@ import com.yahoo.search.query.QueryTree;
/**
* Defines the interface of a query parser. To construct an instance of this class, use the {@link ParserFactory}.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public interface Parser {
diff --git a/container-search/src/main/java/com/yahoo/search/query/parser/ParserEnvironment.java b/container-search/src/main/java/com/yahoo/search/query/parser/ParserEnvironment.java
index ba782cd17c6..ca437fb9def 100644
--- a/container-search/src/main/java/com/yahoo/search/query/parser/ParserEnvironment.java
+++ b/container-search/src/main/java/com/yahoo/search/query/parser/ParserEnvironment.java
@@ -13,7 +13,7 @@ import com.yahoo.search.searchchain.Execution;
* This class encapsulates the environment of a {@link Parser}. In case you are creating a parser from within a
* {@link Searcher}, you can use the {@link #fromExecutionContext(Execution.Context)} factory for convenience.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public final class ParserEnvironment {
diff --git a/container-search/src/main/java/com/yahoo/search/query/parser/ParserFactory.java b/container-search/src/main/java/com/yahoo/search/query/parser/ParserFactory.java
index d102bc39a88..8d008abaac2 100644
--- a/container-search/src/main/java/com/yahoo/search/query/parser/ParserFactory.java
+++ b/container-search/src/main/java/com/yahoo/search/query/parser/ParserFactory.java
@@ -8,7 +8,7 @@ import com.yahoo.search.yql.YqlParser;
/**
* Implements a factory for {@link Parser}.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public final class ParserFactory {
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/config/QueryProfileConfigurer.java b/container-search/src/main/java/com/yahoo/search/query/profile/config/QueryProfileConfigurer.java
index 410f10edda8..1af78982b9c 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/config/QueryProfileConfigurer.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/config/QueryProfileConfigurer.java
@@ -42,23 +42,23 @@ public class QueryProfileConfigurer implements ConfigSubscriber.SingleSubscriber
}
public static QueryProfileRegistry createFromConfig(QueryProfilesConfig config) {
- QueryProfileRegistry registry=new QueryProfileRegistry();
+ QueryProfileRegistry registry = new QueryProfileRegistry();
// Pass 1: Create all profiles and profile types
for (QueryProfilesConfig.Queryprofiletype profileTypeConfig : config.queryprofiletype()) {
- createProfileType(profileTypeConfig,registry.getTypeRegistry());
+ createProfileType(profileTypeConfig, registry.getTypeRegistry());
}
for (QueryProfilesConfig.Queryprofile profileConfig : config.queryprofile()) {
- createProfile(profileConfig,registry);
+ createProfile(profileConfig, registry);
}
// Pass 2: Resolve references and add content
for (QueryProfilesConfig.Queryprofiletype profileTypeConfig : config.queryprofiletype()) {
- fillProfileType(profileTypeConfig,registry.getTypeRegistry());
+ fillProfileType(profileTypeConfig, registry.getTypeRegistry());
}
- // To ensure topological sorting, using DPS. This will _NOT_ detect cycles (but it will not fail if they
- // exist either)
+ // To ensure topological sorting, using DPS. This will _NOT_ detect cycles
+ // (but it will not fail if they exist)
Set<ComponentId> filled = new HashSet<>();
for (QueryProfilesConfig.Queryprofile profileConfig : config.queryprofile()) {
fillProfile(profileConfig, config, registry, filled);
@@ -73,29 +73,29 @@ public class QueryProfileConfigurer implements ConfigSubscriber.SingleSubscriber
subscriber.close();
}
- private static void createProfile(QueryProfilesConfig.Queryprofile config,QueryProfileRegistry registry) {
- QueryProfile profile=new QueryProfile(config.id());
+ private static void createProfile(QueryProfilesConfig.Queryprofile config, QueryProfileRegistry registry) {
+ QueryProfile profile = new QueryProfile(config.id());
try {
- String typeId=config.type();
- if (typeId!=null && !typeId.isEmpty())
+ String typeId = config.type();
+ if (typeId != null && ! typeId.isEmpty())
profile.setType(registry.getType(typeId));
if (config.dimensions().size()>0) {
- String[] dimensions=new String[config.dimensions().size()];
- for (int i=0; i<config.dimensions().size(); i++)
- dimensions[i]=config.dimensions().get(i);
+ String[] dimensions = new String[config.dimensions().size()];
+ for (int i = 0; i < config.dimensions().size(); i++)
+ dimensions[i] = config.dimensions().get(i);
profile.setDimensions(dimensions);
}
registry.register(profile);
}
catch (IllegalArgumentException e) {
- throw new IllegalArgumentException("Invalid " + profile,e);
+ throw new IllegalArgumentException("Invalid " + profile, e);
}
}
private static void createProfileType(QueryProfilesConfig.Queryprofiletype config, QueryProfileTypeRegistry registry) {
- QueryProfileType type=new QueryProfileType(config.id());
+ QueryProfileType type = new QueryProfileType(config.id());
type.setStrict(config.strict());
type.setMatchAsPath(config.matchaspath());
registry.register(type);
@@ -105,46 +105,46 @@ public class QueryProfileConfigurer implements ConfigSubscriber.SingleSubscriber
QueryProfilesConfig queryProfilesConfig,
QueryProfileRegistry registry,
Set<ComponentId> filled) {
- QueryProfile profile=registry.getComponent(new ComponentSpecification(config.id()).toId());
+ QueryProfile profile = registry.getComponent(new ComponentSpecification(config.id()).toId());
if (filled.contains(profile.getId())) return;
filled.add(profile.getId());
try {
for (String inheritedId : config.inherit()) {
- QueryProfile inherited=registry.getComponent(inheritedId);
- if (inherited==null)
+ QueryProfile inherited = registry.getComponent(inheritedId);
+ if (inherited == null)
throw new IllegalArgumentException("Inherited query profile '" + inheritedId + "' in " + profile + " was not found");
fillProfile(inherited, queryProfilesConfig, registry, filled);
profile.addInherited(inherited);
}
for (QueryProfilesConfig.Queryprofile.Reference referenceConfig : config.reference()) {
- QueryProfile referenced=registry.getComponent(referenceConfig.value());
- if (referenced==null)
+ QueryProfile referenced = registry.getComponent(referenceConfig.value());
+ if (referenced == null)
throw new IllegalArgumentException("Query profile '" + referenceConfig.value() + "' referenced as '" +
referenceConfig.name() + "' in " + profile + " was not found");
- profile.set(referenceConfig.name(),referenced, registry);
- if (referenceConfig.overridable()!=null && !referenceConfig.overridable().isEmpty())
- profile.setOverridable(referenceConfig.name(),BooleanParser.parseBoolean(referenceConfig.overridable()),null);
+ profile.set(referenceConfig.name(), referenced, registry);
+ if (referenceConfig.overridable() != null && !referenceConfig.overridable().isEmpty())
+ profile.setOverridable(referenceConfig.name(), BooleanParser.parseBoolean(referenceConfig.overridable()), null);
}
for (QueryProfilesConfig.Queryprofile.Property propertyConfig : config.property()) {
- profile.set(propertyConfig.name(),propertyConfig.value(), registry);
- if (propertyConfig.overridable()!=null && !propertyConfig.overridable().isEmpty())
- profile.setOverridable(propertyConfig.name(),BooleanParser.parseBoolean(propertyConfig.overridable()),null);
+ profile.set(propertyConfig.name(), propertyConfig.value(), registry);
+ if (propertyConfig.overridable() != null && ! propertyConfig.overridable().isEmpty())
+ profile.setOverridable(propertyConfig.name(), BooleanParser.parseBoolean(propertyConfig.overridable()), null);
}
for (QueryProfilesConfig.Queryprofile.Queryprofilevariant variantConfig : config.queryprofilevariant()) {
- String[] forDimensionValueArray=new String[variantConfig.fordimensionvalues().size()];
- for (int i=0; i<variantConfig.fordimensionvalues().size(); i++) {
- forDimensionValueArray[i]=variantConfig.fordimensionvalues().get(i).trim();
+ String[] forDimensionValueArray = new String[variantConfig.fordimensionvalues().size()];
+ for (int i = 0; i<variantConfig.fordimensionvalues().size(); i++) {
+ forDimensionValueArray[i] = variantConfig.fordimensionvalues().get(i).trim();
if ("*".equals(forDimensionValueArray[i]))
- forDimensionValueArray[i]=null;
+ forDimensionValueArray[i] = null;
}
- DimensionValues forDimensionValues=DimensionValues.createFrom(forDimensionValueArray);
+ DimensionValues forDimensionValues = DimensionValues.createFrom(forDimensionValueArray);
for (String inheritedId : variantConfig.inherit()) {
- QueryProfile inherited=registry.getComponent(inheritedId);
- if (inherited==null)
+ QueryProfile inherited = registry.getComponent(inheritedId);
+ if (inherited == null)
throw new IllegalArgumentException("Inherited query profile '" + inheritedId + "' in " + profile +
" for '" + forDimensionValues + "' was not found");
fillProfile(inherited, queryProfilesConfig, registry, filled);
@@ -153,7 +153,7 @@ public class QueryProfileConfigurer implements ConfigSubscriber.SingleSubscriber
for (QueryProfilesConfig.Queryprofile.Queryprofilevariant.Reference referenceConfig : variantConfig.reference()) {
QueryProfile referenced=registry.getComponent(referenceConfig.value());
- if (referenced==null)
+ if (referenced == null)
throw new IllegalArgumentException("Query profile '" + referenceConfig.value() + "' referenced as '" +
referenceConfig.name() + "' in " + profile + " for '" + forDimensionValues + "' was not found");
profile.set(referenceConfig.name(), referenced, forDimensionValues, registry);
@@ -167,7 +167,7 @@ public class QueryProfileConfigurer implements ConfigSubscriber.SingleSubscriber
}
catch (IllegalArgumentException e) {
- throw new IllegalArgumentException("Invalid " + profile,e);
+ throw new IllegalArgumentException("Invalid " + profile, e);
}
}
@@ -218,7 +218,7 @@ public class QueryProfileConfigurer implements ConfigSubscriber.SingleSubscriber
type.addField(field, registry);
}
catch (IllegalArgumentException e) {
- throw new IllegalArgumentException("Invalid field '" + fieldConfig.name() + "' in " + type,e);
+ throw new IllegalArgumentException("Invalid field '" + fieldConfig.name() + "' in " + type, e);
}
}
diff --git a/container-search/src/main/java/com/yahoo/search/query/textserialize/TextSerialize.java b/container-search/src/main/java/com/yahoo/search/query/textserialize/TextSerialize.java
index ccf343889a0..f44dab92fc7 100644
--- a/container-search/src/main/java/com/yahoo/search/query/textserialize/TextSerialize.java
+++ b/container-search/src/main/java/com/yahoo/search/query/textserialize/TextSerialize.java
@@ -12,7 +12,7 @@ import com.yahoo.search.query.textserialize.serializer.QueryTreeSerializer;
import java.io.StringReader;
/**
- * @author tonytv
+ * @author Tony Vaagenes
* Facade
* Allows serializing/deserializing a query to the programmatic format.
*/
diff --git a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/AndNotRestConverter.java b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/AndNotRestConverter.java
index d60979d7820..243596f2b2d 100644
--- a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/AndNotRestConverter.java
+++ b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/AndNotRestConverter.java
@@ -10,7 +10,7 @@ import static com.yahoo.search.query.textserialize.item.ListUtil.butFirst;
import static com.yahoo.search.query.textserialize.item.ListUtil.first;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class AndNotRestConverter extends CompositeConverter<NotItem> {
static final String andNotRest = "AND-NOT-REST";
diff --git a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/CompositeConverter.java b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/CompositeConverter.java
index 77b9bf45494..332f03c896c 100644
--- a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/CompositeConverter.java
+++ b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/CompositeConverter.java
@@ -9,7 +9,7 @@ import com.yahoo.search.query.textserialize.serializer.ItemIdMapper;
import java.util.ListIterator;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class CompositeConverter<T extends CompositeItem> implements ItemFormConverter {
private final Class<T> itemClass;
diff --git a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/IntConverter.java b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/IntConverter.java
index d01e80393b7..520d6eb7ec5 100644
--- a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/IntConverter.java
+++ b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/IntConverter.java
@@ -5,7 +5,7 @@ import com.yahoo.prelude.query.IntItem;
import com.yahoo.prelude.query.TermItem;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class IntConverter extends TermConverter {
@Override
diff --git a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemArguments.java b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemArguments.java
index d4bf0a1a412..4d998f3553c 100644
--- a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemArguments.java
+++ b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemArguments.java
@@ -8,7 +8,7 @@ import java.util.Map;
import static com.yahoo.search.query.textserialize.item.ListUtil.firstInstanceOf;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ItemArguments {
public final Map<?, ?> properties;
diff --git a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemContext.java b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemContext.java
index c34b1434366..e739608387d 100644
--- a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemContext.java
+++ b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemContext.java
@@ -9,7 +9,7 @@ import java.util.IdentityHashMap;
import java.util.Map;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ItemContext {
private class Connectivity {
diff --git a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemExecutorRegistry.java b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemExecutorRegistry.java
index 8f1d557aadb..4f18232df24 100644
--- a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemExecutorRegistry.java
+++ b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemExecutorRegistry.java
@@ -15,7 +15,7 @@ import java.util.HashMap;
import java.util.Map;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ItemExecutorRegistry {
diff --git a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemFormConverter.java b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemFormConverter.java
index 929f31a0e92..1c101943326 100644
--- a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemFormConverter.java
+++ b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemFormConverter.java
@@ -6,7 +6,7 @@ import com.yahoo.search.query.textserialize.serializer.DispatchForm;
import com.yahoo.search.query.textserialize.serializer.ItemIdMapper;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public interface ItemFormConverter {
Object formToItem(String name, ItemArguments arguments, ItemContext context);
diff --git a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemFormHandler.java b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemFormHandler.java
index faf03c1f9df..34841564c68 100644
--- a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemFormHandler.java
+++ b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemFormHandler.java
@@ -6,7 +6,7 @@ import com.yahoo.search.query.textserialize.parser.DispatchFormHandler;
import java.util.List;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ItemFormHandler implements DispatchFormHandler{
@Override
diff --git a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemInitializer.java b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemInitializer.java
index 43bec08b3f5..a1a240b9bfb 100644
--- a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemInitializer.java
+++ b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ItemInitializer.java
@@ -12,7 +12,7 @@ import java.util.List;
import java.util.Map;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ItemInitializer {
diff --git a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ListUtil.java b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ListUtil.java
index 7119669b0ca..71ac07fe2ea 100644
--- a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ListUtil.java
+++ b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/ListUtil.java
@@ -4,7 +4,7 @@ package com.yahoo.search.query.textserialize.item;
import java.util.*;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ListUtil {
public static <T> List<T> rest(List<T> list) {
diff --git a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/NearConverter.java b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/NearConverter.java
index 26829a70491..d622068b384 100644
--- a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/NearConverter.java
+++ b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/NearConverter.java
@@ -7,7 +7,7 @@ import com.yahoo.search.query.textserialize.serializer.DispatchForm;
import com.yahoo.search.query.textserialize.serializer.ItemIdMapper;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
@SuppressWarnings("rawtypes")
public class NearConverter extends CompositeConverter {
diff --git a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/PrefixConverter.java b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/PrefixConverter.java
index 0bfb69463ec..bbf87ef8532 100644
--- a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/PrefixConverter.java
+++ b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/PrefixConverter.java
@@ -4,7 +4,7 @@ package com.yahoo.search.query.textserialize.item;
import com.yahoo.prelude.query.PrefixItem;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class PrefixConverter extends WordConverter {
@Override
diff --git a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/SubStringConverter.java b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/SubStringConverter.java
index d99ef575597..d7e6aa3900c 100644
--- a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/SubStringConverter.java
+++ b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/SubStringConverter.java
@@ -4,7 +4,7 @@ package com.yahoo.search.query.textserialize.item;
import com.yahoo.prelude.query.SubstringItem;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class SubStringConverter extends WordConverter {
@Override
diff --git a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/SuffixConverter.java b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/SuffixConverter.java
index 3002ec39e48..c2de4772806 100644
--- a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/SuffixConverter.java
+++ b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/SuffixConverter.java
@@ -4,7 +4,7 @@ package com.yahoo.search.query.textserialize.item;
import com.yahoo.prelude.query.SuffixItem;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class SuffixConverter extends WordConverter {
@Override
diff --git a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/TermConverter.java b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/TermConverter.java
index 3a6bbf77d5e..04c01d7acc1 100644
--- a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/TermConverter.java
+++ b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/TermConverter.java
@@ -7,7 +7,7 @@ import com.yahoo.search.query.textserialize.serializer.DispatchForm;
import com.yahoo.search.query.textserialize.serializer.ItemIdMapper;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public abstract class TermConverter implements ItemFormConverter {
@Override
diff --git a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/TypeCheck.java b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/TypeCheck.java
index ebfcc8aa54c..b7843a300dc 100644
--- a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/TypeCheck.java
+++ b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/TypeCheck.java
@@ -4,7 +4,7 @@ package com.yahoo.search.query.textserialize.item;
import com.yahoo.protect.Validator;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class TypeCheck {
public static void ensureInstanceOf(Object object, Class<?> c) {
diff --git a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/WordConverter.java b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/WordConverter.java
index b4d40f26a1d..f37059cfe1d 100644
--- a/container-search/src/main/java/com/yahoo/search/query/textserialize/item/WordConverter.java
+++ b/container-search/src/main/java/com/yahoo/search/query/textserialize/item/WordConverter.java
@@ -5,7 +5,7 @@ import com.yahoo.prelude.query.TermItem;
import com.yahoo.prelude.query.WordItem;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class WordConverter extends TermConverter {
@Override
diff --git a/container-search/src/main/java/com/yahoo/search/query/textserialize/parser/DispatchFormHandler.java b/container-search/src/main/java/com/yahoo/search/query/textserialize/parser/DispatchFormHandler.java
index e196952c903..d3d3b030d7a 100644
--- a/container-search/src/main/java/com/yahoo/search/query/textserialize/parser/DispatchFormHandler.java
+++ b/container-search/src/main/java/com/yahoo/search/query/textserialize/parser/DispatchFormHandler.java
@@ -4,7 +4,7 @@ package com.yahoo.search.query.textserialize.parser;
import java.util.List;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public interface DispatchFormHandler {
Object dispatch(String name, List<Object> arguments, Object dispatchContext);
diff --git a/container-search/src/main/java/com/yahoo/search/query/textserialize/serializer/DispatchForm.java b/container-search/src/main/java/com/yahoo/search/query/textserialize/serializer/DispatchForm.java
index 27032065e6b..11ec6e4b020 100644
--- a/container-search/src/main/java/com/yahoo/search/query/textserialize/serializer/DispatchForm.java
+++ b/container-search/src/main/java/com/yahoo/search/query/textserialize/serializer/DispatchForm.java
@@ -7,7 +7,7 @@ import java.util.List;
import java.util.Map;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class DispatchForm {
private final String name;
diff --git a/container-search/src/main/java/com/yahoo/search/query/textserialize/serializer/ItemIdMapper.java b/container-search/src/main/java/com/yahoo/search/query/textserialize/serializer/ItemIdMapper.java
index e5473d77574..df589fcd76a 100644
--- a/container-search/src/main/java/com/yahoo/search/query/textserialize/serializer/ItemIdMapper.java
+++ b/container-search/src/main/java/com/yahoo/search/query/textserialize/serializer/ItemIdMapper.java
@@ -7,7 +7,7 @@ import java.util.IdentityHashMap;
import java.util.Map;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ItemIdMapper {
private final Map<Item, String> idByItem = new IdentityHashMap<>();
diff --git a/container-search/src/main/java/com/yahoo/search/query/textserialize/serializer/QueryTreeSerializer.java b/container-search/src/main/java/com/yahoo/search/query/textserialize/serializer/QueryTreeSerializer.java
index 337c1d4a59e..7cdeb9f9597 100644
--- a/container-search/src/main/java/com/yahoo/search/query/textserialize/serializer/QueryTreeSerializer.java
+++ b/container-search/src/main/java/com/yahoo/search/query/textserialize/serializer/QueryTreeSerializer.java
@@ -6,7 +6,7 @@ import com.yahoo.search.query.textserialize.item.ItemExecutorRegistry;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class QueryTreeSerializer {
public String serialize(Item root) {
diff --git a/container-search/src/main/java/com/yahoo/search/query/textserialize/serializer/Serializer.java b/container-search/src/main/java/com/yahoo/search/query/textserialize/serializer/Serializer.java
index 828fb6ce415..d4e499bab79 100644
--- a/container-search/src/main/java/com/yahoo/search/query/textserialize/serializer/Serializer.java
+++ b/container-search/src/main/java/com/yahoo/search/query/textserialize/serializer/Serializer.java
@@ -11,7 +11,7 @@ import static com.yahoo.search.query.textserialize.item.ListUtil.butFirst;
import static com.yahoo.search.query.textserialize.item.ListUtil.first;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
class Serializer {
static String serialize(Object child, ItemIdMapper itemIdMapper) {
diff --git a/container-search/src/main/java/com/yahoo/search/rendering/DefaultRenderer.java b/container-search/src/main/java/com/yahoo/search/rendering/DefaultRenderer.java
index 2d69a262f15..aa825149bb7 100644
--- a/container-search/src/main/java/com/yahoo/search/rendering/DefaultRenderer.java
+++ b/container-search/src/main/java/com/yahoo/search/rendering/DefaultRenderer.java
@@ -42,7 +42,7 @@ import java.util.stream.Collectors;
/**
* XML rendering of search results. This is NOT the default (but it once was).
*
- * @author tonytv
+ * @author Tony Vaagenes
* @deprecated use JsonRenderer instead
*/
@SuppressWarnings({ "rawtypes", "deprecation" })
diff --git a/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java b/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java
index 55c846ccb5b..7d60d7cf9ee 100644
--- a/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java
+++ b/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java
@@ -9,7 +9,10 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Preconditions;
import com.yahoo.data.JsonProducer;
import com.yahoo.data.access.Inspectable;
+import com.yahoo.data.access.Inspector;
+import com.yahoo.data.access.Type;
import com.yahoo.data.access.simple.JsonRender;
+import com.yahoo.data.access.simple.Value;
import com.yahoo.document.datatypes.FieldValue;
import com.yahoo.document.datatypes.StringFieldValue;
import com.yahoo.document.datatypes.TensorFieldValue;
@@ -741,6 +744,34 @@ public class JsonRenderer extends AsynchronousSectionedRenderer<Result> {
return true;
}
+ private static Inspector wrapAsMap(Inspector data) {
+ if (data.type() != Type.ARRAY) return null;
+ if (data.entryCount() == 0) return null;
+ Value.ObjectValue map = new Value.ObjectValue();
+ for (int i = 0; i < data.entryCount(); i++) {
+ Inspector obj = data.entry(i);
+ if (obj.type() != Type.OBJECT) return null;
+ if (obj.fieldCount() != 2) return null;
+ Inspector key = obj.field("key");
+ Inspector value = obj.field("value");
+ if (key.type() != Type.STRING) return null;
+ if (! value.valid()) return null;
+ map.put(key.asString(), value);
+ }
+ return map;
+ }
+
+ private void renderInspector(Inspector data) throws IOException {
+ StringBuilder intermediate = new StringBuilder();
+ Inspector asMap = wrapAsMap(data);
+ if (asMap != null) {
+ JsonRender.render(asMap, intermediate, true);
+ } else {
+ JsonRender.render(data, intermediate, true);
+ }
+ generator.writeRawValue(intermediate.toString());
+ }
+
private void renderFieldContents(Object field) throws IOException {
if (field == null) {
generator.writeNull();
@@ -750,12 +781,10 @@ public class JsonRenderer extends AsynchronousSectionedRenderer<Result> {
generator.writeTree((TreeNode) field);
} else if (field instanceof Tensor) {
renderTensor(Optional.of((Tensor)field));
+ } else if (field instanceof Inspectable) {
+ renderInspector(((Inspectable)field).inspect());
} else if (field instanceof JsonProducer) {
generator.writeRawValue(((JsonProducer) field).toJson());
- } else if (field instanceof Inspectable) {
- StringBuilder intermediate = new StringBuilder();
- JsonRender.render((Inspectable) field, intermediate, true);
- generator.writeRawValue(intermediate.toString());
} else if (field instanceof StringFieldValue) {
generator.writeString(((StringFieldValue)field).getString());
} else if (field instanceof TensorFieldValue) {
diff --git a/container-search/src/main/java/com/yahoo/search/rendering/Renderer.java b/container-search/src/main/java/com/yahoo/search/rendering/Renderer.java
index a5ade01b1be..a61c657b709 100644
--- a/container-search/src/main/java/com/yahoo/search/rendering/Renderer.java
+++ b/container-search/src/main/java/com/yahoo/search/rendering/Renderer.java
@@ -26,7 +26,7 @@ import java.nio.charset.CharsetEncoder;
* <li>State mutated during rendering shall be initialized in the init method.</li>
* </ol>
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
abstract public class Renderer extends com.yahoo.processing.rendering.Renderer<Result> {
diff --git a/container-search/src/main/java/com/yahoo/search/rendering/SectionedRenderer.java b/container-search/src/main/java/com/yahoo/search/rendering/SectionedRenderer.java
index c35a4efc50c..bd647bd74f0 100644
--- a/container-search/src/main/java/com/yahoo/search/rendering/SectionedRenderer.java
+++ b/container-search/src/main/java/com/yahoo/search/rendering/SectionedRenderer.java
@@ -29,7 +29,7 @@ import java.util.List;
* <li>State mutated during rendering shall be initialized in the init method.
* </ol>
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
abstract public class SectionedRenderer<WRITER> extends Renderer {
/**
diff --git a/container-search/src/main/java/com/yahoo/search/rendering/SyncDefaultRenderer.java b/container-search/src/main/java/com/yahoo/search/rendering/SyncDefaultRenderer.java
index 08599540cb1..6919f6bd3f8 100644
--- a/container-search/src/main/java/com/yahoo/search/rendering/SyncDefaultRenderer.java
+++ b/container-search/src/main/java/com/yahoo/search/rendering/SyncDefaultRenderer.java
@@ -35,7 +35,7 @@ import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
@SuppressWarnings({ "rawtypes", "deprecation" })
public final class SyncDefaultRenderer extends Renderer {
diff --git a/container-search/src/main/java/com/yahoo/search/result/MetaHitsFirstComparator.java b/container-search/src/main/java/com/yahoo/search/result/MetaHitsFirstComparator.java
index 389222f6cec..a15cd30d664 100644
--- a/container-search/src/main/java/com/yahoo/search/result/MetaHitsFirstComparator.java
+++ b/container-search/src/main/java/com/yahoo/search/result/MetaHitsFirstComparator.java
@@ -7,7 +7,7 @@ import java.util.Comparator;
* Ensures that meta hits are sorted before normal hits. All meta hits are
* considered equal.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class MetaHitsFirstComparator extends ChainableComparator {
diff --git a/container-search/src/main/java/com/yahoo/search/result/Relevance.java b/container-search/src/main/java/com/yahoo/search/result/Relevance.java
index 7737b01cc14..69d49dc33ed 100644
--- a/container-search/src/main/java/com/yahoo/search/result/Relevance.java
+++ b/container-search/src/main/java/com/yahoo/search/result/Relevance.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.search.result;
-import com.yahoo.text.DoubleFormatter;
-
/**
* A relevance double value. These values should always be normalized between 0 and 1 (where 1 means perfect),
* however, this is not enforced.
@@ -47,7 +45,7 @@ public class Relevance implements Comparable<Relevance> {
*/
@Override
public String toString() {
- return DoubleFormatter.stringValue(score);
+ return String.valueOf(score);
}
/** Compares relevancy values with */
diff --git a/container-search/src/main/java/com/yahoo/search/searchchain/model/VespaSearchers.java b/container-search/src/main/java/com/yahoo/search/searchchain/model/VespaSearchers.java
index cfef91ee0ec..1657b45b1b4 100644
--- a/container-search/src/main/java/com/yahoo/search/searchchain/model/VespaSearchers.java
+++ b/container-search/src/main/java/com/yahoo/search/searchchain/model/VespaSearchers.java
@@ -17,7 +17,7 @@ import java.util.*;
/**
* Defines the searcher models used in the vespa and native search chains, except for federation.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
@SuppressWarnings({"rawtypes", "deprecation", "unchecked"})
public class VespaSearchers {
diff --git a/container-search/src/main/java/com/yahoo/search/searchchain/model/federation/FederationOptions.java b/container-search/src/main/java/com/yahoo/search/searchchain/model/federation/FederationOptions.java
index a24b13a6d17..0843ec074b6 100644
--- a/container-search/src/main/java/com/yahoo/search/searchchain/model/federation/FederationOptions.java
+++ b/container-search/src/main/java/com/yahoo/search/searchchain/model/federation/FederationOptions.java
@@ -9,7 +9,7 @@ import java.util.Objects;
* Options for controlling federation to a single source.
* This is a value object.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
@Immutable
public class FederationOptions implements Cloneable {
diff --git a/container-search/src/main/java/com/yahoo/search/searchchain/model/federation/FederationSearcherModel.java b/container-search/src/main/java/com/yahoo/search/searchchain/model/federation/FederationSearcherModel.java
index 9088575b881..e1c95e9478e 100644
--- a/container-search/src/main/java/com/yahoo/search/searchchain/model/federation/FederationSearcherModel.java
+++ b/container-search/src/main/java/com/yahoo/search/searchchain/model/federation/FederationSearcherModel.java
@@ -15,7 +15,7 @@ import com.yahoo.search.federation.FederationSearcher;
/**
* Specifies how a federation searcher is to be set up.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
@Immutable
public class FederationSearcherModel extends ChainedComponentModel {
diff --git a/container-search/src/main/java/com/yahoo/search/searchchain/model/federation/HttpProviderSpec.java b/container-search/src/main/java/com/yahoo/search/searchchain/model/federation/HttpProviderSpec.java
index 4495ec25908..4103514c103 100644
--- a/container-search/src/main/java/com/yahoo/search/searchchain/model/federation/HttpProviderSpec.java
+++ b/container-search/src/main/java/com/yahoo/search/searchchain/model/federation/HttpProviderSpec.java
@@ -13,7 +13,7 @@ import java.util.List;
/**
* Specifies how a http provider is to be set up.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
@Immutable
public class HttpProviderSpec {
diff --git a/container-search/src/main/java/com/yahoo/search/searchchain/model/federation/LocalProviderSpec.java b/container-search/src/main/java/com/yahoo/search/searchchain/model/federation/LocalProviderSpec.java
index 5d244286c57..a18b93b8a6f 100644
--- a/container-search/src/main/java/com/yahoo/search/searchchain/model/federation/LocalProviderSpec.java
+++ b/container-search/src/main/java/com/yahoo/search/searchchain/model/federation/LocalProviderSpec.java
@@ -16,7 +16,7 @@ import net.jcip.annotations.Immutable;
/**
* Specifies how a local provider is to be set up.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
@Immutable
public class LocalProviderSpec {
@@ -42,7 +42,8 @@ public class LocalProviderSpec {
com.yahoo.prelude.searcher.ValidatePredicateSearcher.class,
com.yahoo.search.searchers.ValidateMatchPhaseSearcher.class,
com.yahoo.search.yql.FieldFiller.class,
- com.yahoo.search.searchers.InputCheckingSearcher.class);
+ com.yahoo.search.searchers.InputCheckingSearcher.class,
+ com.yahoo.search.searchers.ContainerLatencySearcher.class);
public final String clusterName;
diff --git a/container-search/src/main/java/com/yahoo/search/searchers/ContainerLatencySearcher.java b/container-search/src/main/java/com/yahoo/search/searchers/ContainerLatencySearcher.java
new file mode 100644
index 00000000000..cd93482be88
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/searchers/ContainerLatencySearcher.java
@@ -0,0 +1,37 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.searchers;
+
+import com.yahoo.component.chain.dependencies.After;
+import com.yahoo.metrics.simple.Gauge;
+import com.yahoo.metrics.simple.Point;
+import com.yahoo.metrics.simple.PointBuilder;
+import com.yahoo.metrics.simple.MetricReceiver;
+import com.yahoo.search.Query;
+import com.yahoo.search.Result;
+import com.yahoo.search.Searcher;
+import com.yahoo.search.searchchain.Execution;
+import com.yahoo.search.searchchain.PhaseNames;
+
+/**
+ * Measure latency in container before query is sent to backend
+ *
+ * @author Arne H Juul
+ */
+@After(PhaseNames.BACKEND)
+public class ContainerLatencySearcher extends Searcher {
+ private final Gauge latencyGauge;
+
+ public ContainerLatencySearcher(MetricReceiver metrics) {
+ latencyGauge = metrics.declareGauge("query_container_latency");
+ }
+
+ @Override
+ public Result search(Query query, Execution execution) {
+ Point dims = latencyGauge.builder()
+ .set("chain", execution.chain().getId().stringValue())
+ .build();
+ latencyGauge.sample(query.getDurationTime(), dims);
+ return execution.search(query);
+ }
+
+}
diff --git a/container-search/src/main/java/com/yahoo/search/yql/VespaGroupingStep.java b/container-search/src/main/java/com/yahoo/search/yql/VespaGroupingStep.java
index 6ba33ee012b..4626109f6cd 100644
--- a/container-search/src/main/java/com/yahoo/search/yql/VespaGroupingStep.java
+++ b/container-search/src/main/java/com/yahoo/search/yql/VespaGroupingStep.java
@@ -8,7 +8,7 @@ import java.util.ArrayList;
import java.util.List;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class VespaGroupingStep {
diff --git a/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java b/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java
index 0b9f79537d0..6bad032600c 100644
--- a/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java
+++ b/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java
@@ -90,7 +90,7 @@ import edu.umd.cs.findbugs.annotations.NonNull;
*
* @author Steinar Knutsen
* @author Stian Kristoffersen
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
@Beta
public class YqlParser implements Parser {
diff --git a/container-search/src/main/javacc/com/yahoo/search/query/textserialize/parser/Parser.jj b/container-search/src/main/javacc/com/yahoo/search/query/textserialize/parser/Parser.jj
index d9766fa40f6..5cf14429aff 100644
--- a/container-search/src/main/javacc/com/yahoo/search/query/textserialize/parser/Parser.jj
+++ b/container-search/src/main/javacc/com/yahoo/search/query/textserialize/parser/Parser.jj
@@ -1,6 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
/**
- * @author <a href="mailto:tonyv@yahoo-inc.com">tonyv</a>
+ * @author Tony Vaagenes
*/
options {
LOOKAHEAD = 1;
diff --git a/container-search/src/test/java/com/yahoo/fs4/test/HexByteIteratorTestCase.java b/container-search/src/test/java/com/yahoo/fs4/test/HexByteIteratorTestCase.java
index b07363d7df2..022feb09b2e 100644
--- a/container-search/src/test/java/com/yahoo/fs4/test/HexByteIteratorTestCase.java
+++ b/container-search/src/test/java/com/yahoo/fs4/test/HexByteIteratorTestCase.java
@@ -12,7 +12,7 @@ import static org.junit.Assert.assertTrue;
/**
* Test of HexByteIterator
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class HexByteIteratorTestCase {
diff --git a/container-search/src/test/java/com/yahoo/prelude/IndexFactsFactory.java b/container-search/src/test/java/com/yahoo/prelude/IndexFactsFactory.java
index fe2a264e03a..2272bbbb947 100644
--- a/container-search/src/test/java/com/yahoo/prelude/IndexFactsFactory.java
+++ b/container-search/src/test/java/com/yahoo/prelude/IndexFactsFactory.java
@@ -7,7 +7,7 @@ import com.yahoo.search.config.IndexInfoConfig;
import com.yahoo.container.QrSearchersConfig;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class IndexFactsFactory {
diff --git a/container-search/src/test/java/com/yahoo/prelude/fastsearch/SlimeSummaryTestCase.java b/container-search/src/test/java/com/yahoo/prelude/fastsearch/SlimeSummaryTestCase.java
index 421f70c8d07..81f16f7f261 100644
--- a/container-search/src/test/java/com/yahoo/prelude/fastsearch/SlimeSummaryTestCase.java
+++ b/container-search/src/test/java/com/yahoo/prelude/fastsearch/SlimeSummaryTestCase.java
@@ -35,9 +35,11 @@ import static org.junit.Assert.fail;
public class SlimeSummaryTestCase {
- private static final String summary_cf = "file:src/test/java/com/yahoo/prelude/fastsearch/summary.cfg";
- private static final String partial_summary1_cf = "file:src/test/java/com/yahoo/prelude/fastsearch/partial-summary1.cfg";
- private static final String partial_summary2_cf = "file:src/test/java/com/yahoo/prelude/fastsearch/partial-summary2.cfg";
+ private static final String cf_pre = "file:src/test/java/com/yahoo/prelude/fastsearch/";
+ private static final String summary_cf = cf_pre + "summary.cfg";
+ private static final String partial_summary1_cf = cf_pre + "partial-summary1.cfg";
+ private static final String partial_summary2_cf = cf_pre + "partial-summary2.cfg";
+ private static final String partial_summary3_cf = cf_pre + "partial-summary3.cfg";
@Test
public void testDecodingEmpty() {
@@ -143,6 +145,7 @@ public class SlimeSummaryTestCase {
public void testFieldAccessAPI() {
DocsumDefinitionSet partialDocsum1 = createDocsumDefinitionSet(partial_summary1_cf);
DocsumDefinitionSet partialDocsum2 = createDocsumDefinitionSet(partial_summary2_cf);
+ DocsumDefinitionSet partialDocsum3 = createDocsumDefinitionSet(partial_summary3_cf);
DocsumDefinitionSet fullDocsum = createDocsumDefinitionSet(summary_cf);
FastHit hit = new FastHit();
Map<String, Object> expected = new HashMap<>();
@@ -273,6 +276,18 @@ public class SlimeSummaryTestCase {
expected.put("string_field", "string_value");
expected.put("longstring_field", "longstring_value");
assertFields(expected, hit);
+
+ hit.removeField("string_field");
+ hit.removeField("integer_field");
+ partialDocsum3.lazyDecode("partial3", partialSummary3(), hit);
+ expected.put("string_field", "new str val");
+ expected.put("integer_field", 5);
+ assertFields(expected, hit);
+
+ hit.removeField("integer_field");
+ partialDocsum2.lazyDecode("partial2", partialSummary2(), hit);
+ expected.put("integer_field", 4);
+ assertFields(expected, hit);
}
@@ -344,6 +359,14 @@ public class SlimeSummaryTestCase {
return encode((slime));
}
+ private byte[] partialSummary3() {
+ Slime slime = new Slime();
+ Cursor docsum = slime.setObject();
+ docsum.setString("string_field", "new str val");
+ docsum.setLong("integer_field", 5);
+ return encode((slime));
+ }
+
private byte[] fullishSummary() {
Slime slime = new Slime();
Cursor docsum = slime.setObject();
diff --git a/container-search/src/test/java/com/yahoo/prelude/fastsearch/partial-summary3.cfg b/container-search/src/test/java/com/yahoo/prelude/fastsearch/partial-summary3.cfg
new file mode 100644
index 00000000000..5d7319fd393
--- /dev/null
+++ b/container-search/src/test/java/com/yahoo/prelude/fastsearch/partial-summary3.cfg
@@ -0,0 +1,10 @@
+documentdb[1]
+documentdb[0].name test
+documentdb[0].summaryclass[1]
+documentdb[0].summaryclass[0].name partial3
+documentdb[0].summaryclass[0].id 3
+documentdb[0].summaryclass[0].fields[3]
+documentdb[0].summaryclass[0].fields[0].name integer_field
+documentdb[0].summaryclass[0].fields[0].type integer
+documentdb[0].summaryclass[0].fields[1].name string_field
+documentdb[0].summaryclass[0].fields[1].type string
diff --git a/container-search/src/test/java/com/yahoo/prelude/fastsearch/test/FastSearcherTestCase.java b/container-search/src/test/java/com/yahoo/prelude/fastsearch/test/FastSearcherTestCase.java
index 2a91319a905..ec586e67763 100644
--- a/container-search/src/test/java/com/yahoo/prelude/fastsearch/test/FastSearcherTestCase.java
+++ b/container-search/src/test/java/com/yahoo/prelude/fastsearch/test/FastSearcherTestCase.java
@@ -115,8 +115,8 @@ public class FastSearcherTestCase {
.name("simpler").hasRankFeatures(false).hasSummaryFeatures(false))));
List<SearchCluster.Node> nodes = new ArrayList<>();
- nodes.add(new SearchCluster.Node("host1", 5000, 0));
- nodes.add(new SearchCluster.Node("host2", 5000, 0));
+ nodes.add(new SearchCluster.Node(0, "host1", 5000, 0));
+ nodes.add(new SearchCluster.Node(2, "host2", 5000, 0));
MockFS4ResourcePool mockFs4ResourcePool = new MockFS4ResourcePool();
FastSearcher fastSearcher = new FastSearcher(new MockBackend(),
@@ -442,7 +442,7 @@ public class FastSearcherTestCase {
public void testSinglePassGroupingIsForcedWithSingleNodeGroups() {
FastSearcher fastSearcher = new FastSearcher(new MockBackend(),
new FS4ResourcePool(1),
- new MockDispatcher(new SearchCluster.Node("host0", 123, 0)),
+ new MockDispatcher(new SearchCluster.Node(0, "host0", 123, 0)),
new SummaryParameters(null),
new ClusterParams("testhittype"),
new CacheParams(100, 1e64),
@@ -465,8 +465,8 @@ public class FastSearcherTestCase {
@Test
public void testSinglePassGroupingIsNotForcedWithSingleNodeGroups() {
MockDispatcher dispatcher =
- new MockDispatcher(ImmutableList.of(new SearchCluster.Node("host0", 123, 0),
- new SearchCluster.Node("host1", 123, 0)));
+ new MockDispatcher(ImmutableList.of(new SearchCluster.Node(0, "host0", 123, 0),
+ new SearchCluster.Node(2, "host1", 123, 0)));
FastSearcher fastSearcher = new FastSearcher(new MockBackend(),
new FS4ResourcePool(1),
diff --git a/container-search/src/test/java/com/yahoo/prelude/fastsearch/test/FastSearcherTester.java b/container-search/src/test/java/com/yahoo/prelude/fastsearch/test/FastSearcherTester.java
index 7c1f05e3e60..035710c612c 100644
--- a/container-search/src/test/java/com/yahoo/prelude/fastsearch/test/FastSearcherTester.java
+++ b/container-search/src/test/java/com/yahoo/prelude/fastsearch/test/FastSearcherTester.java
@@ -58,9 +58,10 @@ class FastSearcherTester {
private static List<SearchCluster.Node> toNodes(String... hostAndPortAndGroupStrings) {
List<SearchCluster.Node> nodes = new ArrayList<>();
+ int key = 0;
for (String s : hostAndPortAndGroupStrings) {
String[] parts = s.split(":");
- nodes.add(new SearchCluster.Node(parts[0], Integer.parseInt(parts[1]), Integer.parseInt(parts[2])));
+ nodes.add(new SearchCluster.Node(key++, parts[0], Integer.parseInt(parts[1]), Integer.parseInt(parts[2])));
}
return nodes;
}
diff --git a/container-search/src/test/java/com/yahoo/prelude/fastsearch/test/fs4mock/MockFS4ResourcePool.java b/container-search/src/test/java/com/yahoo/prelude/fastsearch/test/fs4mock/MockFS4ResourcePool.java
index 997fbb198ea..704dcd31c0d 100644
--- a/container-search/src/test/java/com/yahoo/prelude/fastsearch/test/fs4mock/MockFS4ResourcePool.java
+++ b/container-search/src/test/java/com/yahoo/prelude/fastsearch/test/fs4mock/MockFS4ResourcePool.java
@@ -7,6 +7,7 @@ import com.yahoo.prelude.fastsearch.FS4ResourcePool;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
/**
@@ -25,7 +26,7 @@ public class MockFS4ResourcePool extends FS4ResourcePool {
}
@Override
- public Backend getBackend(String hostname, int port) {
+ public Backend getBackend(String hostname, int port, Optional<Integer> distributionKey) {
countRequest(hostname + ":" + port);
if (nonRespondingBackends.contains(hostname))
return new MockBackend(hostname, NonWorkingMockFSChannel::new);
diff --git a/container-search/src/test/java/com/yahoo/prelude/query/parser/TestLinguistics.java b/container-search/src/test/java/com/yahoo/prelude/query/parser/TestLinguistics.java
index f281b3fcf53..fbe40494231 100644
--- a/container-search/src/test/java/com/yahoo/prelude/query/parser/TestLinguistics.java
+++ b/container-search/src/test/java/com/yahoo/prelude/query/parser/TestLinguistics.java
@@ -9,7 +9,7 @@ import com.yahoo.language.process.*;
import com.yahoo.language.simple.SimpleLinguistics;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class TestLinguistics implements Linguistics {
diff --git a/container-search/src/test/java/com/yahoo/prelude/query/textualrepresentation/test/TextualQueryRepresentationTestCase.java b/container-search/src/test/java/com/yahoo/prelude/query/textualrepresentation/test/TextualQueryRepresentationTestCase.java
index 1e0c1400518..4b24c88a1db 100644
--- a/container-search/src/test/java/com/yahoo/prelude/query/textualrepresentation/test/TextualQueryRepresentationTestCase.java
+++ b/container-search/src/test/java/com/yahoo/prelude/query/textualrepresentation/test/TextualQueryRepresentationTestCase.java
@@ -20,7 +20,7 @@ import static org.junit.Assert.assertEquals;
/**
* Test of TextualQueryRepresentation.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class TextualQueryRepresentationTestCase {
diff --git a/container-search/src/test/java/com/yahoo/prelude/querytransform/test/RecallSearcherTestCase.java b/container-search/src/test/java/com/yahoo/prelude/querytransform/test/RecallSearcherTestCase.java
index ad1c12629c3..f7d5e5b1a48 100755
--- a/container-search/src/test/java/com/yahoo/prelude/querytransform/test/RecallSearcherTestCase.java
+++ b/container-search/src/test/java/com/yahoo/prelude/querytransform/test/RecallSearcherTestCase.java
@@ -24,7 +24,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class RecallSearcherTestCase {
diff --git a/container-search/src/test/java/com/yahoo/prelude/searcher/test/MultipleResultsTestCase.java b/container-search/src/test/java/com/yahoo/prelude/searcher/test/MultipleResultsTestCase.java
index 5c74e288d18..aca2386facc 100644
--- a/container-search/src/test/java/com/yahoo/prelude/searcher/test/MultipleResultsTestCase.java
+++ b/container-search/src/test/java/com/yahoo/prelude/searcher/test/MultipleResultsTestCase.java
@@ -19,7 +19,7 @@ import static org.junit.Assert.assertEquals;
/**
* Test of MultipleResultsSearcher
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
@SuppressWarnings("deprecation")
public class MultipleResultsTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/federation/sourceref/test/SearchChainResolverTestCase.java b/container-search/src/test/java/com/yahoo/search/federation/sourceref/test/SearchChainResolverTestCase.java
index 4485a016ee6..5b6d3b6abce 100644
--- a/container-search/src/test/java/com/yahoo/search/federation/sourceref/test/SearchChainResolverTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/federation/sourceref/test/SearchChainResolverTestCase.java
@@ -22,7 +22,7 @@ import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class SearchChainResolverTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/federation/sourceref/test/SourceRefResolverTestCase.java b/container-search/src/test/java/com/yahoo/search/federation/sourceref/test/SourceRefResolverTestCase.java
index 714d1803605..ab07baf438a 100644
--- a/container-search/src/test/java/com/yahoo/search/federation/sourceref/test/SourceRefResolverTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/federation/sourceref/test/SourceRefResolverTestCase.java
@@ -28,7 +28,7 @@ import static org.junit.matchers.JUnitMatchers.hasItems;
/**
* Test for SourceRefResolver.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class SourceRefResolverTestCase {
private static final String cluster1 = "cluster1";
diff --git a/container-search/src/test/java/com/yahoo/search/federation/test/AddHitsWithRelevanceSearcher.java b/container-search/src/test/java/com/yahoo/search/federation/test/AddHitsWithRelevanceSearcher.java
index d50057438d8..b3cf5968651 100644
--- a/container-search/src/test/java/com/yahoo/search/federation/test/AddHitsWithRelevanceSearcher.java
+++ b/container-search/src/test/java/com/yahoo/search/federation/test/AddHitsWithRelevanceSearcher.java
@@ -8,7 +8,7 @@ import com.yahoo.search.result.Hit;
import com.yahoo.search.searchchain.Execution;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class AddHitsWithRelevanceSearcher extends Searcher {
public static final int numHitsAdded = 5;
diff --git a/container-search/src/test/java/com/yahoo/search/federation/test/BlockingSearcher.java b/container-search/src/test/java/com/yahoo/search/federation/test/BlockingSearcher.java
index 7c24ba74a46..7d96638adc9 100644
--- a/container-search/src/test/java/com/yahoo/search/federation/test/BlockingSearcher.java
+++ b/container-search/src/test/java/com/yahoo/search/federation/test/BlockingSearcher.java
@@ -7,7 +7,7 @@ import com.yahoo.search.Searcher;
import com.yahoo.search.searchchain.Execution;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class BlockingSearcher extends Searcher {
@Override
diff --git a/container-search/src/test/java/com/yahoo/search/federation/test/FederationSearcherTest.java b/container-search/src/test/java/com/yahoo/search/federation/test/FederationSearcherTest.java
index 939dd7ab42c..088a432324d 100644
--- a/container-search/src/test/java/com/yahoo/search/federation/test/FederationSearcherTest.java
+++ b/container-search/src/test/java/com/yahoo/search/federation/test/FederationSearcherTest.java
@@ -37,7 +37,7 @@ import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class FederationSearcherTest {
diff --git a/container-search/src/test/java/com/yahoo/search/federation/test/FederationTester.java b/container-search/src/test/java/com/yahoo/search/federation/test/FederationTester.java
index c3d78757b41..a863bd4f687 100644
--- a/container-search/src/test/java/com/yahoo/search/federation/test/FederationTester.java
+++ b/container-search/src/test/java/com/yahoo/search/federation/test/FederationTester.java
@@ -15,7 +15,7 @@ import com.yahoo.search.searchchain.model.federation.FederationOptions;
import java.util.Collections;
/**
-* @author tonytv
+* @author Tony Vaagenes
*/
class FederationTester {
diff --git a/container-search/src/test/java/com/yahoo/search/federation/test/HitCountTestCase.java b/container-search/src/test/java/com/yahoo/search/federation/test/HitCountTestCase.java
index 9278d8999d7..a5eb5b35087 100644
--- a/container-search/src/test/java/com/yahoo/search/federation/test/HitCountTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/federation/test/HitCountTestCase.java
@@ -16,7 +16,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class HitCountTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/federation/test/SetHitCountsSearcher.java b/container-search/src/test/java/com/yahoo/search/federation/test/SetHitCountsSearcher.java
index 24d6801f2bd..ab49f3c313e 100644
--- a/container-search/src/test/java/com/yahoo/search/federation/test/SetHitCountsSearcher.java
+++ b/container-search/src/test/java/com/yahoo/search/federation/test/SetHitCountsSearcher.java
@@ -8,7 +8,7 @@ import com.yahoo.search.result.Hit;
import com.yahoo.search.searchchain.Execution;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
class SetHitCountsSearcher extends Searcher {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/ContinuationTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/ContinuationTestCase.java
index 3f84c324429..8546769ad07 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/ContinuationTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/ContinuationTestCase.java
@@ -7,7 +7,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ContinuationTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/GroupingQueryParserTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/GroupingQueryParserTestCase.java
index 08d36f8748d..8de704a8a0e 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/GroupingQueryParserTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/GroupingQueryParserTestCase.java
@@ -16,7 +16,7 @@ import java.util.TimeZone;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GroupingQueryParserTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/GroupingRequestTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/GroupingRequestTestCase.java
index b69061d20c0..494602be7b3 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/GroupingRequestTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/GroupingRequestTestCase.java
@@ -16,7 +16,7 @@ import java.util.Collections;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GroupingRequestTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/GroupingValidatorTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/GroupingValidatorTestCase.java
index fd0162acc5c..82c05c1d995 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/GroupingValidatorTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/GroupingValidatorTestCase.java
@@ -16,7 +16,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GroupingValidatorTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/request/BucketResolverTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/request/BucketResolverTestCase.java
index d76d704f37b..ed2d158397f 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/request/BucketResolverTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/request/BucketResolverTestCase.java
@@ -10,7 +10,7 @@ import java.util.List;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings({ "rawtypes" })
public class BucketResolverTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/request/ExpressionVisitorTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/request/ExpressionVisitorTestCase.java
index b245404ca30..13a5160ba86 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/request/ExpressionVisitorTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/request/ExpressionVisitorTestCase.java
@@ -10,7 +10,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ExpressionVisitorTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/request/GroupingOperationTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/request/GroupingOperationTestCase.java
index ee1f07970d1..cc834002fd8 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/request/GroupingOperationTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/request/GroupingOperationTestCase.java
@@ -10,7 +10,7 @@ import java.util.List;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GroupingOperationTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/request/MathResolverTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/request/MathResolverTestCase.java
index 5dee372e7d7..68d2177f2f7 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/request/MathResolverTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/request/MathResolverTestCase.java
@@ -7,7 +7,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MathResolverTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/request/RequestTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/request/RequestTestCase.java
index ce73fde9504..b049721a525 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/request/RequestTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/request/RequestTestCase.java
@@ -8,7 +8,7 @@ import java.util.Arrays;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RequestTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/request/parser/GroupingParserBenchmarkTest.java b/container-search/src/test/java/com/yahoo/search/grouping/request/parser/GroupingParserBenchmarkTest.java
index 37932e6f05e..60ada5124a0 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/request/parser/GroupingParserBenchmarkTest.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/request/parser/GroupingParserBenchmarkTest.java
@@ -11,7 +11,7 @@ import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GroupingParserBenchmarkTest {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/request/parser/GroupingParserTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/request/parser/GroupingParserTestCase.java
index 227530ae52d..db1f2f59e8d 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/request/parser/GroupingParserTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/request/parser/GroupingParserTestCase.java
@@ -20,7 +20,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GroupingParserTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/result/GroupIdTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/result/GroupIdTestCase.java
index 3dc6c185d8d..9ca2efb8e07 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/result/GroupIdTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/result/GroupIdTestCase.java
@@ -6,7 +6,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GroupIdTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/result/GroupListTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/result/GroupListTestCase.java
index f24c1371daf..651321a3b69 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/result/GroupListTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/result/GroupListTestCase.java
@@ -8,7 +8,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GroupListTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/result/GroupTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/result/GroupTestCase.java
index 1d0e6528521..fd4e8d60711 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/result/GroupTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/result/GroupTestCase.java
@@ -9,7 +9,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GroupTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/result/HitListTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/result/HitListTestCase.java
index 106b227599f..05a78b71cac 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/result/HitListTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/result/HitListTestCase.java
@@ -8,7 +8,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class HitListTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/result/HitRendererTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/result/HitRendererTestCase.java
index bee9aa90f56..85b8ff7a6d5 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/result/HitRendererTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/result/HitRendererTestCase.java
@@ -15,7 +15,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class HitRendererTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/vespa/CompositeContinuationTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/vespa/CompositeContinuationTestCase.java
index ea81f607eaf..6a6741ae1b4 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/vespa/CompositeContinuationTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/vespa/CompositeContinuationTestCase.java
@@ -9,7 +9,7 @@ import java.util.Iterator;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CompositeContinuationTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/vespa/GroupingExecutorTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/vespa/GroupingExecutorTestCase.java
index f8a9b06888d..11415b46b82 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/vespa/GroupingExecutorTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/vespa/GroupingExecutorTestCase.java
@@ -53,7 +53,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class GroupingExecutorTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/vespa/GroupingTransformTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/vespa/GroupingTransformTestCase.java
index 185bcae424a..038df42bd28 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/vespa/GroupingTransformTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/vespa/GroupingTransformTestCase.java
@@ -7,7 +7,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class GroupingTransformTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/vespa/HitConverterTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/vespa/HitConverterTestCase.java
index d21562a2569..4cd091098fe 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/vespa/HitConverterTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/vespa/HitConverterTestCase.java
@@ -24,7 +24,7 @@ import java.util.Collections;
import static org.junit.Assert.*;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class HitConverterTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/vespa/IntegerDecoderTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/vespa/IntegerDecoderTestCase.java
index 735befffb20..251dcff0ba4 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/vespa/IntegerDecoderTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/vespa/IntegerDecoderTestCase.java
@@ -7,7 +7,7 @@ import static org.junit.Assert.*;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class IntegerDecoderTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/vespa/IntegerEncoderTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/vespa/IntegerEncoderTestCase.java
index a3957fc1be6..3b48ae35fcf 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/vespa/IntegerEncoderTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/vespa/IntegerEncoderTestCase.java
@@ -6,7 +6,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class IntegerEncoderTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/vespa/OffsetContinuationTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/vespa/OffsetContinuationTestCase.java
index 84830e97874..f99b170dcbb 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/vespa/OffsetContinuationTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/vespa/OffsetContinuationTestCase.java
@@ -7,7 +7,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class OffsetContinuationTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/vespa/RequestBuilderTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/vespa/RequestBuilderTestCase.java
index 33e8dbbbef1..b8571aacca4 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/vespa/RequestBuilderTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/vespa/RequestBuilderTestCase.java
@@ -14,7 +14,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RequestBuilderTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/vespa/ResultBuilderTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/vespa/ResultBuilderTestCase.java
index 2417e2516e3..e27003984d3 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/vespa/ResultBuilderTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/vespa/ResultBuilderTestCase.java
@@ -21,7 +21,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ResultBuilderTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/grouping/vespa/ResultIdTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/vespa/ResultIdTestCase.java
index 8a031775381..35d39bcc2a9 100644
--- a/container-search/src/test/java/com/yahoo/search/grouping/vespa/ResultIdTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/grouping/vespa/ResultIdTestCase.java
@@ -8,7 +8,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ResultIdTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/handler/test/JSONSearchHandlerTestCase.java b/container-search/src/test/java/com/yahoo/search/handler/test/JSONSearchHandlerTestCase.java
new file mode 100644
index 00000000000..eea58d5444e
--- /dev/null
+++ b/container-search/src/test/java/com/yahoo/search/handler/test/JSONSearchHandlerTestCase.java
@@ -0,0 +1,466 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.handler.test;
+
+import com.yahoo.container.Container;
+import com.yahoo.container.core.config.testutil.HandlersConfigurerTestWrapper;
+import com.yahoo.container.jdisc.HttpRequest;
+
+import com.yahoo.container.jdisc.RequestHandlerTestDriver;
+import com.yahoo.container.protect.Error;
+import com.yahoo.io.IOUtils;
+import com.yahoo.net.HostName;
+import com.yahoo.search.handler.SearchHandler;
+import com.yahoo.search.searchchain.config.test.SearchChainConfigurerTestCase;
+import com.yahoo.slime.Inspector;
+import com.yahoo.vespa.config.SlimeUtils;
+import org.json.JSONObject;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+import java.util.HashMap;
+
+import static com.yahoo.jdisc.http.HttpRequest.Method.GET;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.*;
+
+
+public class JSONSearchHandlerTestCase {
+
+ private static final String testDir = "src/test/java/com/yahoo/search/handler/test/config";
+ private static final String myHostnameHeader = "my-hostname-header";
+ private static final String selfHostname = HostName.getLocalhost();
+
+ private static String tempDir = "";
+ private static String configId = null;
+ private static final String uri = "http://localhost?";
+ private static final String JSON_CONTENT_TYPE = "application/json";
+
+ @Rule
+ public TemporaryFolder tempfolder = new TemporaryFolder();
+
+ private RequestHandlerTestDriver driver = null;
+ private HandlersConfigurerTestWrapper configurer = null;
+ private SearchHandler searchHandler;
+
+ @Before
+ public void startUp() throws IOException {
+ File cfgDir = tempfolder.newFolder("SearchHandlerTestCase");
+ tempDir = cfgDir.getAbsolutePath();
+ configId = "dir:" + tempDir;
+
+ IOUtils.copyDirectory(new File(testDir), cfgDir, 1); // make configs active
+ generateComponentsConfigForActive();
+
+ configurer = new HandlersConfigurerTestWrapper(new Container(), configId);
+ searchHandler = (SearchHandler)configurer.getRequestHandlerRegistry().getComponent(SearchHandler.class.getName());
+ driver = new RequestHandlerTestDriver(searchHandler);
+ }
+
+ @After
+ public void shutDown() {
+ if (configurer != null) configurer.shutdown();
+ if (driver != null) driver.close();
+ }
+
+ private void generateComponentsConfigForActive() throws IOException {
+ File activeConfig = new File(tempDir);
+ SearchChainConfigurerTestCase.
+ createComponentsConfig(new File(activeConfig, "chains.cfg").getPath(),
+ new File(activeConfig, "handlers.cfg").getPath(),
+ new File(activeConfig, "components.cfg").getPath());
+ }
+
+ private SearchHandler fetchSearchHandler(HandlersConfigurerTestWrapper configurer) {
+ return (SearchHandler) configurer.getRequestHandlerRegistry().getComponent(SearchHandler.class.getName());
+ }
+
+ @Test
+ public void testBadJSON() throws Exception{
+ String json = "Not a valid JSON-string";
+ RequestHandlerTestDriver.MockResponseHandler responseHandler = driver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json, JSON_CONTENT_TYPE);
+ String response = responseHandler.readAll();
+ assertThat(responseHandler.getStatus(), is(400));
+ assertThat(response, containsString("errors"));
+ assertThat(response, containsString("\"code\":" + Error.ILLEGAL_QUERY.code));
+ }
+
+ @Test
+ public void testFailing() throws Exception {
+ JSONObject json = new JSONObject();
+ json.put("query", "test");
+ json.put("searchChain", "classLoadingError");
+ assertTrue(driver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), JSON_CONTENT_TYPE).readAll().contains("NoClassDefFoundError"));
+ }
+
+
+ @Test
+ public synchronized void testPluginError() throws Exception {
+ JSONObject json = new JSONObject();
+ json.put("query", "test");
+ json.put("searchChain", "exceptionInPlugin");
+ assertTrue(driver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), JSON_CONTENT_TYPE).readAll().contains("NullPointerException"));
+ }
+
+ @Test
+ public synchronized void testWorkingReconfiguration() throws Exception {
+ JSONObject json = new JSONObject();
+ json.put("query", "abc");
+ assertJsonResult(json, driver);
+
+ // reconfiguration
+ IOUtils.copyDirectory(new File(testDir, "handlers2"), new File(tempDir), 1);
+ generateComponentsConfigForActive();
+ configurer.reloadConfig();
+
+ // ...and check the resulting config
+ SearchHandler newSearchHandler = fetchSearchHandler(configurer);
+ assertNotSame("Have a new instance of the search handler", searchHandler, newSearchHandler);
+ assertNotNull("Have the new search chain", fetchSearchHandler(configurer).getSearchChainRegistry().getChain("hello"));
+ assertNull("Don't have the new search chain", fetchSearchHandler(configurer).getSearchChainRegistry().getChain("classLoadingError"));
+ try (RequestHandlerTestDriver newDriver = new RequestHandlerTestDriver(searchHandler)) {
+ assertJsonResult(json, newDriver);
+ }
+ }
+
+ @Test
+ public void testInvalidYqlQuery() throws Exception {
+ IOUtils.copyDirectory(new File(testDir, "config_yql"), new File(tempDir), 1);
+ generateComponentsConfigForActive();
+ configurer.reloadConfig();
+
+ SearchHandler newSearchHandler = fetchSearchHandler(configurer);
+ assertTrue("Do I have a new instance of the search handler?", searchHandler != newSearchHandler);
+ try (RequestHandlerTestDriver newDriver = new RequestHandlerTestDriver(newSearchHandler)) {
+ JSONObject json = new JSONObject();
+ json.put("yql", "select * from foo where bar > 1453501295");
+ RequestHandlerTestDriver.MockResponseHandler responseHandler = newDriver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), JSON_CONTENT_TYPE);
+ responseHandler.readAll();
+ assertThat(responseHandler.getStatus(), is(400));
+ }
+ }
+
+ // Query handling takes a different code path when a query profile is active, so we test both paths.
+ @Test
+ public void testInvalidQueryParamWithQueryProfile() throws Exception {
+ try (RequestHandlerTestDriver newDriver = driverWithConfig("config_invalid_param")) {
+ testInvalidQueryParam(newDriver);
+ }
+ }
+
+ private void testInvalidQueryParam(final RequestHandlerTestDriver testDriver) throws Exception{
+ JSONObject json = new JSONObject();
+ json.put("query", "status_code:0");
+ json.put("hits", 20);
+ json.put("offset", -20);
+ RequestHandlerTestDriver.MockResponseHandler responseHandler =
+ testDriver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), JSON_CONTENT_TYPE);
+ String response = responseHandler.readAll();
+ assertThat(responseHandler.getStatus(), is(400));
+ assertThat(response, containsString("offset"));
+ assertThat(response, containsString("\"code\":" + com.yahoo.container.protect.Error.INVALID_QUERY_PARAMETER.code));
+ }
+
+
+
+
+ @Test
+ public void testNormalResultJsonAliasRendering() throws Exception {
+ JSONObject json = new JSONObject();
+ json.put("format", "json");
+ json.put("query", "abc");
+ assertJsonResult(json, driver);
+ }
+
+
+
+ @Test
+ public void testNullQuery() throws Exception {
+ JSONObject json = new JSONObject();
+ json.put("format", "xml");
+
+ assertEquals("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" +
+ "<result total-hit-count=\"0\">\n" +
+ " <hit relevancy=\"1.0\">\n" +
+ " <field name=\"relevancy\">1.0</field>\n" +
+ " <field name=\"uri\">testHit</field>\n" +
+ " </hit>\n" +
+ "</result>\n", driver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), JSON_CONTENT_TYPE).readAll());
+ }
+
+
+
+ @Test
+ public void testWebServiceStatus() throws Exception {
+ JSONObject json = new JSONObject();
+ json.put("query", "web_service_status_code");
+ RequestHandlerTestDriver.MockResponseHandler responseHandler =
+ driver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), JSON_CONTENT_TYPE);
+ String response = responseHandler.readAll();
+ assertThat(responseHandler.getStatus(), is(406));
+ assertThat(response, containsString("\"code\":" + 406));
+ }
+
+ @Test
+ public void testNormalResultImplicitDefaultRendering() throws Exception {
+ JSONObject json = new JSONObject();
+ json.put("query", "abc");
+ assertJsonResult(json, driver);
+ }
+
+ @Test
+ public void testNormalResultExplicitDefaultRendering() throws Exception {
+ JSONObject json = new JSONObject();
+ json.put("query", "abc");
+ json.put("format", "default");
+ assertJsonResult(json, driver);
+ }
+
+ @Test
+ public void testNormalResultXmlAliasRendering() throws Exception {
+ JSONObject json = new JSONObject();
+ json.put("query", "abc");
+ json.put("format", "xml");
+ assertXmlResult(json, driver);
+ }
+
+
+ @Test
+ public void testNormalResultExplicitDefaultRenderingFullRendererName1() throws Exception {
+ JSONObject json = new JSONObject();
+ json.put("query", "abc");
+ json.put("format", "DefaultRenderer");
+ assertXmlResult(json, driver);
+ }
+
+ @Test
+ public void testNormalResultExplicitDefaultRenderingFullRendererName2() throws Exception {
+ JSONObject json = new JSONObject();
+ json.put("query", "abc");
+ json.put("format", "JsonRenderer");
+ assertJsonResult(json, driver);
+ }
+
+ @Test
+ public void testResultLegacyTiledFormat() throws Exception {
+ JSONObject json = new JSONObject();
+ json.put("query", "abc");
+ json.put("format", "tiled");
+ assertTiledResult(json, driver);
+ }
+
+ @Test
+ public void testResultLegacyPageFormat() throws Exception {
+ JSONObject json = new JSONObject();
+ json.put("query", "abc");
+ json.put("format", "page");
+ assertPageResult(json, driver);
+ }
+
+
+ private static final String xmlResult =
+ "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" +
+ "<result total-hit-count=\"0\">\n" +
+ " <hit relevancy=\"1.0\">\n" +
+ " <field name=\"relevancy\">1.0</field>\n" +
+ " <field name=\"uri\">testHit</field>\n" +
+ " </hit>\n" +
+ "</result>\n";
+
+ private void assertXmlResult(JSONObject json, RequestHandlerTestDriver driver) throws Exception {
+ assertOkResult(driver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), JSON_CONTENT_TYPE), xmlResult);
+ }
+
+
+ private static final String jsonResult = "{\"root\":{"
+ + "\"id\":\"toplevel\",\"relevance\":1.0,\"fields\":{\"totalCount\":0},"
+ + "\"children\":["
+ + "{\"id\":\"testHit\",\"relevance\":1.0,\"fields\":{\"uri\":\"testHit\"}}"
+ + "]}}";
+
+ private void assertJsonResult(JSONObject json, RequestHandlerTestDriver driver) throws Exception {
+ assertOkResult(driver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), JSON_CONTENT_TYPE), jsonResult);
+
+ }
+
+ private static final String tiledResult =
+ "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" +
+ "<result version=\"1.0\">\n" +
+ "\n" +
+ " <hit relevance=\"1.0\">\n" +
+ " <id>testHit</id>\n" +
+ " <uri>testHit</uri>\n" +
+ " </hit>\n" +
+ "\n" +
+ "</result>\n";
+
+ private void assertTiledResult(JSONObject json, RequestHandlerTestDriver driver) throws Exception {
+ assertOkResult(driver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), JSON_CONTENT_TYPE), tiledResult);
+ }
+
+ private static final String pageResult =
+ "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" +
+ "<page version=\"1.0\">\n" +
+ "\n" +
+ " <content>\n" +
+ " <hit relevance=\"1.0\">\n" +
+ " <id>testHit</id>\n" +
+ " <uri>testHit</uri>\n" +
+ " </hit>\n" +
+ " </content>\n" +
+ "\n" +
+ "</page>\n";
+
+ private void assertPageResult(JSONObject json, RequestHandlerTestDriver driver) throws Exception {
+ assertOkResult(driver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), JSON_CONTENT_TYPE), pageResult);
+ }
+
+ private void assertOkResult(RequestHandlerTestDriver.MockResponseHandler response, String expected) {
+ assertEquals(expected, response.readAll());
+ assertEquals(200, response.getStatus());
+ assertEquals(selfHostname, response.getResponse().headers().get(myHostnameHeader).get(0));
+ }
+
+
+ private RequestHandlerTestDriver driverWithConfig(String configDirectory) throws Exception {
+ IOUtils.copyDirectory(new File(testDir, configDirectory), new File(tempDir), 1);
+ generateComponentsConfigForActive();
+ configurer.reloadConfig();
+
+ SearchHandler newSearchHandler = fetchSearchHandler(configurer);
+ assertTrue("Do I have a new instance of the search handler?", searchHandler != newSearchHandler);
+ return new RequestHandlerTestDriver(newSearchHandler);
+ }
+
+
+
+ @Test
+ public void testRequestMapping() throws Exception {
+ JSONObject json = new JSONObject();
+ json.put("yql", "select * from sources * where sddocname contains \"blog_post\" limit 0 | all(group(date) max(3) order(-count())each(output(count())));");
+ json.put("hits", 10.0);
+ json.put("offset", 5);
+ json.put("queryProfile", "foo");
+ json.put("nocache", false);
+ json.put("groupingSessionCache", false);
+ json.put("searchChain", "exceptionInPlugin");
+ json.put("timeout", 0);
+ json.put("select", "_all");
+
+
+ JSONObject model = new JSONObject();
+ model.put("defaultIndex", 1);
+ model.put("encoding", "json");
+ model.put("filter", "default");
+ model.put("language", "en");
+ model.put("queryString", "abc");
+ model.put("restrict", "_doc,json,xml");
+ model.put("searchPath", "node1");
+ model.put("sources", "source1,source2");
+ model.put("type", "yql");
+ json.put("model", model);
+
+ JSONObject ranking = new JSONObject();
+ ranking.put("location", "123789.89123N;128123W");
+ ranking.put("features", "none");
+ ranking.put("listFeatures", false);
+ ranking.put("profile", "1");
+ ranking.put("properties", "default");
+ ranking.put("sorting", "desc");
+ ranking.put("freshness", "0.05");
+ ranking.put("queryCache", false);
+
+ JSONObject matchPhase = new JSONObject();
+ matchPhase.put("maxHits", "100");
+ matchPhase.put("attribute", "title");
+ matchPhase.put("ascending", true);
+
+ JSONObject diversity = new JSONObject();
+ diversity.put("attribute", "title");
+ diversity.put("minGroups", 1);
+ matchPhase.put("diversity", diversity);
+ ranking.put("matchPhase", matchPhase);
+ json.put("ranking", ranking);
+
+ JSONObject presentation = new JSONObject();
+ presentation.put("bolding", true);
+ presentation.put("format", "json");
+ presentation.put("summary", "none");
+ presentation.put("template", "json");
+ presentation.put("timing", false);
+ json.put("presentation", presentation);
+
+ JSONObject collapse = new JSONObject();
+ collapse.put("field", "none");
+ collapse.put("size", 2);
+ collapse.put("summary", "default");
+ json.put("collapse", collapse);
+
+ JSONObject trace = new JSONObject();
+ trace.put("level", 1);
+ trace.put("timestamps", false);
+ trace.put("rules", "none");
+ json.put("trace", trace);
+
+ JSONObject pos = new JSONObject();
+ pos.put("ll", "1263123N;1231.9W");
+ pos.put("radius", "71234m");
+ pos.put("bb", "1237123W;123218N");
+ pos.put("attribute", "default");
+ json.put("pos", pos);
+
+ JSONObject streaming = new JSONObject();
+ streaming.put("userid", 123);
+ streaming.put("groupname", "abc");
+ streaming.put("selection", "none");
+ streaming.put("priority", 10);
+ streaming.put("maxbucketspervisitor", 5);
+ json.put("streaming", streaming);
+
+ JSONObject rules = new JSONObject();
+ rules.put("off", false);
+ rules.put("rulebase", "default");
+ json.put("rules", rules);
+
+ JSONObject metrics = new JSONObject();
+ metrics.put("ignore", "_all");
+ json.put("metrics", metrics);
+
+ json.put("recall", "none");
+ json.put("user", 123);
+ json.put("nocachewrite", false);
+ json.put("hitcountestimate", true);
+
+
+
+ // Create mapping
+ Inspector inspector = SlimeUtils.jsonToSlime(json.toString().getBytes("utf-8")).get();
+ Map<String, String> map = new HashMap<>();
+ searchHandler.createRequestMapping(inspector, map, "");
+
+ // Create GET-request with same query
+ String url = uri + "&model.sources=source1%2Csource2&select=_all&model.language=en&presentation.timing=false&pos.attribute=default&pos.radius=71234m&model.searchPath=node1&nocachewrite=false&ranking.matchPhase.maxHits=100&presentation.summary=none" +
+ "&nocache=false&model.type=yql&collapse.summary=default&ranking.matchPhase.diversity.minGroups=1&ranking.location=123789.89123N%3B128123W&ranking.queryCache=false&offset=5&streaming.groupname=abc&groupingSessionCache=false" +
+ "&presentation.template=json&trace.rules=none&rules.off=false&ranking.properties=default&searchChain=exceptionInPlugin&pos.ll=1263123N%3B1231.9W&ranking.sorting=desc&ranking.matchPhase.ascending=true&ranking.features=none&hitcountestimate=true" +
+ "&model.filter=default&metrics.ignore=_all&collapse.field=none&ranking.profile=1&rules.rulebase=default&model.defaultIndex=1&trace.level=1&ranking.listFeatures=false&timeout=0&presentation.format=json" +
+ "&yql=select+%2A+from+sources+%2A+where+sddocname+contains+%22blog_post%22+limit+0+%7C+all%28group%28date%29+max%283%29+order%28-count%28%29%29each%28output%28count%28%29%29%29%29%3B&recall=none&streaming.maxbucketspervisitor=5" +
+ "&queryProfile=foo&presentation.bolding=true&model.encoding=json&model.queryString=abc&streaming.selection=none&trace.timestamps=false&collapse.size=2&streaming.priority=10&ranking.matchPhase.diversity.attribute=title" +
+ "&ranking.matchPhase.attribute=title&hits=10&streaming.userid=123&pos.bb=1237123W%3B123218N&model.restrict=_doc%2Cjson%2Cxml&ranking.freshness=0.05&user=123";
+
+
+
+ final HttpRequest request = HttpRequest.createTestRequest(url, GET);
+
+ // Get mapping
+ Map<String, String> propertyMap = request.propertyMap();
+ assertEquals("Should have same mapping for properties", map, propertyMap);
+ }
+
+
+
+}
diff --git a/container-search/src/test/java/com/yahoo/search/handler/test/SearchHandlerTestCase.java b/container-search/src/test/java/com/yahoo/search/handler/test/SearchHandlerTestCase.java
index ce40bd1f06b..6dcb34ec3e9 100644
--- a/container-search/src/test/java/com/yahoo/search/handler/test/SearchHandlerTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/handler/test/SearchHandlerTestCase.java
@@ -3,16 +3,13 @@ package com.yahoo.search.handler.test;
import com.yahoo.container.Container;
import com.yahoo.container.core.config.testutil.HandlersConfigurerTestWrapper;
-import com.yahoo.container.jdisc.AsyncHttpResponse;
import com.yahoo.container.jdisc.HttpRequest;
-
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.container.jdisc.RequestHandlerTestDriver;
import com.yahoo.container.jdisc.ThreadedHttpRequestHandler;
import com.yahoo.io.IOUtils;
import com.yahoo.jdisc.handler.RequestHandler;
import com.yahoo.net.HostName;
-import com.yahoo.processing.handler.ResponseStatus;
import com.yahoo.search.Query;
import com.yahoo.search.Result;
import com.yahoo.search.Searcher;
@@ -30,12 +27,9 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
-import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.URI;
-import java.util.List;
-import java.util.Map;
import java.util.concurrent.Executors;
import static org.hamcrest.CoreMatchers.containsString;
@@ -187,6 +181,8 @@ public class SearchHandlerTestCase {
}
}
+
+
// Query handling takes a different code path when a query profile is active, so we test both paths.
@Test
public void testInvalidQueryParamWithQueryProfile() throws Exception {
diff --git a/container-search/src/test/java/com/yahoo/search/pagetemplates/engine/test/MapSectionsToSectionsResult.xml b/container-search/src/test/java/com/yahoo/search/pagetemplates/engine/test/MapSectionsToSectionsResult.xml
index cb63af526a0..4272e54aeec 100644
--- a/container-search/src/test/java/com/yahoo/search/pagetemplates/engine/test/MapSectionsToSectionsResult.xml
+++ b/container-search/src/test/java/com/yahoo/search/pagetemplates/engine/test/MapSectionsToSectionsResult.xml
@@ -64,10 +64,10 @@
<hit relevance="0.2" source="source5">
<id>source5-5</id>
</hit>
- <hit relevance="0.1666666666666667" source="source5">
+ <hit relevance="0.16666666666666666" source="source5">
<id>source5-6</id>
</hit>
- <hit relevance="0.1428571428571428" source="source5">
+ <hit relevance="0.14285714285714285" source="source5">
<id>source5-7</id>
</hit>
</section>
@@ -87,7 +87,7 @@
<hit relevance="0.2" source="source4">
<id>source4-5</id>
</hit>
- <hit relevance="0.1666666666666667" source="source4">
+ <hit relevance="0.16666666666666666" source="source4">
<id>source4-6</id>
</hit>
</section>
diff --git a/container-search/src/test/java/com/yahoo/search/pagetemplates/engine/test/MapSourcesToSectionsResult.xml b/container-search/src/test/java/com/yahoo/search/pagetemplates/engine/test/MapSourcesToSectionsResult.xml
index 1b8b5a71e35..623918c8739 100644
--- a/container-search/src/test/java/com/yahoo/search/pagetemplates/engine/test/MapSourcesToSectionsResult.xml
+++ b/container-search/src/test/java/com/yahoo/search/pagetemplates/engine/test/MapSourcesToSectionsResult.xml
@@ -64,7 +64,7 @@
<hit relevance="0.2" source="source4">
<id>source4-5</id>
</hit>
- <hit relevance="0.1666666666666667" source="source4">
+ <hit relevance="0.16666666666666666" source="source4">
<id>source4-6</id>
</hit>
</section>
diff --git a/container-search/src/test/java/com/yahoo/search/pagetemplates/engine/test/TwoSectionsFourSourcesResult.xml b/container-search/src/test/java/com/yahoo/search/pagetemplates/engine/test/TwoSectionsFourSourcesResult.xml
index eb2e458ac6d..73b62afd6c1 100644
--- a/container-search/src/test/java/com/yahoo/search/pagetemplates/engine/test/TwoSectionsFourSourcesResult.xml
+++ b/container-search/src/test/java/com/yahoo/search/pagetemplates/engine/test/TwoSectionsFourSourcesResult.xml
@@ -57,7 +57,7 @@
<hit relevance="0.2" source="source4">
<id>source4-5</id>
</hit>
- <hit relevance="0.1666666666666667" source="source4">
+ <hit relevance="0.16666666666666666" source="source4">
<id>source4-6</id>
</hit>
</section>
diff --git a/container-search/src/test/java/com/yahoo/search/query/profile/test/QueryProfileVariantsCloneTestCase.java b/container-search/src/test/java/com/yahoo/search/query/profile/test/QueryProfileVariantsCloneTestCase.java
index 9e41f7a84e7..84326d9370d 100644
--- a/container-search/src/test/java/com/yahoo/search/query/profile/test/QueryProfileVariantsCloneTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/query/profile/test/QueryProfileVariantsCloneTestCase.java
@@ -14,7 +14,7 @@ import java.util.Map;
import static org.junit.Assert.assertEquals;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class QueryProfileVariantsCloneTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/query/textserialize/item/test/ParseItemTestCase.java b/container-search/src/test/java/com/yahoo/search/query/textserialize/item/test/ParseItemTestCase.java
index 7208626253c..382d9cf8cee 100644
--- a/container-search/src/test/java/com/yahoo/search/query/textserialize/item/test/ParseItemTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/query/textserialize/item/test/ParseItemTestCase.java
@@ -16,7 +16,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ParseItemTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/query/textserialize/serializer/test/SerializeItemTestCase.java b/container-search/src/test/java/com/yahoo/search/query/textserialize/serializer/test/SerializeItemTestCase.java
index 61fc59bccbe..5aefc90121d 100644
--- a/container-search/src/test/java/com/yahoo/search/query/textserialize/serializer/test/SerializeItemTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/query/textserialize/serializer/test/SerializeItemTestCase.java
@@ -20,7 +20,7 @@ import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.assertThat;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class SerializeItemTestCase {
@Test
diff --git a/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java b/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java
index bf56ad19f44..caad1c76362 100644
--- a/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java
@@ -9,6 +9,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.ListenableFuture;
import com.yahoo.component.chain.Chain;
+import com.yahoo.data.access.simple.Value;
import com.yahoo.data.access.slime.SlimeAdapter;
import com.yahoo.document.DataType;
import com.yahoo.document.DocumentId;
@@ -1111,6 +1112,53 @@ public class JsonRendererTestCase {
}
@Test
+ public final void testMapInField() throws IOException, InterruptedException, ExecutionException, JSONException {
+ String expected = "{\n"
+ + " \"root\": {\n"
+ + " \"children\": [\n"
+ + " {\n"
+ + " \"fields\": {\n"
+ + " \"structured\": {\n"
+ + " \"foo\": \"string foo\",\n"
+ + " \"bar\": [\"array bar elem 1\", \"array bar elem 2\"],\n"
+ + " \"baz\": {\"f1\": \"object baz field 1\", \"f2\": \"object baz field 2\"}\n"
+ + " }\n"
+ + " },\n"
+ + " \"id\": \"MapInField\",\n"
+ + " \"relevance\": 1.0\n"
+ + " }\n"
+ + " ],\n"
+ + " \"fields\": {\n"
+ + " \"totalCount\": 1\n"
+ + " },\n"
+ + " \"id\": \"toplevel\",\n"
+ + " \"relevance\": 1.0\n"
+ + " }\n"
+ + "}\n";
+ Result r = newEmptyResult();
+ Hit h = new Hit("MapInField");
+ Value.ArrayValue atop = new Value.ArrayValue();
+ atop.add(new Value.ObjectValue()
+ .put("key", new Value.StringValue("foo"))
+ .put("value", new Value.StringValue("string foo")))
+ .add(new Value.ObjectValue()
+ .put("key", new Value.StringValue("bar"))
+ .put("value", new Value.ArrayValue()
+ .add(new Value.StringValue("array bar elem 1"))
+ .add(new Value.StringValue("array bar elem 2"))))
+ .add(new Value.ObjectValue()
+ .put("key", new Value.StringValue("baz"))
+ .put("value", new Value.ObjectValue()
+ .put("f1", new Value.StringValue("object baz field 1"))
+ .put("f2", new Value.StringValue("object baz field 2"))));
+ h.setField("structured", atop);
+ r.hits().add(h);
+ r.setTotalHitCount(1L);
+ String summary = render(r);
+ assertEqualJson(expected, summary);
+ }
+
+ @Test
public void testThatTheJsonValidatorCanCatchErrors() {
String json = "{"
+ " \"root\": {"
diff --git a/container-search/src/test/java/com/yahoo/search/searchchain/config/test/DependencyConfigTestCase.java b/container-search/src/test/java/com/yahoo/search/searchchain/config/test/DependencyConfigTestCase.java
index 847d7c2daf9..71863c09883 100644
--- a/container-search/src/test/java/com/yahoo/search/searchchain/config/test/DependencyConfigTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/searchchain/config/test/DependencyConfigTestCase.java
@@ -24,7 +24,7 @@ import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class DependencyConfigTestCase {
diff --git a/container-search/src/test/java/com/yahoo/search/searchchain/test/SimpleSearchChain.java b/container-search/src/test/java/com/yahoo/search/searchchain/test/SimpleSearchChain.java
index 2913cc36f41..3e29fd6e1f4 100644
--- a/container-search/src/test/java/com/yahoo/search/searchchain/test/SimpleSearchChain.java
+++ b/container-search/src/test/java/com/yahoo/search/searchchain/test/SimpleSearchChain.java
@@ -20,7 +20,7 @@ import com.yahoo.search.searchchain.SearchChainRegistry;
/**
* A search chain consisting of two searchers.
* @author bratseth
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class SimpleSearchChain {
diff --git a/container-test-jars/bundle-with-provided-bundle/pom.xml b/container-test-jars/bundle-with-provided-bundle/pom.xml
index d0b378de8ba..e133ee955dd 100644
--- a/container-test-jars/bundle-with-provided-bundle/pom.xml
+++ b/container-test-jars/bundle-with-provided-bundle/pom.xml
@@ -29,11 +29,6 @@
</dependency>
<dependency>
- <groupId>org.scala-lang</groupId>
- <artifactId>scala-library</artifactId>
- </dependency>
-
- <dependency>
<groupId>com.yahoo.vespa</groupId>
<artifactId>vespa_jersey2</artifactId>
<version>${project.version}</version>
@@ -44,27 +39,6 @@
<build>
<plugins>
<plugin>
- <groupId>net.alchim31.maven</groupId>
- <artifactId>scala-maven-plugin</artifactId>
- <executions>
- <execution>
- <goals>
- <goal>add-source</goal>
- <goal>compile</goal>
- <goal>testCompile</goal>
- </goals>
- </execution>
- </executions>
- <configuration>
- <args>
- <arg>-unchecked</arg>
- <arg>-deprecation</arg>
- <arg>-feature</arg>
- </args>
- </configuration>
- </plugin>
-
- <plugin>
<groupId>com.yahoo.vespa</groupId>
<artifactId>bundle-plugin</artifactId>
<version>${project.version}</version>
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/EnvironmentResource.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/EnvironmentResource.java
index 812c4a60288..bea496c19d7 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/EnvironmentResource.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/EnvironmentResource.java
@@ -36,8 +36,10 @@ public interface EnvironmentResource {
String API_PATH = "environment";
+ String APPLICATION_TEST_ZIP = "applicationTestZip";
String APPLICATION_ZIP = "applicationZip";
String DEPLOY_OPTIONS = "deployOptions";
+ String SUBMIT_OPTIONS = "submitOptions";
@POST
@Path("{environmentId}/region/{regionId}/instance/{instanceId}/deploy")
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/SubmitOptions.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/SubmitOptions.java
new file mode 100644
index 00000000000..fb97a3c1004
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/SubmitOptions.java
@@ -0,0 +1,55 @@
+// 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.application.v4.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.yahoo.vespa.hosted.controller.api.identifiers.GitBranch;
+import com.yahoo.vespa.hosted.controller.api.identifiers.GitCommit;
+import com.yahoo.vespa.hosted.controller.api.identifiers.GitRepository;
+
+import java.util.Objects;
+
+/**
+ * Additional options to be sent along the application package and the application test package
+ * when submitting an application to the controller
+ *
+ * @author freva
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class SubmitOptions {
+
+ public GitRepository repository;
+ public GitBranch branch;
+ public GitCommit commit;
+
+ public static SubmitOptions from(String repository, String branch, String commit) {
+ SubmitOptions options = new SubmitOptions();
+ options.repository = new GitRepository(repository);
+ options.branch = new GitBranch(branch);
+ options.commit = new GitCommit(commit);
+ return options;
+ }
+
+ @Override
+ public String toString() {
+ return "SubmitOptions{" +
+ "repository=" + repository +
+ ", branch=" + branch +
+ ", commit=" + commit +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SubmitOptions that = (SubmitOptions) o;
+ return Objects.equals(repository, that.repository) &&
+ Objects.equals(branch, that.branch) &&
+ Objects.equals(commit, that.commit);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(repository, branch, commit);
+ }
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/SubmitResult.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/SubmitResult.java
new file mode 100644
index 00000000000..a7f136cf3ce
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/SubmitResult.java
@@ -0,0 +1,37 @@
+// 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.application.v4.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+import java.util.Objects;
+
+/**
+ * Represents the response from application submit request
+ *
+ * @author freva
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class SubmitResult {
+
+ public String version;
+
+ @Override
+ public String toString() {
+ return "SubmitResult{" +
+ "version='" + version + '\'' +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SubmitResult that = (SubmitResult) o;
+ return Objects.equals(version, that.version);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(version);
+ }
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/bcp/BrooklynStatusResource.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/bcp/BrooklynStatusResource.java
deleted file mode 100644
index c77f9fceef9..00000000000
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/bcp/BrooklynStatusResource.java
+++ /dev/null
@@ -1,23 +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.hosted.controller.api.bcp;
-
-import com.fasterxml.jackson.databind.JsonNode;
-
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-
-/**
- * @author andreer
- */
-@Path("") //Ensures that the produces annotation is inherited
-@Produces(MediaType.APPLICATION_JSON)
-public interface BrooklynStatusResource {
-
- @GET
- @Path("{rotation}")
- @Produces(MediaType.APPLICATION_JSON)
- JsonNode rotationStatus(@PathParam("rotation") String page);
-}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/BuildService.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/BuildService.java
index bb2de87796b..e91a5909f80 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/BuildService.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/BuildService.java
@@ -4,7 +4,7 @@ package com.yahoo.vespa.hosted.controller.api.integration;
import com.yahoo.config.provision.ApplicationId;
/**
- * @author jvenstad
+ * @author jonmv
*/
public interface BuildService {
@@ -20,6 +20,11 @@ public interface BuildService {
*/
JobState stateOf(BuildJob buildJob);
+ /**
+ * Returns whether the given build job should be performed by this build service.
+ */
+ default boolean builds(BuildJob buildJob) { return true; }
+
enum JobState {
/** Job is not running, and may be triggered. */
@@ -37,6 +42,8 @@ public interface BuildService {
}
+ // TODO jvenstad: Argh, refactor this, considering the new JobId, etc..
+ // TODO jvenstad: Probably: make jobName JobType instead.
class BuildJob {
private final ApplicationId applicationId;
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/LogStore.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/LogStore.java
new file mode 100644
index 00000000000..23da48b4aad
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/LogStore.java
@@ -0,0 +1,22 @@
+package com.yahoo.vespa.hosted.controller.api.integration;
+
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.PrepareResponse;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
+
+import java.util.Optional;
+
+/**
+ * @author freva
+ */
+public interface LogStore {
+
+ /** @return the log of the given step of the given deployment job, or an empty byte array if non-existent. */
+ byte[] get(RunId id, String step);
+
+ /** Stores the given log for the given step of the given deployment job. */
+ void append(RunId id, String step, byte[] log);
+
+ /** Deletes all data associated with the given deployment job */
+ void delete(RunId id);
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzClientFactory.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzClientFactory.java
index 72e7c758070..e95e97527da 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzClientFactory.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzClientFactory.java
@@ -1,13 +1,17 @@
// 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.controller.api.integration.athenz;
+import com.yahoo.vespa.athenz.api.AthenzIdentity;
import com.yahoo.vespa.athenz.api.NToken;
+import com.yahoo.vespa.athenz.client.zts.ZtsClient;
/**
* @author bjorncs
*/
public interface AthenzClientFactory {
+ AthenzIdentity getControllerIdentity();
+
ZmsClient createZmsClientWithServicePrincipal();
ZtsClient createZtsClientWithServicePrincipal();
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/InvalidTokenException.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/InvalidTokenException.java
deleted file mode 100644
index 967af1c553f..00000000000
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/InvalidTokenException.java
+++ /dev/null
@@ -1,11 +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.hosted.controller.api.integration.athenz;
-
-/**
- * @author bjorncs
- */
-public class InvalidTokenException extends RuntimeException {
- public InvalidTokenException(String message) {
- super(message);
- }
-}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsClient.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsClient.java
index e8bc16ca271..3630748b10a 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsClient.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsClient.java
@@ -3,8 +3,6 @@ package com.yahoo.vespa.hosted.controller.api.integration.athenz;
import com.yahoo.vespa.athenz.api.AthenzDomain;
import com.yahoo.vespa.athenz.api.AthenzIdentity;
-import com.yahoo.vespa.athenz.api.AthenzPublicKey;
-import com.yahoo.vespa.athenz.api.AthenzService;
import com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId;
import java.util.List;
@@ -33,8 +31,4 @@ public interface ZmsClient {
List<AthenzDomain> getDomainList(String prefix);
- AthenzPublicKey getPublicKey(AthenzService service, String keyId);
-
- List<AthenzPublicKey> getPublicKeys(AthenzService service);
-
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsKeystore.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsKeystore.java
deleted file mode 100644
index b3dc9fd4fe1..00000000000
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsKeystore.java
+++ /dev/null
@@ -1,18 +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.hosted.controller.api.integration.athenz;
-
-import com.yahoo.vespa.athenz.api.AthenzService;
-
-import java.security.PublicKey;
-import java.util.Optional;
-
-/**
- * @author bjorncs
- */
-public interface ZmsKeystore {
-
- Optional<PublicKey> getPublicKey(AthenzService service, String keyId);
-
- default void preloadKeys(AthenzService service) { /* Default implementation is noop */ }
-
-}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZtsClient.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZtsClient.java
deleted file mode 100644
index eefd11bdeec..00000000000
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZtsClient.java
+++ /dev/null
@@ -1,16 +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.hosted.controller.api.integration.athenz;
-
-import com.yahoo.vespa.athenz.api.AthenzDomain;
-import com.yahoo.vespa.athenz.api.AthenzIdentity;
-
-import java.util.List;
-
-/**
- * @author bjorncs
- */
-public interface ZtsClient {
-
- List<AthenzDomain> getTenantDomainsForUser(AthenzIdentity principal);
-
-}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZtsException.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZtsException.java
deleted file mode 100644
index 2be998e1544..00000000000
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZtsException.java
+++ /dev/null
@@ -1,19 +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.hosted.controller.api.integration.athenz;
-
-/**
- * @author bjorncs
- */
-public class ZtsException extends RuntimeException {
-
- private final int code;
-
- public ZtsException(int code, Throwable cause) {
- super(cause.getMessage(), cause);
- this.code = code;
- }
-
- public int getCode() {
- return code;
- }
-}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/chef/ChefMock.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/chef/ChefMock.java
index 1b2dad34b8d..bd19cfe6ce1 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/chef/ChefMock.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/chef/ChefMock.java
@@ -16,7 +16,6 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.stream.Collectors;
/**
* @author mpolden
@@ -24,11 +23,14 @@ import java.util.stream.Collectors;
public class ChefMock implements Chef {
private final NodeResult result;
+ private final PartialNodeResult partialResult;
private final List<String> chefEnvironments;
public ChefMock() {
result = new NodeResult();
result.rows = new ArrayList<>();
+ partialResult = new PartialNodeResult();
+ partialResult.rows = new ArrayList<>();
chefEnvironments = new ArrayList<>();
chefEnvironments.add("hosted-verified-prod");
chefEnvironments.add("hosted-infra-cd");
@@ -59,8 +61,14 @@ public class ChefMock implements Chef {
return null;
}
- public void addSearchResult(ChefNode node) {
+ public ChefMock addSearchResult(ChefNode node) {
result.rows.add(node);
+ return this;
+ }
+
+ public ChefMock addPartialResult(List<PartialNode> partialNodes) {
+ partialResult.rows.addAll(partialNodes);
+ return this;
}
@Override
@@ -76,13 +84,15 @@ public class ChefMock implements Chef {
@Override
public PartialNodeResult partialSearchNodes(String query, List<AttributeMapping> returnAttributes) {
PartialNodeResult partialNodeResult = new PartialNodeResult();
- partialNodeResult.rows = result.rows.stream()
- .map(chefNode -> {
- Map<String, String> data = new HashMap<>();
- data.put("fqdn", chefNode.name);
- return new PartialNode(data);
- })
- .collect(Collectors.toList());
+ partialNodeResult.rows = new ArrayList<>();
+ partialNodeResult.rows.addAll(partialResult.rows);
+ result.rows.stream()
+ .map(chefNode -> {
+ Map<String, String> data = new HashMap<>();
+ data.put("fqdn", chefNode.name);
+ return new PartialNode(data);
+ })
+ .forEach(node -> partialNodeResult.rows.add(node));
return partialNodeResult;
}
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 b15acd726d7..54e057e4187 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
@@ -64,4 +64,7 @@ public interface ConfigServer {
/** The node repository on this config server */
NodeRepository nodeRepository();
+ /** Get service convergence status for given deployment */
+ Optional<ServiceConvergence> serviceConvergence(DeploymentId deployment);
+
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Node.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Node.java
index d75f0e90d63..90864730a15 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Node.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Node.java
@@ -5,6 +5,7 @@ import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.NodeType;
+import org.jetbrains.annotations.TestOnly;
import java.util.Objects;
import java.util.Optional;
@@ -13,24 +14,43 @@ import java.util.Optional;
* A node in hosted Vespa.
*
* @author mpolden
+ * @author jonmv
*/
public class Node {
private final HostName hostname;
private final State state;
- private final Optional<ApplicationId> owner;
private final NodeType type;
+ private final Optional<ApplicationId> owner;
private final Version currentVersion;
private final Version wantedVersion;
+ private final ServiceState serviceState;
+ private final long restartGeneration;
+ private final long wantedRestartGeneration;
+ private final long rebootGeneration;
+ private final long wantedRebootGeneration;
- public Node(HostName hostname, State state, NodeType type, Optional<ApplicationId> owner, Version currentVersion,
- Version wantedVersion) {
+ public Node(HostName hostname, State state, NodeType type, Optional<ApplicationId> owner,
+ Version currentVersion, Version wantedVersion, ServiceState serviceState,
+ long restartGeneration, long wantedRestartGeneration, long rebootGeneration, long wantedRebootGeneration) {
this.hostname = hostname;
this.state = state;
this.type = type;
this.owner = owner;
this.currentVersion = currentVersion;
this.wantedVersion = wantedVersion;
+ this.serviceState = serviceState;
+ this.restartGeneration = restartGeneration;
+ this.wantedRestartGeneration = wantedRestartGeneration;
+ this.rebootGeneration = rebootGeneration;
+ this.wantedRebootGeneration = wantedRebootGeneration;
+ }
+
+ @TestOnly
+ public Node(HostName hostname, State state, NodeType type, Optional<ApplicationId> owner,
+ Version currentVersion, Version wantedVersion) {
+ this(hostname, state, type, owner, currentVersion, wantedVersion,
+ ServiceState.unorchestrated, 0, 0, 0, 0);
}
public HostName hostname() {
@@ -55,6 +75,26 @@ public class Node {
return wantedVersion;
}
+ public ServiceState serviceState() {
+ return serviceState;
+ }
+
+ public long restartGeneration() {
+ return restartGeneration;
+ }
+
+ public long wantedRestartGeneration() {
+ return wantedRestartGeneration;
+ }
+
+ public long rebootGeneration() {
+ return rebootGeneration;
+ }
+
+ public long wantedRebootGeneration() {
+ return wantedRebootGeneration;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -80,4 +120,11 @@ public class Node {
parked
}
+ /** Known node states with regards to service orchestration */
+ public enum ServiceState {
+ expectedUp,
+ allowedDown,
+ unorchestrated
+ }
+
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepository.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepository.java
index 741fee0210c..8147646bf06 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepository.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepository.java
@@ -19,19 +19,10 @@ public interface NodeRepository {
/** List all nodes in zone owned by given application */
List<Node> list(ZoneId zone, ApplicationId application);
- /** List all operational nodes in zone owned by given application */
- default List<Node> listOperational(ZoneId zone, ApplicationId application) {
+ /** List all nodes in states, in zone owned by given application */
+ default List<Node> list(ZoneId zone, ApplicationId application, List<Node.State> states) {
return list(zone, application).stream()
- .filter(node -> {
- switch (node.state()) {
- case ready:
- case active:
- case inactive:
- case reserved:
- return true;
- }
- return false;
- })
+ .filter(node -> states.contains(node.state()))
.collect(Collectors.toList());
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ServiceConvergence.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ServiceConvergence.java
new file mode 100644
index 00000000000..8a90224083b
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ServiceConvergence.java
@@ -0,0 +1,35 @@
+// 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.configserver;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
+
+/**
+ * Service convergence status for an application.
+ *
+ * @author mpolden
+ */
+public class ServiceConvergence {
+
+ private final ApplicationId application;
+ private final ZoneId zone;
+ private final boolean converged;
+
+ public ServiceConvergence(ApplicationId application, ZoneId zone, boolean converged) {
+ this.application = application;
+ this.zone = zone;
+ this.converged = converged;
+ }
+
+ public ApplicationId application() {
+ return application;
+ }
+
+ public ZoneId zone() {
+ return zone;
+ }
+
+ public boolean converged() {
+ return converged;
+ }
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/ArtifactRepository.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/ArtifactRepository.java
index b77e085e733..117dbd38a3b 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/ArtifactRepository.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/ArtifactRepository.java
@@ -12,10 +12,19 @@ import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
*/
public interface ArtifactRepository {
- /** Get tenant application package of given version */
+ /** Returns the tenant application package of the given version. */
byte[] getApplicationPackage(ApplicationId application, String applicationVersion);
- /** Get system application package of given version */
+ /** Stores the given tenant application package of the given version. */
+ void putApplicationPackage(ApplicationId application, String applicationVersion, byte[] applicationPackage);
+
+ /** Returns the system application package of the given version. */
byte[] getSystemApplicationPackage(ApplicationId application, ZoneId zone, Version version);
+ /** Stores the given tester application package of the given version. Does NOT contain the services.xml. */
+ void putTesterPackage(ApplicationId tester, String applicationVersion, byte[] testerPackage);
+
+ /** Returns the tester application package of the given version. Does NOT contain the services.xml. */
+ byte[] getTesterPackage(ApplicationId tester, String applicationVersion);
+
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/JobType.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/JobType.java
new file mode 100644
index 00000000000..3d8fe06e9b0
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/JobType.java
@@ -0,0 +1,98 @@
+package com.yahoo.vespa.hosted.controller.api.integration.deployment;
+
+import com.google.common.collect.ImmutableMap;
+import com.yahoo.config.provision.Environment;
+import com.yahoo.config.provision.RegionName;
+import com.yahoo.config.provision.SystemName;
+import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
+
+import java.util.Optional;
+import java.util.stream.Stream;
+
+/** Job types that exist in the build system */
+public enum JobType {
+// | enum name ------------| job name ------------------| Zone in main system ---------------------------------------| Zone in CD system -------------------------------------------
+ component ("component" , null , null ),
+ systemTest ("system-test" , ZoneId.from("test" , "us-east-1") , ZoneId.from("test" , "cd-us-central-1") ),
+ stagingTest ("staging-test" , ZoneId.from("staging", "us-east-3") , ZoneId.from("staging", "cd-us-central-1") ),
+ productionCorpUsEast1 ("production-corp-us-east-1" , ZoneId.from("prod" , "corp-us-east-1") , null ),
+ productionUsEast3 ("production-us-east-3" , ZoneId.from("prod" , "us-east-3") , null ),
+ productionUsWest1 ("production-us-west-1" , ZoneId.from("prod" , "us-west-1") , null ),
+ productionUsCentral1 ("production-us-central-1" , ZoneId.from("prod" , "us-central-1") , null ),
+ productionApNortheast1 ("production-ap-northeast-1" , ZoneId.from("prod" , "ap-northeast-1") , null ),
+ productionApNortheast2 ("production-ap-northeast-2" , ZoneId.from("prod" , "ap-northeast-2") , null ),
+ productionApSoutheast1 ("production-ap-southeast-1" , ZoneId.from("prod" , "ap-southeast-1") , null ),
+ productionEuWest1 ("production-eu-west-1" , ZoneId.from("prod" , "eu-west-1") , null ),
+ productionAwsUsEast1a ("production-aws-us-east-1a" , ZoneId.from("prod" , "aws-us-east-1a") , null ),
+ productionAwsUsWest1b ("production-aws-us-west-1b" , ZoneId.from("prod" , "aws-us-west-1b") , null ),
+ productionCdAwsUsEast1a("production-cd-aws-us-east-1a", null , ZoneId.from("prod" , "cd-aws-us-east-1a")),
+ productionCdUsCentral1 ("production-cd-us-central-1" , null , ZoneId.from("prod" , "cd-us-central-1") ),
+ productionCdUsCentral2 ("production-cd-us-central-2" , null , ZoneId.from("prod" , "cd-us-central-2") );
+
+ private final String jobName;
+ private final ImmutableMap<SystemName, ZoneId> zones;
+
+ JobType(String jobName, ZoneId mainZone, ZoneId cdZone) {
+ this.jobName = jobName;
+ ImmutableMap.Builder<SystemName, ZoneId> builder = ImmutableMap.builder();
+ if (mainZone != null) builder.put(SystemName.main, mainZone);
+ if (cdZone != null) builder.put(SystemName.cd, cdZone);
+ this.zones = builder.build();
+ }
+
+ public String jobName() { return jobName; }
+
+ /** Returns the zone for this job in the given system, or empty if this job does not have a zone */
+ public Optional<ZoneId> zone(SystemName system) {
+ return Optional.ofNullable(zones.get(system));
+ }
+
+ /** Returns whether this is a production job */
+ public boolean isProduction() { return environment() == Environment.prod; }
+
+ /** Returns whether this is an automated test job */
+ public boolean isTest() { return environment() != null && environment().isTest(); }
+
+ /** Returns the environment of this job type, or null if it does not have an environment */
+ public Environment environment() {
+ switch (this) {
+ case component: return null;
+ case systemTest: return Environment.test;
+ case stagingTest: return Environment.staging;
+ default: return Environment.prod;
+ }
+ }
+
+ /** Returns the region of this job type, or null if it does not have a region */
+ public Optional<RegionName> region(SystemName system) {
+ return zone(system).map(ZoneId::region);
+ }
+
+ public static Optional<JobType> fromOptionalJobName(String jobName) {
+ return Stream.of(values())
+ .filter(jobType -> jobType.jobName.equals(jobName))
+ .findAny();
+ }
+
+ public static JobType fromJobName(String jobName) {
+ return fromOptionalJobName(jobName)
+ .orElseThrow(() -> new IllegalArgumentException("Unknown job name '" + jobName + "'"));
+ }
+
+ /** Returns the job type for the given zone */
+ public static Optional<JobType> from(SystemName system, ZoneId zone) {
+ return Stream.of(values())
+ .filter(job -> job.zone(system).filter(zone::equals).isPresent())
+ .findAny();
+ }
+
+ /** Returns the job job type for the given environment and region or null if none */
+ public static Optional<JobType> from(SystemName system, Environment environment, RegionName region) {
+ switch (environment) {
+ case test: return Optional.of(systemTest);
+ case staging: return Optional.of(stagingTest);
+ }
+ return from(system, ZoneId.from(environment, region));
+ }
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/RunId.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/RunId.java
new file mode 100644
index 00000000000..a46cec1bb40
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/RunId.java
@@ -0,0 +1,54 @@
+package com.yahoo.vespa.hosted.controller.api.integration.deployment;
+
+import com.yahoo.config.provision.ApplicationId;
+
+import java.util.Objects;
+
+/**
+ * Immutable ID of a job run by a {@link com.yahoo.vespa.hosted.controller.api.integration.BuildService}.
+ *
+ * @author jonmv
+ */
+public class RunId {
+
+ private final ApplicationId application;
+ private final JobType type;
+ private final long number;
+
+ public RunId(ApplicationId application, JobType type, long number) {
+ this.application = Objects.requireNonNull(application, "ApplicationId cannot be null!");
+ this.type = Objects.requireNonNull(type, "JobType cannot be null!");
+ if (number <= 0) throw new IllegalArgumentException("Build number must be a positive integer!");
+ this.number = number;
+ }
+
+ public ApplicationId application() { return application; }
+ public JobType type() { return type; }
+ public long number() { return number; }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if ( ! (o instanceof RunId)) return false;
+
+ RunId id = (RunId) o;
+
+ if (number != id.number) return false;
+ if ( ! application.equals(id.application)) return false;
+ return type == id.type;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = application.hashCode();
+ result = 31 * result + type.hashCode();
+ result = 31 * result + (int) (number ^ (number >>> 32));
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "Run " + number + " of " + type + " for " + application;
+ }
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/Testers.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/Testers.java
new file mode 100644
index 00000000000..dccc0e47ceb
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/Testers.java
@@ -0,0 +1,59 @@
+package com.yahoo.vespa.hosted.controller.api.integration.deployment;
+
+import java.net.URI;
+
+/**
+ * Allows running some predefined tests -- typically remotely.
+ *
+ * @author jonmv
+ */
+public interface Testers {
+
+ /** Signals the tester to run its tests. */
+ void startTests(URI testerUrl, Suite suite, byte[] config);
+
+ /** Returns the currently stored logs from the tester. */
+ byte[] getLogs(URI testerUrl);
+
+ /** Returns the current status of the tester. */
+ Status getStatus(URI testerUrl);
+
+
+ enum Status {
+
+ /** Tests have not yet started. */
+ NOT_STARTED,
+
+ /** Tests are running. */
+ RUNNING,
+
+ /** Tests failed. */
+ FAILURE,
+
+ /** The tester encountered an exception. */
+ ERROR,
+
+ /** The tests were successful. */
+ SUCCESS
+
+ }
+
+
+ enum Suite {
+
+ system,
+
+ staging,
+
+ production;
+
+ public static Suite of(JobType type) {
+ if (type == JobType.systemTest) return system;
+ if (type == JobType.stagingTest) return staging;
+ if (type.isProduction()) return production;
+ throw new AssertionError("Unknown JobType '" + type + "'!");
+ }
+
+ }
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/github/GitHubMock.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/github/GitHubMock.java
index 9a398ef7cb5..9825887dcf5 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/github/GitHubMock.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/github/GitHubMock.java
@@ -8,7 +8,7 @@ import java.util.Map;
import java.util.UUID;
/**
- * @author jvenstad
+ * @author jonmv
*/
public class GitHubMock implements GitHub {
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/DeploymentIssues.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/DeploymentIssues.java
index fdebcca6d83..6888e8ac06d 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/DeploymentIssues.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/DeploymentIssues.java
@@ -12,7 +12,7 @@ import java.util.Optional;
/**
* Represents the people responsible for keeping Vespa up and running in a given organization, etc..
*
- * @author jvenstad
+ * @author jonmv
*/
public interface DeploymentIssues {
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 086487b8be7..a9bc7868f7a 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
@@ -14,7 +14,7 @@ import java.util.Optional;
* Represents an issue which needs to reported, typically from the controller, to a responsible party,
* the identity of which is determined by the propertyId and, possibly, assignee fields.
*
- * @author jvenstad
+ * @author jonmv
*/
public class Issue {
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/IssueId.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/IssueId.java
index 84b441ff4a8..ccb65582d3c 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/IssueId.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/IssueId.java
@@ -7,7 +7,7 @@ import java.util.Objects;
* Used to identify issues stored in some issue tracking system.
* The {@code value()} and {@code from()} methods should be inverses.
*
- * @author jvenstad
+ * @author jonmv
*/
public class IssueId {
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/Organization.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/Organization.java
index 776002f31cb..6dccaec3b7a 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/Organization.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/Organization.java
@@ -12,7 +12,7 @@ import java.util.Optional;
* Represents the humans who use this software, and their organization.
* Lets the software report issues to its caretakers, and provides other useful human resource lookups.
*
- * @author jvenstad
+ * @author jonmv
*/
public interface Organization {
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/OwnershipIssues.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/OwnershipIssues.java
index 91b5eb89c38..ee17859c0fb 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/OwnershipIssues.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/OwnershipIssues.java
@@ -14,7 +14,7 @@ import java.util.Optional;
* with reassurance that any misbehaving applications will swiftly be dealt with.
* Ignored confirmation requests are periodically redirected to humans of higher rank, until they are acknowledged.
*
- * @author jvenstad
+ * @author jonmv
*/
public interface OwnershipIssues {
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/User.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/User.java
index 82a86de3824..a88a9432c89 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/User.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/User.java
@@ -6,7 +6,7 @@ import java.util.Objects;
/**
* Represents a human computer user, typically by UNIX account name.
*
- * @author jvenstad
+ * @author jonmv
*/
public class User {
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/LoggingDeploymentIssues.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/LoggingDeploymentIssues.java
index b169194fd40..c5efffd979a 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/LoggingDeploymentIssues.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/LoggingDeploymentIssues.java
@@ -26,7 +26,7 @@ import java.util.logging.Logger;
* A memory backed implementation of the Issues API which logs changes and does nothing else.
*
* @author bratseth
- * @author jvenstad
+ * @author jonmv
*/
public class LoggingDeploymentIssues implements DeploymentIssues {
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockBuildService.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockBuildService.java
index a8320f56fff..2a8b06888b0 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockBuildService.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockBuildService.java
@@ -13,7 +13,7 @@ import static com.yahoo.vespa.hosted.controller.api.integration.BuildService.Job
import static com.yahoo.vespa.hosted.controller.api.integration.BuildService.JobState.running;
/**
- * @author jvenstad
+ * @author jonmv
*/
public class MockBuildService extends AbstractComponent implements BuildService {
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockLogStore.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockLogStore.java
new file mode 100644
index 00000000000..330b967c1b5
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockLogStore.java
@@ -0,0 +1,38 @@
+// 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.stubs;
+
+import com.yahoo.vespa.hosted.controller.api.integration.LogStore;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author jonmv
+ */
+public class MockLogStore implements LogStore {
+
+ private final Map<RunId, Map<String, byte[]>> logs = new ConcurrentHashMap<>();
+
+ @Override
+ public byte[] get(RunId id, String step) {
+ return logs.getOrDefault(id, Collections.emptyMap()).getOrDefault(step, new byte[0]);
+ }
+
+ @Override
+ public void append(RunId id, String step, byte[] log) {
+ logs.putIfAbsent(id, new ConcurrentHashMap<>());
+ byte[] old = get(id, step);
+ byte[] union = new byte[old.length + log.length];
+ System.arraycopy(old, 0, union, 0, old.length);
+ System.arraycopy(log, 0, union, old.length, log.length);
+ logs.get(id).put(step, union);
+ }
+
+ @Override
+ public void delete(RunId id) {
+ logs.remove(id);
+ }
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockTesters.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockTesters.java
new file mode 100644
index 00000000000..021e4d7f293
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockTesters.java
@@ -0,0 +1,38 @@
+package com.yahoo.vespa.hosted.controller.api.integration.stubs;
+
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.Testers;
+
+import java.net.URI;
+import java.util.Arrays;
+
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.Testers.Status.FAILURE;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.Testers.Status.NOT_STARTED;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.Testers.Status.RUNNING;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.Testers.Status.SUCCESS;
+
+public class MockTesters implements Testers {
+
+ private byte[] logs = new byte[0];
+ private Status status = NOT_STARTED;
+
+ @Override
+ public void startTests(URI testerUrl, Suite suite, byte[] config) {
+ status = RUNNING;
+ }
+
+ @Override
+ public byte[] getLogs(URI testerUrl) {
+ return Arrays.copyOf(logs, logs.length);
+ }
+
+ @Override
+ public Status getStatus(URI testerUrl) {
+ return status;
+ }
+
+ public void set(byte[] logs, Status status) {
+ this.logs = Arrays.copyOf(logs, logs.length);
+ this.status = status;
+ }
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneFilter.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneFilter.java
index 1cb5cd22b8d..3bcd7298a15 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneFilter.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneFilter.java
@@ -7,7 +7,7 @@ package com.yahoo.vespa.hosted.controller.api.integration.zone;
* The methods here return instances of {@link ZoneList}, which extends ZoneFilter, but with accessors and additional filters.
* This forces the developer to consider which of the filters in this class to apply, prior to processing any zones.
*
- * @author jvenstad
+ * @author jonmv
*/
public interface ZoneFilter {
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneFilterMock.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneFilterMock.java
index 7f4637e0492..67d2fd14e6b 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneFilterMock.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneFilterMock.java
@@ -15,7 +15,7 @@ import java.util.stream.Collectors;
/**
* A Zones.List implementation which assumes all zones are controllerManaged.
*
- * @author jvenstad
+ * @author jonmv
*/
public class ZoneFilterMock implements ZoneList {
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneId.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneId.java
index 21ac7a654b8..b53b81398c6 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneId.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneId.java
@@ -11,7 +11,7 @@ import java.util.Objects;
*
* Serialised form is 'environment.region'.
*
- * @author jvenstad
+ * @author jonmv
*/
public class ZoneId {
// TODO: Replace usages of environment + region with usages of this.
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneList.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneList.java
index 27e8a598043..4205f30e995 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneList.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneList.java
@@ -12,7 +12,7 @@ import java.util.List;
* This is typically offered after an initial filter from {@link ZoneFilter} has been applied.
* This forces the developer to consider which zones to process.
*
- * @author jvenstad
+ * @author jonmv
*/
public interface ZoneList extends ZoneFilter {
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/nonpublic/HeaderFields.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/nonpublic/HeaderFields.java
deleted file mode 100644
index 78a6750aedb..00000000000
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/nonpublic/HeaderFields.java
+++ /dev/null
@@ -1,14 +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.hosted.controller.api.nonpublic;
-
-/**
- * Non public header fields that are not part of the public api.
- *
- * Placed here since this is the only module we own that both the
- * command-line client and controller-server depend on.
- *
- * @author Tony Vaagenes
- */
-public class HeaderFields {
- public static final String USER_ID_HEADER_FIELD = "vespa.hosted.trusted.username";
-}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/statuspage/StatusPageResource.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/statuspage/StatusPageResource.java
deleted file mode 100644
index 65c5e0f9365..00000000000
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/statuspage/StatusPageResource.java
+++ /dev/null
@@ -1,24 +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.hosted.controller.api.statuspage;
-
-import com.fasterxml.jackson.databind.JsonNode;
-
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.MediaType;
-
-/**
- * @author andreer
- */
-@Path("/v1/")
-@Produces(MediaType.APPLICATION_JSON)
-public interface StatusPageResource {
-
- @GET
- @Path("{page}")
- @Produces(MediaType.APPLICATION_JSON)
- JsonNode statusPage(@PathParam("page") String page, @QueryParam("since") String since);
-}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/common/ContextAttributes.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/common/ContextAttributes.java
deleted file mode 100644
index 1cdff0f920b..00000000000
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/common/ContextAttributes.java
+++ /dev/null
@@ -1,13 +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.hosted.controller.common;
-
-/**
- * Constants for request context attributes used in our APIs.
- *
- * @author mpolden
- */
-public interface ContextAttributes {
-
- String SECURITY_CONTEXT_ATTRIBUTE = "vespa.hosted.security_context";
-
-}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/common/NotFoundCheckedException.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/common/NotFoundCheckedException.java
deleted file mode 100644
index a55a7e2bdfc..00000000000
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/common/NotFoundCheckedException.java
+++ /dev/null
@@ -1,23 +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.hosted.controller.common;
-
-/**
- * We have tons of places where we throw exceptions when
- * some hosted resource is not found. This is usually
- * done with IllegalArgumentExceptions, java.ws.rs exceptions or
- * the servermodel runtime exceptions in the controller-server module.
- *
- * This is a checked alternative to do the same thing.
- *
- * @author smorgrav
- */
-public class NotFoundCheckedException extends Exception {
-
- public NotFoundCheckedException() {
- super();
- }
-
- public NotFoundCheckedException(String msg) {
- super(msg);
- }
-}
diff --git a/controller-server/pom.xml b/controller-server/pom.xml
index ade85a845fd..66fefd6f2fe 100644
--- a/controller-server/pom.xml
+++ b/controller-server/pom.xml
@@ -49,13 +49,6 @@
<dependency>
<groupId>com.yahoo.vespa</groupId>
- <artifactId>container-jersey2</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
-
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
<artifactId>serviceview</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
@@ -121,6 +114,11 @@
</dependency>
<dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ </dependency>
+
+ <dependency>
<groupId>com.yahoo.vespa</groupId>
<artifactId>config-model-api</artifactId>
<version>${project.version}</version>
@@ -176,31 +174,6 @@
</exclusions>
</dependency>
- <dependency>
- <groupId>com.yahoo.athenz</groupId>
- <artifactId>athenz-zts-java-client</artifactId>
- <scope>compile</scope>
- <exclusions>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </exclusion>
- <!--Exclude all Jackson bundles provided by JDisc -->
- <exclusion>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-core</artifactId>
- </exclusion>
- <exclusion>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-databind</artifactId>
- </exclusion>
- <exclusion>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-annotations</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
-
<!-- test -->
<dependency>
@@ -217,12 +190,6 @@
</dependency>
<dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <scope>test</scope>
- </dependency>
-
- <dependency>
<groupId>com.yahoo.vespa</groupId>
<artifactId>testutil</artifactId>
<version>${project.version}</version>
@@ -235,6 +202,12 @@
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>com.github.tomakehurst</groupId>
+ <artifactId>wiremock-standalone</artifactId>
+ <scope>test</scope>
+ </dependency>
+
</dependencies>
<build>
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 295e0102782..677f2363c08 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
@@ -21,6 +21,7 @@ import com.yahoo.vespa.hosted.controller.rotation.RotationId;
import java.time.Instant;
import java.util.Collections;
import java.util.Comparator;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -52,7 +53,7 @@ public class Application {
/** Creates an empty application */
public Application(ApplicationId id) {
this(id, DeploymentSpec.empty, ValidationOverrides.empty, Collections.emptyMap(),
- new DeploymentJobs(OptionalLong.empty(), Collections.emptyList(), Optional.empty()),
+ new DeploymentJobs(OptionalLong.empty(), Collections.emptyList(), Optional.empty(), false),
Change.empty(), Change.empty(), Optional.empty(), new ApplicationMetrics(0, 0),
Optional.empty());
}
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 f0e278c3e6d..7ae21e21f99 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
@@ -37,7 +37,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.ApplicationVersion;
import com.yahoo.vespa.hosted.controller.application.Deployment;
-import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.application.JobStatus;
import com.yahoo.vespa.hosted.controller.application.JobStatus.JobRun;
import com.yahoo.vespa.hosted.controller.application.SystemApplication;
@@ -55,7 +55,6 @@ import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.Clock;
-import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -141,6 +140,8 @@ public class ApplicationController {
return sort(curator.readApplications(tenant));
}
+ public ArtifactRepository artifacts() { return artifactRepository; }
+
/**
* Set the rotations marked as 'global' either 'in' or 'out of' service.
*
@@ -232,8 +233,8 @@ public class ApplicationController {
* @throws IllegalArgumentException if the application already exists
*/
public Application createApplication(ApplicationId id, Optional<NToken> token) {
- if ( ! (id.instance().isDefault() || id.instance().value().matches("\\d+"))) // TODO: Support instances properly
- throw new UnsupportedOperationException("Only the instance names 'default' and names which are just the PR number are supported at the moment");
+ if ( ! (id.instance().isDefault())) // TODO: Support instances properly
+ throw new UnsupportedOperationException("Only the instance name 'default' is supported at the moment");
try (Lock lock = lock(id)) {
// Validate only application names which do not already exist.
if (asList(id.tenant()).stream().noneMatch(application -> application.id().application().equals(id.application())))
@@ -354,6 +355,14 @@ public class ApplicationController {
}
}
+ /** Assembles and deploys a tester application to the given zone. */
+ public ActivateResult deployTester(ApplicationId tester, ApplicationPackage applicationPackage, ZoneId zone, DeployOptions options) {
+ if ( ! tester.instance().value().endsWith("-t"))
+ throw new IllegalArgumentException("'" + tester + "' is not a tester application!");
+
+ return deploy(tester, applicationPackage, zone, options, Collections.emptySet(), Collections.emptySet());
+ }
+
private ActivateResult deploy(ApplicationId application, ApplicationPackage applicationPackage,
ZoneId zone, DeployOptions deployOptions,
Set<String> rotationNames, Set<String> cnames) {
@@ -599,7 +608,7 @@ public class ApplicationController {
* and store the application, and finally release (close) the lock.
*/
Lock lock(ApplicationId application) {
- return curator.lock(application, Duration.ofMinutes(10));
+ return curator.lock(application);
}
/** Verify that each of the production zones listed in the deployment spec exist in this system. */
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 b65d3bc0849..790d6d00035 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,6 +12,7 @@ import com.yahoo.vespa.curator.Lock;
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.integration.BuildService;
+import com.yahoo.vespa.hosted.controller.api.integration.LogStore;
import com.yahoo.vespa.hosted.controller.api.integration.MetricsService;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzClientFactory;
import com.yahoo.vespa.hosted.controller.api.integration.chef.Chef;
@@ -25,6 +26,9 @@ import com.yahoo.vespa.hosted.controller.api.integration.routing.GlobalRoutingSe
import com.yahoo.vespa.hosted.controller.api.integration.routing.RotationStatus;
import com.yahoo.vespa.hosted.controller.api.integration.routing.RoutingGenerator;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneRegistry;
+import com.yahoo.vespa.hosted.controller.deployment.DelegatingBuildService;
+import com.yahoo.vespa.hosted.controller.deployment.InternalBuildService;
+import com.yahoo.vespa.hosted.controller.deployment.JobController;
import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
import com.yahoo.vespa.hosted.controller.rotation.Rotation;
import com.yahoo.vespa.hosted.controller.versions.VersionStatus;
@@ -61,6 +65,7 @@ public class Controller extends AbstractComponent {
private final CuratorDb curator;
private final ApplicationController applicationController;
private final TenantController tenantController;
+ private final JobController jobController;
private final Clock clock;
private final GitHub gitHub;
private final EntityService entityService;
@@ -84,12 +89,12 @@ public class Controller extends AbstractComponent {
ZoneRegistry zoneRegistry, ConfigServer configServer,
MetricsService metricsService, NameService nameService,
RoutingGenerator routingGenerator, Chef chef, AthenzClientFactory athenzClientFactory,
- ArtifactRepository artifactRepository, BuildService buildService) {
+ ArtifactRepository artifactRepository, BuildService buildService, LogStore logStore) {
this(curator, rotationsConfig,
gitHub, entityService, organization, globalRoutingService, zoneRegistry,
configServer, metricsService, nameService, routingGenerator, chef,
Clock.systemUTC(), athenzClientFactory, artifactRepository, buildService,
- com.yahoo.net.HostName::getLocalhost);
+ logStore, com.yahoo.net.HostName::getLocalhost);
}
public Controller(CuratorDb curator, RotationsConfig rotationsConfig,
@@ -99,7 +104,7 @@ public class Controller extends AbstractComponent {
MetricsService metricsService, NameService nameService,
RoutingGenerator routingGenerator, Chef chef, Clock clock,
AthenzClientFactory athenzClientFactory, ArtifactRepository artifactRepository,
- BuildService buildService, Supplier<String> hostnameSupplier) {
+ BuildService buildService, LogStore logStore, Supplier<String> hostnameSupplier) {
this.hostnameSupplier = Objects.requireNonNull(hostnameSupplier, "HostnameSupplier cannot be null");
this.curator = Objects.requireNonNull(curator, "Curator cannot be null");
@@ -114,18 +119,22 @@ public class Controller extends AbstractComponent {
this.clock = Objects.requireNonNull(clock, "Clock cannot be null");
this.athenzClientFactory = Objects.requireNonNull(athenzClientFactory, "AthenzClientFactory cannot be null");
+ jobController = new JobController(this, logStore);
applicationController = new ApplicationController(this, curator, athenzClientFactory,
Objects.requireNonNull(rotationsConfig, "RotationsConfig cannot be null"),
Objects.requireNonNull(nameService, "NameService cannot be null"),
configServer,
Objects.requireNonNull(artifactRepository, "ArtifactRepository cannot be null"),
Objects.requireNonNull(routingGenerator, "RoutingGenerator cannot be null"),
- Objects.requireNonNull(buildService, "BuildService cannot be null"),
+ new DelegatingBuildService(Objects.requireNonNull(buildService, "BuildService cannot be null"),
+ new InternalBuildService(jobController)),
clock);
tenantController = new TenantController(this, curator, athenzClientFactory);
// Record the version of this controller
curator().writeControllerVersion(this.hostname(), Vtag.currentVersion);
+
+ jobController.updateStorage();
}
/** Returns the instance controlling tenants */
@@ -134,6 +143,9 @@ public class Controller extends AbstractComponent {
/** Returns the instance controlling applications */
public ApplicationController applications() { return applicationController; }
+ /** Returns the instance controlling deployment jobs. */
+ public JobController jobController() { return jobController; }
+
public List<AthenzDomain> getDomainList(String prefix) {
return athenzClientFactory.createZmsClientWithServicePrincipal().getDomainList(prefix);
}
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 3207d4b8399..2209cdf3013 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
@@ -18,7 +18,7 @@ import com.yahoo.vespa.hosted.controller.application.ClusterInfo;
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.DeploymentJobs.JobType;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.application.DeploymentMetrics;
import com.yahoo.vespa.hosted.controller.application.JobStatus;
import com.yahoo.vespa.hosted.controller.rotation.RotationId;
@@ -34,7 +34,7 @@ import java.util.OptionalLong;
* An application that has been locked for modification. Provides methods for modifying an application's fields.
*
* @author mpolden
- * @author jvenstad
+ * @author jonmv
*/
public class LockedApplication {
@@ -89,6 +89,12 @@ public class LockedApplication {
outstandingChange, ownershipIssueId, metrics, rotation);
}
+ public LockedApplication withBuiltInternally(boolean builtInternally) {
+ return new LockedApplication(lock, id, deploymentSpec, validationOverrides, deployments,
+ deploymentJobs.withBuiltInternally(builtInternally), change, outstandingChange,
+ ownershipIssueId, metrics, rotation);
+ }
+
public LockedApplication withProjectId(OptionalLong projectId) {
return new LockedApplication(lock, id, deploymentSpec, validationOverrides, deployments,
deploymentJobs.withProjectId(projectId), change, outstandingChange,
@@ -158,7 +164,7 @@ public class LockedApplication {
return with(deployments);
}
- public LockedApplication withoutDeploymentJob(DeploymentJobs.JobType jobType) {
+ public LockedApplication withoutDeploymentJob(JobType jobType) {
return new LockedApplication(lock, id, deploymentSpec, validationOverrides, deployments,
deploymentJobs.without(jobType), change, outstandingChange,
ownershipIssueId, metrics, rotation);
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/TenantController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/TenantController.java
index 2e8fe795fb5..228ca01e764 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/TenantController.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/TenantController.java
@@ -5,6 +5,7 @@ import com.yahoo.config.provision.TenantName;
import com.yahoo.vespa.athenz.api.AthenzDomain;
import com.yahoo.vespa.athenz.api.AthenzUser;
import com.yahoo.vespa.athenz.api.NToken;
+import com.yahoo.vespa.athenz.client.zts.ZtsClient;
import com.yahoo.vespa.curator.Lock;
import com.yahoo.vespa.hosted.controller.api.identifiers.UserId;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzClientFactory;
@@ -69,12 +70,13 @@ public class TenantController {
/** Returns a list of all tenants accessible by the given user */
public List<Tenant> asList(UserId user) {
AthenzUser athenzUser = AthenzUser.fromUserId(user.id());
- Set<AthenzDomain> userDomains = new HashSet<>(athenzClientFactory.createZtsClientWithServicePrincipal()
- .getTenantDomainsForUser(athenzUser));
- return asList().stream()
- .filter(tenant -> isUser(tenant, user) ||
- userDomains.stream().anyMatch(domain -> inDomain(tenant, domain)))
- .collect(Collectors.toList());
+ try (ZtsClient ztsClient = athenzClientFactory.createZtsClientWithServicePrincipal()) {
+ Set<AthenzDomain> userDomains = new HashSet<>(ztsClient.getTenantDomains(athenzClientFactory.getControllerIdentity(), athenzUser, "admin"));
+ return asList().stream()
+ .filter(tenant -> isUser(tenant, user) ||
+ userDomains.stream().anyMatch(domain -> inDomain(tenant, domain)))
+ .collect(Collectors.toList());
+ }
}
/** Create an user tenant with given username */
@@ -203,7 +205,7 @@ public class TenantController {
* and store the tenant, and finally release (close) the lock.
*/
private Lock lock(TenantName tenant) {
- return curator.lock(tenant, Duration.ofMinutes(10));
+ return curator.lock(tenant);
}
private static boolean inDomain(Tenant tenant, AthenzDomain domain) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/api/ActivateResult.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/api/ActivateResult.java
index 271942ff9a3..a890c50a0af 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/api/ActivateResult.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/api/ActivateResult.java
@@ -8,6 +8,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.configserver.PrepareRes
* @author Oyvind Gronnesby
*/
public class ActivateResult {
+ // TODO jvenstad: Replace this class with just the PrepareResponse.
private final RevisionId revisionId;
private final PrepareResponse prepareResponse;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/DeploymentJobs.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/DeploymentJobs.java
index d3193fd486d..65ba7e68d31 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/DeploymentJobs.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/DeploymentJobs.java
@@ -3,12 +3,9 @@ package com.yahoo.vespa.hosted.controller.application;
import com.google.common.collect.ImmutableMap;
import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.Environment;
-import com.yahoo.config.provision.RegionName;
-import com.yahoo.config.provision.SystemName;
import com.yahoo.vespa.hosted.controller.api.integration.BuildService;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId;
-import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
import java.util.Collection;
import java.util.HashMap;
@@ -17,7 +14,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;
-import java.util.stream.Stream;
/**
* Information about which deployment jobs an application should run and their current status.
@@ -31,19 +27,22 @@ public class DeploymentJobs {
private final OptionalLong projectId;
private final ImmutableMap<JobType, JobStatus> status;
private final Optional<IssueId> issueId;
+ private final boolean builtInternally;
public DeploymentJobs(OptionalLong projectId, Collection<JobStatus> jobStatusEntries,
- Optional<IssueId> issueId) {
- this(projectId, asMap(jobStatusEntries), issueId);
+ Optional<IssueId> issueId, boolean builtInternally) {
+ this(projectId, asMap(jobStatusEntries), issueId, builtInternally);
}
- private DeploymentJobs(OptionalLong projectId, Map<JobType, JobStatus> status, Optional<IssueId> issueId) {
+ private DeploymentJobs(OptionalLong projectId, Map<JobType, JobStatus> status, Optional<IssueId> issueId,
+ boolean builtInternally) {
requireId(projectId, "projectId must be a positive integer");
Objects.requireNonNull(status, "status cannot be null");
Objects.requireNonNull(issueId, "issueId cannot be null");
this.projectId = projectId;
this.status = ImmutableMap.copyOf(status);
this.issueId = issueId;
+ this.builtInternally = builtInternally;
}
private static Map<JobType, JobStatus> asMap(Collection<JobStatus> jobStatusEntries) {
@@ -60,7 +59,7 @@ public class DeploymentJobs {
if (job == null) job = JobStatus.initial(jobType);
return job.withCompletion(completion, jobError);
});
- return new DeploymentJobs(OptionalLong.of(projectId), status, issueId);
+ return new DeploymentJobs(OptionalLong.of(projectId), status, issueId, builtInternally);
}
public DeploymentJobs withTriggering(JobType jobType, JobStatus.JobRun jobRun) {
@@ -69,21 +68,25 @@ public class DeploymentJobs {
if (job == null) job = JobStatus.initial(jobType);
return job.withTriggering(jobRun);
});
- return new DeploymentJobs(projectId, status, issueId);
+ return new DeploymentJobs(projectId, status, issueId, builtInternally);
}
public DeploymentJobs withProjectId(OptionalLong projectId) {
- return new DeploymentJobs(projectId, status, issueId);
+ return new DeploymentJobs(projectId, status, issueId, builtInternally);
}
public DeploymentJobs with(IssueId issueId) {
- return new DeploymentJobs(projectId, status, Optional.ofNullable(issueId));
+ return new DeploymentJobs(projectId, status, Optional.ofNullable(issueId), builtInternally);
}
public DeploymentJobs without(JobType job) {
Map<JobType, JobStatus> status = new HashMap<>(this.status);
status.remove(job);
- return new DeploymentJobs(projectId, status, issueId);
+ return new DeploymentJobs(projectId, status, issueId, builtInternally);
+ }
+
+ public DeploymentJobs withBuiltInternally(boolean builtInternally) {
+ return new DeploymentJobs(projectId, status, issueId, builtInternally);
}
/** Returns an immutable map of the status entries in this */
@@ -111,6 +114,8 @@ public class DeploymentJobs {
public Optional<IssueId> issueId() { return issueId; }
+ public boolean builtInternally() { return builtInternally; }
+
private static OptionalLong requireId(OptionalLong id, String message) {
Objects.requireNonNull(id, message);
if ( ! id.isPresent()) {
@@ -122,93 +127,6 @@ public class DeploymentJobs {
return id;
}
- /** Job types that exist in the build system */
- public enum JobType {
-// | enum name ------------| job name ------------------| Zone in main system ---------------------------------------| Zone in CD system -------------------------------------------
- component ("component" , null , null ),
- systemTest ("system-test" , ZoneId.from("test" , "us-east-1") , ZoneId.from("test" , "cd-us-central-1")),
- stagingTest ("staging-test" , ZoneId.from("staging", "us-east-3") , ZoneId.from("staging", "cd-us-central-1")),
- productionCorpUsEast1 ("production-corp-us-east-1" , ZoneId.from("prod" , "corp-us-east-1") , null ),
- productionUsEast3 ("production-us-east-3" , ZoneId.from("prod" , "us-east-3") , null ),
- productionUsWest1 ("production-us-west-1" , ZoneId.from("prod" , "us-west-1") , null ),
- productionUsCentral1 ("production-us-central-1" , ZoneId.from("prod" , "us-central-1") , null ),
- productionApNortheast1 ("production-ap-northeast-1" , ZoneId.from("prod" , "ap-northeast-1") , null ),
- productionApNortheast2 ("production-ap-northeast-2" , ZoneId.from("prod" , "ap-northeast-2") , null ),
- productionApSoutheast1 ("production-ap-southeast-1" , ZoneId.from("prod" , "ap-southeast-1") , null ),
- productionEuWest1 ("production-eu-west-1" , ZoneId.from("prod" , "eu-west-1") , null ),
- productionAwsUsEast1a ("production-aws-us-east-1a" , ZoneId.from("prod" , "aws-us-east-1a") , null ),
- productionCdAwsUsEast1a("production-cd-aws-us-east-1a" , null , ZoneId.from("prod" , "cd-aws-us-east-1a")),
- productionCdUsCentral1 ("production-cd-us-central-1", null , ZoneId.from("prod" , "cd-us-central-1")),
- productionCdUsCentral2 ("production-cd-us-central-2", null , ZoneId.from("prod" , "cd-us-central-2"));
-
- private final String jobName;
- private final ImmutableMap<SystemName, ZoneId> zones;
-
- JobType(String jobName, ZoneId mainZone, ZoneId cdZone) {
- this.jobName = jobName;
- ImmutableMap.Builder<SystemName, ZoneId> builder = ImmutableMap.builder();
- if (mainZone != null) builder.put(SystemName.main, mainZone);
- if (cdZone != null) builder.put(SystemName.cd, cdZone);
- this.zones = builder.build();
- }
-
- public String jobName() { return jobName; }
-
- /** Returns the zone for this job in the given system, or empty if this job does not have a zone */
- public Optional<ZoneId> zone(SystemName system) {
- return Optional.ofNullable(zones.get(system));
- }
-
- /** Returns whether this is a production job */
- public boolean isProduction() { return environment() == Environment.prod; }
-
- /** Returns whether this is an automated test job */
- public boolean isTest() { return environment() != null && environment().isTest(); }
-
- /** Returns the environment of this job type, or null if it does not have an environment */
- public Environment environment() {
- switch (this) {
- case component: return null;
- case systemTest: return Environment.test;
- case stagingTest: return Environment.staging;
- default: return Environment.prod;
- }
- }
-
- /** Returns the region of this job type, or null if it does not have a region */
- public Optional<RegionName> region(SystemName system) {
- return zone(system).map(ZoneId::region);
- }
-
- public static Optional<JobType> fromOptionalJobName(String jobName) {
- return Stream.of(values())
- .filter(jobType -> jobType.jobName.equals(jobName))
- .findAny();
- }
-
- public static JobType fromJobName(String jobName) {
- return fromOptionalJobName(jobName)
- .orElseThrow(() -> new IllegalArgumentException("Unknown job name '" + jobName + "'"));
- }
-
- /** Returns the job type for the given zone */
- public static Optional<JobType> from(SystemName system, ZoneId zone) {
- return Stream.of(values())
- .filter(job -> job.zone(system).filter(zone::equals).isPresent())
- .findAny();
- }
-
- /** Returns the job job type for the given environment and region or null if none */
- public static Optional<JobType> from(SystemName system, Environment environment, RegionName region) {
- switch (environment) {
- case test: return Optional.of(systemTest);
- case staging: return Optional.of(stagingTest);
- }
- return from(system, ZoneId.from(environment, region));
- }
-
- }
-
/** A job report. This class is immutable. */
public static class JobReport {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/JobList.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/JobList.java
index 90435e13a1b..cd15556ba9b 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/JobList.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/JobList.java
@@ -4,7 +4,7 @@ package com.yahoo.vespa.hosted.controller.application;
import com.google.common.collect.ImmutableList;
import com.yahoo.component.Version;
import com.yahoo.vespa.hosted.controller.Application;
-import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.application.JobStatus.JobRun;
import java.time.Instant;
@@ -16,7 +16,7 @@ import java.util.function.Predicate;
/**
* A list of deployment jobs that can be filtered in various ways.
*
- * @author jvenstad
+ * @author jonmv
*/
public class JobList {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/JobStatus.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/JobStatus.java
index f44d2218145..a06a3e00340 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/JobStatus.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/JobStatus.java
@@ -2,8 +2,7 @@
package com.yahoo.vespa.hosted.controller.application;
import com.yahoo.component.Version;
-import com.yahoo.vespa.hosted.controller.Controller;
-import com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import java.time.Instant;
import java.util.Objects;
@@ -20,7 +19,7 @@ import static java.util.Objects.requireNonNull;
*/
public class JobStatus {
- private final DeploymentJobs.JobType type;
+ private final JobType type;
private final Optional<JobRun> lastTriggered;
private final Optional<JobRun> lastCompleted;
@@ -33,7 +32,7 @@ public class JobStatus {
* Used by the persistence layer (only) to create a complete JobStatus instance.
* Other creation should be by using initial- and with- methods.
*/
- public JobStatus(DeploymentJobs.JobType type, Optional<DeploymentJobs.JobError> jobError,
+ public JobStatus(JobType type, Optional<DeploymentJobs.JobError> jobError,
Optional<JobRun> lastTriggered, Optional<JobRun> lastCompleted,
Optional<JobRun> firstFailing, Optional<JobRun> lastSuccess) {
requireNonNull(type, "jobType cannot be null");
@@ -47,14 +46,14 @@ public class JobStatus {
this.jobError = jobError;
// Never say we triggered component because we don't:
- this.lastTriggered = type == DeploymentJobs.JobType.component ? Optional.empty() : lastTriggered;
+ this.lastTriggered = type == JobType.component ? Optional.empty() : lastTriggered;
this.lastCompleted = lastCompleted;
this.firstFailing = firstFailing;
this.lastSuccess = lastSuccess;
}
/** Returns an empty job status */
- public static JobStatus initial(DeploymentJobs.JobType type) {
+ public static JobStatus initial(JobType type) {
return new JobStatus(type, Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty());
}
@@ -84,7 +83,7 @@ public class JobStatus {
return new JobStatus(type, jobError, lastTriggered, Optional.of(completion), firstFailing, lastSuccess);
}
- public DeploymentJobs.JobType type() { return type; }
+ public JobType type() { return type; }
/** Returns true unless this job last completed with a failure */
public boolean isSuccess() {
@@ -94,6 +93,11 @@ public class JobStatus {
/** The error of the last completion, or empty if the last run succeeded */
public Optional<DeploymentJobs.JobError> jobError() { return jobError; }
+ /** Returns whether this last failed on out of capacity */
+ public boolean isOutOfCapacity() {
+ return jobError.filter(error -> error == DeploymentJobs.JobError.outOfCapacity).isPresent();
+ }
+
/**
* Returns the last triggering of this job, or empty if the controller has never triggered it
* and not seen a deployment for it
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/SystemApplication.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/SystemApplication.java
index 1e96f33c275..05ac311c514 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/SystemApplication.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/SystemApplication.java
@@ -3,6 +3,12 @@ package com.yahoo.vespa.hosted.controller.application;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.NodeType;
+import com.yahoo.config.provision.RegionName;
+import com.yahoo.vespa.hosted.controller.Controller;
+import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.ServiceConvergence;
+import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
import java.util.Arrays;
import java.util.List;
@@ -48,14 +54,37 @@ public enum SystemApplication {
return nodeType == NodeType.proxy;
}
+ /** Returns whether config for this application has converged in given zone */
+ public boolean configConvergedIn(ZoneId zone, Controller controller) {
+ if (!hasApplicationPackage()) {
+ return true;
+ }
+ // TODO: Remove this hack once Docker hosts are removed from zone-application.
+ if (isAws(zone.region())) {
+ return true; // Skip checking config convergence on AWS as Docker hosts do not have cloud config
+ }
+ return controller.configServer().serviceConvergence(new DeploymentId(id(), zone))
+ .map(ServiceConvergence::converged)
+ .orElse(false);
+ }
+
/** All known system applications */
public static List<SystemApplication> all() {
return Arrays.asList(values());
}
+ /** Node states to consider when upgrading */
+ public static List<Node.State> activeStates() {
+ return Arrays.asList(Node.State.active, Node.State.reserved);
+ }
+
@Override
public String toString() {
return String.format("system application %s of type %s", id, nodeType);
}
+ private static boolean isAws(RegionName region) {
+ return region.value().startsWith("cd-aws-") || region.value().startsWith("aws-");
+ }
+
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/config/package-info.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/config/package-info.java
new file mode 100644
index 00000000000..f3f2acf1e01
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/config/package-info.java
@@ -0,0 +1,10 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+/**
+ * Required for using {@link com.yahoo.vespa.hosted.controller.athenz.config.AthenzConfig} outside controller-server module.
+ *
+ * @author bjorncs
+ */
+@ExportPackage
+package com.yahoo.vespa.hosted.controller.athenz.config;
+
+import com.yahoo.osgi.annotation.ExportPackage; \ No newline at end of file
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzPrincipalFilter.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzPrincipalFilter.java
index 5166f53c6d2..b7ede7635c6 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzPrincipalFilter.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzPrincipalFilter.java
@@ -9,15 +9,16 @@ import com.yahoo.jdisc.http.filter.security.cors.CorsRequestFilterBase;
import com.yahoo.vespa.athenz.api.AthenzPrincipal;
import com.yahoo.vespa.athenz.api.NToken;
import com.yahoo.vespa.athenz.utils.AthenzIdentities;
-import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsKeystore;
+import com.yahoo.vespa.athenz.utils.ntoken.AthenzConfTruststore;
+import com.yahoo.vespa.athenz.utils.ntoken.NTokenValidator;
import com.yahoo.vespa.hosted.controller.athenz.config.AthenzConfig;
+import java.nio.file.Paths;
import java.security.cert.X509Certificate;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
-import java.util.concurrent.Executor;
/**
@@ -30,31 +31,23 @@ import java.util.concurrent.Executor;
*
* @author bjorncs
*/
-// TODO bjorncs: Move this class to vespa-athenz bundle
+// TODO bjorncs: Move this class to jdisc-security-filters bundle
public class AthenzPrincipalFilter extends CorsRequestFilterBase {
private final NTokenValidator validator;
private final String principalTokenHeader;
- /**
- * @param executor to preload the ZMS public keys with
- */
@Inject
- public AthenzPrincipalFilter(ZmsKeystore zmsKeystore,
- Executor executor,
- AthenzConfig athenzConfig,
- CorsFilterConfig corsConfig) {
- this(new NTokenValidator(zmsKeystore), executor, athenzConfig.principalHeaderName(), new HashSet<>(corsConfig.allowedUrls()));
+ public AthenzPrincipalFilter(AthenzConfig athenzConfig, CorsFilterConfig corsConfig) {
+ this(new NTokenValidator(Paths.get(athenzConfig.athenzConfFile())), athenzConfig.principalHeaderName(), new HashSet<>(corsConfig.allowedUrls()));
}
AthenzPrincipalFilter(NTokenValidator validator,
- Executor executor,
String principalTokenHeader,
Set<String> corsAllowedUrls) {
super(corsAllowedUrls);
this.validator = validator;
this.principalTokenHeader = principalTokenHeader;
- executor.execute(validator::preloadPublicKeys);
}
@Override
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/filter/NTokenValidator.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/filter/NTokenValidator.java
deleted file mode 100644
index 4dcca519058..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/filter/NTokenValidator.java
+++ /dev/null
@@ -1,81 +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.hosted.controller.athenz.filter;
-
-import com.yahoo.athenz.auth.token.PrincipalToken;
-import com.yahoo.log.LogLevel;
-import com.yahoo.vespa.athenz.api.AthenzDomain;
-import com.yahoo.vespa.athenz.api.AthenzPrincipal;
-import com.yahoo.vespa.athenz.api.NToken;
-import com.yahoo.vespa.athenz.utils.AthenzIdentities;
-import com.yahoo.vespa.hosted.controller.api.integration.athenz.InvalidTokenException;
-import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsKeystore;
-
-import java.security.PublicKey;
-import java.time.Duration;
-import java.util.Optional;
-import java.util.logging.Logger;
-
-import static com.yahoo.vespa.athenz.utils.AthenzIdentities.ZMS_ATHENZ_SERVICE;
-
-
-/**
- * Validates the content of an NToken:
- * 1) Verifies that the token is signed by the sys.auth.zms service (by validating the signature)
- * 2) Verifies that the token is not expired
- *
- * @author bjorncs
- */
-// TODO Move to vespa-athenz
-class NTokenValidator {
-
- // Max allowed skew in token timestamp (only for creation, not expiry timestamp)
- private static final long ALLOWED_TIMESTAMP_OFFSET = Duration.ofMinutes(5).getSeconds();
-
- private static final Logger log = Logger.getLogger(NTokenValidator.class.getName());
-
- private final ZmsKeystore keystore;
-
- NTokenValidator(ZmsKeystore keystore) {
- this.keystore = keystore;
- }
-
- void preloadPublicKeys() {
- keystore.preloadKeys(ZMS_ATHENZ_SERVICE);
- }
-
- AthenzPrincipal validate(NToken token) throws InvalidTokenException {
- PrincipalToken principalToken = new PrincipalToken(token.getRawToken());
- PublicKey zmsPublicKey = getPublicKey(principalToken.getKeyId())
- .orElseThrow(() -> new InvalidTokenException("NToken has an unknown keyId"));
- validateSignatureAndExpiration(principalToken, zmsPublicKey);
- return new AthenzPrincipal(
- AthenzIdentities.from(
- new AthenzDomain(principalToken.getDomain()),
- principalToken.getName()),
- token);
- }
-
- private Optional<PublicKey> getPublicKey(String keyId) throws InvalidTokenException {
- try {
- return keystore.getPublicKey(ZMS_ATHENZ_SERVICE, keyId);
- } catch (Exception e) {
- logDebug(e.getMessage());
- throw new InvalidTokenException("Failed to retrieve public key");
- }
- }
-
- private static void validateSignatureAndExpiration(PrincipalToken token,
- PublicKey zmsPublicKey) throws InvalidTokenException {
- StringBuilder errorMessageBuilder = new StringBuilder();
- if (!token.validate(zmsPublicKey, (int) ALLOWED_TIMESTAMP_OFFSET, true, errorMessageBuilder)) {
- String message = "NToken is expired or has invalid signature: " + errorMessageBuilder.toString();
- logDebug(message);
- throw new InvalidTokenException(message);
- }
- }
-
- private static void logDebug(String message) {
- log.log(LogLevel.DEBUG, "Failed to validate NToken: " + message);
- }
-
-}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/filter/UserAuthWithAthenzPrincipalFilter.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/filter/UserAuthWithAthenzPrincipalFilter.java
index b801c038bd8..0aa5c89c971 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/filter/UserAuthWithAthenzPrincipalFilter.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/filter/UserAuthWithAthenzPrincipalFilter.java
@@ -10,13 +10,11 @@ import com.yahoo.vespa.athenz.api.AthenzPrincipal;
import com.yahoo.vespa.athenz.api.AthenzUser;
import com.yahoo.vespa.athenz.api.NToken;
import com.yahoo.vespa.hosted.controller.api.identifiers.UserId;
-import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsKeystore;
import com.yahoo.vespa.hosted.controller.athenz.config.AthenzConfig;
import com.yahoo.yolean.chain.After;
import java.security.Principal;
import java.util.Optional;
-import java.util.concurrent.Executor;
import java.util.logging.Logger;
import java.util.stream.Stream;
@@ -38,11 +36,8 @@ public class UserAuthWithAthenzPrincipalFilter extends AthenzPrincipalFilter {
private final String principalHeaderName;
@Inject
- public UserAuthWithAthenzPrincipalFilter(ZmsKeystore zmsKeystore,
- Executor executor,
- AthenzConfig athenzConfig,
- CorsFilterConfig corsConfig) {
- super(zmsKeystore, executor, athenzConfig, corsConfig);
+ public UserAuthWithAthenzPrincipalFilter(AthenzConfig athenzConfig, CorsFilterConfig corsConfig) {
+ super(athenzConfig, corsConfig);
this.userAuthenticationPassThruAttribute = athenzConfig.userAuthenticationPassThruAttribute();
this.principalHeaderName = athenzConfig.principalHeaderName();
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzClientFactoryImpl.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzClientFactoryImpl.java
index 159a4f11619..633c0470080 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzClientFactoryImpl.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzClientFactoryImpl.java
@@ -8,16 +8,19 @@ import com.yahoo.athenz.auth.impl.SimplePrincipal;
import com.yahoo.athenz.auth.token.PrincipalToken;
import com.yahoo.athenz.auth.util.Crypto;
import com.yahoo.athenz.zms.ZMSClient;
-import com.yahoo.athenz.zts.ZTSClient;
import com.yahoo.container.jdisc.secretstore.SecretStore;
+import com.yahoo.vespa.athenz.api.AthenzIdentity;
+import com.yahoo.vespa.athenz.api.AthenzService;
import com.yahoo.vespa.athenz.api.NToken;
+import com.yahoo.vespa.athenz.client.zts.DefaultZtsClient;
+import com.yahoo.vespa.athenz.client.zts.ZtsClient;
import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider;
import com.yahoo.vespa.athenz.utils.AthenzIdentities;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzClientFactory;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsClient;
-import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZtsClient;
import com.yahoo.vespa.hosted.controller.athenz.config.AthenzConfig;
+import java.net.URI;
import java.security.PrivateKey;
/**
@@ -38,6 +41,11 @@ public class AthenzClientFactoryImpl implements AthenzClientFactory {
this.athenzPrincipalAuthority = new AthenzPrincipalAuthority(config.principalHeaderName());
}
+ @Override
+ public AthenzIdentity getControllerIdentity() {
+ return identityProvider.identity();
+ }
+
/**
* @return A ZMS client instance with the service identity as principal.
*/
@@ -51,7 +59,7 @@ public class AthenzClientFactoryImpl implements AthenzClientFactory {
*/
@Override
public ZtsClient createZtsClientWithServicePrincipal() {
- return new ZtsClientImpl(new ZTSClient(config.ztsUrl(), identityProvider.getIdentitySslContext()), config);
+ return new DefaultZtsClient(URI.create(config.ztsUrl()), identityProvider);
}
/**
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/ZmsClientImpl.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/ZmsClientImpl.java
index 67191d4c09d..6179d9891fd 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/ZmsClientImpl.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/ZmsClientImpl.java
@@ -1,22 +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.vespa.hosted.controller.athenz.impl;
-import com.yahoo.athenz.auth.util.Crypto;
import com.yahoo.athenz.zms.DomainList;
import com.yahoo.athenz.zms.ProviderResourceGroupRoles;
-import com.yahoo.athenz.zms.PublicKeyEntry;
-import com.yahoo.athenz.zms.ServiceIdentity;
import com.yahoo.athenz.zms.Tenancy;
import com.yahoo.athenz.zms.TenantRoleAction;
import com.yahoo.athenz.zms.ZMSClient;
import com.yahoo.athenz.zms.ZMSClientException;
import com.yahoo.log.LogLevel;
-import com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId;
import com.yahoo.vespa.athenz.api.AthenzDomain;
-import com.yahoo.vespa.hosted.controller.api.integration.athenz.ApplicationAction;
import com.yahoo.vespa.athenz.api.AthenzIdentity;
-import com.yahoo.vespa.athenz.api.AthenzPublicKey;
import com.yahoo.vespa.athenz.api.AthenzService;
+import com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId;
+import com.yahoo.vespa.hosted.controller.api.integration.athenz.ApplicationAction;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsClient;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsException;
import com.yahoo.vespa.hosted.controller.athenz.config.AthenzConfig;
@@ -130,28 +126,6 @@ public class ZmsClientImpl implements ZmsClient {
});
}
- @Override
- public AthenzPublicKey getPublicKey(AthenzService service, String keyId) {
- log("getPublicKeyEntry(domain=%s, service=%s, keyId=%s)", service.getDomain().getName(), service.getName(), keyId);
- return getOrThrow(() -> {
- PublicKeyEntry entry = zmsClient.getPublicKeyEntry(service.getDomain().getName(), service.getName(), keyId);
- return fromYbase64EncodedKey(entry.getKey(), keyId);
- });
- }
-
- @Override
- public List<AthenzPublicKey> getPublicKeys(AthenzService service) {
- log("getServiceIdentity(domain=%s, service=%s)", service.getDomain().getName(), service.getName());
- return getOrThrow(() -> {
- ServiceIdentity serviceIdentity = zmsClient.getServiceIdentity(service.getDomain().getName(), service.getName());
- return toAthenzPublicKeys(serviceIdentity.getPublicKeys());
- });
- }
-
- private static AthenzPublicKey fromYbase64EncodedKey(String encodedKey, String keyId) {
- return new AthenzPublicKey(Crypto.loadPublicKey(Crypto.ybase64DecodeString(encodedKey)), keyId);
- }
-
private static List<TenantRoleAction> createTenantRoleActions() {
return Arrays.stream(ApplicationAction.values())
.map(action -> new TenantRoleAction().setAction(action.name()).setRole(action.roleName))
@@ -162,12 +136,6 @@ public class ZmsClientImpl implements ZmsClient {
return domains.stream().map(AthenzDomain::new).collect(toList());
}
- private static List<AthenzPublicKey> toAthenzPublicKeys(List<PublicKeyEntry> publicKeys) {
- return publicKeys.stream()
- .map(entry -> fromYbase64EncodedKey(entry.getKey(), entry.getId()))
- .collect(toList());
- }
-
private boolean hasAccess(String action, String resource, AthenzIdentity identity) {
log("getAccess(action=%s, resource=%s, principal=%s)", action, resource, identity);
return getOrThrow(
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/ZmsKeystoreImpl.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/ZmsKeystoreImpl.java
deleted file mode 100644
index 4b194651439..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/ZmsKeystoreImpl.java
+++ /dev/null
@@ -1,120 +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.hosted.controller.athenz.impl;
-
-import com.google.inject.Inject;
-import com.yahoo.log.LogLevel;
-import com.yahoo.vespa.athenz.api.AthenzPublicKey;
-import com.yahoo.vespa.athenz.api.AthenzService;
-import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzClientFactory;
-import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsException;
-import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsKeystore;
-
-import java.security.PublicKey;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.logging.Logger;
-
-/**
- * Downloads and caches public keys for Athens services.
- *
- * @author bjorncs
- */
-public class ZmsKeystoreImpl implements ZmsKeystore {
- private static final Logger log = Logger.getLogger(ZmsKeystoreImpl.class.getName());
-
- private final Map<FullKeyId, PublicKey> cachedKeys = new ConcurrentHashMap<>();
- private final AthenzClientFactory athenzClientFactory;
-
- @Inject
- public ZmsKeystoreImpl(AthenzClientFactory factory) {
- this.athenzClientFactory = factory;
- }
-
- @Override
- public Optional<PublicKey> getPublicKey(AthenzService service, String keyId) {
- FullKeyId fullKeyId = new FullKeyId(service, keyId);
- PublicKey cachedKey = cachedKeys.get(fullKeyId);
- if (cachedKey != null) {
- return Optional.of(cachedKey);
- }
- Optional<PublicKey> downloadedKey = downloadPublicKey(fullKeyId);
- downloadedKey.ifPresent(key -> {
- log.log(LogLevel.INFO, "Adding key " + fullKeyId + " to the cache");
- cachedKeys.put(fullKeyId, key);
- });
- return downloadedKey;
- }
-
- @Override
- public void preloadKeys(AthenzService service) {
- try {
- log.log(LogLevel.INFO, "Downloading keys for " + service);
- List<AthenzPublicKey> publicKeys = athenzClientFactory.createZmsClientWithServicePrincipal()
- .getPublicKeys(service);
- for (AthenzPublicKey publicKey : publicKeys) {
- FullKeyId fullKeyId = new FullKeyId(service, publicKey.getKeyId());
- log.log(LogLevel.DEBUG, "Adding key " + fullKeyId + " to the cache");
- cachedKeys.put(fullKeyId, publicKey.getPublicKey());
- }
- log.log(LogLevel.INFO, "Successfully downloaded keys for " + service);
- } catch (ZmsException e) {
- log.log(LogLevel.WARNING, "Failed to download keys for " + service + ": " + e.getMessage());
- }
- }
-
- private Optional<PublicKey> downloadPublicKey(FullKeyId fullKeyId) {
- try {
- log.log(LogLevel.INFO, "Downloading key " + fullKeyId);
- AthenzPublicKey publicKey = athenzClientFactory.createZmsClientWithServicePrincipal()
- .getPublicKey(fullKeyId.service, fullKeyId.keyId);
- return Optional.of(publicKey.getPublicKey());
- } catch (ZmsException e) {
- if (e.getCode() == 404) { // Key does not exist
- log.log(LogLevel.INFO, "Key " + fullKeyId + " not found");
- return Optional.empty();
- }
- String msg = String.format("Unable to retrieve public key from Athens (%s): %s", fullKeyId, e.getMessage());
- throw createException(msg, e);
- }
- }
-
- private static RuntimeException createException(String message, Exception cause) {
- log.log(LogLevel.ERROR, message);
- return new RuntimeException(message, cause);
- }
-
- private static class FullKeyId {
- private final AthenzService service;
- private final String keyId;
-
- private FullKeyId(AthenzService service, String keyId) {
- this.service = service;
- this.keyId = keyId;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- FullKeyId fullKeyId1 = (FullKeyId) o;
- return Objects.equals(service, fullKeyId1.service) &&
- Objects.equals(keyId, fullKeyId1.keyId);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(service, keyId);
- }
-
- @Override
- public String toString() {
- return "FullKeyId{" +
- "service=" + service +
- ", keyId='" + keyId + '\'' +
- '}';
- }
- }
-}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/ZtsClientImpl.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/ZtsClientImpl.java
deleted file mode 100644
index c3ed8f0a99f..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/ZtsClientImpl.java
+++ /dev/null
@@ -1,59 +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.hosted.controller.athenz.impl;
-
-import com.yahoo.athenz.zts.TenantDomains;
-import com.yahoo.athenz.zts.ZTSClient;
-import com.yahoo.athenz.zts.ZTSClientException;
-import com.yahoo.log.LogLevel;
-import com.yahoo.vespa.athenz.api.AthenzDomain;
-import com.yahoo.vespa.athenz.api.AthenzIdentity;
-import com.yahoo.vespa.athenz.api.AthenzService;
-import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZtsClient;
-import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZtsException;
-import com.yahoo.vespa.hosted.controller.athenz.config.AthenzConfig;
-
-import java.util.List;
-import java.util.function.Supplier;
-import java.util.logging.Logger;
-
-import static java.util.stream.Collectors.toList;
-
-/**
- * @author bjorncs
- */
-public class ZtsClientImpl implements ZtsClient {
-
- private static final Logger log = Logger.getLogger(ZtsClientImpl.class.getName());
-
- private final ZTSClient ztsClient;
- private final AthenzService service;
-
- public ZtsClientImpl(ZTSClient ztsClient, AthenzConfig config) {
- this.ztsClient = ztsClient;
- this.service = new AthenzService(config.domain(), config.service().name());
- }
-
- @Override
- public List<AthenzDomain> getTenantDomainsForUser(AthenzIdentity identity) {
- return getOrThrow(() -> {
- log.log(LogLevel.DEBUG, String.format(
- "getTenantDomains(domain=%s, identity=%s, rolename=admin, service=%s)",
- service.getDomain().getName(), identity.getFullName(), service.getFullName()));
- TenantDomains domains = ztsClient.getTenantDomains(
- service.getDomain().getName(), identity.getFullName(), "admin", service.getName());
- return domains.getTenantDomainNames().stream()
- .map(AthenzDomain::new)
- .collect(toList());
- });
- }
-
- private static <T> T getOrThrow(Supplier<T> wrappedCode) {
- try {
- return wrappedCode.get();
- } catch (ZTSClientException e) {
- log.warning("Error from Athenz: " + e.getMessage());
- throw new ZtsException(e.getCode(), e);
- }
- }
-
-}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/AthenzClientFactoryMock.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/AthenzClientFactoryMock.java
index f7939422170..6f829113016 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/AthenzClientFactoryMock.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/AthenzClientFactoryMock.java
@@ -3,10 +3,12 @@ package com.yahoo.vespa.hosted.controller.athenz.mock;
import com.google.inject.Inject;
import com.yahoo.component.AbstractComponent;
+import com.yahoo.vespa.athenz.api.AthenzIdentity;
+import com.yahoo.vespa.athenz.api.AthenzService;
import com.yahoo.vespa.athenz.api.NToken;
+import com.yahoo.vespa.athenz.client.zts.ZtsClient;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsClient;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzClientFactory;
-import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZtsClient;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -34,6 +36,11 @@ public class AthenzClientFactoryMock extends AbstractComponent implements Athenz
}
@Override
+ public AthenzIdentity getControllerIdentity() {
+ return new AthenzService("vespa.hosting");
+ }
+
+ @Override
public ZmsClient createZmsClientWithServicePrincipal() {
log("createZmsClientWithServicePrincipal()");
return new ZmsClientMock(athenz);
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 3ee2655108a..5e8674ce637 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
@@ -5,8 +5,6 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId;
import com.yahoo.vespa.athenz.api.AthenzDomain;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.ApplicationAction;
import com.yahoo.vespa.athenz.api.AthenzIdentity;
-import com.yahoo.vespa.athenz.api.AthenzPublicKey;
-import com.yahoo.vespa.athenz.api.AthenzService;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsClient;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsException;
@@ -96,16 +94,6 @@ public class ZmsClientMock implements ZmsClient {
return new ArrayList<>(athenz.domains.keySet());
}
- @Override
- public AthenzPublicKey getPublicKey(AthenzService service, String keyId) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public List<AthenzPublicKey> getPublicKeys(AthenzService service) {
- throw new UnsupportedOperationException();
- }
-
private AthenzDbMock.Domain getDomainOrThrow(AthenzDomain domainName, boolean verifyVespaTenant) {
AthenzDbMock.Domain domain = Optional.ofNullable(athenz.domains.get(domainName))
.orElseThrow(() -> zmsException(400, "Domain '%s' not found", domainName));
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/ZtsClientMock.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/ZtsClientMock.java
index 4aa1c2b93a5..8b3fb3ca47e 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/ZtsClientMock.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/ZtsClientMock.java
@@ -3,8 +3,17 @@ package com.yahoo.vespa.hosted.controller.athenz.mock;
import com.yahoo.vespa.athenz.api.AthenzDomain;
import com.yahoo.vespa.athenz.api.AthenzIdentity;
-import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZtsClient;
+import com.yahoo.vespa.athenz.api.AthenzRole;
+import com.yahoo.vespa.athenz.api.AthenzService;
+import com.yahoo.vespa.athenz.api.ZToken;
+import com.yahoo.vespa.athenz.client.zts.Identity;
+import com.yahoo.vespa.athenz.client.zts.InstanceIdentity;
+import com.yahoo.vespa.athenz.client.zts.ZtsClient;
+import com.yahoo.vespa.athenz.tls.Pkcs10Csr;
+import java.security.KeyPair;
+import java.security.cert.X509Certificate;
+import java.time.Duration;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -24,12 +33,57 @@ public class ZtsClientMock implements ZtsClient {
}
@Override
- public List<AthenzDomain> getTenantDomainsForUser(AthenzIdentity identity) {
- log.log(Level.INFO, "getTenantDomainsForUser(principal='%s')", identity);
+ public List<AthenzDomain> getTenantDomains(AthenzIdentity providerIdentity, AthenzIdentity userIdentity, String roleName) {
+ log.log(Level.INFO, String.format("getTenantDomains(providerIdentity='%s', userIdentity='%s', roleName='%s')",
+ providerIdentity.getFullName(), userIdentity.getFullName(), roleName));
return athenz.domains.values().stream()
- .filter(domain -> domain.tenantAdmins.contains(identity) || domain.admins.contains(identity))
+ .filter(domain -> domain.tenantAdmins.contains(userIdentity) || domain.admins.contains(userIdentity))
.map(domain -> domain.name)
.collect(toList());
}
+ @Override
+ public InstanceIdentity registerInstance(AthenzService providerIdentity, AthenzService instanceIdentity, String instanceId, String attestationData, boolean requestServiceToken, Pkcs10Csr csr) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public InstanceIdentity refreshInstance(AthenzService providerIdentity, AthenzService instanceIdentity, String instanceId, boolean requestServiceToken, Pkcs10Csr csr) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Identity getServiceIdentity(AthenzService identity, String keyId, Pkcs10Csr csr) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Identity getServiceIdentity(AthenzService identity, String keyId, KeyPair keyPair, String dnsSuffix) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ZToken getRoleToken(AthenzDomain domain) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ZToken getRoleToken(AthenzRole athenzRole) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public X509Certificate getRoleCertificate(AthenzRole role, Duration expiry, KeyPair keyPair, String cloud) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public X509Certificate getRoleCertificate(AthenzRole role, KeyPair keyPair, String cloud) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void close() {
+
+ }
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/concurrent/TimeoutException.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/concurrent/TimeoutException.java
deleted file mode 100644
index 260761fa6ac..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/concurrent/TimeoutException.java
+++ /dev/null
@@ -1,15 +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.hosted.controller.concurrent;
-
-/**
- * Throws on timeout
- *
- * @author bratseth
- */
-public class TimeoutException extends RuntimeException {
-
- public TimeoutException(String message) {
- super(message);
- }
-
-}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DelegatingBuildService.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DelegatingBuildService.java
new file mode 100644
index 00000000000..d2159841c9d
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DelegatingBuildService.java
@@ -0,0 +1,30 @@
+package com.yahoo.vespa.hosted.controller.deployment;
+
+import com.yahoo.vespa.hosted.controller.api.integration.BuildService;
+
+/**
+ * Sends build jobs to an internal build system whenever it accepts them, or to an external one otherwise.
+ *
+ * @author jonmv
+ */
+public class DelegatingBuildService implements BuildService {
+
+ private final BuildService external;
+ private final BuildService internal;
+
+ public DelegatingBuildService(BuildService external, BuildService internal) {
+ this.external = external;
+ this.internal = internal;
+ }
+
+ @Override
+ public void trigger(BuildJob buildJob) {
+ (internal.builds(buildJob) ? internal : external).trigger(buildJob);
+ }
+
+ @Override
+ public JobState stateOf(BuildJob buildJob) {
+ return (internal.builds(buildJob) ? internal : external).stateOf(buildJob);
+ }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentOrder.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentOrder.java
deleted file mode 100644
index 1c535a5a331..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentOrder.java
+++ /dev/null
@@ -1,68 +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.hosted.controller.deployment;
-
-import com.yahoo.config.application.api.DeploymentSpec;
-import com.yahoo.config.provision.SystemName;
-import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
-import com.yahoo.vespa.hosted.controller.application.Deployment;
-import com.yahoo.vespa.hosted.controller.application.DeploymentJobs;
-import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType;
-import com.yahoo.vespa.hosted.controller.application.JobStatus;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-import java.util.function.Supplier;
-
-import static java.util.Comparator.comparingInt;
-import static java.util.stream.Collectors.collectingAndThen;
-import static java.util.stream.Collectors.toList;
-
-/**
- * This class determines the order of deployments according to an application's deployment spec.
- *
- * @author mpolden
- */
-public class DeploymentOrder {
-
- private final Supplier<SystemName> system;
-
- public DeploymentOrder(Supplier<SystemName> system) {
- this.system = Objects.requireNonNull(system, "system may not be null");
- }
-
- /** Returns jobs for given deployment spec, in the order they are declared */
- public List<JobType> jobsFrom(DeploymentSpec deploymentSpec) {
- return deploymentSpec.steps().stream()
- .flatMap(step -> step.zones().stream())
- .map(this::toJob)
- .collect(collectingAndThen(toList(), Collections::unmodifiableList));
- }
-
- /** Returns job status sorted according to deployment spec */
- public List<JobStatus> sortBy(DeploymentSpec deploymentSpec, Collection<JobStatus> jobStatus) {
- List<DeploymentJobs.JobType> sortedJobs = jobsFrom(deploymentSpec);
- return jobStatus.stream()
- .sorted(comparingInt(job -> sortedJobs.indexOf(job.type())))
- .collect(collectingAndThen(toList(), Collections::unmodifiableList));
- }
-
- /** Returns deployments sorted according to declared zones */
- public List<Deployment> sortBy(List<DeploymentSpec.DeclaredZone> zones, Collection<Deployment> deployments) {
- List<ZoneId> productionZones = zones.stream()
- .filter(z -> z.region().isPresent())
- .map(z -> ZoneId.from(z.environment(), z.region().get()))
- .collect(toList());
- return deployments.stream()
- .sorted(comparingInt(deployment -> productionZones.indexOf(deployment.zone())))
- .collect(collectingAndThen(toList(), Collections::unmodifiableList));
- }
-
- /** Resolve job from deployment step */
- public JobType toJob(DeploymentSpec.DeclaredZone zone) {
- return JobType.from(system.get(), zone.environment(), zone.region().orElse(null))
- .orElseThrow(() -> new IllegalArgumentException("Invalid zone " + zone));
- }
-
-}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentSteps.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentSteps.java
new file mode 100644
index 00000000000..f60b7400219
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentSteps.java
@@ -0,0 +1,112 @@
+// 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.controller.deployment;
+
+import com.yahoo.config.application.api.DeploymentSpec;
+import com.yahoo.config.provision.Environment;
+import com.yahoo.config.provision.SystemName;
+import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
+import com.yahoo.vespa.hosted.controller.application.Deployment;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
+import com.yahoo.vespa.hosted.controller.application.JobStatus;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+import static java.util.Collections.singletonList;
+import static java.util.Comparator.comparingInt;
+import static java.util.stream.Collectors.collectingAndThen;
+
+/**
+ * This class provides helper methods for reading a deployment spec.
+ *
+ * @author mpolden
+ */
+public class DeploymentSteps {
+
+ private final DeploymentSpec spec;
+ private final Supplier<SystemName> system;
+
+ public DeploymentSteps(DeploymentSpec spec, Supplier<SystemName> system) {
+ this.spec = Objects.requireNonNull(spec, "spec cannot be null");
+ this.system = Objects.requireNonNull(system, "system cannot be null");
+ }
+
+ /** Returns jobs for this, in the order they are declared */
+ public List<JobType> jobs() {
+ return spec.steps().stream()
+ .flatMap(step -> step.zones().stream())
+ .map(this::toJob)
+ .collect(collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
+ }
+
+ /** Returns job status sorted according to deployment spec */
+ public List<JobStatus> sortBy(Collection<JobStatus> jobStatus) {
+ List<JobType> sortedJobs = jobs();
+ return jobStatus.stream()
+ .sorted(comparingInt(job -> sortedJobs.indexOf(job.type())))
+ .collect(collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
+ }
+
+ /** Returns deployments sorted according to declared zones */
+ public List<Deployment> sortBy2(Collection<Deployment> deployments) {
+ List<ZoneId> productionZones = spec.zones().stream()
+ .filter(z -> z.region().isPresent())
+ .map(z -> ZoneId.from(z.environment(), z.region().get()))
+ .collect(Collectors.toList());
+ return deployments.stream()
+ .sorted(comparingInt(deployment -> productionZones.indexOf(deployment.zone())))
+ .collect(collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
+ }
+
+ /** Resolve jobs from step */
+ public List<JobType> toJobs(DeploymentSpec.Step step) {
+ return step.zones().stream()
+ .map(this::toJob)
+ .collect(collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
+ }
+
+ /** Returns test jobs in this */
+ public List<JobType> testJobs() {
+ return toJobs(test());
+ }
+
+ /** Returns production jobs in this */
+ public List<JobType> productionJobs() {
+ return toJobs(production());
+ }
+
+ /** Returns test steps in this */
+ public List<DeploymentSpec.Step> test() {
+ if (spec.steps().isEmpty()) {
+ return singletonList(new DeploymentSpec.DeclaredZone(Environment.test));
+ }
+ return spec.steps().stream()
+ .filter(step -> step.deploysTo(Environment.test) || step.deploysTo(Environment.staging))
+ .collect(collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
+ }
+
+ /** Returns production steps in this */
+ public List<DeploymentSpec.Step> production() {
+ return spec.steps().stream()
+ .filter(step -> step.deploysTo(Environment.prod) || step.zones().isEmpty())
+ .collect(collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
+ }
+
+ /** Resolve job from deployment zone */
+ private JobType toJob(DeploymentSpec.DeclaredZone zone) {
+ return JobType.from(system.get(), zone.environment(), zone.region().orElse(null))
+ .orElseThrow(() -> new IllegalArgumentException("Invalid zone " + zone));
+ }
+
+ /** Resolve jobs from steps */
+ private List<JobType> toJobs(List<DeploymentSpec.Step> steps) {
+ return steps.stream()
+ .flatMap(step -> toJobs(step).stream())
+ .collect(collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
+ }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java
index 63a6ac234ff..e3b4b4cef8c 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.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.hosted.controller.deployment;
-import com.yahoo.component.Version;
import com.yahoo.config.application.api.DeploymentSpec;
import com.yahoo.config.application.api.DeploymentSpec.Step;
import com.yahoo.config.provision.ApplicationId;
@@ -15,9 +14,8 @@ import com.yahoo.vespa.hosted.controller.application.ApplicationList;
import com.yahoo.vespa.hosted.controller.application.ApplicationVersion;
import com.yahoo.vespa.hosted.controller.application.Change;
import com.yahoo.vespa.hosted.controller.application.Deployment;
-import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobError;
import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobReport;
-import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.application.JobStatus;
import com.yahoo.vespa.hosted.controller.application.JobStatus.JobRun;
@@ -26,6 +24,7 @@ import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
@@ -33,21 +32,17 @@ import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;
-import java.util.Set;
import java.util.function.Supplier;
import java.util.logging.Logger;
import java.util.stream.Stream;
-import static com.yahoo.config.provision.Environment.prod;
-import static com.yahoo.config.provision.Environment.staging;
-import static com.yahoo.config.provision.Environment.test;
import static com.yahoo.vespa.hosted.controller.api.integration.BuildService.BuildJob;
import static com.yahoo.vespa.hosted.controller.api.integration.BuildService.JobState.idle;
import static com.yahoo.vespa.hosted.controller.api.integration.BuildService.JobState.queued;
import static com.yahoo.vespa.hosted.controller.api.integration.BuildService.JobState.running;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.component;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.stagingTest;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.systemTest;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.component;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.stagingTest;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.systemTest;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.Comparator.comparing;
@@ -56,7 +51,6 @@ import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.partitioningBy;
import static java.util.stream.Collectors.toList;
-import static java.util.stream.Collectors.toSet;
/**
* Responsible for scheduling deployment jobs in a build system and keeping
@@ -66,7 +60,7 @@ import static java.util.stream.Collectors.toSet;
*
* @author bratseth
* @author mpolden
- * @author jvenstad
+ * @author jonmv
*/
public class DeploymentTrigger {
@@ -74,18 +68,16 @@ public class DeploymentTrigger {
private final Controller controller;
private final Clock clock;
- private final DeploymentOrder order;
private final BuildService buildService;
public DeploymentTrigger(Controller controller, BuildService buildService, Clock clock) {
this.controller = Objects.requireNonNull(controller, "controller cannot be null");
this.buildService = Objects.requireNonNull(buildService, "buildService cannot be null");
this.clock = Objects.requireNonNull(clock, "clock cannot be null");
- this.order = new DeploymentOrder(controller::system);
}
- public DeploymentOrder deploymentOrder() {
- return order;
+ public DeploymentSteps steps(DeploymentSpec spec) {
+ return new DeploymentSteps(spec, controller::system);
}
/**
@@ -109,8 +101,8 @@ public class DeploymentTrigger {
JobRun triggering;
if (report.jobType() == component) {
ApplicationVersion applicationVersion = ApplicationVersion.from(report.sourceRevision().get(), report.buildNumber());
- triggering = JobRun.triggering(controller.systemVersion(), applicationVersion, Optional
- .empty(), Optional.empty(), "Application commit", clock.instant());
+ triggering = JobRun.triggering(controller.systemVersion(), applicationVersion, Optional.empty(),
+ Optional.empty(), "Application commit", clock.instant());
if (report.success()) {
if (acceptNewApplicationVersion(application.get()))
application = application.withChange(application.get().change().with(applicationVersion))
@@ -195,7 +187,8 @@ public class DeploymentTrigger {
buildService.trigger(BuildJob.of(applicationId, application.deploymentJobs().projectId().getAsLong(), jobType.jobName()));
return singletonList(component);
}
- Versions versions = versions(application, application.change(), deploymentFor(application, jobType));
+ Versions versions = Versions.from(application.change(), application, deploymentFor(application, jobType),
+ controller.systemVersion());
String reason = "Job triggered manually by " + user;
return (jobType.isProduction() && ! isTested(application, versions)
? testJobs(application, versions, reason, clock.instant()).stream()
@@ -241,7 +234,7 @@ public class DeploymentTrigger {
private Optional<JobRun> successOn(Application application, JobType jobType, Versions versions) {
return application.deploymentJobs().statusOf(jobType).flatMap(JobStatus::lastSuccess)
- .filter(run -> targetsMatch(versions, run));
+ .filter(versions::targetsMatch);
}
private Optional<Deployment> deploymentFor(Application application, JobType jobType) {
@@ -279,47 +272,90 @@ public class DeploymentTrigger {
.<Instant>flatMap(job -> job.lastSuccess().map(JobRun::at)));
String reason = "New change available";
List<Job> testJobs = null; // null means "uninitialised", while empty means "don't run any jobs".
+ DeploymentSteps steps = steps(application.deploymentSpec());
- if (change.isPresent())
- for (Step step : productionStepsOf(application)) {
- Set<JobType> stepJobs = step.zones().stream().map(order::toJob).collect(toSet());
- List<JobType> remainingJobs = stepJobs.stream().filter(job -> ! isComplete(change, application, job)).collect(toList());
- if ( ! remainingJobs.isEmpty()) { // Step is incomplete; trigger remaining jobs if ready, or their test jobs if untested.
+ if (change.isPresent()) {
+ for (Step step : steps.production()) {
+ List<JobType> stepJobs = steps.toJobs(step);
+ List<JobType> remainingJobs = stepJobs.stream().filter(job -> !isComplete(change, application, job)).collect(toList());
+ if (!remainingJobs.isEmpty()) { // Step is incomplete; trigger remaining jobs if ready, or their test jobs if untested.
for (JobType job : remainingJobs) {
- Versions versions = versions(application, change, deploymentFor(application, job));
+ Versions versions = Versions.from(change, application, deploymentFor(application, job),
+ controller.systemVersion());
if (isTested(application, versions)) {
- if ( completedAt.isPresent()
- && jobStateOf(application, job) == idle
- && stepJobs.containsAll(runningProductionJobs(application)))
+ if (completedAt.isPresent() && canTrigger(job, versions, application, stepJobs)) {
jobs.add(deploymentJob(application, versions, change, job, reason, completedAt.get()));
- if ( ! alreadyTriggered(application, versions))
+ }
+ if (!alreadyTriggered(application, versions)) {
testJobs = emptyList();
- }
- else if (testJobs == null) {
- testJobs = testJobs(application, versions, String.format("Testing deployment for %s (%s)", job.jobName(), versions.toString()),
- completedAt.orElse(clock.instant()));
+ }
+ } else if (testJobs == null) {
+ testJobs = testJobs(application, versions,
+ String.format("Testing deployment for %s (%s)",
+ job.jobName(), versions.toString()),
+ completedAt.orElseGet(clock::instant));
}
}
completedAt = Optional.empty();
- }
- else { // All jobs are complete; find the time of completion of this step.
+ } else { // All jobs are complete; find the time of completion of this step.
if (stepJobs.isEmpty()) { // No jobs means this is delay step.
Duration delay = ((DeploymentSpec.Delay) step).duration();
- completedAt = completedAt.map(at -> at.plus(delay)).filter(at -> ! at.isAfter(clock.instant()));
+ completedAt = completedAt.map(at -> at.plus(delay)).filter(at -> !at.isAfter(clock.instant()));
reason += " after a delay of " + delay;
- }
- else {
+ } else {
completedAt = stepJobs.stream().map(job -> application.deploymentJobs().statusOf(job).get().lastCompleted().get().at()).max(naturalOrder());
reason = "Available change in " + stepJobs.stream().map(JobType::jobName).collect(joining(", "));
}
}
}
- if (testJobs == null)
- testJobs = testJobs(application, versions(application, application.change(), Optional.empty()),
+ }
+ if (testJobs == null) {
+ testJobs = testJobs(application, Versions.from(application, controller.systemVersion()),
"Testing last changes outside prod", clock.instant());
+ }
jobs.addAll(testJobs);
});
- return jobs;
+ return Collections.unmodifiableList(jobs);
+ }
+
+ /** Returns whether given job should be triggered */
+ private boolean canTrigger(JobType job, Versions versions, Application application, List<JobType> parallelJobs) {
+ if (jobStateOf(application, job) != idle) return false;
+ if (parallelJobs != null && !parallelJobs.containsAll(runningProductionJobs(application))) return false;
+
+ return triggerAt(clock.instant(), job, versions, application);
+ }
+
+ /** Returns whether given job should be triggered */
+ private boolean canTrigger(JobType job, Versions versions, Application application) {
+ return canTrigger(job, versions, application, null);
+ }
+
+ /** Returns whether job can trigger at given instant */
+ private boolean triggerAt(Instant instant, JobType job, Versions versions, Application application) {
+ Optional<JobStatus> jobStatus = application.deploymentJobs().statusOf(job);
+ if (!jobStatus.isPresent()) return true;
+ if (jobStatus.get().isSuccess()) return true; // Success
+ if (!jobStatus.get().lastCompleted().isPresent()) return true; // Never completed
+ if (!jobStatus.get().firstFailing().isPresent()) return true; // Should not happen as firstFailing should be set for an unsuccessful job
+ if (!versions.targetsMatch(jobStatus.get().lastCompleted().get())) return true; // Always trigger as targets have changed
+
+ Instant firstFailing = jobStatus.get().firstFailing().get().at();
+ Instant lastCompleted = jobStatus.get().lastCompleted().get().at();
+
+ // Retry all errors immediately for 1 minute
+ if (firstFailing.isAfter(instant.minus(Duration.ofMinutes(1)))) return true;
+
+ // Retry out of capacity errors in test environments every minute
+ if (job.isTest() && jobStatus.get().isOutOfCapacity()) {
+ return lastCompleted.isBefore(instant.minus(Duration.ofMinutes(1)));
+ }
+
+ // Retry other errors
+ if (firstFailing.isAfter(instant.minus(Duration.ofHours(1)))) { // If we failed within the last hour ...
+ return lastCompleted.isBefore(instant.minus(Duration.ofMinutes(10))); // ... retry every 10 minutes
+ }
+ return lastCompleted.isBefore(instant.minus(Duration.ofHours(2))); // Retry at most every 2 hours
}
// ---------- Job state helpers ----------
@@ -358,7 +394,7 @@ public class DeploymentTrigger {
*/
private boolean isComplete(Change change, Application application, JobType jobType) {
Optional<Deployment> existingDeployment = deploymentFor(application, jobType);
- return successOn(application, jobType, versions(application, change, existingDeployment)).isPresent()
+ return successOn(application, jobType, Versions.from(change, application, existingDeployment, controller.systemVersion())).isPresent()
|| jobType.isProduction()
&& existingDeployment.map(deployment -> ! isUpgrade(change, deployment) && isDowngrade(application.change(), deployment))
.orElse(false);
@@ -379,7 +415,7 @@ public class DeploymentTrigger {
private Optional<Instant> testedAt(Application application, Versions versions) {
Optional<JobRun> testRun = successOn(application, systemTest, versions);
Optional<JobRun> stagingRun = successOn(application, stagingTest, versions)
- .filter(run -> sourcesMatchIfPresent(versions, run));
+ .filter(versions::sourcesMatchIfPresent);
return max(testRun.map(JobRun::at), stagingRun.map(JobRun::at))
.filter(__ -> testRun.isPresent() && stagingRun.isPresent());
}
@@ -388,24 +424,11 @@ public class DeploymentTrigger {
return application.deploymentJobs().jobStatus().values().stream()
.filter(job -> job.type().isProduction())
.anyMatch(job -> job.lastTriggered()
- .filter(run -> targetsMatch(versions, run))
- .filter(run -> sourcesMatchIfPresent(versions, run))
+ .filter(versions::targetsMatch)
+ .filter(versions::sourcesMatchIfPresent)
.isPresent());
}
- /** If the given state's sources are present and differ from its targets, returns whether they are equal to those
- * of the given job run. */
- private static boolean sourcesMatchIfPresent(Versions versions, JobRun jobRun) {
- return ( ! versions.sourcePlatform.filter(version -> ! version.equals(versions.targetPlatform)).isPresent()
- || versions.sourcePlatform.equals(jobRun.sourcePlatform()))
- && ( ! versions.sourceApplication.filter(version -> ! version.equals(versions.targetApplication)).isPresent()
- || versions.sourceApplication.equals(jobRun.sourceApplication()));
- }
-
- private static boolean targetsMatch(Versions versions, JobRun jobRun) {
- return versions.targetPlatform.equals(jobRun.platform()) && versions.targetApplication.equals(jobRun.application());
- }
-
// ---------- Change management o_O ----------
private boolean acceptNewApplicationVersion(Application application) {
@@ -415,9 +438,10 @@ public class DeploymentTrigger {
}
private Change remainingChange(Application application) {
- List<JobType> jobs = productionStepsOf(application).isEmpty()
- ? jobsOf(testStepsOf(application))
- : jobsOf(productionStepsOf(application));
+ DeploymentSteps steps = steps(application.deploymentSpec());
+ List<JobType> jobs = steps.production().isEmpty()
+ ? steps.testJobs()
+ : steps.productionJobs();
Change change = application.change();
if (jobs.stream().allMatch(job -> isComplete(application.change().withoutApplication(), application, job)))
@@ -436,61 +460,28 @@ public class DeploymentTrigger {
*/
private List<Job> testJobs(Application application, Versions versions, String reason, Instant availableSince) {
List<Job> jobs = new ArrayList<>();
- for (JobType jobType : jobsOf(testStepsOf(application))) {
+ for (JobType jobType : steps(application.deploymentSpec()).testJobs()) {
Optional<JobRun> completion = successOn(application, jobType, versions)
- .filter(run -> sourcesMatchIfPresent(versions, run) || jobType == systemTest);
- if ( ! completion.isPresent() && jobStateOf(application, jobType) == idle)
+ .filter(run -> versions.sourcesMatchIfPresent(run) || jobType == systemTest);
+ if (!completion.isPresent() && canTrigger(jobType, versions, application)) {
jobs.add(deploymentJob(application, versions, application.change(), jobType, reason, availableSince));
+ }
}
return jobs;
}
- private List<JobType> jobsOf(Collection<Step> steps) {
- return steps.stream().flatMap(step -> step.zones().stream()).map(order::toJob).collect(toList());
- }
-
- private List<Step> testStepsOf(Application application) {
- return application.deploymentSpec().steps().isEmpty()
- ? singletonList(new DeploymentSpec.DeclaredZone(test))
- : application.deploymentSpec().steps().stream()
- .filter(step -> step.deploysTo(test) || step.deploysTo(staging))
- .collect(toList());
- }
-
- private List<Step> productionStepsOf(Application application) {
- return application.deploymentSpec().steps().stream()
- .filter(step -> step.deploysTo(prod) || step.zones().isEmpty())
- .collect(toList());
- }
-
private Job deploymentJob(Application application, Versions versions, Change change, JobType jobType, String reason, Instant availableSince) {
- boolean isRetry = application.deploymentJobs().statusOf(jobType).flatMap(JobStatus::jobError)
- .filter(JobError.outOfCapacity::equals).isPresent();
+ boolean isRetry = application.deploymentJobs().statusOf(jobType)
+ .map(JobStatus::isOutOfCapacity)
+ .orElse(false);
if (isRetry) reason += "; retrying on out of capacity";
- JobRun triggering = JobRun.triggering(versions.targetPlatform, versions.targetApplication, versions.sourcePlatform, versions.sourceApplication, reason, clock.instant());
+ JobRun triggering = JobRun.triggering(versions.targetPlatform(), versions.targetApplication(),
+ versions.sourcePlatform(), versions.sourceApplication(),
+ reason, clock.instant());
return new Job(application, triggering, jobType, availableSince, isRetry, change.application().isPresent());
}
- private Versions versions(Application application, Change change, Optional<Deployment> deployment) {
- return new Versions(targetPlatform(application, change, deployment),
- targetApplication(application, change, deployment),
- deployment.map(Deployment::version),
- deployment.map(Deployment::applicationVersion));
- }
-
- private Version targetPlatform(Application application, Change change, Optional<Deployment> deployment) {
- return max(deployment.map(Deployment::version), change.platform())
- .orElse(application.oldestDeployedPlatform()
- .orElse(controller.systemVersion()));
- }
-
- private ApplicationVersion targetApplication(Application application, Change change, Optional<Deployment> deployment) {
- return max(deployment.map(Deployment::applicationVersion), change.application())
- .orElse(application.oldestDeployedApplication()
- .orElse(application.deploymentJobs().jobStatus().get(component).lastSuccess().get().application()));
- }
-
// ---------- Data containers ----------
@@ -519,34 +510,5 @@ public class DeploymentTrigger {
}
-
- private static class Versions {
-
- private final Version targetPlatform;
- private final ApplicationVersion targetApplication;
- private final Optional<Version> sourcePlatform;
- private final Optional<ApplicationVersion> sourceApplication;
-
- private Versions(Version targetPlatform, ApplicationVersion targetApplication, Optional<Version> sourcePlatform,
- Optional<ApplicationVersion> sourceApplication) {
- this.targetPlatform = targetPlatform;
- this.targetApplication = targetApplication;
- this.sourcePlatform = sourcePlatform;
- this.sourceApplication = sourceApplication;
- }
-
- @Override
- public String toString() {
- return String.format("platform %s%s, application %s%s",
- sourcePlatform.filter(source -> ! source.equals(targetPlatform))
- .map(source -> source + " -> ").orElse(""),
- targetPlatform,
- sourceApplication.filter(source -> ! source.equals(targetApplication))
- .map(source -> source.id() + " -> ").orElse(""),
- targetApplication.id());
- }
-
- }
-
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DummyStepRunner.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DummyStepRunner.java
new file mode 100644
index 00000000000..17b523c60bf
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DummyStepRunner.java
@@ -0,0 +1,12 @@
+package com.yahoo.vespa.hosted.controller.deployment;
+
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
+
+public class DummyStepRunner implements StepRunner {
+
+ @Override
+ public Step.Status run(LockedStep step, RunId id) {
+ return Step.Status.succeeded;
+ }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalBuildService.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalBuildService.java
new file mode 100644
index 00000000000..18b62f5ea0f
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalBuildService.java
@@ -0,0 +1,39 @@
+package com.yahoo.vespa.hosted.controller.deployment;
+
+import com.yahoo.vespa.hosted.controller.api.integration.BuildService;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
+
+import java.util.Optional;
+
+/**
+ * Wraps a JobController as a BuildService.
+ *
+ * Shall be inlined when the {@link DelegatingBuildService} delegates all jobs to it.
+ *
+ * @author jonmv
+ */
+public class InternalBuildService implements BuildService {
+
+ private final JobController jobs;
+
+ public InternalBuildService(JobController jobs) {
+ this.jobs = jobs;
+ }
+
+ @Override
+ public void trigger(BuildJob buildJob) {
+ jobs.start(buildJob.applicationId(), JobType.fromJobName(buildJob.jobName()));
+ }
+
+ @Override
+ public JobState stateOf(BuildJob buildJob) {
+ Optional<RunStatus> run = jobs.last(buildJob.applicationId(), JobType.fromJobName(buildJob.jobName()));
+ return run.isPresent() && ! run.get().hasEnded() ? JobState.running : JobState.idle;
+ }
+
+ @Override
+ public boolean builds(BuildJob buildJob) {
+ return jobs.builds(buildJob.applicationId());
+ }
+
+}
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
new file mode 100644
index 00000000000..c5bcffd7ffe
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java
@@ -0,0 +1,550 @@
+package com.yahoo.vespa.hosted.controller.deployment;
+
+import com.google.common.collect.ImmutableMap;
+import com.yahoo.component.Version;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.SystemName;
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.Slime;
+import com.yahoo.vespa.config.SlimeUtils;
+import com.yahoo.vespa.hosted.controller.Application;
+import com.yahoo.vespa.hosted.controller.Controller;
+import com.yahoo.vespa.hosted.controller.api.ActivateResult;
+import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeployOptions;
+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.configserver.ConfigServerException;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.NoInstanceException;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.PrepareResponse;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.ServiceConvergence;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.Testers;
+import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
+import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
+import com.yahoo.vespa.hosted.controller.application.ApplicationVersion;
+import com.yahoo.vespa.hosted.controller.application.DeploymentJobs;
+import com.yahoo.vespa.hosted.controller.application.JobStatus;
+import com.yahoo.vespa.hosted.controller.deployment.Step.Status;
+import com.yahoo.yolean.Exceptions;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.UncheckedIOException;
+import java.net.URI;
+import java.text.SimpleDateFormat;
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.TimeZone;
+import java.util.function.Supplier;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+import static com.yahoo.log.LogLevel.DEBUG;
+import static com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException.ErrorCode.ACTIVATION_CONFLICT;
+import static com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException.ErrorCode.APPLICATION_LOCK_FAILURE;
+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.Node.State.active;
+import static com.yahoo.vespa.hosted.controller.api.integration.configserver.Node.State.reserved;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.failed;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.succeeded;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.unfinished;
+import static java.util.logging.Level.INFO;
+import static java.util.logging.Level.WARNING;
+
+/**
+ * Runs steps of a deployment job against its provided controller.
+ *
+ * A dual-purpose logger is set up for each thread that runs a step here:
+ * 1. All messages are logged to a buffer which is stored in an external log storage at the end of execution, and
+ * 2. all messages are also logged through the usual logging framework; thus, by default, any messages of level
+ * {@code Level.INFO} or higher end up in the Vespa log, and all messages may be sent there by means of log-control.
+ *
+ * @author jonmv
+ */
+public class InternalStepRunner implements StepRunner {
+
+ static final Duration endpointTimeout = Duration.ofMinutes(15);
+ static final Duration installationTimeout = Duration.ofMinutes(150);
+
+ // TODO jvenstad: Move this tester logic to the application controller, perhaps?
+ public static ApplicationId testerOf(ApplicationId id) {
+ return ApplicationId.from(id.tenant().value(),
+ id.application().value(),
+ id.instance().value() + "-t");
+ }
+
+ private final Controller controller;
+ private final Testers testers;
+ private final ThreadLocal<ByteArrayLogger> logger = new ThreadLocal<>();
+
+ public InternalStepRunner(Controller controller, Testers testers) {
+ this.controller = controller;
+ this.testers = testers;
+ }
+
+ @Override
+ public Status run(LockedStep step, RunId id) {
+ try {
+ logger.set(ByteArrayLogger.of(id.application(), id.type(), step.get()));
+ switch (step.get()) {
+ case deployInitialReal: return deployInitialReal(id);
+ case installInitialReal: return installInitialReal(id);
+ case deployReal: return deployReal(id);
+ case deployTester: return deployTester(id);
+ case installReal: return installReal(id);
+ case installTester: return installTester(id);
+ case startTests: return startTests(id);
+ case endTests: return endTests(id);
+ case deactivateReal: return deactivateReal(id);
+ case deactivateTester: return deactivateTester(id);
+ case report: return report(id);
+ default: throw new AssertionError("Unknown step '" + step + "'!");
+ }
+ }
+ catch (RuntimeException e) {
+ logger.get().log(INFO, "Unexpected exception: " + Exceptions.toMessageString(e));
+ return failed;
+ }
+ finally {
+ controller.jobController().log(id, step.get(), logger.get().getLog());
+ logger.remove();
+ }
+ }
+
+ private Status deployInitialReal(RunId id) {
+ JobStatus.JobRun triggering = triggering(id.application(), id.type());
+ logger.get().log("Deploying platform version " +
+ triggering.sourcePlatform().orElse(triggering.platform()) +
+ " and application version " +
+ triggering.sourceApplication().orElse(triggering.application()) + " ...");
+ return deployReal(id, true);
+ }
+
+ private Status deployReal(RunId id) {
+ JobStatus.JobRun triggering = triggering(id.application(), id.type());
+ logger.get().log("Deploying platform version " + triggering.platform() +
+ " and application version " + triggering.application() + " ...");
+ return deployReal(id, false);
+ }
+
+ private Status deployReal(RunId id, boolean setTheStage) {
+ return deploy(id.application(),
+ id.type(),
+ () -> controller.applications().deploy(id.application(),
+ zone(id.type()),
+ Optional.empty(),
+ new DeployOptions(false,
+ Optional.empty(),
+ false,
+ setTheStage)));
+ }
+
+ private Status deployTester(RunId id) {
+ // TODO jvenstad: Consider deploying old version of tester for initial staging feeding?
+ logger.get().log("Deploying the tester container ...");
+ return deploy(testerOf(id.application()),
+ id.type(),
+ () -> controller.applications().deployTester(testerOf(id.application()),
+ testerPackage(id),
+ zone(id.type()),
+ new DeployOptions(true,
+ Optional.of(controller.systemVersion()),
+ false,
+ false)));
+ }
+
+ private Status deploy(ApplicationId id, JobType type, Supplier<ActivateResult> deployment) {
+ try {
+ PrepareResponse prepareResponse = deployment.get().prepareResponse();
+ if ( ! prepareResponse.configChangeActions.refeedActions.stream().allMatch(action -> action.allowed)) {
+ logger.get().log("Deploy failed due to non-compatible changes that require re-feed. " +
+ "Your options are: \n" +
+ "1. Revert the incompatible changes.\n" +
+ "2. If you think it is safe in your case, you can override this validation, see\n" +
+ " http://docs.vespa.ai/documentation/reference/validation-overrides.html\n" +
+ "3. Deploy as a new application under a different name.\n" +
+ "Illegal actions:\n" +
+ prepareResponse.configChangeActions.refeedActions.stream()
+ .filter(action -> ! action.allowed)
+ .flatMap(action -> action.messages.stream())
+ .collect(Collectors.joining("\n")) + "\n" +
+ "Details:\n" +
+ prepareResponse.log.stream()
+ .map(entry -> entry.message)
+ .collect(Collectors.joining("\n")));
+ return failed;
+ }
+
+ if (prepareResponse.configChangeActions.restartActions.isEmpty())
+ logger.get().log("No services requiring restart.");
+ else
+ prepareResponse.configChangeActions.restartActions.stream()
+ .flatMap(action -> action.services.stream())
+ .map(service -> service.hostName)
+ .sorted().distinct()
+ .map(Hostname::new)
+ .forEach(hostname -> {
+ controller.applications().restart(new DeploymentId(id, zone(type)), Optional.of(hostname));
+ logger.get().log("Restarting services on host " + hostname.id() + ".");
+ });
+ logger.get().log("Deployment successful.");
+ return succeeded;
+ }
+ catch (ConfigServerException e) {
+ if ( e.getErrorCode() == OUT_OF_CAPACITY && type.isTest()
+ || e.getErrorCode() == ACTIVATION_CONFLICT
+ || e.getErrorCode() == APPLICATION_LOCK_FAILURE) {
+ logger.get().log("Will retry, because of '" + e.getErrorCode() + "' deploying:\n" + e.getMessage());
+ return unfinished;
+ }
+ throw e;
+ }
+ }
+
+ private Status installInitialReal(RunId id) {
+ return installReal(id.application(), id.type(), true);
+ }
+
+ private Status installReal(RunId id) {
+ return installReal(id.application(), id.type(), false);
+ }
+
+ private Status installReal(ApplicationId id, JobType type, boolean setTheStage) {
+ JobStatus.JobRun triggering = triggering(id, type);
+ Version platform = setTheStage ? triggering.sourcePlatform().orElse(triggering.platform()) : triggering.platform();
+ ApplicationVersion application = setTheStage ? triggering.sourceApplication().orElse(triggering.application()) : triggering.application();
+ logger.get().log("Checking installation of " + platform + " and " + application + " ...");
+
+ if (nodesConverged(id, type, platform) && servicesConverged(id, type)) {
+ logger.get().log("Installation succeeded!");
+ return succeeded;
+ }
+
+ if (timedOut(id, type, installationTimeout)) {
+ logger.get().log(INFO, "Installation failed to complete within " + installationTimeout.toMinutes() + " minutes!");
+ return failed;
+ }
+
+ logger.get().log("Installation not yet complete.");
+ return unfinished;
+ }
+
+ private Status installTester(RunId id) {
+ logger.get().log("Checking installation of tester container ...");
+
+ if (servicesConverged(testerOf(id.application()), id.type())) {
+ logger.get().log("Tester container successfully installed!");
+ return succeeded;
+ }
+
+ if (timedOut(id.application(), id.type(), installationTimeout)) {
+ logger.get().log(WARNING, "Installation of tester failed to complete within " + installationTimeout.toMinutes() + " minutes of real deployment!");
+ return failed;
+ }
+
+ logger.get().log("Installation of tester not yet complete.");
+ return unfinished;
+ }
+
+ private boolean nodesConverged(ApplicationId id, JobType type, Version target) {
+ List<Node> nodes = controller.configServer().nodeRepository().list(zone(type), id, Arrays.asList(active, reserved));
+ for (Node node : nodes)
+ // TODO jvenstad: Add ALLOWED_TO_BE_DOWN and reboot and restart generation information as well.
+ logger.get().log(String.format("%70s: %-12s%-25s%-32s%s",
+ node.hostname(),
+ node.serviceState(),
+ node.wantedVersion() + (node.currentVersion().equals(node.wantedVersion()) ? "" : " <-- " + node.currentVersion()),
+ node.restartGeneration() == node.wantedRestartGeneration() ? ""
+ : "restart pending (" + node.wantedRestartGeneration() + " <-- " + node.restartGeneration() + ")",
+ node.rebootGeneration() == node.wantedRebootGeneration() ? ""
+ : "reboot pending (" + node.wantedRebootGeneration() + " <-- " + node.rebootGeneration() + ")"));
+
+ return nodes.stream().allMatch(node -> node.currentVersion().equals(target)
+ && node.restartGeneration() == node.wantedRestartGeneration()
+ && node.rebootGeneration() == node.wantedRebootGeneration());
+ }
+
+ private boolean servicesConverged(ApplicationId id, JobType type) {
+ // TODO jvenstad: Print information for each host.
+ return controller.configServer().serviceConvergence(new DeploymentId(id, zone(type))).map(ServiceConvergence::converged).orElse(false);
+ }
+
+ private Status startTests(RunId id) {
+ logger.get().log("Attempting to find endpoints ...");
+ Map<ZoneId, List<URI>> endpoints = deploymentEndpoints(id.application());
+ logger.get().log("Found endpoints:\n" +
+ endpoints.entrySet().stream()
+ .map(zoneEndpoints -> "- " + zoneEndpoints.getKey() + ":\n" +
+ zoneEndpoints.getValue().stream()
+ .map(uri -> " |-- " + uri)
+ .collect(Collectors.joining("\n"))));
+ if ( ! endpoints.containsKey(zone(id.type()))) {
+ if (timedOut(id.application(), id.type(), endpointTimeout)) {
+ logger.get().log(WARNING, "Endpoints failed to show up within " + endpointTimeout.toMinutes() + " minutes!");
+ return failed;
+ }
+
+ logger.get().log("Endpoints for the deployment to test are not yet ready.");
+ return unfinished;
+ }
+
+ Optional<URI> testerEndpoint = testerEndpoint(id);
+ if (testerEndpoint.isPresent()) {
+ logger.get().log("Starting tests ...");
+ testers.startTests(testerEndpoint.get(),
+ Testers.Suite.of(id.type()),
+ testConfig(id.application(), zone(id.type()), controller.system(), endpoints));
+ return succeeded;
+ }
+
+ if (timedOut(id.application(), id.type(), installationTimeout)) {
+ logger.get().log(WARNING, "Endpoint for tester failed to show up within " + installationTimeout.toMinutes() + " minutes of real deployment!");
+ return failed;
+ }
+
+ logger.get().log("Endpoints of tester container not yet available.");
+ return unfinished;
+ }
+
+ private Status endTests(RunId id) {
+ URI testerEndpoint = testerEndpoint(id)
+ .orElseThrow(() -> new NoSuchElementException("Endpoint for tester vanished again before tests were complete!"));
+
+ Status status;
+ switch (testers.getStatus(testerEndpoint)) {
+ case NOT_STARTED:
+ throw new IllegalStateException("Tester reports tests not started, even though they should have!");
+ case RUNNING:
+ logger.get().log("Tests still running ...");
+ return unfinished;
+ case FAILURE:
+ logger.get().log("Tests failed.");
+ status = failed; break;
+ case ERROR:
+ logger.get().log(INFO, "Tester failed running its tests!");
+ status = failed; break;
+ case SUCCESS:
+ logger.get().log("Tests completed successfully.");
+ status = succeeded; break;
+ default:
+ throw new AssertionError("Unknown status!");
+ }
+ logger.get().log(new String(testers.getLogs(testerEndpoint))); // TODO jvenstad: Replace with something less hopeless!
+ return status;
+ }
+
+ private Status deactivateReal(RunId id) {
+ logger.get().log("Deactivating deployment of " + id.application() + " in " + zone(id.type()) + " ...");
+ return deactivate(id.application(), id.type());
+ }
+
+ private Status deactivateTester(RunId id) {
+ logger.get().log("Deactivating tester of " + id.application() + " in " + zone(id.type()) + " ...");
+ return deactivate(testerOf(id.application()), id.type());
+ }
+
+ private Status deactivate(ApplicationId id, JobType type) {
+ try {
+ controller.configServer().deactivate(new DeploymentId(id, zone(type)));
+ }
+ catch (NoInstanceException e) { }
+ return succeeded;
+ }
+
+ private Status report(RunId id) {
+ controller.jobController().active(id).ifPresent(run -> controller.applications().deploymentTrigger().notifyOfCompletion(report(run)));
+ return succeeded;
+ }
+
+ /** Returns the real application with the given id. */
+ private Application application(ApplicationId id) {
+ return controller.applications().require(id);
+ }
+
+ /** Returns the zone of the given job type. */
+ private ZoneId zone(JobType type) {
+ return type.zone(controller.system()).get();
+ }
+
+ /** Returns the triggering of the currently running job, i.e., this job. */
+ private JobStatus.JobRun triggering(ApplicationId id, JobType type) {
+ return application(id).deploymentJobs().statusOf(type).get().lastTriggered().get();
+ }
+
+ /** Returns whether the time elapsed since the last real deployment in the given zone is more than the given timeout. */
+ private boolean timedOut(ApplicationId id, JobType type, Duration timeout) {
+ return application(id).deployments().get(zone(type)).at().isBefore(controller.clock().instant().minus(timeout));
+ }
+
+ /** Returns a generated job report for the given run. */
+ private DeploymentJobs.JobReport report(RunStatus run) {
+ return new DeploymentJobs.JobReport(run.id().application(),
+ run.id().type(),
+ Long.MAX_VALUE,
+ run.id().number(),
+ Optional.empty(),
+ run.hasFailed() ? Optional.of(DeploymentJobs.JobError.unknown) : Optional.empty());
+ }
+
+ /** Returns the application package for the tester application, assembled from a generated config, fat-jar and services.xml. */
+ private ApplicationPackage testerPackage(RunId id) {
+ ApplicationVersion version = application(id.application()).deploymentJobs()
+ .statusOf(id.type()).get()
+ .lastTriggered().get()
+ .application();
+
+ byte[] testPackage = controller.applications().artifacts().getTesterPackage(testerOf(id.application()), version.id());
+ byte[] servicesXml = servicesXml(controller.system());
+
+ try (ZipBuilder zipBuilder = new ZipBuilder(testPackage.length + servicesXml.length + 1000)) {
+ zipBuilder.add(testPackage);
+ zipBuilder.add("services.xml", servicesXml);
+ return new ApplicationPackage(zipBuilder.toByteArray());
+ }
+ }
+
+ /** Returns all endpoints for all current deployments of the given real application. */
+ private Map<ZoneId, List<URI>> deploymentEndpoints(ApplicationId id) {
+ ImmutableMap.Builder<ZoneId, List<URI>> deployments = ImmutableMap.builder();
+ application(id).deployments().keySet()
+ .forEach(zone -> controller.applications().getDeploymentEndpoints(new DeploymentId(id, zone))
+ .ifPresent(endpoints -> deployments.put(zone, endpoints)));
+ return deployments.build();
+ }
+
+ /** Returns a URI of the tester endpoint retrieved from the routing generator, provided it matches an expected form. */
+ private Optional<URI> testerEndpoint(RunId id) {
+ ApplicationId tester = testerOf(id.application());
+ return controller.applications().getDeploymentEndpoints(new DeploymentId(tester, zone(id.type())))
+ .flatMap(uris -> uris.stream()
+ .filter(uri -> uri.getHost().contains(String.format("%s--%s--%s.",
+ tester.instance().value(),
+ tester.application().value(),
+ tester.tenant().value())))
+ .findAny());
+ }
+
+ /** Returns the generated services.xml content for the tester application. */
+ static byte[] servicesXml(SystemName systemName) {
+ String domain = systemName == SystemName.main ? "vespa.vespa" : "vespa.vespa.cd";
+
+ String servicesXml = "<?xml version='1.0' encoding='UTF-8'?>\n" +
+ "<services xmlns:deploy='vespa' version='1.0'>\n" +
+ " <container version='1.0' id='default'>\n" +
+ "\n" +
+ " <component id=\"com.yahoo.vespa.hosted.testrunner.TestRunner\" bundle=\"vepsa-testrunner-components\">\n" +
+ " <config name=\"com.yahoo.vespa.hosted.testrunner.test-runner\">\n" +
+ " <artifactsPath>artifacts</artifactsPath>\n" +
+ " </config>\n" +
+ " </component>\n" +
+ "\n" +
+ " <handler id=\"com.yahoo.vespa.hosted.testrunner.TestRunnerHandler\" bundle=\"vespa-testrunner-components\">\n" +
+ " <binding>http://*/tester/v1/*</binding>\n" +
+ " </handler>\n" +
+ "\n" +
+ " <http>\n" +
+ " <filtering>\n" +
+ " <request-chain id=\"testrunner-api\">\n" +
+ " <filter id='authz-filter' class='com.yahoo.jdisc.http.filter.security.athenz.AthenzAuthorizationFilter' bundle=\"jdisc-security-filters\">\n" +
+ " <config name=\"jdisc.http.filter.security.athenz.athenz-authorization-filter\">\n" +
+ " <credentialsToVerify>TOKEN_ONLY</credentialsToVerify>\n" +
+ " <roleTokenHeaderName>Yahoo-Role-Auth</roleTokenHeaderName>\n" +
+ " </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" +
+ " <action>deploy</action>\n" +
+ " </config>\n" +
+ " </component>\n" +
+ " </filter>\n" +
+ " </request-chain>\n" +
+ " </filtering>\n" +
+ " </http>\n" +
+ "\n" +
+ " <nodes count=\"1\" flavor=\"d-2-8-50\" />\n" +
+ " </container>\n" +
+ "</services>\n";
+
+ return servicesXml.getBytes();
+ }
+
+ /** Returns the config for the tests to run for the given job. */
+ private static byte[] testConfig(ApplicationId id, ZoneId testerZone, SystemName system, Map<ZoneId, List<URI>> deployments) {
+ Slime slime = new Slime();
+ Cursor root = slime.setObject();
+ root.setString("application", id.serializedForm());
+ root.setString("zone", testerZone.value());
+ root.setString("system", system.name());
+ Cursor endpointsObject = root.setObject("endpoints");
+ deployments.forEach((zone, endpoints) -> {
+ Cursor endpointArray = endpointsObject.setArray(zone.value());
+ for (URI endpoint : endpoints)
+ endpointArray.addString(endpoint.toString());
+ });
+ try {
+ return SlimeUtils.toJsonBytes(slime);
+ }
+ catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ /** Logger which logs all records to a private byte array, as well as to its parent. */
+ static class ByteArrayLogger extends Logger {
+
+ private static final Logger parent = Logger.getLogger(InternalStepRunner.class.getName());
+ private static final SimpleDateFormat timestampFormat = new SimpleDateFormat("[HH:mm:ss.SSS] ");
+ static { timestampFormat.setTimeZone(TimeZone.getTimeZone("UTC")); }
+
+ private final ByteArrayOutputStream bytes;
+ private final PrintStream out;
+
+ private ByteArrayLogger(Logger parent, String suffix) {
+ super(parent.getName() + suffix, null);
+ setParent(parent);
+
+ bytes = new ByteArrayOutputStream();
+ out = new PrintStream(bytes);
+ }
+
+ static ByteArrayLogger of(ApplicationId id, JobType type, Step step) {
+ return new ByteArrayLogger(parent, String.format(".%s.%s.%s", id.serializedForm(), type.jobName(), step));
+ }
+
+ @Override
+ public void log(LogRecord record) {
+ // TODO jvenstad: Store log records in a serialised format.
+ String timestamp = timestampFormat.format(new Date(record.getMillis()));
+ for (String line : record.getMessage().split("\n"))
+ out.println(timestamp + ": " + line);
+
+ getParent().log(record);
+ }
+
+ public void log(String message) {
+ log(DEBUG, message);
+ }
+
+ @Override
+ public boolean isLoggable(Level __) {
+ return true;
+ }
+
+ public byte[] getLog() {
+ out.flush();
+ return bytes.toByteArray();
+ }
+
+ }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java
new file mode 100644
index 00000000000..ba79364fa34
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java
@@ -0,0 +1,306 @@
+package com.yahoo.vespa.hosted.controller.deployment;
+
+import com.google.common.collect.ImmutableMap;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.vespa.curator.Lock;
+import com.yahoo.vespa.hosted.controller.Application;
+import com.yahoo.vespa.hosted.controller.Controller;
+import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
+import com.yahoo.vespa.hosted.controller.api.integration.LogStore;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.NoInstanceException;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
+import com.yahoo.vespa.hosted.controller.application.ApplicationVersion;
+import com.yahoo.vespa.hosted.controller.application.DeploymentJobs;
+import com.yahoo.vespa.hosted.controller.application.JobStatus;
+import com.yahoo.vespa.hosted.controller.application.SourceRevision;
+import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.function.UnaryOperator;
+import java.util.stream.Stream;
+
+import static com.google.common.collect.ImmutableList.copyOf;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.deactivateTester;
+import static com.yahoo.vespa.hosted.controller.deployment.InternalStepRunner.testerOf;
+
+/**
+ * A singleton owned by the controller, which contains the state and methods for controlling deployment jobs.
+ *
+ * Keys are the {@link ApplicationId} of the real application, for which the deployment job is run, and the
+ * {@link JobType} of the real deployment to test.
+ * Although the deployment jobs are themselves applications, their IDs are not to be referenced.
+ *
+ * Jobs consist of sets of {@link Step}s, defined in {@link JobProfile}s.
+ * Each run is represented by a {@link RunStatus}, which holds the status of each step of the run, as well as
+ * some other meta data.
+ *
+ * @author jonmv
+ */
+public class JobController {
+
+ private final Controller controller;
+ private final CuratorDb curator;
+ private final LogStore logs;
+
+ public JobController(Controller controller, LogStore logStore) {
+ this.controller = controller;
+ this.curator = controller.curator();
+ this.logs = logStore;
+
+ }
+
+ /** Rewrite all job data with the newest format. */
+ public void updateStorage() {
+ for (ApplicationId id : applications())
+ for (JobType type : jobs(id)) {
+ locked(id, type, runs -> {
+ curator.readLastRun(id, type).ifPresent(curator::writeLastRun);
+ });
+ }
+ }
+
+ /** Returns the details currently logged for the given run, if known. */
+ public Optional<RunDetails> details(RunId id) {
+ RunStatus run = runs(id.application(), id.type()).get(id);
+ if (run == null)
+ return Optional.empty();
+
+ Map<Step, byte[]> details = new HashMap<>();
+ for (Step step : run.steps().keySet()) {
+ byte[] log = logs.get(id, step.name());
+ if (log.length > 0)
+ details.put(step, log);
+ }
+ return Optional.of(new RunDetails(details));
+ }
+
+ /** Appends the given log bytes to the currently stored bytes for the given run and step. */
+ public void log(RunId id, Step step, byte[] log) {
+ try (Lock __ = curator.lock(id.application(), id.type())) {
+ logs.append(id, step.name(), log);
+ }
+ }
+
+ // TODO jvenstad: Remove this, and let the DeploymentTrigger trigger directly with the correct BuildService.
+ /** Returns whether the given application has registered with this build service. */
+ public boolean builds(ApplicationId id) {
+ return controller.applications().get(id)
+ .map(application -> application.deploymentJobs().builtInternally())
+ .orElse(false);
+ }
+
+ /** Returns a list of all application which have registered. */
+ public List<ApplicationId> applications() {
+ return copyOf(controller.applications().asList().stream()
+ .filter(application -> application.deploymentJobs().builtInternally())
+ .map(Application::id)
+ .iterator());
+ }
+
+ /** Returns all job types which have been run for the given application. */
+ public List<JobType> jobs(ApplicationId id) {
+ return copyOf(Stream.of(JobType.values())
+ .filter(type -> last(id, type).isPresent())
+ .iterator());
+ }
+
+ /** Returns an immutable map of all known runs for the given application and job type. */
+ public Map<RunId, RunStatus> runs(ApplicationId id, JobType type) {
+ Map<RunId, RunStatus> runs = curator.readHistoricRuns(id, type);
+ last(id, type).ifPresent(run -> runs.putIfAbsent(run.id(), run));
+ return ImmutableMap.copyOf(runs);
+ }
+
+ /** Returns the run with the given id, if it exists. */
+ public Optional<RunStatus> run(RunId id) {
+ return runs(id.application(), id.type()).values().stream()
+ .filter(run -> run.id().equals(id))
+ .findAny();
+ }
+
+ /** Returns the last run of the given type, for the given application, if one has been run. */
+ public Optional<RunStatus> last(ApplicationId id, JobType type) {
+ return curator.readLastRun(id, type);
+ }
+
+ /** Returns the run with the given id, provided it is still active. */
+ public Optional<RunStatus> active(RunId id) {
+ return last(id.application(), id.type())
+ .filter(run -> ! run.hasEnded())
+ .filter(run -> run.id().equals(id));
+ }
+
+ /** Returns a list of all active runs. */
+ public List<RunStatus> active() {
+ return copyOf(applications().stream()
+ .flatMap(id -> Stream.of(JobType.values())
+ .map(type -> last(id, type))
+ .filter(Optional::isPresent).map(Optional::get)
+ .filter(run -> ! run.hasEnded()))
+ .iterator());
+ }
+
+ /** Changes the status of the given step, for the given run, provided it is still active. */
+ public void update(RunId id, Step.Status status, LockedStep step) {
+ locked(id, run -> run.with(status, step));
+ }
+
+ /** Changes the status of the given run to inactive, and stores it as a historic run. */
+ public void finish(RunId id) {
+ locked(id, run -> { // Store the modified run after it has been written to the collection, in case the latter fails.
+ RunStatus finishedRun = run.finished(controller.clock().instant());
+ locked(id.application(), id.type(), runs -> runs.put(run.id(), finishedRun));
+ return finishedRun;
+ });
+ }
+
+ /** Marks the given run as aborted; no further normal steps will run, but run-always steps will try to succeed. */
+ public void abort(RunId id) {
+ locked(id, run -> run.aborted());
+ }
+
+ /** Registers the given application, such that it may have deployment jobs run here. */
+ public void register(ApplicationId id) {
+ controller.applications().lockIfPresent(id, application ->
+ controller.applications().store(application.withBuiltInternally(true)));
+ }
+
+ /** Accepts and stores a new application package and test jar pair under a generated application version key. */
+ public ApplicationVersion submit(ApplicationId id, SourceRevision revision,
+ byte[] applicationPackage, byte[] applicationTestPackage) {
+ AtomicReference<ApplicationVersion> version = new AtomicReference<>();
+ controller.applications().lockOrThrow(id, application -> {
+ controller.applications().store(application.withBuiltInternally(true));
+
+ long run = nextBuild(id);
+ version.set(ApplicationVersion.from(revision, run));
+
+ // TODO smorgrav: Store the pair.
+// controller.applications().artifacts().putApplicationPackage(id, version.toString(), applicationPackage);
+// controller.applications().artifacts().putTesterPackage(
+// InternalStepRunner.testerOf(id), version.toString(), applicationTestPackage);
+//
+// notifyOfNewSubmission(id, revision, run);
+ });
+ return version.get();
+ }
+
+ /** Orders a run of the given type, or throws an IllegalStateException if that job type is already running. */
+ public void start(ApplicationId id, JobType type) {
+ controller.applications().lockIfPresent(id, application -> {
+ if ( ! application.get().deploymentJobs().builtInternally())
+ throw new IllegalArgumentException(id + " is not built here!");
+
+ locked(id, type, __ -> {
+ Optional<RunStatus> last = last(id, type);
+ if (last.flatMap(run -> active(run.id())).isPresent())
+ throw new IllegalStateException("Can not start " + type + " for " + id + "; it is already running!");
+
+ RunId newId = new RunId(id, type, last.map(run -> run.id().number()).orElse(0L) + 1);
+ curator.writeLastRun(RunStatus.initial(newId, controller.clock().instant()));
+ });
+ });
+ }
+
+ /** Unregisters the given application and makes all associated data eligible for garbage collection. */
+ public void unregister(ApplicationId id) {
+ controller.applications().lockIfPresent(id, application -> {
+ controller.applications().store(application.withBuiltInternally(false));
+ jobs(id).forEach(type -> {
+ try (Lock __ = curator.lock(id, type)) {
+ last(id, type).ifPresent(last -> active(last.id()).ifPresent(active -> abort(active.id())));
+ }
+ });
+ });
+ }
+
+ /** Deletes stale data and tester deployments for applications which are unknown, or no longer built internally. */
+ public void collectGarbage() {
+ Set<ApplicationId> applicationsToBuild = new HashSet<>(applications());
+ curator.applicationsWithJobs().stream()
+ .filter(id -> ! applicationsToBuild.contains(id))
+ .forEach(id -> {
+ try {
+ for (JobType type : jobs(id))
+ locked(id, type, deactivateTester, __ -> {
+ try (Lock ___ = curator.lock(id, type)) {
+ deactivateTester(id, type);
+ curator.deleteJobData(id, type);
+ }
+ });
+ }
+ catch (TimeoutException e) {
+ return; // Don't remove the data if we couldn't deactivate all testers.
+ }
+ curator.deleteJobData(id);
+ });
+ }
+
+ // TODO jvenstad: Urgh, clean this up somehow?
+ public void deactivateTester(ApplicationId id, JobType type) {
+ try {
+ controller.configServer().deactivate(new DeploymentId(testerOf(id), type.zone(controller.system()).get()));
+ }
+ catch (NoInstanceException ignored) {
+ // ok; already gone
+ }
+ }
+
+ // TODO jvenstad: Find a more appropriate way of doing this, at least when this is the only build service.
+ private long nextBuild(ApplicationId id) {
+ return 1 + controller.applications().require(id).deploymentJobs()
+ .statusOf(JobType.component)
+ .flatMap(JobStatus::lastCompleted)
+ .map(JobStatus.JobRun::id)
+ .orElse(0L);
+ }
+
+ // TODO jvenstad: Find a more appropriate way of doing this when this is the only build service.
+ private void notifyOfNewSubmission(ApplicationId id, SourceRevision revision, long number) {
+ DeploymentJobs.JobReport report = new DeploymentJobs.JobReport(id,
+ JobType.component,
+ 1,
+ number,
+ Optional.of(revision),
+ Optional.empty());
+ controller.applications().deploymentTrigger().notifyOfCompletion(report);
+ }
+
+ /** Locks and modifies the list of historic runs for the given application and job type. */
+ private void locked(ApplicationId id, JobType type, Consumer<Map<RunId, RunStatus>> modifications) {
+ try (Lock __ = curator.lock(id, type)) {
+ Map<RunId, RunStatus> runs = curator.readHistoricRuns(id, type);
+ modifications.accept(runs);
+ curator.writeHistoricRuns(id, type, runs.values());
+ }
+ }
+
+ /** Locks and modifies the run with the given id, provided it is still active. */
+ private void locked(RunId id, UnaryOperator<RunStatus> modifications) {
+ try (Lock __ = curator.lock(id.application(), id.type())) {
+ RunStatus run = active(id).orElseThrow(() -> new IllegalArgumentException(id + " is not an active run!"));
+ run = modifications.apply(run);
+ curator.writeLastRun(run);
+ }
+ }
+
+ /** Locks the given step and checks none of its prerequisites are running, then performs the given actions. */
+ public void locked(ApplicationId id, JobType type, Step step, Consumer<LockedStep> action) throws TimeoutException {
+ try (Lock lock = curator.lock(id, type, step)) {
+ for (Step prerequisite : step.prerequisites()) // Check that no prerequisite is still running.
+ try (Lock __ = curator.lock(id, type, prerequisite)) { ; }
+
+ action.accept(new LockedStep(lock, step));
+ }
+ }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobProfile.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobProfile.java
new file mode 100644
index 00000000000..0cad9e98d5d
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobProfile.java
@@ -0,0 +1,75 @@
+package com.yahoo.vespa.hosted.controller.deployment;
+
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+import static com.yahoo.vespa.hosted.controller.deployment.Step.*;
+
+/**
+ * Static profiles defining the {@link Step}s of a deployment job.
+ *
+ * @author jonmv
+ */
+public enum JobProfile {
+
+ // TODO jvenstad: runTests is not a run-always step, as it really means: check if tests are done, and store whatever is ready.
+ systemTest(EnumSet.of(deployReal,
+ installReal,
+ deployTester,
+ installTester,
+ startTests,
+ endTests),
+ EnumSet.of(deactivateTester,
+ deactivateReal,
+ report)),
+
+ stagingTest(EnumSet.of(deployInitialReal,
+ installInitialReal,
+ deployReal,
+ installReal,
+ deployTester,
+ installTester,
+ startTests,
+ endTests),
+ EnumSet.of(deactivateTester,
+ deactivateReal,
+ report)),
+
+ production(EnumSet.of(deployReal,
+ installReal,
+ deployTester,
+ installTester,
+ startTests,
+ endTests),
+ EnumSet.of(deactivateTester,
+ report));
+
+
+ private final Set<Step> steps;
+ private final Set<Step> alwaysRun;
+
+ JobProfile(Set<Step> runWhileSuccess, Set<Step> alwaysRun) {
+ runWhileSuccess.addAll(alwaysRun);
+ this.steps = Collections.unmodifiableSet(runWhileSuccess);
+ this.alwaysRun = Collections.unmodifiableSet(alwaysRun);
+ }
+
+ public static JobProfile of(JobType type) {
+ switch (type.environment()) {
+ case test: return systemTest;
+ case staging: return stagingTest;
+ case prod: return production;
+ default: throw new AssertionError("Unexpected environment '" + type.environment() + "'!");
+ }
+ }
+
+ /** Returns all steps in this profile, the default for which is to run only when all prerequisites are successes. */
+ public Set<Step> steps() { return steps; }
+
+ /** Returns the set of steps that should always be run, regardless of outcome. */
+ public Set<Step> alwaysRun() { return alwaysRun; }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/LockedStep.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/LockedStep.java
new file mode 100644
index 00000000000..1a35169488a
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/LockedStep.java
@@ -0,0 +1,11 @@
+package com.yahoo.vespa.hosted.controller.deployment;
+
+import com.yahoo.vespa.curator.Lock;
+
+public class LockedStep {
+
+ private final Step step;
+ LockedStep(Lock lock, Step step) { this.step = step; }
+ public Step get() { return step; }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunDetails.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunDetails.java
new file mode 100644
index 00000000000..60d7a6a8f04
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunDetails.java
@@ -0,0 +1,26 @@
+package com.yahoo.vespa.hosted.controller.deployment;
+
+import com.google.common.collect.ImmutableMap;
+
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Contains details about a deployment job run.
+ *
+ * @author jonmv
+ */
+public class RunDetails {
+
+ // TODO jvenstad: Store a serialised structure, rather than a flat text.
+ private final Map<Step, byte[]> logs;
+
+ public RunDetails(Map<Step, byte[]> logs) {
+ this.logs = ImmutableMap.copyOf(logs);
+ }
+
+ public Optional<byte[]> get(Step step) {
+ return Optional.ofNullable(logs.get(step));
+ }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunResult.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunResult.java
new file mode 100644
index 00000000000..aaf43097908
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunResult.java
@@ -0,0 +1,31 @@
+package com.yahoo.vespa.hosted.controller.deployment;
+
+/**
+ * Outcomes of jobs run by an {@link InternalBuildService}.
+ *
+ * @author jonmv
+ */
+public enum RunResult {
+
+ /** Deployment of the real application was rejected due to missing capacity. */
+ outOfCapacity,
+
+ /** Deployment of the real application was rejected. */
+ deploymentFailed,
+
+ /** Installation of the real application timed out. */
+ installationFailed,
+
+ /** Real application was deployed, but the tester application was not. */
+ testError,
+
+ /** Real application was deployed, but the tests failed. */
+ testFailure,
+
+ /** Deployment and tests completed with great success! */
+ success,
+
+ /** Job completed abnormally, due to user intervention or unexpected system error. */
+ aborted
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunStatus.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunStatus.java
new file mode 100644
index 00000000000..1fd32524c88
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunStatus.java
@@ -0,0 +1,174 @@
+package com.yahoo.vespa.hosted.controller.deployment;
+
+import com.google.common.collect.ImmutableList;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
+
+import java.time.Instant;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.failed;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.succeeded;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.unfinished;
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Immutable class containing status information for a deployment job run by an {@link InternalBuildService}.
+ *
+ * @author jonmv
+ */
+public class RunStatus {
+
+ private final RunId id;
+ private final Map<Step, Step.Status> steps;
+ private final Instant start;
+ private final Optional<Instant> end;
+ private final boolean aborted;
+ // TODO jvenstad: Add a Versions object and a reason String. Requires shortcutting of triggering of these runs.
+
+ // For deserialisation only -- do not use!
+ public RunStatus(RunId id, Map<Step, Step.Status> steps, Instant start, Optional<Instant> end, boolean aborted) {
+ this.id = id;
+ this.steps = Collections.unmodifiableMap(new EnumMap<>(steps));
+ this.start = start;
+ this.end = end;
+ this.aborted = aborted;
+ }
+
+ public static RunStatus initial(RunId id, Instant now) {
+ EnumMap<Step, Step.Status> steps = new EnumMap<>(Step.class);
+ JobProfile.of(id.type()).steps().forEach(step -> steps.put(step, unfinished));
+ return new RunStatus(id, steps, requireNonNull(now), Optional.empty(), false);
+ }
+
+ public RunStatus with(Step.Status status, LockedStep step) {
+ if (hasEnded())
+ throw new AssertionError("This step ended at " + end.get() + " -- it can't be further modified!");
+
+ EnumMap<Step, Step.Status> steps = new EnumMap<>(this.steps);
+ steps.put(step.get(), requireNonNull(status));
+ return new RunStatus(id, steps, start, end, aborted);
+ }
+
+ public RunStatus finished(Instant now) {
+ if (hasEnded())
+ throw new AssertionError("This step ended at " + end.get() + " -- it can't be ended again!");
+
+ return new RunStatus(id, new EnumMap<>(steps), start, Optional.of(now), aborted);
+ }
+
+ public RunStatus aborted() {
+ if (hasEnded())
+ throw new AssertionError("This step ended at " + end.get() + " -- it can't be aborted now!");
+
+ return new RunStatus(id, new EnumMap<>(steps), start, end, true);
+ }
+
+ /** Returns the id of this run. */
+ public RunId id() {
+ return id;
+ }
+
+ /** Returns an unmodifiable view of the status of all steps in this run.
+ * TODO maybe reflect in the signature that the map is a EnumMap or at least behaves as a sorted map?
+ * */
+ public Map<Step, Step.Status> steps() {
+ return steps;
+ }
+
+ /** Returns the final result of this run, if it has ended. */
+ public Optional<RunResult> result() {
+
+ // No result of not finished yet
+ if (!hasEnded()) return Optional.empty();
+
+ // If any steps has failed - then we need to figure out what - for now return fixed error result
+ if (hasFailed()) return Optional.of(RunResult.testError);
+
+ return Optional.of(RunResult.success);
+ }
+
+ /** Returns the instant at which this run began. */
+ public Instant start() {
+ return start;
+ }
+
+ /** Returns the instant at which this run ended, if it has. */
+ public Optional<Instant> end() {
+ return end;
+ }
+
+ /** Returns whether the run has failed, and should switch to its run-always steps. */
+ public boolean hasFailed() {
+ return aborted || steps.values().contains(failed);
+ }
+
+ /** Returns whether the run has been forcefully aborted. */
+ public boolean isAborted() {
+ return aborted;
+ }
+
+ /** Returns whether the run has ended, i.e., has become inactive, and can no longer be updated. */
+ public boolean hasEnded() {
+ return end.isPresent();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if ( ! (o instanceof RunStatus)) return false;
+
+ RunStatus status = (RunStatus) o;
+
+ return id.equals(status.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "RunStatus{" +
+ "id=" + id +
+ ", start=" + start +
+ ", end=" + end +
+ ", aborted=" + aborted +
+ ", steps=" + steps +
+ '}';
+ }
+
+ /** Returns the list of steps to run for this job right now, depending on whether the job has failed. */
+ public List<Step> readySteps() {
+ return hasFailed() ? forcedSteps() : normalSteps();
+ }
+
+ /** Returns the list of unfinished steps whose prerequisites have all succeeded. */
+ private List<Step> normalSteps() {
+ return ImmutableList.copyOf(steps.entrySet().stream()
+ .filter(entry -> entry.getValue() == unfinished
+ && entry.getKey().prerequisites().stream()
+ .allMatch(step -> steps.get(step) == null
+ || steps.get(step) == succeeded))
+ .map(Map.Entry::getKey)
+ .iterator());
+ }
+
+ /** Returns the list of not-yet-succeeded run-always steps whose run-always prerequisites have all succeeded. */
+ private List<Step> forcedSteps() {
+ return ImmutableList.copyOf(steps.entrySet().stream()
+ .filter(entry -> entry.getValue() != succeeded
+ && JobProfile.of(id.type()).alwaysRun().contains(entry.getKey())
+ && entry.getKey().prerequisites().stream()
+ .filter(JobProfile.of(id.type()).alwaysRun()::contains)
+ .allMatch(step -> steps.get(step) == null
+ || steps.get(step) == succeeded))
+ .map(Map.Entry::getKey)
+ .iterator());
+ }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Step.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Step.java
new file mode 100644
index 00000000000..e1e2281c5ea
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Step.java
@@ -0,0 +1,86 @@
+package com.yahoo.vespa.hosted.controller.deployment;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/**
+ * Steps that make up a deployment job. See {@link JobProfile} for preset profiles.
+ *
+ * Each step lists its prerequisites; this serves two purposes:
+ *
+ * 1. A step may only run after its prerequisites, so these define a topological order in which
+ * the steps can be run. Since a job profile may list only a subset of the existing steps,
+ * only the prerequisites of a step which are included in a run's profile will be considered.
+ * Under normal circumstances, a step will run only after each of its prerequisites have succeeded.
+ * When a run has failed, however, each of the always-run steps of the run's profile will be run,
+ * again in a topological order, and again requiring success of all their always-run prerequisites.
+ *
+ * 2. A step will never run concurrently with its prerequisites. This is to ensure, e.g., that relevant
+ * information from a failed run is stored, and that deployment does not occur after deactivation.
+ *
+ * @see JobController
+ * @author jonmv
+ */
+public enum Step {
+
+ /** Download and deploy the initial real application, for staging tests. */
+ deployInitialReal,
+
+ /** See that the real application has had its nodes converge to the initial state. */
+ installInitialReal(deployInitialReal),
+
+ /** Download and deploy real application, restarting services if required. */
+ deployReal(installInitialReal),
+
+ /** See that real application has had its nodes converge to the wanted version and generation. */
+ installReal(deployReal),
+
+ /** Download test-jar and assemble and deploy tester application. */
+ deployTester,
+
+ /** See that tester is done deploying, and is ready to serve. */
+ installTester(deployTester),
+
+ /** Ask the tester to run its tests. */
+ startTests(installReal, installTester),
+
+ /** See that the tests are done running. */
+ endTests(startTests),
+
+ /** Delete the real application -- used for test deployments. */
+ deactivateReal(deployInitialReal, deployReal, endTests),
+
+ /** Deactivate the tester. */
+ deactivateTester(deployTester, endTests),
+
+ /** Report completion to the deployment orchestration machinery. */
+ report(deactivateReal, deactivateTester);
+
+
+ private final List<Step> prerequisites;
+
+ Step(Step... prerequisites) {
+ this.prerequisites = ImmutableList.copyOf(prerequisites);
+ }
+
+ public List<Step> prerequisites() { return prerequisites; }
+
+ public static Step last() {
+ return report;
+ }
+
+ public enum Status {
+
+ /** Step still has unsatisfied finish criteria -- it may not even have started. */
+ unfinished,
+
+ /** Step failed and subsequent steps may not start. */
+ failed,
+
+ /** Step succeeded and subsequent steps may now start. */
+ succeeded
+
+ }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/StepRunner.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/StepRunner.java
new file mode 100644
index 00000000000..cf024064cc4
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/StepRunner.java
@@ -0,0 +1,25 @@
+package com.yahoo.vespa.hosted.controller.deployment;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
+import com.yahoo.vespa.hosted.controller.deployment.LockedStep;
+import com.yahoo.vespa.hosted.controller.deployment.RunStatus;
+import com.yahoo.vespa.hosted.controller.deployment.Step;
+
+/**
+ * Advances a given job run by running the appropriate {@link Step}s, based on their current status.
+ *
+ * When an attempt is made to advance a given job, a lock for that job (application and type) is
+ * taken, and released again only when the attempt finishes. Multiple other attempts may be made in
+ * the meantime, but they should give up unless the lock is promptly acquired.
+ *
+ * @author jonmv
+ */
+public interface StepRunner {
+
+ /** Attempts to run the given locked step in the given run, and returns its new status. */
+ Step.Status run(LockedStep step, RunId id);
+
+}
+
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
new file mode 100644
index 00000000000..bf58bac177c
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Versions.java
@@ -0,0 +1,114 @@
+// 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.deployment;
+
+import com.yahoo.component.Version;
+import com.yahoo.vespa.hosted.controller.Application;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
+import com.yahoo.vespa.hosted.controller.application.ApplicationVersion;
+import com.yahoo.vespa.hosted.controller.application.Change;
+import com.yahoo.vespa.hosted.controller.application.Deployment;
+import com.yahoo.vespa.hosted.controller.application.JobStatus;
+
+import java.util.Optional;
+
+/**
+ * Source and target versions for an application.
+ *
+ * @author jvenstad
+ * @author mpolden
+ */
+public class Versions {
+
+ private final Version targetPlatform;
+ private final ApplicationVersion targetApplication;
+ private final Optional<Version> sourcePlatform;
+ private final Optional<ApplicationVersion> sourceApplication;
+
+ public Versions(Version targetPlatform, ApplicationVersion targetApplication, Optional<Version> sourcePlatform,
+ Optional<ApplicationVersion> sourceApplication) {
+ this.targetPlatform = targetPlatform;
+ this.targetApplication = targetApplication;
+ this.sourcePlatform = sourcePlatform;
+ this.sourceApplication = sourceApplication;
+ }
+
+ /** Target platform version for this */
+ public Version targetPlatform() {
+ return targetPlatform;
+ }
+
+ /** Target application version for this */
+ public ApplicationVersion targetApplication() {
+ return targetApplication;
+ }
+
+ /** Source platform version for this */
+ public Optional<Version> sourcePlatform() {
+ return sourcePlatform;
+ }
+
+ /** Source application version for this */
+ public Optional<ApplicationVersion> sourceApplication() {
+ return sourceApplication;
+ }
+
+ /** Returns whether source versions are present and match those of the given job run */
+ public boolean sourcesMatchIfPresent(JobStatus.JobRun jobRun) {
+ return (!sourcePlatform.filter(version -> !version.equals(targetPlatform)).isPresent() ||
+ sourcePlatform.equals(jobRun.sourcePlatform())) &&
+ (!sourceApplication.filter(version -> !version.equals(targetApplication)).isPresent() ||
+ sourceApplication.equals(jobRun.sourceApplication()));
+ }
+
+ public boolean targetsMatch(JobStatus.JobRun jobRun) {
+ return targetPlatform.equals(jobRun.platform()) &&
+ targetApplication.equals(jobRun.application());
+ }
+
+ @Override
+ public String toString() {
+ return String.format("platform %s%s, application %s%s",
+ sourcePlatform.filter(source -> !source.equals(targetPlatform))
+ .map(source -> source + " -> ").orElse(""),
+ targetPlatform,
+ sourceApplication.filter(source -> !source.equals(targetApplication))
+ .map(source -> source.id() + " -> ").orElse(""),
+ targetApplication.id());
+ }
+
+ /** Create versions using change contained in application */
+ public static Versions from(Application application, Version defaultPlatformVersion) {
+ return from(application.change(), application, Optional.empty(), defaultPlatformVersion);
+ }
+
+ /** Create versions using given change and application */
+ public static Versions from(Change change, Application application, Optional<Deployment> deployment,
+ Version defaultPlatformVersion) {
+ return new Versions(targetPlatform(application, change, deployment, defaultPlatformVersion),
+ targetApplication(application, change, deployment),
+ deployment.map(Deployment::version),
+ deployment.map(Deployment::applicationVersion));
+ }
+
+ private static Version targetPlatform(Application application, Change change, Optional<Deployment> deployment,
+ Version defaultVersion) {
+ return max(deployment.map(Deployment::version), change.platform())
+ .orElse(application.oldestDeployedPlatform()
+ .orElse(defaultVersion));
+ }
+
+ private static ApplicationVersion targetApplication(Application application, Change change,
+ Optional<Deployment> deployment) {
+ return max(deployment.map(Deployment::applicationVersion), change.application())
+ .orElse(application.oldestDeployedApplication()
+ .orElse(application.deploymentJobs().jobStatus().get(JobType.component)
+ .lastSuccess()
+ .get()
+ .application()));
+ }
+
+ private static <T extends Comparable<T>> Optional<T> max(Optional<T> o1, Optional<T> o2) {
+ return ! o1.isPresent() ? o2 : ! o2.isPresent() ? o1 : o1.get().compareTo(o2.get()) >= 0 ? o1 : o2;
+ }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/ZipBuilder.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/ZipBuilder.java
new file mode 100644
index 00000000000..e3ede999c11
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/ZipBuilder.java
@@ -0,0 +1,65 @@
+// 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.deployment;
+
+import org.apache.commons.io.IOUtils;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * Utility class to build zipped content by adding already zipped byte content or
+ * adding new unzipped entries.
+ *
+ * @author freva
+ */
+public class ZipBuilder implements AutoCloseable {
+
+ private final ByteArrayOutputStream byteArrayOutputStream;
+ private final ZipOutputStream zipOutputStream;
+
+ public ZipBuilder(int initialSize) {
+ byteArrayOutputStream = new ByteArrayOutputStream(initialSize);
+ zipOutputStream = new ZipOutputStream(byteArrayOutputStream);
+ }
+
+ public void add(byte[] zippedContent) {
+ try (ZipInputStream zin = new ZipInputStream(new ByteArrayInputStream(zippedContent))) {
+ for (ZipEntry entry = zin.getNextEntry(); entry != null; entry = zin.getNextEntry()) {
+ zipOutputStream.putNextEntry(entry);
+ IOUtils.copy(zin, zipOutputStream);
+ zipOutputStream.closeEntry();
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException("Failed to add zipped content", e);
+ }
+ }
+
+ public void add(String entryName, byte[] content) {
+ try {
+ zipOutputStream.putNextEntry(new ZipEntry(entryName));
+ zipOutputStream.write(content);
+ zipOutputStream.closeEntry();
+ } catch (IOException e) {
+ throw new UncheckedIOException("Failed to add entry " + entryName, e);
+ }
+ }
+
+ /** @return zipped byte array */
+ public byte[] toByteArray() {
+ return byteArrayOutputStream.toByteArray();
+ }
+
+ @Override
+ public void close() {
+ try {
+ zipOutputStream.close();
+ } catch (IOException e) {
+ throw new UncheckedIOException("Failed to close zip output stream", e);
+ }
+ }
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/statuspage/package-info.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/package-info.java
index 3f9117bf931..e8fb638bc34 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/statuspage/package-info.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/package-info.java
@@ -1,5 +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.vespa.hosted.controller.api.statuspage;
+package com.yahoo.vespa.hosted.controller.deployment;
import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmer.java
index 1a6bce2dba9..8db7231c207 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmer.java
@@ -22,7 +22,7 @@ import java.util.logging.Level;
*
* When to file new issues, escalate inactive ones, etc., is handled by the enclosed OwnershipIssues.
*
- * @author jvenstad
+ * @author jonmv
*/
public class ApplicationOwnershipConfirmer extends Maintainer {
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 b889179750e..a046ed87a05 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
@@ -22,7 +22,7 @@ import java.util.Map;
*/
public class ClusterUtilizationMaintainer extends Maintainer {
- Controller controller;
+ private final Controller controller;
public ClusterUtilizationMaintainer(Controller controller, Duration duration, JobControl jobControl) {
super(controller, duration, jobControl);
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java
index 7154cf7d600..7fa16a02649 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java
@@ -4,11 +4,14 @@ package com.yahoo.vespa.hosted.controller.maintenance;
import com.yahoo.component.AbstractComponent;
import com.yahoo.jdisc.Metric;
import com.yahoo.vespa.hosted.controller.Controller;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.Testers;
import com.yahoo.vespa.hosted.controller.api.integration.dns.NameService;
import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryClientInterface;
import com.yahoo.vespa.hosted.controller.api.integration.organization.OwnershipIssues;
import com.yahoo.vespa.hosted.controller.api.integration.organization.DeploymentIssues;
import com.yahoo.vespa.hosted.controller.api.integration.chef.Chef;
+import com.yahoo.vespa.hosted.controller.deployment.DummyStepRunner;
+import com.yahoo.vespa.hosted.controller.deployment.InternalStepRunner;
import com.yahoo.vespa.hosted.controller.maintenance.config.MaintainerConfig;
import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
@@ -38,10 +41,11 @@ public class ControllerMaintenance extends AbstractComponent {
private final ApplicationOwnershipConfirmer applicationOwnershipConfirmer;
private final DnsMaintainer dnsMaintainer;
private final SystemUpgrader systemUpgrader;
+ private final JobRunner jobRunner;
@SuppressWarnings("unused") // instantiated by Dependency Injection
public ControllerMaintenance(MaintainerConfig maintainerConfig, Controller controller, CuratorDb curator,
- JobControl jobControl, Metric metric, Chef chefClient,
+ JobControl jobControl, Metric metric, Chef chefClient, Testers testers,
DeploymentIssues deploymentIssues, OwnershipIssues ownershipIssues,
NameService nameService, NodeRepositoryClientInterface nodeRepositoryClient) {
Duration maintenanceInterval = Duration.ofMinutes(maintainerConfig.intervalMinutes());
@@ -59,6 +63,7 @@ public class ControllerMaintenance extends AbstractComponent {
applicationOwnershipConfirmer = new ApplicationOwnershipConfirmer(controller, Duration.ofHours(12), jobControl, ownershipIssues);
dnsMaintainer = new DnsMaintainer(controller, Duration.ofHours(12), jobControl, nameService);
systemUpgrader = new SystemUpgrader(controller, Duration.ofMinutes(1), jobControl);
+ jobRunner = new JobRunner(controller, Duration.ofSeconds(30), jobControl, new InternalStepRunner(controller, testers));
}
public Upgrader upgrader() { return upgrader; }
@@ -80,7 +85,8 @@ public class ControllerMaintenance extends AbstractComponent {
deploymentMetricsMaintainer.deconstruct();
applicationOwnershipConfirmer.deconstruct();
dnsMaintainer.deconstruct();
- systemUpgrader.maintain();
+ systemUpgrader.deconstruct();
+ jobRunner.deconstruct();
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporter.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporter.java
index d471e553bb9..91eda31d779 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporter.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporter.java
@@ -28,7 +28,7 @@ import static com.yahoo.vespa.hosted.controller.versions.VespaVersion.Confidence
* Maintenance job which files issues for tenants when they have jobs which fails continuously
* and escalates issues which are not handled in a timely manner.
*
- * @author jvenstad
+ * @author jonmv
*/
public class DeploymentIssueReporter extends Maintainer {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunner.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunner.java
new file mode 100644
index 00000000000..7dbf1a2c05e
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunner.java
@@ -0,0 +1,101 @@
+package com.yahoo.vespa.hosted.controller.maintenance;
+
+import com.yahoo.log.LogLevel;
+import com.yahoo.vespa.hosted.controller.Controller;
+import com.yahoo.vespa.hosted.controller.deployment.InternalBuildService;
+import com.yahoo.vespa.hosted.controller.deployment.JobController;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
+import com.yahoo.vespa.hosted.controller.deployment.RunStatus;
+import com.yahoo.vespa.hosted.controller.deployment.Step;
+import com.yahoo.vespa.hosted.controller.deployment.StepRunner;
+import org.jetbrains.annotations.TestOnly;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Logger;
+
+/**
+ * Advances the set of {@link RunStatus}es for an {@link InternalBuildService}.
+ *
+ * @see JobController
+ * @author jonmv
+ */
+public class JobRunner extends Maintainer {
+
+ private static final Logger log = Logger.getLogger(JobRunner.class.getName());
+
+ private final JobController jobs;
+ private final ExecutorService executors;
+ private final StepRunner runner;
+
+ public JobRunner(Controller controller, Duration duration, JobControl jobControl, StepRunner runner) {
+ this(controller, duration, jobControl, Executors.newFixedThreadPool(32), runner);
+ }
+
+ @TestOnly
+ JobRunner(Controller controller, Duration duration, JobControl jobControl, ExecutorService executors, StepRunner runner) {
+ super(controller, duration, jobControl);
+ this.jobs = controller.jobController();
+ this.executors = executors;
+ this.runner = runner;
+ }
+
+ @Override
+ protected void maintain() {
+ jobs.active().forEach(this::advance);
+ jobs.collectGarbage();
+ }
+
+ @Override
+ public void deconstruct() {
+ super.deconstruct();
+ executors.shutdown();
+ try {
+ executors.awaitTermination(50, TimeUnit.SECONDS);
+ }
+ catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ /** Advances each of the ready steps for the given run, or marks it as finished, and stashes it. */
+ void advance(RunStatus run) {
+ List<Step> steps = run.readySteps();
+ steps.forEach(step -> executors.execute(() -> advance(run.id(), step)));
+ if (steps.isEmpty())
+ jobs.finish(run.id());
+ }
+
+ /** Attempts to advance the status of the given step, for the given run. */
+ void advance(RunId id, Step step) {
+ try {
+ AtomicBoolean changed = new AtomicBoolean(false);
+ jobs.locked(id.application(), id.type(), step, lockedStep -> {
+ jobs.active(id).ifPresent(run -> { // The run may have become inactive, so we bail out.
+ if ( ! run.readySteps().contains(step))
+ return; // Someone may have updated the run status, making this step obsolete, so we bail out.
+
+ Step.Status status = runner.run(lockedStep, run.id());
+ if (run.steps().get(step) != status) {
+ jobs.update(run.id(), status, lockedStep);
+ changed.set(true);
+ }
+ });
+ });
+ if (changed.get())
+ jobs.active(id).ifPresent(this::advance);
+ }
+ catch (TimeoutException e) {
+ // Something else is already advancing this step, or a prerequisite -- try again later!
+ }
+ catch (RuntimeException e) {
+ log.log(LogLevel.WARNING, "Exception attempting to advance " + step + " of " + id, e);
+ }
+ }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Maintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Maintainer.java
index 40563c4cf95..f6ccbf6aa4e 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Maintainer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Maintainer.java
@@ -12,6 +12,7 @@ import java.time.Duration;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -50,7 +51,7 @@ public abstract class Maintainer extends AbstractComponent implements Runnable {
}
}
}
- catch (UncheckedTimeoutException e) {
+ catch (TimeoutException e) {
// another controller instance is running this job at the moment; ok
}
catch (Throwable t) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporter.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporter.java
index 2f1345eb49e..feec83d226e 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporter.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporter.java
@@ -106,9 +106,7 @@ public class MetricsReporter extends Maintainer {
node.getValue("tenant").ifPresent(tenant -> dimensions.put("tenantName", tenant));
Optional<String> application = node.getValue("application");
- if (application.isPresent()) {
- dimensions.put("app",String.format("%s.%s", application.get(), node.getValue("instance").orElse("default")));
- }
+ application.ifPresent(app -> dimensions.put("app", String.format("%s.%s", app, node.getValue("instance").orElse("default"))));
Metric.Context context = metric.createContext(dimensions);
metric.set(convergeMetric, secondsSinceConverge, context);
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/SystemUpgrader.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/SystemUpgrader.java
index ac0d08f5105..a71ce4299b1 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/SystemUpgrader.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/SystemUpgrader.java
@@ -2,7 +2,6 @@
package com.yahoo.vespa.hosted.controller.maintenance;
import com.yahoo.component.Version;
-import com.yahoo.config.provision.ApplicationId;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
@@ -33,27 +32,23 @@ public class SystemUpgrader extends Maintainer {
@Override
protected void maintain() {
- Optional<Version> target = targetVersion();
- if (!target.isPresent()) {
- return;
- }
-
- deploy(SystemApplication.all(), target.get());
+ targetVersion().ifPresent(target -> deployAll(target, SystemApplication.all()));
}
/** Deploy a list of system applications until they converge on the given version */
- private void deploy(List<SystemApplication> applications, Version target) {
+ private void deployAll(Version target, List<SystemApplication> applications) {
for (List<ZoneId> zones : controller().zoneRegistry().upgradePolicy().asList()) {
boolean converged = true;
for (ZoneId zone : zones) {
try {
- converged &= deployInZone(zone, applications, target);
+ converged &= deployAll(target, applications, zone);
} catch (UnreachableNodeRepositoryException e) {
converged = false;
log.log(Level.WARNING, e.getMessage() + ". Continuing to next parallel deployed zone");
} catch (Exception e) {
converged = false;
- log.log(Level.WARNING, "Failed to upgrade " + zone + ". Continuing to next parallel deployed zone", e);
+ log.log(Level.WARNING, "Failed to upgrade " + zone +
+ ". Continuing to next parallel deployed zone", e);
}
}
if (!converged) {
@@ -62,8 +57,8 @@ public class SystemUpgrader extends Maintainer {
}
}
- /** @return true if all applications have converged to the target version in the zone */
- private boolean deployInZone(ZoneId zone, List<SystemApplication> applications, Version target) {
+ /** Returns whether all applications have converged to the target version in zone */
+ private boolean deployAll(Version target, List<SystemApplication> applications, ZoneId zone) {
boolean converged = true;
for (SystemApplication application : applications) {
if (convergedOn(target, application.dependencies(), zone)) {
@@ -76,10 +71,11 @@ public class SystemUpgrader extends Maintainer {
/** Deploy application on given version idempotently */
private void deploy(Version target, SystemApplication application, ZoneId zone) {
- if (!wantedVersion(zone, application.id(), target).equals(target)) {
- log.info(String.format("Deploying %s version %s in %s", application.id(), target, zone));
- controller().applications().deploy(application, zone, target);
+ if (wantedVersion(zone, application, target).equals(target)) {
+ return;
}
+ log.info(String.format("Deploying %s version %s in %s", application.id(), target, zone));
+ controller().applications().deploy(application, zone, target);
}
private boolean convergedOn(Version target, List<SystemApplication> applications, ZoneId zone) {
@@ -87,28 +83,28 @@ public class SystemUpgrader extends Maintainer {
}
private boolean convergedOn(Version target, SystemApplication application, ZoneId zone) {
- return currentVersion(zone, application.id(), target).equals(target);
+ return currentVersion(zone, application, target).equals(target) && application.configConvergedIn(zone, controller());
}
- private Version wantedVersion(ZoneId zone, ApplicationId application, Version defaultVersion) {
+ private Version wantedVersion(ZoneId zone, SystemApplication application, Version defaultVersion) {
return minVersion(zone, application, Node::wantedVersion).orElse(defaultVersion);
}
- private Version currentVersion(ZoneId zone, ApplicationId application, Version defaultVersion) {
+ private Version currentVersion(ZoneId zone, SystemApplication application, Version defaultVersion) {
return minVersion(zone, application, Node::currentVersion).orElse(defaultVersion);
}
- private Optional<Version> minVersion(ZoneId zone, ApplicationId application, Function<Node, Version> versionField) {
+ private Optional<Version> minVersion(ZoneId zone, SystemApplication application, Function<Node, Version> versionField) {
try {
return controller().configServer()
.nodeRepository()
- .listOperational(zone, application)
+ .list(zone, application.id(), SystemApplication.activeStates())
.stream()
.map(versionField)
.min(Comparator.naturalOrder());
} catch (Exception e) {
throw new UnreachableNodeRepositoryException(String.format("Failed to get version for %s in %s: %s",
- application, zone, Exceptions.toMessageString(e)));
+ application.id(), zone, Exceptions.toMessageString(e)));
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Upgrader.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Upgrader.java
index 22cbe942932..6286f89cc9d 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Upgrader.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Upgrader.java
@@ -117,15 +117,15 @@ public class Upgrader extends Maintainer {
/** Returns the number of applications to upgrade in this run */
private int numberOfApplicationsToUpgrade() {
- return Math.max(1, (int)(maintenanceInterval().getSeconds() * (upgradesPerMinute() / 60)));
+ return Math.max(1, (int) (maintenanceInterval().getSeconds() * (upgradesPerMinute() / 60)));
}
- /** Returns number upgrades per minute */
+ /** Returns number of upgrades per minute */
public double upgradesPerMinute() {
return curator.readUpgradesPerMinute();
}
- /** Sets the number upgrades per minute */
+ /** Sets the number of upgrades per minute */
public void setUpgradesPerMinute(double n) {
curator.writeUpgradesPerMinute(n);
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/VersionStatusUpdater.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/VersionStatusUpdater.java
index dea991bc653..565acf6ebd5 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/VersionStatusUpdater.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/VersionStatusUpdater.java
@@ -1,10 +1,11 @@
// 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.controller.maintenance;
+import com.yahoo.log.LogLevel;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.versions.VersionStatus;
+import com.yahoo.yolean.Exceptions;
-import java.io.UncheckedIOException;
import java.time.Duration;
/**
@@ -25,8 +26,9 @@ public class VersionStatusUpdater extends Maintainer {
try {
VersionStatus newStatus = VersionStatus.compute(controller());
controller().updateVersionStatus(newStatus);
- } catch (UncheckedIOException e) {
- log.warning("Failed to compute version status. This is likely a transient error: " + e.getMessage());
+ } catch (Exception e) {
+ log.log(LogLevel.WARNING, "Failed to compute version status: " + Exceptions.toMessageString(e) +
+ ". Retrying in " + maintenanceInterval());
}
}
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 96d252d3d1c..763d26834e6 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
@@ -13,6 +13,7 @@ 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.deployment.JobType;
import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.application.ApplicationVersion;
@@ -79,6 +80,7 @@ public class ApplicationSerializer {
private final String projectIdField = "projectId";
private final String jobStatusField = "jobStatus";
private final String issueIdField = "jiraIssueId";
+ private final String builtInternallyField = "builtInternally";
// JobStatus field
private final String jobTypeField = "jobType";
@@ -226,6 +228,7 @@ public class ApplicationSerializer {
deploymentJobs.projectId().ifPresent(projectId -> cursor.setLong(projectIdField, projectId));
jobStatusToSlime(deploymentJobs.jobStatus().values(), cursor.setArray(jobStatusField));
deploymentJobs.issueId().ifPresent(jiraIssueId -> cursor.setString(issueIdField, jiraIssueId.value()));
+ cursor.setBool(builtInternallyField, deploymentJobs.builtInternally());
}
private void jobStatusToSlime(Collection<JobStatus> jobStatuses, Cursor jobStatusArray) {
@@ -373,8 +376,9 @@ public class ApplicationSerializer {
OptionalLong projectId = optionalLong(object.field(projectIdField));
List<JobStatus> jobStatusList = jobStatusListFromSlime(object.field(jobStatusField));
Optional<IssueId> issueId = optionalString(object.field(issueIdField)).map(IssueId::from);
+ boolean builtInternally = object.field(builtInternallyField).asBool();
- return new DeploymentJobs(projectId, jobStatusList, issueId);
+ return new DeploymentJobs(projectId, jobStatusList, issueId, builtInternally);
}
private Change changeFromSlime(Inspector object) {
@@ -398,8 +402,8 @@ public class ApplicationSerializer {
private Optional<JobStatus> jobStatusFromSlime(Inspector object) {
// if the job type has since been removed, ignore it
- Optional<DeploymentJobs.JobType> jobType =
- DeploymentJobs.JobType.fromOptionalJobName(object.field(jobTypeField).asString());
+ Optional<JobType> jobType =
+ JobType.fromOptionalJobName(object.field(jobTypeField).asString());
if (! jobType.isPresent()) return Optional.empty();
Optional<JobError> jobError = Optional.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 184ac90691a..49e8d7498ab 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
@@ -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.controller.persistence;
+import com.google.common.util.concurrent.UncheckedTimeoutException;
import com.google.inject.Inject;
import com.yahoo.component.Version;
import com.yahoo.component.Vtag;
@@ -13,6 +14,10 @@ import com.yahoo.vespa.config.SlimeUtils;
import com.yahoo.vespa.curator.Curator;
import com.yahoo.vespa.curator.Lock;
import com.yahoo.vespa.hosted.controller.Application;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
+import com.yahoo.vespa.hosted.controller.deployment.RunStatus;
+import com.yahoo.vespa.hosted.controller.deployment.Step;
import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant;
import com.yahoo.vespa.hosted.controller.tenant.Tenant;
import com.yahoo.vespa.hosted.controller.tenant.UserTenant;
@@ -26,11 +31,13 @@ import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;
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.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeoutException;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.logging.Level;
@@ -53,6 +60,7 @@ public class CuratorDb {
private static final Path lockRoot = root.append("locks");
private static final Path tenantRoot = root.append("tenants");
private static final Path applicationRoot = root.append("applications");
+ private static final Path jobRoot = root.append("jobs");
private static final Path controllerRoot = root.append("controllers");
private final StringSetSerializer stringSetSerializer = new StringSetSerializer();
@@ -61,6 +69,7 @@ public class CuratorDb {
private final ConfidenceOverrideSerializer confidenceOverrideSerializer = new ConfidenceOverrideSerializer();
private final TenantSerializer tenantSerializer = new TenantSerializer();
private final ApplicationSerializer applicationSerializer = new ApplicationSerializer();
+ private final RunSerializer runSerializer = new RunSerializer();
private final Curator curator;
@@ -93,12 +102,20 @@ public class CuratorDb {
return lock;
}
- public Lock lock(TenantName name, Duration timeout) {
- return lock(lockPath(name), timeout);
+ public Lock lock(TenantName name) {
+ return lock(lockPath(name), defaultLockTimeout.multipliedBy(2));
}
- public Lock lock(ApplicationId id, Duration timeout) {
- return lock(lockPath(id), timeout);
+ public Lock lock(ApplicationId id) {
+ return lock(lockPath(id), defaultLockTimeout.multipliedBy(2));
+ }
+
+ public Lock lock(ApplicationId id, JobType type) {
+ return lock(lockPath(id, type), defaultLockTimeout);
+ }
+
+ public Lock lock(ApplicationId id, JobType type, Step step) throws TimeoutException {
+ return tryLock(lockPath(id, type, step));
}
public Lock lockRotations() {
@@ -113,11 +130,8 @@ public class CuratorDb {
return lock(lockRoot.append("inactiveJobsLock"), defaultLockTimeout);
}
- public Lock lockMaintenanceJob(String jobName) {
- // Use a short timeout such that if maintenance jobs are started at about the same time on different nodes
- // and the maintenance job takes a long time to complete, only one of the nodes will run the job
- // in each maintenance interval
- return lock(lockRoot.append("maintenanceJobLocks").append(jobName), Duration.ofSeconds(1));
+ public Lock lockMaintenanceJob(String jobName) throws TimeoutException {
+ return tryLock(lockRoot.append("maintenanceJobLocks").append(jobName));
}
@SuppressWarnings("unused") // Called by internal code
@@ -137,6 +151,19 @@ public class CuratorDb {
// -------------- Helpers ------------------------------------------
+ /** Try locking with a low timeout, meaning it is OK to fail lock acquisition.
+ *
+ * Useful for maintenance jobs, where there is no point in running the jobs back to back.
+ */
+ private Lock tryLock(Path path) throws TimeoutException {
+ try {
+ return lock(path, Duration.ofSeconds(1));
+ }
+ catch (UncheckedTimeoutException e) {
+ throw new TimeoutException(e.getMessage());
+ }
+ }
+
private <T> Optional<T> read(Path path, Function<byte[], T> mapper) {
return curator.getData(path).filter(data -> data.length > 0).map(mapper);
}
@@ -278,6 +305,40 @@ public class CuratorDb {
curator.delete(applicationPath(application));
}
+ // -------------- Job Runs ------------------------------------------------
+
+ public void writeLastRun(RunStatus run) {
+ curator.set(lastRunPath(run.id().application(), run.id().type()), asJson(runSerializer.toSlime(run)));
+ }
+
+ public void writeHistoricRuns(ApplicationId id, JobType type, Iterable<RunStatus> runs) {
+ curator.set(jobPath(id, type), asJson(runSerializer.toSlime(runs)));
+ }
+
+ public Optional<RunStatus> readLastRun(ApplicationId id, JobType type) {
+ return readSlime(lastRunPath(id, type)).map(runSerializer::runFromSlime);
+ }
+
+ public Map<RunId, RunStatus> readHistoricRuns(ApplicationId id, JobType type) {
+ // TODO jvenstad: Add, somewhere, a retention filter based on age or count.
+ return readSlime(jobPath(id, type)).map(runSerializer::runsFromSlime).orElse(new LinkedHashMap<>());
+ }
+
+ public void deleteJobData(ApplicationId id, JobType type) {
+ curator.delete(jobPath(id, type));
+ curator.delete(lastRunPath(id, type));
+ }
+
+ public void deleteJobData(ApplicationId id) {
+ curator.delete(jobRoot.append(id.serializedForm()));
+ }
+
+ public List<ApplicationId> applicationsWithJobs() {
+ return curator.getChildren(jobRoot).stream()
+ .map(ApplicationId::fromSerializedForm)
+ .collect(Collectors.toList());
+ }
+
// -------------- Provisioning (called by internal code) ------------------
@SuppressWarnings("unused")
@@ -333,6 +394,27 @@ public class CuratorDb {
return lockPath;
}
+ private Path lockPath(ApplicationId application, JobType type) {
+ Path lockPath = lockRoot
+ .append(application.tenant().value())
+ .append(application.application().value())
+ .append(application.instance().value())
+ .append(type.jobName());
+ curator.create(lockPath);
+ return lockPath;
+ }
+
+ private Path lockPath(ApplicationId application, JobType type, Step step) {
+ Path lockPath = lockRoot
+ .append(application.tenant().value())
+ .append(application.application().value())
+ .append(application.instance().value())
+ .append(type.jobName())
+ .append(step.name());
+ curator.create(lockPath);
+ return lockPath;
+ }
+
private Path lockPath(String provisionId) {
Path lockPath = lockRoot
.append(provisionStatePath())
@@ -381,6 +463,14 @@ public class CuratorDb {
return applicationRoot.append(application.serializedForm());
}
+ private static Path jobPath(ApplicationId id, JobType type) {
+ return jobRoot.append(id.serializedForm()).append(type.jobName());
+ }
+
+ private static Path lastRunPath(ApplicationId id, JobType type) {
+ return jobPath(id, type).append("last");
+ }
+
private static Path controllerPath(String hostname) {
return controllerRoot.append(hostname);
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializer.java
new file mode 100644
index 00000000000..7df60278390
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializer.java
@@ -0,0 +1,157 @@
+package com.yahoo.vespa.hosted.controller.persistence;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.slime.ArrayTraverser;
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.Inspector;
+import com.yahoo.slime.ObjectTraverser;
+import com.yahoo.slime.Slime;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
+import com.yahoo.vespa.hosted.controller.deployment.RunStatus;
+import com.yahoo.vespa.hosted.controller.deployment.Step;
+import com.yahoo.vespa.hosted.controller.deployment.Step.Status;
+
+import java.time.Instant;
+import java.util.EnumMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Optional;
+
+import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.failed;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.succeeded;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.unfinished;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.deactivateReal;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.deactivateTester;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.deployInitialReal;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.deployReal;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.deployTester;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.installInitialReal;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.installReal;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.installTester;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.report;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.startTests;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.endTests;
+
+/**
+ * Serialises and deserialises RunStatus objects for persistent storage.
+ *
+ * @author jonmv
+ */
+public class RunSerializer {
+
+ private static final String stepsField = "steps";
+ private static final String applicationField = "id";
+ private static final String jobTypeField = "type";
+ private static final String numberField = "number";
+ private static final String startField = "start";
+ private static final String endField = "end";
+ private static final String abortedField = "aborted";
+
+ RunStatus runFromSlime(Slime slime) {
+ return runFromSlime(slime.get());
+ }
+
+ Map<RunId, RunStatus> runsFromSlime(Slime slime) {
+ Map<RunId, RunStatus> runs = new LinkedHashMap<>();
+ Inspector runArray = slime.get();
+ runArray.traverse((ArrayTraverser) (__, runObject) -> {
+ RunStatus run = runFromSlime(runObject);
+ runs.put(run.id(), run);
+ });
+ return runs;
+ }
+
+ private RunStatus runFromSlime(Inspector runObject) {
+ EnumMap<Step, Status> steps = new EnumMap<>(Step.class);
+ runObject.field(stepsField).traverse((ObjectTraverser) (step, status) -> {
+ steps.put(stepOf(step), statusOf(status.asString()));
+ });
+ return new RunStatus(new RunId(ApplicationId.fromSerializedForm(runObject.field(applicationField).asString()),
+ JobType.fromJobName(runObject.field(jobTypeField).asString()),
+ runObject.field(numberField).asLong()),
+ steps,
+ Instant.ofEpochMilli(runObject.field(startField).asLong()),
+ Optional.of(runObject.field(endField))
+ .filter(Inspector::valid)
+ .map(end -> Instant.ofEpochMilli(end.asLong())),
+ runObject.field(abortedField).asBool());
+ }
+
+ Slime toSlime(Iterable<RunStatus> runs) {
+ Slime slime = new Slime();
+ Cursor runArray = slime.setArray();
+ runs.forEach(run -> toSlime(run, runArray.addObject()));
+ return slime;
+ }
+
+ Slime toSlime(RunStatus run) {
+ Slime slime = new Slime();
+ toSlime(run, slime.setObject());
+ return slime;
+ }
+
+ private void toSlime(RunStatus run, Cursor runObject) {
+ runObject.setString(applicationField, run.id().application().serializedForm());
+ runObject.setString(jobTypeField, run.id().type().jobName());
+ runObject.setLong(numberField, run.id().number());
+ runObject.setLong(startField, run.start().toEpochMilli());
+ run.end().ifPresent(end -> runObject.setLong(endField, end.toEpochMilli()));
+ if (run.isAborted()) runObject.setBool(abortedField, true);
+ Cursor stepsObject = runObject.setObject(stepsField);
+ run.steps().forEach((step, status) -> stepsObject.setString(valueOf(step), valueOf(status)));
+ }
+
+ static String valueOf(Step step) {
+ switch (step) {
+ case deployInitialReal : return "DIR";
+ case installInitialReal : return "IIR";
+ case deployReal : return "DR" ;
+ case installReal : return "IR" ;
+ case deactivateReal : return "DAR";
+ case deployTester : return "DT" ;
+ case installTester : return "IT" ;
+ case deactivateTester : return "DAT";
+ case startTests : return "ST" ;
+ case endTests : return "ET" ;
+ case report : return "R" ;
+ default : throw new AssertionError("No value defined for '" + step + "'!");
+ }
+ }
+
+ static Step stepOf(String step) {
+ switch (step) {
+ case "DIR" : return deployInitialReal ;
+ case "IIR" : return installInitialReal;
+ case "DR" : return deployReal ;
+ case "IR" : return installReal ;
+ case "DAR" : return deactivateReal ;
+ case "DT" : return deployTester ;
+ case "IT" : return installTester ;
+ case "DAT" : return deactivateTester ;
+ case "ST" : return startTests ;
+ case "ET" : return endTests ;
+ case "R" : return report ;
+ default : throw new IllegalArgumentException("No step defined by '" + step + "'!");
+ }
+ }
+
+ static String valueOf(Status status) {
+ switch (status) {
+ case unfinished : return "U";
+ case failed : return "F";
+ case succeeded : return "S";
+ default : throw new AssertionError("No value defined for '" + status + "'!");
+ }
+ }
+
+ static Status statusOf(String status) {
+ switch (status) {
+ case "U" : return unfinished;
+ case "F" : return failed ;
+ case "S" : return succeeded ;
+ default : throw new IllegalArgumentException("No status defined by '" + status + "'!");
+ }
+ }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/proxy/ConfigServerRestExecutorImpl.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/proxy/ConfigServerRestExecutorImpl.java
index e5e03cf5dfc..ae1102e2cef 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/proxy/ConfigServerRestExecutorImpl.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/proxy/ConfigServerRestExecutorImpl.java
@@ -145,7 +145,7 @@ public class ConfigServerRestExecutorImpl implements ConfigServerRestExecutor {
.setSocketTimeout((int) PROXY_REQUEST_TIMEOUT.toMillis()).build();
try (
CloseableHttpClient client = createHttpClient(config, sslContextProvider, zoneRegistry, proxyRequest);
- CloseableHttpResponse response = client.execute(requestBase);
+ CloseableHttpResponse response = client.execute(requestBase)
) {
String content = getContent(response);
int status = response.getStatusLine().getStatusCode();
@@ -246,7 +246,7 @@ public class ConfigServerRestExecutorImpl implements ConfigServerRestExecutor {
.setSocketTimeout(timeout).build();
try (
CloseableHttpClient client = createHttpClient(config, sslContextProvider, zoneRegistry, proxyRequest);
- CloseableHttpResponse response = client.execute(httpget);
+ CloseableHttpResponse response = client.execute(httpget)
) {
if (response.getStatusLine().getStatusCode() == 200) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/proxy/package-info.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/proxy/package-info.java
index f6c300268a2..c4472fb79e4 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/proxy/package-info.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/proxy/package-info.java
@@ -1,8 +1,8 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-@ExportPackage
-package com.yahoo.vespa.hosted.controller.proxy;
-
/**
* @author Haakon Dybdahl
*/
+@ExportPackage
+package com.yahoo.vespa.hosted.controller.proxy;
+
import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
index edf68054481..9e45526e5e4 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
@@ -48,6 +48,8 @@ import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzClientFact
import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsException;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.Log;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
import com.yahoo.vespa.hosted.controller.api.integration.organization.User;
import com.yahoo.vespa.hosted.controller.api.integration.routing.RotationStatus;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
@@ -62,9 +64,11 @@ 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.SourceRevision;
+import com.yahoo.vespa.hosted.controller.deployment.DeploymentSteps;
+import com.yahoo.vespa.hosted.controller.deployment.RunStatus;
import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse;
import com.yahoo.vespa.hosted.controller.restapi.MessageResponse;
-import com.yahoo.vespa.hosted.controller.restapi.Path;
+import com.yahoo.restapi.Path;
import com.yahoo.vespa.hosted.controller.restapi.ResourceResponse;
import com.yahoo.vespa.hosted.controller.restapi.SlimeJsonResponse;
import com.yahoo.vespa.hosted.controller.restapi.StringResponse;
@@ -87,6 +91,7 @@ import java.security.Principal;
import java.time.DayOfWeek;
import java.time.Duration;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -167,6 +172,12 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
if (path.matches("/application/v4/tenant/{tenant}")) return tenant(path.get("tenant"), request);
if (path.matches("/application/v4/tenant/{tenant}/application")) return applications(path.get("tenant"), request);
if (path.matches("/application/v4/tenant/{tenant}/application/{application}")) return application(path.get("tenant"), path.get("application"), request);
+ if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/job"))
+ return JobControllerApiHandlerHelper.jobTypeResponse(jobTypes(path), latestRuns(path), request.getUri());
+ if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/job/{jobtype}"))
+ return JobControllerApiHandlerHelper.runStatusResponse(controller.jobController().runs(appIdFromPath(path), jobTypeFromPath(path)), request.getUri());
+ if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/job/{jobtype}/run/{number}"))
+ return JobControllerApiHandlerHelper.runDetailsResponse(controller.jobController(), runIdFromPath(path));
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}")) return deployment(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), request);
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/service")) return services(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), request);
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/service/{service}/{*}")) return service(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), path.get("service"), path.getRest(), request);
@@ -192,6 +203,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/promote")) return promoteApplication(path.get("tenant"), path.get("application"), request);
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/deploying")) return deploy(path.get("tenant"), path.get("application"), request);
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/jobreport")) return notifyJobCompletion(path.get("tenant"), path.get("application"), request);
+ if (path.matches("/application/v4/tenant/{tenant}/application/{application}/submit")) return submit(path.get("tenant"), path.get("application"), request);
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}")) return deploy(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), request);
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/deploy")) return deploy(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), request); // legacy synonym of the above
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/restart")) return restart(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), request);
@@ -344,19 +356,21 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
private void toSlime(Cursor object, Application application, HttpRequest request) {
object.setString("application", application.id().application().value());
object.setString("instance", application.id().instance().value());
+
// Currently deploying change
if (application.change().isPresent()) {
- Cursor deployingObject = object.setObject("deploying");
- application.change().platform().ifPresent(v -> deployingObject.setString("version", v.toString()));
- application.change().application()
- .filter(v -> v != ApplicationVersion.unknown)
- .ifPresent(v -> toSlime(v, deployingObject.setObject("revision")));
+ toSlime(object.setObject("deploying"), application.change());
+ }
+
+ // Outstanding change
+ if (application.outstandingChange().isPresent()) {
+ toSlime(object.setObject("outstandingChange"), application.outstandingChange());
}
// Jobs sorted according to deployment spec
List<JobStatus> jobStatus = controller.applications().deploymentTrigger()
- .deploymentOrder()
- .sortBy(application.deploymentSpec(), application.deploymentJobs().jobStatus().values());
+ .steps(application.deploymentSpec())
+ .sortBy(application.deploymentJobs().jobStatus().values());
Cursor deploymentsArray = object.setArray("deploymentJobs");
for (JobStatus job : jobStatus) {
@@ -396,8 +410,8 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
// Deployments sorted according to deployment spec
List<Deployment> deployments = controller.applications().deploymentTrigger()
- .deploymentOrder()
- .sortBy(application.deploymentSpec().zones(), application.deployments().values());
+ .steps(application.deploymentSpec())
+ .sortBy2(application.deployments().values());
Cursor instancesArray = object.setArray("instances");
for (Deployment deployment : deployments) {
Cursor deploymentObject = instancesArray.addObject();
@@ -453,6 +467,13 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
return new SlimeJsonResponse(slime);
}
+ private void toSlime(Cursor object, Change change) {
+ change.platform().ifPresent(version -> object.setString("version", version.toString()));
+ change.application()
+ .filter(version -> !version.isUnknown())
+ .ifPresent(version -> toSlime(version, object.setObject("revision")));
+ }
+
private void toSlime(Cursor response, DeploymentId deploymentId, Deployment deployment, HttpRequest request) {
Cursor serviceUrlArray = response.setArray("serviceUrls");
@@ -687,7 +708,8 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
ApplicationId id = ApplicationId.from(tenantName, applicationName, "default");
controller.applications().lockOrThrow(id, application -> {
- controller.applications().deploymentTrigger().triggerChange(application.get().id(), Change.of(version));
+ controller.applications().deploymentTrigger().triggerChange(application.get().id(),
+ application.get().change().with(version));
});
return new MessageResponse("Triggered deployment of application '" + id + "' on version " + version);
}
@@ -856,7 +878,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
}
return new DeploymentJobs.JobReport(
ApplicationId.from(tenantName, applicationName, report.field("instance").asString()),
- DeploymentJobs.JobType.fromJobName(report.field("jobName").asString()),
+ JobType.fromJobName(report.field("jobName").asString()),
report.field("projectId").asLong(),
report.field("buildNumber").asLong(),
toSourceRevision(report.field("sourceRevision")),
@@ -1193,4 +1215,48 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
message + ": No NToken provided"));
}
+ private ApplicationId appIdFromPath(Path path) {
+ return ApplicationId.from(path.get("tenant"), path.get("application"), path.get("instance"));
+ }
+
+ private JobType jobTypeFromPath(Path path) {
+ return JobType.fromJobName(path.get("jobtype"));
+ }
+
+ private RunId runIdFromPath(Path path) {
+ long number = Long.parseLong(path.get("number"));
+ return new RunId(appIdFromPath(path), jobTypeFromPath(path), number);
+ }
+
+ private List<JobType> jobTypes(Path path) {
+ ApplicationId appId = appIdFromPath(path);
+ DeploymentSpec deploymentSpec = controller.applications().get(appId).get().deploymentSpec();
+ DeploymentSteps deploymentSteps = new DeploymentSteps(deploymentSpec, controller::system);
+ return deploymentSteps.jobs();
+ }
+
+ private Map<JobType, RunStatus> latestRuns(Path path) {
+ Map<JobType, RunStatus> jobMap = new HashMap<>();
+ ApplicationId appId = appIdFromPath(path);
+ controller.jobController().jobs(appId)
+ .forEach(jobType -> jobMap.put(jobType, controller.jobController()
+ .last(appId, jobType)
+ .orElseThrow(() -> new RuntimeException(String.format("Job %s for application %s appears in " +
+ "the list of previously ran jobs, but no status of the last execution found",
+ jobType.jobName(), appId.toShortString())))));
+
+ return jobMap;
+ }
+
+ private HttpResponse submit(String tenant, String application, HttpRequest request) {
+ Map<String, byte[]> dataParts = new MultipartParser().parse(request);
+ Inspector submitOptions = SlimeUtils.jsonToSlime(dataParts.get(EnvironmentResource.SUBMIT_OPTIONS)).get();
+ SourceRevision sourceRevision = toSourceRevision(submitOptions).orElseThrow(() ->
+ new IllegalArgumentException("Must specify 'repository', 'branch', and 'commit'"));
+
+ return JobControllerApiHandlerHelper.submitResponse(controller.jobController(), tenant, application,
+ sourceRevision,
+ dataParts.get(EnvironmentResource.APPLICATION_ZIP),
+ dataParts.get(EnvironmentResource.APPLICATION_TEST_ZIP));
+ }
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/EmptyJsonResponse.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/EmptyJsonResponse.java
index 3e8d4182c42..be3222cc1a8 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/EmptyJsonResponse.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/EmptyJsonResponse.java
@@ -2,9 +2,7 @@
package com.yahoo.vespa.hosted.controller.restapi.application;
import com.yahoo.container.jdisc.HttpResponse;
-import com.yahoo.slime.Slime;
-import java.io.IOException;
import java.io.OutputStream;
/**
@@ -17,7 +15,7 @@ public class EmptyJsonResponse extends HttpResponse {
}
@Override
- public void render(OutputStream stream) throws IOException { }
+ public void render(OutputStream stream) {}
@Override
public String getContentType() { return "application/json"; }
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java
new file mode 100644
index 00000000000..281e59ef19f
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java
@@ -0,0 +1,122 @@
+package com.yahoo.vespa.hosted.controller.restapi.application;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.container.jdisc.HttpResponse;
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.Slime;
+import com.yahoo.vespa.hosted.controller.NotExistsException;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
+import com.yahoo.vespa.hosted.controller.application.ApplicationVersion;
+import com.yahoo.vespa.hosted.controller.application.SourceRevision;
+import com.yahoo.vespa.hosted.controller.deployment.JobController;
+import com.yahoo.vespa.hosted.controller.deployment.RunDetails;
+import com.yahoo.vespa.hosted.controller.deployment.RunStatus;
+import com.yahoo.vespa.hosted.controller.deployment.Step;
+import com.yahoo.vespa.hosted.controller.restapi.SlimeJsonResponse;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Implements the REST API for the job controller delegated from the Application API.
+ *
+ * @see JobController
+ * @see ApplicationApiHandler
+ */
+class JobControllerApiHandlerHelper {
+
+ /**
+ * @return Response with all job types that have recorded runs for the application _and_ the status for the last run of that type
+ */
+ static HttpResponse jobTypeResponse(List<JobType> sortedJobs, Map<JobType, RunStatus> lastStatus, URI baseUriForJobs) {
+ Slime slime = new Slime();
+ Cursor responseObject = slime.setObject();
+ Cursor jobArray = responseObject.setArray("jobs");
+
+ sortedJobs.forEach(jobType ->
+ jobTypeToSlime(jobArray.addObject(), jobType, Optional.ofNullable(lastStatus.get(jobType)), baseUriForJobs));
+ return new SlimeJsonResponse(slime);
+ }
+
+ private static void jobTypeToSlime(Cursor cursor, JobType jobType, Optional<RunStatus> runStatus, URI baseUriForJobs) {
+ Cursor jobObject = cursor.setObject(jobType.jobName());
+
+ // Url that are specific to the jobtype
+ String jobTypePath = baseUriForJobs.getPath() + "/" + jobType.jobName();
+ URI baseUriForJobType = baseUriForJobs.resolve(jobTypePath);
+ jobObject.setString("url", baseUriForJobType.toString());
+
+ // Add the last run status for the jobtype if present
+ runStatus.ifPresent(status -> {
+ Cursor lastObject = jobObject.setObject("last");
+ runStatusToSlime(lastObject, status, baseUriForJobType);
+ });
+ }
+
+ /**
+ * @return Response with the runstatuses for a specific jobtype
+ */
+ static HttpResponse runStatusResponse(Map<RunId, RunStatus> runStatuses, URI baseUriForJobType) {
+ Slime slime = new Slime();
+ Cursor cursor = slime.setObject();
+
+ runStatuses.forEach((runid, runstatus) -> runStatusToSlime(cursor.setObject(Long.toString(runid.number())), runstatus, baseUriForJobType));
+
+ return new SlimeJsonResponse(slime);
+ }
+
+ private static void runStatusToSlime(Cursor cursor, RunStatus runStatus, URI baseUriForJobType) {
+ runStatus.result().ifPresent(result -> cursor.setString("result", result.name()));
+ runStatus.end().ifPresent(instant -> cursor.setString("end", instant.toString()));
+
+ Cursor stepsArray = cursor.setArray("steps");
+ runStatus.steps().forEach((step, status) -> {
+ Cursor stepObject = stepsArray.addObject();
+ stepObject.setString(step.name(), status.name());
+ });
+
+ cursor.setString("start", runStatus.start().toString());
+ cursor.setLong("id", runStatus.id().number());
+ String logsPath = baseUriForJobType.getPath() + "/run/" + runStatus.id().number();
+ cursor.setString("logs", baseUriForJobType.resolve(logsPath).toString());
+ }
+
+ /**
+ * @return Response with logs from a single run
+ */
+ static HttpResponse runDetailsResponse(JobController jobController, RunId runId) {
+ Slime slime = new Slime();
+ Cursor logsObject = slime.setObject();
+
+ RunDetails runDetails = jobController.details(runId).orElseThrow(() ->
+ new NotExistsException(String.format(
+ "No run details exist for application: %s, job type: %s, number: %d",
+ runId.application().toShortString(), runId.type().jobName(), runId.number())));
+ for (Step step : Step.values()) {
+ runDetails.get(step).ifPresent(stepLog -> logsObject.setString(step.name(), stepLog));
+ }
+
+ return new SlimeJsonResponse(slime);
+ }
+
+ /**
+ * Unpack payload and submit to job controller. Defaults instance to 'default' and renders the
+ * application version on success.
+ *
+ * @return Response with the new application version
+ */
+ static HttpResponse submitResponse(JobController jobController, String tenant, String application,
+ SourceRevision sourceRevision, byte[] appPackage, byte[] testPackage) {
+ ApplicationVersion version = jobController.submit(ApplicationId.from(tenant, application, "default"),
+ sourceRevision, appPackage, testPackage);
+
+ Slime slime = new Slime();
+ Cursor responseObject = slime.setObject();
+ responseObject.setString("version", version.id());
+ return new SlimeJsonResponse(slime);
+ }
+}
+
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java
index 6fefb7099f1..19ec6316841 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java
@@ -12,7 +12,7 @@ import com.yahoo.vespa.hosted.controller.maintenance.ControllerMaintenance;
import com.yahoo.vespa.hosted.controller.maintenance.Upgrader;
import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse;
import com.yahoo.vespa.hosted.controller.restapi.MessageResponse;
-import com.yahoo.vespa.hosted.controller.restapi.Path;
+import com.yahoo.restapi.Path;
import com.yahoo.vespa.hosted.controller.restapi.ResourceResponse;
import com.yahoo.vespa.hosted.controller.versions.VespaVersion.Confidence;
import com.yahoo.yolean.Exceptions;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiHandler.java
index 2f2e72120ea..8a5013a9c16 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiHandler.java
@@ -19,7 +19,7 @@ import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse;
import com.yahoo.vespa.hosted.controller.restapi.SlimeJsonResponse;
import com.yahoo.vespa.hosted.controller.restapi.Uri;
import com.yahoo.vespa.hosted.controller.restapi.application.EmptyJsonResponse;
-import com.yahoo.vespa.hosted.controller.restapi.Path;
+import com.yahoo.restapi.Path;
import com.yahoo.yolean.Exceptions;
import java.util.Optional;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/BlockingRequestFilter.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/BlockingRequestFilter.java
index 5d0a66a040e..3bd504b37d8 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/BlockingRequestFilter.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/BlockingRequestFilter.java
@@ -11,6 +11,7 @@ import com.yahoo.jdisc.http.filter.SecurityRequestFilter;
/**
* @author bjorncs
*/
+@SuppressWarnings("unused") // Injected
public class BlockingRequestFilter implements SecurityRequestFilter {
@Override
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilter.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilter.java
index a05d30a0232..803138e8b3b 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilter.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilter.java
@@ -18,7 +18,7 @@ import com.yahoo.vespa.hosted.controller.TenantController;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.ApplicationAction;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzClientFactory;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsException;
-import com.yahoo.vespa.hosted.controller.restapi.Path;
+import com.yahoo.restapi.Path;
import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant;
import com.yahoo.vespa.hosted.controller.tenant.Tenant;
import com.yahoo.vespa.hosted.controller.tenant.UserTenant;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/screwdriver/ScrewdriverApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/screwdriver/ScrewdriverApiHandler.java
index c1a2c575fc2..74caa4dcb47 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/screwdriver/ScrewdriverApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/screwdriver/ScrewdriverApiHandler.java
@@ -10,9 +10,9 @@ import com.yahoo.slime.Cursor;
import com.yahoo.slime.Slime;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.api.integration.BuildService;
-import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse;
-import com.yahoo.vespa.hosted.controller.restapi.Path;
+import com.yahoo.restapi.Path;
import com.yahoo.vespa.hosted.controller.restapi.SlimeJsonResponse;
import com.yahoo.vespa.hosted.controller.versions.VespaVersion;
import com.yahoo.yolean.Exceptions;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageClient.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageClient.java
new file mode 100644
index 00000000000..93213172048
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageClient.java
@@ -0,0 +1,117 @@
+// 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.restapi.statuspage;
+
+import com.yahoo.slime.Slime;
+import com.yahoo.vespa.config.SlimeUtils;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.time.Duration;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * A basic client for the StatusPage API.
+ *
+ * @author mpolden
+ */
+public class StatusPageClient {
+
+ private static final Duration requestTimeout = Duration.ofSeconds(30);
+
+ private final URI url;
+ private final String key;
+
+ private StatusPageClient(URI url, String key) {
+ this.url = Objects.requireNonNull(url, "url cannot be null");
+ this.key = Objects.requireNonNull(key, "key cannot be null");
+ }
+
+ /** GET given page and return response body as slime */
+ public Slime get(String page, Optional<String> since) {
+ HttpGet get = new HttpGet(pageUrl(page, since));
+ try (CloseableHttpClient client = client()) {
+ try (CloseableHttpResponse response = client.execute(get)) {
+ if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
+ throw new IllegalArgumentException("Received status " + response.getStatusLine().getStatusCode() +
+ " from StatusPage");
+ }
+ byte[] body = EntityUtils.toByteArray(response.getEntity());
+ return SlimeUtils.jsonToSlime(body);
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ URI pageUrl(String page, Optional<String> since) {
+ if (!allowAccess(page)) {
+ throw new IllegalArgumentException("Invalid resource: '" + page + "'");
+ }
+ URIBuilder builder = new URIBuilder(url)
+ .setPath("/api/v2/" + page + ".json")
+ .setParameter("api_key", key);
+ since.ifPresent(s -> builder.setParameter("since", s));
+ try {
+ return builder.build();
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static StatusPageClient create(URI url, String secret) {
+ String[] parts = secret.split(":");
+ if (parts.length != 2) {
+ throw new IllegalArgumentException("Invalid secret");
+ }
+ String pageId = parts[0];
+ String apiKey = parts[1];
+ if (isDefault(url)) {
+ // Rewrite URL to include page ID
+ try {
+ url = new URIBuilder(url).setHost(pageId + "." + url.getHost()).build();
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return new StatusPageClient(url, apiKey);
+ }
+
+ private static CloseableHttpClient client() {
+ HttpClientBuilder builder = HttpClients.custom();
+ RequestConfig requestConfig = RequestConfig.custom()
+ .setConnectionRequestTimeout((int) requestTimeout.toMillis())
+ .setConnectTimeout((int) requestTimeout.toMillis())
+ .setSocketTimeout((int) requestTimeout.toMillis())
+ .build();
+ return builder.setDefaultRequestConfig(requestConfig)
+ .setUserAgent("vespa-statuspage-client")
+ .build();
+ }
+
+ /** Returns whether given page is allowed to be accessed */
+ private static boolean allowAccess(String page) {
+ switch (page) {
+ case "incidents":
+ case "scheduled-maintenances":
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean isDefault(URI url) {
+ return "statuspage.io".equals(url.getHost());
+ }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyHandler.java
new file mode 100644
index 00000000000..9021de366cb
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyHandler.java
@@ -0,0 +1,67 @@
+// 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.restapi.statuspage;
+
+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.container.jdisc.secretstore.SecretStore;
+import com.yahoo.slime.Slime;
+import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse;
+import com.yahoo.restapi.Path;
+import com.yahoo.vespa.hosted.controller.restapi.SlimeJsonResponse;
+import com.yahoo.vespa.hosted.controller.statuspage.config.StatuspageConfig;
+import com.yahoo.yolean.Exceptions;
+
+import java.net.URI;
+import java.util.Optional;
+import java.util.logging.Level;
+
+/**
+ * Proxies requests from the controller to StatusPage.
+ *
+ * @author andreer
+ * @author mpolden
+ */
+@SuppressWarnings("unused") // Handler
+public class StatusPageProxyHandler extends LoggingRequestHandler {
+
+ private static final String secretKey = "vespa_hosted.controller.statuspage_api_key";
+
+ private final SecretStore secretStore;
+ private final URI apiUrl;
+
+ @Inject
+ public StatusPageProxyHandler(Context parentCtx, SecretStore secretStore, StatuspageConfig config) {
+ super(parentCtx);
+ this.secretStore = secretStore;
+ this.apiUrl = URI.create(config.apiUrl());
+ }
+
+ @Override
+ public HttpResponse handle(HttpRequest request) {
+ try {
+ switch (request.getMethod()) {
+ case GET: return handleGET(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(Level.WARNING, "Unexpected error handling '" + request.getUri() + "'", e);
+ return ErrorResponse.internalServerError(Exceptions.toMessageString(e));
+ }
+ }
+
+ private HttpResponse handleGET(HttpRequest request) {
+ Path path = new Path(request.getUri().getPath());
+ if (!path.matches("/statuspage/v1/{page}")) {
+ return ErrorResponse.notFoundError("Nothing at " + path);
+ }
+ StatusPageClient client = StatusPageClient.create(apiUrl, secretStore.getSecret(secretKey));
+ Optional<String> since = Optional.ofNullable(request.getProperty("since"));
+ Slime statusPageResponse = client.get(path.get("page"), since);
+ return new SlimeJsonResponse(statusPageResponse);
+ }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiHandler.java
index 28c17770f6a..11c1e5ec6df 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiHandler.java
@@ -11,7 +11,7 @@ import com.yahoo.slime.Cursor;
import com.yahoo.slime.Slime;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneRegistry;
import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse;
-import com.yahoo.vespa.hosted.controller.restapi.Path;
+import com.yahoo.restapi.Path;
import com.yahoo.vespa.hosted.controller.restapi.SlimeJsonResponse;
import com.yahoo.yolean.Exceptions;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiHandler.java
index 6eec5c4965d..3d86d5da262 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiHandler.java
@@ -13,7 +13,7 @@ import com.yahoo.vespa.hosted.controller.proxy.ConfigServerRestExecutor;
import com.yahoo.vespa.hosted.controller.proxy.ProxyException;
import com.yahoo.vespa.hosted.controller.proxy.ProxyRequest;
import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse;
-import com.yahoo.vespa.hosted.controller.restapi.Path;
+import com.yahoo.restapi.Path;
import com.yahoo.vespa.hosted.controller.restapi.SlimeJsonResponse;
import com.yahoo.yolean.Exceptions;
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 fdfe20d004f..2e7f204e50c 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
@@ -6,10 +6,11 @@ import com.yahoo.collections.ListMap;
import com.yahoo.component.Version;
import com.yahoo.component.Vtag;
import com.yahoo.config.provision.HostName;
+import com.yahoo.log.LogLevel;
import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.Controller;
-import com.yahoo.vespa.hosted.controller.api.integration.github.GitSha;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node;
+import com.yahoo.vespa.hosted.controller.api.integration.github.GitSha;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.application.ApplicationList;
import com.yahoo.vespa.hosted.controller.application.Deployment;
@@ -130,8 +131,15 @@ public class VersionStatus {
ListMap<Version, HostName> versions = new ListMap<>();
for (ZoneId zone : zones) {
for (SystemApplication application : SystemApplication.all()) {
- for (Node node : controller.configServer().nodeRepository().listOperational(zone, application.id())) {
- versions.put(node.currentVersion(), node.hostname());
+ boolean configConverged = application.configConvergedIn(zone, controller);
+ if (!configConverged) {
+ log.log(LogLevel.WARNING, "Config for " + application.id() + " in " + zone + " has not converged");
+ }
+ for (Node node : controller.configServer().nodeRepository().list(zone, application.id(),
+ SystemApplication.activeStates())) {
+ // Only use current node version if config has converged
+ Version nodeVersion = configConverged ? node.currentVersion() : controller.systemVersion();
+ versions.put(nodeVersion, node.hostname());
}
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/restapi/impl/StatusPageResource.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/restapi/impl/StatusPageResource.java
deleted file mode 100644
index 4c479267f2c..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/restapi/impl/StatusPageResource.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.hosted.restapi.impl;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.google.inject.Inject;
-import com.yahoo.container.jaxrs.annotation.Component;
-import com.yahoo.container.jdisc.secretstore.SecretStore;
-
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.ClientBuilder;
-import javax.ws.rs.client.WebTarget;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.UriBuilder;
-
-/**
- * Proxies requests from controller to https://xxx.statuspage.io/api/v2/yyy.json?api_key=zzz[&amp;since=YYYY-MM-DDThh:mm[:ss]±hh:mm]
- *
- * @author andreer
- */
-@Path("/v1/")
-@Produces(MediaType.APPLICATION_JSON)
-public class StatusPageResource implements com.yahoo.vespa.hosted.controller.api.statuspage.StatusPageResource {
-
- private final Client client;
- private final SecretStore secretStore;
-
- @Inject
- public StatusPageResource(@Component SecretStore secretStore) {
- this(secretStore, ClientBuilder.newClient());
- }
-
- protected StatusPageResource(SecretStore secretStore, Client client) {
- this.secretStore = secretStore;
- this.client = client;
- }
-
- protected UriBuilder statusPageURL(String page, String since) {
- String[] secrets = secretStore.getSecret("vespa_hosted.controller.statuspage_api_key").split(":");
- UriBuilder uriBuilder = UriBuilder.fromUri("https://" + secrets[0] + ".statuspage.io/api/v2/" + page + ".json?api_key=" + secrets[1]);
- if (since != null) {
- uriBuilder.queryParam("since", since);
- }
-
- return uriBuilder;
- }
-
- @Override
- public JsonNode statusPage(String page, String since) {
- WebTarget target = client.target(statusPageURL(page, since));
- return target.request().get(JsonNode.class);
- }
-
-}
diff --git a/controller-server/src/main/resources/configdefinitions/athenz.def b/controller-server/src/main/resources/configdefinitions/athenz.def
index f8d65c25e47..8026c0d7f44 100644
--- a/controller-server/src/main/resources/configdefinitions/athenz.def
+++ b/controller-server/src/main/resources/configdefinitions/athenz.def
@@ -42,3 +42,6 @@ service.privateKeySecretName string
# Expiry of service principal token and certificate
service.credentialsExpiryMinutes int default=43200 # 30 days
+
+# Path to athenz.conf file
+athenzConfFile string
diff --git a/controller-server/src/main/resources/configdefinitions/statuspage.def b/controller-server/src/main/resources/configdefinitions/statuspage.def
new file mode 100644
index 00000000000..db8b034f99b
--- /dev/null
+++ b/controller-server/src/main/resources/configdefinitions/statuspage.def
@@ -0,0 +1,4 @@
+# Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+namespace=vespa.hosted.controller.statuspage.config
+
+apiUrl string default=https://statuspage.io
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
index c24c8693688..6e953dcdd57 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
@@ -16,52 +16,39 @@ import com.yahoo.vespa.config.SlimeUtils;
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.identifiers.DeploymentId;
-import com.yahoo.vespa.hosted.controller.api.identifiers.ScrewdriverId;
-import com.yahoo.vespa.hosted.controller.api.integration.BuildService;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.api.integration.dns.Record;
import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordName;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.ApplicationVersion;
-import com.yahoo.vespa.hosted.controller.application.Change;
import com.yahoo.vespa.hosted.controller.application.Deployment;
import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobError;
-import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType;
import com.yahoo.vespa.hosted.controller.application.JobStatus;
import com.yahoo.vespa.hosted.controller.application.SourceRevision;
import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
import com.yahoo.vespa.hosted.controller.deployment.BuildJob;
-import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockBuildService;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester;
-import com.yahoo.vespa.hosted.controller.persistence.ApplicationSerializer;
import com.yahoo.vespa.hosted.controller.rotation.RotationId;
import com.yahoo.vespa.hosted.controller.rotation.RotationLock;
-import com.yahoo.vespa.hosted.controller.versions.DeploymentStatistics;
-import com.yahoo.vespa.hosted.controller.versions.VersionStatus;
-import com.yahoo.vespa.hosted.controller.versions.VespaVersion;
import org.junit.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.Duration;
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
-import java.util.stream.Collectors;
import static com.yahoo.config.provision.SystemName.main;
-import static com.yahoo.vespa.hosted.controller.ControllerTester.buildJob;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.component;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.productionCorpUsEast1;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.productionUsEast3;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.productionUsWest1;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.stagingTest;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.systemTest;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.component;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionCorpUsEast1;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionUsEast3;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.stagingTest;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.systemTest;
+import static java.time.temporal.ChronoUnit.MILLIS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -75,11 +62,6 @@ import static org.junit.Assert.fail;
*/
public class ControllerTest {
- private static final ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
- .environment(Environment.prod)
- .region("corp-us-east-1")
- .build();
-
@Test
public void testDeployment() {
// Setup system
@@ -103,14 +85,14 @@ public class ControllerTest {
assertEquals(4, applications.require(app1.id()).deploymentJobs().jobStatus().size());
ApplicationVersion applicationVersion = tester.controller().applications().require(app1.id()).change().application().get();
- assertTrue("Application version has been set during deployment", applicationVersion != ApplicationVersion.unknown);
+ assertFalse("Application version has been set during deployment", applicationVersion.isUnknown());
assertStatus(JobStatus.initial(stagingTest)
- .withTriggering(version1, applicationVersion, Optional.empty(),"", tester.clock().instant())
- .withCompletion(42, Optional.empty(), tester.clock().instant()), app1.id(), tester.controller());
+ .withTriggering(version1, applicationVersion, Optional.empty(),"", tester.clock().instant().truncatedTo(MILLIS))
+ .withCompletion(42, Optional.empty(), tester.clock().instant().truncatedTo(MILLIS)), app1.id(), tester.controller());
// Causes first deployment job to be triggered
assertStatus(JobStatus.initial(productionCorpUsEast1)
- .withTriggering(version1, applicationVersion, Optional.empty(), "", tester.clock().instant()), app1.id(), tester.controller());
+ .withTriggering(version1, applicationVersion, Optional.empty(), "", tester.clock().instant().truncatedTo(MILLIS)), app1.id(), tester.controller());
tester.clock().advance(Duration.ofSeconds(1));
// production job (failing) after deployment
@@ -119,13 +101,13 @@ public class ControllerTest {
assertEquals(4, applications.require(app1.id()).deploymentJobs().jobStatus().size());
JobStatus expectedJobStatus = JobStatus.initial(productionCorpUsEast1)
- .withTriggering(version1, applicationVersion, Optional.empty(), "", tester.clock().instant()) // Triggered first without application version info
- .withCompletion(42, Optional.of(JobError.unknown), tester.clock().instant())
+ .withTriggering(version1, applicationVersion, Optional.empty(), "", tester.clock().instant().truncatedTo(MILLIS)) // Triggered first without application version info
+ .withCompletion(42, Optional.of(JobError.unknown), tester.clock().instant().truncatedTo(MILLIS))
.withTriggering(version1,
applicationVersion,
Optional.of(tester.application(app1.id()).deployments().get(productionCorpUsEast1.zone(main).get())),
"",
- tester.clock().instant()); // Re-triggering (due to failure) has application version info
+ tester.clock().instant().truncatedTo(MILLIS)); // Re-triggering (due to failure) has application version info
assertStatus(expectedJobStatus, app1.id(), tester.controller());
@@ -148,22 +130,23 @@ public class ControllerTest {
applicationVersion = tester.application("app1").change().application().get();
tester.deployAndNotify(app1, applicationPackage, true, systemTest);
assertStatus(JobStatus.initial(systemTest)
- .withTriggering(version1, applicationVersion, productionCorpUsEast1.zone(main).map(tester.application(app1.id()).deployments()::get), "", tester.clock().instant())
- .withCompletion(42, Optional.empty(), tester.clock().instant()),
+ .withTriggering(version1, applicationVersion, productionCorpUsEast1.zone(main).map(tester.application(app1.id()).deployments()::get), "", tester.clock().instant().truncatedTo(MILLIS))
+ .withCompletion(42, Optional.empty(), tester.clock().instant().truncatedTo(MILLIS)),
app1.id(), tester.controller());
+ tester.clock().advance(Duration.ofHours(1)); // Stop retrying
tester.jobCompletion(productionCorpUsEast1).application(app1).unsuccessful().submit();
tester.deployAndNotify(app1, applicationPackage, true, stagingTest);
// production job succeeding now
expectedJobStatus = expectedJobStatus
- .withTriggering(version1, applicationVersion, productionCorpUsEast1.zone(main).map(tester.application(app1.id()).deployments()::get), "", tester.clock().instant())
- .withCompletion(42, Optional.empty(), tester.clock().instant());
+ .withTriggering(version1, applicationVersion, productionCorpUsEast1.zone(main).map(tester.application(app1.id()).deployments()::get), "", tester.clock().instant().truncatedTo(MILLIS))
+ .withCompletion(42, Optional.empty(), tester.clock().instant().truncatedTo(MILLIS));
tester.deployAndNotify(app1, applicationPackage, true, productionCorpUsEast1);
assertStatus(expectedJobStatus, app1.id(), tester.controller());
// causes triggering of next production job
assertStatus(JobStatus.initial(productionUsEast3)
- .withTriggering(version1, applicationVersion, Optional.empty(), "", tester.clock().instant()),
+ .withTriggering(version1, applicationVersion, Optional.empty(), "", tester.clock().instant().truncatedTo(MILLIS)),
app1.id(), tester.controller());
tester.deployAndNotify(app1, applicationPackage, true, productionUsEast3);
@@ -227,304 +210,6 @@ public class ControllerTest {
}
@Test
- public void testDeployVersion() {
- // Setup system
- DeploymentTester tester = new DeploymentTester();
- ApplicationController applications = tester.controller().applications();
- ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
- .environment(Environment.prod)
- .region("us-west-1")
- .build();
- Version systemVersion = tester.controller().versionStatus().systemVersion().get().versionNumber();
-
- Application app1 = tester.createApplication("application1", "tenant1", 1, 1L);
-
- // First deployment: An application change
- tester.jobCompletion(component).application(app1).uploadArtifact(applicationPackage).submit();
- tester.deployAndNotify(app1, applicationPackage, true, systemTest);
- tester.deployAndNotify(app1, applicationPackage, true, stagingTest);
- tester.deployAndNotify(app1, applicationPackage, true, productionUsWest1);
-
- app1 = applications.require(app1.id());
- assertEquals("First deployment gets system version", systemVersion, app1.oldestDeployedPlatform().get());
- assertEquals(systemVersion, tester.configServer().lastPrepareVersion().get());
-
- // Unexpected deployment
- tester.deploy(productionUsWest1, app1, applicationPackage);
- // applications are immutable, so any change to one, including deployment changes, would give rise to a new instance.
- assertEquals("Unexpected deployment is ignored", app1, applications.require(app1.id()));
-
- // Application change after a new system version, and a region added
- Version newSystemVersion = incrementSystemVersion(tester.controller());
- assertTrue(newSystemVersion.isAfter(systemVersion));
-
- applicationPackage = new ApplicationPackageBuilder()
- .environment(Environment.prod)
- .region("us-west-1")
- .region("us-east-3")
- .build();
- tester.jobCompletion(component).application(app1).nextBuildNumber().uploadArtifact(applicationPackage).submit();
- tester.deployAndNotify(app1, applicationPackage, true, systemTest);
- tester.deployAndNotify(app1, applicationPackage, true, stagingTest);
- tester.deployAndNotify(app1, applicationPackage, true, productionUsWest1);
-
- app1 = applications.require(app1.id());
- assertEquals("Application change preserves version", systemVersion, app1.oldestDeployedPlatform().get());
- assertEquals(systemVersion, tester.configServer().lastPrepareVersion().get());
-
- // A deployment to the new region gets the same version
- tester.deployAndNotify(app1, applicationPackage, true, productionUsEast3);
- app1 = applications.require(app1.id());
- assertEquals("Application change preserves version", systemVersion, app1.oldestDeployedPlatform().get());
- assertEquals(systemVersion, tester.configServer().lastPrepareVersion().get());
- assertFalse("Change deployed", app1.change().isPresent());
-
- // Version upgrade changes system version
- applications.deploymentTrigger().triggerChange(app1.id(), Change.of(newSystemVersion));
- tester.deploymentTrigger().triggerReadyJobs();
- tester.deployAndNotify(app1, applicationPackage, true, systemTest);
- tester.deployAndNotify(app1, applicationPackage, true, stagingTest);
- tester.deployAndNotify(app1, applicationPackage, true, productionUsWest1);
- tester.deployAndNotify(app1, applicationPackage, true, productionUsEast3);
-
- app1 = applications.require(app1.id());
- assertEquals("Version upgrade changes version", newSystemVersion, app1.oldestDeployedPlatform().get());
- assertEquals(newSystemVersion, tester.configServer().lastPrepareVersion().get());
- }
-
- /** Adds a new version, higher than the current system version, makes it the system version and returns it */
- private Version incrementSystemVersion(Controller controller) {
- Version systemVersion = controller.versionStatus().systemVersion().get().versionNumber();
- Version newSystemVersion = new Version(systemVersion.getMajor(), systemVersion.getMinor()+1, 0);
- VespaVersion newSystemVespaVersion = new VespaVersion(DeploymentStatistics.empty(newSystemVersion),
- "commit1",
- Instant.now(),
- true,
- true,
- Collections.emptyList(),
- VespaVersion.Confidence.low
- );
- List<VespaVersion> versions = new ArrayList<>(controller.versionStatus().versions());
- for (int i = 0; i < versions.size(); i++) {
- VespaVersion c = versions.get(i);
- if (c.isSystemVersion())
- versions.set(i, new VespaVersion(c.statistics(), c.releaseCommit(), c.committedAt(),
- false,
- false,
- c.systemApplicationHostnames(),
- c.confidence()));
- }
- versions.add(newSystemVespaVersion);
- controller.updateVersionStatus(new VersionStatus(versions));
- return newSystemVersion;
- }
-
- @Test
- public void testPullRequestDeployment() {
- // Setup system
- ControllerTester tester = new ControllerTester();
- ApplicationController applications = tester.controller().applications();
-
- // staging deployment
- long app1ProjectId = 22;
- ApplicationId app1 = tester.createAndDeploy("tenant1", "domain1",
- "application1", Environment.staging,
- app1ProjectId).id();
-
- // pull-request deployment - uses different instance id
- ApplicationId app1pr = tester.createAndDeploy("tenant1", "domain1",
- "application1", "1",
- Environment.staging, app1ProjectId, null).id();
-
- assertTrue(applications.get(app1).isPresent());
- assertEquals(app1, applications.get(app1).get().id());
- assertTrue(applications.get(app1pr).isPresent());
- assertEquals(app1pr, applications.get(app1pr).get().id());
-
- // Simulate restart
- tester.createNewController();
- applications = tester.controller().applications();
-
- assertTrue(applications.get(app1).isPresent());
- assertEquals(app1, applications.get(app1).get().id());
- assertTrue(applications.get(app1pr).isPresent());
- assertEquals(app1pr, applications.get(app1pr).get().id());
-
- // Deleting application also removes PR instance
- ApplicationId app2 = tester.createAndDeploy("tenant1", "domain1",
- "application2", Environment.staging,
- 33).id();
- tester.controller().applications().deleteApplication(app1, Optional.of(new NToken("ntoken")));
- assertEquals("All instances deleted", 0,
- tester.controller().applications().asList(app1.tenant()).stream()
- .filter(app -> app.id().application().equals(app1.application()))
- .count());
- assertEquals("Other application survives", 1,
- tester.controller().applications().asList(app1.tenant()).stream()
- .filter(app -> app.id().application().equals(app2.application()))
- .count());
- }
-
- @Test
- public void testFailingSinceUpdates() {
- // Setup system
- DeploymentTester tester = new DeploymentTester();
-
- // Setup application
- Application app = tester.createApplication("app1", "foo", 1, 1L);
-
- // Initial failure
- Instant initialFailure = tester.clock().instant();
- tester.jobCompletion(component).application(app).uploadArtifact(applicationPackage).submit();
- tester.deployAndNotify(app, applicationPackage, false, systemTest);
- assertEquals("Failure age is right at initial failure",
- initialFailure, firstFailing(app, tester).get().at());
-
- // Failure again -- failingSince should remain the same
- tester.clock().advance(Duration.ofMillis(1000));
- tester.deployAndNotify(app, applicationPackage, false, systemTest);
- assertEquals("Failure age is right at second consecutive failure",
- initialFailure, firstFailing(app, tester).get().at());
-
- // Success resets failingSince
- tester.clock().advance(Duration.ofMillis(1000));
- tester.deployAndNotify(app, applicationPackage, true, systemTest);
- assertFalse(firstFailing(app, tester).isPresent());
-
- // Complete deployment
- tester.deployAndNotify(app, applicationPackage, true, stagingTest);
- tester.deployAndNotify(app, applicationPackage, true, productionCorpUsEast1);
-
- // Two repeated failures again.
- // Initial failure
- tester.clock().advance(Duration.ofMillis(1000));
- initialFailure = tester.clock().instant();
- tester.jobCompletion(component).application(app).nextBuildNumber().uploadArtifact(applicationPackage).submit();
- tester.deployAndNotify(app, applicationPackage, false, systemTest);
- assertEquals("Failure age is right at initial failure",
- initialFailure, firstFailing(app, tester).get().at());
-
- // Failure again -- failingSince should remain the same
- tester.clock().advance(Duration.ofMillis(1000));
- tester.deployAndNotify(app, applicationPackage, false, systemTest);
- assertEquals("Failure age is right at second consecutive failure",
- initialFailure, firstFailing(app, tester).get().at());
- }
-
- private Optional<JobStatus.JobRun> firstFailing(Application application, DeploymentTester tester) {
- return tester.controller().applications().get(application.id()).get().deploymentJobs().jobStatus().get(systemTest).firstFailing();
- }
-
- @Test
- public void requeueOutOfCapacityStagingJob() {
- DeploymentTester tester = new DeploymentTester();
-
- long project1 = 1;
- long project2 = 2;
- long project3 = 3;
- Application app1 = tester.createApplication("app1", "tenant1", project1, 1L);
- Application app2 = tester.createApplication("app2", "tenant2", project2, 1L);
- Application app3 = tester.createApplication("app3", "tenant3", project3, 1L);
- MockBuildService mockBuildService = tester.buildService();
-
- // all applications: system-test completes successfully with some time in between, to determine trigger order.
- tester.jobCompletion(component).application(app2).uploadArtifact(applicationPackage).submit();
- tester.deployAndNotify(app2, applicationPackage, true, systemTest);
- tester.clock().advance(Duration.ofMinutes(1));
-
- tester.jobCompletion(component).application(app1).uploadArtifact(applicationPackage).submit();
- tester.deployAndNotify(app1, applicationPackage, true, systemTest);
- tester.clock().advance(Duration.ofMinutes(1));
-
- tester.jobCompletion(component).application(app3).uploadArtifact(applicationPackage).submit();
- tester.deployAndNotify(app3, applicationPackage, true, systemTest);
-
- // all applications: staging test jobs queued
- assertEquals(3, mockBuildService.jobs().size());
-
- // Abort all running jobs, so we have three candidate jobs, of which only one should be triggered at a time.
- tester.buildService().clear();
-
- List<BuildService.BuildJob> jobs = new ArrayList<>();
- assertJobsInOrder(jobs, tester.buildService().jobs());
-
- tester.triggerUntilQuiescence();
- jobs.add(buildJob(app2, stagingTest));
- jobs.add(buildJob(app1, stagingTest));
- jobs.add(buildJob(app3, stagingTest));
- assertJobsInOrder(jobs, tester.buildService().jobs());
-
- // Remove the jobs for app1 and app2, and then let app3 fail with outOfCapacity.
- // All three jobs are now eligible, but the one for app3 should trigger first as an outOfCapacity-retry.
- tester.buildService().remove(buildJob(app1, stagingTest));
- tester.buildService().remove(buildJob(app2, stagingTest));
- jobs.remove(buildJob(app1, stagingTest));
- jobs.remove(buildJob(app2, stagingTest));
- tester.jobCompletion(stagingTest).application(app3).error(JobError.outOfCapacity).submit();
- assertJobsInOrder(jobs, tester.buildService().jobs());
-
- tester.triggerUntilQuiescence();
- jobs.add(buildJob(app2, stagingTest));
- jobs.add(buildJob(app1, stagingTest));
- assertJobsInOrder(jobs, tester.buildService().jobs());
-
- // Finish deployment for apps 2 and 3, then release a new version, leaving only app1 with an application upgrade.
- tester.deployAndNotify(app2, applicationPackage, true, stagingTest);
- tester.deployAndNotify(app2, applicationPackage, true, productionCorpUsEast1);
- tester.deployAndNotify(app3, applicationPackage, true, stagingTest);
- tester.deployAndNotify(app3, applicationPackage, true, productionCorpUsEast1);
-
- tester.upgradeSystem(new Version("6.2"));
- // app1 also gets a new application change, so its time of availability is after the version upgrade.
- tester.clock().advance(Duration.ofMinutes(1));
- tester.buildService().clear();
- tester.jobCompletion(component).application(app1).nextBuildNumber().uploadArtifact(applicationPackage).submit();
- jobs.clear();
- jobs.add(buildJob(app1, stagingTest));
- jobs.add(buildJob(app1, systemTest));
- // Tests for app1 trigger before the others since it carries an application upgrade.
- assertJobsInOrder(jobs, tester.buildService().jobs());
-
- // Let the test jobs start, remove everything expect system test for app3, which fails with outOfCapacity again.
- tester.triggerUntilQuiescence();
- tester.buildService().remove(buildJob(app1, systemTest));
- tester.buildService().remove(buildJob(app2, systemTest));
- tester.buildService().remove(buildJob(app1, stagingTest));
- tester.buildService().remove(buildJob(app2, stagingTest));
- tester.buildService().remove(buildJob(app3, stagingTest));
- tester.jobCompletion(systemTest).application(app3).error(JobError.outOfCapacity).submit();
- jobs.clear();
- jobs.add(buildJob(app1, stagingTest));
- jobs.add(buildJob(app3, systemTest));
- assertJobsInOrder(jobs, tester.buildService().jobs());
-
- tester.triggerUntilQuiescence();
- jobs.add(buildJob(app2, stagingTest));
- jobs.add(buildJob(app1, systemTest));
- jobs.add(buildJob(app3, stagingTest));
- jobs.add(buildJob(app2, systemTest));
- assertJobsInOrder(jobs, tester.buildService().jobs());
-
- }
-
- /** Verifies that the given job lists have the same jobs, ignoring order of jobs that may have been triggered concurrently. */
- private static void assertJobsInOrder(List<BuildService.BuildJob> expected, List<BuildService.BuildJob> actual) {
- assertEquals(expected.stream().filter(job -> job.jobName().equals("system-test")).collect(Collectors.toList()),
- actual.stream().filter(job -> job.jobName().equals("system-test")).collect(Collectors.toList()));
- assertEquals(expected.stream().filter(job -> job.jobName().equals("staging-test")).collect(Collectors.toList()),
- actual.stream().filter(job -> job.jobName().equals("staging-test")).collect(Collectors.toList()));
- assertTrue(expected.containsAll(actual));
- assertTrue(actual.containsAll(expected));
- }
-
- private void assertStatus(JobStatus expectedStatus, ApplicationId id, Controller controller) {
- Application app = controller.applications().get(id).get();
- JobStatus existingStatus = app.deploymentJobs().jobStatus().get(expectedStatus.type());
- assertNotNull("Status of type " + expectedStatus.type() + " is present", existingStatus);
- assertEquals(expectedStatus, existingStatus);
- }
-
- @Test
public void testGlobalRotations() throws IOException {
// Setup tester and app def
ControllerTester tester = new ControllerTester();
@@ -536,18 +221,18 @@ public class ControllerTest {
Map<String, EndpointStatus> rotationStatus = tester.controller().applications().getGlobalRotationStatus(deployId);
assertEquals(1, rotationStatus.size());
- assertTrue(rotationStatus.get("qrs-endpoint").getStatus().equals(EndpointStatus.Status.in));
+ assertEquals(rotationStatus.get("qrs-endpoint").getStatus(), EndpointStatus.Status.in);
// Set the global rotations out of service
- EndpointStatus status = new EndpointStatus(EndpointStatus.Status.out, "Testing I said", "Test", tester.clock().instant().getEpochSecond());
+ EndpointStatus status = new EndpointStatus(EndpointStatus.Status.out, "unit-test", "Test", tester.clock().instant().getEpochSecond());
List<String> overrides = tester.controller().applications().setGlobalRotationStatus(deployId, status);
assertEquals(1, overrides.size());
// Recheck the override rotation status
rotationStatus = tester.controller().applications().getGlobalRotationStatus(deployId);
assertEquals(1, rotationStatus.size());
- assertTrue(rotationStatus.get("qrs-endpoint").getStatus().equals(EndpointStatus.Status.out));
- assertTrue(rotationStatus.get("qrs-endpoint").getReason().equals("Testing I said"));
+ assertEquals(rotationStatus.get("qrs-endpoint").getStatus(), EndpointStatus.Status.out);
+ assertEquals("unit-test", rotationStatus.get("qrs-endpoint").getReason());
}
@Test
@@ -781,7 +466,7 @@ public class ControllerTest {
}
@Test
- public void testDeployWithoutProjectId() {
+ public void testDeployDirectly() {
DeploymentTester tester = new DeploymentTester();
tester.controllerTester().zoneRegistry().setSystemName(SystemName.cd);
tester.controllerTester().zoneRegistry().setZones(ZoneId.from("prod", "cd-us-central-1"));
@@ -793,7 +478,7 @@ public class ControllerTest {
// Create application
Application app = tester.createApplication("app1", "tenant1", 1, 2L);
- // Direct deploy is allowed when project ID is missing
+ // Direct deploy is allowed when deployDirectly is true
ZoneId zone = ZoneId.from("prod", "cd-us-central-1");
// Same options as used in our integration tests
DeployOptions options = new DeployOptions(true, Optional.empty(), false,
@@ -805,7 +490,6 @@ public class ControllerTest {
assertTrue("No job status added",
tester.applications().require(app.id()).deploymentJobs().jobStatus().isEmpty());
-
}
private void runUpgrade(DeploymentTester tester, ApplicationId application, ApplicationVersion version) {
@@ -831,6 +515,13 @@ public class ControllerTest {
runDeployment(tester, app, version, Optional.empty(), Optional.of(applicationPackage));
}
+ private void assertStatus(JobStatus expectedStatus, ApplicationId id, Controller controller) {
+ Application app = controller.applications().get(id).get();
+ JobStatus existingStatus = app.deploymentJobs().jobStatus().get(expectedStatus.type());
+ assertNotNull("Status of type " + expectedStatus.type() + " is present", existingStatus);
+ assertEquals(expectedStatus, existingStatus);
+ }
+
private void runDeployment(DeploymentTester tester, Application app, ApplicationVersion version,
Optional<Version> upgrade, Optional<ApplicationPackage> applicationPackage) {
Version vespaVersion = upgrade.orElseGet(tester::defaultPlatformVersion);
@@ -839,23 +530,23 @@ public class ControllerTest {
tester.deployAndNotify(app, applicationPackage, true, systemTest);
tester.deployAndNotify(app, applicationPackage, true, stagingTest);
JobStatus expected = JobStatus.initial(stagingTest)
- .withTriggering(vespaVersion, version, productionCorpUsEast1.zone(main).map(tester.application(app.id()).deployments()::get), "",
- tester.clock().instant())
- .withCompletion(42, Optional.empty(), tester.clock().instant());
+ .withTriggering(vespaVersion, version, productionCorpUsEast1.zone(main).map(tester.application(app.id()).deployments()::get), "",
+ tester.clock().instant().truncatedTo(MILLIS))
+ .withCompletion(42, Optional.empty(), tester.clock().instant().truncatedTo(MILLIS));
assertStatus(expected, app.id(), tester.controller());
// Deploy in production
expected = JobStatus.initial(productionCorpUsEast1)
- .withTriggering(vespaVersion, version, productionCorpUsEast1.zone(main).map(tester.application(app.id()).deployments()::get), "",
- tester.clock().instant())
- .withCompletion(42, Optional.empty(), tester.clock().instant());
+ .withTriggering(vespaVersion, version, productionCorpUsEast1.zone(main).map(tester.application(app.id()).deployments()::get), "",
+ tester.clock().instant().truncatedTo(MILLIS))
+ .withCompletion(42, Optional.empty(), tester.clock().instant().truncatedTo(MILLIS));
tester.deployAndNotify(app, applicationPackage, true, productionCorpUsEast1);
assertStatus(expected, app.id(), tester.controller());
expected = JobStatus.initial(productionUsEast3)
- .withTriggering(vespaVersion, version, productionUsEast3.zone(main).map(tester.application(app.id()).deployments()::get), "",
- tester.clock().instant())
- .withCompletion(42, Optional.empty(), tester.clock().instant());
+ .withTriggering(vespaVersion, version, productionUsEast3.zone(main).map(tester.application(app.id()).deployments()::get), "",
+ tester.clock().instant().truncatedTo(MILLIS))
+ .withCompletion(42, Optional.empty(), tester.clock().instant().truncatedTo(MILLIS));
tester.deployAndNotify(app, applicationPackage, true, productionUsEast3);
assertStatus(expected, app.id(), tester.controller());
@@ -867,33 +558,4 @@ public class ControllerTest {
}
}
- @Test
- public void testDeploymentOfNewInstanceWithIllegalApplicationName() {
- ControllerTester tester = new ControllerTester();
- String application = "this_application_name_is_far_too_long_and_has_underscores";
- ZoneId zone = ZoneId.from("test", "us-east-1");
- DeployOptions options = new DeployOptions(false,
- Optional.empty(),
- false,
- false);
-
- tester.createTenant("tenant", "domain", null);
-
- // Deploy an application which doesn't yet exist, and which has an illegal application name.
- try {
- tester.controller().applications().deploy(ApplicationId.from("tenant", application, "123"), zone, Optional.empty(), options);
- fail("Illegal application name should cause validation exception.");
- }
- catch (IllegalArgumentException e) {
- assertTrue(e.getMessage().contains("Invalid id"));
- }
-
- // Sneak an illegal application in the back door.
- tester.createApplication(new ApplicationSerializer().toSlime(new Application(ApplicationId.from("tenant", application, "default"))));
-
- // Deploy a PR instance for the application, with no NToken.
- tester.controller().applications().deploy(ApplicationId.from("tenant", application, "456"), zone, Optional.empty(), options);
- assertTrue(tester.controller().applications().get(ApplicationId.from("tenant", application, "456")).isPresent());
- }
-
}
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 cf2fa182d0a..324ae4440cd 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
@@ -16,6 +16,7 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId;
import com.yahoo.vespa.hosted.controller.api.integration.BuildService;
import com.yahoo.vespa.hosted.controller.api.integration.chef.ChefMock;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.ArtifactRepository;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.api.integration.dns.MemoryNameService;
import com.yahoo.vespa.hosted.controller.api.integration.entity.EntityService;
import com.yahoo.vespa.hosted.controller.api.integration.entity.MemoryEntityService;
@@ -23,16 +24,19 @@ import com.yahoo.vespa.hosted.controller.api.integration.github.GitHubMock;
import com.yahoo.vespa.hosted.controller.api.integration.organization.MockOrganization;
import com.yahoo.vespa.hosted.controller.api.integration.routing.MemoryGlobalRoutingService;
import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockBuildService;
+import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockLogStore;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
-import com.yahoo.vespa.hosted.controller.application.DeploymentJobs;
import com.yahoo.vespa.hosted.controller.athenz.mock.AthenzClientFactoryMock;
import com.yahoo.vespa.hosted.controller.athenz.mock.AthenzDbMock;
-import com.yahoo.vespa.hosted.controller.integration.MockMetricsService;
+import com.yahoo.vespa.hosted.controller.integration.ArtifactRepositoryMock;
+import com.yahoo.vespa.hosted.controller.integration.ConfigServerMock;
+import com.yahoo.vespa.hosted.controller.integration.MetricsServiceMock;
+import com.yahoo.vespa.hosted.controller.integration.ZoneRegistryMock;
import com.yahoo.vespa.hosted.controller.persistence.ApplicationSerializer;
import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
import com.yahoo.vespa.hosted.controller.persistence.MockCuratorDb;
-import com.yahoo.vespa.hosted.controller.routing.MockRoutingGenerator;
+import com.yahoo.vespa.hosted.controller.integration.RoutingGeneratorMock;
import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant;
import com.yahoo.vespa.hosted.controller.tenant.Tenant;
import com.yahoo.vespa.hosted.controller.versions.VersionStatus;
@@ -64,12 +68,12 @@ public final class ControllerTester {
private final ArtifactRepositoryMock artifactRepository;
private final EntityService entityService;
private final MockBuildService buildService;
- private final MockMetricsService metricsService;
+ private final MetricsServiceMock metricsService;
private Controller controller;
public ControllerTester(ManualClock clock, RotationsConfig rotationsConfig, MockCuratorDb curatorDb,
- MockMetricsService metricsService) {
+ MetricsServiceMock metricsService) {
this(new AthenzDbMock(), clock, new ConfigServerMock(new ZoneRegistryMock()),
new ZoneRegistryMock(), new GitHubMock(), curatorDb, rotationsConfig,
new MemoryNameService(), new ArtifactRepositoryMock(), new MemoryEntityService(), new MockBuildService(),
@@ -77,15 +81,15 @@ public final class ControllerTester {
}
public ControllerTester(ManualClock clock) {
- this(clock, defaultRotationsConfig(), new MockCuratorDb(), new MockMetricsService());
+ this(clock, defaultRotationsConfig(), new MockCuratorDb(), new MetricsServiceMock());
}
public ControllerTester(RotationsConfig rotationsConfig) {
- this(new ManualClock(), rotationsConfig, new MockCuratorDb(), new MockMetricsService());
+ this(new ManualClock(), rotationsConfig, new MockCuratorDb(), new MetricsServiceMock());
}
public ControllerTester(MockCuratorDb curatorDb) {
- this(new ManualClock(), defaultRotationsConfig(), curatorDb, new MockMetricsService());
+ this(new ManualClock(), defaultRotationsConfig(), curatorDb, new MetricsServiceMock());
}
public ControllerTester() {
@@ -97,7 +101,7 @@ public final class ControllerTester {
GitHubMock gitHub, CuratorDb curator, RotationsConfig rotationsConfig,
MemoryNameService nameService, ArtifactRepositoryMock artifactRepository,
EntityService entityService, MockBuildService buildService,
- MockMetricsService metricsService) {
+ MetricsServiceMock metricsService) {
this.athenzDb = athenzDb;
this.clock = clock;
this.configServer = configServer;
@@ -122,7 +126,7 @@ public final class ControllerTester {
});
}
- public static BuildService.BuildJob buildJob(Application application, DeploymentJobs.JobType jobType) {
+ public static BuildService.BuildJob buildJob(Application application, JobType jobType) {
return BuildService.BuildJob.of(application.id(), application.deploymentJobs().projectId().getAsLong(), jobType.jobName());
}
@@ -144,7 +148,7 @@ public final class ControllerTester {
public MockBuildService buildService() { return buildService; }
- public MockMetricsService metricsService() { return metricsService; }
+ public MetricsServiceMock metricsService() { return metricsService; }
/** Create a new controller instance. Useful to verify that controller state is rebuilt from persistence */
public final void createNewController() {
@@ -263,7 +267,7 @@ public final class ControllerTester {
GitHubMock gitHub, ZoneRegistryMock zoneRegistryMock,
AthenzDbMock athensDb, MemoryNameService nameService,
ArtifactRepository artifactRepository, EntityService entityService,
- BuildService buildService, MockMetricsService metricsService) {
+ BuildService buildService, MetricsServiceMock metricsService) {
Controller controller = new Controller(curator,
rotationsConfig,
gitHub,
@@ -274,13 +278,15 @@ public final class ControllerTester {
configServer,
metricsService,
nameService,
- new MockRoutingGenerator(),
+ new RoutingGeneratorMock(),
new ChefMock(),
clock,
new AthenzClientFactoryMock(athensDb),
artifactRepository,
buildService,
+ new MockLogStore(),
() -> "test-controller");
+ // Calculate initial versions
controller.updateVersionStatus(VersionStatus.compute(controller));
return controller;
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/TestIdentities.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/TestIdentities.java
index a2fec7cfdbf..18d3e92620d 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/TestIdentities.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/TestIdentities.java
@@ -12,14 +12,14 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.RegionId;
*/
public class TestIdentities {
- public static EnvironmentId environment = new EnvironmentId("dev");
+ public static final EnvironmentId environment = new EnvironmentId("dev");
- public static RegionId region = new RegionId("us-east-1");
+ public static final RegionId region = new RegionId("us-east-1");
- public static InstanceId instance = new InstanceId("default");
+ public static final InstanceId instance = new InstanceId("default");
- public static Property property = new Property("property");
+ public static final Property property = new Property("property");
- public static NToken userNToken = new NToken("dummy");
+ public static final NToken userNToken = new NToken("dummy");
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzPrincipalFilterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzPrincipalFilterTest.java
index cec3930f9dd..d6481b741dd 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzPrincipalFilterTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzPrincipalFilterTest.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.controller.athenz.filter;
+import com.yahoo.application.container.handler.Request;
import com.yahoo.jdisc.Response;
import com.yahoo.jdisc.handler.ContentChannel;
import com.yahoo.jdisc.handler.ReadableContentChannel;
@@ -13,7 +14,9 @@ import com.yahoo.vespa.athenz.api.NToken;
import com.yahoo.vespa.athenz.tls.KeyAlgorithm;
import com.yahoo.vespa.athenz.tls.KeyUtils;
import com.yahoo.vespa.athenz.tls.X509CertificateBuilder;
-import com.yahoo.vespa.hosted.controller.api.integration.athenz.InvalidTokenException;
+import com.yahoo.vespa.athenz.utils.ntoken.AthenzTruststore;
+import com.yahoo.vespa.athenz.utils.ntoken.NTokenValidator;
+import com.yahoo.vespa.hosted.controller.restapi.ApplicationRequestToDiscFilterRequestWrapper;
import org.junit.Before;
import org.junit.Test;
@@ -26,22 +29,20 @@ import java.security.KeyPair;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.time.Instant;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import static com.yahoo.jdisc.Response.Status.UNAUTHORIZED;
import static com.yahoo.vespa.athenz.tls.SignatureAlgorithm.SHA256_WITH_RSA;
-import static java.util.Collections.emptyList;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.joining;
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.notNullValue;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
/**
* @author bjorncs
@@ -55,115 +56,98 @@ public class AthenzPrincipalFilterTest {
private static final String ORIGIN = "http://localhost";
private static final Set<String> CORS_ALLOWED_URLS = singleton(ORIGIN);
- private NTokenValidator validator;
+ private NTokenValidatorMock validator;
+ private ResponseHandlerMock responseHandler;
@Before
public void before() {
- validator = mock(NTokenValidator.class);
+ this.validator = new NTokenValidatorMock();
+ this.responseHandler = new ResponseHandlerMock();
}
@Test
public void valid_ntoken_is_accepted() {
- DiscFilterRequest request = createRequestMock();
- AthenzPrincipal principal = new AthenzPrincipal(IDENTITY, NTOKEN);
- when(request.getHeader(ATHENZ_PRINCIPAL_HEADER)).thenReturn(NTOKEN.getRawToken());
- when(request.getClientCertificateChain()).thenReturn(emptyList());
- when(validator.validate(NTOKEN)).thenReturn(principal);
+ Request request = defaultRequest();
- AthenzPrincipalFilter filter = new AthenzPrincipalFilter(validator, Runnable::run, ATHENZ_PRINCIPAL_HEADER, CORS_ALLOWED_URLS);
- filter.filter(request, new ResponseHandlerMock());
+ AthenzPrincipal principal = new AthenzPrincipal(IDENTITY, NTOKEN);
+ validator.add(NTOKEN, principal);
- verify(request).setUserPrincipal(principal);
- }
+ AthenzPrincipalFilter filter = new AthenzPrincipalFilter(validator, ATHENZ_PRINCIPAL_HEADER, CORS_ALLOWED_URLS);
+ DiscFilterRequest filterRequest = new ApplicationRequestToDiscFilterRequestWrapper(request);
+ filter.filter(filterRequest, new ResponseHandlerMock());
- private DiscFilterRequest createRequestMock() {
- DiscFilterRequest request = mock(DiscFilterRequest.class);
- when(request.getHeader("Origin")).thenReturn(ORIGIN);
- return request;
+ assertEquals(principal, filterRequest.getUserPrincipal());
}
@Test
public void missing_token_and_certificate_is_unauthorized() {
- DiscFilterRequest request = createRequestMock();
- when(request.getHeader(ATHENZ_PRINCIPAL_HEADER)).thenReturn(null);
- when(request.getClientCertificateChain()).thenReturn(emptyList());
-
- ResponseHandlerMock responseHandler = new ResponseHandlerMock();
-
- AthenzPrincipalFilter filter = new AthenzPrincipalFilter(validator, Runnable::run, ATHENZ_PRINCIPAL_HEADER, CORS_ALLOWED_URLS);
- filter.filter(request, responseHandler);
+ AthenzPrincipalFilter filter = new AthenzPrincipalFilter(validator, ATHENZ_PRINCIPAL_HEADER, CORS_ALLOWED_URLS);
+ DiscFilterRequest filterRequest = new ApplicationRequestToDiscFilterRequestWrapper(new Request("/"));
+ filter.filter(filterRequest, responseHandler);
assertUnauthorized(responseHandler, "Unable to authenticate Athenz identity");
}
@Test
public void invalid_token_is_unauthorized() {
- DiscFilterRequest request = createRequestMock();
- String errorMessage = "Invalid token";
- when(request.getHeader(ATHENZ_PRINCIPAL_HEADER)).thenReturn(NTOKEN.getRawToken());
- when(request.getClientCertificateChain()).thenReturn(emptyList());
- when(validator.validate(NTOKEN)).thenThrow(new InvalidTokenException(errorMessage));
-
- ResponseHandlerMock responseHandler = new ResponseHandlerMock();
+ Request request = defaultRequest();
- AthenzPrincipalFilter filter = new AthenzPrincipalFilter(validator, Runnable::run, ATHENZ_PRINCIPAL_HEADER, CORS_ALLOWED_URLS);
- filter.filter(request, responseHandler);
+ AthenzPrincipalFilter filter = new AthenzPrincipalFilter(validator, ATHENZ_PRINCIPAL_HEADER, CORS_ALLOWED_URLS);
+ DiscFilterRequest filterRequest = new ApplicationRequestToDiscFilterRequestWrapper(request);
+ filter.filter(filterRequest, responseHandler);
+ String errorMessage = "Invalid token";
assertUnauthorized(responseHandler, errorMessage);
}
@Test
public void certificate_is_accepted() {
- DiscFilterRequest request = createRequestMock();
- when(request.getHeader(ATHENZ_PRINCIPAL_HEADER)).thenReturn(null);
- when(request.getClientCertificateChain()).thenReturn(singletonList(CERTIFICATE));
-
- ResponseHandlerMock responseHandler = new ResponseHandlerMock();
-
- AthenzPrincipalFilter filter = new AthenzPrincipalFilter(validator, Runnable::run, ATHENZ_PRINCIPAL_HEADER, CORS_ALLOWED_URLS);
- filter.filter(request, responseHandler);
+ AthenzPrincipalFilter filter = new AthenzPrincipalFilter(validator, ATHENZ_PRINCIPAL_HEADER, CORS_ALLOWED_URLS);
+ DiscFilterRequest filterRequest = new ApplicationRequestToDiscFilterRequestWrapper(new Request("/"), singletonList(CERTIFICATE));
+ filter.filter(filterRequest, responseHandler);
AthenzPrincipal expectedPrincipal = new AthenzPrincipal(IDENTITY);
- verify(request).setUserPrincipal(expectedPrincipal);
+ assertEquals(expectedPrincipal, filterRequest.getUserPrincipal());
}
@Test
public void both_ntoken_and_certificate_is_accepted() {
- DiscFilterRequest request = createRequestMock();
- AthenzPrincipal principalWithToken = new AthenzPrincipal(IDENTITY, NTOKEN);
- when(request.getHeader(ATHENZ_PRINCIPAL_HEADER)).thenReturn(NTOKEN.getRawToken());
- when(request.getClientCertificateChain()).thenReturn(singletonList(CERTIFICATE));
- when(validator.validate(NTOKEN)).thenReturn(principalWithToken);
+ Request request = defaultRequest();
- ResponseHandlerMock responseHandler = new ResponseHandlerMock();
+ AthenzPrincipal principalWithToken = new AthenzPrincipal(IDENTITY, NTOKEN);
+ validator.add(NTOKEN, principalWithToken);
- AthenzPrincipalFilter filter = new AthenzPrincipalFilter(validator, Runnable::run, ATHENZ_PRINCIPAL_HEADER, CORS_ALLOWED_URLS);
- filter.filter(request, responseHandler);
+ AthenzPrincipalFilter filter = new AthenzPrincipalFilter(validator, ATHENZ_PRINCIPAL_HEADER, CORS_ALLOWED_URLS);
+ DiscFilterRequest filterRequest = new ApplicationRequestToDiscFilterRequestWrapper(request, singletonList(CERTIFICATE));
+ filter.filter(filterRequest, responseHandler);
- verify(request).setUserPrincipal(principalWithToken);
+ assertEquals(principalWithToken, filterRequest.getUserPrincipal());
}
@Test
public void conflicting_ntoken_and_certificate_is_unauthorized() {
- DiscFilterRequest request = createRequestMock();
- AthenzUser conflictingIdentity = AthenzUser.fromUserId("mallory");
- when(request.getHeader(ATHENZ_PRINCIPAL_HEADER)).thenReturn(NTOKEN.getRawToken());
- when(request.getClientCertificateChain())
- .thenReturn(singletonList(createSelfSignedCertificate(conflictingIdentity)));
- when(validator.validate(NTOKEN)).thenReturn(new AthenzPrincipal(IDENTITY));
+ Request request = defaultRequest();
+ validator.add(NTOKEN, new AthenzPrincipal(IDENTITY));
- ResponseHandlerMock responseHandler = new ResponseHandlerMock();
-
- AthenzPrincipalFilter filter = new AthenzPrincipalFilter(validator, Runnable::run, ATHENZ_PRINCIPAL_HEADER, CORS_ALLOWED_URLS);
- filter.filter(request, responseHandler);
+ AthenzUser conflictingIdentity = AthenzUser.fromUserId("mallory");
+ DiscFilterRequest filterRequest = new ApplicationRequestToDiscFilterRequestWrapper(request, singletonList(createSelfSignedCertificate(conflictingIdentity)));
+ AthenzPrincipalFilter filter = new AthenzPrincipalFilter(validator, ATHENZ_PRINCIPAL_HEADER, CORS_ALLOWED_URLS);
+ filter.filter(filterRequest, responseHandler);
assertUnauthorized(responseHandler, "Identity in principal token does not match x509 CN");
}
+ private static Request defaultRequest() {
+ Request request = new Request("/");
+ request.getHeaders().add("Origin", ORIGIN);
+ request.getHeaders().add(ATHENZ_PRINCIPAL_HEADER, NTOKEN.getRawToken());
+ return request;
+ }
+
private static void assertUnauthorized(ResponseHandlerMock responseHandler, String expectedMessageSubstring) {
- assertThat(responseHandler.response, notNullValue());
- assertThat(responseHandler.response.getStatus(), equalTo(UNAUTHORIZED));
- assertThat(responseHandler.getResponseContent(), containsString(expectedMessageSubstring));
+ assertNotNull(responseHandler.response);
+ assertEquals(UNAUTHORIZED, responseHandler.response.getStatus());
+ assertTrue(responseHandler.getResponseContent().contains(expectedMessageSubstring));
}
private static class ResponseHandlerMock implements ResponseHandler {
@@ -188,6 +172,29 @@ public class AthenzPrincipalFilterTest {
}
+ private static class NTokenValidatorMock extends NTokenValidator {
+
+ private final Map<NToken, AthenzPrincipal> validTokens = new HashMap<>();
+
+ NTokenValidatorMock() {
+ super((AthenzTruststore)null);
+ }
+
+ public NTokenValidatorMock add(NToken token, AthenzPrincipal principal) {
+ validTokens.put(token, principal);
+ return this;
+ }
+
+ @Override
+ public AthenzPrincipal validate(NToken token) throws InvalidTokenException {
+ if (!validTokens.containsKey(token)) {
+ throw new InvalidTokenException("Invalid token");
+ }
+ return validTokens.get(token);
+ }
+
+ }
+
private static X509Certificate createSelfSignedCertificate(AthenzIdentity identity) {
KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.RSA, 512);
X500Principal x500Name = new X500Principal("CN="+ identity.getFullName());
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzTestUtils.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzTestUtils.java
deleted file mode 100644
index 40b38254dda..00000000000
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzTestUtils.java
+++ /dev/null
@@ -1,22 +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.hosted.controller.athenz.filter;
-
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
-
-/**
- * @author bjorncs
- */
-public class AthenzTestUtils {
- public static KeyPair generateRsaKeypair() {
- try {
- KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
- keyGen.initialize(512);
- return keyGen.genKeyPair();
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException(e);
- }
- }
-
-}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ApplicationPackageBuilder.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ApplicationPackageBuilder.java
index a81c4adcb2e..2f9703b91e1 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ApplicationPackageBuilder.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ApplicationPackageBuilder.java
@@ -26,12 +26,13 @@ import java.util.zip.ZipOutputStream;
*/
public class ApplicationPackageBuilder {
- private String upgradePolicy = null;
- private Environment environment = Environment.prod;
- private String globalServiceId = null;
private final StringBuilder environmentBody = new StringBuilder();
private final StringBuilder validationOverridesBody = new StringBuilder();
private final StringBuilder blockChange = new StringBuilder();
+
+ private String upgradePolicy = null;
+ private Environment environment = Environment.prod;
+ private String globalServiceId = null;
private String athenzIdentityAttributes = null;
private String searchDefinition = "search test { }";
@@ -145,8 +146,7 @@ public class ApplicationPackageBuilder {
public ApplicationPackage build() {
ByteArrayOutputStream zip = new ByteArrayOutputStream();
- ZipOutputStream out = new ZipOutputStream(zip);
- try {
+ try (ZipOutputStream out = new ZipOutputStream(zip)) {
out.putNextEntry(new ZipEntry("deployment.xml"));
out.write(deploymentSpec());
out.closeEntry();
@@ -158,10 +158,6 @@ public class ApplicationPackageBuilder {
out.closeEntry();
} catch (IOException e) {
throw new UncheckedIOException(e);
- } finally {
- try {
- out.close();
- } catch (IOException ignored) {}
}
return new ApplicationPackage(zip.toByteArray());
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/BuildJob.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/BuildJob.java
index 821eb237530..0a36787e8f7 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/BuildJob.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/BuildJob.java
@@ -3,8 +3,10 @@ package com.yahoo.vespa.hosted.controller.deployment;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.vespa.hosted.controller.Application;
-import com.yahoo.vespa.hosted.controller.ArtifactRepositoryMock;
+import com.yahoo.vespa.hosted.controller.integration.ArtifactRepositoryMock;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
+import com.yahoo.vespa.hosted.controller.application.ApplicationVersion;
import com.yahoo.vespa.hosted.controller.application.DeploymentJobs;
import com.yahoo.vespa.hosted.controller.application.SourceRevision;
@@ -24,7 +26,7 @@ public class BuildJob {
"master", "commit1");
public static final long defaultBuildNumber = 42;
- private DeploymentJobs.JobType job;
+ private JobType job;
private ApplicationId applicationId;
private Optional<DeploymentJobs.JobError> jobError = Optional.empty();
private Optional<SourceRevision> sourceRevision = Optional.of(defaultSourceRevision);
@@ -41,7 +43,7 @@ public class BuildJob {
this.artifactRepository = artifactRepository;
}
- public BuildJob type(DeploymentJobs.JobType job) {
+ public BuildJob type(JobType job) {
this.job = job;
return this;
}
@@ -105,24 +107,20 @@ public class BuildJob {
public BuildJob uploadArtifact(ApplicationPackage applicationPackage) {
Objects.requireNonNull(job, "job cannot be null");
Objects.requireNonNull(applicationId, "applicationId cannot be null");
- if (job != DeploymentJobs.JobType.component) {
+ if (job != JobType.component) {
throw new IllegalStateException(job + " cannot upload artifact");
}
- artifactRepository.put(applicationId, applicationPackage, applicationVersion());
+ artifactRepository.put(applicationId, applicationPackage, ApplicationVersion.from(sourceRevision.get(), buildNumber).id());
return this;
}
/** Send report for this build job to the controller */
public void submit() {
- if (job == DeploymentJobs.JobType.component &&
- !artifactRepository.contains(applicationId, applicationVersion())) {
+ if (job == JobType.component &&
+ !artifactRepository.contains(applicationId, ApplicationVersion.from(sourceRevision.get(), buildNumber).id())) {
throw new IllegalStateException(job + " must upload artifact before reporting completion");
}
reportConsumer.accept(report());
}
- private String applicationVersion() {
- return String.format("1.0.%d-%s", buildNumber, sourceRevision.get().commit());
- }
-
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTester.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTester.java
index ffc93f7b5ba..1f3de70b3a0 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTester.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTester.java
@@ -8,17 +8,17 @@ import com.yahoo.config.provision.TenantName;
import com.yahoo.test.ManualClock;
import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.ApplicationController;
-import com.yahoo.vespa.hosted.controller.ArtifactRepositoryMock;
-import com.yahoo.vespa.hosted.controller.ConfigServerMock;
+import com.yahoo.vespa.hosted.controller.integration.ArtifactRepositoryMock;
+import com.yahoo.vespa.hosted.controller.integration.ConfigServerMock;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.ControllerTester;
-import com.yahoo.vespa.hosted.controller.api.integration.BuildService;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockBuildService;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.Change;
import com.yahoo.vespa.hosted.controller.application.DeploymentJobs;
-import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType;
+import com.yahoo.vespa.hosted.controller.application.JobStatus;
import com.yahoo.vespa.hosted.controller.application.SystemApplication;
import com.yahoo.vespa.hosted.controller.maintenance.JobControl;
import com.yahoo.vespa.hosted.controller.maintenance.ReadyJobsTrigger;
@@ -59,11 +59,11 @@ public class DeploymentTester {
public DeploymentTester(ControllerTester tester) {
this.tester = tester;
tester.curator().writeUpgradesPerMinute(100);
- this.upgrader = new Upgrader(tester.controller(), maintenanceInterval, new JobControl(tester.curator()),
- tester.curator());
- this.systemUpgrader = new SystemUpgrader(tester.controller(), maintenanceInterval, new JobControl(tester.curator()));
- this.readyJobTrigger = new ReadyJobsTrigger(tester.controller(), maintenanceInterval,
- new JobControl(tester.curator()));
+
+ JobControl jobControl = new JobControl(tester.curator());
+ this.upgrader = new Upgrader(tester.controller(), maintenanceInterval, jobControl, tester.curator());
+ this.systemUpgrader = new SystemUpgrader(tester.controller(), maintenanceInterval, jobControl);
+ this.readyJobTrigger = new ReadyJobsTrigger(tester.controller(), maintenanceInterval, jobControl);
}
public SystemUpgrader systemUpgrader() {
@@ -112,7 +112,10 @@ public class DeploymentTester {
/** Upgrade system applications in all zones to given version */
public void upgradeSystemApplications(Version version) {
for (ZoneId zone : tester.zoneRegistry().zones().all().ids()) {
- tester.configServer().setVersion(version, zone, SystemApplication.all());
+ for (SystemApplication application : SystemApplication.all()) {
+ tester.configServer().setVersion(application.id(), zone, version);
+ tester.configServer().convergeServices(application.id(), zone);
+ }
}
computeVersionStatus();
}
@@ -193,8 +196,8 @@ public class DeploymentTester {
private void completeDeployment(Application application, ApplicationPackage applicationPackage,
Optional<JobType> failOnJob, boolean includingProductionZones) {
- DeploymentOrder order = new DeploymentOrder(controller()::system);
- List<JobType> jobs = order.jobsFrom(applicationPackage.deploymentSpec());
+ DeploymentSteps steps = controller().applications().deploymentTrigger().steps(applicationPackage.deploymentSpec());
+ List<JobType> jobs = steps.jobs();
if ( ! includingProductionZones)
jobs = jobs.stream().filter(job -> ! job.isProduction()).collect(Collectors.toList());
for (JobType job : jobs) {
@@ -262,6 +265,10 @@ public class DeploymentTester {
deployAndNotify(application, Optional.of(applicationPackage), success, job);
}
+ public void deployAndNotify(Application application, boolean success, JobType job) {
+ deployAndNotify(application, Optional.empty(), success, job);
+ }
+
public void deployAndNotify(Application application, Optional<ApplicationPackage> applicationPackage, boolean success, JobType job) {
if (success) {
// Staging deploys twice, once with current version and once with new version
@@ -277,6 +284,11 @@ public class DeploymentTester {
jobCompletion(job).application(application).success(success).submit();
}
+ public Optional<JobStatus.JobRun> firstFailing(Application application, JobType job) {
+ return tester.controller().applications().get(application.id()).get()
+ .deploymentJobs().jobStatus().get(job).firstFailing();
+ }
+
private void notifyJobCompletion(DeploymentJobs.JobReport report) {
if (report.jobType() != JobType.component && ! buildService().remove(report.buildJob()))
throw new IllegalArgumentException(report.jobType() + " is not running for " + report.applicationId());
@@ -296,8 +308,16 @@ public class DeploymentTester {
.build();
}
- public void assertRunning(ApplicationId id, JobType jobType) {
- assertTrue(buildService().jobs().contains(BuildService.BuildJob.of(id, application(id).deploymentJobs().projectId().getAsLong(), jobType.jobName())));
+ public void assertRunning(JobType job, ApplicationId application) {
+ assertTrue(String.format("Job %s for %s is running", job, application), isRunning(job, application));
+ }
+
+ public void assertNotRunning(JobType job, ApplicationId application) {
+ assertFalse(String.format("Job %s for %s is not running", job, application), isRunning(job, application));
+ }
+
+ private boolean isRunning(JobType job, ApplicationId application) {
+ return buildService().jobs().contains(ControllerTester.buildJob(application(application), job));
}
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java
index c26852a2153..af7261149ad 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java
@@ -3,53 +3,73 @@ package com.yahoo.vespa.hosted.controller.deployment;
import com.yahoo.component.Version;
import com.yahoo.config.provision.Environment;
+import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.TenantName;
+import com.yahoo.slime.Slime;
import com.yahoo.test.ManualClock;
+import com.yahoo.vespa.config.SlimeUtils;
import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.ControllerTester;
+import com.yahoo.vespa.hosted.controller.api.integration.BuildService;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockBuildService;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.ApplicationVersion;
import com.yahoo.vespa.hosted.controller.application.Change;
import com.yahoo.vespa.hosted.controller.application.DeploymentJobs;
-import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType;
import com.yahoo.vespa.hosted.controller.application.SourceRevision;
import com.yahoo.vespa.hosted.controller.maintenance.JobControl;
import com.yahoo.vespa.hosted.controller.maintenance.ReadyJobsTrigger;
+import org.junit.Before;
import org.junit.Test;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.time.Duration;
import java.time.Instant;
-import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
import static com.yahoo.config.provision.SystemName.main;
import static com.yahoo.vespa.hosted.controller.ControllerTester.buildJob;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.component;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.productionEuWest1;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.productionUsCentral1;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.productionUsEast3;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.productionUsWest1;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.stagingTest;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.systemTest;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.component;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionCorpUsEast1;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionEuWest1;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionUsCentral1;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionUsEast3;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionUsWest1;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.stagingTest;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.systemTest;
+import static java.time.temporal.ChronoUnit.MILLIS;
import static java.util.Collections.singletonList;
-import static java.util.Optional.empty;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
/**
+ * Tests a wide variety of deployment scenarios and configurations
+ *
* @author bratseth
* @author mpolden
- * @author jvenstad
+ * @author jonmv
*/
public class DeploymentTriggerTest {
+ private DeploymentTester tester;
+
+ @Before
+ public void before() {
+ tester = new DeploymentTester();
+ }
+
@Test
public void testTriggerFailing() {
- DeploymentTester tester = new DeploymentTester();
Application app = tester.createApplication("app1", "tenant1", 1, 1L);
ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
.upgradePolicy("default")
@@ -62,8 +82,8 @@ public class DeploymentTriggerTest {
// Deploy completely once
tester.jobCompletion(component).application(app).uploadArtifact(applicationPackage).submit();
- tester.deployAndNotify(app, applicationPackage, true, DeploymentJobs.JobType.systemTest);
- tester.deployAndNotify(app, applicationPackage, true, DeploymentJobs.JobType.stagingTest);
+ tester.deployAndNotify(app, applicationPackage, true, JobType.systemTest);
+ tester.deployAndNotify(app, applicationPackage, true, JobType.stagingTest);
tester.deployAndNotify(app, applicationPackage, true, JobType.productionUsWest1);
// New version is released
@@ -74,11 +94,11 @@ public class DeploymentTriggerTest {
tester.buildService().remove(buildJob(app, stagingTest));
tester.readyJobTrigger().maintain();
assertEquals("Retried dead job", 2, tester.buildService().jobs().size());
- tester.assertRunning(app.id(), stagingTest);
+ tester.assertRunning(stagingTest, app.id());
tester.deployAndNotify(app, applicationPackage, true, stagingTest);
// system-test is now the only running job -- production jobs haven't started yet, since it is unfinished.
- tester.assertRunning(app.id(), systemTest);
+ tester.assertRunning(systemTest, app.id());
assertEquals(1, tester.buildService().jobs().size());
// system-test fails and is retried
@@ -86,12 +106,11 @@ public class DeploymentTriggerTest {
assertEquals("Job is retried on failure", 1, tester.buildService().jobs().size());
tester.deployAndNotify(app, applicationPackage, true, JobType.systemTest);
- tester.assertRunning(app.id(), productionUsWest1);
+ tester.assertRunning(productionUsWest1, app.id());
}
@Test
public void deploymentSpecDecidesTriggerOrder() {
- DeploymentTester tester = new DeploymentTester();
TenantName tenant = tester.controllerTester().createTenant("tenant1", "domain1", 1L);
MockBuildService mockBuildService = tester.buildService();
Application application = tester.controllerTester().createApplication(tenant, "app1", "default", 1L);
@@ -116,7 +135,6 @@ public class DeploymentTriggerTest {
@Test
public void deploymentsSpecWithDelays() {
- DeploymentTester tester = new DeploymentTester();
MockBuildService mockBuildService = tester.buildService();
Application application = tester.createApplication("app1", "tenant1", 1, 1L);
@@ -145,13 +163,13 @@ public class DeploymentTriggerTest {
// 30 seconds later, the first jobs may trigger.
assertEquals(1, mockBuildService.jobs().size());
- tester.assertRunning(application.id(), productionUsWest1);
+ tester.assertRunning(productionUsWest1, application.id());
// 3 minutes pass, delayed trigger does nothing as us-west-1 is still in progress
tester.clock().advance(Duration.ofMinutes(3));
tester.deploymentTrigger().triggerReadyJobs();
assertEquals(1, mockBuildService.jobs().size());
- tester.assertRunning(application.id(), productionUsWest1);
+ tester.assertRunning(productionUsWest1, application.id());
// us-west-1 completes
tester.deployAndNotify(application, applicationPackage, true, productionUsWest1);
@@ -179,7 +197,6 @@ public class DeploymentTriggerTest {
@Test
public void deploymentSpecWithParallelDeployments() {
- DeploymentTester tester = new DeploymentTester();
Application application = tester.createApplication("app1", "tenant1", 1, 1L);
ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
@@ -202,8 +219,8 @@ public class DeploymentTriggerTest {
// Deploys in two regions in parallel
assertEquals(2, tester.buildService().jobs().size());
- tester.assertRunning(application.id(), productionUsEast3);
- tester.assertRunning(application.id(), productionUsWest1);
+ tester.assertRunning(productionUsEast3, application.id());
+ tester.assertRunning(productionUsWest1, application.id());
tester.deploy(JobType.productionUsWest1, application, applicationPackage, false);
tester.jobCompletion(JobType.productionUsWest1).application(application).submit();
@@ -220,7 +237,6 @@ public class DeploymentTriggerTest {
@Test
public void parallelDeploymentCompletesOutOfOrder() {
- DeploymentTester tester = new DeploymentTester();
ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
.environment(Environment.prod)
.parallel("us-east-3", "us-west-1")
@@ -230,17 +246,17 @@ public class DeploymentTriggerTest {
tester.jobCompletion(component).application(app).uploadArtifact(applicationPackage).submit();
// Test environments pass
- tester.deployAndNotify(app, applicationPackage, true, DeploymentJobs.JobType.systemTest);
- tester.deployAndNotify(app, applicationPackage, true, DeploymentJobs.JobType.stagingTest);
+ tester.deployAndNotify(app, applicationPackage, true, JobType.systemTest);
+ tester.deployAndNotify(app, applicationPackage, true, JobType.stagingTest);
// Last declared job completes first
- tester.deploy(DeploymentJobs.JobType.productionUsWest1, app, applicationPackage);
- tester.jobCompletion(DeploymentJobs.JobType.productionUsWest1).application(app).submit();
+ tester.deploy(JobType.productionUsWest1, app, applicationPackage);
+ tester.jobCompletion(JobType.productionUsWest1).application(app).submit();
assertTrue("Change is present as not all jobs are complete",
tester.applications().require(app.id()).change().isPresent());
// All jobs complete
- tester.deploy(DeploymentJobs.JobType.productionUsEast3, app, applicationPackage);
+ tester.deploy(JobType.productionUsEast3, app, applicationPackage);
tester.jobCompletion(JobType.productionUsEast3).application(app).submit();
assertFalse("Change has been deployed",
tester.applications().require(app.id()).change().isPresent());
@@ -248,7 +264,6 @@ public class DeploymentTriggerTest {
@Test
public void testSuccessfulDeploymentApplicationPackageChanged() {
- DeploymentTester tester = new DeploymentTester();
TenantName tenant = tester.controllerTester().createTenant("tenant1", "domain1", 1L);
MockBuildService mockBuildService = tester.buildService();
Application application = tester.controllerTester().createApplication(tenant, "app1", "default", 1L);
@@ -394,7 +409,6 @@ public class DeploymentTriggerTest {
@Test
public void testUpgradingButNoJobStarted() {
- DeploymentTester tester = new DeploymentTester();
ReadyJobsTrigger readyJobsTrigger = new ReadyJobsTrigger(tester.controller(),
Duration.ofHours(1),
new JobControl(tester.controllerTester().curator()));
@@ -405,13 +419,12 @@ public class DeploymentTriggerTest {
});
assertEquals(0, tester.buildService().jobs().size());
readyJobsTrigger.run();
- tester.assertRunning(app.id(), systemTest);
- tester.assertRunning(app.id(), stagingTest);
+ tester.assertRunning(systemTest, app.id());
+ tester.assertRunning(stagingTest, app.id());
}
@Test
public void applicationVersionIsNotDowngraded() {
- DeploymentTester tester = new DeploymentTester();
Application application = tester.createApplication("app1", "tenant1", 1, 1L);
Supplier<Application> app = () -> tester.application(application.id());
ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
@@ -426,8 +439,8 @@ public class DeploymentTriggerTest {
tester.completeDeploymentWithError(application, applicationPackage, BuildJob.defaultBuildNumber + 1, productionUsCentral1);
// deployAndNotify doesn't actually deploy if the job fails, so we need to do that manually.
- tester.deployAndNotify(application, empty(), false, productionUsCentral1);
- tester.deploy(productionUsCentral1, application, empty(), false);
+ tester.deployAndNotify(application, false, productionUsCentral1);
+ tester.deploy(productionUsCentral1, application, Optional.empty(), false);
ApplicationVersion appVersion1 = ApplicationVersion.from(BuildJob.defaultSourceRevision, BuildJob.defaultBuildNumber + 1);
assertEquals(appVersion1, app.get().deployments().get(ZoneId.from("prod.us-central-1")).applicationVersion());
@@ -444,24 +457,23 @@ public class DeploymentTriggerTest {
Version version1 = new Version("6.2");
tester.upgradeSystem(version1);
tester.jobCompletion(productionUsCentral1).application(application).unsuccessful().submit();
- tester.deployAndNotify(application, empty(), true, systemTest);
- tester.deployAndNotify(application, empty(), true, stagingTest);
- tester.deployAndNotify(application, empty(), false, productionUsCentral1);
+ tester.deployAndNotify(application, true, systemTest);
+ tester.deployAndNotify(application, true, stagingTest);
+ tester.deployAndNotify(application, false, productionUsCentral1);
// The last job has a different target, and the tests need to run again.
// These may now start, since the first job has been triggered once, and thus is verified already.
- tester.deployAndNotify(application, empty(), true, systemTest);
- tester.deployAndNotify(application, empty(), true, stagingTest);
+ tester.deployAndNotify(application, true, systemTest);
+ tester.deployAndNotify(application, true, stagingTest);
// Finally, the two production jobs complete, in order.
- tester.deployAndNotify(application, empty(), true, productionUsCentral1);
- tester.deployAndNotify(application, empty(), true, productionEuWest1);
+ tester.deployAndNotify(application, true, productionUsCentral1);
+ tester.deployAndNotify(application, true, productionEuWest1);
assertEquals(appVersion1, app.get().deployments().get(ZoneId.from("prod.us-central-1")).applicationVersion());
}
@Test
public void stepIsCompletePreciselyWhenItShouldBe() {
- DeploymentTester tester = new DeploymentTester();
Application application = tester.createApplication("app1", "tenant1", 1, 1L);
Supplier<Application> app = () -> tester.application(application.id());
ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
@@ -500,22 +512,26 @@ public class DeploymentTriggerTest {
tester.deployAndNotify(application, applicationPackage, true, systemTest);
tester.deployAndNotify(application, applicationPackage, true, stagingTest);
+ tester.assertRunning(productionUsCentral1, application.id());
assertEquals(v2, app.get().deployments().get(productionUsCentral1.zone(main).get()).version());
- assertEquals((Long) 42L, app.get().deployments().get(productionUsCentral1.zone(main).get()).applicationVersion().buildNumber().get());
+ assertEquals(Long.valueOf(42L), app.get().deployments().get(productionUsCentral1.zone(main).get()).applicationVersion().buildNumber().get());
assertNotEquals(triggered, app.get().deploymentJobs().jobStatus().get(productionUsCentral1).lastTriggered().get().at());
// Change has a higher application version than what is deployed -- deployment should trigger.
tester.deployAndNotify(application, applicationPackage, false, productionUsCentral1);
tester.deploy(productionUsCentral1, application, applicationPackage);
assertEquals(v2, app.get().deployments().get(productionUsCentral1.zone(main).get()).version());
- assertEquals((Long) 43L, app.get().deployments().get(productionUsCentral1.zone(main).get()).applicationVersion().buildNumber().get());
+ assertEquals(Long.valueOf(43), app.get().deployments().get(productionUsCentral1.zone(main).get()).applicationVersion().buildNumber().get());
// Change is again strictly dominated, and us-central-1 is skipped, even though it is still failing.
- tester.deployAndNotify(application, applicationPackage, false, productionUsCentral1);
+ tester.clock().advance(Duration.ofHours(2).plus(Duration.ofSeconds(1))); // Enough time for retry
+ tester.readyJobTrigger().maintain();
+ // Failing job is not retried as change has been deployed
+ tester.assertNotRunning(productionUsCentral1, application.id());
// Last job has a different deployment target, so tests need to run again.
- tester.deployAndNotify(application, empty(), true, systemTest);
- tester.deployAndNotify(application, empty(), true, stagingTest);
+ tester.deployAndNotify(application, true, systemTest);
+ tester.deployAndNotify(application, true, stagingTest);
tester.deployAndNotify(application, applicationPackage, true, productionEuWest1);
assertFalse(app.get().change().isPresent());
assertFalse(app.get().deploymentJobs().jobStatus().get(productionUsCentral1).isSuccess());
@@ -523,7 +539,6 @@ public class DeploymentTriggerTest {
@Test
public void eachDeployTargetIsTested() {
- DeploymentTester tester = new DeploymentTester();
Application application = tester.createApplication("app1", "tenant1", 1, 1L);
Supplier<Application> app = () -> tester.application(application.id());
ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
@@ -538,8 +553,8 @@ public class DeploymentTriggerTest {
Version v1 = new Version("6.1");
Version v2 = new Version("6.2");
tester.upgradeSystem(v2);
- tester.deployAndNotify(application, empty(), true, systemTest);
- tester.deployAndNotify(application, empty(), true, stagingTest);
+ tester.deployAndNotify(application, true, systemTest);
+ tester.deployAndNotify(application, true, stagingTest);
tester.deploymentTrigger().cancelChange(application.id(), true);
tester.deploy(productionEuWest1, application, applicationPackage);
assertEquals(v2, app.get().deployments().get(productionEuWest1.zone(main).get()).version());
@@ -550,8 +565,8 @@ public class DeploymentTriggerTest {
Version firstTested = app.get().deploymentJobs().jobStatus().get(systemTest).lastTriggered().get().platform();
assertEquals(firstTested, app.get().deploymentJobs().jobStatus().get(stagingTest).lastTriggered().get().platform());
- tester.deployAndNotify(application, empty(), true, systemTest);
- tester.deployAndNotify(application, empty(), true, stagingTest);
+ tester.deployAndNotify(application, true, systemTest);
+ tester.deployAndNotify(application, true, stagingTest);
// Tests are not re-triggered, because the jobs they were run for has not yet been triggered with the tested versions.
assertEquals(firstTested, app.get().deploymentJobs().jobStatus().get(systemTest).lastTriggered().get().platform());
@@ -565,14 +580,14 @@ public class DeploymentTriggerTest {
// New upgrade is already tested for one of the jobs, which has now been triggered, and tests may run for the other job.
assertNotEquals(firstTested, app.get().deploymentJobs().jobStatus().get(systemTest).lastTriggered().get().platform());
assertNotEquals(firstTested, app.get().deploymentJobs().jobStatus().get(stagingTest).lastTriggered().get().platform());
- tester.deployAndNotify(application, empty(), true, systemTest);
- tester.deployAndNotify(application, empty(), true, stagingTest);
+ tester.deployAndNotify(application, true, systemTest);
+ tester.deployAndNotify(application, true, stagingTest);
// Both jobs fail again, and must be re-triggered -- this is ok, as they are both already triggered on their current targets.
- tester.deployAndNotify(application, empty(), false, productionEuWest1);
- tester.deployAndNotify(application, empty(), false, productionUsEast3);
- tester.deployAndNotify(application, empty(), true, productionUsEast3);
- tester.deployAndNotify(application, empty(), true, productionEuWest1);
+ tester.deployAndNotify(application, false, productionEuWest1);
+ tester.deployAndNotify(application, false, productionUsEast3);
+ tester.deployAndNotify(application, true, productionUsEast3);
+ tester.deployAndNotify(application, true, productionEuWest1);
assertFalse(app.get().change().isPresent());
assertEquals(43, app.get().deploymentJobs().jobStatus().get(productionEuWest1).lastSuccess().get().application().buildNumber().get().longValue());
assertEquals(43, app.get().deploymentJobs().jobStatus().get(productionUsEast3).lastSuccess().get().application().buildNumber().get().longValue());
@@ -580,7 +595,6 @@ public class DeploymentTriggerTest {
@Test
public void eachDifferentUpgradeCombinationIsTested() {
- DeploymentTester tester = new DeploymentTester();
Application application = tester.createApplication("app1", "tenant1", 1, 1L);
Supplier<Application> app = () -> tester.application(application.id());
ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
@@ -595,28 +609,436 @@ public class DeploymentTriggerTest {
Version v1 = new Version("6.1");
Version v2 = new Version("6.2");
tester.upgradeSystem(v2);
- tester.deployAndNotify(application, empty(), true, systemTest);
- tester.deployAndNotify(application, empty(), true, stagingTest);
- tester.deployAndNotify(application, empty(), true, productionUsCentral1);
- tester.deployAndNotify(application, empty(), true, productionEuWest1);
- tester.deployAndNotify(application, empty(), false, productionUsEast3);
+ tester.deployAndNotify(application, true, systemTest);
+ tester.deployAndNotify(application, true, stagingTest);
+ tester.deployAndNotify(application, true, productionUsCentral1);
+ tester.deployAndNotify(application, true, productionEuWest1);
+ tester.deployAndNotify(application, false, productionUsEast3);
assertEquals(v2, app.get().deployments().get(ZoneId.from("prod", "us-central-1")).version());
assertEquals(v2, app.get().deployments().get(ZoneId.from("prod", "eu-west-1")).version());
assertEquals(v1, app.get().deployments().get(ZoneId.from("prod", "us-east-3")).version());
Version v3 = new Version("6.3");
tester.upgradeSystem(v3);
- tester.deployAndNotify(application, empty(), false, productionUsEast3);
+ tester.deployAndNotify(application, false, productionUsEast3);
// See that sources for staging are: first v2, then v1.
- tester.deployAndNotify(application, empty(), true, systemTest);
- tester.deployAndNotify(application, empty(), true, stagingTest);
+ tester.deployAndNotify(application, true, systemTest);
+ tester.deployAndNotify(application, true, stagingTest);
assertEquals(v2, app.get().deploymentJobs().jobStatus().get(stagingTest).lastSuccess().get().sourcePlatform().get());
- tester.deployAndNotify(application, empty(), true, productionUsCentral1);
+ tester.deployAndNotify(application, true, productionUsCentral1);
assertEquals(v1, app.get().deploymentJobs().jobStatus().get(stagingTest).lastTriggered().get().sourcePlatform().get());
- tester.deployAndNotify(application, empty(), true, stagingTest);
- tester.deployAndNotify(application, empty(), true, productionEuWest1);
- tester.deployAndNotify(application, empty(), true, productionUsEast3);
+ tester.deployAndNotify(application, true, stagingTest);
+ tester.deployAndNotify(application, true, productionEuWest1);
+ tester.deployAndNotify(application, true, productionUsEast3);
+ }
+
+ @Test
+ public void retriesFailingJobs() {
+ Application application = tester.createApplication("app1", "tenant1", 1, 1L);
+ ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
+ .environment(Environment.prod)
+ .region("us-central-1")
+ .build();
+
+ // Deploy completely on default application and platform versions
+ tester.deployCompletely(application, applicationPackage);
+
+ // New application change is deployed and fails in system-test for a while
+ tester.jobCompletion(component).application(application).nextBuildNumber().uploadArtifact(applicationPackage).submit();
+ tester.deployAndNotify(application, false, systemTest);
+ tester.deployAndNotify(application, true, stagingTest);
+
+ // Retries immediately in the first minute after failing
+ tester.clock().advance(Duration.ofSeconds(59));
+ tester.jobCompletion(systemTest).application(application).unsuccessful().submit();
+ tester.readyJobTrigger().maintain();
+ tester.assertRunning(systemTest, application.id());
+
+ // Stops immediate retry after failing for 1 minute
+ tester.clock().advance(Duration.ofSeconds(1));
+ tester.jobCompletion(systemTest).application(application).unsuccessful().submit();
+ tester.readyJobTrigger().maintain();
+ tester.assertNotRunning(systemTest, application.id());
+
+ // Retries after 10 minutes since previous completion as we failed within the last hour
+ tester.clock().advance(Duration.ofMinutes(10).plus(Duration.ofSeconds(1)));
+ tester.readyJobTrigger().maintain();
+ tester.assertRunning(systemTest, application.id());
+
+ // Retries less frequently after 1 hour of failure
+ tester.clock().advance(Duration.ofMinutes(50));
+ tester.jobCompletion(systemTest).application(application).unsuccessful().submit();
+ tester.readyJobTrigger().maintain();
+ tester.assertNotRunning(systemTest, application.id());
+
+ // Retries after two hours pass since last completion
+ tester.clock().advance(Duration.ofHours(2).plus(Duration.ofSeconds(1)));
+ tester.readyJobTrigger().maintain();
+ tester.assertRunning(systemTest, application.id());
+
+ // Still fails and is not retried
+ tester.jobCompletion(systemTest).application(application).unsuccessful().submit();
+ tester.readyJobTrigger().maintain();
+ tester.assertNotRunning(systemTest, application.id());
+
+ // Another application change is deployed and fixes system-test. Change is triggered immediately as target changes
+ tester.jobCompletion(component).application(application).nextBuildNumber(2).uploadArtifact(applicationPackage).submit();
+ tester.deployAndNotify(application, true, systemTest);
+ tester.deployAndNotify(application, true, stagingTest);
+ tester.deployAndNotify(application, true, productionUsCentral1);
+ assertTrue("Deployment completed", tester.buildService().jobs().isEmpty());
+ }
+
+ @Test
+ public void testRetryingFailedJobsDuringDeployment() {
+ ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
+ .upgradePolicy("canary")
+ .environment(Environment.prod)
+ .region("us-east-3")
+ .build();
+ Version version = Version.fromString("5.0");
+ tester.upgradeSystem(version);
+
+ Application app = tester.createApplication("app1", "tenant1", 1, 11L);
+ tester.jobCompletion(component).application(app).uploadArtifact(applicationPackage).submit();
+ tester.deployAndNotify(app, applicationPackage, true, JobType.systemTest);
+ tester.deployAndNotify(app, applicationPackage, true, JobType.stagingTest);
+ tester.deployAndNotify(app, applicationPackage, true, JobType.productionUsEast3);
+
+ // New version is released
+ version = Version.fromString("5.1");
+ tester.upgradeSystem(version);
+ assertEquals(version, tester.controller().versionStatus().systemVersion().get().versionNumber());
+ tester.upgrader().maintain();
+ tester.readyJobTrigger().maintain();
+
+ // Test environments pass
+ tester.deployAndNotify(app, applicationPackage, true, JobType.systemTest);
+ tester.deployAndNotify(app, applicationPackage, true, JobType.stagingTest);
+
+ // Production job fails and is retried
+ tester.clock().advance(Duration.ofSeconds(1)); // Advance time so that we can detect jobs in progress
+ tester.deployAndNotify(app, applicationPackage, false, JobType.productionUsEast3);
+ assertEquals("Production job is retried", 1, tester.buildService().jobs().size());
+ assertEquals("Application has pending upgrade to " + version, version, tester.application(app.id()).change().platform().get());
+
+ // Another version is released, which cancels any pending upgrades to lower versions
+ version = Version.fromString("5.2");
+ tester.upgradeSystem(version);
+ tester.upgrader().maintain();
+ tester.jobCompletion(JobType.productionUsEast3).application(app).unsuccessful().submit();
+ assertEquals("Application starts upgrading to new version", 2, tester.buildService().jobs().size());
+ assertEquals("Application has pending upgrade to " + version, version, tester.application(app.id()).change().platform().get());
+
+ // Failure re-deployer did not retry failing job for prod.us-east-3, since it no longer had an available change
+ assertFalse("Job is not retried", tester.buildService().jobs().stream()
+ .anyMatch(j -> j.jobName().equals(JobType.productionUsEast3.jobName())));
+
+ // Test environments pass
+ tester.deployAndNotify(app, applicationPackage, true, JobType.systemTest);
+ tester.deployAndNotify(app, applicationPackage, true, JobType.stagingTest);
+
+ // Production job fails again, and is retried
+ tester.deployAndNotify(app, applicationPackage, false, JobType.productionUsEast3);
+ assertEquals("Job is retried", Collections.singletonList(ControllerTester.buildJob(app, productionUsEast3)), tester.buildService().jobs());
+
+ // Production job finally succeeds
+ tester.deployAndNotify(app, applicationPackage, true, JobType.productionUsEast3);
+ assertTrue("All jobs consumed", tester.buildService().jobs().isEmpty());
+ assertFalse("No failures", tester.application(app.id()).deploymentJobs().hasFailures());
+ }
+ @Test
+ public void testRetriesJobsFailingForCurrentChange() {
+ ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
+ .upgradePolicy("canary")
+ .environment(Environment.prod)
+ .region("us-east-3")
+ .build();
+ Version version = Version.fromString("5.0");
+ tester.upgradeSystem(version);
+
+ Application app = tester.createApplication("app1", "tenant1", 1, 11L);
+ tester.jobCompletion(component).application(app).uploadArtifact(applicationPackage).submit();
+ tester.deployAndNotify(app, applicationPackage, true, JobType.systemTest);
+ tester.deployAndNotify(app, applicationPackage, true, JobType.stagingTest);
+ tester.deployAndNotify(app, applicationPackage, true, JobType.productionUsEast3);
+
+ // New version is released
+ version = Version.fromString("5.1");
+ tester.upgradeSystem(version);
+ assertEquals(version, tester.controller().versionStatus().systemVersion().get().versionNumber());
+ tester.upgrader().maintain();
+ tester.readyJobTrigger().maintain();
+ assertEquals("Application has pending upgrade to " + version, version, tester.application(app.id()).change().platform().get());
+
+ // system-test fails and is left with a retry
+ tester.deployAndNotify(app, applicationPackage, false, JobType.systemTest);
+
+ // Another version is released
+ version = Version.fromString("5.2");
+ tester.upgradeSystem(version);
+ assertEquals(version, tester.controller().versionStatus().systemVersion().get().versionNumber());
+
+ tester.buildService().remove(ControllerTester.buildJob(app, systemTest));
+ tester.upgrader().maintain();
+ tester.readyJobTrigger().maintain();
+ assertEquals("Application has pending upgrade to " + version, version, tester.application(app.id()).change().platform().get());
+
+ // Cancellation of outdated version and triggering on a new version is done by the upgrader.
+ assertEquals(version, tester.application(app.id()).deploymentJobs().jobStatus().get(systemTest).lastTriggered().get().platform());
+ }
+
+ @Test
+ public void testUpdatesFailingJobStatus() {
+ // Setup application
+ Application app = tester.createApplication("app1", "foo", 1, 1L);
+ ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
+ .environment(Environment.prod)
+ .region("corp-us-east-1")
+ .build();
+
+ // Initial failure
+ Instant initialFailure = tester.clock().instant().truncatedTo(MILLIS);
+ tester.jobCompletion(component).application(app).uploadArtifact(applicationPackage).submit();
+ tester.deployAndNotify(app, applicationPackage, false, systemTest);
+ assertEquals("Failure age is right at initial failure",
+ initialFailure, tester.firstFailing(app, systemTest).get().at());
+
+ // Failure again -- failingSince should remain the same
+ tester.clock().advance(Duration.ofMillis(1000));
+ tester.deployAndNotify(app, applicationPackage, false, systemTest);
+ assertEquals("Failure age is right at second consecutive failure",
+ initialFailure, tester.firstFailing(app, systemTest).get().at());
+
+ // Success resets failingSince
+ tester.clock().advance(Duration.ofMillis(1000));
+ tester.deployAndNotify(app, applicationPackage, true, systemTest);
+ assertFalse(tester.firstFailing(app, systemTest).isPresent());
+
+ // Complete deployment
+ tester.deployAndNotify(app, applicationPackage, true, stagingTest);
+ tester.deployAndNotify(app, applicationPackage, true, productionCorpUsEast1);
+
+ // Two repeated failures again.
+ // Initial failure
+ tester.clock().advance(Duration.ofMillis(1000));
+ initialFailure = tester.clock().instant().truncatedTo(MILLIS);
+ tester.jobCompletion(component).application(app).nextBuildNumber().uploadArtifact(applicationPackage).submit();
+ tester.deployAndNotify(app, applicationPackage, false, systemTest);
+ assertEquals("Failure age is right at initial failure",
+ initialFailure, tester.firstFailing(app, systemTest).get().at());
+
+ // Failure again -- failingSince should remain the same
+ tester.clock().advance(Duration.ofMillis(1000));
+ tester.deployAndNotify(app, applicationPackage, false, systemTest);
+ assertEquals("Failure age is right at second consecutive failure",
+ initialFailure, tester.firstFailing(app, systemTest).get().at());
+ }
+
+ @Test
+ public void ignoresPullRequestInstances() throws Exception {
+ tester.controllerTester().zoneRegistry().setSystemName(SystemName.cd);
+
+ // Current system version, matches version in test data
+ Version version = Version.fromString("6.42.1");
+ tester.upgradeSystem(version);
+ assertEquals(version, tester.controller().versionStatus().systemVersion().get().versionNumber());
+
+ // Load test data data
+ byte[] json = Files.readAllBytes(Paths.get("src/test/java/com/yahoo/vespa/hosted/controller/maintenance/testdata/pr-instance-with-dead-locked-job.json"));
+ Slime slime = SlimeUtils.jsonToSlime(json);
+ tester.controllerTester().createApplication(slime);
+
+ // Failure redeployer does not restart deployment
+ tester.readyJobTrigger().maintain();
+ assertTrue("No jobs scheduled", tester.buildService().jobs().isEmpty());
+ }
+
+ @Test
+ public void applicationWithoutProjectIdIsNotTriggered() throws Exception {
+ // Current system version, matches version in test data
+ Version version = Version.fromString("6.42.1");
+ tester.upgradeSystem(version);
+ assertEquals(version, tester.controller().versionStatus().systemVersion().get().versionNumber());
+
+ // Load test data data
+ byte[] json = Files.readAllBytes(Paths.get("src/test/java/com/yahoo/vespa/hosted/controller/maintenance/testdata/application-without-project-id.json"));
+ Slime slime = SlimeUtils.jsonToSlime(json);
+ tester.controllerTester().createApplication(slime);
+
+ // Failure redeployer does not restart deployment
+ tester.readyJobTrigger().maintain();
+ assertTrue("No jobs scheduled", tester.buildService().jobs().isEmpty());
+ }
+
+ @Test
+ public void testPlatformVersionSelection() {
+ // Setup system
+ ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
+ .environment(Environment.prod)
+ .region("us-west-1")
+ .build();
+ Version version1 = tester.controller().versionStatus().systemVersion().get().versionNumber();
+
+ Application app1 = tester.createApplication("application1", "tenant1", 1, 1L);
+
+ // First deployment: An application change
+ tester.jobCompletion(component).application(app1).uploadArtifact(applicationPackage).submit();
+ tester.deployAndNotify(app1, applicationPackage, true, systemTest);
+ tester.deployAndNotify(app1, applicationPackage, true, stagingTest);
+ tester.deployAndNotify(app1, applicationPackage, true, productionUsWest1);
+
+ app1 = tester.application(app1.id());
+ assertEquals("First deployment gets system version", version1, app1.oldestDeployedPlatform().get());
+ assertEquals(version1, tester.configServer().lastPrepareVersion().get());
+
+ // Unexpected deployment
+ tester.deploy(productionUsWest1, app1, applicationPackage);
+ // applications are immutable, so any change to one, including deployment changes, would give rise to a new instance.
+ assertEquals("Unexpected deployment is ignored", app1, tester.application(app1.id()));
+
+ // Application change after a new system version, and a region added
+ Version version2 = new Version(version1.getMajor(), version1.getMinor() + 1);
+ tester.upgradeController(version2);
+ tester.upgradeSystemApplications(version2);
+
+ applicationPackage = new ApplicationPackageBuilder()
+ .environment(Environment.prod)
+ .region("us-west-1")
+ .region("us-east-3")
+ .build();
+ tester.jobCompletion(component).application(app1).nextBuildNumber().uploadArtifact(applicationPackage).submit();
+ tester.deployAndNotify(app1, applicationPackage, true, systemTest);
+ tester.deployAndNotify(app1, applicationPackage, true, stagingTest);
+ tester.deployAndNotify(app1, applicationPackage, true, productionUsWest1);
+
+ app1 = tester.application(app1.id());
+ assertEquals("Application change preserves version", version1, app1.oldestDeployedPlatform().get());
+ assertEquals(version1, tester.configServer().lastPrepareVersion().get());
+
+ // A deployment to the new region gets the same version
+ tester.deployAndNotify(app1, applicationPackage, true, productionUsEast3);
+ app1 = tester.application(app1.id());
+ assertEquals("Application change preserves version", version1, app1.oldestDeployedPlatform().get());
+ assertEquals(version1, tester.configServer().lastPrepareVersion().get());
+ assertFalse("Change deployed", app1.change().isPresent());
+
+ // Version upgrade changes system version
+ tester.deploymentTrigger().triggerChange(app1.id(), Change.of(version2));
+ tester.deploymentTrigger().triggerReadyJobs();
+ tester.deployAndNotify(app1, applicationPackage, true, systemTest);
+ tester.deployAndNotify(app1, applicationPackage, true, stagingTest);
+ tester.deployAndNotify(app1, applicationPackage, true, productionUsWest1);
+ tester.deployAndNotify(app1, applicationPackage, true, productionUsEast3);
+
+ app1 = tester.application(app1.id());
+ assertEquals("Version upgrade changes version", version2, app1.oldestDeployedPlatform().get());
+ assertEquals(version2, tester.configServer().lastPrepareVersion().get());
+ }
+
+ @Test
+ public void requeueOutOfCapacityStagingJob() {
+ ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
+ .environment(Environment.prod)
+ .region("corp-us-east-1")
+ .build();
+
+ long project1 = 1;
+ long project2 = 2;
+ long project3 = 3;
+ Application app1 = tester.createApplication("app1", "tenant1", project1, 1L);
+ Application app2 = tester.createApplication("app2", "tenant2", project2, 1L);
+ Application app3 = tester.createApplication("app3", "tenant3", project3, 1L);
+ MockBuildService mockBuildService = tester.buildService();
+
+ // all applications: system-test completes successfully with some time in between, to determine trigger order.
+ tester.jobCompletion(component).application(app2).uploadArtifact(applicationPackage).submit();
+ tester.deployAndNotify(app2, applicationPackage, true, systemTest);
+ tester.clock().advance(Duration.ofMinutes(1));
+
+ tester.jobCompletion(component).application(app1).uploadArtifact(applicationPackage).submit();
+ tester.deployAndNotify(app1, applicationPackage, true, systemTest);
+ tester.clock().advance(Duration.ofMinutes(1));
+
+ tester.jobCompletion(component).application(app3).uploadArtifact(applicationPackage).submit();
+ tester.deployAndNotify(app3, applicationPackage, true, systemTest);
+
+ // all applications: staging test jobs queued
+ assertEquals(3, mockBuildService.jobs().size());
+
+ // Abort all running jobs, so we have three candidate jobs, of which only one should be triggered at a time.
+ tester.buildService().clear();
+
+ List<BuildService.BuildJob> jobs = new ArrayList<>();
+ assertJobsInOrder(jobs, tester.buildService().jobs());
+
+ tester.triggerUntilQuiescence();
+ jobs.add(buildJob(app2, stagingTest));
+ jobs.add(buildJob(app1, stagingTest));
+ jobs.add(buildJob(app3, stagingTest));
+ assertJobsInOrder(jobs, tester.buildService().jobs());
+
+ // Remove the jobs for app1 and app2, and then let app3 fail with outOfCapacity.
+ // All three jobs are now eligible, but the one for app3 should trigger first as an outOfCapacity-retry.
+ tester.buildService().remove(buildJob(app1, stagingTest));
+ tester.buildService().remove(buildJob(app2, stagingTest));
+ jobs.remove(buildJob(app1, stagingTest));
+ jobs.remove(buildJob(app2, stagingTest));
+ tester.jobCompletion(stagingTest).application(app3).error(DeploymentJobs.JobError.outOfCapacity).submit();
+ assertJobsInOrder(jobs, tester.buildService().jobs());
+
+ tester.triggerUntilQuiescence();
+ jobs.add(buildJob(app2, stagingTest));
+ jobs.add(buildJob(app1, stagingTest));
+ assertJobsInOrder(jobs, tester.buildService().jobs());
+
+ // Finish deployment for apps 2 and 3, then release a new version, leaving only app1 with an application upgrade.
+ tester.deployAndNotify(app2, applicationPackage, true, stagingTest);
+ tester.deployAndNotify(app2, applicationPackage, true, productionCorpUsEast1);
+ tester.deployAndNotify(app3, applicationPackage, true, stagingTest);
+ tester.deployAndNotify(app3, applicationPackage, true, productionCorpUsEast1);
+
+ tester.upgradeSystem(new Version("6.2"));
+ // app1 also gets a new application change, so its time of availability is after the version upgrade.
+ tester.clock().advance(Duration.ofMinutes(1));
+ tester.buildService().clear();
+ tester.jobCompletion(component).application(app1).nextBuildNumber().uploadArtifact(applicationPackage).submit();
+ jobs.clear();
+ jobs.add(buildJob(app1, stagingTest));
+ jobs.add(buildJob(app1, systemTest));
+ // Tests for app1 trigger before the others since it carries an application upgrade.
+ assertJobsInOrder(jobs, tester.buildService().jobs());
+
+ // Let the test jobs start, remove everything expect system test for app3, which fails with outOfCapacity again.
+ tester.triggerUntilQuiescence();
+ tester.buildService().remove(buildJob(app1, systemTest));
+ tester.buildService().remove(buildJob(app2, systemTest));
+ tester.buildService().remove(buildJob(app1, stagingTest));
+ tester.buildService().remove(buildJob(app2, stagingTest));
+ tester.buildService().remove(buildJob(app3, stagingTest));
+ tester.jobCompletion(systemTest).application(app3).error(DeploymentJobs.JobError.outOfCapacity).submit();
+ jobs.clear();
+ jobs.add(buildJob(app1, stagingTest));
+ jobs.add(buildJob(app3, systemTest));
+ assertJobsInOrder(jobs, tester.buildService().jobs());
+
+ tester.triggerUntilQuiescence();
+ jobs.add(buildJob(app2, stagingTest));
+ jobs.add(buildJob(app1, systemTest));
+ jobs.add(buildJob(app3, stagingTest));
+ jobs.add(buildJob(app2, systemTest));
+ assertJobsInOrder(jobs, tester.buildService().jobs());
+ }
+
+ /** Verifies that the given job lists have the same jobs, ignoring order of jobs that may have been triggered concurrently. */
+ private static void assertJobsInOrder(List<BuildService.BuildJob> expected, List<BuildService.BuildJob> actual) {
+ assertEquals(expected.stream().filter(job -> job.jobName().equals(systemTest.jobName())).collect(Collectors.toList()),
+ actual.stream().filter(job -> job.jobName().equals(systemTest.jobName())).collect(Collectors.toList()));
+ assertEquals(expected.stream().filter(job -> job.jobName().equals(stagingTest.jobName())).collect(Collectors.toList()),
+ actual.stream().filter(job -> job.jobName().equals(stagingTest.jobName())).collect(Collectors.toList()));
+ assertTrue(expected.containsAll(actual));
+ assertTrue(actual.containsAll(expected));
}
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/IntegerationStepRunnerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/IntegerationStepRunnerTest.java
new file mode 100644
index 00000000000..d45f5cc98f8
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/IntegerationStepRunnerTest.java
@@ -0,0 +1,34 @@
+// 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.deployment;
+
+import com.yahoo.config.provision.SystemName;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author freva
+ */
+public class IntegerationStepRunnerTest {
+
+ @Test
+ public void generates_correct_services_xml_test() {
+ assertFile("test_runner_services.xml-cd", new String(InternalStepRunner.servicesXml(SystemName.cd)));
+ }
+
+ private void assertFile(String resourceName, String actualContent) {
+ try {
+ Path path = Paths.get(getClass().getClassLoader().getResource(resourceName).getPath());
+ String expectedContent = new String(Files.readAllBytes(path));
+ assertEquals(expectedContent, actualContent);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ZipBuilderTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ZipBuilderTest.java
new file mode 100644
index 00000000000..d54fd2124f0
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ZipBuilderTest.java
@@ -0,0 +1,66 @@
+// 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.deployment;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author freva
+ */
+public class ZipBuilderTest {
+
+ @Test
+ public void test() {
+ Map<String, String> expected = new HashMap<>();
+ expected.put("dir/myfile", "my content");
+ expected.put("rootfile", "this is root");
+ expected.put("dir/newfile", "new file");
+ expected.put("dir/dir2/file", "nested file");
+
+ try (ZipBuilder zipBuilder1 = new ZipBuilder(100);
+ ZipBuilder zipBuilder2 = new ZipBuilder(1000)) {
+
+ // Add the entries to both zip builders one by one
+ Iterator<Map.Entry<String, String>> entries = expected.entrySet().iterator();
+ for (int i = 0; entries.hasNext(); i++) {
+ Map.Entry<String, String> entry = entries.next();
+ (i % 2 == 0 ? zipBuilder1 : zipBuilder2)
+ .add(entry.getKey(), entry.getValue().getBytes(StandardCharsets.UTF_8));
+ }
+
+ // Add the zipped data from zip1 to zip2
+ zipBuilder2.add(zipBuilder1.toByteArray());
+
+ System.out.println(zipBuilder1.toByteArray().length);
+ System.out.println(zipBuilder2.toByteArray().length);
+ Map<String, String> actual = unzipToMap(zipBuilder2.toByteArray());
+
+ assertEquals(expected, actual);
+ }
+ }
+
+ Map<String, String> unzipToMap(byte[] zippedContent) {
+ Map<String, String> contents = new HashMap<>();
+ try (ZipInputStream zin = new ZipInputStream(new ByteArrayInputStream(zippedContent))) {
+ for (ZipEntry entry = zin.getNextEntry(); entry != null; entry = zin.getNextEntry()) {
+ if (entry.isDirectory()) continue;
+ contents.put(entry.getName(), IOUtils.toString(zin, StandardCharsets.UTF_8));
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException("Failed to read zipped content", e);
+ }
+ return contents;
+ }
+} \ No newline at end of file
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ArtifactRepositoryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ArtifactRepositoryMock.java
index a623f3552c0..3f800ad9a56 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ArtifactRepositoryMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ArtifactRepositoryMock.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;
+package com.yahoo.vespa.hosted.controller.integration;
import com.yahoo.component.AbstractComponent;
import com.yahoo.component.Version;
@@ -47,6 +47,21 @@ public class ArtifactRepositoryMock extends AbstractComponent implements Artifac
}
@Override
+ public void putApplicationPackage(ApplicationId application, String applicationVersion, byte[] applicationPackage) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public byte[] getTesterPackage(ApplicationId tester, String applicationVersion) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public void putTesterPackage(ApplicationId tester, String applicationVersion, byte[] testerPackage) {
+ throw new AssertionError();
+ }
+
+ @Override
public byte[] getSystemApplicationPackage(ApplicationId application, ZoneId zone, Version version) {
return new byte[0];
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/AthenzFilterMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/AthenzFilterMock.java
index ab97aaae201..4acd3a34c8d 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/AthenzFilterMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/AthenzFilterMock.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;
+package com.yahoo.vespa.hosted.controller.integration;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ConfigServerMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java
index ac9afd9752a..354f736202a 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ConfigServerMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.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;
+package com.yahoo.vespa.hosted.controller.integration;
import com.google.inject.Inject;
import com.yahoo.component.AbstractComponent;
@@ -16,6 +16,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServ
import com.yahoo.vespa.hosted.controller.api.integration.configserver.Log;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.PrepareResponse;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.ServiceConvergence;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.application.SystemApplication;
import com.yahoo.vespa.serviceview.bindings.ApplicationView;
@@ -44,8 +45,9 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer
private final Map<String, EndpointStatus> endpoints = new HashMap<>();
private final Map<URI, Version> versions = new HashMap<>();
private final NodeRepositoryMock nodeRepository = new NodeRepositoryMock();
+ private final Map<DeploymentId, ServiceConvergence> serviceStatus = new HashMap<>();
+ private final Version initialVersion = new Version(6, 1, 0);
- private Version initialVersion = new Version(6, 1, 0);
private Version lastPrepareVersion = null;
private RuntimeException prepareException = null;
@@ -73,10 +75,16 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer
))
.collect(Collectors.toList());
nodeRepository().add(zone, nodes);
+ convergeServices(application.id(), zone);
}
}
}
+ /** Converge all services belonging to the given application */
+ public void convergeServices(ApplicationId application, ZoneId zone) {
+ serviceStatus.put(new DeploymentId(application, zone), new ServiceConvergence(application, zone, true));
+ }
+
/** The version given in the previous prepare call, or empty if no call has been made */
public Optional<Version> lastPrepareVersion() {
return Optional.ofNullable(lastPrepareVersion);
@@ -87,21 +95,11 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer
this.prepareException = prepareException;
}
- /**
- * Returns the (initially empty) mutable map of config server urls to versions.
- * This API will return defaultVersion as response to any version(url) call for versions not added to the map.
- */
- public Map<URI, Version> versions() {
- return versions;
- }
-
/** Set version for system applications in given zone */
- public void setVersion(Version version, ZoneId zone, List<SystemApplication> applications) {
- for (SystemApplication application : applications) {
- for (Node node : nodeRepository().list(zone, application.id())) {
- nodeRepository().add(zone, new Node(node.hostname(), node.state(), node.type(), node.owner(),
- version, version));
- }
+ public void setVersion(ApplicationId application, ZoneId zone, Version version) {
+ for (Node node : nodeRepository().list(zone, application)) {
+ nodeRepository().add(zone, new Node(node.hostname(), node.state(), node.type(), node.owner(),
+ version, version));
}
}
@@ -121,6 +119,11 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer
}
@Override
+ public Optional<ServiceConvergence> serviceConvergence(DeploymentId deployment) {
+ return Optional.ofNullable(serviceStatus.get(deployment));
+ }
+
+ @Override
public PreparedApplication deploy(DeploymentId deployment, DeployOptions deployOptions, Set<String> rotationCnames,
Set<String> rotationNames, byte[] content) {
lastPrepareVersion = deployOptions.vespaVersion.map(Version::fromString).orElse(null);
@@ -162,6 +165,7 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer
node.currentVersion(),
application.version().get()));
}
+ serviceStatus.remove(deployment); // Deployment is no longer converging after new deployment
PrepareResponse prepareResponse = new PrepareResponse();
prepareResponse.message = "foo";
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ConfigServerProxyMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerProxyMock.java
index 02b33e4640a..4d70987ff28 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ConfigServerProxyMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerProxyMock.java
@@ -1,5 +1,5 @@
// 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.controller;
+package com.yahoo.vespa.hosted.controller.integration;
import com.yahoo.component.AbstractComponent;
import com.yahoo.container.jdisc.HttpResponse;
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MetricsMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/MetricsMock.java
index 8b903f5c921..3dd6689dfdf 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MetricsMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/MetricsMock.java
@@ -1,5 +1,5 @@
// 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.controller;
+package com.yahoo.vespa.hosted.controller.integration;
import com.yahoo.jdisc.Metric;
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/MockMetricsService.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/MetricsServiceMock.java
index 67a4139ecf1..eca78c01e09 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/MockMetricsService.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/MetricsServiceMock.java
@@ -11,11 +11,11 @@ import java.util.Map;
/**
* @author bratseth
*/
-public class MockMetricsService implements MetricsService {
+public class MetricsServiceMock implements MetricsService {
private final Map<String, Double> metrics = new HashMap<>();
- public MockMetricsService setMetric(String key, Double value) {
+ public MetricsServiceMock setMetric(String key, Double value) {
metrics.put(key, value);
return this;
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/NodeRepositoryClientMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryClientMock.java
index f73f2e992d9..1b12b441272 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/NodeRepositoryClientMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryClientMock.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;
+package com.yahoo.vespa.hosted.controller.integration;
import com.yahoo.vespa.hosted.controller.api.integration.noderepository.MaintenanceJobList;
import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeList;
@@ -9,7 +9,6 @@ import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepo
import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeState;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
-import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
@@ -17,28 +16,29 @@ import java.util.Collection;
* @author bjorncs
*/
public class NodeRepositoryClientMock implements NodeRepositoryClientInterface {
+
@Override
- public void addNodes(ZoneId zone, Collection<NodeRepositoryNode> nodes) throws IOException {
+ public void addNodes(ZoneId zone, Collection<NodeRepositoryNode> nodes) {
throw new UnsupportedOperationException();
}
@Override
- public NodeRepositoryNode getNode(ZoneId zone, String hostname) throws IOException {
+ public NodeRepositoryNode getNode(ZoneId zone, String hostname) {
throw new UnsupportedOperationException();
}
@Override
- public void deleteNode(ZoneId zone, String hostname) throws IOException {
+ public void deleteNode(ZoneId zone, String hostname) {
throw new UnsupportedOperationException();
}
@Override
- public NodeList listNodes(ZoneId zone, boolean recursive) throws IOException {
+ public NodeList listNodes(ZoneId zone, boolean recursive) {
throw new UnsupportedOperationException();
}
@Override
- public NodeList listNodes(ZoneId zone, String tenant, String applicationId, String instance) throws IOException {
+ public NodeList listNodes(ZoneId zone, String tenant, String applicationId, String instance) {
NodeRepositoryNode nodeA = createNodeA();
NodeRepositoryNode nodeB = createNodeB();
return new NodeList(Arrays.asList(nodeA, nodeB));
@@ -69,57 +69,58 @@ public class NodeRepositoryClientMock implements NodeRepositoryClientInterface {
}
@Override
- public String resetFailureInformation(ZoneId zone, String nodename) throws IOException {
+ public String resetFailureInformation(ZoneId zone, String nodename) {
throw new UnsupportedOperationException();
}
@Override
- public String restart(ZoneId zone, String nodename) throws IOException {
+ public String restart(ZoneId zone, String nodename) {
throw new UnsupportedOperationException();
}
@Override
- public String reboot(ZoneId zone, String nodename) throws IOException {
+ public String reboot(ZoneId zone, String nodename) {
throw new UnsupportedOperationException();
}
@Override
- public String cancelReboot(ZoneId zone, String nodename) throws IOException {
+ public String cancelReboot(ZoneId zone, String nodename) {
throw new UnsupportedOperationException();
}
@Override
- public String wantTo(ZoneId zone, String nodename, WantTo... actions) throws IOException {
+ public String wantTo(ZoneId zone, String nodename, WantTo... actions) {
throw new UnsupportedOperationException();
}
@Override
- public String cancelRestart(ZoneId zone, String nodename) throws IOException {
+ public String cancelRestart(ZoneId zone, String nodename) {
throw new UnsupportedOperationException();
}
@Override
- public String setHardwareFailureDescription(ZoneId zone, String nodename, String hardwareFailureDescription) throws IOException {
+ public String setHardwareFailureDescription(ZoneId zone, String nodename, String hardwareFailureDescription) {
throw new UnsupportedOperationException();
}
@Override
- public void setState(ZoneId zone, NodeState nodeState, String nodename) throws IOException {
+ public void setState(ZoneId zone, NodeState nodeState, String nodename) {
throw new UnsupportedOperationException();
}
@Override
- public String enableMaintenanceJob(ZoneId zone, String jobName) throws IOException {
+ public String enableMaintenanceJob(ZoneId zone, String jobName) {
throw new UnsupportedOperationException();
}
@Override
- public String disableMaintenanceJob(ZoneId zone, String jobName) throws IOException {
+ public String disableMaintenanceJob(ZoneId zone, String jobName) {
throw new UnsupportedOperationException();
}
@Override
- public MaintenanceJobList listMaintenanceJobs(ZoneId zone) throws IOException {
+ public MaintenanceJobList listMaintenanceJobs(ZoneId zone) {
throw new UnsupportedOperationException();
}
+
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/NodeRepositoryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java
index a720b0efa82..9ca9802eac8 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/NodeRepositoryMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.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;
+package com.yahoo.vespa.hosted.controller.integration;
import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/MockRoutingGenerator.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/RoutingGeneratorMock.java
index bc2da7287e1..bfc50a5a93b 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/MockRoutingGenerator.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/RoutingGeneratorMock.java
@@ -1,5 +1,5 @@
// 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.controller.routing;
+package com.yahoo.vespa.hosted.controller.integration;
import com.yahoo.vespa.hosted.controller.api.integration.routing.RoutingEndpoint;
import com.yahoo.vespa.hosted.controller.api.integration.routing.RoutingGenerator;
@@ -11,7 +11,7 @@ import java.util.List;
/**
* @author bratseth
*/
-public class MockRoutingGenerator implements RoutingGenerator {
+public class RoutingGeneratorMock implements RoutingGenerator {
@Override
public List<RoutingEndpoint> endpoints(DeploymentId deployment) {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/SecretStoreMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/SecretStoreMock.java
new file mode 100644
index 00000000000..a14b7b82d67
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/SecretStoreMock.java
@@ -0,0 +1,48 @@
+// 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.integration;
+
+import com.yahoo.component.AbstractComponent;
+import com.yahoo.container.jdisc.secretstore.SecretStore;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * @author mpolden
+ */
+public class SecretStoreMock extends AbstractComponent implements SecretStore {
+
+ private final Map<String, TreeMap<Integer, String>> secrets = new HashMap<>();
+
+ public SecretStoreMock setSecret(String name, String value, int version) {
+ TreeMap<Integer, String> values = secrets.getOrDefault(name, new TreeMap<>());
+ values.put(version, value);
+ secrets.put(name, values);
+ return this;
+ }
+
+ public SecretStoreMock setSecret(String name, String value) {
+ return setSecret(name, value, 1);
+ }
+
+ public SecretStoreMock clear() {
+ secrets.clear();
+ return this;
+ }
+
+ @Override
+ public String getSecret(String key) {
+ TreeMap<Integer, String> values = secrets.get(key);
+ if (values == null || values.isEmpty()) {
+ return null;
+ }
+ return values.lastEntry().getValue();
+ }
+
+ @Override
+ public String getSecret(String key, int version) {
+ return secrets.getOrDefault(key, new TreeMap<>()).get(version);
+ }
+
+}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ZoneRegistryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java
index 28388a48c37..29ebd60d7c6 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ZoneRegistryMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.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;
+package com.yahoo.vespa.hosted.controller.integration;
import com.google.inject.Inject;
import com.yahoo.component.AbstractComponent;
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmerTest.java
index b5941c441e2..703d65c8f9d 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmerTest.java
@@ -23,7 +23,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
- * @author jvenstad
+ * @author jonmv
*/
public class ApplicationOwnershipConfirmerTest {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterInfoMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterInfoMaintainerTest.java
index ba6a858c18e..ec504b1c6dd 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterInfoMaintainerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterInfoMaintainerTest.java
@@ -5,7 +5,7 @@ import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Environment;
import com.yahoo.vespa.hosted.controller.ControllerTester;
-import com.yahoo.vespa.hosted.controller.NodeRepositoryClientMock;
+import com.yahoo.vespa.hosted.controller.integration.NodeRepositoryClientMock;
import com.yahoo.vespa.hosted.controller.application.Deployment;
import com.yahoo.vespa.hosted.controller.persistence.MockCuratorDb;
import org.junit.Test;
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporterTest.java
index 2abd01927a1..2b76d386fdd 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporterTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporterTest.java
@@ -19,10 +19,10 @@ import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.component;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.productionCorpUsEast1;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.stagingTest;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.systemTest;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.component;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionCorpUsEast1;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.stagingTest;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.systemTest;
import static com.yahoo.vespa.hosted.controller.maintenance.DeploymentIssueReporter.maxFailureAge;
import static com.yahoo.vespa.hosted.controller.maintenance.DeploymentIssueReporter.maxInactivity;
import static com.yahoo.vespa.hosted.controller.maintenance.DeploymentIssueReporter.upgradeGracePeriod;
@@ -31,7 +31,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
- * @author jvenstad
+ * @author jonmv
*/
public class DeploymentIssueReporterTest {
@@ -168,8 +168,8 @@ public class DeploymentIssueReporterTest {
class MockDeploymentIssues extends LoggingDeploymentIssues {
- Map<ApplicationId, IssueId> applicationIssues = new HashMap<>();
- Map<IssueId, Integer> issueLevels = new HashMap<>();
+ private final Map<ApplicationId, IssueId> applicationIssues = new HashMap<>();
+ private final Map<IssueId, Integer> issueLevels = new HashMap<>();
MockDeploymentIssues() {
super(tester.clock());
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainerTest.java
index 768164c3002..d3e42bae526 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainerTest.java
@@ -15,6 +15,7 @@ import java.time.Duration;
import java.time.Instant;
import java.util.function.Supplier;
+import static java.time.temporal.ChronoUnit.MILLIS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -50,13 +51,13 @@ public class DeploymentMetricsMaintainerTest {
assertEquals(3, deployment.get().metrics().documentCount(), Double.MIN_VALUE);
assertEquals(4, deployment.get().metrics().queryLatencyMillis(), Double.MIN_VALUE);
assertEquals(5, deployment.get().metrics().writeLatencyMillis(), Double.MIN_VALUE);
- Instant t1 = tester.clock().instant();
+ Instant t1 = tester.clock().instant().truncatedTo(MILLIS);
assertEquals(t1, deployment.get().activity().lastQueried().get());
assertEquals(t1, deployment.get().activity().lastWritten().get());
// Time passes. Activity is updated as app is still receiving traffic
tester.clock().advance(Duration.ofHours(1));
- Instant t2 = tester.clock().instant();
+ Instant t2 = tester.clock().instant().truncatedTo(MILLIS);
maintainer.maintain();
assertEquals(t2, deployment.get().activity().lastQueried().get());
assertEquals(t2, deployment.get().activity().lastWritten().get());
@@ -65,7 +66,7 @@ public class DeploymentMetricsMaintainerTest {
// Query traffic disappears. Query activity stops updating
tester.clock().advance(Duration.ofHours(1));
- Instant t3 = tester.clock().instant();
+ Instant t3 = tester.clock().instant().truncatedTo(MILLIS);
tester.metricsService().setMetric("queriesPerSecond", 0D);
tester.metricsService().setMetric("writesPerSecond", 5D);
maintainer.maintain();
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DnsMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DnsMaintainerTest.java
index af7325950e4..1bedb29ec97 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DnsMaintainerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DnsMaintainerTest.java
@@ -18,8 +18,8 @@ import org.junit.Test;
import java.time.Duration;
import java.util.Optional;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.component;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.systemTest;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.component;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.systemTest;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/FailureRedeployerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/FailureRedeployerTest.java
deleted file mode 100644
index 981b8c8c52a..00000000000
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/FailureRedeployerTest.java
+++ /dev/null
@@ -1,174 +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.hosted.controller.maintenance;
-
-import com.yahoo.component.Version;
-import com.yahoo.config.provision.Environment;
-import com.yahoo.config.provision.SystemName;
-import com.yahoo.slime.Slime;
-import com.yahoo.vespa.config.SlimeUtils;
-import com.yahoo.vespa.hosted.controller.Application;
-import com.yahoo.vespa.hosted.controller.ControllerTester;
-import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
-import com.yahoo.vespa.hosted.controller.application.DeploymentJobs;
-import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
-import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester;
-import org.junit.Test;
-
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.time.Duration;
-import java.util.Collections;
-
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.component;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.productionUsEast3;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.systemTest;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-/**
- * @author mpolden
- */
-public class FailureRedeployerTest {
-
- @Test
- public void testRetryingFailedJobsDuringDeployment() {
- DeploymentTester tester = new DeploymentTester();
- ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
- .upgradePolicy("canary")
- .environment(Environment.prod)
- .region("us-east-3")
- .build();
- Version version = Version.fromString("5.0");
- tester.upgradeSystem(version);
-
- Application app = tester.createApplication("app1", "tenant1", 1, 11L);
- tester.jobCompletion(component).application(app).uploadArtifact(applicationPackage).submit();
- tester.deployAndNotify(app, applicationPackage, true, DeploymentJobs.JobType.systemTest);
- tester.deployAndNotify(app, applicationPackage, true, DeploymentJobs.JobType.stagingTest);
- tester.deployAndNotify(app, applicationPackage, true, DeploymentJobs.JobType.productionUsEast3);
-
- // New version is released
- version = Version.fromString("5.1");
- tester.upgradeSystem(version);
- assertEquals(version, tester.controller().versionStatus().systemVersion().get().versionNumber());
- tester.upgrader().maintain();
- tester.readyJobTrigger().maintain();
-
- // Test environments pass
- tester.deployAndNotify(app, applicationPackage, true, DeploymentJobs.JobType.systemTest);
- tester.deployAndNotify(app, applicationPackage, true, DeploymentJobs.JobType.stagingTest);
-
- // Production job fails and is retried
- tester.clock().advance(Duration.ofSeconds(1)); // Advance time so that we can detect jobs in progress
- tester.deployAndNotify(app, applicationPackage, false, DeploymentJobs.JobType.productionUsEast3);
- assertEquals("Production job is retried", 1, tester.buildService().jobs().size());
- assertEquals("Application has pending upgrade to " + version, version, tester.application(app.id()).change().platform().get());
-
- // Another version is released, which cancels any pending upgrades to lower versions
- version = Version.fromString("5.2");
- tester.upgradeSystem(version);
- tester.upgrader().maintain();
- tester.jobCompletion(DeploymentJobs.JobType.productionUsEast3).application(app).unsuccessful().submit();
- assertEquals("Application starts upgrading to new version", 2, tester.buildService().jobs().size());
- assertEquals("Application has pending upgrade to " + version, version, tester.application(app.id()).change().platform().get());
-
- // Failure re-deployer did not retry failing job for prod.us-east-3, since it no longer had an available change
- assertFalse("Job is not retried", tester.buildService().jobs().stream()
- .anyMatch(j -> j.jobName().equals(DeploymentJobs.JobType.productionUsEast3.jobName())));
-
- // Test environments pass
- tester.deployAndNotify(app, applicationPackage, true, DeploymentJobs.JobType.systemTest);
- tester.deployAndNotify(app, applicationPackage, true, DeploymentJobs.JobType.stagingTest);
-
- // Production job fails again, and is retried
- tester.deployAndNotify(app, applicationPackage, false, DeploymentJobs.JobType.productionUsEast3);
- assertEquals("Job is retried", Collections.singletonList(ControllerTester.buildJob(app, productionUsEast3)), tester.buildService().jobs());
-
- // Production job finally succeeds
- tester.deployAndNotify(app, applicationPackage, true, DeploymentJobs.JobType.productionUsEast3);
- assertTrue("All jobs consumed", tester.buildService().jobs().isEmpty());
- assertFalse("No failures", tester.application(app.id()).deploymentJobs().hasFailures());
- }
- @Test
- public void testRetriesJobsFailingForCurrentChange() {
- DeploymentTester tester = new DeploymentTester();
- ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
- .upgradePolicy("canary")
- .environment(Environment.prod)
- .region("us-east-3")
- .build();
- Version version = Version.fromString("5.0");
- tester.upgradeSystem(version);
-
- Application app = tester.createApplication("app1", "tenant1", 1, 11L);
- tester.jobCompletion(component).application(app).uploadArtifact(applicationPackage).submit();
- tester.deployAndNotify(app, applicationPackage, true, DeploymentJobs.JobType.systemTest);
- tester.deployAndNotify(app, applicationPackage, true, DeploymentJobs.JobType.stagingTest);
- tester.deployAndNotify(app, applicationPackage, true, DeploymentJobs.JobType.productionUsEast3);
-
- // New version is released
- version = Version.fromString("5.1");
- tester.upgradeSystem(version);
- assertEquals(version, tester.controller().versionStatus().systemVersion().get().versionNumber());
- tester.upgrader().maintain();
- tester.readyJobTrigger().maintain();
- assertEquals("Application has pending upgrade to " + version, version, tester.application(app.id()).change().platform().get());
-
- // system-test fails and is left with a retry
- tester.deployAndNotify(app, applicationPackage, false, DeploymentJobs.JobType.systemTest);
-
- // Another version is released
- version = Version.fromString("5.2");
- tester.upgradeSystem(version);
- assertEquals(version, tester.controller().versionStatus().systemVersion().get().versionNumber());
-
- tester.buildService().remove(ControllerTester.buildJob(app, systemTest));
- tester.upgrader().maintain();
- tester.readyJobTrigger().maintain();
- assertEquals("Application has pending upgrade to " + version, version, tester.application(app.id()).change().platform().get());
-
- // Cancellation of outdated version and triggering on a new version is done by the upgrader.
- assertEquals(version, tester.application(app.id()).deploymentJobs().jobStatus().get(systemTest).lastTriggered().get().platform());
- }
-
- @Test
- public void ignoresPullRequestInstances() throws Exception {
- DeploymentTester tester = new DeploymentTester();
- tester.controllerTester().zoneRegistry().setSystemName(SystemName.cd);
-
- // Current system version, matches version in test data
- Version version = Version.fromString("6.42.1");
- tester.upgradeSystem(version);
- assertEquals(version, tester.controller().versionStatus().systemVersion().get().versionNumber());
-
- // Load test data data
- byte[] json = Files.readAllBytes(Paths.get("src/test/java/com/yahoo/vespa/hosted/controller/maintenance/testdata/pr-instance-with-dead-locked-job.json"));
- Slime slime = SlimeUtils.jsonToSlime(json);
- tester.controllerTester().createApplication(slime);
-
- // Failure redeployer does not restart deployment
- tester.readyJobTrigger().maintain();
- assertTrue("No jobs scheduled", tester.buildService().jobs().isEmpty());
- }
-
- @Test
- public void applicationWithoutProjectIdIsNotTriggered() throws Exception {
- DeploymentTester tester = new DeploymentTester();
-
- // Current system version, matches version in test data
- Version version = Version.fromString("6.42.1");
- tester.upgradeSystem(version);
- assertEquals(version, tester.controller().versionStatus().systemVersion().get().versionNumber());
-
- // Load test data data
- byte[] json = Files.readAllBytes(Paths.get("src/test/java/com/yahoo/vespa/hosted/controller/maintenance/testdata/application-without-project-id.json"));
- Slime slime = SlimeUtils.jsonToSlime(json);
- tester.controllerTester().createApplication(slime);
-
- // Failure redeployer does not restart deployment
- tester.readyJobTrigger().maintain();
- assertTrue("No jobs scheduled", tester.buildService().jobs().isEmpty());
- }
-
-}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunnerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunnerTest.java
new file mode 100644
index 00000000000..e084e9aa46d
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunnerTest.java
@@ -0,0 +1,262 @@
+package com.yahoo.vespa.hosted.controller.maintenance;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.vespa.hosted.controller.TestIdentities;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
+import com.yahoo.vespa.hosted.controller.application.SourceRevision;
+import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester;
+import com.yahoo.vespa.hosted.controller.deployment.JobController;
+import com.yahoo.vespa.hosted.controller.deployment.RunStatus;
+import com.yahoo.vespa.hosted.controller.deployment.Step;
+import com.yahoo.vespa.hosted.controller.deployment.Step.Status;
+import com.yahoo.vespa.hosted.controller.deployment.StepRunner;
+import org.junit.Test;
+
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.AbstractExecutorService;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.stagingTest;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.systemTest;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.failed;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.succeeded;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.unfinished;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.deactivateReal;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.deactivateTester;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.deployReal;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.deployTester;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.installInitialReal;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.installReal;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.installTester;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.report;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.startTests;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.endTests;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @author jonmv
+ */
+public class JobRunnerTest {
+
+ @Test
+ public void multiThreadedExecutionFinishes() throws InterruptedException {
+ DeploymentTester tester = new DeploymentTester();
+ JobController jobs = tester.controller().jobController();
+ // Fail the installation of the initial version of the real application in staging tests, and succeed everything else.
+ StepRunner stepRunner = (step, id) -> id.type() == stagingTest && step.get() == startTests? failed : succeeded;
+ CountDownLatch latch = new CountDownLatch(19); // Number of steps that will run, below: all but endTests in staging and all 9 in system.
+ JobRunner runner = new JobRunner(tester.controller(), Duration.ofDays(1), new JobControl(tester.controller().curator()),
+ Executors.newFixedThreadPool(32), notifying(stepRunner, latch));
+
+ ApplicationId id = tester.createApplication("real", "tenant", 1, 1L).id();
+ jobs.submit(id, new SourceRevision("repo", "branch", "bada55"), new byte[0], new byte[0]);
+
+ jobs.start(id, systemTest);
+ try {
+ jobs.start(id, systemTest);
+ fail("Job is already running, so this should not be allowed!");
+ }
+ catch (IllegalStateException e) { }
+ jobs.start(id, stagingTest);
+
+ assertTrue(jobs.last(id, systemTest).get().steps().values().stream().allMatch(unfinished::equals));
+ runner.maintain();
+ assertFalse(jobs.last(id, systemTest).get().hasEnded());
+ assertFalse(jobs.last(id, stagingTest).get().hasEnded());
+
+ latch.await(1, TimeUnit.SECONDS);
+ assertEquals(0, latch.getCount());
+
+ runner.deconstruct(); // Ensures all workers have finished writing to the curator.
+ assertTrue(jobs.last(id, systemTest).get().steps().values().stream().allMatch(succeeded::equals));
+ assertTrue(jobs.last(id, stagingTest).get().hasEnded());
+ assertTrue(jobs.last(id, stagingTest).get().hasFailed());
+ }
+
+ @Test
+ public void stepLogic() {
+ DeploymentTester tester = new DeploymentTester();
+ JobController jobs = tester.controller().jobController();
+ Map<Step, Status> outcomes = new EnumMap<>(Step.class);
+ JobRunner runner = new JobRunner(tester.controller(), Duration.ofDays(1), new JobControl(tester.controller().curator()),
+ inThreadExecutor(), mappedRunner(outcomes));
+
+ ApplicationId id = tester.createApplication("real", "tenant", 1, 1L).id();
+ jobs.submit(id, new SourceRevision("repo", "branch", "bada55"), new byte[0], new byte[0]);
+ Supplier<RunStatus> run = () -> jobs.last(id, systemTest).get();
+
+ jobs.start(id, systemTest);
+ RunId first = run.get().id();
+
+ Map<Step, Status> steps = run.get().steps();
+ runner.maintain();
+ assertEquals(steps, run.get().steps());
+ assertEquals(Arrays.asList(deployReal, deployTester), run.get().readySteps());
+
+ outcomes.put(deployReal, succeeded);
+ runner.maintain();
+ assertEquals(Arrays.asList(installReal, deployTester), run.get().readySteps());
+
+ outcomes.put(installReal, succeeded);
+ runner.maintain();
+ assertEquals(Arrays.asList(deployTester), run.get().readySteps());
+
+ outcomes.put(deployTester, succeeded);
+ runner.maintain();
+ assertEquals(Arrays.asList(installTester), run.get().readySteps());
+
+ outcomes.put(installTester, succeeded);
+ runner.maintain();
+ assertEquals(Arrays.asList(startTests), run.get().readySteps());
+
+ outcomes.put(startTests, succeeded);
+ runner.maintain();
+ assertEquals(Arrays.asList(endTests), run.get().readySteps());
+
+ outcomes.put(endTests, succeeded);
+ runner.maintain();
+ assertEquals(Arrays.asList(deactivateReal, deactivateTester), run.get().readySteps());
+
+ // Failure deactivating real fails the run, but run-always steps continue.
+ outcomes.put(deactivateReal, failed);
+ runner.maintain();
+ assertTrue(run.get().hasFailed());
+ assertEquals(Arrays.asList(deactivateReal, deactivateTester), run.get().readySteps());
+
+ // Abortion does nothing, as the run has already failed.
+ jobs.abort(run.get().id());
+ runner.maintain();
+ assertEquals(Arrays.asList(deactivateReal, deactivateTester), run.get().readySteps());
+
+ outcomes.put(deactivateReal, succeeded);
+ outcomes.put(deactivateTester, succeeded);
+ outcomes.put(report, succeeded);
+ runner.maintain();
+ assertTrue(run.get().hasFailed());
+ assertTrue(run.get().hasEnded());
+ assertTrue(run.get().isAborted());
+
+ // A new run is attempted.
+ jobs.start(id, systemTest);
+ assertEquals(first.number() + 1, run.get().id().number());
+
+ // Run fails on tester deployment -- remaining run-always steps succeed, and the run finishes.
+ outcomes.put(deployTester, failed);
+ runner.maintain();
+ assertTrue(run.get().hasEnded());
+ assertTrue(run.get().hasFailed());
+ assertFalse(run.get().isAborted());
+ assertEquals(failed, run.get().steps().get(deployTester));
+ assertEquals(unfinished, run.get().steps().get(installTester));
+ assertEquals(succeeded, run.get().steps().get(report));
+
+ assertEquals(2, jobs.runs(id, systemTest).size());
+
+ // Start a third run, then unregister and wait for data to be deleted.
+ jobs.start(id, systemTest);
+ jobs.unregister(id);
+ runner.maintain();
+ assertFalse(jobs.last(id, systemTest).isPresent());
+ assertTrue(jobs.runs(id, systemTest).isEmpty());
+ }
+
+ @Test
+ public void locksAndGarbage() throws InterruptedException, BrokenBarrierException {
+ DeploymentTester tester = new DeploymentTester();
+ JobController jobs = tester.controller().jobController();
+ // Hang during tester deployment, until notified.
+ CyclicBarrier barrier = new CyclicBarrier(2);
+ JobRunner runner = new JobRunner(tester.controller(), Duration.ofDays(1), new JobControl(tester.controller().curator()),
+ Executors.newFixedThreadPool(32), waitingRunner(barrier));
+
+ ApplicationId id = tester.createApplication("real", "tenant", 1, 1L).id();
+ jobs.submit(id, new SourceRevision("repo", "branch", "bada55"), new byte[0], new byte[0]);
+
+ RunId runId = new RunId(id, systemTest, 1);
+ jobs.start(id, systemTest);
+ runner.maintain();
+ barrier.await();
+ try {
+ jobs.locked(id, systemTest, deactivateTester, step -> { });
+ fail("deployTester step should still be locked!");
+ }
+ catch (TimeoutException e) { }
+
+ // Thread is still trying to deploy tester -- delete application, and see all data is garbage collected.
+ assertEquals(Collections.singletonList(runId), jobs.active().stream().map(run -> run.id()).collect(Collectors.toList()));
+ tester.controller().applications().deleteApplication(id, Optional.of(TestIdentities.userNToken));
+ assertEquals(Collections.emptyList(), jobs.active());
+ assertEquals(runId, jobs.last(id, systemTest).get().id());
+
+ // Deployment still ongoing, so garbage is not yet collected.
+ runner.maintain();
+ assertEquals(runId, jobs.last(id, systemTest).get().id());
+
+ // Deployment lets go, deactivation may now run, and trash is thrown out.
+ barrier.await();
+ runner.maintain();
+ assertEquals(Optional.empty(), jobs.last(id, systemTest));
+ }
+
+ private static ExecutorService inThreadExecutor() {
+ return new AbstractExecutorService() {
+ AtomicBoolean shutDown = new AtomicBoolean(false);
+ @Override public void shutdown() { shutDown.set(true); }
+ @Override public List<Runnable> shutdownNow() { shutDown.set(true); return Collections.emptyList(); }
+ @Override public boolean isShutdown() { return shutDown.get(); }
+ @Override public boolean isTerminated() { return shutDown.get(); }
+ @Override public boolean awaitTermination(long timeout, TimeUnit unit) { return true; }
+ @Override public void execute(Runnable command) { command.run(); }
+ };
+ }
+
+ private static StepRunner notifying(StepRunner runner, CountDownLatch latch) {
+ return (step, id) -> {
+ Status status = runner.run(step, id);
+ synchronized (latch) {
+ assertTrue(latch.getCount() > 0);
+ latch.countDown();
+ }
+ return status;
+ };
+ }
+
+ private static StepRunner mappedRunner(Map<Step, Status> outcomes) {
+ return (step, id) -> outcomes.getOrDefault(step.get(), Status.unfinished);
+ }
+
+ private static StepRunner waitingRunner(CyclicBarrier barrier) {
+ return (step, id) -> {
+ try {
+ if (step.get() == deployTester) {
+ barrier.await(); // Wake up the main thread, which waits for this step to be locked.
+ barrier.reset();
+ barrier.await(); // Then wait while holding the lock for this step, until the main thread wakes us up.
+ }
+ }
+ catch (InterruptedException | BrokenBarrierException e) {
+ throw new AssertionError(e);
+ }
+ return succeeded;
+ };
+ }
+
+}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporterTest.java
index af61af6da52..fa6edd939c4 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporterTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporterTest.java
@@ -8,10 +8,9 @@ import com.yahoo.config.provision.SystemName;
import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.ControllerTester;
-import com.yahoo.vespa.hosted.controller.MetricsMock;
-import com.yahoo.vespa.hosted.controller.MetricsMock.MapContext;
-import com.yahoo.vespa.hosted.controller.api.integration.chef.AttributeMapping;
-import com.yahoo.vespa.hosted.controller.api.integration.chef.Chef;
+import com.yahoo.vespa.hosted.controller.integration.MetricsMock;
+import com.yahoo.vespa.hosted.controller.integration.MetricsMock.MapContext;
+import com.yahoo.vespa.hosted.controller.api.integration.chef.ChefMock;
import com.yahoo.vespa.hosted.controller.api.integration.chef.rest.PartialNodeResult;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
@@ -19,7 +18,6 @@ import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester;
import com.yahoo.vespa.hosted.controller.persistence.MockCuratorDb;
import org.junit.Before;
import org.junit.Test;
-import org.mockito.Mockito;
import java.io.IOException;
import java.io.UncheckedIOException;
@@ -31,16 +29,13 @@ import java.time.Instant;
import java.time.ZoneId;
import java.util.Map;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.component;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.productionUsWest1;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.stagingTest;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.systemTest;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.component;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionUsWest1;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.stagingTest;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.systemTest;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
-import static org.mockito.Matchers.anyListOf;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.when;
/**
* @author mortent
@@ -57,7 +52,7 @@ public class MetricsReporterTest {
@Test
public void test_chef_metrics() {
- Clock clock = Clock.fixed(Instant.ofEpochSecond(1475497913), ZoneId.systemDefault());;
+ Clock clock = Clock.fixed(Instant.ofEpochSecond(1475497913), ZoneId.systemDefault());
ControllerTester tester = new ControllerTester();
MetricsReporter metricsReporter = createReporter(clock, tester.controller(), metrics, SystemName.cd);
metricsReporter.maintain();
@@ -170,7 +165,7 @@ public class MetricsReporterTest {
private MetricsReporter createReporter(Clock clock, Controller controller, MetricsMock metricsMock,
SystemName system) {
- Chef client = Mockito.mock(Chef.class);
+ ChefMock chef = new ChefMock();
PartialNodeResult result;
try {
result = new ObjectMapper()
@@ -179,9 +174,8 @@ public class MetricsReporterTest {
} catch (IOException e) {
throw new UncheckedIOException(e);
}
- when(client.partialSearchNodes(anyString(), anyListOf(AttributeMapping.class))).thenReturn(result);
-
- return new MetricsReporter(controller, metricsMock, client, clock, new JobControl(new MockCuratorDb()), system);
+ chef.addPartialResult(result.rows);
+ return new MetricsReporter(controller, metricsMock, chef, clock, new JobControl(new MockCuratorDb()), system);
}
private Map<MapContext, Map<String, Number>> getMetricsByHost(String hostname) {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployerTest.java
index 0b55d13a5ad..4483122d554 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployerTest.java
@@ -5,9 +5,9 @@ import com.yahoo.component.Version;
import com.yahoo.config.provision.Environment;
import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.api.integration.BuildService;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.Change;
-import com.yahoo.vespa.hosted.controller.application.DeploymentJobs;
import com.yahoo.vespa.hosted.controller.application.SourceRevision;
import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester;
@@ -46,7 +46,7 @@ public class OutstandingChangeDeployerTest {
assertEquals(Change.of(version), tester.application("app1").change());
assertFalse(tester.application("app1").outstandingChange().isPresent());
- tester.jobCompletion(DeploymentJobs.JobType.component)
+ tester.jobCompletion(JobType.component)
.application(tester.application("app1"))
.sourceRevision(new SourceRevision("repository1","master", "cafed00d"))
.nextBuildNumber()
@@ -63,9 +63,9 @@ public class OutstandingChangeDeployerTest {
assertEquals("No effect as job is in progress", 2, tester.buildService().jobs().size());
assertEquals("1.0.43-cafed00d", app.outstandingChange().application().get().id());
- tester.deployAndNotify(app, applicationPackage, true, DeploymentJobs.JobType.systemTest);
- tester.deployAndNotify(app, applicationPackage, true, DeploymentJobs.JobType.stagingTest);
- tester.deployAndNotify(app, applicationPackage, true, DeploymentJobs.JobType.productionUsWest1);
+ tester.deployAndNotify(app, applicationPackage, true, JobType.systemTest);
+ tester.deployAndNotify(app, applicationPackage, true, JobType.stagingTest);
+ tester.deployAndNotify(app, applicationPackage, true, JobType.productionUsWest1);
assertEquals("Upgrade done", 0, tester.buildService().jobs().size());
deployer.maintain();
@@ -75,8 +75,8 @@ public class OutstandingChangeDeployerTest {
List<BuildService.BuildJob> jobs = tester.buildService().jobs();
assertEquals(2, jobs.size());
assertEquals(11, jobs.get(0).projectId());
- tester.assertRunning(app.id(), DeploymentJobs.JobType.systemTest);
- tester.assertRunning(app.id(), DeploymentJobs.JobType.stagingTest);
+ tester.assertRunning(JobType.systemTest, app.id());
+ tester.assertRunning(JobType.stagingTest, app.id());
assertFalse(tester.application("app1").outstandingChange().isPresent());
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/SystemUpgraderTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/SystemUpgraderTest.java
index 4b563ed203d..35393302459 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/SystemUpgraderTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/SystemUpgraderTest.java
@@ -2,17 +2,17 @@
package com.yahoo.vespa.hosted.controller.maintenance;
import com.yahoo.component.Version;
-import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.vespa.hosted.controller.NodeRepositoryMock;
+import com.yahoo.vespa.hosted.controller.integration.NodeRepositoryMock;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node;
import com.yahoo.vespa.hosted.controller.api.integration.zone.UpgradePolicy;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.application.SystemApplication;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester;
-import org.junit.Ignore;
+import org.junit.Before;
import org.junit.Test;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.function.Function;
@@ -29,7 +29,12 @@ public class SystemUpgraderTest {
private static final ZoneId zone3 = ZoneId.from("prod", "us-central-1");
private static final ZoneId zone4 = ZoneId.from("prod", "us-east-3");
- private final DeploymentTester tester = new DeploymentTester();
+ private DeploymentTester tester;
+
+ @Before
+ public void before() {
+ tester = new DeploymentTester();
+ }
@Test
public void upgrade_system() {
@@ -55,7 +60,7 @@ public class SystemUpgraderTest {
// Controller upgrades
Version version2 = Version.fromString("6.6");
tester.upgradeController(version2);
- assertEquals(version2, tester.controller().versionStatus().controllerVersion().get().versionNumber());
+ assertControllerVersion(version2);
// System upgrade starts
tester.systemUpgrader().maintain();
@@ -79,6 +84,11 @@ public class SystemUpgraderTest {
assertWantedVersion(SystemApplication.configServer, version1, zone2, zone3, zone4);
assertWantedVersion(SystemApplication.zone, version1, zone2, zone3, zone4);
+ // zone 2 and 3: upgrade does not start until zone 1 zone-application config converges
+ tester.systemUpgrader().maintain();
+ assertWantedVersion(SystemApplication.configServer, version1, zone2, zone3);
+ convergeServices(SystemApplication.zone, zone1);
+
// zone 2 and 3: zone-config-server upgrades, first in zone 2, then in zone 3
tester.systemUpgrader().maintain();
assertWantedVersion(SystemApplication.configServer, version2, zone2, zone3);
@@ -96,6 +106,7 @@ public class SystemUpgraderTest {
tester.systemUpgrader().maintain();
assertWantedVersion(SystemApplication.zone, version2, zone2, zone3);
completeUpgrade(SystemApplication.zone, version2, zone2, zone3);
+ convergeServices(SystemApplication.zone, zone2, zone3);
// zone 4: zone-config-server upgrades
tester.systemUpgrader().maintain();
@@ -105,14 +116,19 @@ public class SystemUpgraderTest {
// System version remains unchanged until final application upgrades
tester.computeVersionStatus();
- assertEquals(version1, tester.controller().versionStatus().systemVersion().get().versionNumber());
+ assertSystemVersion(version1);
// zone 4: zone-application upgrades
tester.systemUpgrader().maintain();
assertWantedVersion(SystemApplication.zone, version2, zone4);
completeUpgrade(SystemApplication.zone, version2, zone4);
+
+ // zone 4: System version remains unchanged until config converges
tester.computeVersionStatus();
- assertEquals(version2, tester.controller().versionStatus().systemVersion().get().versionNumber());
+ assertSystemVersion(version1);
+ convergeServices(SystemApplication.zone, zone4);
+ tester.computeVersionStatus();
+ assertSystemVersion(version2);
// Next run does nothing as system is now upgraded
tester.systemUpgrader().maintain();
@@ -121,6 +137,41 @@ public class SystemUpgraderTest {
}
@Test
+ public void upgrade_controller_with_non_converging_application() {
+ tester.controllerTester().zoneRegistry().setUpgradePolicy(UpgradePolicy.create().upgrade(zone1));
+
+ // Bootstrap system
+ tester.configServer().bootstrap(Collections.singletonList(zone1), SystemApplication.configServer,
+ SystemApplication.zone);
+ Version version1 = Version.fromString("6.5");
+ tester.upgradeSystem(version1);
+
+ // Controller upgrades
+ Version version2 = Version.fromString("6.6");
+ tester.upgradeController(version2);
+
+ // zone 1: System applications upgrade
+ tester.systemUpgrader().maintain();
+ completeUpgrade(SystemApplication.configServer, version2, zone1);
+ tester.systemUpgrader().maintain();
+ completeUpgrade(SystemApplication.zone, version2, zone1);
+ tester.computeVersionStatus();
+ assertSystemVersion(version1); // Unchanged until zone-application converges
+
+ // Controller upgrades again
+ Version version3 = Version.fromString("6.7");
+ tester.upgradeController(version3);
+ assertSystemVersion(version1);
+ assertControllerVersion(version3);
+
+ // zone 1: zone-application converges and system version changes
+ convergeServices(SystemApplication.zone, zone1);
+ tester.computeVersionStatus();
+ assertSystemVersion(version2);
+ assertControllerVersion(version3);
+ }
+
+ @Test
public void upgrade_system_containing_host_applications() {
tester.controllerTester().zoneRegistry().setUpgradePolicy(
UpgradePolicy.create()
@@ -138,7 +189,7 @@ public class SystemUpgraderTest {
// Controller upgrades
Version version2 = Version.fromString("6.6");
tester.upgradeController(version2);
- assertEquals(version2, tester.controller().versionStatus().controllerVersion().get().versionNumber());
+ assertControllerVersion(version2);
// System upgrades in zone 1:
tester.systemUpgrader().maintain();
@@ -148,6 +199,7 @@ public class SystemUpgraderTest {
completeUpgrade(allExceptZone, version2, zone1);
tester.systemUpgrader().maintain();
completeUpgrade(SystemApplication.zone, version2, zone1);
+ convergeServices(SystemApplication.zone, zone1);
assertWantedVersion(SystemApplication.all(), version1, zone2, zone3, zone4);
// zone 2 and 3:
@@ -155,6 +207,7 @@ public class SystemUpgraderTest {
completeUpgrade(allExceptZone, version2, zone2, zone3);
tester.systemUpgrader().maintain();
completeUpgrade(SystemApplication.zone, version2, zone2, zone3);
+ convergeServices(SystemApplication.zone, zone2, zone3);
assertWantedVersion(SystemApplication.all(), version1, zone4);
// zone 4:
@@ -170,36 +223,42 @@ public class SystemUpgraderTest {
@Test
public void never_downgrades_system() {
- ZoneId zone = ZoneId.from("prod", "eu-west-1");
- tester.controllerTester().zoneRegistry().setUpgradePolicy(UpgradePolicy.create().upgrade(zone));
+ tester.controllerTester().zoneRegistry().setUpgradePolicy(UpgradePolicy.create().upgrade(zone1));
Version version = Version.fromString("6.5");
tester.upgradeSystem(version);
tester.systemUpgrader().maintain();
- assertWantedVersion(SystemApplication.configServer, version, zone);
- assertWantedVersion(SystemApplication.zone, version, zone);
+ assertWantedVersion(SystemApplication.configServer, version, zone1);
+ assertWantedVersion(SystemApplication.zone, version, zone1);
// Controller is downgraded
tester.upgradeController(Version.fromString("6.4"));
// Wanted version for zone remains unchanged
tester.systemUpgrader().maintain();
- assertWantedVersion(SystemApplication.configServer, version, zone);
- assertWantedVersion(SystemApplication.zone, version, zone);
+ assertWantedVersion(SystemApplication.configServer, version, zone1);
+ assertWantedVersion(SystemApplication.zone, version, zone1);
}
/** Simulate upgrade of nodes allocated to given application. In a real system this is done by the node itself */
private void completeUpgrade(SystemApplication application, Version version, ZoneId... zones) {
assertWantedVersion(application, version, zones);
for (ZoneId zone : zones) {
- for (Node node : nodeRepository().listOperational(zone, application.id())) {
+ for (Node node : nodeRepository().list(zone, application.id(), SystemApplication.activeStates())) {
nodeRepository().add(zone, new Node(node.hostname(), node.state(), node.type(), node.owner(),
node.wantedVersion(), node.wantedVersion()));
}
+
assertCurrentVersion(application, version, zone);
}
}
+ private void convergeServices(SystemApplication application, ZoneId... zones) {
+ for (ZoneId zone : zones) {
+ tester.controllerTester().configServer().convergeServices(application.id(), zone);
+ }
+ }
+
private void completeUpgrade(List<SystemApplication> applications, Version version, ZoneId... zones) {
applications.forEach(application -> completeUpgrade(application, version, zones));
}
@@ -214,26 +273,34 @@ public class SystemUpgraderTest {
node.currentVersion(), node.wantedVersion()));
}
+ private void assertSystemVersion(Version version) {
+ assertEquals(version, tester.controller().versionStatus().systemVersion().get().versionNumber());
+ }
+
+ private void assertControllerVersion(Version version) {
+ assertEquals(version, tester.controller().versionStatus().controllerVersion().get().versionNumber());
+ }
+
private void assertWantedVersion(SystemApplication application, Version version, ZoneId... zones) {
- assertVersion(application.id(), version, Node::wantedVersion, zones);
+ assertVersion(application, version, Node::wantedVersion, zones);
}
private void assertCurrentVersion(SystemApplication application, Version version, ZoneId... zones) {
- assertVersion(application.id(), version, Node::currentVersion, zones);
+ assertVersion(application, version, Node::currentVersion, zones);
}
private void assertWantedVersion(List<SystemApplication> applications, Version version, ZoneId... zones) {
- applications.forEach(application -> assertVersion(application.id(), version, Node::wantedVersion, zones));
+ applications.forEach(application -> assertVersion(application, version, Node::wantedVersion, zones));
}
private void assertCurrentVersion(List<SystemApplication> applications, Version version, ZoneId... zones) {
- applications.forEach(application -> assertVersion(application.id(), version, Node::currentVersion, zones));
+ applications.forEach(application -> assertVersion(application, version, Node::currentVersion, zones));
}
- private void assertVersion(ApplicationId application, Version version, Function<Node, Version> versionField,
+ private void assertVersion(SystemApplication application, Version version, Function<Node, Version> versionField,
ZoneId... zones) {
for (ZoneId zone : zones) {
- for (Node node : nodeRepository().listOperational(zone, application)) {
+ for (Node node : nodeRepository().list(zone, application.id(), SystemApplication.activeStates())) {
assertEquals(application + " version", version, versionField.apply(node));
}
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UpgraderTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UpgraderTest.java
index 5d6fb76cacf..f1b20694f3d 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UpgraderTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UpgraderTest.java
@@ -7,24 +7,25 @@ import com.yahoo.config.provision.RegionName;
import com.yahoo.test.ManualClock;
import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.ControllerTester;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.Deployment;
-import com.yahoo.vespa.hosted.controller.application.DeploymentJobs;
import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester;
import com.yahoo.vespa.hosted.controller.versions.VespaVersion;
+import org.junit.Before;
import org.junit.Test;
import java.time.Duration;
import java.time.Instant;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.component;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.productionUsCentral1;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.productionUsEast3;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.productionUsWest1;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.stagingTest;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.systemTest;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.component;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionUsCentral1;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionUsEast3;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionUsWest1;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.stagingTest;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.systemTest;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -34,11 +35,16 @@ import static org.junit.Assert.assertTrue;
*/
public class UpgraderTest {
+ private DeploymentTester tester;
+
+ @Before
+ public void before() {
+ tester = new DeploymentTester();
+ }
+
@Test
public void testUpgrading() {
// --- Setup
- DeploymentTester tester = new DeploymentTester();
-
Version version = Version.fromString("5.0");
tester.upgradeSystem(version);
@@ -220,7 +226,7 @@ public class UpgraderTest {
tester.completeUpgradeWithError(default3, version54, "default", stagingTest);
- tester.completeUpgradeWithError(default4, version54, "default", DeploymentJobs.JobType.productionUsWest1);
+ tester.completeUpgradeWithError(default4, version54, "default", JobType.productionUsWest1);
// State: Default applications started upgrading to 5.5
tester.clock().advance(Duration.ofHours(1));
tester.upgrader().maintain();
@@ -229,13 +235,15 @@ public class UpgraderTest {
tester.completeUpgradeWithError(default0, version55, "default", stagingTest);
tester.completeUpgradeWithError(default1, version55, "default", stagingTest);
tester.completeUpgradeWithError(default2, version55, "default", stagingTest);
- tester.completeUpgradeWithError(default3, version55, "default", DeploymentJobs.JobType.productionUsWest1);
+ tester.clock().advance(Duration.ofHours(2).plus(Duration.ofSeconds(1))); // Retry failing job for default3
+ tester.readyJobTrigger().maintain();
+ tester.completeUpgradeWithError(default3, version55, "default", JobType.productionUsWest1);
tester.upgradeSystem(version55);
assertEquals(VespaVersion.Confidence.broken, tester.controller().versionStatus().systemVersion().get().confidence());
// Finish running job, without retry.
tester.clock().advance(Duration.ofHours(1));
- tester.jobCompletion(DeploymentJobs.JobType.productionUsWest1).application(default3).unsuccessful().submit();
+ tester.jobCompletion(JobType.productionUsWest1).application(default3).unsuccessful().submit();
tester.upgrader().maintain();
tester.buildService().clear();
@@ -249,7 +257,6 @@ public class UpgraderTest {
@Test
public void testUpgradingToVersionWhichBreaksSomeNonCanaries() {
// --- Setup
- DeploymentTester tester = new DeploymentTester();
tester.upgrader().maintain();
tester.triggerUntilQuiescence();
assertEquals("No system version: Nothing to do", 0, tester.buildService().jobs().size());
@@ -320,7 +327,6 @@ public class UpgraderTest {
@Test
public void testDeploymentAlreadyInProgressForUpgrade() {
- DeploymentTester tester = new DeploymentTester();
ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
.upgradePolicy("canary")
.environment(Environment.prod)
@@ -375,7 +381,6 @@ public class UpgraderTest {
@Test
public void testUpgradeCancelledWithDeploymentInProgress() {
- DeploymentTester tester = new DeploymentTester();
Version version = Version.fromString("5.0");
tester.upgradeSystem(version);
@@ -441,7 +446,6 @@ public class UpgraderTest {
*/
@Test
public void testVersionIsBrokenAfterAZoneIsLive() {
- DeploymentTester tester = new DeploymentTester();
Version v0 = Version.fromString("5.0");
tester.upgradeSystem(v0);
@@ -525,7 +529,6 @@ public class UpgraderTest {
@Test
public void testConfidenceIgnoresFailingApplicationChanges() {
- DeploymentTester tester = new DeploymentTester();
Version version = Version.fromString("5.0");
tester.upgradeSystem(version);
@@ -732,7 +735,6 @@ public class UpgraderTest {
@Test
public void testReschedulesUpgradeAfterTimeout() {
- DeploymentTester tester = new DeploymentTester();
Version version = Version.fromString("5.0");
tester.upgradeSystem(version);
@@ -801,7 +803,7 @@ public class UpgraderTest {
// 5th app never reports back and has a dead job, but no ongoing change
Application deadLocked = tester.applications().require(default4.id());
- tester.assertRunning(deadLocked.id(), systemTest);
+ tester.assertRunning(systemTest, deadLocked.id());
assertFalse("No change present", deadLocked.change().isPresent());
// 4 out of 5 applications are repaired and confidence is restored
@@ -836,7 +838,6 @@ public class UpgraderTest {
@Test
public void testThrottlesUpgrades() {
- DeploymentTester tester = new DeploymentTester();
Version version = Version.fromString("5.0");
tester.upgradeSystem(version);
@@ -888,7 +889,6 @@ public class UpgraderTest {
@Test
public void testAllowApplicationChangeDuringFailingUpgrade() {
- DeploymentTester tester = new DeploymentTester();
Version version = Version.fromString("5.0");
tester.upgradeSystem(version);
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 656c377b84b..a17584f9bfa 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
@@ -6,10 +6,10 @@ import com.yahoo.config.application.api.DeploymentSpec;
import com.yahoo.config.application.api.ValidationOverrides;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
-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;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.application.ApplicationVersion;
@@ -26,7 +26,6 @@ import com.yahoo.vespa.hosted.controller.application.SourceRevision;
import com.yahoo.vespa.hosted.controller.rotation.RotationId;
import org.junit.Test;
-import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -78,17 +77,17 @@ public class ApplicationSerializerTest {
OptionalLong projectId = OptionalLong.of(123L);
List<JobStatus> statusList = new ArrayList<>();
- statusList.add(JobStatus.initial(DeploymentJobs.JobType.systemTest)
+ statusList.add(JobStatus.initial(JobType.systemTest)
.withTriggering(Version.fromString("5.6.7"), ApplicationVersion.unknown, empty(), "Test", Instant.ofEpochMilli(7))
.withCompletion(30, empty(), Instant.ofEpochMilli(8)));
- statusList.add(JobStatus.initial(DeploymentJobs.JobType.stagingTest)
+ statusList.add(JobStatus.initial(JobType.stagingTest)
.withTriggering(Version.fromString("5.6.6"), ApplicationVersion.unknown, empty(), "Test 2", Instant.ofEpochMilli(5))
.withCompletion(11, Optional.of(JobError.unknown), Instant.ofEpochMilli(6)));
- statusList.add(JobStatus.initial(DeploymentJobs.JobType.from(main, zone1).get())
+ statusList.add(JobStatus.initial(JobType.from(main, zone1).get())
.withTriggering(Version.fromString("5.6.6"), ApplicationVersion.unknown, deployments.stream().findFirst(), "Test 3", Instant.ofEpochMilli(6))
.withCompletion(11, empty(), Instant.ofEpochMilli(7)));
- DeploymentJobs deploymentJobs = new DeploymentJobs(projectId, statusList, empty());
+ DeploymentJobs deploymentJobs = new DeploymentJobs(projectId, statusList, empty(), true);
Application original = new Application(ApplicationId.from("t1", "a1", "i1"),
deploymentSpec,
@@ -119,10 +118,10 @@ public class ApplicationSerializerTest {
assertEquals(original.deploymentJobs().projectId(), serialized.deploymentJobs().projectId());
assertEquals(original.deploymentJobs().jobStatus().size(), serialized.deploymentJobs().jobStatus().size());
- assertEquals( original.deploymentJobs().jobStatus().get(DeploymentJobs.JobType.systemTest),
- serialized.deploymentJobs().jobStatus().get(DeploymentJobs.JobType.systemTest));
- assertEquals( original.deploymentJobs().jobStatus().get(DeploymentJobs.JobType.stagingTest),
- serialized.deploymentJobs().jobStatus().get(DeploymentJobs.JobType.stagingTest));
+ assertEquals( original.deploymentJobs().jobStatus().get(JobType.systemTest),
+ serialized.deploymentJobs().jobStatus().get(JobType.systemTest));
+ assertEquals( original.deploymentJobs().jobStatus().get(JobType.stagingTest),
+ serialized.deploymentJobs().jobStatus().get(JobType.stagingTest));
assertEquals(original.outstandingChange(), serialized.outstandingChange());
@@ -220,41 +219,4 @@ public class ApplicationSerializerTest {
// ok if no error
}
- private Slime applicationSlime(boolean error) {
- return applicationSlime(123, error);
- }
-
- private Slime applicationSlime(long projectId, boolean error) {
- return SlimeUtils.jsonToSlime(applicationJson(projectId, error).getBytes(StandardCharsets.UTF_8));
- }
-
- private String applicationJson(long projectId, boolean error) {
- return
- "{\n" +
- " \"id\": \"t1:a1:i1\",\n" +
- " \"deploymentSpecField\": \"<deployment version='1.0'/>\",\n" +
- " \"deploymentJobs\": {\n" +
- " \"projectId\": " + projectId + ",\n" +
- " \"jobStatus\": [\n" +
- " {\n" +
- " \"jobType\": \"system-test\",\n" +
- (error ? " \"jobError\": \"" + JobError.unknown + "\",\n" : "") +
- " \"lastCompleted\": {\n" +
- " \"version\": \"6.1\",\n" +
- " \"revision\": {\n" +
- " \"applicationPackageHash\": \"dead\",\n" +
- " \"sourceRevision\": {\n" +
- " \"repositoryField\": \"git@git.foo\",\n" +
- " \"branchField\": \"origin/master\",\n" +
- " \"commitField\": \"cafe\"\n" +
- " }\n" +
- " },\n" +
- " \"at\": 1505725189469\n" +
- " }\n" +
- " }\n" +
- " ]\n" +
- " }\n" +
- "}\n";
- }
-
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java
new file mode 100644
index 00000000000..12640a5e8fa
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java
@@ -0,0 +1,90 @@
+package com.yahoo.vespa.hosted.controller.persistence;
+
+import com.google.common.collect.ImmutableMap;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.vespa.config.SlimeUtils;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
+import com.yahoo.vespa.hosted.controller.deployment.RunStatus;
+import com.yahoo.vespa.hosted.controller.deployment.Step;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Instant;
+import java.util.Collections;
+
+import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.failed;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.succeeded;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.unfinished;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.deactivateReal;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.deactivateTester;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.deployInitialReal;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.deployReal;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.deployTester;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.installInitialReal;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.installReal;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.installTester;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.report;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.startTests;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.endTests;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class RunSerializerTest {
+
+ private static final RunSerializer serializer = new RunSerializer();
+ private static final Path runFile = Paths.get("src/test/java/com/yahoo/vespa/hosted/controller/persistence/testdata/run-status.json");
+ private static final RunId id = new RunId(ApplicationId.from("tenant", "application", "default"),
+ JobType.productionUsEast3,
+ (long) 112358);
+ private static final Instant start = Instant.parse("2007-12-03T10:15:30.00Z");
+
+ @Test
+ public void testSerialization() throws IOException {
+ for (Step step : Step.values())
+ assertEquals(step, RunSerializer.stepOf(RunSerializer.valueOf(step)));
+
+ for (Step.Status status : Step.Status.values())
+ assertEquals(status, RunSerializer.statusOf(RunSerializer.valueOf(status)));
+
+ // The purpose of this serialised data is to ensure a new format does not break everything, so keep it up to date!
+ RunStatus run = serializer.runsFromSlime(SlimeUtils.jsonToSlime(Files.readAllBytes(runFile))).get(id);
+ for (Step step : Step.values())
+ assertTrue(run.steps().containsKey(step));
+
+ assertEquals(id, run.id());
+ assertEquals(start, run.start());
+ assertFalse(run.hasEnded());
+ assertFalse(run.isAborted());
+ assertEquals(ImmutableMap.<Step, Step.Status>builder()
+ .put(deployInitialReal, unfinished)
+ .put(installInitialReal, failed)
+ .put(deployReal, succeeded)
+ .put(installReal, unfinished)
+ .put(deactivateReal, failed)
+ .put(deployTester, succeeded)
+ .put(installTester, unfinished)
+ .put(deactivateTester, failed)
+ .put(startTests, succeeded)
+ .put(endTests, unfinished)
+ .put(report, failed)
+ .build(),
+ run.steps());
+
+ run = run.aborted().finished(Instant.now());
+ assertTrue(run.isAborted());
+ assertTrue(run.hasEnded());
+
+ RunStatus phoenix = serializer.runsFromSlime(serializer.toSlime(Collections.singleton(run))).get(id);
+ assertEquals(run.id(), phoenix.id());
+ assertEquals(run.start(), phoenix.start());
+ assertEquals(run.end(), phoenix.end());
+ assertEquals(run.isAborted(), phoenix.isAborted());
+ assertEquals(run.steps(), phoenix.steps());
+ }
+
+}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/VersionStatusSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/VersionStatusSerializerTest.java
index bba53f42a61..87e145f9f93 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/VersionStatusSerializerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/VersionStatusSerializerTest.java
@@ -12,9 +12,11 @@ import org.junit.Test;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
+import static java.time.temporal.ChronoUnit.MILLIS;
import static org.junit.Assert.assertEquals;
/**
@@ -27,7 +29,7 @@ public class VersionStatusSerializerTest {
List<VespaVersion> vespaVersions = new ArrayList<>();
DeploymentStatistics statistics = new DeploymentStatistics(
Version.fromString("5.0"),
- Arrays.asList(ApplicationId.from("tenant1", "failing1", "default")),
+ Collections.singletonList(ApplicationId.from("tenant1", "failing1", "default")),
Arrays.asList(ApplicationId.from("tenant2", "success1", "default"),
ApplicationId.from("tenant2", "success2", "default")),
Arrays.asList(ApplicationId.from("tenant1", "failing1", "default"),
@@ -46,7 +48,7 @@ public class VersionStatusSerializerTest {
VespaVersion a = status.versions().get(i);
VespaVersion b = deserialized.versions().get(i);
assertEquals(a.releaseCommit(), b.releaseCommit());
- assertEquals(a.committedAt(), b.committedAt());
+ assertEquals(a.committedAt().truncatedTo(MILLIS), b.committedAt());
assertEquals(a.isControllerVersion(), b.isControllerVersion());
assertEquals(a.isSystemVersion(), b.isSystemVersion());
assertEquals(a.statistics(), b.statistics());
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/testdata/run-status.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/testdata/run-status.json
new file mode 100644
index 00000000000..d659bd9fff0
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/testdata/run-status.json
@@ -0,0 +1,21 @@
+[
+ {
+ "id": "tenant:application:default",
+ "type": "production-us-east-3",
+ "number": 112358,
+ "start": 1196676930000,
+ "steps": {
+ "DIR": "U",
+ "IIR": "F",
+ "DR": "S",
+ "IR": "U",
+ "DAR": "F",
+ "DT": "S",
+ "IT": "U",
+ "DAT": "F",
+ "ST": "S",
+ "ET": "U",
+ "R": "F"
+ }
+ }
+] \ No newline at end of file
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ApplicationRequestToDiscFilterRequestWrapper.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ApplicationRequestToDiscFilterRequestWrapper.java
index eee0519b12b..4883bde99b1 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ApplicationRequestToDiscFilterRequestWrapper.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ApplicationRequestToDiscFilterRequestWrapper.java
@@ -27,9 +27,14 @@ import java.util.concurrent.TimeUnit;
public class ApplicationRequestToDiscFilterRequestWrapper extends DiscFilterRequest {
private final Request request;
+ private final List<X509Certificate> clientCertificateChain;
private Principal userPrincipal;
public ApplicationRequestToDiscFilterRequestWrapper(Request request) {
+ this(request, Collections.emptyList());
+ }
+
+ public ApplicationRequestToDiscFilterRequestWrapper(Request request, List<X509Certificate> clientCertificateChain) {
super(new ServletOrJdiscHttpRequest() {
@Override
public void copyHeaders(HeaderFields target) {
@@ -93,6 +98,7 @@ public class ApplicationRequestToDiscFilterRequestWrapper extends DiscFilterRequ
});
this.request = request;
this.userPrincipal = request.getUserPrincipal().orElse(null);
+ this.clientCertificateChain = clientCertificateChain;
}
public Request getUpdatedRequest() {
@@ -178,7 +184,7 @@ public class ApplicationRequestToDiscFilterRequestWrapper extends DiscFilterRequ
@Override
public List<X509Certificate> getClientCertificateChain() {
- return Collections.emptyList();
+ return clientCertificateChain;
}
@Override
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 51437be2832..60a05e4f938 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
@@ -8,18 +8,16 @@ import com.yahoo.config.provision.TenantName;
import com.yahoo.vespa.athenz.api.AthenzDomain;
import com.yahoo.vespa.athenz.utils.AthenzIdentities;
import com.yahoo.vespa.hosted.controller.Application;
-import com.yahoo.vespa.hosted.controller.ArtifactRepositoryMock;
+import com.yahoo.vespa.hosted.controller.integration.ArtifactRepositoryMock;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.TestIdentities;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeployOptions;
-import com.yahoo.vespa.hosted.controller.api.identifiers.GitBranch;
-import com.yahoo.vespa.hosted.controller.api.identifiers.GitCommit;
-import com.yahoo.vespa.hosted.controller.api.identifiers.GitRepository;
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.integration.athenz.ApplicationAction;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.HostedAthenzIdentities;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockBuildService;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
@@ -38,7 +36,7 @@ import java.io.IOException;
import java.time.Duration;
import java.util.Optional;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.component;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.component;
import static org.junit.Assert.assertFalse;
/**
@@ -85,14 +83,14 @@ public class ContainerControllerTester {
return controller().applications().createApplication(app, Optional.of(TestIdentities.userNToken));
}
- public Application deploy(Application application, ApplicationPackage applicationPackage, ZoneId zone, long projectId) {
+ public Application deploy(Application application, ApplicationPackage applicationPackage, ZoneId zone) {
controller().applications().deploy(application.id(), zone, Optional.of(applicationPackage),
new DeployOptions(false, Optional.empty(), false, false));
return application;
}
/** Notify the controller about a job completing */
- public BuildJob jobCompletion(DeploymentJobs.JobType job) {
+ public BuildJob jobCompletion(JobType job) {
return new BuildJob(this::notifyJobCompletion, artifactRepository()).type(job);
}
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 c72edf7e403..cc275b0636f 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,18 +7,21 @@ import com.yahoo.application.container.handler.Response;
import com.yahoo.component.ComponentSpecification;
import com.yahoo.component.Version;
import com.yahoo.container.http.filter.FilterChainRepository;
-import com.yahoo.io.IOUtils;
import com.yahoo.jdisc.http.filter.SecurityRequestFilter;
import com.yahoo.jdisc.http.filter.SecurityRequestFilterChain;
-import com.yahoo.vespa.hosted.controller.ConfigServerMock;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.application.SystemApplication;
+import com.yahoo.vespa.hosted.controller.integration.ConfigServerMock;
import com.yahoo.vespa.hosted.controller.versions.VersionStatus;
import org.junit.ComparisonFailure;
import java.io.File;
import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.charset.CharacterCodingException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.regex.Pattern;
@@ -57,31 +60,39 @@ public class ContainerTester {
public void upgradeSystem(Version version) {
controller().curator().writeControllerVersion(controller().hostname(), version);
for (ZoneId zone : controller().zoneRegistry().zones().all().ids()) {
- configServer().setVersion(version, zone, SystemApplication.all());
+ for (SystemApplication application : SystemApplication.all()) {
+ configServer().setVersion(application.id(), zone, version);
+ configServer().convergeServices(application.id(), zone);
+ }
}
computeVersionStatus();
}
- public void assertResponse(Supplier<Request> request, File responseFile) throws IOException {
+ public void assertResponse(Supplier<Request> request, File responseFile) {
assertResponse(request.get(), responseFile);
}
- public void assertResponse(Request request, File responseFile) throws IOException {
+ public void assertResponse(Request request, File responseFile) {
assertResponse(request, responseFile, 200);
}
- public void assertResponse(Supplier<Request> request, File responseFile, int expectedStatusCode) throws IOException {
+ public void assertResponse(Supplier<Request> request, File responseFile, int expectedStatusCode) {
assertResponse(request.get(), responseFile, expectedStatusCode);
}
- public void assertResponse(Request request, File responseFile, int expectedStatusCode) throws IOException {
- String expectedResponse = IOUtils.readFile(new File(responseFilePath + responseFile.toString()));
+ public void assertResponse(Request request, File responseFile, int expectedStatusCode) {
+ String expectedResponse = readTestFile(responseFile.toString());
expectedResponse = include(expectedResponse);
expectedResponse = expectedResponse.replaceAll("(\"[^\"]*\")|\\s*", "$1"); // Remove whitespace
FilterResult filterResult = invokeSecurityFilters(request);
request = filterResult.request;
Response response = filterResult.response != null ? filterResult.response : container.handleRequest(request);
- String responseString = response.getBodyAsString();
+ String responseString;
+ try {
+ responseString = response.getBodyAsString();
+ } catch (CharacterCodingException e) {
+ throw new UncheckedIOException(e);
+ }
if (expectedResponse.contains("(ignore)")) {
// Convert expected response to a literal pattern and replace any ignored field with a pattern that matches
// until the first stop character
@@ -138,7 +149,7 @@ public class ContainerTester {
}
/** Replaces @include(localFile) with the content of the file */
- private String include(String response) throws IOException {
+ private String include(String response) {
// Please don't look at this code
int includeIndex = response.indexOf("@include(");
if (includeIndex < 0) return response;
@@ -146,14 +157,22 @@ public class ContainerTester {
String rest = response.substring(includeIndex + "@include(".length());
int filenameEnd = rest.indexOf(")");
String includeFileName = rest.substring(0, filenameEnd);
- String includedContent = IOUtils.readFile(new File(responseFilePath + includeFileName));
+ String includedContent = readTestFile(includeFileName);
includedContent = include(includedContent);
String postFix = rest.substring(filenameEnd + 1);
postFix = include(postFix);
return prefix + includedContent + postFix;
}
- static class FilterResult {
+ private String readTestFile(String name) {
+ try {
+ return new String(Files.readAllBytes(Paths.get(responseFilePath, name)));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private static class FilterResult {
final Request request;
final Response response;
@@ -162,5 +181,6 @@ public class ContainerTester {
this.response = response;
}
}
+
}
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 833e2d8b552..de645cff96c 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
@@ -15,8 +15,8 @@ import org.junit.Before;
import java.io.UncheckedIOException;
import java.nio.charset.CharacterCodingException;
-import static com.yahoo.vespa.hosted.controller.AthenzFilterMock.ATHENZ_NTOKEN_HEADER_NAME;
-import static com.yahoo.vespa.hosted.controller.AthenzFilterMock.IDENTITY_HEADER_NAME;
+import static com.yahoo.vespa.hosted.controller.integration.AthenzFilterMock.ATHENZ_NTOKEN_HEADER_NAME;
+import static com.yahoo.vespa.hosted.controller.integration.AthenzFilterMock.IDENTITY_HEADER_NAME;
import static org.junit.Assert.assertEquals;
/**
@@ -68,18 +68,20 @@ public class ControllerContainerTest {
" <component id='com.yahoo.vespa.hosted.controller.api.integration.routing.MemoryGlobalRoutingService'/>\n" +
" <component id='com.yahoo.vespa.hosted.controller.api.integration.stubs.LoggingDeploymentIssues'/>\n" +
" <component id='com.yahoo.vespa.hosted.controller.api.integration.stubs.DummyOwnershipIssues'/>\n" +
+ " <component id='com.yahoo.vespa.hosted.controller.api.integration.stubs.MockLogStore'/>\n" +
" <component id='com.yahoo.vespa.hosted.controller.api.integration.organization.MockOrganization'/>\n" +
- " <component id='com.yahoo.vespa.hosted.controller.ConfigServerMock'/>\n" +
- " <component id='com.yahoo.vespa.hosted.controller.NodeRepositoryClientMock'/>\n" +
- " <component id='com.yahoo.vespa.hosted.controller.ZoneRegistryMock'/>\n" +
+ " <component id='com.yahoo.vespa.hosted.controller.integration.ConfigServerMock'/>\n" +
+ " <component id='com.yahoo.vespa.hosted.controller.integration.NodeRepositoryClientMock'/>\n" +
+ " <component id='com.yahoo.vespa.hosted.controller.integration.ZoneRegistryMock'/>\n" +
" <component id='com.yahoo.vespa.hosted.controller.Controller'/>\n" +
" <component id='com.yahoo.vespa.hosted.controller.api.integration.stubs.MockBuildService'/>\n" +
- " <component id='com.yahoo.vespa.hosted.controller.ConfigServerProxyMock'/>\n" +
- " <component id='com.yahoo.vespa.hosted.controller.integration.MockMetricsService'/>\n" +
+ " <component id='com.yahoo.vespa.hosted.controller.integration.ConfigServerProxyMock'/>\n" +
+ " <component id='com.yahoo.vespa.hosted.controller.integration.MetricsServiceMock'/>\n" +
" <component id='com.yahoo.vespa.hosted.controller.maintenance.ControllerMaintenance'/>\n" +
" <component id='com.yahoo.vespa.hosted.controller.maintenance.JobControl'/>\n" +
- " <component id='com.yahoo.vespa.hosted.controller.routing.MockRoutingGenerator'/>\n" +
- " <component id='com.yahoo.vespa.hosted.controller.ArtifactRepositoryMock'/>\n" +
+ " <component id='com.yahoo.vespa.hosted.controller.integration.RoutingGeneratorMock'/>\n" +
+ " <component id='com.yahoo.vespa.hosted.controller.integration.ArtifactRepositoryMock'/>\n" +
+ " <component id='com.yahoo.vespa.hosted.controller.api.integration.stubs.MockTesters'/>\n" +
" <handler id='com.yahoo.vespa.hosted.controller.restapi.application.ApplicationApiHandler'>\n" +
" <binding>http://*/application/v4/*</binding>\n" +
" </handler>\n" +
@@ -104,7 +106,7 @@ public class ControllerContainerTest {
" <server id='default' port='8080' />\n" +
" <filtering>\n" +
" <request-chain id='default'>\n" +
- " <filter id='com.yahoo.vespa.hosted.controller.AthenzFilterMock'/>\n" +
+ " <filter id='com.yahoo.vespa.hosted.controller.integration.AthenzFilterMock'/>\n" +
" <filter id='com.yahoo.vespa.hosted.controller.restapi.filter.ControllerAuthorizationFilter'/>\n" +
" <binding>http://*/*</binding>\n" +
" </request-chain>\n" +
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 545ee529635..45513c2294f 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
@@ -18,7 +18,6 @@ import com.yahoo.vespa.athenz.api.AthenzUser;
import com.yahoo.vespa.athenz.api.NToken;
import com.yahoo.vespa.config.SlimeUtils;
import com.yahoo.vespa.hosted.controller.Application;
-import com.yahoo.vespa.hosted.controller.ConfigServerMock;
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;
@@ -27,11 +26,13 @@ import com.yahoo.vespa.hosted.controller.api.integration.MetricsService.Applicat
import com.yahoo.vespa.hosted.controller.api.integration.athenz.ApplicationAction;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.HostedAthenzIdentities;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId;
import com.yahoo.vespa.hosted.controller.api.integration.organization.MockOrganization;
import com.yahoo.vespa.hosted.controller.api.integration.organization.User;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
+import com.yahoo.vespa.hosted.controller.application.Change;
import com.yahoo.vespa.hosted.controller.application.ClusterInfo;
import com.yahoo.vespa.hosted.controller.application.ClusterUtilization;
import com.yahoo.vespa.hosted.controller.application.Deployment;
@@ -42,6 +43,7 @@ import com.yahoo.vespa.hosted.controller.athenz.mock.AthenzClientFactoryMock;
import com.yahoo.vespa.hosted.controller.athenz.mock.AthenzDbMock;
import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
import com.yahoo.vespa.hosted.controller.deployment.BuildJob;
+import com.yahoo.vespa.hosted.controller.integration.ConfigServerMock;
import com.yahoo.vespa.hosted.controller.restapi.ContainerControllerTester;
import com.yahoo.vespa.hosted.controller.restapi.ContainerTester;
import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerTest;
@@ -96,6 +98,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
.build();
private static final AthenzDomain ATHENZ_TENANT_DOMAIN = new AthenzDomain("domain1");
+ private static final AthenzDomain ATHENZ_TENANT_DOMAIN_2 = new AthenzDomain("domain2");
private static final ScrewdriverId SCREWDRIVER_ID = new ScrewdriverId("12345");
private static final UserId USER_ID = new UserId("myuser");
private static final UserId HOSTED_VESPA_OPERATOR = new UserId("johnoperator");
@@ -144,7 +147,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
// Add another Athens domain, so we can try to create more tenants
- createAthenzDomainWithAdmin(new AthenzDomain("domain2"), USER_ID); // New domain to test tenant w/property ID
+ createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN_2, USER_ID); // New domain to test tenant w/property ID
// Add property info for that property id, as well, in the mock organization.
addPropertyData((MockOrganization) controllerTester.controller().organization(), "1234");
// POST (add) a tenant with property ID
@@ -193,8 +196,8 @@ public class ApplicationApiTest extends ControllerContainerTest {
ATHENZ_TENANT_DOMAIN,
new com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId(id.application().value())); // (Necessary but not provided in this API)
- // Trigger deployment from completion of component job
- controllerTester.jobCompletion(DeploymentJobs.JobType.component)
+ // Pipeline notifies about completed component job
+ controllerTester.jobCompletion(JobType.component)
.application(id)
.projectId(screwdriverProjectId)
.uploadArtifact(applicationPackage)
@@ -202,42 +205,79 @@ public class ApplicationApiTest extends ControllerContainerTest {
// ... systemtest
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/test/region/us-east-1/instance/default/", POST)
- .data(createApplicationDeployData(applicationPackage, false))
+ .data(createApplicationDeployData(Optional.empty(), false))
.screwdriverIdentity(SCREWDRIVER_ID),
new File("deploy-result.json"));
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/test/region/us-east-1/instance/default", DELETE)
.screwdriverIdentity(SCREWDRIVER_ID),
"Deactivated tenant/tenant1/application/application1/environment/test/region/us-east-1/instance/default");
- // Called through the separate screwdriver/v1 API
- controllerTester.jobCompletion(DeploymentJobs.JobType.systemTest)
+
+ controllerTester.jobCompletion(JobType.systemTest)
.application(id)
.projectId(screwdriverProjectId)
.submit();
// ... staging
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/staging/region/us-east-3/instance/default/", POST)
- .data(createApplicationDeployData(applicationPackage, false))
+ .data(createApplicationDeployData(Optional.empty(), false))
.screwdriverIdentity(SCREWDRIVER_ID),
new File("deploy-result.json"));
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/staging/region/us-east-3/instance/default", DELETE)
.screwdriverIdentity(SCREWDRIVER_ID),
"Deactivated tenant/tenant1/application/application1/environment/staging/region/us-east-3/instance/default");
- controllerTester.jobCompletion(DeploymentJobs.JobType.stagingTest)
+ controllerTester.jobCompletion(JobType.stagingTest)
.application(id)
.projectId(screwdriverProjectId)
.submit();
// ... prod zone
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/corp-us-east-1/instance/default/", POST)
- .data(createApplicationDeployData(applicationPackage, false))
+ .data(createApplicationDeployData(Optional.empty(), false))
.screwdriverIdentity(SCREWDRIVER_ID),
new File("deploy-result.json"));
- controllerTester.jobCompletion(DeploymentJobs.JobType.productionCorpUsEast1)
+ controllerTester.jobCompletion(JobType.productionCorpUsEast1)
.application(id)
.projectId(screwdriverProjectId)
.unsuccessful()
.submit();
+ // POST (create) another application
+ ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
+ .environment(Environment.prod)
+ .region("us-west-1")
+ .build();
+
+ tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2", POST)
+ .userIdentity(USER_ID)
+ .nToken(N_TOKEN),
+ new File("application-reference-2.json"));
+
+ ApplicationId app2 = ApplicationId.from("tenant2", "application2", "default");
+ long screwdriverProjectId2 = 456;
+ addScrewdriverUserToDeployRole(SCREWDRIVER_ID,
+ ATHENZ_TENANT_DOMAIN_2,
+ new com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId(app2.application().value()));
+
+ // Trigger upgrade and then application change
+ controllerTester.controller().applications().deploymentTrigger().triggerChange(app2, Change.of(Version.fromString("7.0")));
+
+ controllerTester.jobCompletion(JobType.component)
+ .application(app2)
+ .projectId(screwdriverProjectId2)
+ .uploadArtifact(applicationPackage)
+ .submit();
+
+ // GET application having both change and outstanding change
+ tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2", GET)
+ .userIdentity(USER_ID),
+ new File("application2.json"));
+
+ // DELETE application
+ tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2", DELETE)
+ .userIdentity(USER_ID)
+ .nToken(N_TOKEN),
+ "");
+
// GET tenant screwdriver projects
tester.assertResponse(request("/application/v4/tenant-pipeline/", GET)
.userIdentity(USER_ID),
@@ -382,8 +422,6 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request("/application/v4/tenant/tenant1", DELETE).userIdentity(USER_ID)
.nToken(N_TOKEN),
new File("tenant-without-applications.json"));
-
- controllerTester.controller().deconstruct();
}
private void addIssues(ContainerControllerTester tester, ApplicationId id) {
@@ -394,7 +432,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
}
@Test
- public void testDeployDirectly() throws Exception {
+ public void testDeployDirectly() {
// Setup
ContainerControllerTester controllerTester = new ContainerControllerTester(container, responseFiles);
ContainerTester tester = controllerTester.containerTester();
@@ -426,11 +464,10 @@ public class ApplicationApiTest extends ControllerContainerTest {
new File("deploy-result.json"));
}
-
// Tests deployment to config server when using just on API call
// For now this depends on a switch in ApplicationController that does this for by- tenants in CD only
@Test
- public void testDeployDirectlyUsingOneCallForDeploy() throws Exception {
+ public void testDeployDirectlyUsingOneCallForDeploy() {
// Setup
ContainerControllerTester controllerTester = new ContainerControllerTester(container, responseFiles);
ContainerTester tester = controllerTester.containerTester();
@@ -492,7 +529,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
.data(deployData)
.screwdriverIdentity(SCREWDRIVER_ID),
new File("deploy-result.json"));
- controllerTester.jobCompletion(DeploymentJobs.JobType.productionUsEast3)
+ controllerTester.jobCompletion(JobType.productionUsEast3)
.application(id)
.projectId(projectId)
.submit();
@@ -511,7 +548,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
.data(deployData)
.screwdriverIdentity(SCREWDRIVER_ID),
new File("deploy-result.json"));
- controllerTester.jobCompletion(DeploymentJobs.JobType.productionUsWest1)
+ controllerTester.jobCompletion(JobType.productionUsWest1)
.application(id)
.projectId(projectId)
.submit();
@@ -521,7 +558,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
.data(deployData)
.screwdriverIdentity(SCREWDRIVER_ID),
new File("deploy-result.json"));
- controllerTester.jobCompletion(DeploymentJobs.JobType.productionUsEast3)
+ controllerTester.jobCompletion(JobType.productionUsEast3)
.application(id)
.projectId(projectId)
.submit();
@@ -614,7 +651,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
"{\"error-code\":\"BAD_REQUEST\",\"message\":\"Could not create 'tenant1.application1': Application already exists\"}",
400);
- ConfigServerMock configServer = (ConfigServerMock)container.components().getComponent("com.yahoo.vespa.hosted.controller.ConfigServerMock");
+ ConfigServerMock configServer = (ConfigServerMock) container.components().getComponent(ConfigServerMock.class.getName());
configServer.throwOnNextPrepare(new ConfigServerException(new URI("server-url"), "Failed to prepare application", ConfigServerException.ErrorCode.INVALID_APPLICATION_PACKAGE, null));
// POST (deploy) an application with an invalid application package
@@ -812,7 +849,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
}
@Test
- public void deployment_succeeds_when_correct_domain_is_used() throws IOException {
+ public void deployment_succeeds_when_correct_domain_is_used() {
ContainerControllerTester controllerTester = new ContainerControllerTester(container, responseFiles);
ContainerTester tester = controllerTester.containerTester();
ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
@@ -830,7 +867,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
controllerTester.authorize(ATHENZ_TENANT_DOMAIN, screwdriverId, ApplicationAction.deploy, application);
// Allow systemtest to succeed by notifying completion of system test
- controllerTester.jobCompletion(DeploymentJobs.JobType.component)
+ controllerTester.jobCompletion(JobType.component)
.application(application.id())
.projectId(screwdriverProjectId)
.uploadArtifact(applicationPackage)
@@ -843,7 +880,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
}
@Test
- public void testJobStatusReporting() throws Exception {
+ public void testJobStatusReporting() {
ContainerControllerTester tester = new ContainerControllerTester(container, responseFiles);
addUserToHostedOperatorRole(HostedAthenzIdentities.from(HOSTED_VESPA_OPERATOR));
tester.containerTester().computeVersionStatus();
@@ -859,27 +896,27 @@ public class ApplicationApiTest extends ControllerContainerTest {
BuildJob job = new BuildJob(report -> notifyCompletion(report, tester), tester.artifactRepository())
.application(app)
.projectId(projectId);
- job.type(DeploymentJobs.JobType.component).uploadArtifact(applicationPackage).submit();
- tester.deploy(app, applicationPackage, TEST_ZONE, projectId);
- job.type(DeploymentJobs.JobType.systemTest).submit();
+ job.type(JobType.component).uploadArtifact(applicationPackage).submit();
+ tester.deploy(app, applicationPackage, TEST_ZONE);
+ job.type(JobType.systemTest).submit();
// Notifying about unknown job fails
Request request = request("/application/v4/tenant/tenant1/application/application1/jobreport", POST)
- .data(asJson(job.type(DeploymentJobs.JobType.productionUsEast3).report()))
+ .data(asJson(job.type(JobType.productionUsEast3).report()))
.userIdentity(HOSTED_VESPA_OPERATOR)
.get();
tester.containerTester().assertResponse(request, new File("jobreport-unexpected-completion.json"), 400);
// ... and assert it was recorded
JobStatus recordedStatus =
- tester.controller().applications().get(app.id()).get().deploymentJobs().jobStatus().get(DeploymentJobs.JobType.component);
+ tester.controller().applications().get(app.id()).get().deploymentJobs().jobStatus().get(JobType.component);
assertNotNull("Status was recorded", recordedStatus);
assertTrue(recordedStatus.isSuccess());
assertEquals(vespaVersion, recordedStatus.lastCompleted().get().platform());
recordedStatus =
- tester.controller().applications().get(app.id()).get().deploymentJobs().jobStatus().get(DeploymentJobs.JobType.productionApNortheast2);
+ tester.controller().applications().get(app.id()).get().deploymentJobs().jobStatus().get(JobType.productionApNortheast2);
assertNull("Status of never-triggered jobs is empty", recordedStatus);
Response response;
@@ -906,19 +943,19 @@ public class ApplicationApiTest extends ControllerContainerTest {
BuildJob job = new BuildJob(report -> notifyCompletion(report, tester), tester.artifactRepository())
.application(app)
.projectId(projectId);
- job.type(DeploymentJobs.JobType.component).uploadArtifact(applicationPackage).submit();
+ job.type(JobType.component).uploadArtifact(applicationPackage).submit();
- tester.deploy(app, applicationPackage, TEST_ZONE, projectId);
- job.type(DeploymentJobs.JobType.systemTest).submit();
- tester.deploy(app, applicationPackage, STAGING_ZONE, projectId);
- job.type(DeploymentJobs.JobType.stagingTest).error(DeploymentJobs.JobError.outOfCapacity).submit();
+ tester.deploy(app, applicationPackage, TEST_ZONE);
+ job.type(JobType.systemTest).submit();
+ tester.deploy(app, applicationPackage, STAGING_ZONE);
+ job.type(JobType.stagingTest).error(DeploymentJobs.JobError.outOfCapacity).submit();
// Appropriate error is recorded
JobStatus jobStatus = tester.controller().applications().get(app.id())
.get()
.deploymentJobs()
.jobStatus()
- .get(DeploymentJobs.JobType.stagingTest);
+ .get(JobType.stagingTest);
assertFalse(jobStatus.isSuccess());
assertEquals(DeploymentJobs.JobError.outOfCapacity, jobStatus.jobError().get());
}
@@ -1062,7 +1099,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
// Trigger application change
controllerTester.artifactRepository().put(application, applicationPackage,"1.0." + buildNumber
+ "-commit1");
- controllerTester.jobCompletion(DeploymentJobs.JobType.component)
+ controllerTester.jobCompletion(JobType.component)
.application(application)
.projectId(projectId)
.buildNumber(buildNumber)
@@ -1078,7 +1115,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request(testPath, DELETE)
.screwdriverIdentity(SCREWDRIVER_ID),
"Deactivated " + testPath.replaceFirst("/application/v4/", ""));
- controllerTester.jobCompletion(DeploymentJobs.JobType.systemTest)
+ controllerTester.jobCompletion(JobType.systemTest)
.application(application)
.projectId(projectId)
.submit();
@@ -1093,7 +1130,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request(stagingPath, DELETE)
.screwdriverIdentity(SCREWDRIVER_ID),
"Deactivated " + stagingPath.replaceFirst("/application/v4/", ""));
- controllerTester.jobCompletion(DeploymentJobs.JobType.stagingTest)
+ controllerTester.jobCompletion(JobType.stagingTest)
.application(application)
.projectId(projectId)
.submit();
@@ -1103,8 +1140,6 @@ public class ApplicationApiTest extends ControllerContainerTest {
* Cluster info, utilization and application and deployment metrics are maintained async by maintainers.
*
* This sets these values as if the maintainers has been ran.
- *
- * @param controllerTester
*/
private void setDeploymentMaintainedInfo(ContainerControllerTester controllerTester) {
for (Application application : controllerTester.controller().applications().asList()) {
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
new file mode 100644
index 00000000000..bc671d2375e
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java
@@ -0,0 +1,158 @@
+package com.yahoo.vespa.hosted.controller.restapi.application;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.TenantName;
+import com.yahoo.container.jdisc.HttpResponse;
+import com.yahoo.vespa.hosted.controller.ControllerTester;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
+import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockLogStore;
+import com.yahoo.vespa.hosted.controller.application.SourceRevision;
+import com.yahoo.vespa.hosted.controller.deployment.JobController;
+import com.yahoo.vespa.hosted.controller.deployment.RunStatus;
+import com.yahoo.vespa.hosted.controller.deployment.Step;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static org.junit.Assert.fail;
+
+public class JobControllerApiHandlerHelperTest {
+
+ private final ApplicationId appId = ApplicationId.from("vespa", "music", "default");
+ private final Instant start = Instant.parse("2018-06-27T10:12:35Z");
+
+ @Test
+ public void jobTypeResponse() {
+ Map<JobType, RunStatus> jobMap = new HashMap<>();
+ List<JobType> jobList = new ArrayList<>();
+ jobMap.put(JobType.systemTest, createStatus(JobType.systemTest, 1, 30, Step.last(), Step.Status.succeeded));
+ jobList.add(JobType.systemTest);
+ jobMap.put(JobType.productionApNortheast1, createStatus(JobType.productionApNortheast1, 1, 60, Step.last(), Step.Status.succeeded));
+ jobList.add(JobType.productionApNortheast1);
+ jobMap.put(JobType.productionUsWest1, createStatus(JobType.productionUsWest1, 1, 60, Step.startTests, Step.Status.failed));
+ jobList.add(JobType.productionUsWest1);
+
+ URI jobUrl = URI.create("https://domain.tld/application/v4/tenant/sometenant/application/someapp/instance/usuallydefault/job");
+
+ HttpResponse response = JobControllerApiHandlerHelper.jobTypeResponse(jobList, jobMap, jobUrl);
+ assertFile(response, "job/job-type-response.json");
+ }
+
+ @Test
+ public void runStatusResponse() {
+ Map<RunId, RunStatus> statusMap = new HashMap<>();
+ RunStatus status;
+
+ status = createStatus(JobType.systemTest, 3, 30, Step.last(), Step.Status.succeeded);
+ statusMap.put(status.id(), status);
+
+ status = createStatus(JobType.systemTest, 2, 56, Step.installReal, Step.Status.failed);
+ statusMap.put(status.id(), status);
+
+ status = createStatus(JobType.systemTest, 1, 44, Step.last(), Step.Status.succeeded);
+ statusMap.put(status.id(), status);
+
+ URI jobTypeUrl = URI.create("https://domain.tld/application/v4/tenant/sometenant/application/someapp/instance/usuallydefault/job/systemtest");
+
+ HttpResponse response = JobControllerApiHandlerHelper.runStatusResponse(statusMap, jobTypeUrl);
+ assertFile(response, "job/run-status-response.json");
+ }
+
+ @Test
+ public void runDetailsResponse() {
+ ControllerTester tester = new ControllerTester();
+ MockLogStore logStore = new MockLogStore();
+ JobController jobController = new JobController(tester.controller(), logStore);
+ RunId runId = new RunId(appId, JobType.systemTest, 42);
+ tester.curator().writeHistoricRuns(
+ runId.application(),
+ runId.type(),
+ Collections.singleton(createStatus(JobType.systemTest, 42, 44, Step.last(), Step.Status.succeeded)));
+
+ logStore.append(runId, Step.deployTester.name(), "INFO\t1234567890\tSUCCESS".getBytes());
+ logStore.append(runId, Step.installTester.name(), "INFO\t1234598760\tSUCCESS".getBytes());
+ logStore.append(runId, Step.deactivateTester.name(), "INFO\t1234678901\tERROR: Something went wrong".getBytes());
+
+ HttpResponse response = JobControllerApiHandlerHelper.runDetailsResponse(jobController, runId);
+ assertFile(response, "job/run-details-response.json");
+ }
+
+ @Test
+ public void submitResponse() {
+ ControllerTester tester = new ControllerTester();
+ tester.createTenant("tenant", "domain", 1L);
+ tester.createApplication(TenantName.from("tenant"), "application", "default", 1L);
+
+ JobController jobController = new JobController(tester.controller(), new MockLogStore());
+ jobController.register(ApplicationId.from("tenant", "application", "default"));
+
+ HttpResponse response = JobControllerApiHandlerHelper.submitResponse(
+ jobController, "tenant", "application", new SourceRevision("repository", "branch", "commit"), new byte[0], new byte[0]);
+ compare(response, "{\"version\":\"1.0.1-commit\"}");
+ }
+
+
+ private RunStatus createStatus(JobType type, long runid, long duration, Step lastStep, Step.Status lastStepStatus) {
+ RunId runId = new RunId(appId, type, runid);
+
+ Map<Step, Step.Status> stepStatusMap = new HashMap<>();
+ Arrays.stream(Step.values()).sorted(Comparator.naturalOrder()).forEach(step -> {
+ if (step.ordinal() < lastStep.ordinal()) {
+ stepStatusMap.put(step, Step.Status.succeeded);
+ } else if (step.equals(lastStep)) {
+ stepStatusMap.put(step, lastStepStatus);
+ } else {
+ stepStatusMap.put(step, Step.Status.unfinished);
+ }
+ });
+
+ Optional<Instant> end = Optional.empty();
+ if (lastStepStatus == Step.Status.failed || (lastStepStatus != Step.Status.unfinished && lastStep == Step.last())) {
+ end = Optional.of(start.plusSeconds(duration));
+ }
+
+ return new RunStatus(runId, stepStatusMap, start, end, false);
+ }
+
+ private void compare(HttpResponse response, String expected) {
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ response.render(baos);
+ String actual = new String(baos.toByteArray());
+
+ JSONObject actualJSON = new JSONObject(actual);
+ JSONObject expectedJSON = new JSONObject(expected);
+ Assert.assertEquals(expectedJSON.toString(), actualJSON.toString());
+ } catch (IOException | JSONException e) {
+ fail();
+ }
+ }
+
+ private void assertFile(HttpResponse response, String resourceName) {
+ try {
+ Path path = Paths.get(getClass().getClassLoader().getResource(resourceName).getPath());
+ String expected = new String(Files.readAllBytes(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/application-reference-2.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-reference-2.json
new file mode 100644
index 00000000000..a4026d6a812
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-reference-2.json
@@ -0,0 +1,5 @@
+{
+ "application": "application2",
+ "instance": "default",
+ "url": "http://localhost:8080/application/v4/tenant/tenant2/application/application2"
+}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application2.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application2.json
new file mode 100644
index 00000000000..fa51d645cfc
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application2.json
@@ -0,0 +1,78 @@
+{
+ "application": "application2",
+ "instance": "default",
+ "deploying": {
+ "version": "(ignore)"
+ },
+ "outstandingChange": {
+ "revision": {
+ "hash": "(ignore)",
+ "source": {
+ "gitRepository": "repository1",
+ "gitBranch": "master",
+ "gitCommit": "commit1"
+ }
+ }
+ },
+ "deploymentJobs": [
+ {
+ "type": "component",
+ "success": true,
+ "lastCompleted": {
+ "id": 42,
+ "version": "(ignore)",
+ "revision": {
+ "hash": "(ignore)",
+ "source": {
+ "gitRepository": "repository1",
+ "gitBranch": "master",
+ "gitCommit": "commit1"
+ }
+ },
+ "reason": "Application commit",
+ "at": "(ignore)"
+ },
+ "lastSuccess": {
+ "id": 42,
+ "version": "(ignore)",
+ "revision": {
+ "hash": "(ignore)",
+ "source": {
+ "gitRepository": "repository1",
+ "gitBranch": "master",
+ "gitCommit": "commit1"
+ }
+ },
+ "reason": "Application commit",
+ "at": "(ignore)"
+ }
+ },
+ {
+ "type": "system-test",
+ "success": false,
+ "lastTriggered": {
+ "id": -1,
+ "version": "7.0.0",
+ "revision": {
+ "hash": "1.0.42-commit1",
+ "source": {
+ "gitRepository": "repository1",
+ "gitBranch": "master",
+ "gitCommit": "commit1"
+ }
+ },
+ "reason": "Testing last changes outside prod",
+ "at": "(ignore)"
+ }
+ }
+ ],
+ "changeBlockers": [],
+ "compileVersion": "6.1.0",
+ "globalRotations": [],
+ "instances": [],
+ "metrics": {
+ "queryServiceQuality": 0.0,
+ "writeServiceQuality": 0.0
+ },
+ "activity": {}
+}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json
index 2ce9d69472a..b790cec3912 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json
@@ -25,12 +25,12 @@
"cost": {
"tco": 74,
"waste": 0,
- "utilization": 2.999999999999999,
+ "utilization": 2.9999999999999996,
"cluster": {
"cluster1": {
"count": 2,
"resource": "cpu",
- "utilization": 2.999999999999999,
+ "utilization": 2.9999999999999996,
"tco": 74,
"waste": 0,
"flavor": "flavor1",
@@ -40,7 +40,7 @@
"flavorDisk":50.0,
"type": "content",
"util": {
- "cpu": 2.999999999999999,
+ "cpu": 2.9999999999999996,
"mem": 0.4285714285714286,
"disk": 0.5714285714285715,
"diskBusy": 1.0
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-west-1.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-west-1.json
index ab86b891fae..f6eae19337a 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-west-1.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-west-1.json
@@ -24,12 +24,12 @@
"cost": {
"tco": 74,
"waste": 0,
- "utilization": 2.999999999999999,
+ "utilization": 2.9999999999999996,
"cluster": {
"cluster1": {
"count": 2,
"resource": "cpu",
- "utilization": 2.999999999999999,
+ "utilization": 2.9999999999999996,
"tco": 74,
"waste": 0,
"flavor": "flavor1",
@@ -39,7 +39,7 @@
"flavorDisk": 50.0,
"type": "content",
"util": {
- "cpu": 2.999999999999999,
+ "cpu": 2.9999999999999996,
"mem": 0.4285714285714286,
"disk": 0.5714285714285715,
"diskBusy": 1.0
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-corp-us-east-1.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-corp-us-east-1.json
index 902bda472cf..b35a53eb1a4 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-corp-us-east-1.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-corp-us-east-1.json
@@ -31,12 +31,12 @@
"cost": {
"tco": 74,
"waste": 0,
- "utilization": 2.999999999999999,
+ "utilization": 2.9999999999999996,
"cluster": {
"cluster1": {
"count": 2,
"resource": "cpu",
- "utilization": 2.999999999999999,
+ "utilization": 2.9999999999999996,
"tco": 74,
"waste": 0,
"flavor": "flavor1",
@@ -46,7 +46,7 @@
"flavorDisk": 50.0,
"type": "content",
"util": {
- "cpu": 2.999999999999999,
+ "cpu": 2.9999999999999996,
"mem": 0.4285714285714286,
"disk": 0.5714285714285715,
"diskBusy": 1.0
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json
index 0ad82df8db1..e3d060ee806 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json
@@ -22,6 +22,9 @@
"name": "DnsMaintainer"
},
{
+ "name": "JobRunner"
+ },
+ {
"name": "MetricsReporter"
},
{
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiTest.java
index fa93c0f1df1..1ed2af1f7b9 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiTest.java
@@ -8,9 +8,9 @@ import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.RegionName;
import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.Controller;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
-import com.yahoo.vespa.hosted.controller.application.DeploymentJobs;
import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
import com.yahoo.vespa.hosted.controller.restapi.ContainerControllerTester;
import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerTest;
@@ -102,28 +102,28 @@ public class DeploymentApiTest extends ControllerContainerTest {
private void deployCompletely(Application application, ApplicationPackage applicationPackage, long projectId,
boolean success) {
- tester.jobCompletion(DeploymentJobs.JobType.component)
+ tester.jobCompletion(JobType.component)
.application(application)
.projectId(projectId)
.uploadArtifact(applicationPackage)
.submit();
- tester.deploy(application, applicationPackage, ZoneId.from(Environment.test, RegionName.from("us-east-1")),
- projectId);
- tester.jobCompletion(DeploymentJobs.JobType.systemTest)
+ tester.deploy(application, applicationPackage, ZoneId.from(Environment.test, RegionName.from("us-east-1"))
+ );
+ tester.jobCompletion(JobType.systemTest)
.application(application)
.projectId(projectId)
.submit();
- tester.deploy(application, applicationPackage, ZoneId.from(Environment.staging, RegionName.from("us-east-3")),
- projectId);
- tester.jobCompletion(DeploymentJobs.JobType.stagingTest)
+ tester.deploy(application, applicationPackage, ZoneId.from(Environment.staging, RegionName.from("us-east-3"))
+ );
+ tester.jobCompletion(JobType.stagingTest)
.application(application)
.projectId(projectId)
.success(success)
.submit();
if (success) {
tester.deploy(application, applicationPackage, ZoneId.from(Environment.prod,
- RegionName.from("corp-us-east-1")), projectId);
- tester.jobCompletion(DeploymentJobs.JobType.productionCorpUsEast1)
+ RegionName.from("corp-us-east-1")));
+ tester.jobCompletion(JobType.productionCorpUsEast1)
.application(application)
.projectId(projectId)
.submit();
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java
index df3600eae9b..af11923b7f2 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.controller.restapi.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.yahoo.application.container.handler.Request;
import com.yahoo.config.provision.TenantName;
import com.yahoo.jdisc.http.HttpRequest.Method;
import com.yahoo.jdisc.http.filter.DiscFilterRequest;
@@ -17,6 +18,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.athenz.ApplicationActio
import com.yahoo.vespa.hosted.controller.api.integration.athenz.HostedAthenzIdentities;
import com.yahoo.vespa.hosted.controller.athenz.mock.AthenzClientFactoryMock;
import com.yahoo.vespa.hosted.controller.athenz.mock.AthenzDbMock;
+import com.yahoo.vespa.hosted.controller.restapi.ApplicationRequestToDiscFilterRequestWrapper;
import org.junit.Test;
import java.io.IOException;
@@ -35,13 +37,12 @@ import static java.util.Collections.singletonList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
/**
* @author bjorncs
*/
public class ControllerAuthorizationFilterTest {
+
private static final ObjectMapper mapper = new ObjectMapper();
private static final AthenzUser USER = user("john");
@@ -157,11 +158,9 @@ public class ControllerAuthorizationFilterTest {
}
private static DiscFilterRequest createRequest(Method method, String path, AthenzIdentity identity) {
- DiscFilterRequest request = mock(DiscFilterRequest.class);
- when(request.getMethod()).thenReturn(method.name());
- when(request.getRequestURI()).thenReturn(path);
- when(request.getUserPrincipal()).thenReturn(new AthenzPrincipal(identity));
- return request;
+ Request request = new Request(path, new byte[0], Request.Method.valueOf(method.name()),
+ new AthenzPrincipal(identity));
+ return new ApplicationRequestToDiscFilterRequestWrapper(request);
}
private static String getErrorMessage(MockResponseHandler responseHandler) {
@@ -185,4 +184,5 @@ public class ControllerAuthorizationFilterTest {
this.message = message;
}
}
+
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/screwdriver/ScrewdriverApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/screwdriver/ScrewdriverApiTest.java
index 99274fd9f44..6cd4464dce4 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/screwdriver/ScrewdriverApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/screwdriver/ScrewdriverApiTest.java
@@ -3,7 +3,7 @@ package com.yahoo.vespa.hosted.controller.restapi.screwdriver;
import com.yahoo.application.container.handler.Request;
import com.yahoo.vespa.hosted.controller.Application;
-import com.yahoo.vespa.hosted.controller.application.DeploymentJobs;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobReport;
import com.yahoo.vespa.hosted.controller.application.SourceRevision;
import com.yahoo.vespa.hosted.controller.restapi.ContainerControllerTester;
@@ -18,7 +18,7 @@ import java.util.OptionalLong;
/**
* @author bratseth
- * @author jvenstad
+ * @author jonmv
*/
public class ScrewdriverApiTest extends ControllerContainerTest {
@@ -62,7 +62,7 @@ public class ScrewdriverApiTest extends ControllerContainerTest {
new byte[0], Request.Method.POST, () -> "user"),
200, "{\"message\":\"Triggered component for tenant1.application1\"}");
tester.controller().applications().deploymentTrigger().notifyOfCompletion(new JobReport(app.id(),
- DeploymentJobs.JobType.component,
+ JobType.component,
1,
42,
Optional.of(new SourceRevision("repo", "branch", "commit")),
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageClientTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageClientTest.java
new file mode 100644
index 00000000000..378e4298552
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageClientTest.java
@@ -0,0 +1,44 @@
+// 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.restapi.statuspage;
+
+import org.junit.Test;
+
+import java.net.URI;
+import java.util.Optional;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author mpolden
+ */
+public class StatusPageClientTest {
+
+ @Test
+ public void test_url_building() {
+ {
+ URI apiUrl = URI.create("https://statuspage.io");
+ String secret = "testpage:testkey";
+ assertEquals("https://testpage.statuspage.io/api/v2/incidents.json?api_key=testkey",
+ StatusPageClient.create(apiUrl, secret).pageUrl("incidents", Optional.empty()).toString());
+ assertEquals("https://testpage.statuspage.io/api/v2/scheduled-maintenances.json?api_key=testkey&since=2015-01-01T00%3A00%2B00%3A00",
+ StatusPageClient.create(apiUrl, secret).pageUrl("scheduled-maintenances",
+ Optional.of("2015-01-01T00:00+00:00")).toString());
+ }
+
+ {
+ URI apiUrl = URI.create("http://foo.bar");
+ assertEquals("http://foo.bar/api/v2/incidents.json?api_key=testkey",
+ StatusPageClient.create(apiUrl, "testpage:testkey").pageUrl("incidents", Optional.empty()).toString());
+ }
+
+ {
+ try {
+ URI apiUrl = URI.create("http://foo.bar");
+ assertEquals("http://foo.bar/api/v2/incidents.json?api_key=testkey",
+ StatusPageClient.create(apiUrl, "").pageUrl("incidents", Optional.empty()).toString());
+ fail("Expected exception");
+ } catch (IllegalArgumentException ignored) {}
+ }
+ }
+
+}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyApiTest.java
new file mode 100644
index 00000000000..2d4c43f1d44
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyApiTest.java
@@ -0,0 +1,93 @@
+// 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.restapi.statuspage;
+
+import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import com.yahoo.application.Networking;
+import com.yahoo.application.container.JDisc;
+import com.yahoo.application.container.handler.Request;
+import com.yahoo.application.container.handler.Response;
+import com.yahoo.vespa.hosted.controller.integration.SecretStoreMock;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.okJson;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author mpolden
+ */
+public class StatusPageProxyApiTest {
+
+ private JDisc container;
+
+ @Before
+ public void startContainer() {
+ container = JDisc.fromServicesXml(servicesXml(), Networking.disable);
+ secretStore().setSecret("vespa_hosted.controller.statuspage_api_key", "page-id:secret");
+ }
+
+ @After
+ public void stopContainer() {
+ container.close();
+ secretStore().clear();
+ }
+
+ @Rule
+ public final WireMockRule wireMock = new WireMockRule(options().dynamicPort(), true);
+
+ @Test
+ public void test_proxy() throws Exception {
+ // Invalid requests
+ assertResponse("/statuspage/v1/", "{\"error-code\":\"NOT_FOUND\",\"message\":\"Nothing at path '/statuspage/v1'\"}", 404);
+ assertResponse("/statuspage/v1/invalid", "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Invalid resource: 'invalid'\"}", 400);
+ assertResponse(Request.Method.POST, "/statuspage/v1/invalid", "{\"error-code\":\"METHOD_NOT_ALLOWED\",\"message\":\"Method 'POST' is not supported\"}", 405);
+
+ // Mock responses from StatusPage
+ wireMock.stubFor(get(urlEqualTo("/api/v2/incidents.json?api_key=secret"))
+ .willReturn(okJson("{\"incidents\":[]}")));
+ wireMock.stubFor(get(urlEqualTo("/api/v2/scheduled-maintenances.json?api_key=secret"))
+ .willReturn(okJson("{\"scheduled_maintenances\":[]}")));
+
+ assertResponse("/statuspage/v1/incidents", "{\"incidents\":[]}", 200);
+ assertResponse("/statuspage/v1/scheduled-maintenances", "{\"scheduled_maintenances\":[]}", 200);
+ }
+
+ private void assertResponse(String path, String expectedResponse, int expectedStatusCode) throws IOException {
+ assertResponse(Request.Method.GET, path, expectedResponse, expectedStatusCode);
+ }
+
+ private void assertResponse(Request.Method method, String path, String expectedResponse, int expectedStatusCode) throws IOException {
+ Response response = container.handleRequest(new Request("http://localhost:8080" + path, new byte[0], method));
+ assertEquals(expectedResponse, response.getBodyAsString());
+ assertEquals("Status code", expectedStatusCode, response.getStatus());
+ assertEquals("application/json; charset=UTF-8", response.getHeaders().getFirst("Content-Type"));
+ }
+
+ private SecretStoreMock secretStore() {
+ return (SecretStoreMock) container.components().getComponent(SecretStoreMock.class.getName());
+ }
+
+ private String servicesXml() {
+ String statusPageApiUrl = "http://127.0.0.1:" + wireMock.port();
+ return "<jdisc version='1.0'>\n" +
+ " <config name='vespa.hosted.controller.statuspage.config.statuspage'>\n" +
+ " <apiUrl>" + statusPageApiUrl + "</apiUrl>\n" +
+ " </config>\n" +
+ " <component id='com.yahoo.vespa.hosted.controller.integration.SecretStoreMock'/>\n" +
+ " <handler id='com.yahoo.vespa.hosted.controller.restapi.statuspage.StatusPageProxyHandler'>\n" +
+ " <binding>http://*/statuspage/v1/*</binding>\n" +
+ " </handler>\n" +
+ " <http>\n" +
+ " <server id='default' port='8080'/>\n" +
+ " </http>\n" +
+ "</jdisc>";
+ }
+
+}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiTest.java
index 054e71465ad..4fbdbdcc813 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiTest.java
@@ -3,7 +3,7 @@ package com.yahoo.vespa.hosted.controller.restapi.zone.v1;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.RegionName;
-import com.yahoo.vespa.hosted.controller.ZoneRegistryMock;
+import com.yahoo.vespa.hosted.controller.integration.ZoneRegistryMock;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.restapi.ContainerControllerTester;
import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerTest;
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiTest.java
index 5e9de74fe1b..2123d32953d 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiTest.java
@@ -8,8 +8,8 @@ import com.yahoo.config.provision.RegionName;
import com.yahoo.text.Utf8;
import com.yahoo.vespa.athenz.api.AthenzIdentity;
import com.yahoo.vespa.athenz.api.AthenzUser;
-import com.yahoo.vespa.hosted.controller.ConfigServerProxyMock;
-import com.yahoo.vespa.hosted.controller.ZoneRegistryMock;
+import com.yahoo.vespa.hosted.controller.integration.ConfigServerProxyMock;
+import com.yahoo.vespa.hosted.controller.integration.ZoneRegistryMock;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.restapi.ContainerControllerTester;
import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerTest;
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/VersionStatusTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/VersionStatusTest.java
index 57676ffc3f5..291e6899a7a 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/VersionStatusTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/VersionStatusTest.java
@@ -24,10 +24,10 @@ import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.productionUsEast3;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.productionUsWest1;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.stagingTest;
-import static com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType.systemTest;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionUsEast3;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionUsWest1;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.stagingTest;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.systemTest;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -170,11 +170,6 @@ public class VersionStatusTest {
// Application without deployment
Application ignored0 = tester.createApplication("ignored0", "tenant1", 1000, 1000L);
- // Pull request builds
- tester.controllerTester().createApplication(TenantName.from("tenant1"),
- "ignored1",
- "43", 1000);
-
assertEquals("All applications running on this version: High",
Confidence.high, confidence(tester.controller(), version0));
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/restapi/impl/StatusPageResourceTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/restapi/impl/StatusPageResourceTest.java
deleted file mode 100644
index 429e1ea2b8d..00000000000
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/restapi/impl/StatusPageResourceTest.java
+++ /dev/null
@@ -1,65 +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.hosted.restapi.impl;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.yahoo.container.jdisc.secretstore.SecretStore;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mockito;
-
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.Invocation;
-import javax.ws.rs.client.WebTarget;
-import javax.ws.rs.core.UriBuilder;
-import java.io.IOException;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-/**
- * @author frodelu
- */
-public class StatusPageResourceTest {
-
- private StatusPageResource statusPage;
-
- @Before
- public void setup() throws IOException {
-
- Client mockClient = Mockito.mock(Client.class);
- WebTarget mockTarget = Mockito.mock(WebTarget.class);
- Invocation.Builder mockRequest = Mockito.mock(Invocation.Builder.class);
- SecretStore secretStore = Mockito.mock(SecretStore.class);
-
- Mockito.when(mockClient.target(Mockito.any(UriBuilder.class))).thenReturn(mockTarget);
- Mockito.when(mockTarget.request()).thenReturn(mockRequest);
- Mockito.when(mockRequest.get(JsonNode.class)).thenReturn(
- new ObjectMapper().readTree("{\"page\":{\"name\":\"Vespa\"}}"));
- Mockito.when(secretStore.getSecret(Mockito.any(String.class))).thenReturn("testpage:testkey");
-
- statusPage = new StatusPageResource(secretStore, mockClient);
- }
-
-
- @Test
- public void default_url() {
- UriBuilder uri = statusPage.statusPageURL("incidents", null);
- assertNotNull("URI not initialized", uri);
- assertEquals("https://testpage.statuspage.io/api/v2/incidents.json?api_key=testkey", uri.toTemplate());
- }
-
- @Test
- public void url_with_since_param() {
- UriBuilder uri = statusPage.statusPageURL("incidents", "2015-01-01T00:00+00:00");
- assertNotNull("URI not initialized", uri);
- assertEquals("https://testpage.statuspage.io/api/v2/incidents.json?api_key=testkey&since=2015-01-01T00%3A00%2B00%3A00", uri.toTemplate());
- }
-
- @Test
- public void valid_status_page() {
- JsonNode result = statusPage.statusPage("incidents", null);
- assertNotNull("No result from StatusPage.io", result);
- assertEquals("Vespa", result.get("page").get("name").asText());
- }
-}
diff --git a/controller-server/src/test/resources/job/job-type-response.json b/controller-server/src/test/resources/job/job-type-response.json
new file mode 100644
index 00000000000..2c51f0d9275
--- /dev/null
+++ b/controller-server/src/test/resources/job/job-type-response.json
@@ -0,0 +1,145 @@
+{
+ "jobs":[
+ {
+ "system-test":{
+ "last":{
+ "result":"success",
+ "start":"2018-06-27T10:12:35Z",
+ "end":"2018-06-27T10:13:05Z",
+ "id":1,
+ "steps":[
+ {
+ "deployInitialReal":"succeeded"
+ },
+ {
+ "installInitialReal":"succeeded"
+ },
+ {
+ "deployReal":"succeeded"
+ },
+ {
+ "installReal":"succeeded"
+ },
+ {
+ "deployTester":"succeeded"
+ },
+ {
+ "installTester":"succeeded"
+ },
+ {
+ "startTests":"succeeded"
+ },
+ {
+ "endTests":"succeeded"
+ },
+ {
+ "deactivateReal":"succeeded"
+ },
+ {
+ "deactivateTester":"succeeded"
+ },
+ {
+ "report":"succeeded"
+ }
+ ],
+ "logs":"https://domain.tld/application/v4/tenant/sometenant/application/someapp/instance/usuallydefault/job/system-test/run/1"
+ },
+ "url":"https://domain.tld/application/v4/tenant/sometenant/application/someapp/instance/usuallydefault/job/system-test"
+ }
+ },
+ {
+ "production-ap-northeast-1":{
+ "last":{
+ "result":"success",
+ "start":"2018-06-27T10:12:35Z",
+ "end":"2018-06-27T10:13:35Z",
+ "id":1,
+ "steps":[
+ {
+ "deployInitialReal":"succeeded"
+ },
+ {
+ "installInitialReal":"succeeded"
+ },
+ {
+ "deployReal":"succeeded"
+ },
+ {
+ "installReal":"succeeded"
+ },
+ {
+ "deployTester":"succeeded"
+ },
+ {
+ "installTester":"succeeded"
+ },
+ {
+ "startTests":"succeeded"
+ },
+ {
+ "endTests":"succeeded"
+ },
+ {
+ "deactivateReal":"succeeded"
+ },
+ {
+ "deactivateTester":"succeeded"
+ },
+ {
+ "report":"succeeded"
+ }
+ ],
+ "logs":"https://domain.tld/application/v4/tenant/sometenant/application/someapp/instance/usuallydefault/job/production-ap-northeast-1/run/1"
+ },
+ "url":"https://domain.tld/application/v4/tenant/sometenant/application/someapp/instance/usuallydefault/job/production-ap-northeast-1"
+ }
+ },
+ {
+ "production-us-west-1":{
+ "last":{
+ "result":"testError",
+ "start":"2018-06-27T10:12:35Z",
+ "end":"2018-06-27T10:13:35Z",
+ "id":1,
+ "steps":[
+ {
+ "deployInitialReal":"succeeded"
+ },
+ {
+ "installInitialReal":"succeeded"
+ },
+ {
+ "deployReal":"succeeded"
+ },
+ {
+ "installReal":"succeeded"
+ },
+ {
+ "deployTester":"succeeded"
+ },
+ {
+ "installTester":"succeeded"
+ },
+ {
+ "startTests":"failed"
+ },
+ {
+ "endTests":"unfinished"
+ },
+ {
+ "deactivateReal":"unfinished"
+ },
+ {
+ "deactivateTester":"unfinished"
+ },
+ {
+ "report":"unfinished"
+ }
+ ],
+ "logs":"https://domain.tld/application/v4/tenant/sometenant/application/someapp/instance/usuallydefault/job/production-us-west-1/run/1"
+ },
+ "url":"https://domain.tld/application/v4/tenant/sometenant/application/someapp/instance/usuallydefault/job/production-us-west-1"
+ }
+ }
+ ]
+}
diff --git a/controller-server/src/test/resources/job/run-details-response.json b/controller-server/src/test/resources/job/run-details-response.json
new file mode 100644
index 00000000000..3ba9bff049e
--- /dev/null
+++ b/controller-server/src/test/resources/job/run-details-response.json
@@ -0,0 +1,5 @@
+{
+ "deployTester":"INFO\t1234567890\tSUCCESS",
+ "installTester":"INFO\t1234598760\tSUCCESS",
+ "deactivateTester":"INFO\t1234678901\tERROR: Something went wrong"
+}
diff --git a/controller-server/src/test/resources/job/run-status-response.json b/controller-server/src/test/resources/job/run-status-response.json
new file mode 100644
index 00000000000..c3041440fd2
--- /dev/null
+++ b/controller-server/src/test/resources/job/run-status-response.json
@@ -0,0 +1,128 @@
+{
+ "1":{
+ "result":"success",
+ "start":"2018-06-27T10:12:35Z",
+ "end":"2018-06-27T10:13:19Z",
+ "id":1,
+ "steps":[
+ {
+ "deployInitialReal":"succeeded"
+ },
+ {
+ "installInitialReal":"succeeded"
+ },
+ {
+ "deployReal":"succeeded"
+ },
+ {
+ "installReal":"succeeded"
+ },
+ {
+ "deployTester":"succeeded"
+ },
+ {
+ "installTester":"succeeded"
+ },
+ {
+ "startTests":"succeeded"
+ },
+ {
+ "endTests":"succeeded"
+ },
+ {
+ "deactivateReal":"succeeded"
+ },
+ {
+ "deactivateTester":"succeeded"
+ },
+ {
+ "report":"succeeded"
+ }
+ ],
+ "logs":"https://domain.tld/application/v4/tenant/sometenant/application/someapp/instance/usuallydefault/job/systemtest/run/1"
+ },
+ "2":{
+ "result":"testError",
+ "start":"2018-06-27T10:12:35Z",
+ "end":"2018-06-27T10:13:31Z",
+ "id":2,
+ "steps":[
+ {
+ "deployInitialReal":"succeeded"
+ },
+ {
+ "installInitialReal":"succeeded"
+ },
+ {
+ "deployReal":"succeeded"
+ },
+ {
+ "installReal":"failed"
+ },
+ {
+ "deployTester":"unfinished"
+ },
+ {
+ "installTester":"unfinished"
+ },
+ {
+ "startTests":"unfinished"
+ },
+ {
+ "endTests":"unfinished"
+ },
+ {
+ "deactivateReal":"unfinished"
+ },
+ {
+ "deactivateTester":"unfinished"
+ },
+ {
+ "report":"unfinished"
+ }
+ ],
+ "logs":"https://domain.tld/application/v4/tenant/sometenant/application/someapp/instance/usuallydefault/job/systemtest/run/2"
+ },
+ "3":{
+ "result":"success",
+ "start":"2018-06-27T10:12:35Z",
+ "end":"2018-06-27T10:13:05Z",
+ "id":3,
+ "steps":[
+ {
+ "deployInitialReal":"succeeded"
+ },
+ {
+ "installInitialReal":"succeeded"
+ },
+ {
+ "deployReal":"succeeded"
+ },
+ {
+ "installReal":"succeeded"
+ },
+ {
+ "deployTester":"succeeded"
+ },
+ {
+ "installTester":"succeeded"
+ },
+ {
+ "startTests":"succeeded"
+ },
+ {
+ "endTests":"succeeded"
+ },
+ {
+ "deactivateReal":"succeeded"
+ },
+ {
+ "deactivateTester":"succeeded"
+ },
+ {
+ "report":"succeeded"
+ }
+ ],
+ "logs":"https://domain.tld/application/v4/tenant/sometenant/application/someapp/instance/usuallydefault/job/systemtest/run/3"
+ }
+}
diff --git a/controller-server/src/test/resources/test_runner_services.xml-cd b/controller-server/src/test/resources/test_runner_services.xml-cd
new file mode 100644
index 00000000000..c539478eec8
--- /dev/null
+++ b/controller-server/src/test/resources/test_runner_services.xml-cd
@@ -0,0 +1,36 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<services xmlns:deploy='vespa' version='1.0'>
+ <container version='1.0' id='default'>
+
+ <component id="com.yahoo.vespa.hosted.testrunner.TestRunner" bundle="vepsa-testrunner-components">
+ <config name="com.yahoo.vespa.hosted.testrunner.test-runner">
+ <artifactsPath>artifacts</artifactsPath>
+ </config>
+ </component>
+
+ <handler id="com.yahoo.vespa.hosted.testrunner.TestRunnerHandler" bundle="vespa-testrunner-components">
+ <binding>http://*/tester/v1/*</binding>
+ </handler>
+
+ <http>
+ <filtering>
+ <request-chain id="testrunner-api">
+ <filter id='authz-filter' class='com.yahoo.jdisc.http.filter.security.athenz.AthenzAuthorizationFilter' bundle="jdisc-security-filters">
+ <config name="jdisc.http.filter.security.athenz.athenz-authorization-filter">
+ <credentialsToVerify>TOKEN_ONLY</credentialsToVerify>
+ <roleTokenHeaderName>Yahoo-Role-Auth</roleTokenHeaderName>
+ </config>
+ <component id="com.yahoo.jdisc.http.filter.security.athenz.StaticRequestResourceMapper" bundle="jdisc-security-filters">
+ <config name="jdisc.http.filter.security.athenz.static-request-resource-mapper">
+ <resourceName>vespa.vespa.cd:tester-application</resourceName>
+ <action>deploy</action>
+ </config>
+ </component>
+ </filter>
+ </request-chain>
+ </filtering>
+ </http>
+
+ <nodes count="1" flavor="d-2-8-50" />
+ </container>
+</services>
diff --git a/defaults/src/apps/printdefault/printdefault.cpp b/defaults/src/apps/printdefault/printdefault.cpp
index 33c47dbcb20..be6b989fd70 100644
--- a/defaults/src/apps/printdefault/printdefault.cpp
+++ b/defaults/src/apps/printdefault/printdefault.cpp
@@ -4,6 +4,30 @@
#include <stdio.h>
#include <string.h>
+void dumpAllVars() {
+ printf("VESPA_HOME = '%s'\n", vespa::Defaults::vespaHome());
+ std::string v = vespa::Defaults::underVespaHome("foo");
+ printf("underVespaHome(foo) = '%s'\n", v.c_str());
+ printf("VESPA_USER = '%s'\n", vespa::Defaults::vespaUser());
+ printf("VESPA_HOSTNAME = '%s'\n", vespa::Defaults::vespaHostname());
+ printf("web service port = %d\n", vespa::Defaults::vespaWebServicePort());
+ printf("VESPA_PORT_BASE = %d\n", vespa::Defaults::vespaPortBase());
+ printf("config server rpc port = %d\n", vespa::Defaults::vespaConfigServerRpcPort());
+ size_t count = 0;
+ for (std::string vv : vespa::Defaults::vespaConfigServerHosts()) {
+ ++count;
+ printf("config server host %zu = '%s'\n", count, vv.c_str());
+ }
+ count = 0;
+ for (std::string vv : vespa::Defaults::vespaConfigServerRestUrls()) {
+ ++count;
+ printf("config server rest URL %zu = '%s'\n", count, vv.c_str());
+ }
+ v = vespa::Defaults::vespaConfigProxyRpcAddr();
+ printf("config proxy RPC addr = '%s'\n", v.c_str());
+ printf("vespa version = '%s'\n", V_TAG_COMPONENT);
+}
+
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "usage: %s <variable>\n", argv[0]);
@@ -15,6 +39,8 @@ int main(int argc, char **argv) {
}
if (strcmp(argv[1], "home") == 0) {
printf("%s\n", vespa::Defaults::vespaHome());
+ } else if (strcmp(argv[1], "everything") == 0) {
+ dumpAllVars();
} else if (strcmp(argv[1], "user") == 0) {
printf("%s\n", vespa::Defaults::vespaUser());
} else if (strcmp(argv[1], "hostname") == 0) {
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 af92a183443..0fce5d654fb 100644
--- a/defaults/src/main/java/com/yahoo/vespa/defaults/Defaults.java
+++ b/defaults/src/main/java/com/yahoo/vespa/defaults/Defaults.java
@@ -27,20 +27,25 @@ public class Defaults {
private final String vespaHost;
private final int vespaWebServicePort;
private final int vespaPortBase;
+ private final int vespaPortConfigServerRpc;
+ private final int vespaPortConfigServerHttp;
+ private final int vespaPortConfigProxyRpc;
private Defaults() {
- vespaHome = findVespaHome();
- vespaUser = findVespaUser();
- vespaHost = findVespaHostname();
- vespaWebServicePort = findVespaWebServicePort();
- vespaPortBase = 19000; // TODO
+ vespaHome = findVespaHome("/opt/vespa");
+ vespaUser = findVespaUser("vespa");
+ vespaHost = findVespaHostname("localhost");
+ vespaWebServicePort = findWebServicePort(8080);
+ vespaPortBase = findVespaPortBase(19000);
+ vespaPortConfigServerRpc = findConfigServerPort(vespaPortBase + 70);
+ vespaPortConfigServerHttp = vespaPortConfigServerRpc + 1;
+ vespaPortConfigProxyRpc = findConfigProxyPort(vespaPortBase + 90);
}
-
- static private String findVespaHome() {
+ static private String findVespaHome(String defHome) {
Optional<String> vespaHomeEnv = Optional.ofNullable(System.getenv("VESPA_HOME"));
if ( ! vespaHomeEnv.isPresent() || vespaHomeEnv.get().trim().isEmpty()) {
- log.info("VESPA_HOME not set, using /opt/vespa");
- return "/opt/vespa";
+ log.info("VESPA_HOME not set, using " + defHome);
+ return defHome;
}
String vespaHome = vespaHomeEnv.get().trim();
if (vespaHome.endsWith("/")) {
@@ -50,38 +55,50 @@ public class Defaults {
return vespaHome;
}
- static private String findVespaHostname() {
+ static private String findVespaHostname(String defHost) {
Optional<String> vespaHostEnv = Optional.ofNullable(System.getenv("VESPA_HOSTNAME"));
if (vespaHostEnv.isPresent() && ! vespaHostEnv.get().trim().isEmpty()) {
return vespaHostEnv.get().trim();
}
- return "localhost";
+ return defHost;
}
- static private String findVespaUser() {
+ static private String findVespaUser(String defUser) {
Optional<String> vespaUserEnv = Optional.ofNullable(System.getenv("VESPA_USER"));
if (! vespaUserEnv.isPresent()) {
- log.fine("VESPA_USER not set, using vespa");
- return "vespa";
+ log.fine("VESPA_USER not set, using "+defUser);
+ return defUser;
}
return vespaUserEnv.get().trim();
}
- static private int findVespaWebServicePort() {
- Optional<String> vespaWebServicePortString = Optional.ofNullable(System.getenv("VESPA_WEB_SERVICE_PORT"));
- if ( ! vespaWebServicePortString.isPresent() || vespaWebServicePortString.get().trim().isEmpty()) {
- log.info("VESPA_WEB_SERVICE_PORT not set, using 8080");
- return 8080;
+ static private int findPort(String varName, int defaultPort) {
+ Optional<String> port = Optional.ofNullable(System.getenv(varName));
+ if ( ! port.isPresent() || port.get().trim().isEmpty()) {
+ log.fine("" + varName + " not set, using " + defaultPort);
+ return defaultPort;
}
try {
- return Integer.parseInt(vespaWebServicePortString.get());
- }
- catch (NumberFormatException e) {
- throw new IllegalArgumentException("VESPA_WEB_SERVICE_PORT must be an integer, was '" +
- vespaWebServicePortString.get() + "'");
+ return Integer.parseInt(port.get());
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("must be an integer, was '" +
+ port.get() + "'");
}
}
+ static private int findVespaPortBase(int defaultPort) {
+ return findPort("VESPA_PORT_BASE", defaultPort);
+ }
+ static private int findConfigServerPort(int defaultPort) {
+ return findPort("port_configserver_rpc", defaultPort);
+ }
+ static private int findConfigProxyPort(int defaultPort) {
+ return findPort("port_configproxy_rpc", defaultPort);
+ }
+ static private int findWebServicePort(int defaultPort) {
+ return findPort("VESPA_WEB_SERVICE_PORT", defaultPort);
+ }
+
/**
* Get the username to own directories, files and processes
* @return the vespa user name
@@ -135,6 +152,15 @@ public class Defaults {
*/
public int vespaPortBase() { return vespaPortBase; }
+ /** @return port number used by cloud config server (for its RPC protocol) */
+ public int vespaConfigServerRpcPort() { return vespaPortConfigServerRpc; }
+
+ /** @return port number used by cloud config server (REST api on HTTP) */
+ public int vespaConfigServerHttpPort() { return vespaPortConfigServerHttp; }
+
+ /** @return port number used by config proxy server (RPC protocol) */
+ public int vespaConfigProxyRpcPort() { return vespaPortConfigProxyRpc; }
+
/** Returns the defaults of this runtime environment */
public static Defaults getDefaults() { return defaults; }
diff --git a/defaults/src/test/java/com/yahoo/vespa/defaults/DefaultsTestCase.java b/defaults/src/test/java/com/yahoo/vespa/defaults/DefaultsTestCase.java
index 75da8e1146f..07d3c39fc9c 100644
--- a/defaults/src/test/java/com/yahoo/vespa/defaults/DefaultsTestCase.java
+++ b/defaults/src/test/java/com/yahoo/vespa/defaults/DefaultsTestCase.java
@@ -22,4 +22,29 @@ public class DefaultsTestCase {
assertEquals("vespa", Defaults.getDefaults().vespaUser());
}
+ @Test
+ public void testPortsArePositive() {
+ Defaults d = Defaults.getDefaults();
+ assertEquals(true, d.vespaPortBase() > 0);
+ assertEquals(true, d.vespaWebServicePort() > 0);
+ assertEquals(true, d.vespaConfigServerRpcPort() > 0);
+ assertEquals(true, d.vespaConfigServerHttpPort() > 0);
+ assertEquals(true, d.vespaConfigProxyRpcPort() > 0);
+ }
+
+ @Test
+ public void dumpAllVars() {
+ Defaults d = Defaults.getDefaults();
+ System.out.println("vespa user = '" + d.vespaUser() + "'");
+ System.out.println("vespa hostname = '" + d.vespaHostname() + "'");
+ System.out.println("vespa home = '" + d.vespaHome() + "'");
+ System.out.println("underVespaHome(foo) = '" + d.underVespaHome("foo") + "'");
+
+ System.out.println("web service port = '" + d.vespaWebServicePort() + "'");
+ System.out.println("vespa port base = '" + d.vespaPortBase() + "'");
+ System.out.println("config server RPC port = '" + d.vespaConfigServerRpcPort() + "'");
+ System.out.println("config server HTTP port = '" + d.vespaConfigServerHttpPort() + "'");
+ System.out.println("config proxy RPC port = '" + d.vespaConfigProxyRpcPort() + "'");
+ }
+
}
diff --git a/defaults/src/vespa/defaults.cpp b/defaults/src/vespa/defaults.cpp
index dcb5e38584d..428dcfa6ce5 100644
--- a/defaults/src/vespa/defaults.cpp
+++ b/defaults/src/vespa/defaults.cpp
@@ -15,15 +15,15 @@ namespace {
#define HOST_BUF_SZ 1024
-const char *defaultHome = "/opt/vespa";
-const char *defaultUser = "vespa";
-const char *defaultHost = "localhost";
-int defaultWebServicePort = 8080;
-int defaultPortBase = 19000;
-int defaultPortConfigServerRpc = 19070;
-int defaultPortConfigServerHttp = 19071;
-int defaultPortConfigProxyRpc = 19090;
-const char *defaultConfigServers = "localhost";
+const char *defaultHome = 0;
+const char *defaultUser = 0;
+const char *defaultHost = 0;
+int defaultWebServicePort = 0;
+int defaultPortBase = 0;
+int defaultPortConfigServerRpc = 0;
+int defaultPortConfigServerHttp = 0;
+int defaultPortConfigProxyRpc = 0;
+const char *defaultConfigServers = 0;
std::atomic<bool> initialized(false);
@@ -42,8 +42,7 @@ long getNumFromEnv(const char *envName)
return -1;
}
-void findDefaults() {
- if (initialized) return;
+const char *findVespaHome(const char *defHome) {
const char *env = getenv("VESPA_HOME");
if (env != NULL && *env != '\0') {
DIR *dp = NULL;
@@ -51,47 +50,72 @@ void findDefaults() {
dp = opendir(env);
}
if (dp != NULL) {
- defaultHome = env;
- // fprintf(stderr, "debug\tVESPA_HOME is '%s'\n", defaultHome);
+ // fprintf(stderr, "debug\tVESPA_HOME is '%s'\n", env);
closedir(dp);
+ return env;
} else {
fprintf(stderr, "warning\tbad VESPA_HOME '%s' (ignored)\n", env);
}
}
- env = getenv("VESPA_USER");
+ return defHome;
+}
+
+const char *findVespaUser(const char *defUser) {
+ const char *env = getenv("VESPA_USER");
if (env != NULL && *env != '\0') {
if (getpwnam(env) == 0) {
fprintf(stderr, "warning\tbad VESPA_USER '%s' (ignored)\n", env);
} else {
- defaultUser = env;
+ return env;
}
}
- env = getenv("VESPA_HOSTNAME");
+ return defUser;
+}
+
+const char *findHostname(const char *defHost) {
+ const char *env = getenv("VESPA_HOSTNAME");
if (env != NULL && *env != '\0') {
- defaultHost = env;
+ return env;
}
+ return defHost;
+}
+
+int findWebServicePort(int defPort) {
long p = getNumFromEnv("VESPA_WEB_SERVICE_PORT");
if (p > 0) {
// fprintf(stderr, "debug\tdefault web service port is '%ld'\n", p);
- defaultWebServicePort = p;
+ return p;
}
- p = getNumFromEnv("VESPA_PORT_BASE");
+ return defPort;
+}
+
+int findVespaPortBase(int defPort) {
+ long p = getNumFromEnv("VESPA_PORT_BASE");
if (p > 0) {
// fprintf(stderr, "debug\tdefault port base is '%ld'\n", p);
- defaultPortBase = p;
+ return p;
}
- p = getNumFromEnv("port_configserver_rpc");
+ return defPort;
+}
+
+int findConfigServerPort(int defPort) {
+ long p = getNumFromEnv("port_configserver_rpc");
if (p > 0) {
- defaultPortConfigServerRpc = p;
- defaultPortConfigServerHttp = p+1;
+ return p;
}
- p = getNumFromEnv("port_configproxy_rpc");
+ return defPort;
+}
+
+int findConfigProxyPort(int defPort) {
+ long p = getNumFromEnv("port_configproxy_rpc");
if (p > 0) {
- defaultPortConfigProxyRpc = p;
- } else {
- defaultPortConfigProxyRpc = defaultPortBase + 90;
+ return p;
}
- env = getenv("VESPA_CONFIGSERVERS");
+ return defPort;
+}
+
+const char *findConfigServers(const char *defServers) {
+ const char *env = getenv("VESPA_CONFIGSERVERS");
if (env == NULL || *env == '\0') {
env = getenv("services__addr_configserver");
}
@@ -103,14 +127,26 @@ void findDefaults() {
}
if (env != NULL && *env != '\0') {
// fprintf(stderr, "debug\tdefault configserver(s) is '%s'\n", env);
- defaultConfigServers = env;
+ return env;
}
- initialized = true;
+ return defServers;
}
-}
+void findDefaults() {
+ if (initialized) return;
-namespace vespa {
+ defaultHome = findVespaHome("/opt/vespa");
+ defaultUser = findVespaUser("vespa");
+ defaultHost = findHostname("localhost");
+ defaultWebServicePort = findWebServicePort(8080);
+ defaultPortBase = findVespaPortBase(19000);
+ defaultPortConfigServerRpc = findConfigServerPort(defaultPortBase + 70);
+ defaultPortConfigServerHttp = defaultPortConfigServerRpc + 1;
+ defaultPortConfigProxyRpc = findConfigProxyPort(defaultPortBase + 90);
+ defaultConfigServers = findConfigServers("localhost");
+
+ initialized = true;
+}
std::string myPath(const char *argv0)
{
@@ -137,6 +173,10 @@ std::string myPath(const char *argv0)
return argv0;
}
+}
+
+namespace vespa {
+
void
Defaults::bootstrap(const char *argv0)
{
@@ -241,6 +281,7 @@ Defaults::vespaConfigServerHosts()
int
Defaults::vespaConfigServerRpcPort()
{
+ findDefaults();
return defaultPortConfigServerRpc;
}
diff --git a/dist/getversion.pl b/dist/getversion.pl
index 77271269aa3..80d35d58f43 100755
--- a/dist/getversion.pl
+++ b/dist/getversion.pl
@@ -5,20 +5,14 @@ use POSIX qw(strftime);
$srcdir = ".";
-my $generatejava = 0;
-my $printdefines = 0;
my $printmap = 0;
-my $simple = 0;
-my $target = "";
-my $pkgname = "";
-my $tagtype = "";
while ($opt = shift) {
if ($opt =~ m/^-/) {
if ($opt eq "-M") {
$printmap = 1;
} elsif ($opt eq "-T") {
- $tagtype = shift;
+ die "option '-T' is removed";
} else {
print STDERR "ERROR: unknown option '$opt' for getversion\n";
print "error\n";
@@ -29,32 +23,21 @@ while ($opt = shift) {
}
}
-if (!defined($srcdir)) {
- die "srcdir must be set";
-}
-
# Read current major-minor release
sub read_head_version() {
my $file = "$srcdir/VERSION";
if (! -f $file) {
- die "Unable to locate version file";
+ die "Unable to locate version file in $srcdir";
}
open(my $fd, "< $file") ||
die "Unable to open VERSION: $!";
my $version = <$fd>;
chomp($version);
close($fd);
-
return $version;
}
-if ( ! -d $srcdir ) {
- print STDERR "ERROR: bad directory '$srcdir' for getversion\n";
- print "error\n";
- exit 1;
-}
-
-# assume HEAD if all else fails
+# assume HEAD
my $mainver = read_head_version();
# date adding logic
@@ -63,11 +46,8 @@ my $mainver = read_head_version();
# vbuild/mbuild also has some logic for this:
$dateadd = $ENV{"VBUILD_VERSION_DATE"};
-$buildtime = $ENV{"CVSBUILDTIME"};
-if ($buildtime && $buildtime =~ m/^(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)$/ ) {
- $dateadd = ".$1$2$3.$4$5$6";
-} elsif ($dateadd) {
+if ($dateadd) {
1;
} else {
$dateadd = (strftime ".%Y%m%d.%H%M%S", gmtime);
@@ -77,18 +57,14 @@ $tag = "HEAD";
if (defined $ENV{FACTORY_VESPA_VERSION}) {
$version = $ENV{FACTORY_VESPA_VERSION};
-} elsif ($tagtype eq 'tag') {
- # only for exact tag, do not add date:
- $version = $mainver;
} else {
$version = $mainver . $dateadd;
}
-if ($printdefines || $generatejava || $printmap) {
+if ($printmap) {
# other useful information
chomp($ostype = `uname -s`);
-
chomp($osver = `uname -r`);
chomp($osarch = `uname -m`);
diff --git a/docker-api/pom.xml b/docker-api/pom.xml
index 6118865a6e0..87761a17f70 100644
--- a/docker-api/pom.xml
+++ b/docker-api/pom.xml
@@ -128,31 +128,11 @@
<groupId>com.yahoo.vespa</groupId>
<artifactId>bundle-plugin</artifactId>
<extensions>true</extensions>
+ <configuration>
+ <attachBundleToArtifact>true</attachBundleToArtifact>
+ </configuration>
</plugin>
<plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>build-helper-maven-plugin</artifactId>
- <executions>
- <execution>
- <id>attach-artifacts</id>
- <phase>package</phase>
- <goals>
- <goal>attach-artifact</goal>
- </goals>
- <configuration>
- <artifacts>
- <artifact>
- <file>target/${project.artifactId}-jar-with-dependencies.jar</file>
- <type>jar</type>
- <classifier>bundle</classifier>
- </artifact>
- </artifacts>
- </configuration>
- </execution>
- </executions>
- </plugin>
-
- <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
diff --git a/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/DockerImplTest.java b/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/DockerImplTest.java
index 82c7d2285e9..13ff45808cf 100644
--- a/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/DockerImplTest.java
+++ b/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/DockerImplTest.java
@@ -29,7 +29,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class DockerImplTest {
diff --git a/docproc/src/main/java/com/yahoo/docproc/jdisc/messagebus/ProcessingFactory.java b/docproc/src/main/java/com/yahoo/docproc/jdisc/messagebus/ProcessingFactory.java
index ae38d9debd6..044f86e93e6 100644
--- a/docproc/src/main/java/com/yahoo/docproc/jdisc/messagebus/ProcessingFactory.java
+++ b/docproc/src/main/java/com/yahoo/docproc/jdisc/messagebus/ProcessingFactory.java
@@ -23,7 +23,7 @@ import com.yahoo.documentapi.messagebus.protocol.UpdateDocumentMessage;
import com.yahoo.messagebus.Message;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
class ProcessingFactory {
diff --git a/docproc/src/main/java/com/yahoo/docproc/proxy/ProxyDocument.java b/docproc/src/main/java/com/yahoo/docproc/proxy/ProxyDocument.java
index 2c2c8e853a3..23555efec50 100644
--- a/docproc/src/main/java/com/yahoo/docproc/proxy/ProxyDocument.java
+++ b/docproc/src/main/java/com/yahoo/docproc/proxy/ProxyDocument.java
@@ -1,4 +1,4 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.docproc.proxy;
import com.yahoo.docproc.Accesses;
@@ -278,6 +278,11 @@ public class ProxyDocument extends Document implements DocumentOperationWrapper
}
@Override
+ public String toJson() {
+ return doc.toJson();
+ }
+
+ @Override
public void onSerialize(Serializer target) throws SerializationException {
doc.onSerialize(target);
}
diff --git a/docproc/src/test/java/com/yahoo/docproc/AccessesAnnotationTestCase.java b/docproc/src/test/java/com/yahoo/docproc/AccessesAnnotationTestCase.java
index aa56cd58880..c82aed31288 100644
--- a/docproc/src/test/java/com/yahoo/docproc/AccessesAnnotationTestCase.java
+++ b/docproc/src/test/java/com/yahoo/docproc/AccessesAnnotationTestCase.java
@@ -14,7 +14,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class AccessesAnnotationTestCase {
diff --git a/docprocs/src/main/java/com/yahoo/docprocs/indexing/DocumentScript.java b/docprocs/src/main/java/com/yahoo/docprocs/indexing/DocumentScript.java
index 7d347bc6ed7..f25603deee9 100644
--- a/docprocs/src/main/java/com/yahoo/docprocs/indexing/DocumentScript.java
+++ b/docprocs/src/main/java/com/yahoo/docprocs/indexing/DocumentScript.java
@@ -23,7 +23,7 @@ import com.yahoo.vespa.indexinglanguage.expressions.Expression;
import java.util.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DocumentScript {
diff --git a/docprocs/src/main/java/com/yahoo/docprocs/indexing/FastLogger.java b/docprocs/src/main/java/com/yahoo/docprocs/indexing/FastLogger.java
index 733a5f94ab6..3ce2a2f1df4 100644
--- a/docprocs/src/main/java/com/yahoo/docprocs/indexing/FastLogger.java
+++ b/docprocs/src/main/java/com/yahoo/docprocs/indexing/FastLogger.java
@@ -5,7 +5,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
class FastLogger {
diff --git a/docprocs/src/main/java/com/yahoo/docprocs/indexing/IndexingProcessor.java b/docprocs/src/main/java/com/yahoo/docprocs/indexing/IndexingProcessor.java
index d07b60ec51b..761661710d4 100644
--- a/docprocs/src/main/java/com/yahoo/docprocs/indexing/IndexingProcessor.java
+++ b/docprocs/src/main/java/com/yahoo/docprocs/indexing/IndexingProcessor.java
@@ -19,7 +19,7 @@ import com.yahoo.vespa.indexinglanguage.SimpleAdapterFactory;
import com.yahoo.vespa.indexinglanguage.expressions.Expression;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
@Provides({ IndexingProcessor.PROVIDED_NAME })
@Before({ IndexingProcessor.INDEXING_END })
diff --git a/docprocs/src/main/java/com/yahoo/docprocs/indexing/ScriptManager.java b/docprocs/src/main/java/com/yahoo/docprocs/indexing/ScriptManager.java
index b1ec78fa0c4..000568214cd 100644
--- a/docprocs/src/main/java/com/yahoo/docprocs/indexing/ScriptManager.java
+++ b/docprocs/src/main/java/com/yahoo/docprocs/indexing/ScriptManager.java
@@ -17,7 +17,7 @@ import java.util.*;
import java.util.logging.Level;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ScriptManager {
diff --git a/docprocs/src/test/java/com/yahoo/docprocs/indexing/DocumentScriptTestCase.java b/docprocs/src/test/java/com/yahoo/docprocs/indexing/DocumentScriptTestCase.java
index 419b60432c4..a47762bfbf3 100644
--- a/docprocs/src/test/java/com/yahoo/docprocs/indexing/DocumentScriptTestCase.java
+++ b/docprocs/src/test/java/com/yahoo/docprocs/indexing/DocumentScriptTestCase.java
@@ -40,7 +40,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings("unchecked")
public class DocumentScriptTestCase {
diff --git a/docprocs/src/test/java/com/yahoo/docprocs/indexing/IndexingProcessorTestCase.java b/docprocs/src/test/java/com/yahoo/docprocs/indexing/IndexingProcessorTestCase.java
index 540832bd4cf..cef020cd828 100644
--- a/docprocs/src/test/java/com/yahoo/docprocs/indexing/IndexingProcessorTestCase.java
+++ b/docprocs/src/test/java/com/yahoo/docprocs/indexing/IndexingProcessorTestCase.java
@@ -25,7 +25,7 @@ import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class IndexingProcessorTestCase {
diff --git a/docprocs/src/test/java/com/yahoo/docprocs/indexing/ScriptManagerTestCase.java b/docprocs/src/test/java/com/yahoo/docprocs/indexing/ScriptManagerTestCase.java
index 4ab5e136106..23df8c0eb25 100644
--- a/docprocs/src/test/java/com/yahoo/docprocs/indexing/ScriptManagerTestCase.java
+++ b/docprocs/src/test/java/com/yahoo/docprocs/indexing/ScriptManagerTestCase.java
@@ -13,7 +13,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ScriptManagerTestCase {
diff --git a/document/src/main/java/com/yahoo/document/BucketDistribution.java b/document/src/main/java/com/yahoo/document/BucketDistribution.java
index d4dd6fb3749..b8752a4f49e 100644
--- a/document/src/main/java/com/yahoo/document/BucketDistribution.java
+++ b/document/src/main/java/com/yahoo/document/BucketDistribution.java
@@ -9,7 +9,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class BucketDistribution {
diff --git a/document/src/main/java/com/yahoo/document/Document.java b/document/src/main/java/com/yahoo/document/Document.java
index 025166b5ef9..23beab7523e 100644
--- a/document/src/main/java/com/yahoo/document/Document.java
+++ b/document/src/main/java/com/yahoo/document/Document.java
@@ -1,17 +1,28 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.document;
import com.yahoo.document.datatypes.FieldValue;
import com.yahoo.document.datatypes.Struct;
import com.yahoo.document.datatypes.StructuredFieldValue;
-import com.yahoo.document.serialization.*;
+import com.yahoo.document.json.JsonWriter;
+import com.yahoo.document.serialization.DocumentReader;
+import com.yahoo.document.serialization.DocumentSerializer;
+import com.yahoo.document.serialization.DocumentSerializerFactory;
+import com.yahoo.document.serialization.DocumentWriter;
+import com.yahoo.document.serialization.FieldReader;
+import com.yahoo.document.serialization.FieldWriter;
+import com.yahoo.document.serialization.SerializationException;
+import com.yahoo.document.serialization.XmlSerializationHelper;
+import com.yahoo.document.serialization.XmlStream;
import com.yahoo.io.GrowableByteBuffer;
import com.yahoo.vespa.objects.BufferSerializer;
import com.yahoo.vespa.objects.Ids;
import com.yahoo.vespa.objects.Serializer;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import java.util.Map;
@@ -266,6 +277,21 @@ public class Document extends StructuredFieldValue {
XmlSerializationHelper.printDocumentXml(this, xml);
}
+ /**
+ * Get JSON representation of the document root and its children contained in a JSON object
+ * @return JSON representation of document
+ */
+ public String toJson() {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ JsonWriter writer = new JsonWriter(buffer);
+ writer.write(this);
+ try {
+ return buffer.toString("UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
/** Returns true if the argument is a document which has the same set of values */
@Override
public boolean equals(Object o) {
@@ -314,8 +340,8 @@ public class Document extends StructuredFieldValue {
@SuppressWarnings("deprecation")
public void serializeHeader(Serializer data) throws SerializationException {
if (data instanceof DocumentWriter) {
- if (data instanceof VespaDocumentSerializer42) {
- ((VespaDocumentSerializer42)data).setHeaderOnly(true);
+ if (data instanceof com.yahoo.document.serialization.VespaDocumentSerializer42) {
+ ((com.yahoo.document.serialization.VespaDocumentSerializer42)data).setHeaderOnly(true);
}
serialize((DocumentWriter)data);
} else if (data instanceof BufferSerializer) {
diff --git a/document/src/main/java/com/yahoo/document/DocumentUpdate.java b/document/src/main/java/com/yahoo/document/DocumentUpdate.java
index 2a9ab9e6169..70c5410534e 100644
--- a/document/src/main/java/com/yahoo/document/DocumentUpdate.java
+++ b/document/src/main/java/com/yahoo/document/DocumentUpdate.java
@@ -1,11 +1,14 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.document;
+import com.yahoo.document.datatypes.FieldValue;
import com.yahoo.document.fieldpathupdate.FieldPathUpdate;
import com.yahoo.document.serialization.DocumentSerializerFactory;
import com.yahoo.document.serialization.DocumentUpdateReader;
import com.yahoo.document.serialization.DocumentUpdateWriter;
+import com.yahoo.document.update.AssignValueUpdate;
import com.yahoo.document.update.FieldUpdate;
+import com.yahoo.document.update.ValueUpdate;
import com.yahoo.io.GrowableByteBuffer;
import java.util.ArrayList;
@@ -94,6 +97,12 @@ public class DocumentUpdate extends DocumentOperation implements Iterable<FieldP
docId = id;
}
+ private void verifyType(Document doc) {
+ if (!documentType.equals(doc.getDataType())) {
+ throw new IllegalArgumentException(
+ "Document " + doc.getId() + " with type " + doc.getDataType() + " must have same type as update, which is type " + documentType);
+ }
+ }
/**
* Applies this document update.
*
@@ -102,10 +111,7 @@ public class DocumentUpdate extends DocumentOperation implements Iterable<FieldP
* @throws IllegalArgumentException if the document does not have the same document type as this update
*/
public DocumentUpdate applyTo(Document doc) {
- if (!documentType.equals(doc.getDataType())) {
- throw new IllegalArgumentException(
- "Document " + doc + " must have same type as update, which is type " + documentType);
- }
+ verifyType(doc);
for (FieldUpdate fieldUpdate : fieldUpdates) {
fieldUpdate.applyTo(doc);
@@ -117,6 +123,30 @@ public class DocumentUpdate extends DocumentOperation implements Iterable<FieldP
}
/**
+ * Prune away any field update that will not modify any field in the document.
+ * @param doc document to check against
+ * @return a reference to itself
+ * @throws IllegalArgumentException if the document does not have the same document type as this update
+ */
+ public DocumentUpdate prune(Document doc) {
+ verifyType(doc);
+
+ for (Iterator<FieldUpdate> iter = fieldUpdates.iterator(); iter.hasNext();) {
+ FieldUpdate update = iter.next();
+ if (!update.isEmpty()) {
+ ValueUpdate last = update.getValueUpdate(update.size() - 1);
+ if (last instanceof AssignValueUpdate) {
+ FieldValue currentValue = doc.getFieldValue(update.getField());
+ if ((currentValue != null) && (currentValue.compareTo(last.getValue()) == 0)) {
+ iter.remove();
+ }
+ }
+ }
+ }
+ return this;
+ }
+
+ /**
* Get an unmodifiable list of all field updates that this document update specifies.
*
* @return a list of all FieldUpdates in this DocumentUpdate
diff --git a/document/src/main/java/com/yahoo/document/GlobalId.java b/document/src/main/java/com/yahoo/document/GlobalId.java
index 95e35c9280c..41b4a2da51c 100644
--- a/document/src/main/java/com/yahoo/document/GlobalId.java
+++ b/document/src/main/java/com/yahoo/document/GlobalId.java
@@ -14,7 +14,7 @@ import java.util.Arrays;
*
* This is immutable (by contract - not enforcable due to exposing the raw byte array).
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class GlobalId implements Comparable {
diff --git a/document/src/main/java/com/yahoo/document/PositionDataType.java b/document/src/main/java/com/yahoo/document/PositionDataType.java
index b5b3fceece6..bb110ee7219 100644
--- a/document/src/main/java/com/yahoo/document/PositionDataType.java
+++ b/document/src/main/java/com/yahoo/document/PositionDataType.java
@@ -12,7 +12,7 @@ import java.text.DecimalFormatSymbols;
import java.util.Locale;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class PositionDataType {
diff --git a/document/src/main/java/com/yahoo/document/SimpleDocument.java b/document/src/main/java/com/yahoo/document/SimpleDocument.java
index 11a05d26f8f..13e287714d0 100644
--- a/document/src/main/java/com/yahoo/document/SimpleDocument.java
+++ b/document/src/main/java/com/yahoo/document/SimpleDocument.java
@@ -5,7 +5,7 @@ import com.yahoo.document.datatypes.FieldValue;
import com.yahoo.document.datatypes.StructuredFieldValue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SimpleDocument {
diff --git a/document/src/main/java/com/yahoo/document/annotation/AnnotationTypes.java b/document/src/main/java/com/yahoo/document/annotation/AnnotationTypes.java
index 248e37345a8..d2409652d3e 100644
--- a/document/src/main/java/com/yahoo/document/annotation/AnnotationTypes.java
+++ b/document/src/main/java/com/yahoo/document/annotation/AnnotationTypes.java
@@ -10,7 +10,7 @@ import java.util.List;
* This is a container for all {@link Annotation}s constants used by built-in Vespa features. These must be in sync with
* the corresponding class in the C++ file 'document/datatype/annotationtype.h'.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings({ "UnusedDeclaration" })
public final class AnnotationTypes {
diff --git a/document/src/main/java/com/yahoo/document/annotation/SpanTrees.java b/document/src/main/java/com/yahoo/document/annotation/SpanTrees.java
index 64d7d7cff68..b28bcbc98bb 100644
--- a/document/src/main/java/com/yahoo/document/annotation/SpanTrees.java
+++ b/document/src/main/java/com/yahoo/document/annotation/SpanTrees.java
@@ -4,7 +4,7 @@ package com.yahoo.document.annotation;
/**
* This is a container for all {@link SpanTree}s constants used by built-in Vespa features.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings({ "UnusedDeclaration" })
// TODO: Remove. This is the wrong place.
diff --git a/document/src/main/java/com/yahoo/document/datatypes/PredicateFieldValue.java b/document/src/main/java/com/yahoo/document/datatypes/PredicateFieldValue.java
index 67d03de658e..048213d00d3 100644
--- a/document/src/main/java/com/yahoo/document/datatypes/PredicateFieldValue.java
+++ b/document/src/main/java/com/yahoo/document/datatypes/PredicateFieldValue.java
@@ -12,7 +12,7 @@ import com.yahoo.document.serialization.XmlStream;
import java.util.Objects;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class PredicateFieldValue extends FieldValue {
diff --git a/document/src/main/java/com/yahoo/document/datatypes/Raw.java b/document/src/main/java/com/yahoo/document/datatypes/Raw.java
index 2a5383705df..23ed0cee23e 100644
--- a/document/src/main/java/com/yahoo/document/datatypes/Raw.java
+++ b/document/src/main/java/com/yahoo/document/datatypes/Raw.java
@@ -17,15 +17,16 @@ import java.util.Arrays;
/**
* FieldValue which encapsulates a Raw value
*
- * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ * @author Einar M R Rosenvinge
*/
public final class Raw extends FieldValue {
+
private static class Factory extends PrimitiveDataType.Factory {
public FieldValue create() {
return new Raw();
}
}
- public static PrimitiveDataType.Factory getFactory() { return new Factory(); }
+
public static final int classId = registerClass(Ids.document + 16, Raw.class);
private ByteBuffer value;
@@ -42,6 +43,8 @@ public final class Raw extends FieldValue {
value.position(0);
}
+ public static PrimitiveDataType.Factory getFactory() { return new Factory(); }
+
public ByteBuffer getByteBuffer() {
return value;
}
@@ -136,11 +139,11 @@ public final class Raw extends FieldValue {
}
/* (non-Javadoc)
- * @see com.yahoo.document.datatypes.FieldValue#deserialize(com.yahoo.document.Field, com.yahoo.document.serialization.FieldReader)
- */
-
+ * @see com.yahoo.document.datatypes.FieldValue#deserialize(com.yahoo.document.Field, com.yahoo.document.serialization.FieldReader)
+ */
@Override
public void deserialize(Field field, FieldReader reader) {
reader.read(field, this);
}
+
}
diff --git a/document/src/main/java/com/yahoo/document/select/BucketSet.java b/document/src/main/java/com/yahoo/document/select/BucketSet.java
index 76a62bdeb87..d2f90f96724 100644
--- a/document/src/main/java/com/yahoo/document/select/BucketSet.java
+++ b/document/src/main/java/com/yahoo/document/select/BucketSet.java
@@ -8,7 +8,7 @@ import java.util.HashSet;
/**
* A set of bucket ids covered by a document selector.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class BucketSet extends HashSet<BucketId> {
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 f2f3f53a3f0..784b7fdc7e7 100644
--- a/document/src/main/java/com/yahoo/document/select/Result.java
+++ b/document/src/main/java/com/yahoo/document/select/Result.java
@@ -4,7 +4,7 @@ package com.yahoo.document.select;
import com.yahoo.document.select.rule.AttributeNode;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public enum Result {
diff --git a/document/src/main/java/com/yahoo/document/select/parser/SelectInput.java b/document/src/main/java/com/yahoo/document/select/parser/SelectInput.java
index 7c75d21ab38..4a4beabc155 100644
--- a/document/src/main/java/com/yahoo/document/select/parser/SelectInput.java
+++ b/document/src/main/java/com/yahoo/document/select/parser/SelectInput.java
@@ -4,7 +4,7 @@ package com.yahoo.document.select.parser;
import com.yahoo.javacc.FastCharStream;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class SelectInput extends FastCharStream implements CharStream {
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 94340355947..6537eefaa7a 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
@@ -6,7 +6,7 @@ import com.yahoo.javacc.UnicodeUtilities;
import java.math.BigInteger;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class SelectParserUtils {
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 a54f5cada97..813c3b27612 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
@@ -10,7 +10,7 @@ import java.util.ArrayList;
import java.util.Stack;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ArithmeticNode implements ExpressionNode {
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 294ffafa7e7..17f95087be1 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
@@ -11,7 +11,7 @@ import java.util.ArrayList;
import java.util.List;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class AttributeNode implements ExpressionNode {
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 372b61bb493..13a990566e3 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
@@ -13,7 +13,7 @@ import java.util.List;
import java.util.regex.Pattern;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ComparisonNode implements ExpressionNode {
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 d12e8d20fa9..3fe3d5d7169 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
@@ -8,7 +8,7 @@ import com.yahoo.document.select.OrderingSpecification;
import com.yahoo.document.select.Visitor;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DocumentNode implements ExpressionNode {
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 234013ce604..e1bd4b2d53f 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
@@ -8,7 +8,7 @@ import com.yahoo.document.select.OrderingSpecification;
import com.yahoo.document.select.Visitor;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class EmbracedNode implements ExpressionNode {
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 71500415bb3..29f49c4459c 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
@@ -11,7 +11,7 @@ import com.yahoo.document.select.Visitor;
* This is the interface of all expression nodes. It declares the methods requires by all expression nodes to maintain
* a working document selector language.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface ExpressionNode {
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 d1c6aacd03c..3c15a2866cc 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
@@ -7,7 +7,7 @@ import com.yahoo.document.select.*;
import com.yahoo.document.idstring.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class IdNode implements ExpressionNode {
diff --git a/document/src/main/java/com/yahoo/document/select/rule/LiteralNode.java b/document/src/main/java/com/yahoo/document/select/rule/LiteralNode.java
index ec186cc59d7..2104a9c0608 100644
--- a/document/src/main/java/com/yahoo/document/select/rule/LiteralNode.java
+++ b/document/src/main/java/com/yahoo/document/select/rule/LiteralNode.java
@@ -9,7 +9,7 @@ import com.yahoo.document.select.Visitor;
import com.yahoo.document.select.parser.SelectParserUtils;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class LiteralNode implements ExpressionNode {
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 6489ca13b17..a7b112fac70 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
@@ -16,7 +16,7 @@ import java.util.Stack;
* This class defines a logical expression of nodes. This implementation uses a stack to evaluate its content as to
* avoid deep recursions when building the parse tree.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class LogicNode implements ExpressionNode {
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 63660814bbe..c89759bbf07 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
@@ -9,7 +9,7 @@ import com.yahoo.document.select.Result;
import com.yahoo.document.select.Visitor;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NegationNode implements ExpressionNode {
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 90cd790539a..ffcf576dcfc 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
@@ -6,7 +6,7 @@ import com.yahoo.document.BucketIdFactory;
import com.yahoo.document.select.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SearchColumnNode implements ExpressionNode {
diff --git a/document/src/main/java/com/yahoo/document/select/rule/VariableNode.java b/document/src/main/java/com/yahoo/document/select/rule/VariableNode.java
index 524674322cf..77abb52938f 100644
--- a/document/src/main/java/com/yahoo/document/select/rule/VariableNode.java
+++ b/document/src/main/java/com/yahoo/document/select/rule/VariableNode.java
@@ -8,7 +8,7 @@ import com.yahoo.document.select.OrderingSpecification;
import com.yahoo.document.select.Visitor;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class VariableNode implements ExpressionNode {
diff --git a/document/src/main/javacc/SelectParser.jj b/document/src/main/javacc/SelectParser.jj
index 8467843ee88..99290370b76 100755
--- a/document/src/main/javacc/SelectParser.jj
+++ b/document/src/main/javacc/SelectParser.jj
@@ -1,6 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
options {
diff --git a/document/src/test/java/com/yahoo/document/DataTypeNameTestCase.java b/document/src/test/java/com/yahoo/document/DataTypeNameTestCase.java
index 8dbad32d820..310c1050c7e 100644
--- a/document/src/test/java/com/yahoo/document/DataTypeNameTestCase.java
+++ b/document/src/test/java/com/yahoo/document/DataTypeNameTestCase.java
@@ -7,7 +7,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DataTypeNameTestCase {
diff --git a/document/src/test/java/com/yahoo/document/DocumentTestCase.java b/document/src/test/java/com/yahoo/document/DocumentTestCase.java
index f7cc6974176..84b33b3881c 100644
--- a/document/src/test/java/com/yahoo/document/DocumentTestCase.java
+++ b/document/src/test/java/com/yahoo/document/DocumentTestCase.java
@@ -1,6 +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.document;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.yahoo.compress.CompressionType;
import com.yahoo.document.datatypes.Array;
import com.yahoo.document.datatypes.ByteFieldValue;
@@ -15,7 +17,12 @@ import com.yahoo.document.datatypes.Raw;
import com.yahoo.document.datatypes.StringFieldValue;
import com.yahoo.document.datatypes.Struct;
import com.yahoo.document.datatypes.WeightedSet;
-import com.yahoo.document.serialization.*;
+import com.yahoo.document.serialization.DocumentDeserializer;
+import com.yahoo.document.serialization.DocumentDeserializerFactory;
+import com.yahoo.document.serialization.DocumentReader;
+import com.yahoo.document.serialization.DocumentSerializer;
+import com.yahoo.document.serialization.DocumentSerializerFactory;
+import com.yahoo.document.serialization.XmlDocumentWriter;
import com.yahoo.io.GrowableByteBuffer;
import com.yahoo.vespa.objects.BufferSerializer;
import org.junit.Test;
@@ -27,10 +34,13 @@ import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.Map;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -40,7 +50,6 @@ import static org.junit.Assert.fail;
* @author <a href="thomasg@yahoo-inc.com>Thomas Gundersen</a>
* @author bratseth
*/
-@SuppressWarnings("deprecation")
public class DocumentTestCase extends DocumentTestCaseBase {
private static final String SERTEST_DOC_AS_XML_HEAD =
@@ -1202,6 +1211,26 @@ public class DocumentTestCase extends DocumentTestCaseBase {
}
@Test
+ public void testSerializationToJson() throws Exception {
+ setUpSertestDocType();
+ Document doc = getSertestDocument();
+ String json = doc.toJson();
+ Map<String, Object> parsed = new ObjectMapper().readValue(json, new TypeReference<Map<String, Object>>() {
+ });
+ assertEquals(parsed.get("id"), "doc:sertest:foobar");
+ assertThat(parsed.get("fields"), instanceOf(Map.class));
+ Object fieldMap = parsed.get("fields");
+ if (fieldMap instanceof Map) {
+ Map<?, ?> fields = (Map<?, ?>) fieldMap;
+ assertEquals(fields.get("mailid"), "emailfromalicetobob");
+ assertEquals(fields.get("date"), -2013512400);
+ assertThat(fields.get("docindoc"), instanceOf(Map.class));
+ assertThat(fields.keySet(),
+ containsInAnyOrder("mailid", "date", "attachmentcount", "rawfield", "weightedfield", "docindoc", "mapfield"));
+ }
+ }
+
+ @Test
public void testEmptyStringsSerialization() {
docMan = new DocumentTypeManager();
DocumentType docType = new DocumentType("emptystrings");
diff --git a/document/src/test/java/com/yahoo/document/DocumentUpdateTestCase.java b/document/src/test/java/com/yahoo/document/DocumentUpdateTestCase.java
index 4392ea932b2..4f3d7d3b820 100644
--- a/document/src/test/java/com/yahoo/document/DocumentUpdateTestCase.java
+++ b/document/src/test/java/com/yahoo/document/DocumentUpdateTestCase.java
@@ -651,6 +651,64 @@ public class DocumentUpdateTestCase {
assertNull(doc.getFieldValue(tensorField));
}
+ @Test
+ public void testThatNonIdenticalAssignCanNotBePrunedAway() {
+ Field field = docType.getField("strfoo");
+ String expected = "some other value";
+ Document doc = createDocument();
+ doc.setFieldValue(field, "some value");
+ DocumentUpdate update = new DocumentUpdate(docType, new DocumentId(documentId));
+ update.addFieldUpdate(FieldUpdate.createAssign(field, new StringFieldValue(expected)));
+ update.prune(doc);
+ assertEquals(1, update.size());
+ update.applyTo(doc);
+ assertEquals(expected, doc.getFieldValue(field).getWrappedValue());
+ }
+
+ @Test
+ public void testThatIdenticalAssignCanBePrunedAway() {
+ Field field = docType.getField("strfoo");
+ String expected = "some value";
+ Document doc = createDocument();
+ doc.setFieldValue(field, "some value");
+ DocumentUpdate update = new DocumentUpdate(docType, new DocumentId(documentId));
+ update.addFieldUpdate(FieldUpdate.createAssign(field,new StringFieldValue(expected)));
+ update.prune(doc);
+ assertEquals(0, update.size());
+ update.applyTo(doc);
+ assertEquals(expected, doc.getFieldValue(field).getWrappedValue());
+ }
+
+ @Test
+ public void testThatIdenticalAssignCanBePrunedAwayIfLast() {
+ Field field = docType.getField("strfoo");
+ String expected = "some value";
+ Document doc = createDocument();
+ doc.setFieldValue(field, "some value");
+ DocumentUpdate update = new DocumentUpdate(docType, new DocumentId(documentId));
+ update.addFieldUpdate(FieldUpdate.createClearField(field));
+ update.addFieldUpdate(FieldUpdate.createAssign(field, new StringFieldValue(expected)));
+ update.prune(doc);
+ assertEquals(0, update.size());
+ update.applyTo(doc);
+ assertEquals(expected, doc.getFieldValue(field).getWrappedValue());
+ }
+
+ @Test
+ public void testThatIdenticalAssignCanNotBePrunedAwayIfNotLast() {
+ Field field = docType.getField("strfoo");
+ String expected = "some random value";
+ Document doc = createDocument();
+ doc.setFieldValue(field, "some value");
+ DocumentUpdate update = new DocumentUpdate(docType, new DocumentId(documentId));
+ update.addFieldUpdate(FieldUpdate.createAssign(field, new StringFieldValue("some value")));
+ update.addFieldUpdate(FieldUpdate.createAssign(field, new StringFieldValue(expected)));
+ update.prune(doc);
+ assertEquals(1, update.size());
+ update.applyTo(doc);
+ assertEquals(expected, doc.getFieldValue(field).getWrappedValue());
+ }
+
private static TensorFieldValue createTensorFieldValue(String tensor) {
return new TensorFieldValue(Tensor.from(tensor));
}
diff --git a/document/src/test/java/com/yahoo/document/PositionTypeTestCase.java b/document/src/test/java/com/yahoo/document/PositionTypeTestCase.java
index 84a4993158d..6249d2a559e 100644
--- a/document/src/test/java/com/yahoo/document/PositionTypeTestCase.java
+++ b/document/src/test/java/com/yahoo/document/PositionTypeTestCase.java
@@ -8,7 +8,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class PositionTypeTestCase {
diff --git a/document/src/test/java/com/yahoo/document/SimpleDocumentTestCase.java b/document/src/test/java/com/yahoo/document/SimpleDocumentTestCase.java
index 3766220a6d9..049b401c39f 100644
--- a/document/src/test/java/com/yahoo/document/SimpleDocumentTestCase.java
+++ b/document/src/test/java/com/yahoo/document/SimpleDocumentTestCase.java
@@ -8,7 +8,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SimpleDocumentTestCase {
diff --git a/document/src/test/java/com/yahoo/document/annotation/AnnotationTypesTestCase.java b/document/src/test/java/com/yahoo/document/annotation/AnnotationTypesTestCase.java
index cb3fe71923d..a19eba1a546 100644
--- a/document/src/test/java/com/yahoo/document/annotation/AnnotationTypesTestCase.java
+++ b/document/src/test/java/com/yahoo/document/annotation/AnnotationTypesTestCase.java
@@ -7,7 +7,7 @@ import org.junit.Test;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class AnnotationTypesTestCase {
diff --git a/document/src/test/java/com/yahoo/document/datatypes/PredicateFieldValueTest.java b/document/src/test/java/com/yahoo/document/datatypes/PredicateFieldValueTest.java
index 41c1daa047d..374f336e483 100644
--- a/document/src/test/java/com/yahoo/document/datatypes/PredicateFieldValueTest.java
+++ b/document/src/test/java/com/yahoo/document/datatypes/PredicateFieldValueTest.java
@@ -21,7 +21,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class PredicateFieldValueTest {
diff --git a/document/src/test/java/com/yahoo/document/datatypes/UriFieldValueTest.java b/document/src/test/java/com/yahoo/document/datatypes/UriFieldValueTest.java
index f6e0f498862..e2727f49239 100644
--- a/document/src/test/java/com/yahoo/document/datatypes/UriFieldValueTest.java
+++ b/document/src/test/java/com/yahoo/document/datatypes/UriFieldValueTest.java
@@ -8,7 +8,7 @@ import java.net.URI;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class UriFieldValueTest {
diff --git a/document/src/test/java/com/yahoo/document/select/DocumentSelectorTestCase.java b/document/src/test/java/com/yahoo/document/select/DocumentSelectorTestCase.java
index c3aa76a1ebb..54f3870fdfd 100644
--- a/document/src/test/java/com/yahoo/document/select/DocumentSelectorTestCase.java
+++ b/document/src/test/java/com/yahoo/document/select/DocumentSelectorTestCase.java
@@ -21,7 +21,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
* @author bratseth
*/
public class DocumentSelectorTestCase {
diff --git a/document/src/test/java/com/yahoo/document/serialization/PredicateFieldValueSerializationTestCase.java b/document/src/test/java/com/yahoo/document/serialization/PredicateFieldValueSerializationTestCase.java
index 01dd86fc8fa..0177748f25c 100644
--- a/document/src/test/java/com/yahoo/document/serialization/PredicateFieldValueSerializationTestCase.java
+++ b/document/src/test/java/com/yahoo/document/serialization/PredicateFieldValueSerializationTestCase.java
@@ -21,7 +21,7 @@ import static com.yahoo.document.serialization.SerializationTestUtils.serializeD
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class PredicateFieldValueSerializationTestCase {
diff --git a/document/src/test/java/com/yahoo/document/serialization/VespaDocumentSerializerTestCase.java b/document/src/test/java/com/yahoo/document/serialization/VespaDocumentSerializerTestCase.java
index 119a2e5aeba..4f34431c729 100644
--- a/document/src/test/java/com/yahoo/document/serialization/VespaDocumentSerializerTestCase.java
+++ b/document/src/test/java/com/yahoo/document/serialization/VespaDocumentSerializerTestCase.java
@@ -24,7 +24,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
* @author vekterli
*/
@SuppressWarnings("deprecation")
diff --git a/document/src/test/java/com/yahoo/document/serialization/XmlDocumentWriterTestCase.java b/document/src/test/java/com/yahoo/document/serialization/XmlDocumentWriterTestCase.java
index 8f0ef317372..3d8875cff52 100644
--- a/document/src/test/java/com/yahoo/document/serialization/XmlDocumentWriterTestCase.java
+++ b/document/src/test/java/com/yahoo/document/serialization/XmlDocumentWriterTestCase.java
@@ -10,7 +10,7 @@ import org.junit.Test;
import org.mockito.Mockito;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class XmlDocumentWriterTestCase {
diff --git a/document/src/test/java/com/yahoo/vespaxmlparser/PositionParserTestCase.java b/document/src/test/java/com/yahoo/vespaxmlparser/PositionParserTestCase.java
index 003660a287f..a7fd782484e 100644
--- a/document/src/test/java/com/yahoo/vespaxmlparser/PositionParserTestCase.java
+++ b/document/src/test/java/com/yahoo/vespaxmlparser/PositionParserTestCase.java
@@ -13,7 +13,7 @@ import java.util.Iterator;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class PositionParserTestCase {
diff --git a/document/src/test/java/com/yahoo/vespaxmlparser/UriParserTestCase.java b/document/src/test/java/com/yahoo/vespaxmlparser/UriParserTestCase.java
index 3f3408bf93e..ea954f0da40 100644
--- a/document/src/test/java/com/yahoo/vespaxmlparser/UriParserTestCase.java
+++ b/document/src/test/java/com/yahoo/vespaxmlparser/UriParserTestCase.java
@@ -15,7 +15,7 @@ import java.util.Iterator;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class UriParserTestCase {
diff --git a/document/src/test/java/com/yahoo/vespaxmlparser/VespaXmlFieldReaderTestCase.java b/document/src/test/java/com/yahoo/vespaxmlparser/VespaXmlFieldReaderTestCase.java
index 1c393cc36f1..99960b205b0 100644
--- a/document/src/test/java/com/yahoo/vespaxmlparser/VespaXmlFieldReaderTestCase.java
+++ b/document/src/test/java/com/yahoo/vespaxmlparser/VespaXmlFieldReaderTestCase.java
@@ -27,7 +27,7 @@ import static org.junit.Assert.fail;
import static org.mockito.Mockito.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class VespaXmlFieldReaderTestCase {
diff --git a/document/src/test/java/com/yahoo/vespaxmlparser/VespaXmlUpdateReaderTestCase.java b/document/src/test/java/com/yahoo/vespaxmlparser/VespaXmlUpdateReaderTestCase.java
index 3c7d02bd6be..424e8bb1bca 100644
--- a/document/src/test/java/com/yahoo/vespaxmlparser/VespaXmlUpdateReaderTestCase.java
+++ b/document/src/test/java/com/yahoo/vespaxmlparser/VespaXmlUpdateReaderTestCase.java
@@ -25,7 +25,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class VespaXmlUpdateReaderTestCase {
diff --git a/document/src/tests/documentselectparsertest.cpp b/document/src/tests/documentselectparsertest.cpp
index 05ec2ce99d0..2433bfecdab 100644
--- a/document/src/tests/documentselectparsertest.cpp
+++ b/document/src/tests/documentselectparsertest.cpp
@@ -163,8 +163,7 @@ DocumentUpdate::SP DocumentSelectParserTest::createUpdate(
const std::string& hstr)
{
const DocumentType* type = _repo->getDocumentType(doctype);
- DocumentUpdate::SP doc(
- new DocumentUpdate(*type, DocumentId(id)));
+ DocumentUpdate::SP doc(new DocumentUpdate(*_repo, *type, DocumentId(id)));
doc->addUpdate(FieldUpdate(doc->getType().getField("headerval"))
.addUpdate(AssignValueUpdate(IntFieldValue(hint))));
doc->addUpdate(FieldUpdate(doc->getType().getField("hstringval"))
diff --git a/document/src/tests/documentupdatetestcase.cpp b/document/src/tests/documentupdatetestcase.cpp
index 6b52d4018b3..b4d98f0dd21 100644
--- a/document/src/tests/documentupdatetestcase.cpp
+++ b/document/src/tests/documentupdatetestcase.cpp
@@ -33,6 +33,7 @@ using namespace document::config_builder;
using vespalib::tensor::Tensor;
using vespalib::tensor::TensorCells;
using vespalib::tensor::TensorDimensions;
+using vespalib::nbostream;
namespace document {
@@ -63,6 +64,7 @@ struct DocumentUpdateTest : public CppUnit::TestFixture {
void testThatCreateIfNonExistentFlagIsSerializedAndDeserialized();
void array_element_update_can_be_roundtrip_serialized();
void array_element_update_applies_to_specified_element();
+ void array_element_update_for_invalid_index_is_ignored();
CPPUNIT_TEST_SUITE(DocumentUpdateTest);
CPPUNIT_TEST(testSimpleUsage);
@@ -90,6 +92,7 @@ struct DocumentUpdateTest : public CppUnit::TestFixture {
CPPUNIT_TEST(testThatCreateIfNonExistentFlagIsSerializedAndDeserialized);
CPPUNIT_TEST(array_element_update_can_be_roundtrip_serialized);
CPPUNIT_TEST(array_element_update_applies_to_specified_element);
+ CPPUNIT_TEST(array_element_update_for_invalid_index_is_ignored);
CPPUNIT_TEST_SUITE_END();
};
@@ -100,7 +103,7 @@ namespace {
ByteBuffer::UP serializeHEAD(const DocumentUpdate & update)
{
- vespalib::nbostream stream;
+ nbostream stream;
VespaDocumentSerializer serializer(stream);
serializer.writeHEAD(update);
ByteBuffer::UP retVal(new ByteBuffer(stream.size()));
@@ -110,7 +113,7 @@ ByteBuffer::UP serializeHEAD(const DocumentUpdate & update)
ByteBuffer::UP serialize42(const DocumentUpdate & update)
{
- vespalib::nbostream stream;
+ nbostream stream;
VespaDocumentSerializer serializer(stream);
serializer.write42(update);
ByteBuffer::UP retVal(new ByteBuffer(stream.size()));
@@ -118,36 +121,28 @@ ByteBuffer::UP serialize42(const DocumentUpdate & update)
return retVal;
}
-ByteBuffer::UP serialize(const ValueUpdate & update)
+nbostream serialize(const ValueUpdate & update)
{
- vespalib::nbostream stream;
+ nbostream stream;
VespaDocumentSerializer serializer(stream);
serializer.write(update);
- ByteBuffer::UP retVal(new ByteBuffer(stream.size()));
- retVal->putBytes(stream.peek(), stream.size());
- return retVal;
+ return stream;
}
-ByteBuffer::UP serialize(const FieldUpdate & update)
+nbostream serialize(const FieldUpdate & update)
{
- vespalib::nbostream stream;
+ nbostream stream;
VespaDocumentSerializer serializer(stream);
serializer.write(update);
- ByteBuffer::UP retVal(new ByteBuffer(stream.size()));
- retVal->putBytes(stream.peek(), stream.size());
- return retVal;
+ return stream;
}
template<typename UpdateType>
void testValueUpdate(const UpdateType& update, const DataType &type) {
try{
DocumentTypeRepo repo;
- ByteBuffer::UP buf = serialize(update);
- buf->flip();
- typename UpdateType::UP copy(dynamic_cast<UpdateType*>(
- ValueUpdate::createInstance(repo, type, *buf,
- Document::getNewestSerializationVersion())
- .release()));
+ nbostream stream = serialize(update);
+ typename UpdateType::UP copy(dynamic_cast<UpdateType*>(ValueUpdate::createInstance(repo, type, stream).release()));
CPPUNIT_ASSERT_EQUAL(update, *copy);
} catch (std::exception& e) {
std::cerr << "Failed while processing update " << update << "\n";
@@ -163,8 +158,7 @@ createTensor(const TensorCells &cells, const TensorDimensions &dimensions) {
FieldValue::UP createTensorFieldValue() {
auto fv(std::make_unique<TensorFieldValue>());
- *fv = createTensor({ {{{"x", "8"}, {"y", "9"}}, 11} },
- {"x", "y"});
+ *fv = createTensor({ {{{"x", "8"}, {"y", "9"}}, 11} }, {"x", "y"});
return std::move(fv);
}
@@ -174,11 +168,8 @@ void
DocumentUpdateTest::testSimpleUsage() {
DocumenttypesConfigBuilderHelper builder;
builder.document(42, "test",
- Struct("test.header")
- .addField("bytef", DataType::T_BYTE)
- .addField("intf", DataType::T_INT),
- Struct("test.body")
- .addField("intarr", Array(DataType::T_INT)));
+ Struct("test.header").addField("bytef", DataType::T_BYTE).addField("intf", DataType::T_INT),
+ Struct("test.body").addField("intarr", Array(DataType::T_INT)));
DocumentTypeRepo repo(builder.config());
const DocumentType* docType(repo.getDocumentType("test"));
const DataType *arrayType = repo.getDataType(*docType, "Array<Int>");
@@ -186,25 +177,22 @@ DocumentUpdateTest::testSimpleUsage() {
// Test that primitive value updates can be serialized
testValueUpdate(ClearValueUpdate(), *DataType::INT);
testValueUpdate(AssignValueUpdate(IntFieldValue(1)), *DataType::INT);
- testValueUpdate(ArithmeticValueUpdate(ArithmeticValueUpdate::Div, 4.3),
- *DataType::FLOAT);
+ testValueUpdate(ArithmeticValueUpdate(ArithmeticValueUpdate::Div, 4.3), *DataType::FLOAT);
testValueUpdate(AddValueUpdate(IntFieldValue(1), 4), *arrayType);
testValueUpdate(RemoveValueUpdate(IntFieldValue(1)), *arrayType);
FieldUpdate fieldUpdate(docType->getField("intf"));
fieldUpdate.addUpdate(AssignValueUpdate(IntFieldValue(1)));
- ByteBuffer::UP fieldBuf = serialize(fieldUpdate);
- fieldBuf->flip();
- FieldUpdate fieldUpdateCopy(repo, *docType, *fieldBuf,
- Document::getNewestSerializationVersion());
+ nbostream stream = serialize(fieldUpdate);
+ FieldUpdate fieldUpdateCopy(repo, *docType, stream);
CPPUNIT_ASSERT_EQUAL(fieldUpdate, fieldUpdateCopy);
// Test that a document update can be serialized
- DocumentUpdate docUpdate(*docType, DocumentId("doc::testdoc"));
+ DocumentUpdate docUpdate(repo, *docType, DocumentId("doc::testdoc"));
docUpdate.addUpdate(fieldUpdateCopy);
- ByteBuffer::UP docBuf = serialize42(docUpdate);
+ ByteBuffer::UP docBuf = serializeHEAD(docUpdate);
docBuf->flip();
- DocumentUpdate::UP docUpdateCopy(DocumentUpdate::create42(repo, *docBuf));
+ auto docUpdateCopy(DocumentUpdate::createHEAD(repo, nbostream(docBuf->getBufferAtPos(), docBuf->getRemaining())));
// Create a test document
Document doc(*docType, DocumentId("doc::testdoc"));
@@ -218,62 +206,53 @@ DocumentUpdateTest::testSimpleUsage() {
// Verify that we can apply simple updates to it
{
Document updated(doc);
- DocumentUpdate upd(*docType, DocumentId("doc::testdoc"));
- upd.addUpdate(FieldUpdate(docType->getField("intf"))
- .addUpdate(ClearValueUpdate()));
+ DocumentUpdate upd(repo, *docType, DocumentId("doc::testdoc"));
+ upd.addUpdate(FieldUpdate(docType->getField("intf")).addUpdate(ClearValueUpdate()));
upd.applyTo(updated);
CPPUNIT_ASSERT(doc != updated);
CPPUNIT_ASSERT(! updated.getValue("intf"));
}
{
Document updated(doc);
- DocumentUpdate upd(*docType, DocumentId("doc::testdoc"));
- upd.addUpdate(FieldUpdate(docType->getField("intf"))
- .addUpdate(AssignValueUpdate(IntFieldValue(15))));
+ DocumentUpdate upd(repo, *docType, DocumentId("doc::testdoc"));
+ upd.addUpdate(FieldUpdate(docType->getField("intf")).addUpdate(AssignValueUpdate(IntFieldValue(15))));
upd.applyTo(updated);
CPPUNIT_ASSERT(doc != updated);
CPPUNIT_ASSERT_EQUAL(15, updated.getValue("intf")->getAsInt());
}
{
Document updated(doc);
- DocumentUpdate upd(*docType, DocumentId("doc::testdoc"));
- upd.addUpdate(FieldUpdate(docType->getField("intf"))
- .addUpdate(ArithmeticValueUpdate(
- ArithmeticValueUpdate::Add, 15)));
+ DocumentUpdate upd(repo, *docType, DocumentId("doc::testdoc"));
+ upd.addUpdate(FieldUpdate(docType->getField("intf")).addUpdate(ArithmeticValueUpdate(ArithmeticValueUpdate::Add, 15)));
upd.applyTo(updated);
CPPUNIT_ASSERT(doc != updated);
CPPUNIT_ASSERT_EQUAL(20, updated.getValue("intf")->getAsInt());
}
{
Document updated(doc);
- DocumentUpdate upd(*docType, DocumentId("doc::testdoc"));
- upd.addUpdate(FieldUpdate(docType->getField("intarr"))
- .addUpdate(AddValueUpdate(IntFieldValue(4))));
+ DocumentUpdate upd(repo, *docType, DocumentId("doc::testdoc"));
+ upd.addUpdate(FieldUpdate(docType->getField("intarr")).addUpdate(AddValueUpdate(IntFieldValue(4))));
upd.applyTo(updated);
CPPUNIT_ASSERT(doc != updated);
- std::unique_ptr<ArrayFieldValue> val(dynamic_cast<ArrayFieldValue*>(
- updated.getValue("intarr").release()));
+ std::unique_ptr<ArrayFieldValue> val(dynamic_cast<ArrayFieldValue*>(updated.getValue("intarr").release()));
CPPUNIT_ASSERT_EQUAL(size_t(3), val->size());
CPPUNIT_ASSERT_EQUAL(4, (*val)[2].getAsInt());
}
{
Document updated(doc);
- DocumentUpdate upd(*docType, DocumentId("doc::testdoc"));
- upd.addUpdate(FieldUpdate(docType->getField("intarr"))
- .addUpdate(RemoveValueUpdate(IntFieldValue(3))));
+ DocumentUpdate upd(repo, *docType, DocumentId("doc::testdoc"));
+ upd.addUpdate(FieldUpdate(docType->getField("intarr")).addUpdate(RemoveValueUpdate(IntFieldValue(3))));
upd.applyTo(updated);
CPPUNIT_ASSERT(doc != updated);
- std::unique_ptr<ArrayFieldValue> val(dynamic_cast<ArrayFieldValue*>(
- updated.getValue("intarr").release()));
+ std::unique_ptr<ArrayFieldValue> val(dynamic_cast<ArrayFieldValue*>(updated.getValue("intarr").release()));
CPPUNIT_ASSERT_EQUAL(size_t(1), val->size());
CPPUNIT_ASSERT_EQUAL(7, (*val)[0].getAsInt());
}
{
Document updated(doc);
- DocumentUpdate upd(*docType, DocumentId("doc::testdoc"));
+ DocumentUpdate upd(repo, *docType, DocumentId("doc::testdoc"));
upd.addUpdate(FieldUpdate(docType->getField("bytef"))
- .addUpdate(ArithmeticValueUpdate(
- ArithmeticValueUpdate::Add, 15)));
+ .addUpdate(ArithmeticValueUpdate(ArithmeticValueUpdate::Add, 15)));
upd.applyTo(updated);
CPPUNIT_ASSERT(doc != updated);
CPPUNIT_ASSERT_EQUAL(15, (int) updated.getValue("bytef")->getAsByte());
@@ -290,9 +269,8 @@ DocumentUpdateTest::testClearField()
CPPUNIT_ASSERT_EQUAL(4, doc->getValue("headerval")->getAsInt());
// Apply an update.
- DocumentUpdate(*doc->getDataType(), doc->getId())
- .addUpdate(FieldUpdate(doc->getField("headerval"))
- .addUpdate(AssignValueUpdate()))
+ DocumentUpdate(docMan.getTypeRepo(), *doc->getDataType(), doc->getId())
+ .addUpdate(FieldUpdate(doc->getField("headerval")).addUpdate(AssignValueUpdate()))
.applyTo(*doc);
CPPUNIT_ASSERT(!doc->getValue("headerval"));
}
@@ -307,9 +285,8 @@ DocumentUpdateTest::testUpdateApplySingleValue()
CPPUNIT_ASSERT_EQUAL(4, doc->getValue("headerval")->getAsInt());
// Apply an update.
- DocumentUpdate(*doc->getDataType(), doc->getId())
- .addUpdate(FieldUpdate(doc->getField("headerval"))
- .addUpdate(AssignValueUpdate(IntFieldValue(9))))
+ DocumentUpdate(docMan.getTypeRepo(), *doc->getDataType(), doc->getId())
+ .addUpdate(FieldUpdate(doc->getField("headerval")).addUpdate(AssignValueUpdate(IntFieldValue(9))))
.applyTo(*doc);
CPPUNIT_ASSERT_EQUAL(9, doc->getValue("headerval")->getAsInt());
}
@@ -320,28 +297,23 @@ DocumentUpdateTest::testUpdateArray()
// Create a document.
TestDocMan docMan;
Document::UP doc(docMan.createDocument());
- CPPUNIT_ASSERT_EQUAL((document::FieldValue*)NULL,
- doc->getValue(doc->getField("tags")).get());
+ CPPUNIT_ASSERT_EQUAL((document::FieldValue*)NULL, doc->getValue(doc->getField("tags")).get());
// Assign array field.
ArrayFieldValue myarray(doc->getType().getField("tags").getDataType());
myarray.add(StringFieldValue("foo"));
myarray.add(StringFieldValue("bar"));
- DocumentUpdate(*doc->getDataType(), doc->getId())
- .addUpdate(FieldUpdate(doc->getField("tags"))
- .addUpdate(AssignValueUpdate(myarray)))
+ DocumentUpdate(docMan.getTypeRepo(), *doc->getDataType(), doc->getId())
+ .addUpdate(FieldUpdate(doc->getField("tags")).addUpdate(AssignValueUpdate(myarray)))
.applyTo(*doc);
- std::unique_ptr<ArrayFieldValue>
- fval1(doc->getAs<ArrayFieldValue>(doc->getField("tags")));
+ auto fval1(doc->getAs<ArrayFieldValue>(doc->getField("tags")));
CPPUNIT_ASSERT_EQUAL((size_t) 2, fval1->size());
- CPPUNIT_ASSERT_EQUAL(std::string("foo"),
- std::string((*fval1)[0].getAsString()));
- CPPUNIT_ASSERT_EQUAL(std::string("bar"),
- std::string((*fval1)[1].getAsString()));
+ CPPUNIT_ASSERT_EQUAL(std::string("foo"), std::string((*fval1)[0].getAsString()));
+ CPPUNIT_ASSERT_EQUAL(std::string("bar"), std::string((*fval1)[1].getAsString()));
// Append array field
- DocumentUpdate(*doc->getDataType(), doc->getId())
+ DocumentUpdate(docMan.getTypeRepo(), *doc->getDataType(), doc->getId())
.addUpdate(FieldUpdate(doc->getField("tags"))
.addUpdate(AddValueUpdate(StringFieldValue("another")))
.addUpdate(AddValueUpdate(StringFieldValue("tag"))))
@@ -349,21 +321,16 @@ DocumentUpdateTest::testUpdateArray()
std::unique_ptr<ArrayFieldValue>
fval2(doc->getAs<ArrayFieldValue>(doc->getField("tags")));
CPPUNIT_ASSERT_EQUAL((size_t) 4, fval2->size());
- CPPUNIT_ASSERT_EQUAL(std::string("foo"),
- std::string((*fval2)[0].getAsString()));
- CPPUNIT_ASSERT_EQUAL(std::string("bar"),
- std::string((*fval2)[1].getAsString()));
- CPPUNIT_ASSERT_EQUAL(std::string("another"),
- std::string((*fval2)[2].getAsString()));
- CPPUNIT_ASSERT_EQUAL(std::string("tag"),
- std::string((*fval2)[3].getAsString()));
+ CPPUNIT_ASSERT_EQUAL(std::string("foo"), std::string((*fval2)[0].getAsString()));
+ CPPUNIT_ASSERT_EQUAL(std::string("bar"), std::string((*fval2)[1].getAsString()));
+ CPPUNIT_ASSERT_EQUAL(std::string("another"), std::string((*fval2)[2].getAsString()));
+ CPPUNIT_ASSERT_EQUAL(std::string("tag"), std::string((*fval2)[3].getAsString()));
// Append single value.
try {
- DocumentUpdate(*doc->getDataType(), doc->getId())
+ DocumentUpdate(docMan.getTypeRepo(), *doc->getDataType(), doc->getId())
.addUpdate(FieldUpdate(doc->getField("tags"))
- .addUpdate(AssignValueUpdate(
- StringFieldValue("THROW MEH!"))))
+ .addUpdate(AssignValueUpdate(StringFieldValue("THROW MEH!"))))
.applyTo(*doc);
CPPUNIT_FAIL("Expected exception when assinging a string value to an "
"array field.");
@@ -373,25 +340,22 @@ DocumentUpdateTest::testUpdateArray()
}
// Remove array field.
- DocumentUpdate(*doc->getDataType(), doc->getId())
+ DocumentUpdate(docMan.getTypeRepo(), *doc->getDataType(), doc->getId())
.addUpdate(FieldUpdate(doc->getField("tags"))
.addUpdate(RemoveValueUpdate(StringFieldValue("foo")))
.addUpdate(RemoveValueUpdate(StringFieldValue("tag"))))
.applyTo(*doc);
- std::unique_ptr<ArrayFieldValue>
- fval3(doc->getAs<ArrayFieldValue>(doc->getField("tags")));
+ auto fval3(doc->getAs<ArrayFieldValue>(doc->getField("tags")));
CPPUNIT_ASSERT_EQUAL((size_t) 2, fval3->size());
- CPPUNIT_ASSERT_EQUAL(std::string("bar"),
- std::string((*fval3)[0].getAsString()));
- CPPUNIT_ASSERT_EQUAL(std::string("another"),
- std::string((*fval3)[1].getAsString()));
+ CPPUNIT_ASSERT_EQUAL(std::string("bar"), std::string((*fval3)[0].getAsString()));
+ CPPUNIT_ASSERT_EQUAL(std::string("another"), std::string((*fval3)[1].getAsString()));
// Remove array from array.
ArrayFieldValue myarray2(doc->getType().getField("tags").getDataType());
myarray2.add(StringFieldValue("foo"));
myarray2.add(StringFieldValue("bar"));
try {
- DocumentUpdate(*doc->getDataType(), doc->getId())
+ DocumentUpdate(docMan.getTypeRepo(), *doc->getDataType(), doc->getId())
.addUpdate(FieldUpdate(doc->getField("tags"))
.addUpdate(RemoveValueUpdate(myarray2)))
.applyTo(*doc);
@@ -416,12 +380,10 @@ DocumentUpdateTest::testUpdateWeightedSet()
WeightedSetFieldValue wset(field.getDataType());
wset.add(StringFieldValue("foo"), 3);
wset.add(StringFieldValue("bar"), 14);
- DocumentUpdate(*doc->getDataType(), doc->getId())
- .addUpdate(FieldUpdate(field)
- .addUpdate(AssignValueUpdate(wset)))
+ DocumentUpdate(docMan.getTypeRepo(), *doc->getDataType(), doc->getId())
+ .addUpdate(FieldUpdate(field).addUpdate(AssignValueUpdate(wset)))
.applyTo(*doc);
- std::unique_ptr<WeightedSetFieldValue>
- fval1(doc->getAs<WeightedSetFieldValue>(field));
+ auto fval1(doc->getAs<WeightedSetFieldValue>(field));
CPPUNIT_ASSERT_EQUAL((size_t) 2, fval1->size());
CPPUNIT_ASSERT(fval1->contains(StringFieldValue("foo")));
CPPUNIT_ASSERT(fval1->find(StringFieldValue("foo")) != fval1->end());
@@ -434,12 +396,11 @@ DocumentUpdateTest::testUpdateWeightedSet()
WeightedSetFieldValue wset2(field.getDataType());
wset2.add(StringFieldValue("foo"), 16);
wset2.add(StringFieldValue("bar"), 24);
- DocumentUpdate(*doc->getDataType(), doc->getId())
+ DocumentUpdate(docMan.getTypeRepo(), *doc->getDataType(), doc->getId())
.addUpdate(FieldUpdate(field)
.addUpdate(AssignValueUpdate(wset2)))
.applyTo(*doc);
- std::unique_ptr<WeightedSetFieldValue>
- fval2(doc->getAs<WeightedSetFieldValue>(field));
+ auto fval2(doc->getAs<WeightedSetFieldValue>(field));
CPPUNIT_ASSERT_EQUAL((size_t) 2, fval2->size());
CPPUNIT_ASSERT(fval2->contains(StringFieldValue("foo")));
CPPUNIT_ASSERT(fval2->find(StringFieldValue("foo")) != fval1->end());
@@ -449,12 +410,10 @@ DocumentUpdateTest::testUpdateWeightedSet()
CPPUNIT_ASSERT_EQUAL(24, fval2->get(StringFieldValue("bar"), 0));
// Append weighted field
- DocumentUpdate(*doc->getDataType(), doc->getId())
+ DocumentUpdate(docMan.getTypeRepo(), *doc->getDataType(), doc->getId())
.addUpdate(FieldUpdate(field)
- .addUpdate(AddValueUpdate(StringFieldValue("foo"))
- .setWeight(3))
- .addUpdate(AddValueUpdate(StringFieldValue("too"))
- .setWeight(14)))
+ .addUpdate(AddValueUpdate(StringFieldValue("foo")).setWeight(3))
+ .addUpdate(AddValueUpdate(StringFieldValue("too")).setWeight(14)))
.applyTo(*doc);
std::unique_ptr<WeightedSetFieldValue>
fval3(doc->getAs<WeightedSetFieldValue>(field));
@@ -467,13 +426,12 @@ DocumentUpdateTest::testUpdateWeightedSet()
CPPUNIT_ASSERT_EQUAL(14, fval3->get(StringFieldValue("too"), 0));
// Remove weighted field
- DocumentUpdate(*doc->getDataType(), doc->getId())
+ DocumentUpdate(docMan.getTypeRepo(), *doc->getDataType(), doc->getId())
.addUpdate(FieldUpdate(field)
.addUpdate(RemoveValueUpdate(StringFieldValue("foo")))
.addUpdate(RemoveValueUpdate(StringFieldValue("too"))))
.applyTo(*doc);
- std::unique_ptr<WeightedSetFieldValue>
- fval4(doc->getAs<WeightedSetFieldValue>(field));
+ auto fval4(doc->getAs<WeightedSetFieldValue>(field));
CPPUNIT_ASSERT_EQUAL((size_t) 1, fval4->size());
CPPUNIT_ASSERT(!fval4->contains(StringFieldValue("foo")));
CPPUNIT_ASSERT(fval4->contains(StringFieldValue("bar")));
@@ -504,21 +462,18 @@ struct WeightedSetAutoCreateFixture
// and remove-if-zero attributes set. Attempting to explicitly create
// a field matching those characteristics will in fact fail with a
// redefinition error.
- builder.document(42, "test",
- Struct("test.header")
- .addField("strwset", DataType::T_TAG),
- Struct("test.body"));
+ builder.document(42, "test", Struct("test.header").addField("strwset", DataType::T_TAG), Struct("test.body"));
return builder.config();
}
};
-WeightedSetAutoCreateFixture::~WeightedSetAutoCreateFixture() {}
+WeightedSetAutoCreateFixture::~WeightedSetAutoCreateFixture() = default;
WeightedSetAutoCreateFixture::WeightedSetAutoCreateFixture()
: repo(makeConfig()),
docType(repo.getDocumentType("test")),
doc(*docType, DocumentId("doc::testdoc")),
field(docType->getField("strwset")),
- update(*docType, DocumentId("doc::testdoc"))
+ update(repo, *docType, DocumentId("doc::testdoc"))
{
update.addUpdate(FieldUpdate(field)
.addUpdate(MapValueUpdate(StringFieldValue("foo"),
@@ -551,8 +506,7 @@ DocumentUpdateTest::testIncrementExistingWSetField()
}
fixture.applyUpdateToDocument();
- std::unique_ptr<WeightedSetFieldValue> ws(
- fixture.doc.getAs<WeightedSetFieldValue>(fixture.field));
+ auto ws(fixture.doc.getAs<WeightedSetFieldValue>(fixture.field));
CPPUNIT_ASSERT_EQUAL(size_t(2), ws->size());
CPPUNIT_ASSERT(ws->contains(StringFieldValue("foo")));
CPPUNIT_ASSERT_EQUAL(1, ws->get(StringFieldValue("foo"), 0));
@@ -564,12 +518,11 @@ DocumentUpdateTest::testIncrementWithZeroResultWeightIsRemoved()
WeightedSetAutoCreateFixture fixture;
fixture.update.addUpdate(FieldUpdate(fixture.field)
.addUpdate(MapValueUpdate(StringFieldValue("baz"),
- ArithmeticValueUpdate(ArithmeticValueUpdate::Add, 0))));
+ ArithmeticValueUpdate(ArithmeticValueUpdate::Add, 0))));
fixture.applyUpdateToDocument();
- std::unique_ptr<WeightedSetFieldValue> ws(
- fixture.doc.getAs<WeightedSetFieldValue>(fixture.field));
+ auto ws(fixture.doc.getAs<WeightedSetFieldValue>(fixture.field));
CPPUNIT_ASSERT_EQUAL(size_t(1), ws->size());
CPPUNIT_ASSERT(ws->contains(StringFieldValue("foo")));
CPPUNIT_ASSERT(!ws->contains(StringFieldValue("baz")));
@@ -591,7 +544,8 @@ void DocumentUpdateTest::testReadSerializedFile()
}
close(fd);
- DocumentUpdate::UP updp(DocumentUpdate::create42(repo, buf));
+ nbostream is(buf.getBufferAtPos(), buf.getRemaining());
+ DocumentUpdate::UP updp(DocumentUpdate::create42(repo, is));
DocumentUpdate& upd(*updp);
const DocumentType *type = repo.getDocumentType("serializetest");
@@ -653,7 +607,7 @@ void DocumentUpdateTest::testGenerateSerializedFile()
DocumentTypeRepo repo(readDocumenttypesConfig(file_name));
const DocumentType *type(repo.getDocumentType("serializetest"));
- DocumentUpdate upd(*type, DocumentId(DocIdString("update", "test")));
+ DocumentUpdate upd(repo, *type, DocumentId(DocIdString("update", "test")));
upd.addUpdate(FieldUpdate(type->getField("intfield"))
.addUpdate(AssignValueUpdate(IntFieldValue(4))));
upd.addUpdate(FieldUpdate(type->getField("floatfield"))
@@ -671,10 +625,9 @@ void DocumentUpdateTest::testGenerateSerializedFile()
ArithmeticValueUpdate(ArithmeticValueUpdate::Mul, 2))));
ByteBuffer::UP buf(serialize42(upd));
- int fd = open(TEST_PATH("data/serializeupdatecpp.dat").c_str(),
- O_WRONLY | O_TRUNC | O_CREAT, 0644);
+ int fd = open(TEST_PATH("data/serializeupdatecpp.dat").c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0644);
if (write(fd, buf->getBuffer(), buf->getPos()) != (ssize_t)buf->getPos()) {
- throw vespalib::Exception("read failed");
+ throw vespalib::Exception("read failed");
}
close(fd);
}
@@ -685,11 +638,10 @@ void DocumentUpdateTest::testSetBadFieldTypes()
// Create a test document
TestDocMan docMan;
Document::UP doc(docMan.createDocument());
- CPPUNIT_ASSERT_EQUAL((document::FieldValue*)NULL,
- doc->getValue(doc->getField("headerval")).get());
+ CPPUNIT_ASSERT_EQUAL((document::FieldValue*)NULL, doc->getValue(doc->getField("headerval")).get());
// Assign a float value to an int field.
- DocumentUpdate update(*doc->getDataType(), doc->getId());
+ DocumentUpdate update(docMan.getTypeRepo(), *doc->getDataType(), doc->getId());
try {
update.addUpdate(FieldUpdate(doc->getField("headerval"))
.addUpdate(AssignValueUpdate(FloatFieldValue(4.00f))));
@@ -698,7 +650,6 @@ void DocumentUpdateTest::testSetBadFieldTypes()
; // fprintf(stderr, "Got exception => OK: %s\n", e.what());
}
- // Apply update
update.applyTo(*doc);
// Verify that the field is NOT set in the document.
@@ -709,23 +660,19 @@ void DocumentUpdateTest::testSetBadFieldTypes()
void
DocumentUpdateTest::testUpdateApplyNoParams()
{
- // Create a test document
TestDocMan docMan;
Document::UP doc(docMan.createDocument());
- CPPUNIT_ASSERT_EQUAL((document::FieldValue*)NULL,
- doc->getValue(doc->getField("tags")).get());
+ CPPUNIT_ASSERT_EQUAL((document::FieldValue*)NULL, doc->getValue(doc->getField("tags")).get());
// Assign array field with no parameters - illegal.
- DocumentUpdate update(*doc->getDataType(), doc->getId());
+ DocumentUpdate update(docMan.getTypeRepo(), *doc->getDataType(), doc->getId());
try {
- update.addUpdate(FieldUpdate(doc->getField("tags"))
- .addUpdate(AssignValueUpdate()));
+ update.addUpdate(FieldUpdate(doc->getField("tags")).addUpdate(AssignValueUpdate()));
CPPUNIT_FAIL("Expected exception when assign a NULL value.");
} catch (std::exception& e) {
; // fprintf(stderr, "Got exception => OK: %s\n", e.what());
}
- // Apply update
update.applyTo(*doc);
// Verify that the field was cleared in the document.
@@ -735,20 +682,16 @@ DocumentUpdateTest::testUpdateApplyNoParams()
void
DocumentUpdateTest::testUpdateApplyNoArrayValues()
{
- // Create a test document
TestDocMan docMan;
Document::UP doc(docMan.createDocument());
const Field &field(doc->getType().getField("tags"));
- CPPUNIT_ASSERT_EQUAL((document::FieldValue*) 0,
- doc->getValue(field).get());
+ CPPUNIT_ASSERT_EQUAL((document::FieldValue*) 0, doc->getValue(field).get());
// Assign array field with no array values = empty array
- DocumentUpdate update(*doc->getDataType(), doc->getId());
+ DocumentUpdate update(docMan.getTypeRepo(), *doc->getDataType(), doc->getId());
update.addUpdate(FieldUpdate(field)
- .addUpdate(AssignValueUpdate(
- ArrayFieldValue(field.getDataType()))));
+ .addUpdate(AssignValueUpdate(ArrayFieldValue(field.getDataType()))));
- // Apply update
update.applyTo(*doc);
// Verify that the field was set in the document
@@ -767,7 +710,7 @@ DocumentUpdateTest::testUpdateArrayEmptyParamValue()
CPPUNIT_ASSERT_EQUAL((document::FieldValue*) 0, doc->getValue(field).get());
// Assign array field with no array values = empty array.
- DocumentUpdate update(*doc->getDataType(), doc->getId());
+ DocumentUpdate update(docMan.getTypeRepo(), *doc->getDataType(), doc->getId());
update.addUpdate(FieldUpdate(field).addUpdate(AssignValueUpdate(ArrayFieldValue(field.getDataType()))));
update.applyTo(*doc);
@@ -777,7 +720,7 @@ DocumentUpdateTest::testUpdateArrayEmptyParamValue()
CPPUNIT_ASSERT_EQUAL((size_t) 0, fval1->size());
// Remove array field.
- DocumentUpdate update2(*doc->getDataType(), doc->getId());
+ DocumentUpdate update2(docMan.getTypeRepo(), *doc->getDataType(), doc->getId());
update2.addUpdate(FieldUpdate(field).addUpdate(ClearValueUpdate()));
update2.applyTo(*doc);
@@ -796,7 +739,7 @@ DocumentUpdateTest::testUpdateWeightedSetEmptyParamValue()
CPPUNIT_ASSERT_EQUAL((document::FieldValue*) 0, doc->getValue(field).get());
// Assign weighted set with no items = empty set.
- DocumentUpdate update(*doc->getDataType(), doc->getId());
+ DocumentUpdate update(docMan.getTypeRepo(), *doc->getDataType(), doc->getId());
update.addUpdate(FieldUpdate(field).addUpdate(AssignValueUpdate(WeightedSetFieldValue(field.getDataType()))));
update.applyTo(*doc);
@@ -806,7 +749,7 @@ DocumentUpdateTest::testUpdateWeightedSetEmptyParamValue()
CPPUNIT_ASSERT_EQUAL((size_t) 0, fval1->size());
// Remove weighted set field.
- DocumentUpdate update2(*doc->getDataType(), doc->getId());
+ DocumentUpdate update2(docMan.getTypeRepo(), *doc->getDataType(), doc->getId());
update2.addUpdate(FieldUpdate(field).addUpdate(ClearValueUpdate()));
update2.applyTo(*doc);
@@ -825,7 +768,7 @@ DocumentUpdateTest::testUpdateArrayWrongSubtype()
CPPUNIT_ASSERT_EQUAL((document::FieldValue*) 0, doc->getValue(field).get());
// Assign int values to string array.
- DocumentUpdate update(*doc->getDataType(), doc->getId());
+ DocumentUpdate update(docMan.getTypeRepo(), *doc->getDataType(), doc->getId());
try {
update.addUpdate(FieldUpdate(field)
.addUpdate(AddValueUpdate(IntFieldValue(123)))
@@ -850,17 +793,14 @@ DocumentUpdateTest::testUpdateWeightedSetWrongSubtype()
TestDocMan docMan;
Document::UP doc(docMan.createDocument());
const Field &field(doc->getType().getField("stringweightedset"));
- CPPUNIT_ASSERT_EQUAL((document::FieldValue*) 0,
- doc->getValue(field).get());
+ CPPUNIT_ASSERT_EQUAL((document::FieldValue*) 0, doc->getValue(field).get());
// Assign int values to string array.
- DocumentUpdate update(*doc->getDataType(), doc->getId());
+ DocumentUpdate update(docMan.getTypeRepo(), *doc->getDataType(), doc->getId());
try {
update.addUpdate(FieldUpdate(field)
- .addUpdate(AddValueUpdate(IntFieldValue(123))
- .setWeight(1000))
- .addUpdate(AddValueUpdate(IntFieldValue(456))
- .setWeight(2000)));
+ .addUpdate(AddValueUpdate(IntFieldValue(123)).setWeight(1000))
+ .addUpdate(AddValueUpdate(IntFieldValue(456)).setWeight(2000)));
CPPUNIT_FAIL("Expected exception when adding wrong type.");
} catch (std::exception& e) {
; // fprintf(stderr, "Got exception => OK: %s\n", e.what());
@@ -887,79 +827,61 @@ DocumentUpdateTest::testMapValueUpdate()
doc->setValue(field1, wsval1);
doc->setValue(field2, wsval2);
- DocumentUpdate(*doc->getDataType(), doc->getId())
+ DocumentUpdate(docMan.getTypeRepo(), *doc->getDataType(), doc->getId())
.addUpdate(FieldUpdate(field1)
- .addUpdate(MapValueUpdate(
- StringFieldValue("banana"),
- ArithmeticValueUpdate(ArithmeticValueUpdate::Add, 1.0)
- )))
+ .addUpdate(MapValueUpdate(StringFieldValue("banana"),
+ ArithmeticValueUpdate(ArithmeticValueUpdate::Add, 1.0))))
.applyTo(*doc);
std::unique_ptr<WeightedSetFieldValue> fv1 =
doc->getAs<WeightedSetFieldValue>(field1);
CPPUNIT_ASSERT(fv1->size() == 0);
- DocumentUpdate(*doc->getDataType(), doc->getId())
+ DocumentUpdate(docMan.getTypeRepo(), *doc->getDataType(), doc->getId())
.addUpdate(FieldUpdate(field2)
- .addUpdate(MapValueUpdate(
- StringFieldValue("banana"),
- ArithmeticValueUpdate(ArithmeticValueUpdate::Add, 1.0)
- )))
+ .addUpdate(MapValueUpdate(StringFieldValue("banana"),
+ ArithmeticValueUpdate(ArithmeticValueUpdate::Add, 1.0))))
.applyTo(*doc);
- std::unique_ptr<WeightedSetFieldValue> fv2 =
- doc->getAs<WeightedSetFieldValue>(field2);
+ auto fv2 = doc->getAs<WeightedSetFieldValue>(field2);
CPPUNIT_ASSERT(fv2->size() == 1);
CPPUNIT_ASSERT(fv1->find(StringFieldValue("apple")) == fv1->end());
- DocumentUpdate(*doc->getDataType(), doc->getId())
- .addUpdate(FieldUpdate(field1)
- .addUpdate(ClearValueUpdate()))
+ DocumentUpdate(docMan.getTypeRepo(), *doc->getDataType(), doc->getId())
+ .addUpdate(FieldUpdate(field1).addUpdate(ClearValueUpdate()))
.applyTo(*doc);
- DocumentUpdate(*doc->getDataType(), doc->getId())
- .addUpdate(FieldUpdate(field1)
- .addUpdate(AddValueUpdate(StringFieldValue("apple"))
- .setWeight(1)))
+ DocumentUpdate(docMan.getTypeRepo(), *doc->getDataType(), doc->getId())
+ .addUpdate(FieldUpdate(field1).addUpdate(AddValueUpdate(StringFieldValue("apple")).setWeight(1)))
.applyTo(*doc);
- std::unique_ptr<WeightedSetFieldValue>
- fval3(doc->getAs<WeightedSetFieldValue>(field1));
+ auto fval3(doc->getAs<WeightedSetFieldValue>(field1));
CPPUNIT_ASSERT(fval3->find(StringFieldValue("apple")) != fval3->end());
CPPUNIT_ASSERT_EQUAL(1, fval3->get(StringFieldValue("apple")));
- DocumentUpdate(*doc->getDataType(), doc->getId())
- .addUpdate(FieldUpdate(field2)
- .addUpdate(AddValueUpdate(StringFieldValue("apple"))
- .setWeight(1)))
+ DocumentUpdate(docMan.getTypeRepo(), *doc->getDataType(), doc->getId())
+ .addUpdate(FieldUpdate(field2).addUpdate(AddValueUpdate(StringFieldValue("apple")).setWeight(1)))
.applyTo(*doc);
- std::unique_ptr<WeightedSetFieldValue>
- fval3b(doc->getAs<WeightedSetFieldValue>(field2));
+ auto fval3b(doc->getAs<WeightedSetFieldValue>(field2));
CPPUNIT_ASSERT(fval3b->find(StringFieldValue("apple")) != fval3b->end());
CPPUNIT_ASSERT_EQUAL(1, fval3b->get(StringFieldValue("apple")));
- DocumentUpdate(*doc->getDataType(), doc->getId())
+ DocumentUpdate(docMan.getTypeRepo(), *doc->getDataType(), doc->getId())
.addUpdate(FieldUpdate(field1)
- .addUpdate(MapValueUpdate(
- StringFieldValue("apple"),
- ArithmeticValueUpdate(ArithmeticValueUpdate::Sub, 1.0)
- )))
+ .addUpdate(MapValueUpdate(StringFieldValue("apple"),
+ ArithmeticValueUpdate(ArithmeticValueUpdate::Sub, 1.0))))
.applyTo(*doc);
- std::unique_ptr<WeightedSetFieldValue> fv3 =
- doc->getAs<WeightedSetFieldValue>(field1);
+ auto fv3 = doc->getAs<WeightedSetFieldValue>(field1);
CPPUNIT_ASSERT(fv3->find(StringFieldValue("apple")) != fv3->end());
CPPUNIT_ASSERT_EQUAL(0, fv3->get(StringFieldValue("apple")));
- DocumentUpdate(*doc->getDataType(), doc->getId())
+ DocumentUpdate(docMan.getTypeRepo(), *doc->getDataType(), doc->getId())
.addUpdate(FieldUpdate(field2)
- .addUpdate(MapValueUpdate(
- StringFieldValue("apple"),
- ArithmeticValueUpdate(ArithmeticValueUpdate::Sub, 1.0)
- )))
+ .addUpdate(MapValueUpdate(StringFieldValue("apple"),
+ ArithmeticValueUpdate(ArithmeticValueUpdate::Sub, 1.0))))
.applyTo(*doc);
- std::unique_ptr<WeightedSetFieldValue> fv4 =
- doc->getAs<WeightedSetFieldValue>(field2);
+ auto fv4 = doc->getAs<WeightedSetFieldValue>(field2);
CPPUNIT_ASSERT(fv4->find(StringFieldValue("apple")) == fv4->end());
}
@@ -973,9 +895,8 @@ DocumentUpdateTest::testTensorAssignUpdate()
Document updated(*doc);
FieldValue::UP new_value(createTensorFieldValue());
testValueUpdate(AssignValueUpdate(*new_value), *DataType::TENSOR);
- DocumentUpdate upd(*doc->getDataType(), doc->getId());
- upd.addUpdate(FieldUpdate(upd.getType().getField("tensor")).
- addUpdate(AssignValueUpdate(*new_value)));
+ DocumentUpdate upd(docMan.getTypeRepo(), *doc->getDataType(), doc->getId());
+ upd.addUpdate(FieldUpdate(upd.getType().getField("tensor")).addUpdate(AssignValueUpdate(*new_value)));
upd.applyTo(updated);
FieldValue::UP fval(updated.getValue("tensor"));
CPPUNIT_ASSERT(fval);
@@ -991,9 +912,8 @@ DocumentUpdateTest::testTensorClearUpdate()
Document updated(*doc);
updated.setValue(updated.getField("tensor"), *createTensorFieldValue());
CPPUNIT_ASSERT(*doc != updated);
- DocumentUpdate upd(*doc->getDataType(), doc->getId());
- upd.addUpdate(FieldUpdate(upd.getType().getField("tensor")).
- addUpdate(ClearValueUpdate()));
+ DocumentUpdate upd(docMan.getTypeRepo(), *doc->getDataType(), doc->getId());
+ upd.addUpdate(FieldUpdate(upd.getType().getField("tensor")).addUpdate(ClearValueUpdate()));
upd.applyTo(updated);
CPPUNIT_ASSERT(!updated.getValue("tensor"));
CPPUNIT_ASSERT(*doc == updated);
@@ -1044,12 +964,11 @@ struct CreateIfNonExistentFixture
CreateIfNonExistentFixture();
};
-CreateIfNonExistentFixture::~CreateIfNonExistentFixture() {}
+CreateIfNonExistentFixture::~CreateIfNonExistentFixture() = default;
CreateIfNonExistentFixture::CreateIfNonExistentFixture()
: docMan(),
document(docMan.createDocument()),
- update(new DocumentUpdate(*document->getDataType(),
- document->getId()))
+ update(new DocumentUpdate(docMan.getTypeRepo(), *document->getDataType(), document->getId()))
{
update->addUpdate(FieldUpdate(document->getField("headerval"))
.addUpdate(AssignValueUpdate(IntFieldValue(1))));
@@ -1077,7 +996,8 @@ DocumentUpdateTest::testThatCreateIfNonExistentFlagIsSerializedAndDeserialized()
ByteBuffer::UP buf(serialize42(*f.update));
buf->flip();
- DocumentUpdate::UP deserialized = DocumentUpdate::create42(f.docMan.getTypeRepo(), *buf);
+ nbostream is(buf->getBufferAtPos(), buf->getRemaining());
+ auto deserialized = DocumentUpdate::create42(f.docMan.getTypeRepo(), is);
CPPUNIT_ASSERT_EQUAL(*f.update, *deserialized);
CPPUNIT_ASSERT(deserialized->getCreateIfNonExistent());
}
@@ -1097,7 +1017,7 @@ ArrayUpdateFixture::ArrayUpdateFixture()
doc(doc_man.createDocument()),
array_field(doc->getType().getField("tags")) // of type array<string>
{
- update = std::make_unique<DocumentUpdate>(*doc->getDataType(), doc->getId());
+ update = std::make_unique<DocumentUpdate>(doc_man.getTypeRepo(), *doc->getDataType(), doc->getId());
update->addUpdate(FieldUpdate(array_field)
.addUpdate(MapValueUpdate(IntFieldValue(1),
AssignValueUpdate(StringFieldValue("bar")))));
@@ -1132,4 +1052,17 @@ void DocumentUpdateTest::array_element_update_applies_to_specified_element() {
CPPUNIT_ASSERT_EQUAL(vespalib::string("blarg"), (*result_array)[2].getAsString());
}
+void DocumentUpdateTest::array_element_update_for_invalid_index_is_ignored() {
+ ArrayUpdateFixture f;
+
+ ArrayFieldValue array_value(f.array_field.getDataType());
+ array_value.add("jerry");
+ f.doc->setValue(f.array_field, array_value);
+
+ f.update->applyTo(*f.doc); // MapValueUpdate for index 1, which does not exist
+
+ auto result_array = f.doc->getAs<ArrayFieldValue>(f.array_field);
+ CPPUNIT_ASSERT_EQUAL(array_value, *result_array);
+}
+
} // namespace document
diff --git a/document/src/tests/fieldpathupdatetestcase.cpp b/document/src/tests/fieldpathupdatetestcase.cpp
index b240f322e4b..fb3ba3f7e40 100644
--- a/document/src/tests/fieldpathupdatetestcase.cpp
+++ b/document/src/tests/fieldpathupdatetestcase.cpp
@@ -69,6 +69,7 @@ struct FieldPathUpdateTestCase : public CppUnit::TestFixture {
void testSerializeAssignMath();
void testReadSerializedFile();
void testGenerateSerializedFile();
+ void array_element_update_for_invalid_index_is_ignored();
CPPUNIT_TEST_SUITE(FieldPathUpdateTestCase);
CPPUNIT_TEST(testWhereClause);
@@ -108,6 +109,7 @@ struct FieldPathUpdateTestCase : public CppUnit::TestFixture {
CPPUNIT_TEST(testSerializeAssignMath);
CPPUNIT_TEST(testReadSerializedFile);
CPPUNIT_TEST(testGenerateSerializedFile);
+ CPPUNIT_TEST(array_element_update_for_invalid_index_is_ignored);
CPPUNIT_TEST_SUITE_END();
private:
DocumentUpdate::UP
@@ -371,7 +373,7 @@ FieldPathUpdateTestCase::testRemoveField()
doc->setValue("strfoo", StringFieldValue("cocacola"));
CPPUNIT_ASSERT_EQUAL(vespalib::string("cocacola"), doc->getValue("strfoo")->getAsString());
//doc->print(std::cerr, true, "");
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
docUp.addFieldPathUpdate(FieldPathUpdate::CP(new RemoveFieldPathUpdate("strfoo")));
docUp.applyTo(*doc);
CPPUNIT_ASSERT(doc->hasValue("strfoo") == false);
@@ -392,7 +394,7 @@ FieldPathUpdateTestCase::testApplyRemoveMultiList()
}
CPPUNIT_ASSERT(doc->hasValue("strarray"));
//doc->print(std::cerr, true, "");
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
docUp.addFieldPathUpdate(FieldPathUpdate::CP(
new RemoveFieldPathUpdate("strarray[$x]", "foobar.strarray[$x] == \"remove val 1\"")));
docUp.applyTo(*doc);
@@ -417,7 +419,7 @@ FieldPathUpdateTestCase::testApplyRemoveEntireListField()
doc->setValue("strarray", strArray);
}
//doc->print(std::cerr, true, "");
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
docUp.addFieldPathUpdate(FieldPathUpdate::CP(new RemoveFieldPathUpdate("strarray", "")));
docUp.applyTo(*doc);
CPPUNIT_ASSERT(!doc->hasValue("strarray"));
@@ -436,7 +438,7 @@ FieldPathUpdateTestCase::testApplyRemoveMultiWset()
}
CPPUNIT_ASSERT(doc->hasValue("strwset"));
//doc->print(std::cerr, true, "");
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
docUp.addFieldPathUpdate(FieldPathUpdate::CP(new RemoveFieldPathUpdate("strwset{remove val 1}")));
docUp.applyTo(*doc);
{
@@ -452,14 +454,14 @@ FieldPathUpdateTestCase::testApplyAssignSingle()
Document::UP doc(new Document(_foobar_type, DocumentId("doc:drekka:karsk")));
CPPUNIT_ASSERT(doc->hasValue("strfoo") == false);
// Test assignment of non-existing
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
docUp.addFieldPathUpdate(FieldPathUpdate::CP(
new AssignFieldPathUpdate(*doc->getDataType(), "strfoo", std::string(), StringFieldValue("himert"))));
docUp.applyTo(*doc);
CPPUNIT_ASSERT(doc->hasValue("strfoo"));
CPPUNIT_ASSERT_EQUAL(vespalib::string("himert"), doc->getValue("strfoo")->getAsString());
// Test overwriting existing
- DocumentUpdate docUp2(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp2(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
docUp2.addFieldPathUpdate(FieldPathUpdate::CP(
new AssignFieldPathUpdate(*doc->getDataType(), "strfoo", std::string(), StringFieldValue("wunderbaum"))));
docUp2.applyTo(*doc);
@@ -472,7 +474,7 @@ FieldPathUpdateTestCase::testApplyAssignMath()
Document::UP doc(new Document(_foobar_type, DocumentId("doc:bat:man")));
doc->setValue("num", IntFieldValue(34));
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
docUp.addFieldPathUpdate(FieldPathUpdate::CP(new AssignFieldPathUpdate("num", "", "($value * 2) / $value")));
docUp.applyTo(*doc);
CPPUNIT_ASSERT_EQUAL(static_cast<const FieldValue&>(IntFieldValue(2)), *doc->getValue("num"));
@@ -484,7 +486,7 @@ FieldPathUpdateTestCase::testApplyAssignMathByteToZero()
Document::UP doc(new Document(_foobar_type, DocumentId("doc:bat:man")));
doc->setValue("byteval", ByteFieldValue(3));
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
docUp.addFieldPathUpdate(FieldPathUpdate::CP(new AssignFieldPathUpdate("byteval", "", "$value - 3")));
docUp.applyTo(*doc);
CPPUNIT_ASSERT_EQUAL(static_cast<const FieldValue&>(ByteFieldValue(0)), *doc->getValue("byteval"));
@@ -497,7 +499,7 @@ FieldPathUpdateTestCase::testApplyAssignMathNotModifiedOnUnderflow()
Document::UP doc(new Document(_foobar_type, DocumentId("doc:bat:man")));
doc->setValue("byteval", ByteFieldValue(low_value));
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
docUp.addFieldPathUpdate(FieldPathUpdate::CP(new AssignFieldPathUpdate("byteval", "", "$value - 4")));
docUp.applyTo(*doc);
// Over/underflow will happen. You must have control of your data types.
@@ -510,7 +512,7 @@ FieldPathUpdateTestCase::testApplyAssignMathNotModifiedOnOverflow()
Document::UP doc(new Document(_foobar_type, DocumentId("doc:bat:man")));
doc->setValue("byteval", ByteFieldValue(127));
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
docUp.addFieldPathUpdate(FieldPathUpdate::CP(new AssignFieldPathUpdate("byteval", "", "$value + 200")));
docUp.applyTo(*doc);
// Over/underflow will happen. You must have control of your data types.
@@ -524,7 +526,7 @@ FieldPathUpdateTestCase::testApplyAssignMathDivZero()
CPPUNIT_ASSERT(doc->hasValue("num") == false);
doc->setValue("num", IntFieldValue(10));
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
docUp.addFieldPathUpdate(FieldPathUpdate::CP(new AssignFieldPathUpdate("num", "", "$value / ($value - 10)")));
docUp.applyTo(*doc);
CPPUNIT_ASSERT_EQUAL(static_cast<const FieldValue&>(IntFieldValue(10)), *doc->getValue("num"));
@@ -538,7 +540,7 @@ FieldPathUpdateTestCase::testApplyAssignFieldNotExistingInExpression()
CPPUNIT_ASSERT(doc->hasValue("num") == false);
doc->setValue("num", IntFieldValue(10));
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
docUp.addFieldPathUpdate(FieldPathUpdate::CP(new AssignFieldPathUpdate("num", "", "foobar.num2 + $value")));
docUp.applyTo(*doc);
CPPUNIT_ASSERT_EQUAL(static_cast<const FieldValue&>(IntFieldValue(10)), *doc->getValue("num"));
@@ -550,7 +552,7 @@ FieldPathUpdateTestCase::testApplyAssignFieldNotExistingInPath()
Document::UP doc(new Document(_foobar_type, DocumentId("doc:bat:man")));
doc->setRepo(*_repo);
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
try {
docUp.addFieldPathUpdate(FieldPathUpdate::CP(new AssignFieldPathUpdate("nosuchnum", "", "foobar.num + $value")));
docUp.applyTo(*doc);
@@ -565,7 +567,7 @@ FieldPathUpdateTestCase::testApplyAssignTargetNotExisting()
Document::UP doc(new Document(_foobar_type, DocumentId("doc:bat:man")));
CPPUNIT_ASSERT(doc->hasValue("num") == false);
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
docUp.addFieldPathUpdate(FieldPathUpdate::CP(new AssignFieldPathUpdate("num", "", "$value + 5")));
docUp.applyTo(*doc);
CPPUNIT_ASSERT_EQUAL(static_cast<const FieldValue&>(IntFieldValue(5)), *doc->getValue("num"));
@@ -582,7 +584,7 @@ FieldPathUpdateTestCase::testAssignSimpleMapValueWithVariable()
mfv.put(StringFieldValue("baz"), StringFieldValue("bananas"));
doc->setValue("strmap", mfv);
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
// Select on value, not key
docUp.addFieldPathUpdate(FieldPathUpdate::CP(
new AssignFieldPathUpdate(*doc->getDataType(),
@@ -608,7 +610,7 @@ FieldPathUpdateTestCase::testApplyAssignMathRemoveIfZero()
doc->setValue("num", IntFieldValue(34));
CPPUNIT_ASSERT(doc->hasValue("num") == true);
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
FieldPathUpdate::CP up1(new AssignFieldPathUpdate("num", "", "($value * 2) / $value - 2"));
static_cast<AssignFieldPathUpdate&>(*up1).setRemoveIfZero(true);
docUp.addFieldPathUpdate(up1);
@@ -635,7 +637,7 @@ FieldPathUpdateTestCase::testApplyAssignMultiList()
updateArray.add(StringFieldValue("assigned val 0"));
updateArray.add(StringFieldValue("assigned val 1"));
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
docUp.addFieldPathUpdate(FieldPathUpdate::CP(
new AssignFieldPathUpdate(*doc->getDataType(), "strarray", std::string(), updateArray)));
docUp.applyTo(*doc);
@@ -667,7 +669,7 @@ FieldPathUpdateTestCase::testApplyAssignMultiWset()
assignWset.add(StringFieldValue("assigned val 0"), 5);
assignWset.add(StringFieldValue("assigned val 1"), 10);
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
docUp.addFieldPathUpdate(FieldPathUpdate::CP(
new AssignFieldPathUpdate(*doc->getDataType(), "strwset", std::string(), assignWset)));
//doc->print(std::cerr, true, "");
@@ -696,7 +698,7 @@ FieldPathUpdateTestCase::testAssignWsetRemoveIfZero()
}
{
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
IntFieldValue zeroWeight(0);
FieldPathUpdate::CP assignUpdate(
new AssignFieldPathUpdate(*doc->getDataType(), "strwset{you say goodbye}", std::string(), zeroWeight));
@@ -724,7 +726,7 @@ FieldPathUpdateTestCase::testApplyAddMultiList()
adds.add(StringFieldValue("a festivus for the rest of us"));
adds.add(StringFieldValue("george is getting upset!"));
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
docUp.addFieldPathUpdate(FieldPathUpdate::CP(
new AddFieldPathUpdate(*doc->getDataType(), "strarray", std::string(), adds)));
//doc->print(std::cerr, true, "");
@@ -747,7 +749,7 @@ FieldPathUpdateTestCase::testAddAndAssignList()
CPPUNIT_ASSERT(doc->hasValue("strarray"));
}
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
docUp.addFieldPathUpdate(FieldPathUpdate::CP(
new AssignFieldPathUpdate(*doc->getDataType(),
"strarray[1]", std::string(), StringFieldValue("assigned val 1"))));
@@ -829,7 +831,7 @@ FieldPathUpdateTestCase::testAssignMap()
Keys k;
Fixture f(_foobar_type, k);
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
docUp.addFieldPathUpdate(FieldPathUpdate::CP(
new AssignFieldPathUpdate(*f.doc->getDataType(), "structmap{" + k.key2 + "}", std::string(), f.fv4)));
docUp.applyTo(*f.doc);
@@ -850,7 +852,7 @@ FieldPathUpdateTestCase::testAssignMapStruct()
Keys k;
Fixture f(_foobar_type, k);
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
docUp.addFieldPathUpdate(FieldPathUpdate::CP(
new AssignFieldPathUpdate(*f.doc->getDataType(), "structmap{" + k.key2 + "}.rating",
std::string(), IntFieldValue(48))));
@@ -872,7 +874,7 @@ FieldPathUpdateTestCase::testAssignMapStructVariable()
Keys k;
Fixture f(_foobar_type, k);
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
docUp.addFieldPathUpdate(FieldPathUpdate::CP(
new AssignFieldPathUpdate(*f.doc->getDataType(), "structmap{$x}.rating",
"foobar.structmap{$x}.title == \"farnsworth\"", IntFieldValue(48))));
@@ -899,7 +901,7 @@ FieldPathUpdateTestCase::testAssignMapNoExist()
fv1.setValue("title", StringFieldValue("fry"));
fv1.setValue("rating", IntFieldValue(30));
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
docUp.addFieldPathUpdate(FieldPathUpdate::CP(
new AssignFieldPathUpdate(*doc->getDataType(), "structmap{foo}", std::string(), fv1)));
//doc->print(std::cerr, true, "");
@@ -922,7 +924,7 @@ FieldPathUpdateTestCase::testAssignMapNoExistNoCreate()
fv1.setValue("title", StringFieldValue("fry"));
fv1.setValue("rating", IntFieldValue(30));
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
FieldPathUpdate::CP assignUpdate(
new AssignFieldPathUpdate(*doc->getDataType(), "structmap{foo}", std::string(), fv1));
static_cast<AssignFieldPathUpdate&>(*assignUpdate).setCreateMissingPath(false);
@@ -944,7 +946,7 @@ FieldPathUpdateTestCase::testQuotedStringKey()
const char field_path[] = "structmap{\"here is a \\\"fancy\\\" 'map' :-} key :-{\"}";
Fixture f(_foobar_type, k);
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
docUp.addFieldPathUpdate(FieldPathUpdate::CP(
new AssignFieldPathUpdate(*f.doc->getDataType(), field_path, std::string(), f.fv4)));
docUp.applyTo(*f.doc);
@@ -970,8 +972,8 @@ FieldPathUpdateTestCase::testEqualityComparison()
fv4.setValue("rating", IntFieldValue(95));
{
- DocumentUpdate docUp1(_foobar_type, DocumentId("doc:barbar:foofoo"));
- DocumentUpdate docUp2(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp1(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp2(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
CPPUNIT_ASSERT(docUp1 == docUp2);
FieldPathUpdate::CP assignUp1(new AssignFieldPathUpdate(*doc->getDataType(),
@@ -982,8 +984,8 @@ FieldPathUpdateTestCase::testEqualityComparison()
CPPUNIT_ASSERT(docUp1 == docUp2);
}
{
- DocumentUpdate docUp1(_foobar_type, DocumentId("doc:barbar:foofoo"));
- DocumentUpdate docUp2(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp1(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp2(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
// where-clause diff
FieldPathUpdate::CP assignUp1(new AssignFieldPathUpdate(*doc->getDataType(),
"structmap{here be dragons}", std::string(), fv4));
@@ -994,8 +996,8 @@ FieldPathUpdateTestCase::testEqualityComparison()
CPPUNIT_ASSERT(docUp1 != docUp2);
}
{
- DocumentUpdate docUp1(_foobar_type, DocumentId("doc:barbar:foofoo"));
- DocumentUpdate docUp2(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp1(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp2(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
// fieldpath diff
FieldPathUpdate::CP assignUp1(new AssignFieldPathUpdate(*doc->getDataType(),
"structmap{here be dragons}", std::string(), fv4));
@@ -1020,7 +1022,7 @@ FieldPathUpdateTestCase::testAffectsDocumentBody()
// structmap is body field
{
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
FieldPathUpdate::CP update1(new AssignFieldPathUpdate(*doc->getDataType(),
"structmap{janitor}", std::string(), fv4));
@@ -1030,7 +1032,7 @@ FieldPathUpdateTestCase::testAffectsDocumentBody()
// strfoo is header field
{
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
FieldPathUpdate::CP update1(new AssignFieldPathUpdate(*doc->getDataType(),
"strfoo", std::string(), StringFieldValue("helloworld")));
static_cast<AssignFieldPathUpdate&>(*update1).setCreateMissingPath(true);
@@ -1045,7 +1047,7 @@ FieldPathUpdateTestCase::testIncompatibleDataTypeFails()
Document::UP doc(new Document(_foobar_type, DocumentId("doc:things:stuff")));
MapFieldValue mfv(doc->getType().getField("structmap").getDataType());
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
try {
FieldPathUpdate::CP update1(new AssignFieldPathUpdate(*doc->getDataType(), "structmap{foo}",
@@ -1066,7 +1068,7 @@ FieldPathUpdateTestCase::testSerializeAssign()
val.setValue("title", StringFieldValue("cool frog"));
val.setValue("rating", IntFieldValue(100));
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
FieldPathUpdate::CP update1(new AssignFieldPathUpdate(*doc->getDataType(), "structmap{ribbit}", "true", val));
static_cast<AssignFieldPathUpdate&>(*update1).setCreateMissingPath(true);
@@ -1086,7 +1088,7 @@ FieldPathUpdateTestCase::testSerializeAdd()
adds.add(StringFieldValue("a festivus for the rest of us"));
adds.add(StringFieldValue("george is getting upset!"));
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
FieldPathUpdate::CP update1(new AddFieldPathUpdate(*doc->getDataType(), "strarray", std::string(), adds));
docUp.addFieldPathUpdate(update1);
@@ -1100,7 +1102,7 @@ FieldPathUpdateTestCase::testSerializeRemove()
Document::UP doc(new Document(_foobar_type, DocumentId("doc:weloveto:serializestuff")));
MapFieldValue mfv(doc->getType().getField("structmap").getDataType());
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
FieldPathUpdate::CP update1(new RemoveFieldPathUpdate("structmap{ribbit}", std::string()));
docUp.addFieldPathUpdate(update1);
@@ -1115,7 +1117,7 @@ FieldPathUpdateTestCase::testSerializeAssignMath()
CPPUNIT_ASSERT(doc->hasValue("num") == false);
doc->setValue("num", IntFieldValue(34));
- DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo"));
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("doc:barbar:foofoo"));
docUp.addFieldPathUpdate(FieldPathUpdate::CP(new AssignFieldPathUpdate("num", "", "($value * 2) / $value")));
testSerialize(*_repo, docUp);
}
@@ -1124,7 +1126,7 @@ DocumentUpdate::UP
FieldPathUpdateTestCase::createDocumentUpdateForSerialization(const DocumentTypeRepo& repo)
{
const DocumentType *docType(repo.getDocumentType("serializetest"));
- DocumentUpdate::UP docUp(new DocumentUpdate(*docType, DocumentId("doc:serialization:xlanguage")));
+ DocumentUpdate::UP docUp(new DocumentUpdate(repo, *docType, DocumentId("doc:serialization:xlanguage")));
FieldPathUpdate::CP assign(new AssignFieldPathUpdate("intfield", "", "3"));
static_cast<AssignFieldPathUpdate&>(*assign).setRemoveIfZero(true);
@@ -1183,4 +1185,23 @@ FieldPathUpdateTestCase::testGenerateSerializedFile()
close(fd);
}
+void FieldPathUpdateTestCase::array_element_update_for_invalid_index_is_ignored() {
+ auto doc = std::make_unique<Document>(_foobar_type, DocumentId("id::foobar::1"));
+ doc->setRepo(*_repo);
+ auto& field = doc->getType().getField("strarray");
+
+ ArrayFieldValue str_array(field.getDataType());
+ str_array.add(StringFieldValue("jerry"));
+ doc->setValue("strarray", str_array);
+
+ DocumentUpdate docUp(*_repo, _foobar_type, DocumentId("id::foobar::1"));
+ docUp.addFieldPathUpdate(FieldPathUpdate::CP(
+ new AssignFieldPathUpdate(*doc->getDataType(), "strarray[1]", "", StringFieldValue("george"))));
+ docUp.applyTo(*doc);
+
+ // Doc is unmodified.
+ auto new_arr = doc->getAs<ArrayFieldValue>(field);
+ CPPUNIT_ASSERT_EQUAL(str_array, *new_arr);
+}
+
}
diff --git a/document/src/tests/testxml.cpp b/document/src/tests/testxml.cpp
index 4cc1306ff45..09c2ef86279 100644
--- a/document/src/tests/testxml.cpp
+++ b/document/src/tests/testxml.cpp
@@ -73,7 +73,7 @@ createTestDocumentUpdate(const DocumentTypeRepo& repo)
const DocumentType* type(repo.getDocumentType("testdoc"));
DocumentId id("doc:crawler/http://www.ntnu.no/");
- DocumentUpdate::UP up(new DocumentUpdate(*type, id));
+ DocumentUpdate::UP up(new DocumentUpdate(repo, *type, id));
up->addUpdate(FieldUpdate(type->getField("intattr"))
.addUpdate(AssignValueUpdate(IntFieldValue(7))));
up->addUpdate(FieldUpdate(type->getField("stringattr"))
diff --git a/document/src/vespa/document/base/forcelink.cpp b/document/src/vespa/document/base/forcelink.cpp
index 8f683e40059..bc0097b68f4 100644
--- a/document/src/vespa/document/base/forcelink.cpp
+++ b/document/src/vespa/document/base/forcelink.cpp
@@ -13,7 +13,7 @@ ForceLink::ForceLink(void)
if (time(NULL) == 0) {
DocumentType type("foo", 1);
Document document(type, DocumentId("doc:ns:bar"));
- DocumentUpdate documentUpdate(type, DocumentId("doc:ns:bar"));
+ DocumentUpdate documentUpdate;
MapValueUpdate mapValueUpdate(IntFieldValue(3), ClearValueUpdate());
AddValueUpdate addValueUpdate(IntFieldValue(3));
RemoveValueUpdate removeValueUpdate(IntFieldValue(3));
diff --git a/document/src/vespa/document/base/globalid.cpp b/document/src/vespa/document/base/globalid.cpp
index 354e6ce67aa..5211647e848 100644
--- a/document/src/vespa/document/base/globalid.cpp
+++ b/document/src/vespa/document/base/globalid.cpp
@@ -7,6 +7,9 @@
#include <vespa/vespalib/stllike/hash_set.hpp>
#include <cassert>
+#include <vespa/log/log.h>
+LOG_SETUP(".document.base.globalid");
+
namespace {
bool
@@ -29,7 +32,7 @@ getHexVal(char c)
return (c - 'A' + 10);
}
assert(validateHex(c));
- abort();
+ LOG_ABORT("should not be reached");
}
}
diff --git a/document/src/vespa/document/datatype/datatype.cpp b/document/src/vespa/document/datatype/datatype.cpp
index 08a91e0df64..3b56942b61e 100644
--- a/document/src/vespa/document/datatype/datatype.cpp
+++ b/document/src/vespa/document/datatype/datatype.cpp
@@ -6,6 +6,7 @@
#include <vespa/document/datatype/documenttype.h>
#include <vespa/document/datatype/weightedsetdatatype.h>
#include <vespa/document/fieldvalue/fieldvalues.h>
+#include <vespa/document/base/exceptions.h>
#include <vespa/vespalib/text/lowercase.h>
#include <stdexcept>
@@ -153,9 +154,7 @@ DataType::DataType(const vespalib::stringref & name)
{
}
-DataType::~DataType()
-{
-}
+DataType::~DataType() = default;
bool
DataType::operator==(const DataType& other) const
@@ -179,4 +178,10 @@ DataType::buildFieldPath(FieldPath & path, const vespalib::stringref & remainFie
}
}
+const Field&
+DataType::getField(int fieldId) const
+{
+ throw FieldNotFoundException(fieldId, 7, VESPA_STRLOC);
+}
+
} // document
diff --git a/document/src/vespa/document/datatype/datatype.h b/document/src/vespa/document/datatype/datatype.h
index fae33ea2a42..247d72db665 100644
--- a/document/src/vespa/document/datatype/datatype.h
+++ b/document/src/vespa/document/datatype/datatype.h
@@ -129,6 +129,9 @@ public:
*/
void buildFieldPath(FieldPath & fieldPath, const vespalib::stringref & remainFieldName) const;
+ /** @throws FieldNotFoundException if field does not exist. */
+ virtual const Field& getField(int fieldId) const;
+
DECLARE_IDENTIFIABLE_ABSTRACT(DataType);
private:
virtual void onBuildFieldPath(FieldPath & fieldPath, const vespalib::stringref & remainFieldName) const = 0;
diff --git a/document/src/vespa/document/datatype/primitivedatatype.cpp b/document/src/vespa/document/datatype/primitivedatatype.cpp
index fef0e532e44..3755ebc370e 100644
--- a/document/src/vespa/document/datatype/primitivedatatype.cpp
+++ b/document/src/vespa/document/datatype/primitivedatatype.cpp
@@ -6,6 +6,9 @@
#include <vespa/vespalib/util/stringfmt.h>
#include <sstream>
+#include <vespa/log/log.h>
+LOG_SETUP(".document.datatype.primitivedatatype");
+
namespace document {
IMPLEMENT_IDENTIFIABLE_ABSTRACT(PrimitiveDataType, DataType);
@@ -64,10 +67,8 @@ PrimitiveDataType::createFieldValue() const
case T_BYTE: return FieldValue::UP(new ByteFieldValue);
case T_PREDICATE: return FieldValue::UP(new PredicateFieldValue);
case T_TENSOR: return std::make_unique<TensorFieldValue>();
- abort();
}
- assert(!"getId() returned value out of range");
- abort();
+ LOG_ABORT("getId() returned value out of range");
}
void
diff --git a/document/src/vespa/document/datatype/structureddatatype.h b/document/src/vespa/document/datatype/structureddatatype.h
index 1454f16d517..31de0d93680 100644
--- a/document/src/vespa/document/datatype/structureddatatype.h
+++ b/document/src/vespa/document/datatype/structureddatatype.h
@@ -30,9 +30,6 @@ public:
/** @throws FieldNotFoundException if field does not exist. */
virtual const Field& getField(const vespalib::stringref & name) const = 0;
- /** @throws FieldNotFoundException if field does not exist. */
- virtual const Field& getField(int fieldId) const = 0;
-
virtual bool hasField(const vespalib::stringref & name) const = 0;
virtual bool hasField(int32_t fieldId) const = 0;
diff --git a/document/src/vespa/document/fieldvalue/document.h b/document/src/vespa/document/fieldvalue/document.h
index 59a6f3d0a31..e0a35411f9b 100644
--- a/document/src/vespa/document/fieldvalue/document.h
+++ b/document/src/vespa/document/fieldvalue/document.h
@@ -41,23 +41,13 @@ public:
Document(const Document&);
Document(const DataType &, const DocumentId&);
Document(const DataType &, DocumentId &, bool iWillAllowSwap);
- Document(const DocumentTypeRepo& repo,
- ByteBuffer& buffer,
- const DataType *anticipatedType = 0);
- Document(const DocumentTypeRepo& repo,
- vespalib::nbostream& stream,
- const DataType *anticipatedType = 0);
+ Document(const DocumentTypeRepo& repo, ByteBuffer& buffer, const DataType *anticipatedType = 0);
+ Document(const DocumentTypeRepo& repo, vespalib::nbostream& stream, const DataType *anticipatedType = 0);
/**
Constructor to deserialize only document and type from a buffer. Only relevant if includeContent is false.
*/
- Document(const DocumentTypeRepo& repo,
- ByteBuffer& buffer,
- bool includeContent,
- const DataType *anticipatedType);
- Document(const DocumentTypeRepo& repo,
- ByteBuffer& header,
- ByteBuffer& body,
- const DataType *anticipatedType = 0);
+ Document(const DocumentTypeRepo& repo, ByteBuffer& buffer, bool includeContent, const DataType *anticipatedType);
+ Document(const DocumentTypeRepo& repo, ByteBuffer& header, ByteBuffer& body, const DataType *anticipatedType = 0);
~Document();
void setRepo(const DocumentTypeRepo & repo);
diff --git a/document/src/vespa/document/fieldvalue/stringfieldvalue.h b/document/src/vespa/document/fieldvalue/stringfieldvalue.h
index be32f007db4..14b67838787 100644
--- a/document/src/vespa/document/fieldvalue/stringfieldvalue.h
+++ b/document/src/vespa/document/fieldvalue/stringfieldvalue.h
@@ -7,15 +7,16 @@
*/
#pragma once
-#include <vespa/document/fieldvalue/literalfieldvalue.h>
+#include "literalfieldvalue.h"
#include <vespa/document/annotation/spantree.h>
#include <vespa/vespalib/stllike/hash_map.h>
-#include <vespa/document/repo/fixedtyperepo.h>
#include <vespa/vespalib/util/buffer.h>
#include <vespa/fastos/dynamiclibrary.h>
namespace document {
+
class FixedTypeRepo;
+class DocumentTypeRepo;
class StringFieldValue : public LiteralFieldValue<StringFieldValue, DataType::T_STRING, true> {
public:
diff --git a/document/src/vespa/document/repo/document_type_repo_factory.cpp b/document/src/vespa/document/repo/document_type_repo_factory.cpp
index 208b64dc77c..becf4face92 100644
--- a/document/src/vespa/document/repo/document_type_repo_factory.cpp
+++ b/document/src/vespa/document/repo/document_type_repo_factory.cpp
@@ -5,6 +5,9 @@
#include <vespa/document/config/config-documenttypes.h>
#include <iostream>
+#include <vespa/log/log.h>
+LOG_SETUP(".document.repo.document_type_repo_factory");
+
namespace document {
std::mutex DocumentTypeRepoFactory::_mutex;
@@ -21,8 +24,7 @@ public:
EmptyFactoryCheck::~EmptyFactoryCheck()
{
if (!DocumentTypeRepoFactory::empty()) {
- std::cerr << "DocumentTypeRepoFactory not empty at shutdown" << std::endl;
- abort();
+ LOG_ABORT("DocumentTypeRepoFactory not empty at shutdown");
}
}
diff --git a/document/src/vespa/document/repo/documenttyperepo.cpp b/document/src/vespa/document/repo/documenttyperepo.cpp
index 6bfae246c10..a2f5aeb8f0b 100644
--- a/document/src/vespa/document/repo/documenttyperepo.cpp
+++ b/document/src/vespa/document/repo/documenttyperepo.cpp
@@ -51,14 +51,14 @@ using DocumentTypeMap = internal::DocumentTypeMap;
namespace {
template <typename Container>
void DeleteContent(Container &c) {
- for (typename Container::iterator it = c.begin(); it != c.end(); ++it) {
- delete *it;
+ for (auto ptr : c) {
+ delete ptr;
}
}
template <typename Map>
void DeleteMapContent(Map &m) {
- for (typename Map::iterator it = m.begin(); it != m.end(); ++it) {
- delete it->second;
+ for (auto & entry : m) {
+ delete entry.second;
}
}
@@ -93,18 +93,14 @@ bool Repo::addDataType(const DataType &type) {
return false; // Redefinition of identical type is ok.
}
throw IllegalArgumentException(
- make_string("Redefinition of data type %d, \"%s\". "
- "Previously defined as \"%s\".",
- type.getId(), type.getName().c_str(),
- data_type->getName().c_str()));
+ make_string("Redefinition of data type %d, \"%s\". Previously defined as \"%s\".",
+ type.getId(), type.getName().c_str(), data_type->getName().c_str()));
}
const DataType *& data_type_by_name = _name_map[type.getName()];
if (data_type_by_name) {
throw IllegalArgumentException(
- make_string("Redefinition of data type \"%s\", with id %d."
- " Previously defined with id %d.",
- type.getName().c_str(), type.getId(),
- data_type_by_name->getId()));
+ make_string("Redefinition of data type \"%s\", with id %d. Previously defined with id %d.",
+ type.getName().c_str(), type.getId(), data_type_by_name->getId()));
}
data_type = &type;
data_type_by_name = &type;
@@ -158,8 +154,7 @@ public:
};
void AnnotationTypeRepo::inherit(const AnnotationTypeRepo &parent) {
- _annotation_types.insert(parent._annotation_types.begin(),
- parent._annotation_types.end());
+ _annotation_types.insert(parent._annotation_types.begin(), parent._annotation_types.end());
}
void AnnotationTypeRepo::addAnnotationType(AnnotationType::UP type) {
@@ -167,10 +162,8 @@ void AnnotationTypeRepo::addAnnotationType(AnnotationType::UP type) {
if (a_type) {
if (*type != *a_type) {
throw IllegalArgumentException(
- make_string("Redefinition of annotation type %d, \"%s\". "
- "Previously defined as \"%s\".",
- type->getId(), type->getName().c_str(),
- a_type->getName().c_str()));
+ make_string("Redefinition of annotation type %d, \"%s\". Previously defined as \"%s\".",
+ type->getId(), type->getName().c_str(), a_type->getName().c_str()));
}
} else {
a_type = type.get();
@@ -185,12 +178,9 @@ void AnnotationTypeRepo::setAnnotationDataType(int32_t id, const DataType &d) {
annotation_type->setDataType(d);
} else if (*(annotation_type->getDataType()) != d) {
throw IllegalArgumentException(
- make_string("Redefinition of annotation type %d, \"%s\" = '%s'. "
- "Previously defined as '%s'.",
- annotation_type->getId(),
- annotation_type->getName().c_str(),
- annotation_type->getDataType()->toString().c_str(),
- d.toString().c_str()));
+ make_string("Redefinition of annotation type %d, \"%s\" = '%s'. Previously defined as '%s'.",
+ annotation_type->getId(), annotation_type->getName().c_str(),
+ annotation_type->getDataType()->toString().c_str(), d.toString().c_str()));
}
}
@@ -214,24 +204,22 @@ struct DataTypeRepo {
};
namespace {
-void addAnnotationType(
- const DocumenttypesConfig::Documenttype::Annotationtype &type,
- AnnotationTypeRepo &annotations) {
+void addAnnotationType(const DocumenttypesConfig::Documenttype::Annotationtype &type, AnnotationTypeRepo &annotations)
+{
AnnotationType::UP a(new AnnotationType(type.id, type.name));
annotations.addAnnotationType(std::move(a));
}
-void addAnnotationTypes(
- const vector<DocumenttypesConfig::Documenttype::Annotationtype> &types,
- AnnotationTypeRepo &annotations) {
+void addAnnotationTypes(const vector<DocumenttypesConfig::Documenttype::Annotationtype> &types,
+ AnnotationTypeRepo &annotations) {
for (size_t i = 0; i < types.size(); ++i) {
addAnnotationType(types[i], annotations);
}
}
-void setAnnotationDataTypes(
- const vector<DocumenttypesConfig::Documenttype::Annotationtype> &types,
- AnnotationTypeRepo &annotations, const Repo &repo) {
+void setAnnotationDataTypes(const vector<DocumenttypesConfig::Documenttype::Annotationtype> &types,
+ AnnotationTypeRepo &annotations, const Repo &repo)
+{
for (size_t i = 0; i < types.size(); ++i) {
if (types[i].datatype == -1) {
continue;
@@ -243,11 +231,10 @@ void setAnnotationDataTypes(
typedef DocumenttypesConfig::Documenttype::Datatype Datatype;
-void addField(const Datatype::Sstruct::Field &field, const Repo &repo,
- StructDataType &struct_type, bool isHeaderField) {
+void addField(const Datatype::Sstruct::Field &field, const Repo &repo, StructDataType &struct_type, bool isHeaderField)
+{
LOG(spam, "Adding field %s to %s (header: %s)",
- field.name.c_str(), struct_type.getName().c_str(),
- isHeaderField ? "yes" : "no");
+ field.name.c_str(), struct_type.getName().c_str(), isHeaderField ? "yes" : "no");
const DataType &field_type = repo.findOrThrow(field.datatype);
struct_type.addField(Field(field.name, field.id, field_type, isHeaderField));
}
@@ -270,24 +257,20 @@ void addStruct(int32_t id, const Datatype::Sstruct &s, Repo &repo) {
} else if (name.rfind(".header") != std::string::npos) {
const DataType *existing = repo.lookup(name);
if (existing) {
- LOG(spam, "Reusing id %u from body struct since its fields "
- "have already been inserted",
- existing->getId());
+ LOG(spam, "Reusing id %u from body struct since its fields have already been inserted", existing->getId());
id = existing->getId();
}
useUglyStructHack = true;
}
- LOG(debug, "Adding struct type %s (%s) with id %u",
- s.name.c_str(), name.c_str(), id);
+ LOG(debug, "Adding struct type %s (%s) with id %u", s.name.c_str(), name.c_str(), id);
StructDataType::UP struct_type_ap;
StructDataType *struct_type;
const DataType *existing = repo.lookup(name);
if (useUglyStructHack && existing) {
LOG(spam, "Type %s already existed", name.c_str());
- const StructDataType& cdt =
- Identifiable::cast<const StructDataType&>(*existing);
+ const StructDataType& cdt = Identifiable::cast<const StructDataType&>(*existing);
struct_type = const_cast<StructDataType*>(&cdt);
} else {
const DataType *existing_retry = repo.lookup(id);
@@ -306,8 +289,7 @@ void addStruct(int32_t id, const Datatype::Sstruct &s, Repo &repo) {
}
struct_type->setCompressionConfig(
- CompressionConfig(type, s.compression.level,
- s.compression.threshold, s.compression.minsize));
+ CompressionConfig(type, s.compression.level, s.compression.threshold, s.compression.minsize));
for (size_t i = 0; i < s.field.size(); ++i) {
addField(s.field[i], repo, *struct_type, hasSuffix(s.name, ".header"));
@@ -321,8 +303,7 @@ void addArray(int32_t id, const Datatype::Array &a, Repo &repo) {
void addWset(int32_t id, const Datatype::Wset &w, Repo &repo) {
const DataType &key = repo.findOrThrow(w.key.id);
- repo.addDataType(DataType::UP(new WeightedSetDataType(
- key, w.createifnonexistent, w.removeifzero, id)));
+ repo.addDataType(DataType::UP(new WeightedSetDataType(key, w.createifnonexistent, w.removeifzero, id)));
}
void addMap(int32_t id, const Datatype::Map &m, Repo &repo) {
@@ -331,18 +312,15 @@ void addMap(int32_t id, const Datatype::Map &m, Repo &repo) {
repo.addDataType(DataType::UP(new MapDataType(key, value, id)));
}
-void addAnnotationRef(int32_t id, const Datatype::Annotationref &a, Repo &r,
- const AnnotationTypeRepo &annotations) {
+void addAnnotationRef(int32_t id, const Datatype::Annotationref &a, Repo &r, const AnnotationTypeRepo &annotations) {
const AnnotationType *type = annotations.lookup(a.annotation.id);
if (!type) {
- throw IllegalArgumentException(
- make_string("Unknown AnnotationType %d", a.annotation.id));
+ throw IllegalArgumentException(make_string("Unknown AnnotationType %d", a.annotation.id));
}
r.addDataType(DataType::UP(new AnnotationReferenceDataType(*type, id)));
}
-void addDataType(const Datatype &type, Repo &repo,
- const AnnotationTypeRepo &a_repo) {
+void addDataType(const Datatype &type, Repo &repo, const AnnotationTypeRepo &a_repo) {
switch (type.type) {
case Datatype::STRUCT:
return addStruct(type.id, type.sstruct, repo);
@@ -355,27 +333,24 @@ void addDataType(const Datatype &type, Repo &repo,
case Datatype::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", type.type, type.id));
}
}
-void addDataTypes(const vector<Datatype> &types, Repo &repo,
- const AnnotationTypeRepo &a_repo) {
+void addDataTypes(const vector<Datatype> &types, Repo &repo, const AnnotationTypeRepo &a_repo) {
for (size_t i = 0; i < types.size(); ++i) {
addDataType(types[i], repo, a_repo);
}
}
void addDocumentTypes(const DocumentTypeMap &type_map, Repo &repo) {
- for (DocumentTypeMap::const_iterator
- it = type_map.begin(); it != type_map.end(); ++it) {
- repo.addDataType(*it->second->doc_type);
+ for (const auto & entry : type_map) {
+ repo.addDataType(*entry.second->doc_type);
}
}
-void addDefaultDocument(DocumentTypeMap &type_map) {
+const DocumentType *
+addDefaultDocument(DocumentTypeMap &type_map) {
DataTypeRepo::UP data_types(new DataTypeRepo);
vector<const DataType *> default_types = DataType::getDefaultDataTypes();
for (size_t i = 0; i < default_types.size(); ++i) {
@@ -385,97 +360,85 @@ void addDefaultDocument(DocumentTypeMap &type_map) {
data_types->repo.addDataType(PositionDataType::getInstance());
data_types->doc_type = new DocumentType("document", 8);
- vector<const AnnotationType *> annotation_types(
- AnnotationType::getDefaultAnnotationTypes());
+ vector<const AnnotationType *> annotation_types(AnnotationType::getDefaultAnnotationTypes());
for(size_t i(0); i < annotation_types.size(); ++i) {
- data_types->annotations.addAnnotationType(
- AnnotationType::UP(new AnnotationType(*annotation_types[i])));
+ data_types->annotations.addAnnotationType(std::make_unique<AnnotationType>(*annotation_types[i]));
}
uint32_t typeId = data_types->doc_type->getId();
+ const DocumentType * docType = data_types->doc_type;
type_map[typeId] = data_types.release();
+ return docType;
}
const DataTypeRepo &lookupRepo(int32_t id, const DocumentTypeMap &type_map) {
DocumentTypeMap::const_iterator it = type_map.find(id);
if (it == type_map.end()) {
- throw IllegalArgumentException(
- make_string("Unable to find document type %d.", id));
+ throw IllegalArgumentException(make_string("Unable to find document type %d.", id));
}
return *it->second;
}
-void inheritDataTypes(
- const vector<DocumenttypesConfig::Documenttype::Inherits> &base_types,
- const DocumentTypeMap &type_map, Repo &repo) {
+void inheritDataTypes(const vector<DocumenttypesConfig::Documenttype::Inherits> &base_types,
+ const DocumentTypeMap &type_map, Repo &repo) {
repo.inherit(lookupRepo(DataType::T_DOCUMENT, type_map).repo);
for (size_t i = 0; i < base_types.size(); ++i) {
repo.inherit(lookupRepo(base_types[i].id, type_map).repo);
}
}
-void inheritAnnotationTypes(
- const vector<DocumenttypesConfig::Documenttype::Inherits> &base_types,
- const DocumentTypeMap &type_map, AnnotationTypeRepo &repo) {
+void inheritAnnotationTypes(const vector<DocumenttypesConfig::Documenttype::Inherits> &base_types,
+ const DocumentTypeMap &type_map, AnnotationTypeRepo &repo) {
repo.inherit(lookupRepo(DataType::T_DOCUMENT, type_map).annotations);
for (size_t i = 0; i < base_types.size(); ++i) {
repo.inherit(lookupRepo(base_types[i].id, type_map).annotations);
}
}
-void inheritDocumentTypes(
- const vector<DocumenttypesConfig::Documenttype::Inherits> &base_types,
- const DocumentTypeMap &type_map, DocumentType &doc_type) {
+void inheritDocumentTypes(const vector<DocumenttypesConfig::Documenttype::Inherits> &base_types,
+ const DocumentTypeMap &type_map, DocumentType &doc_type) {
for (size_t i = 0; i < base_types.size(); ++i) {
const DataTypeRepo &parent = lookupRepo(base_types[i].id, type_map);
doc_type.inherit(*parent.doc_type);
}
}
-DataTypeRepo::UP makeDataTypeRepo(
- const DocumentType &doc_type,
- const DocumentTypeMap &type_map) {
+DataTypeRepo::UP makeDataTypeRepo(const DocumentType &doc_type, const DocumentTypeMap &type_map) {
DataTypeRepo::UP data_types(new DataTypeRepo);
data_types->repo.inherit(lookupRepo(DataType::T_DOCUMENT, type_map).repo);
- data_types->annotations.inherit(
- lookupRepo(DataType::T_DOCUMENT, type_map).annotations);
+ data_types->annotations.inherit(lookupRepo(DataType::T_DOCUMENT, type_map).annotations);
data_types->doc_type = doc_type.clone();
return data_types;
}
void addFieldSet(const DocumenttypesConfig::Documenttype::FieldsetsMap & fsv, DocumentType &doc_type) {
- for (DocumenttypesConfig::Documenttype::FieldsetsMap::const_iterator it(fsv.begin()), mt(fsv.end()); it != mt; it++) {
- const DocumenttypesConfig::Documenttype::Fieldsets & fs(it->second);
+ for (const auto & entry : fsv) {
+ const DocumenttypesConfig::Documenttype::Fieldsets & fs(entry.second);
DocumentType::FieldSet::Fields fields;
for (size_t j(0); j < fs.fields.size(); j++) {
fields.insert(fs.fields[j]);
}
- doc_type.addFieldSet(it->first, fields);
+ doc_type.addFieldSet(entry.first, fields);
}
}
-void addReferenceTypes(
- const vector<DocumenttypesConfig::Documenttype::Referencetype> &ref_types,
- Repo& data_type_repo,
- const DocumentTypeMap& doc_type_map) {
+void addReferenceTypes(const vector<DocumenttypesConfig::Documenttype::Referencetype> &ref_types,
+ Repo& data_type_repo, const DocumentTypeMap& doc_type_map)
+{
for (const auto& ref_type : ref_types) {
const auto* target_doc_type = lookupRepo(ref_type.targetTypeId, doc_type_map).doc_type;
data_type_repo.addDataType(std::make_unique<ReferenceDataType>(*target_doc_type, ref_type.id));
}
}
-void configureDataTypeRepo(
- const DocumenttypesConfig::Documenttype &doc_type,
- DocumentTypeMap &type_map) {
+void configureDataTypeRepo(const DocumenttypesConfig::Documenttype &doc_type, DocumentTypeMap &type_map) {
DataTypeRepo *data_types = type_map[doc_type.id];
- inheritAnnotationTypes(
- doc_type.inherits, type_map, data_types->annotations);
+ inheritAnnotationTypes(doc_type.inherits, type_map, data_types->annotations);
addAnnotationTypes(doc_type.annotationtype, data_types->annotations);
inheritDataTypes(doc_type.inherits, type_map, data_types->repo);
addReferenceTypes(doc_type.referencetype, data_types->repo, type_map);
addDataTypes(doc_type.datatype, data_types->repo, data_types->annotations);
- setAnnotationDataTypes(doc_type.annotationtype, data_types->annotations,
- data_types->repo);
+ setAnnotationDataTypes(doc_type.annotationtype, data_types->annotations, data_types->repo);
inheritDocumentTypes(doc_type.inherits, type_map, *data_types->doc_type);
addFieldSet(doc_type.fieldsets, *data_types->doc_type);
}
@@ -483,39 +446,33 @@ void configureDataTypeRepo(
void addDataTypeRepo(DataTypeRepo::UP data_types, DocumentTypeMap &doc_types) {
DataTypeRepo *& p = doc_types[data_types->doc_type->getId()];
if (p) {
- LOG(warning, "Type repo already exists for id %d.",
- data_types->doc_type->getId());
+ LOG(warning, "Type repo already exists for id %d.", data_types->doc_type->getId());
throw IllegalArgumentException("Trying to redefine a document type.");
}
p = data_types.release();
}
-DataTypeRepo::UP makeSkeletonDataTypeRepo(
- const DocumenttypesConfig::Documenttype &type) {
+DataTypeRepo::UP makeSkeletonDataTypeRepo(const DocumenttypesConfig::Documenttype &type) {
DataTypeRepo::UP data_types(new DataTypeRepo);
- StructDataType::UP
- type_ap(new StructDataType(type.name + ".header", type.headerstruct));
+ auto type_ap = std::make_unique<StructDataType>(type.name + ".header", type.headerstruct);
data_types->doc_type = new DocumentType(type.name, type.id, *type_ap);
data_types->repo.addDataType(std::move(type_ap));
return data_types;
}
-void createAllDocumentTypes(const DocumenttypesConfig::DocumenttypeVector &t,
- DocumentTypeMap &type_map) {
+void createAllDocumentTypes(const DocumenttypesConfig::DocumenttypeVector &t, DocumentTypeMap &type_map) {
for (size_t i = 0; i < t.size(); ++i) {
addDataTypeRepo(makeSkeletonDataTypeRepo(t[i]), type_map);
}
}
void addAllDocumentTypesToRepos(DocumentTypeMap &type_map) {
- for (DocumentTypeMap::const_iterator
- it = type_map.begin(); it != type_map.end(); ++it) {
- addDocumentTypes(type_map, it->second->repo);
+ for (const auto & entry : type_map) {
+ addDocumentTypes(type_map, entry.second->repo);
}
}
-void configureAllRepos(const DocumenttypesConfig::DocumenttypeVector &t,
- DocumentTypeMap &type_map) {
+void configureAllRepos(const DocumenttypesConfig::DocumenttypeVector &t, DocumentTypeMap &type_map) {
for (size_t i = 0; i < t.size(); ++i) {
configureDataTypeRepo(t[i], type_map);
}
@@ -524,15 +481,15 @@ void configureAllRepos(const DocumenttypesConfig::DocumenttypeVector &t,
} // namespace
DocumentTypeRepo::DocumentTypeRepo() :
- _doc_types(std::make_unique<internal::DocumentTypeMap>())
+ _doc_types(std::make_unique<internal::DocumentTypeMap>()),
+ _default(addDefaultDocument(*_doc_types))
{
- addDefaultDocument(*_doc_types);
}
DocumentTypeRepo::DocumentTypeRepo(const DocumentType & type) :
- _doc_types(std::make_unique<internal::DocumentTypeMap>())
+ _doc_types(std::make_unique<internal::DocumentTypeMap>()),
+ _default(addDefaultDocument(*_doc_types))
{
- addDefaultDocument(*_doc_types);
try {
addDataTypeRepo(makeDataTypeRepo(type, *_doc_types), *_doc_types);
} catch (...) {
@@ -542,9 +499,9 @@ DocumentTypeRepo::DocumentTypeRepo(const DocumentType & type) :
}
DocumentTypeRepo::DocumentTypeRepo(const DocumenttypesConfig &config) :
- _doc_types(std::make_unique<internal::DocumentTypeMap>())
+ _doc_types(std::make_unique<internal::DocumentTypeMap>()),
+ _default(addDefaultDocument(*_doc_types))
{
- addDefaultDocument(*_doc_types);
try {
createAllDocumentTypes(config.documenttype, *_doc_types);
addAllDocumentTypesToRepos(*_doc_types);
@@ -559,14 +516,15 @@ DocumentTypeRepo::~DocumentTypeRepo() {
DeleteMapContent(*_doc_types);
}
-const DocumentType *DocumentTypeRepo::getDocumentType(int32_t type_id) const {
+const DocumentType *
+DocumentTypeRepo::getDocumentType(int32_t type_id) const {
const DataTypeRepo *repo = FindPtr(*_doc_types, type_id);
return repo ? repo->doc_type : nullptr;
}
-const DocumentType *DocumentTypeRepo::getDocumentType(const stringref &name) const {
- DocumentTypeMap::const_iterator it =
- _doc_types->find(DocumentType::createId(name));
+const DocumentType *
+DocumentTypeRepo::getDocumentType(const stringref &name) const {
+ DocumentTypeMap::const_iterator it = _doc_types->find(DocumentType::createId(name));
if (it != _doc_types->end() && it->second->doc_type->getName() == name) {
return it->second->doc_type;
@@ -586,23 +544,21 @@ DocumentTypeRepo::getDataType(const DocumentType &doc_type, int32_t id) const {
}
const DataType *
-DocumentTypeRepo::getDataType(
- const DocumentType &doc_type, const stringref &name) const {
+DocumentTypeRepo::getDataType(const DocumentType &doc_type, const stringref &name) const {
const DataTypeRepo *dt_repo = FindPtr(*_doc_types, doc_type.getId());
return dt_repo ? dt_repo->repo.lookup(name) : nullptr;
}
-const AnnotationType *DocumentTypeRepo::getAnnotationType(
- const DocumentType &doc_type, int32_t id) const {
+const AnnotationType *
+DocumentTypeRepo::getAnnotationType(const DocumentType &doc_type, int32_t id) const {
const DataTypeRepo *dt_repo = FindPtr(*_doc_types, doc_type.getId());
return dt_repo ? dt_repo->annotations.lookup(id) : nullptr;
}
-void DocumentTypeRepo::forEachDocumentType(
- Closure1<const DocumentType &> &c) const {
- for (DocumentTypeMap::const_iterator
- it = _doc_types->begin(); it != _doc_types->end(); ++it) {
- c.call(*it->second->doc_type);
+void
+DocumentTypeRepo::forEachDocumentType(Closure1<const DocumentType &> &c) const {
+ for (const auto & entry : *_doc_types) {
+ c.call(*entry.second->doc_type);
}
}
diff --git a/document/src/vespa/document/repo/documenttyperepo.h b/document/src/vespa/document/repo/documenttyperepo.h
index 71410197405..c1c25204b3f 100644
--- a/document/src/vespa/document/repo/documenttyperepo.h
+++ b/document/src/vespa/document/repo/documenttyperepo.h
@@ -20,6 +20,7 @@ class DocumentType;
class DocumentTypeRepo {
std::unique_ptr<internal::DocumentTypeMap> _doc_types;
+ const DocumentType * _default;
public:
using DocumenttypesConfig = const internal::InternalDocumenttypesType;
@@ -39,7 +40,7 @@ public:
const DataType *getDataType(const DocumentType &doc_type, const vespalib::stringref &name) const;
const AnnotationType *getAnnotationType(const DocumentType &doc_type, int32_t id) const;
void forEachDocumentType(vespalib::Closure1<const DocumentType &> &c) const;
-
+ const DocumentType *getDefaultDocType() const { return _default; }
};
} // namespace document
diff --git a/document/src/vespa/document/repo/fixedtyperepo.cpp b/document/src/vespa/document/repo/fixedtyperepo.cpp
index 7644dddb64d..81a26265830 100644
--- a/document/src/vespa/document/repo/fixedtyperepo.cpp
+++ b/document/src/vespa/document/repo/fixedtyperepo.cpp
@@ -1,38 +1,14 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "fixedtyperepo.h"
-#include "documenttyperepo.h"
#include <cassert>
namespace document {
-FixedTypeRepo::FixedTypeRepo(const DocumentTypeRepo &repo)
- : _repo(&repo), _doc_type(repo.getDocumentType(DataType::T_DOCUMENT))
+FixedTypeRepo::FixedTypeRepo(const DocumentTypeRepo &repo, const vespalib::string &type)
+ : _repo(&repo), _doc_type(repo.getDocumentType(type))
{
-}
-
-FixedTypeRepo::FixedTypeRepo(const DocumentTypeRepo &repo,
- const vespalib::string &type)
- : _repo(&repo), _doc_type(repo.getDocumentType(type)) {
assert(_doc_type);
}
-const DataType *
-FixedTypeRepo::getDataType(int32_t id) const
-{
- return _repo->getDataType(*_doc_type, id);
-}
-
-const DataType *
-FixedTypeRepo::getDataType(const vespalib::string &name) const
-{
- return _repo->getDataType(*_doc_type, name);
-}
-
-const AnnotationType *
-FixedTypeRepo::getAnnotationType(int32_t id) const
-{
- return _repo->getAnnotationType(*_doc_type, id);
-}
-
} // namespace document
diff --git a/document/src/vespa/document/repo/fixedtyperepo.h b/document/src/vespa/document/repo/fixedtyperepo.h
index eb8a5a328dd..67e7571e31d 100644
--- a/document/src/vespa/document/repo/fixedtyperepo.h
+++ b/document/src/vespa/document/repo/fixedtyperepo.h
@@ -2,13 +2,10 @@
#pragma once
-#include <vespa/document/datatype/datatype.h>
+#include "documenttyperepo.h"
namespace document {
-class DocumentTypeRepo;
-class AnnotationType;
-
// Combines a DocumentTypeRepo and a DocumentType to allow easy access
// to the types contained in the DocumentType's namespace.
class FixedTypeRepo {
@@ -16,15 +13,15 @@ class FixedTypeRepo {
const DocumentType *_doc_type;
public:
- explicit FixedTypeRepo(const DocumentTypeRepo &repo);
+ explicit FixedTypeRepo(const DocumentTypeRepo &repo)
+ : _repo(&repo), _doc_type(repo.getDefaultDocType()) {}
FixedTypeRepo(const DocumentTypeRepo &repo, const DocumentType &doc_type)
: _repo(&repo), _doc_type(&doc_type) {}
FixedTypeRepo(const DocumentTypeRepo &repo, const vespalib::string &type);
- const DataType *getDataType(int32_t id) const;
- const DataType *getDataType(const vespalib::string &name) const;
- const AnnotationType *getAnnotationType(int32_t id) const;
-
+ const DataType *getDataType(int32_t id) const { return _repo->getDataType(*_doc_type, id); }
+ const DataType *getDataType(const vespalib::string &name) const { return _repo->getDataType(*_doc_type, name); }
+ const AnnotationType *getAnnotationType(int32_t id) const { return _repo->getAnnotationType(*_doc_type, id); }
const DocumentTypeRepo &getDocumentTypeRepo() const { return *_repo; }
const DocumentType &getDocumentType() const { return *_doc_type; }
};
diff --git a/document/src/vespa/document/select/operator.cpp b/document/src/vespa/document/select/operator.cpp
index b1c1c77ab4e..b2e7ddd82b8 100644
--- a/document/src/vespa/document/select/operator.cpp
+++ b/document/src/vespa/document/select/operator.cpp
@@ -6,6 +6,9 @@
#include <vespa/vespalib/stllike/hash_map.hpp>
#include <cassert>
+#include <vespa/log/log.h>
+LOG_SETUP(".document.select.operator");
+
namespace document::select {
Operator::OperatorMap Operator::_operators;
@@ -15,7 +18,7 @@ Operator::Operator(const vespalib::stringref & name)
{
OperatorMap::iterator it = _operators.find(name);
if (it != _operators.end()) {
- assert(false);
+ LOG_ABORT("unknown operator, should not happen");
}
_operators[_name] = this;
}
@@ -25,7 +28,7 @@ Operator::get(const vespalib::stringref & name)
{
OperatorMap::iterator it = _operators.find(name);
if (it == _operators.end()) {
- assert(false);
+ LOG_ABORT("unknown operator, should not happen");
}
return *it->second;
}
diff --git a/document/src/vespa/document/select/result.h b/document/src/vespa/document/select/result.h
index bdfb6c8c0ee..4e30cb5a8af 100644
--- a/document/src/vespa/document/select/result.h
+++ b/document/src/vespa/document/select/result.h
@@ -19,6 +19,8 @@
#pragma once
+#include <assert.h>
+#include <vespa/vespalib/util/hdr_abort.h>
#include <vespa/document/util/printable.h>
namespace document::select {
@@ -48,7 +50,7 @@ public:
return 1u;
if (this == &Result::True)
return 2u;
- abort();
+ HDR_ABORT("should not be reached");
}
static const Result &fromEnum(uint32_t val) {
@@ -58,7 +60,7 @@ public:
return Result::False;
if (val == 2u)
return Result::True;
- abort();
+ HDR_ABORT("should not be reached");
}
private:
diff --git a/document/src/vespa/document/serialization/vespadocumentserializer.cpp b/document/src/vespa/document/serialization/vespadocumentserializer.cpp
index 1a628ea2618..08fddbaad41 100644
--- a/document/src/vespa/document/serialization/vespadocumentserializer.cpp
+++ b/document/src/vespa/document/serialization/vespadocumentserializer.cpp
@@ -425,7 +425,7 @@ namespace {
void VespaDocumentSerializer::write42(const DocumentUpdate &value)
{
- _stream << static_cast<uint16_t>(value.getVersion());
+ _stream << static_cast<uint16_t>(Document::getNewestSerializationVersion());
write(value.getId());
_stream << static_cast<uint8_t>(CONTENT_HASTYPE);
_stream.write(value.getType().getName().c_str(), value.getType().getName().size() + 1);
@@ -439,17 +439,19 @@ void VespaDocumentSerializer::write42(const DocumentUpdate &value)
void VespaDocumentSerializer::writeHEAD(const DocumentUpdate &value)
{
+ if (!value._needHardReserialize) {
+ _stream.write(value._backing.peek(), value._backing.size());
+ return;
+ }
write(value.getId());
_stream.write(value.getType().getName().c_str(), value.getType().getName().size() + 1);
_stream << static_cast<uint16_t>(0);
- const DocumentUpdate::FieldUpdateV & updates(value.getUpdates());
- _stream << static_cast<uint32_t>(updates.size());
- for (const auto & update : updates) {
+ _stream << static_cast<uint32_t>(value._updates.size());
+ for (const auto & update : value._updates) {
write(update);
}
- const DocumentUpdate::FieldPathUpdateV & fieldPathUpdates(value.getFieldPathUpdates());
- _stream << static_cast<uint32_t>(value.serializeFlags(fieldPathUpdates.size()));
- for (const auto & update : fieldPathUpdates) {
+ _stream << static_cast<uint32_t>(value.serializeFlags(value._fieldPathUpdates.size()));
+ for (const auto & update : value._fieldPathUpdates) {
_stream << update->getSerializedType();
write(*update);
}
diff --git a/document/src/vespa/document/update/addfieldpathupdate.cpp b/document/src/vespa/document/update/addfieldpathupdate.cpp
index d0796b9b2a0..ab719de38d4 100644
--- a/document/src/vespa/document/update/addfieldpathupdate.cpp
+++ b/document/src/vespa/document/update/addfieldpathupdate.cpp
@@ -3,8 +3,8 @@
#include "addfieldpathupdate.h"
#include <vespa/document/fieldvalue/iteratorhandler.h>
#include <vespa/document/fieldvalue/arrayfieldvalue.h>
+#include <vespa/document/fieldvalue/document.h>
#include <vespa/document/serialization/vespadocumentdeserializer.h>
-#include <vespa/document/util/bytebuffer.h>
#include <vespa/vespalib/objects/nbostream.h>
#include <vespa/vespalib/util/exceptions.h>
#include <ostream>
@@ -88,10 +88,9 @@ AddFieldPathUpdate::print(std::ostream& out, bool verbose, const std::string& in
}
void
-AddFieldPathUpdate::deserialize(const DocumentTypeRepo& repo, const DataType& type,
- ByteBuffer& buffer, uint16_t version)
+AddFieldPathUpdate::deserialize(const DocumentTypeRepo& repo, const DataType& type, nbostream & stream)
{
- FieldPathUpdate::deserialize(repo, type, buffer, version);
+ FieldPathUpdate::deserialize(repo, type, stream);
FieldPath path;
type.buildFieldPath(path, getOriginalFieldPath());
@@ -99,10 +98,8 @@ AddFieldPathUpdate::deserialize(const DocumentTypeRepo& repo, const DataType& ty
assert(fieldType.inherits(ArrayDataType::classId));
FieldValue::UP val = fieldType.createFieldValue();
_values.reset(static_cast<ArrayFieldValue*>(val.release()));
- nbostream stream(buffer.getBufferAtPos(), buffer.getRemaining());
- VespaDocumentDeserializer deserializer(repo, stream, version);
+ VespaDocumentDeserializer deserializer(repo, stream, Document::getNewestSerializationVersion());
deserializer.read(*_values);
- buffer.incPos(buffer.getRemaining() - stream.size());
}
std::unique_ptr<IteratorHandler>
diff --git a/document/src/vespa/document/update/addfieldpathupdate.h b/document/src/vespa/document/update/addfieldpathupdate.h
index 2943248d9d8..692a30e3a73 100644
--- a/document/src/vespa/document/update/addfieldpathupdate.h
+++ b/document/src/vespa/document/update/addfieldpathupdate.h
@@ -26,8 +26,7 @@ public:
private:
uint8_t getSerializedType() const override { return AddMagic; }
- void deserialize(const DocumentTypeRepo& repo, const DataType& type,
- ByteBuffer& buffer, uint16_t version) override;
+ void deserialize(const DocumentTypeRepo& repo, const DataType& type, nbostream & stream) override;
std::unique_ptr<fieldvalue::IteratorHandler> getIteratorHandler(Document &, const DocumentTypeRepo &) const override;
diff --git a/document/src/vespa/document/update/addvalueupdate.cpp b/document/src/vespa/document/update/addvalueupdate.cpp
index 8ac1ee301eb..051ffcf8b2a 100644
--- a/document/src/vespa/document/update/addvalueupdate.cpp
+++ b/document/src/vespa/document/update/addvalueupdate.cpp
@@ -5,14 +5,13 @@
#include <vespa/document/fieldvalue/fieldvalues.h>
#include <vespa/document/serialization/vespadocumentdeserializer.h>
#include <vespa/document/util/serializableexceptions.h>
-#include <vespa/document/util/bytebuffer.h>
#include <vespa/vespalib/objects/nbostream.h>
#include <vespa/vespalib/util/xmlstream.h>
-
using vespalib::IllegalArgumentException;
using vespalib::IllegalStateException;
using vespalib::nbostream;
+using vespalib::make_string;
using namespace vespalib::xml;
namespace document {
@@ -25,7 +24,7 @@ AddValueUpdate:: AddValueUpdate(const FieldValue& value, int weight)
_weight(weight)
{}
-AddValueUpdate::~AddValueUpdate() { }
+AddValueUpdate::~AddValueUpdate() = default;
bool
AddValueUpdate::operator==(const ValueUpdate& other) const
{
@@ -41,19 +40,14 @@ void
AddValueUpdate::checkCompatibility(const Field& field) const
{
if (field.getDataType().inherits(CollectionDataType::classId)) {
- const CollectionDataType& type(
- static_cast<const CollectionDataType&>(field.getDataType()));
+ const CollectionDataType& type(static_cast<const CollectionDataType&>(field.getDataType()));
if (!type.getNestedType().isValueType(*_value)) {
- throw IllegalArgumentException(
- "Cannot add value of type "
- + _value->getDataType()->toString() + " to field "
- + field.getName().c_str() + " of container type "
- + field.getDataType().toString(), VESPA_STRLOC);
+ throw IllegalArgumentException("Cannot add value of type " + _value->getDataType()->toString() +
+ " to field " + field.getName().c_str() + " of container type " +
+ field.getDataType().toString(), VESPA_STRLOC);
}
} else {
- throw IllegalArgumentException(
- "Can not add a value to field of type"
- + field.getDataType().toString(), VESPA_STRLOC);
+ throw IllegalArgumentException("Can not add a value to field of type" + field.getDataType().toString(), VESPA_STRLOC);
}
}
@@ -75,9 +69,7 @@ AddValueUpdate::applyTo(FieldValue& value) const
WeightedSetFieldValue& doc(static_cast<WeightedSetFieldValue&>(value));
doc.add(*_value, _weight);
} else {
- std::string err = vespalib::make_string(
- "Unable to add a value to a \"%s\" field value.",
- value.getClass().name());
+ std::string err = make_string("Unable to add a value to a \"%s\" field value.", value.getClass().name());
throw IllegalStateException(err, VESPA_STRLOC);
}
return true;
@@ -93,23 +85,16 @@ AddValueUpdate::printXml(XmlOutputStream& xos) const
// Deserialize this update from the given buffer.
void
-AddValueUpdate::deserialize(const DocumentTypeRepo& repo,
- const DataType& type,
- ByteBuffer& buffer, uint16_t version)
+AddValueUpdate::deserialize(const DocumentTypeRepo& repo, const DataType& type, nbostream& stream)
{
- const CollectionDataType* ctype =
- Identifiable::cast<const CollectionDataType*>(&type);
- if (ctype == NULL) {
- throw DeserializeException("Can not perform add operation on "
- "non-collection type.");
+ const CollectionDataType* ctype = Identifiable::cast<const CollectionDataType*>(&type);
+ if (ctype == nullptr) {
+ throw DeserializeException("Can not perform add operation on non-collection type.");
}
_value.reset(ctype->getNestedType().createFieldValue().release());
- nbostream stream(buffer.getBufferAtPos(), buffer.getRemaining());
- VespaDocumentDeserializer deserializer(repo, stream, version);
+ VespaDocumentDeserializer deserializer(repo, stream, Document::getNewestSerializationVersion());
deserializer.read(*_value);
- buffer.incPos(buffer.getRemaining() - stream.size());
- buffer.getIntNetwork(_weight);
+ stream >> _weight;
}
}
-
diff --git a/document/src/vespa/document/update/addvalueupdate.h b/document/src/vespa/document/update/addvalueupdate.h
index 439263c5089..015d517c5a1 100644
--- a/document/src/vespa/document/update/addvalueupdate.h
+++ b/document/src/vespa/document/update/addvalueupdate.h
@@ -7,8 +7,8 @@
*/
#pragma once
+#include "valueupdate.h"
#include <vespa/document/fieldvalue/fieldvalue.h>
-#include <vespa/document/update/valueupdate.h>
namespace document {
@@ -62,18 +62,14 @@ public:
return *this;
}
- // ValueUpdate implementation
void checkCompatibility(const Field& field) const override;
bool applyTo(FieldValue& value) const override;
void printXml(XmlOutputStream& xos) const override;
void print(std::ostream& out, bool verbose, const std::string& indent) const override;
- void deserialize(const DocumentTypeRepo& repo, const DataType& type,
- ByteBuffer& buffer, uint16_t version) override;
+ void deserialize(const DocumentTypeRepo& repo, const DataType& type, nbostream & buffer) override;
AddValueUpdate* clone() const override { return new AddValueUpdate(*this); }
DECLARE_IDENTIFIABLE(AddValueUpdate);
-
};
} // document
-
diff --git a/document/src/vespa/document/update/arithmeticvalueupdate.cpp b/document/src/vespa/document/update/arithmeticvalueupdate.cpp
index b84dbe0f366..9ae7dd17a52 100644
--- a/document/src/vespa/document/update/arithmeticvalueupdate.cpp
+++ b/document/src/vespa/document/update/arithmeticvalueupdate.cpp
@@ -2,7 +2,7 @@
#include "arithmeticvalueupdate.h"
#include <vespa/document/base/field.h>
#include <vespa/document/fieldvalue/fieldvalues.h>
-#include <vespa/document/util/bytebuffer.h>
+#include <vespa/vespalib/objects/nbostream.h>
#include <vespa/vespalib/util/exceptions.h>
#include <vespa/vespalib/util/xmlstream.h>
@@ -127,14 +127,11 @@ ArithmeticValueUpdate::printXml(XmlOutputStream& xos) const
// Deserialize this update from the given buffer.
void
-ArithmeticValueUpdate::deserialize(
- const DocumentTypeRepo&, const DataType&,
- ByteBuffer& buffer, uint16_t)
+ArithmeticValueUpdate::deserialize(const DocumentTypeRepo&, const DataType&, nbostream & stream)
{
int32_t opt;
- buffer.getIntNetwork(opt);
+ stream >> opt >>_operand;
_operator = static_cast<ArithmeticValueUpdate::Operator>(opt);
- buffer.getDoubleNetwork(_operand);
}
} // document
diff --git a/document/src/vespa/document/update/arithmeticvalueupdate.h b/document/src/vespa/document/update/arithmeticvalueupdate.h
index 60718f8ad1f..1f9e33a3c3a 100644
--- a/document/src/vespa/document/update/arithmeticvalueupdate.h
+++ b/document/src/vespa/document/update/arithmeticvalueupdate.h
@@ -8,7 +8,7 @@
*/
#pragma once
-#include <vespa/document/update/valueupdate.h>
+#include "valueupdate.h"
namespace document {
@@ -57,10 +57,7 @@ public:
bool operator==(const ValueUpdate& other) const override;
- /** @return the operator of this arithmetic update. */
Operator getOperator() const { return _operator; }
-
- /** @return the operand of this arithmetic update. */
double getOperand() const { return _operand; }
/**
@@ -87,17 +84,14 @@ public:
*/
long applyTo(int64_t value) const;
- // ValueUpdate implementation
void checkCompatibility(const Field& field) const override;
bool applyTo(FieldValue& value) const override;
void printXml(XmlOutputStream& xos) const override;
void print(std::ostream& out, bool verbose, const std::string& indent) const override;
- void deserialize(const DocumentTypeRepo& repo, const DataType& type,
- ByteBuffer& buffer, uint16_t version) override;
+ void deserialize(const DocumentTypeRepo& repo, const DataType& type, nbostream & buffer) override;
ArithmeticValueUpdate* clone() const override { return new ArithmeticValueUpdate(*this); }
DECLARE_IDENTIFIABLE(ArithmeticValueUpdate);
-
};
} // document
diff --git a/document/src/vespa/document/update/assignfieldpathupdate.cpp b/document/src/vespa/document/update/assignfieldpathupdate.cpp
index 1de38b982d0..bec717874dc 100644
--- a/document/src/vespa/document/update/assignfieldpathupdate.cpp
+++ b/document/src/vespa/document/update/assignfieldpathupdate.cpp
@@ -6,7 +6,6 @@
#include <vespa/document/select/parser.h>
#include <vespa/document/select/variablemap.h>
#include <vespa/document/serialization/vespadocumentdeserializer.h>
-#include <vespa/document/util/bytebuffer.h>
#include <vespa/vespalib/objects/nbostream.h>
#include <vespa/vespalib/util/exceptions.h>
#include <boost/numeric/conversion/cast.hpp>
@@ -218,27 +217,24 @@ AssignFieldPathUpdate::print(std::ostream& out, bool verbose, const std::string&
}
void
-AssignFieldPathUpdate::deserialize(const DocumentTypeRepo& repo, const DataType& type,
- ByteBuffer& buffer, uint16_t version)
+AssignFieldPathUpdate::deserialize(const DocumentTypeRepo& repo, const DataType& type, nbostream & stream)
{
- FieldPathUpdate::deserialize(repo, type, buffer, version);
+ FieldPathUpdate::deserialize(repo, type, stream);
uint8_t flags = 0x00;
- buffer.getByte(flags);
+ stream >> flags;
_removeIfZero = (flags & REMOVE_IF_ZERO) != 0;
_createMissingPath = (flags & CREATE_MISSING_PATH) != 0;
if (flags & ARITHMETIC_EXPRESSION) {
- _expression = getString(buffer);
+ _expression = getString(stream);
} else {
FieldPath path;
type.buildFieldPath(path, getOriginalFieldPath());
_newValue.reset(getResultingDataType(path).createFieldValue().release());
- nbostream stream(buffer.getBufferAtPos(), buffer.getRemaining());
- VespaDocumentDeserializer deserializer(repo, stream, version);
+ VespaDocumentDeserializer deserializer(repo, stream, Document::getNewestSerializationVersion());
deserializer.read(*_newValue);
- buffer.incPos(buffer.getRemaining() - stream.size());
}
}
diff --git a/document/src/vespa/document/update/assignfieldpathupdate.h b/document/src/vespa/document/update/assignfieldpathupdate.h
index 329ce5d8c93..a1349bab96a 100644
--- a/document/src/vespa/document/update/assignfieldpathupdate.h
+++ b/document/src/vespa/document/update/assignfieldpathupdate.h
@@ -44,7 +44,7 @@ public:
private:
uint8_t getSerializedType() const override { return AssignMagic; }
- void deserialize(const DocumentTypeRepo& repo, const DataType& type, ByteBuffer& buffer, uint16_t version) override;
+ void deserialize(const DocumentTypeRepo& repo, const DataType& type, nbostream & stream) override;
std::unique_ptr<fieldvalue::IteratorHandler> getIteratorHandler(Document& doc, const DocumentTypeRepo & repo) const override;
@@ -54,4 +54,4 @@ private:
bool _createMissingPath;
};
-} // ns document
+}
diff --git a/document/src/vespa/document/update/assignvalueupdate.cpp b/document/src/vespa/document/update/assignvalueupdate.cpp
index fb38a78690b..0f7cf243ba3 100644
--- a/document/src/vespa/document/update/assignvalueupdate.cpp
+++ b/document/src/vespa/document/update/assignvalueupdate.cpp
@@ -4,7 +4,6 @@
#include <vespa/document/base/field.h>
#include <vespa/document/fieldvalue/fieldvalues.h>
#include <vespa/document/serialization/vespadocumentdeserializer.h>
-#include <vespa/document/util/bytebuffer.h>
#include <vespa/vespalib/objects/nbostream.h>
#include <vespa/vespalib/util/exceptions.h>
#include <vespa/vespalib/util/xmlstream.h>
@@ -90,19 +89,17 @@ AssignValueUpdate::printXml(XmlOutputStream& xos) const
// Deserialize this update from the given buffer.
void
-AssignValueUpdate::deserialize(const DocumentTypeRepo& repo, const DataType& type, ByteBuffer& buffer, uint16_t version)
+AssignValueUpdate::deserialize(const DocumentTypeRepo& repo, const DataType& type, nbostream & stream)
{
// Read content bit vector.
- unsigned char content = 0x00;
- buffer.getByte(content);
+ uint8_t content = 0x00;
+ stream >> content;
// Read field value, if any.
if (content & CONTENT_HASVALUE) {
_value.reset(type.createFieldValue().release());
- nbostream stream(buffer.getBufferAtPos(), buffer.getRemaining());
- VespaDocumentDeserializer deserializer(repo, stream, version);
+ VespaDocumentDeserializer deserializer(repo, stream, Document::getNewestSerializationVersion());
deserializer.read(*_value);
- buffer.incPos(buffer.getRemaining() - stream.size());
}
}
diff --git a/document/src/vespa/document/update/assignvalueupdate.h b/document/src/vespa/document/update/assignvalueupdate.h
index 05802857f65..978e61cbb3c 100644
--- a/document/src/vespa/document/update/assignvalueupdate.h
+++ b/document/src/vespa/document/update/assignvalueupdate.h
@@ -26,34 +26,26 @@ public:
AssignValueUpdate();
AssignValueUpdate(const FieldValue& value);
- ~AssignValueUpdate();
+ ~AssignValueUpdate() override;
bool operator==(const ValueUpdate& other) const override;
- /** @return The field value to assign during this update. */
bool hasValue() const { return bool(_value); }
const FieldValue& getValue() const { return *_value; }
- const FieldValue* getValuePtr() const { return _value.get(); }
- /**
- * Sets the field value to assign during this update.
- * @return A reference to this.
- */
AssignValueUpdate& setValue(const FieldValue* value) {
_value.reset(value ? value->clone() : 0);
return *this;
}
- // ValueUpdate implementation.
void checkCompatibility(const Field& field) const override;
bool applyTo(FieldValue& value) const override;
void printXml(XmlOutputStream& xos) const override;
void print(std::ostream& out, bool verbose, const std::string& indent) const override;
- void deserialize(const DocumentTypeRepo& repo, const DataType& type,
- ByteBuffer& buffer, uint16_t version) override;
+ void deserialize(const DocumentTypeRepo& repo, const DataType& type, nbostream & buffer) override;
AssignValueUpdate* clone() const override { return new AssignValueUpdate(*this); }
DECLARE_IDENTIFIABLE(AssignValueUpdate);
};
-} // document
+}
diff --git a/document/src/vespa/document/update/clearvalueupdate.cpp b/document/src/vespa/document/update/clearvalueupdate.cpp
index 0a69881181a..e2b8dc86afd 100644
--- a/document/src/vespa/document/update/clearvalueupdate.cpp
+++ b/document/src/vespa/document/update/clearvalueupdate.cpp
@@ -1,14 +1,9 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "clearvalueupdate.h"
-#include <vespa/document/base/field.h>
-#include <vespa/document/fieldvalue/document.h>
-#include <vespa/vespalib/util/exceptions.h>
#include <vespa/vespalib/util/xmlstream.h>
#include <ostream>
-using vespalib::IllegalArgumentException;
-using vespalib::IllegalStateException;
using namespace vespalib::xml;
namespace document {
@@ -29,9 +24,8 @@ ClearValueUpdate::checkCompatibility(const Field&) const
// Apply this update to the given document.
bool
-ClearValueUpdate::applyTo(FieldValue& value) const
+ClearValueUpdate::applyTo(FieldValue& ) const
{
- (void) value;
return false;
}
@@ -50,8 +44,7 @@ ClearValueUpdate::print(std::ostream& out, bool, const std::string&) const
// Deserialize this update from the given buffer.
void
-ClearValueUpdate::deserialize(const DocumentTypeRepo&, const DataType&,
- ByteBuffer&, uint16_t)
+ClearValueUpdate::deserialize(const DocumentTypeRepo&, const DataType&, nbostream &)
{
}
diff --git a/document/src/vespa/document/update/clearvalueupdate.h b/document/src/vespa/document/update/clearvalueupdate.h
index 12f2b888ea1..2ac47deaaf9 100644
--- a/document/src/vespa/document/update/clearvalueupdate.h
+++ b/document/src/vespa/document/update/clearvalueupdate.h
@@ -19,17 +19,15 @@ public:
ClearValueUpdate(const ClearValueUpdate& update) : ValueUpdate(update) {}
bool operator==(const ValueUpdate& other) const override;
- // ValueUpdate implementation
void checkCompatibility(const Field& field) const override;
bool applyTo(FieldValue& value) const override;
void printXml(XmlOutputStream& xos) const override;
void print(std::ostream& out, bool verbose, const std::string& indent) const override;
- void deserialize(const DocumentTypeRepo& repo, const DataType& type,
- ByteBuffer& buffer, uint16_t version) override;
+ void deserialize(const DocumentTypeRepo& repo, const DataType& type, nbostream& buffer) override;
ClearValueUpdate* clone() const override { return new ClearValueUpdate(*this); }
DECLARE_IDENTIFIABLE(ClearValueUpdate);
};
-} // document
+}
diff --git a/document/src/vespa/document/update/documentupdate.cpp b/document/src/vespa/document/update/documentupdate.cpp
index 30b760a6102..d23165cc815 100644
--- a/document/src/vespa/document/update/documentupdate.cpp
+++ b/document/src/vespa/document/update/documentupdate.cpp
@@ -22,62 +22,96 @@ using namespace vespalib::xml;
namespace document {
-// Declare content bits.
-static const unsigned char CONTENT_HASTYPE = 0x01;
+namespace {
+
+constexpr unsigned char CONTENT_HASTYPE = 0x01;
+
+vespalib::stringref
+readCStr(nbostream & stream) {
+ const char * s = stream.peek();
+ size_t sz = strnlen(s, stream.size());
+ stream.adjustReadPos(sz+1);
+ return vespalib::stringref(s, sz);
+}
+
+std::pair<const DocumentType *, DocumentId>
+deserializeTypeAndId(const DocumentTypeRepo& repo, vespalib::nbostream & stream) {
+ DocumentId docId(readCStr(stream));
+
+ // Read content bit vector.
+ unsigned char content = 0x00;
+ stream >> content;
+
+ // Why on earth do we have this whether we have type part?
+ // We need type for object to work, so just throwing exception if it's
+ // not there.
+ if((content & CONTENT_HASTYPE) == 0) {
+ throw IllegalStateException("Missing document type", VESPA_STRLOC);
+ }
+
+ vespalib::stringref typestr = readCStr(stream);
+
+ int16_t version = 0;
+ stream >> version;
+ const DocumentType *type = repo.getDocumentType(typestr);
+ if (!type) {
+ throw DocumentTypeNotFoundException(typestr, VESPA_STRLOC);
+ }
+ return std::make_pair(type, docId);
+}
+
+const DocumentType *
+deserializeHeader(const DocumentTypeRepo &repo, vespalib::nbostream & stream, vespalib::stringref & documentId)
+{
+ documentId = readCStr(stream);
+ vespalib::stringref typestr = readCStr(stream);
+ int16_t version = 0;
+ stream >> version;
+ const DocumentType * docType = repo.getDocumentType(typestr);
+ if (!docType) {
+ throw DocumentTypeNotFoundException(typestr, VESPA_STRLOC);
+ }
+ return docType;
+}
+
+}
+
-DocumentUpdate::DocumentUpdate(const DataType &type, const DocumentId& id)
+DocumentUpdate::DocumentUpdate(const DocumentTypeRepo & repo, const DataType &type, const DocumentId& id)
: _documentId(id),
_type(&type),
+ _repo(&repo),
+ _backing(),
_updates(),
_fieldPathUpdates(),
- _version(Document::getNewestSerializationVersion()),
- _createIfNonExistent(false)
+ _createIfNonExistent(false),
+ _needHardReserialize(false)
{
if (!type.getClass().inherits(DocumentType::classId)) {
- throw IllegalArgumentException("Cannot generate a document with non-document type " + type.toString() + ".",
- VESPA_STRLOC);
+ throw IllegalArgumentException("Cannot generate a document with non-document type " + type.toString() + ".", VESPA_STRLOC);
}
+ serializeHeader();
}
-DocumentUpdate::DocumentUpdate(const DocumentTypeRepo& repo,
- ByteBuffer& buffer,
- SerializeVersion serializeVersion)
- : _documentId("doc::"),
+DocumentUpdate::DocumentUpdate()
+ : _documentId(),
_type(DataType::DOCUMENT),
+ _repo(nullptr),
+ _backing(),
_updates(),
- _version(Document::getNewestSerializationVersion()),
- _createIfNonExistent(false)
+ _fieldPathUpdates(),
+ _createIfNonExistent(false),
+ _needHardReserialize(false)
{
- switch (serializeVersion) {
- case SerializeVersion::SERIALIZE_HEAD:
- deserializeHEAD(repo, buffer);
- break;
- case SerializeVersion::SERIALIZE_42:
- deserialize42(repo, buffer);
- break;
- default:
- throw IllegalArgumentException("bad serializeVersion provided.", VESPA_STRLOC);
- }
}
DocumentUpdate::~DocumentUpdate() = default;
-
bool
DocumentUpdate::operator==(const DocumentUpdate& other) const
{
- if (_documentId != other._documentId) return false;
- if (*_type != *other._type) return false;
- if (_updates.size() != other._updates.size()) return false;
- for (std::size_t i = 0, n = _updates.size(); i < n; ++i) {
- if (_updates[i] != other._updates[i]) return false;
- }
- if (_fieldPathUpdates.size() != other._fieldPathUpdates.size()) return false;
- for (std::size_t i = 0, n = _fieldPathUpdates.size(); i < n; ++i) {
- if (*_fieldPathUpdates[i] != *other._fieldPathUpdates[i]) return false;
- }
- if (_createIfNonExistent != other._createIfNonExistent) return false;
- return true;
+ return (_backing.size() == other._backing.size()) &&
+ (memcmp(_backing.peek(), other._backing.peek(), _backing.size()) == 0);
}
const DocumentType&
@@ -85,22 +119,69 @@ DocumentUpdate::getType() const {
return static_cast<const DocumentType &> (*_type);
}
+const DocumentUpdate::FieldUpdateV &
+DocumentUpdate::getUpdates() const {
+ ensureDeserialized();
+ return _updates;
+}
+
+const DocumentUpdate::FieldPathUpdateV &
+DocumentUpdate::getFieldPathUpdates() const {
+ ensureDeserialized();
+ return _fieldPathUpdates;
+}
+
+void
+DocumentUpdate::eagerDeserialize() const {
+ ensureDeserialized();
+}
+
+void DocumentUpdate::lazyDeserialize(const DocumentTypeRepo & repo, nbostream & stream) {
+ size_t start(stream.rp());
+ vespalib::stringref voidId;
+ deserializeHeader(repo, stream, voidId);
+ deserializeBody(repo, stream);
+ stream.rp(start);
+}
+void DocumentUpdate::ensureDeserialized() const {
+ if (_updates.empty() && _fieldPathUpdates.empty()) {
+ const_cast<DocumentUpdate &>(*this).lazyDeserialize(*_repo, const_cast<nbostream &>(_backing));
+ }
+}
+
DocumentUpdate&
DocumentUpdate::addUpdate(const FieldUpdate& update) {
+ ensureDeserialized();
_updates.push_back(update);
+ reserialize();
return *this;
}
DocumentUpdate&
DocumentUpdate::addFieldPathUpdate(const FieldPathUpdate::CP& update) {
+ ensureDeserialized();
_fieldPathUpdates.push_back(update);
+ reserialize();
return *this;
}
void
-DocumentUpdate::print(std::ostream& out, bool verbose,
- const std::string& indent) const
+DocumentUpdate::setCreateIfNonExistent(bool value) {
+ ensureDeserialized();
+ _createIfNonExistent = value;
+ reserialize();
+}
+
+bool
+DocumentUpdate::getCreateIfNonExistent() const {
+ ensureDeserialized();
+ return _createIfNonExistent;
+}
+
+void
+DocumentUpdate::print(std::ostream& out, bool verbose, const std::string& indent) const
{
+ ensureDeserialized();
out << "DocumentUpdate(";
if (_type) {
_type->print(out, verbose, indent + " ");
@@ -131,6 +212,7 @@ DocumentUpdate::print(std::ostream& out, bool verbose,
void
DocumentUpdate::applyTo(Document& doc) const
{
+ ensureDeserialized();
const DocumentType& type = doc.getType();
if (_type->getName() != type.getName()) {
string err = make_string("Can not apply a \"%s\" document update to a \"%s\" document.",
@@ -150,6 +232,17 @@ DocumentUpdate::applyTo(Document& doc) const
}
void
+DocumentUpdate::serializeHeader() {
+ string id_string = _documentId.getScheme().toString();
+ _backing.write(id_string.data(), id_string.size());
+ _backing << static_cast<uint8_t>(0);
+ _backing.write(getType().getName().c_str(), getType().getName().size() + 1);
+ _backing << static_cast<uint16_t>(0); // version
+ _backing << static_cast<uint32_t>(0); // Number of updates
+ _backing << static_cast<uint32_t>(0); // Number of field path updates
+}
+
+void
DocumentUpdate::serializeHEAD(nbostream &stream) const
{
VespaDocumentSerializer serializer(stream);
@@ -164,121 +257,122 @@ DocumentUpdate::serializeFlags(int size_) const
return flags.injectInto(size_);
}
-namespace {
- std::pair<const DocumentType *, DocumentId>
- deserializeTypeAndId(const DocumentTypeRepo& repo, ByteBuffer& buffer) {
- nbostream stream(buffer.getBufferAtPos(), buffer.getRemaining());
- DocumentId docId(stream);
- buffer.incPos(stream.rp());
-
- // Read content bit vector.
- unsigned char content = 0x00;
- buffer.getByte(content);
-
- // Why on earth do we have this whether we have type part?
- // We need type for object to work, so just throwing exception if it's
- // not there.
- if((content & CONTENT_HASTYPE) == 0) {
- throw IllegalStateException("Missing document type", VESPA_STRLOC);
- }
-
- vespalib::stringref typestr = buffer.getBufferAtPos();
- buffer.incPos(typestr.length() + 1);
-
- int16_t version = 0;
- buffer.getShortNetwork(version);
- const DocumentType *type = repo.getDocumentType(typestr);
- if (!type) {
- throw DocumentTypeNotFoundException(typestr, VESPA_STRLOC);
- }
- return std::make_pair(type, docId);
- }
-}
-
// Deserialize the content of the given buffer into this document update.
DocumentUpdate::UP
-DocumentUpdate::create42(const DocumentTypeRepo& repo, ByteBuffer& buffer)
+DocumentUpdate::create42(const DocumentTypeRepo& repo, vespalib::nbostream & stream)
{
- return std::make_unique<DocumentUpdate>(repo, buffer, SerializeVersion::SERIALIZE_42);
+ auto update = std::make_unique<DocumentUpdate>();
+ update->init42(repo, stream);
+ return update;
}
DocumentUpdate::UP
DocumentUpdate::createHEAD(const DocumentTypeRepo& repo, ByteBuffer& buffer)
{
- return std::make_unique<DocumentUpdate>(repo, buffer, SerializeVersion::SERIALIZE_HEAD);
+ vespalib::nbostream is(buffer.getBufferAtPos(), buffer.getRemaining());
+ auto update = std::make_unique<DocumentUpdate>();
+ update->initHEAD(repo, is);
+ buffer.setPos(buffer.getPos() + is.rp());
+ return update;
+}
+
+DocumentUpdate::UP
+DocumentUpdate::createHEAD(const DocumentTypeRepo& repo, vespalib::nbostream stream)
+{
+ auto update = std::make_unique<DocumentUpdate>();
+ update->initHEAD(repo, std::move(stream));
+ return update;
}
void
-DocumentUpdate::deserialize42(const DocumentTypeRepo& repo, ByteBuffer& buffer)
+DocumentUpdate::init42(const DocumentTypeRepo & repo, vespalib::nbostream & stream)
{
- int pos = buffer.getPos();
- try{
- buffer.getShortNetwork(_version);
+ _repo = &repo;
+ deserialize42(repo, stream);
+ reserialize();
+}
+void
+DocumentUpdate::initHEAD(const DocumentTypeRepo & repo, vespalib::nbostream && stream)
+{
+ _repo = &repo;
+ _backing = std::move(stream);
+ size_t startPos = _backing.rp();
+ vespalib::stringref docId;
+ _type = deserializeHeader(repo, _backing, docId);
+ _documentId.set(docId);
+ _backing.rp(startPos);
+}
+
+void
+DocumentUpdate::initHEAD(const DocumentTypeRepo & repo, vespalib::nbostream & stream)
+{
+ size_t startPos = stream.rp();
+ vespalib::stringref docId;
+ _type = deserializeHeader(repo, stream, docId);
+ _documentId.set(docId);
+ deserializeBody(repo, stream);
+ size_t sz = stream.rp() - startPos;
+ _backing = nbostream(stream.peek() - sz, sz);
+}
- std::pair<const DocumentType *, DocumentId> typeAndId(deserializeTypeAndId(repo, buffer));
+void
+DocumentUpdate::deserialize42(const DocumentTypeRepo& repo, vespalib::nbostream & stream)
+{
+ size_t pos = stream.rp();
+ try{
+ int16_t version(0);
+ stream >> version;
+ std::pair<const DocumentType *, DocumentId> typeAndId(deserializeTypeAndId(repo, stream));
_type = typeAndId.first;
_documentId = typeAndId.second;
// Read field updates, if any.
- if(buffer.getRemaining() > 0) {
- int sizeAndFlags = 0;
- buffer.getIntNetwork(sizeAndFlags);
+ if (! stream.empty()) {
+ int32_t sizeAndFlags = 0;
+ stream >> sizeAndFlags;
int numUpdates = deserializeFlags(sizeAndFlags);
_updates.reserve(numUpdates);
for (int i = 0; i < numUpdates; i++) {
- _updates.emplace_back(repo, *typeAndId.first, buffer, _version);
+ _updates.emplace_back(repo, *typeAndId.first, stream);
}
}
} catch (const DeserializeException &) {
- buffer.setPos(pos);
+ stream.rp(pos);
throw;
} catch (const BufferOutOfBoundsException &) {
- buffer.setPos(pos);
+ stream.rp(pos);
throw;
}
}
void
-DocumentUpdate::deserializeHEAD(const DocumentTypeRepo &repo, ByteBuffer &buffer)
+DocumentUpdate::deserializeBody(const DocumentTypeRepo &repo, vespalib::nbostream &stream)
{
- int pos = buffer.getPos();
+ _updates.clear();
+ _fieldPathUpdates.clear();
+ size_t pos = stream.rp();
try {
- nbostream stream(buffer.getBufferAtPos(), buffer.getRemaining());
- _documentId = DocumentId(stream);
- buffer.incPos(stream.rp());
-
- vespalib::stringref typestr = buffer.getBufferAtPos();
- buffer.incPos(typestr.length() + 1);
-
- int16_t version = 0;
- buffer.getShortNetwork(version);
- const DocumentType *docType = repo.getDocumentType(typestr);
- if (!docType) {
- throw DocumentTypeNotFoundException(typestr, VESPA_STRLOC);
- }
- _type = docType;
-
// Read field updates, if any.
- if (buffer.getRemaining() > 0) {
- int numUpdates = 0;
- buffer.getIntNetwork(numUpdates);
+ if ( ! stream.empty() ) {
+ int32_t numUpdates = 0;
+ stream >> numUpdates;
_updates.reserve(numUpdates);
for (int i = 0; i < numUpdates; i++) {
- _updates.emplace_back(repo, *docType, buffer, 8);
+ _updates.emplace_back(repo, *_type, stream);
}
}
// Read fieldpath updates, if any
- int sizeAndFlags = 0;
- buffer.getIntNetwork(sizeAndFlags);
+ int32_t sizeAndFlags = 0;
+ stream >> sizeAndFlags;
int numUpdates = deserializeFlags(sizeAndFlags);
_fieldPathUpdates.reserve(numUpdates);
for (int i = 0; i < numUpdates; ++i) {
- _fieldPathUpdates.emplace_back(FieldPathUpdate::createInstance(repo, *_type, buffer, 8).release());
+ _fieldPathUpdates.emplace_back(FieldPathUpdate::createInstance(repo, *_type, stream).release());
}
} catch (const DeserializeException &) {
- buffer.setPos(pos);
+ stream.rp(pos);
throw;
} catch (const BufferOutOfBoundsException &) {
- buffer.setPos(pos);
+ stream.rp(pos);
throw;
}
}
@@ -293,6 +387,7 @@ DocumentUpdate::deserializeFlags(int sizeAndFlags)
void
DocumentUpdate::printXml(XmlOutputStream& xos) const
{
+ ensureDeserialized();
xos << XmlTag("document")
<< XmlAttribute("type", _type->getName())
<< XmlAttribute("id", getId().toString());
@@ -304,4 +399,15 @@ DocumentUpdate::printXml(XmlOutputStream& xos) const
xos << XmlEndTag();
}
+void
+DocumentUpdate::reserialize()
+{
+ nbostream stream;
+ VespaDocumentSerializer serializer(stream);
+ _needHardReserialize = true;
+ serializer.writeHEAD(*this);
+ _backing = std::move(stream);
+ _needHardReserialize = false;
+}
+
}
diff --git a/document/src/vespa/document/update/documentupdate.h b/document/src/vespa/document/update/documentupdate.h
index c4b351674b5..a7ff138af87 100644
--- a/document/src/vespa/document/update/documentupdate.h
+++ b/document/src/vespa/document/update/documentupdate.h
@@ -33,7 +33,7 @@
namespace document {
class Document;
-
+class VespaDocumentSerializer;
/**
* Class containing a document update. In vespa 5.0, support for field
* path updates was added, and a new serialization format was
@@ -41,16 +41,6 @@ class Document;
*/
class DocumentUpdate final : public Printable, public XmlSerializable
{
-private:
- /**
- * Enum class containing the legal serialization version for
- * document updates. This version is not encoded in the serialized
- * document update.
- */
- enum class SerializeVersion {
- SERIALIZE_42, // old style format, before vespa 5.0
- SERIALIZE_HEAD // new style format, since vespa 5.0
- };
public:
typedef std::unique_ptr<DocumentUpdate> UP;
typedef std::shared_ptr<DocumentUpdate> SP;
@@ -60,22 +50,15 @@ public:
/**
* Create old style document update, no support for field path updates.
*/
- static DocumentUpdate::UP create42(const DocumentTypeRepo&, ByteBuffer&);
+ static DocumentUpdate::UP create42(const DocumentTypeRepo & repo, vespalib::nbostream & stream);
/**
* Create new style document update, possibly with field path updates.
*/
- static DocumentUpdate::UP createHEAD(const DocumentTypeRepo&, ByteBuffer&);
+ static DocumentUpdate::UP createHEAD(const DocumentTypeRepo & repo, vespalib::nbostream stream);
+ static DocumentUpdate::UP createHEAD(const DocumentTypeRepo & repo, ByteBuffer & buffer);
- /**
- * Create a document update from a byte buffer containing a serialized
- * document update. Public to allow useage in std::make_unique/shared.
- *
- * @param repo Document type repo used to find proper document type
- * @param buffer The buffer containing the serialized document update
- * @param serializeVersion Selector between serialization formats.
- */
- DocumentUpdate(const DocumentTypeRepo &repo, ByteBuffer &buffer, SerializeVersion serializeVersion);
+ DocumentUpdate();
/**
* The document type is not strictly needed, as we know this at applyTo()
* time, but search does not use applyTo() code to do the update, and don't
@@ -86,7 +69,7 @@ public:
* @param type The document type that this update is applicable for.
* @param id The identifier of the document that this update is created for.
*/
- DocumentUpdate(const DataType &type, const DocumentId& id);
+ DocumentUpdate(const DocumentTypeRepo & repo, const DataType &type, const DocumentId& id);
DocumentUpdate(const DocumentUpdate &) = delete;
DocumentUpdate & operator = (const DocumentUpdate &) = delete;
@@ -104,63 +87,59 @@ public:
* type as this.
*/
void applyTo(Document& doc) const;
-
- /**
- * Add a field update to this document update.
- * @return A reference to this.
- */
- DocumentUpdate& addUpdate(const FieldUpdate& update);
- /**
- * Add a fieldpath update to this document update.
- * @return A reference to this.
- */
+ DocumentUpdate& addUpdate(const FieldUpdate& update);
DocumentUpdate& addFieldPathUpdate(const FieldPathUpdate::CP& update);
/** @return The list of updates. */
- const FieldUpdateV & getUpdates() const { return _updates; }
+ const FieldUpdateV & getUpdates() const;
/** @return The list of fieldpath updates. */
- const FieldPathUpdateV & getFieldPathUpdates() const { return _fieldPathUpdates; }
+ const FieldPathUpdateV & getFieldPathUpdates() const;
+
+ void eagerDeserialize() const;
/** @return The type of document this update is for. */
const DocumentType& getType() const;
-
void print(std::ostream& out, bool verbose, const std::string& indent) const override;
-
void serializeHEAD(vespalib::nbostream &stream) const;
-
void printXml(XmlOutputStream&) const override;
/**
* Sets whether this update should create the document it updates if that document does not exist.
* In this case an empty document is created before the update is applied.
*/
- void setCreateIfNonExistent(bool value) {
- _createIfNonExistent = value;
- }
+ void setCreateIfNonExistent(bool value);
/**
* Gets whether this update should create the document it updates if that document does not exist.
*/
- bool getCreateIfNonExistent() const {
- return _createIfNonExistent;
- }
+ bool getCreateIfNonExistent() const;
int serializeFlags(int size_) const;
- int16_t getVersion() const { return _version; }
private:
- DocumentId _documentId; // The ID of the document to update.
- const DataType *_type; // The type of document this update is for.
- FieldUpdateV _updates; // The list of field updates.
- FieldPathUpdateV _fieldPathUpdates;
- int16_t _version; // Serialization version
- bool _createIfNonExistent;
+ DocumentId _documentId; // The ID of the document to update.
+ const DataType *_type; // The type of document this update is for.
+ const DocumentTypeRepo *_repo;
+ vespalib::nbostream _backing;
+ FieldUpdateV _updates; // The list of field updates.
+ FieldPathUpdateV _fieldPathUpdates;
+ bool _createIfNonExistent;
+ bool _needHardReserialize;
int deserializeFlags(int sizeAndFlags);
- void deserialize42(const DocumentTypeRepo&, ByteBuffer&);
- void deserializeHEAD(const DocumentTypeRepo&, ByteBuffer&);
+ void init42(const DocumentTypeRepo & repo, vespalib::nbostream & stream);
+ void initHEAD(const DocumentTypeRepo & repo, vespalib::nbostream && stream);
+ void initHEAD(const DocumentTypeRepo & repo, vespalib::nbostream & stream);
+ void deserialize42(const DocumentTypeRepo & repo, vespalib::nbostream & stream);
+ void deserializeBody(const DocumentTypeRepo &repo, vespalib::nbostream &stream);
+ void lazyDeserialize(const DocumentTypeRepo & repo, vespalib::nbostream & stream);
+ void ensureDeserialized() const;
+ void serializeHeader();
+ void reserialize();
+ friend VespaDocumentSerializer;
};
-} // document
+}
+
diff --git a/document/src/vespa/document/update/fieldpathupdate.cpp b/document/src/vespa/document/update/fieldpathupdate.cpp
index 239ffff2fef..e7d5824b6e0 100644
--- a/document/src/vespa/document/update/fieldpathupdate.cpp
+++ b/document/src/vespa/document/update/fieldpathupdate.cpp
@@ -5,7 +5,7 @@
#include <vespa/document/fieldvalue/iteratorhandler.h>
#include <vespa/document/select/parser.h>
#include <vespa/document/util/serializableexceptions.h>
-#include <vespa/document/util/bytebuffer.h>
+#include <vespa/vespalib/objects/nbostream.h>
#include <ostream>
#include <vespa/log/log.h>
@@ -106,29 +106,29 @@ FieldPathUpdate::getResultingDataType(const FieldPath & path) const
return path.back().getDataType();
}
-vespalib::string
-FieldPathUpdate::getString(ByteBuffer& buffer)
+vespalib::stringref
+FieldPathUpdate::getString(nbostream & stream)
{
- int32_t length = 0;
- buffer.getIntNetwork(length);
- vespalib::string s(buffer.getBufferAtPos());
- buffer.incPos(length);
+ uint32_t sz(0);
+ stream >> sz;
+
+ vespalib::stringref s(stream.peek(), sz - 1);
+ stream.adjustReadPos(sz);
return s;
}
void
-FieldPathUpdate::deserialize(const DocumentTypeRepo&, const DataType&, ByteBuffer& buffer, uint16_t)
+FieldPathUpdate::deserialize(const DocumentTypeRepo&, const DataType&, nbostream & stream)
{
- _originalFieldPath = getString(buffer);
- _originalWhereClause = getString(buffer);
+ _originalFieldPath = getString(stream);
+ _originalWhereClause = getString(stream);
}
std::unique_ptr<FieldPathUpdate>
-FieldPathUpdate::createInstance(const DocumentTypeRepo& repo, const DataType &type,
- ByteBuffer& buffer, int serializationVersion)
+FieldPathUpdate::createInstance(const DocumentTypeRepo& repo, const DataType &type, nbostream& stream)
{
unsigned char updateType = 0;
- buffer.getByte(updateType);
+ stream >> updateType;
std::unique_ptr<FieldPathUpdate> update;
switch (updateType) {
@@ -144,7 +144,7 @@ FieldPathUpdate::createInstance(const DocumentTypeRepo& repo, const DataType &ty
default:
throw DeserializeException(make_string("Unknown fieldpath update type: %d", updateType), VESPA_STRLOC);
}
- update->deserialize(repo, type, buffer, serializationVersion);
+ update->deserialize(repo, type, stream);
return update;
}
diff --git a/document/src/vespa/document/update/fieldpathupdate.h b/document/src/vespa/document/update/fieldpathupdate.h
index 76d529fec68..3e19420d0d0 100644
--- a/document/src/vespa/document/update/fieldpathupdate.h
+++ b/document/src/vespa/document/update/fieldpathupdate.h
@@ -23,13 +23,14 @@ class FieldPathUpdate : public vespalib::Cloneable,
public vespalib::Identifiable
{
protected:
+ using nbostream = vespalib::nbostream;
using stringref = vespalib::stringref;
/** To be used for deserialization */
FieldPathUpdate();
FieldPathUpdate(const FieldPathUpdate &);
FieldPathUpdate & operator =(const FieldPathUpdate &);
- static vespalib::string getString(ByteBuffer& buffer);
+ static stringref getString(nbostream & stream);
public:
using SP = std::shared_ptr<FieldPathUpdate>;
using CP = vespalib::CloneablePtr<FieldPathUpdate>;
@@ -69,26 +70,14 @@ public:
virtual uint8_t getSerializedType() const = 0;
/** Deserializes and creates a new FieldPathUpdate instance.
- * Requires type id to be not yet read.
+ * Requires type id to be not yet consumed.
*/
- static std::unique_ptr<FieldPathUpdate> createInstance(
- const DocumentTypeRepo& repo,
- const DataType &type, ByteBuffer& buffer,
- int serializationVersion);
+ static std::unique_ptr<FieldPathUpdate> createInstance(const DocumentTypeRepo& repo, const DataType &type, nbostream & stream);
protected:
FieldPathUpdate(stringref fieldPath, stringref whereClause = stringref());
- /**
- * Deserializes the given byte buffer into an instance of a FieldPathUpdate
- * object.
- *
- * @param type A data type that describes the content of the buffer.
- * @param buffer The byte buffer that contains the serialized object.
- * @param version The serialization version of the object to deserialize.
- */
- virtual void deserialize(const DocumentTypeRepo& repo, const DataType& type,
- ByteBuffer& buffer, uint16_t version);
+ virtual void deserialize(const DocumentTypeRepo& repo, const DataType& type, nbostream & stream);
/** @return the datatype of the last path element in the field path */
const DataType& getResultingDataType(const FieldPath & path) const;
diff --git a/document/src/vespa/document/update/fieldpathupdates.h b/document/src/vespa/document/update/fieldpathupdates.h
index db2da82f705..fb2cf1dffe2 100644
--- a/document/src/vespa/document/update/fieldpathupdates.h
+++ b/document/src/vespa/document/update/fieldpathupdates.h
@@ -1,7 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#pragma once
-#include <vespa/document/update/addfieldpathupdate.h>
-#include <vespa/document/update/assignfieldpathupdate.h>
-#include <vespa/document/update/removefieldpathupdate.h>
+#include "addfieldpathupdate.h"
+#include "assignfieldpathupdate.h"
+#include "removefieldpathupdate.h"
diff --git a/document/src/vespa/document/update/fieldupdate.cpp b/document/src/vespa/document/update/fieldupdate.cpp
index 52fe0071b94..d36a134ecaa 100644
--- a/document/src/vespa/document/update/fieldupdate.cpp
+++ b/document/src/vespa/document/update/fieldupdate.cpp
@@ -4,11 +4,11 @@
#include <vespa/document/base/exceptions.h>
#include <vespa/document/fieldvalue/document.h>
#include <vespa/document/datatype/documenttype.h>
-#include <vespa/document/util/bytebuffer.h>
+#include <vespa/vespalib/objects/nbostream.h>
namespace document {
-typedef std::vector<ValueUpdate::CP> ValueUpdateList;
+using vespalib::nbostream;
FieldUpdate::FieldUpdate(const Field& field)
: Printable(),
@@ -19,24 +19,24 @@ FieldUpdate::FieldUpdate(const Field& field)
namespace {
-int readInt(ByteBuffer & buffer) {
+int readInt(nbostream & stream) {
int tmp;
- buffer.getIntNetwork(tmp);
+ stream >> tmp;
return tmp;
}
}
-FieldUpdate::FieldUpdate(const DocumentTypeRepo& repo, const DocumentType& type, ByteBuffer& buffer, int16_t version)
+FieldUpdate::FieldUpdate(const DocumentTypeRepo& repo, const DataType & type, nbostream & stream)
: Printable(),
- _field(type.getField(readInt(buffer))),
+ _field(type.getField(readInt(stream))),
_updates()
{
- int numUpdates = readInt(buffer);
+ int numUpdates = readInt(stream);
_updates.reserve(numUpdates);
const DataType& dataType = _field.getDataType();
for(int i(0); i < numUpdates; i++) {
- _updates.emplace_back(ValueUpdate::createInstance(repo, dataType, buffer, version).release());
+ _updates.emplace_back(ValueUpdate::createInstance(repo, dataType, stream).release());
}
}
@@ -104,20 +104,17 @@ FieldUpdate::print(std::ostream& out, bool verbose, const std::string& indent) c
// Deserialize this field update from the given buffer.
void
-FieldUpdate::deserialize(const DocumentTypeRepo& repo, const DocumentType& docType,
- ByteBuffer& buffer, int16_t version)
+FieldUpdate::deserialize(const DocumentTypeRepo& repo, const DocumentType& docType, nbostream& stream)
{
- int fieldId;
- buffer.getIntNetwork(fieldId);
+ int fieldId = readInt(stream);
_field = docType.getField(fieldId);
const DataType& dataType = _field.getDataType();
- int numUpdates = 0;
- buffer.getIntNetwork(numUpdates);
+ int numUpdates = readInt(stream);
_updates.clear();
_updates.resize(numUpdates);
for(int i = 0; i < numUpdates; i++) {
- _updates[i].reset(ValueUpdate::createInstance(repo, dataType, buffer, version).release());
+ _updates[i].reset(ValueUpdate::createInstance(repo, dataType, stream).release());
}
}
diff --git a/document/src/vespa/document/update/fieldupdate.h b/document/src/vespa/document/update/fieldupdate.h
index fc94df605af..6bd46c3a172 100644
--- a/document/src/vespa/document/update/fieldupdate.h
+++ b/document/src/vespa/document/update/fieldupdate.h
@@ -19,8 +19,6 @@
namespace document {
class Document;
-class DocumentType;
-class DocumentTypeRepo;
class FieldUpdate : public vespalib::Identifiable,
public Printable,
@@ -28,6 +26,7 @@ class FieldUpdate : public vespalib::Identifiable,
{
Field _field;
std::vector<ValueUpdate::CP> _updates;
+ using nbostream = vespalib::nbostream;
public:
typedef vespalib::CloneablePtr<FieldUpdate> CP;
@@ -41,14 +40,12 @@ public:
/**
* This is a convenience function to construct a field update directly from
- * a byte buffer by deserializing all its content from the buffer.
+ * a stream by deserializing all its content from the stream.
*
- * @param type A document type that describes the buffer content.
- * @param buffer A byte buffer that contains a serialized field update.
- * @param serializationVersion The serialization version the update was serialized with.
+ * @param type A document type that describes the stream content.
+ * @param stream A stream that contains a serialized field update.
*/
- FieldUpdate(const DocumentTypeRepo& repo, const DocumentType& type,
- ByteBuffer& buffer, int16_t version);
+ FieldUpdate(const DocumentTypeRepo& repo, const DataType & type, nbostream & stream);
bool operator==(const FieldUpdate&) const;
bool operator!=(const FieldUpdate & rhs) const { return ! (*this == rhs); }
@@ -73,30 +70,18 @@ public:
const std::vector<ValueUpdate::CP>& getUpdates() const { return _updates; }
const Field& getField() const { return _field; }
-
- /**
- * Applies this update object to the given {@link Document} object.
- *
- * @param doc The document to apply this update to.
- */
void applyTo(Document& doc) const;
-
- // Printable implementation
void print(std::ostream& out, bool verbose, const std::string& indent) const override;
-
- // XmlSerializable implementation
void printXml(XmlOutputStream&) const override;
/**
- * Deserializes the given byte buffer into an instance of an update object.
+ * Deserializes the given stream into an instance of an update object.
* Not a Deserializable, as document type is needed as extra information.
*
- * @param type A document type that describes the buffer content.
- * @param buffer The byte buffer that contains the serialized update object.
- * @param serializationVersion The serialization version the update was serialized with.
+ * @param type A document type that describes the stream content.
+ * @param buffer The stream that contains the serialized update object.
*/
- void deserialize(const DocumentTypeRepo& repo, const DocumentType& type,
- ByteBuffer& buffer, int16_t serializationVersion);
+ void deserialize(const DocumentTypeRepo& repo, const DocumentType& type, nbostream& stream);
};
diff --git a/document/src/vespa/document/update/mapvalueupdate.cpp b/document/src/vespa/document/update/mapvalueupdate.cpp
index 568b661e03d..3fc9c8cbea5 100644
--- a/document/src/vespa/document/update/mapvalueupdate.cpp
+++ b/document/src/vespa/document/update/mapvalueupdate.cpp
@@ -5,7 +5,6 @@
#include <vespa/document/fieldvalue/fieldvalues.h>
#include <vespa/document/serialization/vespadocumentdeserializer.h>
#include <vespa/document/util/serializableexceptions.h>
-#include <vespa/document/util/bytebuffer.h>
#include <vespa/vespalib/util/xmlstream.h>
#include <vespa/vespalib/objects/nbostream.h>
@@ -72,9 +71,9 @@ MapValueUpdate::applyTo(FieldValue& value) const
ArrayFieldValue& val(static_cast<ArrayFieldValue&>(value));
int32_t index = _key->getAsInt();
if (index < 0 || static_cast<uint32_t>(index) >= val.size()) {
- throw IllegalStateException(vespalib::make_string(
- "Tried to update element %i in an array of %zu elements",
- index, val.size()), VESPA_STRLOC);
+ // Silently ignoring updates with index out of bounds matches
+ // behavior of functionally identical fieldpath updates.
+ return true;
}
if (!_update->applyTo(val[_key->getAsInt()])) {
val.remove(_key->getAsInt());
@@ -128,18 +127,16 @@ MapValueUpdate::printXml(XmlOutputStream& xos) const
// Deserialize this update from the given buffer.
void
-MapValueUpdate::deserialize(const DocumentTypeRepo& repo, const DataType& type, ByteBuffer& buffer, uint16_t version)
+MapValueUpdate::deserialize(const DocumentTypeRepo& repo, const DataType& type, nbostream & stream)
{
- nbostream stream(buffer.getBufferAtPos(), buffer.getRemaining());
- VespaDocumentDeserializer deserializer(repo, stream, version);
+ VespaDocumentDeserializer deserializer(repo, stream, Document::getNewestSerializationVersion());
switch(type.getClass().id()) {
case ArrayDataType::classId:
{
_key.reset(new IntFieldValue);
deserializer.read(*_key);
- buffer.incPos(buffer.getRemaining() - stream.size());
const ArrayDataType& arrayType = static_cast<const ArrayDataType&>(type);
- _update.reset(ValueUpdate::createInstance(repo, arrayType.getNestedType(), buffer, version).release());
+ _update.reset(ValueUpdate::createInstance(repo, arrayType.getNestedType(), stream).release());
break;
}
case WeightedSetDataType::classId:
@@ -147,8 +144,7 @@ MapValueUpdate::deserialize(const DocumentTypeRepo& repo, const DataType& type,
const WeightedSetDataType& wset(static_cast<const WeightedSetDataType&>(type));
_key.reset(wset.getNestedType().createFieldValue().release());
deserializer.read(*_key);
- buffer.incPos(buffer.getRemaining() - stream.size());
- _update.reset(ValueUpdate::createInstance(repo, *DataType::INT, buffer, version).release());
+ _update.reset(ValueUpdate::createInstance(repo, *DataType::INT, stream).release());
break;
}
default:
diff --git a/document/src/vespa/document/update/mapvalueupdate.h b/document/src/vespa/document/update/mapvalueupdate.h
index 3cfab4fff69..4e407097405 100644
--- a/document/src/vespa/document/update/mapvalueupdate.h
+++ b/document/src/vespa/document/update/mapvalueupdate.h
@@ -18,8 +18,6 @@ namespace document {
class MapValueUpdate : public ValueUpdate {
FieldValue::CP _key; // The field value this update is mapping to.
- // This is shared pointer to be able to lookup key
- // in weighted set map.
ValueUpdate::CP _update; //The update to apply to the value member of this.
// Used by ValueUpdate's static factory function
@@ -47,11 +45,9 @@ public:
bool operator==(const ValueUpdate& other) const override;
- /** @return The key of the field value to update. */
const FieldValue& getKey() const { return *_key; }
FieldValue& getKey() { return *_key; }
- /** @return The update to apply to the field value of this. */
const ValueUpdate& getUpdate() const { return *_update; }
ValueUpdate& getUpdate() { return *_update; }
@@ -77,18 +73,16 @@ public:
return *this;
}
- // ValueUpdate implementation
void checkCompatibility(const Field& field) const override;
bool applyTo(FieldValue& value) const override;
void printXml(XmlOutputStream& xos) const override;
void print(std::ostream& out, bool verbose, const std::string& indent) const override;
- void deserialize(const DocumentTypeRepo& repo, const DataType& type,
- ByteBuffer& buffer, uint16_t version) override;
+ void deserialize(const DocumentTypeRepo& repo, const DataType& type, nbostream& buffer) override;
MapValueUpdate* clone() const override { return new MapValueUpdate(*this); }
DECLARE_IDENTIFIABLE(MapValueUpdate);
};
-} // document
+}
diff --git a/document/src/vespa/document/update/removefieldpathupdate.cpp b/document/src/vespa/document/update/removefieldpathupdate.cpp
index c0ae2f869e4..57832055001 100644
--- a/document/src/vespa/document/update/removefieldpathupdate.cpp
+++ b/document/src/vespa/document/update/removefieldpathupdate.cpp
@@ -36,10 +36,9 @@ RemoveFieldPathUpdate::print(std::ostream& out, bool verbose, const std::string&
}
void
-RemoveFieldPathUpdate::deserialize(const DocumentTypeRepo& repo, const DataType& type,
- ByteBuffer& buffer, uint16_t version)
+RemoveFieldPathUpdate::deserialize(const DocumentTypeRepo& repo, const DataType& type, nbostream & stream)
{
- FieldPathUpdate::deserialize(repo, type, buffer, version);
+ FieldPathUpdate::deserialize(repo, type, stream);
}
namespace {
diff --git a/document/src/vespa/document/update/removefieldpathupdate.h b/document/src/vespa/document/update/removefieldpathupdate.h
index db7e234bfc8..1329361621e 100644
--- a/document/src/vespa/document/update/removefieldpathupdate.h
+++ b/document/src/vespa/document/update/removefieldpathupdate.h
@@ -23,10 +23,10 @@ public:
private:
uint8_t getSerializedType() const override { return RemoveMagic; }
- void deserialize(const DocumentTypeRepo& repo, const DataType& type,
- ByteBuffer& buffer, uint16_t version) override;
+ void deserialize(const DocumentTypeRepo& repo, const DataType& type, nbostream & buffer) override;
std::unique_ptr<fieldvalue::IteratorHandler> getIteratorHandler(Document &, const DocumentTypeRepo &) const override;
};
-} // ns document
+}
+
diff --git a/document/src/vespa/document/update/removevalueupdate.cpp b/document/src/vespa/document/update/removevalueupdate.cpp
index c21e7c93abe..28c69652a0e 100644
--- a/document/src/vespa/document/update/removevalueupdate.cpp
+++ b/document/src/vespa/document/update/removevalueupdate.cpp
@@ -7,7 +7,6 @@
#include <vespa/document/serialization/vespadocumentdeserializer.h>
#include <vespa/vespalib/objects/nbostream.h>
#include <vespa/document/util/serializableexceptions.h>
-#include <vespa/document/util/bytebuffer.h>
#include <vespa/vespalib/util/xmlstream.h>
using vespalib::IllegalArgumentException;
@@ -85,7 +84,7 @@ RemoveValueUpdate::print(std::ostream& out, bool, const std::string&) const
// Deserialize this update from the given buffer.
void
-RemoveValueUpdate::deserialize(const DocumentTypeRepo& repo, const DataType& type, ByteBuffer& buffer, uint16_t version)
+RemoveValueUpdate::deserialize(const DocumentTypeRepo& repo, const DataType& type, nbostream & stream)
{
switch(type.getClass().id()) {
case ArrayDataType::classId:
@@ -93,10 +92,8 @@ RemoveValueUpdate::deserialize(const DocumentTypeRepo& repo, const DataType& typ
{
const CollectionDataType& c(static_cast<const CollectionDataType&>(type));
_key.reset(c.getNestedType().createFieldValue().release());
- nbostream stream(buffer.getBufferAtPos(), buffer.getRemaining());
- VespaDocumentDeserializer deserializer(repo, stream, version);
+ VespaDocumentDeserializer deserializer(repo, stream, Document::getNewestSerializationVersion());
deserializer.read(*_key);
- buffer.incPos(buffer.getRemaining() - stream.size());
break;
}
default:
diff --git a/document/src/vespa/document/update/removevalueupdate.h b/document/src/vespa/document/update/removevalueupdate.h
index 6912a6e0bf3..c674ebf430c 100644
--- a/document/src/vespa/document/update/removevalueupdate.h
+++ b/document/src/vespa/document/update/removevalueupdate.h
@@ -46,18 +46,16 @@ public:
return *this;
}
- // ValueUpdate implementation
void checkCompatibility(const Field& field) const override;
bool applyTo(FieldValue& value) const override;
void printXml(XmlOutputStream& xos) const override;
void print(std::ostream& out, bool verbose, const std::string& indent) const override;
- void deserialize(const DocumentTypeRepo& repo, const DataType& type,
- ByteBuffer& buffer, uint16_t version) override;
+ void deserialize(const DocumentTypeRepo& repo, const DataType& type, nbostream& buffer) override;
RemoveValueUpdate* clone() const override { return new RemoveValueUpdate(*this); }
DECLARE_IDENTIFIABLE(RemoveValueUpdate);
};
-} // document
+}
diff --git a/document/src/vespa/document/update/updates.h b/document/src/vespa/document/update/updates.h
index 92cb51699cd..50a6e318b6a 100644
--- a/document/src/vespa/document/update/updates.h
+++ b/document/src/vespa/document/update/updates.h
@@ -2,13 +2,12 @@
#pragma once
-#include <vespa/document/update/documentupdate.h>
-#include <vespa/document/update/fieldupdate.h>
-
-#include <vespa/document/update/addvalueupdate.h>
-#include <vespa/document/update/arithmeticvalueupdate.h>
-#include <vespa/document/update/assignvalueupdate.h>
-#include <vespa/document/update/clearvalueupdate.h>
-#include <vespa/document/update/mapvalueupdate.h>
-#include <vespa/document/update/removevalueupdate.h>
+#include "documentupdate.h"
+#include "fieldupdate.h"
+#include "addvalueupdate.h"
+#include "arithmeticvalueupdate.h"
+#include "assignvalueupdate.h"
+#include "clearvalueupdate.h"
+#include "mapvalueupdate.h"
+#include "removevalueupdate.h"
diff --git a/document/src/vespa/document/update/valueupdate.cpp b/document/src/vespa/document/update/valueupdate.cpp
index 103d9341f0e..6b893a80fd0 100644
--- a/document/src/vespa/document/update/valueupdate.cpp
+++ b/document/src/vespa/document/update/valueupdate.cpp
@@ -2,31 +2,29 @@
#include "valueupdate.h"
#include <vespa/vespalib/util/stringfmt.h>
-#include <vespa/document/util/bytebuffer.h>
+#include <vespa/vespalib/objects/nbostream.h>
#include <stdexcept>
namespace document {
IMPLEMENT_IDENTIFIABLE_ABSTRACT(ValueUpdate, Identifiable);
-// Create a value update from a byte buffer.
std::unique_ptr<ValueUpdate>
-ValueUpdate::createInstance(const DocumentTypeRepo& repo, const DataType& type, ByteBuffer& buffer, int serializationVersion)
+ValueUpdate::createInstance(const DocumentTypeRepo& repo, const DataType& type, nbostream & stream)
{
- ValueUpdate* update(NULL);
int32_t classId = 0;
- buffer.getIntNetwork(classId);
+ stream >> classId;
const Identifiable::RuntimeClass * rtc(Identifiable::classFromId(classId));
- if (rtc != NULL) {
- update = static_cast<ValueUpdate*>(Identifiable::classFromId(classId)->create());
- /// \todo TODO (was warning): Updates are not versioned in serialization format. Will not work with altering it.
- update->deserialize(repo, type, buffer, serializationVersion);
+ if (rtc != nullptr) {
+ std::unique_ptr<ValueUpdate> update(static_cast<ValueUpdate*>(rtc->create()));
+ /// \todo TODO (was warning): Updates are not versioned in serialization format. Will not work without altering it.
+ /// Should also use the serializer, not this deserialize into self.
+ update->deserialize(repo, type, stream);
+ return update;
} else {
throw std::runtime_error(vespalib::make_string("Could not find a class for classId %d(%x)", classId, classId));
}
-
- return std::unique_ptr<ValueUpdate>(update);
}
}
diff --git a/document/src/vespa/document/update/valueupdate.h b/document/src/vespa/document/update/valueupdate.h
index ffa40b1c191..963e1ad1d96 100644
--- a/document/src/vespa/document/update/valueupdate.h
+++ b/document/src/vespa/document/update/valueupdate.h
@@ -24,7 +24,6 @@
namespace document {
-class ByteBuffer;
class DocumentTypeRepo;
class Field;
class FieldValue;
@@ -34,18 +33,18 @@ class ValueUpdate : public vespalib::Identifiable,
public vespalib::Cloneable,
public XmlSerializable
{
+protected:
+ using nbostream = vespalib::nbostream;
public:
- typedef vespalib::CloneablePtr<ValueUpdate> CP;
+ using CP = vespalib::CloneablePtr<ValueUpdate>;
/**
- * Create a value update object from the given byte buffer.
+ * Create a value update object from the given stream.
*
* @param type A data type that describes the content of the buffer.
- * @param buffer The byte buffer that containes the serialized update.
+ * @param buffer The stream that containes the serialized update.
*/
- static std::unique_ptr<ValueUpdate> createInstance(
- const DocumentTypeRepo& repo, const DataType& type,
- ByteBuffer& buffer, int serializationVersion);
+ static std::unique_ptr<ValueUpdate> createInstance(const DocumentTypeRepo& repo, const DataType& type, nbostream & buffer);
/** Define all types of value updates. */
enum ValueUpdateType {
@@ -69,8 +68,7 @@ public:
* Recursively checks the compatibility of this value update as
* applied to the given document field.
*
- * @throws IllegalArgumentException Thrown if this value update
- * is not compatible.
+ * @throws IllegalArgumentException Thrown if this value update is not compatible.
*/
virtual void checkCompatibility(const Field& field) const = 0;
@@ -84,15 +82,12 @@ public:
ValueUpdate* clone() const override = 0;
/**
- * Deserializes the given byte buffer into an instance of an update object.
+ * Deserializes the given stream into an instance of an update object.
*
- * @param type A data type that describes the content of the buffer.
- * @param buffer The byte buffer that contains the serialized update object.
- * @param version The serialization version of the object to deserialize.
+ * @param type A data type that describes the content of the stream.
+ * @param buffer The stream that contains the serialized update object.
*/
- virtual void deserialize(const DocumentTypeRepo& repo,
- const DataType& type,
- ByteBuffer& buffer, uint16_t version) = 0;
+ virtual void deserialize(const DocumentTypeRepo& repo, const DataType& type, nbostream & stream) = 0;
/** @return The operation type. */
ValueUpdateType getType() const {
@@ -105,8 +100,7 @@ public:
virtual void accept(UpdateVisitor &visitor) const = 0;
DECLARE_IDENTIFIABLE_ABSTRACT(ValueUpdate);
-
};
-} // document
+}
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/DocumentAccess.java b/documentapi/src/main/java/com/yahoo/documentapi/DocumentAccess.java
index 51b3b5a5756..2d6010fce41 100644
--- a/documentapi/src/main/java/com/yahoo/documentapi/DocumentAccess.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/DocumentAccess.java
@@ -42,7 +42,7 @@ import com.yahoo.config.subscription.ConfigSubscriber;
*
* @author bratseth
* @author Einar Rosenvinge
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public abstract class DocumentAccess {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/DocumentAccessParams.java b/documentapi/src/main/java/com/yahoo/documentapi/DocumentAccessParams.java
index fe55090494e..33cebd0836c 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/DocumentAccessParams.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/DocumentAccessParams.java
@@ -8,7 +8,7 @@ import java.util.Optional;
/**
* Superclass of the classes which contains the parameters for creating or opening a document access.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DocumentAccessParams {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/RemoveResponse.java b/documentapi/src/main/java/com/yahoo/documentapi/RemoveResponse.java
index 1b0ace11f10..502588a3d5f 100644
--- a/documentapi/src/main/java/com/yahoo/documentapi/RemoveResponse.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/RemoveResponse.java
@@ -5,7 +5,7 @@ package com.yahoo.documentapi;
* This response is provided for successful document remove operations. Use the
* wasFound() method to check whether or not the document was actually found.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RemoveResponse extends Response {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/ResponseHandler.java b/documentapi/src/main/java/com/yahoo/documentapi/ResponseHandler.java
index 481957ada3f..0471c44cb14 100644
--- a/documentapi/src/main/java/com/yahoo/documentapi/ResponseHandler.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/ResponseHandler.java
@@ -2,7 +2,7 @@
package com.yahoo.documentapi;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface ResponseHandler {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/SyncParameters.java b/documentapi/src/main/java/com/yahoo/documentapi/SyncParameters.java
index cbe322aef71..b1986680532 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/SyncParameters.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/SyncParameters.java
@@ -8,7 +8,7 @@ import java.util.Optional;
* Parameters for creating a synchronous session
*
* @author bjorncs
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class SyncParameters extends Parameters {
private final Duration defaultTimeout;
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/SyncSession.java b/documentapi/src/main/java/com/yahoo/documentapi/SyncSession.java
index ca55933e302..0ce0ccc2377 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/SyncSession.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/SyncSession.java
@@ -14,7 +14,7 @@ import java.time.Duration;
* <p>A session for synchronous access to a document repository. This class
* provides simple document access where throughput is not a concern.</p>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
* @author bjorncs
*/
public interface SyncSession extends Session {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/UpdateResponse.java b/documentapi/src/main/java/com/yahoo/documentapi/UpdateResponse.java
index 1f6c3ef4a71..96bf58c1e64 100644
--- a/documentapi/src/main/java/com/yahoo/documentapi/UpdateResponse.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/UpdateResponse.java
@@ -5,7 +5,7 @@ package com.yahoo.documentapi;
* This response is provided for successful document update operations. Use the
* wasFound() method to check whether or not the document was actually found.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class UpdateResponse extends Response {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/MessageBusSession.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/MessageBusSession.java
index c7a29b8b2b5..6022fbf6e39 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/MessageBusSession.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/MessageBusSession.java
@@ -4,7 +4,7 @@ package com.yahoo.documentapi.messagebus;
/**
* This class defines a common interface for message bus sessions.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface MessageBusSession {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/MessageBusSyncSession.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/MessageBusSyncSession.java
index e02b6029dcf..9a16cef2f84 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/MessageBusSyncSession.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/MessageBusSyncSession.java
@@ -30,7 +30,7 @@ import java.time.Duration;
/**
* An implementation of the SyncSession interface running over message bus.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
* @author bjorncs
*/
public class MessageBusSyncSession implements MessageBusSession, SyncSession, ReplyHandler {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/ANDPolicy.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/ANDPolicy.java
index 54bd2faa8af..7423792693b 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/ANDPolicy.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/ANDPolicy.java
@@ -15,7 +15,7 @@ import java.util.List;
* all configured recipients, or it can be configured using the policy parameter (i.e. a string following the name of
* the policy). Note that configured recipients take precedence over recipients configured in the parameter string.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class ANDPolicy implements DocumentProtocolRoutingPolicy {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/AbstractRoutableFactory.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/AbstractRoutableFactory.java
index 05e26822570..2a7633ebd70 100644
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/AbstractRoutableFactory.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/AbstractRoutableFactory.java
@@ -6,7 +6,7 @@ import com.yahoo.vespa.objects.Deserializer;
import com.yahoo.vespa.objects.Serializer;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public abstract class AbstractRoutableFactory implements RoutableFactory {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/BatchDocumentUpdateMessage.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/BatchDocumentUpdateMessage.java
index c540c2b957a..d8288a94ab6 100644
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/BatchDocumentUpdateMessage.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/BatchDocumentUpdateMessage.java
@@ -15,7 +15,7 @@ import java.util.ArrayList;
import java.util.List;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class BatchDocumentUpdateMessage extends DocumentMessage {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentMessage.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentMessage.java
index d40917afdd0..0db239b33bf 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentMessage.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentMessage.java
@@ -7,7 +7,7 @@ import com.yahoo.messagebus.Routable;
import com.yahoo.text.Utf8String;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class DocumentMessage extends Message {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentProtocol.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentProtocol.java
index c8f798d6ddf..b765b928869 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentProtocol.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentProtocol.java
@@ -30,7 +30,7 @@ import java.util.logging.Logger;
/**
* Implements the message bus protocol that is used by all components of Vespa.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DocumentProtocol implements Protocol {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentReply.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentReply.java
index 6c17e2ad1ce..dc669dccaa5 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentReply.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentReply.java
@@ -8,7 +8,7 @@ import com.yahoo.text.Utf8String;
* This class implements a generic document protocol reply that can be reused by document messages that require no
* special reply implementation while still allowing applications to distinguish between types.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DocumentReply extends Reply {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentRouteSelectorPolicy.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentRouteSelectorPolicy.java
index 29a9548579e..e48026ae7d7 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentRouteSelectorPolicy.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentRouteSelectorPolicy.java
@@ -23,7 +23,7 @@ import java.util.logging.Logger;
* names to a document selector and a feed name of every search cluster. This can very well be extended to include
* storage at a later time.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DocumentRouteSelectorPolicy
implements DocumentProtocolRoutingPolicy, ConfigSubscriber.SingleSubscriber<DocumentrouteselectorpolicyConfig> {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/ErrorPolicy.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/ErrorPolicy.java
index ed3378ac665..b0b662d1e70 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/ErrorPolicy.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/ErrorPolicy.java
@@ -10,7 +10,7 @@ import com.yahoo.messagebus.routing.RoutingContext;
* is invoked. This is useful for returning error states to the client instead of those auto-generated by mbus when a
* routing policy can not be created.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ErrorPolicy implements DocumentProtocolRoutingPolicy {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/ExternPolicy.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/ExternPolicy.java
index eb003fa6acb..5a68e5bda2a 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/ExternPolicy.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/ExternPolicy.java
@@ -18,7 +18,7 @@ import java.util.List;
* This policy implements the necessary logic to communicate with an external Vespa application and resolve its list of
* recipients using that other application's slobrok servers.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ExternPolicy implements DocumentProtocolRoutingPolicy {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetBucketStateMessage.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetBucketStateMessage.java
index 3ca55549b39..f9b715c9937 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetBucketStateMessage.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetBucketStateMessage.java
@@ -7,7 +7,7 @@ import com.yahoo.document.BucketId;
* This message is a request to return the state of a given bucket. The corresponding reply is {@link
* GetBucketStateReply}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GetBucketStateMessage extends DocumentMessage {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetBucketStateReply.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetBucketStateReply.java
index 337a4f73b57..024aceceb5b 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetBucketStateReply.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetBucketStateReply.java
@@ -7,7 +7,7 @@ import java.util.List;
/**
* This is a reply to a {@link GetBucketStateMessage}. It contains the state of the bucket id requested by the message.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GetBucketStateReply extends DocumentReply {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetDocumentMessage.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetDocumentMessage.java
index a7c3e400e4c..cd115cfbf0e 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetDocumentMessage.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetDocumentMessage.java
@@ -6,7 +6,7 @@ import com.yahoo.document.DocumentId;
import java.util.Arrays;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GetDocumentMessage extends DocumentMessage {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetDocumentReply.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetDocumentReply.java
index 95e53887d2e..9d9695e71fc 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetDocumentReply.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetDocumentReply.java
@@ -7,7 +7,7 @@ import com.yahoo.document.serialization.DocumentDeserializer;
import java.nio.ByteBuffer;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GetDocumentReply extends DocumentAcceptedReply {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/LocalServicePolicy.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/LocalServicePolicy.java
index 2b71f403382..e27479a4995 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/LocalServicePolicy.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/LocalServicePolicy.java
@@ -13,7 +13,7 @@ import java.util.Map;
/**
* This policy implements the logic to prefer local services that matches a slobrok pattern.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class LocalServicePolicy implements DocumentProtocolRoutingPolicy {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/PutDocumentMessage.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/PutDocumentMessage.java
index 469bef6927d..ec24798f7f2 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/PutDocumentMessage.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/PutDocumentMessage.java
@@ -10,7 +10,7 @@ import java.nio.ByteBuffer;
import java.util.Arrays;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class PutDocumentMessage extends TestAndSetMessage {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RemoveDocumentMessage.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RemoveDocumentMessage.java
index f122fdf31c3..101fee73fc7 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RemoveDocumentMessage.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RemoveDocumentMessage.java
@@ -8,7 +8,7 @@ import com.yahoo.document.TestAndSetCondition;
import java.util.Arrays;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RemoveDocumentMessage extends TestAndSetMessage {
private DocumentRemove remove = null;
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RemoveDocumentReply.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RemoveDocumentReply.java
index bef7cc8e8cc..167183acec6 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RemoveDocumentReply.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RemoveDocumentReply.java
@@ -2,7 +2,7 @@
package com.yahoo.documentapi.messagebus.protocol;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RemoveDocumentReply extends WriteDocumentReply {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoundRobinPolicy.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoundRobinPolicy.java
index 59895e0b34e..32becdb43fe 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoundRobinPolicy.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoundRobinPolicy.java
@@ -19,7 +19,7 @@ import java.util.Map;
/**
* This policy implements round-robin selection of the configured recipients that are currently registered in slobrok.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RoundRobinPolicy implements DocumentProtocolRoutingPolicy {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutableFactories50.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutableFactories50.java
index 0bf83ab290c..dfc87b77474 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutableFactories50.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutableFactories50.java
@@ -29,7 +29,7 @@ public abstract class RoutableFactories50 {
* Implements the shared factory logic required for {@link DocumentMessage} objects, and it offers a more convenient
* interface for implementing {@link RoutableFactory}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public static abstract class DocumentMessageFactory extends AbstractRoutableFactory {
@@ -85,7 +85,7 @@ public abstract class RoutableFactories50 {
* Implements the shared factory logic required for {@link DocumentReply} objects, and it offers a more convenient
* interface for implementing {@link RoutableFactory}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public static abstract class DocumentReplyFactory extends AbstractRoutableFactory {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutableFactory.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutableFactory.java
index 96dd206ec73..db17309aab9 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutableFactory.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutableFactory.java
@@ -16,7 +16,7 @@ import com.yahoo.messagebus.Routable;
* you may NOT share a factory across multiple routable types. To share serialization logic between factory use a common
* superclass or composition with a common serialization utility.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface RoutableFactory {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutableRepository.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutableRepository.java
index 91d6694eacf..04f2188a594 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutableRepository.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutableRepository.java
@@ -21,7 +21,7 @@ import java.util.stream.Stream;
* RoutableFactory}. It is owned and accessed through a {@link DocumentProtocol} instance. This class uses a factory
* cache to reduce the latency of matching version specifications to actual versions when resolving factories.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
final class RoutableRepository {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutingPolicyFactories.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutingPolicyFactories.java
index 6c13b7468c7..6954d8f3a1d 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutingPolicyFactories.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutingPolicyFactories.java
@@ -2,7 +2,7 @@
package com.yahoo.documentapi.messagebus.protocol;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class RoutingPolicyFactories {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutingPolicyFactory.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutingPolicyFactory.java
index 9b1065cfce3..15967f9e693 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutingPolicyFactory.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutingPolicyFactory.java
@@ -7,7 +7,7 @@ import com.yahoo.messagebus.routing.RoutingPolicy;
* This interface defines the necessary methods of a routing policy factory that can be plugged into a {@link
* DocumentProtocol} using the {@link DocumentProtocol#putRoutingPolicyFactory(String, RoutingPolicyFactory)} method.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface RoutingPolicyFactory {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutingPolicyRepository.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutingPolicyRepository.java
index e706a0ace39..05bbb919805 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutingPolicyRepository.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/RoutingPolicyRepository.java
@@ -10,7 +10,7 @@ import java.util.logging.Logger;
import java.util.concurrent.ConcurrentHashMap;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
class RoutingPolicyRepository {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/SubsetServicePolicy.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/SubsetServicePolicy.java
index 2b341ff2600..d6499c196f4 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/SubsetServicePolicy.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/SubsetServicePolicy.java
@@ -15,7 +15,7 @@ import java.util.logging.Logger;
/**
* This policy implements the logic to select a subset of services that matches a slobrok pattern.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SubsetServicePolicy implements DocumentProtocolRoutingPolicy {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/UpdateDocumentMessage.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/UpdateDocumentMessage.java
index eb5f207c5f5..a9bd6852e42 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/UpdateDocumentMessage.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/UpdateDocumentMessage.java
@@ -9,7 +9,7 @@ import java.nio.ByteBuffer;
import java.util.Arrays;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class UpdateDocumentMessage extends TestAndSetMessage {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/UpdateDocumentReply.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/UpdateDocumentReply.java
index 9d1fd058d52..7c9b42b4f4c 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/UpdateDocumentReply.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/UpdateDocumentReply.java
@@ -2,7 +2,7 @@
package com.yahoo.documentapi.messagebus.protocol;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class UpdateDocumentReply extends WriteDocumentReply {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/WrongDistributionReply.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/WrongDistributionReply.java
index c206c30c258..bae78233df2 100644
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/WrongDistributionReply.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/WrongDistributionReply.java
@@ -2,7 +2,7 @@
package com.yahoo.documentapi.messagebus.protocol;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class WrongDistributionReply extends DocumentReply {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/systemstate/rule/Argument.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/systemstate/rule/Argument.java
index e22abcd9934..c8b8e80f905 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/systemstate/rule/Argument.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/systemstate/rule/Argument.java
@@ -2,7 +2,7 @@
package com.yahoo.documentapi.messagebus.systemstate.rule;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Argument {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/systemstate/rule/Location.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/systemstate/rule/Location.java
index db297f616f1..bb5bc55778d 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/systemstate/rule/Location.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/systemstate/rule/Location.java
@@ -6,7 +6,7 @@ import java.util.List;
import java.util.Arrays;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Location {
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/systemstate/rule/NodeState.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/systemstate/rule/NodeState.java
index d118210b7ea..69a5346adb5 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/systemstate/rule/NodeState.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/systemstate/rule/NodeState.java
@@ -12,7 +12,7 @@ import java.util.Map;
import java.util.logging.Logger;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NodeState {
diff --git a/documentapi/src/main/javacc/StateParser.jj b/documentapi/src/main/javacc/StateParser.jj
index 72e8f4dcb87..659d06fa5a4 100755
--- a/documentapi/src/main/javacc/StateParser.jj
+++ b/documentapi/src/main/javacc/StateParser.jj
@@ -3,7 +3,7 @@
* A system state parser.
* When this file is changed, do "ant compileparser" to rebuild the parser.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id: StateParser.jj,v 1.7 2007-11-15 13:24:45 simon Exp $
*/
options {
diff --git a/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/DocumentProtocolTest.java b/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/DocumentProtocolTest.java
index fdaa7d6df1f..50778e00519 100644
--- a/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/DocumentProtocolTest.java
+++ b/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/DocumentProtocolTest.java
@@ -11,7 +11,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class DocumentProtocolTest {
diff --git a/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/LoadBalancerTestCase.java b/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/LoadBalancerTestCase.java
index 1eae82f344f..284e338394f 100644
--- a/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/LoadBalancerTestCase.java
+++ b/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/LoadBalancerTestCase.java
@@ -14,7 +14,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class LoadBalancerTestCase {
diff --git a/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/Messages50TestCase.java b/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/Messages50TestCase.java
index 0ef83c6b5af..3d02aeee54d 100644
--- a/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/Messages50TestCase.java
+++ b/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/Messages50TestCase.java
@@ -22,7 +22,7 @@ import java.util.Map;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Messages50TestCase extends MessagesTestBase {
diff --git a/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/Messages51TestCase.java b/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/Messages51TestCase.java
index 1c55b5f8e69..862244236f8 100644
--- a/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/Messages51TestCase.java
+++ b/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/Messages51TestCase.java
@@ -16,7 +16,7 @@ import java.util.Map;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Messages51TestCase extends Messages50TestCase {
diff --git a/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/MessagesTestBase.java b/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/MessagesTestBase.java
index 9e355fa3381..9f6ed39f599 100755
--- a/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/MessagesTestBase.java
+++ b/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/MessagesTestBase.java
@@ -17,7 +17,7 @@ import java.util.*;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class MessagesTestBase {
diff --git a/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/PolicyFactoryTestCase.java b/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/PolicyFactoryTestCase.java
index a32d6022fef..8f2fee880e6 100755
--- a/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/PolicyFactoryTestCase.java
+++ b/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/PolicyFactoryTestCase.java
@@ -25,7 +25,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class PolicyFactoryTestCase {
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 9db4d1c0d2e..1192f8bc7ad 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
@@ -24,7 +24,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings("deprecation")
public class PolicyTestCase {
diff --git a/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/PolicyTestFrame.java b/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/PolicyTestFrame.java
index d77f5f4e39e..875ef276b01 100755
--- a/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/PolicyTestFrame.java
+++ b/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/PolicyTestFrame.java
@@ -30,7 +30,7 @@ import static org.junit.Assert.fail;
* This is a utility class to allow easier policy test cases. The most important reason to use this is to make sure that
* each test uses a "clean" mbus and slobrok instance.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings("deprecation")
public class PolicyTestFrame {
diff --git a/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/PriorityTestCase.java b/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/PriorityTestCase.java
index 93b3fbb60b3..32b9785c7e7 100644
--- a/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/PriorityTestCase.java
+++ b/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/PriorityTestCase.java
@@ -17,7 +17,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class PriorityTestCase {
diff --git a/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/RoutableFactoryTestCase.java b/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/RoutableFactoryTestCase.java
index a01787004c9..89698e44705 100755
--- a/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/RoutableFactoryTestCase.java
+++ b/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/RoutableFactoryTestCase.java
@@ -35,7 +35,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class RoutableFactoryTestCase {
diff --git a/documentapi/src/tests/messagebus/messagebus_test.cpp b/documentapi/src/tests/messagebus/messagebus_test.cpp
index cd2820d8c05..dff3f3f1bac 100644
--- a/documentapi/src/tests/messagebus/messagebus_test.cpp
+++ b/documentapi/src/tests/messagebus/messagebus_test.cpp
@@ -59,7 +59,7 @@ void Test::testMessage() {
// Test one update.
UpdateDocumentMessage upd1(
document::DocumentUpdate::SP(
- new document::DocumentUpdate(*testdoc_type,
+ new document::DocumentUpdate(*_repo, *testdoc_type,
document::DocumentId(document::DocIdString(
"testdoc", "testme1")))));
@@ -80,7 +80,7 @@ void Test::testMessage() {
// Compare to another.
UpdateDocumentMessage upd2(
document::DocumentUpdate::SP(
- new document::DocumentUpdate(*testdoc_type,
+ new document::DocumentUpdate(*_repo, *testdoc_type,
document::DocumentId(document::DocIdString(
"testdoc", "testme2")))));
EXPECT_TRUE(!(upd1.getDocumentUpdate().getId() == upd2.getDocumentUpdate().getId()));
diff --git a/documentapi/src/tests/messages/messages50test.cpp b/documentapi/src/tests/messages/messages50test.cpp
index 48728ff6057..6705277d5c3 100644
--- a/documentapi/src/tests/messages/messages50test.cpp
+++ b/documentapi/src/tests/messages/messages50test.cpp
@@ -672,8 +672,7 @@ Messages50Test::testUpdateDocumentMessage()
{
const DocumentTypeRepo &repo = getTypeRepo();
const document::DocumentType &docType = *repo.getDocumentType("testdoc");
- document::DocumentUpdate::SP
- upd(new document::DocumentUpdate(docType, document::DocumentId("doc:scheme:")));
+ auto upd(std::make_shared<document::DocumentUpdate>(repo, docType, document::DocumentId("doc:scheme:")));
upd->addFieldPathUpdate(document::FieldPathUpdate::CP(
new document::RemoveFieldPathUpdate("intfield", "testdoc.intfield > 0")));
UpdateDocumentMessage msg(upd);
@@ -682,7 +681,7 @@ Messages50Test::testUpdateDocumentMessage()
EXPECT_EQUAL(MESSAGE_BASE_LENGTH + 89u, serialize("UpdateDocumentMessage", msg));
for (uint32_t lang = 0; lang < NUM_LANGUAGES; ++lang) {
mbus::Routable::UP obj = deserialize("UpdateDocumentMessage", DocumentProtocol::MESSAGE_UPDATEDOCUMENT, lang);
- if (EXPECT_TRUE(obj.get() != NULL)) {
+ if (EXPECT_TRUE(obj.get() != nullptr)) {
UpdateDocumentMessage &ref = static_cast<UpdateDocumentMessage&>(*obj);
EXPECT_EQUAL(*upd, ref.getDocumentUpdate());
EXPECT_EQUAL(666u, ref.getOldTimestamp());
@@ -703,21 +702,21 @@ Messages50Test::testBatchDocumentUpdateMessage()
{
document::DocumentUpdate::SP upd;
- upd.reset(new document::DocumentUpdate(docType, document::DocumentId("userdoc:footype:1234:foo")));
+ upd.reset(new document::DocumentUpdate(repo, docType, document::DocumentId("userdoc:footype:1234:foo")));
upd->addFieldPathUpdate(document::FieldPathUpdate::CP(
new document::RemoveFieldPathUpdate("intfield", "testdoc.intfield > 0")));
msg.addUpdate(upd);
}
{
document::DocumentUpdate::SP upd;
- upd.reset(new document::DocumentUpdate(docType, document::DocumentId("orderdoc(32,17):footype:1234:123456789:foo")));
+ upd.reset(new document::DocumentUpdate(repo, docType, document::DocumentId("orderdoc(32,17):footype:1234:123456789:foo")));
upd->addFieldPathUpdate(document::FieldPathUpdate::CP(
new document::RemoveFieldPathUpdate("intfield", "testdoc.intfield > 0")));
msg.addUpdate(upd);
}
try {
document::DocumentUpdate::SP upd;
- upd.reset(new document::DocumentUpdate(docType, document::DocumentId("userdoc:footype:5678:foo")));
+ upd.reset(new document::DocumentUpdate(repo, docType, document::DocumentId("userdoc:footype:5678:foo")));
upd->addFieldPathUpdate(document::FieldPathUpdate::CP(
new document::RemoveFieldPathUpdate("intfield", "testdoc.intfield > 0")));
msg.addUpdate(upd);
@@ -726,7 +725,7 @@ Messages50Test::testBatchDocumentUpdateMessage()
}
try {
document::DocumentUpdate::SP upd;
- upd.reset(new document::DocumentUpdate(docType, document::DocumentId("groupdoc:footype:hable:foo")));
+ upd.reset(new document::DocumentUpdate(repo, docType, document::DocumentId("groupdoc:footype:hable:foo")));
upd->addFieldPathUpdate(document::FieldPathUpdate::CP(
new document::RemoveFieldPathUpdate("intfield", "testdoc.intfield > 0")));
msg.addUpdate(upd);
diff --git a/documentapi/src/tests/messages/messages52test.cpp b/documentapi/src/tests/messages/messages52test.cpp
index eaf8bbec8c4..8f5d7381500 100644
--- a/documentapi/src/tests/messages/messages52test.cpp
+++ b/documentapi/src/tests/messages/messages52test.cpp
@@ -91,7 +91,7 @@ Messages52Test::testUpdateDocumentMessage()
const DocumentTypeRepo & repo = getTypeRepo();
const document::DocumentType & docType = *repo.getDocumentType("testdoc");
- auto docUpdate = std::make_shared<document::DocumentUpdate>(docType, document::DocumentId("doc:scheme:"));
+ auto docUpdate = std::make_shared<document::DocumentUpdate>(repo, docType, document::DocumentId("doc:scheme:"));
docUpdate->addFieldPathUpdate(document::FieldPathUpdate::CP(
new document::RemoveFieldPathUpdate("intfield", "testdoc.intfield > 0")));
diff --git a/documentapi/src/tests/policies/policies_test.cpp b/documentapi/src/tests/policies/policies_test.cpp
index 7852136dfc7..3e804c30415 100644
--- a/documentapi/src/tests/policies/policies_test.cpp
+++ b/documentapi/src/tests/policies/policies_test.cpp
@@ -621,7 +621,7 @@ Test::testDocumentRouteSelector()
EXPECT_TRUE(frame.testSelect(StringList().add("foo").add("bar")));
frame.setMessage(make_unique<UpdateDocumentMessage>(
- make_shared<DocumentUpdate>(*_docType, DocumentId("doc:scheme:"))));
+ make_shared<DocumentUpdate>(*_repo, *_docType, DocumentId("doc:scheme:"))));
EXPECT_TRUE(frame.testSelect(StringList().add("foo")));
put = make_unique<PutDocumentMessage>(make_shared<Document>(*_docType, DocumentId("doc:scheme:")));
@@ -650,7 +650,7 @@ Test::testDocumentRouteSelectorIgnore()
EXPECT_EQUAL(0u, reply->getNumErrors());
frame.setMessage(make_unique<UpdateDocumentMessage>(
- make_shared<DocumentUpdate>(*_docType, DocumentId("doc:scheme:"))));
+ make_shared<DocumentUpdate>(*_repo, *_docType, DocumentId("doc:scheme:"))));
EXPECT_TRUE(frame.testSelect(StringList().add("docproc/cluster.foo")));
}
diff --git a/documentapi/src/vespa/documentapi/messagebus/policies/andpolicy.h b/documentapi/src/vespa/documentapi/messagebus/policies/andpolicy.h
index f700f087732..2858fc28807 100644
--- a/documentapi/src/vespa/documentapi/messagebus/policies/andpolicy.h
+++ b/documentapi/src/vespa/documentapi/messagebus/policies/andpolicy.h
@@ -13,7 +13,7 @@ namespace documentapi {
* all configured recipients, or it can be configured using the policy parameter (i.e. a string following the name of
* the policy). Note that configured recipients take precedence over recipients configured in the parameter string.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class ANDPolicy : public mbus::IRoutingPolicy {
diff --git a/documentapi/src/vespa/documentapi/messagebus/policies/errorpolicy.cpp b/documentapi/src/vespa/documentapi/messagebus/policies/errorpolicy.cpp
index 1e9911ecf77..7b87e7df834 100644
--- a/documentapi/src/vespa/documentapi/messagebus/policies/errorpolicy.cpp
+++ b/documentapi/src/vespa/documentapi/messagebus/policies/errorpolicy.cpp
@@ -3,6 +3,8 @@
#include "errorpolicy.h"
#include <vespa/documentapi/messagebus/documentprotocol.h>
#include <vespa/messagebus/emptyreply.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".documentapi.messagebus.policies.error_policy");
namespace documentapi {
@@ -19,7 +21,7 @@ ErrorPolicy::select(mbus::RoutingContext &ctx)
void
ErrorPolicy::merge(mbus::RoutingContext &)
{
- abort();
+ LOG_ABORT("should not be reached");
}
}
diff --git a/documentapi/src/vespa/documentapi/messagebus/policies/localservicepolicy.h b/documentapi/src/vespa/documentapi/messagebus/policies/localservicepolicy.h
index 9814dbf35bf..4dd22a77069 100644
--- a/documentapi/src/vespa/documentapi/messagebus/policies/localservicepolicy.h
+++ b/documentapi/src/vespa/documentapi/messagebus/policies/localservicepolicy.h
@@ -13,7 +13,7 @@ namespace documentapi {
/**
* This policy implements the logic to prefer local services that matches a slobrok pattern.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class LocalServicePolicy : public mbus::IRoutingPolicy {
diff --git a/documentapi/src/vespa/documentapi/messagebus/policies/roundrobinpolicy.h b/documentapi/src/vespa/documentapi/messagebus/policies/roundrobinpolicy.h
index 0919c41045d..0bfd2f04ea7 100644
--- a/documentapi/src/vespa/documentapi/messagebus/policies/roundrobinpolicy.h
+++ b/documentapi/src/vespa/documentapi/messagebus/policies/roundrobinpolicy.h
@@ -12,7 +12,7 @@ namespace documentapi {
/**
* This policy implements the logic to prefer round robins that matches a slobrok pattern.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class RoundRobinPolicy : public mbus::IRoutingPolicy {
diff --git a/documentapi/src/vespa/documentapi/messagebus/policies/subsetservicepolicy.h b/documentapi/src/vespa/documentapi/messagebus/policies/subsetservicepolicy.h
index 983d41549f4..bf44afa318f 100644
--- a/documentapi/src/vespa/documentapi/messagebus/policies/subsetservicepolicy.h
+++ b/documentapi/src/vespa/documentapi/messagebus/policies/subsetservicepolicy.h
@@ -13,7 +13,7 @@ namespace documentapi {
/**
* This policy implements the logic to select a subset of services that matches a slobrok pattern.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class SubsetServicePolicy : public mbus::IRoutingPolicy {
diff --git a/documentapi/src/vespa/documentapi/messagebus/routablefactories50.h b/documentapi/src/vespa/documentapi/messagebus/routablefactories50.h
index e052eff78c3..12d5c560786 100644
--- a/documentapi/src/vespa/documentapi/messagebus/routablefactories50.h
+++ b/documentapi/src/vespa/documentapi/messagebus/routablefactories50.h
@@ -42,12 +42,8 @@ DocumentMessage::UP
decodeMessage(const FactoryType * self, document::ByteBuffer & buf) {
auto msg = std::make_unique<MessageType>();
ScopedApproxSizeSetter sizeSetter(*msg, buf);
-
self->decodeInto(*msg, buf);
-
- // Doing an explicit move here to force converting result to an rvalue.
- // This is done automatically in GCC >= 5.
- return std::move(msg);
+ return msg;
}
/**
diff --git a/documentgen-test/pom.xml b/documentgen-test/pom.xml
index d009ef3d592..fb6515c3f35 100644
--- a/documentgen-test/pom.xml
+++ b/documentgen-test/pom.xml
@@ -20,12 +20,19 @@
<scope>test</scope>
</dependency>
<dependency>
+ <!-- TODO: Explicitly list deps instead - container-dev is for bundle development. -->
<groupId>com.yahoo.vespa</groupId>
<artifactId>container-dev</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
+ <!-- TODO: Excluded from container-dev. Remove when deps are explicitly listed. -->
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>linguistics</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
<groupId>com.yahoo.vespa</groupId>
<artifactId>config-model</artifactId>
<version>${project.version}</version>
diff --git a/eval/src/vespa/eval/eval/aggr.cpp b/eval/src/vespa/eval/eval/aggr.cpp
index f6f04cde6be..8aacac64041 100644
--- a/eval/src/vespa/eval/eval/aggr.cpp
+++ b/eval/src/vespa/eval/eval/aggr.cpp
@@ -3,6 +3,9 @@
#include "aggr.h"
#include <vespa/vespalib/util/stash.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".eval.eval.aggr");
+
namespace vespalib {
namespace eval {
@@ -117,7 +120,7 @@ Aggregator::create(Aggr aggr, Stash &stash)
case Aggr::MAX: return stash.create<Max>();
case Aggr::MIN: return stash.create<Min>();
}
- abort();
+ LOG_ABORT("should not be reached");
}
} // namespace vespalib::eval
diff --git a/eval/src/vespa/eval/eval/basic_nodes.h b/eval/src/vespa/eval/eval/basic_nodes.h
index ebf65178b99..95e6cdf34f7 100644
--- a/eval/src/vespa/eval/eval/basic_nodes.h
+++ b/eval/src/vespa/eval/eval/basic_nodes.h
@@ -2,6 +2,7 @@
#pragma once
+#include <vespa/vespalib/util/hdr_abort.h>
#include <vespa/vespalib/stllike/string.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/util/string_hash.h>
@@ -80,7 +81,7 @@ const T *as(const Node &node) { return dynamic_cast<const T *>(&node); }
struct Leaf : public Node {
size_t num_children() const override { return 0; }
const Node &get_child(size_t) const override {
- abort();
+ HDR_ABORT("should not be reached");
}
void detach_children(NodeHandler &) override {}
};
diff --git a/eval/src/vespa/eval/eval/llvm/compiled_function.cpp b/eval/src/vespa/eval/eval/llvm/compiled_function.cpp
index a696f16f849..4c911ee6e44 100644
--- a/eval/src/vespa/eval/eval/llvm/compiled_function.cpp
+++ b/eval/src/vespa/eval/eval/llvm/compiled_function.cpp
@@ -10,6 +10,9 @@
#include <vespa/vespalib/util/benchmark_timer.h>
#include <vespa/vespalib/util/approx.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".eval.eval.llvm.compiled_function");
+
namespace vespalib {
namespace eval {
@@ -113,7 +116,7 @@ CompiledFunction::estimate_cost_us(const std::vector<double> &params, double bud
auto baseline = [&](){empty(params[0], params[1], params[2], params[3], params[4]);};
return BenchmarkTimer::benchmark(actual, baseline, budget) * 1000.0 * 1000.0;
}
- abort();
+ LOG_ABORT("should not be reached");
}
Function::Issues
diff --git a/eval/src/vespa/eval/eval/tensor_function.cpp b/eval/src/vespa/eval/eval/tensor_function.cpp
index 3f79ac848ce..10e589c832c 100644
--- a/eval/src/vespa/eval/eval/tensor_function.cpp
+++ b/eval/src/vespa/eval/eval/tensor_function.cpp
@@ -9,6 +9,9 @@
#include "visit_stuff.h"
#include <vespa/vespalib/objects/objectdumper.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".eval.eval.tensor_function");
+
namespace vespalib {
namespace eval {
@@ -292,7 +295,7 @@ If::compile_self(Stash &) const
{
// 'if' is handled directly by compile_tensor_function to enable
// lazy-evaluation of true/false sub-expressions.
- abort();
+ LOG_ABORT("should not be reached");
}
void
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 9baaa596d9f..cd874186e46 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
@@ -6,6 +6,9 @@
#include <vespa/eval/eval/operation.h>
#include <vespa/eval/eval/value.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".eval.tensor.dense.add_dimension_optimizer");
+
namespace vespalib::tensor {
using eval::ValueType;
diff --git a/eval/src/vespa/eval/tensor/serialization/typed_binary_format.cpp b/eval/src/vespa/eval/tensor/serialization/typed_binary_format.cpp
index 5db4f0aeb12..fe35ce4c831 100644
--- a/eval/src/vespa/eval/tensor/serialization/typed_binary_format.cpp
+++ b/eval/src/vespa/eval/tensor/serialization/typed_binary_format.cpp
@@ -10,6 +10,9 @@
#include <vespa/eval/eval/simple_tensor.h>
#include <vespa/eval/tensor/wrapped_simple_tensor.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".eval.tensor.serialization.typed_binary_format");
+
using vespalib::nbostream;
namespace vespalib {
@@ -48,7 +51,7 @@ TypedBinaryFormat::deserialize(nbostream &stream)
stream.adjustReadPos(read_pos - stream.rp());
return std::make_unique<WrappedSimpleTensor>(eval::SimpleTensor::decode(stream));
}
- abort();
+ LOG_ABORT("should not be reached");
}
diff --git a/eval/src/vespa/eval/tensor/sparse/direct_sparse_tensor_builder.h b/eval/src/vespa/eval/tensor/sparse/direct_sparse_tensor_builder.h
index 9ec98b2c11d..89370458136 100644
--- a/eval/src/vespa/eval/tensor/sparse/direct_sparse_tensor_builder.h
+++ b/eval/src/vespa/eval/tensor/sparse/direct_sparse_tensor_builder.h
@@ -2,6 +2,7 @@
#pragma once
+#include <vespa/vespalib/util/hdr_abort.h>
#include <vespa/eval/tensor/direct_tensor_builder.h>
#include "sparse_tensor.h"
#include "sparse_tensor_address_builder.h"
@@ -105,7 +106,7 @@ public:
void insertCell(SparseTensorAddressRef address, double value) {
// This address should not already exist and a new cell should be inserted.
- insertCell(address, value, [](double, double) -> double { abort(); });
+ insertCell(address, value, [](double, double) -> double { HDR_ABORT("should not be reached"); });
}
template <class Function>
@@ -116,7 +117,7 @@ public:
void insertCell(SparseTensorAddressBuilder &address, double value) {
// This address should not already exist and a new cell should be inserted.
- insertCell(address.getAddressRef(), value, [](double, double) -> double { abort(); });
+ insertCell(address.getAddressRef(), value, [](double, double) -> double { HDR_ABORT("should not be reached"); });
}
eval::ValueType &fast_type() { return _type; }
diff --git a/eval/src/vespa/eval/tensor/wrapped_simple_tensor.cpp b/eval/src/vespa/eval/tensor/wrapped_simple_tensor.cpp
index 463105b7c1f..5c6c70099ad 100644
--- a/eval/src/vespa/eval/tensor/wrapped_simple_tensor.cpp
+++ b/eval/src/vespa/eval/tensor/wrapped_simple_tensor.cpp
@@ -7,6 +7,9 @@
#include <vespa/eval/eval/tensor_spec.h>
#include <vespa/vespalib/util/stringfmt.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".eval.tensor.wrapped_simple_tensor");
+
namespace vespalib::tensor {
bool
@@ -59,22 +62,19 @@ WrappedSimpleTensor::clone() const
Tensor::UP
WrappedSimpleTensor::apply(const CellFunction &) const
{
- abort();
- return Tensor::UP();
+ LOG_ABORT("should not be reached");
}
Tensor::UP
WrappedSimpleTensor::join(join_fun_t, const Tensor &) const
{
- abort();
- return Tensor::UP();
+ LOG_ABORT("should not be reached");
}
Tensor::UP
WrappedSimpleTensor::reduce(join_fun_t, const std::vector<vespalib::string> &) const
{
- abort();
- return Tensor::UP();
+ LOG_ABORT("should not be reached");
}
} // namespace vespalib::tensor
diff --git a/fastlib/src/vespa/fastlib/io/bufferedfile.h b/fastlib/src/vespa/fastlib/io/bufferedfile.h
index db96d0d6a42..ccb9644b49b 100644
--- a/fastlib/src/vespa/fastlib/io/bufferedfile.h
+++ b/fastlib/src/vespa/fastlib/io/bufferedfile.h
@@ -2,6 +2,7 @@
#pragma once
+#include <vespa/vespalib/util/hdr_abort.h>
#include <vespa/vespalib/util/alloc.h>
#include <vespa/fastos/file.h>
@@ -87,7 +88,7 @@ public:
* or reads as much as possible if the (rest of) the file
* is smaller than the buffer.
* Caution: If the amount read is smaller than the expected
- * amount, the method will abort().
+ * amount, the method will abort.
*/
void fillReadBuf(void);
/**
diff --git a/fat-model-dependencies/pom.xml b/fat-model-dependencies/pom.xml
index 0011d108b98..0421f0bbd01 100644
--- a/fat-model-dependencies/pom.xml
+++ b/fat-model-dependencies/pom.xml
@@ -26,6 +26,13 @@
<groupId>com.yahoo.vespa</groupId>
<artifactId>provided-dependencies</artifactId>
<version>${project.version}</version>
+ <exclusions>
+ <exclusion>
+ <!-- Not needed runtime, and a multi-release jar which is currently not supported by maven-bundle-plugin (really bndtools) -->
+ <groupId>javax.xml.bind</groupId>
+ <artifactId>jaxb-api</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
<dependency>
<groupId>commons-io</groupId>
@@ -204,10 +211,6 @@
<version>${project.version}</version>
</dependency>
<dependency>
- <groupId>org.scalatest</groupId>
- <artifactId>scalatest_${scala.major-version}</artifactId>
- </dependency>
- <dependency>
<groupId>com.yahoo.vespa</groupId>
<artifactId>jdisc_http_service</artifactId>
<version>${project.version}</version>
diff --git a/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/FileAcquirerFactory.java b/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/FileAcquirerFactory.java
index 88858d568d5..d8ea45e716d 100644
--- a/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/FileAcquirerFactory.java
+++ b/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/FileAcquirerFactory.java
@@ -4,7 +4,7 @@ package com.yahoo.filedistribution.fileacquirer;
/**
* Hides the real file acquirer type from 3rd party developers.
* Not intended to be used by 3rd parties.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class FileAcquirerFactory {
public static FileAcquirer create(String configId) {
diff --git a/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/FileReferenceDoesNotExistException.java b/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/FileReferenceDoesNotExistException.java
index e8d1666bb61..3ef6072eb29 100644
--- a/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/FileReferenceDoesNotExistException.java
+++ b/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/FileReferenceDoesNotExistException.java
@@ -2,7 +2,7 @@
package com.yahoo.filedistribution.fileacquirer;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class FileReferenceDoesNotExistException extends RuntimeException {
diff --git a/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/FileReferenceRemovedException.java b/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/FileReferenceRemovedException.java
index 93a81e5b527..e019318c4dd 100644
--- a/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/FileReferenceRemovedException.java
+++ b/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/FileReferenceRemovedException.java
@@ -2,7 +2,7 @@
package com.yahoo.filedistribution.fileacquirer;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class FileReferenceRemovedException extends RuntimeException {
public final String fileReference;
diff --git a/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/MockFileAcquirer.java b/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/MockFileAcquirer.java
index f07530cac8a..25732d2dcc8 100644
--- a/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/MockFileAcquirer.java
+++ b/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/MockFileAcquirer.java
@@ -11,7 +11,7 @@ import java.util.concurrent.TimeUnit;
/**
* For use when testing searchers that uses file distribution.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public abstract class MockFileAcquirer implements FileAcquirer {
/** Creates a FileAcquirer that always returns the given file. **/
diff --git a/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/TimeoutException.java b/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/TimeoutException.java
index e6d12a6a0a5..a5d3d8fb768 100644
--- a/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/TimeoutException.java
+++ b/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/TimeoutException.java
@@ -2,7 +2,7 @@
package com.yahoo.filedistribution.fileacquirer;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class TimeoutException extends RuntimeException {
diff --git a/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/Timer.java b/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/Timer.java
index 6a044740758..e4ce5ae5e83 100644
--- a/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/Timer.java
+++ b/fileacquirer/src/main/java/com/yahoo/filedistribution/fileacquirer/Timer.java
@@ -5,7 +5,7 @@ import java.util.concurrent.TimeUnit;
/**
* Handles timeout of a task.
- * @author tonytv
+ * @author Tony Vaagenes
*/
class Timer {
private final long endTime;
diff --git a/fileacquirer/src/test/java/MockFileAcquirerTest.java b/fileacquirer/src/test/java/MockFileAcquirerTest.java
index b357b84a4fd..b03c3f6ed3d 100644
--- a/fileacquirer/src/test/java/MockFileAcquirerTest.java
+++ b/fileacquirer/src/test/java/MockFileAcquirerTest.java
@@ -16,7 +16,7 @@ import static org.junit.Assert.assertThat;
/**
* Test of public API of MockFileAcquirer, since it is intended to be used by 3rd parties.
* Do not place it in the same package as MockFileAcquirer.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class MockFileAcquirerTest {
@org.junit.Test
diff --git a/filedistribution/src/main/sh/vespa-status-filedistribution.sh b/filedistribution/src/main/sh/vespa-status-filedistribution.sh
index e6ab8d40678..941319b1b4b 100644
--- a/filedistribution/src/main/sh/vespa-status-filedistribution.sh
+++ b/filedistribution/src/main/sh/vespa-status-filedistribution.sh
@@ -80,7 +80,7 @@ if [ "$cloudconfig_server__region" != "" ]; then
fi
defaults="--tenant default --application default --instance default"
-jvmoptions="-XX:MaxJavaStackTraceDepth=-1 $(getJavaOptionsIPV46) -Xms48m -Xmx48m"
+jvmoptions="-XX:MaxJavaStackTraceDepth=1000000 $(getJavaOptionsIPV46) -Xms48m -Xmx48m"
jar="-cp $VESPA_HOME/lib/jars/filedistribution-jar-with-dependencies.jar"
exec java $jvmoptions $jar com.yahoo.vespa.filedistribution.status.FileDistributionStatusClient $defaults $environment $region "$@"
diff --git a/fnet/ethereal/packet-fnetrpc.c b/fnet/ethereal/packet-fnetrpc.c
index d2ca9e7a8e8..5f071836e30 100644
--- a/fnet/ethereal/packet-fnetrpc.c
+++ b/fnet/ethereal/packet-fnetrpc.c
@@ -385,5 +385,5 @@ decode_params_litend(proto_tree *tree, tvbuff_t *tvb, int offset, int len)
void
decode_params_bigend(proto_tree *tree, tvbuff_t *tvb, int offset, int len)
{
- abort();
+ LOG_ABORT("should not be reached");
}
diff --git a/fnet/src/vespa/fnet/controlpacket.cpp b/fnet/src/vespa/fnet/controlpacket.cpp
index 53c23f444ab..f4d10e48353 100644
--- a/fnet/src/vespa/fnet/controlpacket.cpp
+++ b/fnet/src/vespa/fnet/controlpacket.cpp
@@ -4,6 +4,9 @@
#include "context.h"
#include <vespa/vespalib/util/stringfmt.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".fnet.controlpacket");
+
void
FNET_ControlPacket::Free()
{
@@ -60,13 +63,13 @@ FNET_ControlPacket::GetLength()
void
FNET_ControlPacket::Encode(FNET_DataBuffer *)
{
- abort();
+ LOG_ABORT("should not be reached");
}
bool
FNET_ControlPacket::Decode(FNET_DataBuffer *, uint32_t)
{
- abort(); return false;
+ LOG_ABORT("should not be reached");
}
vespalib::string
diff --git a/fnet/src/vespa/fnet/dummypacket.cpp b/fnet/src/vespa/fnet/dummypacket.cpp
index 9e7ce37236b..0c2c2ee883d 100644
--- a/fnet/src/vespa/fnet/dummypacket.cpp
+++ b/fnet/src/vespa/fnet/dummypacket.cpp
@@ -4,6 +4,8 @@
#include "context.h"
#include <vespa/vespalib/util/stringfmt.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".fnet.controlpacket");
FNET_DummyPacket::FNET_DummyPacket()
{
@@ -36,13 +38,13 @@ FNET_DummyPacket::GetLength()
void
FNET_DummyPacket::Encode(FNET_DataBuffer *)
{
- abort();
+ LOG_ABORT("should not be reached");
}
bool
FNET_DummyPacket::Decode(FNET_DataBuffer *, uint32_t)
{
- abort(); return false;
+ LOG_ABORT("should not be reached");
}
vespalib::string
diff --git a/fnet/src/vespa/fnet/frt/invoker.cpp b/fnet/src/vespa/fnet/frt/invoker.cpp
index ce4daa988de..f2dc331c707 100644
--- a/fnet/src/vespa/fnet/frt/invoker.cpp
+++ b/fnet/src/vespa/fnet/frt/invoker.cpp
@@ -142,7 +142,7 @@ void
FRT_HookInvoker::HandleReturn()
{
// hooks cannot be detached
- abort();
+ LOG_ABORT("should not be reached");
}
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/AdapterFactory.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/AdapterFactory.java
index e50ee86a597..c172e50daeb 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/AdapterFactory.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/AdapterFactory.java
@@ -7,7 +7,7 @@ import com.yahoo.document.DocumentUpdate;
import java.util.List;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface AdapterFactory {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/DocumentAdapter.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/DocumentAdapter.java
index 76762837061..6becfffaa8c 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/DocumentAdapter.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/DocumentAdapter.java
@@ -5,7 +5,7 @@ import com.yahoo.document.Document;
import com.yahoo.vespa.indexinglanguage.expressions.FieldValueAdapter;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface DocumentAdapter extends FieldValueAdapter {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ExpressionConverter.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ExpressionConverter.java
index 98beac5f02f..264c54ce5cb 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ExpressionConverter.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ExpressionConverter.java
@@ -11,7 +11,7 @@ import java.util.List;
import java.util.Map;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings({ "UnusedDeclaration" })
public abstract class ExpressionConverter implements Cloneable {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ExpressionSearcher.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ExpressionSearcher.java
index e249eb73cce..f1d338371f3 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ExpressionSearcher.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ExpressionSearcher.java
@@ -4,7 +4,7 @@ package com.yahoo.vespa.indexinglanguage;
import com.yahoo.vespa.indexinglanguage.expressions.Expression;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ExpressionSearcher<T extends Expression> {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ExpressionVisitor.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ExpressionVisitor.java
index 4d8df5835a1..657901bc169 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ExpressionVisitor.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ExpressionVisitor.java
@@ -4,7 +4,7 @@ package com.yahoo.vespa.indexinglanguage;
import com.yahoo.vespa.indexinglanguage.expressions.Expression;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public abstract class ExpressionVisitor {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldPathUpdateAdapter.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldPathUpdateAdapter.java
index add7e9811af..84245d789fd 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldPathUpdateAdapter.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldPathUpdateAdapter.java
@@ -16,7 +16,7 @@ import java.util.List;
import java.util.Map;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class FieldPathUpdateAdapter implements UpdateAdapter {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldPathUpdateHelper.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldPathUpdateHelper.java
index 5c170fe147e..916b442fb74 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldPathUpdateHelper.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldPathUpdateHelper.java
@@ -12,7 +12,7 @@ import com.yahoo.document.fieldpathupdate.FieldPathUpdate;
import com.yahoo.document.fieldpathupdate.RemoveFieldPathUpdate;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class FieldPathUpdateHelper {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldUpdateAdapter.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldUpdateAdapter.java
index cb89792b854..303c973aa63 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldUpdateAdapter.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldUpdateAdapter.java
@@ -10,7 +10,7 @@ import com.yahoo.vespa.indexinglanguage.expressions.FieldValueAdapter;
import java.util.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings("rawtypes")
public class FieldUpdateAdapter implements UpdateAdapter {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldUpdateHelper.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldUpdateHelper.java
index 0f08bf0bf21..43c7ebc6f7a 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldUpdateHelper.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldUpdateHelper.java
@@ -6,7 +6,7 @@ import com.yahoo.document.datatypes.*;
import com.yahoo.document.update.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings("rawtypes")
public abstract class FieldUpdateHelper {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldValueConverter.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldValueConverter.java
index 906a8ba8462..96c740df7bf 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldValueConverter.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/FieldValueConverter.java
@@ -8,7 +8,7 @@ import com.yahoo.document.datatypes.*;
import java.util.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class FieldValueConverter {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ScriptParser.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ScriptParser.java
index 727bba1d4ae..ed8b0918bf3 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ScriptParser.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ScriptParser.java
@@ -11,7 +11,7 @@ import com.yahoo.vespa.indexinglanguage.parser.ParseException;
import com.yahoo.vespa.indexinglanguage.parser.TokenMgrError;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class ScriptParser {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ScriptParserContext.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ScriptParserContext.java
index 0d79500cc60..77bd3e0306f 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ScriptParserContext.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ScriptParserContext.java
@@ -7,7 +7,7 @@ import com.yahoo.vespa.indexinglanguage.linguistics.AnnotatorConfig;
import com.yahoo.vespa.indexinglanguage.parser.CharStream;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ScriptParserContext {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/SimpleAdapterFactory.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/SimpleAdapterFactory.java
index 509bdcaa32d..252f6c5bd12 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/SimpleAdapterFactory.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/SimpleAdapterFactory.java
@@ -11,7 +11,7 @@ import java.util.ArrayList;
import java.util.List;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings("rawtypes")
public class SimpleAdapterFactory implements AdapterFactory {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/SimpleDocumentAdapter.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/SimpleDocumentAdapter.java
index eff142e119d..16afa08cf45 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/SimpleDocumentAdapter.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/SimpleDocumentAdapter.java
@@ -10,7 +10,7 @@ import com.yahoo.vespa.indexinglanguage.expressions.Expression;
import com.yahoo.vespa.indexinglanguage.expressions.VerificationException;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SimpleDocumentAdapter implements DocumentAdapter {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/StringFieldConverter.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/StringFieldConverter.java
index 8ba84eeeca3..f66f1d00f93 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/StringFieldConverter.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/StringFieldConverter.java
@@ -6,7 +6,7 @@ import com.yahoo.document.datatypes.FieldValue;
import com.yahoo.document.datatypes.StringFieldValue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class StringFieldConverter extends FieldValueConverter {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/TypedExpressionConverter.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/TypedExpressionConverter.java
index 44a359ffcf4..b4085e65505 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/TypedExpressionConverter.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/TypedExpressionConverter.java
@@ -4,7 +4,7 @@ package com.yahoo.vespa.indexinglanguage;
import com.yahoo.vespa.indexinglanguage.expressions.Expression;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class TypedExpressionConverter<T extends Expression> extends ExpressionConverter {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/UpdateAdapter.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/UpdateAdapter.java
index e214c1c475a..f1cf9a4ae4b 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/UpdateAdapter.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/UpdateAdapter.java
@@ -6,7 +6,7 @@ import com.yahoo.vespa.indexinglanguage.expressions.Expression;
import com.yahoo.vespa.indexinglanguage.expressions.FieldValueAdapter;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface UpdateAdapter extends FieldValueAdapter {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ValueTransformProvider.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ValueTransformProvider.java
index 812cd847bca..e9bb3dd418d 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ValueTransformProvider.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/ValueTransformProvider.java
@@ -7,7 +7,7 @@ import com.yahoo.vespa.indexinglanguage.expressions.StatementExpression;
/**
* Inserts a "newTransform()" before expressions that "requiresTransform()"
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public abstract class ValueTransformProvider extends ExpressionConverter {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ArithmeticExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ArithmeticExpression.java
index f42dd7acd4b..71a2c276091 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ArithmeticExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ArithmeticExpression.java
@@ -11,7 +11,7 @@ import java.math.BigDecimal;
import java.math.MathContext;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ArithmeticExpression extends CompositeExpression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/AttributeExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/AttributeExpression.java
index 30767143474..9ce38a9d56c 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/AttributeExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/AttributeExpression.java
@@ -2,7 +2,7 @@
package com.yahoo.vespa.indexinglanguage.expressions;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class AttributeExpression extends OutputExpression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Base64DecodeExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Base64DecodeExpression.java
index 2b99b864882..c63f2c89bc6 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Base64DecodeExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Base64DecodeExpression.java
@@ -7,7 +7,7 @@ import com.yahoo.document.datatypes.LongFieldValue;
import org.apache.commons.codec.binary.Base64;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Base64DecodeExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Base64EncodeExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Base64EncodeExpression.java
index ccd408d1f40..008aacba59d 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Base64EncodeExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Base64EncodeExpression.java
@@ -8,7 +8,7 @@ import com.yahoo.document.datatypes.StringFieldValue;
import org.apache.commons.codec.binary.Base64;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Base64EncodeExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/CatExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/CatExpression.java
index bcfe2cd76af..d2d558ab8d5 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/CatExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/CatExpression.java
@@ -13,7 +13,7 @@ import com.yahoo.document.datatypes.WeightedSet;
import java.util.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CatExpression extends ExpressionList<Expression> {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ClearStateExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ClearStateExpression.java
index 75ac3ba469e..0b90922a874 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ClearStateExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ClearStateExpression.java
@@ -5,7 +5,7 @@ import com.yahoo.document.DataType;
import com.yahoo.document.DocumentType;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ClearStateExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/CompositeExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/CompositeExpression.java
index d7604324d7f..656569766dd 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/CompositeExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/CompositeExpression.java
@@ -2,7 +2,7 @@
package com.yahoo.vespa.indexinglanguage.expressions;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class CompositeExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/EchoExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/EchoExpression.java
index e5befd5a750..6e47b7d9a57 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/EchoExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/EchoExpression.java
@@ -7,7 +7,7 @@ import com.yahoo.document.DocumentType;
import java.io.PrintStream;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class EchoExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExactExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExactExpression.java
index fef80e4e0c9..187ad14f41d 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExactExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExactExpression.java
@@ -11,7 +11,7 @@ import com.yahoo.language.process.TokenType;
import static com.yahoo.language.LinguisticsCase.toLowerCase;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ExactExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExecutionContext.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExecutionContext.java
index cafd4ed067c..a4f1e3a5ca4 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExecutionContext.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExecutionContext.java
@@ -12,7 +12,7 @@ import java.util.HashMap;
import java.util.Map;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ExecutionContext implements FieldTypeAdapter, FieldValueAdapter, Cloneable {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Expression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Expression.java
index cfe90f50606..b5a83780ae2 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Expression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Expression.java
@@ -13,7 +13,7 @@ import com.yahoo.vespa.indexinglanguage.parser.ParseException;
import com.yahoo.vespa.objects.Selectable;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public abstract class Expression extends Selectable {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionList.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionList.java
index e5cba1cef4d..59cf0f08cac 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionList.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionList.java
@@ -11,7 +11,7 @@ import java.util.LinkedList;
import java.util.List;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class ExpressionList<T extends Expression> extends CompositeExpression implements Iterable<T> {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/FieldTypeAdapter.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/FieldTypeAdapter.java
index d37692076dd..1e8c9e430ab 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/FieldTypeAdapter.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/FieldTypeAdapter.java
@@ -4,7 +4,7 @@ package com.yahoo.vespa.indexinglanguage.expressions;
import com.yahoo.document.DataType;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface FieldTypeAdapter {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/FieldValueAdapter.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/FieldValueAdapter.java
index 09cd28b33e7..db3cabf8b4c 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/FieldValueAdapter.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/FieldValueAdapter.java
@@ -5,7 +5,7 @@ import com.yahoo.document.FieldPath;
import com.yahoo.document.datatypes.FieldValue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface FieldValueAdapter extends FieldTypeAdapter {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/FlattenExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/FlattenExpression.java
index ba79e1173c9..a207769d7cf 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/FlattenExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/FlattenExpression.java
@@ -10,7 +10,7 @@ import com.yahoo.document.datatypes.StringFieldValue;
import java.util.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class FlattenExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ForEachExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ForEachExpression.java
index e60ba141c9b..15721db3b61 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ForEachExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ForEachExpression.java
@@ -11,7 +11,7 @@ import com.yahoo.vespa.objects.ObjectOperation;
import com.yahoo.vespa.objects.ObjectPredicate;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class ForEachExpression extends CompositeExpression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GetFieldExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GetFieldExpression.java
index 778cc5af3ec..483139f746a 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GetFieldExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GetFieldExpression.java
@@ -9,7 +9,7 @@ import com.yahoo.document.datatypes.FieldValue;
import com.yahoo.document.datatypes.StructuredFieldValue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GetFieldExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GetVarExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GetVarExpression.java
index 09423878c77..7bb99a4506a 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GetVarExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GetVarExpression.java
@@ -5,7 +5,7 @@ import com.yahoo.document.DataType;
import com.yahoo.document.DocumentType;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GetVarExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GuardExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GuardExpression.java
index 8c17997a29a..474764248d8 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GuardExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GuardExpression.java
@@ -9,7 +9,7 @@ import com.yahoo.vespa.objects.ObjectOperation;
import com.yahoo.vespa.objects.ObjectPredicate;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GuardExpression extends CompositeExpression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HexDecodeExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HexDecodeExpression.java
index 2514b951ffa..fcaa2c623c0 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HexDecodeExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HexDecodeExpression.java
@@ -8,7 +8,7 @@ import com.yahoo.document.datatypes.LongFieldValue;
import java.math.BigInteger;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class HexDecodeExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HexEncodeExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HexEncodeExpression.java
index 2f53dab60a7..81c1b0f5e8e 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HexEncodeExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HexEncodeExpression.java
@@ -7,7 +7,7 @@ import com.yahoo.document.datatypes.LongFieldValue;
import com.yahoo.document.datatypes.StringFieldValue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class HexEncodeExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HostNameExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HostNameExpression.java
index e35e9b5a52c..daf7dee6652 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HostNameExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HostNameExpression.java
@@ -7,7 +7,7 @@ import com.yahoo.document.datatypes.StringFieldValue;
import static com.yahoo.vespa.defaults.Defaults.getDefaults;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class HostNameExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/IfThenExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/IfThenExpression.java
index 78acaf5d2a9..9940e54b6c5 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/IfThenExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/IfThenExpression.java
@@ -11,7 +11,7 @@ import com.yahoo.vespa.objects.ObjectPredicate;
import java.math.BigDecimal;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class IfThenExpression extends CompositeExpression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/IndexExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/IndexExpression.java
index 8ea5d158cf9..af5e4d3a2c3 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/IndexExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/IndexExpression.java
@@ -2,7 +2,7 @@
package com.yahoo.vespa.indexinglanguage.expressions;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class IndexExpression extends OutputExpression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/InputExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/InputExpression.java
index 435ccc283c1..676bcd74a6c 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/InputExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/InputExpression.java
@@ -12,7 +12,7 @@ import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class InputExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/JoinExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/JoinExpression.java
index e9566eb391e..08a0cf38126 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/JoinExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/JoinExpression.java
@@ -12,7 +12,7 @@ import com.yahoo.text.StringUtilities;
import java.util.Iterator;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class JoinExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/LowerCaseExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/LowerCaseExpression.java
index 11ea73dee87..2843a4e0676 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/LowerCaseExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/LowerCaseExpression.java
@@ -8,7 +8,7 @@ import com.yahoo.document.datatypes.StringFieldValue;
import static com.yahoo.language.LinguisticsCase.toLowerCase;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class LowerCaseExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/MathResolver.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/MathResolver.java
index 1d6b8dc795d..c221cc91ebc 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/MathResolver.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/MathResolver.java
@@ -6,7 +6,7 @@ import java.util.List;
import java.util.Stack;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MathResolver {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/NormalizeExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/NormalizeExpression.java
index 4d9836d4538..cfd85dcb625 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/NormalizeExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/NormalizeExpression.java
@@ -8,7 +8,7 @@ import com.yahoo.language.Linguistics;
import com.yahoo.language.process.Transformer;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NormalizeExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/NowExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/NowExpression.java
index 901ece39438..f0dfd5a33f3 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/NowExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/NowExpression.java
@@ -6,7 +6,7 @@ import com.yahoo.document.DocumentType;
import com.yahoo.document.datatypes.LongFieldValue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NowExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/OptimizePredicateExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/OptimizePredicateExpression.java
index e948b2ea2fb..06f90795bc6 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/OptimizePredicateExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/OptimizePredicateExpression.java
@@ -14,7 +14,7 @@ import com.yahoo.search.predicate.optimization.PredicateOptions;
import com.yahoo.search.predicate.optimization.PredicateProcessor;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class OptimizePredicateExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/OutputExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/OutputExpression.java
index f27f46dc056..41cdc8d6ccf 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/OutputExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/OutputExpression.java
@@ -5,7 +5,7 @@ import com.yahoo.document.DataType;
import com.yahoo.document.DocumentType;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class OutputExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ParenthesisExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ParenthesisExpression.java
index 2265758dac8..0f4bf717fa4 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ParenthesisExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ParenthesisExpression.java
@@ -7,7 +7,7 @@ import com.yahoo.vespa.objects.ObjectOperation;
import com.yahoo.vespa.objects.ObjectPredicate;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ParenthesisExpression extends CompositeExpression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/RandomExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/RandomExpression.java
index e066b20aa53..54911b7c617 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/RandomExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/RandomExpression.java
@@ -8,7 +8,7 @@ import com.yahoo.document.datatypes.IntegerFieldValue;
import java.util.concurrent.ThreadLocalRandom;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RandomExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ScriptExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ScriptExpression.java
index 1f01e66a2b4..23d215b29fc 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ScriptExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ScriptExpression.java
@@ -15,7 +15,7 @@ import java.util.Collection;
import java.util.Iterator;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ScriptExpression extends ExpressionList<StatementExpression> {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SelectInputExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SelectInputExpression.java
index 751822128c2..35d33e39bdd 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SelectInputExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SelectInputExpression.java
@@ -13,7 +13,7 @@ import java.util.Collections;
import java.util.List;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SelectInputExpression extends CompositeExpression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SetLanguageExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SetLanguageExpression.java
index 2f02c9fd19f..049f08aef4a 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SetLanguageExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SetLanguageExpression.java
@@ -7,7 +7,7 @@ import com.yahoo.language.Language;
/**
* Sets the language in the execution context.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class SetLanguageExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SetValueExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SetValueExpression.java
index 85932c69e5b..9605956e8f0 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SetValueExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SetValueExpression.java
@@ -9,7 +9,7 @@ import com.yahoo.document.datatypes.StringFieldValue;
import com.yahoo.text.StringUtilities;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SetValueExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SetVarExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SetVarExpression.java
index 655263c2417..3b23179554c 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SetVarExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SetVarExpression.java
@@ -5,7 +5,7 @@ import com.yahoo.document.DataType;
import com.yahoo.document.DocumentType;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SetVarExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SplitExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SplitExpression.java
index 979b1e59d84..1004fc74704 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SplitExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SplitExpression.java
@@ -10,7 +10,7 @@ import com.yahoo.text.StringUtilities;
import java.util.regex.Pattern;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SplitExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/StatementExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/StatementExpression.java
index f3e72a62cfe..7ec64a421f7 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/StatementExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/StatementExpression.java
@@ -15,7 +15,7 @@ import java.util.LinkedList;
import java.util.List;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class StatementExpression extends ExpressionList<Expression> {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SubstringExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SubstringExpression.java
index e8b95114c24..79b6f7ec81f 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SubstringExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SubstringExpression.java
@@ -6,7 +6,7 @@ import com.yahoo.document.DocumentType;
import com.yahoo.document.datatypes.StringFieldValue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SubstringExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SummaryExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SummaryExpression.java
index 2b6449004b5..bf1a121d7c4 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SummaryExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SummaryExpression.java
@@ -2,7 +2,7 @@
package com.yahoo.vespa.indexinglanguage.expressions;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SummaryExpression extends OutputExpression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SwitchExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SwitchExpression.java
index dc66342e0f6..41d68057891 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SwitchExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SwitchExpression.java
@@ -14,7 +14,7 @@ import java.util.LinkedHashMap;
import java.util.Map;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SwitchExpression extends CompositeExpression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ThisExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ThisExpression.java
index 5a0bb39b0ff..af572e38487 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ThisExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ThisExpression.java
@@ -5,7 +5,7 @@ import com.yahoo.document.DataType;
import com.yahoo.document.DocumentType;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ThisExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToArrayExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToArrayExpression.java
index 3dfb2ba3c34..980861dd1fd 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToArrayExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToArrayExpression.java
@@ -8,7 +8,7 @@ import com.yahoo.document.datatypes.Array;
import com.yahoo.document.datatypes.FieldValue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ToArrayExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToByteExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToByteExpression.java
index 5fb028dcabc..cf16351b573 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToByteExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToByteExpression.java
@@ -6,7 +6,7 @@ import com.yahoo.document.DocumentType;
import com.yahoo.document.datatypes.ByteFieldValue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ToByteExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToDoubleExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToDoubleExpression.java
index 00a6394fb3a..6f86f18cb38 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToDoubleExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToDoubleExpression.java
@@ -6,7 +6,7 @@ import com.yahoo.document.DocumentType;
import com.yahoo.document.datatypes.DoubleFieldValue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ToDoubleExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToFloatExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToFloatExpression.java
index fc21cc0ff0f..31884d29633 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToFloatExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToFloatExpression.java
@@ -6,7 +6,7 @@ import com.yahoo.document.DocumentType;
import com.yahoo.document.datatypes.FloatFieldValue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ToFloatExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToIntegerExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToIntegerExpression.java
index e59cb6b3b19..c0c23b04350 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToIntegerExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToIntegerExpression.java
@@ -6,7 +6,7 @@ import com.yahoo.document.DocumentType;
import com.yahoo.document.datatypes.IntegerFieldValue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ToIntegerExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToLongExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToLongExpression.java
index 798e5491264..ddde87a086b 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToLongExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToLongExpression.java
@@ -6,7 +6,7 @@ import com.yahoo.document.DocumentType;
import com.yahoo.document.datatypes.LongFieldValue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ToLongExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToPositionExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToPositionExpression.java
index 583a4f6ba0d..bb50422d81d 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToPositionExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToPositionExpression.java
@@ -6,7 +6,7 @@ import com.yahoo.document.DocumentType;
import com.yahoo.document.PositionDataType;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ToPositionExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToStringExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToStringExpression.java
index b24a65e6465..298292c03c6 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToStringExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToStringExpression.java
@@ -5,7 +5,7 @@ import com.yahoo.document.DataType;
import com.yahoo.document.datatypes.StringFieldValue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ToStringExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToWsetExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToWsetExpression.java
index 8556ca6198d..0108c2db98d 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToWsetExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToWsetExpression.java
@@ -8,7 +8,7 @@ import com.yahoo.document.datatypes.FieldValue;
import com.yahoo.document.datatypes.WeightedSet;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ToWsetExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/TokenizeExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/TokenizeExpression.java
index b3cee971258..0a1a9e0be1a 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/TokenizeExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/TokenizeExpression.java
@@ -11,7 +11,7 @@ import com.yahoo.vespa.indexinglanguage.linguistics.AnnotatorConfig;
import com.yahoo.vespa.indexinglanguage.linguistics.LinguisticsAnnotator;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class TokenizeExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/TrimExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/TrimExpression.java
index 3b5e12b7710..b26048e1867 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/TrimExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/TrimExpression.java
@@ -6,7 +6,7 @@ import com.yahoo.document.DocumentType;
import com.yahoo.document.datatypes.StringFieldValue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class TrimExpression extends Expression {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedDataType.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedDataType.java
index 86afa9cdbe9..ef08c01633a 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedDataType.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedDataType.java
@@ -5,7 +5,7 @@ import com.yahoo.document.PrimitiveDataType;
import com.yahoo.document.datatypes.FieldValue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
final class UnresolvedDataType extends PrimitiveDataType {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedFieldValue.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedFieldValue.java
index 1520c8adc5e..2681865bb42 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedFieldValue.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedFieldValue.java
@@ -10,7 +10,7 @@ import com.yahoo.document.serialization.FieldWriter;
import com.yahoo.document.serialization.XmlStream;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class UnresolvedFieldValue extends FieldValue {
private static class Factory extends PrimitiveDataType.Factory {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationContext.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationContext.java
index ba62afa721a..38b8b82f9e9 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationContext.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationContext.java
@@ -7,7 +7,7 @@ import java.util.HashMap;
import java.util.Map;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class VerificationContext implements FieldTypeAdapter, Cloneable {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationException.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationException.java
index 75ec68cb61e..32f337f500c 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationException.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationException.java
@@ -2,7 +2,7 @@
package com.yahoo.vespa.indexinglanguage.expressions;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class VerificationException extends RuntimeException {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ZCurveExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ZCurveExpression.java
index b14c4371e6f..9e77c439303 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ZCurveExpression.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ZCurveExpression.java
@@ -10,7 +10,7 @@ import com.yahoo.document.datatypes.Struct;
import com.yahoo.geo.ZCurve;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ZCurveExpression extends Expression {
@@ -22,7 +22,7 @@ public class ZCurveExpression extends Expression {
if (x != null && y != null) {
ctx.setValue(new LongFieldValue(ZCurve.encode(x, y)));
} else {
- ctx.setValue(null);
+ ctx.setValue(DataType.LONG.createFieldValue());
}
}
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/linguistics/AnnotatorConfig.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/linguistics/AnnotatorConfig.java
index 6d160c489df..1d8b296d18c 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/linguistics/AnnotatorConfig.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/linguistics/AnnotatorConfig.java
@@ -6,7 +6,7 @@ import com.yahoo.language.process.StemMode;
import com.yahoo.vespa.configdefinition.IlscriptsConfig;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class AnnotatorConfig implements Cloneable {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/linguistics/LinguisticsAnnotator.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/linguistics/LinguisticsAnnotator.java
index 2b2dcc90e41..8b6ef83f05e 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/linguistics/LinguisticsAnnotator.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/linguistics/LinguisticsAnnotator.java
@@ -21,7 +21,7 @@ import static com.yahoo.language.LinguisticsCase.toLowerCase;
/**
* This is a tool for adding {@link AnnotationTypes} type annotations to {@link StringFieldValue} objects.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class LinguisticsAnnotator {
diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/parser/IndexingInput.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/parser/IndexingInput.java
index 619e0ba1148..8406d48ba09 100644
--- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/parser/IndexingInput.java
+++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/parser/IndexingInput.java
@@ -4,7 +4,7 @@ package com.yahoo.vespa.indexinglanguage.parser;
import com.yahoo.javacc.FastCharStream;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class IndexingInput extends FastCharStream implements CharStream {
diff --git a/indexinglanguage/src/main/javacc/IndexingParser.jj b/indexinglanguage/src/main/javacc/IndexingParser.jj
index d564443bb48..3afb52a8039 100644
--- a/indexinglanguage/src/main/javacc/IndexingParser.jj
+++ b/indexinglanguage/src/main/javacc/IndexingParser.jj
@@ -37,7 +37,7 @@ import com.yahoo.language.process.StemMode;
import com.yahoo.language.Linguistics;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
public class IndexingParser {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/DocumentTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/DocumentTestCase.java
index 4bc102212cb..740645cc895 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/DocumentTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/DocumentTestCase.java
@@ -10,7 +10,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public class DocumentTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/DocumentToPathUpdateTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/DocumentToPathUpdateTestCase.java
index da451406369..459f3ce827c 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/DocumentToPathUpdateTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/DocumentToPathUpdateTestCase.java
@@ -13,7 +13,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DocumentToPathUpdateTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/DocumentToValueUpdateTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/DocumentToValueUpdateTestCase.java
index 180f40e1e7e..de090163b7b 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/DocumentToValueUpdateTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/DocumentToValueUpdateTestCase.java
@@ -9,7 +9,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public class DocumentToValueUpdateTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/DocumentUpdateTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/DocumentUpdateTestCase.java
index aa9f5618274..bdb8dbedf78 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/DocumentUpdateTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/DocumentUpdateTestCase.java
@@ -16,7 +16,7 @@ import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings({ "rawtypes" })
public class DocumentUpdateTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ExpressionConverterTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ExpressionConverterTestCase.java
index fa5c3ba51f8..ae7fa0f3123 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ExpressionConverterTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ExpressionConverterTestCase.java
@@ -71,7 +71,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ExpressionConverterTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ExpressionSearcherTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ExpressionSearcherTestCase.java
index 27445717cd2..db539549980 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ExpressionSearcherTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ExpressionSearcherTestCase.java
@@ -24,7 +24,7 @@ import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings({ "rawtypes" })
public class ExpressionSearcherTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ExpressionVisitorTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ExpressionVisitorTestCase.java
index af7f56dcac1..bf396b79588 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ExpressionVisitorTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ExpressionVisitorTestCase.java
@@ -61,7 +61,7 @@ import java.util.Collections;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ExpressionVisitorTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/FieldValueConverterTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/FieldValueConverterTestCase.java
index a7f5cc31f0f..0759d043900 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/FieldValueConverterTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/FieldValueConverterTestCase.java
@@ -15,7 +15,7 @@ import java.util.Iterator;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings({ "rawtypes" })
public class FieldValueConverterTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/PathUpdateToDocumentTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/PathUpdateToDocumentTestCase.java
index e7615f40756..69d019364f9 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/PathUpdateToDocumentTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/PathUpdateToDocumentTestCase.java
@@ -12,7 +12,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings({ "rawtypes" })
public class PathUpdateToDocumentTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ScriptParserTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ScriptParserTestCase.java
index 2f91173759a..cd6008d67d1 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ScriptParserTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ScriptParserTestCase.java
@@ -13,7 +13,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ScriptParserTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ScriptTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ScriptTestCase.java
index 5e449c4482b..f3cd40d71c5 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ScriptTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ScriptTestCase.java
@@ -12,7 +12,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ScriptTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/SimpleDocumentAdapterTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/SimpleDocumentAdapterTestCase.java
index f9f5126b130..d32c69a6340 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/SimpleDocumentAdapterTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/SimpleDocumentAdapterTestCase.java
@@ -12,7 +12,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SimpleDocumentAdapterTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/SimpleTestAdapter.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/SimpleTestAdapter.java
index dabdb439c06..5d1cc514d35 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/SimpleTestAdapter.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/SimpleTestAdapter.java
@@ -13,7 +13,7 @@ import java.util.HashMap;
import java.util.Map;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SimpleTestAdapter implements FieldValueAdapter {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/TypedExpressionConverterTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/TypedExpressionConverterTestCase.java
index e2fb26e2dff..ec5edbdd351 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/TypedExpressionConverterTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/TypedExpressionConverterTestCase.java
@@ -7,7 +7,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class TypedExpressionConverterTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ValueTransformProviderTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ValueTransformProviderTestCase.java
index 2c8d1d566ef..ad92a16019a 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ValueTransformProviderTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ValueTransformProviderTestCase.java
@@ -13,7 +13,7 @@ import java.util.Map;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ValueTransformProviderTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ValueUpdateToDocumentTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ValueUpdateToDocumentTestCase.java
index bd8748fbd7b..b60a6e789ce 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ValueUpdateToDocumentTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ValueUpdateToDocumentTestCase.java
@@ -9,7 +9,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings({ "rawtypes" })
public class ValueUpdateToDocumentTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ArithmeticTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ArithmeticTestCase.java
index 51a5a8df325..c930f4e4d38 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ArithmeticTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ArithmeticTestCase.java
@@ -13,7 +13,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ArithmeticExpression.
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ArithmeticTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/AttributeExpressionTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/AttributeExpressionTestCase.java
index f3c5e4fbe6b..f747bdfec3a 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/AttributeExpressionTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/AttributeExpressionTestCase.java
@@ -9,7 +9,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class AttributeExpressionTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/Base64DecodeTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/Base64DecodeTestCase.java
index bcc31dcf7a1..df4fd981e01 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/Base64DecodeTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/Base64DecodeTestCase.java
@@ -13,7 +13,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Base64DecodeTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/Base64EncodeTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/Base64EncodeTestCase.java
index 96159711436..e2395acb19b 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/Base64EncodeTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/Base64EncodeTestCase.java
@@ -13,7 +13,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Base64EncodeTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/CatTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/CatTestCase.java
index 5e8c4d5b817..e83c18786bc 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/CatTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/CatTestCase.java
@@ -12,7 +12,7 @@ import java.util.Arrays;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings({ "rawtypes" })
public class CatTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ClearStateTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ClearStateTestCase.java
index d092c27275a..54983d5df60 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ClearStateTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ClearStateTestCase.java
@@ -8,7 +8,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ClearStateTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/CompositeExpressionTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/CompositeExpressionTestCase.java
index 59afc130062..1a851716eb4 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/CompositeExpressionTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/CompositeExpressionTestCase.java
@@ -9,7 +9,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class CompositeExpressionTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/EchoTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/EchoTestCase.java
index 56f91ceee85..665ea3d36e2 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/EchoTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/EchoTestCase.java
@@ -14,7 +14,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class EchoTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExactTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExactTestCase.java
index 860bff8d186..d491b80fd3d 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExactTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExactTestCase.java
@@ -14,7 +14,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ExactTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExecutionContextTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExecutionContextTestCase.java
index 7f8f5ea46bb..988931e6daf 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExecutionContextTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExecutionContextTestCase.java
@@ -11,7 +11,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ExecutionContextTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionAssert.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionAssert.java
index 8dbaa3e85c8..0f2f8353791 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionAssert.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionAssert.java
@@ -9,7 +9,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
class ExpressionAssert {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionAssertTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionAssertTestCase.java
index 7c14f59f822..4b180ba7734 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionAssertTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionAssertTestCase.java
@@ -9,7 +9,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.assertNotNull;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ExpressionAssertTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionTestCase.java
index c0b9226d35d..c5ae67d1d47 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionTestCase.java
@@ -10,7 +10,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ExpressionTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/FlattenTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/FlattenTestCase.java
index 1afc1231afc..8867d64d193 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/FlattenTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/FlattenTestCase.java
@@ -12,7 +12,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class FlattenTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ForEachTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ForEachTestCase.java
index c98620c573f..867d9ef0b23 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ForEachTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ForEachTestCase.java
@@ -17,7 +17,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings({ "rawtypes" })
public class ForEachTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GetFieldTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GetFieldTestCase.java
index 67fac6e5dd9..3b5569995e6 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GetFieldTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GetFieldTestCase.java
@@ -13,7 +13,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GetFieldTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GetVarTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GetVarTestCase.java
index e980ba9d606..dfe0dfc7c35 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GetVarTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GetVarTestCase.java
@@ -12,7 +12,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GetVarTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GuardTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GuardTestCase.java
index 98ad285ab80..033034fed1f 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GuardTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GuardTestCase.java
@@ -20,7 +20,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings({ "rawtypes" })
public class GuardTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/HexDecodeTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/HexDecodeTestCase.java
index f66b0ca2ffa..84c410d4168 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/HexDecodeTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/HexDecodeTestCase.java
@@ -13,7 +13,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class HexDecodeTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/HexEncodeTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/HexEncodeTestCase.java
index 149d38997f8..7381226776a 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/HexEncodeTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/HexEncodeTestCase.java
@@ -13,7 +13,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class HexEncodeTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/HostNameTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/HostNameTestCase.java
index 27d7b49bb9c..3d5792b5c1b 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/HostNameTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/HostNameTestCase.java
@@ -13,7 +13,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class HostNameTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/IfThenTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/IfThenTestCase.java
index 514d07c0ffe..01c6b0f122d 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/IfThenTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/IfThenTestCase.java
@@ -19,7 +19,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.IfThenExpression.Comp
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class IfThenTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/IndexExpressionTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/IndexExpressionTestCase.java
index caabccc4c22..f3720f61c0b 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/IndexExpressionTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/IndexExpressionTestCase.java
@@ -9,7 +9,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class IndexExpressionTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/InputTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/InputTestCase.java
index 3d78e6475f6..0f71f920639 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/InputTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/InputTestCase.java
@@ -12,7 +12,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class InputTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/JoinTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/JoinTestCase.java
index e3f9239d753..9b9c02d87b8 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/JoinTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/JoinTestCase.java
@@ -12,7 +12,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class JoinTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/LowerCaseTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/LowerCaseTestCase.java
index 3625b2d85c6..54f7aac49b3 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/LowerCaseTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/LowerCaseTestCase.java
@@ -12,7 +12,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class LowerCaseTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/MathResolverTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/MathResolverTestCase.java
index 23fa82f70c9..c67948e39a9 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/MathResolverTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/MathResolverTestCase.java
@@ -9,7 +9,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MathResolverTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/NormalizeTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/NormalizeTestCase.java
index df45be3f4b1..8b4f1a8b344 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/NormalizeTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/NormalizeTestCase.java
@@ -16,7 +16,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NormalizeTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/NowTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/NowTestCase.java
index 5877b9d19aa..2c0727e43da 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/NowTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/NowTestCase.java
@@ -11,7 +11,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NowTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/OptimizePredicateTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/OptimizePredicateTestCase.java
index f8a3fb5a4a8..06d74a49ed6 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/OptimizePredicateTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/OptimizePredicateTestCase.java
@@ -16,7 +16,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class OptimizePredicateTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/OutputAssert.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/OutputAssert.java
index 996ddc48869..7c0b2aa98a4 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/OutputAssert.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/OutputAssert.java
@@ -10,7 +10,7 @@ import com.yahoo.vespa.indexinglanguage.SimpleTestAdapter;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
class OutputAssert {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/OutputAssertTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/OutputAssertTestCase.java
index d5ebd9eccd4..872efd470f6 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/OutputAssertTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/OutputAssertTestCase.java
@@ -9,7 +9,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.OutputAssert.assertVe
import static org.junit.Assert.assertNotNull;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class OutputAssertTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ParenthesisTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ParenthesisTestCase.java
index f2c522e7cbb..f613f560796 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ParenthesisTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ParenthesisTestCase.java
@@ -12,7 +12,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ParenthesisTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/RandomTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/RandomTestCase.java
index 8723a56f38d..cb735aa9045 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/RandomTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/RandomTestCase.java
@@ -12,7 +12,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RandomTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ScriptTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ScriptTestCase.java
index dc21c67e3c2..b260f2933fb 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ScriptTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ScriptTestCase.java
@@ -14,7 +14,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ScriptTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SelectInputTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SelectInputTestCase.java
index 48f1b95e740..02644db25f0 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SelectInputTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SelectInputTestCase.java
@@ -15,7 +15,7 @@ import java.util.List;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SelectInputTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SetLanguageTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SetLanguageTestCase.java
index 270c6ab4386..0a3d7ff10a8 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SetLanguageTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SetLanguageTestCase.java
@@ -13,7 +13,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class SetLanguageTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SetValueTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SetValueTestCase.java
index d23f04079c3..cb0fc92edf6 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SetValueTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SetValueTestCase.java
@@ -12,7 +12,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SetValueTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SetVarTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SetVarTestCase.java
index 2a0847ab3e1..66eb29796ad 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SetVarTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SetVarTestCase.java
@@ -12,7 +12,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SetVarTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SimpleExpression.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SimpleExpression.java
index 3ce37d83908..0a8c8697bb7 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SimpleExpression.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SimpleExpression.java
@@ -6,7 +6,7 @@ import com.yahoo.document.DocumentType;
import com.yahoo.document.datatypes.FieldValue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
class SimpleExpression extends Expression {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SimpleExpressionTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SimpleExpressionTestCase.java
index 1557a454d57..2644ed5af7a 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SimpleExpressionTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SimpleExpressionTestCase.java
@@ -8,7 +8,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class SimpleExpressionTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SplitTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SplitTestCase.java
index 61bf1cc492c..329dca5527e 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SplitTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SplitTestCase.java
@@ -13,7 +13,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings({ "rawtypes" })
public class SplitTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/StatementTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/StatementTestCase.java
index 47011d0e9b8..27466313fc6 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/StatementTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/StatementTestCase.java
@@ -14,7 +14,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class StatementTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SubstringTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SubstringTestCase.java
index d175677626d..308f1e09719 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SubstringTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SubstringTestCase.java
@@ -12,7 +12,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SubstringTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SummaryExpressionTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SummaryExpressionTestCase.java
index e2fe8c8bb87..45136af93cb 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SummaryExpressionTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SummaryExpressionTestCase.java
@@ -9,7 +9,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class SummaryExpressionTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SwitchTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SwitchTestCase.java
index 5f239ece0a1..e81e2ec1e7b 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SwitchTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SwitchTestCase.java
@@ -16,7 +16,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SwitchTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ThisTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ThisTestCase.java
index 1e77d2417d2..8f1419eaa3f 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ThisTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ThisTestCase.java
@@ -12,7 +12,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ThisTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToArrayTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToArrayTestCase.java
index 3923f940794..7bd11afbbb8 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToArrayTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToArrayTestCase.java
@@ -15,7 +15,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings({ "rawtypes" })
public class ToArrayTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToByteTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToByteTestCase.java
index 7d851e6aa5c..8c43afde558 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToByteTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToByteTestCase.java
@@ -13,7 +13,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ToByteTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToDoubleTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToDoubleTestCase.java
index eb1e9f34362..fe8881fb837 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToDoubleTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToDoubleTestCase.java
@@ -13,7 +13,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ToDoubleTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToFloatTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToFloatTestCase.java
index 64844b80102..4f3769bbd73 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToFloatTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToFloatTestCase.java
@@ -13,7 +13,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ToFloatTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToIntegerTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToIntegerTestCase.java
index 0a333b07c3a..c0e84270c0f 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToIntegerTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToIntegerTestCase.java
@@ -13,7 +13,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ToIntegerTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToLongTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToLongTestCase.java
index ef70272f0a3..9ca4fa68948 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToLongTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToLongTestCase.java
@@ -13,7 +13,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ToLongTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToPositionTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToPositionTestCase.java
index cacc821ed46..95dc8b36cd0 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToPositionTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToPositionTestCase.java
@@ -15,7 +15,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ToPositionTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToStringTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToStringTestCase.java
index e8c40b1499f..e60c9aa4c72 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToStringTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToStringTestCase.java
@@ -13,7 +13,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ToStringTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToWsetTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToWsetTestCase.java
index 28e1bad888f..0e029420386 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToWsetTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToWsetTestCase.java
@@ -14,7 +14,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings({ "rawtypes" })
public class ToWsetTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/TokenizeTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/TokenizeTestCase.java
index 1757176f8dc..1ab1dd1a6c2 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/TokenizeTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/TokenizeTestCase.java
@@ -18,7 +18,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class TokenizeTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/TrimTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/TrimTestCase.java
index 49bf1def795..be062d55fbc 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/TrimTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/TrimTestCase.java
@@ -12,7 +12,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.asse
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class TrimTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedDataTypeTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedDataTypeTestCase.java
index 736cf7e7b05..f95bd58a825 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedDataTypeTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedDataTypeTestCase.java
@@ -12,7 +12,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class UnresolvedDataTypeTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedFieldValueTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedFieldValueTestCase.java
index 88c388abfea..d590076601b 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedFieldValueTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedFieldValueTestCase.java
@@ -7,7 +7,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class UnresolvedFieldValueTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationContextTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationContextTestCase.java
index 421c2698cbf..eb4aec37770 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationContextTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationContextTestCase.java
@@ -8,7 +8,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class VerificationContextTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationExceptionTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationExceptionTestCase.java
index fcd82a24516..a22cc64e115 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationExceptionTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationExceptionTestCase.java
@@ -6,7 +6,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class VerificationExceptionTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ZCurveTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ZCurveTestCase.java
index 11ae27667ff..746fdc36e13 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ZCurveTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ZCurveTestCase.java
@@ -14,7 +14,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertFalse;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ZCurveTestCase {
@@ -41,8 +41,12 @@ public class ZCurveTestCase {
}
@Test
- public void requireThatMissingFieldEvaluatesToNull() {
- assertNull(new ZCurveExpression().execute(PositionDataType.valueOf(null, 9)));
- assertNull(new ZCurveExpression().execute(PositionDataType.valueOf(6, null)));
+ public void requireThatMissingFieldEvaluatesToDefaultValue() {
+ assertEquals(DataType.LONG.createFieldValue(),
+ new ZCurveExpression().execute(PositionDataType.valueOf(null, 9)));
+ assertEquals(DataType.LONG.createFieldValue(),
+ new ZCurveExpression().execute(PositionDataType.valueOf(6, null)));
+ assertEquals(DataType.LONG.createFieldValue(),
+ new ZCurveExpression().execute(PositionDataType.valueOf(null, null)));
}
}
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/linguistics/AnnotatorConfigTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/linguistics/AnnotatorConfigTestCase.java
index c80f2ccde58..e0a6d3adf75 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/linguistics/AnnotatorConfigTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/linguistics/AnnotatorConfigTestCase.java
@@ -8,7 +8,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class AnnotatorConfigTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/linguistics/LinguisticsAnnotatorTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/linguistics/LinguisticsAnnotatorTestCase.java
index 2d18d410e66..afbcf597a46 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/linguistics/LinguisticsAnnotatorTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/linguistics/LinguisticsAnnotatorTestCase.java
@@ -24,7 +24,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class LinguisticsAnnotatorTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/DefaultFieldNameTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/DefaultFieldNameTestCase.java
index da0b06e90c4..77998a9ac05 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/DefaultFieldNameTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/DefaultFieldNameTestCase.java
@@ -10,7 +10,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class DefaultFieldNameTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/ExpressionTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/ExpressionTestCase.java
index edfe4eb1aec..095842544a6 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/ExpressionTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/ExpressionTestCase.java
@@ -9,7 +9,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings({ "rawtypes" })
public class ExpressionTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/FieldNameTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/FieldNameTestCase.java
index 447b23d5a90..cd85a60cc14 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/FieldNameTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/FieldNameTestCase.java
@@ -8,7 +8,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class FieldNameTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/IdentifierTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/IdentifierTestCase.java
index 5f4262e7cc9..d7f1582fd50 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/IdentifierTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/IdentifierTestCase.java
@@ -9,7 +9,7 @@ import java.util.List;
import static org.junit.Assert.assertEquals;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class IdentifierTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/MathTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/MathTestCase.java
index ecae141e4ef..0e9f136d473 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/MathTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/MathTestCase.java
@@ -10,7 +10,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MathTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/NumberTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/NumberTestCase.java
index b32cc67be9e..975da5db2b5 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/NumberTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/NumberTestCase.java
@@ -9,7 +9,7 @@ import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NumberTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/PrecedenceTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/PrecedenceTestCase.java
index 7f307f242a3..f7e5e8f509e 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/PrecedenceTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/PrecedenceTestCase.java
@@ -7,7 +7,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class PrecedenceTestCase {
diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/ScriptTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/ScriptTestCase.java
index 4a0c8bb24e6..dfd49e50396 100644
--- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/ScriptTestCase.java
+++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/ScriptTestCase.java
@@ -10,7 +10,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings({ "rawtypes" })
public class ScriptTestCase {
diff --git a/jaxrs_client_utils/src/main/java/com/yahoo/vespa/jaxrs/client/JaxRsStrategyFactory.java b/jaxrs_client_utils/src/main/java/com/yahoo/vespa/jaxrs/client/JaxRsStrategyFactory.java
index 6523a0c138f..1459024767d 100644
--- a/jaxrs_client_utils/src/main/java/com/yahoo/vespa/jaxrs/client/JaxRsStrategyFactory.java
+++ b/jaxrs_client_utils/src/main/java/com/yahoo/vespa/jaxrs/client/JaxRsStrategyFactory.java
@@ -53,7 +53,7 @@ public class JaxRsStrategyFactory {
this.scheme = scheme;
}
- public <T> JaxRsStrategy<T> apiWithRetries(final Class<T> apiClass, final String pathPrefix) {
+ public <T> RetryingJaxRsStrategy<T> apiWithRetries(final Class<T> apiClass, final String pathPrefix) {
Objects.requireNonNull(apiClass, "apiClass argument may not be null");
Objects.requireNonNull(pathPrefix, "pathPrefix argument may not be null");
return new RetryingJaxRsStrategy<T>(hostNames, port, jaxRsClientFactory, apiClass, pathPrefix, scheme);
diff --git a/jaxrs_client_utils/src/main/java/com/yahoo/vespa/jaxrs/client/RetryingJaxRsStrategy.java b/jaxrs_client_utils/src/main/java/com/yahoo/vespa/jaxrs/client/RetryingJaxRsStrategy.java
index 73320a4c72d..65b302ef4ff 100644
--- a/jaxrs_client_utils/src/main/java/com/yahoo/vespa/jaxrs/client/RetryingJaxRsStrategy.java
+++ b/jaxrs_client_utils/src/main/java/com/yahoo/vespa/jaxrs/client/RetryingJaxRsStrategy.java
@@ -21,7 +21,6 @@ import java.util.logging.Logger;
*/
public class RetryingJaxRsStrategy<T> implements JaxRsStrategy<T> {
private static final Logger logger = Logger.getLogger(RetryingJaxRsStrategy.class.getName());
- private static final int NUM_LOOP_ATTEMPTS = 2;
private final List<HostName> hostNames;
private final int port;
@@ -30,6 +29,8 @@ public class RetryingJaxRsStrategy<T> implements JaxRsStrategy<T> {
private String pathPrefix;
private final String scheme;
+ private int maxIterations = 2;
+
public RetryingJaxRsStrategy(
final Set<HostName> hostNames,
final int port,
@@ -52,11 +53,21 @@ public class RetryingJaxRsStrategy<T> implements JaxRsStrategy<T> {
this.scheme = scheme;
}
+ /**
+ * The the max number of times the hostnames should be iterated over, before giving up.
+ *
+ * <p>By default, maxIterations is 2.
+ */
+ public RetryingJaxRsStrategy<T> setMaxIterations(int maxIterations) {
+ this.maxIterations = maxIterations;
+ return this;
+ }
+
@Override
public <R> R apply(final Function<T, R> function) throws IOException {
ProcessingException sampleException = null;
- for (int i = 0; i < NUM_LOOP_ATTEMPTS; ++i) {
+ for (int i = 0; i < maxIterations; ++i) {
for (final HostName hostName : hostNames) {
final T jaxRsClient = jaxRsClientFactory.createClient(apiClass, hostName, port, pathPrefix, scheme);
try {
@@ -72,7 +83,7 @@ public class RetryingJaxRsStrategy<T> implements JaxRsStrategy<T> {
final String message = String.format(
"Giving up invoking REST API after %d tries against hosts %s.%s",
- NUM_LOOP_ATTEMPTS,
+ maxIterations,
hostNames,
sampleException == null ? "" : ", sample error: " + sampleException.getMessage());
diff --git a/jaxrs_client_utils/src/test/java/com/yahoo/vespa/jaxrs/client/RetryingJaxRsStrategyTest.java b/jaxrs_client_utils/src/test/java/com/yahoo/vespa/jaxrs/client/RetryingJaxRsStrategyTest.java
index 10dde1ff820..e31920febd6 100644
--- a/jaxrs_client_utils/src/test/java/com/yahoo/vespa/jaxrs/client/RetryingJaxRsStrategyTest.java
+++ b/jaxrs_client_utils/src/test/java/com/yahoo/vespa/jaxrs/client/RetryingJaxRsStrategyTest.java
@@ -6,6 +6,7 @@ import com.yahoo.vespa.defaults.Defaults;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
+import org.mockito.stubbing.OngoingStubbing;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
@@ -47,7 +48,7 @@ public class RetryingJaxRsStrategyTest {
private final JaxRsClientFactory jaxRsClientFactory = mock(JaxRsClientFactory.class);
private final TestJaxRsApi mockApi = mock(TestJaxRsApi.class);
- private final JaxRsStrategy<TestJaxRsApi> jaxRsStrategy = new RetryingJaxRsStrategy<>(
+ private final RetryingJaxRsStrategy<TestJaxRsApi> jaxRsStrategy = new RetryingJaxRsStrategy<>(
SERVER_HOSTS, REST_PORT, jaxRsClientFactory, TestJaxRsApi.class, API_PATH, "http");
@Before
@@ -111,14 +112,24 @@ public class RetryingJaxRsStrategyTest {
}
@Test
- public void testRetryGivesUpAfterTwoLoopsOverAvailableServers() throws Exception {
- when(mockApi.doSomething())
- .thenThrow(new ProcessingException("Fake timeout 1 induced by test"))
- .thenThrow(new ProcessingException("Fake timeout 2 induced by test"))
- .thenThrow(new ProcessingException("Fake timeout 3 induced by test"))
- .thenThrow(new ProcessingException("Fake timeout 4 induced by test"))
- .thenThrow(new ProcessingException("Fake timeout 5 induced by test"))
- .thenThrow(new ProcessingException("Fake timeout 6 induced by test"));
+ public void testRetryGivesUpAfterOneLoopOverAvailableServers() {
+ jaxRsStrategy.setMaxIterations(1);
+ testRetryGivesUpAfterXIterations(1);
+ }
+
+ @Test
+ public void testRetryGivesUpAfterTwoLoopsOverAvailableServers() {
+ testRetryGivesUpAfterXIterations(2);
+ }
+
+ private void testRetryGivesUpAfterXIterations(int iterations) {
+ OngoingStubbing<String> stub = when(mockApi.doSomething());
+ for (int i = 0; i < iterations; ++i) {
+ stub = stub
+ .thenThrow(new ProcessingException("Fake timeout 1 iteration " + i))
+ .thenThrow(new ProcessingException("Fake timeout 2 iteration " + i))
+ .thenThrow(new ProcessingException("Fake timeout 3 iteration " + i));
+ }
try {
jaxRsStrategy.apply(TestJaxRsApi::doSomething);
@@ -127,7 +138,7 @@ public class RetryingJaxRsStrategyTest {
// As expected.
}
- verify(mockApi, times(6)).doSomething();
+ verify(mockApi, times(iterations * 3)).doSomething();
verifyAllServersContacted(jaxRsClientFactory);
}
diff --git a/jdisc-security-filters/pom.xml b/jdisc-security-filters/pom.xml
index bcee244ef69..5e8356e94f1 100644
--- a/jdisc-security-filters/pom.xml
+++ b/jdisc-security-filters/pom.xml
@@ -23,6 +23,12 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>vespa-athenz</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
<!-- test -->
<dependency>
@@ -41,6 +47,7 @@
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
+
</dependencies>
<build>
diff --git a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzAuthorizationFilter.java b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzAuthorizationFilter.java
new file mode 100644
index 00000000000..74e0ee36959
--- /dev/null
+++ b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzAuthorizationFilter.java
@@ -0,0 +1,145 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.jdisc.http.filter.security.athenz;
+
+import com.google.inject.Inject;
+import com.yahoo.jdisc.Response;
+import com.yahoo.jdisc.http.filter.DiscFilterRequest;
+import com.yahoo.jdisc.http.filter.security.athenz.AthenzAuthorizationFilterConfig.CredentialsToVerify;
+import com.yahoo.jdisc.http.filter.security.athenz.RequestResourceMapper.ResourceNameAndAction;
+import com.yahoo.jdisc.http.filter.security.base.JsonSecurityRequestFilterBase;
+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.ZToken;
+import com.yahoo.vespa.athenz.tls.AthenzX509CertificateUtils;
+import com.yahoo.vespa.athenz.zpe.AuthorizationResult;
+import com.yahoo.vespa.athenz.zpe.DefaultZpe;
+import com.yahoo.vespa.athenz.zpe.Zpe;
+
+import java.security.cert.X509Certificate;
+import java.util.Optional;
+import java.util.function.Function;
+
+import static java.util.Collections.singletonList;
+
+/**
+ * An Athenz security filter that uses a configured action and resource name to control access.
+ *
+ * @author bjorncs
+ */
+public class AthenzAuthorizationFilter extends JsonSecurityRequestFilterBase {
+
+ private final String headerName;
+ private final Zpe zpe;
+ private final RequestResourceMapper requestResourceMapper;
+ private final CredentialsToVerify.Enum credentialsToVerify;
+
+ @Inject
+ public AthenzAuthorizationFilter(AthenzAuthorizationFilterConfig config, RequestResourceMapper resourceMapper) {
+ this(config, resourceMapper, new DefaultZpe());
+ }
+
+ AthenzAuthorizationFilter(AthenzAuthorizationFilterConfig config,
+ RequestResourceMapper resourceMapper,
+ Zpe zpe) {
+ this.headerName = config.roleTokenHeaderName();
+ this.credentialsToVerify = config.credentialsToVerify();
+ this.requestResourceMapper = resourceMapper;
+ this.zpe = zpe;
+ }
+
+ @Override
+ protected Optional<ErrorResponse> filter(DiscFilterRequest request) {
+ Optional<ResourceNameAndAction> resourceMapping =
+ requestResourceMapper.getResourceNameAndAction(request.getMethod(), request.getRequestURI(), request.getQueryString());
+ if (!resourceMapping.isPresent()) {
+ return Optional.empty();
+ }
+ Optional<X509Certificate> roleCertificate = getRoleCertificate(request);
+ Optional<ZToken> roleToken = getRoleToken(request, headerName);
+ switch (credentialsToVerify) {
+ case CERTIFICATE_ONLY: {
+ if (!roleCertificate.isPresent()) {
+ return Optional.of(new ErrorResponse(Response.Status.UNAUTHORIZED, "Missing client certificate"));
+ }
+ return checkAccessAllowed(roleCertificate.get(), resourceMapping.get(), request);
+ }
+ case TOKEN_ONLY: {
+ if (!roleToken.isPresent()) {
+ return Optional.of(new ErrorResponse(Response.Status.UNAUTHORIZED,
+ String.format("Role token header '%s' is missing or does not have a value.", headerName)));
+ }
+ return checkAccessAllowed(roleToken.get(), resourceMapping.get(), request);
+ }
+ case ANY: {
+ if (!roleCertificate.isPresent() && !roleToken.isPresent()) {
+ return Optional.of(new ErrorResponse(Response.Status.UNAUTHORIZED, "Both role token and role certificate is missing"));
+ }
+ if (roleCertificate.isPresent()) {
+ return checkAccessAllowed(roleCertificate.get(), resourceMapping.get(), request);
+ } else {
+ return checkAccessAllowed(roleToken.get(), resourceMapping.get(), request);
+ }
+ }
+ default: {
+ throw new IllegalStateException("Unexpected mode: " + credentialsToVerify);
+ }
+ }
+ }
+
+ private static Optional<X509Certificate> getRoleCertificate(DiscFilterRequest request) {
+ return Optional.of(request.getClientCertificateChain())
+ .filter(chain -> !chain.isEmpty())
+ .map(chain -> chain.get(0))
+ .filter(AthenzX509CertificateUtils::isAthenzRoleCertificate);
+ }
+
+ private static Optional<ZToken> getRoleToken(DiscFilterRequest request, String headerName) {
+ return Optional.ofNullable(request.getHeader(headerName))
+ .filter(token -> !token.isEmpty())
+ .map(ZToken::new);
+ }
+
+ private Optional<ErrorResponse> checkAccessAllowed(X509Certificate certificate,
+ ResourceNameAndAction resourceNameAndAction,
+ DiscFilterRequest request) {
+ return checkAccessAllowed(
+ certificate, resourceNameAndAction, request, zpe::checkAccessAllowed, AthenzAuthorizationFilter::createPrincipal);
+ }
+
+ private Optional<ErrorResponse> checkAccessAllowed(ZToken roleToken,
+ ResourceNameAndAction resourceNameAndAction,
+ DiscFilterRequest request) {
+ return checkAccessAllowed(
+ roleToken, resourceNameAndAction, request, zpe::checkAccessAllowed, AthenzAuthorizationFilter::createPrincipal);
+ }
+
+ private static <C> Optional<ErrorResponse> checkAccessAllowed(C credentials,
+ ResourceNameAndAction resAndAction,
+ DiscFilterRequest request,
+ ZpeCheck<C> accessCheck,
+ Function<C, AthenzPrincipal> principalFactory) {
+ AuthorizationResult authorizationResult = accessCheck.checkAccess(credentials, resAndAction.resourceName(), resAndAction.action());
+ if (authorizationResult == AuthorizationResult.ALLOW) {
+ request.setUserPrincipal(principalFactory.apply(credentials));
+ return Optional.empty();
+ }
+ return Optional.of(new ErrorResponse(Response.Status.FORBIDDEN, "Access forbidden: " + authorizationResult.getDescription()));
+ }
+
+ private static AthenzPrincipal createPrincipal(X509Certificate certificate) {
+ AthenzIdentity identity = AthenzX509CertificateUtils.getIdentityFromRoleCertificate(certificate);
+ AthenzRole role = AthenzX509CertificateUtils.getRolesFromRoleCertificate(certificate);
+ return new AthenzPrincipal(identity, singletonList(role));
+ }
+
+ private static AthenzPrincipal createPrincipal(ZToken roleToken) {
+ return new AthenzPrincipal(roleToken.getIdentity(), roleToken.getRoles());
+ }
+
+ @FunctionalInterface private interface ZpeCheck<C> {
+ AuthorizationResult checkAccess(C credentials, AthenzResourceName resourceName, String action);
+ }
+
+}
diff --git a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/RequestResourceMapper.java b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/RequestResourceMapper.java
new file mode 100644
index 00000000000..77709975cba
--- /dev/null
+++ b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/RequestResourceMapper.java
@@ -0,0 +1,37 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.jdisc.http.filter.security.athenz;
+
+import com.yahoo.vespa.athenz.api.AthenzResourceName;
+
+import java.util.Optional;
+
+/**
+ * Maps a request to an {@link AthenzResourceName} and an action.
+ *
+ * @author bjorncs
+ */
+public interface RequestResourceMapper {
+
+ /**
+ * @return A resource name + action to use for access control, empty if no access control should be performed.
+ */
+ Optional<ResourceNameAndAction> getResourceNameAndAction(String method, String uriPath, String uriQuery);
+
+ class ResourceNameAndAction {
+ private final AthenzResourceName resourceName;
+ private final String action;
+
+ public ResourceNameAndAction(AthenzResourceName resourceName, String action) {
+ this.resourceName = resourceName;
+ this.action = action;
+ }
+
+ public AthenzResourceName resourceName() {
+ return resourceName;
+ }
+
+ public String action() {
+ return action;
+ }
+ }
+}
diff --git a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/StaticRequestResourceMapper.java b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/StaticRequestResourceMapper.java
new file mode 100644
index 00000000000..ded13a9def4
--- /dev/null
+++ b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/StaticRequestResourceMapper.java
@@ -0,0 +1,33 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.jdisc.http.filter.security.athenz;
+
+import com.google.inject.Inject;
+import com.yahoo.vespa.athenz.api.AthenzResourceName;
+
+import java.util.Optional;
+
+/**
+ * A simple {@link RequestResourceMapper} that uses a fixed resource name and action
+ *
+ * @author bjorncs
+ */
+public class StaticRequestResourceMapper implements RequestResourceMapper {
+
+ private final AthenzResourceName resourceName;
+ private final String action;
+
+ @Inject
+ public StaticRequestResourceMapper(StaticRequestResourceMapperConfig config) {
+ this(AthenzResourceName.fromString(config.resourceName()), config.action());
+ }
+
+ StaticRequestResourceMapper(AthenzResourceName resourceName, String action) {
+ this.resourceName = resourceName;
+ this.action = action;
+ }
+
+ @Override
+ public Optional<ResourceNameAndAction> getResourceNameAndAction(String method, String uriPath, String uriQuery) {
+ return Optional.of(new ResourceNameAndAction(resourceName, action));
+ }
+}
diff --git a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/package-info.java b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/package-info.java
new file mode 100644
index 00000000000..6ec5bd4322e
--- /dev/null
+++ b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/package-info.java
@@ -0,0 +1,8 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+/**
+ * @author bjorncs
+ */
+@ExportPackage
+package com.yahoo.jdisc.http.filter.security.athenz;
+
+import com.yahoo.osgi.annotation.ExportPackage; \ No newline at end of file
diff --git a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/base/JsonSecurityRequestFilterBase.java b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/base/JsonSecurityRequestFilterBase.java
index e2440bc4c5f..ec8a93019b0 100644
--- a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/base/JsonSecurityRequestFilterBase.java
+++ b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/base/JsonSecurityRequestFilterBase.java
@@ -36,6 +36,7 @@ public abstract class JsonSecurityRequestFilterBase implements SecurityRequestFi
errorMessage.put("code", error.errorCode);
errorMessage.put("message", error.message);
error.response.headers().put("Content-Type", "application/json"); // Note: Overwrites header if already exists
+ error.response.headers().put("Cache-Control", "must-revalidate,no-cache,no-store");
try (FastContentWriter writer = ResponseDispatch.newInstance(error.response).connectFastWriter(responseHandler)) {
writer.write(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(errorMessage));
} catch (JsonProcessingException e) {
diff --git a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/misc/SecurityHeadersResponseFilter.java b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/misc/SecurityHeadersResponseFilter.java
index f50e7454f19..09d02d66b1f 100644
--- a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/misc/SecurityHeadersResponseFilter.java
+++ b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/misc/SecurityHeadersResponseFilter.java
@@ -17,5 +17,6 @@ public class SecurityHeadersResponseFilter implements SecurityResponseFilter {
response.setHeader("Cache-control", "no-store");
response.setHeader("Pragma", "no-cache");
response.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
+ response.setHeader("X-Content-Type-Options", "nosniff");
}
}
diff --git a/jdisc-security-filters/src/main/resources/configdefinitions/athenz-authorization-filter.def b/jdisc-security-filters/src/main/resources/configdefinitions/athenz-authorization-filter.def
new file mode 100644
index 00000000000..c60b7a125f8
--- /dev/null
+++ b/jdisc-security-filters/src/main/resources/configdefinitions/athenz-authorization-filter.def
@@ -0,0 +1,8 @@
+# Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+namespace=jdisc.http.filter.security.athenz
+
+# Which credentials to verify. Note: ANY will prioritize token over certificate if both are present.
+credentialsToVerify enum { CERTIFICATE_ONLY, TOKEN_ONLY, ANY } default=ANY
+
+# Name of header which includes role token. Must be set if 'credentialsTypeRequired' is set to TOKEN_ONLY or ANY.
+roleTokenHeaderName string default=""
diff --git a/jdisc-security-filters/src/main/resources/configdefinitions/static-request-resource-mapper.def b/jdisc-security-filters/src/main/resources/configdefinitions/static-request-resource-mapper.def
new file mode 100644
index 00000000000..de89c1f9198
--- /dev/null
+++ b/jdisc-security-filters/src/main/resources/configdefinitions/static-request-resource-mapper.def
@@ -0,0 +1,8 @@
+# Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+namespace=jdisc.http.filter.security.athenz
+
+# Athenz resource name on format '<domain-name>:<entity-name>'
+resourceName string
+
+# Action name
+action string \ No newline at end of file
diff --git a/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzAuthorizationFilterTest.java b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzAuthorizationFilterTest.java
new file mode 100644
index 00000000000..a5242571130
--- /dev/null
+++ b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzAuthorizationFilterTest.java
@@ -0,0 +1,103 @@
+package com.yahoo.jdisc.http.filter.security.athenz;
+
+import com.yahoo.container.jdisc.RequestHandlerTestDriver;
+import com.yahoo.jdisc.Response;
+import com.yahoo.jdisc.http.filter.DiscFilterRequest;
+import com.yahoo.vespa.athenz.api.AthenzResourceName;
+import com.yahoo.vespa.athenz.api.ZToken;
+import com.yahoo.vespa.athenz.zpe.AuthorizationResult;
+import com.yahoo.vespa.athenz.zpe.Zpe;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.security.cert.X509Certificate;
+
+import static com.yahoo.jdisc.http.filter.security.athenz.AthenzAuthorizationFilterConfig.CredentialsToVerify.Enum.ANY;
+import static java.util.Collections.emptyList;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.when;
+
+/**
+ * @author bjorncs
+ */
+public class AthenzAuthorizationFilterTest {
+
+ private static final AthenzResourceName RESOURCE_NAME = new AthenzResourceName("domain", "my-resource-name");
+ private static final String ACTION = "update";
+ private static final String HEADER_NAME = "Athenz-Role-Token";
+ private static final AthenzAuthorizationFilterConfig CONFIG = createConfig();
+
+ private static AthenzAuthorizationFilterConfig createConfig() {
+ return new AthenzAuthorizationFilterConfig(
+ new AthenzAuthorizationFilterConfig.Builder()
+ .roleTokenHeaderName(HEADER_NAME)
+ .credentialsToVerify(ANY));
+ }
+
+ @Test
+ public void accepts_valid_requests() {
+ AthenzAuthorizationFilter filter =
+ new AthenzAuthorizationFilter(
+ CONFIG, new StaticRequestResourceMapper(RESOURCE_NAME, ACTION), new AllowingZpe());
+
+ RequestHandlerTestDriver.MockResponseHandler responseHandler = new RequestHandlerTestDriver.MockResponseHandler();
+ filter.filter(createRequest(), responseHandler);
+
+ assertNull(responseHandler.getResponse());
+ }
+
+ @Test
+ public void returns_error_on_forbidden_requests() {
+ AthenzAuthorizationFilter filter =
+ new AthenzAuthorizationFilter(
+ CONFIG, new StaticRequestResourceMapper(RESOURCE_NAME, ACTION), new DenyingZpe());
+
+ RequestHandlerTestDriver.MockResponseHandler responseHandler = new RequestHandlerTestDriver.MockResponseHandler();
+ filter.filter(createRequest(), responseHandler);
+
+ Response response = responseHandler.getResponse();
+ assertNotNull(response);
+ assertEquals(403, response.getStatus());
+ String content = responseHandler.readAll();
+ assertThat(content, containsString(AuthorizationResult.DENY.getDescription()));
+ }
+
+ private static DiscFilterRequest createRequest() {
+ DiscFilterRequest request = Mockito.mock(DiscFilterRequest.class);
+ when(request.getHeader(HEADER_NAME)).thenReturn("v=Z1;d=domain;r=my-role;p=my-domain.my-service");
+ when(request.getMethod()).thenReturn("GET");
+ when(request.getRequestURI()).thenReturn("/my/path");
+ when(request.getQueryString()).thenReturn(null);
+ when(request.getClientCertificateChain()).thenReturn(emptyList());
+ return request;
+ }
+
+ static class AllowingZpe implements Zpe {
+ @Override
+ public AuthorizationResult checkAccessAllowed(ZToken roleToken, AthenzResourceName resourceName, String action) {
+ return AuthorizationResult.ALLOW;
+ }
+
+ @Override
+ public AuthorizationResult checkAccessAllowed(X509Certificate roleCertificate, AthenzResourceName resourceName, String action) {
+ return AuthorizationResult.ALLOW;
+ }
+ }
+
+ static class DenyingZpe implements Zpe {
+ @Override
+ public AuthorizationResult checkAccessAllowed(ZToken roleToken, AthenzResourceName resourceName, String action) {
+ return AuthorizationResult.DENY;
+ }
+
+ @Override
+ public AuthorizationResult checkAccessAllowed(X509Certificate roleCertificate, AthenzResourceName resourceName, String action) {
+ return AuthorizationResult.DENY;
+ }
+ }
+
+} \ No newline at end of file
diff --git a/jdisc_core/pom.xml b/jdisc_core/pom.xml
index 55da222994f..efa4e62affd 100644
--- a/jdisc_core/pom.xml
+++ b/jdisc_core/pom.xml
@@ -16,6 +16,25 @@
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<dependencies>
+ <!-- Necessary for jaxb support from java 9. (Could be deployed as bundles since 2.2.11.) -->
+ <dependency>
+ <groupId>javax.xml.bind</groupId>
+ <artifactId>jaxb-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.xml.bind</groupId>
+ <artifactId>jaxb-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.xml.bind</groupId>
+ <artifactId>jaxb-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.activation</groupId>
+ <artifactId>javax.activation</artifactId>
+ </dependency>
+ <!-- jaxb end -->
+
<dependency>
<!-- Newer version than the one in rt.jar, including the ElementTraversal class needed by Xerces (Aug 2015, still valid Sep 2017) -->
<groupId>xml-apis</groupId>
@@ -147,13 +166,6 @@
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
- <dependency>
- <!-- This seems odd. Used for export-package parsing. Lazy stuff. Should be separated out. -->
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>bundle-plugin</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
</dependencies>
<build>
<plugins>
@@ -235,6 +247,10 @@
<argument>${project.build.directory}/dependency/log4j-over-slf4j.jar</argument>
<argument>${project.build.directory}/dependency/config-lib.jar</argument>
<argument>${project.build.directory}/dependency/yolean.jar</argument>
+ <argument>${project.build.directory}/dependency/jaxb-api.jar</argument>
+ <argument>${project.build.directory}/dependency/jaxb-core.jar</argument>
+ <argument>${project.build.directory}/dependency/jaxb-impl.jar</argument>
+ <argument>${project.build.directory}/dependency/javax.activation.jar</argument>
</arguments>
</configuration>
</execution>
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/AbstractResource.java b/jdisc_core/src/main/java/com/yahoo/jdisc/AbstractResource.java
index bc3887e2e5f..28f0bb75b33 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/AbstractResource.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/AbstractResource.java
@@ -18,7 +18,7 @@ import java.util.logging.Logger;
* all subclasses of {@link RequestHandler}, {@link ClientProvider} and {@link ServerProvider}. Once the reference count
* of this resource reaches zero, the {@link #destroy()} method is called.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public abstract class AbstractResource implements SharedResource {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/Container.java b/jdisc_core/src/main/java/com/yahoo/jdisc/Container.java
index 88e4d842bf5..7d7d808fecc 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/Container.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/Container.java
@@ -26,7 +26,7 @@ import java.net.URI;
* <p>The only way to <u>create</u> a new instance of this class is to 1) create and configure a {@link
* ContainerBuilder}, and 2) pass that to the {@link ContainerActivator#activateContainer(ContainerBuilder)} method.</p>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public interface Container extends SharedResource, Timer {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/HeaderFields.java b/jdisc_core/src/main/java/com/yahoo/jdisc/HeaderFields.java
index b24f0a24d20..500b1258735 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/HeaderFields.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/HeaderFields.java
@@ -10,7 +10,7 @@ import java.util.*;
* a multimap from String to String, with some additional methods for convenience. The keys of this map are compared by
* ignoring their case, so that <tt>get("foo")</tt> returns the same entry as <tt>get("FOO")</tt>.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class HeaderFields implements Map<String, List<String>> {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/Metric.java b/jdisc_core/src/main/java/com/yahoo/jdisc/Metric.java
index 9e39d8d5ef8..d6206dcf966 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/Metric.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/Metric.java
@@ -16,7 +16,7 @@ import java.util.Map;
*
* <p>An instance of this class can be injected anywhere.</p>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
@ProvidedBy(MetricProvider.class)
public interface Metric {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/Request.java b/jdisc_core/src/main/java/com/yahoo/jdisc/Request.java
index 91bb69243c3..65aafee0060 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/Request.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/Request.java
@@ -29,7 +29,7 @@ import java.util.concurrent.TimeUnit;
* <p>For every successfully dispatched Request (i.e. a non-null ContentChannel has been retrieved), there will be
* exactly one {@link Response} returned to the provided {@link ResponseHandler}.</p>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
* @see Container
* @see Response
*/
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/Response.java b/jdisc_core/src/main/java/com/yahoo/jdisc/Response.java
index ed6dccace30..e4bd35ca393 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/Response.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/Response.java
@@ -19,7 +19,7 @@ import java.util.Map;
* <p>The usage pattern of the Response is similar to that of the Request in that the {@link ResponseHandler} returns a
* {@link ContentChannel} into which to write the Response content.</p>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
* @see Request
* @see ResponseHandler
*/
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/SharedResource.java b/jdisc_core/src/main/java/com/yahoo/jdisc/SharedResource.java
index c97f61b6603..20656bf7d1d 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/SharedResource.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/SharedResource.java
@@ -23,7 +23,7 @@ import com.yahoo.jdisc.service.ServerProvider;
* in terms of resource ownership. You retain a resource to prevent it from being destroyed while you are using it, and
* you release a resource once you are done using it.</p>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public interface SharedResource {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/TimeoutManager.java b/jdisc_core/src/main/java/com/yahoo/jdisc/TimeoutManager.java
index 6f6e5dce557..0f3d553d96b 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/TimeoutManager.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/TimeoutManager.java
@@ -10,7 +10,7 @@ import java.util.concurrent.TimeUnit;
* handler is registered at the time where the target {@link RequestHandler} is called, the default timeout manager will
* be injected.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public interface TimeoutManager {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/Timer.java b/jdisc_core/src/main/java/com/yahoo/jdisc/Timer.java
index f0dc26844e7..a822ed46ef9 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/Timer.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/Timer.java
@@ -11,7 +11,7 @@ import java.time.Instant;
* instance of this class into any component that needs to access time, instead of using
* <code>System.currentTimeMillis()</code>.</p>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
@ImplementedBy(SystemTimer.class)
public interface Timer {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/AbstractApplication.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/AbstractApplication.java
index 4075aa711c2..be362157739 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/AbstractApplication.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/AbstractApplication.java
@@ -39,7 +39,7 @@ import java.util.concurrent.TimeUnit;
* }
* </pre>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public abstract class AbstractApplication implements Application {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/Application.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/Application.java
index 74546951f15..6f618006091 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/Application.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/Application.java
@@ -11,7 +11,7 @@ import com.yahoo.jdisc.service.ServerProvider;
* will always have its {@link #destroy()} method called, regardless of whether {@link #start()} or {@link #stop()}
* threw any exceptions.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public interface Application {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/ApplicationNotReadyException.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/ApplicationNotReadyException.java
index 92ed2b5827e..0afdafb8141 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/ApplicationNotReadyException.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/ApplicationNotReadyException.java
@@ -6,7 +6,7 @@ package com.yahoo.jdisc.application;
* thrown by the {@link ContainerActivator#activateContainer(ContainerBuilder)} method if it is called before the call
* to {@link Application#start()} or after the call to {@link Application#stop()}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class ApplicationNotReadyException extends RuntimeException {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/BindingRepository.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/BindingRepository.java
index 9325a266bba..b6591ef4825 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/BindingRepository.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/BindingRepository.java
@@ -16,7 +16,7 @@ import java.util.logging.Logger;
* ContainerBuilder} has a mapping of named instances of this class for {@link RequestHandler}s, and is used to
* configure the set of {@link BindingSet}s that eventually become part of the active {@link Container}.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class BindingRepository<T> implements Iterable<Map.Entry<UriPattern, T>> {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/BindingSet.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/BindingSet.java
index 1e25846f63c..eea932b6a27 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/BindingSet.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/BindingSet.java
@@ -17,7 +17,7 @@ import java.util.Map;
* of this class, you must 1) create a {@link BindingRepository}, 2) configure it using the {@link
* BindingRepository#bind(String, Object)} method, and finally 3) call {@link BindingRepository#activate()}.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class BindingSet<T> implements Iterable<Map.Entry<UriPattern, T>> {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/BindingSetSelector.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/BindingSetSelector.java
index 3e097f4f211..25d13c6a4e3 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/BindingSetSelector.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/BindingSetSelector.java
@@ -16,7 +16,7 @@ import java.net.URI;
* {@link BindingSet#DEFAULT} regardless of input. To specify your own selector you need to {@link
* GuiceRepository#install(Module) install} a Guice {@link Module} that provides a binding for this interface.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@ImplementedBy(DefaultBindingSelector.class)
public interface BindingSetSelector {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/BundleInstallationException.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/BundleInstallationException.java
index 9821c7d3f77..468604428ac 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/BundleInstallationException.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/BundleInstallationException.java
@@ -17,7 +17,7 @@ import java.util.List;
* OsgiFramework#startBundles(java.util.List, boolean)} for a description of exception-safety issues to consider when
* installing bundles that use the {@link OsgiHeader#PREINSTALL_BUNDLE} manifest instruction.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public final class BundleInstallationException extends BundleException {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/BundleInstaller.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/BundleInstaller.java
index e57a8c1a107..fa14579a00c 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/BundleInstaller.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/BundleInstaller.java
@@ -16,7 +16,7 @@ import java.util.List;
* <p>Please see commentary on {@link OsgiFramework#installBundle(String)} for a description of exception-safety issues
* to consider when installing bundles that use the {@link OsgiHeader#PREINSTALL_BUNDLE} manifest instruction.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class BundleInstaller {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/ContainerActivator.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/ContainerActivator.java
index 59db453e2c4..46f8789e1ed 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/ContainerActivator.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/ContainerActivator.java
@@ -13,7 +13,7 @@ import com.yahoo.jdisc.Container;
* #newContainerBuilder()}, 2) configure the returned {@link ContainerBuilder}, and 3) pass the builder to the {@link
* #activateContainer(ContainerBuilder)} method.</p>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public interface ContainerActivator {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/ContainerBuilder.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/ContainerBuilder.java
index ac0316b2cc9..2607ba14640 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/ContainerBuilder.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/ContainerBuilder.java
@@ -19,7 +19,7 @@ import java.util.concurrent.ThreadFactory;
* attach an arbitrary object to a Container, which will be available in the corresponding {@link
* DeactivatedContainer}.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ContainerBuilder {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/ContainerThread.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/ContainerThread.java
index 1e947d44fcd..d13b983c3a4 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/ContainerThread.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/ContainerThread.java
@@ -12,7 +12,7 @@ import java.util.concurrent.ThreadFactory;
* application should use this class instead of Thread. The {@link ContainerThread.Factory} class is a helper-class for
* working with the {@link Executors} framework.</p>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class ContainerThread extends Thread {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/DeactivatedContainer.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/DeactivatedContainer.java
index 994b55d153a..5b997a1fa6d 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/DeactivatedContainer.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/DeactivatedContainer.java
@@ -11,7 +11,7 @@ import com.yahoo.jdisc.handler.ContentChannel;
* the {@link ContainerActivator#activateContainer(ContainerBuilder)} method, and is used to schedule a cleanup task
* that is executed once the the deactivated Container has terminated.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface DeactivatedContainer {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/GlobPattern.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/GlobPattern.java
index 4f562c9eb30..dcaf1af00d8 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/GlobPattern.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/GlobPattern.java
@@ -6,7 +6,7 @@ import java.util.LinkedList;
import java.util.List;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
class GlobPattern implements Comparable<GlobPattern> {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/GuiceRepository.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/GuiceRepository.java
index ef2074b7703..8cd9998cd9c 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/GuiceRepository.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/GuiceRepository.java
@@ -29,7 +29,7 @@ import java.util.logging.Logger;
* This is a repository of {@link Module}s. An instance of this class is owned by the {@link ContainerBuilder}, and is
* used to configure the set of Modules that eventually form the {@link Injector} of the active {@link Container}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GuiceRepository implements Iterable<Module> {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/MetricConsumer.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/MetricConsumer.java
index 0b52b847bf9..d9bf67b530f 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/MetricConsumer.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/MetricConsumer.java
@@ -33,7 +33,7 @@ import java.util.Map;
* }
* </pre>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@ProvidedBy(MetricNullProvider.class)
public interface MetricConsumer {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/MetricImpl.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/MetricImpl.java
index be26b2db753..c02563c106f 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/MetricImpl.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/MetricImpl.java
@@ -8,7 +8,7 @@ import com.yahoo.jdisc.Metric;
import java.util.Map;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
class MetricImpl implements Metric {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/MetricNullProvider.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/MetricNullProvider.java
index 625e53ea81f..f971aa78773 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/MetricNullProvider.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/MetricNullProvider.java
@@ -4,7 +4,7 @@ package com.yahoo.jdisc.application;
import com.google.inject.Provider;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
class MetricNullProvider implements Provider<MetricConsumer> {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/MetricProvider.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/MetricProvider.java
index 59b8ca3ea63..7df8082dff3 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/MetricProvider.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/MetricProvider.java
@@ -6,7 +6,7 @@ import com.google.inject.Provider;
import com.yahoo.jdisc.Metric;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class MetricProvider implements Provider<Metric> {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/OsgiFramework.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/OsgiFramework.java
index 11234296c9a..6ab5f798d92 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/OsgiFramework.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/OsgiFramework.java
@@ -12,7 +12,7 @@ import java.util.List;
* this interface, simply inject it into your Application. In most cases, however, you are better of injecting a
* {@link BundleInstaller} since that provides common convenience methods.</p>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public interface OsgiFramework {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/OsgiHeader.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/OsgiHeader.java
index 16207f8b5c1..257c3858836 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/OsgiHeader.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/OsgiHeader.java
@@ -8,7 +8,7 @@ import java.util.List;
/**
* This interface acts as a namespace for the supported OSGi bundle headers.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class OsgiHeader {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/ResourcePool.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/ResourcePool.java
index 3fe3bfdc065..279d99f63b7 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/ResourcePool.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/ResourcePool.java
@@ -55,7 +55,7 @@ import java.util.List;
*
* <p>This class is not thread-safe.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public final class ResourcePool extends AbstractResource implements AutoCloseable {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/ServerRepository.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/ServerRepository.java
index f009948c4bf..def3332944e 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/ServerRepository.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/ServerRepository.java
@@ -13,7 +13,7 @@ import java.util.logging.Logger;
* This is a repository of {@link ServerProvider}s. An instance of this class is owned by the {@link ContainerBuilder},
* and is used to configure the set of ServerProviders that eventually become part of the active {@link Container}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ServerRepository implements Iterable<ServerProvider> {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/UriPattern.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/UriPattern.java
index 3cf9db04140..3481e0612fc 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/UriPattern.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/UriPattern.java
@@ -24,7 +24,7 @@ import java.util.regex.Pattern;
* <li><code>http://host/path/*</code> evaluated before <code>http://host/path</code></li>
* </ul>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class UriPattern implements Comparable<UriPattern> {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/client/AbstractClientApplication.java b/jdisc_core/src/main/java/com/yahoo/jdisc/client/AbstractClientApplication.java
index 00a1e99e749..45b005dc110 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/client/AbstractClientApplication.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/client/AbstractClientApplication.java
@@ -16,7 +16,7 @@ import java.util.concurrent.TimeUnit;
* {@link #start()} (and optionally {@link #stop()}), and provide a reference to it to whatever component is responsible
* for signaling shutdown.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class AbstractClientApplication extends AbstractApplication implements ClientApplication {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/client/ClientApplication.java b/jdisc_core/src/main/java/com/yahoo/jdisc/client/ClientApplication.java
index db94842d40d..33c9b6d3941 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/client/ClientApplication.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/client/ClientApplication.java
@@ -9,7 +9,7 @@ import com.yahoo.jdisc.application.Application;
* provides a {@link Runnable#run()} method that will be invoked once the Application has been created and {@link
* Application#start() started}. When run() returns, the {@link ClientDriver} will initiate Application shutdown.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface ClientApplication extends Application, Runnable {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/client/ClientDriver.java b/jdisc_core/src/main/java/com/yahoo/jdisc/client/ClientDriver.java
index e70e29dc278..15283b7ab0b 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/client/ClientDriver.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/client/ClientDriver.java
@@ -41,7 +41,7 @@ import java.util.List;
* framework created by this ClientDriver is disabled. Calling any method on that framework will throw an
* exception. If you need OSGi support, use either of the runApplicationWithOsgi() methods.</p>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public abstract class ClientDriver {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/core/ActiveContainer.java b/jdisc_core/src/main/java/com/yahoo/jdisc/core/ActiveContainer.java
index f1232259ced..a9fd2c747ff 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/core/ActiveContainer.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/core/ActiveContainer.java
@@ -19,7 +19,7 @@ import java.net.URI;
import java.util.Map;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
* @author bjorncs
*/
public class ActiveContainer extends AbstractResource implements CurrentContainer {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/core/ApplicationConfigModule.java b/jdisc_core/src/main/java/com/yahoo/jdisc/core/ApplicationConfigModule.java
index b92e4f30fee..4c78e0c464f 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/core/ApplicationConfigModule.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/core/ApplicationConfigModule.java
@@ -11,7 +11,7 @@ import java.io.InputStream;
import java.util.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
class ApplicationConfigModule extends AbstractModule {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/core/ApplicationEnvironmentModule.java b/jdisc_core/src/main/java/com/yahoo/jdisc/core/ApplicationEnvironmentModule.java
index 6c09cdf92f7..ef02b0791ae 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/core/ApplicationEnvironmentModule.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/core/ApplicationEnvironmentModule.java
@@ -13,7 +13,7 @@ import com.yahoo.jdisc.statistics.ContainerWatchdogMetrics;
import java.util.concurrent.ThreadFactory;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
class ApplicationEnvironmentModule extends AbstractModule {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/core/ApplicationLoader.java b/jdisc_core/src/main/java/com/yahoo/jdisc/core/ApplicationLoader.java
index 3ba48ffd8cd..68be5bb5d94 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/core/ApplicationLoader.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/core/ApplicationLoader.java
@@ -29,7 +29,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
* @author bjorncs
*/
public class ApplicationLoader implements BootstrapLoader, ContainerActivator, CurrentContainer {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/core/BootstrapDaemon.java b/jdisc_core/src/main/java/com/yahoo/jdisc/core/BootstrapDaemon.java
index ed43ffc5e1b..de6d5c5073f 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/core/BootstrapDaemon.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/core/BootstrapDaemon.java
@@ -12,7 +12,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class BootstrapDaemon implements Daemon {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/core/BootstrapLoader.java b/jdisc_core/src/main/java/com/yahoo/jdisc/core/BootstrapLoader.java
index a2e447dddf1..57d1c518bee 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/core/BootstrapLoader.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/core/BootstrapLoader.java
@@ -2,7 +2,7 @@
package com.yahoo.jdisc.core;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public interface BootstrapLoader {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/core/BundleLocationResolver.java b/jdisc_core/src/main/java/com/yahoo/jdisc/core/BundleLocationResolver.java
index b55f42aac47..ed14b5840ad 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/core/BundleLocationResolver.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/core/BundleLocationResolver.java
@@ -5,7 +5,7 @@ import java.io.File;
import java.io.IOException;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
class BundleLocationResolver {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/core/ConsoleLogFormatter.java b/jdisc_core/src/main/java/com/yahoo/jdisc/core/ConsoleLogFormatter.java
index bcba166f17d..efe051a628f 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/core/ConsoleLogFormatter.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/core/ConsoleLogFormatter.java
@@ -11,7 +11,7 @@ import java.io.StringWriter;
import java.io.Writer;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
class ConsoleLogFormatter {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/core/ContainerSnapshot.java b/jdisc_core/src/main/java/com/yahoo/jdisc/core/ContainerSnapshot.java
index 8b5768d0a9e..1a3f7068024 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/core/ContainerSnapshot.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/core/ContainerSnapshot.java
@@ -16,7 +16,7 @@ import com.yahoo.jdisc.handler.ResponseHandler;
import java.util.Objects;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
class ContainerSnapshot extends AbstractResource implements Container {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/core/ContainerTermination.java b/jdisc_core/src/main/java/com/yahoo/jdisc/core/ContainerTermination.java
index d4b1bf2ab2c..7d32238bc56 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/core/ContainerTermination.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/core/ContainerTermination.java
@@ -4,7 +4,7 @@ package com.yahoo.jdisc.core;
import com.yahoo.jdisc.application.DeactivatedContainer;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ContainerTermination implements DeactivatedContainer, Runnable {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/core/DefaultBindingSelector.java b/jdisc_core/src/main/java/com/yahoo/jdisc/core/DefaultBindingSelector.java
index eda50894a1f..83fe5e25cdb 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/core/DefaultBindingSelector.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/core/DefaultBindingSelector.java
@@ -7,7 +7,7 @@ import com.yahoo.jdisc.application.BindingSetSelector;
import java.net.URI;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DefaultBindingSelector implements BindingSetSelector {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/core/ExportPackages.java b/jdisc_core/src/main/java/com/yahoo/jdisc/core/ExportPackages.java
index f2e9c1a6e53..86ab016bded 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/core/ExportPackages.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/core/ExportPackages.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.jdisc.core;
-import com.yahoo.container.plugin.bundle.TransformExportPackages;
-import com.yahoo.container.plugin.osgi.ExportPackages.Export;
import org.apache.felix.framework.util.Util;
import org.osgi.framework.Constants;
-import scala.collection.immutable.List;
import java.io.File;
import java.io.FileInputStream;
@@ -41,9 +38,11 @@ public class ExportPackages {
.append("javax.inject;version=1.0.0,") // Included in guice, but not exported. Needed by container-jersey.
.append("org.aopalliance.intercept,")
.append("org.aopalliance.aop,")
+ .append("sun.misc,")
+ .append("sun.net.util,")
+ .append("sun.security.krb5,")
- // xml-apis:xml-apis:1.4.01 is not a bundle
- .append("org.w3c.dom,")
+ // TODO: remove for Vespa 7 (xml-apis:xml-apis:1.4.01 is not a bundle, but exposed from system classpath on Java 9)
.append("org.w3c.dom.bootstrap,")
.append("org.w3c.dom.css,")
.append("org.w3c.dom.events,")
@@ -52,11 +51,8 @@ public class ExportPackages {
.append("org.w3c.dom.ranges,")
.append("org.w3c.dom.stylesheets,")
.append("org.w3c.dom.traversal,")
- .append("org.w3c.dom.views,")
+ .append("org.w3c.dom.views");
- .append("sun.misc,")
- .append("sun.net.util,")
- .append("sun.security.krb5");
for (int i = 1; i < args.length; ++i) {
out.append(",").append(getExportedPackages(args[i]));
}
@@ -96,10 +92,4 @@ public class ExportPackages {
return jar.getManifest().getMainAttributes().getValue(Constants.EXPORT_PACKAGE);
}
}
-
- private static String transformExports(List<Export> exports, String newVersion) {
- return TransformExportPackages.toExportPackageProperty(
- TransformExportPackages.removeUses(
- TransformExportPackages.replaceVersions(exports, newVersion)));
- }
}
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/core/FelixFramework.java b/jdisc_core/src/main/java/com/yahoo/jdisc/core/FelixFramework.java
index 08ea9f92f64..a050fbf6496 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/core/FelixFramework.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/core/FelixFramework.java
@@ -26,7 +26,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class FelixFramework implements OsgiFramework {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/core/FelixParams.java b/jdisc_core/src/main/java/com/yahoo/jdisc/core/FelixParams.java
index 0b0fdec039f..10dbd09792f 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/core/FelixParams.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/core/FelixParams.java
@@ -8,7 +8,7 @@ import java.util.HashMap;
import java.util.Map;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class FelixParams {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/core/Main.java b/jdisc_core/src/main/java/com/yahoo/jdisc/core/Main.java
index ce091fa82d0..1e7f82e2f4c 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/core/Main.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/core/Main.java
@@ -10,7 +10,7 @@ import java.util.Arrays;
import java.util.Collections;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class Main {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/core/ScheduledQueue.java b/jdisc_core/src/main/java/com/yahoo/jdisc/core/ScheduledQueue.java
index 7e52e59b9d4..d15947296eb 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/core/ScheduledQueue.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/core/ScheduledQueue.java
@@ -6,7 +6,7 @@ import java.util.Queue;
/**
* @author <a href="mailto:havardpe@yahoo-inc.com">Haavard Pettersen</a>
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
class ScheduledQueue {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/core/SystemTimer.java b/jdisc_core/src/main/java/com/yahoo/jdisc/core/SystemTimer.java
index 224e2ae3583..15e234079b0 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/core/SystemTimer.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/core/SystemTimer.java
@@ -6,7 +6,7 @@ import com.yahoo.jdisc.Timer;
/**
* A timer which returns the System time
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SystemTimer implements Timer {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/core/TimeoutManagerImpl.java b/jdisc_core/src/main/java/com/yahoo/jdisc/core/TimeoutManagerImpl.java
index 271c1218402..43cddaea803 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/core/TimeoutManagerImpl.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/core/TimeoutManagerImpl.java
@@ -21,7 +21,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class TimeoutManagerImpl {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/AbstractRequestHandler.java b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/AbstractRequestHandler.java
index 935b98493e2..2a9b10428c6 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/AbstractRequestHandler.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/AbstractRequestHandler.java
@@ -25,7 +25,7 @@ import com.yahoo.jdisc.Response;
* }
* </pre>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public abstract class AbstractRequestHandler extends com.yahoo.jdisc.AbstractResource implements RequestHandler {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/BindingNotFoundException.java b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/BindingNotFoundException.java
index deeccc0472c..c02588e5802 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/BindingNotFoundException.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/BindingNotFoundException.java
@@ -11,7 +11,7 @@ import java.net.URI;
* instance of this class will be thrown by the {@link Request#connect(ResponseHandler)} method when the current {@link
* BindingSet} has not binding that matches the corresponding Request's URI.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public final class BindingNotFoundException extends RuntimeException {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/BlockingContentWriter.java b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/BlockingContentWriter.java
index 3a2adf60ccf..db50e4661be 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/BlockingContentWriter.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/BlockingContentWriter.java
@@ -11,7 +11,7 @@ import java.util.Objects;
* {@link ContentChannel} calls, and wait for these to be called before returning. If {@link
* CompletionHandler#failed(Throwable)} is called, the corresponding Throwable is thrown to the caller.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
* @see FastContentWriter
*/
public final class BlockingContentWriter {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/BufferedContentChannel.java b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/BufferedContentChannel.java
index 406ee0ff6e5..fdfb2cb3abb 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/BufferedContentChannel.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/BufferedContentChannel.java
@@ -12,7 +12,7 @@ import java.util.Objects;
* {@link #connectTo(ContentChannel)} is called. Once connected, this class becomes a non-buffering proxy for the
* connected ContentChannel.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public final class BufferedContentChannel implements ContentChannel {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/CompletionHandler.java b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/CompletionHandler.java
index 4975f32adfe..123c004f37f 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/CompletionHandler.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/CompletionHandler.java
@@ -18,7 +18,7 @@ import com.yahoo.jdisc.Container;
* throughout its lifetime. This also means that the either {@link #completed()} or {@link #failed(Throwable)} MUST be
* called in order to release that reference. Failure to do so will prevent the Container from ever shutting down.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public interface CompletionHandler {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/ContentChannel.java b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/ContentChannel.java
index e01a3c312be..a1a4503eff2 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/ContentChannel.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/ContentChannel.java
@@ -21,7 +21,7 @@ import java.nio.ByteBuffer;
* requirement is regardless of any errors that may occur while calling any of its other methods or its derived {@link
* CompletionHandler}s.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public interface ContentChannel {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/ContentInputStream.java b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/ContentInputStream.java
index 04be3c1e3d4..6145163f1fc 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/ContentInputStream.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/ContentInputStream.java
@@ -5,7 +5,7 @@ package com.yahoo.jdisc.handler;
* This class extends {@link UnsafeContentInputStream} and adds a finalizer to it that calls {@link #close()}. This
* has a performance impact, but ensures that an unclosed stream does not prevent shutdown.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public final class ContentInputStream extends UnsafeContentInputStream {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/FutureCompletion.java b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/FutureCompletion.java
index e18c88382b6..2cf24ef07de 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/FutureCompletion.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/FutureCompletion.java
@@ -11,7 +11,7 @@ import com.google.common.util.concurrent.AbstractFuture;
*
* <p>Notice that calling {@link #cancel(boolean)} throws an UnsupportedOperationException.</p>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public final class FutureCompletion extends AbstractFuture<Boolean> implements CompletionHandler {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/FutureConjunction.java b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/FutureConjunction.java
index bda0f845af0..4380ad0cbd2 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/FutureConjunction.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/FutureConjunction.java
@@ -15,7 +15,7 @@ import java.util.concurrent.*;
* simply create an instance of it and add operands to it using the {@link #addOperand(ListenableFuture)} method.</p>
* TODO: consider rewriting usage of FutureConjunction to use CompletableFuture instead.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public final class FutureConjunction implements ListenableFuture<Boolean> {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/FutureResponse.java b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/FutureResponse.java
index 3360812864a..74ed9f1c101 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/FutureResponse.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/FutureResponse.java
@@ -8,7 +8,7 @@ import com.yahoo.jdisc.Response;
* This class provides an implementation of {@link ResponseHandler} that allows you to wait for a {@link Response} to
* be returned.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public final class FutureResponse extends AbstractFuture<Response> implements ResponseHandler {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/NullContent.java b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/NullContent.java
index a561823891f..6c7bbe21410 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/NullContent.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/NullContent.java
@@ -13,7 +13,7 @@ import java.nio.ByteBuffer;
* <p>A {@link RequestHandler}s that does not expect content can simply return the {@link #INSTANCE} of this class for
* every invocation of its {@link RequestHandler#handleRequest(Request, ResponseHandler)}.</p>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public final class NullContent implements ContentChannel {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/ReadableContentChannel.java b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/ReadableContentChannel.java
index 50cd2ab2e8c..2066e6982f1 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/ReadableContentChannel.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/ReadableContentChannel.java
@@ -15,7 +15,7 @@ import java.util.Queue;
* a {@link BufferedContentChannel} up front, and {@link BufferedContentChannel#connectTo(ContentChannel) connect} that
* to a ReadableContentChannel at the point where you decide to consume the data.</p>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public final class ReadableContentChannel implements ContentChannel, Iterable<ByteBuffer> {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/RequestDeniedException.java b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/RequestDeniedException.java
index 012ac92b057..63638e4913e 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/RequestDeniedException.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/RequestDeniedException.java
@@ -11,7 +11,7 @@ import java.net.URI;
* or {@link RequestHandler}. There is no automation in throwing an instance of this class, but all RequestHandlers are
* encouraged to use this where appropriate.</p>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public final class RequestDeniedException extends RuntimeException {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/RequestHandler.java b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/RequestHandler.java
index f0447949c64..c71ddb4f167 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/RequestHandler.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/RequestHandler.java
@@ -18,7 +18,7 @@ import java.util.concurrent.TimeUnit;
* {@link ContainerBuilder}, and that builder must be {@link ContainerActivator#activateContainer(ContainerBuilder)
* activated}.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public interface RequestHandler extends SharedResource {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/ResponseHandler.java b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/ResponseHandler.java
index 8e22192570c..9451191a31c 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/ResponseHandler.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/ResponseHandler.java
@@ -15,7 +15,7 @@ import com.yahoo.jdisc.service.ClientProvider;
* corresponding Request, but rather leave that to the implementation of context-aware ResponseHandlers. By creating
* light-weight ResponseHandlers on a per-Request basis, any necessary reference can be embedded within.</p>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public interface ResponseHandler {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/ThreadedRequestHandler.java b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/ThreadedRequestHandler.java
index c50df27120f..a2bea1566de 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/ThreadedRequestHandler.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/ThreadedRequestHandler.java
@@ -40,7 +40,7 @@ import java.util.concurrent.TimeUnit;
* }
* </pre>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public abstract class ThreadedRequestHandler extends AbstractRequestHandler {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/UnsafeContentInputStream.java b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/UnsafeContentInputStream.java
index c895e8fe1a5..748c2951a6a 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/handler/UnsafeContentInputStream.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/handler/UnsafeContentInputStream.java
@@ -13,7 +13,7 @@ import java.util.Objects;
* always call {@link #close()} before discarding it. Failure to do so will prevent the Container from ever shutting
* down.</p>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class UnsafeContentInputStream extends InputStream {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/service/AbstractClientProvider.java b/jdisc_core/src/main/java/com/yahoo/jdisc/service/AbstractClientProvider.java
index 8a4994ef13e..f46cdda47c5 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/service/AbstractClientProvider.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/service/AbstractClientProvider.java
@@ -9,7 +9,7 @@ import com.yahoo.jdisc.handler.ResponseHandler;
* <p>This is a convenient parent class for {@link ClientProvider} with default implementations for all but the
* essential {@link #handleRequest(Request, ResponseHandler)} method.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class AbstractClientProvider extends AbstractRequestHandler implements ClientProvider {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/service/AbstractServerProvider.java b/jdisc_core/src/main/java/com/yahoo/jdisc/service/AbstractServerProvider.java
index 5e45fbab80b..139a52a133b 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/service/AbstractServerProvider.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/service/AbstractServerProvider.java
@@ -12,7 +12,7 @@ import java.util.Objects;
* essential {@link #start()} and {@link #close()} methods. It requires that the {@link CurrentContainer} is injected in
* the constructor, since that interface is needed to dispatch {@link Request}s.</p>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public abstract class AbstractServerProvider extends AbstractResource implements ServerProvider {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/service/BindingSetNotFoundException.java b/jdisc_core/src/main/java/com/yahoo/jdisc/service/BindingSetNotFoundException.java
index a0549d91591..2ba12b52998 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/service/BindingSetNotFoundException.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/service/BindingSetNotFoundException.java
@@ -10,7 +10,7 @@ import java.net.URI;
* thrown by the {@link CurrentContainer#newReference(URI)} method when a BindingSet with the specified name does not
* exist.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class BindingSetNotFoundException extends RuntimeException {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/service/ClientProvider.java b/jdisc_core/src/main/java/com/yahoo/jdisc/service/ClientProvider.java
index fa036a24bdb..c178147a952 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/service/ClientProvider.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/service/ClientProvider.java
@@ -11,7 +11,7 @@ import com.yahoo.jdisc.handler.RequestHandler;
* {@link ContainerBuilder}, and that builder must be {@link ContainerActivator#activateContainer(ContainerBuilder)
* activated}.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface ClientProvider extends RequestHandler {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/service/ContainerNotReadyException.java b/jdisc_core/src/main/java/com/yahoo/jdisc/service/ContainerNotReadyException.java
index 2a85e9b29c1..3b34f1fe2cc 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/service/ContainerNotReadyException.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/service/ContainerNotReadyException.java
@@ -13,7 +13,7 @@ import java.net.URI;
* class will be thrown by the {@link CurrentContainer#newReference(URI)} method if it is called before a Container has
* been activated, or after a <em>null</em> argument has been passed to {@link ContainerActivator#activateContainer(ContainerBuilder)}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class ContainerNotReadyException extends RuntimeException {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/service/CurrentContainer.java b/jdisc_core/src/main/java/com/yahoo/jdisc/service/CurrentContainer.java
index 3eeba23202f..9b7af7e49f5 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/service/CurrentContainer.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/service/CurrentContainer.java
@@ -17,7 +17,7 @@ import java.net.URI;
* Request#Request(CurrentContainer, URI) appropriate Request constructor} to avoid having to worry about the keep-alive
* issue.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface CurrentContainer {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/service/NoBindingSetSelectedException.java b/jdisc_core/src/main/java/com/yahoo/jdisc/service/NoBindingSetSelectedException.java
index 232968a9248..05647379caf 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/service/NoBindingSetSelectedException.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/service/NoBindingSetSelectedException.java
@@ -11,7 +11,7 @@ import java.net.URI;
* class will be thrown by the {@link CurrentContainer#newReference(URI)} method if {@link
* BindingSetSelector#select(URI)} returned <em>null</em>.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class NoBindingSetSelectedException extends RuntimeException {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/service/ServerProvider.java b/jdisc_core/src/main/java/com/yahoo/jdisc/service/ServerProvider.java
index 756a699e821..b58f3bc5138 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/service/ServerProvider.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/service/ServerProvider.java
@@ -28,7 +28,7 @@ import java.net.URI;
* <p>All implementations of this interface will need to have a {@link CurrentContainer} injected into its constructor
* so that it is able to create and dispatch new {@link Request}s.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface ServerProvider extends SharedResource {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingClientProvider.java b/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingClientProvider.java
index 385d34fff31..b592a4eb4fc 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingClientProvider.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingClientProvider.java
@@ -8,7 +8,7 @@ import com.yahoo.jdisc.handler.ResponseHandler;
import com.yahoo.jdisc.service.ClientProvider;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class NonWorkingClientProvider extends NoopSharedResource implements ClientProvider {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingCompletionHandler.java b/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingCompletionHandler.java
index 2a21378f946..2d51fc260f4 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingCompletionHandler.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingCompletionHandler.java
@@ -4,7 +4,7 @@ package com.yahoo.jdisc.test;
import com.yahoo.jdisc.handler.CompletionHandler;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class NonWorkingCompletionHandler implements CompletionHandler {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingContentChannel.java b/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingContentChannel.java
index c3b4e0deedc..d188907d7d7 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingContentChannel.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingContentChannel.java
@@ -7,7 +7,7 @@ import com.yahoo.jdisc.handler.ContentChannel;
import java.nio.ByteBuffer;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class NonWorkingContentChannel implements ContentChannel {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingOsgiFramework.java b/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingOsgiFramework.java
index 2994bb33cc2..6b129e82a45 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingOsgiFramework.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingOsgiFramework.java
@@ -9,7 +9,7 @@ import java.util.Collections;
import java.util.List;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NonWorkingOsgiFramework implements OsgiFramework {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingRequest.java b/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingRequest.java
index 1c9c341d2f1..2286417b6e3 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingRequest.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingRequest.java
@@ -9,7 +9,7 @@ import com.yahoo.jdisc.application.Application;
import java.net.URI;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class NonWorkingRequest {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingRequestHandler.java b/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingRequestHandler.java
index 009fc1badf7..d411fcf644f 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingRequestHandler.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingRequestHandler.java
@@ -8,7 +8,7 @@ import com.yahoo.jdisc.handler.RequestHandler;
import com.yahoo.jdisc.handler.ResponseHandler;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class NonWorkingRequestHandler extends NoopSharedResource implements RequestHandler {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingResponseHandler.java b/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingResponseHandler.java
index fbefada0beb..48ebbff29fc 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingResponseHandler.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingResponseHandler.java
@@ -6,7 +6,7 @@ import com.yahoo.jdisc.handler.ContentChannel;
import com.yahoo.jdisc.handler.ResponseHandler;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NonWorkingResponseHandler implements ResponseHandler {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingServerProvider.java b/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingServerProvider.java
index 931dc705fd9..a754d36d036 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingServerProvider.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/test/NonWorkingServerProvider.java
@@ -5,7 +5,7 @@ import com.yahoo.jdisc.NoopSharedResource;
import com.yahoo.jdisc.service.ServerProvider;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class NonWorkingServerProvider extends NoopSharedResource implements ServerProvider {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/test/TestDriver.java b/jdisc_core/src/main/java/com/yahoo/jdisc/test/TestDriver.java
index a30ecd7dacd..4a87217e08f 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/test/TestDriver.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/test/TestDriver.java
@@ -42,7 +42,7 @@ import java.util.concurrent.atomic.AtomicInteger;
* FALSE, it means that either your components or the test case itself does not conform to the reference counting
* requirements of {@link Request}, {@link RequestHandler}, {@link ContentChannel}, or {@link CompletionHandler}.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class TestDriver implements ContainerActivator, CurrentContainer {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/AbstractResourceTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/AbstractResourceTestCase.java
index 6cbf901f383..aad746df40f 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/AbstractResourceTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/AbstractResourceTestCase.java
@@ -9,7 +9,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class AbstractResourceTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/ContainerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/ContainerTestCase.java
index 64d39c1d709..4c08b8f49a0 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/ContainerTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/ContainerTestCase.java
@@ -13,7 +13,7 @@ import static org.junit.Assert.assertSame;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ContainerTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/HeaderFieldsTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/HeaderFieldsTestCase.java
index 55d646f9668..8d0e93ada1a 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/HeaderFieldsTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/HeaderFieldsTestCase.java
@@ -20,7 +20,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class HeaderFieldsTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/ProxyRequestHandlerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/ProxyRequestHandlerTestCase.java
index e6758f7a4c3..b7787a76716 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/ProxyRequestHandlerTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/ProxyRequestHandlerTestCase.java
@@ -26,7 +26,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ProxyRequestHandlerTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/RequestTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/RequestTestCase.java
index cd5e07f1224..ab92f5fdcca 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/RequestTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/RequestTestCase.java
@@ -29,7 +29,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RequestTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/ResponseTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/ResponseTestCase.java
index 7b1d9f28551..b52f380f427 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/ResponseTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/ResponseTestCase.java
@@ -9,7 +9,7 @@ import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ResponseTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/AbstractApplicationTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/AbstractApplicationTestCase.java
index b38460d5596..ecc8dca1acb 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/application/AbstractApplicationTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/AbstractApplicationTestCase.java
@@ -16,7 +16,7 @@ import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class AbstractApplicationTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/ApplicationNotReadyTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/ApplicationNotReadyTestCase.java
index 3f48e46abe0..463336abfe6 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/application/ApplicationNotReadyTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/ApplicationNotReadyTestCase.java
@@ -12,7 +12,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ApplicationNotReadyTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingMatchTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingMatchTestCase.java
index 21a3ae08c49..d6112947663 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingMatchTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingMatchTestCase.java
@@ -11,7 +11,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class BindingMatchTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingRepositoryTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingRepositoryTestCase.java
index d69121d219e..273c45b2fbe 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingRepositoryTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingRepositoryTestCase.java
@@ -22,7 +22,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class BindingRepositoryTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingSetTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingSetTestCase.java
index 09b7bf11948..028d0d69df6 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingSetTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingSetTestCase.java
@@ -23,7 +23,7 @@ import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class BindingSetTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/BundleInstallationExceptionTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/BundleInstallationExceptionTestCase.java
index 53f751d0b86..e45b448dd06 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/application/BundleInstallationExceptionTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/BundleInstallationExceptionTestCase.java
@@ -15,7 +15,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class BundleInstallationExceptionTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/ContainerBuilderTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/ContainerBuilderTestCase.java
index df7d380732d..aeb3c310c17 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/application/ContainerBuilderTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/ContainerBuilderTestCase.java
@@ -20,7 +20,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ContainerBuilderTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/ContainerThreadTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/ContainerThreadTestCase.java
index bb7fcbe4b22..bda43414667 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/application/ContainerThreadTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/ContainerThreadTestCase.java
@@ -12,7 +12,7 @@ import static org.junit.Assert.assertSame;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ContainerThreadTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/GlobPatternTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/GlobPatternTestCase.java
index 4d9e8cdc7ba..739fc2e2118 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/application/GlobPatternTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/GlobPatternTestCase.java
@@ -15,7 +15,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class GlobPatternTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/GuiceRepositoryTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/GuiceRepositoryTestCase.java
index 70ca6fe4ac9..804f439964e 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/application/GuiceRepositoryTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/GuiceRepositoryTestCase.java
@@ -25,7 +25,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GuiceRepositoryTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/MetricImplTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/MetricImplTestCase.java
index a8e4608d502..2dd3ead9fd9 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/application/MetricImplTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/MetricImplTestCase.java
@@ -19,7 +19,7 @@ import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MetricImplTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/OsgiHeaderTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/OsgiHeaderTestCase.java
index 9e0ec357f44..b668ec210c5 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/application/OsgiHeaderTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/OsgiHeaderTestCase.java
@@ -7,7 +7,7 @@ import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class OsgiHeaderTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/OsgiRepositoryTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/OsgiRepositoryTestCase.java
index 9d8ae0745b8..9c0483aac3a 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/application/OsgiRepositoryTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/OsgiRepositoryTestCase.java
@@ -7,7 +7,7 @@ import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class OsgiRepositoryTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/ResourcePoolTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/ResourcePoolTestCase.java
index 59cacbc308b..051db65d56b 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/application/ResourcePoolTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/ResourcePoolTestCase.java
@@ -15,7 +15,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ResourcePoolTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/ServerRepositoryTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/ServerRepositoryTestCase.java
index cbde68eece8..f6f4df25c60 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/application/ServerRepositoryTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/ServerRepositoryTestCase.java
@@ -14,7 +14,7 @@ import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ServerRepositoryTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/UriPatternTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/UriPatternTestCase.java
index 2533e20a268..7ee5b038d6a 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/application/UriPatternTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/UriPatternTestCase.java
@@ -16,7 +16,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class UriPatternTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/BindingMatchingTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/BindingMatchingTestCase.java
index 96fb314ba5f..ebc78d0b70e 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/BindingMatchingTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/BindingMatchingTestCase.java
@@ -21,7 +21,7 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class BindingMatchingTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/LatencyTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/LatencyTestCase.java
index 0c94cc7b3f0..9eafb93f356 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/LatencyTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/LatencyTestCase.java
@@ -20,7 +20,7 @@ import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class LatencyTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/ThroughputTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/ThroughputTestCase.java
index 7dc55cdef69..aefcac2fa91 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/ThroughputTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/ThroughputTestCase.java
@@ -29,7 +29,7 @@ import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ThroughputTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/UriMatchingTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/UriMatchingTestCase.java
index 8c6c6ff2d25..50b427e61f5 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/UriMatchingTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/UriMatchingTestCase.java
@@ -11,7 +11,7 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class UriMatchingTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/client/AbstractClientApplicationTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/client/AbstractClientApplicationTestCase.java
index 9565d13db30..2eaaf6d9041 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/client/AbstractClientApplicationTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/client/AbstractClientApplicationTestCase.java
@@ -20,7 +20,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class AbstractClientApplicationTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/client/ClientDriverTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/client/ClientDriverTestCase.java
index f033cf740eb..f3f405ac595 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/client/ClientDriverTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/client/ClientDriverTestCase.java
@@ -9,7 +9,7 @@ import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ClientDriverTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ActiveContainerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ActiveContainerTestCase.java
index a6f3bd440cb..a0ac265ce78 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ActiveContainerTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ActiveContainerTestCase.java
@@ -22,7 +22,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ActiveContainerTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationConfigModuleTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationConfigModuleTestCase.java
index 3c5f97a3bc8..a0992f11a02 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationConfigModuleTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationConfigModuleTestCase.java
@@ -20,7 +20,7 @@ import static org.junit.Assert.fail;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ApplicationConfigModuleTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationEnvironmentModuleTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationEnvironmentModuleTestCase.java
index 965a132cd92..9c2f1308355 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationEnvironmentModuleTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationEnvironmentModuleTestCase.java
@@ -22,7 +22,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ApplicationEnvironmentModuleTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationLoaderTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationLoaderTestCase.java
index 62f9fa1d395..89ca49a6273 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationLoaderTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationLoaderTestCase.java
@@ -36,7 +36,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ApplicationLoaderTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationRestartTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationRestartTestCase.java
index 08496fe2fde..96c9d2135ac 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationRestartTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationRestartTestCase.java
@@ -24,7 +24,7 @@ import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ApplicationRestartTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationShutdownTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationShutdownTestCase.java
index 4dd4fad7de4..0a3aa974f76 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationShutdownTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationShutdownTestCase.java
@@ -20,7 +20,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ApplicationShutdownTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/BootstrapDaemonTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/BootstrapDaemonTestCase.java
index 7fd7af71e7b..df8223a6d86 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/BootstrapDaemonTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/BootstrapDaemonTestCase.java
@@ -13,7 +13,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class BootstrapDaemonTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/BundleLocationResolverTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/BundleLocationResolverTestCase.java
index c59cc152fa6..686ca0a6835 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/BundleLocationResolverTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/BundleLocationResolverTestCase.java
@@ -9,7 +9,7 @@ import java.io.IOException;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class BundleLocationResolverTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ConsoleLogFormatterTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ConsoleLogFormatterTestCase.java
index a0b6478ce3f..54809897084 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ConsoleLogFormatterTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ConsoleLogFormatterTestCase.java
@@ -16,7 +16,7 @@ import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ConsoleLogFormatterTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ConsoleLogManagerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ConsoleLogManagerTestCase.java
index d002483a101..ba90baccffd 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ConsoleLogManagerTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ConsoleLogManagerTestCase.java
@@ -16,7 +16,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ConsoleLogManagerTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerResourceTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerResourceTestCase.java
index e8a41000183..5e4b7db5683 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerResourceTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerResourceTestCase.java
@@ -20,7 +20,7 @@ import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ContainerResourceTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerShutdownTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerShutdownTestCase.java
index 45ce985e46b..5777c8ff126 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerShutdownTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerShutdownTestCase.java
@@ -21,7 +21,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ContainerShutdownTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerSnapshotTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerSnapshotTestCase.java
index 7e41bdcead2..0ea1040306c 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerSnapshotTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerSnapshotTestCase.java
@@ -26,7 +26,7 @@ import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ContainerSnapshotTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerTerminationTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerTerminationTestCase.java
index 8f210cd7939..69e66c96cd8 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerTerminationTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerTerminationTestCase.java
@@ -11,7 +11,7 @@ import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ContainerTerminationTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/DefaultBindingSelectorTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/DefaultBindingSelectorTestCase.java
index cca2853ea17..e372836a918 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/DefaultBindingSelectorTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/DefaultBindingSelectorTestCase.java
@@ -13,7 +13,7 @@ import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DefaultBindingSelectorTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ExportPackagesTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ExportPackagesTestCase.java
index b6b1eba6719..1811e173e73 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ExportPackagesTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ExportPackagesTestCase.java
@@ -11,7 +11,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ExportPackagesTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/FelixFrameworkTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/FelixFrameworkTestCase.java
index d0f73f768e7..3e898abeb8d 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/FelixFrameworkTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/FelixFrameworkTestCase.java
@@ -9,7 +9,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class FelixFrameworkTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/FelixParamsTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/FelixParamsTestCase.java
index 03694020b00..55fca97841a 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/FelixParamsTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/FelixParamsTestCase.java
@@ -14,7 +14,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class FelixParamsTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogHandlerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogHandlerTestCase.java
index 7dca8c45d0b..3338b631030 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogHandlerTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogHandlerTestCase.java
@@ -20,7 +20,7 @@ import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class OsgiLogHandlerTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogManagerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogManagerTestCase.java
index eff88af2846..2f4f005cbb6 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogManagerTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogManagerTestCase.java
@@ -19,7 +19,7 @@ import static org.junit.Assert.assertSame;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class OsgiLogManagerTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogServiceTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogServiceTestCase.java
index 50d9800d752..1434ca1b704 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogServiceTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogServiceTestCase.java
@@ -15,7 +15,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class OsgiLogServiceTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ScheduledQueueTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ScheduledQueueTestCase.java
index 10c66aa8c93..5af251bd3a0 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ScheduledQueueTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ScheduledQueueTestCase.java
@@ -16,7 +16,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ScheduledQueueTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/SystemTimerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/SystemTimerTestCase.java
index 0241b3a72be..147831de5c0 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/SystemTimerTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/SystemTimerTestCase.java
@@ -9,7 +9,7 @@ import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SystemTimerTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/TimeoutManagerImplTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/TimeoutManagerImplTestCase.java
index e5481f8b607..100fb5d884d 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/core/TimeoutManagerImplTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/TimeoutManagerImplTestCase.java
@@ -37,7 +37,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class TimeoutManagerImplTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/AbstractContentOutputStreamTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/AbstractContentOutputStreamTestCase.java
index 63435df71c2..59404293660 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/AbstractContentOutputStreamTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/AbstractContentOutputStreamTestCase.java
@@ -16,7 +16,7 @@ import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class AbstractContentOutputStreamTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/AbstractRequestHandlerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/AbstractRequestHandlerTestCase.java
index 8db808d2c87..a80f3725b51 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/AbstractRequestHandlerTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/AbstractRequestHandlerTestCase.java
@@ -21,7 +21,7 @@ import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class AbstractRequestHandlerTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/BindingNotFoundTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/BindingNotFoundTestCase.java
index c301cf385a1..481ba728067 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/BindingNotFoundTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/BindingNotFoundTestCase.java
@@ -13,7 +13,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class BindingNotFoundTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/BlockingContentWriterTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/BlockingContentWriterTestCase.java
index 1ffc0d34b1c..637ff8888f5 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/BlockingContentWriterTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/BlockingContentWriterTestCase.java
@@ -12,7 +12,7 @@ import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class BlockingContentWriterTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/BufferedContentChannelTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/BufferedContentChannelTestCase.java
index 050ef68f1a5..0f33e52c772 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/BufferedContentChannelTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/BufferedContentChannelTestCase.java
@@ -16,7 +16,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class BufferedContentChannelTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/CallableRequestDispatchTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/CallableRequestDispatchTestCase.java
index a72a59c9732..97530cdfe8a 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/CallableRequestDispatchTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/CallableRequestDispatchTestCase.java
@@ -13,7 +13,7 @@ import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CallableRequestDispatchTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/CallableResponseDispatchTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/CallableResponseDispatchTestCase.java
index 6a2b3fb6987..44c382260cb 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/CallableResponseDispatchTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/CallableResponseDispatchTestCase.java
@@ -10,7 +10,7 @@ import static org.junit.Assert.assertSame;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CallableResponseDispatchTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ContentInputStreamTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ContentInputStreamTestCase.java
index bff37e4db99..cdc75fbe61a 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ContentInputStreamTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ContentInputStreamTestCase.java
@@ -9,7 +9,7 @@ import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ContentInputStreamTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FastContentOutputStreamTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FastContentOutputStreamTestCase.java
index 1df371a237e..96c80d491be 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FastContentOutputStreamTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FastContentOutputStreamTestCase.java
@@ -13,7 +13,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class FastContentOutputStreamTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FastContentWriterTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FastContentWriterTestCase.java
index e754948a241..96e7799f54f 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FastContentWriterTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FastContentWriterTestCase.java
@@ -26,7 +26,7 @@ import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertArrayEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class FastContentWriterTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureCompletionTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureCompletionTestCase.java
index c15de4f0490..46a5d89dcaf 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureCompletionTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureCompletionTestCase.java
@@ -14,7 +14,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class FutureCompletionTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureConjunctionTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureConjunctionTestCase.java
index e0351a42232..aacffc92a4a 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureConjunctionTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureConjunctionTestCase.java
@@ -19,7 +19,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class FutureConjunctionTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureResponseTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureResponseTestCase.java
index 32952c110da..9ec3586d563 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureResponseTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureResponseTestCase.java
@@ -15,7 +15,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class FutureResponseTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/NullContentTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/NullContentTestCase.java
index 4a9ec928640..5a1f4a5854f 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/NullContentTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/NullContentTestCase.java
@@ -9,7 +9,7 @@ import java.nio.ByteBuffer;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NullContentTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ReadableContentChannelTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ReadableContentChannelTestCase.java
index fcd0151abc0..34ca028bba2 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ReadableContentChannelTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ReadableContentChannelTestCase.java
@@ -18,7 +18,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ReadableContentChannelTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/RequestDeniedTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/RequestDeniedTestCase.java
index 26568f78052..981df95ef41 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/RequestDeniedTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/RequestDeniedTestCase.java
@@ -14,7 +14,7 @@ import static org.junit.Assert.fail;
import static org.junit.Assert.assertSame;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RequestDeniedTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/RequestDispatchTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/RequestDispatchTestCase.java
index 2ce27038345..0a15ead00e8 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/RequestDispatchTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/RequestDispatchTestCase.java
@@ -25,7 +25,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class RequestDispatchTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ResponseDispatchTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ResponseDispatchTestCase.java
index af1396eec95..c7d52f588b0 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ResponseDispatchTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ResponseDispatchTestCase.java
@@ -23,7 +23,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ResponseDispatchTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/RunnableLatch.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/RunnableLatch.java
index 410aacdad05..006012551be 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/RunnableLatch.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/RunnableLatch.java
@@ -5,7 +5,7 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
class RunnableLatch implements Runnable {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ThreadedRequestHandlerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ThreadedRequestHandlerTestCase.java
index cd4710e9030..92d2fbf78b7 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ThreadedRequestHandlerTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ThreadedRequestHandlerTestCase.java
@@ -26,7 +26,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ThreadedRequestHandlerTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/UnsafeContentInputStreamTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/UnsafeContentInputStreamTestCase.java
index d7527bbdc1b..c00fab6cb56 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/UnsafeContentInputStreamTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/UnsafeContentInputStreamTestCase.java
@@ -15,7 +15,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class UnsafeContentInputStreamTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/service/AbstractClientProviderTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/service/AbstractClientProviderTestCase.java
index 4d35d919ac2..29cc2ebe581 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/service/AbstractClientProviderTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/service/AbstractClientProviderTestCase.java
@@ -10,7 +10,7 @@ import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class AbstractClientProviderTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/service/AbstractServerProviderTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/service/AbstractServerProviderTestCase.java
index 809b857b4f1..e79b7201fdf 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/service/AbstractServerProviderTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/service/AbstractServerProviderTestCase.java
@@ -11,7 +11,7 @@ import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class AbstractServerProviderTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/service/BindingSetNotFoundTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/service/BindingSetNotFoundTestCase.java
index a1635b44571..b875e0b1dc8 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/service/BindingSetNotFoundTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/service/BindingSetNotFoundTestCase.java
@@ -13,7 +13,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class BindingSetNotFoundTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/service/ConnectToHandlerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/service/ConnectToHandlerTestCase.java
index 6c2c445a136..2e4dbb66aa0 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/service/ConnectToHandlerTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/service/ConnectToHandlerTestCase.java
@@ -20,7 +20,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ConnectToHandlerTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/service/ContainerNotReadyTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/service/ContainerNotReadyTestCase.java
index 451cbc7ab08..bdc9f3a0c2c 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/service/ContainerNotReadyTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/service/ContainerNotReadyTestCase.java
@@ -10,7 +10,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ContainerNotReadyTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/service/CurrentContainerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/service/CurrentContainerTestCase.java
index 212916bdc9a..d8fc266da03 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/service/CurrentContainerTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/service/CurrentContainerTestCase.java
@@ -11,7 +11,7 @@ import static org.junit.Assert.assertNotNull;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CurrentContainerTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/service/NoBindingSetSelectedTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/service/NoBindingSetSelectedTestCase.java
index 5354e865db6..d6561e92202 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/service/NoBindingSetSelectedTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/service/NoBindingSetSelectedTestCase.java
@@ -13,7 +13,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NoBindingSetSelectedTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingClientTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingClientTestCase.java
index 80b18458230..e1b664e91ca 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingClientTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingClientTestCase.java
@@ -8,7 +8,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NonWorkingClientTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingCompletionHandlerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingCompletionHandlerTestCase.java
index 96c12cbd861..e05c937e671 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingCompletionHandlerTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingCompletionHandlerTestCase.java
@@ -8,7 +8,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NonWorkingCompletionHandlerTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingContentChannelTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingContentChannelTestCase.java
index 6bc7bde9123..446377cb472 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingContentChannelTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingContentChannelTestCase.java
@@ -11,7 +11,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NonWorkingContentChannelTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingOsgiFrameworkTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingOsgiFrameworkTestCase.java
index 1b624cc3931..f88c6951e56 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingOsgiFrameworkTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingOsgiFrameworkTestCase.java
@@ -14,7 +14,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NonWorkingOsgiFrameworkTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingRequestHandlerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingRequestHandlerTestCase.java
index 067a61b6c47..f91e29084ae 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingRequestHandlerTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingRequestHandlerTestCase.java
@@ -8,7 +8,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NonWorkingRequestHandlerTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingRequestTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingRequestTestCase.java
index a7269aec9e9..0e321f35eb2 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingRequestTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingRequestTestCase.java
@@ -12,7 +12,7 @@ import static org.junit.Assert.assertNotNull;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NonWorkingRequestTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingResponseHandlerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingResponseHandlerTestCase.java
index 6087d4ef90b..d6d6dc05094 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingResponseHandlerTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingResponseHandlerTestCase.java
@@ -8,7 +8,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NonWorkingResponseHandlerTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingServerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingServerTestCase.java
index 842cbfd459b..6c27c516f93 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingServerTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingServerTestCase.java
@@ -5,7 +5,7 @@ import com.yahoo.jdisc.service.ServerProvider;
import org.junit.Test;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NonWorkingServerTestCase {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/test/ServerProviderConformanceTestTest.java b/jdisc_core/src/test/java/com/yahoo/jdisc/test/ServerProviderConformanceTestTest.java
index 343429eff8c..678c9b2796c 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/test/ServerProviderConformanceTestTest.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/test/ServerProviderConformanceTestTest.java
@@ -25,7 +25,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ServerProviderConformanceTestTest extends ServerProviderConformanceTest {
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/test/TestDriverTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/test/TestDriverTestCase.java
index 292ebbe93ca..3d59bd692aa 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/test/TestDriverTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/test/TestDriverTestCase.java
@@ -21,7 +21,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class TestDriverTestCase {
diff --git a/jdisc_core_test/integration_test/pom.xml b/jdisc_core_test/integration_test/pom.xml
index d68d6e2966b..e31fa122232 100644
--- a/jdisc_core_test/integration_test/pom.xml
+++ b/jdisc_core_test/integration_test/pom.xml
@@ -230,14 +230,34 @@
<scope>test</scope>
</dependency>
</dependencies>
+ <profiles>
+ <profile>
+ <id>java9-surefire</id>
+ <activation>
+ <jdk>[9, )</jdk>
+ </activation>
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <!-- Allow installing fragment bundles, see felix.framework:ExtensionManager.addExtensionBundle -->
+ <argLine>
+ --add-opens=java.base/jdk.internal.loader=ALL-UNNAMED
+ </argLine>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+ </profile>
+ </profiles>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
diff --git a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/AbstractApplicationTestCase.java b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/AbstractApplicationTestCase.java
index 80c3c72986a..8059bff7bb0 100644
--- a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/AbstractApplicationTestCase.java
+++ b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/AbstractApplicationTestCase.java
@@ -15,7 +15,7 @@ import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class AbstractApplicationTestCase {
diff --git a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/BundleActivatorIntegrationTest.java b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/BundleActivatorIntegrationTest.java
index b957ea7d083..6388643148c 100644
--- a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/BundleActivatorIntegrationTest.java
+++ b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/BundleActivatorIntegrationTest.java
@@ -12,7 +12,7 @@ import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class BundleActivatorIntegrationTest {
diff --git a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/BundleInstallerIntegrationTest.java b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/BundleInstallerIntegrationTest.java
index 68a80330322..e743d04607f 100644
--- a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/BundleInstallerIntegrationTest.java
+++ b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/BundleInstallerIntegrationTest.java
@@ -17,7 +17,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class BundleInstallerIntegrationTest {
diff --git a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/GuiceRepositoryIntegrationTest.java b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/GuiceRepositoryIntegrationTest.java
index e5c14356213..7929de8c63b 100644
--- a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/GuiceRepositoryIntegrationTest.java
+++ b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/GuiceRepositoryIntegrationTest.java
@@ -23,7 +23,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GuiceRepositoryIntegrationTest {
diff --git a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/ServerRepositoryIntegrationTest.java b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/ServerRepositoryIntegrationTest.java
index ec84a824175..056fba7652f 100644
--- a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/ServerRepositoryIntegrationTest.java
+++ b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/application/ServerRepositoryIntegrationTest.java
@@ -20,7 +20,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ServerRepositoryIntegrationTest {
diff --git a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/client/ClientDriverIntegrationTest.java b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/client/ClientDriverIntegrationTest.java
index 20509738435..8b9b031e982 100644
--- a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/client/ClientDriverIntegrationTest.java
+++ b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/client/ClientDriverIntegrationTest.java
@@ -15,7 +15,7 @@ import java.util.logging.Logger;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ClientDriverIntegrationTest {
diff --git a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/ApplicationLoaderIntegrationTest.java b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/ApplicationLoaderIntegrationTest.java
index a47d745ab57..d1b907a05ec 100644
--- a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/ApplicationLoaderIntegrationTest.java
+++ b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/ApplicationLoaderIntegrationTest.java
@@ -21,7 +21,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ApplicationLoaderIntegrationTest {
diff --git a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/BootstrapDaemonIntegrationTest.java b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/BootstrapDaemonIntegrationTest.java
index a49701fd839..d052d2d4715 100644
--- a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/BootstrapDaemonIntegrationTest.java
+++ b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/BootstrapDaemonIntegrationTest.java
@@ -12,7 +12,7 @@ import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class BootstrapDaemonIntegrationTest {
diff --git a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/ExportPackagesIntegrationTest.java b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/ExportPackagesIntegrationTest.java
index 0a80ce141ee..c965bec8544 100644
--- a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/ExportPackagesIntegrationTest.java
+++ b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/ExportPackagesIntegrationTest.java
@@ -13,7 +13,7 @@ import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ExportPackagesIntegrationTest {
diff --git a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/FelixFrameworkIntegrationTest.java b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/FelixFrameworkIntegrationTest.java
index 7995def7045..e8551b44a3c 100644
--- a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/FelixFrameworkIntegrationTest.java
+++ b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/FelixFrameworkIntegrationTest.java
@@ -23,7 +23,7 @@ import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class FelixFrameworkIntegrationTest {
diff --git a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/OsgiLogManagerIntegrationTest.java b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/OsgiLogManagerIntegrationTest.java
index bb660003b63..6c6b3611993 100644
--- a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/OsgiLogManagerIntegrationTest.java
+++ b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/core/OsgiLogManagerIntegrationTest.java
@@ -13,7 +13,7 @@ import static org.junit.Assert.assertNotNull;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class OsgiLogManagerIntegrationTest {
diff --git a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/test/TestDriverIntegrationTest.java b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/test/TestDriverIntegrationTest.java
index 44cfe8318b2..9ffe11afb19 100644
--- a/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/test/TestDriverIntegrationTest.java
+++ b/jdisc_core_test/integration_test/src/test/java/com/yahoo/jdisc/test/TestDriverIntegrationTest.java
@@ -8,7 +8,7 @@ import org.junit.Test;
import java.util.concurrent.CountDownLatch;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class TestDriverIntegrationTest {
diff --git a/jdisc_core_test/test_bundles/app-a/src/main/java/com/yahoo/jdisc/bundle/ApplicationA.java b/jdisc_core_test/test_bundles/app-a/src/main/java/com/yahoo/jdisc/bundle/ApplicationA.java
index 642c14a03fa..8d67813bc7e 100644
--- a/jdisc_core_test/test_bundles/app-a/src/main/java/com/yahoo/jdisc/bundle/ApplicationA.java
+++ b/jdisc_core_test/test_bundles/app-a/src/main/java/com/yahoo/jdisc/bundle/ApplicationA.java
@@ -8,7 +8,7 @@ import com.yahoo.jdisc.application.Application;
import java.util.concurrent.CountDownLatch;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ApplicationA implements Application {
diff --git a/jdisc_core_test/test_bundles/app-b-priv/src/main/java/com/yahoo/jdisc/bundle/ApplicationB.java b/jdisc_core_test/test_bundles/app-b-priv/src/main/java/com/yahoo/jdisc/bundle/ApplicationB.java
index 1c9f91cdf77..ddc85519fe9 100644
--- a/jdisc_core_test/test_bundles/app-b-priv/src/main/java/com/yahoo/jdisc/bundle/ApplicationB.java
+++ b/jdisc_core_test/test_bundles/app-b-priv/src/main/java/com/yahoo/jdisc/bundle/ApplicationB.java
@@ -8,7 +8,7 @@ import com.yahoo.jdisc.application.Application;
import java.util.concurrent.CountDownLatch;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ApplicationB implements Application {
diff --git a/jdisc_core_test/test_bundles/app-ca/src/main/java/com/yahoo/jdisc/bundle/ApplicationC.java b/jdisc_core_test/test_bundles/app-ca/src/main/java/com/yahoo/jdisc/bundle/ApplicationC.java
index 084ee44fd71..18eefb0772b 100644
--- a/jdisc_core_test/test_bundles/app-ca/src/main/java/com/yahoo/jdisc/bundle/ApplicationC.java
+++ b/jdisc_core_test/test_bundles/app-ca/src/main/java/com/yahoo/jdisc/bundle/ApplicationC.java
@@ -5,7 +5,7 @@ import com.yahoo.jdisc.application.Application;
import com.yahoo.jdisc.bundle.a.CertificateA;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ApplicationC implements Application {
diff --git a/jdisc_core_test/test_bundles/app-dj/src/main/java/com/yahoo/jdisc/bundle/ApplicationD.java b/jdisc_core_test/test_bundles/app-dj/src/main/java/com/yahoo/jdisc/bundle/ApplicationD.java
index 402ac2cfb1c..ad2d9856f7b 100644
--- a/jdisc_core_test/test_bundles/app-dj/src/main/java/com/yahoo/jdisc/bundle/ApplicationD.java
+++ b/jdisc_core_test/test_bundles/app-dj/src/main/java/com/yahoo/jdisc/bundle/ApplicationD.java
@@ -5,7 +5,7 @@ import com.yahoo.jdisc.application.Application;
import com.yahoo.jdisc.bundle.j.CertificateJ;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ApplicationD implements Application {
diff --git a/jdisc_core_test/test_bundles/app-ej-priv/src/main/java/com/yahoo/jdisc/bundle/ApplicationE.java b/jdisc_core_test/test_bundles/app-ej-priv/src/main/java/com/yahoo/jdisc/bundle/ApplicationE.java
index ecb912553a7..201bf2ecf9d 100644
--- a/jdisc_core_test/test_bundles/app-ej-priv/src/main/java/com/yahoo/jdisc/bundle/ApplicationE.java
+++ b/jdisc_core_test/test_bundles/app-ej-priv/src/main/java/com/yahoo/jdisc/bundle/ApplicationE.java
@@ -5,7 +5,7 @@ import com.yahoo.jdisc.application.Application;
import com.yahoo.jdisc.bundle.j.CertificateJ;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ApplicationE implements Application {
diff --git a/jdisc_core_test/test_bundles/app-f-more/src/main/java/com/yahoo/jdisc/bundle/ApplicationF.java b/jdisc_core_test/test_bundles/app-f-more/src/main/java/com/yahoo/jdisc/bundle/ApplicationF.java
index 2a1a14e0f0a..7223ed693cd 100644
--- a/jdisc_core_test/test_bundles/app-f-more/src/main/java/com/yahoo/jdisc/bundle/ApplicationF.java
+++ b/jdisc_core_test/test_bundles/app-f-more/src/main/java/com/yahoo/jdisc/bundle/ApplicationF.java
@@ -4,7 +4,7 @@ package com.yahoo.jdisc.bundle;
import com.yahoo.jdisc.application.Application;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ApplicationF implements Application {
diff --git a/jdisc_core_test/test_bundles/app-g-act/src/main/java/com/yahoo/jdisc/bundle/ApplicationG.java b/jdisc_core_test/test_bundles/app-g-act/src/main/java/com/yahoo/jdisc/bundle/ApplicationG.java
index c22c6cadd69..9b4d7501ac0 100644
--- a/jdisc_core_test/test_bundles/app-g-act/src/main/java/com/yahoo/jdisc/bundle/ApplicationG.java
+++ b/jdisc_core_test/test_bundles/app-g-act/src/main/java/com/yahoo/jdisc/bundle/ApplicationG.java
@@ -4,7 +4,7 @@ package com.yahoo.jdisc.bundle;
import com.yahoo.jdisc.application.Application;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ApplicationG implements Application {
diff --git a/jdisc_core_test/test_bundles/app-g-act/src/main/java/com/yahoo/jdisc/bundle/MyBundleActivator.java b/jdisc_core_test/test_bundles/app-g-act/src/main/java/com/yahoo/jdisc/bundle/MyBundleActivator.java
index 7b0db195f06..51a95b8e3a9 100644
--- a/jdisc_core_test/test_bundles/app-g-act/src/main/java/com/yahoo/jdisc/bundle/MyBundleActivator.java
+++ b/jdisc_core_test/test_bundles/app-g-act/src/main/java/com/yahoo/jdisc/bundle/MyBundleActivator.java
@@ -8,7 +8,7 @@ import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MyBundleActivator implements BundleActivator {
diff --git a/jdisc_core_test/test_bundles/app-g-act/src/main/java/com/yahoo/jdisc/bundle/MyService.java b/jdisc_core_test/test_bundles/app-g-act/src/main/java/com/yahoo/jdisc/bundle/MyService.java
index 2eb06e9dd94..a5cb9be9a47 100644
--- a/jdisc_core_test/test_bundles/app-g-act/src/main/java/com/yahoo/jdisc/bundle/MyService.java
+++ b/jdisc_core_test/test_bundles/app-g-act/src/main/java/com/yahoo/jdisc/bundle/MyService.java
@@ -5,7 +5,7 @@ import com.yahoo.jdisc.service.AbstractServerProvider;
import com.yahoo.jdisc.service.CurrentContainer;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class MyService extends AbstractServerProvider {
diff --git a/jdisc_core_test/test_bundles/app-h-log/src/main/java/com/yahoo/jdisc/bundle/ApplicationH.java b/jdisc_core_test/test_bundles/app-h-log/src/main/java/com/yahoo/jdisc/bundle/ApplicationH.java
index f014a16ddb3..04eabc67900 100644
--- a/jdisc_core_test/test_bundles/app-h-log/src/main/java/com/yahoo/jdisc/bundle/ApplicationH.java
+++ b/jdisc_core_test/test_bundles/app-h-log/src/main/java/com/yahoo/jdisc/bundle/ApplicationH.java
@@ -8,7 +8,7 @@ import com.yahoo.jdisc.application.ContainerActivator;
import com.yahoo.jdisc.service.CurrentContainer;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class ApplicationH extends AbstractApplication {
diff --git a/jdisc_core_test/test_bundles/cert-a/src/main/java/com/yahoo/jdisc/bundle/a/CertificateA.java b/jdisc_core_test/test_bundles/cert-a/src/main/java/com/yahoo/jdisc/bundle/a/CertificateA.java
index 31d17534a9b..d815c164639 100644
--- a/jdisc_core_test/test_bundles/cert-a/src/main/java/com/yahoo/jdisc/bundle/a/CertificateA.java
+++ b/jdisc_core_test/test_bundles/cert-a/src/main/java/com/yahoo/jdisc/bundle/a/CertificateA.java
@@ -2,7 +2,7 @@
package com.yahoo.jdisc.bundle.a;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CertificateA {
diff --git a/jdisc_core_test/test_bundles/cert-b/pom.xml b/jdisc_core_test/test_bundles/cert-b/pom.xml
index fb2a18ebc97..c634dda581a 100644
--- a/jdisc_core_test/test_bundles/cert-b/pom.xml
+++ b/jdisc_core_test/test_bundles/cert-b/pom.xml
@@ -12,20 +12,17 @@
</parent>
<artifactId>cert-b</artifactId>
<version>6-SNAPSHOT</version>
- <packaging>bundle</packaging>
+ <packaging>container-plugin</packaging>
<name>${project.artifactId}</name>
<build>
<plugins>
<plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
- <instructions>
- <Export-Package>
- com.yahoo.jdisc.bundle.b
- </Export-Package>
- </instructions>
+ <!-- The Vespa bundle-plugin doesn't include groupId. TODO Vespa 7: remove if that is fixed. -->
+ <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
</configuration>
</plugin>
</plugins>
diff --git a/jdisc_core_test/test_bundles/cert-b/src/main/java/com/yahoo/jdisc/bundle/b/CertificateB.java b/jdisc_core_test/test_bundles/cert-b/src/main/java/com/yahoo/jdisc/bundle/b/CertificateB.java
index 78767a38366..811c95745ad 100644
--- a/jdisc_core_test/test_bundles/cert-b/src/main/java/com/yahoo/jdisc/bundle/b/CertificateB.java
+++ b/jdisc_core_test/test_bundles/cert-b/src/main/java/com/yahoo/jdisc/bundle/b/CertificateB.java
@@ -2,7 +2,7 @@
package com.yahoo.jdisc.bundle.b;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CertificateB {
diff --git a/jdisc_core_test/test_bundles/cert-b/src/main/java/com/yahoo/jdisc/bundle/b/package-info.java b/jdisc_core_test/test_bundles/cert-b/src/main/java/com/yahoo/jdisc/bundle/b/package-info.java
new file mode 100644
index 00000000000..2773287114b
--- /dev/null
+++ b/jdisc_core_test/test_bundles/cert-b/src/main/java/com/yahoo/jdisc/bundle/b/package-info.java
@@ -0,0 +1,4 @@
+@ExportPackage
+package com.yahoo.jdisc.bundle.b;
+
+import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/jdisc_core_test/test_bundles/cert-ca/src/main/java/com/yahoo/jdisc/bundle/c/CertificateC.java b/jdisc_core_test/test_bundles/cert-ca/src/main/java/com/yahoo/jdisc/bundle/c/CertificateC.java
index 6e688d1c462..e6d534cf0d0 100644
--- a/jdisc_core_test/test_bundles/cert-ca/src/main/java/com/yahoo/jdisc/bundle/c/CertificateC.java
+++ b/jdisc_core_test/test_bundles/cert-ca/src/main/java/com/yahoo/jdisc/bundle/c/CertificateC.java
@@ -4,7 +4,7 @@ package com.yahoo.jdisc.bundle.c;
import com.yahoo.jdisc.bundle.a.CertificateA;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CertificateC {
diff --git a/jdisc_core_test/test_bundles/cert-dc/src/main/java/com/yahoo/jdisc/bundle/d/CertificateD.java b/jdisc_core_test/test_bundles/cert-dc/src/main/java/com/yahoo/jdisc/bundle/d/CertificateD.java
index ec715b593ee..106a43a170d 100644
--- a/jdisc_core_test/test_bundles/cert-dc/src/main/java/com/yahoo/jdisc/bundle/d/CertificateD.java
+++ b/jdisc_core_test/test_bundles/cert-dc/src/main/java/com/yahoo/jdisc/bundle/d/CertificateD.java
@@ -4,7 +4,7 @@ package com.yahoo.jdisc.bundle.d;
import com.yahoo.jdisc.bundle.c.CertificateC;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CertificateD {
diff --git a/jdisc_core_test/test_bundles/cert-eab/src/main/java/com/yahoo/jdisc/bundle/e/CertificateE.java b/jdisc_core_test/test_bundles/cert-eab/src/main/java/com/yahoo/jdisc/bundle/e/CertificateE.java
index bbe2827fb37..aeacd8e55fe 100644
--- a/jdisc_core_test/test_bundles/cert-eab/src/main/java/com/yahoo/jdisc/bundle/e/CertificateE.java
+++ b/jdisc_core_test/test_bundles/cert-eab/src/main/java/com/yahoo/jdisc/bundle/e/CertificateE.java
@@ -5,7 +5,7 @@ import com.yahoo.jdisc.bundle.a.CertificateA;
import com.yahoo.jdisc.bundle.b.CertificateB;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CertificateE {
diff --git a/jdisc_core_test/test_bundles/cert-fac/src/main/java/com/yahoo/jdisc/bundle/f/CertificateF.java b/jdisc_core_test/test_bundles/cert-fac/src/main/java/com/yahoo/jdisc/bundle/f/CertificateF.java
index d1390a0ef12..c663ba9d7e2 100644
--- a/jdisc_core_test/test_bundles/cert-fac/src/main/java/com/yahoo/jdisc/bundle/f/CertificateF.java
+++ b/jdisc_core_test/test_bundles/cert-fac/src/main/java/com/yahoo/jdisc/bundle/f/CertificateF.java
@@ -5,7 +5,7 @@ import com.yahoo.jdisc.bundle.a.CertificateA;
import com.yahoo.jdisc.bundle.c.CertificateC;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CertificateF {
diff --git a/jdisc_core_test/test_bundles/cert-gg/src/main/java/com/yahoo/jdisc/bundle/g/CertificateG.java b/jdisc_core_test/test_bundles/cert-gg/src/main/java/com/yahoo/jdisc/bundle/g/CertificateG.java
index ea2f366c9cd..4d4339a230b 100644
--- a/jdisc_core_test/test_bundles/cert-gg/src/main/java/com/yahoo/jdisc/bundle/g/CertificateG.java
+++ b/jdisc_core_test/test_bundles/cert-gg/src/main/java/com/yahoo/jdisc/bundle/g/CertificateG.java
@@ -2,7 +2,7 @@
package com.yahoo.jdisc.bundle.g;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CertificateG {
diff --git a/jdisc_core_test/test_bundles/cert-hi/src/main/java/com/yahoo/jdisc/bundle/h/CertificateH.java b/jdisc_core_test/test_bundles/cert-hi/src/main/java/com/yahoo/jdisc/bundle/h/CertificateH.java
index 316b5fe83f1..d3d2e7fd622 100644
--- a/jdisc_core_test/test_bundles/cert-hi/src/main/java/com/yahoo/jdisc/bundle/h/CertificateH.java
+++ b/jdisc_core_test/test_bundles/cert-hi/src/main/java/com/yahoo/jdisc/bundle/h/CertificateH.java
@@ -2,7 +2,7 @@
package com.yahoo.jdisc.bundle.h;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CertificateH {
diff --git a/jdisc_core_test/test_bundles/cert-ih/src/main/java/com/yahoo/jdisc/bundle/i/CertificateI.java b/jdisc_core_test/test_bundles/cert-ih/src/main/java/com/yahoo/jdisc/bundle/i/CertificateI.java
index 7b67a0fbe32..9a10bde4cc1 100644
--- a/jdisc_core_test/test_bundles/cert-ih/src/main/java/com/yahoo/jdisc/bundle/i/CertificateI.java
+++ b/jdisc_core_test/test_bundles/cert-ih/src/main/java/com/yahoo/jdisc/bundle/i/CertificateI.java
@@ -2,7 +2,7 @@
package com.yahoo.jdisc.bundle.i;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CertificateI {
diff --git a/jdisc_core_test/test_bundles/cert-j-priv/src/main/java/com/yahoo/jdisc/bundle/j/CertificateJ.java b/jdisc_core_test/test_bundles/cert-j-priv/src/main/java/com/yahoo/jdisc/bundle/j/CertificateJ.java
index f672b604e5c..05928a84299 100644
--- a/jdisc_core_test/test_bundles/cert-j-priv/src/main/java/com/yahoo/jdisc/bundle/j/CertificateJ.java
+++ b/jdisc_core_test/test_bundles/cert-j-priv/src/main/java/com/yahoo/jdisc/bundle/j/CertificateJ.java
@@ -2,7 +2,7 @@
package com.yahoo.jdisc.bundle.j;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CertificateJ {
diff --git a/jdisc_core_test/test_bundles/cert-k-pkgs/pom.xml b/jdisc_core_test/test_bundles/cert-k-pkgs/pom.xml
index f0e16644a41..37e8469afdc 100644
--- a/jdisc_core_test/test_bundles/cert-k-pkgs/pom.xml
+++ b/jdisc_core_test/test_bundles/cert-k-pkgs/pom.xml
@@ -14,13 +14,6 @@
<version>6-SNAPSHOT</version>
<packaging>bundle</packaging>
<name>${project.artifactId}</name>
- <dependencies>
- <dependency>
- <groupId>javax.xml.bind</groupId>
- <artifactId>jaxb-api</artifactId>
- <scope>provided</scope>
- </dependency>
- </dependencies>
<build>
<plugins>
<plugin>
diff --git a/jdisc_core_test/test_bundles/cert-k-pkgs/src/main/java/com/yahoo/jdisc/bundle/k/CertificateK.java b/jdisc_core_test/test_bundles/cert-k-pkgs/src/main/java/com/yahoo/jdisc/bundle/k/CertificateK.java
index 44ec6a2ff6c..459ee60c740 100644
--- a/jdisc_core_test/test_bundles/cert-k-pkgs/src/main/java/com/yahoo/jdisc/bundle/k/CertificateK.java
+++ b/jdisc_core_test/test_bundles/cert-k-pkgs/src/main/java/com/yahoo/jdisc/bundle/k/CertificateK.java
@@ -2,7 +2,7 @@
package com.yahoo.jdisc.bundle.k;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
@SuppressWarnings({ "UnusedDeclaration", "deprecation" })
public class CertificateK {
@@ -112,12 +112,6 @@ public class CertificateK {
private final javax.tools.FileObject fileObject = null;
private final javax.transaction.xa.XAException xaException = null;
private final javax.xml.XMLConstants xmlConstants = null;
- private final javax.xml.bind.DataBindingException dataBindingException = null;
- private final javax.xml.bind.annotation.DomHandler<?,?> domHandler = null;
- private final javax.xml.bind.annotation.adapters.CollapsedStringAdapter collapsedStringAdapter = null;
- private final javax.xml.bind.attachment.AttachmentMarshaller attachmentMarshaller = null;
- private final javax.xml.bind.helpers.AbstractMarshallerImpl abstractMarshaller = null;
- private final javax.xml.bind.util.JAXBResult jaxbResult = null;
private final javax.xml.crypto.AlgorithmMethod algorithmMethod = null;
private final javax.xml.crypto.dom.DOMCryptoContext domCryptoContext = null;
private final javax.xml.crypto.dsig.CanonicalizationMethod canonicalizationMethod = null;
@@ -174,54 +168,20 @@ public class CertificateK {
private final org.xml.sax.ext.Attributes2Impl attributes2 = null;
private final org.xml.sax.helpers.AttributesImpl attributes = null;
+
// Packages made invisible from Java 9
-// private final javax.activation.CommandInfo commandInfo = null;
-// private final javax.activity.ActivityCompletedException activityCompletedException = null;
-// private final javax.jws.HandlerChain handlerChain = null;
-// private final javax.jws.soap.SOAPBinding soapBinding = null;
-// private final javax.rmi.PortableRemoteObject portableRemoteObject = null;
-// private final javax.rmi.CORBA.ClassDesc classDesc = null;
-// private final javax.transaction.InvalidTransactionException invalidTransactionException = null;
-// private final org.omg.CORBA.Any any = null;
-// private final org.omg.CORBA_2_3.ORB orb = null;
-// private final org.omg.CORBA_2_3.portable.InputStream inputStream = null;
-// private final org.omg.CORBA.DynAnyPackage.Invalid invalid = null;
-// private final org.omg.CORBA.ORBPackage.InvalidName invalidName = null;
-// private final org.omg.CORBA.portable.ApplicationException applicationException = null;
-// private final org.omg.CORBA.TypeCodePackage.BadKind badKind = null;
-// private final org.omg.CosNaming.Binding binding = null;
-// private final org.omg.CosNaming.NamingContextExtPackage.AddressHelper addressHelper = null;
-// private final org.omg.CosNaming.NamingContextPackage.AlreadyBound alreadyBound = null;
-// private final org.omg.Dynamic.Parameter parameter = null;
-// private final org.omg.DynamicAny.AnySeqHelper anySeqHelper = null;
-// private final org.omg.DynamicAny.DynAnyFactoryPackage.InconsistentTypeCode inconsistentTypeCode = null;
-// private final org.omg.DynamicAny.DynAnyPackage.InvalidValue invalidValue = null;
-// private final org.omg.IOP.Codec codex = null;
-// private final org.omg.IOP.CodecFactoryPackage.UnknownEncoding unknownEncoding = null;
-// private final org.omg.IOP.CodecPackage.FormatMismatch formatMismatch = null;
-// private final org.omg.Messaging.SyncScopeHelper syncScopeHelper = null;
-// private final org.omg.PortableInterceptor.AdapterManagerIdHelper adapterManagerIdHelper = null;
-// private final org.omg.PortableInterceptor.ORBInitInfoPackage.DuplicateName duplicateName = null;
-// private final org.omg.PortableServer.AdapterActivator adapterActivator = null;
-// private final org.omg.PortableServer.CurrentPackage.NoContext noContext = null;
-// private final org.omg.PortableServer.POAManagerPackage.AdapterInactive adapterInactive = null;
-// private final org.omg.PortableServer.POAPackage.AdapterAlreadyExists adapterAlreadyExists = null;
-// private final org.omg.PortableServer.portable.Delegate delegate = null;
-// private final org.omg.PortableServer.ServantLocatorPackage.CookieHolder cookieHolder = null;
-// private final org.omg.SendingContext.RunTime runTime = null;
-// private final org.omg.stub.java.rmi._Remote_Stub remote_stub = null;
-// private final javax.xml.soap.AttachmentPart attachmentPartt = null;
-// private final javax.xml.ws.Action action = null;
-// private final javax.xml.ws.handler.HandlerResolver handlerResolver = null;
-// private final javax.xml.ws.handler.soap.SOAPMessageContext soapMessageContext = null;
-// private final javax.xml.ws.http.HTTPBinding httpBinding = null;
-// private final javax.xml.ws.soap.AddressingFeature addressingFeature = null;
-// private final javax.xml.ws.spi.Provider provider = null;
-// private final javax.xml.ws.wsaddressing.W3CEndpointReference w3CEndpointReference = null;
- // We should only access these old sun-internal classes if we
- // really, really have to
- // private final sun.misc.ASCIICaseInsensitiveComparator comparator = null;
- // private final sun.net.util.IPAddressUtil ipaddressutil = null;
- // private final sun.security.krb5.Checksum checksum = null;
+ // Added as dep in jdisc_core: com.sun.activation:javax.activation (OSGi bundle)
+ private final com.sun.activation.viewers.ImageViewer imageViewer = null;
+ private final com.sun.activation.registries.MimeTypeFile mimeTypeFile = null;
+ private final javax.activation.CommandInfo commandInfo = null;
+
+ // Added as dep in jdisc_core: javax.xml.bind:jaxb-api (OSGi bundle)
+ private final javax.xml.bind.DataBindingException dataBindingException = null;
+ private final javax.xml.bind.annotation.DomHandler<?,?> domHandler = null;
+ private final javax.xml.bind.annotation.adapters.CollapsedStringAdapter collapsedStringAdapter = null;
+ private final javax.xml.bind.attachment.AttachmentMarshaller attachmentMarshaller = null;
+ private final javax.xml.bind.helpers.AbstractMarshallerImpl abstractMarshaller = null;
+ private final javax.xml.bind.util.JAXBResult jaxbResult = null;
+
}
diff --git a/jdisc_core_test/test_bundles/cert-l1/src/main/java/com/yahoo/jdisc/bundle/l/CertificateL.java b/jdisc_core_test/test_bundles/cert-l1/src/main/java/com/yahoo/jdisc/bundle/l/CertificateL.java
index 956c96017a7..fb8fbbd669c 100644
--- a/jdisc_core_test/test_bundles/cert-l1/src/main/java/com/yahoo/jdisc/bundle/l/CertificateL.java
+++ b/jdisc_core_test/test_bundles/cert-l1/src/main/java/com/yahoo/jdisc/bundle/l/CertificateL.java
@@ -4,7 +4,7 @@ package com.yahoo.jdisc.bundle.l;
import java.util.concurrent.Callable;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CertificateL implements Callable<Integer> {
diff --git a/jdisc_core_test/test_bundles/cert-l2/src/main/java/com/yahoo/jdisc/bundle/l/CertificateL.java b/jdisc_core_test/test_bundles/cert-l2/src/main/java/com/yahoo/jdisc/bundle/l/CertificateL.java
index 89041410fc6..a8e9ba78ada 100644
--- a/jdisc_core_test/test_bundles/cert-l2/src/main/java/com/yahoo/jdisc/bundle/l/CertificateL.java
+++ b/jdisc_core_test/test_bundles/cert-l2/src/main/java/com/yahoo/jdisc/bundle/l/CertificateL.java
@@ -4,7 +4,7 @@ package com.yahoo.jdisc.bundle.l;
import java.util.concurrent.Callable;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CertificateL implements Callable<Integer> {
diff --git a/jdisc_core_test/test_bundles/cert-ml/src/main/java/com/yahoo/jdisc/bundle/m/CertificateM.java b/jdisc_core_test/test_bundles/cert-ml/src/main/java/com/yahoo/jdisc/bundle/m/CertificateM.java
index 2407b6732c0..131c9e41a1e 100644
--- a/jdisc_core_test/test_bundles/cert-ml/src/main/java/com/yahoo/jdisc/bundle/m/CertificateM.java
+++ b/jdisc_core_test/test_bundles/cert-ml/src/main/java/com/yahoo/jdisc/bundle/m/CertificateM.java
@@ -4,7 +4,7 @@ package com.yahoo.jdisc.bundle.m;
import java.util.concurrent.Callable;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CertificateM implements Callable<Integer> {
diff --git a/jdisc_core_test/test_bundles/cert-nac/src/main/java/com/yahoo/jdisc/bundle/n/CertificateN.java b/jdisc_core_test/test_bundles/cert-nac/src/main/java/com/yahoo/jdisc/bundle/n/CertificateN.java
index 7a9fd205182..d2e47599822 100644
--- a/jdisc_core_test/test_bundles/cert-nac/src/main/java/com/yahoo/jdisc/bundle/n/CertificateN.java
+++ b/jdisc_core_test/test_bundles/cert-nac/src/main/java/com/yahoo/jdisc/bundle/n/CertificateN.java
@@ -5,7 +5,7 @@ import com.yahoo.jdisc.bundle.a.CertificateA;
import com.yahoo.jdisc.bundle.c.CertificateC;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CertificateN {
diff --git a/jdisc_core_test/test_bundles/cert-oa-path/src/main/java/com/yahoo/jdisc/bundle/o/CertificateO.java b/jdisc_core_test/test_bundles/cert-oa-path/src/main/java/com/yahoo/jdisc/bundle/o/CertificateO.java
index 0de8f424e85..7c790d76692 100644
--- a/jdisc_core_test/test_bundles/cert-oa-path/src/main/java/com/yahoo/jdisc/bundle/o/CertificateO.java
+++ b/jdisc_core_test/test_bundles/cert-oa-path/src/main/java/com/yahoo/jdisc/bundle/o/CertificateO.java
@@ -4,7 +4,7 @@ package com.yahoo.jdisc.bundle.o;
import com.yahoo.jdisc.bundle.a.CertificateA;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CertificateO {
diff --git a/jdisc_core_test/test_bundles/cert-p-jar/src/main/java/com/yahoo/jdisc/bundle/p/CertificateP.java b/jdisc_core_test/test_bundles/cert-p-jar/src/main/java/com/yahoo/jdisc/bundle/p/CertificateP.java
index 1d6451d7368..160d1d69cab 100644
--- a/jdisc_core_test/test_bundles/cert-p-jar/src/main/java/com/yahoo/jdisc/bundle/p/CertificateP.java
+++ b/jdisc_core_test/test_bundles/cert-p-jar/src/main/java/com/yahoo/jdisc/bundle/p/CertificateP.java
@@ -2,7 +2,7 @@
package com.yahoo.jdisc.bundle.p;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CertificateP {
diff --git a/jdisc_core_test/test_bundles/cert-q-frag/src/main/java/com/yahoo/jdisc/bundle/q/CertificateQ.java b/jdisc_core_test/test_bundles/cert-q-frag/src/main/java/com/yahoo/jdisc/bundle/q/CertificateQ.java
index ed00646ebf8..3f0169de859 100644
--- a/jdisc_core_test/test_bundles/cert-q-frag/src/main/java/com/yahoo/jdisc/bundle/q/CertificateQ.java
+++ b/jdisc_core_test/test_bundles/cert-q-frag/src/main/java/com/yahoo/jdisc/bundle/q/CertificateQ.java
@@ -2,7 +2,7 @@
package com.yahoo.jdisc.bundle.q;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CertificateQ {
diff --git a/jdisc_core_test/test_bundles/cert-rq/src/main/java/com/yahoo/jdisc/bundle/r/CertificateR.java b/jdisc_core_test/test_bundles/cert-rq/src/main/java/com/yahoo/jdisc/bundle/r/CertificateR.java
index a93b2dac6fc..84c6156d908 100644
--- a/jdisc_core_test/test_bundles/cert-rq/src/main/java/com/yahoo/jdisc/bundle/r/CertificateR.java
+++ b/jdisc_core_test/test_bundles/cert-rq/src/main/java/com/yahoo/jdisc/bundle/r/CertificateR.java
@@ -4,7 +4,7 @@ package com.yahoo.jdisc.bundle.r;
import com.yahoo.jdisc.bundle.q.CertificateQ;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CertificateR {
diff --git a/jdisc_core_test/test_bundles/cert-s-act/src/main/java/com/yahoo/jdisc/bundle/s/CertificateS.java b/jdisc_core_test/test_bundles/cert-s-act/src/main/java/com/yahoo/jdisc/bundle/s/CertificateS.java
index 0ccc85020e6..a5b0e819874 100644
--- a/jdisc_core_test/test_bundles/cert-s-act/src/main/java/com/yahoo/jdisc/bundle/s/CertificateS.java
+++ b/jdisc_core_test/test_bundles/cert-s-act/src/main/java/com/yahoo/jdisc/bundle/s/CertificateS.java
@@ -2,7 +2,7 @@
package com.yahoo.jdisc.bundle.s;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CertificateS {
diff --git a/jdisc_core_test/test_bundles/cert-s-act/src/main/java/com/yahoo/jdisc/bundle/s/MyBundleActivator.java b/jdisc_core_test/test_bundles/cert-s-act/src/main/java/com/yahoo/jdisc/bundle/s/MyBundleActivator.java
index 0d350ab9841..1cd4bdb07ce 100644
--- a/jdisc_core_test/test_bundles/cert-s-act/src/main/java/com/yahoo/jdisc/bundle/s/MyBundleActivator.java
+++ b/jdisc_core_test/test_bundles/cert-s-act/src/main/java/com/yahoo/jdisc/bundle/s/MyBundleActivator.java
@@ -6,7 +6,7 @@ import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MyBundleActivator implements BundleActivator {
diff --git a/jdisc_core_test/test_bundles/cert-tp/src/main/java/com/yahoo/jdisc/bundle/t/CertificateT.java b/jdisc_core_test/test_bundles/cert-tp/src/main/java/com/yahoo/jdisc/bundle/t/CertificateT.java
index 1e2cf790129..f2078119ad8 100644
--- a/jdisc_core_test/test_bundles/cert-tp/src/main/java/com/yahoo/jdisc/bundle/t/CertificateT.java
+++ b/jdisc_core_test/test_bundles/cert-tp/src/main/java/com/yahoo/jdisc/bundle/t/CertificateT.java
@@ -4,7 +4,7 @@ package com.yahoo.jdisc.bundle.t;
import com.yahoo.jdisc.bundle.p.CertificateP;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CertificateT {
diff --git a/jdisc_core_test/test_bundles/cert-us/src/main/java/com/yahoo/jdisc/bundle/u/CertificateU.java b/jdisc_core_test/test_bundles/cert-us/src/main/java/com/yahoo/jdisc/bundle/u/CertificateU.java
index b1a91cec9aa..ee3c8bab31e 100644
--- a/jdisc_core_test/test_bundles/cert-us/src/main/java/com/yahoo/jdisc/bundle/u/CertificateU.java
+++ b/jdisc_core_test/test_bundles/cert-us/src/main/java/com/yahoo/jdisc/bundle/u/CertificateU.java
@@ -4,7 +4,7 @@ package com.yahoo.jdisc.bundle.u;
import com.yahoo.jdisc.bundle.s.CertificateS;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CertificateU {
diff --git a/jdisc_core_test/test_bundles/my-bundle-activator/src/main/java/com/yahoo/jdisc/bundle/MyBundleActivator.java b/jdisc_core_test/test_bundles/my-bundle-activator/src/main/java/com/yahoo/jdisc/bundle/MyBundleActivator.java
index 70b31b5dc59..b4af03bdf60 100644
--- a/jdisc_core_test/test_bundles/my-bundle-activator/src/main/java/com/yahoo/jdisc/bundle/MyBundleActivator.java
+++ b/jdisc_core_test/test_bundles/my-bundle-activator/src/main/java/com/yahoo/jdisc/bundle/MyBundleActivator.java
@@ -8,7 +8,7 @@ import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MyBundleActivator implements BundleActivator {
diff --git a/jdisc_core_test/test_bundles/my-bundle-activator/src/main/java/com/yahoo/jdisc/bundle/MyService.java b/jdisc_core_test/test_bundles/my-bundle-activator/src/main/java/com/yahoo/jdisc/bundle/MyService.java
index 773faa6131e..8eb1543776d 100644
--- a/jdisc_core_test/test_bundles/my-bundle-activator/src/main/java/com/yahoo/jdisc/bundle/MyService.java
+++ b/jdisc_core_test/test_bundles/my-bundle-activator/src/main/java/com/yahoo/jdisc/bundle/MyService.java
@@ -2,7 +2,7 @@
package com.yahoo.jdisc.bundle;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class MyService {
diff --git a/jdisc_core_test/test_bundles/my-guice-module/src/main/java/com/yahoo/jdisc/bundle/MyGuiceModule.java b/jdisc_core_test/test_bundles/my-guice-module/src/main/java/com/yahoo/jdisc/bundle/MyGuiceModule.java
index 84b11e4edd0..ef515b0b40e 100644
--- a/jdisc_core_test/test_bundles/my-guice-module/src/main/java/com/yahoo/jdisc/bundle/MyGuiceModule.java
+++ b/jdisc_core_test/test_bundles/my-guice-module/src/main/java/com/yahoo/jdisc/bundle/MyGuiceModule.java
@@ -8,7 +8,7 @@ import com.google.inject.name.Named;
import java.util.concurrent.CountDownLatch;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MyGuiceModule extends AbstractModule {
diff --git a/jdisc_core_test/test_bundles/my-server-provider/src/main/java/com/yahoo/jdisc/bundle/MyServerProvider.java b/jdisc_core_test/test_bundles/my-server-provider/src/main/java/com/yahoo/jdisc/bundle/MyServerProvider.java
index e00c8ba1a36..83b783bd473 100644
--- a/jdisc_core_test/test_bundles/my-server-provider/src/main/java/com/yahoo/jdisc/bundle/MyServerProvider.java
+++ b/jdisc_core_test/test_bundles/my-server-provider/src/main/java/com/yahoo/jdisc/bundle/MyServerProvider.java
@@ -9,7 +9,7 @@ import com.yahoo.jdisc.service.ServerProvider;
import java.util.concurrent.CountDownLatch;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MyServerProvider extends AbstractResource implements ServerProvider {
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/core/CompletionHandlers.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/core/CompletionHandlers.java
index 89099e98399..ce8ce2c0797 100644
--- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/core/CompletionHandlers.java
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/core/CompletionHandlers.java
@@ -6,7 +6,7 @@ import com.yahoo.jdisc.handler.CompletionHandler;
import java.util.Arrays;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class CompletionHandlers {
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/RequestView.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/RequestView.java
index 0edc083b76c..cde43881249 100644
--- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/RequestView.java
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/RequestView.java
@@ -11,7 +11,7 @@ import java.util.Optional;
/**
* Read-only view of the request for use by SecurityResponseFilters.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public interface RequestView {
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/SecurityFilterInvoker.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/SecurityFilterInvoker.java
index 5c74734c7c1..8207a67cfdd 100644
--- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/SecurityFilterInvoker.java
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/SecurityFilterInvoker.java
@@ -25,7 +25,7 @@ import java.util.Optional;
*
* Assumes that SecurityResponseFilters mutate DiscFilterResponse in the thread they are invoked from.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
@Beta
public class SecurityFilterInvoker implements FilterInvoker {
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/SecurityRequestFilter.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/SecurityRequestFilter.java
index 7b94d1b1d7f..e6f4add49de 100644
--- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/SecurityRequestFilter.java
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/SecurityRequestFilter.java
@@ -4,7 +4,7 @@ package com.yahoo.jdisc.http.filter;
import com.yahoo.jdisc.handler.ResponseHandler;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public interface SecurityRequestFilter extends RequestFilterBase {
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/chain/ResponseFilterChain.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/chain/ResponseFilterChain.java
index 6518ac6be38..5c5eda1f139 100644
--- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/chain/ResponseFilterChain.java
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/chain/ResponseFilterChain.java
@@ -12,7 +12,7 @@ import java.util.Arrays;
import java.util.List;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class ResponseFilterChain extends AbstractResource implements ResponseFilter {
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/chain/ResponseHandlerGuard.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/chain/ResponseHandlerGuard.java
index 03270c24393..02600683e27 100644
--- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/chain/ResponseHandlerGuard.java
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/filter/chain/ResponseHandlerGuard.java
@@ -6,7 +6,7 @@ import com.yahoo.jdisc.handler.ContentChannel;
import com.yahoo.jdisc.handler.ResponseHandler;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
final class ResponseHandlerGuard implements ResponseHandler {
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/AsyncCompleteListener.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/AsyncCompleteListener.java
index be60a5b15d6..7dba217e01c 100644
--- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/AsyncCompleteListener.java
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/AsyncCompleteListener.java
@@ -7,7 +7,7 @@ import java.io.IOException;
/**
* Interface for async listeners only interested in onComplete.
- * @author tonytv
+ * @author Tony Vaagenes
*/
@FunctionalInterface
interface AsyncCompleteListener extends AsyncListener {
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/Exceptions.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/Exceptions.java
index 90979adaa03..0806f352ae9 100644
--- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/Exceptions.java
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/Exceptions.java
@@ -4,7 +4,7 @@ package com.yahoo.jdisc.http.server.jetty;
/**
* Utility methods for exceptions
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class Exceptions {
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/FilterInvokingPrintWriter.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/FilterInvokingPrintWriter.java
index 155ccc72871..3ebc7bbc551 100644
--- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/FilterInvokingPrintWriter.java
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/FilterInvokingPrintWriter.java
@@ -11,7 +11,7 @@ import java.util.Locale;
* The filter must be invoked before the first output call since this might cause the response
* to be committed, i.e. locked and potentially put on the wire.
* Any changes to the response after it has been committed might be ignored or cause exceptions.
- * @author tonytv
+ * @author Tony Vaagenes
*/
final class FilterInvokingPrintWriter extends PrintWriter {
private final PrintWriter delegate;
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/FilterInvokingServletOutputStream.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/FilterInvokingServletOutputStream.java
index 9c256503e5e..a605ccebfa7 100644
--- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/FilterInvokingServletOutputStream.java
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/FilterInvokingServletOutputStream.java
@@ -11,7 +11,7 @@ import java.io.IOException;
* to be committed, i.e. locked and potentially put on the wire.
* Any changes to the response after it has been committed might be ignored or cause exceptions.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
class FilterInvokingServletOutputStream extends ServletOutputStream {
private final ServletOutputStream delegate;
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/HttpRequestDispatch.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/HttpRequestDispatch.java
index 31268c823ba..0a20e4f4757 100644
--- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/HttpRequestDispatch.java
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/HttpRequestDispatch.java
@@ -14,6 +14,7 @@ import com.yahoo.jdisc.http.HttpHeaders;
import com.yahoo.jdisc.http.HttpRequest;
import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.server.HttpConnection;
+import org.eclipse.jetty.server.Request;
import javax.servlet.AsyncContext;
import javax.servlet.ServletInputStream;
@@ -45,7 +46,7 @@ class HttpRequestDispatch {
private final JDiscContext jDiscContext;
private final AsyncContext async;
- private final HttpServletRequest servletRequest;
+ private final Request jettyRequest;
private final ServletResponseController servletResponseController;
private final RequestHandler requestHandler;
@@ -60,9 +61,8 @@ class HttpRequestDispatch {
requestHandler = newRequestHandler(jDiscContext, accessLogEntry, servletRequest);
- this.metricReporter = new MetricReporter(jDiscContext.metric, metricContext,
- ((org.eclipse.jetty.server.Request) servletRequest).getTimeStamp());
- this.servletRequest = servletRequest;
+ this.jettyRequest = (Request) servletRequest;
+ this.metricReporter = new MetricReporter(jDiscContext.metric, metricContext, jettyRequest.getTimeStamp());
honourMaxKeepAliveRequests();
this.servletResponseController = new ServletResponseController(
servletRequest,
@@ -73,6 +73,7 @@ class HttpRequestDispatch {
this.async = servletRequest.startAsync();
async.setTimeout(0);
+ metricReporter.uriLength(jettyRequest.getOriginalURI().length());
}
public void dispatch() throws IOException {
@@ -102,7 +103,7 @@ class HttpRequestDispatch {
private void honourMaxKeepAliveRequests() {
if (jDiscContext.serverConfig.maxKeepAliveRequests() > 0) {
- HttpConnection connection = getConnection(servletRequest);
+ HttpConnection connection = getConnection(jettyRequest);
if (connection.getMessagesIn() >= jDiscContext.serverConfig.maxKeepAliveRequests()) {
connection.getGenerator().setPersistent(false);
}
@@ -128,9 +129,10 @@ class HttpRequestDispatch {
if (error instanceof CompletionException && error.getCause() instanceof EofException) {
log.log(Level.FINE,
error,
- () -> "Network connection was unexpectedly terminated: " + parent.servletRequest.getRequestURI());
+ () -> "Network connection was unexpectedly terminated: " + parent.jettyRequest.getRequestURI());
+ parent.metricReporter.prematurelyClosed();
} else if (!(error instanceof OverloadException || error instanceof BindingNotFoundException)) {
- log.log(Level.WARNING, "Request failed: " + parent.servletRequest.getRequestURI(), error);
+ log.log(Level.WARNING, "Request failed: " + parent.jettyRequest.getRequestURI(), error);
}
reportedError = true;
parent.metricReporter.failedResponse();
@@ -140,7 +142,7 @@ class HttpRequestDispatch {
try {
parent.async.complete();
- log.finest(() -> "Request completed successfully: " + parent.servletRequest.getRequestURI());
+ log.finest(() -> "Request completed successfully: " + parent.jettyRequest.getRequestURI());
} catch (Throwable throwable) {
Level level = reportedError ? Level.FINE: Level.WARNING;
log.log(level, "async.complete failed", throwable);
@@ -150,15 +152,15 @@ class HttpRequestDispatch {
@SuppressWarnings("try")
private ServletRequestReader handleRequest() throws IOException {
- HttpRequest jdiscRequest = HttpRequestFactory.newJDiscRequest(jDiscContext.container, servletRequest);
+ HttpRequest jdiscRequest = HttpRequestFactory.newJDiscRequest(jDiscContext.container, jettyRequest);
ContentChannel requestContentChannel;
try (ResourceReference ref = References.fromResource(jdiscRequest)) {
- HttpRequestFactory.copyHeaders(servletRequest, jdiscRequest);
+ HttpRequestFactory.copyHeaders(jettyRequest, jdiscRequest);
requestContentChannel = requestHandler.handleRequest(jdiscRequest, servletResponseController.responseHandler);
}
- ServletInputStream servletInputStream = servletRequest.getInputStream();
+ ServletInputStream servletInputStream = jettyRequest.getInputStream();
ServletRequestReader servletRequestReader =
new ServletRequestReader(
@@ -181,7 +183,7 @@ class HttpRequestDispatch {
ContentChannel handleRequestFilterResponse(Response response) {
try {
- servletRequest.getInputStream().close();
+ jettyRequest.getInputStream().close();
ContentChannel responseContentChannel = servletResponseController.responseHandler.handleResponse(response);
servletResponseController.finishedFuture().whenComplete(completeRequestCallback);
return responseContentChannel;
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscFilterInvokerFilter.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscFilterInvokerFilter.java
index 7227d21e7a2..609456c562a 100644
--- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscFilterInvokerFilter.java
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscFilterInvokerFilter.java
@@ -34,7 +34,7 @@ import static com.yahoo.jdisc.http.server.jetty.JDiscHttpServlet.getConnector;
* 1) JDiscFilterInvokerFilter, which uses package private methods to support JDisc APIs
* 2) SecurityFilterInvoker, which uses Security filter classes and therefore must reside in jdisc_http_filters
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
class JDiscFilterInvokerFilter implements Filter {
private final JDiscContext jDiscContext;
@@ -127,7 +127,7 @@ class JDiscFilterInvokerFilter implements Filter {
final AccessLogEntry accessLogEntry = null; // Not used in this context.
return new HttpRequestDispatch(jDiscContext,
accessLogEntry,
- getConnector(request).getMetricContext(),
+ getConnector(request).getRequestMetricContext(request),
request, response);
} catch (IOException e) {
throw throwUnchecked(e);
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServlet.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServlet.java
index 27f72c7b4bf..2f5fe7612c8 100644
--- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServlet.java
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServlet.java
@@ -6,7 +6,6 @@ import com.yahoo.jdisc.Metric;
import com.yahoo.jdisc.handler.OverloadException;
import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
@@ -130,8 +129,8 @@ class JDiscHttpServlet extends HttpServlet {
}
}
- private static Metric.Context getMetricContext(ServletRequest request) {
- return JDiscServerConnector.fromRequest(request).getMetricContext();
+ private static Metric.Context getMetricContext(HttpServletRequest request) {
+ return JDiscServerConnector.fromRequest(request).getRequestMetricContext(request);
}
private static String formatAttributes(final HttpServletRequest request) {
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscServerConnector.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscServerConnector.java
index 8dd50074c32..f7d6e1717af 100644
--- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscServerConnector.java
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscServerConnector.java
@@ -9,13 +9,15 @@ import org.eclipse.jetty.server.ServerConnectionStatistics;
import org.eclipse.jetty.server.ServerConnector;
import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.Socket;
import java.net.SocketException;
import java.nio.channels.ServerSocketChannel;
+import java.util.HashMap;
import java.util.Map;
-import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -26,10 +28,14 @@ class JDiscServerConnector extends ServerConnector {
public static final String REQUEST_ATTRIBUTE = JDiscServerConnector.class.getName();
private final static Logger log = Logger.getLogger(JDiscServerConnector.class.getName());
private final Metric.Context metricCtx;
+ private final Map<String, Metric.Context> requestMetricContextCache = new ConcurrentHashMap<>();
private final ServerConnectionStatistics statistics;
private final boolean tcpKeepAlive;
private final boolean tcpNoDelay;
private final ServerSocketChannel channelOpenedByActivator;
+ private final Metric metric;
+ private final String connectorName;
+ private final int listenPort;
JDiscServerConnector(ConnectorConfig config, Metric metric, Server server,
ServerSocketChannel channelOpenedByActivator, ConnectionFactory... factories) {
@@ -37,19 +43,15 @@ class JDiscServerConnector extends ServerConnector {
this.channelOpenedByActivator = channelOpenedByActivator;
this.tcpKeepAlive = config.tcpKeepAliveEnabled();
this.tcpNoDelay = config.tcpNoDelay();
- this.metricCtx = createMetricContext(config, metric);
+ this.metric = metric;
+ this.connectorName = config.name();
+ this.listenPort = config.listenPort();
+ this.metricCtx = metric.createContext(createConnectorDimensions(listenPort, connectorName));
this.statistics = new ServerConnectionStatistics();
addBean(statistics);
}
- private Metric.Context createMetricContext(ConnectorConfig config, Metric metric) {
- Map<String, Object> props = new TreeMap<>();
- props.put(JettyHttpServer.Metrics.NAME_DIMENSION, config.name());
- props.put(JettyHttpServer.Metrics.PORT_DIMENSION, config.listenPort());
- return metric.createContext(props);
- }
-
@Override
protected void configure(final Socket socket) {
super.configure(socket);
@@ -112,11 +114,28 @@ class JDiscServerConnector extends ServerConnector {
return statistics;
}
- public Metric.Context getMetricContext() {
+ public Metric.Context getConnectorMetricContext() {
return metricCtx;
}
+ public Metric.Context getRequestMetricContext(HttpServletRequest request) {
+ String method = request.getMethod();
+ return requestMetricContextCache.computeIfAbsent(method, ignored -> {
+ Map<String, Object> dimensions = createConnectorDimensions(listenPort, connectorName);
+ dimensions.put(JettyHttpServer.Metrics.METHOD_DIMENSION, method);
+ return metric.createContext(dimensions);
+ });
+ }
+
public static JDiscServerConnector fromRequest(ServletRequest request) {
return (JDiscServerConnector) request.getAttribute(REQUEST_ATTRIBUTE);
}
+
+ private static Map<String, Object> createConnectorDimensions(int listenPort, String connectorName) {
+ Map<String, Object> props = new HashMap<>();
+ props.put(JettyHttpServer.Metrics.NAME_DIMENSION, connectorName);
+ props.put(JettyHttpServer.Metrics.PORT_DIMENSION, listenPort);
+ return props;
+ }
+
}
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyHttpServer.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyHttpServer.java
index c85917c4c7e..452d1aed874 100644
--- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyHttpServer.java
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyHttpServer.java
@@ -66,12 +66,14 @@ public class JettyHttpServer extends AbstractServerProvider {
public interface Metrics {
String NAME_DIMENSION = "serverName";
String PORT_DIMENSION = "serverPort";
+ String METHOD_DIMENSION = "httpMethod";
String NUM_OPEN_CONNECTIONS = "serverNumOpenConnections";
String NUM_CONNECTIONS_OPEN_MAX = "serverConnectionsOpenMax";
String CONNECTION_DURATION_MAX = "serverConnectionDurationMax";
String CONNECTION_DURATION_MEAN = "serverConnectionDurationMean";
String CONNECTION_DURATION_STD_DEV = "serverConnectionDurationStdDev";
+ String NUM_PREMATURELY_CLOSED_CONNECTIONS = "jdisc.http.request.prematurely_closed";
String NUM_BYTES_RECEIVED = "serverBytesReceived";
String NUM_BYTES_SENT = "serverBytesSent";
@@ -104,6 +106,9 @@ public class JettyHttpServer extends AbstractServerProvider {
String STARTED_MILLIS = "serverStartedMillis";
@Deprecated String MANHATTAN_STARTED_MILLIS = "proc.uptime";
+
+ String URI_LENGTH = "jdisc.http.request.uri_length";
+ String CONTENT_SIZE = "jdisc.http.request.content_size";
}
private final static Logger log = Logger.getLogger(JettyHttpServer.class.getName());
@@ -346,12 +351,12 @@ public class JettyHttpServer extends AbstractServerProvider {
private void setConnectorMetrics(JDiscServerConnector connector) {
ServerConnectionStatistics statistics = connector.getStatistics();
- metric.set(Metrics.NUM_CONNECTIONS, statistics.getConnectionsTotal(), connector.getMetricContext());
- metric.set(Metrics.NUM_OPEN_CONNECTIONS, statistics.getConnections(), connector.getMetricContext());
- metric.set(Metrics.NUM_CONNECTIONS_OPEN_MAX, statistics.getConnectionsMax(), connector.getMetricContext());
- metric.set(Metrics.CONNECTION_DURATION_MAX, statistics.getConnectionDurationMax(), connector.getMetricContext());
- metric.set(Metrics.CONNECTION_DURATION_MEAN, statistics.getConnectionDurationMean(), connector.getMetricContext());
- metric.set(Metrics.CONNECTION_DURATION_STD_DEV, statistics.getConnectionDurationStdDev(), connector.getMetricContext());
+ metric.set(Metrics.NUM_CONNECTIONS, statistics.getConnectionsTotal(), connector.getConnectorMetricContext());
+ metric.set(Metrics.NUM_OPEN_CONNECTIONS, statistics.getConnections(), connector.getConnectorMetricContext());
+ metric.set(Metrics.NUM_CONNECTIONS_OPEN_MAX, statistics.getConnectionsMax(), connector.getConnectorMetricContext());
+ metric.set(Metrics.CONNECTION_DURATION_MAX, statistics.getConnectionDurationMax(), connector.getConnectorMetricContext());
+ metric.set(Metrics.CONNECTION_DURATION_MEAN, statistics.getConnectionDurationMean(), connector.getConnectorMetricContext());
+ metric.set(Metrics.CONNECTION_DURATION_STD_DEV, statistics.getConnectionDurationStdDev(), connector.getConnectorMetricContext());
}
private StatisticsHandler newStatisticsHandler() {
@@ -360,12 +365,11 @@ public class JettyHttpServer extends AbstractServerProvider {
return statisticsHandler;
}
- @SuppressWarnings("deprecation")
private GzipHandler newGzipHandler(ServerConfig serverConfig) {
GzipHandler gzipHandler = new GzipHandlerWithVaryHeaderFixed();
gzipHandler.setCompressionLevel(serverConfig.responseCompressionLevel());
- gzipHandler.setCheckGzExists(false); // TODO: will be removed without replacement in Jetty 10
- gzipHandler.setIncludedMethods("GET", "POST");
+ gzipHandler.setInflateBufferSize(8 * 1024);
+ gzipHandler.setIncludedMethods("GET", "POST", "PUT", "PATCH");
return gzipHandler;
}
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/MetricReporter.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/MetricReporter.java
index 2824ca66c29..53f330bbc7e 100644
--- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/MetricReporter.java
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/MetricReporter.java
@@ -12,7 +12,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
/**
* Responsible for metric reporting for JDisc http request handler support.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class MetricReporter {
private final Metric metric;
@@ -72,6 +72,10 @@ public class MetricReporter {
metric.add(Metrics.NUM_FAILED_RESPONSES, 1, context);
}
+ public void prematurelyClosed() {
+ metric.add(Metrics.NUM_PREMATURELY_CLOSED_CONNECTIONS, 1, context);
+ }
+
@SuppressWarnings("deprecation")
public void successfulRead(int bytes_received) {
metric.set(JettyHttpServer.Metrics.NUM_BYTES_RECEIVED, bytes_received, context);
@@ -81,4 +85,12 @@ public class MetricReporter {
private long getRequestLatency() {
return System.currentTimeMillis() - requestStartTime;
}
+
+ public void uriLength(int length) {
+ metric.set(Metrics.URI_LENGTH, length, context);
+ }
+
+ public void contentSize(int size) {
+ metric.set(Metrics.CONTENT_SIZE, size, context);
+ }
}
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/OneTimeRunnable.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/OneTimeRunnable.java
index dcf4e1fb105..eb83d3d7d03 100644
--- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/OneTimeRunnable.java
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/OneTimeRunnable.java
@@ -4,7 +4,7 @@ package com.yahoo.jdisc.http.server.jetty;
import java.util.concurrent.atomic.AtomicBoolean;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class OneTimeRunnable {
private final Runnable runnable;
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletOutputStreamWriter.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletOutputStreamWriter.java
index 20a9ab59ca1..eb7174e2f0f 100644
--- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletOutputStreamWriter.java
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletOutputStreamWriter.java
@@ -21,7 +21,7 @@ import java.util.logging.Logger;
import static com.yahoo.jdisc.http.server.jetty.CompletionHandlerUtils.NOOP_COMPLETION_HANDLER;
/**
- * @author tonytv
+ * @author Tony Vaagenes
* @author bjorncs
*/
public class ServletOutputStreamWriter {
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletRequestReader.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletRequestReader.java
index 47ef20e181e..a80f5bb3c14 100644
--- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletRequestReader.java
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletRequestReader.java
@@ -45,6 +45,8 @@ class ServletRequestReader implements ReadListener {
private final Executor executor;
private final MetricReporter metricReporter;
+ private int bytesRead;
+
/**
* Rules:
* 1. If state != State.READING, then numberOfOutstandingUserCalls must not increase
@@ -134,6 +136,7 @@ class ServletRequestReader implements ReadListener {
int bytesReceived = buf.remaining();
requestContentChannel.write(buf, writeCompletionHandler);
metricReporter.successfulRead(bytesReceived);
+ bytesRead += bytesReceived;
} catch (final Throwable t) {
finishedFuture.completeExceptionally(t);
} finally {
@@ -184,6 +187,7 @@ class ServletRequestReader implements ReadListener {
private void doneReading() {
final boolean shouldCloseRequestContentChannel;
+ int bytesRead;
synchronized (monitor) {
if (state != State.READING) {
return;
@@ -195,11 +199,14 @@ class ServletRequestReader implements ReadListener {
if (shouldCloseRequestContentChannel) {
state = State.REQUEST_CONTENT_CLOSED;
}
+ bytesRead = this.bytesRead;
}
if (shouldCloseRequestContentChannel) {
closeCompletionHandler_noThrow();
}
+
+ metricReporter.contentSize(bytesRead);
}
private void closeCompletionHandler_noThrow() {
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletResponseController.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletResponseController.java
index bc14a063cd1..846850b6244 100644
--- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletResponseController.java
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletResponseController.java
@@ -28,7 +28,7 @@ import java.util.logging.Logger;
import static com.yahoo.jdisc.http.server.jetty.CompletionHandlerUtils.NOOP_COMPLETION_HANDLER;
/**
- * @author tonytv
+ * @author Tony Vaagenes
* @author bjorncs
*/
public class ServletResponseController {
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/UnsupportedFilterInvoker.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/UnsupportedFilterInvoker.java
index 6b3da0d21ad..ce52bccf52d 100644
--- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/UnsupportedFilterInvoker.java
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/UnsupportedFilterInvoker.java
@@ -10,7 +10,7 @@ import javax.servlet.http.HttpServletRequest;
import java.net.URI;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class UnsupportedFilterInvoker implements FilterInvoker {
@Override
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/CookieTestCase.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/CookieTestCase.java
index ca12de72ec2..dab4f91f631 100644
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/CookieTestCase.java
+++ b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/CookieTestCase.java
@@ -16,7 +16,7 @@ import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
* @author bjorncs
*/
public class CookieTestCase {
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/HttpHeadersTestCase.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/HttpHeadersTestCase.java
index ab2b118f0f0..120a1c5049c 100644
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/HttpHeadersTestCase.java
+++ b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/HttpHeadersTestCase.java
@@ -6,7 +6,7 @@ import org.testng.annotations.Test;
import static org.testng.AssertJUnit.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class HttpHeadersTestCase {
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/HttpRequestTestCase.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/HttpRequestTestCase.java
index 4b83eb62c6d..0afe079289e 100644
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/HttpRequestTestCase.java
+++ b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/HttpRequestTestCase.java
@@ -22,7 +22,7 @@ import static org.testng.AssertJUnit.assertNull;
import static org.testng.AssertJUnit.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class HttpRequestTestCase {
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/HttpResponseTestCase.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/HttpResponseTestCase.java
index 1c4118a826c..027d50317b2 100644
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/HttpResponseTestCase.java
+++ b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/HttpResponseTestCase.java
@@ -22,7 +22,7 @@ import static org.testng.AssertJUnit.assertSame;
import static org.testng.AssertJUnit.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class HttpResponseTestCase {
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/filter/EmptyRequestFilterTestCase.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/filter/EmptyRequestFilterTestCase.java
index 90c74877df3..03871956641 100644
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/filter/EmptyRequestFilterTestCase.java
+++ b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/filter/EmptyRequestFilterTestCase.java
@@ -19,7 +19,7 @@ import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class EmptyRequestFilterTestCase {
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/filter/EmptyResponseFilterTestCase.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/filter/EmptyResponseFilterTestCase.java
index 8ddad6e9ff0..dce80be06c8 100644
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/filter/EmptyResponseFilterTestCase.java
+++ b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/filter/EmptyResponseFilterTestCase.java
@@ -19,7 +19,7 @@ import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class EmptyResponseFilterTestCase {
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/filter/ResponseHeaderFilter.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/filter/ResponseHeaderFilter.java
index 15806c0b116..3855c3a494b 100644
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/filter/ResponseHeaderFilter.java
+++ b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/filter/ResponseHeaderFilter.java
@@ -6,7 +6,7 @@ import com.yahoo.jdisc.Request;
import com.yahoo.jdisc.Response;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ResponseHeaderFilter extends AbstractResource implements ResponseFilter {
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/guiceModules/ConnectorFactoryRegistryModule.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/guiceModules/ConnectorFactoryRegistryModule.java
index a0fcce28086..d1a78f33e8f 100644
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/guiceModules/ConnectorFactoryRegistryModule.java
+++ b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/guiceModules/ConnectorFactoryRegistryModule.java
@@ -17,7 +17,7 @@ import com.yahoo.jdisc.http.ssl.DefaultSslTrustStoreConfigurator;
/**
* Guice module for test ConnectorFactories
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ConnectorFactoryRegistryModule implements Module {
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/guiceModules/ServletModule.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/guiceModules/ServletModule.java
index af20fb63a52..dd6511d1f88 100644
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/guiceModules/ServletModule.java
+++ b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/guiceModules/ServletModule.java
@@ -9,7 +9,7 @@ import com.yahoo.component.provider.ComponentRegistry;
import org.eclipse.jetty.servlet.ServletHolder;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ServletModule implements Module {
@Override
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java
index 7c79897ac27..30d5f9e657a 100644
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java
+++ b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java
@@ -65,7 +65,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
- * @author bakksjo
+ * @author Oyvind Bakksjo
* @author Simon Thoresen Hult
*/
public class HttpServerTest {
@@ -466,6 +466,20 @@ public class HttpServerTest {
assertThat(driver.close(), is(true));
}
+ @Test
+ public void requireThatGzipEncodingRequestsAreAutomaticallyDecompressed() throws Exception {
+ final TestDriver driver = TestDrivers.newInstance(new ParameterPrinterRequestHandler());
+ final String requestContent = generateContent('a', 30);
+ final ResponseValidator response =
+ driver.client().newPost("/status.html")
+ .addHeader(CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED)
+ .setGzipContent(requestContent)
+ .execute();
+ response.expectStatusCode(is(OK))
+ .expectContent(startsWith('{' + requestContent + "=[]}"));
+ assertThat(driver.close(), is(true));
+ }
+
private static RequestHandler mockRequestHandler() {
final RequestHandler mockRequestHandler = mock(RequestHandler.class);
when(mockRequestHandler.refer()).thenReturn(References.NOOP_REFERENCE);
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServletTest.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServletTest.java
index 1f5b50a4726..0c1256dff12 100644
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServletTest.java
+++ b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServletTest.java
@@ -24,7 +24,7 @@ import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class JDiscHttpServletTest {
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/SimpleHttpClient.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/SimpleHttpClient.java
index 30d33c991d9..1836a73d2fd 100644
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/SimpleHttpClient.java
+++ b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/SimpleHttpClient.java
@@ -6,12 +6,14 @@ import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
+import org.apache.http.client.entity.GzipCompressingEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
@@ -43,7 +45,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
/**
* A simple http client for testing
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class SimpleHttpClient {
@@ -59,7 +61,7 @@ public class SimpleHttpClient {
if (sslContext != null) {
SSLConnectionSocketFactory sslConnectionFactory = new SSLConnectionSocketFactory(
sslContext,
- SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+ NoopHostnameVerifier.INSTANCE);
builder.setSSLSocketFactory(sslConnectionFactory);
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
@@ -154,6 +156,11 @@ public class SimpleHttpClient {
return this;
}
+ public RequestExecutor setGzipContent(String content) {
+ this.entity = new GzipCompressingEntity(new StringEntity(content, StandardCharsets.UTF_8));
+ return this;
+ }
+
public RequestExecutor setBinaryContent(final byte[] content) {
this.entity = new ByteArrayEntity(content);
return this;
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/TestDriver.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/TestDriver.java
index bcc23facd95..39b68fcf1f6 100644
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/TestDriver.java
+++ b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/TestDriver.java
@@ -19,7 +19,7 @@ import static com.google.inject.name.Names.named;
* This class is based on the class by the same name in the jdisc_http_service module.
* It provides functionality for setting up a jdisc container with an HTTP server and handlers.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
* @author bakksjo
*/
public class TestDriver {
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/TestDrivers.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/TestDrivers.java
index 7e1a5eea0cb..f4344545637 100644
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/TestDrivers.java
+++ b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/TestDrivers.java
@@ -20,7 +20,7 @@ import java.io.IOException;
import static com.google.inject.name.Names.named;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class TestDrivers {
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/servlet/JDiscFilterForServletTest.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/servlet/JDiscFilterForServletTest.java
index c8cd725b493..82f37d6b416 100644
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/servlet/JDiscFilterForServletTest.java
+++ b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/servlet/JDiscFilterForServletTest.java
@@ -31,7 +31,7 @@ import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class JDiscFilterForServletTest extends ServletTestBase {
@Test
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/servlet/ServletTestBase.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/servlet/ServletTestBase.java
index ee7d92c234e..fe6a1974a35 100644
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/servlet/ServletTestBase.java
+++ b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/servlet/ServletTestBase.java
@@ -29,7 +29,7 @@ import java.io.IOException;
import java.io.PrintWriter;
/**
- * @author tonytv
+ * @author Tony Vaagenes
* @author bakksjo
*/
public class ServletTestBase {
diff --git a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/IgnoredCompletionHandler.java b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/IgnoredCompletionHandler.java
index 13cc6729b97..c64fea8653b 100644
--- a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/IgnoredCompletionHandler.java
+++ b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/IgnoredCompletionHandler.java
@@ -4,7 +4,7 @@ package com.yahoo.messagebus.jdisc;
import com.yahoo.jdisc.handler.CompletionHandler;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
enum IgnoredCompletionHandler implements CompletionHandler {
diff --git a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusClient.java b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusClient.java
index c03ef6e03e5..76ddddebe4e 100644
--- a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusClient.java
+++ b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusClient.java
@@ -24,7 +24,7 @@ import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class MbusClient extends AbstractResource implements ClientProvider, ReplyHandler {
diff --git a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusRequest.java b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusRequest.java
index ce73a30cbc0..a0bedd678eb 100644
--- a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusRequest.java
+++ b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusRequest.java
@@ -8,7 +8,7 @@ import com.yahoo.messagebus.Message;
import java.net.URI;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MbusRequest extends Request {
diff --git a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusRequestHandler.java b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusRequestHandler.java
index 4cb19c8454a..fb5657a9215 100644
--- a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusRequestHandler.java
+++ b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusRequestHandler.java
@@ -12,7 +12,7 @@ import com.yahoo.messagebus.Reply;
import com.yahoo.messagebus.ReplyHandler;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public abstract class MbusRequestHandler extends AbstractRequestHandler implements MessageHandler {
diff --git a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusResponse.java b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusResponse.java
index ce03ac3a2a8..37da4d8569f 100644
--- a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusResponse.java
+++ b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusResponse.java
@@ -5,7 +5,7 @@ import com.yahoo.jdisc.Response;
import com.yahoo.messagebus.Reply;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MbusResponse extends Response {
diff --git a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusServer.java b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusServer.java
index e29a64517ce..d0f5de54b4f 100644
--- a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusServer.java
+++ b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/MbusServer.java
@@ -20,7 +20,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class MbusServer extends AbstractResource implements ServerProvider, MessageHandler {
diff --git a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/StatusCodes.java b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/StatusCodes.java
index adbe07df8d0..6570c910af3 100644
--- a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/StatusCodes.java
+++ b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/StatusCodes.java
@@ -7,7 +7,7 @@ import com.yahoo.messagebus.ErrorCode;
import com.yahoo.messagebus.Reply;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class StatusCodes {
diff --git a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/ClientTestDriver.java b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/ClientTestDriver.java
index b3fff309594..111805d61b0 100644
--- a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/ClientTestDriver.java
+++ b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/ClientTestDriver.java
@@ -20,7 +20,7 @@ import java.net.URI;
import java.util.concurrent.TimeUnit;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ClientTestDriver {
diff --git a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/MessageQueue.java b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/MessageQueue.java
index 0269e313b51..c5287165e27 100644
--- a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/MessageQueue.java
+++ b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/MessageQueue.java
@@ -9,7 +9,7 @@ import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MessageQueue implements MessageHandler {
diff --git a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/RemoteClient.java b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/RemoteClient.java
index d56704886e3..c998f84ec5a 100644
--- a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/RemoteClient.java
+++ b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/RemoteClient.java
@@ -11,7 +11,7 @@ import com.yahoo.messagebus.test.SimpleProtocol;
import java.util.concurrent.TimeUnit;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RemoteClient {
diff --git a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/RemoteServer.java b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/RemoteServer.java
index 65002a839b8..1f0f82c4903 100644
--- a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/RemoteServer.java
+++ b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/RemoteServer.java
@@ -12,7 +12,7 @@ import com.yahoo.messagebus.test.SimpleProtocol;
import java.util.concurrent.TimeUnit;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RemoteServer {
diff --git a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/ReplyQueue.java b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/ReplyQueue.java
index 236365a255d..6c48aab5a7f 100644
--- a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/ReplyQueue.java
+++ b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/ReplyQueue.java
@@ -9,7 +9,7 @@ import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ReplyQueue implements ReplyHandler {
diff --git a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/ServerTestDriver.java b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/ServerTestDriver.java
index 6af90cfb345..266a26a7afb 100644
--- a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/ServerTestDriver.java
+++ b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/jdisc/test/ServerTestDriver.java
@@ -20,7 +20,7 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ServerTestDriver {
diff --git a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/ClientSession.java b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/ClientSession.java
index 81f17f81a18..0964a254cf2 100644
--- a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/ClientSession.java
+++ b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/ClientSession.java
@@ -6,7 +6,7 @@ import com.yahoo.messagebus.Message;
import com.yahoo.messagebus.Result;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface ClientSession extends SharedResource {
diff --git a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/ServerSession.java b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/ServerSession.java
index b0bc8538178..56713815c7a 100644
--- a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/ServerSession.java
+++ b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/ServerSession.java
@@ -6,7 +6,7 @@ import com.yahoo.messagebus.MessageHandler;
import com.yahoo.messagebus.Reply;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface ServerSession extends SharedResource {
diff --git a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/SharedDestinationSession.java b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/SharedDestinationSession.java
index 6d3055298cc..752be1bdde7 100644
--- a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/SharedDestinationSession.java
+++ b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/SharedDestinationSession.java
@@ -11,7 +11,7 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Logger;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SharedDestinationSession extends AbstractResource implements MessageHandler, ServerSession {
diff --git a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/SharedIntermediateSession.java b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/SharedIntermediateSession.java
index aca0803c12c..6208bc97077 100644
--- a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/SharedIntermediateSession.java
+++ b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/SharedIntermediateSession.java
@@ -11,7 +11,7 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Logger;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SharedIntermediateSession extends AbstractResource
implements ClientSession, ServerSession, MessageHandler, ReplyHandler
diff --git a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/SharedMessageBus.java b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/SharedMessageBus.java
index d0441dac1f2..d36457cf42d 100644
--- a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/SharedMessageBus.java
+++ b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/SharedMessageBus.java
@@ -13,7 +13,7 @@ import com.yahoo.cloud.config.SlobroksConfig;
import java.util.logging.Logger;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SharedMessageBus extends AbstractResource {
diff --git a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/SharedSourceSession.java b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/SharedSourceSession.java
index 880d627a514..6fed667992c 100644
--- a/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/SharedSourceSession.java
+++ b/jdisc_messagebus_service/src/main/java/com/yahoo/messagebus/shared/SharedSourceSession.java
@@ -14,7 +14,7 @@ import com.yahoo.messagebus.SourceSessionParams;
import java.util.logging.Logger;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SharedSourceSession extends AbstractResource implements ClientSession, ReplyHandler {
diff --git a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/ClientThreadingTestCase.java b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/ClientThreadingTestCase.java
index ab5c3aaf281..62a9a864781 100644
--- a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/ClientThreadingTestCase.java
+++ b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/ClientThreadingTestCase.java
@@ -35,7 +35,7 @@ import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ClientThreadingTestCase {
diff --git a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusClientTestCase.java b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusClientTestCase.java
index 761b3a3a557..9cfd1fd02b9 100644
--- a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusClientTestCase.java
+++ b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusClientTestCase.java
@@ -23,7 +23,7 @@ import java.util.concurrent.TimeUnit;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MbusClientTestCase {
diff --git a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusRequestHandlerTestCase.java b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusRequestHandlerTestCase.java
index 08464876d60..316ad18bae9 100644
--- a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusRequestHandlerTestCase.java
+++ b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusRequestHandlerTestCase.java
@@ -23,7 +23,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class MbusRequestHandlerTestCase {
diff --git a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusRequestTestCase.java b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusRequestTestCase.java
index ed61ed409d3..3d998a239f1 100644
--- a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusRequestTestCase.java
+++ b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusRequestTestCase.java
@@ -12,7 +12,7 @@ import java.net.URI;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MbusRequestTestCase {
diff --git a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusResponseTestCase.java b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusResponseTestCase.java
index 7a778dc057c..eb4cb949770 100644
--- a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusResponseTestCase.java
+++ b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusResponseTestCase.java
@@ -10,7 +10,7 @@ import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MbusResponseTestCase {
diff --git a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusServerConformanceTest.java b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusServerConformanceTest.java
index a6f55ef765e..71cbc422d9b 100644
--- a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusServerConformanceTest.java
+++ b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusServerConformanceTest.java
@@ -38,7 +38,7 @@ import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class MbusServerConformanceTest extends ServerProviderConformanceTest {
diff --git a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusServerTestCase.java b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusServerTestCase.java
index 117d81e2db4..f20eeb6abaa 100644
--- a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusServerTestCase.java
+++ b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/MbusServerTestCase.java
@@ -27,7 +27,7 @@ import java.util.concurrent.TimeUnit;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MbusServerTestCase {
diff --git a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/ServerThreadingTestCase.java b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/ServerThreadingTestCase.java
index caf5c53ba3a..a7ee355094f 100644
--- a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/ServerThreadingTestCase.java
+++ b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/ServerThreadingTestCase.java
@@ -33,7 +33,7 @@ import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ServerThreadingTestCase {
diff --git a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/test/ClientTestDriverTestCase.java b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/test/ClientTestDriverTestCase.java
index ace3b4fa78a..ef290a070cb 100644
--- a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/test/ClientTestDriverTestCase.java
+++ b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/test/ClientTestDriverTestCase.java
@@ -10,7 +10,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ClientTestDriverTestCase {
diff --git a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/test/ServerTestDriverTestCase.java b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/test/ServerTestDriverTestCase.java
index a70608ccc9a..6ede2bb3f33 100644
--- a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/test/ServerTestDriverTestCase.java
+++ b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/jdisc/test/ServerTestDriverTestCase.java
@@ -11,7 +11,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ServerTestDriverTestCase {
diff --git a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/shared/SharedDestinationSessionTestCase.java b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/shared/SharedDestinationSessionTestCase.java
index e04a2000dfe..7226fdf8d85 100644
--- a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/shared/SharedDestinationSessionTestCase.java
+++ b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/shared/SharedDestinationSessionTestCase.java
@@ -19,7 +19,7 @@ import java.util.concurrent.TimeUnit;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SharedDestinationSessionTestCase {
diff --git a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/shared/SharedIntermediateSessionTestCase.java b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/shared/SharedIntermediateSessionTestCase.java
index 62357252c52..3b063d148e6 100644
--- a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/shared/SharedIntermediateSessionTestCase.java
+++ b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/shared/SharedIntermediateSessionTestCase.java
@@ -20,7 +20,7 @@ import java.util.concurrent.TimeUnit;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SharedIntermediateSessionTestCase {
diff --git a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/shared/SharedMessageBusTestCase.java b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/shared/SharedMessageBusTestCase.java
index b5649991e0f..a54489a89e6 100644
--- a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/shared/SharedMessageBusTestCase.java
+++ b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/shared/SharedMessageBusTestCase.java
@@ -11,7 +11,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SharedMessageBusTestCase {
diff --git a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/shared/SharedSourceSessionTestCase.java b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/shared/SharedSourceSessionTestCase.java
index df9fae2354c..1f0966fc961 100644
--- a/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/shared/SharedSourceSessionTestCase.java
+++ b/jdisc_messagebus_service/src/test/java/com/yahoo/messagebus/shared/SharedSourceSessionTestCase.java
@@ -19,7 +19,7 @@ import java.util.concurrent.TimeUnit;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SharedSourceSessionTestCase {
diff --git a/jrt/src/com/yahoo/jrt/slobrok/api/IMirror.java b/jrt/src/com/yahoo/jrt/slobrok/api/IMirror.java
index ec1f5f2e4da..9b3bd81fc3c 100644
--- a/jrt/src/com/yahoo/jrt/slobrok/api/IMirror.java
+++ b/jrt/src/com/yahoo/jrt/slobrok/api/IMirror.java
@@ -4,7 +4,7 @@ package com.yahoo.jrt.slobrok.api;
/**
* Defines an interface for the name server lookup.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public interface IMirror {
diff --git a/jrt/tests/com/yahoo/jrt/slobrok/api/SlobrokListTestCase.java b/jrt/tests/com/yahoo/jrt/slobrok/api/SlobrokListTestCase.java
index 8ee76e87e24..02c2333b696 100644
--- a/jrt/tests/com/yahoo/jrt/slobrok/api/SlobrokListTestCase.java
+++ b/jrt/tests/com/yahoo/jrt/slobrok/api/SlobrokListTestCase.java
@@ -7,7 +7,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SlobrokListTestCase {
diff --git a/juniper/src/vespa/juniper/SummaryConfig.h b/juniper/src/vespa/juniper/SummaryConfig.h
index 77e7b77e004..96023b9523b 100644
--- a/juniper/src/vespa/juniper/SummaryConfig.h
+++ b/juniper/src/vespa/juniper/SummaryConfig.h
@@ -6,8 +6,7 @@
enum ConfigFlag {
CF_OFF,
CF_ON,
- CF_AUTO,
- CF_MAXVAL
+ CF_AUTO
};
@@ -27,6 +26,8 @@ public:
ConfigFlag esc_markup,
ConfigFlag preserve_white_space_);
+ ~SummaryConfig() {}
+
inline const std::string & highlight_on() const { return _highlight_on; }
inline const std::string & highlight_off() const { return _highlight_off; }
inline const std::string & dots() const { return _dots; }
diff --git a/juniper/src/vespa/juniper/appender.h b/juniper/src/vespa/juniper/appender.h
index 39ea3a649fb..a74b038c5f1 100644
--- a/juniper/src/vespa/juniper/appender.h
+++ b/juniper/src/vespa/juniper/appender.h
@@ -1,7 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#pragma once
-/* $Id: */
+#include <vespa/vespalib/util/hdr_abort.h>
namespace juniper {
@@ -118,9 +118,6 @@ public:
_sumconf->highlight_off()[0] == '<' ||
_sumconf->dots()[0] == '<');
break;
- default:
- assert(false);
- break;
}
if (_sumconf->preserve_white_space() == CF_ON) {
diff --git a/juniper/src/vespa/juniper/queryparser.cpp b/juniper/src/vespa/juniper/queryparser.cpp
index 6214c7d09e5..cf300970fda 100644
--- a/juniper/src/vespa/juniper/queryparser.cpp
+++ b/juniper/src/vespa/juniper/queryparser.cpp
@@ -181,7 +181,7 @@ QueryItem* QueryParser::ParseExpr()
LOG(debug, "constraint operator %s - value %d", op.c_str(), p1);
break;
default:
- assert(false);
+ LOG_ABORT("should not reach here");
}
next();
if (!match("(", true)) return NULL;
diff --git a/linguistics/pom.xml b/linguistics/pom.xml
index e4aa7c3049e..f743348dde3 100644
--- a/linguistics/pom.xml
+++ b/linguistics/pom.xml
@@ -62,6 +62,14 @@
<scope>provided</scope>
<classifier>no_aop</classifier>
</dependency>
+ <dependency>
+ <groupId>org.apache.opennlp</groupId>
+ <artifactId>opennlp-tools</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.optimaize.languagedetector</groupId>
+ <artifactId>language-detector</artifactId>
+ </dependency>
</dependencies>
<build>
<plugins>
diff --git a/linguistics/src/main/java/com/yahoo/language/Linguistics.java b/linguistics/src/main/java/com/yahoo/language/Linguistics.java
index e275f189b0c..035de415aa7 100644
--- a/linguistics/src/main/java/com/yahoo/language/Linguistics.java
+++ b/linguistics/src/main/java/com/yahoo/language/Linguistics.java
@@ -25,7 +25,7 @@ import com.yahoo.language.simple.SimpleLinguistics;
* for each thread.</p>
*
* @author Mathias Mølster Lidal
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
* @author bratseth
*/
public interface Linguistics {
@@ -41,7 +41,12 @@ public interface Linguistics {
CHARACTER_CLASSES
}
- /** The same as new com.yahoo.language.simple.SimpleLinguistics(). Prefer using that directly. */
+ /**
+ * The same as new com.yahoo.language.simple.SimpleLinguistics(). Prefer using that directly.
+ *
+ * @deprecated use new com.yahoo.language.simple.SimpleLinguistics()
+ */
+ @Deprecated // TODO: Remove this field on Vespa 7
Linguistics SIMPLE = new SimpleLinguistics();
/**
diff --git a/linguistics/src/main/java/com/yahoo/language/LinguisticsCase.java b/linguistics/src/main/java/com/yahoo/language/LinguisticsCase.java
index 37b9463fddb..7a3f5fa4055 100644
--- a/linguistics/src/main/java/com/yahoo/language/LinguisticsCase.java
+++ b/linguistics/src/main/java/com/yahoo/language/LinguisticsCase.java
@@ -9,7 +9,7 @@ import java.util.Locale;
* This class provides a case normalization operation to be used e.g. when
* document search should be case insensitive.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class LinguisticsCase {
diff --git a/linguistics/src/main/java/com/yahoo/language/LocaleFactory.java b/linguistics/src/main/java/com/yahoo/language/LocaleFactory.java
index 2bc510bb038..39345399ea5 100644
--- a/linguistics/src/main/java/com/yahoo/language/LocaleFactory.java
+++ b/linguistics/src/main/java/com/yahoo/language/LocaleFactory.java
@@ -4,7 +4,7 @@ package com.yahoo.language;
import java.util.Locale;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public final class LocaleFactory {
diff --git a/linguistics/src/main/java/com/yahoo/language/detect/AbstractDetector.java b/linguistics/src/main/java/com/yahoo/language/detect/AbstractDetector.java
index 4d933836b1d..2c37ee08cd0 100644
--- a/linguistics/src/main/java/com/yahoo/language/detect/AbstractDetector.java
+++ b/linguistics/src/main/java/com/yahoo/language/detect/AbstractDetector.java
@@ -6,7 +6,7 @@ import com.yahoo.text.Utf8;
import java.nio.ByteBuffer;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public abstract class AbstractDetector implements Detector {
diff --git a/linguistics/src/main/java/com/yahoo/language/opennlp/OpenNlpLinguistics.java b/linguistics/src/main/java/com/yahoo/language/opennlp/OpenNlpLinguistics.java
new file mode 100644
index 00000000000..12de309a2d3
--- /dev/null
+++ b/linguistics/src/main/java/com/yahoo/language/opennlp/OpenNlpLinguistics.java
@@ -0,0 +1,11 @@
+package com.yahoo.language.opennlp;
+
+import com.yahoo.language.process.Tokenizer;
+import com.yahoo.language.simple.SimpleLinguistics;
+
+public class OpenNlpLinguistics extends SimpleLinguistics {
+ @Override
+ public Tokenizer getTokenizer() {
+ return new OpenNlpTokenizer(getNormalizer(), getTransformer());
+ }
+}
diff --git a/linguistics/src/main/java/com/yahoo/language/opennlp/OpenNlpTokenizer.java b/linguistics/src/main/java/com/yahoo/language/opennlp/OpenNlpTokenizer.java
new file mode 100644
index 00000000000..5d5f5cbfba9
--- /dev/null
+++ b/linguistics/src/main/java/com/yahoo/language/opennlp/OpenNlpTokenizer.java
@@ -0,0 +1,135 @@
+package com.yahoo.language.opennlp;
+
+import com.yahoo.language.Language;
+import com.yahoo.language.LinguisticsCase;
+import com.yahoo.language.process.*;
+import com.yahoo.language.simple.*;
+import opennlp.tools.stemmer.Stemmer;
+import opennlp.tools.stemmer.snowball.SnowballStemmer;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class OpenNlpTokenizer implements Tokenizer {
+ private final static int SPACE_CODE = 32;
+ private final Normalizer normalizer;
+ private final Transformer transformer;
+ private final SimpleTokenizer simpleTokenizer;
+
+ public OpenNlpTokenizer() {
+ this(new SimpleNormalizer(), new SimpleTransformer());
+ }
+
+ public OpenNlpTokenizer(Normalizer normalizer, Transformer transformer) {
+ this.normalizer = normalizer;
+ this.transformer = transformer;
+ simpleTokenizer = new SimpleTokenizer(normalizer, transformer);
+ }
+
+ @Override
+ public Iterable<Token> tokenize(String input, Language language, StemMode stemMode, boolean removeAccents) {
+ if (input.isEmpty()) return Collections.emptyList();
+ Stemmer stemmer = getStemmerForLanguage(language, stemMode);
+ if (stemmer == null) {
+ return simpleTokenizer.tokenize(input, language, stemMode, removeAccents);
+ }
+
+ List<Token> tokens = new ArrayList<>();
+ int nextCode = input.codePointAt(0);
+ TokenType prevType = SimpleTokenType.valueOf(nextCode);
+ for (int prev = 0, next = Character.charCount(nextCode); next <= input.length(); ) {
+ nextCode = next < input.length() ? input.codePointAt(next) : SPACE_CODE;
+ TokenType nextType = SimpleTokenType.valueOf(nextCode);
+ if (!prevType.isIndexable() || !nextType.isIndexable()) {
+ String original = input.substring(prev, next);
+ String token = processToken(original, language, stemMode, removeAccents, stemmer);
+ tokens.add(new SimpleToken(original).setOffset(prev)
+ .setType(prevType)
+ .setTokenString(token));
+ prev = next;
+ prevType = nextType;
+ }
+ next += Character.charCount(nextCode);
+ }
+ return tokens;
+ }
+
+ private Stemmer getStemmerForLanguage(Language language, StemMode stemMode) {
+ if (language == null || Language.ENGLISH.equals(language) || StemMode.NONE.equals(stemMode)) {
+ return null;
+ }
+ SnowballStemmer.ALGORITHM alg;
+ switch (language) {
+ case DANISH:
+ alg = SnowballStemmer.ALGORITHM.DANISH;
+ break;
+ case DUTCH:
+ alg = SnowballStemmer.ALGORITHM.DUTCH;
+ break;
+ case FINNISH:
+ alg = SnowballStemmer.ALGORITHM.FINNISH;
+ break;
+ case FRENCH:
+ alg = SnowballStemmer.ALGORITHM.FRENCH;
+ break;
+ case GERMAN:
+ alg = SnowballStemmer.ALGORITHM.GERMAN;
+ break;
+ case HUNGARIAN:
+ alg = SnowballStemmer.ALGORITHM.HUNGARIAN;
+ break;
+ case IRISH:
+ alg = SnowballStemmer.ALGORITHM.IRISH;
+ break;
+ case ITALIAN:
+ alg = SnowballStemmer.ALGORITHM.ITALIAN;
+ break;
+ case NORWEGIAN_BOKMAL:
+ case NORWEGIAN_NYNORSK:
+ alg = SnowballStemmer.ALGORITHM.NORWEGIAN;
+ break;
+ case PORTUGUESE:
+ alg = SnowballStemmer.ALGORITHM.PORTUGUESE;
+ break;
+ case ROMANIAN:
+ alg = SnowballStemmer.ALGORITHM.ROMANIAN;
+ break;
+ case RUSSIAN:
+ alg = SnowballStemmer.ALGORITHM.RUSSIAN;
+ break;
+ case SPANISH:
+ alg = SnowballStemmer.ALGORITHM.SPANISH;
+ break;
+ case SWEDISH:
+ alg = SnowballStemmer.ALGORITHM.SWEDISH;
+ break;
+ case TURKISH:
+ alg = SnowballStemmer.ALGORITHM.TURKISH;
+ break;
+ case ENGLISH:
+ alg = SnowballStemmer.ALGORITHM.ENGLISH;
+ break;
+ default:
+ return null;
+
+ }
+ return new SnowballStemmer(alg);
+ }
+
+ private String processToken(String token, Language language, StemMode stemMode, boolean removeAccents,
+ Stemmer stemmer) {
+ token = normalizer.normalize(token);
+ token = LinguisticsCase.toLowerCase(token);
+ if (removeAccents)
+ token = transformer.accentDrop(token, language);
+ if (stemMode != StemMode.NONE) {
+ token = doStemming(token, stemmer);
+ }
+ return token;
+ }
+
+ private String doStemming(String token, Stemmer stemmer) {
+ return stemmer.stem(token).toString();
+ }
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/common/package-info.java b/linguistics/src/main/java/com/yahoo/language/opennlp/package-info.java
index 95decd86e8b..2bdd315418f 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/common/package-info.java
+++ b/linguistics/src/main/java/com/yahoo/language/opennlp/package-info.java
@@ -1,5 +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.vespa.hosted.controller.common;
+package com.yahoo.language.opennlp;
import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/linguistics/src/main/java/com/yahoo/language/process/ProcessingException.java b/linguistics/src/main/java/com/yahoo/language/process/ProcessingException.java
index 3b6c01a18e8..941afa07347 100644
--- a/linguistics/src/main/java/com/yahoo/language/process/ProcessingException.java
+++ b/linguistics/src/main/java/com/yahoo/language/process/ProcessingException.java
@@ -4,7 +4,7 @@ package com.yahoo.language.process;
/**
* <p>Exception class indicating that a fatal error occured during linguistic processing.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ProcessingException extends RuntimeException {
diff --git a/linguistics/src/main/java/com/yahoo/language/process/StemmerImpl.java b/linguistics/src/main/java/com/yahoo/language/process/StemmerImpl.java
index c3faa7404aa..f401ddaba99 100644
--- a/linguistics/src/main/java/com/yahoo/language/process/StemmerImpl.java
+++ b/linguistics/src/main/java/com/yahoo/language/process/StemmerImpl.java
@@ -7,7 +7,7 @@ import java.util.ArrayList;
import java.util.List;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class StemmerImpl implements Stemmer {
diff --git a/linguistics/src/main/java/com/yahoo/language/simple/SimpleDetector.java b/linguistics/src/main/java/com/yahoo/language/simple/SimpleDetector.java
index 24ac0091e72..4ae3644d62c 100644
--- a/linguistics/src/main/java/com/yahoo/language/simple/SimpleDetector.java
+++ b/linguistics/src/main/java/com/yahoo/language/simple/SimpleDetector.java
@@ -1,28 +1,62 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.language.simple;
+import com.google.common.base.Optional;
+import com.optimaize.langdetect.LanguageDetector;
+import com.optimaize.langdetect.LanguageDetectorBuilder;
+import com.optimaize.langdetect.i18n.LdLocale;
+import com.optimaize.langdetect.ngram.NgramExtractors;
+import com.optimaize.langdetect.profiles.LanguageProfile;
+import com.optimaize.langdetect.profiles.LanguageProfileReader;
+import com.optimaize.langdetect.text.CommonTextObjectFactories;
+import com.optimaize.langdetect.text.TextObject;
+import com.optimaize.langdetect.text.TextObjectFactory;
import com.yahoo.language.Language;
import com.yahoo.language.detect.Detection;
import com.yahoo.language.detect.Detector;
import com.yahoo.language.detect.Hint;
import com.yahoo.text.Utf8;
+import java.io.IOException;
import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Locale;
/**
- * <p>Includes functionality for determining the langCode from a sample or from the encoding. Currently only Chinese,
- * Japanese and Korean are supported. There are two ways to guess a String's langCode, by encoding and by character
+ * Includes functionality for determining the langCode from a sample or from the encoding.
+ * There are two ways to guess a String's langCode, by encoding and by character
* set. If the encoding is available this is a very good indication of the langCode. If the encoding is not available,
* then the actual characters in the string can be used to make an educated guess at the String's langCode. Recall a
* String in Java is unicode. Therefore, we can simply look at the unicode blocks of the characters in the string.
* Unfortunately, its not 100% fool-proof. From what I've been able to determine, Korean characters do not overlap with
* Japanese or Chinese characters, so their presence is a good indication of Korean. If a string contains phonetic
* japanese, this is a good indication of Japanese. However, Japanese and Chinese characters occupy many of the same
- * character blocks, so if there are no definitive signs of Japanese then it is assumed that the String is Chinese.</p>
+ * character blocks, so if there are no definitive signs of Japanese then it is assumed that the String is Chinese.
*
* @author Rich Pito
*/
public class SimpleDetector implements Detector {
+ static private TextObjectFactory textObjectFactory;
+ static private LanguageDetector languageDetector;
+
+ static {
+ // origin: https://github.com/optimaize/language-detector
+ //load all languages:
+ List<LanguageProfile> languageProfiles;
+ try {
+ languageProfiles = new LanguageProfileReader().readAllBuiltIn();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ //build language detector:
+ languageDetector = LanguageDetectorBuilder.create(NgramExtractors.standard())
+ .withProfiles(languageProfiles)
+ .build();
+
+ //create a text object factory
+ textObjectFactory = CommonTextObjectFactories.forDetectingOnLargeText();
+ }
@Override
public Detection detect(byte[] input, int offset, int length, Hint hint) {
@@ -109,10 +143,26 @@ public class SimpleDetector implements Detector {
return Language.THAI;
}
}
+ if (Language.UNKNOWN.equals(soFar)){
+ return detectLangOptimaize(input);
+ }
// got to the end, so return the current best guess
return soFar;
}
+ private static Language detectLangOptimaize(String input) {
+ if (input == null || input.length() == 0) {
+ return Language.UNKNOWN;
+ }
+ TextObject textObject = textObjectFactory.forText(input);
+ Optional<LdLocale> lang = languageDetector.detect(textObject);
+ if (lang.isPresent()) {
+ String language = lang.get().getLanguage();
+ return Language.fromLocale(new Locale(language));
+ }
+ return Language.UNKNOWN;
+ }
+
private boolean isTrailingOctet(byte i) {
return ((i >>> 6) & 3) == 2;
}
@@ -137,7 +187,7 @@ public class SimpleDetector implements Detector {
}
}
- private String guessEncoding(byte[] input) {
+ public String guessEncoding(byte[] input) {
boolean isUtf8 = true;
boolean hasHighs = false;
scan:
diff --git a/linguistics/src/main/java/com/yahoo/language/simple/SimpleNormalizer.java b/linguistics/src/main/java/com/yahoo/language/simple/SimpleNormalizer.java
index 33231733a13..98135bf72fc 100644
--- a/linguistics/src/main/java/com/yahoo/language/simple/SimpleNormalizer.java
+++ b/linguistics/src/main/java/com/yahoo/language/simple/SimpleNormalizer.java
@@ -4,7 +4,7 @@ package com.yahoo.language.simple;
import com.yahoo.language.process.Normalizer;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class SimpleNormalizer implements Normalizer {
diff --git a/linguistics/src/main/java/com/yahoo/language/simple/SimpleToken.java b/linguistics/src/main/java/com/yahoo/language/simple/SimpleToken.java
index 20c41d657e1..122b9b6dff6 100644
--- a/linguistics/src/main/java/com/yahoo/language/simple/SimpleToken.java
+++ b/linguistics/src/main/java/com/yahoo/language/simple/SimpleToken.java
@@ -9,7 +9,7 @@ import java.util.ArrayList;
import java.util.List;
/**
- * @author <a href="mailto:mathiasm@yahoo-inc.com">Mathias Mølster Lidal</a>
+ * @author Mathias Mølster Lidal
*/
public class SimpleToken implements Token {
diff --git a/linguistics/src/main/java/com/yahoo/language/simple/SimpleTokenType.java b/linguistics/src/main/java/com/yahoo/language/simple/SimpleTokenType.java
index c9c6286336d..d7eb8a72ed8 100644
--- a/linguistics/src/main/java/com/yahoo/language/simple/SimpleTokenType.java
+++ b/linguistics/src/main/java/com/yahoo/language/simple/SimpleTokenType.java
@@ -6,7 +6,7 @@ import com.yahoo.language.process.TokenType;
/**
* @author arnej27959
*/
-class SimpleTokenType {
+public class SimpleTokenType {
public static TokenType valueOf(int codePoint) {
switch (Character.getType(codePoint)) {
diff --git a/linguistics/src/main/java/com/yahoo/language/simple/SimpleTransformer.java b/linguistics/src/main/java/com/yahoo/language/simple/SimpleTransformer.java
index fce3344bfad..3ab1750bcee 100644
--- a/linguistics/src/main/java/com/yahoo/language/simple/SimpleTransformer.java
+++ b/linguistics/src/main/java/com/yahoo/language/simple/SimpleTransformer.java
@@ -11,7 +11,7 @@ import java.util.regex.Pattern;
* Converts all accented characters into their de-accented counterparts followed by their combining diacritics, then
* strips off the diacritics using a regex.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SimpleTransformer implements Transformer {
diff --git a/linguistics/src/test/java/com/yahoo/language/LocaleFactoryTestCase.java b/linguistics/src/test/java/com/yahoo/language/LocaleFactoryTestCase.java
index 09dc67200b7..c76a85fbbd0 100644
--- a/linguistics/src/test/java/com/yahoo/language/LocaleFactoryTestCase.java
+++ b/linguistics/src/test/java/com/yahoo/language/LocaleFactoryTestCase.java
@@ -9,7 +9,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class LocaleFactoryTestCase {
diff --git a/linguistics/src/test/java/com/yahoo/language/detect/AbstractDetectorTestCase.java b/linguistics/src/test/java/com/yahoo/language/detect/AbstractDetectorTestCase.java
index aa161c05258..c0f1b92a6bf 100644
--- a/linguistics/src/test/java/com/yahoo/language/detect/AbstractDetectorTestCase.java
+++ b/linguistics/src/test/java/com/yahoo/language/detect/AbstractDetectorTestCase.java
@@ -10,7 +10,7 @@ import java.nio.charset.Charset;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class AbstractDetectorTestCase {
diff --git a/linguistics/src/test/java/com/yahoo/language/opennlp/OpenNlpTokenizationTestCase.java b/linguistics/src/test/java/com/yahoo/language/opennlp/OpenNlpTokenizationTestCase.java
new file mode 100644
index 00000000000..914e3817568
--- /dev/null
+++ b/linguistics/src/test/java/com/yahoo/language/opennlp/OpenNlpTokenizationTestCase.java
@@ -0,0 +1,237 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.language.opennlp;
+
+import com.yahoo.language.Language;
+import com.yahoo.language.process.StemMode;
+import com.yahoo.language.process.Token;
+import com.yahoo.language.process.Tokenizer;
+import org.junit.Test;
+
+import java.util.*;
+
+import static com.yahoo.language.LinguisticsCase.toLowerCase;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.*;
+
+/**
+ * Test of tokenization, with stemming and accent removal
+ *
+ * @author <a href="mailto:mathiasm@yahoo-inc.com">Mathias Mølster Lidal</a>
+ */
+public class OpenNlpTokenizationTestCase {
+
+ private final Tokenizer tokenizer = new OpenNlpTokenizer();
+
+ @Test
+ public void testTokenizer() {
+ assertTokenize("This is a test, 123",
+ Arrays.asList("this", "is", "a", "test", "123"),
+ Arrays.asList("This", " ", "is", " ", "a", " ", "test", ",", " ", "123"));
+ }
+
+ @Test
+ public void testUnderScoreTokenization() {
+ assertTokenize("ugcapi_1", Language.ENGLISH, StemMode.SHORTEST, true, Arrays.asList("ugcapi", "1"), null);
+ }
+
+ @Test
+ public void testPhrasesWithPunctuation() {
+ assertTokenize("PHY_101.html a space/time or space-time course", Language.ENGLISH, StemMode.NONE,
+ false,
+ Arrays.asList("phy", "101", "html", "a", "space", "time", "or", "space", "time", "course"),
+ null);
+ assertTokenize("PHY_101.", Language.ENGLISH, StemMode.NONE, false, Arrays.asList("phy", "101"), null);
+ assertTokenize("101.3", Language.ENGLISH, StemMode.NONE, false, Arrays.asList("101", "3"), null);
+ }
+
+ @Test
+ public void testDoubleWidthTokenization() {
+ // "sony"
+ assertTokenize("\uFF53\uFF4F\uFF4E\uFF59", Language.ENGLISH, StemMode.NONE, false,
+ Arrays.asList("sony"), null);
+ assertTokenize("\uFF53\uFF4F\uFF4E\uFF59", Language.ENGLISH, StemMode.SHORTEST, false,
+ Arrays.asList("sony"), null);
+ // "SONY"
+ assertTokenize("\uFF33\uFF2F\uFF2E\uFF39", Language.ENGLISH, StemMode.NONE, false,
+ Arrays.asList("sony"), null);
+ assertTokenize("\uFF33\uFF2F\uFF2E\uFF39", Language.ENGLISH, StemMode.SHORTEST, false,
+ Arrays.asList("sony"), null);
+ // "on"
+ assertTokenize("\uFF4F\uFF4E", Language.ENGLISH, StemMode.NONE, false,
+ Arrays.asList("on"), null);
+ assertTokenize("\uFF4F\uFF4E", Language.ENGLISH, StemMode.SHORTEST, false,
+ Arrays.asList("on"), null);
+ // "ON"
+ assertTokenize("\uFF2F\uFF2E", Language.ENGLISH, StemMode.NONE, false,
+ Arrays.asList("on"), null);
+ assertTokenize("\uFF2F\uFF2E", Language.ENGLISH, StemMode.SHORTEST, false,
+ Arrays.asList("on"), null);
+ assertTokenize("наименование", Language.RUSSIAN, StemMode.SHORTEST, false,
+ Arrays.asList("наименован"), null);
+ }
+
+ @Test
+ public void testLargeTextTokenization() {
+ StringBuilder sb = new StringBuilder();
+ String s = "teststring ";
+ for (int i = 0; i < 100000; i++) {
+ sb.append(s);
+ }
+
+ String input = sb.toString();
+
+ int numTokens = 0;
+ List<Long> pos = new ArrayList<>();
+ for (Token t : tokenizer.tokenize(input, Language.ENGLISH, StemMode.NONE, false)) {
+ numTokens++;
+ if ((numTokens % 100) == 0) {
+ pos.add(t.getOffset());
+ }
+ }
+
+ assertEquals("Check that all tokens have been tokenized", numTokens, 200000);
+ assertTrue("Increasing token pos", assertMonoIncr(pos));
+ }
+
+ @Test
+ public void testLargeTokenGuard() {
+ StringBuilder str = new StringBuilder();
+ for (int i = 0; i < 128 * 256; i++) {
+ str.append("ab");
+ }
+ Iterator<Token> it = tokenizer.tokenize(str.toString(), Language.ENGLISH, StemMode.NONE, false).iterator();
+ assertTrue(it.hasNext());
+ assertNotNull(it.next().getTokenString());
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void testTokenIterator() {
+ Iterator<Token> it = tokenizer.tokenize("", Language.ENGLISH, StemMode.NONE, false).iterator();
+ assertFalse(it.hasNext());
+ try {
+ it.next();
+ fail();
+ } catch (NoSuchElementException e) {
+ // success
+ }
+
+ it = tokenizer.tokenize("", Language.ENGLISH, StemMode.NONE, false).iterator();
+ assertFalse(it.hasNext());
+
+ it = tokenizer.tokenize("one two three", Language.ENGLISH, StemMode.NONE, false).iterator();
+ assertNotNull(it.next());
+ assertNotNull(it.next());
+ assertNotNull(it.next());
+ assertNotNull(it.next());
+ assertNotNull(it.next());
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void testGetOffsetLength() {
+ String input = "Deka-Chef Weber r\u00e4umt Kommunikationsfehler ein";
+ long[] expOffset = { 0, 4, 5, 9, 10, 15, 16, 21, 22, 42, 43 };
+ int[] len = { 4, 1, 4, 1, 5, 1, 5, 1, 20, 1, 3 };
+
+ int idx = 0;
+ for (Token token : tokenizer.tokenize(input, Language.GERMAN, StemMode.SHORTEST, false)) {
+ assertThat("Token offset for token #" + idx, token.getOffset(), is(expOffset[idx]));
+ assertThat("Token len for token #" + idx, token.getOrig().length(), is(len[idx]));
+ idx++;
+ }
+ }
+
+ @Test
+ public void testRecursiveDecompose() {
+ for (Token t : tokenizer.tokenize("\u00a510%", Language.ENGLISH, StemMode.SHORTEST, false)) {
+ recurseDecompose(t);
+ }
+ }
+
+ @Test
+ public void testIndexability() {
+ String input = "tafsirnya\u0648\u0643\u064F\u0646\u0652";
+ for (StemMode stemMode : new StemMode[] { StemMode.NONE,
+ StemMode.SHORTEST }) {
+ for (Language l : new Language[] { Language.INDONESIAN,
+ Language.ENGLISH, Language.ARABIC }) {
+ for (boolean accentDrop : new boolean[] { true, false }) {
+ for (Token token : tokenizer.tokenize(input,
+ l, stemMode, accentDrop)) {
+ if (token.getTokenString().length() == 0) {
+ assertFalse(token.isIndexable());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void recurseDecompose(Token t) {
+ assertTrue(t.getOffset() >= 0);
+ assertTrue(t.getOrig().length() >= 0);
+
+ int numComp = t.getNumComponents();
+ for (int i = 0; i < numComp; i++) {
+ Token comp = t.getComponent(i);
+ recurseDecompose(comp);
+ }
+ }
+
+ private boolean assertMonoIncr(Iterable<Long> n) {
+ long trailing = -1;
+ for (long i : n) {
+ if (i < trailing) {
+ return false;
+ }
+ trailing = i;
+ }
+ return true;
+ }
+
+ private void assertTokenize(String input, List<String> indexed, List<String> orig) {
+ assertTokenize(input, Language.ENGLISH, StemMode.NONE, false, indexed, orig);
+ }
+
+ /**
+ * <p>Compare the results of running an input string through the tokenizer with an "index" truth, and an optional
+ * "orig" truth.</p>
+ *
+ * @param input The text to process, passed to tokenizer.
+ * @param language The language tag, passed to tokenizer.
+ * @param stemMode If stemMode != NONE, test will silently succeed if tokenizer does not do stemming.
+ * @param accentDrop Passed to the tokenizer.
+ * @param indexed Compared to the "TokenString" result from the tokenizer.
+ * @param orig Compared to the "Orig" result from the tokenizer.
+ */
+ private void assertTokenize(String input, Language language, StemMode stemMode, boolean accentDrop,
+ List<String> indexed, List<String> orig) {
+ int i = 0;
+ int j = 0;
+ for (Token token : tokenizer.tokenize(input, language, stemMode, accentDrop)) {
+ // System.err.println("got token orig '"+token.getOrig()+"'");
+ // System.err.println("got token stem '"+token.getTokenString(stemMode)+"'");
+ if (token.getNumComponents() > 0) {
+ for (int comp = 0; comp < token.getNumComponents(); comp++) {
+ Token t = token.getComponent(comp);
+ if (t.getType().isIndexable()) {
+ assertThat("comp index: " + i, toLowerCase(t.getTokenString()), is(indexed.get(i++)));
+ }
+ }
+ } else {
+ if (token.getType().isIndexable()) {
+ assertThat("exp index: " + i, toLowerCase(token.getTokenString()), is(indexed.get(i++)));
+ }
+ }
+ if (orig != null) {
+ assertThat("orig index: " + j, token.getOrig(), is(orig.get(j++)));
+ }
+ }
+ assertThat("indexed length", i, is(indexed.size()));
+ if (orig != null) {
+ assertThat("orig length", j, is(orig.size()));
+ }
+ }
+
+}
diff --git a/linguistics/src/test/java/com/yahoo/language/process/AbstractTokenizerTestCase.java b/linguistics/src/test/java/com/yahoo/language/process/AbstractTokenizerTestCase.java
index 0c2b70b1768..48dcd462573 100644
--- a/linguistics/src/test/java/com/yahoo/language/process/AbstractTokenizerTestCase.java
+++ b/linguistics/src/test/java/com/yahoo/language/process/AbstractTokenizerTestCase.java
@@ -10,7 +10,7 @@ import java.util.List;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public abstract class AbstractTokenizerTestCase {
diff --git a/linguistics/src/test/java/com/yahoo/language/process/ProcessingExceptionTestCase.java b/linguistics/src/test/java/com/yahoo/language/process/ProcessingExceptionTestCase.java
index 08a0f0794cb..e8bbdaeb006 100644
--- a/linguistics/src/test/java/com/yahoo/language/process/ProcessingExceptionTestCase.java
+++ b/linguistics/src/test/java/com/yahoo/language/process/ProcessingExceptionTestCase.java
@@ -7,7 +7,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ProcessingExceptionTestCase {
diff --git a/linguistics/src/test/java/com/yahoo/language/process/SegmenterImplTestCase.java b/linguistics/src/test/java/com/yahoo/language/process/SegmenterImplTestCase.java
index 1fcd7177c44..bbe424b7f14 100644
--- a/linguistics/src/test/java/com/yahoo/language/process/SegmenterImplTestCase.java
+++ b/linguistics/src/test/java/com/yahoo/language/process/SegmenterImplTestCase.java
@@ -12,7 +12,7 @@ import java.util.List;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class SegmenterImplTestCase {
diff --git a/linguistics/src/test/java/com/yahoo/language/process/StemModeTestCase.java b/linguistics/src/test/java/com/yahoo/language/process/StemModeTestCase.java
index 666f78a75b6..6433d0d491a 100644
--- a/linguistics/src/test/java/com/yahoo/language/process/StemModeTestCase.java
+++ b/linguistics/src/test/java/com/yahoo/language/process/StemModeTestCase.java
@@ -6,7 +6,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class StemModeTestCase {
diff --git a/linguistics/src/test/java/com/yahoo/language/process/StemmerImplTestCase.java b/linguistics/src/test/java/com/yahoo/language/process/StemmerImplTestCase.java
index b088441369f..665857514be 100644
--- a/linguistics/src/test/java/com/yahoo/language/process/StemmerImplTestCase.java
+++ b/linguistics/src/test/java/com/yahoo/language/process/StemmerImplTestCase.java
@@ -15,7 +15,7 @@ import java.util.ArrayList;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class StemmerImplTestCase {
diff --git a/linguistics/src/test/java/com/yahoo/language/process/TokenTypeTestCase.java b/linguistics/src/test/java/com/yahoo/language/process/TokenTypeTestCase.java
index 70bd614d7d5..11263ccafe8 100644
--- a/linguistics/src/test/java/com/yahoo/language/process/TokenTypeTestCase.java
+++ b/linguistics/src/test/java/com/yahoo/language/process/TokenTypeTestCase.java
@@ -6,7 +6,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class TokenTypeTestCase {
diff --git a/linguistics/src/test/java/com/yahoo/language/process/TokenizationTestCase.java b/linguistics/src/test/java/com/yahoo/language/process/TokenizationTestCase.java
index e36d90b3206..72dd6f8ce58 100644
--- a/linguistics/src/test/java/com/yahoo/language/process/TokenizationTestCase.java
+++ b/linguistics/src/test/java/com/yahoo/language/process/TokenizationTestCase.java
@@ -64,6 +64,8 @@ public class TokenizationTestCase {
Arrays.asList("on"), null);
assertTokenize("\uFF2F\uFF2E", Language.ENGLISH, StemMode.SHORTEST, false,
Arrays.asList("on"), null);
+
+
}
@Test
diff --git a/linguistics/src/test/java/com/yahoo/language/simple/SimpleDetectorTestCase.java b/linguistics/src/test/java/com/yahoo/language/simple/SimpleDetectorTestCase.java
index 58e9dcb76c3..1905c6d98a9 100644
--- a/linguistics/src/test/java/com/yahoo/language/simple/SimpleDetectorTestCase.java
+++ b/linguistics/src/test/java/com/yahoo/language/simple/SimpleDetectorTestCase.java
@@ -11,7 +11,7 @@ import java.nio.charset.Charset;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SimpleDetectorTestCase {
@@ -50,6 +50,11 @@ public class SimpleDetectorTestCase {
// a string from http://www.columbia.edu/kermit/utf8.html that says "I can eat glass (and it doesn't hurt me)".
assertLanguage(Language.KOREAN, "\ub098\ub294 \uc720\ub9ac\ub97c \uba39\uc744 \uc218 \uc788\uc5b4\uc694. " +
"\uadf8\ub798\ub3c4 \uc544\ud504\uc9c0 \uc54a\uc544\uc694");
+
+ // from https://ru.wikipedia.org/wiki/%D0%A0%D0%BE%D1%81%D1%81%D0%B8%D1%8F
+ assertLanguage(Language.RUSSIAN, "Материал из Википедии — свободной энциклопедии");
+ // https://he.wikipedia.org/wiki/Yahoo!
+ assertLanguage(Language.HEBREW, "אתר יאהו! הוא אחד מאתרי האינטרנט הפופולריים ביותר בעולם, עם מעל 500 מיליון כניסות בכל יום");
}
@Test
diff --git a/linguistics/src/test/java/com/yahoo/language/simple/SimpleNormalizerTestCase.java b/linguistics/src/test/java/com/yahoo/language/simple/SimpleNormalizerTestCase.java
index ae8fefcc15c..488d63f4327 100644
--- a/linguistics/src/test/java/com/yahoo/language/simple/SimpleNormalizerTestCase.java
+++ b/linguistics/src/test/java/com/yahoo/language/simple/SimpleNormalizerTestCase.java
@@ -7,7 +7,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class SimpleNormalizerTestCase {
diff --git a/linguistics/src/test/java/com/yahoo/language/simple/SimpleTokenTestCase.java b/linguistics/src/test/java/com/yahoo/language/simple/SimpleTokenTestCase.java
index 5a0080230d4..563a09c4e52 100644
--- a/linguistics/src/test/java/com/yahoo/language/simple/SimpleTokenTestCase.java
+++ b/linguistics/src/test/java/com/yahoo/language/simple/SimpleTokenTestCase.java
@@ -8,7 +8,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class SimpleTokenTestCase {
diff --git a/linguistics/src/test/java/com/yahoo/language/simple/SimpleTransformerTestCase.java b/linguistics/src/test/java/com/yahoo/language/simple/SimpleTransformerTestCase.java
index f32efce0b94..926736490a6 100644
--- a/linguistics/src/test/java/com/yahoo/language/simple/SimpleTransformerTestCase.java
+++ b/linguistics/src/test/java/com/yahoo/language/simple/SimpleTransformerTestCase.java
@@ -8,7 +8,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class SimpleTransformerTestCase {
diff --git a/logserver/bin/logserver-start.sh b/logserver/bin/logserver-start.sh
index d72a3f10119..b1082ddc6a7 100755
--- a/logserver/bin/logserver-start.sh
+++ b/logserver/bin/logserver-start.sh
@@ -74,9 +74,9 @@ ROOT=${VESPA_HOME%/}
export ROOT
cd $ROOT || { echo "Cannot cd to $ROOT" 1>&2; exit 1; }
-addopts="-server -Xms64m -Xmx256m -XX:MaxDirectMemorySize=76m -XX:MaxJavaStackTraceDepth=-1"
+addopts="-server -Xms64m -Xmx256m -XX:MaxDirectMemorySize=76m -XX:MaxJavaStackTraceDepth=1000000"
-oomopt="-XX:OnOutOfMemoryError=kill -9 %p"
+oomopt="-XX:+ExitOnOutOfMemoryError"
jar="-jar $ROOT/lib/jars/logserver-jar-with-dependencies.jar"
diff --git a/maven-plugins/pom.xml b/maven-plugins/pom.xml
index 0aabe630a63..546dfdafcd9 100644
--- a/maven-plugins/pom.xml
+++ b/maven-plugins/pom.xml
@@ -21,5 +21,7 @@
<module>../bundle-plugin</module>
<module>../configgen</module>
<module>../config-class-plugin</module>
+ <module>../application-deploy-plugin</module>
+ <module>../vespa-application-maven-plugin</module>
</modules>
</project>
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/CallStack.java b/messagebus/src/main/java/com/yahoo/messagebus/CallStack.java
index ce2fc469595..4a280317692 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/CallStack.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/CallStack.java
@@ -10,7 +10,7 @@ import java.util.Stack;
* move the content of itself to another, never to copy, since a callback is unique and might be counted by
* implementations such as Resender.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CallStack {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/ConfigAgent.java b/messagebus/src/main/java/com/yahoo/messagebus/ConfigAgent.java
index 286587e66e0..c88d3f1308d 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/ConfigAgent.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/ConfigAgent.java
@@ -13,7 +13,7 @@ import com.yahoo.messagebus.routing.RoutingTableSpec;
* This class implements subscription to message bus config. To use configuration one must implement the {@link
* ConfigHandler} interface.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class ConfigAgent implements ConfigSubscriber.SingleSubscriber<MessagebusConfig>{
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/ConfigHandler.java b/messagebus/src/main/java/com/yahoo/messagebus/ConfigHandler.java
index 4dd87509e68..4e29d662bd2 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/ConfigHandler.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/ConfigHandler.java
@@ -8,7 +8,7 @@ import com.yahoo.messagebus.routing.RoutingSpec;
* Instead of declaring separate subscribers and handlers for all types of configurations, this pair is intended to hold
* everything. Extend this handler whenever new configs are added to {@link ConfigAgent}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface ConfigHandler {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/DestinationSession.java b/messagebus/src/main/java/com/yahoo/messagebus/DestinationSession.java
index 72322fdee8b..c0b1ee179c6 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/DestinationSession.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/DestinationSession.java
@@ -10,7 +10,7 @@ import java.util.logging.Logger;
* A session supporting receiving and replying to messages. A destination is expected to reply to every message
* received.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class DestinationSession implements MessageHandler {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/DestinationSessionParams.java b/messagebus/src/main/java/com/yahoo/messagebus/DestinationSessionParams.java
index 32cee06c3db..cf022aa52da 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/DestinationSessionParams.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/DestinationSessionParams.java
@@ -5,7 +5,7 @@ package com.yahoo.messagebus;
* To facilitate several configuration parameters to the {@link MessageBus#createDestinationSession(DestinationSessionParams)},
* all parameters are held by this class. This class has reasonable default values for each parameter.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DestinationSessionParams {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/DynamicThrottlePolicy.java b/messagebus/src/main/java/com/yahoo/messagebus/DynamicThrottlePolicy.java
index 9850b93bfdf..41a192a24be 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/DynamicThrottlePolicy.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/DynamicThrottlePolicy.java
@@ -12,7 +12,7 @@ import java.util.logging.Logger;
*
* <b>NOTE:</b> By context, "pending" is refering to the number of sent messages that have not been replied to yet.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DynamicThrottlePolicy extends StaticThrottlePolicy {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/EmptyReply.java b/messagebus/src/main/java/com/yahoo/messagebus/EmptyReply.java
index 1e7b96f1694..38fb4c723b6 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/EmptyReply.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/EmptyReply.java
@@ -8,7 +8,7 @@ import com.yahoo.text.Utf8String;
* generate replies to events that occur within the messagebus, and since the messagebus by design knows nothing about
* the messages that have been implemented by the users it requires a class such as this.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public final class EmptyReply extends Reply {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/Error.java b/messagebus/src/main/java/com/yahoo/messagebus/Error.java
index 1a1ad824b21..4aa85c1ea87 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/Error.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/Error.java
@@ -4,7 +4,7 @@ package com.yahoo.messagebus;
/**
* This class implements the pair (code, message) that is used in Reply to hold errors.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class Error {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/ErrorCode.java b/messagebus/src/main/java/com/yahoo/messagebus/ErrorCode.java
index ee8a4cc2b55..8794bd507a2 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/ErrorCode.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/ErrorCode.java
@@ -4,7 +4,7 @@ package com.yahoo.messagebus;
/**
* This interface contains the reserved error codes that are used for errors that occur within the messagebus.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class ErrorCode {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/IntermediateSession.java b/messagebus/src/main/java/com/yahoo/messagebus/IntermediateSession.java
index 03065d09dbd..fef5f8afa2d 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/IntermediateSession.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/IntermediateSession.java
@@ -10,7 +10,7 @@ import java.util.logging.Logger;
* A session which supports receiving, forwarding and acknowledgement of messages. An intermediate session is expacted
* to either forward or acknowledge every message received.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class IntermediateSession implements MessageHandler, ReplyHandler {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/IntermediateSessionParams.java b/messagebus/src/main/java/com/yahoo/messagebus/IntermediateSessionParams.java
index 98414100bcd..19c6e6b4d7c 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/IntermediateSessionParams.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/IntermediateSessionParams.java
@@ -5,7 +5,7 @@ package com.yahoo.messagebus;
* To facilitate several configuration parameters to the {@link MessageBus#createIntermediateSession(IntermediateSessionParams)},
* all parameters are held by this class. This class has reasonable default values for each parameter.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class IntermediateSessionParams {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/Message.java b/messagebus/src/main/java/com/yahoo/messagebus/Message.java
index 54b4e0ece6a..22496487f61 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/Message.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/Message.java
@@ -9,7 +9,7 @@ import com.yahoo.messagebus.routing.Route;
* contains a retry counter that holds what retry the message is currently on. See the method comment {@link #getRetry}
* for more information.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class Message extends Routable {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/MessageBus.java b/messagebus/src/main/java/com/yahoo/messagebus/MessageBus.java
index e1540740621..26e61e8917b 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/MessageBus.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/MessageBus.java
@@ -51,7 +51,7 @@ import java.util.logging.Logger;
* </ul>
*
* @author bratseth
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class MessageBus implements ConfigHandler, NetworkOwner, MessageHandler, ReplyHandler {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/MessageBusParams.java b/messagebus/src/main/java/com/yahoo/messagebus/MessageBusParams.java
index b1f5bdcf4de..06b2af7ea92 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/MessageBusParams.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/MessageBusParams.java
@@ -11,7 +11,7 @@ import java.util.List;
* To facilitate several configuration parameters to the {@link MessageBus} constructor, all parameters are held by this
* class. This class has reasonable default values for each parameter.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MessageBusParams {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/MessageHandler.java b/messagebus/src/main/java/com/yahoo/messagebus/MessageHandler.java
index 4ac355491a2..3f18e36b076 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/MessageHandler.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/MessageHandler.java
@@ -6,7 +6,7 @@ package com.yahoo.messagebus;
* As opposed to the {@link ReplyHandler} which handles replies as they return from the receiver to the sender, this
* interface is intended for handling messages as they travel from the sender to the receiver.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface MessageHandler {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/Messenger.java b/messagebus/src/main/java/com/yahoo/messagebus/Messenger.java
index fdfff98d182..6ef5e9adca7 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/Messenger.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/Messenger.java
@@ -16,7 +16,7 @@ import java.util.logging.Logger;
* tasks. Tasks are enqueued using the synchronized {@link #enqueue(Task)}
* method, and are run in the order they were enqueued.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Messenger implements Runnable {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/Protocol.java b/messagebus/src/main/java/com/yahoo/messagebus/Protocol.java
index 7e9093ec643..11b135cac62 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/Protocol.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/Protocol.java
@@ -9,7 +9,7 @@ import com.yahoo.messagebus.routing.RoutingPolicy;
* Interface implemented by the concrete application message protocol.
*
* @author bratseth
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface Protocol {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/ProtocolRepository.java b/messagebus/src/main/java/com/yahoo/messagebus/ProtocolRepository.java
index 84aa0c20008..ad26475bf74 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/ProtocolRepository.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/ProtocolRepository.java
@@ -12,7 +12,7 @@ import java.util.logging.Logger;
* Implements a thread-safe repository for protocols and their routing policies. This manages an internal cache of
* routing policies so that similarly referenced policy directives share the same instance of a policy.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ProtocolRepository {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/RPCMessageBus.java b/messagebus/src/main/java/com/yahoo/messagebus/RPCMessageBus.java
index 1db532b52c9..4bbe88ffc2c 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/RPCMessageBus.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/RPCMessageBus.java
@@ -16,7 +16,7 @@ import java.util.logging.Logger;
* The RPCMessageBus class wraps a MessageBus with an RPCNetwork and handles reconfiguration. Please note that according
* to the object shutdown order, you must shut down all sessions before shutting down this object.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RPCMessageBus extends NetworkMessageBus {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/Reply.java b/messagebus/src/main/java/com/yahoo/messagebus/Reply.java
index cf4eeda2d51..445f1c966d8 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/Reply.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/Reply.java
@@ -12,7 +12,7 @@ import java.util.stream.Stream;
* corresponding message. There are no error-replies defined, as errors can instead piggyback any reply by the {@link
* #errors} member variable.</p>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public abstract class Reply extends Routable {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/ReplyHandler.java b/messagebus/src/main/java/com/yahoo/messagebus/ReplyHandler.java
index 61e0d82126a..e37983799fb 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/ReplyHandler.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/ReplyHandler.java
@@ -6,7 +6,7 @@ package com.yahoo.messagebus;
* opposed to the {@link MessageHandler} which handles messages as they travel from the sender to the receiver, this
* interface is intended for handling replies as they return from the receiver to the sender.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface ReplyHandler {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/Routable.java b/messagebus/src/main/java/com/yahoo/messagebus/Routable.java
index d135f26bbe1..b797c103e67 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/Routable.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/Routable.java
@@ -14,7 +14,7 @@ import com.yahoo.text.Utf8String;
* invoking the {@link #swapState(Routable)} method. That method is used to transfer the state from a message to the
* corresponding reply, or to a different message if the application decides to replace it.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public abstract class Routable {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/SendProxy.java b/messagebus/src/main/java/com/yahoo/messagebus/SendProxy.java
index d17a290c101..ecc6bfebc41 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/SendProxy.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/SendProxy.java
@@ -14,7 +14,7 @@ import java.util.logging.Logger;
* This class owns a message that is being sent by message bus. Once a reply is received, the message is attached to it
* and returned to the application. This also implements the discard policy of {@link RoutingNode}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SendProxy implements MessageHandler, ReplyHandler {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/Sequencer.java b/messagebus/src/main/java/com/yahoo/messagebus/Sequencer.java
index 452b9ecc046..6a99614fa4e 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/Sequencer.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/Sequencer.java
@@ -13,7 +13,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
* id, and messages are only sent when they are at the front of their list. When a reply arrives, the current front of
* the list is removed and the next message, if any, is sent.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Sequencer implements MessageHandler, ReplyHandler {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/SourceSession.java b/messagebus/src/main/java/com/yahoo/messagebus/SourceSession.java
index 2c49e733ed3..3bfdfaa1f4a 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/SourceSession.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/SourceSession.java
@@ -15,7 +15,7 @@ import java.util.logging.Logger;
/**
* <p>A session supporting sending new messages.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class SourceSession implements ReplyHandler, MessageBus.SendBlockedMessages {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/SourceSessionParams.java b/messagebus/src/main/java/com/yahoo/messagebus/SourceSessionParams.java
index 01a67175834..e671a36fa26 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/SourceSessionParams.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/SourceSessionParams.java
@@ -6,7 +6,7 @@ package com.yahoo.messagebus;
* SourceSessionParams)}, all parameters are held by this class. This class has reasonable default values for each
* parameter.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SourceSessionParams {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/StaticThrottlePolicy.java b/messagebus/src/main/java/com/yahoo/messagebus/StaticThrottlePolicy.java
index ef3c8d73aed..7d73cd4bf3f 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/StaticThrottlePolicy.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/StaticThrottlePolicy.java
@@ -9,7 +9,7 @@ package com.yahoo.messagebus;
*
* <b>NOTE:</b> By context, "pending" is refering to the number of sent messages that have not been replied to yet.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class StaticThrottlePolicy implements ThrottlePolicy {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/ThrottlePolicy.java b/messagebus/src/main/java/com/yahoo/messagebus/ThrottlePolicy.java
index 19c53e8feff..30a0b82f2cd 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/ThrottlePolicy.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/ThrottlePolicy.java
@@ -7,7 +7,7 @@ package com.yahoo.messagebus;
* All messages accepted are passed through the {@link #processMessage(Message)} method, and the corresponding replies
* are passed through the {@link #processReply(Reply)} method.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface ThrottlePolicy {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/Trace.java b/messagebus/src/main/java/com/yahoo/messagebus/Trace.java
index d5f3883e9ab..85b066318ff 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/Trace.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/Trace.java
@@ -11,7 +11,7 @@ import java.util.Date;
* has the ability to trace information will have a predefined level attached to that information. If the level on the
* information is lower or equal to the level set in the Trace object, the information will be traced.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Trace {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/TraceLevel.java b/messagebus/src/main/java/com/yahoo/messagebus/TraceLevel.java
index 03821d7520b..719e1ed571c 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/TraceLevel.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/TraceLevel.java
@@ -4,7 +4,7 @@ package com.yahoo.messagebus;
/**
* This class defines the {@link Trace} levels used by message bus.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public final class TraceLevel {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/TraceNode.java b/messagebus/src/main/java/com/yahoo/messagebus/TraceNode.java
index 7ef89b28110..7b40b683227 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/TraceNode.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/TraceNode.java
@@ -17,7 +17,7 @@ import java.util.logging.Logger;
* The most important feature to notice is the {@link #normalize()} method that will compact, sort and 'rootify' the
* trace tree so that trees become well-formed (and can be compared for equality).
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class TraceNode implements Comparable<TraceNode> {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/network/Identity.java b/messagebus/src/main/java/com/yahoo/messagebus/network/Identity.java
index 91ef352b944..5fafc139c70 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/network/Identity.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/network/Identity.java
@@ -8,7 +8,7 @@ import com.yahoo.net.HostName;
* contains a servicePrefix identifier, which is the configuration id of the current servicePrefix, and the canonical
* host name of the host running this.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Identity {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/network/ServiceAddress.java b/messagebus/src/main/java/com/yahoo/messagebus/network/ServiceAddress.java
index 77e19ca16b9..a37d948f3b6 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/network/ServiceAddress.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/network/ServiceAddress.java
@@ -5,7 +5,7 @@ package com.yahoo.messagebus.network;
* This interface represents an abstract network service; i.e. somewhere to send messages. An instance of this is
* retrieved by calling {@link Network#allocServiceAddress(com.yahoo.messagebus.routing.RoutingNode)}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface ServiceAddress {
// empty
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/network/local/LocalServiceAddress.java b/messagebus/src/main/java/com/yahoo/messagebus/network/local/LocalServiceAddress.java
index f7ece1cc900..b99fdfaecd3 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/network/local/LocalServiceAddress.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/network/local/LocalServiceAddress.java
@@ -4,7 +4,7 @@ package com.yahoo.messagebus.network.local;
import com.yahoo.messagebus.network.ServiceAddress;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class LocalServiceAddress implements ServiceAddress {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCNetworkParams.java b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCNetworkParams.java
index 0d4cee5a939..e50670530d3 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCNetworkParams.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCNetworkParams.java
@@ -8,7 +8,7 @@ import com.yahoo.cloud.config.SlobroksConfig;
* To facilitate several configuration parameters to the {@link RPCNetwork} constructor, all parameters are held by this
* class. This class has reasonable default values for each parameter.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RPCNetworkParams {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCSendAdapter.java b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCSendAdapter.java
index 01a2b14da7f..dbdb6da6477 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCSendAdapter.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCSendAdapter.java
@@ -8,7 +8,7 @@ import com.yahoo.messagebus.routing.RoutingNode;
* This interface defines the necessary methods to process incoming and send outgoing RPC requests. The {@link
* RPCNetwork} maintains a list of supported RPC signatures, and dispatches requests to the corresponding adapter.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface RPCSendAdapter {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCSendV1.java b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCSendV1.java
index 480a716e382..952bcdcfe04 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCSendV1.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCSendV1.java
@@ -25,7 +25,7 @@ import com.yahoo.text.Utf8Array;
/**
* Implements the request adapter for method "mbus.send1".
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RPCSendV1 extends RPCSend {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCServicePool.java b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCServicePool.java
index 6758e1426db..abd33d6c9c2 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCServicePool.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCServicePool.java
@@ -7,7 +7,7 @@ import java.util.Map;
/**
* Class used to reuse services for the same address when sending messages over the rpc network.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RPCServicePool {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCTarget.java b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCTarget.java
index 71cf906848d..6179097e5d9 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCTarget.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCTarget.java
@@ -15,7 +15,7 @@ import java.util.logging.Logger;
* target. Instances of this class are returned by {@link RPCService}, and
* cached by {@link RPCTargetPool}.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RPCTarget implements RequestWaiter {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/SlobrokConfigSubscriber.java b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/SlobrokConfigSubscriber.java
index e4be7cad963..7f4c27a45f9 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/SlobrokConfigSubscriber.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/SlobrokConfigSubscriber.java
@@ -8,7 +8,7 @@ import com.yahoo.cloud.config.SlobroksConfig;
/**
* This class implements subscription to slobrok config.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SlobrokConfigSubscriber implements ConfigSubscriber.SingleSubscriber<SlobroksConfig>{
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/test/SlobrokState.java b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/test/SlobrokState.java
index f3936af4694..1c5c6c09a51 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/test/SlobrokState.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/test/SlobrokState.java
@@ -6,7 +6,7 @@ import java.util.Map;
import java.util.Set;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SlobrokState {
private Map<String, Integer> data = new LinkedHashMap<String, Integer>();
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/routing/ApplicationSpec.java b/messagebus/src/main/java/com/yahoo/messagebus/routing/ApplicationSpec.java
index 6c859c7bf5d..2936c9c22ee 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/ApplicationSpec.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/ApplicationSpec.java
@@ -10,7 +10,7 @@ import java.util.regex.Pattern;
* This class holds the specifications of an application running message bus services. It is used for ensuring that a
* {@link RoutingSpec} holds valid routing specifications.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ApplicationSpec {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/routing/ErrorDirective.java b/messagebus/src/main/java/com/yahoo/messagebus/routing/ErrorDirective.java
index 8ee57d2b16a..6c6a0fa9b07 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/ErrorDirective.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/ErrorDirective.java
@@ -5,7 +5,7 @@ package com.yahoo.messagebus.routing;
* This class represents an error directive within a {@link Hop}'s selector. This means to stop whatever is being
* resolved, and instead return a reply containing a specified error.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ErrorDirective implements HopDirective {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/routing/HopBlueprint.java b/messagebus/src/main/java/com/yahoo/messagebus/routing/HopBlueprint.java
index d8dcac95a29..62cddba791d 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/HopBlueprint.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/HopBlueprint.java
@@ -8,7 +8,7 @@ import java.util.*;
* are stored in a {@link RoutingTable}.
*
* @author bratseth
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class HopBlueprint {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/routing/HopDirective.java b/messagebus/src/main/java/com/yahoo/messagebus/routing/HopDirective.java
index 5247e1753f9..809b2da69c4 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/HopDirective.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/HopDirective.java
@@ -4,7 +4,7 @@ package com.yahoo.messagebus.routing;
/**
* This class is the base class for the primitives that make up a {@link Hop}'s selector.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface HopDirective {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/routing/HopSpec.java b/messagebus/src/main/java/com/yahoo/messagebus/routing/HopSpec.java
index a981f8f8980..fb51ddbf667 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/HopSpec.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/HopSpec.java
@@ -11,7 +11,7 @@ import java.util.List;
* <p>
* This class contains the spec for a single hop.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class HopSpec {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/routing/PolicyDirective.java b/messagebus/src/main/java/com/yahoo/messagebus/routing/PolicyDirective.java
index 0b4787170dc..bf793238b3b 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/PolicyDirective.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/PolicyDirective.java
@@ -5,7 +5,7 @@ package com.yahoo.messagebus.routing;
* This class represents a policy directive within a {@link Hop}'s selector. This means to create the named protocol
* using the given parameter string, and the running that protocol within the context of this directive.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class PolicyDirective implements HopDirective {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/routing/Resender.java b/messagebus/src/main/java/com/yahoo/messagebus/routing/Resender.java
index cf521759026..2813498babc 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/Resender.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/Resender.java
@@ -13,7 +13,7 @@ import java.util.List;
* is owned by {@link com.yahoo.messagebus.MessageBus}. Because this class does not have any internal thread, it depends
* on message bus to keep polling it whenever it has time.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Resender {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/routing/RetryPolicy.java b/messagebus/src/main/java/com/yahoo/messagebus/routing/RetryPolicy.java
index d02fb4e97c7..76ed29dc1a0 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/RetryPolicy.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/RetryPolicy.java
@@ -7,7 +7,7 @@ package com.yahoo.messagebus.routing;
* com.yahoo.messagebus.Message}. The policy is passed to the message bus at creation time using the {@link
* com.yahoo.messagebus.MessageBusParams#setRetryPolicy(RetryPolicy)} method.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface RetryPolicy {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/routing/RetryTransientErrorsPolicy.java b/messagebus/src/main/java/com/yahoo/messagebus/routing/RetryTransientErrorsPolicy.java
index cb01d5f0633..2623d2a6e0b 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/RetryTransientErrorsPolicy.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/RetryTransientErrorsPolicy.java
@@ -7,7 +7,7 @@ import com.yahoo.messagebus.ErrorCode;
* Implements a retry policy that allows resending of any error that is not fatal. It also does progressive back-off,
* delaying each attempt by the given time multiplied by the retry attempt.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RetryTransientErrorsPolicy implements RetryPolicy {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/routing/Route.java b/messagebus/src/main/java/com/yahoo/messagebus/routing/Route.java
index bb729bfcf3a..a07c6e16100 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/Route.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/Route.java
@@ -16,7 +16,7 @@ import java.util.List;
* you may build one programatically through the hop accessors.</p>
*
* @author bratseth
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Route {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/routing/RouteDirective.java b/messagebus/src/main/java/com/yahoo/messagebus/routing/RouteDirective.java
index 11115b1aeaa..6725e11d77b 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/RouteDirective.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/RouteDirective.java
@@ -5,7 +5,7 @@ package com.yahoo.messagebus.routing;
* This class represents a route directive within a {@link Hop}'s selector. This will be replaced by the named route
* when evaluated. If the route is not present in the running protocol's routing table, routing will fail.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RouteDirective implements HopDirective {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/routing/RouteSpec.java b/messagebus/src/main/java/com/yahoo/messagebus/routing/RouteSpec.java
index d1e02874363..b6bdb92b578 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/RouteSpec.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/RouteSpec.java
@@ -11,7 +11,7 @@ import java.util.List;
* <p>
* This class contains the spec for a single route.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RouteSpec {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingContext.java b/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingContext.java
index 272420016f5..30965ebc28b 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingContext.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingContext.java
@@ -13,7 +13,7 @@ import java.util.*;
* a policy is expected to need. An instance of this is created for every {@link
* RoutingNode} that contains a policy.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RoutingContext {
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 341e3613b76..0dcc1dd67fd 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingNode.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingNode.java
@@ -17,7 +17,7 @@ import java.util.concurrent.atomic.AtomicInteger;
* modification of the route. For every {@link RoutingPolicy} there will be an instance of this that has its policy and
* {@link RoutingContext} member set. A policy is oblivious to this class, it can only access the context object.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RoutingNode implements ReplyHandler {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingNodeIterator.java b/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingNodeIterator.java
index fb09a234646..66085d36ec7 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingNodeIterator.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingNodeIterator.java
@@ -10,7 +10,7 @@ import java.util.List;
* Implements an iterator for routing nodes. Use {@link RoutingContext#getChildIterator()} to retrieve an instance of
* this.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RoutingNodeIterator {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingPolicy.java b/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingPolicy.java
index 53848920d95..6f65f73cf65 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingPolicy.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingPolicy.java
@@ -18,7 +18,7 @@ package com.yahoo.messagebus.routing;
* This class is pluggable per template point in the address of a hop.
*
* @author bratseth
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public interface RoutingPolicy {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingSpec.java b/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingSpec.java
index de2ba2a18dc..032f13a008a 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingSpec.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingSpec.java
@@ -13,7 +13,7 @@ import java.util.Map;
* <p>
* This class is the root spec class for configuring message bus routing.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RoutingSpec {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingTable.java b/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingTable.java
index fd9e9e9257a..82f409dbb44 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingTable.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingTable.java
@@ -9,7 +9,7 @@ import java.util.Map;
* At any time there may only ever be zero or one routing table registered in message bus for each protocol. This class
* contains a list of named hops and routes that may be used to substitute references to these during route resolving.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RoutingTable {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingTableSpec.java b/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingTableSpec.java
index 5054520f8e4..ef93d2a4454 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingTableSpec.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingTableSpec.java
@@ -12,7 +12,7 @@ import java.util.*;
* <p>
* This class contains the spec for a single routing table, which corresponds to exactly one protocol.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RoutingTableSpec {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/routing/TcpDirective.java b/messagebus/src/main/java/com/yahoo/messagebus/routing/TcpDirective.java
index 67b060d515a..72f11fbd049 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/TcpDirective.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/TcpDirective.java
@@ -5,7 +5,7 @@ package com.yahoo.messagebus.routing;
* This class represents a tcp directive within a {@link Hop}'s selector. This is a connection string used to establish
* a direct connection to a host, bypassing service lookups through Slobrok.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class TcpDirective implements HopDirective {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/routing/VerbatimDirective.java b/messagebus/src/main/java/com/yahoo/messagebus/routing/VerbatimDirective.java
index 666fa9ad102..dfd2333dc4e 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/VerbatimDirective.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/VerbatimDirective.java
@@ -5,7 +5,7 @@ package com.yahoo.messagebus.routing;
* This class represents a verbatim match within a {@link Hop}'s selector. This is nothing more than a string that will
* be used as-is when performing service name lookups.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class VerbatimDirective implements HopDirective {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/routing/test/CustomPolicy.java b/messagebus/src/main/java/com/yahoo/messagebus/routing/test/CustomPolicy.java
index 7aab6362717..15168821207 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/test/CustomPolicy.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/test/CustomPolicy.java
@@ -12,7 +12,7 @@ import java.util.ArrayList;
import java.util.List;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class CustomPolicy implements RoutingPolicy {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/routing/test/CustomPolicyFactory.java b/messagebus/src/main/java/com/yahoo/messagebus/routing/test/CustomPolicyFactory.java
index 6ab897cbbc4..6509887ddfb 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/routing/test/CustomPolicyFactory.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/routing/test/CustomPolicyFactory.java
@@ -10,7 +10,7 @@ import java.util.Arrays;
import java.util.List;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CustomPolicyFactory implements SimpleProtocol.PolicyFactory {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/test/SimpleProtocol.java b/messagebus/src/main/java/com/yahoo/messagebus/test/SimpleProtocol.java
index e922c37d916..c4673685767 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/test/SimpleProtocol.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/test/SimpleProtocol.java
@@ -19,7 +19,7 @@ import java.util.List;
import java.util.Map;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SimpleProtocol implements Protocol {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/ChokeTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/ChokeTestCase.java
index 585ce2913c0..d2726627e39 100755
--- a/messagebus/src/test/java/com/yahoo/messagebus/ChokeTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/ChokeTestCase.java
@@ -23,7 +23,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertFalse;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class ChokeTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/ConfigAgentTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/ConfigAgentTestCase.java
index 8e5592f0182..3fbc5d25cd9 100755
--- a/messagebus/src/test/java/com/yahoo/messagebus/ConfigAgentTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/ConfigAgentTestCase.java
@@ -20,7 +20,7 @@ import java.util.concurrent.TimeUnit;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ConfigAgentTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/ErrorTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/ErrorTestCase.java
index 89c641808f5..805f743536e 100755
--- a/messagebus/src/test/java/com/yahoo/messagebus/ErrorTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/ErrorTestCase.java
@@ -14,7 +14,7 @@ import java.util.Arrays;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ErrorTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/MessengerTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/MessengerTestCase.java
index 8315f5d83ea..9c9a08e9200 100644
--- a/messagebus/src/test/java/com/yahoo/messagebus/MessengerTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/MessengerTestCase.java
@@ -9,7 +9,7 @@ import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MessengerTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/ProtocolRepositoryTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/ProtocolRepositoryTestCase.java
index d1ed44403fe..98877d14e50 100644
--- a/messagebus/src/test/java/com/yahoo/messagebus/ProtocolRepositoryTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/ProtocolRepositoryTestCase.java
@@ -9,7 +9,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ProtocolRepositoryTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/SendProxyTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/SendProxyTestCase.java
index 3e6d95c6c54..b5e39035bfb 100644
--- a/messagebus/src/test/java/com/yahoo/messagebus/SendProxyTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/SendProxyTestCase.java
@@ -27,7 +27,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class SendProxyTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/SequencerTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/SequencerTestCase.java
index 562243b8b79..b15b65cb816 100644
--- a/messagebus/src/test/java/com/yahoo/messagebus/SequencerTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/SequencerTestCase.java
@@ -12,7 +12,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class SequencerTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/SimpleTripTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/SimpleTripTestCase.java
index b0d61268e76..96445945d82 100755
--- a/messagebus/src/test/java/com/yahoo/messagebus/SimpleTripTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/SimpleTripTestCase.java
@@ -18,7 +18,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class SimpleTripTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/ThrottlerTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/ThrottlerTestCase.java
index 4e3520f2419..432524ea2a9 100644
--- a/messagebus/src/test/java/com/yahoo/messagebus/ThrottlerTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/ThrottlerTestCase.java
@@ -22,7 +22,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class ThrottlerTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/TimeoutTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/TimeoutTestCase.java
index 0b5d4fcf38f..08cecdb481a 100755
--- a/messagebus/src/test/java/com/yahoo/messagebus/TimeoutTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/TimeoutTestCase.java
@@ -20,7 +20,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class TimeoutTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/TraceTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/TraceTestCase.java
index f0a8a127492..3914edd22e2 100755
--- a/messagebus/src/test/java/com/yahoo/messagebus/TraceTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/TraceTestCase.java
@@ -7,7 +7,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class TraceTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/TraceTripTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/TraceTripTestCase.java
index 1f16e9bb5af..91914051f56 100755
--- a/messagebus/src/test/java/com/yahoo/messagebus/TraceTripTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/TraceTripTestCase.java
@@ -18,7 +18,7 @@ import java.util.Arrays;
import static org.junit.Assert.assertTrue;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class TraceTripTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/network/IdentityTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/network/IdentityTestCase.java
index df824ec7b42..d35ff62ba41 100644
--- a/messagebus/src/test/java/com/yahoo/messagebus/network/IdentityTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/network/IdentityTestCase.java
@@ -7,7 +7,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class IdentityTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/network/local/LocalNetworkTest.java b/messagebus/src/test/java/com/yahoo/messagebus/network/local/LocalNetworkTest.java
index 163f859d68c..e2706a66671 100644
--- a/messagebus/src/test/java/com/yahoo/messagebus/network/local/LocalNetworkTest.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/network/local/LocalNetworkTest.java
@@ -23,7 +23,7 @@ import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class LocalNetworkTest {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/RPCNetworkTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/RPCNetworkTestCase.java
index ddb2260cb8a..01e0fbf34b6 100644
--- a/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/RPCNetworkTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/RPCNetworkTestCase.java
@@ -18,7 +18,7 @@ import java.io.StringWriter;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class RPCNetworkTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/SendAdapterTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/SendAdapterTestCase.java
index c8185aaeaf4..98e821d7d38 100755
--- a/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/SendAdapterTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/SendAdapterTestCase.java
@@ -23,7 +23,7 @@ import java.util.List;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SendAdapterTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/ServiceAddressTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/ServiceAddressTestCase.java
index 82a03d06c6c..476d85f59e9 100755
--- a/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/ServiceAddressTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/ServiceAddressTestCase.java
@@ -18,7 +18,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class ServiceAddressTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/ServicePoolTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/ServicePoolTestCase.java
index 9bc9ef41af9..5904c990278 100644
--- a/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/ServicePoolTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/ServicePoolTestCase.java
@@ -10,7 +10,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class ServicePoolTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/TargetPoolTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/TargetPoolTestCase.java
index 626cb7031d8..a75a765d5a8 100755
--- a/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/TargetPoolTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/TargetPoolTestCase.java
@@ -19,7 +19,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class TargetPoolTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/routing/AdvancedRoutingTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/routing/AdvancedRoutingTestCase.java
index bd90107f52e..e96f414af99 100755
--- a/messagebus/src/test/java/com/yahoo/messagebus/routing/AdvancedRoutingTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/routing/AdvancedRoutingTestCase.java
@@ -32,7 +32,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class AdvancedRoutingTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/routing/ResenderTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/routing/ResenderTestCase.java
index fcf3923c940..1e3764d8e4e 100755
--- a/messagebus/src/test/java/com/yahoo/messagebus/routing/ResenderTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/routing/ResenderTestCase.java
@@ -30,7 +30,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class ResenderTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/routing/RetryPolicyTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/routing/RetryPolicyTestCase.java
index 41cd0897f9e..95a6eabf6f1 100644
--- a/messagebus/src/test/java/com/yahoo/messagebus/routing/RetryPolicyTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/routing/RetryPolicyTestCase.java
@@ -9,7 +9,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class RetryPolicyTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/routing/RouteParserTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/routing/RouteParserTestCase.java
index c2e4aee1bd8..e3f43b9491d 100755
--- a/messagebus/src/test/java/com/yahoo/messagebus/routing/RouteParserTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/routing/RouteParserTestCase.java
@@ -8,7 +8,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class RouteParserTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/routing/RoutingContextTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/routing/RoutingContextTestCase.java
index 9c980ad0d51..fd3cea2378f 100755
--- a/messagebus/src/test/java/com/yahoo/messagebus/routing/RoutingContextTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/routing/RoutingContextTestCase.java
@@ -24,7 +24,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class RoutingContextTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/routing/RoutingSpecTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/routing/RoutingSpecTestCase.java
index e7ea6346999..ac60c84638e 100755
--- a/messagebus/src/test/java/com/yahoo/messagebus/routing/RoutingSpecTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/routing/RoutingSpecTestCase.java
@@ -15,7 +15,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class RoutingSpecTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/routing/RoutingTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/routing/RoutingTestCase.java
index 03906e7e352..0ac962ad947 100644
--- a/messagebus/src/test/java/com/yahoo/messagebus/routing/RoutingTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/routing/RoutingTestCase.java
@@ -27,7 +27,7 @@ import static org.junit.Assert.*;
/**
* @author <a href="mailto:havardpe@yahoo-inc.com">Haavard Pettersen</a>
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RoutingTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/test/QueueAdapterTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/test/QueueAdapterTestCase.java
index 7cd4e4bc858..039499feeca 100644
--- a/messagebus/src/test/java/com/yahoo/messagebus/test/QueueAdapterTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/test/QueueAdapterTestCase.java
@@ -11,7 +11,7 @@ import java.util.concurrent.atomic.AtomicReference;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class QueueAdapterTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/test/ReceptorTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/test/ReceptorTestCase.java
index e35b7783917..aa037f3de8f 100644
--- a/messagebus/src/test/java/com/yahoo/messagebus/test/ReceptorTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/test/ReceptorTestCase.java
@@ -12,7 +12,7 @@ import java.util.concurrent.TimeUnit;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ReceptorTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/test/SimpleMessageTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/test/SimpleMessageTestCase.java
index 8d3fdc9dfdb..3d810ab7e72 100644
--- a/messagebus/src/test/java/com/yahoo/messagebus/test/SimpleMessageTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/test/SimpleMessageTestCase.java
@@ -8,7 +8,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class SimpleMessageTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/test/SimpleProtocolTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/test/SimpleProtocolTestCase.java
index 21f3b230b1e..898d5a36431 100644
--- a/messagebus/src/test/java/com/yahoo/messagebus/test/SimpleProtocolTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/test/SimpleProtocolTestCase.java
@@ -9,7 +9,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class SimpleProtocolTestCase {
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/test/SimpleReplyTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/test/SimpleReplyTestCase.java
index c2174016fb6..160a0c33685 100644
--- a/messagebus/src/test/java/com/yahoo/messagebus/test/SimpleReplyTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/test/SimpleReplyTestCase.java
@@ -6,7 +6,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class SimpleReplyTestCase {
diff --git a/messagebus/src/vespa/messagebus/destinationsessionparams.h b/messagebus/src/vespa/messagebus/destinationsessionparams.h
index 826a20d2fda..d5acb1e8acb 100644
--- a/messagebus/src/vespa/messagebus/destinationsessionparams.h
+++ b/messagebus/src/vespa/messagebus/destinationsessionparams.h
@@ -11,7 +11,7 @@ namespace mbus {
* DestinationSessionParams)}, all parameters are held by this class. This class has reasonable default values for each
* parameter.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class DestinationSessionParams {
diff --git a/messagebus/src/vespa/messagebus/intermediatesessionparams.h b/messagebus/src/vespa/messagebus/intermediatesessionparams.h
index b5a43b2dc24..e9415ea31c9 100644
--- a/messagebus/src/vespa/messagebus/intermediatesessionparams.h
+++ b/messagebus/src/vespa/messagebus/intermediatesessionparams.h
@@ -12,7 +12,7 @@ namespace mbus {
* ReplyHandler, IntermediateSessionParams)}, all parameters are held by this class. This class has reasonable default
* values for each parameter.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class IntermediateSessionParams {
diff --git a/messagebus/src/vespa/messagebus/messagebusparams.h b/messagebus/src/vespa/messagebus/messagebusparams.h
index be7e4a72b83..077301fe422 100644
--- a/messagebus/src/vespa/messagebus/messagebusparams.h
+++ b/messagebus/src/vespa/messagebus/messagebusparams.h
@@ -14,7 +14,7 @@ class MessageBus;
* To facilitate several configuration parameters to the {@link MessageBus} constructor, all parameters are held by this
* class. This class has reasonable default values for each parameter.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class MessageBusParams {
diff --git a/messagebus/src/vespa/messagebus/network/iserviceaddress.h b/messagebus/src/vespa/messagebus/network/iserviceaddress.h
index c676887ace8..b579e71a52a 100644
--- a/messagebus/src/vespa/messagebus/network/iserviceaddress.h
+++ b/messagebus/src/vespa/messagebus/network/iserviceaddress.h
@@ -9,7 +9,7 @@ namespace mbus {
* This interface represents an abstract network service; i.e. somewhere to send messages. An instance of this is
* retrieved by calling {@link Network#lookup(String)}.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class IServiceAddress {
diff --git a/messagebus/src/vespa/messagebus/network/rpcnetwork.cpp b/messagebus/src/vespa/messagebus/network/rpcnetwork.cpp
index f3e3359fabe..108b94070bf 100644
--- a/messagebus/src/vespa/messagebus/network/rpcnetwork.cpp
+++ b/messagebus/src/vespa/messagebus/network/rpcnetwork.cpp
@@ -120,11 +120,13 @@ RPCNetwork::RPCNetwork(const RPCNetworkParams &params) :
_mirror(std::make_unique<slobrok::api::MirrorAPI>(*_orb, *_slobrokCfgFactory)),
_regAPI(std::make_unique<slobrok::api::RegisterAPI>(*_orb, *_slobrokCfgFactory)),
_requestedPort(params.getListenPort()),
- _executor(std::make_unique<vespalib::ThreadStackExecutor>(4,65536)),
+ _executor(std::make_unique<vespalib::ThreadStackExecutor>(params.getNumThreads(), 65536)),
_sendV1(std::make_unique<RPCSendV1>()),
_sendV2(std::make_unique<RPCSendV2>()),
_sendAdapters(),
- _compressionConfig(params.getCompressionConfig())
+ _compressionConfig(params.getCompressionConfig()),
+ _allowDispatchForEncode(params.getDispatchOnEncode()),
+ _allowDispatchForDecode(params.getDispatchOnDecode())
{
_transport->SetDirectWrite(false);
_transport->SetMaxInputBufferSize(params.getMaxInputBufferSize());
@@ -224,11 +226,6 @@ RPCNetwork::start()
return true;
}
-vespalib::Executor &
-RPCNetwork::getExecutor() {
- return *_executor;
-}
-
bool
RPCNetwork::waitUntilReady(double seconds) const
{
diff --git a/messagebus/src/vespa/messagebus/network/rpcnetwork.h b/messagebus/src/vespa/messagebus/network/rpcnetwork.h
index e29d01c8b04..9c6516eced7 100644
--- a/messagebus/src/vespa/messagebus/network/rpcnetwork.h
+++ b/messagebus/src/vespa/messagebus/network/rpcnetwork.h
@@ -77,6 +77,9 @@ private:
std::unique_ptr<RPCSendAdapter> _sendV2;
SendAdapterMap _sendAdapters;
CompressionConfig _compressionConfig;
+ bool _allowDispatchForEncode;
+ bool _allowDispatchForDecode;
+
/**
* Resolves and assigns a service address for the given recipient using the
@@ -135,7 +138,7 @@ public:
/**
* Destruct
**/
- virtual ~RPCNetwork();
+ ~RPCNetwork() override;
/**
* Obtain the owner of this network. This method may only be invoked after
@@ -226,7 +229,10 @@ public:
const slobrok::api::IMirrorAPI &getMirror() const override;
CompressionConfig getCompressionConfig() { return _compressionConfig; }
void invoke(FRT_RPCRequest *req);
- vespalib::Executor & getExecutor();
+ vespalib::Executor & getExecutor() const { return *_executor; }
+ bool allowDispatchForEncode() const { return _allowDispatchForEncode; }
+ bool allowDispatchForDecode() const { return _allowDispatchForDecode; }
+
};
} // namespace mbus
diff --git a/messagebus/src/vespa/messagebus/network/rpcnetworkparams.cpp b/messagebus/src/vespa/messagebus/network/rpcnetworkparams.cpp
index 5e54de1bce6..b6f0231e619 100644
--- a/messagebus/src/vespa/messagebus/network/rpcnetworkparams.cpp
+++ b/messagebus/src/vespa/messagebus/network/rpcnetworkparams.cpp
@@ -10,6 +10,9 @@ RPCNetworkParams::RPCNetworkParams() :
_listenPort(0),
_maxInputBufferSize(256*1024),
_maxOutputBufferSize(256*1024),
+ _numThreads(4),
+ _dispatchOnEncode(true),
+ _dispatchOnDecode(false),
_connectionExpireSecs(600),
_compressionConfig(CompressionConfig::LZ4, 6, 90, 1024)
{ }
diff --git a/messagebus/src/vespa/messagebus/network/rpcnetworkparams.h b/messagebus/src/vespa/messagebus/network/rpcnetworkparams.h
index 0a4ed806c27..1dcc8178e68 100644
--- a/messagebus/src/vespa/messagebus/network/rpcnetworkparams.h
+++ b/messagebus/src/vespa/messagebus/network/rpcnetworkparams.h
@@ -19,6 +19,9 @@ private:
int _listenPort;
uint32_t _maxInputBufferSize;
uint32_t _maxOutputBufferSize;
+ uint32_t _numThreads;
+ bool _dispatchOnEncode;
+ bool _dispatchOnDecode;
double _connectionExpireSecs;
CompressionConfig _compressionConfig;
@@ -97,6 +100,19 @@ public:
}
/**
+ * Sets number of threads for the thread pool.
+ *
+ * @param numThreads number of threads for thread pool
+ * @return This, to allow chaining.
+ */
+ RPCNetworkParams &setNumThreads(uint32_t numThreads) {
+ _numThreads = numThreads;
+ return *this;
+ }
+
+ uint32_t getNumThreads() const { return _numThreads; }
+
+ /**
* Returns the number of seconds before an idle network connection expires.
*
* @return The number of seconds.
@@ -165,6 +181,21 @@ public:
return *this;
}
CompressionConfig getCompressionConfig() const { return _compressionConfig; }
+
+
+ RPCNetworkParams &setDispatchOnDecode(bool dispatchOnDecode) {
+ _dispatchOnDecode = dispatchOnDecode;
+ return *this;
+ }
+
+ uint32_t getDispatchOnDecode() const { return _dispatchOnDecode; }
+
+ RPCNetworkParams &setDispatchOnEncode(bool dispatchOnEncode) {
+ _dispatchOnEncode = dispatchOnEncode;
+ return *this;
+ }
+
+ uint32_t getDispatchOnEncode() const { return _dispatchOnEncode; }
};
}
diff --git a/messagebus/src/vespa/messagebus/network/rpcsend.cpp b/messagebus/src/vespa/messagebus/network/rpcsend.cpp
index e23f4dc29d9..04cccd59903 100644
--- a/messagebus/src/vespa/messagebus/network/rpcsend.cpp
+++ b/messagebus/src/vespa/messagebus/network/rpcsend.cpp
@@ -61,7 +61,7 @@ RPCSend::RPCSend() :
_serverIdent("server")
{ }
-RPCSend::~RPCSend() {}
+RPCSend::~RPCSend() = default;
void
RPCSend::attach(RPCNetwork &net)
@@ -221,7 +221,7 @@ void
RPCSend::handleReply(Reply::UP reply)
{
const IProtocol * protocol = _net->getOwner().getProtocol(reply->getProtocol());
- if (!protocol || protocol->requireSequencing()) {
+ if (!protocol || protocol->requireSequencing() || !_net->allowDispatchForEncode()) {
doHandleReply(protocol, std::move(reply));
} else {
auto rejected = _net->getExecutor().execute(makeLambdaTask([this, protocol, reply = std::move(reply)]() mutable {
@@ -256,22 +256,29 @@ void
RPCSend::invoke(FRT_RPCRequest *req)
{
req->Detach();
- doRequest(req);
-}
-
-void
-RPCSend::doRequest(FRT_RPCRequest *req)
-{
FRT_Values &args = *req->GetParams();
std::unique_ptr<Params> params = toParams(args);
IProtocol * protocol = _net->getOwner().getProtocol(params->getProtocol());
if (protocol == nullptr) {
replyError(req, params->getVersion(), params->getTraceLevel(),
- Error(ErrorCode::UNKNOWN_PROTOCOL,
- make_string("Protocol '%s' is not known by %s.", params->getProtocol().c_str(), _serverIdent.c_str())));
+ Error(ErrorCode::UNKNOWN_PROTOCOL, make_string("Protocol '%s' is not known by %s.",
+ params->getProtocol().c_str(), _serverIdent.c_str())));
return;
}
+ if (protocol->requireSequencing() || !_net->allowDispatchForDecode()) {
+ doRequest(req, protocol, std::move(params));
+ } else {
+ auto rejected = _net->getExecutor().execute(makeLambdaTask([this, req, protocol, params = std::move(params)]() mutable {
+ doRequest(req, protocol, std::move(params));
+ }));
+ assert (!rejected);
+ }
+}
+
+void
+RPCSend::doRequest(FRT_RPCRequest *req, const IProtocol * protocol, std::unique_ptr<Params> params)
+{
Routable::UP routable = protocol->decode(params->getVersion(), params->getPayload());
req->DiscardBlobs();
if ( ! routable ) {
diff --git a/messagebus/src/vespa/messagebus/network/rpcsend.h b/messagebus/src/vespa/messagebus/network/rpcsend.h
index 11a042b91c0..cfc1d72418a 100644
--- a/messagebus/src/vespa/messagebus/network/rpcsend.h
+++ b/messagebus/src/vespa/messagebus/network/rpcsend.h
@@ -83,7 +83,7 @@ public:
void invoke(FRT_RPCRequest *req);
private:
- void doRequest(FRT_RPCRequest *req);
+ void doRequest(FRT_RPCRequest *req, const IProtocol * protocol, std::unique_ptr<Params> params);
void doRequestDone(FRT_RPCRequest *req);
void doHandleReply(const IProtocol * protocol, std::unique_ptr<Reply> reply);
void attach(RPCNetwork &net) final override;
diff --git a/messagebus/src/vespa/messagebus/routing/errordirective.h b/messagebus/src/vespa/messagebus/routing/errordirective.h
index 2b41f0444ac..715c6f9327c 100644
--- a/messagebus/src/vespa/messagebus/routing/errordirective.h
+++ b/messagebus/src/vespa/messagebus/routing/errordirective.h
@@ -9,7 +9,7 @@ namespace mbus {
* This class represents an error directive within a {@link Hop}'s selector. This means to stop whatever is being
* resolved, and instead return a reply containing a specified error.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class ErrorDirective : public IHopDirective {
diff --git a/messagebus/src/vespa/messagebus/routing/hopspec.h b/messagebus/src/vespa/messagebus/routing/hopspec.h
index 96196c92188..f3e5e6297c0 100644
--- a/messagebus/src/vespa/messagebus/routing/hopspec.h
+++ b/messagebus/src/vespa/messagebus/routing/hopspec.h
@@ -13,7 +13,7 @@ namespace mbus {
*
* This class contains the spec for a single hop.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class HopSpec {
diff --git a/messagebus/src/vespa/messagebus/routing/ihopdirective.h b/messagebus/src/vespa/messagebus/routing/ihopdirective.h
index 93d8b9d2f90..0e5729e5d2e 100644
--- a/messagebus/src/vespa/messagebus/routing/ihopdirective.h
+++ b/messagebus/src/vespa/messagebus/routing/ihopdirective.h
@@ -8,7 +8,7 @@ namespace mbus {
/**
* This class is the base class for the primitives that make up a {@link Hop}'s selector.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class IHopDirective {
diff --git a/messagebus/src/vespa/messagebus/routing/policydirective.h b/messagebus/src/vespa/messagebus/routing/policydirective.h
index faa21cc22ce..23c53c58121 100644
--- a/messagebus/src/vespa/messagebus/routing/policydirective.h
+++ b/messagebus/src/vespa/messagebus/routing/policydirective.h
@@ -9,7 +9,7 @@ namespace mbus {
* This class represents a policy directive within a {@link Hop}'s selector. This means to create the named protocol
* using the given parameter string, and the running that protocol within the context of this directive.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class PolicyDirective : public IHopDirective {
diff --git a/messagebus/src/vespa/messagebus/routing/routedirective.h b/messagebus/src/vespa/messagebus/routing/routedirective.h
index 24b2635cd73..63661a3a61c 100644
--- a/messagebus/src/vespa/messagebus/routing/routedirective.h
+++ b/messagebus/src/vespa/messagebus/routing/routedirective.h
@@ -9,7 +9,7 @@ namespace mbus {
* This class represents a route directive within a {@link Hop}'s selector. This will be replaced by the named route
* when evaluated. If the route is not present in the running protocol's routing table, routing will fail.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class RouteDirective : public IHopDirective {
diff --git a/messagebus/src/vespa/messagebus/routing/routespec.h b/messagebus/src/vespa/messagebus/routing/routespec.h
index d6b183418ea..d638801c9ca 100644
--- a/messagebus/src/vespa/messagebus/routing/routespec.h
+++ b/messagebus/src/vespa/messagebus/routing/routespec.h
@@ -13,7 +13,7 @@ namespace mbus {
*
* This class contains the spec for a single route.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class RouteSpec {
diff --git a/messagebus/src/vespa/messagebus/routing/routingspec.h b/messagebus/src/vespa/messagebus/routing/routingspec.h
index c8f999ee47a..f4d769c3037 100644
--- a/messagebus/src/vespa/messagebus/routing/routingspec.h
+++ b/messagebus/src/vespa/messagebus/routing/routingspec.h
@@ -13,7 +13,7 @@ namespace mbus {
*
* This class is the root spec class for configuring message bus routing.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class RoutingSpec {
diff --git a/messagebus/src/vespa/messagebus/routing/routingtable.h b/messagebus/src/vespa/messagebus/routing/routingtable.h
index ab708aa844d..d182ea05644 100644
--- a/messagebus/src/vespa/messagebus/routing/routingtable.h
+++ b/messagebus/src/vespa/messagebus/routing/routingtable.h
@@ -18,7 +18,7 @@ class Message;
* At any time there may only ever be zero or one routing table registered in message bus for each protocol. This class
* contains a list of named hops and routes that may be used to substitute references to these during route resolving.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class RoutingTable {
diff --git a/messagebus/src/vespa/messagebus/routing/routingtablespec.h b/messagebus/src/vespa/messagebus/routing/routingtablespec.h
index f32236df57f..299c030bc26 100644
--- a/messagebus/src/vespa/messagebus/routing/routingtablespec.h
+++ b/messagebus/src/vespa/messagebus/routing/routingtablespec.h
@@ -15,7 +15,7 @@ namespace mbus {
*
* This class contains the spec for a single routing table, which corresponds to exactly one protocol.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class RoutingTableSpec {
diff --git a/messagebus/src/vespa/messagebus/routing/tcpdirective.h b/messagebus/src/vespa/messagebus/routing/tcpdirective.h
index 62ce974b5fa..8687298049f 100644
--- a/messagebus/src/vespa/messagebus/routing/tcpdirective.h
+++ b/messagebus/src/vespa/messagebus/routing/tcpdirective.h
@@ -9,7 +9,7 @@ namespace mbus {
* This class represents a tcp directive within a {@link Hop}'s selector. This is a connection string used to establish
* a direct connection to a host, bypassing service lookups through Slobrok.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class TcpDirective : public IHopDirective {
diff --git a/messagebus/src/vespa/messagebus/routing/verbatimdirective.h b/messagebus/src/vespa/messagebus/routing/verbatimdirective.h
index 74c7c9566d5..5745937380e 100644
--- a/messagebus/src/vespa/messagebus/routing/verbatimdirective.h
+++ b/messagebus/src/vespa/messagebus/routing/verbatimdirective.h
@@ -9,7 +9,7 @@ namespace mbus {
* This class represents a verbatim match within a {@link Hop}'s selector. This is nothing more than a string that will
* be used as-is when performing service name lookups.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class VerbatimDirective : public IHopDirective {
diff --git a/messagebus/src/vespa/messagebus/sourcesessionparams.h b/messagebus/src/vespa/messagebus/sourcesessionparams.h
index 2e02c124322..7c14ee4524d 100644
--- a/messagebus/src/vespa/messagebus/sourcesessionparams.h
+++ b/messagebus/src/vespa/messagebus/sourcesessionparams.h
@@ -11,7 +11,7 @@ namespace mbus {
* SourceSessionParams)}, all parameters are held by this class. This class has reasonable default values for each
* parameter.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class SourceSessionParams {
diff --git a/metrics/src/test/java/com/yahoo/metrics/AveragedDoubleValueMetric.java b/metrics/src/test/java/com/yahoo/metrics/AveragedDoubleValueMetric.java
index fc32ab39c71..41fe7f177dc 100644
--- a/metrics/src/test/java/com/yahoo/metrics/AveragedDoubleValueMetric.java
+++ b/metrics/src/test/java/com/yahoo/metrics/AveragedDoubleValueMetric.java
@@ -2,7 +2,7 @@
package com.yahoo.metrics;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
class AveragedDoubleValueMetric extends ValueMetric<Double> {
diff --git a/metrics/src/test/java/com/yahoo/metrics/AveragedLongValueMetric.java b/metrics/src/test/java/com/yahoo/metrics/AveragedLongValueMetric.java
index 5f743c6c75f..2f27720cfc1 100644
--- a/metrics/src/test/java/com/yahoo/metrics/AveragedLongValueMetric.java
+++ b/metrics/src/test/java/com/yahoo/metrics/AveragedLongValueMetric.java
@@ -2,7 +2,7 @@
package com.yahoo.metrics;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
class AveragedLongValueMetric extends ValueMetric<Long> {
diff --git a/metrics/src/test/java/com/yahoo/metrics/SummedDoubleValueMetric.java b/metrics/src/test/java/com/yahoo/metrics/SummedDoubleValueMetric.java
index 4996186a26e..5d616d93821 100644
--- a/metrics/src/test/java/com/yahoo/metrics/SummedDoubleValueMetric.java
+++ b/metrics/src/test/java/com/yahoo/metrics/SummedDoubleValueMetric.java
@@ -2,7 +2,7 @@
package com.yahoo.metrics;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
class SummedDoubleValueMetric extends ValueMetric<Double> {
diff --git a/metrics/src/test/java/com/yahoo/metrics/SummedLongValueMetric.java b/metrics/src/test/java/com/yahoo/metrics/SummedLongValueMetric.java
index 82ba169dec3..cabd715cc63 100644
--- a/metrics/src/test/java/com/yahoo/metrics/SummedLongValueMetric.java
+++ b/metrics/src/test/java/com/yahoo/metrics/SummedLongValueMetric.java
@@ -2,7 +2,7 @@
package com.yahoo.metrics;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
class SummedLongValueMetric extends ValueMetric<Long> {
diff --git a/model-inference/pom.xml b/model-inference/pom.xml
new file mode 100644
index 00000000000..ad258d1edf4
--- /dev/null
+++ b/model-inference/pom.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0"?>
+<!-- Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+ http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>parent</artifactId>
+ <version>6-SNAPSHOT</version>
+ <relativePath>../parent/pom.xml</relativePath>
+ </parent>
+ <artifactId>model-inference</artifactId>
+ <version>6-SNAPSHOT</version>
+ <packaging>container-plugin</packaging>
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>component</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>configdefinitions</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>vespajlib</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>searchlib</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifestEntries>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Vespa-Version>${project.version}</Vespa-Version>
+ </manifestEntries>
+ </archive>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/model-inference/src/main/java/ai/vespa/models/evaluation/FunctionEvaluator.java b/model-inference/src/main/java/ai/vespa/models/evaluation/FunctionEvaluator.java
new file mode 100644
index 00000000000..c4ecc1b25c9
--- /dev/null
+++ b/model-inference/src/main/java/ai/vespa/models/evaluation/FunctionEvaluator.java
@@ -0,0 +1,54 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.models.evaluation;
+
+import com.yahoo.searchlib.rankingexpression.evaluation.TensorValue;
+import com.yahoo.tensor.Tensor;
+import com.yahoo.tensor.TensorType;
+
+/**
+ * An evaluator which can be used to evaluate a single function once.
+ *
+ * @author bratseth
+ */
+// This wraps all access to the context and the ranking expression to avoid incorrect usage
+public class FunctionEvaluator {
+
+ private final LazyArrayContext context;
+ private boolean evaluated = false;
+
+ FunctionEvaluator(LazyArrayContext context) {
+ this.context = context;
+ }
+
+ /**
+ * Binds the given variable referred in this expression to the given value.
+ *
+ * @param name the variable to bind
+ * @param value the value this becomes bound to
+ * @return this for chaining
+ */
+ public FunctionEvaluator bind(String name, Tensor value) {
+ if (evaluated)
+ throw new IllegalStateException("You cannot bind a value in a used evaluator");
+ context.put(name, new TensorValue(value));
+ return this;
+ }
+
+ /**
+ * Binds the given variable referred in this expression to the given value.
+ * This is equivalent to <code>bind(name, Tensor.Builder.of(TensorType.empty).cell(value).build())</code>
+ *
+ * @param name the variable to bind
+ * @param value the value this becomes bound to
+ * @return this for chaining
+ */
+ public FunctionEvaluator bind(String name, double value) {
+ return bind(name, Tensor.Builder.of(TensorType.empty).cell(value).build());
+ }
+
+ public Tensor evaluate() {
+ evaluated = true;
+ return context.expression().evaluate(context).asTensor();
+ }
+
+}
diff --git a/model-inference/src/main/java/ai/vespa/models/evaluation/LazyArrayContext.java b/model-inference/src/main/java/ai/vespa/models/evaluation/LazyArrayContext.java
new file mode 100644
index 00000000000..729d8af01dc
--- /dev/null
+++ b/model-inference/src/main/java/ai/vespa/models/evaluation/LazyArrayContext.java
@@ -0,0 +1,213 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.models.evaluation;
+
+import com.google.common.collect.ImmutableMap;
+import com.yahoo.searchlib.rankingexpression.ExpressionFunction;
+import com.yahoo.searchlib.rankingexpression.RankingExpression;
+import com.yahoo.searchlib.rankingexpression.Reference;
+import com.yahoo.searchlib.rankingexpression.evaluation.AbstractArrayContext;
+import com.yahoo.searchlib.rankingexpression.evaluation.Context;
+import com.yahoo.searchlib.rankingexpression.evaluation.ContextIndex;
+import com.yahoo.searchlib.rankingexpression.evaluation.DoubleValue;
+import com.yahoo.searchlib.rankingexpression.evaluation.Value;
+import com.yahoo.searchlib.rankingexpression.rule.CompositeNode;
+import com.yahoo.searchlib.rankingexpression.rule.ExpressionNode;
+import com.yahoo.searchlib.rankingexpression.rule.ReferenceNode;
+import com.yahoo.tensor.TensorType;
+
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * An array context supporting functions invocations implemented as lazy values.
+ *
+ * @author bratseth
+ */
+final class LazyArrayContext extends Context implements ContextIndex {
+
+ private final RankingExpression expression;
+ private final IndexedBindings indexedBindings;
+
+ private LazyArrayContext(RankingExpression expression, IndexedBindings indexedBindings) {
+ this.expression = expression;
+ this.indexedBindings = indexedBindings.copy(this);
+ }
+
+ /**
+ * Create a fast lookup, lazy context for an expression.
+ *
+ * @param expression the expression to create a context for
+ */
+ LazyArrayContext(RankingExpression expression, Map<String, ExpressionFunction> functions) {
+ this.expression = expression;
+ this.indexedBindings = new IndexedBindings(expression, functions, this);
+ }
+
+ /**
+ * Puts a value by name.
+ * The value will be frozen if it isn't already.
+ *
+ * @throws IllegalArgumentException if the name is not present in the ranking expression this was created with, and
+ * ignoredUnknownValues is false
+ */
+ @Override
+ public void put(String name, Value value) {
+ put(requireIndexOf(name), value);
+ }
+
+ /** Same as put(index,DoubleValue.frozen(value)) */
+ public final void put(int index, double value) {
+ put(index, DoubleValue.frozen(value));
+ }
+
+ /**
+ * Puts a value by index.
+ * The value will be frozen if it isn't already.
+ */
+ public void put(int index, Value value) {
+ indexedBindings.set(index, value.freeze());
+ }
+
+ @Override
+ public TensorType getType(Reference reference) {
+ // TODO: Add type information so we do not need to evaluate to get this
+ return get(requireIndexOf(reference.toString())).type();
+ }
+
+ /** Perform a slow lookup by name */
+ @Override
+ public Value get(String name) {
+ return get(requireIndexOf(name));
+ }
+
+ /** Perform a fast lookup by index */
+ @Override
+ public Value get(int index) {
+ return indexedBindings.get(index);
+ }
+
+ @Override
+ public double getDouble(int index) {
+ double value = get(index).asDouble();
+ if (value == Double.NaN)
+ throw new UnsupportedOperationException("Value at " + index + " has no double representation");
+ return value;
+ }
+
+ @Override
+ public int getIndex(String name) {
+ return requireIndexOf(name);
+ }
+
+ @Override
+ public int size() {
+ return indexedBindings.names().size();
+ }
+
+ @Override
+ public Set<String> names() { return indexedBindings.names(); }
+
+ private Integer requireIndexOf(String name) {
+ Integer index = indexedBindings.indexOf(name);
+ if (index == null)
+ throw new IllegalArgumentException("Value '" + name + "' can not be bound in " + this);
+ return index;
+ }
+
+ @Override
+ public String toString() { return "context of '" + expression.getName() + "'"; }
+
+ RankingExpression expression() { return expression; }
+
+ /**
+ * Creates a copy of this context suitable for evaluating against the same ranking expression
+ * in a different thread or for re-binding free variables.
+ */
+ LazyArrayContext copy() {
+ return new LazyArrayContext(expression, indexedBindings);
+ }
+
+ private static class IndexedBindings {
+
+ /** The mapping from variable name to index */
+ private final ImmutableMap<String, Integer> nameToIndex;
+
+ /** The current values set, pre-converted to doubles */
+ private final Value[] values;
+
+ private IndexedBindings(ImmutableMap<String, Integer> nameToIndex, Value[] values) {
+ this.nameToIndex = nameToIndex;
+ this.values = values;
+ }
+
+ IndexedBindings(RankingExpression expression, Map<String, ExpressionFunction> functions, LazyArrayContext owner) {
+ Set<String> bindTargets = new LinkedHashSet<>();
+ extractBindTargets(expression.getRoot(), functions, bindTargets);
+
+ values = new Value[bindTargets.size()];
+ Arrays.fill(values, DoubleValue.zero);
+
+ int i = 0;
+ ImmutableMap.Builder<String, Integer> nameToIndexBuilder = new ImmutableMap.Builder<>();
+ for (String variable : bindTargets)
+ nameToIndexBuilder.put(variable,i++);
+ nameToIndex = nameToIndexBuilder.build();
+
+ for (Map.Entry<String, ExpressionFunction> function : functions.entrySet()) {
+ Integer index = nameToIndex.get(function.getKey());
+ if (index != null) // Referenced in this, so bind it
+ values[index] = new LazyValue(function.getValue(), owner);
+ }
+ }
+
+ private void extractBindTargets(ExpressionNode node, Map<String, ExpressionFunction> functions, Set<String> bindTargets) {
+ if (isFunctionReference(node)) {
+ String reference = node.toString();
+ bindTargets.add(reference);
+
+ extractBindTargets(functions.get(reference).getBody().getRoot(), functions, bindTargets);
+ }
+ else if (isConstant(node)) {
+ // Ignore
+ }
+ else if (node instanceof ReferenceNode) {
+ bindTargets.add(node.toString());
+ }
+ else if (node instanceof CompositeNode) {
+ CompositeNode cNode = (CompositeNode)node;
+ for (ExpressionNode child : cNode.children())
+ extractBindTargets(child, functions, bindTargets);
+ }
+ }
+
+ private boolean isFunctionReference(ExpressionNode node) {
+ if ( ! (node instanceof ReferenceNode)) return false;
+
+ ReferenceNode reference = (ReferenceNode)node;
+ return reference.getName().equals("rankingExpression") && reference.getArguments().size() == 1;
+ }
+
+ private boolean isConstant(ExpressionNode node) {
+ if ( ! (node instanceof ReferenceNode)) return false;
+
+ ReferenceNode reference = (ReferenceNode)node;
+ return reference.getName().equals("value") && reference.getArguments().size() == 1;
+ }
+
+ Value get(int index) { return values[index]; }
+ void set(int index, Value value) { values[index] = value; }
+ Set<String> names() { return nameToIndex.keySet(); }
+ Integer indexOf(String name) { return nameToIndex.get(name); }
+
+ IndexedBindings copy(Context context) {
+ Value[] valueCopy = new Value[values.length];
+ for (int i = 0; i < values.length; i++)
+ valueCopy[i] = values[i] instanceof LazyValue ? ((LazyValue)values[i]).copyFor(context) : values[i];
+ return new IndexedBindings(nameToIndex, valueCopy);
+ }
+
+ }
+
+}
diff --git a/model-inference/src/main/java/ai/vespa/models/evaluation/LazyValue.java b/model-inference/src/main/java/ai/vespa/models/evaluation/LazyValue.java
new file mode 100644
index 00000000000..7e21e426962
--- /dev/null
+++ b/model-inference/src/main/java/ai/vespa/models/evaluation/LazyValue.java
@@ -0,0 +1,150 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.models.evaluation;
+
+import com.yahoo.searchlib.rankingexpression.ExpressionFunction;
+import com.yahoo.searchlib.rankingexpression.evaluation.Context;
+import com.yahoo.searchlib.rankingexpression.evaluation.Value;
+import com.yahoo.searchlib.rankingexpression.rule.Function;
+import com.yahoo.searchlib.rankingexpression.rule.TruthOperator;
+import com.yahoo.tensor.Tensor;
+import com.yahoo.tensor.TensorType;
+
+/**
+ * A Value which is computed from an expression when first requested.
+ * This is not multithread safe.
+ *
+ * @author bratseth
+ */
+class LazyValue extends Value {
+
+ /** The function computing the value of this */
+ private final ExpressionFunction function;
+
+ /** The context used to compute the function of this */
+ private final Context context;
+
+ private Value computedValue = null;
+
+ public LazyValue(ExpressionFunction function, Context context) {
+ this.function = function;
+ this.context = context;
+ }
+
+ private Value computedValue() {
+ if (computedValue == null)
+ computedValue = function.getBody().evaluate(context);
+ return computedValue;
+ }
+
+ @Override
+ public TensorType type() {
+ return computedValue().type(); // TODO: Keep type information in this/ExpressionFunction to avoid computing here
+ }
+
+ @Override
+ public double asDouble() {
+ return computedValue().asDouble();
+ }
+
+ @Override
+ public Tensor asTensor() {
+ return computedValue().asTensor();
+ }
+
+ @Override
+ public boolean hasDouble() {
+ return type().rank() == 0;
+ }
+
+ @Override
+ public boolean asBoolean() {
+ return computedValue().asBoolean();
+ }
+
+ @Override
+ public Value negate() {
+ return computedValue().negate();
+ }
+
+ @Override
+ public Value add(Value value) {
+ return computedValue().add(value);
+ }
+
+ @Override
+ public Value subtract(Value value) {
+ return computedValue().subtract(value);
+ }
+
+ @Override
+ public Value multiply(Value value) {
+ return computedValue().multiply(value);
+ }
+
+ @Override
+ public Value divide(Value value) {
+ return computedValue().divide(value);
+ }
+
+ @Override
+ public Value modulo(Value value) {
+ return computedValue().modulo(value);
+ }
+
+ @Override
+ public Value and(Value value) {
+ return computedValue().and(value);
+ }
+
+ @Override
+ public Value or(Value value) {
+ return computedValue().or(value);
+ }
+
+ @Override
+ public Value not() {
+ return computedValue().not();
+ }
+
+ @Override
+ public Value power(Value value) {
+ return computedValue().power(value);
+ }
+
+ @Override
+ public Value compare(TruthOperator operator, Value value) {
+ return computedValue().compare(operator, value);
+ }
+
+ @Override
+ public Value function(Function function, Value value) {
+ return computedValue().function(function, value);
+ }
+
+ @Override
+ public Value asMutable() {
+ return computedValue().asMutable();
+ }
+
+ @Override
+ public String toString() {
+ return "value of " + function;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) return true;
+ if (!(other instanceof Value)) return false;
+ return computedValue().equals(other);
+ }
+
+ @Override
+ public int hashCode() {
+ return computedValue().hashCode();
+ }
+
+ LazyValue copyFor(Context context) {
+ return new LazyValue(this.function, context);
+ }
+
+}
diff --git a/model-inference/src/main/java/ai/vespa/models/evaluation/Model.java b/model-inference/src/main/java/ai/vespa/models/evaluation/Model.java
new file mode 100644
index 00000000000..9a639d0803f
--- /dev/null
+++ b/model-inference/src/main/java/ai/vespa/models/evaluation/Model.java
@@ -0,0 +1,106 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.models.evaluation;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.yahoo.searchlib.rankingexpression.ExpressionFunction;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * A named collection of functions
+ *
+ * @author bratseth
+ */
+public class Model {
+
+ private final String name;
+
+ /** Free functions */
+ private final ImmutableList<ExpressionFunction> functions;
+
+ /** Instances of each usage of the above function, where variables (if any) are replaced by their bindings */
+ private final ImmutableMap<String, ExpressionFunction> referredFunctions;
+
+ private final ImmutableMap<String, LazyArrayContext> contextPrototypes;
+
+ public Model(String name, Collection<ExpressionFunction> functions) {
+ this(name, functions, Collections.emptyList());
+ }
+
+ Model(String name, Collection<ExpressionFunction> functions, Collection<ExpressionFunction> referredFunctions) {
+ // TODO: Optimize functions
+ this.name = name;
+ this.functions = ImmutableList.copyOf(functions);
+
+ ImmutableMap.Builder<String, ExpressionFunction> functionsBuilder = new ImmutableMap.Builder<>();
+ for (ExpressionFunction function : referredFunctions)
+ functionsBuilder.put(function.getName(), function);
+ this.referredFunctions = functionsBuilder.build();
+
+ ImmutableMap.Builder<String, LazyArrayContext> contextBuilder = new ImmutableMap.Builder<>();
+ for (ExpressionFunction function : functions) {
+ try {
+ contextBuilder.put(function.getName(), new LazyArrayContext(function.getBody(), this.referredFunctions));
+ }
+ catch (RuntimeException e) {
+ throw new IllegalArgumentException("Could not prepare an evaluation context for " + function, e);
+ }
+ }
+ this.contextPrototypes = contextBuilder.build();
+ }
+
+ public String name() { return name; }
+
+ /** Returns an immutable list of the free functions of this */
+ public List<ExpressionFunction> functions() { return functions; }
+
+ /** Returns the given function, or throws a IllegalArgumentException if it does not exist */
+ ExpressionFunction requireFunction(String name) {
+ ExpressionFunction function = function(name);
+ if (function == null)
+ throw new IllegalArgumentException("No function named '" + name + "' in " + this + ". Available functions: " +
+ functions.stream().map(f -> f.getName()).collect(Collectors.joining(", ")));
+ return function;
+ }
+
+ /** Returns the given function, or throws a IllegalArgumentException if it does not exist */
+ private LazyArrayContext requireContextProprotype(String name) {
+ LazyArrayContext context = contextPrototypes.get(name);
+ if (context == null) // Implies function is not present
+ throw new IllegalArgumentException("No function named '" + name + "' in " + this + ". Available functions: " +
+ functions.stream().map(f -> f.getName()).collect(Collectors.joining(", ")));
+ return context;
+ }
+
+ /** Returns the function withe the given name, or null if none */ // TODO: Parameter overloading?
+ ExpressionFunction function(String name) {
+ for (ExpressionFunction function : functions)
+ if (function.getName().equals(name))
+ return function;
+ return null;
+ }
+
+ /** Returns an immutable map of the bound function instances of this, indexed by the bound instance if */
+ Map<String, ExpressionFunction> boundFunctions() { return referredFunctions; }
+
+ /**
+ * Returns an evaluator which can be used to evaluate the given function in a single thread once.
+
+ * Usage:
+ * <code>Tensor result = model.evaluatorOf("myFunction").bind("foo", value).bind("bar", value).evaluate()</code>
+ *
+ * @throws IllegalArgumentException if the function is not present
+ */
+ public FunctionEvaluator evaluatorOf(String function) { // TODO: Parameter overloading?
+ return new FunctionEvaluator(requireContextProprotype(function).copy());
+ }
+
+ @Override
+ public String toString() { return "model '" + name + "'"; }
+
+}
diff --git a/model-inference/src/main/java/ai/vespa/models/evaluation/ModelsEvaluator.java b/model-inference/src/main/java/ai/vespa/models/evaluation/ModelsEvaluator.java
new file mode 100644
index 00000000000..b36e06e5505
--- /dev/null
+++ b/model-inference/src/main/java/ai/vespa/models/evaluation/ModelsEvaluator.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 ai.vespa.models.evaluation;
+
+import com.google.common.collect.ImmutableMap;
+import com.yahoo.vespa.config.search.RankProfilesConfig;
+
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * Evaluates machine-learned models added to Vespa applications and available as config form.
+ * Usage:
+ * <code>Tensor result = evaluator.bind("foo", value).bind("bar", value").evaluate()</code>
+ *
+ * @author bratseth
+ */
+public class ModelsEvaluator {
+
+ private final ImmutableMap<String, Model> models;
+
+ public ModelsEvaluator(RankProfilesConfig config) {
+ models = ImmutableMap.copyOf(new RankProfilesConfigImporter().importFrom(config));
+ }
+
+ /** Returns the models of this as an immutable map */
+ public Map<String, Model> models() { return models; }
+
+ /**
+ * Returns a function which can be used to evaluate the given function in the given model
+ *
+ * @throws IllegalArgumentException if the function or model is not present
+ */
+ public FunctionEvaluator evaluatorOf(String modelName, String functionName) {
+ return requireModel(modelName).evaluatorOf(functionName);
+ }
+
+ /** Returns the given model, or throws a IllegalArgumentException if it does not exist */
+ Model requireModel(String name) {
+ Model model = models.get(name);
+ if (model == null)
+ throw new IllegalArgumentException("No model named '" + name + ". Available models: " +
+ models.keySet().stream().collect(Collectors.joining(", ")));
+ return model;
+ }
+
+}
diff --git a/model-inference/src/main/java/ai/vespa/models/evaluation/RankProfilesConfigImporter.java b/model-inference/src/main/java/ai/vespa/models/evaluation/RankProfilesConfigImporter.java
new file mode 100644
index 00000000000..bd0453f2826
--- /dev/null
+++ b/model-inference/src/main/java/ai/vespa/models/evaluation/RankProfilesConfigImporter.java
@@ -0,0 +1,93 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.models.evaluation;
+
+import com.yahoo.searchlib.rankingexpression.ExpressionFunction;
+import com.yahoo.searchlib.rankingexpression.RankingExpression;
+import com.yahoo.searchlib.rankingexpression.parser.ParseException;
+import com.yahoo.vespa.config.search.RankProfilesConfig;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Converts RankProfilesConfig instances to RankingExpressions for evaluation
+ *
+ * @author bratseth
+ */
+class RankProfilesConfigImporter {
+
+ private static final Pattern expressionPattern =
+ Pattern.compile("rankingExpression\\(([a-zA-Z0-9_]+)(@[a-f0-9]+\\.[a-f0-9]+)?\\)\\.rankingScript");
+
+ /**
+ * Returns a map of the models contained in this config, indexed on name.
+ * The map is modifiable and owned by the caller.
+ */
+ Map<String, Model> importFrom(RankProfilesConfig config) {
+ try {
+ Map<String, Model> models = new HashMap<>();
+ for (RankProfilesConfig.Rankprofile profile : config.rankprofile()) {
+ Model model = importProfile(profile);
+ models.put(model.name(), model);
+ }
+ return models;
+ }
+ catch (ParseException e) {
+ throw new IllegalArgumentException("Could not read rank profiles config - version mismatch?", e);
+ }
+ }
+
+ private Model importProfile(RankProfilesConfig.Rankprofile profile) throws ParseException {
+ List<ExpressionFunction> functions = new ArrayList<>();
+ List<ExpressionFunction> boundFunctions = new ArrayList<>();
+ ExpressionFunction firstPhase = null;
+ ExpressionFunction secondPhase = null;
+ for (RankProfilesConfig.Rankprofile.Fef.Property property : profile.fef().property()) {
+ Matcher expressionMatcher = expressionPattern.matcher(property.name());
+ if ( expressionMatcher.matches()) {
+ String name = expressionMatcher.group(1);
+ String instance = expressionMatcher.group(2);
+ List<String> arguments = new ArrayList<>(); // TODO: Arguments?
+ RankingExpression expression = new RankingExpression(name, property.value());
+
+ if (instance == null) // free function; make available in model under configured name
+ functions.add(new ExpressionFunction(name, arguments, expression)); //
+
+ // Make all functions, bound or not available under the name they are referenced by in expressions
+ boundFunctions.add(new ExpressionFunction("rankingExpression(" + name + (instance != null ? instance : "") + ")",
+ arguments, expression));
+ }
+ else if (property.name().equals("vespa.rank.firstphase")) { // Include in addition to macros
+ firstPhase = new ExpressionFunction("firstphase", new ArrayList<>(),
+ new RankingExpression("first-phase", property.value()));
+ }
+ else if (property.name().equals("vespa.rank.secondphase")) { // Include in addition to macros
+ secondPhase = new ExpressionFunction("secondphase", new ArrayList<>(),
+ new RankingExpression("second-phase", property.value()));
+ }
+ }
+ if (functionByName("firstphase", functions) == null && firstPhase != null) // may be already included, depending on body
+ functions.add(firstPhase);
+ if (functionByName("secondphase", functions) == null && secondPhase != null) // may be already included, depending on body
+ functions.add(secondPhase);
+
+ try {
+ return new Model(profile.name(), functions, boundFunctions);
+ }
+ catch (RuntimeException e) {
+ throw new IllegalArgumentException("Could not load model '" + profile.name() + "'", e);
+ }
+ }
+
+ private ExpressionFunction functionByName(String name, List<ExpressionFunction> functions) {
+ for (ExpressionFunction function : functions)
+ if (function.getName().equals(name))
+ return function;
+ return null;
+ }
+
+}
diff --git a/model-inference/src/test/java/ai/vespa/models/evaluation/ModelsEvaluatorTest.java b/model-inference/src/test/java/ai/vespa/models/evaluation/ModelsEvaluatorTest.java
new file mode 100644
index 00000000000..f2031b140e7
--- /dev/null
+++ b/model-inference/src/test/java/ai/vespa/models/evaluation/ModelsEvaluatorTest.java
@@ -0,0 +1,51 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.models.evaluation;
+
+import com.yahoo.config.subscription.ConfigGetter;
+import com.yahoo.config.subscription.FileSource;
+import com.yahoo.tensor.Tensor;
+import com.yahoo.vespa.config.search.RankProfilesConfig;
+import org.junit.Test;
+
+import java.io.File;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author bratseth
+ */
+public class ModelsEvaluatorTest {
+
+ private static final double delta = 0.00000000001;
+
+ private ModelsEvaluator createModels() {
+ String configPath = "src/test/resources/config/rankexpression/rank-profiles.cfg";
+ RankProfilesConfig config = new ConfigGetter<>(new FileSource(new File(configPath)), RankProfilesConfig.class).getConfig("");
+ return new ModelsEvaluator(config);
+ }
+
+ @Test
+ public void testTensorEvaluation() {
+ ModelsEvaluator models = createModels();
+ FunctionEvaluator function = models.evaluatorOf("macros", "fourtimessum");
+ function.bind("var1", Tensor.from("{{x:0}:3,{x:1}:5}"));
+ function.bind("var2", Tensor.from("{{x:0}:7,{x:1}:11}"));
+ assertEquals(Tensor.from("{{x:0}:40.0,{x:1}:64.0}"), function.evaluate());
+ }
+
+ @Test
+ public void testEvaluationDependingOnMacroTakingArguments() {
+ ModelsEvaluator models = createModels();
+ FunctionEvaluator function = models.evaluatorOf("macros", "secondphase");
+ function.bind("match", 3);
+ function.bind("rankBoost", 5);
+ assertEquals(32.0, function.evaluate().asDouble(), delta);
+ }
+
+ // TODO: Test argument-less function
+ // TODO: Test that binding nonexisting variable doesn't work
+ // TODO: Test that rebinding dopesn't work
+ // TODO: Test with nested macros
+ // TODO: Test TF/ONNX model
+
+}
diff --git a/model-inference/src/test/java/ai/vespa/models/evaluation/RankProfilesImporterTest.java b/model-inference/src/test/java/ai/vespa/models/evaluation/RankProfilesImporterTest.java
new file mode 100644
index 00000000000..c520a389b15
--- /dev/null
+++ b/model-inference/src/test/java/ai/vespa/models/evaluation/RankProfilesImporterTest.java
@@ -0,0 +1,60 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.models.evaluation;
+
+import com.yahoo.config.subscription.ConfigGetter;
+import com.yahoo.config.subscription.FileSource;
+import com.yahoo.searchlib.rankingexpression.ExpressionFunction;
+import com.yahoo.vespa.config.search.RankProfilesConfig;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * Tests instantiating models from rank-profiles configs.
+ *
+ * @author bratseth
+ */
+public class RankProfilesImporterTest {
+
+ @Test
+ public void testImporting() {
+ String configPath = "src/test/resources/config/rankexpression/rank-profiles.cfg";
+ RankProfilesConfig config = new ConfigGetter<>(new FileSource(new File(configPath)), RankProfilesConfig.class).getConfig("");
+ Map<String, Model> models = new RankProfilesConfigImporter().importFrom(config);
+ assertEquals(18, models.size());
+
+ Model macros = models.get("macros");
+ assertNotNull(macros);
+ assertEquals("macros", macros.name());
+ assertEquals(4, macros.functions().size());
+ assertFunction("fourtimessum", "4 * (var1 + var2)", macros);
+ assertFunction("firstphase", "match + fieldMatch(title) + rankingExpression(myfeature)", macros);
+ assertFunction("secondphase", "rankingExpression(fourtimessum@5cf279212355b980.67f1e87166cfef86)", macros);
+ assertFunction("myfeature",
+ "70 * fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness,2) + " +
+ "30 * pow(0 - fieldMatch(description).earliness,2)",
+ macros);
+ assertEquals(4, macros.boundFunctions().size());
+ assertBoundFunction("rankingExpression(fourtimessum@5cf279212355b980.67f1e87166cfef86)",
+ "4 * (match + rankBoost)", macros);
+ }
+
+ private void assertFunction(String name, String expression, Model model) {
+ ExpressionFunction function = model.function(name);
+ assertNotNull(function);
+ assertEquals(name, function.getName());
+ assertEquals(expression, function.getBody().getRoot().toString());
+ }
+
+ private void assertBoundFunction(String name, String expression, Model model) {
+ ExpressionFunction function = model.boundFunctions().get(name);
+ assertNotNull("Function '" + name + "' is present", function);
+ assertEquals(name, function.getName());
+ assertEquals(expression, function.getBody().getRoot().toString());
+ }
+
+}
diff --git a/model-inference/src/test/resources/config/rankexpression/rank-profiles.cfg b/model-inference/src/test/resources/config/rankexpression/rank-profiles.cfg
new file mode 100644
index 00000000000..f5652c31d2a
--- /dev/null
+++ b/model-inference/src/test/resources/config/rankexpression/rank-profiles.cfg
@@ -0,0 +1,296 @@
+rankprofile[0].name "default"
+rankprofile[0].fef.property[0].name "foo"
+rankprofile[0].fef.property[0].value "bar, baz"
+rankprofile[0].fef.property[1].name "foo"
+rankprofile[0].fef.property[1].value "foobar"
+rankprofile[0].fef.property[2].name "qux"
+rankprofile[0].fef.property[2].value "quux"
+rankprofile[0].fef.property[3].name "foo.bar"
+rankprofile[0].fef.property[3].value "foo.bar"
+rankprofile[0].fef.property[4].name "foo.bar.baz"
+rankprofile[0].fef.property[4].value "123"
+rankprofile[0].fef.property[5].name "foo(bar).baz.2"
+rankprofile[0].fef.property[5].value "123.4"
+rankprofile[0].fef.property[6].name "foo(bar).baz.qux"
+rankprofile[0].fef.property[6].value "foo(bar)"
+rankprofile[0].fef.property[7].name "nud"
+rankprofile[0].fef.property[7].value "ity"
+rankprofile[0].fef.property[8].name "vespa.rank.firstphase"
+rankprofile[0].fef.property[8].value "classicRank"
+rankprofile[0].fef.property[9].name "vespa.rank.secondphase"
+rankprofile[0].fef.property[9].value "rankingExpression(secondphase)"
+rankprofile[0].fef.property[10].name "rankingExpression(secondphase).rankingScript"
+rankprofile[0].fef.property[10].value "4"
+rankprofile[0].fef.property[11].name "vespa.dump.feature"
+rankprofile[0].fef.property[11].value "attribute(foo1).out"
+rankprofile[0].fef.property[12].name "vespa.dump.feature"
+rankprofile[0].fef.property[12].value "attribute(bar1)"
+rankprofile[0].fef.property[13].name "vespa.dump.feature"
+rankprofile[0].fef.property[13].value "attribute(foo2).out"
+rankprofile[0].fef.property[14].name "vespa.dump.feature"
+rankprofile[0].fef.property[14].value "attribute(bar2).out"
+rankprofile[0].fef.property[15].name "vespa.dump.feature"
+rankprofile[0].fef.property[15].value "attribute(foo3).out"
+rankprofile[0].fef.property[16].name "vespa.dump.feature"
+rankprofile[0].fef.property[16].value "attribute(bar3).out"
+rankprofile[0].fef.property[17].name "vespa.dump.feature"
+rankprofile[0].fef.property[17].value "attribute(foo4).out"
+rankprofile[0].fef.property[18].name "vespa.dump.feature"
+rankprofile[0].fef.property[18].value "attribute(bar4).out"
+rankprofile[0].fef.property[19].name "vespa.hitcollector.heapsize"
+rankprofile[0].fef.property[19].value "10"
+rankprofile[0].fef.property[20].name "vespa.hitcollector.arraysize"
+rankprofile[0].fef.property[20].value "20"
+rankprofile[0].fef.property[21].name "vespa.hitcollector.rankscoredroplimit"
+rankprofile[0].fef.property[21].value "-0.5"
+rankprofile[0].fef.property[22].name "vespa.dump.ignoredefaultfeatures"
+rankprofile[0].fef.property[22].value "true"
+rankprofile[1].name "unranked"
+rankprofile[1].fef.property[0].name "vespa.rank.firstphase"
+rankprofile[1].fef.property[0].value "value(0)"
+rankprofile[1].fef.property[1].name "vespa.hitcollector.heapsize"
+rankprofile[1].fef.property[1].value "0"
+rankprofile[1].fef.property[2].name "vespa.hitcollector.arraysize"
+rankprofile[1].fef.property[2].value "0"
+rankprofile[1].fef.property[3].name "vespa.dump.ignoredefaultfeatures"
+rankprofile[1].fef.property[3].value "true"
+rankprofile[2].name "static"
+rankprofile[2].fef.property[0].name "vespa.rank.firstphase"
+rankprofile[2].fef.property[0].value "attribute"
+rankprofile[2].fef.property[1].name "vespa.rank.secondphase"
+rankprofile[2].fef.property[1].value "rankingExpression(secondphase)"
+rankprofile[2].fef.property[2].name "rankingExpression(secondphase).rankingScript"
+rankprofile[2].fef.property[2].value "10 + feature(arg1).out.out"
+rankprofile[2].fef.property[3].name "vespa.summary.feature"
+rankprofile[2].fef.property[3].value "attribute(foo1).out"
+rankprofile[2].fef.property[4].name "vespa.summary.feature"
+rankprofile[2].fef.property[4].value "attribute(bar1)"
+rankprofile[2].fef.property[5].name "vespa.summary.feature"
+rankprofile[2].fef.property[5].value "attribute(foo2).out"
+rankprofile[2].fef.property[6].name "vespa.summary.feature"
+rankprofile[2].fef.property[6].value "attribute(bar2).out"
+rankprofile[2].fef.property[7].name "vespa.summary.feature"
+rankprofile[2].fef.property[7].value "attribute(foo3).out"
+rankprofile[2].fef.property[8].name "vespa.summary.feature"
+rankprofile[2].fef.property[8].value "attribute(bar3).out"
+rankprofile[2].fef.property[9].name "vespa.summary.feature"
+rankprofile[2].fef.property[9].value "attribute(foo4).out"
+rankprofile[2].fef.property[10].name "vespa.summary.feature"
+rankprofile[2].fef.property[10].value "attribute(bar4).out"
+rankprofile[3].name "overflow"
+rankprofile[3].fef.property[0].name "vespa.rank.firstphase"
+rankprofile[3].fef.property[0].value "rankingExpression(firstphase)"
+rankprofile[3].fef.property[1].name "rankingExpression(firstphase).rankingScript"
+rankprofile[3].fef.property[1].value "feature1(argument1,argument2,argument3,argument4).output + feature2(argument1,argument2,argument3,argument4).output + feature3(argument1,argument2,argument3,argument4).output + feature4(argument1,argument2,argument3,argument4).output + feature5(argument1,argument2,argument3,argument4).output + feature6(argument1,argument2,argument3,argument4).output + feature7(argument1,argument2,argument3,argument4).output + feature8(argument1,argument2,argument3,argument4).output + feature9(argument1,argument2,argument3,argument4).output + feature10(argument1,argument2,argument3,argument4).output + feature11(argument1,argument2,argument3,argument4).output + feature12(argument1,argument2,argument3,argument4).output + feature13(argument1,argument2,argument3,argument4).output + feature14(argument1,argument2,argument3,argument4).output + feature15(argument1,argument2,argument3,argument4).output + feature16(argument1,argument2,argument3,argument4).output + feature17(argument1,argument2,argument3,argument4).output + feature18(argument1,argument2,argument3,argument4).output + feature19(argument1,argument2,argument3,argument4).output + feature20(argument1,argument2,argument3,argument4).output + feature21(argument1,argument2,argument3,argument4).output + feature22(argument1,argument2,argument3,argument4).output + feature23(argument1,argument2,argument3,argument4).output + feature24(argument1,argument2,argument3,argument4).output + feature25(argument1,argument2,argument3,argument4).output + feature26(argument1,argument2,argument3,argument4).output + feature27(argument1,argument2,argument3,argument4).output + feature28(argument1,argument2,argument3,argument4).output + feature29(argument1,argument2,argument3,argument4).output + feature30(argument1,argument2,argument3,argument4).output + feature31(argument1,argument2,argument3,argument4).output + feature32(argument1,argument2,argument3,argument4).output + feature33(argument1,argument2,argument3,argument4).output + feature34(argument1,argument2,argument3,argument4).output + feature35(argument1,argument2,argument3,argument4).output + feature36(argument1,argument2,argument3,argument4).output + feature37(argument1,argument2,argument3,argument4).output + feature38(argument1,argument2,argument3,argument4).output + feature39(argument1,argument2,argument3,argument4).output + feature40(argument1,argument2,argument3,argument4).output + feature41(argument1,argument2,argument3,argument4).output + feature42(argument1,argument2,argument3,argument4).output + feature43(argument1,argument2,argument3,argument4).output + feature44(argument1,argument2,argument3,argument4).output + feature45(argument1,argument2,argument3,argument4).output + feature46(argument1,argument2,argument3,argument4).output + feature47(argument1,argument2,argument3,argument4).output + feature48(argument1,argument2,argument3,argument4).output + feature49(argument1,argument2,argument3,argument4).output + feature50(argument1,argument2,argument3,argument4).output + feature51(argument1,argument2,argument3,argument4).output + feature52(argument1,argument2,argument3,argument4).output + feature53(argument1,argument2,argument3,argument4).output + feature54(argument1,argument2,argument3,argument4).output + feature55(argument1,argument2,argument3,argument4).output + feature56(argument1,argument2,argument3,argument4).output + feature57(argument1,argument2,argument3,argument4).output + feature58(argument1,argument2,argument3,argument4).output + feature59(argument1,argument2,argument3,argument4).output + feature60(argument1,argument2,argument3,argument4).output + feature61(argument1,argument2,argument3,argument4).output + feature62(argument1,argument2,argument3,argument4).output + feature63(argument1,argument2,argument3,argument4).output + feature64(argument1,argument2,argument3,argument4).output + feature65(argument1,argument2,argument3,argument4).output + feature66(argument1,argument2,argument3,argument4).output + feature67(argument1,argument2,argument3,argument4).output + feature68(argument1,argument2,argument3,argument4).output + feature69(argument1,argument2,argument3,argument4).output + feature70(argument1,argument2,argument3,argument4).output + feature71(argument1,argument2,argument3,argument4).output + feature72(argument1,argument2,argument3,argument4).output + feature73(argument1,argument2,argument3,argument4).output + feature74(argument1,argument2,argument3,argument4).output + feature75(argument1,argument2,argument3,argument4).output + feature76(argument1,argument2,argument3,argument4).output + feature77(argument1,argument2,argument3,argument4).output + feature78(argument1,argument2,argument3,argument4).output + feature79(argument1,argument2,argument3,argument4).output + feature80(argument1,argument2,argument3,argument4).output + feature81(argument1,argument2,argument3,argument4).output + feature82(argument1,argument2,argument3,argument4).output + feature83(argument1,argument2,argument3,argument4).output + feature84(argument1,argument2,argument3,argument4).output + feature85(argument1,argument2,argument3,argument4).output + feature86(argument1,argument2,argument3,argument4).output + feature87(argument1,argument2,argument3,argument4).output + feature88(argument1,argument2,argument3,argument4).output + feature89(argument1,argument2,argument3,argument4).output + feature90(argument1,argument2,argument3,argument4).output + feature91(argument1,argument2,argument3,argument4).output + feature92(argument1,argument2,argument3,argument4).output + feature93(argument1,argument2,argument3,argument4).output + feature94(argument1,argument2,argument3,argument4).output + feature95(argument1,argument2,argument3,argument4).output + feature96(argument1,argument2,argument3,argument4).output + feature97(argument1,argument2,argument3,argument4).output + feature98(argument1,argument2,argument3,argument4).output + feature99(argument1,argument2,argument3,argument4).output + feature100(argument1,argument2,argument3,argument4).output + feature101(argument1,argument2,argument3,argument4).output + feature102(argument1,argument2,argument3,argument4).output + feature103(argument1,argument2,argument3,argument4).output + feature104(argument1,argument2,argument3,argument4).output + feature105(argument1,argument2,argument3,argument4).output + feature106(argument1,argument2,argument3,argument4).output + feature107(argument1,argument2,argument3,argument4).output + feature108(argument1,argument2,argument3,argument4).output + feature109(argument1,argument2,argument3,argument4).output + feature110(argument1,argument2,argument3,argument4).output + feature111(argument1,argument2,argument3,argument4).output + feature112(argument1,argument2,argument3,argument4).output + feature113(argument1,argument2,argument3,argument4).output + feature114(argument1,argument2,argument3,argument4).output + feature115(argument1,argument2,argument3,argument4).output + feature116(argument1,argument2,argument3,argument4).output + feature117(argument1,argument2,argument3,argument4).output + feature118(argument1,argument2,argument3,argument4).output + feature119(argument1,argument2,argument3,argument4).output + feature120(argument1,argument2,argument3,argument4).output + feature121(argument1,argument2,argument3,argument4).output + feature122(argument1,argument2,argument3,argument4).output + feature123(argument1,argument2,argument3,argument4).output + feature124(argument1,argument2,argument3,argument4).output + feature125(argument1,argument2,argument3,argument4).output + feature126(argument1,argument2,argument3,argument4).output + feature127(argument1,argument2,argument3,argument4).output + feature128(argument1,argument2,argument3,argument4).output + feature129(argument1,argument2,argument3,argument4).output + feature130(argument1,argument2,argument3,argument4).output + feature131(argument1,argument2,argument3,argument4).output + feature132(argument1,argument2,argument3,argument4).output + feature133(argument1,argument2,argument3,argument4).output + feature134(argument1,argument2,argument3,argument4).output + feature135(argument1,argument2,argument3,argument4).output + feature136(argument1,argument2,argument3,argument4).output + feature137(argument1,argument2,argument3,argument4).output + feature138(argument1,argument2,argument3,argument4).output + feature139(argument1,argument2,argument3,argument4).output + feature140(argument1,argument2,argument3,argument4).output + feature141(argument1,argument2,argument3,argument4).output + feature142(argument1,argument2,argument3,argument4).output + feature143(argument1,argument2,argument3,argument4).output + feature144(argument1,argument2,argument3,argument4).output + feature145(argument1,argument2,argument3,argument4).output + feature146(argument1,argument2,argument3,argument4).output + feature147(argument1,argument2,argument3,argument4).output + feature148(argument1,argument2,argument3,argument4).output + feature149(argument1,argument2,argument3,argument4).output + feature150(argument1,argument2,argument3,argument4).output + feature151(argument1,argument2,argument3,argument4).output + feature152(argument1,argument2,argument3,argument4).output + feature153(argument1,argument2,argument3,argument4).output + feature154(argument1,argument2,argument3,argument4).output + feature155(argument1,argument2,argument3,argument4).output + feature156(argument1,argument2,argument3,argument4).output + feature157(argument1,argument2,argument3,argument4).output + feature158(argument1,argument2,argument3,argument4).output + feature159(argument1,argument2,argument3,argument4).output + feature160(argument1,argument2,argument3,argument4).output + feature161(argument1,argument2,argument3,argument4).output + feature162(argument1,argument2,argument3,argument4).output + feature163(argument1,argument2,argument3,argument4).output + feature164(argument1,argument2,argument3,argument4).output + feature165(argument1,argument2,argument3,argument4).output + feature166(argument1,argument2,argument3,argument4).output + feature167(argument1,argument2,argument3,argument4).output + feature168(argument1,argument2,argument3,argument4).output + feature169(argument1,argument2,argument3,argument4).output + feature170(argument1,argument2,argument3,argument4).output + feature171(argument1,argument2,argument3,argument4).output + feature172(argument1,argument2,argument3,argument4).output + feature173(argument1,argument2,argument3,argument4).output + feature174(argument1,argument2,argument3,argument4).output + feature175(argument1,argument2,argument3,argument4).output + feature176(argument1,argument2,argument3,argument4).output + feature177(argument1,argument2,argument3,argument4).output + feature178(argument1,argument2,argument3,argument4).output + feature179(argument1,argument2,argument3,argument4).output + feature180(argument1,argument2,argument3,argument4).output + feature181(argument1,argument2,argument3,argument4).output + feature182(argument1,argument2,argument3,argument4).output + feature183(argument1,argument2,argument3,argument4).output + feature184(argument1,argument2,argument3,argument4).output + feature185(argument1,argument2,argument3,argument4).output + feature186(argument1,argument2,argument3,argument4).output + feature187(argument1,argument2,argument3,argument4).output + feature188(argument1,argument2,argument3,argument4).output + feature189(argument1,argument2,argument3,argument4).output + feature190(argument1,argument2,argument3,argument4).output + feature191(argument1,argument2,argument3,argument4).output + feature192(argument1,argument2,argument3,argument4).output + feature193(argument1,argument2,argument3,argument4).output + feature194(argument1,argument2,argument3,argument4).output + feature195(argument1,argument2,argument3,argument4).output + feature196(argument1,argument2,argument3,argument4).output + feature197(argument1,argument2,argument3,argument4).output + feature198(argument1,argument2,argument3,argument4).output + feature199(argument1,argument2,argument3,argument4).output + feature200(argument1,argument2,argument3,argument4).output + feature201(argument1,argument2,argument3,argument4).output + feature202(argument1,argument2,argument3,argument4).output + feature203(argument1,argument2,argument3,argument4).output + feature204(argument1,argument2,argument3,argument4).output + feature205(argument1,argument2,argument3,argument4).output + feature206(argument1,argument2,argument3,argument4).output + feature207(argument1,argument2,argument3,argument4).output + feature208(argument1,argument2,argument3,argument4).output + feature209(argument1,argument2,argument3,argument4).output + feature210(argument1,argument2,argument3,argument4).output + feature211(argument1,argument2,argument3,argument4).output + feature212(argument1,argument2,argument3,argument4).output + feature213(argument1,argument2,argument3,argument4).output + feature214(argument1,argument2,argument3,argument4).output + feature215(argument1,argument2,argument3,argument4).output + feature216(argument1,argument2,argument3,argument4).output + feature217(argument1,argument2,argument3,argument4).output + feature218(argument1,argument2,argument3,argument4).output + feature219(argument1,argument2,argument3,argument4).output + feature220(argument1,argument2,argument3,argument4).output + feature221(argument1,argument2,argument3,argument4).output + feature222(argument1,argument2,argument3,argument4).output + feature223(argument1,argument2,argument3,argument4).output + feature224(argument1,argument2,argument3,argument4).output + feature225(argument1,argument2,argument3,argument4).output + feature226(argument1,argument2,argument3,argument4).output + feature227(argument1,argument2,argument3,argument4).output + feature228(argument1,argument2,argument3,argument4).output + feature229(argument1,argument2,argument3,argument4).output + feature230(argument1,argument2,argument3,argument4).output + feature231(argument1,argument2,argument3,argument4).output + feature232(argument1,argument2,argument3,argument4).output + feature233(argument1,argument2,argument3,argument4).output + feature234(argument1,argument2,argument3,argument4).output + feature235(argument1,argument2,argument3,argument4).output + feature236(argument1,argument2,argument3,argument4).output + feature237(argument1,argument2,argument3,argument4).output + feature238(argument1,argument2,argument3,argument4).output + feature239(argument1,argument2,argument3,argument4).output + feature240(argument1,argument2,argument3,argument4).output + feature241(argument1,argument2,argument3,argument4).output + feature242(argument1,argument2,argument3,argument4).output + feature243(argument1,argument2,argument3,argument4).output + feature244(argument1,argument2,argument3,argument4).output + feature245(argument1,argument2,argument3,argument4).output + feature246(argument1,argument2,argument3,argument4).output + feature247(argument1,argument2,argument3,argument4).output + feature248(argument1,argument2,argument3,argument4).output + feature249(argument1,argument2,argument3,argument4).output + feature250(argument1,argument2,argument3,argument4).output + feature251(argument1,argument2,argument3,argument4).output + feature252(argument1,argument2,argument3,argument4).output + feature253(argument1,argument2,argument3,argument4).output + feature254(argument1,argument2,argument3,argument4).output + feature255(argument1,argument2,argument3,argument4).output + feature256(argument1,argument2,argument3,argument4).output + feature257(argument1,argument2,argument3,argument4).output + feature258(argument1,argument2,argument3,argument4).output + feature259(argument1,argument2,argument3,argument4).output + feature260(argument1,argument2,argument3,argument4).output + feature261(argument1,argument2,argument3,argument4).output + feature262(argument1,argument2,argument3,argument4).output + feature263(argument1,argument2,argument3,argument4).output + feature264(argument1,argument2,argument3,argument4).output + feature265(argument1,argument2,argument3,argument4).output + feature266(argument1,argument2,argument3,argument4).output + feature267(argument1,argument2,argument3,argument4).output + feature268(argument1,argument2,argument3,argument4).output + feature269(argument1,argument2,argument3,argument4).output + feature270(argument1,argument2,argument3,argument4).output + feature271(argument1,argument2,argument3,argument4).output + feature272(argument1,argument2,argument3,argument4).output + feature273(argument1,argument2,argument3,argument4).output + feature274(argument1,argument2,argument3,argument4).output + feature275(argument1,argument2,argument3,argument4).output + feature276(argument1,argument2,argument3,argument4).output + feature277(argument1,argument2,argument3,argument4).output + feature278(argument1,argument2,argument3,argument4).output + feature279(argument1,argument2,argument3,argument4).output + feature280(argument1,argument2,argument3,argument4).output + feature281(argument1,argument2,argument3,argument4).output + feature282(argument1,argument2,argument3,argument4).output + feature283(argument1,argument2,argument3,argument4).output + feature284(argument1,argument2,argument3,argument4).output + feature285(argument1,argument2,argument3,argument4).output + feature286(argument1,argument2,argument3,argument4).output + feature287(argument1,argument2,argument3,argument4).output + feature288(argument1,argument2,argument3,argument4).output + feature289(argument1,argument2,argument3,argument4).output + feature290(argument1,argument2,argument3,argument4).output + feature291(argument1,argument2,argument3,argument4).output + feature292(argument1,argument2,argument3,argument4).output + feature293(argument1,argument2,argument3,argument4).output + feature294(argument1,argument2,argument3,argument4).output + feature295(argument1,argument2,argument3,argument4).output + feature296(argument1,argument2,argument3,argument4).output + feature297(argument1,argument2,argument3,argument4).output + feature298(argument1,argument2,argument3,argument4).output + feature299(argument1,argument2,argument3,argument4).output + feature300(argument1,argument2,argument3,argument4).output"
+rankprofile[3].fef.property[2].name "vespa.rank.secondphase"
+rankprofile[3].fef.property[2].value "rankingExpression(secondphase)"
+rankprofile[3].fef.property[3].name "rankingExpression(secondphase).rankingScript"
+rankprofile[3].fef.property[3].value "exp(0) + mysum(attribute(foo),\"attribute( bar )\",\"attribute( \\\"baz\\\" )\")"
+rankprofile[3].fef.property[4].name "vespa.hitcollector.heapsize"
+rankprofile[3].fef.property[4].value "101"
+rankprofile[3].fef.property[5].name "vespa.hitcollector.arraysize"
+rankprofile[3].fef.property[5].value "201"
+rankprofile[3].fef.property[6].name "vespa.hitcollector.rankscoredroplimit"
+rankprofile[3].fef.property[6].value "501.5"
+rankprofile[4].name "duplicates"
+rankprofile[4].fef.property[0].name "fieldMatch(a).proximityLimit"
+rankprofile[4].fef.property[0].value "4"
+rankprofile[4].fef.property[1].name "fieldMatch(a).proximityTable"
+rankprofile[4].fef.property[1].value "0.2"
+rankprofile[4].fef.property[2].name "fieldMatch(a).proximityTable"
+rankprofile[4].fef.property[2].value "0.4"
+rankprofile[4].fef.property[3].name "fieldMatch(a).proximityTable"
+rankprofile[4].fef.property[3].value "0.6"
+rankprofile[4].fef.property[4].name "fieldMatch(a).proximityTable"
+rankprofile[4].fef.property[4].value "0.8"
+rankprofile[4].fef.property[5].name "fieldMatch(a).proximityTable"
+rankprofile[4].fef.property[5].value "1"
+rankprofile[4].fef.property[6].name "fieldMatch(a).proximityTable"
+rankprofile[4].fef.property[6].value "0.8"
+rankprofile[4].fef.property[7].name "fieldMatch(a).proximityTable"
+rankprofile[4].fef.property[7].value "0.6"
+rankprofile[4].fef.property[8].name "fieldMatch(a).proximityTable"
+rankprofile[4].fef.property[8].value "0.4"
+rankprofile[4].fef.property[9].name "fieldMatch(a).proximityTable"
+rankprofile[4].fef.property[9].value "0.2"
+rankprofile[5].name "whitespace1"
+rankprofile[5].fef.property[0].name "vespa.rank.firstphase"
+rankprofile[5].fef.property[0].value "rankingExpression(firstphase)"
+rankprofile[5].fef.property[1].name "rankingExpression(firstphase).rankingScript"
+rankprofile[5].fef.property[1].value "1"
+rankprofile[6].name "whitespace2"
+rankprofile[6].fef.property[0].name "vespa.rank.firstphase"
+rankprofile[6].fef.property[0].value "rankingExpression(firstphase)"
+rankprofile[6].fef.property[1].name "rankingExpression(firstphase).rankingScript"
+rankprofile[6].fef.property[1].value "1"
+rankprofile[7].name "macros"
+rankprofile[7].fef.property[0].name "rankingExpression(fourtimessum).rankingScript"
+rankprofile[7].fef.property[0].value "4 * (var1 + var2)"
+rankprofile[7].fef.property[1].name "rankingExpression(myfeature).rankingScript"
+rankprofile[7].fef.property[1].value "70 * fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness,2) + 30 * pow(0 - fieldMatch(description).earliness,2)"
+rankprofile[7].fef.property[2].name "rankingExpression(fourtimessum@5cf279212355b980.67f1e87166cfef86).rankingScript"
+rankprofile[7].fef.property[2].value "4 * (match + rankBoost)"
+rankprofile[7].fef.property[3].name "vespa.rank.firstphase"
+rankprofile[7].fef.property[3].value "rankingExpression(firstphase)"
+rankprofile[7].fef.property[4].name "rankingExpression(firstphase).rankingScript"
+rankprofile[7].fef.property[4].value "match + fieldMatch(title) + rankingExpression(myfeature)"
+rankprofile[7].fef.property[5].name "vespa.rank.secondphase"
+rankprofile[7].fef.property[5].value "rankingExpression(fourtimessum@5cf279212355b980.67f1e87166cfef86)"
+rankprofile[7].fef.property[6].name "vespa.summary.feature"
+rankprofile[7].fef.property[6].value "fieldMatch(title)"
+rankprofile[8].name "macros2"
+rankprofile[8].fef.property[0].name "foo"
+rankprofile[8].fef.property[0].value "some, list"
+rankprofile[8].fef.property[1].name "rankingExpression(fourtimessum).rankingScript"
+rankprofile[8].fef.property[1].value "4 * (var1 + var2)"
+rankprofile[8].fef.property[2].name "rankingExpression(myfeature).rankingScript"
+rankprofile[8].fef.property[2].value "70 * fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness,2) + 30 * pow(0 - fieldMatch(description).earliness,2)"
+rankprofile[8].fef.property[3].name "rankingExpression(mysummaryfeature).rankingScript"
+rankprofile[8].fef.property[3].value "70 * fieldMatch(title).completeness"
+rankprofile[8].fef.property[4].name "rankingExpression(mysummaryfeature2).rankingScript"
+rankprofile[8].fef.property[4].value "71 * fieldMatch(title).completeness"
+rankprofile[8].fef.property[5].name "rankingExpression(fourtimessum@2b1138e8965e7ff5.67f1e87166cfef86).rankingScript"
+rankprofile[8].fef.property[5].value "4 * (match + match)"
+rankprofile[8].fef.property[6].name "vespa.rank.firstphase"
+rankprofile[8].fef.property[6].value "classicRank"
+rankprofile[8].fef.property[7].name "vespa.rank.secondphase"
+rankprofile[8].fef.property[7].value "rankingExpression(secondphase)"
+rankprofile[8].fef.property[8].name "rankingExpression(secondphase).rankingScript"
+rankprofile[8].fef.property[8].value "rankingExpression(fourtimessum@2b1138e8965e7ff5.67f1e87166cfef86) + rankingExpression(mysummaryfeature) + rankingExpression(myfeature)"
+rankprofile[8].fef.property[9].name "vespa.summary.feature"
+rankprofile[8].fef.property[9].value "rankingExpression(mysummaryfeature2)"
+rankprofile[8].fef.property[10].name "vespa.summary.feature"
+rankprofile[8].fef.property[10].value "rankingExpression(mysummaryfeature)"
+rankprofile[9].name "macros3"
+rankprofile[9].fef.property[0].name "rankingExpression(onlyusedinsummaryfeature).rankingScript"
+rankprofile[9].fef.property[0].value "5"
+rankprofile[9].fef.property[1].name "vespa.summary.feature"
+rankprofile[9].fef.property[1].value "rankingExpression(matches(title,rankingExpression(onlyusedinsummaryfeature)))"
+rankprofile[10].name "macros3-inherited"
+rankprofile[10].fef.property[0].name "rankingExpression(onlyusedinsummaryfeature).rankingScript"
+rankprofile[10].fef.property[0].value "5"
+rankprofile[10].fef.property[1].name "vespa.summary.feature"
+rankprofile[10].fef.property[1].value "rankingExpression(matches(title,rankingExpression(onlyusedinsummaryfeature)))"
+rankprofile[11].name "macros-inherited"
+rankprofile[11].fef.property[0].name "foo"
+rankprofile[11].fef.property[0].value "some, list"
+rankprofile[11].fef.property[1].name "rankingExpression(fourtimessum).rankingScript"
+rankprofile[11].fef.property[1].value "4 * (var1 + var2)"
+rankprofile[11].fef.property[2].name "rankingExpression(myfeature).rankingScript"
+rankprofile[11].fef.property[2].value "70 * fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness,2) + 30 * pow(0 - fieldMatch(description).earliness,2)"
+rankprofile[11].fef.property[3].name "rankingExpression(mysummaryfeature).rankingScript"
+rankprofile[11].fef.property[3].value "80 * fieldMatch(title).completeness"
+rankprofile[11].fef.property[4].name "rankingExpression(mysummaryfeature2).rankingScript"
+rankprofile[11].fef.property[4].value "71 * fieldMatch(title).completeness"
+rankprofile[11].fef.property[5].name "rankingExpression(fourtimessum@2b1138e8965e7ff5.67f1e87166cfef86).rankingScript"
+rankprofile[11].fef.property[5].value "4 * (match + match)"
+rankprofile[11].fef.property[6].name "vespa.rank.firstphase"
+rankprofile[11].fef.property[6].value "rankingExpression(firstphase)"
+rankprofile[11].fef.property[7].name "rankingExpression(firstphase).rankingScript"
+rankprofile[11].fef.property[7].value "20000 * rankingExpression(myfeature) + rankingExpression(mysummaryfeature)"
+rankprofile[11].fef.property[8].name "vespa.rank.secondphase"
+rankprofile[11].fef.property[8].value "rankingExpression(secondphase)"
+rankprofile[11].fef.property[9].name "rankingExpression(secondphase).rankingScript"
+rankprofile[11].fef.property[9].value "rankingExpression(fourtimessum@2b1138e8965e7ff5.67f1e87166cfef86) + rankingExpression(mysummaryfeature) + rankingExpression(myfeature)"
+rankprofile[11].fef.property[10].name "vespa.summary.feature"
+rankprofile[11].fef.property[10].value "rankingExpression(mysummaryfeature2)"
+rankprofile[11].fef.property[11].name "vespa.summary.feature"
+rankprofile[11].fef.property[11].value "rankingExpression(mysummaryfeature)"
+rankprofile[12].name "macros-inherited2"
+rankprofile[12].fef.property[0].name "foo"
+rankprofile[12].fef.property[0].value "some, list"
+rankprofile[12].fef.property[1].name "rankingExpression(fourtimessum).rankingScript"
+rankprofile[12].fef.property[1].value "4 * (var1 + var2)"
+rankprofile[12].fef.property[2].name "rankingExpression(myfeature).rankingScript"
+rankprofile[12].fef.property[2].value "70 * fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness,2) + 30 * pow(0 - fieldMatch(description).earliness,2)"
+rankprofile[12].fef.property[3].name "rankingExpression(mysummaryfeature).rankingScript"
+rankprofile[12].fef.property[3].value "80 * fieldMatch(title).completeness"
+rankprofile[12].fef.property[4].name "rankingExpression(mysummaryfeature2).rankingScript"
+rankprofile[12].fef.property[4].value "71 * fieldMatch(title).completeness"
+rankprofile[12].fef.property[5].name "rankingExpression(fourtimessum@2b1138e8965e7ff5.67f1e87166cfef86).rankingScript"
+rankprofile[12].fef.property[5].value "4 * (match + match)"
+rankprofile[12].fef.property[6].name "vespa.rank.firstphase"
+rankprofile[12].fef.property[6].value "rankingExpression(firstphase)"
+rankprofile[12].fef.property[7].name "rankingExpression(firstphase).rankingScript"
+rankprofile[12].fef.property[7].value "30000 * rankingExpression(mysummaryfeature) + rankingExpression(myfeature)"
+rankprofile[12].fef.property[8].name "vespa.rank.secondphase"
+rankprofile[12].fef.property[8].value "rankingExpression(secondphase)"
+rankprofile[12].fef.property[9].name "rankingExpression(secondphase).rankingScript"
+rankprofile[12].fef.property[9].value "rankingExpression(fourtimessum@2b1138e8965e7ff5.67f1e87166cfef86) + rankingExpression(mysummaryfeature) + rankingExpression(myfeature)"
+rankprofile[12].fef.property[10].name "vespa.summary.feature"
+rankprofile[12].fef.property[10].value "rankingExpression(mysummaryfeature2)"
+rankprofile[12].fef.property[11].name "vespa.summary.feature"
+rankprofile[12].fef.property[11].value "rankingExpression(mysummaryfeature)"
+rankprofile[13].name "macros-inherited3"
+rankprofile[13].fef.property[0].name "foo"
+rankprofile[13].fef.property[0].value "some, list"
+rankprofile[13].fef.property[1].name "rankingExpression(fourtimessum).rankingScript"
+rankprofile[13].fef.property[1].value "4 * (var1 + var2)"
+rankprofile[13].fef.property[2].name "rankingExpression(myfeature).rankingScript"
+rankprofile[13].fef.property[2].value "700 * fieldMatch(title).completeness"
+rankprofile[13].fef.property[3].name "rankingExpression(mysummaryfeature).rankingScript"
+rankprofile[13].fef.property[3].value "80 * fieldMatch(title).completeness"
+rankprofile[13].fef.property[4].name "rankingExpression(mysummaryfeature2).rankingScript"
+rankprofile[13].fef.property[4].value "71 * fieldMatch(title).completeness"
+rankprofile[13].fef.property[5].name "vespa.rank.firstphase"
+rankprofile[13].fef.property[5].value "rankingExpression(firstphase)"
+rankprofile[13].fef.property[6].name "rankingExpression(firstphase).rankingScript"
+rankprofile[13].fef.property[6].value "30000 * rankingExpression(mysummaryfeature) + rankingExpression(myfeature)"
+rankprofile[13].fef.property[7].name "vespa.rank.secondphase"
+rankprofile[13].fef.property[7].value "rankingExpression(secondphase)"
+rankprofile[13].fef.property[8].name "rankingExpression(secondphase).rankingScript"
+rankprofile[13].fef.property[8].value "40000 * rankingExpression(mysummaryfeature) + rankingExpression(myfeature)"
+rankprofile[13].fef.property[9].name "vespa.summary.feature"
+rankprofile[13].fef.property[9].value "rankingExpression(mysummaryfeature2)"
+rankprofile[13].fef.property[10].name "vespa.summary.feature"
+rankprofile[13].fef.property[10].value "rankingExpression(mysummaryfeature)"
+rankprofile[14].name "macros-refering-macros"
+rankprofile[14].fef.property[0].name "rankingExpression(m1).rankingScript"
+rankprofile[14].fef.property[0].value "700 * fieldMatch(title).completeness"
+rankprofile[14].fef.property[1].name "rankingExpression(m2).rankingScript"
+rankprofile[14].fef.property[1].value "rankingExpression(m1) * 67"
+rankprofile[14].fef.property[2].name "rankingExpression(m4).rankingScript"
+rankprofile[14].fef.property[2].value "703 * fieldMatch(fromfile).completeness"
+rankprofile[14].fef.property[3].name "vespa.rank.secondphase"
+rankprofile[14].fef.property[3].value "rankingExpression(secondphase)"
+rankprofile[14].fef.property[4].name "rankingExpression(secondphase).rankingScript"
+rankprofile[14].fef.property[4].value "40000 * rankingExpression(m2)"
+rankprofile[15].name "macros-refering-macros-inherited"
+rankprofile[15].fef.property[0].name "rankingExpression(m1).rankingScript"
+rankprofile[15].fef.property[0].value "700 * fieldMatch(title).completeness"
+rankprofile[15].fef.property[1].name "rankingExpression(m2).rankingScript"
+rankprofile[15].fef.property[1].value "rankingExpression(m1) * 67"
+rankprofile[15].fef.property[2].name "rankingExpression(m4).rankingScript"
+rankprofile[15].fef.property[2].value "701 * fieldMatch(title).completeness"
+rankprofile[15].fef.property[3].name "rankingExpression(m3).rankingScript"
+rankprofile[15].fef.property[3].value "if (isNan(attribute(nrtgmp)) == 1, 0.0, rankingExpression(m2))"
+rankprofile[15].fef.property[4].name "vespa.rank.secondphase"
+rankprofile[15].fef.property[4].value "rankingExpression(secondphase)"
+rankprofile[15].fef.property[5].name "rankingExpression(secondphase).rankingScript"
+rankprofile[15].fef.property[5].value "3000 * rankingExpression(m2)"
+rankprofile[16].name "macros-refering-macros-inherited2"
+rankprofile[16].fef.property[0].name "rankingExpression(m1).rankingScript"
+rankprofile[16].fef.property[0].value "700 * fieldMatch(title).completeness"
+rankprofile[16].fef.property[1].name "rankingExpression(m2).rankingScript"
+rankprofile[16].fef.property[1].value "rankingExpression(m1) * 67"
+rankprofile[16].fef.property[2].name "rankingExpression(m4).rankingScript"
+rankprofile[16].fef.property[2].value "703 * fieldMatch(fromfile).completeness"
+rankprofile[16].fef.property[3].name "vespa.rank.secondphase"
+rankprofile[16].fef.property[3].value "rankingExpression(secondphase)"
+rankprofile[16].fef.property[4].name "rankingExpression(secondphase).rankingScript"
+rankprofile[16].fef.property[4].value "3002 * rankingExpression(m2)"
+rankprofile[17].name "macros-refering-macros-inherited-two-levels"
+rankprofile[17].fef.property[0].name "rankingExpression(m1).rankingScript"
+rankprofile[17].fef.property[0].value "700 * fieldMatch(title).completeness"
+rankprofile[17].fef.property[1].name "rankingExpression(m2).rankingScript"
+rankprofile[17].fef.property[1].value "rankingExpression(m1) * 67"
+rankprofile[17].fef.property[2].name "rankingExpression(m4).rankingScript"
+rankprofile[17].fef.property[2].value "701 * fieldMatch(title).completeness"
+rankprofile[17].fef.property[3].name "rankingExpression(m3).rankingScript"
+rankprofile[17].fef.property[3].value "if (isNan(attribute(nrtgmp)) == 1, 0.0, rankingExpression(m2))"
+rankprofile[17].fef.property[4].name "rankingExpression(m5).rankingScript"
+rankprofile[17].fef.property[4].value "if (isNan(attribute(glmpfw)) == 1, rankingExpression(m1), rankingExpression(m4))"
+rankprofile[17].fef.property[5].name "vespa.rank.secondphase"
+rankprofile[17].fef.property[5].value "rankingExpression(secondphase)"
+rankprofile[17].fef.property[6].name "rankingExpression(secondphase).rankingScript"
+rankprofile[17].fef.property[6].value "3000 * rankingExpression(m2)" \ No newline at end of file
diff --git a/model-inference/src/test/resources/config/rankexpression/rankexpression.sd b/model-inference/src/test/resources/config/rankexpression/rankexpression.sd
new file mode 100644
index 00000000000..d3e0057cfe1
--- /dev/null
+++ b/model-inference/src/test/resources/config/rankexpression/rankexpression.sd
@@ -0,0 +1,327 @@
+# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+search rankexpression {
+
+ document rankexpression {
+
+ field artist type string {
+ indexing: summary | index
+ }
+
+ field title type string {
+ indexing: summary | index
+ }
+
+ field surl type string {
+ indexing: summary
+ }
+
+ field year type int {
+ indexing: summary | attribute
+ }
+
+ field foo1 type int {
+ indexing: attribute
+ }
+
+ field foo2 type int {
+ indexing: attribute
+ }
+
+ field foo3 type int {
+ indexing: attribute
+ }
+
+ field foo4 type int {
+ indexing: attribute
+ }
+
+ field bar1 type int {
+ indexing: attribute
+ }
+
+ field bar2 type int {
+ indexing: attribute
+ }
+
+ field bar3 type int {
+ indexing: attribute
+ }
+
+ field bar4 type int {
+ indexing: attribute
+ }
+
+ }
+
+ rank-profile default {
+ first-phase {
+ expression: classicRank
+ keep-rank-count: 20
+ rank-score-drop-limit: -0.5
+ }
+ second-phase {
+ expression: if(3>2,4,2)
+ rerank-count: 10
+ }
+ rank-features: attribute(foo1).out attribute(bar1)
+ rank-features { attribute(foo2).out attribute(bar2).out }
+ rank-features {
+ attribute(foo3).out attribute(bar3).out }
+ rank-features {
+ attribute(foo4).out
+ attribute(bar4).out
+ }
+ ignore-default-rank-features
+
+ rank-properties {
+ foo: "bar, baz"
+ qux: "quux"
+ foo: "foobar"
+ foo.bar: "foo.bar"
+ foo.bar.baz: 123
+ foo ( bar ) . baz.2 : 123.4
+ foo(bar).baz.qux: "foo(bar)"
+ "nud":"ity"
+ }
+
+ }
+
+ rank-profile static {
+ first-phase {
+ expression { attribute }
+ }
+ second-phase {
+ expression {
+ file:rankexpression
+ }
+ }
+ summary-features: attribute(foo1).out attribute(bar1)
+ summary-features { attribute(foo2).out attribute(bar2).out }
+ summary-features {
+ attribute(foo3).out attribute(bar3).out }
+ summary-features {
+ attribute(foo4).out
+ attribute(bar4).out
+ }
+ }
+
+ rank-profile overflow {
+ first-phase {
+ expression: file:overflow.expression
+ keep-rank-count: 201
+ rank-score-drop-limit: 501.5
+ }
+ second-phase {
+ expression {
+ exp(0) +
+ mysum(attribute(foo),
+ "attribute( bar )",
+ "attribute( \"baz\" )")
+ }
+ rerank-count: 101
+ }
+ }
+
+ rank-profile duplicates {
+ rank-properties {
+ fieldMatch(a).proximityLimit: 4
+ fieldMatch(a).proximityTable: 0.2
+ fieldMatch(a).proximityTable: 0.4
+ fieldMatch(a).proximityTable: 0.6
+ fieldMatch(a).proximityTable: 0.8
+ fieldMatch(a).proximityTable: 1
+ fieldMatch(a).proximityTable: 0.8
+ fieldMatch(a).proximityTable: 0.6
+ fieldMatch(a).proximityTable: 0.4
+ fieldMatch(a).proximityTable: 0.2
+ }
+ }
+
+ rank-profile whitespace1 {
+ first-phase {
+ expression
+ {
+
+ 1
+ }}}
+
+ rank-profile whitespace2 {
+ first-phase
+ {
+ expression { 1 }
+ }
+ }
+
+ rank-profile macros {
+ first-phase {
+ expression: match + fieldMatch(title) + myfeature
+ }
+ second-phase {
+ expression: fourtimessum(match,rankBoost)
+ }
+ macro fourtimessum(var1, var2) {
+ expression: 4*(var1+var2)
+ }
+ macro myfeature() {
+ expression {
+ 70 * fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness, 2) +
+ 30 * pow(0 - fieldMatch(description).earliness, 2)
+ }
+ }
+ summary-features {
+ fieldMatch(title)
+ }
+ }
+
+ rank-profile macros2 {
+ first-phase {
+ expression: classicRank
+ }
+ rank-properties {
+ foo: "some, list"
+ }
+
+ second-phase {
+ expression: fourtimessum(match,match) + mysummaryfeature + myfeature
+ }
+ macro fourtimessum(var1, var2) {
+ expression: 4*(var1+var2)
+ }
+ macro myfeature() {
+ expression {
+ 70 * fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness, 2) +
+ 30 * pow(0 - fieldMatch(description).earliness, 2)
+ }
+ }
+ macro mysummaryfeature() {
+ expression {
+ 70 * fieldMatch(title).completeness
+ }
+ }
+ macro mysummaryfeature2() {
+ expression {
+ 71 * fieldMatch(title).completeness
+ }
+ }
+ summary-features {
+ mysummaryfeature
+ rankingExpression(mysummaryfeature2) # Required form earlier
+ }
+ }
+
+ rank-profile macros3 {
+ macro onlyusedinsummaryfeature() {
+ expression: 5
+ }
+ summary-features {
+ rankingExpression(matches(title,rankingExpression(onlyusedinsummaryfeature)))
+ }
+
+ }
+
+ rank-profile macros3-inherited inherits macros3 {
+ summary-features {
+ rankingExpression(matches(title,rankingExpression(onlyusedinsummaryfeature)))
+ }
+ }
+
+ rank-profile macros-inherited inherits macros2 {
+ macro mysummaryfeature() {
+ expression {
+ 80 * fieldMatch(title).completeness
+ }
+ }
+ first-phase {
+ expression {
+ 20000 * myfeature + mysummaryfeature
+ }
+ }
+ }
+
+ rank-profile macros-inherited2 inherits macros-inherited {
+ first-phase {
+ expression {
+ 30000 * mysummaryfeature + myfeature
+ }
+ }
+ }
+
+ rank-profile macros-inherited3 inherits macros-inherited2 {
+ macro myfeature() {
+ expression {
+ 700 * fieldMatch(title).completeness
+ }
+ }
+ second-phase {
+ expression {
+ 40000 * mysummaryfeature + myfeature
+ }
+ }
+ }
+
+ rank-profile macros-refering-macros {
+ macro m2() {
+ expression: m1 * 67
+ }
+
+ macro m1() {
+ expression {
+ 700 * fieldMatch(title).completeness
+ }
+ }
+
+ macro m4() {
+ expression: file:macro.expression
+ }
+
+ second-phase {
+ expression {
+ 40000 * m2
+ }
+ }
+
+ }
+
+ rank-profile macros-refering-macros-inherited inherits macros-refering-macros {
+ macro m3() {
+ expression {
+ if(isNan(attribute(nrtgmp))==1,
+ 0.0,
+ (m2)
+ )
+ }
+ }
+ macro m4() {
+ expression {
+ 701 * fieldMatch(title).completeness
+ }
+ }
+ second-phase {
+ expression {
+ 3000 * m2
+ }
+ }
+ }
+
+ rank-profile macros-refering-macros-inherited2 inherits macros-refering-macros {
+ second-phase {
+ expression {
+ 3002 * m2
+ }
+ }
+ }
+
+ rank-profile macros-refering-macros-inherited-two-levels inherits macros-refering-macros-inherited {
+ macro m5() {
+ expression {
+ if(isNan(attribute(glmpfw))==1,
+ m1,
+ (m4)
+ )
+ }
+ }
+ }
+
+}
+
+
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 8cd877b25e5..43fb11497c0 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
@@ -76,7 +76,6 @@ public class DockerOperationsImpl implements DockerOperations {
containerName,
node.getHostname())
.withManagedBy(MANAGER_NAME)
- .withEnvironment("CONFIG_SERVER_ADDRESS", configServers) // TODO: Remove when all images support VESPA_CONFIGSERVERS
.withEnvironment("VESPA_CONFIGSERVERS", configServers)
.withEnvironment("CONTAINER_ENVIRONMENT_SETTINGS",
environment.getContainerEnvironmentResolver().createSettings(environment, node))
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 a7bf22591d4..ea92ca9b56f 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
@@ -200,6 +200,7 @@ public class StorageMaintainer {
Process duCommand = new ProcessBuilder().command(command).start();
if (!duCommand.waitFor(60, TimeUnit.SECONDS)) {
duCommand.destroy();
+ duCommand.waitFor();
throw new RuntimeException("Disk usage command timed out, aborting.");
}
String output = IOUtils.readAll(new InputStreamReader(duCommand.getInputStream()));
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java
index ff85c49bb13..a8403b8b10d 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java
@@ -7,6 +7,7 @@ import com.yahoo.vespa.athenz.api.AthenzService;
import com.yahoo.vespa.athenz.client.zts.DefaultZtsClient;
import com.yahoo.vespa.athenz.client.zts.InstanceIdentity;
import com.yahoo.vespa.athenz.client.zts.ZtsClient;
+import com.yahoo.vespa.athenz.client.zts.ZtsClientException;
import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider;
import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper;
import com.yahoo.vespa.athenz.identityprovider.api.IdentityDocumentClient;
@@ -142,6 +143,8 @@ public class AthenzCredentialsMaintainer {
log.info(String.format("Deleted private key file (path=%s)", privateKeyFile));
if (Files.deleteIfExists(certificateFile))
log.info(String.format("Deleted certificate file (path=%s)", certificateFile));
+ if (Files.deleteIfExists(identityDocumentFile))
+ log.info(String.format("Deleted identity document file (path=%s)", certificateFile));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
@@ -201,6 +204,12 @@ public class AthenzCredentialsMaintainer {
csr);
writePrivateKeyAndCertificate(keyPair.getPrivate(), instanceIdentity.certificate());
log.info("Instance successfully refreshed and credentials written to file");
+ } catch (ZtsClientException e) {
+ // TODO Find out why certificate was revoked and hopefully remove this workaround
+ if (e.getErrorCode() == 403 && e.getDescription().startsWith("Certificate revoked")) {
+ log.error("Certificate cannot be refreshed as it is revoked by ZTS - re-registering the instance now", e);
+ registerIdentity();
+ }
} catch (IOException e) {
throw new UncheckedIOException(e);
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/Template.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/Template.java
index 6092cc1f038..14fea240baa 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/Template.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/Template.java
@@ -13,7 +13,7 @@ import java.nio.file.Path;
* Uses the Velocity engine to render a template, to and from both String and Path objects.
*
* @author hakonhall
- * @author jvenstad
+ * @author jonmv
*/
public class Template {
diff --git a/node-maintainer/pom.xml b/node-maintainer/pom.xml
index 743e92f7f7d..f4e1b399cfa 100644
--- a/node-maintainer/pom.xml
+++ b/node-maintainer/pom.xml
@@ -71,7 +71,6 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
- <version>18.0</version>
</dependency>
</dependencies>
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java
index 5024296b0a7..567ba4708f7 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java
@@ -245,11 +245,6 @@ public class NodeRepository extends AbstractComponent {
}
}
- /** Get default flavor override for an application, if present. */
- public Optional<String> getDefaultFlavorOverride(ApplicationId applicationId) {
- return db.getDefaultFlavorForApplication(applicationId);
- }
-
public NodeFlavors getAvailableFlavors() {
return flavors;
}
@@ -661,4 +656,5 @@ public class NodeRepository extends AbstractComponent {
private Mutex lock(Node node) {
return node.allocation().isPresent() ? lock(node.allocation().get().owner()) : lockUnallocated();
}
+
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ApplicationMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ApplicationMaintainer.java
index 1d894f80eca..3b7c4857f48 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ApplicationMaintainer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ApplicationMaintainer.java
@@ -5,6 +5,7 @@ import com.yahoo.concurrent.DaemonThreadFactory;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Deployer;
import com.yahoo.config.provision.Deployment;
+import com.yahoo.log.LogLevel;
import com.yahoo.transaction.Mutex;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
@@ -16,7 +17,6 @@ import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
-import java.util.logging.Level;
import java.util.stream.Collectors;
/**
@@ -27,7 +27,10 @@ public abstract class ApplicationMaintainer extends Maintainer {
private final Deployer deployer;
- private final Executor deploymentExecutor = Executors.newCachedThreadPool(new DaemonThreadFactory("node repo application maintainer"));
+ // Use a fixed thread pool to avoid overload on config servers. Resource usage when deploying varies
+ // a lot between applications, so doing one by one avoids issues where one or more resource-demanding
+ // deployments happen simultaneously
+ private final Executor deploymentExecutor = Executors.newSingleThreadExecutor(new DaemonThreadFactory("node repo application maintainer"));
protected ApplicationMaintainer(Deployer deployer, NodeRepository nodeRepository, Duration interval, JobControl jobControl) {
super(nodeRepository, interval, jobControl);
@@ -39,24 +42,27 @@ public abstract class ApplicationMaintainer extends Maintainer {
Set<ApplicationId> applications = applicationsNeedingMaintenance();
for (ApplicationId application : applications) {
deploy(application);
- throttle(applications.size());
}
}
+ protected boolean canDeployNow(ApplicationId application) {
+ return true;
+ }
+
/**
* Redeploy this application.
*
- * The default implementation deploys asynchronously to make sure we do all applications timely
+ * The default implementation deploys asynchronously to make sure we do all applications timely
* even when deployments are slow.
*/
protected void deploy(ApplicationId application) {
deploymentExecutor.execute(() -> deployWithLock(application));
}
- /** Block in this method until the next application should be maintained */
- protected abstract void throttle(int applicationCount);
+ protected Deployer deployer() { return deployer; }
- private Set<ApplicationId> applicationsNeedingMaintenance() {
+
+ protected Set<ApplicationId> applicationsNeedingMaintenance() {
return nodesNeedingMaintenance().stream()
.map(node -> node.allocation().get().owner())
.collect(Collectors.toCollection(LinkedHashSet::new));
@@ -70,18 +76,19 @@ public abstract class ApplicationMaintainer extends Maintainer {
/** Redeploy this application. A lock will be taken for the duration of the deployment activation */
final void deployWithLock(ApplicationId application) {
- // An application might change it's state between the time the set of applications is retrieved and the
+ // An application might change its state between the time the set of applications is retrieved and the
// time deployment happens. Lock the application and check if it's still active.
//
// Lock is acquired with a low timeout to reduce the chance of colliding with an external deployment.
try (Mutex lock = nodeRepository().lock(application, Duration.ofSeconds(1))) {
if ( ! isActive(application)) return; // became inactive since deployment was requested
+ if ( ! canDeployNow(application)) return; // redeployment is no longer needed
Optional<Deployment> deployment = deployer.deployFromLocalActive(application);
if ( ! deployment.isPresent()) return; // this will be done at another config server
-
+ log.log(LogLevel.DEBUG, this.getClass().getSimpleName() + " deploying " + application);
deployment.get().activate();
} catch (RuntimeException e) {
- log.log(Level.WARNING, "Exception on maintenance redeploy", e);
+ log.log(LogLevel.WARNING, "Exception on maintenance redeploy", e);
}
}
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 391807ece95..6985206b78e 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
@@ -71,7 +71,7 @@ public class NodeRepositoryMaintenance extends AbstractComponent {
infrastructureVersions = new InfrastructureVersions(nodeRepository.database());
nodeFailer = new NodeFailer(deployer, hostLivenessTracker, serviceMonitor, nodeRepository, durationFromEnv("fail_grace").orElse(defaults.failGrace), clock, orchestrator, throttlePolicyFromEnv("throttle_policy").orElse(defaults.throttlePolicy), metric, jobControl, configserverConfig);
- periodicApplicationMaintainer = new PeriodicApplicationMaintainer(deployer, nodeRepository, durationFromEnv("periodic_redeploy_interval").orElse(defaults.periodicRedeployInterval), jobControl);
+ periodicApplicationMaintainer = new PeriodicApplicationMaintainer(deployer, nodeRepository, defaults.redeployMaintainerInterval, durationFromEnv("periodic_redeploy_interval").orElse(defaults.periodicRedeployInterval), jobControl);
operatorChangeApplicationMaintainer = new OperatorChangeApplicationMaintainer(deployer, nodeRepository, clock, durationFromEnv("operator_change_redeploy_interval").orElse(defaults.operatorChangeRedeployInterval), jobControl);
reservationExpirer = new ReservationExpirer(nodeRepository, clock, durationFromEnv("reservation_expiry").orElse(defaults.reservationExpiry), jobControl);
retiredExpirer = new RetiredExpirer(nodeRepository, orchestrator, deployer, clock, durationFromEnv("retired_interval").orElse(defaults.retiredInterval), durationFromEnv("retired_expiry").orElse(defaults.retiredExpiry), jobControl);
@@ -130,8 +130,11 @@ public class NodeRepositoryMaintenance extends AbstractComponent {
private static class DefaultTimes {
- /** All applications are redeployed with this period */
+ // TODO: Rename, kept now for compatibility reasons, want to change this and corresponding env variable
+ /** Minimum time to wait between deployments by periodic application maintainer*/
private final Duration periodicRedeployInterval;
+ /** Time between each run of maintainer that does periodic redeployment */
+ private final Duration redeployMaintainerInterval;
/** Applications are redeployed after manual operator changes within this time period */
private final Duration operatorChangeRedeployInterval;
@@ -155,6 +158,7 @@ public class NodeRepositoryMaintenance extends AbstractComponent {
DefaultTimes(Zone zone) {
failGrace = Duration.ofMinutes(60);
periodicRedeployInterval = Duration.ofMinutes(30);
+ redeployMaintainerInterval = Duration.ofMinutes(1);
operatorChangeRedeployInterval = Duration.ofMinutes(1);
failedExpirerInterval = Duration.ofMinutes(10);
provisionedExpiry = Duration.ofHours(4);
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainer.java
index 57dee7b0dbc..45db9ea90d9 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainer.java
@@ -53,9 +53,6 @@ public class OperatorChangeApplicationMaintainer extends ApplicationMaintainer {
.anyMatch(event -> event.agent() == Agent.operator && event.at().isAfter(instant));
}
- @Override
- protected void throttle(int applicationCount) { }
-
/**
* Deploy in the maintenance thread to avoid scheduling multiple deployments of the same application if it takes
* longer to deploy than the (short) maintenance interval of this
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java
index cdc5ecf77ec..ee5d6a04ddc 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java
@@ -1,12 +1,19 @@
// 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.provision.maintenance;
+import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Deployer;
+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.time.Instant;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
+import java.util.Optional;
+import java.util.Set;
/**
* The application maintainer regularly redeploys all applications to make sure the node repo and application
@@ -16,16 +23,51 @@ import java.util.List;
* @author bratseth
*/
public class PeriodicApplicationMaintainer extends ApplicationMaintainer {
+ private final Duration minTimeBetweenRedeployments;
+ private final Instant start;
public PeriodicApplicationMaintainer(Deployer deployer, NodeRepository nodeRepository,
- Duration interval, JobControl jobControl) {
+ Duration interval, Duration minTimeBetweenRedeployments, JobControl jobControl) {
super(deployer, nodeRepository, interval, jobControl);
+ this.minTimeBetweenRedeployments = minTimeBetweenRedeployments;
+ this.start = Instant.now();
}
@Override
- protected void throttle(int applicationCount) {
- // Sleep for a length of time that will spread deployment evenly over the maintenance period
- try { Thread.sleep(interval().toMillis() / applicationCount); } catch (InterruptedException e) { return; }
+ protected boolean canDeployNow(ApplicationId application) {
+ // Don't deploy if a regular deploy just happened
+ return getLastDeployTime(application).isBefore(nodeRepository().clock().instant().minus(minTimeBetweenRedeployments));
+ }
+
+ // Returns the app that was deployed the longest time ago
+ @Override
+ protected Set<ApplicationId> applicationsNeedingMaintenance() {
+ if (waitInitially()) return Collections.emptySet();
+
+ Optional<ApplicationId> app = (nodesNeedingMaintenance().stream()
+ .map(node -> node.allocation().get().owner())
+ .distinct()
+ .filter(this::shouldBeDeployedOnThisServer)
+ .min(Comparator.comparing(this::getLastDeployTime)))
+ .filter(this::canDeployNow);
+ app.ifPresent(applicationId -> log.log(LogLevel.INFO, applicationId + " will be deployed, last deploy time " +
+ getLastDeployTime(applicationId)));
+ return app.map(Collections::singleton).orElseGet(Collections::emptySet);
+ }
+
+ private Instant getLastDeployTime(ApplicationId application) {
+ return deployer().lastDeployTime(application).orElse(Instant.EPOCH);
+ }
+
+ // We only know last deploy time for applications that were deployed on this config server,
+ // the rest will be deployed on another config server
+ protected boolean shouldBeDeployedOnThisServer(ApplicationId application) {
+ return deployer().lastDeployTime(application).isPresent();
+ }
+
+ // TODO: Do not start deploying until some time has gone (ideally only until bootstrap of config server is finished)
+ protected boolean waitInitially() {
+ return Instant.now().isBefore(start.plus(minTimeBetweenRedeployments));
}
@Override
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabaseClient.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabaseClient.java
index 44eda5e83ec..f66cec0706a 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabaseClient.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabaseClient.java
@@ -19,7 +19,6 @@ import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.Status;
-import java.nio.charset.StandardCharsets;
import java.time.Clock;
import java.time.Duration;
import java.util.ArrayList;
@@ -49,7 +48,7 @@ public class CuratorDatabaseClient {
private static final Path root = Path.fromString("/provision/v1");
- private static final Duration defaultLockTimeout = Duration.ofMinutes(1);
+ private static final Duration defaultLockTimeout = Duration.ofMinutes(2);
private final NodeSerializer nodeSerializer;
private final StringSetSerializer stringSetSerializer = new StringSetSerializer();
@@ -327,18 +326,6 @@ public class CuratorDatabaseClient {
return curatorDatabase.lock(path, timeout);
}
- /**
- * Returns a default flavor specific for an application, or empty if not available.
- */
- public Optional<String> getDefaultFlavorForApplication(ApplicationId applicationId) {
- Optional<byte[]> utf8DefaultFlavor = curatorDatabase.getData(defaultFlavorPath(applicationId));
- return utf8DefaultFlavor.map((flavor) -> new String(flavor, StandardCharsets.UTF_8));
- }
-
- private Path defaultFlavorPath(ApplicationId applicationId) {
- return root.append("defaultFlavor").append(applicationId.serializedForm());
- }
-
public Set<String> readInactiveJobs() {
try {
byte[] data = curatorDatabase.getData(inactiveJobsPath()).get();
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 4b26769b1fb..efa1cd3745d 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
@@ -40,16 +40,14 @@ public class CapacityPolicies {
}
}
- public Flavor decideFlavor(Capacity requestedCapacity, ClusterSpec cluster, Optional<String> defaultFlavorOverride) {
+ public Flavor decideFlavor(Capacity requestedCapacity, ClusterSpec cluster) {
// for now, always use the requested flavor if a docker flavor is requested
Optional<String> requestedFlavor = requestedCapacity.flavor();
if (requestedFlavor.isPresent() &&
flavors.getFlavorOrThrow(requestedFlavor.get()).getType() == Flavor.Type.DOCKER_CONTAINER)
return flavors.getFlavorOrThrow(requestedFlavor.get());
- String defaultFlavorName = defaultFlavorOverride.isPresent() ?
- defaultFlavorOverride.get() : zone.defaultFlavor(cluster.type());
-
+ String defaultFlavorName = zone.defaultFlavor(cluster.type());
switch(zone.environment()) {
case dev : case test : case staging : return flavors.getFlavorOrThrow(defaultFlavorName);
default : return flavors.getFlavorOrThrow(requestedFlavor.orElse(defaultFlavorName));
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java
index d31b4438a38..dff6378a19a 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java
@@ -64,7 +64,7 @@ public class GroupPreparer {
// Allocate from the prioritized list
NodeAllocation allocation = new NodeAllocation(application, cluster, requestedNodes, highestIndex, nodeRepository);
allocation.offer(prioritizer.prioritize());
- if (! allocation.fullfilled())
+ if (! allocation.fullfilled() && requestedNodes.canFail())
throw new OutOfCapacityException("Could not satisfy " + requestedNodes + " for " + cluster +
" in " + application.toShortString() +
outOfCapacityDetails(allocation));
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java
index 3fa70b3242f..0ef5c03e543 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java
@@ -85,12 +85,11 @@ public class NodeRepositoryProvisioner implements Provisioner {
if (zone.environment().isManuallyDeployed() && nodeCount < requestedCapacity.nodeCount())
logger.log(Level.INFO, "Requested " + requestedCapacity.nodeCount() + " nodes for " + cluster +
", downscaling to " + nodeCount + " nodes in " + zone.environment());
- Optional<String> defaultFlavorOverride = nodeRepository.getDefaultFlavorOverride(application);
- Flavor flavor = capacityPolicies.decideFlavor(requestedCapacity, cluster, defaultFlavorOverride);
+ Flavor flavor = capacityPolicies.decideFlavor(requestedCapacity, cluster);
log.log(LogLevel.DEBUG, () -> "Decided flavor for requested tenant nodes: " + flavor);
boolean exclusive = capacityPolicies.decideExclusivity(cluster.isExclusive());
effectiveGroups = wantedGroups > nodeCount ? nodeCount : wantedGroups; // cannot have more groups than nodes
- requestedNodes = NodeSpec.from(nodeCount, flavor, exclusive);
+ requestedNodes = NodeSpec.from(nodeCount, flavor, exclusive, requestedCapacity.canFail());
}
else {
requestedNodes = NodeSpec.from(requestedCapacity.type());
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java
index b2572a781fe..e8c2926700e 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java
@@ -40,6 +40,9 @@ public interface NodeSpec {
/** Returns whether the given node count is sufficient to fulfill this spec */
boolean fulfilledBy(int count);
+ /** Returns whether this should throw an exception if the requested nodes are not fully available */
+ boolean canFail();
+
/** Returns the ideal number of nodes that should be retired to fulfill this spec */
int idealRetiredCount(int acceptedCount, int currentRetiredCount);
@@ -53,8 +56,8 @@ public interface NodeSpec {
*/
Node assignRequestedFlavor(Node node);
- static NodeSpec from(int nodeCount, Flavor flavor, boolean exclusive) {
- return new CountNodeSpec(nodeCount, flavor, exclusive);
+ static NodeSpec from(int nodeCount, Flavor flavor, boolean exclusive, boolean canFail) {
+ return new CountNodeSpec(nodeCount, flavor, exclusive, canFail);
}
static NodeSpec from(NodeType type) {
@@ -67,12 +70,14 @@ public interface NodeSpec {
private final int count;
private final Flavor requestedFlavor;
private final boolean exclusive;
+ private final boolean canFail;
- public CountNodeSpec(int count, Flavor flavor, boolean exclusive) {
+ public CountNodeSpec(int count, Flavor flavor, boolean exclusive, boolean canFail) {
Objects.requireNonNull(flavor, "A flavor must be specified");
this.count = count;
this.requestedFlavor = flavor;
this.exclusive = exclusive;
+ this.canFail = canFail;
}
// TODO: Remove usage of this
@@ -102,16 +107,21 @@ public interface NodeSpec {
public boolean specifiesNonStockFlavor() { return ! requestedFlavor.isStock(); }
@Override
+ public boolean saturatedBy(int count) { return fulfilledBy(count); } // min=max for count specs
+
+ @Override
public boolean fulfilledBy(int count) { return count >= this.count; }
@Override
- public boolean saturatedBy(int count) { return fulfilledBy(count); } // min=max for count specs
+ public boolean canFail() { return canFail; }
@Override
public int idealRetiredCount(int acceptedCount, int currentRetiredCount) { return acceptedCount - this.count; }
@Override
- public NodeSpec fraction(int divisor) { return new CountNodeSpec(count/divisor, requestedFlavor, exclusive); }
+ public NodeSpec fraction(int divisor) {
+ return new CountNodeSpec(count/divisor, requestedFlavor, exclusive, canFail);
+ }
@Override
public Node assignRequestedFlavor(Node node) {
@@ -166,6 +176,9 @@ public interface NodeSpec {
public boolean saturatedBy(int count) { return false; }
@Override
+ public boolean canFail() { return false; }
+
+ @Override
public int idealRetiredCount(int acceptedCount, int currentRetiredCount) {
/*
* All nodes marked with wantToRetire get marked as retired just before this function is called,
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizationFilter.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizationFilter.java
index 7a782f9de22..1b0bbffe507 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizationFilter.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizationFilter.java
@@ -9,7 +9,6 @@ import com.yahoo.jdisc.http.filter.DiscFilterRequest;
import com.yahoo.jdisc.http.filter.SecurityRequestFilter;
import com.yahoo.net.HostName;
import com.yahoo.vespa.hosted.provision.NodeRepository;
-import com.yahoo.vespa.hosted.provision.restapi.v2.Authorizer;
import com.yahoo.vespa.hosted.provision.restapi.v2.ErrorResponse;
import com.yahoo.yolean.chain.After;
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/Authorizer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/Authorizer.java
index fa12ab1d88f..ad078e09c45 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/Authorizer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/Authorizer.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.provision.restapi.v2;
+package com.yahoo.vespa.hosted.provision.restapi.v2.filter;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.SystemName;
@@ -7,7 +7,6 @@ import com.yahoo.vespa.athenz.api.AthenzIdentity;
import com.yahoo.vespa.athenz.api.AthenzService;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
-import com.yahoo.vespa.hosted.provision.restapi.v2.filter.NodePrincipal;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
@@ -52,6 +51,11 @@ public class Authorizer implements BiPredicate<NodePrincipal, URI> {
return true;
}
if (principal.getHostname().isPresent()) {
+ String hostname = principal.getHostname().get();
+ if (isAthenzProviderApi(uri)) {
+ return hostname.equals(NodeIdentifier.ZTS_AWS_IDENTITY) || hostname.equals(NodeIdentifier.ZTS_ON_PREM_IDENTITY);
+ }
+
// Individual nodes can only access their own resources
if (canAccessAll(hostnamesFrom(uri), principal, this::isSelfOrParent)) {
return true;
@@ -63,13 +67,18 @@ public class Authorizer implements BiPredicate<NodePrincipal, URI> {
}
// The host itself can access all resources
- if (whitelistedHostnames.contains(principal.getHostname().get())) {
+ if (whitelistedHostnames.contains(hostname)) {
return true;
}
}
return false;
}
+ private static boolean isAthenzProviderApi(URI uri) {
+ return "/athenz/v1/provider/instance".equals(uri.getPath()) ||
+ "/athenz/v1/provider/refresh".equals(uri.getPath());
+ }
+
/** Returns whether principal is the node itself or the parent of the node */
private boolean isSelfOrParent(String hostname, NodePrincipal principal) {
// Node can always access itself
@@ -153,6 +162,9 @@ public class Authorizer implements BiPredicate<NodePrincipal, URI> {
"/nodes/v2/node/".equals(uri.getPath())) {
return hostnamesFromQuery(uri);
}
+ if (isChildOf("/athenz/v1/provider/identity-document", uri.getPath())) {
+ return Collections.singletonList(lastChildOf(uri.getPath()));
+ }
return Collections.emptyList();
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodeIdentifier.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodeIdentifier.java
index e78bcb6b5e8..49f8b704c5e 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodeIdentifier.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodeIdentifier.java
@@ -97,6 +97,10 @@ class NodeIdentifier {
}
private String getHostFromVespaCertificate(List<SubjectAlternativeName> sans) {
+ // TODO Remove this branch once all BM nodes are gone
+ if (sans.stream().anyMatch(san -> san.getValue().endsWith("ostk.yahoo.cloud"))) {
+ return getHostFromCalypsoCertificate(sans);
+ }
VespaUniqueInstanceId instanceId = VespaUniqueInstanceId.fromDottedString(getUniqueInstanceId(sans));
if (!zone.environment().value().equals(instanceId.environment()))
throw new NodeIdentifierException("Invalid environment: " + instanceId.environment());
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockDeployer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockDeployer.java
index c880c66abc7..99beab50e16 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockDeployer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockDeployer.java
@@ -12,8 +12,11 @@ import com.yahoo.config.provision.HostSpec;
import com.yahoo.transaction.NestedTransaction;
import com.yahoo.vespa.hosted.provision.provisioning.NodeRepositoryProvisioner;
+import java.time.Clock;
import java.time.Duration;
+import java.time.Instant;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -26,35 +29,52 @@ public class MockDeployer implements Deployer {
private final NodeRepositoryProvisioner provisioner;
private final Map<ApplicationId, ApplicationContext> applications;
+ private final Map<ApplicationId, Instant> lastDeployTimes = new HashMap<>();
/** The number of redeployments done to this */
public int redeployments = 0;
+ private final Clock clock;
+
@Inject
@SuppressWarnings("unused")
public MockDeployer() {
- this(null, Collections.emptyMap());
+ this(null, Clock.systemUTC(), Collections.emptyMap());
}
/**
* Create a mock deployer which contains a substitute for an application repository, fullfilled to
* be able to call provision with the right parameters.
*/
- public MockDeployer(NodeRepositoryProvisioner provisioner, Map<ApplicationId, ApplicationContext> applications) {
+ public MockDeployer(NodeRepositoryProvisioner provisioner,
+ Clock clock,
+ Map<ApplicationId, ApplicationContext> applications) {
this.provisioner = provisioner;
+ this.clock = clock;
this.applications = applications;
}
@Override
- public Optional<Deployment> deployFromLocalActive(ApplicationId application) {
- return deployFromLocalActive(application, Duration.ofSeconds(60));
+ public Optional<Deployment> deployFromLocalActive(ApplicationId id, boolean bootstrap) {
+ return deployFromLocalActive(id, Duration.ofSeconds(60));
}
@Override
public Optional<Deployment> deployFromLocalActive(ApplicationId id, Duration timeout) {
+ lastDeployTimes.put(id, clock.instant());
return Optional.of(new MockDeployment(provisioner, applications.get(id)));
}
+ @Override
+ public Optional<Deployment> deployFromLocalActive(ApplicationId application, Duration timeout, boolean bootstrap) {
+ return deployFromLocalActive(application, timeout);
+ }
+
+ @Override
+ public Optional<Instant> lastDeployTime(ApplicationId application) {
+ return Optional.ofNullable(lastDeployTimes.get(application));
+ }
+
public class MockDeployment implements Deployment {
private final NodeRepositoryProvisioner provisioner;
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java
index 04a4a7f5fb8..de14bc7e480 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java
@@ -140,7 +140,7 @@ public class MockNodeRepository extends NodeRepository {
ClusterSpec.Id.from("id3"),
Version.fromString("6.42"),
false);
- activate(provisioner.prepare(app3, cluster3, Capacity.fromNodeCount(2, Optional.of("docker"), false), 1, null), app3, provisioner);
+ activate(provisioner.prepare(app3, cluster3, Capacity.fromNodeCount(2, Optional.of("docker"), false, true), 1, null), app3, provisioner);
}
private void activate(List<HostSpec> hosts, ApplicationId application, NodeRepositoryProvisioner provisioner) {
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTest.java
index b33d776ac08..aaeb4316c57 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTest.java
@@ -1,19 +1,12 @@
// 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.provision;
-import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.ApplicationName;
-import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.NodeType;
-import com.yahoo.config.provision.TenantName;
-import com.yahoo.path.Path;
import com.yahoo.vespa.hosted.provision.node.Agent;
import org.junit.Test;
-import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashSet;
-import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -47,24 +40,6 @@ public class NodeRepositoryTest {
}
@Test
- public void applicationDefaultFlavor() {
- NodeRepositoryTester tester = new NodeRepositoryTester();
-
- ApplicationId application = ApplicationId.from(TenantName.from("a"), ApplicationName.from("b"), InstanceName.from("c"));
-
- Path path = Path.fromString("/provision/v1/defaultFlavor").append(application.serializedForm());
- String flavor = "example-flavor";
- tester.curator().create(path);
- tester.curator().set(path, flavor.getBytes(StandardCharsets.UTF_8));
-
- assertEquals(Optional.of(flavor), tester.nodeRepository().getDefaultFlavorOverride(application));
-
- ApplicationId applicationWithoutDefaultFlavor =
- ApplicationId.from(TenantName.from("does"), ApplicationName.from("not"), InstanceName.from("exist"));
- assertFalse(tester.nodeRepository().getDefaultFlavorOverride(applicationWithoutDefaultFlavor).isPresent());
- }
-
- @Test
public void only_allow_docker_containers_remove_in_ready() {
NodeRepositoryTester tester = new NodeRepositoryTester();
tester.addNode("id1", "host1", "docker", NodeType.tenant);
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java
index 84569077053..dc00eda01a0 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java
@@ -294,7 +294,7 @@ public class FailedExpirerTest {
List<HostSpec> preparedNodes = provisioner.prepare(applicationId,
clusterSpec,
Capacity.fromNodeCount(hostname.length, Optional.of(flavor.name()),
- false),
+ false, true),
1, null);
NestedTransaction transaction = new NestedTransaction().add(new CuratorTransaction(curator));
provisioner.activate(transaction, applicationId, new HashSet<>(preparedNodes));
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java
index d03e1b9ed4b..84e366c9510 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java
@@ -13,6 +13,7 @@ import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.Zone;
+import com.yahoo.test.ManualClock;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.History;
@@ -142,12 +143,13 @@ public class InactiveAndFailedExpirerTest {
tester.advanceTime(Duration.ofMinutes(11)); // Trigger RetiredExpirer
MockDeployer deployer = new MockDeployer(
tester.provisioner(),
+ tester.clock(),
Collections.singletonMap(
applicationId,
new MockDeployer.ApplicationContext(applicationId, cluster,
Capacity.fromNodeCount(2,
Optional.of("default"),
- false),
+ false, true),
1)
)
);
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InfrastructureProvisionerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InfrastructureProvisionerTest.java
index 586498619c6..f0c4ad2ef2d 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InfrastructureProvisionerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InfrastructureProvisionerTest.java
@@ -100,7 +100,7 @@ public class InfrastructureProvisionerTest {
Node node = tester.addNode("id-" + id, "node-" + id, "default", NodeType.config);
Optional<Node> nodeWithAllocation = wantedVespaVersion.map(version -> {
ConfigServerApplication application = ConfigServerApplication.CONFIG_SERVER_APPLICATION;
- ClusterSpec clusterSpec = ClusterSpec.from(application.getClusterType(), application.getClusterId(), ClusterSpec.Group.from(0), version);
+ ClusterSpec clusterSpec = ClusterSpec.from(application.getClusterType(), application.getClusterId(), ClusterSpec.Group.from(0), version, false);
ClusterMembership membership = ClusterMembership.from(clusterSpec, 1);
Allocation allocation = new Allocation(application.getApplicationId(), membership, new Generation(0, 0), false);
return node.with(allocation);
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java
index e4560ef685d..ea28f7bafc8 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java
@@ -35,6 +35,7 @@ import com.yahoo.vespa.hosted.provision.testutils.ServiceMonitorStub;
import com.yahoo.vespa.hosted.provision.testutils.TestHostLivenessTracker;
import com.yahoo.vespa.orchestrator.Orchestrator;
+import java.time.Clock;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
@@ -109,9 +110,9 @@ public class NodeFailTester {
assertEquals(wantedNodesApp2, tester.nodeRepository.getNodes(app2, Node.State.active).size());
Map<ApplicationId, MockDeployer.ApplicationContext> apps = new HashMap<>();
- apps.put(app1, new MockDeployer.ApplicationContext(app1, clusterApp1, Capacity.fromNodeCount(wantedNodesApp1, Optional.of("default"), false), 1));
- apps.put(app2, new MockDeployer.ApplicationContext(app2, clusterApp2, Capacity.fromNodeCount(wantedNodesApp2, Optional.of("default"), false), 1));
- tester.deployer = new MockDeployer(tester.provisioner, apps);
+ apps.put(app1, new MockDeployer.ApplicationContext(app1, clusterApp1, Capacity.fromNodeCount(wantedNodesApp1, Optional.of("default"), false, true), 1));
+ apps.put(app2, new MockDeployer.ApplicationContext(app2, clusterApp2, Capacity.fromNodeCount(wantedNodesApp2, Optional.of("default"), false, true), 1));
+ tester.deployer = new MockDeployer(tester.provisioner, tester.clock(), apps);
tester.serviceMonitor = new ServiceMonitorStub(apps, tester.nodeRepository);
tester.metric = new MetricsReporterTest.TestMetric();
tester.failer = tester.createFailer();
@@ -133,8 +134,8 @@ public class NodeFailTester {
ClusterSpec clusterApp1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("6.75.0"), false);
ClusterSpec clusterApp2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.75.0"), false);
Capacity allHosts = Capacity.fromRequiredNodeType(NodeType.host);
- Capacity capacity1 = Capacity.fromNodeCount(3, Optional.of("docker"), false);
- Capacity capacity2 = Capacity.fromNodeCount(5, Optional.of("docker"), false);
+ Capacity capacity1 = Capacity.fromNodeCount(3, Optional.of("docker"), false, true);
+ Capacity capacity2 = Capacity.fromNodeCount(5, Optional.of("docker"), false, true);
tester.activate(nodeAdminApp, clusterNodeAdminApp, allHosts);
tester.activate(app1, clusterApp1, capacity1);
tester.activate(app2, clusterApp2, capacity2);
@@ -147,7 +148,7 @@ public class NodeFailTester {
apps.put(nodeAdminApp, new MockDeployer.ApplicationContext(nodeAdminApp, clusterNodeAdminApp, allHosts, 1));
apps.put(app1, new MockDeployer.ApplicationContext(app1, clusterApp1, capacity1, 1));
apps.put(app2, new MockDeployer.ApplicationContext(app2, clusterApp2, capacity2, 1));
- tester.deployer = new MockDeployer(tester.provisioner, apps);
+ tester.deployer = new MockDeployer(tester.provisioner, tester.clock(), apps);
tester.serviceMonitor = new ServiceMonitorStub(apps, tester.nodeRepository);
tester.metric = new MetricsReporterTest.TestMetric();
tester.failer = tester.createFailer();
@@ -170,7 +171,7 @@ public class NodeFailTester {
Map<ApplicationId, MockDeployer.ApplicationContext> apps = new HashMap<>();
apps.put(app1, new MockDeployer.ApplicationContext(app1, clusterApp1, allProxies, 1));
- tester.deployer = new MockDeployer(tester.provisioner, apps);
+ tester.deployer = new MockDeployer(tester.provisioner, tester.clock(), apps);
tester.serviceMonitor = new ServiceMonitorStub(apps, tester.nodeRepository);
tester.metric = new MetricsReporterTest.TestMetric();
tester.failer = tester.createFailer();
@@ -179,7 +180,7 @@ public class NodeFailTester {
public static NodeFailTester withNoApplications() {
NodeFailTester tester = new NodeFailTester();
- tester.deployer = new MockDeployer(tester.provisioner, Collections.emptyMap());
+ tester.deployer = new MockDeployer(tester.provisioner, tester.clock(), Collections.emptyMap());
tester.serviceMonitor = new ServiceMonitorStub(Collections.emptyMap(), tester.nodeRepository);
tester.metric = new MetricsReporterTest.TestMetric();
tester.failer = tester.createFailer();
@@ -210,6 +211,8 @@ public class NodeFailTester {
}
}
+ public Clock clock() { return clock; }
+
public List<Node> createReadyNodes(int count) {
return createReadyNodes(count, 0);
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRetirerTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRetirerTester.java
index ec71b36064f..2bf4f831072 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRetirerTester.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRetirerTester.java
@@ -75,7 +75,7 @@ public class NodeRetirerTester {
new DockerImage("docker-registry.domain.tld:8080/dist/vespa"), true);
jobControl = new JobControl(nodeRepository.database());
NodeRepositoryProvisioner provisioner = new NodeRepositoryProvisioner(nodeRepository, nodeFlavors, zone);
- deployer = new MockDeployer(provisioner, apps);
+ deployer = new MockDeployer(provisioner, clock, apps);
flavors = nodeFlavors.getFlavors().stream().sorted(Comparator.comparing(Flavor::name)).collect(Collectors.toList());
try {
@@ -112,7 +112,7 @@ public class NodeRetirerTester {
for (int i = 0; i < flavorIds.length; i++) {
Flavor flavor = flavors.get(flavorIds[i]);
ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("cluster-" + i), Version.fromString("6.99"), false);
- Capacity capacity = Capacity.fromNodeCount(numNodes[i], Optional.of(flavor.name()), false);
+ Capacity capacity = Capacity.fromNodeCount(numNodes[i], Optional.of(flavor.name()), false, true);
// If the number of node the app wants is divisible by 2, make it into 2 groups, otherwise as 1
int numGroups = numNodes[i] % 2 == 0 ? 2 : 1;
clusterContexts.add(new MockDeployer.ClusterContext(applicationId, cluster, capacity, numGroups));
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainerTest.java
index 898054f23ff..961e9991d71 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainerTest.java
@@ -50,7 +50,7 @@ public class OperatorChangeApplicationMaintainerTest {
private Fixture fixture;
@Test
- public void test_application_maintenance() throws InterruptedException {
+ public void test_application_maintenance() {
ManualClock clock = new ManualClock();
Curator curator = new MockCurator();
Zone zone = new Zone(Environment.prod, RegionName.from("us-east"));
@@ -133,10 +133,10 @@ public class OperatorChangeApplicationMaintainerTest {
Map<ApplicationId, MockDeployer.ApplicationContext> apps = new HashMap<>();
apps.put(app1, new MockDeployer.ApplicationContext(app1, clusterApp1,
- Capacity.fromNodeCount(wantedNodesApp1, Optional.of("default"), false), 1));
+ Capacity.fromNodeCount(wantedNodesApp1, Optional.of("default"), false, true), 1));
apps.put(app2, new MockDeployer.ApplicationContext(app2, clusterApp2,
- Capacity.fromNodeCount(wantedNodesApp2, Optional.of("default"), false), 1));
- this.deployer = new MockDeployer(provisioner, apps);
+ Capacity.fromNodeCount(wantedNodesApp2, Optional.of("default"), false, true), 1));
+ this.deployer = new MockDeployer(provisioner, nodeRepository.clock(), apps);
}
private void activate(ApplicationId applicationId, ClusterSpec cluster, int nodeCount, NodeRepositoryProvisioner provisioner) {
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java
index ed360abc5ea..2ddb2e0d004 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java
@@ -33,6 +33,7 @@ import org.junit.Before;
import org.junit.Test;
import java.time.Duration;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -63,7 +64,7 @@ public class PeriodicApplicationMaintainerTest {
}
@Test
- public void test_application_maintenance() throws InterruptedException {
+ public void test_application_maintenance() {
createReadyNodes(15, nodeRepository, nodeFlavors);
createHostNodes(2, nodeRepository, nodeFlavors);
@@ -101,6 +102,7 @@ public class PeriodicApplicationMaintainerTest {
0, fixture.getNodes(Node.State.active).retired().size());
// Cause maintenance deployment which will update the applications with the re-activated nodes
+ ((ManualClock)nodeRepository.clock()).advance(Duration.ofMinutes(35)); // Otherwise redeploys are inhibited
fixture.runApplicationMaintainer();
assertEquals("Superflous content nodes are retired",
reactivatedInApp2, fixture.getNodes(Node.State.active).retired().size());
@@ -129,6 +131,31 @@ public class PeriodicApplicationMaintainerTest {
nodeRepository.getNodes(fixture.app2, Node.State.inactive).size());
}
+ @Test
+ public void application_deploy_inhibits_redeploy_for_a_while() {
+ ManualClock clock = (ManualClock)nodeRepository.clock();
+ createReadyNodes(15, nodeRepository, nodeFlavors);
+ createHostNodes(2, nodeRepository, nodeFlavors);
+
+ // Create applications
+ fixture.activate();
+ fixture.runApplicationMaintainer();
+ Instant firstDeployTime = clock.instant();
+ assertEquals(firstDeployTime, fixture.deployer.lastDeployTime(fixture.app1).get());
+ assertEquals(firstDeployTime, fixture.deployer.lastDeployTime(fixture.app2).get());
+ ((ManualClock) nodeRepository.clock()).advance(Duration.ofMinutes(5));
+ fixture.runApplicationMaintainer();
+ // Too soon: Not redeployed:
+ assertEquals(firstDeployTime, fixture.deployer.lastDeployTime(fixture.app1).get());
+ assertEquals(firstDeployTime, fixture.deployer.lastDeployTime(fixture.app2).get());
+
+ ((ManualClock) nodeRepository.clock()).advance(Duration.ofMinutes(30));
+ fixture.runApplicationMaintainer();
+ // Redeployed:
+ assertEquals(clock.instant(), fixture.deployer.lastDeployTime(fixture.app1).get());
+ assertEquals(clock.instant(), fixture.deployer.lastDeployTime(fixture.app2).get());
+ }
+
private void createReadyNodes(int count, NodeRepository nodeRepository, NodeFlavors nodeFlavors) {
List<Node> nodes = new ArrayList<>(count);
for (int i = 0; i < count; i++)
@@ -152,6 +179,7 @@ public class PeriodicApplicationMaintainerTest {
final NodeRepository nodeRepository;
final NodeRepositoryProvisioner provisioner;
final Curator curator;
+ final Deployer deployer;
final ApplicationId app1 = ApplicationId.from(TenantName.from("foo1"), ApplicationName.from("bar"), InstanceName.from("fuz"));
final ApplicationId app2 = ApplicationId.from(TenantName.from("foo2"), ApplicationName.from("bar"), InstanceName.from("fuz"));
@@ -164,6 +192,13 @@ public class PeriodicApplicationMaintainerTest {
this.nodeRepository = nodeRepository;
this.curator = curator;
this.provisioner = new NodeRepositoryProvisioner(nodeRepository, flavors, zone);
+
+ Map<ApplicationId, MockDeployer.ApplicationContext> apps = new HashMap<>();
+ apps.put(app1, new MockDeployer.ApplicationContext(app1, clusterApp1,
+ Capacity.fromNodeCount(wantedNodesApp1, Optional.of("default"), false, true), 1));
+ apps.put(app2, new MockDeployer.ApplicationContext(app2, clusterApp2,
+ Capacity.fromNodeCount(wantedNodesApp2, Optional.of("default"), false, true), 1));
+ this.deployer = new MockDeployer(provisioner, nodeRepository.clock(), apps);
}
void activate() {
@@ -191,13 +226,12 @@ public class PeriodicApplicationMaintainerTest {
}
void runApplicationMaintainer(Optional<List<Node>> overriddenNodesNeedingMaintenance) {
- Map<ApplicationId, MockDeployer.ApplicationContext> apps = new HashMap<>();
- apps.put(app1, new MockDeployer.ApplicationContext(app1, clusterApp1,
- Capacity.fromNodeCount(wantedNodesApp1, Optional.of("default"), false), 1));
- apps.put(app2, new MockDeployer.ApplicationContext(app2, clusterApp2,
- Capacity.fromNodeCount(wantedNodesApp2, Optional.of("default"), false), 1));
- MockDeployer deployer = new MockDeployer(provisioner, apps);
- new TestablePeriodicApplicationMaintainer(deployer, nodeRepository, Duration.ofMinutes(30), overriddenNodesNeedingMaintenance).run();
+ TestablePeriodicApplicationMaintainer maintainer =
+ new TestablePeriodicApplicationMaintainer(deployer, nodeRepository, Duration.ofMinutes(1),
+ Duration.ofMinutes(30), overriddenNodesNeedingMaintenance);
+ // Need to run twice, as only one app is deployed per run
+ maintainer.run();
+ maintainer.run();
}
NodeList getNodes(Node.State ... states) {
@@ -211,8 +245,8 @@ public class PeriodicApplicationMaintainerTest {
private Optional<List<Node>> overriddenNodesNeedingMaintenance;
TestablePeriodicApplicationMaintainer(Deployer deployer, NodeRepository nodeRepository, Duration interval,
- Optional<List<Node>> overriddenNodesNeedingMaintenance) {
- super(deployer, nodeRepository, interval, new JobControl(nodeRepository.database()));
+ Duration minTimeBetweenRedeployments, Optional<List<Node>> overriddenNodesNeedingMaintenance) {
+ super(deployer, nodeRepository, interval, minTimeBetweenRedeployments, new JobControl(nodeRepository.database()));
this.overriddenNodesNeedingMaintenance = overriddenNodesNeedingMaintenance;
}
@@ -221,8 +255,6 @@ public class PeriodicApplicationMaintainerTest {
deployWithLock(application);
}
- protected void throttle(int applicationCount) { }
-
@Override
protected List<Node> nodesNeedingMaintenance() {
if (overriddenNodesNeedingMaintenance.isPresent())
@@ -230,6 +262,14 @@ public class PeriodicApplicationMaintainerTest {
return super.nodesNeedingMaintenance();
}
+ protected boolean shouldBeDeployedOnThisServer(ApplicationId application) {
+ return true;
+ }
+
+ protected boolean waitInitially() {
+ return false;
+ }
+
}
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java
index ca4929ece14..03f82e5c3d7 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java
@@ -92,7 +92,8 @@ public class RetiredExpirerTest {
clock.advance(Duration.ofHours(30)); // Retire period spent
MockDeployer deployer =
new MockDeployer(provisioner,
- Collections.singletonMap(applicationId, new MockDeployer.ApplicationContext(applicationId, cluster, Capacity.fromNodeCount(wantedNodes, Optional.of("default"), false), 1)));
+ clock,
+ Collections.singletonMap(applicationId, new MockDeployer.ApplicationContext(applicationId, cluster, Capacity.fromNodeCount(wantedNodes, Optional.of("default"), false, true), 1)));
createRetiredExpirer(deployer).run();
assertEquals(3, nodeRepository.getNodes(applicationId, Node.State.active).size());
assertEquals(4, nodeRepository.getNodes(applicationId, Node.State.inactive).size());
@@ -120,7 +121,8 @@ public class RetiredExpirerTest {
clock.advance(Duration.ofHours(30)); // Retire period spent
MockDeployer deployer =
new MockDeployer(provisioner,
- Collections.singletonMap(applicationId, new MockDeployer.ApplicationContext(applicationId, cluster, Capacity.fromNodeCount(2, Optional.of("default"), false), 1)));
+ clock,
+ Collections.singletonMap(applicationId, new MockDeployer.ApplicationContext(applicationId, cluster, Capacity.fromNodeCount(2, Optional.of("default"), false, true), 1)));
createRetiredExpirer(deployer).run();
assertEquals(2, nodeRepository.getNodes(applicationId, Node.State.active).size());
assertEquals(6, nodeRepository.getNodes(applicationId, Node.State.inactive).size());
@@ -151,9 +153,10 @@ public class RetiredExpirerTest {
// Cause inactivation of retired nodes
MockDeployer deployer =
new MockDeployer(provisioner,
- Collections.singletonMap(
- applicationId,
- new MockDeployer.ApplicationContext(applicationId, cluster, Capacity.fromNodeCount(wantedNodes, Optional.of("default"), false), 1)));
+ clock,
+ Collections.singletonMap(
+ applicationId,
+ new MockDeployer.ApplicationContext(applicationId, cluster, Capacity.fromNodeCount(wantedNodes, Optional.of("default"), false, true), 1)));
// Allow the 1st and 3rd retired nodes permission to inactivate
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/SerializationTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/SerializationTest.java
index 9c9e4e0bc7a..0c32c13f387 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/SerializationTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/SerializationTest.java
@@ -29,6 +29,7 @@ import java.util.Collections;
import java.util.Optional;
import java.util.stream.Collectors;
+import static java.time.temporal.ChronoUnit.MILLIS;
import static java.util.Collections.singleton;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -93,7 +94,7 @@ public class SerializationTest {
assertEquals(node.allocation().get().membership(), copy.allocation().get().membership());
assertEquals(node.allocation().get().isRemovable(), copy.allocation().get().isRemovable());
assertEquals(1, copy.history().events().size());
- assertEquals(clock.instant(), copy.history().event(History.Event.Type.reserved).get().at());
+ assertEquals(clock.instant().truncatedTo(MILLIS), copy.history().event(History.Event.Type.reserved).get().at());
assertEquals(NodeType.tenant, copy.type());
}
@@ -170,7 +171,7 @@ public class SerializationTest {
node = node.retire(Agent.application, clock.instant());
Node copy = nodeSerializer.fromJson(Node.State.provisioned, nodeSerializer.toJson(node));
assertEquals(2, copy.history().events().size());
- assertEquals(clock.instant(), copy.history().event(History.Event.Type.retired).get().at());
+ assertEquals(clock.instant().truncatedTo(MILLIS), copy.history().event(History.Event.Type.retired).get().at());
assertEquals(Agent.application,
(copy.history().event(History.Event.Type.retired).get()).agent());
assertTrue(copy.allocation().get().membership().retired());
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationSimulator.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationSimulator.java
index 943cb60bf04..62212447c2e 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationSimulator.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationSimulator.java
@@ -118,7 +118,7 @@ public class AllocationSimulator {
public void addCluster(String task, int count, Flavor flavor, String id) {
// TODO: Implement
- NodeSpec.CountNodeSpec nodeSpec = new NodeSpec.CountNodeSpec(count, flavor, false);
+ NodeSpec.CountNodeSpec nodeSpec = new NodeSpec.CountNodeSpec(count, flavor, false, true);
nodes = new NodeList(nodes.asList());
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java
index 2cabee98c0d..16aa613db4a 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java
@@ -201,7 +201,7 @@ public class DockerProvisioningTest {
private void prepareAndActivate(ApplicationId application, int nodeCount, boolean exclusive, ProvisioningTester tester) {
Set<HostSpec> hosts = new HashSet<>(tester.prepare(application,
ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("myContainer"), Version.fromString("6.39"), exclusive),
- Capacity.fromNodeCount(nodeCount, Optional.of(dockerFlavor), false),
+ Capacity.fromNodeCount(nodeCount, Optional.of(dockerFlavor), false, true),
1));
tester.activate(application, hosts);
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisioningTest.java
index b6f262775d7..3be56131a05 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisioningTest.java
@@ -68,15 +68,15 @@ public class DynamicDockerProvisioningTest {
// Application 1
ApplicationId application1 = makeApplicationId("t1", "a1");
- ClusterSpec clusterSpec1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
- addAndAssignNode(application1, "1a", dockerHosts.get(2).hostname(), flavor, 0, tester);
- addAndAssignNode(application1, "1b", dockerHosts.get(3).hostname(), flavor, 1, tester);
+ ClusterSpec clusterSpec1 = clusterSpec("myContent.t1.a1");
+ addAndAssignNode(application1, "1a", dockerHosts.get(2).hostname(), clusterSpec1, flavor, 0, tester);
+ addAndAssignNode(application1, "1b", dockerHosts.get(3).hostname(), clusterSpec1, flavor, 1, tester);
// Application 2
ApplicationId application2 = makeApplicationId("t2", "a2");
- ClusterSpec clusterSpec2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
- addAndAssignNode(application2, "2a", dockerHosts.get(2).hostname(), flavor, 0, tester);
- addAndAssignNode(application2, "2b", dockerHosts.get(3).hostname(), flavor, 1, tester);
+ ClusterSpec clusterSpec2 = clusterSpec("myContent.t2.a2");
+ addAndAssignNode(application2, "2a", dockerHosts.get(2).hostname(), clusterSpec2, flavor, 0, tester);
+ addAndAssignNode(application2, "2b", dockerHosts.get(3).hostname(), clusterSpec2, flavor, 1, tester);
// Redeploy one of the applications
deployapp(application1, clusterSpec1, flavor, tester, 2);
@@ -115,15 +115,15 @@ public class DynamicDockerProvisioningTest {
// Application 1
ApplicationId application1 = makeApplicationId("t1", "a1");
- ClusterSpec clusterSpec1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
- addAndAssignNode(application1, "1a", dockerHosts.get(0).hostname(), flavor, 0, tester);
- addAndAssignNode(application1, "1b", dockerHosts.get(1).hostname(), flavor, 1, tester);
+ ClusterSpec clusterSpec1 = clusterSpec("myContent.t1.a1");
+ addAndAssignNode(application1, "1a", dockerHosts.get(0).hostname(), clusterSpec1, flavor, 0, tester);
+ addAndAssignNode(application1, "1b", dockerHosts.get(1).hostname(), clusterSpec1, flavor, 1, tester);
// Application 2
ApplicationId application2 = makeApplicationId("t2", "a2");
- ClusterSpec clusterSpec2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
- addAndAssignNode(application2, "2a", dockerHosts.get(2).hostname(), flavor, 0, tester);
- addAndAssignNode(application2, "2b", dockerHosts.get(3).hostname(), flavor, 1, tester);
+ ClusterSpec clusterSpec2 = clusterSpec("myContent.t2.a2");
+ addAndAssignNode(application2, "2a", dockerHosts.get(2).hostname(), clusterSpec2, flavor, 0, tester);
+ addAndAssignNode(application2, "2b", dockerHosts.get(3).hostname(), clusterSpec2, flavor, 1, tester);
// Redeploy both applications (to be agnostic on which hosts are picked as spares)
deployapp(application1, clusterSpec1, flavor, tester, 2);
@@ -162,17 +162,17 @@ public class DynamicDockerProvisioningTest {
// Application 1
ApplicationId application1 = makeApplicationId("t1", "1");
- ClusterSpec clusterSpec1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
+ ClusterSpec clusterSpec1 = clusterSpec("myContent.t1.a1");
String hostParent2 = dockerHosts.get(2).hostname();
String hostParent3 = dockerHosts.get(3).hostname();
- addAndAssignNode(application1, "1a", hostParent2, flavorD2, 0, tester);
- addAndAssignNode(application1, "1b", hostParent3, flavorD2, 1, tester);
+ addAndAssignNode(application1, "1a", hostParent2, clusterSpec1, flavorD2, 0, tester);
+ addAndAssignNode(application1, "1b", hostParent3, clusterSpec1, flavorD2, 1, tester);
// Application 2
ApplicationId application2 = makeApplicationId("t2", "2");
- ClusterSpec clusterSpec2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
- addAndAssignNode(application2, "2a", hostParent2, flavorD1, 0, tester);
- addAndAssignNode(application2, "2b", hostParent3, flavorD1, 1, tester);
+ ClusterSpec clusterSpec2 = clusterSpec("myContent.t2.a2");
+ addAndAssignNode(application2, "2a", hostParent2, clusterSpec2, flavorD1, 0, tester);
+ addAndAssignNode(application2, "2b", hostParent3, clusterSpec2, flavorD1, 1, tester);
// Assert allocation placement - prior to re-deployment
assertApplicationHosts(tester.nodeRepository().getNodes(application1), hostParent2, hostParent3);
@@ -217,17 +217,17 @@ public class DynamicDockerProvisioningTest {
// Application 1
ApplicationId application1 = makeApplicationId("t1", "1");
- ClusterSpec clusterSpec1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
+ ClusterSpec clusterSpec1 = clusterSpec("myContent.t1.a1");
String hostParent2 = dockerHosts.get(2).hostname();
String hostParent3 = dockerHosts.get(3).hostname();
- addAndAssignNode(application1, "1a", hostParent2, flavorD2, 0, tester);
- addAndAssignNode(application1, "1b", hostParent3, flavorD2, 1, tester);
+ addAndAssignNode(application1, "1a", hostParent2, clusterSpec1, flavorD2, 0, tester);
+ addAndAssignNode(application1, "1b", hostParent3, clusterSpec1, flavorD2, 1, tester);
// Application 2
ApplicationId application2 = makeApplicationId("t2", "2");
- ClusterSpec clusterSpec2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
- addAndAssignNode(application2, "2a", hostParent2, flavorD1, 0, tester);
- addAndAssignNode(application2, "2b", hostParent3, flavorD1, 1, tester);
+ ClusterSpec clusterSpec2 = clusterSpec("myContent.t2.a2");
+ addAndAssignNode(application2, "2a", hostParent2, clusterSpec2, flavorD1, 0, tester);
+ addAndAssignNode(application2, "2b", hostParent3, clusterSpec2, flavorD1, 1, tester);
// Assert allocation placement - prior to re-deployment
assertApplicationHosts(tester.nodeRepository().getNodes(application1), hostParent2, hostParent3);
@@ -275,7 +275,7 @@ public class DynamicDockerProvisioningTest {
* - Fail host and check redistribution
*/
@Test
- public void reloacte_failed_nodes() {
+ public void relocate_failed_nodes() {
ProvisioningTester tester = new ProvisioningTester(new Zone(Environment.prod, RegionName.from("us-east")), flavorsConfig());
tester.makeReadyNodes(5, "host-small", NodeType.host, 32);
deployZoneApp(tester);
@@ -284,20 +284,20 @@ public class DynamicDockerProvisioningTest {
// Application 1
ApplicationId application1 = makeApplicationId("t1", "a1");
- ClusterSpec clusterSpec1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
+ ClusterSpec clusterSpec1 = clusterSpec("myContent.t1.a1");
deployapp(application1, clusterSpec1, flavor, tester, 3);
// Application 2
ApplicationId application2 = makeApplicationId("t2", "a2");
- ClusterSpec clusterSpec2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
+ ClusterSpec clusterSpec2 = clusterSpec("myContent.t2.a2");
deployapp(application2, clusterSpec2, flavor, tester, 2);
// Application 3
ApplicationId application3 = makeApplicationId("t3", "a3");
- ClusterSpec clusterSpec3 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
+ ClusterSpec clusterSpec3 = clusterSpec("myContent.t3.a3");
deployapp(application3, clusterSpec3, flavor, tester, 2);
- // App 2 and 3 should have been allocated to the same nodes - fail on of the parent hosts from there
+ // App 2 and 3 should have been allocated to the same nodes - fail one of the parent hosts from there
String parent = tester.nodeRepository().getNodes(application2).stream().findAny().get().parentHostname().get();
tester.nodeRepository().failRecursively(parent, Agent.system, "Testing");
@@ -315,9 +315,9 @@ public class DynamicDockerProvisioningTest {
numberOfChildrenStat.put(nofChildren, numberOfChildrenStat.get(nofChildren) + 1);
}
- assertEquals(3l, (long) numberOfChildrenStat.get(3));
- assertEquals(1l, (long) numberOfChildrenStat.get(0));
- assertEquals(1l, (long) numberOfChildrenStat.get(1));
+ assertEquals(3, numberOfChildrenStat.get(3).intValue());
+ assertEquals(1, numberOfChildrenStat.get(0).intValue());
+ assertEquals(1, numberOfChildrenStat.get(1).intValue());
}
/**
@@ -342,9 +342,9 @@ public class DynamicDockerProvisioningTest {
// Application 1
ApplicationId application1 = makeApplicationId("t1", "a1");
- ClusterSpec clusterSpec1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
- addAndAssignNode(application1, "1a", dockerHosts.get(0).hostname(), flavor, 0, tester);
- addAndAssignNode(application1, "1b", dockerHosts.get(1).hostname(), flavor, 1, tester);
+ ClusterSpec clusterSpec1 = clusterSpec("myContent.t1.a1");
+ addAndAssignNode(application1, "1a", dockerHosts.get(0).hostname(), clusterSpec1, flavor, 0, tester);
+ addAndAssignNode(application1, "1b", dockerHosts.get(1).hostname(), clusterSpec1, flavor, 1, tester);
// Redeploy both applications (to be agnostic on which hosts are picked as spares)
deployapp(application1, clusterSpec1, flavor, tester, 2);
@@ -366,11 +366,9 @@ public class DynamicDockerProvisioningTest {
deployZoneApp(tester);
Flavor flavor = tester.nodeRepository().getAvailableFlavors().getFlavorOrThrow("d-1");
- //Deploy an application of 6 nodes of 3 nodes in each cluster. We only have 3 docker hosts available
+ //Deploy an application having 6 nodes (3 nodes in 2 groups). We only have 5 docker hosts available
ApplicationId application1 = tester.makeApplicationId();
- tester.prepare(application1,
- ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false),
- 6, 2, flavor.canonicalName());
+ tester.prepare(application1, clusterSpec("myContent.t1.a1"), 6, 2, flavor.canonicalName());
fail("Two groups have been allocated to the same parent host");
}
@@ -396,9 +394,8 @@ public class DynamicDockerProvisioningTest {
Flavor flavor = tester.nodeRepository().getAvailableFlavors().getFlavorOrThrow("d-3");
// Deploy initial state (can max deploy 3 nodes due to redundancy requirements)
- List<HostSpec> hosts = tester.prepare(application1,
- ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false),
- 3, 1, flavor.canonicalName());
+ ClusterSpec clusterSpec = clusterSpec("myContent.t1.a1");
+ List<HostSpec> hosts = tester.prepare(application1, clusterSpec, 3, 1, flavor.canonicalName());
tester.activate(application1, ImmutableSet.copyOf(hosts));
DockerHostCapacity capacity = new DockerHostCapacity(tester.nodeRepository().getNodes(Node.State.values()));
@@ -408,17 +405,13 @@ public class DynamicDockerProvisioningTest {
assertThat(initialSpareCapacity.size(), is(2));
try {
- hosts = tester.prepare(application1,
- ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false),
- 4, 1, flavor.canonicalName());
+ hosts = tester.prepare(application1, clusterSpec, 4, 1, flavor.canonicalName());
fail("Was able to deploy with 4 nodes, should not be able to use spare capacity");
} catch (OutOfCapacityException e) {
}
tester.fail(hosts.get(0));
- hosts = tester.prepare(application1,
- ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false),
- 3, 1, flavor.canonicalName());
+ hosts = tester.prepare(application1, clusterSpec, 3, 1, flavor.canonicalName());
tester.activate(application1, ImmutableSet.copyOf(hosts));
List<Node> finalSpareCapacity = findSpareCapacity(tester);
@@ -427,16 +420,14 @@ public class DynamicDockerProvisioningTest {
}
@Test
- public void non_prod_do_not_have_spares() {
+ public void non_prod_zones_do_not_have_spares() {
ProvisioningTester tester = new ProvisioningTester(new Zone(Environment.perf, RegionName.from("us-east")), flavorsConfig());
tester.makeReadyNodes(3, "host-small", NodeType.host, 32);
deployZoneApp(tester);
Flavor flavor = tester.nodeRepository().getAvailableFlavors().getFlavorOrThrow("d-3");
ApplicationId application1 = tester.makeApplicationId();
- List<HostSpec> hosts = tester.prepare(application1,
- ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false),
- 3, 1, flavor.canonicalName());
+ List<HostSpec> hosts = tester.prepare(application1, clusterSpec("myContent.t1.a1"), 3, 1, flavor.canonicalName());
tester.activate(application1, ImmutableSet.copyOf(hosts));
List<Node> initialSpareCapacity = findSpareCapacity(tester);
@@ -446,14 +437,12 @@ public class DynamicDockerProvisioningTest {
@Test(expected = OutOfCapacityException.class)
public void allocation_should_fail_when_host_is_not_active() {
ProvisioningTester tester = new ProvisioningTester(new Zone(Environment.prod, RegionName.from("us-east")), flavorsConfig());
-
tester.makeProvisionedNodes(3, "host-small", NodeType.host, 32);
deployZoneApp(tester);
ApplicationId application = tester.makeApplicationId();
Flavor flavor = tester.nodeRepository().getAvailableFlavors().getFlavorOrThrow("d-3");
- tester.prepare(application, ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false),
- 2, 1, flavor.canonicalName());
+ tester.prepare(application, clusterSpec("myContent.t2.a2"), 2, 1, flavor.canonicalName());
}
private ApplicationId makeApplicationId(String tenant, String appName) {
@@ -465,18 +454,16 @@ public class DynamicDockerProvisioningTest {
tester.activate(id, new HashSet<>(hostSpec));
}
- private Node addAndAssignNode(ApplicationId id, String hostname, String parentHostname, Flavor flavor, int index, ProvisioningTester tester) {
+ private void addAndAssignNode(ApplicationId id, String hostname, String parentHostname, ClusterSpec clusterSpec, Flavor flavor, int index, ProvisioningTester tester) {
Node node1a = Node.create("open1", Collections.singleton("127.0.0.100"), new HashSet<>(), hostname, Optional.of(parentHostname), flavor, NodeType.tenant);
- ClusterSpec clusterSpec = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false).with(Optional.of(ClusterSpec.Group.from(0)));
- ClusterMembership clusterMembership1 = ClusterMembership.from(clusterSpec, index);
+ ClusterMembership clusterMembership1 = ClusterMembership.from(
+ clusterSpec.with(Optional.of(ClusterSpec.Group.from(0))), index); // Need to add group here so that group is serialized in node allocation
Node node1aAllocation = node1a.allocate(id, clusterMembership1, Instant.now());
tester.nodeRepository().addNodes(Collections.singletonList(node1aAllocation));
NestedTransaction transaction = new NestedTransaction().add(new CuratorTransaction(tester.getCurator()));
tester.nodeRepository().activate(Collections.singletonList(node1aAllocation), transaction);
transaction.commit();
-
- return node1aAllocation;
}
private List<Node> findSpareCapacity(ProvisioningTester tester) {
@@ -506,19 +493,10 @@ public class DynamicDockerProvisioningTest {
}
private FlavorsConfig flavorsConfig() {
- FlavorConfigBuilder b = new FlavorConfigBuilder();
- b.addFlavor("host-large", 6., 6., 6, Flavor.Type.BARE_METAL);
- b.addFlavor("host-small", 3., 3., 3, Flavor.Type.BARE_METAL);
- b.addFlavor("d-1", 1, 1., 1, Flavor.Type.DOCKER_CONTAINER);
- b.addFlavor("d-2", 2, 2., 2, Flavor.Type.DOCKER_CONTAINER);
- b.addFlavor("d-3", 3, 3., 3, Flavor.Type.DOCKER_CONTAINER);
- b.addFlavor("d-3-disk", 3, 3., 5, Flavor.Type.DOCKER_CONTAINER);
- b.addFlavor("d-3-mem", 3, 5., 3, Flavor.Type.DOCKER_CONTAINER);
- b.addFlavor("d-3-cpu", 5, 3., 3, Flavor.Type.DOCKER_CONTAINER);
- return b.build();
+ return flavorsConfig(false);
}
- private List<HostSpec> deployZoneApp(ProvisioningTester tester) {
+ private void deployZoneApp(ProvisioningTester tester) {
ApplicationId applicationId = tester.makeApplicationId();
List<HostSpec> list = tester.prepare(applicationId,
ClusterSpec.request(ClusterSpec.Type.container,
@@ -528,7 +506,6 @@ public class DynamicDockerProvisioningTest {
Capacity.fromRequiredNodeType(NodeType.host),
1);
tester.activate(applicationId, ImmutableSet.copyOf(list));
- return list;
}
private boolean isInactiveOrRetired(Node node) {
@@ -540,4 +517,8 @@ public class DynamicDockerProvisioningTest {
return isInactive || isRetired;
}
+
+ private ClusterSpec clusterSpec(String clusterId) {
+ return ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from(clusterId), Version.fromString("6.100"), false);
+ }
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/MultigroupProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/MultigroupProvisioningTest.java
index 142789eea51..86daf636875 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/MultigroupProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/MultigroupProvisioningTest.java
@@ -100,8 +100,8 @@ public class MultigroupProvisioningTest {
tester.makeReadyNodes(10, "small");
- deploy(application1, Capacity.fromNodeCount(1, Optional.of("small"), true), 1, tester);
- deploy(application1, Capacity.fromNodeCount(2, Optional.of("small"), true), 2, tester);
+ deploy(application1, Capacity.fromNodeCount(1, Optional.of("small"), true, true), 1, tester);
+ deploy(application1, Capacity.fromNodeCount(2, Optional.of("small"), true, true), 2, tester);
}
@Test
@@ -113,8 +113,8 @@ public class MultigroupProvisioningTest {
tester.makeReadyNodes(10, "small");
tester.makeReadyNodes(10, "large");
- deploy(application1, Capacity.fromNodeCount(1, Optional.of("small"), true), 1, tester);
- deploy(application1, Capacity.fromNodeCount(2, Optional.of("large"), true), 2, tester);
+ deploy(application1, Capacity.fromNodeCount(1, Optional.of("small"), true, true), 1, tester);
+ deploy(application1, Capacity.fromNodeCount(2, Optional.of("large"), true, true), 2, tester);
}
@Test
@@ -133,9 +133,10 @@ public class MultigroupProvisioningTest {
tester.advanceTime(Duration.ofDays(7));
MockDeployer deployer =
new MockDeployer(tester.provisioner(),
+ tester.clock(),
Collections.singletonMap(application1,
new MockDeployer.ApplicationContext(application1, cluster(),
- Capacity.fromNodeCount(8, Optional.of("large"), false), 1)));
+ Capacity.fromNodeCount(8, Optional.of("large"), false, true), 1)));
new RetiredExpirer(tester.nodeRepository(), tester.orchestrator(), deployer, tester.clock(), Duration.ofDays(30),
Duration.ofHours(12), new JobControl(tester.nodeRepository().database())).run();
@@ -144,10 +145,10 @@ public class MultigroupProvisioningTest {
}
private void deploy(ApplicationId application, int nodeCount, int groupCount, String flavor, ProvisioningTester tester) {
- deploy(application, Capacity.fromNodeCount(nodeCount, Optional.of(flavor), false), groupCount, tester);
+ deploy(application, Capacity.fromNodeCount(nodeCount, Optional.of(flavor), false, true), groupCount, tester);
}
private void deploy(ApplicationId application, int nodeCount, int groupCount, ProvisioningTester tester) {
- deploy(application, Capacity.fromNodeCount(nodeCount, Optional.of("default"), false), groupCount, tester);
+ deploy(application, Capacity.fromNodeCount(nodeCount, Optional.of("default"), false, true), groupCount, tester);
}
private void deploy(ApplicationId application, Capacity capacity, int wantedGroups, ProvisioningTester tester) {
int nodeCount = capacity.nodeCount();
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeTypeProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeTypeProvisioningTest.java
index 5f3976ab137..03cc3a3c20b 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeTypeProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeTypeProvisioningTest.java
@@ -97,6 +97,7 @@ public class NodeTypeProvisioningTest {
public void retire_proxy() {
MockDeployer deployer = new MockDeployer(
tester.provisioner(),
+ tester.clock(),
Collections.singletonMap(
application, new MockDeployer.ApplicationContext(application, clusterSpec, capacity, 1)));
RetiredExpirer retiredExpirer = new RetiredExpirer(tester.nodeRepository(), tester.orchestrator(), deployer,
@@ -161,6 +162,7 @@ public class NodeTypeProvisioningTest {
public void retire_multiple_proxy_simultaneously() {
MockDeployer deployer = new MockDeployer(
tester.provisioner(),
+ tester.clock(),
Collections.singletonMap(
application, new MockDeployer.ApplicationContext(application, clusterSpec, capacity, 1)));
RetiredExpirer retiredExpirer = new RetiredExpirer(tester.nodeRepository(), tester.orchestrator(), deployer,
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java
index 2ffe23cea07..758d7cc71d9 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java
@@ -45,6 +45,7 @@ import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
+import static java.time.temporal.ChronoUnit.MILLIS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -493,6 +494,19 @@ public class ProvisioningTest {
}
@Test
+ public void out_of_capacity_but_cannot_fail() {
+ ProvisioningTester tester = new ProvisioningTester(new Zone(Environment.prod, RegionName.from("us-east")));
+ tester.makeReadyNodes(4, "default");
+ ApplicationId application = tester.makeApplicationId();
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content,
+ ClusterSpec.Id.from("music"),
+ new com.yahoo.component.Version(4, 5, 6),
+ false);
+ tester.prepare(application, cluster, Capacity.fromNodeCount(5, Optional.empty(), false, false), 1);
+ // No exception; Success
+ }
+
+ @Test
public void out_of_desired_flavor() {
ProvisioningTester tester = new ProvisioningTester(new Zone(Environment.prod, RegionName.from("us-east")));
@@ -736,11 +750,12 @@ public class ProvisioningTest {
"default", tester);
List<Node> reserved = tester.getNodes(application, Node.State.reserved).asList();
assertEquals("Reserved required nodes", 4, reserved.size());
- assertTrue("Time of event is updated for all nodes", reserved.stream()
- .allMatch(n -> n.history()
- .event(History.Event.Type.reserved)
- .get().at()
- .equals(tester.clock().instant())));
+ assertTrue("Time of event is updated for all nodes",
+ reserved.stream()
+ .allMatch(n -> n.history()
+ .event(History.Event.Type.reserved)
+ .get().at()
+ .equals(tester.clock().instant().truncatedTo(MILLIS))));
// Over 10 minutes pass since first reservation. First set of reserved nodes are not expired
tester.clock().advance(Duration.ofMinutes(8).plus(Duration.ofSeconds(1)));
@@ -830,7 +845,7 @@ public class ProvisioningTest {
allHosts.addAll(content0);
allHosts.addAll(content1);
- Function<Integer, Capacity> capacity = count -> Capacity.fromNodeCount(count, Optional.empty(), required);
+ Function<Integer, Capacity> capacity = count -> Capacity.fromNodeCount(count, Optional.empty(), required, true);
int expectedContainer0Size = tester.capacityPolicies().decideSize(capacity.apply(container0Size));
int expectedContainer1Size = tester.capacityPolicies().decideSize(capacity.apply(container1Size));
int expectedContent0Size = tester.capacityPolicies().decideSize(capacity.apply(content0Size));
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java
index 60ce3c9b567..1f26cf035b1 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java
@@ -136,7 +136,7 @@ public class ProvisioningTester {
}
public List<HostSpec> prepare(ApplicationId application, ClusterSpec cluster, int nodeCount, int groups, boolean required, String flavor) {
- return prepare(application, cluster, Capacity.fromNodeCount(nodeCount, Optional.ofNullable(flavor), required), groups);
+ return prepare(application, cluster, Capacity.fromNodeCount(nodeCount, Optional.ofNullable(flavor), required, true), groups);
}
public List<HostSpec> prepare(ApplicationId application, ClusterSpec cluster, Capacity capacity, int groups) {
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/AuthorizerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizerTest.java
index 201f1bcdc7d..38128e66861 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/AuthorizerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizerTest.java
@@ -1,5 +1,5 @@
// 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.provision.restapi.v2;
+package com.yahoo.vespa.hosted.provision.restapi.v2.filter;
import com.yahoo.config.provision.Flavor;
import com.yahoo.config.provision.NodeFlavors;
@@ -7,7 +7,6 @@ import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.SystemName;
import com.yahoo.vespa.curator.mock.MockCurator;
import com.yahoo.vespa.hosted.provision.Node;
-import com.yahoo.vespa.hosted.provision.restapi.v2.filter.NodePrincipal;
import com.yahoo.vespa.hosted.provision.testutils.MockNodeFlavors;
import com.yahoo.vespa.hosted.provision.testutils.MockNodeRepository;
import org.junit.Before;
@@ -102,6 +101,8 @@ public class AuthorizerTest {
assertTrue(authorizedTenantHostNode("host1", "/nodes/v2/node/host1"));
assertTrue(authorizedTenantHostNode("host1", "/nodes/v2/node/child1-1"));
assertTrue(authorizedTenantHostNode("host1", "/nodes/v2/command/reboot?hostname=child1-1"));
+ assertTrue(authorizedTenantHostNode("host1", "/athenz/v1/provider/identity-document/tenant/host1"));
+ assertTrue(authorizedTenantHostNode("host1", "/athenz/v1/provider/identity-document/node/child1-1"));
// Trusted services can access everything in their own system
assertFalse(authorizedController("vespa.vespa.cd.hosting", "/")); // Wrong system
@@ -152,6 +153,12 @@ public class AuthorizerTest {
assertTrue(authorizedLegacyNode("cfghost1", "/application/v2"));
}
+ @Test
+ public void zts_allowed_for_athenz_provider_api() {
+ assertTrue(authorizedLegacyNode(NodeIdentifier.ZTS_AWS_IDENTITY, "/athenz/v1/provider/refresh"));
+ assertTrue(authorizedLegacyNode(NodeIdentifier.ZTS_ON_PREM_IDENTITY, "/athenz/v1/provider/instance"));
+ }
+
private boolean authorizedTenantNode(String hostname, String path) {
return authorized(NodePrincipal.withAthenzIdentity("vespa.vespa.tenant", hostname, emptyList()), path);
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodeIdentifierTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodeIdentifierTest.java
index 9c441e82a84..20168074513 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodeIdentifierTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodeIdentifierTest.java
@@ -201,6 +201,25 @@ public class NodeIdentifierTest {
assertEquals(CONTROLLER_IDENTITY, identity.getHostIdentityName());
}
+ @Test
+ public void accepts_openstack_bm_tenant_certificate() {
+ NodeRepositoryTester nodeRepositoryDummy = new NodeRepositoryTester();
+ nodeRepositoryDummy.addNode(OPENSTACK_ID, HOSTNAME, INSTANCE_ID, NodeType.tenant);
+ nodeRepositoryDummy.setNodeState(HOSTNAME, Node.State.active);
+ Pkcs10Csr csr = Pkcs10CsrBuilder
+ .fromKeypair(new X500Principal("CN=" + TENANT_DOCKER_CONTAINER_IDENTITY), KEYPAIR, SHA256_WITH_RSA)
+ .build();
+ X509Certificate certificate = X509CertificateBuilder
+ .fromCsr(csr, ATHENZ_YAHOO_CA_CERT.getSubjectX500Principal(), Instant.EPOCH, Instant.EPOCH.plusSeconds(60), KEYPAIR.getPrivate(), SHA256_WITH_RSA, 1)
+ .addSubjectAlternativeName(OPENSTACK_ID + ".instanceid.athenz.ostk.yahoo.cloud")
+ .build();
+ NodeIdentifier identifier = new NodeIdentifier(ZONE, nodeRepositoryDummy.nodeRepository());
+ NodePrincipal identity = identifier.resolveNode(singletonList(certificate));
+ assertTrue(identity.getHostname().isPresent());
+ assertEquals(HOSTNAME, identity.getHostname().get());
+ assertEquals(TENANT_DOCKER_CONTAINER_IDENTITY, identity.getHostIdentityName());
+ }
+
private static Node createNode(String clusterId, int clusterIndex, String tenant, String application) {
return Node
.createDockerNode(
@@ -219,7 +238,8 @@ public class NodeIdentifierTest {
ClusterSpec.Type.container,
new ClusterSpec.Id(clusterId),
ClusterSpec.Group.from(0),
- Version.emptyVersion),
+ Version.emptyVersion,
+ false),
clusterIndex),
Generation.inital(),
false));
diff --git a/orchestrator/pom.xml b/orchestrator/pom.xml
index 3559e4282c3..ae05a1908c9 100644
--- a/orchestrator/pom.xml
+++ b/orchestrator/pom.xml
@@ -42,6 +42,12 @@
</dependency>
<dependency>
<groupId>com.yahoo.vespa</groupId>
+ <artifactId>vespajlib</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
<artifactId>jaxrs_client_utils</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorContext.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorContext.java
new file mode 100644
index 00000000000..6577b4b96cc
--- /dev/null
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorContext.java
@@ -0,0 +1,40 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.orchestrator;
+
+import com.google.common.util.concurrent.UncheckedTimeoutException;
+import com.yahoo.time.TimeBudget;
+
+import java.time.Clock;
+import java.time.Duration;
+
+/**
+ * Context for the Orchestrator, e.g. timeout management.
+ *
+ * @author hakon
+ */
+public class OrchestratorContext {
+ private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(10);
+
+ private TimeBudget timeBudget;
+
+ public OrchestratorContext(Clock clock) {
+ this.timeBudget = TimeBudget.fromNow(clock, DEFAULT_TIMEOUT);
+ }
+
+ /** Get the original timeout in seconds. */
+ public long getOriginalTimeoutInSeconds() {
+ return timeBudget.originalTimeout().get().getSeconds();
+ }
+
+ /**
+ * Get timeout for a suboperation that should take up {@code shareOfRemaining} of the
+ * remaining time, or throw an {@link UncheckedTimeoutException} if timed out.
+ */
+ public float getSuboperationTimeoutInSeconds(float shareOfRemaining) {
+ if (!(0f <= shareOfRemaining && shareOfRemaining <= 1.0f)) {
+ throw new IllegalArgumentException("Share of remaining time must be between 0 and 1: " + shareOfRemaining);
+ }
+
+ return shareOfRemaining * timeBudget.timeLeftOrThrow().get().toMillis() / 1000.0f;
+ }
+}
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorImpl.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorImpl.java
index 095a7da8322..ad8a35312e4 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorImpl.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorImpl.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.orchestrator;
+import com.google.common.util.concurrent.UncheckedTimeoutException;
import com.google.inject.Inject;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.log.LogLevel;
@@ -30,6 +31,7 @@ import com.yahoo.vespa.orchestrator.status.MutableStatusRegistry;
import com.yahoo.vespa.orchestrator.status.StatusService;
import java.io.IOException;
+import java.time.Clock;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -43,7 +45,6 @@ import java.util.stream.Collectors;
* @author smorgrav
*/
public class OrchestratorImpl implements Orchestrator {
-
private static final Logger log = Logger.getLogger(OrchestratorImpl.class.getName());
private final Policy policy;
@@ -51,6 +52,7 @@ public class OrchestratorImpl implements Orchestrator {
private final InstanceLookupService instanceLookupService;
private final int serviceMonitorConvergenceLatencySeconds;
private final ClusterControllerClientFactory clusterControllerClientFactory;
+ private final Clock clock;
@Inject
public OrchestratorImpl(ClusterControllerClientFactory clusterControllerClientFactory,
@@ -62,21 +64,23 @@ public class OrchestratorImpl implements Orchestrator {
clusterControllerClientFactory,
statusService,
instanceLookupService,
- orchestratorConfig.serviceMonitorConvergenceLatencySeconds());
+ orchestratorConfig.serviceMonitorConvergenceLatencySeconds(),
+ Clock.systemUTC());
}
public OrchestratorImpl(Policy policy,
ClusterControllerClientFactory clusterControllerClientFactory,
StatusService statusService,
InstanceLookupService instanceLookupService,
- int serviceMonitorConvergenceLatencySeconds)
+ int serviceMonitorConvergenceLatencySeconds,
+ Clock clock)
{
this.policy = policy;
this.clusterControllerClientFactory = clusterControllerClientFactory;
this.statusService = statusService;
this.serviceMonitorConvergenceLatencySeconds = serviceMonitorConvergenceLatencySeconds;
this.instanceLookupService = instanceLookupService;
-
+ this.clock = clock;
}
@Override
@@ -123,7 +127,10 @@ public class OrchestratorImpl implements Orchestrator {
ApplicationInstance appInstance = getApplicationInstance(hostName);
- try (MutableStatusRegistry statusRegistry = statusService.lockApplicationInstance_forCurrentThreadOnly(appInstance.reference())) {
+ OrchestratorContext context = new OrchestratorContext(clock);
+ try (MutableStatusRegistry statusRegistry = statusService.lockApplicationInstance_forCurrentThreadOnly(
+ appInstance.reference(),
+ context.getOriginalTimeoutInSeconds())) {
final HostStatus currentHostState = statusRegistry.getHostStatus(hostName);
if (HostStatus.NO_REMARKS == currentHostState) {
@@ -132,7 +139,7 @@ public class OrchestratorImpl implements Orchestrator {
ApplicationInstanceStatus appStatus = statusService.forApplicationInstance(appInstance.reference()).getApplicationInstanceStatus();
if (appStatus == ApplicationInstanceStatus.NO_REMARKS) {
- policy.releaseSuspensionGrant(appInstance, hostName, statusRegistry);
+ policy.releaseSuspensionGrant(context, appInstance, hostName, statusRegistry);
}
}
}
@@ -149,13 +156,16 @@ public class OrchestratorImpl implements Orchestrator {
ApplicationInstance appInstance = getApplicationInstance(hostName);
NodeGroup nodeGroup = new NodeGroup(appInstance, hostName);
- try (MutableStatusRegistry statusRegistry = statusService.lockApplicationInstance_forCurrentThreadOnly(appInstance.reference())) {
+ OrchestratorContext context = new OrchestratorContext(clock);
+ try (MutableStatusRegistry statusRegistry = statusService.lockApplicationInstance_forCurrentThreadOnly(
+ appInstance.reference(),
+ context.getOriginalTimeoutInSeconds())) {
ApplicationApi applicationApi = new ApplicationApiImpl(
nodeGroup,
statusRegistry,
clusterControllerClientFactory);
- policy.acquirePermissionToRemove(applicationApi);
+ policy.acquirePermissionToRemove(context, applicationApi);
}
}
@@ -164,7 +174,11 @@ public class OrchestratorImpl implements Orchestrator {
public void suspendGroup(NodeGroup nodeGroup) throws HostStateChangeDeniedException, HostNameNotFoundException {
ApplicationInstanceReference applicationReference = nodeGroup.getApplicationReference();
- try (MutableStatusRegistry hostStatusRegistry = statusService.lockApplicationInstance_forCurrentThreadOnly(applicationReference)) {
+ OrchestratorContext context = new OrchestratorContext(clock);
+ try (MutableStatusRegistry hostStatusRegistry =
+ statusService.lockApplicationInstance_forCurrentThreadOnly(
+ applicationReference,
+ context.getOriginalTimeoutInSeconds())) {
ApplicationInstanceStatus appStatus = statusService.forApplicationInstance(applicationReference).getApplicationInstanceStatus();
if (appStatus == ApplicationInstanceStatus.ALLOWED_TO_BE_DOWN) {
return;
@@ -174,7 +188,7 @@ public class OrchestratorImpl implements Orchestrator {
nodeGroup,
hostStatusRegistry,
clusterControllerClientFactory);
- policy.grantSuspensionRequest(applicationApi);
+ policy.grantSuspensionRequest(context, applicationApi);
}
}
@@ -287,9 +301,12 @@ public class OrchestratorImpl implements Orchestrator {
private void setApplicationStatus(ApplicationId appId, ApplicationInstanceStatus status)
throws ApplicationStateChangeDeniedException, ApplicationIdNotFoundException{
+ OrchestratorContext context = new OrchestratorContext(clock);
ApplicationInstanceReference appRef = OrchestratorUtil.toApplicationInstanceReference(appId, instanceLookupService);
try (MutableStatusRegistry statusRegistry =
- statusService.lockApplicationInstance_forCurrentThreadOnly(appRef)) {
+ statusService.lockApplicationInstance_forCurrentThreadOnly(
+ appRef,
+ context.getOriginalTimeoutInSeconds())) {
// Short-circuit if already in wanted state
if (status == statusRegistry.getApplicationInstanceStatus()) return;
@@ -304,14 +321,15 @@ public class OrchestratorImpl implements Orchestrator {
// If the clustercontroller throws an error the nodes will be marked as allowed to be down
// and be set back up on next resume invocation.
- setClusterStateInController(application, ClusterControllerNodeState.MAINTENANCE);
+ setClusterStateInController(context, application, ClusterControllerNodeState.MAINTENANCE);
}
statusRegistry.setApplicationInstanceStatus(status);
}
}
- private void setClusterStateInController(ApplicationInstance application,
+ private void setClusterStateInController(OrchestratorContext context,
+ ApplicationInstance application,
ClusterControllerNodeState state)
throws ApplicationStateChangeDeniedException, ApplicationIdNotFoundException {
// Get all content clusters for this application
@@ -329,7 +347,7 @@ public class OrchestratorImpl implements Orchestrator {
clusterControllers,
clusterId.s());
try {
- ClusterControllerStateResponse response = client.setApplicationState(state);
+ ClusterControllerStateResponse response = client.setApplicationState(context, state);
if (!response.wasModified) {
String msg = String.format("Fail to set application %s, cluster name %s to cluster state %s due to: %s",
application.applicationInstanceId(), clusterId, state, response.reason);
@@ -337,6 +355,10 @@ public class OrchestratorImpl implements Orchestrator {
}
} catch (IOException e) {
throw new ApplicationStateChangeDeniedException(e.getMessage());
+ } catch (UncheckedTimeoutException e) {
+ throw new ApplicationStateChangeDeniedException(
+ "Timed out while waiting for cluster controllers " + clusterControllers +
+ " with cluster ID " + clusterId.s() + ": " + e.getMessage());
}
}
}
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClient.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClient.java
index 38cabd5d86d..15ae69b3a0d 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClient.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClient.java
@@ -1,6 +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.vespa.orchestrator.controller;
+import com.google.common.util.concurrent.UncheckedTimeoutException;
+import com.yahoo.vespa.orchestrator.OrchestratorContext;
+
import java.io.IOException;
/**
@@ -12,14 +15,16 @@ public interface ClusterControllerClient {
* Requests that a cluster controller sets the requested node to the requested state.
*
* @throws IOException if there was a problem communicating with the cluster controller
+ * @throws UncheckedTimeoutException if operation times out
*/
- ClusterControllerStateResponse setNodeState(int storageNodeIndex, ClusterControllerNodeState wantedState) throws IOException;
+ ClusterControllerStateResponse setNodeState(OrchestratorContext context, int storageNodeIndex, ClusterControllerNodeState wantedState) throws IOException;
/**
* Requests that a cluster controller sets all nodes in the cluster to the requested state.
*
* @throws IOException if there was a problem communicating with the cluster controller
+ * @throws UncheckedTimeoutException if operation times out
*/
- ClusterControllerStateResponse setApplicationState(ClusterControllerNodeState wantedState) throws IOException;
+ ClusterControllerStateResponse setApplicationState(OrchestratorContext context, ClusterControllerNodeState wantedState) throws IOException;
}
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientImpl.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientImpl.java
index b70e1a56aea..467b534f809 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientImpl.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientImpl.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.orchestrator.controller;
import com.yahoo.vespa.jaxrs.client.JaxRsStrategy;
+import com.yahoo.vespa.orchestrator.OrchestratorContext;
import java.io.IOException;
@@ -14,6 +15,23 @@ public class ClusterControllerClientImpl implements ClusterControllerClient{
public static final String REQUEST_REASON = "Orchestrator";
+ // On setNodeState calls against the CC ensemble.
+ //
+ // We'd like to set a timeout for the request to the first CC such that if the first
+ // CC is faulty, there's sufficient time to send the request to the second and third CC.
+ // The timeouts would be:
+ // timeout(1. request) = SHARE_REMAINING_TIME * T
+ // timeout(2. request) = SHARE_REMAINING_TIME * T * (1 - SHARE_REMAINING_TIME)
+ // timeout(3. request) = SHARE_REMAINING_TIME * T * (1 - SHARE_REMAINING_TIME)^2
+ //
+ // Using a share of 50% gives approximately:
+ // timeout(1. request) = T * 0.5
+ // timeout(2. request) = T * 0.25
+ // timeout(3. request) = T * 0.125
+ //
+ // which seems fine
+ public static final float SHARE_REMAINING_TIME = 0.5f;
+
private final JaxRsStrategy<ClusterControllerJaxRsApi> clusterControllerApi;
private final String clusterName;
@@ -29,12 +47,19 @@ public class ClusterControllerClientImpl implements ClusterControllerClient{
* @throws IOException if there was a problem communicating with the cluster controller
*/
@Override
- public ClusterControllerStateResponse setNodeState(int storageNodeIndex, ClusterControllerNodeState wantedState) throws IOException {
+ public ClusterControllerStateResponse setNodeState(OrchestratorContext context,
+ int storageNodeIndex,
+ ClusterControllerNodeState wantedState) throws IOException {
ClusterControllerStateRequest.State state = new ClusterControllerStateRequest.State(wantedState, REQUEST_REASON);
ClusterControllerStateRequest stateRequest = new ClusterControllerStateRequest(state, ClusterControllerStateRequest.Condition.SAFE);
try {
- return clusterControllerApi.apply(api -> api.setNodeState(clusterName, storageNodeIndex, stateRequest));
+ return clusterControllerApi.apply(api -> api.setNodeState(
+ clusterName,
+ storageNodeIndex,
+ context.getSuboperationTimeoutInSeconds(SHARE_REMAINING_TIME),
+ stateRequest)
+ );
} catch (IOException e) {
String message = String.format(
"Giving up setting %s for storage node with index %d in cluster %s",
@@ -52,12 +77,17 @@ public class ClusterControllerClientImpl implements ClusterControllerClient{
* @throws IOException if there was a problem communicating with the cluster controller
*/
@Override
- public ClusterControllerStateResponse setApplicationState(final ClusterControllerNodeState wantedState) throws IOException {
+ public ClusterControllerStateResponse setApplicationState(
+ OrchestratorContext context,
+ final ClusterControllerNodeState wantedState) throws IOException {
final ClusterControllerStateRequest.State state = new ClusterControllerStateRequest.State(wantedState, REQUEST_REASON);
final ClusterControllerStateRequest stateRequest = new ClusterControllerStateRequest(state, ClusterControllerStateRequest.Condition.FORCE);
try {
- return clusterControllerApi.apply(api -> api.setClusterState(clusterName, stateRequest));
+ return clusterControllerApi.apply(api -> api.setClusterState(
+ clusterName,
+ context.getSuboperationTimeoutInSeconds(SHARE_REMAINING_TIME),
+ stateRequest));
} catch (IOException e) {
final String message = String.format(
"Giving up setting %s for cluster %s",
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerJaxRsApi.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerJaxRsApi.java
index 7059706c569..4831f4c121a 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerJaxRsApi.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerJaxRsApi.java
@@ -6,6 +6,7 @@ import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
/**
@@ -20,6 +21,7 @@ public interface ClusterControllerJaxRsApi {
ClusterControllerStateResponse setNodeState(
@PathParam("clusterName") String clusterName,
@PathParam("storageNodeIndex") int storageNodeIndex,
+ @QueryParam("timeout") Float timeoutSeconds,
ClusterControllerStateRequest request);
@POST
@@ -28,6 +30,7 @@ public interface ClusterControllerJaxRsApi {
@Produces(MediaType.APPLICATION_JSON)
ClusterControllerStateResponse setClusterState(
@PathParam("clusterName") String clusterName,
+ @QueryParam("timeout") Float timeoutSeconds,
ClusterControllerStateRequest request);
}
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 c7aae6ea93d..5571eedeec6 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
@@ -41,7 +41,13 @@ public class RetryingClusterControllerClientFactory implements ClusterController
Set<HostName> clusterControllerSet = clusterControllers.stream().collect(Collectors.toSet());
JaxRsStrategy<ClusterControllerJaxRsApi> jaxRsApi
= new JaxRsStrategyFactory(clusterControllerSet, HARDCODED_CLUSTERCONTROLLER_PORT, jaxRsClientFactory, CLUSTERCONTROLLER_SCHEME)
- .apiWithRetries(ClusterControllerJaxRsApi.class, CLUSTERCONTROLLER_API_PATH);
+ .apiWithRetries(ClusterControllerJaxRsApi.class, CLUSTERCONTROLLER_API_PATH)
+ // Use max iteration 1. The JaxRsStrategyFactory will try host 1, 2, then 3:
+ // - If host 1 responds, it will redirect to master if necessary. Otherwise
+ // - If host 2 responds, it will redirect to master if necessary. Otherwise
+ // - If host 3 responds, it may redirect to master if necessary (if they're up
+ // after all), but more likely there's no quorum and this will fail too.
+ .setMaxIterations(1);
return new ClusterControllerClientImpl(jaxRsApi, clusterName);
}
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/StorageNode.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/StorageNode.java
index 210f68e8fda..a54d829a029 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/StorageNode.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/StorageNode.java
@@ -2,10 +2,11 @@
package com.yahoo.vespa.orchestrator.model;
import com.yahoo.vespa.applicationmodel.HostName;
+import com.yahoo.vespa.orchestrator.OrchestratorContext;
import com.yahoo.vespa.orchestrator.controller.ClusterControllerNodeState;
import com.yahoo.vespa.orchestrator.policy.HostStateChangeDeniedException;
public interface StorageNode extends Comparable<StorageNode> {
HostName hostName();
- void setNodeState(ClusterControllerNodeState wantedState) throws HostStateChangeDeniedException;
+ void setNodeState(OrchestratorContext context, ClusterControllerNodeState wantedState) throws HostStateChangeDeniedException;
}
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/StorageNodeImpl.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/StorageNodeImpl.java
index 109acbc6486..3e387012d2c 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/StorageNodeImpl.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/StorageNodeImpl.java
@@ -1,12 +1,14 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.orchestrator.model;
+import com.google.common.util.concurrent.UncheckedTimeoutException;
import com.yahoo.log.LogLevel;
import com.yahoo.vespa.applicationmodel.ApplicationInstance;
import com.yahoo.vespa.applicationmodel.ClusterId;
import com.yahoo.vespa.applicationmodel.ConfigId;
import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.applicationmodel.ServiceInstance;
+import com.yahoo.vespa.orchestrator.OrchestratorContext;
import com.yahoo.vespa.orchestrator.controller.ClusterControllerClient;
import com.yahoo.vespa.orchestrator.controller.ClusterControllerClientFactory;
import com.yahoo.vespa.orchestrator.controller.ClusterControllerNodeState;
@@ -43,7 +45,7 @@ public class StorageNodeImpl implements StorageNode {
}
@Override
- public void setNodeState(ClusterControllerNodeState wantedNodeState)
+ public void setNodeState(OrchestratorContext context, ClusterControllerNodeState wantedNodeState)
throws HostStateChangeDeniedException {
// The "cluster name" used by the Cluster Controller IS the cluster ID.
String clusterId = this.clusterId.s();
@@ -66,13 +68,20 @@ public class StorageNodeImpl implements StorageNode {
ClusterControllerStateResponse response;
try {
- response = client.setNodeState(nodeIndex, wantedNodeState);
+ response = client.setNodeState(context, nodeIndex, wantedNodeState);
} catch (IOException e) {
throw new HostStateChangeDeniedException(
hostName(),
HostedVespaPolicy.CLUSTER_CONTROLLER_AVAILABLE_CONSTRAINT,
"Failed to communicate with cluster controllers " + clusterControllers + ": " + e,
e);
+ } catch (UncheckedTimeoutException e) {
+ throw new HostStateChangeDeniedException(
+ hostName(),
+ HostedVespaPolicy.DEADLINE_CONSTRAINT,
+ "Timeout while waiting for setNodeState(" + nodeIndex + ", " + wantedNodeState +
+ ") against " + clusterControllers + ": " + e.getMessage(),
+ e);
}
if ( ! response.wasModified) {
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicy.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicy.java
index 8e02f940127..a781fd2358a 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicy.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicy.java
@@ -1,10 +1,10 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.orchestrator.policy;
-import com.yahoo.config.provision.ApplicationId;
import com.yahoo.log.LogLevel;
import com.yahoo.vespa.applicationmodel.ApplicationInstance;
import com.yahoo.vespa.applicationmodel.HostName;
+import com.yahoo.vespa.orchestrator.OrchestratorContext;
import com.yahoo.vespa.orchestrator.controller.ClusterControllerClientFactory;
import com.yahoo.vespa.orchestrator.controller.ClusterControllerNodeState;
import com.yahoo.vespa.orchestrator.model.ApplicationApi;
@@ -28,6 +28,7 @@ public class HostedVespaPolicy implements Policy {
public static final String ENOUGH_SERVICES_UP_CONSTRAINT = "enough-services-up";
public static final String SET_NODE_STATE_CONSTRAINT = "controller-set-node-state";
public static final String CLUSTER_CONTROLLER_AVAILABLE_CONSTRAINT = "controller-available";
+ public static final String DEADLINE_CONSTRAINT = "deadline";
private static final Logger log = Logger.getLogger(HostedVespaPolicy.class.getName());
@@ -40,7 +41,7 @@ public class HostedVespaPolicy implements Policy {
}
@Override
- public void grantSuspensionRequest(ApplicationApi application)
+ public void grantSuspensionRequest(OrchestratorContext context, ApplicationApi application)
throws HostStateChangeDeniedException {
// Apply per-cluster policy
for (ClusterApi cluster : application.getClusters()) {
@@ -50,7 +51,7 @@ public class HostedVespaPolicy implements Policy {
// Ask Cluster Controller to set UP storage nodes in maintenance.
// These storage nodes are guaranteed to be NO_REMARKS
for (StorageNode storageNode : application.getUpStorageNodesInGroupInClusterOrder()) {
- storageNode.setNodeState(ClusterControllerNodeState.MAINTENANCE);
+ storageNode.setNodeState(context, ClusterControllerNodeState.MAINTENANCE);
log.log(LogLevel.INFO, "The storage node on " + storageNode.hostName() + " has been set to MAINTENANCE");
}
@@ -62,10 +63,11 @@ public class HostedVespaPolicy implements Policy {
}
@Override
- public void releaseSuspensionGrant(ApplicationApi application) throws HostStateChangeDeniedException {
+ public void releaseSuspensionGrant(OrchestratorContext context, ApplicationApi application)
+ throws HostStateChangeDeniedException {
// Always defer to Cluster Controller whether it's OK to resume storage node
for (StorageNode storageNode : application.getStorageNodesAllowedToBeDownInGroupInReverseClusterOrder()) {
- storageNode.setNodeState(ClusterControllerNodeState.UP);
+ storageNode.setNodeState(context, ClusterControllerNodeState.UP);
log.log(LogLevel.INFO, "The storage node on " + storageNode.hostName() + " has been set to UP");
}
@@ -76,7 +78,8 @@ public class HostedVespaPolicy implements Policy {
}
@Override
- public void acquirePermissionToRemove(ApplicationApi applicationApi) throws HostStateChangeDeniedException {
+ public void acquirePermissionToRemove(OrchestratorContext context, ApplicationApi applicationApi)
+ throws HostStateChangeDeniedException {
ApplicationInstanceStatus applicationStatus = applicationApi.getApplicationStatus();
if (applicationStatus == ApplicationInstanceStatus.ALLOWED_TO_BE_DOWN) {
throw new HostStateChangeDeniedException(
@@ -94,7 +97,7 @@ public class HostedVespaPolicy implements Policy {
// Ask Cluster Controller to set storage nodes to DOWN.
// These storage nodes are guaranteed to be NO_REMARKS
for (StorageNode storageNode : applicationApi.getStorageNodesInGroupInClusterOrder()) {
- storageNode.setNodeState(ClusterControllerNodeState.DOWN);
+ storageNode.setNodeState(context, ClusterControllerNodeState.DOWN);
log.log(LogLevel.INFO, "The storage node on " + storageNode.hostName() + " has been set DOWN");
}
@@ -107,24 +110,14 @@ public class HostedVespaPolicy implements Policy {
// TODO: Remove later - currently used for backward compatibility testing
@Override
- public void grantSuspensionRequest(ApplicationInstance applicationInstance,
- HostName hostName,
- MutableStatusRegistry hostStatusService) throws HostStateChangeDeniedException {
- NodeGroup nodeGroup = new NodeGroup(applicationInstance);
- nodeGroup.addNode(hostName);
- ApplicationApi applicationApi = new ApplicationApiImpl(nodeGroup, hostStatusService, clusterControllerClientFactory);
- grantSuspensionRequest(applicationApi);
- }
-
- // TODO: Remove later - currently used for backward compatibility testing
- @Override
public void releaseSuspensionGrant(
+ OrchestratorContext context,
ApplicationInstance applicationInstance,
HostName hostName,
MutableStatusRegistry hostStatusService) throws HostStateChangeDeniedException {
NodeGroup nodeGroup = new NodeGroup(applicationInstance, hostName);
ApplicationApi applicationApi = new ApplicationApiImpl(nodeGroup, hostStatusService, clusterControllerClientFactory);
- releaseSuspensionGrant(applicationApi);
+ releaseSuspensionGrant(context, applicationApi);
}
}
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/policy/Policy.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/policy/Policy.java
index 4ea4f81182f..9938d244657 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/policy/Policy.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/policy/Policy.java
@@ -3,6 +3,7 @@ package com.yahoo.vespa.orchestrator.policy;
import com.yahoo.vespa.applicationmodel.ApplicationInstance;
import com.yahoo.vespa.applicationmodel.HostName;
+import com.yahoo.vespa.orchestrator.OrchestratorContext;
import com.yahoo.vespa.orchestrator.model.ApplicationApi;
import com.yahoo.vespa.orchestrator.status.MutableStatusRegistry;
@@ -10,30 +11,20 @@ import com.yahoo.vespa.orchestrator.status.MutableStatusRegistry;
* @author oyving
*/
public interface Policy {
-
- /**
- * Decide whether to grant a request for temporarily suspending the services on a host.
- *
- * @throws HostStateChangeDeniedException if the grant was not given.
- */
- void grantSuspensionRequest(
- ApplicationInstance applicationInstance,
- HostName hostName,
- MutableStatusRegistry hostStatusService) throws HostStateChangeDeniedException;
-
/**
* Decide whether to grant a request for temporarily suspending the services on all hosts in the group.
*/
- void grantSuspensionRequest(ApplicationApi applicationApi) throws HostStateChangeDeniedException;
+ void grantSuspensionRequest(OrchestratorContext context, ApplicationApi applicationApi) throws HostStateChangeDeniedException;
- void releaseSuspensionGrant(ApplicationApi application) throws HostStateChangeDeniedException;
+ void releaseSuspensionGrant(OrchestratorContext context, ApplicationApi application) throws HostStateChangeDeniedException;
/**
* Give all hosts in a group permission to be removed from the application.
*
+ * @param context
* @param applicationApi
*/
- void acquirePermissionToRemove(ApplicationApi applicationApi) throws HostStateChangeDeniedException;
+ void acquirePermissionToRemove(OrchestratorContext context, ApplicationApi applicationApi) throws HostStateChangeDeniedException;
/**
* Release an earlier grant for suspension.
@@ -41,7 +32,7 @@ public interface Policy {
* @throws HostStateChangeDeniedException if the release failed.
*/
void releaseSuspensionGrant(
- ApplicationInstance applicationInstance,
+ OrchestratorContext context, ApplicationInstance applicationInstance,
HostName hostName,
MutableStatusRegistry hostStatusService) throws HostStateChangeDeniedException;
}
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/InMemoryStatusService.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/InMemoryStatusService.java
index cab14a2e77f..c5ae553a98c 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/InMemoryStatusService.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/InMemoryStatusService.java
@@ -44,6 +44,13 @@ public class InMemoryStatusService implements StatusService {
@Override
public MutableStatusRegistry lockApplicationInstance_forCurrentThreadOnly(ApplicationInstanceReference applicationInstanceReference) {
+ return lockApplicationInstance_forCurrentThreadOnly(applicationInstanceReference, 10);
+ }
+
+ @Override
+ public MutableStatusRegistry lockApplicationInstance_forCurrentThreadOnly(
+ ApplicationInstanceReference applicationInstanceReference,
+ long timeoutSeconds) {
Lock lock = instanceLockService.get(applicationInstanceReference);
return new InMemoryMutableStatusRegistry(lock, applicationInstanceReference);
}
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/MutableStatusRegistry.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/MutableStatusRegistry.java
index 42b869473a1..b699a9f8962 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/MutableStatusRegistry.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/MutableStatusRegistry.java
@@ -7,7 +7,7 @@ import com.yahoo.vespa.applicationmodel.HostName;
* Registry of the suspension and host statuses for an application instance.
*
* @author oyving
- * @author tonytv
+ * @author Tony Vaagenes
* @author bakksjo
*/
public interface MutableStatusRegistry extends ReadOnlyStatusRegistry, AutoCloseable {
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/NoThrow.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/NoThrow.java
index 4228c92e225..fd6757f83cf 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/NoThrow.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/NoThrow.java
@@ -11,7 +11,7 @@ import java.lang.annotation.Target;
* They are still allowed to throw Errors, such as AssertionError
*
* TODO: move to vespajlib or find a suitable replacement
- * @author tonytv
+ * @author Tony Vaagenes
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ReadOnlyStatusRegistry.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ReadOnlyStatusRegistry.java
index 5e23af70d13..09300ef18a8 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ReadOnlyStatusRegistry.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ReadOnlyStatusRegistry.java
@@ -7,7 +7,7 @@ import com.yahoo.vespa.applicationmodel.HostName;
* Read-only view of statuses for the application instance and its hosts.
*
* @author oyving
- * @author tonytv
+ * @author Tony Vaagenes
* @author bakksjo
*/
public interface ReadOnlyStatusRegistry {
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/StatusService.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/StatusService.java
index cf7b40ce0ef..c47be096242 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/StatusService.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/StatusService.java
@@ -13,7 +13,7 @@ import java.util.Set;
* TODO Remove readonly registry class (replace with actual methods) - only adds complexity.
*
* @author oyving
- * @author tonytv
+ * @author Tony Vaagenes
* @author smorgrav
*/
public interface StatusService {
@@ -54,6 +54,11 @@ public interface StatusService {
*/
MutableStatusRegistry lockApplicationInstance_forCurrentThreadOnly(ApplicationInstanceReference applicationInstanceReference);
+ /** Lock application instance with timeout. */
+ MutableStatusRegistry lockApplicationInstance_forCurrentThreadOnly(
+ ApplicationInstanceReference applicationInstanceReference,
+ long timeoutSeconds);
+
/**
* Returns all application instances that are allowed to be down. The intention is to use this
* for visualization, informational and debugging purposes.
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ZookeeperStatusService.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ZookeeperStatusService.java
index c84473a5199..deece6a4a65 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ZookeeperStatusService.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ZookeeperStatusService.java
@@ -25,7 +25,7 @@ import java.util.logging.Logger;
* Stores instance suspension status and which hosts are allowed to go down in zookeeper.
*
* TODO: expiry of old application instances
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ZookeeperStatusService implements StatusService {
@@ -93,7 +93,8 @@ public class ZookeeperStatusService implements StatusService {
}
}
- MutableStatusRegistry lockApplicationInstance_forCurrentThreadOnly(
+ @Override
+ public MutableStatusRegistry lockApplicationInstance_forCurrentThreadOnly(
ApplicationInstanceReference applicationInstanceReference,
long timeoutSeconds) {
String lockPath = applicationInstanceLock2Path(applicationInstanceReference);
diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/TestIds.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/TestIds.java
index c71643b08e0..41e02d20a2a 100644
--- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/TestIds.java
+++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/TestIds.java
@@ -7,7 +7,7 @@ import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.applicationmodel.TenantId;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class TestIds {
public static final ApplicationInstanceReference APPLICATION_INSTANCE_REFERENCE =
diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientFactoryMock.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientFactoryMock.java
index 230e36469d3..5c5ee7d2260 100644
--- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientFactoryMock.java
+++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientFactoryMock.java
@@ -5,6 +5,7 @@ import com.yahoo.vespa.applicationmodel.ApplicationInstance;
import com.yahoo.vespa.applicationmodel.ClusterId;
import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.orchestrator.DummyInstanceLookupService;
+import com.yahoo.vespa.orchestrator.OrchestratorContext;
import com.yahoo.vespa.orchestrator.model.VespaModelUtil;
import java.io.IOException;
@@ -52,13 +53,13 @@ public class ClusterControllerClientFactoryMock implements ClusterControllerClie
return new ClusterControllerClient() {
@Override
- public ClusterControllerStateResponse setNodeState(int storageNodeIndex, ClusterControllerNodeState wantedState) throws IOException {
+ public ClusterControllerStateResponse setNodeState(OrchestratorContext context, int storageNodeIndex, ClusterControllerNodeState wantedState) throws IOException {
nodes.put(clusterName + storageNodeIndex, wantedState);
return new ClusterControllerStateResponse(true, "Yes");
}
@Override
- public ClusterControllerStateResponse setApplicationState(ClusterControllerNodeState wantedState) throws IOException {
+ public ClusterControllerStateResponse setApplicationState(OrchestratorContext context, ClusterControllerNodeState wantedState) throws IOException {
Set<String> keyCopy = new HashSet<>(nodes.keySet());
for (String s : keyCopy) {
if (s.startsWith(clusterName)) {
diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientTest.java
index 09d52326eb8..228174a9b3d 100644
--- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientTest.java
+++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientTest.java
@@ -3,12 +3,15 @@ package com.yahoo.vespa.orchestrator.controller;
import com.yahoo.vespa.jaxrs.client.JaxRsStrategy;
import com.yahoo.vespa.jaxrs.client.LocalPassThroughJaxRsStrategy;
+import com.yahoo.vespa.orchestrator.OrchestratorContext;
import org.junit.Test;
+import static org.mockito.Matchers.anyFloat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
public class ClusterControllerClientTest {
private static final String CLUSTER_NAME = "clusterName";
@@ -24,7 +27,9 @@ public class ClusterControllerClientTest {
final ClusterControllerNodeState wantedState = ClusterControllerNodeState.MAINTENANCE;
- clusterControllerClient.setNodeState(STORAGE_NODE_INDEX, wantedState);
+ OrchestratorContext context = mock(OrchestratorContext.class);
+ when(context.getSuboperationTimeoutInSeconds(anyFloat())).thenReturn(1.0f);
+ clusterControllerClient.setNodeState(context, STORAGE_NODE_INDEX, wantedState);
final ClusterControllerStateRequest expectedNodeStateRequest = new ClusterControllerStateRequest(
new ClusterControllerStateRequest.State(wantedState, ClusterControllerClientImpl.REQUEST_REASON),
@@ -33,6 +38,7 @@ public class ClusterControllerClientTest {
.setNodeState(
eq(CLUSTER_NAME),
eq(STORAGE_NODE_INDEX),
+ eq(1.0f),
eq(expectedNodeStateRequest));
}
}
diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/SingleInstanceClusterControllerClientFactoryTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/SingleInstanceClusterControllerClientFactoryTest.java
index b26d11a113f..4dabae14add 100644
--- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/SingleInstanceClusterControllerClientFactoryTest.java
+++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/SingleInstanceClusterControllerClientFactoryTest.java
@@ -4,6 +4,7 @@ package com.yahoo.vespa.orchestrator.controller;
import com.yahoo.vespa.applicationmodel.ConfigId;
import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.jaxrs.client.JaxRsClientFactory;
+import com.yahoo.vespa.orchestrator.OrchestratorContext;
import org.junit.Before;
import org.junit.Test;
@@ -15,6 +16,7 @@ import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyFloat;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.argThat;
@@ -31,6 +33,7 @@ public class SingleInstanceClusterControllerClientFactoryTest {
private static final HostName HOST_NAME_2 = new HostName("host2");
private static final HostName HOST_NAME_3 = new HostName("host3");
+ OrchestratorContext context = mock(OrchestratorContext.class);
private final ClusterControllerJaxRsApi mockApi = mock(ClusterControllerJaxRsApi.class);
private final JaxRsClientFactory jaxRsClientFactory = mock(JaxRsClientFactory.class);
private final ClusterControllerClientFactory clientFactory
@@ -64,8 +67,9 @@ public class SingleInstanceClusterControllerClientFactoryTest {
public void testCreateClientWithSingleClusterControllerInstance() throws Exception {
final List<HostName> clusterControllers = Arrays.asList(HOST_NAME_1);
+ when(context.getSuboperationTimeoutInSeconds(anyFloat())).thenReturn(1.0f);
clientFactory.createClient(clusterControllers, "clusterName")
- .setNodeState(0, ClusterControllerNodeState.MAINTENANCE);
+ .setNodeState(context, 0, ClusterControllerNodeState.MAINTENANCE);
verify(jaxRsClientFactory).createClient(
ClusterControllerJaxRsApi.class,
@@ -91,8 +95,9 @@ public class SingleInstanceClusterControllerClientFactoryTest {
public void testCreateClientWithThreeClusterControllerInstances() throws Exception {
final List<HostName> clusterControllers = Arrays.asList(HOST_NAME_1, HOST_NAME_2, HOST_NAME_3);
+ when(context.getSuboperationTimeoutInSeconds(anyFloat())).thenReturn(1.0f);
clientFactory.createClient(clusterControllers, "clusterName")
- .setNodeState(0, ClusterControllerNodeState.MAINTENANCE);
+ .setNodeState(context, 0, ClusterControllerNodeState.MAINTENANCE);
verify(jaxRsClientFactory).createClient(
eq(ClusterControllerJaxRsApi.class),
diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicyTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicyTest.java
index 220371c4a17..329c9576f2c 100644
--- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicyTest.java
+++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicyTest.java
@@ -5,6 +5,7 @@ package com.yahoo.vespa.orchestrator.policy;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.orchestrator.OrchestrationException;
+import com.yahoo.vespa.orchestrator.OrchestratorContext;
import com.yahoo.vespa.orchestrator.controller.ClusterControllerClient;
import com.yahoo.vespa.orchestrator.controller.ClusterControllerClientFactory;
import com.yahoo.vespa.orchestrator.controller.ClusterControllerNodeState;
@@ -70,7 +71,8 @@ public class HostedVespaPolicyTest {
InOrder order = inOrder(applicationApi, clusterPolicy, storageNode1, storageNode3);
- policy.grantSuspensionRequest(applicationApi);
+ OrchestratorContext context = mock(OrchestratorContext.class);
+ policy.grantSuspensionRequest(context, applicationApi);
order.verify(applicationApi).getClusters();
order.verify(clusterPolicy).verifyGroupGoingDownIsFine(clusterApi1);
@@ -78,8 +80,8 @@ public class HostedVespaPolicyTest {
order.verify(clusterPolicy).verifyGroupGoingDownIsFine(clusterApi3);
order.verify(applicationApi).getUpStorageNodesInGroupInClusterOrder();
- order.verify(storageNode1).setNodeState(ClusterControllerNodeState.MAINTENANCE);
- order.verify(storageNode3).setNodeState(ClusterControllerNodeState.MAINTENANCE);
+ order.verify(storageNode1).setNodeState(context, ClusterControllerNodeState.MAINTENANCE);
+ order.verify(storageNode3).setNodeState(context, ClusterControllerNodeState.MAINTENANCE);
order.verify(applicationApi).getNodesInGroupWithStatus(HostStatus.NO_REMARKS);
order.verify(applicationApi).setHostState(hostName1, HostStatus.ALLOWED_TO_BE_DOWN);
@@ -120,7 +122,8 @@ public class HostedVespaPolicyTest {
InOrder order = inOrder(applicationApi, clusterPolicy, storageNode1, storageNode3);
- policy.acquirePermissionToRemove(applicationApi);
+ OrchestratorContext context = mock(OrchestratorContext.class);
+ policy.acquirePermissionToRemove(context, applicationApi);
order.verify(applicationApi).getClusters();
order.verify(clusterPolicy).verifyGroupGoingDownPermanentlyIsFine(clusterApi1);
@@ -128,8 +131,8 @@ public class HostedVespaPolicyTest {
order.verify(clusterPolicy).verifyGroupGoingDownPermanentlyIsFine(clusterApi3);
order.verify(applicationApi).getStorageNodesInGroupInClusterOrder();
- order.verify(storageNode1).setNodeState(ClusterControllerNodeState.DOWN);
- order.verify(storageNode3).setNodeState(ClusterControllerNodeState.DOWN);
+ order.verify(storageNode1).setNodeState(context, ClusterControllerNodeState.DOWN);
+ order.verify(storageNode3).setNodeState(context, ClusterControllerNodeState.DOWN);
order.verify(applicationApi).getNodesInGroupWithStatus(HostStatus.NO_REMARKS);
order.verify(applicationApi).setHostState(hostName1, HostStatus.ALLOWED_TO_BE_DOWN);
diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/HostResourceTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/HostResourceTest.java
index 2c7db25ae30..45ba862c8f1 100644
--- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/HostResourceTest.java
+++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/HostResourceTest.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.orchestrator.resources;
+import com.yahoo.jdisc.Timer;
import com.yahoo.vespa.applicationmodel.ApplicationInstance;
import com.yahoo.vespa.applicationmodel.ApplicationInstanceId;
import com.yahoo.vespa.applicationmodel.ApplicationInstanceReference;
@@ -16,6 +17,7 @@ import com.yahoo.vespa.orchestrator.Host;
import com.yahoo.vespa.orchestrator.InstanceLookupService;
import com.yahoo.vespa.orchestrator.OrchestrationException;
import com.yahoo.vespa.orchestrator.Orchestrator;
+import com.yahoo.vespa.orchestrator.OrchestratorContext;
import com.yahoo.vespa.orchestrator.OrchestratorImpl;
import com.yahoo.vespa.orchestrator.controller.ClusterControllerClientFactoryMock;
import com.yahoo.vespa.orchestrator.model.ApplicationApi;
@@ -38,6 +40,7 @@ import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.net.URI;
+import java.time.Clock;
import java.util.Arrays;
import java.util.Collections;
import java.util.Optional;
@@ -59,7 +62,7 @@ import static org.mockito.Mockito.when;
* @author hakonhall
*/
public class HostResourceTest {
-
+ private static final Clock clock = mock(Clock.class);
private static final int SERVICE_MONITOR_CONVERGENCE_LATENCY_SECONDS = 0;
private static final TenantId TENANT_ID = new TenantId("tenantId");
private static final ApplicationInstanceId APPLICATION_INSTANCE_ID = new ApplicationInstanceId("applicationId");
@@ -110,28 +113,20 @@ public class HostResourceTest {
private static class AlwaysAllowPolicy implements Policy {
@Override
- public void grantSuspensionRequest(
- ApplicationInstance applicationInstance,
- HostName hostName,
- MutableStatusRegistry hostStatusService) {
-
- }
-
- @Override
- public void grantSuspensionRequest(ApplicationApi applicationApi) {
+ public void grantSuspensionRequest(OrchestratorContext context, ApplicationApi applicationApi) {
}
@Override
- public void releaseSuspensionGrant(ApplicationApi application) {
+ public void releaseSuspensionGrant(OrchestratorContext context, ApplicationApi application) {
}
@Override
- public void acquirePermissionToRemove(ApplicationApi applicationApi) {
+ public void acquirePermissionToRemove(OrchestratorContext context, ApplicationApi applicationApi) {
}
@Override
public void releaseSuspensionGrant(
- ApplicationInstance applicationInstance,
+ OrchestratorContext context, ApplicationInstance applicationInstance,
HostName hostName,
MutableStatusRegistry hostStatusRegistry) {
}
@@ -141,14 +136,16 @@ public class HostResourceTest {
new AlwaysAllowPolicy(),
new ClusterControllerClientFactoryMock(),
EVERY_HOST_IS_UP_HOST_STATUS_SERVICE, mockInstanceLookupService,
- SERVICE_MONITOR_CONVERGENCE_LATENCY_SECONDS
+ SERVICE_MONITOR_CONVERGENCE_LATENCY_SECONDS,
+ clock
);
private static final OrchestratorImpl hostNotFoundOrchestrator = new OrchestratorImpl(
new AlwaysAllowPolicy(),
new ClusterControllerClientFactoryMock(),
EVERY_HOST_IS_UP_HOST_STATUS_SERVICE, alwaysEmptyInstanceLookUpService,
- SERVICE_MONITOR_CONVERGENCE_LATENCY_SECONDS
+ SERVICE_MONITOR_CONVERGENCE_LATENCY_SECONDS,
+ clock
);
private final UriInfo uriInfo = mock(UriInfo.class);
@@ -209,31 +206,23 @@ public class HostResourceTest {
private static class AlwaysFailPolicy implements Policy {
@Override
- public void grantSuspensionRequest(
- ApplicationInstance applicationInstance,
- HostName hostName,
- MutableStatusRegistry hostStatusRegistry) throws HostStateChangeDeniedException {
- doThrow();
- }
-
- @Override
- public void grantSuspensionRequest(ApplicationApi applicationApi) throws HostStateChangeDeniedException {
+ public void grantSuspensionRequest(OrchestratorContext context, ApplicationApi applicationApi) throws HostStateChangeDeniedException {
doThrow();
}
@Override
- public void releaseSuspensionGrant(ApplicationApi application) throws HostStateChangeDeniedException {
+ public void releaseSuspensionGrant(OrchestratorContext context, ApplicationApi application) throws HostStateChangeDeniedException {
doThrow();
}
@Override
- public void acquirePermissionToRemove(ApplicationApi applicationApi) throws HostStateChangeDeniedException {
+ public void acquirePermissionToRemove(OrchestratorContext context, ApplicationApi applicationApi) throws HostStateChangeDeniedException {
doThrow();
}
@Override
public void releaseSuspensionGrant(
- ApplicationInstance applicationInstance,
+ OrchestratorContext context, ApplicationInstance applicationInstance,
HostName hostName,
MutableStatusRegistry hostStatusRegistry) throws HostStateChangeDeniedException {
doThrow();
@@ -253,7 +242,8 @@ public class HostResourceTest {
new AlwaysFailPolicy(),
new ClusterControllerClientFactoryMock(),
EVERY_HOST_IS_UP_HOST_STATUS_SERVICE,mockInstanceLookupService,
- SERVICE_MONITOR_CONVERGENCE_LATENCY_SECONDS);
+ SERVICE_MONITOR_CONVERGENCE_LATENCY_SECONDS,
+ clock);
try {
HostResource hostResource = new HostResource(alwaysRejectResolver, uriInfo);
@@ -271,7 +261,8 @@ public class HostResourceTest {
new ClusterControllerClientFactoryMock(),
EVERY_HOST_IS_UP_HOST_STATUS_SERVICE,
mockInstanceLookupService,
- SERVICE_MONITOR_CONVERGENCE_LATENCY_SECONDS);
+ SERVICE_MONITOR_CONVERGENCE_LATENCY_SECONDS,
+ clock);
try {
HostSuspensionResource hostSuspensionResource = new HostSuspensionResource(alwaysRejectResolver);
diff --git a/parent/pom.xml b/parent/pom.xml
index 10e93d4ffbf..51f6bc52da3 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -82,6 +82,12 @@
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>3.5.0</version>
+ <configuration>
+ <!-- TODO: remove when bundle-plugin understands java 10, https://issues.apache.org/jira/browse/FELIX-5879 -->
+ <instructions>
+ <_noee>true</_noee>
+ </instructions>
+ </configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -123,6 +129,11 @@
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-enforcer-plugin</artifactId>
+ <version>3.0.0-M2</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
<configuration>
@@ -140,12 +151,12 @@
<configuration>
<doclint>${doclint},-missing</doclint>
</configuration>
- <version>3.0.0</version>
+ <version>3.0.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
- <version>3.5</version>
+ <version>3.5.2</version>
<configuration>
<!-- see http://jira.codehaus.org/browse/MNG-5346 -->
<skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
@@ -171,6 +182,14 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.1</version>
+ <dependencies>
+ <!-- TODO: Remove for shade-plugin 3.1.2 - https://issues.apache.org/jira/browse/MSHADE-289 -->
+ <dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm</artifactId>
+ <version>${asm.version}</version>
+ </dependency>
+ </dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -226,19 +245,6 @@
<version>1.0.0</version>
</plugin>
<plugin>
- <groupId>net.alchim31.maven</groupId>
- <artifactId>scala-maven-plugin</artifactId>
- <version>3.2.2</version>
- <configuration>
- <args>
- <arg>-unchecked</arg>
- <arg>-deprecation</arg>
- <arg>-feature</arg>
- <arg>-Xfatal-warnings</arg>
- </args>
- </configuration>
- </plugin>
- <plugin>
<groupId>com.yahoo.vespa</groupId>
<artifactId>bundle-plugin</artifactId>
<version>${project.version}</version>
@@ -252,6 +258,27 @@
</build>
<profiles>
<profile>
+ <!-- TODO: move config into pluginManagement when we don't have to support older jdks -->
+ <id>jdk10</id>
+ <activation>
+ <jdk>[10, )</jdk>
+ </activation>
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <configuration>
+ <!-- Avoid javadoc warning ".. not specified the version of HTML .." -->
+ <additionalJOption>-html4</additionalJOption>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+ </profile>
+ <profile>
<id>attach-sources</id>
<activation>
<property>
@@ -317,35 +344,6 @@
<includePluginDependencies>true</includePluginDependencies>
</configuration>
</plugin>
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>build-helper-maven-plugin</artifactId>
- <executions>
- <execution>
- <phase>generate-sources</phase>
- <goals>
- <goal>add-source</goal>
- </goals>
- <configuration>
- <sources>
- <source>src/main/scala</source>
- </sources>
- </configuration>
- </execution>
- <execution>
- <id>add-test-source</id>
- <phase>generate-test-sources</phase>
- <goals>
- <goal>add-test-source</goal>
- </goals>
- <configuration>
- <sources>
- <source>src/test/scala</source>
- </sources>
- </configuration>
- </execution>
- </executions>
- </plugin>
</plugins>
</build>
</profile>
@@ -406,12 +404,7 @@
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
- <version>6.1.1</version>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava-testlib</artifactId>
- <version>18.0</version>
+ <version>${asm.version}</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
@@ -616,21 +609,6 @@
<scope>test</scope>
</dependency>
<dependency>
- <groupId>org.scala-lang.modules</groupId>
- <artifactId>scala-parser-combinators_${scala.major-version}</artifactId>
- <version>1.0.1</version>
- </dependency>
- <dependency>
- <groupId>org.scala-lang.modules</groupId>
- <artifactId>scala-xml_${scala.major-version}</artifactId>
- <version>1.0.2</version>
- </dependency>
- <dependency>
- <groupId>org.scalatest</groupId>
- <artifactId>scalatest_${scala.major-version}</artifactId>
- <version>2.2.2</version>
- </dependency>
- <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.0.6.RELEASE</version>
@@ -671,16 +649,32 @@
<version>${athenz.version}</version>
</dependency>
<dependency>
+ <groupId>com.yahoo.athenz</groupId>
+ <artifactId>athenz-zpe-java-client</artifactId>
+ <version>${athenz.version}</version>
+ </dependency>
+ <dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-standalone</artifactId>
<version>2.6.0</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.opennlp</groupId>
+ <artifactId>opennlp-tools</artifactId>
+ <version>1.8.4</version>
+ </dependency>
+ <dependency>
+ <groupId>com.optimaize.languagedetector</groupId>
+ <artifactId>language-detector</artifactId>
+ <version>0.6</version>
+ </dependency>
</dependencies>
</dependencyManagement>
<properties>
<antlr.version>3.5.2</antlr.version>
<antlr4.version>4.5</antlr4.version>
+ <asm.version>6.2</asm.version>
<!-- Athenz dependencies. Make sure these dependencies matches those in Vespa's internal repositories -->
<athenz.version>1.7.43</athenz.version>
<commons-lang.version>2.6</commons-lang.version>
@@ -695,7 +689,6 @@
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<test.hide>true</test.hide>
<doclint>all</doclint>
- <scala.major-version>2.11</scala.major-version> <!-- TODO: factorylib magic for maven cache bootstrapping prevents moving this to container-dep-versions. -->
<surefire.version>2.21.0</surefire.version>
</properties>
diff --git a/persistence/src/vespa/persistence/conformancetest/conformancetest.cpp b/persistence/src/vespa/persistence/conformancetest/conformancetest.cpp
index f9538a94c59..c1373a391f0 100644
--- a/persistence/src/vespa/persistence/conformancetest/conformancetest.cpp
+++ b/persistence/src/vespa/persistence/conformancetest/conformancetest.cpp
@@ -897,10 +897,8 @@ void ConformanceTest::testUpdate() {
const document::DocumentType *docType(
testDocMan.getTypeRepo().getDocumentType("testdoctype1"));
- document::DocumentUpdate::SP
- update(new DocumentUpdate(*docType, doc1->getId()));
- std::shared_ptr<document::AssignValueUpdate> assignUpdate(
- new document::AssignValueUpdate(document::IntFieldValue(42)));
+ document::DocumentUpdate::SP update(new DocumentUpdate(testDocMan.getTypeRepo(), *docType, doc1->getId()));
+ std::shared_ptr<document::AssignValueUpdate> assignUpdate(new document::AssignValueUpdate(document::IntFieldValue(42)));
document::FieldUpdate fieldUpdate(docType->getField("headerval"));
fieldUpdate.addUpdate(*assignUpdate);
update->addUpdate(fieldUpdate);
diff --git a/persistence/src/vespa/persistence/dummyimpl/dummypersistence.cpp b/persistence/src/vespa/persistence/dummyimpl/dummypersistence.cpp
index 0ce3cbe8b3d..25c6b71f7ff 100644
--- a/persistence/src/vespa/persistence/dummyimpl/dummypersistence.cpp
+++ b/persistence/src/vespa/persistence/dummyimpl/dummypersistence.cpp
@@ -188,7 +188,7 @@ BucketContent::insert(DocEntry::SP e)
"Was trying to insert %s.",
it->entry->toString().c_str(),
e->toString().c_str());
- assert(false);
+ LOG_ABORT("should not reach here");
}
}
}
@@ -528,7 +528,7 @@ DummyPersistence::get(const Bucket& b,
b.toString().c_str(),
did.toString().c_str());
assert(b.getBucketSpace() == FixedBucketSpaces::default_space());
- BucketContentGuard::UP bc(acquireBucketWithLock(b));
+ BucketContentGuard::UP bc(acquireBucketWithLock(b, LockMode::Shared));
if (!bc.get()) {
} else {
DocEntry::SP entry((*bc)->getEntry(did));
@@ -568,7 +568,7 @@ DummyPersistence::createIterator(
"Got invalid/unparseable document selection string");
}
}
- BucketContentGuard::UP bc(acquireBucketWithLock(b));
+ BucketContentGuard::UP bc(acquireBucketWithLock(b, LockMode::Shared));
if (!bc.get()) {
return CreateIteratorResult(Result::TRANSIENT_ERROR, "Bucket not found");
}
@@ -656,7 +656,7 @@ DummyPersistence::iterate(IteratorId id, uint64_t maxByteSize, Context& ctx) con
it = iter->second.get();
}
- BucketContentGuard::UP bc(acquireBucketWithLock(it->_bucket));
+ 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");
@@ -942,11 +942,11 @@ DummyPersistence::isActive(const Bucket& b) const
BucketContentGuard::~BucketContentGuard()
{
- _persistence.releaseBucketNoLock(_content);
+ _persistence.releaseBucketNoLock(_content, _lock_mode);
}
BucketContentGuard::UP
-DummyPersistence::acquireBucketWithLock(const Bucket& b) const
+DummyPersistence::acquireBucketWithLock(const Bucket& b, LockMode lock_mode) const
{
assert(b.getBucketSpace() == FixedBucketSpaces::default_space());
vespalib::MonitorGuard lock(_monitor);
@@ -955,28 +955,32 @@ DummyPersistence::acquireBucketWithLock(const Bucket& b) const
if (it == ncp._content[b.getPartition()].end()) {
return BucketContentGuard::UP();
}
- // Sanity check that SPI-level locking is doing its job correctly.
- // Atomic CAS might be a bit overkill, but since we "release" the bucket
- // outside of the mutex, we want to ensure the write is visible across all
- // threads.
- bool my_false(false);
- bool bucketNotInUse(it->second->_inUse.compare_exchange_strong(my_false, true));
- if (!bucketNotInUse) {
- LOG(error, "Attempted to acquire %s, but it was already marked as being in use!",
- b.toString().c_str());
- assert(false);
+ if (lock_mode == LockMode::Exclusive) {
+ // Sanity check that SPI-level locking is doing its job correctly.
+ // Atomic CAS might be a bit overkill, but since we "release" the bucket
+ // outside of the mutex, we want to ensure the write is visible across all
+ // threads.
+ bool my_false(false);
+ bool bucketNotInUse(it->second->_inUse.compare_exchange_strong(my_false, true));
+ if (!bucketNotInUse) {
+ LOG(error, "Attempted to acquire %s, but it was already marked as being in use!",
+ b.toString().c_str());
+ LOG_ABORT("dummy persistence bucket locking invariant violation");
+ }
}
- return BucketContentGuard::UP(new BucketContentGuard(ncp, *it->second));
+ return std::make_unique<BucketContentGuard>(ncp, *it->second, lock_mode);
}
void
-DummyPersistence::releaseBucketNoLock(const BucketContent& bc) const
+DummyPersistence::releaseBucketNoLock(const BucketContent& bc, LockMode lock_mode) const noexcept
{
- bool my_true(true);
- bool bucketInUse(bc._inUse.compare_exchange_strong(my_true, false));
- assert(bucketInUse);
- (void) bucketInUse;
+ if (lock_mode == LockMode::Exclusive) {
+ bool my_true(true);
+ bool bucketInUse(bc._inUse.compare_exchange_strong(my_true, false));
+ assert(bucketInUse);
+ (void) bucketInUse;
+ }
}
}
diff --git a/persistence/src/vespa/persistence/dummyimpl/dummypersistence.h b/persistence/src/vespa/persistence/dummyimpl/dummypersistence.h
index c93b7fd22c7..c97aab822ac 100644
--- a/persistence/src/vespa/persistence/dummyimpl/dummypersistence.h
+++ b/persistence/src/vespa/persistence/dummyimpl/dummypersistence.h
@@ -24,13 +24,18 @@ class DocumentTypeRepo;
namespace storage::spi::dummy {
+enum class LockMode {
+ Exclusive,
+ Shared
+};
+
struct BucketEntry
{
DocEntry::SP entry;
GlobalId gid;
BucketEntry(DocEntry::SP e, const GlobalId& g)
- : entry(e),
+ : entry(std::move(e)),
gid(g)
{ }
};
@@ -98,30 +103,33 @@ class BucketContentGuard
BucketContentGuard(const BucketContentGuard&);
BucketContentGuard& operator=(const BucketContentGuard&);
public:
- typedef std::unique_ptr<BucketContentGuard> UP;
+ using UP = std::unique_ptr<BucketContentGuard>;
BucketContentGuard(DummyPersistence& persistence,
- BucketContent& content)
+ BucketContent& content,
+ LockMode lock_mode)
: _persistence(persistence),
- _content(content)
+ _content(content),
+ _lock_mode(lock_mode)
{
}
~BucketContentGuard();
- BucketContent& getContent() {
+ BucketContent& getContent() noexcept {
return _content;
}
- BucketContent* operator->() {
+ BucketContent* operator->() noexcept {
return &_content;
}
- BucketContent& operator*() {
+ BucketContent& operator*() noexcept {
return _content;
}
private:
DummyPersistence& _persistence;
BucketContent& _content;
+ LockMode _lock_mode;
};
class DummyPersistence : public AbstractPersistenceProvider
@@ -207,8 +215,8 @@ public:
private:
friend class BucketContentGuard;
// Const since funcs only alter mutable field in BucketContent
- BucketContentGuard::UP acquireBucketWithLock(const Bucket& b) const;
- void releaseBucketNoLock(const BucketContent& bc) const;
+ BucketContentGuard::UP acquireBucketWithLock(const Bucket& b, LockMode lock_mode = LockMode::Exclusive) const;
+ void releaseBucketNoLock(const BucketContent& bc, LockMode lock_mode = LockMode::Exclusive) const noexcept;
mutable bool _initialized;
std::shared_ptr<const document::DocumentTypeRepo> _repo;
diff --git a/persistence/src/vespa/persistence/spi/persistenceprovider.h b/persistence/src/vespa/persistence/spi/persistenceprovider.h
index b5f2fc198c4..96b3d385b87 100644
--- a/persistence/src/vespa/persistence/spi/persistenceprovider.h
+++ b/persistence/src/vespa/persistence/spi/persistenceprovider.h
@@ -232,6 +232,9 @@ struct PersistenceProvider
* document id. If no versions were found, or the document was removed,
* the result should be successful, but contain no document (see GetResult).
*
+ * Concurrency note: may be called concurrently with other read-only
+ * operations.
+ *
* @param fieldSet A set of fields that should be retrieved.
* @param id The document id to retrieve.
*/
@@ -253,6 +256,9 @@ struct PersistenceProvider
* iteration progress and selection criteria. destroyIterator will NOT
* be called when createIterator returns an error.
*
+ * Concurrency note: may be called concurrently with other read-only
+ * operations.
+ *
* @param selection Selection criteria used to limit the subset of
* the bucket's documents that will be returned by the iterator. The
* provider implementation may use these criteria to optimize its
@@ -323,6 +329,9 @@ struct PersistenceProvider
* iterator must only set this flag on the result and return without any
* documents.
*
+ * Concurrency note: may be called concurrently with other read-only
+ * operations.
+ *
* @param id An iterator ID returned by a previous call to createIterator
* @param maxByteSize An indication of the maximum number of bytes that
* should be returned.
diff --git a/persistence/src/vespa/persistence/spi/read_consistency.cpp b/persistence/src/vespa/persistence/spi/read_consistency.cpp
index d6a7abe425b..cad15a5263d 100644
--- a/persistence/src/vespa/persistence/spi/read_consistency.cpp
+++ b/persistence/src/vespa/persistence/spi/read_consistency.cpp
@@ -1,7 +1,9 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "read_consistency.h"
#include <iostream>
-#include <cassert>
+
+#include <vespa/log/log.h>
+LOG_SETUP(".persistence.spi.read_consistency");
namespace storage {
namespace spi {
@@ -17,7 +19,7 @@ operator<<(std::ostream& os, ReadConsistency consistency)
os << "WEAK";
break;
default:
- assert(false);
+ LOG_ABORT("should not reach here");
}
return os;
}
diff --git a/pom.xml b/pom.xml
index 1f766b2878d..b20d038ab3c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -25,7 +25,6 @@
<modules>
<module>application</module>
- <module>application-deploy-plugin</module>
<module>application-model</module>
<module>application-preprocessor</module>
<module>athenz-identity-provider-service</module>
@@ -57,10 +56,12 @@
<module>container-dev</module>
<module>container-di</module>
<module>container-disc</module>
+ <module>container-integration-test</module>
<module>container-jersey2</module>
<module>container-messagebus</module>
<module>container-search-and-docproc</module>
<module>container-search</module>
+ <module>container-search-gui</module>
<module>container-test</module>
<module>container-test-jars</module>
<module>controller-api</module>
@@ -93,6 +94,7 @@
<module>messagebus-disc</module>
<module>messagebus</module>
<module>metrics</module>
+ <module>model-inference</module>
<module>node-repository</module>
<module>node-admin</module>
<module>node-maintainer</module>
@@ -118,7 +120,6 @@
<module>vespaclient-core</module>
<module>vespaclient-container-plugin</module>
<module>vespaclient-java</module>
- <module>vespa-application-maven-plugin</module>
<module>vespa-athenz</module>
<module>vespa-documentgen-plugin</module>
<module>vespa_feed_perf</module>
diff --git a/predicate-search-core/src/main/java/com/yahoo/document/predicate/BinaryFormat.java b/predicate-search-core/src/main/java/com/yahoo/document/predicate/BinaryFormat.java
index aedfd415e1a..848306260ab 100644
--- a/predicate-search-core/src/main/java/com/yahoo/document/predicate/BinaryFormat.java
+++ b/predicate-search-core/src/main/java/com/yahoo/document/predicate/BinaryFormat.java
@@ -8,7 +8,7 @@ import com.yahoo.slime.Slime;
import java.util.Objects;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class BinaryFormat {
diff --git a/predicate-search-core/src/main/java/com/yahoo/document/predicate/BooleanPredicate.java b/predicate-search-core/src/main/java/com/yahoo/document/predicate/BooleanPredicate.java
index 9545d729b5e..4fe5675b03b 100644
--- a/predicate-search-core/src/main/java/com/yahoo/document/predicate/BooleanPredicate.java
+++ b/predicate-search-core/src/main/java/com/yahoo/document/predicate/BooleanPredicate.java
@@ -2,7 +2,7 @@
package com.yahoo.document.predicate;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class BooleanPredicate extends PredicateValue {
diff --git a/predicate-search-core/src/main/java/com/yahoo/document/predicate/Conjunction.java b/predicate-search-core/src/main/java/com/yahoo/document/predicate/Conjunction.java
index ec7eebec020..bfd2122dd69 100644
--- a/predicate-search-core/src/main/java/com/yahoo/document/predicate/Conjunction.java
+++ b/predicate-search-core/src/main/java/com/yahoo/document/predicate/Conjunction.java
@@ -8,7 +8,7 @@ import java.util.Iterator;
import java.util.List;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class Conjunction extends PredicateOperator {
diff --git a/predicate-search-core/src/main/java/com/yahoo/document/predicate/Disjunction.java b/predicate-search-core/src/main/java/com/yahoo/document/predicate/Disjunction.java
index fa7b97065de..a21a1ab897f 100644
--- a/predicate-search-core/src/main/java/com/yahoo/document/predicate/Disjunction.java
+++ b/predicate-search-core/src/main/java/com/yahoo/document/predicate/Disjunction.java
@@ -8,7 +8,7 @@ import java.util.Iterator;
import java.util.List;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class Disjunction extends PredicateOperator {
diff --git a/predicate-search-core/src/main/java/com/yahoo/document/predicate/FeatureRange.java b/predicate-search-core/src/main/java/com/yahoo/document/predicate/FeatureRange.java
index f3062f24e45..e6cf22c1d6b 100644
--- a/predicate-search-core/src/main/java/com/yahoo/document/predicate/FeatureRange.java
+++ b/predicate-search-core/src/main/java/com/yahoo/document/predicate/FeatureRange.java
@@ -6,7 +6,7 @@ import java.util.List;
import java.util.Objects;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class FeatureRange extends PredicateValue {
diff --git a/predicate-search-core/src/main/java/com/yahoo/document/predicate/FeatureSet.java b/predicate-search-core/src/main/java/com/yahoo/document/predicate/FeatureSet.java
index bd004ae7c30..525e9b417e1 100644
--- a/predicate-search-core/src/main/java/com/yahoo/document/predicate/FeatureSet.java
+++ b/predicate-search-core/src/main/java/com/yahoo/document/predicate/FeatureSet.java
@@ -9,7 +9,7 @@ import java.util.Set;
import java.util.TreeSet;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class FeatureSet extends PredicateValue {
diff --git a/predicate-search-core/src/main/java/com/yahoo/document/predicate/Negation.java b/predicate-search-core/src/main/java/com/yahoo/document/predicate/Negation.java
index 48481e255e4..ec63c442329 100644
--- a/predicate-search-core/src/main/java/com/yahoo/document/predicate/Negation.java
+++ b/predicate-search-core/src/main/java/com/yahoo/document/predicate/Negation.java
@@ -5,7 +5,7 @@ import java.util.List;
import java.util.Objects;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class Negation extends PredicateOperator {
diff --git a/predicate-search-core/src/main/java/com/yahoo/document/predicate/PredicateOperator.java b/predicate-search-core/src/main/java/com/yahoo/document/predicate/PredicateOperator.java
index c2df6f79b4c..032b7422ac8 100644
--- a/predicate-search-core/src/main/java/com/yahoo/document/predicate/PredicateOperator.java
+++ b/predicate-search-core/src/main/java/com/yahoo/document/predicate/PredicateOperator.java
@@ -4,7 +4,7 @@ package com.yahoo.document.predicate;
import java.util.List;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
abstract public class PredicateOperator extends Predicate {
diff --git a/predicate-search-core/src/main/java/com/yahoo/document/predicate/PredicateValue.java b/predicate-search-core/src/main/java/com/yahoo/document/predicate/PredicateValue.java
index ac4269c4529..607eef86273 100644
--- a/predicate-search-core/src/main/java/com/yahoo/document/predicate/PredicateValue.java
+++ b/predicate-search-core/src/main/java/com/yahoo/document/predicate/PredicateValue.java
@@ -2,7 +2,7 @@
package com.yahoo.document.predicate;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
abstract class PredicateValue extends Predicate {
diff --git a/predicate-search-core/src/main/java/com/yahoo/document/predicate/Predicates.java b/predicate-search-core/src/main/java/com/yahoo/document/predicate/Predicates.java
index 3ed31118c9b..55fb64376b0 100644
--- a/predicate-search-core/src/main/java/com/yahoo/document/predicate/Predicates.java
+++ b/predicate-search-core/src/main/java/com/yahoo/document/predicate/Predicates.java
@@ -2,7 +2,7 @@
package com.yahoo.document.predicate;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class Predicates {
diff --git a/predicate-search-core/src/main/java/com/yahoo/document/predicate/SimplePredicates.java b/predicate-search-core/src/main/java/com/yahoo/document/predicate/SimplePredicates.java
index 2c3216b18c9..ed2ac725095 100644
--- a/predicate-search-core/src/main/java/com/yahoo/document/predicate/SimplePredicates.java
+++ b/predicate-search-core/src/main/java/com/yahoo/document/predicate/SimplePredicates.java
@@ -5,7 +5,7 @@ import java.util.ArrayList;
import java.util.List;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class SimplePredicates {
diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/BinaryFormatTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/BinaryFormatTest.java
index 38a213bd9af..230b22b4ff5 100644
--- a/predicate-search-core/src/test/java/com/yahoo/document/predicate/BinaryFormatTest.java
+++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/BinaryFormatTest.java
@@ -9,7 +9,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class BinaryFormatTest {
diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/BooleanPredicateTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/BooleanPredicateTest.java
index 9d6b791d9e9..46acbc921d7 100644
--- a/predicate-search-core/src/test/java/com/yahoo/document/predicate/BooleanPredicateTest.java
+++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/BooleanPredicateTest.java
@@ -9,7 +9,7 @@ import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class BooleanPredicateTest {
diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/ConjunctionTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/ConjunctionTest.java
index a254c5f32cb..4397fc39c8a 100644
--- a/predicate-search-core/src/test/java/com/yahoo/document/predicate/ConjunctionTest.java
+++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/ConjunctionTest.java
@@ -11,7 +11,7 @@ import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ConjunctionTest {
diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/DisjunctionTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/DisjunctionTest.java
index 3946538b93e..4a3804dbfa9 100644
--- a/predicate-search-core/src/test/java/com/yahoo/document/predicate/DisjunctionTest.java
+++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/DisjunctionTest.java
@@ -11,7 +11,7 @@ import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class DisjunctionTest {
diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/FeatureRangeTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/FeatureRangeTest.java
index c7ffce040d1..581ce5cdccf 100644
--- a/predicate-search-core/src/test/java/com/yahoo/document/predicate/FeatureRangeTest.java
+++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/FeatureRangeTest.java
@@ -8,7 +8,7 @@ import java.util.Arrays;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class FeatureRangeTest {
diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/FeatureSetTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/FeatureSetTest.java
index 3c2d19cede1..baed1223a7f 100644
--- a/predicate-search-core/src/test/java/com/yahoo/document/predicate/FeatureSetTest.java
+++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/FeatureSetTest.java
@@ -17,7 +17,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class FeatureSetTest {
diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/NegationTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/NegationTest.java
index 6fb58d9382b..33caef5eaec 100644
--- a/predicate-search-core/src/test/java/com/yahoo/document/predicate/NegationTest.java
+++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/NegationTest.java
@@ -11,7 +11,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class NegationTest {
diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateOperatorTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateOperatorTest.java
index 833676f68dd..f69a0275454 100644
--- a/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateOperatorTest.java
+++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateOperatorTest.java
@@ -6,7 +6,7 @@ import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class PredicateOperatorTest {
diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateTest.java
index 26a37d55d69..729b31c81b6 100644
--- a/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateTest.java
+++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateTest.java
@@ -11,7 +11,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class PredicateTest {
diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateValueTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateValueTest.java
index b6a2016b2ab..78fb15e4160 100644
--- a/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateValueTest.java
+++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateValueTest.java
@@ -6,7 +6,7 @@ import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class PredicateValueTest {
diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicatesTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicatesTest.java
index fe2a21f36d6..b07df5e99d7 100644
--- a/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicatesTest.java
+++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicatesTest.java
@@ -11,7 +11,7 @@ import static com.yahoo.document.predicate.Predicates.value;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class PredicatesTest {
diff --git a/processing/src/main/java/com/yahoo/processing/execution/chain/ChainRegistry.java b/processing/src/main/java/com/yahoo/processing/execution/chain/ChainRegistry.java
index b07243c31b5..3d308965897 100644
--- a/processing/src/main/java/com/yahoo/processing/execution/chain/ChainRegistry.java
+++ b/processing/src/main/java/com/yahoo/processing/execution/chain/ChainRegistry.java
@@ -8,7 +8,7 @@ import com.yahoo.component.provider.ComponentRegistry;
/**
* A registry of chains
*
- * @author tonytv
+ * @author Tony Vaagenes
* @since 5.1.7
*/
public class ChainRegistry<T extends ChainedComponent> extends ComponentRegistry<Chain<T>> {
diff --git a/searchcommon/src/vespa/searchcommon/attribute/attributecontent.h b/searchcommon/src/vespa/searchcommon/attribute/attributecontent.h
index 6c48b02f357..508a2d04c27 100644
--- a/searchcommon/src/vespa/searchcommon/attribute/attributecontent.h
+++ b/searchcommon/src/vespa/searchcommon/attribute/attributecontent.h
@@ -3,10 +3,9 @@
#pragma once
#include "iattributevector.h"
-#include <stdint.h>
+#include <cstdint>
-namespace search {
-namespace attribute {
+namespace search::attribute {
/**
@@ -154,7 +153,6 @@ public:
}
};
-
typedef AttributeContent<double> FloatContent;
typedef AttributeContent<const char *> ConstCharContent;
typedef AttributeContent<IAttributeVector::largeint_t> IntegerContent;
@@ -166,7 +164,4 @@ typedef AttributeContent<IAttributeVector::WeightedString> WeightedStringCont
typedef AttributeContent<IAttributeVector::WeightedEnum> WeightedEnumContent;
typedef IAttributeVector::EnumHandle EnumHandle;
-
-} // namespace attribute
-} // namespace search
-
+}
diff --git a/searchcommon/src/vespa/searchcommon/attribute/basictype.cpp b/searchcommon/src/vespa/searchcommon/attribute/basictype.cpp
index c3cf0905b27..67093c686b5 100644
--- a/searchcommon/src/vespa/searchcommon/attribute/basictype.cpp
+++ b/searchcommon/src/vespa/searchcommon/attribute/basictype.cpp
@@ -3,8 +3,7 @@
#include <vespa/searchcommon/attribute/basictype.h>
#include <vespa/vespalib/util/exceptions.h>
-namespace search {
-namespace attribute {
+namespace search::attribute {
const BasicType::TypeInfo BasicType::_typeTable[BasicType::MAX_TYPE] = {
{ BasicType::NONE, 0, "none" },
@@ -31,11 +30,8 @@ BasicType::asType(const vespalib::string &t)
return _typeTable[i]._type;
}
}
- throw vespalib::IllegalStateException(t +
- " not recognized as "
- "valid attribute data type");
+ throw vespalib::IllegalStateException(t + " not recognized as valid attribute data type");
return NONE;
}
}
-}
diff --git a/searchcommon/src/vespa/searchcommon/attribute/basictype.h b/searchcommon/src/vespa/searchcommon/attribute/basictype.h
index d7023aa1c59..3a2a319afd8 100644
--- a/searchcommon/src/vespa/searchcommon/attribute/basictype.h
+++ b/searchcommon/src/vespa/searchcommon/attribute/basictype.h
@@ -4,8 +4,7 @@
#include <vespa/vespalib/stllike/string.h>
-namespace search {
-namespace attribute {
+namespace search::attribute {
class BasicType
{
@@ -66,5 +65,3 @@ class BasicType
};
}
-}
-
diff --git a/searchcommon/src/vespa/searchcommon/attribute/collectiontype.cpp b/searchcommon/src/vespa/searchcommon/attribute/collectiontype.cpp
index 1d0dcc5328b..d5d33718183 100644
--- a/searchcommon/src/vespa/searchcommon/attribute/collectiontype.cpp
+++ b/searchcommon/src/vespa/searchcommon/attribute/collectiontype.cpp
@@ -3,8 +3,7 @@
#include <vespa/searchcommon/attribute/collectiontype.h>
#include <vespa/vespalib/util/exceptions.h>
-namespace search {
-namespace attribute {
+namespace search::attribute {
const CollectionType::TypeInfo CollectionType::_typeTable[CollectionType::MAX_TYPE] = {
{ CollectionType::SINGLE, "single" },
@@ -20,11 +19,8 @@ CollectionType::asType(const vespalib::string &t)
return _typeTable[i]._type;
}
}
- throw vespalib::IllegalStateException(t +
- " not recognized as valid attribute "
- "collection type");
+ throw vespalib::IllegalStateException(t + " not recognized as valid attribute collection type");
return SINGLE;
}
}
-}
diff --git a/searchcommon/src/vespa/searchcommon/attribute/collectiontype.h b/searchcommon/src/vespa/searchcommon/attribute/collectiontype.h
index b5b43083307..2cd46ce2bba 100644
--- a/searchcommon/src/vespa/searchcommon/attribute/collectiontype.h
+++ b/searchcommon/src/vespa/searchcommon/attribute/collectiontype.h
@@ -4,8 +4,7 @@
#include <vespa/vespalib/stllike/string.h>
-namespace search {
-namespace attribute {
+namespace search::attribute {
class CollectionType
{
@@ -74,5 +73,3 @@ class CollectionType
};
}
-}
-
diff --git a/searchcommon/src/vespa/searchcommon/attribute/config.cpp b/searchcommon/src/vespa/searchcommon/attribute/config.cpp
index 36dd0c94b40..221924a1689 100644
--- a/searchcommon/src/vespa/searchcommon/attribute/config.cpp
+++ b/searchcommon/src/vespa/searchcommon/attribute/config.cpp
@@ -20,10 +20,7 @@ Config::Config() :
{
}
-Config::Config(BasicType bt,
- CollectionType ct,
- bool fastSearch_,
- bool huge_)
+Config::Config(BasicType bt, CollectionType ct, bool fastSearch_, bool huge_)
: _basicType(bt),
_type(ct),
_fastSearch(fastSearch_),
@@ -41,6 +38,6 @@ Config::Config(BasicType bt,
Config::Config(const Config &) = default;
Config & Config::operator = (const Config &) = default;
-Config::~Config() {}
+Config::~Config() = default;
}
diff --git a/searchcommon/src/vespa/searchcommon/attribute/config.h b/searchcommon/src/vespa/searchcommon/attribute/config.h
index 8092d620f36..683b45b59e5 100644
--- a/searchcommon/src/vespa/searchcommon/attribute/config.h
+++ b/searchcommon/src/vespa/searchcommon/attribute/config.h
@@ -9,8 +9,7 @@
#include <vespa/searchcommon/common/compaction_strategy.h>
#include <vespa/eval/eval/value_type.h>
-namespace search {
-namespace attribute {
+namespace search::attribute {
class Config
{
@@ -120,6 +119,5 @@ private:
PredicateParams _predicateParams;
vespalib::eval::ValueType _tensorType;
};
-} // namespace attribute
-} // namespace search
+}
diff --git a/searchcommon/src/vespa/searchcommon/attribute/i_search_context.h b/searchcommon/src/vespa/searchcommon/attribute/i_search_context.h
index d18c4840009..be6b8d213d8 100644
--- a/searchcommon/src/vespa/searchcommon/attribute/i_search_context.h
+++ b/searchcommon/src/vespa/searchcommon/attribute/i_search_context.h
@@ -5,7 +5,6 @@
#include <vespa/searchcommon/common/range.h>
#include <vespa/vespalib/stllike/string.h>
-
namespace search::fef { class TermFieldMatchData; }
namespace search::queryeval { class SearchIterator; }
namespace search { class QueryTermBase; }
@@ -48,7 +47,7 @@ public:
virtual bool valid() const = 0;
virtual Int64Range getAsIntegerTerm() const = 0;
- virtual const QueryTermBase &queryTerm() const = 0;
+ virtual const QueryTermBase * queryTerm() const = 0;
virtual const vespalib::string &attributeName() const = 0;
int32_t find(DocId docId, int32_t elementId, int32_t &weight) const { return onFind(docId, elementId, weight); }
diff --git a/searchcommon/src/vespa/searchcommon/attribute/iattributecontext.h b/searchcommon/src/vespa/searchcommon/attribute/iattributecontext.h
index 2ba61512338..c283abf3ced 100644
--- a/searchcommon/src/vespa/searchcommon/attribute/iattributecontext.h
+++ b/searchcommon/src/vespa/searchcommon/attribute/iattributecontext.h
@@ -3,11 +3,8 @@
#pragma once
#include "iattributevector.h"
-#include <vector>
-#include <memory>
-namespace search {
-namespace attribute {
+namespace search::attribute {
/**
* This is an interface used to access all registered attribute vectors.
@@ -53,6 +50,4 @@ public:
virtual ~IAttributeContext() {}
};
-} // namespace attribute
-} // namespace search
-
+}
diff --git a/searchcommon/src/vespa/searchcommon/attribute/iattributevector.h b/searchcommon/src/vespa/searchcommon/attribute/iattributevector.h
index c7993503107..0d3e58331c6 100644
--- a/searchcommon/src/vespa/searchcommon/attribute/iattributevector.h
+++ b/searchcommon/src/vespa/searchcommon/attribute/iattributevector.h
@@ -5,18 +5,18 @@
#include "collectiontype.h"
#include "basictype.h"
#include <vespa/searchcommon/common/iblobconverter.h>
-#include <vespa/vespalib/stllike/string.h>
+#include <vector>
namespace search {
+ class IDocumentWeightAttribute;
+ class QueryTermSimple;
+}
-class IDocumentWeightAttribute;
-class QueryTermSimple;
-
-namespace tensor {
-class ITensorAttribute;
+namespace search::tensor {
+ class ITensorAttribute;
}
-namespace attribute {
+namespace search::attribute {
class ISearchContext;
class SearchContextParams;
@@ -71,6 +71,11 @@ public:
**/
virtual const vespalib::string & getName() const = 0;
+ vespalib::stringref getNamePrefix() const {
+ vespalib::stringref name = getName();
+ return name.substr(0, name.find('.'));
+ }
+
/**
* Returns the number of documents stored in this attribute vector.
*
@@ -246,6 +251,16 @@ public:
virtual bool findEnum(const char * value, EnumHandle & e) const = 0;
/**
+ * Finds all enum values matching the given string value.
+ * This method will only have effect if @ref getBasicType() returns BasicType::STRING and
+ * @ref hasEnum() returns true.
+ *
+ * @param value the string value to lookup.
+ * @return vector of EnumHandles, size 0 if no match found.
+ **/
+ virtual std::vector<EnumHandle> findFoldedEnums(const char * value) const = 0;
+
+ /**
* Given an enum handle, returns the string it refers to.
* This method will only have effect if @ref getBasicType() returns BasicType::STRING and
* @ref hasEnum() returns true.
@@ -422,6 +437,4 @@ private:
};
-} // namespace fef
-} // namespace search
-
+}
diff --git a/searchcommon/src/vespa/searchcommon/attribute/predicate_params.h b/searchcommon/src/vespa/searchcommon/attribute/predicate_params.h
index 5510da7f54e..6a49bdd4d8e 100644
--- a/searchcommon/src/vespa/searchcommon/attribute/predicate_params.h
+++ b/searchcommon/src/vespa/searchcommon/attribute/predicate_params.h
@@ -4,8 +4,7 @@
#include "persistent_predicate_params.h"
-namespace search {
-namespace attribute {
+namespace search::attribute {
/*
* Parameters for predicate attributes.
@@ -28,5 +27,4 @@ public:
}
};
-} // namespace attribute
-} // namespace search
+}
diff --git a/searchcommon/src/vespa/searchcommon/attribute/status.cpp b/searchcommon/src/vespa/searchcommon/attribute/status.cpp
index 09b1e00a35e..d6d684a9f56 100644
--- a/searchcommon/src/vespa/searchcommon/attribute/status.cpp
+++ b/searchcommon/src/vespa/searchcommon/attribute/status.cpp
@@ -1,9 +1,8 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include <vespa/searchcommon/attribute/status.h>
+#include "status.h"
-namespace search {
-namespace attribute {
+namespace search::attribute {
Status::Status(const vespalib::string &)
: _numDocs (0),
@@ -42,8 +41,7 @@ Status::Status()
vespalib::string
-Status::createName(const vespalib::stringref &index,
- const vespalib::stringref &attr)
+Status::createName(vespalib::stringref index, vespalib::stringref attr)
{
vespalib::string name (index);
name += ".attribute.";
@@ -53,12 +51,8 @@ Status::createName(const vespalib::stringref &index,
void
-Status::updateStatistics(uint64_t numValues,
- uint64_t numUniqueValue,
- uint64_t allocated,
- uint64_t used,
- uint64_t dead,
- uint64_t onHold)
+Status::updateStatistics(uint64_t numValues, uint64_t numUniqueValue, uint64_t allocated,
+ uint64_t used, uint64_t dead, uint64_t onHold)
{
_numValues = numValues;
_numUniqueValues = numUniqueValue;
@@ -71,4 +65,3 @@ Status::updateStatistics(uint64_t numValues,
}
}
-}
diff --git a/searchcommon/src/vespa/searchcommon/attribute/status.h b/searchcommon/src/vespa/searchcommon/attribute/status.h
index 3f6a12cb88e..dc1ccc4d5d3 100644
--- a/searchcommon/src/vespa/searchcommon/attribute/status.h
+++ b/searchcommon/src/vespa/searchcommon/attribute/status.h
@@ -4,8 +4,7 @@
#include <vespa/vespalib/stllike/string.h>
-namespace search {
-namespace attribute {
+namespace search::attribute {
class Status
{
@@ -14,13 +13,8 @@ public:
Status(const vespalib::string &name);
Status();
- void
- updateStatistics(uint64_t numValues,
- uint64_t numUniqueValue,
- uint64_t allocated,
- uint64_t used,
- uint64_t dead,
- uint64_t onHold);
+ void updateStatistics(uint64_t numValues, uint64_t numUniqueValue, uint64_t allocated,
+ uint64_t used, uint64_t dead, uint64_t onHold);
uint64_t getNumDocs() const { return _numDocs; }
uint64_t getNumValues() const { return _numValues; }
@@ -33,32 +27,18 @@ public:
uint64_t getLastSyncToken() const { return _lastSyncToken; }
uint64_t getUpdateCount() const { return _updates; }
uint64_t getNonIdempotentUpdateCount() const { return _nonIdempotentUpdates; }
- uint32_t
- getBitVectors() const
- {
- return _bitVectors;
- }
+ uint32_t getBitVectors() const { return _bitVectors; }
void setNumDocs(uint64_t v) { _numDocs = v; }
void incNumDocs() { ++_numDocs; }
void setLastSyncToken(uint64_t v) { _lastSyncToken = v; }
void incUpdates(uint64_t v=1) { _updates += v; }
void incNonIdempotentUpdates(uint64_t v = 1) { _nonIdempotentUpdates += v; }
- void
- incBitVectors()
- {
- ++_bitVectors;
- }
-
- void
- decBitVectors()
- {
- --_bitVectors;
- }
+ void incBitVectors() { ++_bitVectors; }
+ void decBitVectors() { --_bitVectors; }
static vespalib::string
- createName(const vespalib::stringref &index,
- const vespalib::stringref & attr);
+ createName(vespalib::stringref index, vespalib::stringref attr);
private:
uint64_t _numDocs;
uint64_t _numValues;
@@ -76,5 +56,3 @@ private:
};
}
-}
-
diff --git a/searchcommon/src/vespa/searchcommon/common/iblobconverter.h b/searchcommon/src/vespa/searchcommon/common/iblobconverter.h
index f9f326b8c69..428a1d7c296 100644
--- a/searchcommon/src/vespa/searchcommon/common/iblobconverter.h
+++ b/searchcommon/src/vespa/searchcommon/common/iblobconverter.h
@@ -5,8 +5,7 @@
#include <vespa/vespalib/util/buffer.h>
#include <memory>
-namespace search {
-namespace common {
+namespace search::common {
class BlobConverter
{
@@ -21,5 +20,3 @@ private:
};
}
-}
-
diff --git a/searchcore/src/apps/vespa-gen-testdocs/vespa-gen-testdocs.cpp b/searchcore/src/apps/vespa-gen-testdocs/vespa-gen-testdocs.cpp
index f40502e14d1..e6b7b34903b 100644
--- a/searchcore/src/apps/vespa-gen-testdocs/vespa-gen-testdocs.cpp
+++ b/searchcore/src/apps/vespa-gen-testdocs/vespa-gen-testdocs.cpp
@@ -71,7 +71,7 @@ shafile(const string &baseDir,
bool openres = f.OpenReadOnly(fullFile.c_str());
if (!openres) {
LOG(error, "Could not open %s for sha256 checksum", fullFile.c_str());
- abort();
+ LOG_ABORT("should not be reached");
}
int64_t flen = f.GetSize();
int64_t remainder = flen;
diff --git a/searchcore/src/apps/vespa-transactionlog-inspect/vespa-transactionlog-inspect.cpp b/searchcore/src/apps/vespa-transactionlog-inspect/vespa-transactionlog-inspect.cpp
index 17f1faffbba..8ffe4807427 100644
--- a/searchcore/src/apps/vespa-transactionlog-inspect/vespa-transactionlog-inspect.cpp
+++ b/searchcore/src/apps/vespa-transactionlog-inspect/vespa-transactionlog-inspect.cpp
@@ -1,8 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include <vespa/config/helper/configgetter.h>
-
#include <vespa/searchcore/proton/server/replaypacketdispatcher.h>
+#include <vespa/searchcore/proton/feedoperation/operations.h>
#include <vespa/searchlib/common/fileheadercontext.h>
#include <vespa/searchlib/transactionlog/translogclient.h>
#include <vespa/searchlib/transactionlog/translogserver.h>
diff --git a/searchcore/src/tests/applyattrupdates/applyattrupdates.cpp b/searchcore/src/tests/applyattrupdates/applyattrupdates.cpp
index 4c4027da6d6..48c02215732 100644
--- a/searchcore/src/tests/applyattrupdates/applyattrupdates.cpp
+++ b/searchcore/src/tests/applyattrupdates/applyattrupdates.cpp
@@ -1,4 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
#include <vespa/document/base/testdocrepo.h>
#include <vespa/document/fieldvalue/arrayfieldvalue.h>
#include <vespa/document/fieldvalue/bytefieldvalue.h>
@@ -21,7 +22,6 @@
#include <vespa/vespalib/testkit/testapp.h>
#include <vespa/log/log.h>
-
LOG_SETUP("applyattrupdates_test");
using namespace document;
diff --git a/searchcore/src/tests/grouping/grouping.cpp b/searchcore/src/tests/grouping/grouping.cpp
index b159be5288c..7e21fffe9fd 100644
--- a/searchcore/src/tests/grouping/grouping.cpp
+++ b/searchcore/src/tests/grouping/grouping.cpp
@@ -12,6 +12,9 @@
#include <vespa/searchcore/proton/matching/sessionmanager.h>
#include <iostream>
+#include <vespa/log/log.h>
+LOG_SETUP("grouping_test");
+
using namespace search::attribute;
using namespace search::aggregation;
using namespace search::expression;
diff --git a/searchcore/src/tests/proton/attribute/attribute_aspect_delayer/attribute_aspect_delayer_test.cpp b/searchcore/src/tests/proton/attribute/attribute_aspect_delayer/attribute_aspect_delayer_test.cpp
index 3ef2461c682..24c38ad118b 100644
--- a/searchcore/src/tests/proton/attribute/attribute_aspect_delayer/attribute_aspect_delayer_test.cpp
+++ b/searchcore/src/tests/proton/attribute/attribute_aspect_delayer/attribute_aspect_delayer_test.cpp
@@ -1,4 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
#include <vespa/vespalib/testkit/testapp.h>
#include <vespa/vespalib/stllike/string.h>
#include <vespa/searchcore/proton/test/attribute_utils.h>
@@ -10,6 +11,9 @@
#include <vespa/config-attributes.h>
#include <vespa/config-summarymap.h>
+#include <vespa/log/log.h>
+LOG_SETUP("attibute_aspect_delayer_test");
+
using vespa::config::search::AttributesConfig;
using vespa::config::search::AttributesConfigBuilder;
using vespa::config::search::IndexschemaConfig;
@@ -36,15 +40,24 @@ namespace proton
namespace {
-AttributesConfig::Attribute make_sv_cfg(AttributesConfig::Attribute::Datatype dataType)
+AttributesConfig::Attribute make_sv_cfg(const vespalib::string &name, AttributesConfig::Attribute::Datatype dataType)
{
AttributesConfig::Attribute attr;
- attr.name = "a";
+ attr.name = name;
attr.datatype = dataType;
attr.collectiontype = AttributesConfig::Attribute::Collectiontype::SINGLE;
return attr;
}
+AttributesConfig::Attribute make_sv_cfg(AttributesConfig::Attribute::Datatype dataType)
+{
+ return make_sv_cfg("a", dataType);
+}
+
+AttributesConfig::Attribute make_int32_sv_cfg(const vespalib::string &name) {
+ return make_sv_cfg(name, AttributesConfig::Attribute::Datatype::INT32);
+}
+
AttributesConfig::Attribute make_int32_sv_cfg() {
return make_sv_cfg(AttributesConfig::Attribute::Datatype::INT32);
}
@@ -104,6 +117,14 @@ SummarymapConfig::Override make_geopos_override(const vespalib::string &name)
return override;
}
+SummarymapConfig::Override make_attribute_combiner_override(const vespalib::string &name)
+{
+ SummarymapConfig::Override override;
+ override.field = name;
+ override.command = "attributecombiner";
+ return override;
+}
+
SummarymapConfig smCfg(std::vector<SummarymapConfig::Override> overrides)
{
SummarymapConfigBuilder result;
@@ -306,6 +327,29 @@ TEST_F("require that fast access flag change is not delayed, true->false edge, s
TEST_DO(f.assertSummarymapConfig({make_attribute_override("a")}));
}
+TEST_F("require that adding attribute aspect to struct field is not delayed if field type is changed", Fixture)
+{
+ f.setup(attrCfg({}), smCfg({}), attrCfg({make_int32_sv_cfg("array.a")}), smCfg({make_attribute_combiner_override("array")}));
+ TEST_DO(f.assertAttributeConfig({make_int32_sv_cfg("array.a")}));
+ TEST_DO(f.assertSummarymapConfig({make_attribute_combiner_override("array")}));
+}
+
+TEST_F("require that adding attribute aspect to struct field is delayed if field type is unchanged", Fixture)
+{
+ f.addFields({"array.a"});
+ f.setup(attrCfg({}), smCfg({}), attrCfg({make_int32_sv_cfg("array.a")}), smCfg({make_attribute_combiner_override("array")}));
+ TEST_DO(f.assertAttributeConfig({}));
+ TEST_DO(f.assertSummarymapConfig({}));
+}
+
+TEST_F("require that removing attribute aspect from struct field is not delayed", Fixture)
+{
+ f.addFields({"array.a"});
+ f.setup(attrCfg({make_int32_sv_cfg("array.a")}), smCfg({make_attribute_combiner_override("array")}), attrCfg({}), smCfg({}));
+ TEST_DO(f.assertAttributeConfig({}));
+ TEST_DO(f.assertSummarymapConfig({}));
+}
+
}
TEST_MAIN()
diff --git a/searchcore/src/tests/proton/attribute/attribute_initializer/attribute_initializer_test.cpp b/searchcore/src/tests/proton/attribute/attribute_initializer/attribute_initializer_test.cpp
index 7958d05d825..4957d3aead2 100644
--- a/searchcore/src/tests/proton/attribute/attribute_initializer/attribute_initializer_test.cpp
+++ b/searchcore/src/tests/proton/attribute/attribute_initializer/attribute_initializer_test.cpp
@@ -10,6 +10,9 @@
#include <vespa/searchlib/test/directory_handler.h>
#include <vespa/vespalib/stllike/string.h>
+#include <vespa/log/log.h>
+LOG_SETUP("attribute_initializer_test");
+
using search::attribute::Config;
using search::attribute::BasicType;
using search::attribute::CollectionType;
diff --git a/searchcore/src/tests/proton/attribute/attribute_manager/attribute_manager_test.cpp b/searchcore/src/tests/proton/attribute/attribute_manager/attribute_manager_test.cpp
index 9bf4d43fff7..ce09a9a3742 100644
--- a/searchcore/src/tests/proton/attribute/attribute_manager/attribute_manager_test.cpp
+++ b/searchcore/src/tests/proton/attribute/attribute_manager/attribute_manager_test.cpp
@@ -1,7 +1,4 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include <vespa/log/log.h>
-LOG_SETUP("attribute_manager_test");
-
#include <vespa/config-attributes.h>
#include <vespa/fastos/file.h>
#include <vespa/searchcommon/attribute/attributecontent.h>
@@ -42,6 +39,9 @@ LOG_SETUP("attribute_manager_test");
#include <vespa/vespalib/testkit/testapp.h>
#include <vespa/vespalib/util/threadstackexecutor.h>
+#include <vespa/log/log.h>
+LOG_SETUP("attribute_manager_test");
+
namespace vespa { namespace config { namespace search {}}}
using std::string;
@@ -84,7 +84,7 @@ class MyAttributeFunctor : public proton::IAttributeFunctor
std::vector<vespalib::string> _names;
public:
- virtual void
+ void
operator()(const search::AttributeVector &attributeVector) override {
_names.push_back(attributeVector.getName());
}
@@ -166,13 +166,12 @@ BaseFixture::BaseFixture()
_hwInfo()
{
}
-BaseFixture::~BaseFixture() {}
+BaseFixture::~BaseFixture() = default;
struct AttributeManagerFixture
{
proton::AttributeManager::SP _msp;
proton::AttributeManager &_m;
- AttributeWriter _aw;
ImportedAttributesRepoBuilder _builder;
AttributeManagerFixture(BaseFixture &bf);
~AttributeManagerFixture();
@@ -191,10 +190,9 @@ AttributeManagerFixture::AttributeManagerFixture(BaseFixture &bf)
: _msp(std::make_shared<proton::AttributeManager>(test_dir, "test.subdb", TuneFileAttributes(),
bf._fileHeaderContext, bf._attributeFieldWriter, bf._hwInfo)),
_m(*_msp),
- _aw(_msp),
_builder()
{}
-AttributeManagerFixture::~AttributeManagerFixture() {}
+AttributeManagerFixture::~AttributeManagerFixture() = default;
struct Fixture : public BaseFixture, public AttributeManagerFixture
{
@@ -318,16 +316,21 @@ TEST_F("require that attributes are flushed and loaded", BaseFixture)
fillAttribute(a1, 1, 2, 200);
EXPECT_EQUAL(4u, a1->getNumDocs());
AttributeVector::SP a2 = amf.addAttribute("a2"); // loaded
- EXPECT_EQUAL(5u, a2->getNumDocs());
- EXPECT_EQUAL(4u, a1->getNumDocs());
- amf._aw.onReplayDone(5u);
- EXPECT_EQUAL(5u, a2->getNumDocs());
- EXPECT_EQUAL(5u, a1->getNumDocs());
- fillAttribute(a2, 1, 4, 200);
- EXPECT_EQUAL(6u, a2->getNumDocs());
+ {
+ AttributeWriter aw(amf._msp);
+
+ EXPECT_EQUAL(5u, a2->getNumDocs());
+ EXPECT_EQUAL(4u, a1->getNumDocs());
+ aw.onReplayDone(5u);
+ EXPECT_EQUAL(5u, a2->getNumDocs());
+ EXPECT_EQUAL(5u, a1->getNumDocs());
+ fillAttribute(a2, 1, 4, 200);
+ EXPECT_EQUAL(6u, a2->getNumDocs());
+ }
AttributeVector::SP a3 = amf.addAttribute("a3"); // not-loaded
+ AttributeWriter aw(amf._msp);
EXPECT_EQUAL(1u, a3->getNumDocs());
- amf._aw.onReplayDone(6);
+ aw.onReplayDone(6);
EXPECT_EQUAL(6u, a3->getNumDocs());
fillAttribute(a3, 1, 7, 6, 200);
EXPECT_EQUAL(7u, a3->getNumDocs());
@@ -352,10 +355,11 @@ TEST_F("require that attributes are flushed and loaded", BaseFixture)
EXPECT_EQUAL(6u, a1->getNumDocs());
EXPECT_EQUAL(6u, a2->getNumDocs());
AttributeVector::SP a3 = amf.addAttribute("a3"); // loaded
+ AttributeWriter aw(amf._msp);
EXPECT_EQUAL(6u, a1->getNumDocs());
EXPECT_EQUAL(6u, a2->getNumDocs());
EXPECT_EQUAL(7u, a3->getNumDocs());
- amf._aw.onReplayDone(7);
+ aw.onReplayDone(7);
EXPECT_EQUAL(7u, a1->getNumDocs());
EXPECT_EQUAL(7u, a2->getNumDocs());
EXPECT_EQUAL(7u, a3->getNumDocs());
@@ -559,6 +563,7 @@ TEST_F("require that lid space can be compacted", Fixture)
AttributeVector::SP a2 = f.addAttribute("a2");
AttributeVector::SP ex(new Int32Attribute("ex"));
f._m.addExtraAttribute(ex);
+ AttributeWriter aw(f._msp);
const int64_t attrValue = 33;
fillAttribute(a1, 20, attrValue, 100);
fillAttribute(a2, 20, attrValue, 100);
@@ -571,7 +576,7 @@ TEST_F("require that lid space can be compacted", Fixture)
EXPECT_EQUAL(21u, a2->getCommittedDocIdLimit());
EXPECT_EQUAL(20u, ex->getCommittedDocIdLimit());
- f._aw.compactLidSpace(10, 101);
+ aw.compactLidSpace(10, 101);
EXPECT_EQUAL(21u, a1->getNumDocs());
EXPECT_EQUAL(21u, a2->getNumDocs());
@@ -587,6 +592,7 @@ TEST_F("require that lid space compaction op can be ignored", Fixture)
AttributeVector::SP a2 = f.addAttribute("a2");
AttributeVector::SP ex(new Int32Attribute("ex"));
f._m.addExtraAttribute(ex);
+ AttributeWriter aw(f._msp);
const int64_t attrValue = 33;
fillAttribute(a1, 20, attrValue, 200);
fillAttribute(a2, 20, attrValue, 100);
@@ -599,7 +605,7 @@ TEST_F("require that lid space compaction op can be ignored", Fixture)
EXPECT_EQUAL(21u, a2->getCommittedDocIdLimit());
EXPECT_EQUAL(20u, ex->getCommittedDocIdLimit());
- f._aw.compactLidSpace(10, 101);
+ aw.compactLidSpace(10, 101);
EXPECT_EQUAL(21u, a1->getNumDocs());
EXPECT_EQUAL(21u, a2->getNumDocs());
diff --git a/searchcore/src/tests/proton/attribute/attribute_test.cpp b/searchcore/src/tests/proton/attribute/attribute_test.cpp
index c41b6bdcb97..0e6bd2c74fe 100644
--- a/searchcore/src/tests/proton/attribute/attribute_test.cpp
+++ b/searchcore/src/tests/proton/attribute/attribute_test.cpp
@@ -445,7 +445,7 @@ TEST_F("require that attribute writer handles update", Fixture)
schema.addAttributeField(Schema::AttributeField("a2", schema::DataType::INT32, CollectionType::SINGLE));
DocBuilder idb(schema);
const document::DocumentType &dt(idb.getDocumentType());
- DocumentUpdate upd(dt, DocumentId("doc::1"));
+ DocumentUpdate upd(*idb.getDocumentTypeRepo(), dt, DocumentId("doc::1"));
upd.addUpdate(FieldUpdate(upd.getType().getField("a1"))
.addUpdate(ArithmeticValueUpdate(ArithmeticValueUpdate::Add, 5)));
upd.addUpdate(FieldUpdate(upd.getType().getField("a2"))
@@ -489,7 +489,7 @@ TEST_F("require that attribute writer handles predicate update", Fixture)
EXPECT_EQUAL(2u, a1->getNumDocs());
const document::DocumentType &dt(idb.getDocumentType());
- DocumentUpdate upd(dt, DocumentId("doc::1"));
+ DocumentUpdate upd(*idb.getDocumentTypeRepo(), dt, DocumentId("doc::1"));
PredicateFieldValue new_value(builder.feature("foo").value("bar").build());
upd.addUpdate(FieldUpdate(upd.getType().getField("a1"))
.addUpdate(AssignValueUpdate(new_value)));
@@ -678,7 +678,7 @@ TEST_F("require that attribute writer handles tensor assign update", Fixture)
EXPECT_TRUE(tensor->equals(*tensor2));
const document::DocumentType &dt(builder.getDocumentType());
- DocumentUpdate upd(dt, DocumentId("doc::1"));
+ DocumentUpdate upd(*builder.getDocumentTypeRepo(), dt, DocumentId("doc::1"));
auto new_tensor = createTensor({ {{{"x", "8"}, {"y", "9"}}, 11} },
{"x", "y"});
TensorFieldValue new_value;
diff --git a/searchcore/src/tests/proton/attribute/attributeflush_test.cpp b/searchcore/src/tests/proton/attribute/attributeflush_test.cpp
index d73cd805af1..8a5f58bd7e0 100644
--- a/searchcore/src/tests/proton/attribute/attributeflush_test.cpp
+++ b/searchcore/src/tests/proton/attribute/attributeflush_test.cpp
@@ -256,13 +256,12 @@ BaseFixture::BaseFixture(const HwInfo &hwInfo)
_attributeFieldWriter(),
_hwInfo(hwInfo)
{}
-BaseFixture::~BaseFixture() {}
+BaseFixture::~BaseFixture() = default;
struct AttributeManagerFixture
{
AttributeManager::SP _msp;
AttributeManager &_m;
- AttributeWriter _aw;
AttributeManagerFixture(BaseFixture &bf);
~AttributeManagerFixture();
AttributeVector::SP addAttribute(const vespalib::string &name) {
@@ -278,10 +277,9 @@ struct AttributeManagerFixture
AttributeManagerFixture::AttributeManagerFixture(BaseFixture &bf)
: _msp(std::make_shared<AttributeManager>(test_dir, "test.subdb", TuneFileAttributes(),
bf._fileHeaderContext, bf._attributeFieldWriter, bf._hwInfo)),
- _m(*_msp),
- _aw(_msp)
+ _m(*_msp)
{}
-AttributeManagerFixture::~AttributeManagerFixture() {}
+AttributeManagerFixture::~AttributeManagerFixture() = default;
struct Fixture : public BaseFixture, public AttributeManagerFixture
{
@@ -530,6 +528,7 @@ Test::requireThatShrinkWorks()
Fixture f;
AttributeManager &am = f._m;
AttributeVector::SP av = f.addAttribute("a10");
+ AttributeWriter aw(f._msp);
av->addDocs(1000 - av->getNumDocs());
av->commit(50, 50);
@@ -546,13 +545,13 @@ Test::requireThatShrinkWorks()
EXPECT_FALSE(av->canShrinkLidSpace());
EXPECT_EQUAL(1000u, av->getNumDocs());
EXPECT_EQUAL(100u, av->getCommittedDocIdLimit());
- f._aw.heartBeat(51);
+ aw.heartBeat(51);
EXPECT_TRUE(av->wantShrinkLidSpace());
EXPECT_FALSE(av->canShrinkLidSpace());
EXPECT_EQUAL(ft->getApproxMemoryGain().getBefore(),
ft->getApproxMemoryGain().getAfter());
g.reset();
- f._aw.heartBeat(52);
+ aw.heartBeat(52);
EXPECT_TRUE(av->wantShrinkLidSpace());
EXPECT_TRUE(av->canShrinkLidSpace());
EXPECT_TRUE(ft->getApproxMemoryGain().getBefore() >
diff --git a/searchcore/src/tests/proton/attribute/exclusive_attribute_read_accessor/exclusive_attribute_read_accessor_test.cpp b/searchcore/src/tests/proton/attribute/exclusive_attribute_read_accessor/exclusive_attribute_read_accessor_test.cpp
index 6d69d9b225b..c5ae0f97875 100644
--- a/searchcore/src/tests/proton/attribute/exclusive_attribute_read_accessor/exclusive_attribute_read_accessor_test.cpp
+++ b/searchcore/src/tests/proton/attribute/exclusive_attribute_read_accessor/exclusive_attribute_read_accessor_test.cpp
@@ -37,7 +37,7 @@ TEST_F("require that attribute write thread is blocked while guard is held", Fix
{
ReadGuard::UP guard = f.accessor.takeGuard();
Gate gate;
- f.writer.execute("myattr", [&gate]() { gate.countDown(); });
+ f.writer.execute(f.writer.getExecutorId(f.attribute->getNamePrefix()), [&gate]() { gate.countDown(); });
bool reachedZero = gate.await(100);
EXPECT_FALSE(reachedZero);
EXPECT_EQUAL(1u, gate.getCount());
diff --git a/searchcore/src/tests/proton/common/document_type_inspector/document_type_inspector_test.cpp b/searchcore/src/tests/proton/common/document_type_inspector/document_type_inspector_test.cpp
index 2dbff7d40dd..167865b5c68 100644
--- a/searchcore/src/tests/proton/common/document_type_inspector/document_type_inspector_test.cpp
+++ b/searchcore/src/tests/proton/common/document_type_inspector/document_type_inspector_test.cpp
@@ -1,78 +1,131 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/searchcore/proton/common/document_type_inspector.h>
#include <vespa/vespalib/testkit/testapp.h>
-#include <vespa/document/repo/configbuilder.h>
-#include <vespa/document/repo/documenttyperepo.h>
+#include <vespa/document/base/field.h>
+#include <vespa/document/datatype/datatypes.h>
using namespace document;
using namespace proton;
-using document::config_builder::DocumenttypesConfigBuilderHelper;
-using document::config_builder::Struct;
-const int32_t doc_type_id = 787121340;
-const vespalib::string type_name = "test";
-const vespalib::string header_name = type_name + ".header";
-const vespalib::string body_name = type_name + ".body";
-
-std::unique_ptr<const DocumentTypeRepo>
-makeOldDocTypeRepo()
+template <class Type>
+void
+addFields(Type &type, bool fieldF3IsString, bool hasFieldF4, bool hasFieldF5)
{
- DocumenttypesConfigBuilderHelper builder;
- builder.document(doc_type_id, type_name,
- Struct(header_name), Struct(body_name).
- addField("f1", DataType::T_STRING).
- addField("f2", DataType::T_STRING).
- addField("f3", DataType::T_STRING).
- addField("f4", DataType::T_STRING));
- return std::unique_ptr<const DocumentTypeRepo>(new DocumentTypeRepo(builder.config()));
+ type.addField(Field("f1", 1, *DataType::STRING, true));
+ type.addField(Field("f2", 2, *DataType::STRING, true));
+ type.addField(Field("f3", 3, fieldF3IsString ? *DataType::STRING : *DataType::INT, true));
+ if (hasFieldF4) {
+ type.addField(Field("f4", 4, *DataType::STRING, true));
+ }
+ if (hasFieldF5) {
+ type.addField(Field("f5", 5, *DataType::STRING, true));
+ }
}
-std::unique_ptr<const DocumentTypeRepo>
-makeNewDocTypeRepo()
+struct DocumentTypeFixture
+{
+ DocumentType _documentType;
+ StructDataType _structFieldType;
+ ArrayDataType _structArrayFieldType;
+ MapDataType _structMapFieldType;
+ MapDataType _mapFieldType;
+
+ DocumentTypeFixture(bool fieldF3IsString, bool hasFieldF4, bool hasFieldF5, bool hasStruct, bool mapKeyIsByte);
+ ~DocumentTypeFixture();
+};
+
+DocumentTypeFixture::DocumentTypeFixture(bool fieldF3IsString, bool hasFieldF4, bool hasFieldF5, bool hasStruct, bool mapKeyIsByte)
+ : _documentType("test"),
+ _structFieldType("struct"),
+ _structArrayFieldType(_structFieldType),
+ _structMapFieldType(mapKeyIsByte ? *DataType::BYTE : *DataType::STRING, _structFieldType),
+ _mapFieldType(mapKeyIsByte ? *DataType::BYTE : *DataType::STRING, *DataType::STRING)
{
- DocumenttypesConfigBuilderHelper builder;
- builder.document(doc_type_id, type_name,
- Struct(header_name), Struct(body_name).
- addField("f1", DataType::T_STRING).
- addField("f2", DataType::T_STRING).
- addField("f3", DataType::T_INT).
- addField("f5", DataType::T_STRING));
- return std::unique_ptr<const DocumentTypeRepo>(new DocumentTypeRepo(builder.config()));
+ addFields(_documentType, fieldF3IsString, hasFieldF4, hasFieldF5);
+ if (hasStruct) {
+ addFields(_structFieldType, fieldF3IsString, hasFieldF4, hasFieldF5);
+ _documentType.addField(Field("sarray", 11, _structArrayFieldType, true));
+ _documentType.addField(Field("smap", 12, _structMapFieldType, true));
+ _documentType.addField(Field("map", 13, _mapFieldType, true));
+ }
}
+DocumentTypeFixture::~DocumentTypeFixture() = default;
+
struct Fixture
{
- std::unique_ptr<const DocumentTypeRepo> _oldRepo;
- std::unique_ptr<const DocumentTypeRepo> _newRepo;
+ DocumentTypeFixture _oldDocType;
+ DocumentTypeFixture _newDocType;
DocumentTypeInspector _inspector;
- Fixture()
- : _oldRepo(makeOldDocTypeRepo()),
- _newRepo(makeNewDocTypeRepo()),
- _inspector(*_oldRepo->getDocumentType("test"), *_newRepo->getDocumentType("test"))
+ Fixture(bool hasStruct = true, bool mapKeyIsByte = false)
+ : _oldDocType(true, true, false, hasStruct, mapKeyIsByte),
+ _newDocType(false, false, true, true, false),
+ _inspector(_oldDocType._documentType, _newDocType._documentType)
{
}
};
TEST_F("require that unchanged fields are known", Fixture)
{
- EXPECT_TRUE(f._inspector.hasUnchangedField("f1"));
- EXPECT_TRUE(f._inspector.hasUnchangedField("f2"));
+ const IDocumentTypeInspector &inspector = f._inspector;
+ EXPECT_TRUE(inspector.hasUnchangedField("f1"));
+ EXPECT_TRUE(inspector.hasUnchangedField("f2"));
+ EXPECT_TRUE(inspector.hasUnchangedField("sarray.f1"));
+ EXPECT_TRUE(inspector.hasUnchangedField("sarray.f2"));
+ EXPECT_TRUE(inspector.hasUnchangedField("smap.key"));
+ EXPECT_TRUE(inspector.hasUnchangedField("smap.value.f1"));
+ EXPECT_TRUE(inspector.hasUnchangedField("smap.value.f2"));
+ EXPECT_TRUE(inspector.hasUnchangedField("map.key"));
+ EXPECT_TRUE(inspector.hasUnchangedField("map.value"));
}
TEST_F("require that changed fields are detected", Fixture)
{
- EXPECT_FALSE(f._inspector.hasUnchangedField("f3"));
+ const IDocumentTypeInspector &inspector = f._inspector;
+ EXPECT_FALSE(inspector.hasUnchangedField("f3"));
+ EXPECT_FALSE(inspector.hasUnchangedField("sarray.f3"));
+ EXPECT_FALSE(inspector.hasUnchangedField("smap.value.f3"));
}
TEST_F("require that partially missing fields are detected", Fixture)
{
- EXPECT_FALSE(f._inspector.hasUnchangedField("f4"));
- EXPECT_FALSE(f._inspector.hasUnchangedField("f5"));
+ const IDocumentTypeInspector &inspector = f._inspector;
+ EXPECT_FALSE(inspector.hasUnchangedField("f4"));
+ EXPECT_FALSE(inspector.hasUnchangedField("f5"));
+ EXPECT_FALSE(inspector.hasUnchangedField("sarray.f4"));
+ EXPECT_FALSE(inspector.hasUnchangedField("sarray.f5"));
+ EXPECT_FALSE(inspector.hasUnchangedField("smap.value.f4"));
+ EXPECT_FALSE(inspector.hasUnchangedField("smap.value.f5"));
}
TEST_F("require that non-existing fields are NOT known", Fixture)
{
- EXPECT_FALSE(f._inspector.hasUnchangedField("not"));
+ const IDocumentTypeInspector &inspector = f._inspector;
+ EXPECT_FALSE(inspector.hasUnchangedField("not"));
+ EXPECT_FALSE(inspector.hasUnchangedField("sarray.not"));
+ EXPECT_FALSE(inspector.hasUnchangedField("smap.not"));
+}
+
+TEST_F("require that map key type change is detected", Fixture(true, true))
+{
+ const IDocumentTypeInspector &inspector = f._inspector;
+ EXPECT_FALSE(inspector.hasUnchangedField("smap.key"));
+ EXPECT_FALSE(inspector.hasUnchangedField("smap.value.f1"));
+ EXPECT_FALSE(inspector.hasUnchangedField("smap.value.f2"));
+ EXPECT_FALSE(inspector.hasUnchangedField("map.key"));
+ EXPECT_FALSE(inspector.hasUnchangedField("map.value"));
+}
+
+TEST_F("require that struct addition is detected", Fixture(false, false))
+{
+ const IDocumentTypeInspector &inspector = f._inspector;
+ EXPECT_FALSE(inspector.hasUnchangedField("sarray.f1"));
+ EXPECT_FALSE(inspector.hasUnchangedField("sarray.f2"));
+ EXPECT_FALSE(inspector.hasUnchangedField("smap.key"));
+ EXPECT_FALSE(inspector.hasUnchangedField("smap.value.f1"));
+ EXPECT_FALSE(inspector.hasUnchangedField("smap.value.f2"));
+ EXPECT_FALSE(inspector.hasUnchangedField("map.key"));
+ EXPECT_FALSE(inspector.hasUnchangedField("map.value"));
}
TEST_MAIN()
diff --git a/searchcore/src/tests/proton/docsummary/docsummary.cpp b/searchcore/src/tests/proton/docsummary/docsummary.cpp
index 7eae1c9d12d..db6116073e6 100644
--- a/searchcore/src/tests/proton/docsummary/docsummary.cpp
+++ b/searchcore/src/tests/proton/docsummary/docsummary.cpp
@@ -213,7 +213,9 @@ public:
_tuneFileDocumentDB, _hwInfo);
_configMgr.forwardConfig(b);
_configMgr.nextGeneration(0);
- if (! FastOS_File::MakeDirectory((std::string("tmpdb/") + docTypeName).c_str())) { abort(); }
+ if (! FastOS_File::MakeDirectory((std::string("tmpdb/") + docTypeName).c_str())) {
+ LOG_ABORT("should not be reached");
+ }
_ddb.reset(new DocumentDB("tmpdb", _configMgr.getConfig(), "tcp/localhost:9013", _queryLimiter, _clock,
DocTypeName(docTypeName), makeBucketSpace(),
*b->getProtonConfigSP(), *this, _summaryExecutor, _summaryExecutor,
@@ -829,20 +831,16 @@ Test::requireThatAttributesAreUsed()
"bi:[]}", *rep, 1, false));
TEST_DO(assertTensor(Tensor::UP(), "bj", *rep, 1, rclass));
- proton::IAttributeManager::SP attributeManager =
- dc._ddb->getReadySubDB()->getAttributeManager();
- search::ISequencedTaskExecutor &attributeFieldWriter =
- attributeManager->getAttributeFieldWriter();
- search::AttributeVector *bjAttr =
- attributeManager->getWritableAttribute("bj");
- search::tensor::TensorAttribute *bjTensorAttr =
- dynamic_cast<search::tensor::TensorAttribute *>(bjAttr);
-
- attributeFieldWriter.
- execute("bj",
- [&]() { bjTensorAttr->setTensor(3,
- *createTensor({ {{{"x", "a"},{"y", "b"}}, 4} }, { "x"}));
- bjTensorAttr->commit(); });
+ proton::IAttributeManager::SP attributeManager = dc._ddb->getReadySubDB()->getAttributeManager();
+ search::ISequencedTaskExecutor &attributeFieldWriter = attributeManager->getAttributeFieldWriter();
+ search::AttributeVector *bjAttr = attributeManager->getWritableAttribute("bj");
+ auto bjTensorAttr = dynamic_cast<search::tensor::TensorAttribute *>(bjAttr);
+
+ attributeFieldWriter.execute(attributeFieldWriter.getExecutorId(bjAttr->getNamePrefix()),
+ [&]() {
+ bjTensorAttr->setTensor(3, *createTensor({ {{{"x", "a"},{"y", "b"}}, 4} }, { "x"}));
+ bjTensorAttr->commit();
+ });
attributeFieldWriter.sync();
DocsumReply::UP rep2 = dc._ddb->getDocsums(req);
@@ -961,8 +959,7 @@ Test::requireThatUrisAreUsed()
Document::UP exp = bc._bld.startDocument("doc::0").
startIndexField("urisingle").
startSubField("all").
- addUrlTokenizedString(
- "http://www.example.com:81/fluke?ab=2#4").
+ addUrlTokenizedString("http://www.example.com:81/fluke?ab=2#4").
endSubField().
startSubField("scheme").
addUrlTokenizedString("http").
@@ -986,8 +983,7 @@ Test::requireThatUrisAreUsed()
startIndexField("uriarray").
startElement(1).
startSubField("all").
- addUrlTokenizedString(
- "http://www.example.com:82/fluke?ab=2#8").
+ addUrlTokenizedString("http://www.example.com:82/fluke?ab=2#8").
endSubField().
startSubField("scheme").
addUrlTokenizedString("http").
@@ -1010,8 +1006,7 @@ Test::requireThatUrisAreUsed()
endElement().
startElement(1).
startSubField("all").
- addUrlTokenizedString(
- "http://www.flickr.com:82/fluke?ab=2#9").
+ addUrlTokenizedString("http://www.flickr.com:82/fluke?ab=2#9").
endSubField().
startSubField("scheme").
addUrlTokenizedString("http").
@@ -1036,8 +1031,7 @@ Test::requireThatUrisAreUsed()
startIndexField("uriwset").
startElement(4).
startSubField("all").
- addUrlTokenizedString(
- "http://www.example.com:83/fluke?ab=2#12").
+ addUrlTokenizedString("http://www.example.com:83/fluke?ab=2#12").
endSubField().
startSubField("scheme").
addUrlTokenizedString("http").
@@ -1060,8 +1054,7 @@ Test::requireThatUrisAreUsed()
endElement().
startElement(7).
startSubField("all").
- addUrlTokenizedString(
- "http://www.flickr.com:85/fluke?ab=2#13").
+ addUrlTokenizedString("http://www.flickr.com:85/fluke?ab=2#13").
endSubField().
startSubField("scheme").
addUrlTokenizedString("http").
diff --git a/searchcore/src/tests/proton/docsummary/summaryfieldconverter_test.cpp b/searchcore/src/tests/proton/docsummary/summaryfieldconverter_test.cpp
index 7f8ce99beb9..73d071be96f 100644
--- a/searchcore/src/tests/proton/docsummary/summaryfieldconverter_test.cpp
+++ b/searchcore/src/tests/proton/docsummary/summaryfieldconverter_test.cpp
@@ -33,7 +33,7 @@
#include <vespa/document/fieldvalue/referencefieldvalue.h>
#include <vespa/document/predicate/predicate.h>
#include <vespa/document/repo/configbuilder.h>
-#include <vespa/document/repo/documenttyperepo.h>
+#include <vespa/document/repo/fixedtyperepo.h>
#include <vespa/searchsummary/docsummary/summaryfieldconverter.h>
#include <vespa/searchsummary/docsummary/linguisticsannotation.h>
#include <vespa/searchsummary/docsummary/searchdatatype.h>
diff --git a/searchcore/src/tests/proton/document_iterator/document_iterator_test.cpp b/searchcore/src/tests/proton/document_iterator/document_iterator_test.cpp
index a7512b41f30..3add3b727b9 100644
--- a/searchcore/src/tests/proton/document_iterator/document_iterator_test.cpp
+++ b/searchcore/src/tests/proton/document_iterator/document_iterator_test.cpp
@@ -17,6 +17,9 @@
#include <vespa/vespalib/objects/nbostream.h>
#include <vespa/vespalib/testkit/test_kit.h>
+#include <vespa/log/log.h>
+LOG_SETUP("document_iterator_test");
+
using document::BucketId;
using document::DataType;
using document::Document;
diff --git a/searchcore/src/tests/proton/documentdb/clusterstatehandler/clusterstatehandler_test.cpp b/searchcore/src/tests/proton/documentdb/clusterstatehandler/clusterstatehandler_test.cpp
index b5cf1733ba9..dc4e88ae005 100644
--- a/searchcore/src/tests/proton/documentdb/clusterstatehandler/clusterstatehandler_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/clusterstatehandler/clusterstatehandler_test.cpp
@@ -6,6 +6,9 @@
#include <vespa/vdslib/distribution/distribution.h>
#include <vespa/vdslib/state/clusterstate.h>
+#include <vespa/log/log.h>
+LOG_SETUP("cluster_state_handler_test");
+
using namespace proton;
using document::BucketId;
using storage::lib::Distribution;
diff --git a/searchcore/src/tests/proton/documentdb/combiningfeedview/combiningfeedview_test.cpp b/searchcore/src/tests/proton/documentdb/combiningfeedview/combiningfeedview_test.cpp
index f06bb124eb8..958be6a4686 100644
--- a/searchcore/src/tests/proton/documentdb/combiningfeedview/combiningfeedview_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/combiningfeedview/combiningfeedview_test.cpp
@@ -1,15 +1,16 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include <vespa/log/log.h>
-LOG_SETUP("combiningfeedview_test");
#include <vespa/document/test/make_bucket_space.h>
-#include <vespa/searchcore/proton/feedoperation/moveoperation.h>
+#include <vespa/searchcore/proton/feedoperation/operations.h>
#include <vespa/searchcore/proton/server/combiningfeedview.h>
#include <vespa/searchcore/proton/test/test.h>
#include <vespa/searchlib/common/idestructorcallback.h>
#include <vespa/document/update/documentupdate.h>
#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/log/log.h>
+LOG_SETUP("combiningfeedview_test");
+
using document::DocumentTypeRepo;
using document::DocumentUpdate;
using document::test::makeBucketSpace;
@@ -22,8 +23,8 @@ typedef std::vector<IFeedView::SP> FeedViewVector;
struct MyStreamHandler : public NewConfigOperation::IStreamHandler
{
- virtual void serializeConfig(SerialNum, vespalib::nbostream &) override {}
- virtual void deserializeConfig(SerialNum, vespalib::nbostream &) override {}
+ void serializeConfig(SerialNum, vespalib::nbostream &) override {}
+ void deserializeConfig(SerialNum, vespalib::nbostream &) override {}
};
diff --git a/searchcore/src/tests/proton/documentdb/document_subdbs/document_subdbs_test.cpp b/searchcore/src/tests/proton/documentdb/document_subdbs/document_subdbs_test.cpp
index bab6aff821f..760f7457d33 100644
--- a/searchcore/src/tests/proton/documentdb/document_subdbs/document_subdbs_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/document_subdbs/document_subdbs_test.cpp
@@ -14,6 +14,7 @@
#include <vespa/searchcore/proton/reference/i_document_db_reference_resolver.h>
#include <vespa/searchcore/proton/reprocessing/i_reprocessing_task.h>
#include <vespa/searchcore/proton/reprocessing/reprocessingrunner.h>
+#include <vespa/searchcore/proton/feedoperation/operations.h>
#include <vespa/searchcore/proton/server/bootstrapconfig.h>
#include <vespa/searchcore/proton/server/document_subdb_explorer.h>
#include <vespa/searchcore/proton/server/emptysearchview.h>
diff --git a/searchcore/src/tests/proton/documentdb/documentbucketmover/documentbucketmover_test.cpp b/searchcore/src/tests/proton/documentdb/documentbucketmover/documentbucketmover_test.cpp
index 11e9aa8c3ec..af6a9d38385 100644
--- a/searchcore/src/tests/proton/documentdb/documentbucketmover/documentbucketmover_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/documentbucketmover/documentbucketmover_test.cpp
@@ -18,6 +18,9 @@
#include <vespa/document/test/make_bucket_space.h>
#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/log/log.h>
+LOG_SETUP("document_bucket_mover_test");
+
using namespace proton;
using document::BucketId;
using document::Document;
diff --git a/searchcore/src/tests/proton/documentdb/feedhandler/feedhandler_test.cpp b/searchcore/src/tests/proton/documentdb/feedhandler/feedhandler_test.cpp
index e774728b41e..a1bce7174bf 100644
--- a/searchcore/src/tests/proton/documentdb/feedhandler/feedhandler_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/feedhandler/feedhandler_test.cpp
@@ -316,7 +316,7 @@ struct UpdateContext {
DocumentUpdate::SP update;
BucketId bucketId;
UpdateContext(const vespalib::string &docId, DocBuilder &builder) :
- update(new DocumentUpdate(builder.getDocumentType(), DocumentId(docId))),
+ update(new DocumentUpdate(*builder.getDocumentTypeRepo(), builder.getDocumentType(), DocumentId(docId))),
bucketId(BucketFactory::getBucketId(update->getId()))
{
}
diff --git a/searchcore/src/tests/proton/documentdb/feedview/feedview_test.cpp b/searchcore/src/tests/proton/documentdb/feedview/feedview_test.cpp
index a9fa79f513c..d21e5abe30b 100644
--- a/searchcore/src/tests/proton/documentdb/feedview/feedview_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/feedview/feedview_test.cpp
@@ -11,6 +11,7 @@
#include <vespa/searchcore/proton/server/isummaryadapter.h>
#include <vespa/searchcore/proton/server/matchview.h>
#include <vespa/searchcore/proton/server/searchable_feed_view.h>
+#include <vespa/searchcore/proton/feedoperation/operations.h>
#include <vespa/searchcore/proton/test/document_meta_store_context_observer.h>
#include <vespa/searchcore/proton/test/dummy_document_store.h>
#include <vespa/searchcore/proton/test/dummy_summary_manager.h>
@@ -478,11 +479,11 @@ struct DocumentContext
DocumentContext::DocumentContext(const vespalib::string &docId, uint64_t timestamp, DocBuilder &builder)
: doc(builder.startDocument(docId).startSummaryField("s1").addStr(docId).endField().endDocument().release()),
- upd(new DocumentUpdate(builder.getDocumentType(), doc->getId())),
+ upd(new DocumentUpdate(*builder.getDocumentTypeRepo(), builder.getDocumentType(), doc->getId())),
bid(BucketFactory::getNumBucketBits(), doc->getId().getGlobalId().convertToBucketId().getRawId()),
ts(timestamp)
{}
-DocumentContext::~DocumentContext() {}
+DocumentContext::~DocumentContext() = default;
struct FeedTokenContext
{
diff --git a/searchcore/src/tests/proton/documentdb/maintenancecontroller/maintenancecontroller_test.cpp b/searchcore/src/tests/proton/documentdb/maintenancecontroller/maintenancecontroller_test.cpp
index c7790f9c507..4a3ced6891c 100644
--- a/searchcore/src/tests/proton/documentdb/maintenancecontroller/maintenancecontroller_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/maintenancecontroller/maintenancecontroller_test.cpp
@@ -153,14 +153,14 @@ struct MyDocumentRetriever : public DocumentRetrieverBaseForTest
virtual const document::DocumentTypeRepo &
getDocumentTypeRepo() const override
{
- abort();
+ LOG_ABORT("should not be reached");
}
virtual void
getBucketMetaData(const storage::spi::Bucket &,
DocumentMetaData::Vector &) const override
{
- abort();
+ LOG_ABORT("should not be reached");
}
virtual DocumentMetaData
getDocumentMetaData(const DocumentId &) const override
diff --git a/searchcore/src/tests/proton/documentdb/storeonlyfeedview/storeonlyfeedview_test.cpp b/searchcore/src/tests/proton/documentdb/storeonlyfeedview/storeonlyfeedview_test.cpp
index e8247a52199..d07c29ead69 100644
--- a/searchcore/src/tests/proton/documentdb/storeonlyfeedview/storeonlyfeedview_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/storeonlyfeedview/storeonlyfeedview_test.cpp
@@ -9,6 +9,8 @@
#include <vespa/searchcore/proton/server/putdonecontext.h>
#include <vespa/searchcore/proton/server/removedonecontext.h>
#include <vespa/searchcore/proton/server/storeonlyfeedview.h>
+#include <vespa/searchcore/proton/feedoperation/moveoperation.h>
+#include <vespa/searchcore/proton/feedoperation/pruneremoveddocumentsoperation.h>
#include <vespa/searchcore/proton/reference/dummy_gid_to_lid_change_handler.h>
#include <vespa/searchcore/proton/test/mock_summary_adapter.h>
#include <vespa/searchcore/proton/test/thread_utils.h>
diff --git a/searchcore/src/tests/proton/documentmetastore/lidreusedelayer/lidreusedelayer_test.cpp b/searchcore/src/tests/proton/documentmetastore/lidreusedelayer/lidreusedelayer_test.cpp
index d56340be2b2..df411ea0346 100644
--- a/searchcore/src/tests/proton/documentmetastore/lidreusedelayer/lidreusedelayer_test.cpp
+++ b/searchcore/src/tests/proton/documentmetastore/lidreusedelayer/lidreusedelayer_test.cpp
@@ -113,7 +113,7 @@ public:
virtual const RawDocumentMetaData &getRawMetaData(DocId) const override
{
- abort();
+ LOG_ABORT("should not be reached");
}
virtual bool getFreeListActive() const override
diff --git a/searchcore/src/tests/proton/feedoperation/feedoperation_test.cpp b/searchcore/src/tests/proton/feedoperation/feedoperation_test.cpp
index 681d3543ee1..e87e9209a17 100644
--- a/searchcore/src/tests/proton/feedoperation/feedoperation_test.cpp
+++ b/searchcore/src/tests/proton/feedoperation/feedoperation_test.cpp
@@ -100,14 +100,12 @@ makeDocTypeRepo()
{
DocumenttypesConfigBuilderHelper builder;
builder.document(doc_type_id, type_name,
- Struct(header_name), Struct(body_name).
- addField("string", DataType::T_STRING).
- addField("struct", Struct("pair").
- addField("x", DataType::T_STRING).
- addField("y", DataType::T_STRING)).
- addField("map", Map(DataType::T_STRING,
- DataType::T_STRING)));
- return std::unique_ptr<const DocumentTypeRepo>(new DocumentTypeRepo(builder.config()));
+ Struct(header_name),
+ Struct(body_name)
+ .addField("string", DataType::T_STRING)
+ .addField("struct", Struct("pair").addField("x", DataType::T_STRING).addField("y", DataType::T_STRING))
+ .addField("map", Map(DataType::T_STRING, DataType::T_STRING)));
+ return std::make_unique<const DocumentTypeRepo>(builder.config());
}
@@ -124,7 +122,7 @@ public:
}
auto makeUpdate() {
- auto upd(std::make_shared<DocumentUpdate>(_docType, docId));
+ auto upd(std::make_shared<DocumentUpdate>(*_repo, _docType, docId));
upd->addUpdate(FieldUpdate(upd->getType().getField("string")).
addUpdate(AssignValueUpdate(StringFieldValue("newval"))));
return upd;
@@ -138,6 +136,7 @@ public:
TEST("require that toString() on derived classes are meaningful")
{
+ DocumentTypeRepo repo;
BucketId bucket_id1(42);
BucketId bucket_id2(43);
BucketId bucket_id3(44);
@@ -148,7 +147,7 @@ TEST("require that toString() on derived classes are meaningful")
MyStreamHandler stream_handler;
DocumentIdT doc_id_limit = 15;
DocumentId doc_id("doc:foo:bar");
- DocumentUpdate::SP update(new DocumentUpdate(*DataType::DOCUMENT, doc_id));
+ DocumentUpdate::SP update(new DocumentUpdate(repo, *DataType::DOCUMENT, doc_id));
EXPECT_EQUAL("DeleteBucket(BucketId(0x0000000000000000), serialNum=0)",
DeleteBucketOperation().toString());
diff --git a/searchcore/src/tests/proton/matching/matching_stats_test.cpp b/searchcore/src/tests/proton/matching/matching_stats_test.cpp
index 8b58a9e271c..85960de2021 100644
--- a/searchcore/src/tests/proton/matching/matching_stats_test.cpp
+++ b/searchcore/src/tests/proton/matching/matching_stats_test.cpp
@@ -240,6 +240,65 @@ TEST("requireThatPartitionsAreAddedCorrectly") {
EXPECT_EQUAL(1.0, all1.getPartition(1).wait_time_max());
}
+TEST("requireThatSoftDoomIsSetAndAdded") {
+ MatchingStats stats;
+ MatchingStats stats2;
+ EXPECT_EQUAL(0ul, stats.softDoomed());
+ EXPECT_EQUAL(0.5, stats.softDoomFactor());
+ stats.softDoomFactor(0.7);
+ stats.softDoomed(3);
+ EXPECT_EQUAL(3ul, stats.softDoomed());
+ EXPECT_EQUAL(0.7, stats.softDoomFactor());
+ stats2.add(stats);
+ EXPECT_EQUAL(3ul, stats2.softDoomed());
+ EXPECT_EQUAL(0.5, stats2.softDoomFactor()); // Not affected by add
+}
+
+TEST("requireThatSoftDoomFacorIsComputedCorrectlyForDownAdjustment") {
+ MatchingStats stats;
+ EXPECT_EQUAL(0ul, stats.softDoomed());
+ EXPECT_EQUAL(0.5, stats.softDoomFactor());
+ stats.softDoomed(1);
+ stats.updatesoftDoomFactor(1.0, 0.5, 2.0);
+ EXPECT_EQUAL(1ul, stats.softDoomed());
+ EXPECT_EQUAL(0.47, stats.softDoomFactor());
+ stats.updatesoftDoomFactor(1.0, 0.5, 2.0);
+ EXPECT_EQUAL(1ul, stats.softDoomed());
+ EXPECT_EQUAL(0.44, stats.softDoomFactor());
+ stats.updatesoftDoomFactor(0.0009, 0.5, 2.0); // hard limits less than 1ms should be ignored
+ EXPECT_EQUAL(1ul, stats.softDoomed());
+ EXPECT_EQUAL(0.44, stats.softDoomFactor());
+ stats.updatesoftDoomFactor(1.0, 0.0009, 2.0); // soft limits less than 1ms should be ignored
+ EXPECT_EQUAL(1ul, stats.softDoomed());
+ EXPECT_EQUAL(0.44, stats.softDoomFactor());
+ stats.updatesoftDoomFactor(1.0, 0.5, 10.0); // Prevent changes above 10%
+ EXPECT_EQUAL(1ul, stats.softDoomed());
+ EXPECT_EQUAL(0.396, stats.softDoomFactor());
+}
+
+TEST("requireThatSoftDoomFacorIsComputedCorrectlyForUpAdjustment") {
+ MatchingStats stats;
+ EXPECT_EQUAL(0ul, stats.softDoomed());
+ EXPECT_EQUAL(0.5, stats.softDoomFactor());
+ stats.softDoomed(1);
+ stats.updatesoftDoomFactor(1.0, 0.9, 0.1);
+ EXPECT_EQUAL(1ul, stats.softDoomed());
+ EXPECT_EQUAL(0.508, stats.softDoomFactor());
+ stats.updatesoftDoomFactor(1.0, 0.9, 0.1);
+ EXPECT_EQUAL(1ul, stats.softDoomed());
+ EXPECT_EQUAL(0.516, stats.softDoomFactor());
+ stats.updatesoftDoomFactor(0.0009, 0.9, 0.1); // hard limits less than 1ms should be ignored
+ EXPECT_EQUAL(1ul, stats.softDoomed());
+ EXPECT_EQUAL(0.516, stats.softDoomFactor());
+ stats.updatesoftDoomFactor(1.0, 0.0009, 0.1); // soft limits less than 1ms should be ignored
+ EXPECT_EQUAL(1ul, stats.softDoomed());
+ EXPECT_EQUAL(0.516, stats.softDoomFactor());
+ stats.softDoomFactor(0.1);
+ stats.updatesoftDoomFactor(1.0, 0.9, 0.001); // Prevent changes above 5%
+ EXPECT_EQUAL(1ul, stats.softDoomed());
+ EXPECT_EQUAL(0.105, stats.softDoomFactor());
+}
+
TEST_MAIN() {
TEST_RUN_ALL();
}
diff --git a/searchcore/src/tests/proton/matching/matching_test.cpp b/searchcore/src/tests/proton/matching/matching_test.cpp
index 76e3b3e4af9..0d474fc57cf 100644
--- a/searchcore/src/tests/proton/matching/matching_test.cpp
+++ b/searchcore/src/tests/proton/matching/matching_test.cpp
@@ -1,7 +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/testkit/testapp.h>
-
#include <vespa/document/base/globalid.h>
#include <initializer_list>
#include <vespa/searchcommon/attribute/iattributecontext.h>
@@ -33,6 +32,7 @@
#include <vespa/searchcore/proton/matching/match_params.h>
#include <vespa/searchcore/proton/matching/match_tools.h>
#include <vespa/searchcore/proton/matching/match_context.h>
+
#include <vespa/log/log.h>
LOG_SETUP("matching_test");
diff --git a/searchcore/src/tests/proton/matching/query_test.cpp b/searchcore/src/tests/proton/matching/query_test.cpp
index 61823a17f09..82aab72068d 100644
--- a/searchcore/src/tests/proton/matching/query_test.cpp
+++ b/searchcore/src/tests/proton/matching/query_test.cpp
@@ -63,6 +63,12 @@ using search::queryeval::SearchIterator;
using search::queryeval::SimpleBlueprint;
using search::queryeval::SimpleResult;
using search::queryeval::ParallelWeakAndBlueprint;
+using search::queryeval::RankBlueprint;
+using search::queryeval::AndBlueprint;
+using search::queryeval::IntermediateBlueprint;
+using search::queryeval::AndNotBlueprint;
+using search::queryeval::SourceBlenderBlueprint;
+
using std::string;
using std::vector;
namespace fef_test = search::fef::test;
@@ -106,6 +112,8 @@ class Test : public vespalib::TestApp {
void requireThatWeakAndBlueprintsAreCreatedCorrectly();
void requireThatParallelWandBlueprintsAreCreatedCorrectly();
void requireThatWhiteListBlueprintCanBeUsed();
+ void requireThatRankBlueprintStaysOnTopAfterWhiteListing();
+ void requireThatAndNotBlueprintStaysOnTopAfterWhiteListing();
void requireThatSameElementTermsAreProperlyPrefixed();
void requireThatSameElementDoesNotAllocateMatchData();
void requireThatSameElementIteratorsCanBeBuilt();
@@ -879,6 +887,54 @@ Test::requireThatWhiteListBlueprintCanBeUsed()
EXPECT_EQUAL(exp, act);
}
+template<typename T1, typename T2>
+void verifyThatRankBlueprintAndAndNotStaysOnTopAfterWhiteListing(QueryBuilder<ProtonNodeTypes> & builder) {
+ builder.addStringTerm("foo", field, field_id, string_weight);
+ builder.addStringTerm("bar", field, field_id, string_weight);
+ builder.addStringTerm("baz", field, field_id, string_weight);
+ std::string stackDump = StackDumpCreator::create(*builder.build());
+ Query query;
+ query.buildTree(stackDump, "", ViewResolver(), plain_index_env);
+ FakeSearchContext context(42);
+ context.addIdx(0).idx(0).getFake()
+ .addResult(field, "foo", FakeResult().doc(1));
+ context.setLimit(42);
+
+ query.setWhiteListBlueprint(std::make_unique<SimpleBlueprint>(SimpleResult()));
+
+ FakeRequestContext requestContext;
+ MatchDataLayout mdl;
+ query.reserveHandles(requestContext, context, mdl);
+ const IntermediateBlueprint * root = dynamic_cast<const T1 *>(query.peekRoot());
+ ASSERT_TRUE(root != nullptr);
+ EXPECT_EQUAL(2u, root->childCnt());
+ const IntermediateBlueprint * second = dynamic_cast<const T2 *>(&root->getChild(0));
+ ASSERT_TRUE(second != nullptr);
+ EXPECT_EQUAL(2u, second->childCnt());
+ const AndBlueprint * first = dynamic_cast<const AndBlueprint *>(&second->getChild(0));
+ ASSERT_TRUE(first != nullptr);
+ EXPECT_EQUAL(2u, first->childCnt());
+ EXPECT_TRUE(dynamic_cast<const SourceBlenderBlueprint *>(&first->getChild(0)));
+ EXPECT_TRUE(dynamic_cast<const SimpleBlueprint *>(&first->getChild(1)));
+ EXPECT_TRUE(dynamic_cast<const SourceBlenderBlueprint *>(&second->getChild(1)));
+ EXPECT_TRUE(dynamic_cast<const SourceBlenderBlueprint *>(&root->getChild(1)));
+}
+
+void Test::requireThatRankBlueprintStaysOnTopAfterWhiteListing() {
+ QueryBuilder<ProtonNodeTypes> builder;
+ builder.addRank(2);
+ builder.addAndNot(2);
+ verifyThatRankBlueprintAndAndNotStaysOnTopAfterWhiteListing<RankBlueprint, AndNotBlueprint>(builder);
+}
+
+void Test::requireThatAndNotBlueprintStaysOnTopAfterWhiteListing() {
+ QueryBuilder<ProtonNodeTypes> builder;
+ builder.addAndNot(2);
+ builder.addRank(2);
+ verifyThatRankBlueprintAndAndNotStaysOnTopAfterWhiteListing<AndNotBlueprint, RankBlueprint>(builder);
+}
+
+
search::query::Node::UP
make_same_element_stack_dump(const vespalib::string &prefix, const vespalib::string &term_prefix)
{
@@ -984,6 +1040,8 @@ Test::Main()
TEST_CALL(requireThatWeakAndBlueprintsAreCreatedCorrectly);
TEST_CALL(requireThatParallelWandBlueprintsAreCreatedCorrectly);
TEST_CALL(requireThatWhiteListBlueprintCanBeUsed);
+ TEST_CALL(requireThatRankBlueprintStaysOnTopAfterWhiteListing);
+ TEST_CALL(requireThatAndNotBlueprintStaysOnTopAfterWhiteListing);
TEST_CALL(requireThatSameElementTermsAreProperlyPrefixed);
TEST_CALL(requireThatSameElementDoesNotAllocateMatchData);
TEST_CALL(requireThatSameElementIteratorsCanBeBuilt);
diff --git a/searchcore/src/tests/proton/matching/querynodes_test.cpp b/searchcore/src/tests/proton/matching/querynodes_test.cpp
index 6607019cccc..297f75054b2 100644
--- a/searchcore/src/tests/proton/matching/querynodes_test.cpp
+++ b/searchcore/src/tests/proton/matching/querynodes_test.cpp
@@ -30,7 +30,6 @@
#include <vespa/searchlib/queryeval/fake_search.h>
#include <vespa/searchlib/queryeval/fake_requestcontext.h>
#include <vespa/vespalib/testkit/testapp.h>
-
#include <vespa/searchlib/attribute/singlenumericattribute.hpp>
#include <vespa/log/log.h>
diff --git a/searchcore/src/tests/proton/persistenceengine/persistenceengine_test.cpp b/searchcore/src/tests/proton/persistenceengine/persistenceengine_test.cpp
index 73580416e65..7f065d0cc15 100644
--- a/searchcore/src/tests/proton/persistenceengine/persistenceengine_test.cpp
+++ b/searchcore/src/tests/proton/persistenceengine/persistenceengine_test.cpp
@@ -67,7 +67,9 @@ createDoc(const DocumentType &docType, const DocumentId &docId)
document::DocumentUpdate::SP
createUpd(const DocumentType& docType, const DocumentId &docId)
{
- return document::DocumentUpdate::SP(new document::DocumentUpdate(docType, docId));
+ static std::vector<std::unique_ptr<document::DocumentTypeRepo>> repoList;
+ repoList.emplace_back(std::make_unique<document::DocumentTypeRepo>(docType));
+ return std::make_shared<document::DocumentUpdate>(*repoList.back(), docType, docId);
}
storage::spi::ClusterState
diff --git a/searchcore/src/tests/proton/proton_configurer/proton_configurer_test.cpp b/searchcore/src/tests/proton/proton_configurer/proton_configurer_test.cpp
index 045c8de9384..a89f2e3f0ff 100644
--- a/searchcore/src/tests/proton/proton_configurer/proton_configurer_test.cpp
+++ b/searchcore/src/tests/proton/proton_configurer/proton_configurer_test.cpp
@@ -12,7 +12,7 @@
#include <vespa/searchcore/proton/server/bootstrapconfig.h>
#include <vespa/searchcore/proton/server/bootstrapconfigmanager.h>
#include <vespa/searchcore/proton/server/documentdbconfigmanager.h>
-#include <vespa/searchcore/proton/server/i_document_db_config_owner.h>
+#include <vespa/searchcore/proton/server/document_db_config_owner.h>
#include <vespa/searchcore/proton/server/proton_config_snapshot.h>
#include <vespa/searchcore/proton/server/proton_configurer.h>
#include <vespa/searchcore/proton/server/i_proton_configurer_owner.h>
@@ -208,13 +208,13 @@ struct ConfigFixture {
struct MyProtonConfigurerOwner;
-struct MyDocumentDBConfigOwner : public IDocumentDBConfigOwner
+struct MyDocumentDBConfigOwner : public DocumentDBConfigOwner
{
vespalib::string _name;
MyProtonConfigurerOwner &_owner;
MyDocumentDBConfigOwner(const vespalib::string &name,
MyProtonConfigurerOwner &owner)
- : IDocumentDBConfigOwner(),
+ : DocumentDBConfigOwner(),
_name(name),
_owner(owner)
{
@@ -240,12 +240,12 @@ struct MyProtonConfigurerOwner : public IProtonConfigurerOwner
}
virtual ~MyProtonConfigurerOwner() { }
- virtual IDocumentDBConfigOwner *addDocumentDB(const DocTypeName &docTypeName,
- document::BucketSpace bucketSpace,
- const vespalib::string &configId,
- const std::shared_ptr<BootstrapConfig> &bootstrapConfig,
- const std::shared_ptr<DocumentDBConfig> &documentDBConfig,
- InitializeThreads initializeThreads) override
+ virtual std::shared_ptr<DocumentDBConfigOwner> addDocumentDB(const DocTypeName &docTypeName,
+ document::BucketSpace bucketSpace,
+ const vespalib::string &configId,
+ const std::shared_ptr<BootstrapConfig> &bootstrapConfig,
+ const std::shared_ptr<DocumentDBConfig> &documentDBConfig,
+ InitializeThreads initializeThreads) override
{
(void) bucketSpace;
(void) configId;
@@ -257,7 +257,7 @@ struct MyProtonConfigurerOwner : public IProtonConfigurerOwner
std::ostringstream os;
os << "add db " << docTypeName.getName() << " " << documentDBConfig->getGeneration();
_log.push_back(os.str());
- return db.get();
+ return db;
}
virtual void removeDocumentDB(const DocTypeName &docTypeName) override {
ASSERT_FALSE(_dbs.find(docTypeName) == _dbs.end());
diff --git a/searchcore/src/tests/proton/reference/document_db_reference/document_db_reference_test.cpp b/searchcore/src/tests/proton/reference/document_db_reference/document_db_reference_test.cpp
index fc9d2d770cd..9b268350b0d 100644
--- a/searchcore/src/tests/proton/reference/document_db_reference/document_db_reference_test.cpp
+++ b/searchcore/src/tests/proton/reference/document_db_reference/document_db_reference_test.cpp
@@ -1,6 +1,5 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/testkit/testapp.h>
-
#include <vespa/searchcore/proton/attribute/imported_attributes_repo.h>
#include <vespa/searchcore/proton/reference/document_db_reference.h>
#include <vespa/searchcore/proton/test/mock_attribute_manager.h>
diff --git a/searchcore/src/tests/proton/reprocessing/attribute_reprocessing_initializer/attribute_reprocessing_initializer_test.cpp b/searchcore/src/tests/proton/reprocessing/attribute_reprocessing_initializer/attribute_reprocessing_initializer_test.cpp
index 93c192b57e8..7be774f7291 100644
--- a/searchcore/src/tests/proton/reprocessing/attribute_reprocessing_initializer/attribute_reprocessing_initializer_test.cpp
+++ b/searchcore/src/tests/proton/reprocessing/attribute_reprocessing_initializer/attribute_reprocessing_initializer_test.cpp
@@ -306,6 +306,18 @@ TEST("require that added attribute aspect with flushed attribute after interrupt
EXPECT_TRUE(f.assertAttributes({}));
}
+TEST_F("require that removed attribute aspect from struct field does not require document field populate", Fixture)
+{
+ f.addOldConfig({"array.a"}, {"array.a"}).addNewConfig({"array.a"}, {}).init();
+ EXPECT_TRUE(f.assertFields({}));
+}
+
+TEST_F("require that added attribute aspect to struct field requires attribute populate", Fixture)
+{
+ f.addOldConfig({"array.a"}, {}).addNewConfig({"array.a"}, {"array.a"}).init();
+ EXPECT_TRUE(f.assertAttributes({"array.a"}));
+}
+
TEST_MAIN()
{
TEST_RUN_ALL();
diff --git a/searchcore/src/tests/proton/server/documentretriever_test.cpp b/searchcore/src/tests/proton/server/documentretriever_test.cpp
index 69e90a6fdf8..f8ec0d20d33 100644
--- a/searchcore/src/tests/proton/server/documentretriever_test.cpp
+++ b/searchcore/src/tests/proton/server/documentretriever_test.cpp
@@ -33,6 +33,9 @@
#include <vespa/searchlib/attribute/stringbase.h>
#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/log/log.h>
+LOG_SETUP("document_retriever_test");
+
using document::ArrayFieldValue;
using document::FieldValue;
using document::BucketId;
diff --git a/searchcore/src/tests/proton/server/feedstates_test.cpp b/searchcore/src/tests/proton/server/feedstates_test.cpp
index dfa461e2b57..f206ffc9b17 100644
--- a/searchcore/src/tests/proton/server/feedstates_test.cpp
+++ b/searchcore/src/tests/proton/server/feedstates_test.cpp
@@ -1,8 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// Unit tests for feedstates.
-#include <vespa/log/log.h>
-LOG_SETUP("feedstates_test");
#include <vespa/document/base/documentid.h>
#include <vespa/document/base/testdocrepo.h>
@@ -11,6 +9,7 @@ LOG_SETUP("feedstates_test");
#include <vespa/searchcore/proton/server/feedstates.h>
#include <vespa/searchcore/proton/server/ireplayconfig.h>
#include <vespa/searchcore/proton/server/memoryconfigstore.h>
+#include <vespa/searchcore/proton/feedoperation/removeoperation.h>
#include <vespa/searchcore/proton/test/dummy_feed_view.h>
#include <vespa/searchlib/common/serialnum.h>
#include <vespa/vespalib/objects/nbostream.h>
@@ -18,6 +17,9 @@ LOG_SETUP("feedstates_test");
#include <vespa/vespalib/util/buffer.h>
#include <vespa/searchcore/proton/bucketdb/bucketdbhandler.h>
+#include <vespa/log/log.h>
+LOG_SETUP("feedstates_test");
+
using document::BucketId;
using document::DocumentId;
using document::DocumentTypeRepo;
@@ -83,7 +85,7 @@ Fixture::Fixture()
state("doctypename", feed_view_ptr, _bucketDBHandler, replay_config, config_store)
{
}
-Fixture::~Fixture() {}
+Fixture::~Fixture() = default;
struct RemoveOperationContext
@@ -107,7 +109,7 @@ RemoveOperationContext::RemoveOperationContext(search::SerialNum serial)
packet.reset(new Packet());
packet->add(Packet::Entry(serial, FeedOperation::REMOVE, buf));
}
-RemoveOperationContext::~RemoveOperationContext() {}
+RemoveOperationContext::~RemoveOperationContext() = default;
TEST_F("require that active FeedView can change during replay", Fixture)
{
RemoveOperationContext opCtx(10);
diff --git a/searchcore/src/vespa/searchcore/config/proton.def b/searchcore/src/vespa/searchcore/config/proton.def
index e66466aa1cb..ca1ea67d288 100644
--- a/searchcore/src/vespa/searchcore/config/proton.def
+++ b/searchcore/src/vespa/searchcore/config/proton.def
@@ -231,6 +231,9 @@ summary.cache.compression.type enum {NONE, LZ4, ZSTD} default=LZ4
## 9 is a reasonable default for both
summary.cache.compression.level int default=9
+## Control if cache entry is updated or ivalidated when changed.
+summary.cache.update_strategy enum {INVALIDATE, UPDATE} default=INVALIDATE
+
## Control compression type of the summary while in memory during compaction
## NB So far only stragey=LOG honours it.
summary.log.compact.compression.type enum {NONE, LZ4, ZSTD} default=ZSTD
@@ -467,7 +470,7 @@ hwinfo.cpu.cores int default = 0 restart
## max(ceil(hwinfo.cpu.cores * feeding.concurrency), summary.log.numthreads)
## The number of threads in each of pools 2-4 is calculated as:
## max(ceil((hwinfo.cpu.cores * feeding.concurrency)/3), indexing.threads)
-feeding.concurrency double default = 0.5 restart
+feeding.concurrency double default = 0.2 restart
## Adjustment to resource limit when determining if maintenance jobs can run.
##
diff --git a/searchcore/src/vespa/searchcore/fdispatch/search/datasetcollection.cpp b/searchcore/src/vespa/searchcore/fdispatch/search/datasetcollection.cpp
index ea981d1cc01..d99b32ac138 100644
--- a/searchcore/src/vespa/searchcore/fdispatch/search/datasetcollection.cpp
+++ b/searchcore/src/vespa/searchcore/fdispatch/search/datasetcollection.cpp
@@ -198,16 +198,13 @@ FastS_DataSetCollection::GetDataSet()
bool
FastS_DataSetCollection::AreEnginesReady()
{
- bool ready = true;
-
- for (uint32_t datasetidx = 0;
- ready && (datasetidx < GetMaxNumDataSets());
- datasetidx++)
- {
+ for (uint32_t datasetidx = 0; datasetidx < GetMaxNumDataSets(); datasetidx++) {
FastS_DataSetBase *dataset = PeekDataSet(datasetidx);
- ready = (dataset != nullptr && !dataset->AreEnginesReady());
+ if ((dataset != nullptr) && !dataset->AreEnginesReady()) {
+ return false;
+ }
}
- return ready;
+ return true;
}
diff --git a/searchcore/src/vespa/searchcore/fdispatch/search/fnet_search.cpp b/searchcore/src/vespa/searchcore/fdispatch/search/fnet_search.cpp
index 9b596d6e992..577d6e7edf5 100644
--- a/searchcore/src/vespa/searchcore/fdispatch/search/fnet_search.cpp
+++ b/searchcore/src/vespa/searchcore/fdispatch/search/fnet_search.cpp
@@ -69,7 +69,7 @@ FastS_FNET_SearchNode::FastS_FNET_SearchNode(FastS_FNET_SearchNode &&)
{
// These objects are referenced everywhere and must never be either copied nor moved,
// but as std::vector requires this to exist so we do this little trick.
- assert(false);
+ LOG_ABORT("should not reach here");
}
bool
diff --git a/searchcore/src/vespa/searchcore/grouping/groupingmanager.cpp b/searchcore/src/vespa/searchcore/grouping/groupingmanager.cpp
index 48baba329ca..3ef5a0f63b1 100644
--- a/searchcore/src/vespa/searchcore/grouping/groupingmanager.cpp
+++ b/searchcore/src/vespa/searchcore/grouping/groupingmanager.cpp
@@ -21,9 +21,7 @@ GroupingManager::GroupingManager(GroupingContext & groupingContext)
{
}
-GroupingManager::~GroupingManager()
-{
-}
+GroupingManager::~GroupingManager() = default;
using search::expression::ExpressionNode;
using search::expression::AttributeNode;
diff --git a/searchcore/src/vespa/searchcore/proton/attribute/attribute_aspect_delayer.cpp b/searchcore/src/vespa/searchcore/proton/attribute/attribute_aspect_delayer.cpp
index 9807faa5021..cf803ec0368 100644
--- a/searchcore/src/vespa/searchcore/proton/attribute/attribute_aspect_delayer.cpp
+++ b/searchcore/src/vespa/searchcore/proton/attribute/attribute_aspect_delayer.cpp
@@ -29,11 +29,15 @@ bool fastPartialUpdateAttribute(const search::attribute::Config &cfg) {
(basicType != BasicType::Type::REFERENCE));
}
+bool isStructFieldAttribute(const vespalib::string &name) {
+ return name.find('.') != vespalib::string::npos;
+}
+
bool willTriggerReprocessOnAttributeAspectRemoval(const search::attribute::Config &cfg,
const IIndexschemaInspector &indexschemaInspector,
const vespalib::string &name)
{
- return fastPartialUpdateAttribute(cfg) && !indexschemaInspector.isStringIndex(name);
+ return fastPartialUpdateAttribute(cfg) && !indexschemaInspector.isStringIndex(name) && !isStructFieldAttribute(name);
}
@@ -73,6 +77,7 @@ handleNewAttributes(const AttributesConfig &oldAttributesConfig,
SummarymapConfigBuilder &summarymapConfig)
{
vespalib::hash_set<vespalib::string> delayed;
+ vespalib::hash_set<vespalib::string> delayedStruct;
AttributesConfigHash oldAttrs(oldAttributesConfig.attribute);
for (const auto &newAttr : newAttributesConfig.attribute) {
search::attribute::Config newCfg = ConfigConverter::convert(newAttr);
@@ -102,6 +107,10 @@ handleNewAttributes(const AttributesConfig &oldAttributesConfig,
} else {
// Delay addition of attribute aspect
delayed.insert(newAttr.name);
+ auto pos = newAttr.name.find('.');
+ if (pos != vespalib::string::npos) {
+ delayedStruct.insert(newAttr.name.substr(0, pos));
+ }
}
}
}
@@ -111,6 +120,11 @@ handleNewAttributes(const AttributesConfig &oldAttributesConfig,
if (itr == delayed.end()) {
summarymapConfig.override.emplace_back(override);
}
+ } else if (override.command == "attributecombiner") {
+ auto itr = delayedStruct.find(override.field);
+ if (itr == delayedStruct.end()) {
+ summarymapConfig.override.emplace_back(override);
+ }
} else {
summarymapConfig.override.emplace_back(override);
}
diff --git a/searchcore/src/vespa/searchcore/proton/attribute/attribute_directory.cpp b/searchcore/src/vespa/searchcore/proton/attribute/attribute_directory.cpp
index f775f4443f8..f2e4ac4905d 100644
--- a/searchcore/src/vespa/searchcore/proton/attribute/attribute_directory.cpp
+++ b/searchcore/src/vespa/searchcore/proton/attribute/attribute_directory.cpp
@@ -92,10 +92,9 @@ AttributeDirectory::saveSnapInfo()
{
if (!_snapInfo.save()) {
vespalib::string dirName(getDirName());
- LOG(warning,
- "Could not save meta-info file for attribute vector '%s' to disk",
+ LOG(warning, "Could not save meta-info file for attribute vector '%s' to disk",
dirName.c_str());
- abort();
+ LOG_ABORT("should not be reached");
}
}
diff --git a/searchcore/src/vespa/searchcore/proton/attribute/attribute_writer.cpp b/searchcore/src/vespa/searchcore/proton/attribute/attribute_writer.cpp
index 239f91b449f..eb6020b8d5f 100644
--- a/searchcore/src/vespa/searchcore/proton/attribute/attribute_writer.cpp
+++ b/searchcore/src/vespa/searchcore/proton/attribute/attribute_writer.cpp
@@ -4,19 +4,19 @@
#include "ifieldupdatecallback.h"
#include "attributemanager.h"
#include "document_field_extractor.h"
-#include <vespa/document/base/exceptions.h>
-#include <vespa/document/datatype/documenttype.h>
-#include <vespa/document/fieldvalue/document.h>
#include <vespa/searchcore/proton/attribute/imported_attributes_repo.h>
#include <vespa/searchcore/proton/common/attrupdate.h>
#include <vespa/searchlib/attribute/attributevector.hpp>
#include <vespa/searchlib/attribute/imported_attribute_vector.h>
#include <vespa/searchlib/common/isequencedtaskexecutor.h>
#include <vespa/searchlib/common/idestructorcallback.h>
-
+#include <vespa/document/base/exceptions.h>
+#include <vespa/document/datatype/documenttype.h>
+#include <vespa/document/fieldvalue/document.h>
+#include <vespa/vespalib/stllike/hash_map.hpp>
#include <vespa/log/log.h>
-LOG_SETUP(".proton.server.attributeadapter");
+LOG_SETUP(".proton.attribute.attribute_writer");
using namespace document;
using namespace search;
@@ -47,6 +47,8 @@ AttributeWriter::WriteField::buildFieldPath(const DocumentType &docType)
docType.buildFieldPath(fp, name);
} catch (document::FieldNotFoundException & e) {
fp = FieldPath();
+ } catch (vespalib::IllegalArgumentException &e) {
+ fp = FieldPath();
}
_fieldPath = std::move(fp);
}
@@ -230,7 +232,7 @@ public:
FieldContext::FieldContext(ISequencedTaskExecutor &writer, AttributeVector *attr)
: _name(attr->getName()),
- _executorId(writer.getExecutorId(_name)),
+ _executorId(writer.getExecutorId(attr->getNamePrefix())),
_attr(attr)
{
}
@@ -412,7 +414,7 @@ AttributeWriter::setupWriteContexts()
{
std::vector<FieldContext> fieldContexts;
assert(_writeContexts.empty());
- for (auto attr : _writableAttributes) {
+ for (auto attr : getWritableAttributes()) {
fieldContexts.emplace_back(_attributeFieldWriter, attr);
}
std::sort(fieldContexts.begin(), fieldContexts.end());
@@ -469,14 +471,23 @@ AttributeWriter::internalRemove(SerialNum serialNum, DocumentIdT lid, bool immed
AttributeWriter::AttributeWriter(const proton::IAttributeManager::SP &mgr)
: _mgr(mgr),
_attributeFieldWriter(mgr->getAttributeFieldWriter()),
- _writableAttributes(mgr->getWritableAttributes()),
_writeContexts(),
_dataType(nullptr),
- _hasStructFieldAttribute(false)
+ _hasStructFieldAttribute(false),
+ _attrMap()
{
setupWriteContexts();
+ setupAttriuteMapping();
}
+void AttributeWriter::setupAttriuteMapping() {
+ for (auto attr : getWritableAttributes()) {
+ vespalib::stringref name = attr->getName();
+ _attrMap[name] = AttrWithId(attr, _attributeFieldWriter.getExecutorId(attr->getNamePrefix()));
+ }
+}
+
+
AttributeWriter::~AttributeWriter()
{
_attributeFieldWriter.sync();
@@ -545,7 +556,8 @@ AttributeWriter::update(SerialNum serialNum, const DocumentUpdate &upd, Document
for (const auto &fupd : upd.getUpdates()) {
LOG(debug, "Retrieving guard for attribute vector '%s'.", fupd.getField().getName().c_str());
- AttributeVector *attrp = _mgr->getWritableAttribute(fupd.getField().getName());
+ auto found = _attrMap.find(fupd.getField().getName());
+ AttributeVector * attrp = (found != _attrMap.end()) ? found->second.first : nullptr;
onUpdate.onUpdateField(fupd.getField().getName(), attrp);
if (attrp == nullptr) {
LOG(spam, "Failed to find attribute vector %s", fupd.getField().getName().c_str());
@@ -555,7 +567,7 @@ AttributeWriter::update(SerialNum serialNum, const DocumentUpdate &upd, Document
// document and attribute.
if (attrp->getStatus().getLastSyncToken() >= serialNum)
continue;
- args[_attributeFieldWriter.getExecutorId(attrp->getName()).getId()]->_updates.emplace_back(attrp, &fupd);
+ args[found->second.second.getId()]->_updates.emplace_back(attrp, &fupd);
LOG(debug, "About to apply update for docId %u in attribute vector '%s'.", lid, attrp->getName().c_str());
}
// NOTE: The lifetime of the field update will be ensured by keeping the document update alive
@@ -572,11 +584,10 @@ AttributeWriter::update(SerialNum serialNum, const DocumentUpdate &upd, Document
void
AttributeWriter::heartBeat(SerialNum serialNum)
{
- for (auto attrp : _writableAttributes) {
- auto &attr = *attrp;
- _attributeFieldWriter.execute(attr.getName(),
- [serialNum, &attr]()
- { applyHeartBeat(serialNum, attr); });
+ for (auto entry : _attrMap) {
+ _attributeFieldWriter.execute(entry.second.second,
+ [serialNum, attr=entry.second.first]()
+ { applyHeartBeat(serialNum, *attr); });
}
}
@@ -601,11 +612,10 @@ AttributeWriter::forceCommit(SerialNum serialNum, OnWriteDoneType onWriteDone)
void
AttributeWriter::onReplayDone(uint32_t docIdLimit)
{
- for (auto attrp : _writableAttributes) {
- auto &attr = *attrp;
- _attributeFieldWriter.execute(attr.getName(),
- [docIdLimit, &attr]()
- { applyReplayDone(docIdLimit, attr); });
+ for (auto entry : _attrMap) {
+ _attributeFieldWriter.execute(entry.second.second,
+ [docIdLimit, attr = entry.second.first]()
+ { applyReplayDone(docIdLimit, *attr); });
}
_attributeFieldWriter.sync();
}
@@ -614,12 +624,11 @@ AttributeWriter::onReplayDone(uint32_t docIdLimit)
void
AttributeWriter::compactLidSpace(uint32_t wantedLidLimit, SerialNum serialNum)
{
- for (auto attrp : _writableAttributes) {
- auto &attr = *attrp;
+ for (auto entry : _attrMap) {
_attributeFieldWriter.
- execute(attr.getName(),
- [wantedLidLimit, serialNum, &attr]()
- { applyCompactLidSpace(wantedLidLimit, serialNum, attr); });
+ execute(entry.second.second,
+ [wantedLidLimit, serialNum, attr=entry.second.first]()
+ { applyCompactLidSpace(wantedLidLimit, serialNum, *attr); });
}
_attributeFieldWriter.sync();
}
diff --git a/searchcore/src/vespa/searchcore/proton/attribute/attribute_writer.h b/searchcore/src/vespa/searchcore/proton/attribute/attribute_writer.h
index f89089ed335..4ea7f3fda6c 100644
--- a/searchcore/src/vespa/searchcore/proton/attribute/attribute_writer.h
+++ b/searchcore/src/vespa/searchcore/proton/attribute/attribute_writer.h
@@ -6,6 +6,7 @@
#include <vespa/searchcore/proton/common/commit_time_tracker.h>
#include <vespa/document/base/fieldpath.h>
#include <vespa/searchlib/common/isequencedtaskexecutor.h>
+#include <vespa/vespalib/stllike/hash_map.h>
namespace document { class DocumentType; }
@@ -25,7 +26,6 @@ private:
typedef document::FieldValue FieldValue;
const IAttributeManager::SP _mgr;
search::ISequencedTaskExecutor &_attributeFieldWriter;
- const std::vector<search::AttributeVector *> &_writableAttributes;
using ExecutorId = search::ISequencedTaskExecutor::ExecutorId;
public:
class WriteField
@@ -58,11 +58,15 @@ public:
bool hasStructFieldAttribute() const { return _hasStructFieldAttribute; }
};
private:
+ using AttrWithId = std::pair<search::AttributeVector *, ExecutorId>;
+ using AttrMap = vespalib::hash_map<vespalib::string, AttrWithId>;
std::vector<WriteContext> _writeContexts;
const DataType *_dataType;
bool _hasStructFieldAttribute;
+ AttrMap _attrMap;
void setupWriteContexts();
+ void setupAttriuteMapping();
void buildFieldPaths(const DocumentType &docType, const DataType *dataType);
void internalPut(SerialNum serialNum, const Document &doc, DocumentIdT lid,
bool immediateCommit, bool allAttributes, OnWriteDoneType onWriteDone);
@@ -73,13 +77,13 @@ public:
AttributeWriter(const proton::IAttributeManager::SP &mgr);
~AttributeWriter();
+ /* Only for in tests that add attributes after AttributeWriter construction. */
+
/**
* Implements IAttributeWriter.
*/
- std::vector<search::AttributeVector *>
- getWritableAttributes() const override;
- search::AttributeVector *
- getWritableAttribute(const vespalib::string &name) const override;
+ std::vector<search::AttributeVector *> getWritableAttributes() const override;
+ search::AttributeVector *getWritableAttribute(const vespalib::string &name) const override;
void put(SerialNum serialNum, const Document &doc, DocumentIdT lid,
bool immediateCommit, OnWriteDoneType onWriteDone) override;
void remove(SerialNum serialNum, DocumentIdT lid,
diff --git a/searchcore/src/vespa/searchcore/proton/attribute/attributemanager.cpp b/searchcore/src/vespa/searchcore/proton/attribute/attributemanager.cpp
index ef818f7b407..5faf89aa149 100644
--- a/searchcore/src/vespa/searchcore/proton/attribute/attributemanager.cpp
+++ b/searchcore/src/vespa/searchcore/proton/attribute/attributemanager.cpp
@@ -78,8 +78,8 @@ std::shared_ptr<ShrinkLidSpaceFlushTarget> allocShrinker(const AttributeVector::
using Type = IFlushTarget::Type;
using Component = IFlushTarget::Component;
+ auto shrinkwrap = std::make_shared<ThreadedCompactableLidSpace>(attr, attributeFieldWriter, attributeFieldWriter.getExecutorId(attr->getNamePrefix()));
const vespalib::string &name = attr->getName();
- auto shrinkwrap = std::make_shared<ThreadedCompactableLidSpace>(attr, attributeFieldWriter, attributeFieldWriter.getExecutorId(name));
auto dir = diskLayout.createAttributeDir(name);
search::SerialNum shrinkSerialNum = estimateShrinkSerialNum(*attr);
return std::make_shared<ShrinkLidSpaceFlushTarget>("attribute.shrink." + name, Type::GC, Component::ATTRIBUTE, shrinkSerialNum, dir->getLastFlushTime(), shrinkwrap);
@@ -569,16 +569,15 @@ AttributeManager::getWritableAttributes() const
void
-AttributeManager::asyncForEachAttribute(std::shared_ptr<IAttributeFunctor>
- func) const
+AttributeManager::asyncForEachAttribute(std::shared_ptr<IAttributeFunctor> func) const
{
for (const auto &attr : _attributes) {
if (attr.second.isExtra()) {
continue;
}
AttributeVector::SP attrsp = attr.second.getAttribute();
- _attributeFieldWriter.
- execute(attr.first, [attrsp, func]() { (*func)(*attrsp); });
+ _attributeFieldWriter.execute(_attributeFieldWriter.getExecutorId(attrsp->getNamePrefix()),
+ [attrsp, func]() { (*func)(*attrsp); });
}
}
diff --git a/searchcore/src/vespa/searchcore/proton/attribute/exclusive_attribute_read_accessor.cpp b/searchcore/src/vespa/searchcore/proton/attribute/exclusive_attribute_read_accessor.cpp
index d9a0ff3d8dd..d1d5b1c9af7 100644
--- a/searchcore/src/vespa/searchcore/proton/attribute/exclusive_attribute_read_accessor.cpp
+++ b/searchcore/src/vespa/searchcore/proton/attribute/exclusive_attribute_read_accessor.cpp
@@ -37,8 +37,7 @@ ExclusiveAttributeReadAccessor(const AttributeVector::SP &attribute,
namespace {
void
-attributeWriteBlockingTask(GateSP entranceGate,
- GateSP exitGate)
+attributeWriteBlockingTask(GateSP entranceGate, GateSP exitGate)
{
entranceGate->countDown();
exitGate->await();
@@ -51,9 +50,8 @@ ExclusiveAttributeReadAccessor::takeGuard()
{
GateSP entranceGate = std::make_shared<Gate>();
GateSP exitGate = std::make_shared<Gate>();
- _attributeFieldWriter.execute(_attribute->getName(),
- [entranceGate, exitGate]()
- { attributeWriteBlockingTask(entranceGate, exitGate); });
+ _attributeFieldWriter.execute(_attributeFieldWriter.getExecutorId(_attribute->getNamePrefix()),
+ [entranceGate, exitGate]() { attributeWriteBlockingTask(entranceGate, exitGate); });
entranceGate->await();
return std::make_unique<Guard>(*_attribute, exitGate);
}
diff --git a/searchcore/src/vespa/searchcore/proton/attribute/filter_attribute_manager.cpp b/searchcore/src/vespa/searchcore/proton/attribute/filter_attribute_manager.cpp
index d3a74bb9a98..8474efb15c9 100644
--- a/searchcore/src/vespa/searchcore/proton/attribute/filter_attribute_manager.cpp
+++ b/searchcore/src/vespa/searchcore/proton/attribute/filter_attribute_manager.cpp
@@ -197,9 +197,8 @@ FilterAttributeManager::asyncForEachAttribute(std::shared_ptr<IAttributeFunctor>
search::AttributeVector::SP attrsp = guard.getSP();
// Name must be extracted in document db master thread or attribute
// writer thread
- vespalib::string attributeName = attrsp->getName();
- attributeFieldWriter.
- execute(attributeName, [attrsp, func]() { (*func)(*attrsp); });
+ attributeFieldWriter.execute(attributeFieldWriter.getExecutorId(attrsp->getNamePrefix()),
+ [attrsp, func]() { (*func)(*attrsp); });
}
}
diff --git a/searchcore/src/vespa/searchcore/proton/attribute/flushableattribute.cpp b/searchcore/src/vespa/searchcore/proton/attribute/flushableattribute.cpp
index a658b11263a..7716fc5ee61 100644
--- a/searchcore/src/vespa/searchcore/proton/attribute/flushableattribute.cpp
+++ b/searchcore/src/vespa/searchcore/proton/attribute/flushableattribute.cpp
@@ -243,10 +243,8 @@ FlushableAttribute::initFlush(SerialNum currentSerial)
// Called by document db executor
std::promise<IFlushTarget::Task::UP> promise;
std::future<IFlushTarget::Task::UP> future = promise.get_future();
- _attributeFieldWriter.execute(_attr->getName(),
- [&]() { promise.set_value(
- internalInitFlush(currentSerial));
- });
+ _attributeFieldWriter.execute(_attributeFieldWriter.getExecutorId(_attr->getNamePrefix()),
+ [&]() { promise.set_value(internalInitFlush(currentSerial)); });
return future.get();
}
@@ -257,5 +255,4 @@ FlushableAttribute::getApproxBytesToWriteToDisk() const
return _attr->getEstimatedSaveByteSize();
}
-
} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/common/attributefieldvaluenode.cpp b/searchcore/src/vespa/searchcore/proton/common/attributefieldvaluenode.cpp
index 6fa19f0f35e..b76451b377d 100644
--- a/searchcore/src/vespa/searchcore/proton/common/attributefieldvaluenode.cpp
+++ b/searchcore/src/vespa/searchcore/proton/common/attributefieldvaluenode.cpp
@@ -5,6 +5,8 @@
#include <vespa/searchcommon/attribute/attributecontent.h>
#include <vespa/searchlib/attribute/attributevector.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".proton.common.attribute_field_value_node");
namespace proton {
@@ -80,7 +82,7 @@ getValue(const Context &context) const
} while (0);
break;
default:
- abort();
+ LOG_ABORT("should not be reached");
}
return Value::UP();
diff --git a/searchcore/src/vespa/searchcore/proton/common/attrupdate.cpp b/searchcore/src/vespa/searchcore/proton/common/attrupdate.cpp
index 7c0a1815019..dac0fbc2cc7 100644
--- a/searchcore/src/vespa/searchcore/proton/common/attrupdate.cpp
+++ b/searchcore/src/vespa/searchcore/proton/common/attrupdate.cpp
@@ -17,13 +17,12 @@
#include <vespa/searchlib/common/base.h>
#include <vespa/searchlib/tensor/tensor_attribute.h>
#include <vespa/searchlib/attribute/reference_attribute.h>
-
#include <vespa/searchlib/attribute/attributevector.hpp>
#include <vespa/searchlib/attribute/changevector.hpp>
#include <sstream>
#include <vespa/log/log.h>
-LOG_SETUP(".attrupdate");
+LOG_SETUP(".proton.common.attrupdate");
using namespace document;
using vespalib::make_string;
diff --git a/searchcore/src/vespa/searchcore/proton/common/document_type_inspector.cpp b/searchcore/src/vespa/searchcore/proton/common/document_type_inspector.cpp
index 6cff162ae08..e19fa5351c2 100644
--- a/searchcore/src/vespa/searchcore/proton/common/document_type_inspector.cpp
+++ b/searchcore/src/vespa/searchcore/proton/common/document_type_inspector.cpp
@@ -1,6 +1,11 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "document_type_inspector.h"
+#include <vespa/document/base/exceptions.h>
+#include <vespa/document/base/fieldpath.h>
+
+using document::FieldPath;
+using document::FieldPathEntry;
namespace proton {
@@ -14,12 +19,28 @@ DocumentTypeInspector::DocumentTypeInspector(const document::DocumentType &oldDo
bool
DocumentTypeInspector::hasUnchangedField(const vespalib::string &name) const
{
- if (!_oldDocType.hasField(name) || !_newDocType.hasField(name)) {
+ FieldPath oldPath;
+ FieldPath newPath;
+ try {
+ _oldDocType.buildFieldPath(oldPath, name);
+ _newDocType.buildFieldPath(newPath, name);
+ } catch (document::FieldNotFoundException &e) {
+ return false;
+ } catch (vespalib::IllegalArgumentException &e) {
return false;
}
- const document::Field &oldField = _oldDocType.getField(name);
- const document::Field &newField = _newDocType.getField(name);
- return oldField == newField;
+ if (oldPath.size() != newPath.size()) {
+ return false;
+ }
+ for (uint32_t i = 0; i < oldPath.size(); ++i) {
+ const auto &oldEntry = oldPath[i];
+ const auto &newEntry = newPath[i];
+ if (oldEntry.getType() != newEntry.getType() ||
+ oldEntry.getDataType() != newEntry.getDataType()) {
+ return false;
+ }
+ }
+ return true;
}
} // namespace proton
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 c9bf81d1310..190e904ad68 100644
--- a/searchcore/src/vespa/searchcore/proton/common/hw_info_sampler.cpp
+++ b/searchcore/src/vespa/searchcore/proton/common/hw_info_sampler.cpp
@@ -10,6 +10,8 @@
#include <vespa/vespalib/io/fileutil.h>
#include <experimental/filesystem>
#include <thread>
+#include <vespa/log/log.h>
+LOG_SETUP(".proton.common.hw_info_sampler");
using config::ConfigHandle;
using config::ConfigSubscriber;
@@ -70,7 +72,7 @@ void writeConfig(const vespalib::string &path,
builder.disk.sampletime = std::chrono::duration_cast<std::chrono::seconds>(sampleTime.time_since_epoch()).count();
config::FileConfigWriter writer(path + "/hwinfo.cfg");
if (!writer.write(builder)) {
- abort();
+ LOG_ABORT("should not be reached");
}
}
diff --git a/searchcore/src/vespa/searchcore/proton/docsummary/summarymanager.cpp b/searchcore/src/vespa/searchcore/proton/docsummary/summarymanager.cpp
index ead5c1d1ddf..54961d10fd3 100644
--- a/searchcore/src/vespa/searchcore/proton/docsummary/summarymanager.cpp
+++ b/searchcore/src/vespa/searchcore/proton/docsummary/summarymanager.cpp
@@ -161,7 +161,7 @@ SummaryManager::SummaryManager(vespalib::ThreadExecutor & executor, const LogDoc
fileHeaderContext, tlSyncer, bucketizer);
}
-SummaryManager::~SummaryManager() {}
+SummaryManager::~SummaryManager() = default;
void
SummaryManager::putDocument(uint64_t syncToken, search::DocumentIdT lid, const Document & doc)
@@ -204,7 +204,7 @@ IFlushTarget::List SummaryManager::getFlushTargets(searchcorespi::index::IThread
{
IFlushTarget::List ret;
ret.push_back(std::make_shared<SummaryFlushTarget>(getBackingStore(), summaryService));
- if (dynamic_cast<LogDocumentStore *>(_docStore.get()) != NULL) {
+ if (dynamic_cast<LogDocumentStore *>(_docStore.get()) != nullptr) {
ret.push_back(std::make_shared<SummaryCompactTarget>(summaryService, getBackingStore()));
}
ret.push_back(createShrinkLidSpaceFlushTarget(summaryService, _docStore));
diff --git a/searchcore/src/vespa/searchcore/proton/docsummary/summarymanagerinitializer.cpp b/searchcore/src/vespa/searchcore/proton/docsummary/summarymanagerinitializer.cpp
index c11326090dc..eee0be77c26 100644
--- a/searchcore/src/vespa/searchcore/proton/docsummary/summarymanagerinitializer.cpp
+++ b/searchcore/src/vespa/searchcore/proton/docsummary/summarymanagerinitializer.cpp
@@ -32,7 +32,7 @@ SummaryManagerInitializer(const search::GrowStrategy &grow,
_result(result)
{ }
-SummaryManagerInitializer::~SummaryManagerInitializer() {}
+SummaryManagerInitializer::~SummaryManagerInitializer() = default;
void
SummaryManagerInitializer::run()
diff --git a/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastore.cpp b/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastore.cpp
index d2bbde1eadd..838efdfe5a4 100644
--- a/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastore.cpp
+++ b/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastore.cpp
@@ -21,6 +21,8 @@
#include <vespa/fastos/file.h>
#include "document_meta_store_versions.h"
+#include <vespa/log/log.h>
+LOG_SETUP(".proton.documentmetastore");
using document::BucketId;
using document::GlobalId;
@@ -77,7 +79,7 @@ public:
_headerLen = _header.readFile(*_datFile);
_datFile->SetPosition(_headerLen);
if (!search::ReaderBase::extractFileSize(_header, *_datFile, _datFileSize)) {
- abort();
+ LOG_ABORT("should not be reached");
}
_docIdLimit = _header.getTag(DOCID_LIMIT).asInteger();
_version = _header.getTag(VERSION).asInteger();
diff --git a/searchcore/src/vespa/searchcore/proton/documentmetastore/search_context.cpp b/searchcore/src/vespa/searchcore/proton/documentmetastore/search_context.cpp
index e10e6ed539b..5a4c15ed27a 100644
--- a/searchcore/src/vespa/searchcore/proton/documentmetastore/search_context.cpp
+++ b/searchcore/src/vespa/searchcore/proton/documentmetastore/search_context.cpp
@@ -36,8 +36,9 @@ private:
protected:
const DocumentMetaStore & _store;
public:
- GidAllSearchIterator(TermFieldMatchData *matchData, const DocumentMetaStore &store)
- : AttributeIteratorBase(matchData),
+ GidAllSearchIterator(const search::attribute::ISearchContext &baseSearchCtx,
+ TermFieldMatchData *matchData, const DocumentMetaStore &store)
+ : AttributeIteratorBase(baseSearchCtx, matchData),
_store(store)
{
}
@@ -64,9 +65,10 @@ private:
}
public:
- GidStrictAllSearchIterator(TermFieldMatchData *matchData,
+ GidStrictAllSearchIterator(const search::attribute::ISearchContext &baseSearchCtx,
+ TermFieldMatchData *matchData,
const DocumentMetaStore &store)
- : GidAllSearchIterator(matchData, store),
+ : GidAllSearchIterator(baseSearchCtx, matchData, store),
_numDocs(store.getNumDocs())
{
}
@@ -88,8 +90,9 @@ private:
}
}
public:
- GidSearchIterator(TermFieldMatchData *matchData, const DocumentMetaStore &store, const GlobalId &gid)
- : GidAllSearchIterator(matchData, store),
+ GidSearchIterator(const search::attribute::ISearchContext &baseSearchCtx,
+ TermFieldMatchData *matchData, const DocumentMetaStore &store, const GlobalId &gid)
+ : GidAllSearchIterator(baseSearchCtx, matchData, store),
_gid(gid)
{
}
@@ -119,10 +122,10 @@ SearchIterator::UP
SearchContext::createIterator(TermFieldMatchData *matchData, bool strict)
{
return _isWord
- ? std::make_unique<GidSearchIterator>(matchData, getStore(), _gid)
+ ? std::make_unique<GidSearchIterator>(*this, matchData, getStore(), _gid)
: strict
- ? std::make_unique<GidStrictAllSearchIterator>(matchData, getStore())
- : std::make_unique<GidAllSearchIterator>(matchData, getStore());
+ ? std::make_unique<GidStrictAllSearchIterator>(*this, matchData, getStore())
+ : std::make_unique<GidAllSearchIterator>(*this, matchData, getStore());
}
const DocumentMetaStore &
diff --git a/searchcore/src/vespa/searchcore/proton/feedoperation/compact_lid_space_operation.cpp b/searchcore/src/vespa/searchcore/proton/feedoperation/compact_lid_space_operation.cpp
index cd906891b92..c2a3439388a 100644
--- a/searchcore/src/vespa/searchcore/proton/feedoperation/compact_lid_space_operation.cpp
+++ b/searchcore/src/vespa/searchcore/proton/feedoperation/compact_lid_space_operation.cpp
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "compact_lid_space_operation.h"
+#include <vespa/vespalib/objects/nbostream.h>
#include <vespa/vespalib/util/stringfmt.h>
namespace proton {
@@ -27,8 +28,7 @@ CompactLidSpaceOperation::serialize(vespalib::nbostream& os) const
}
void
-CompactLidSpaceOperation::deserialize(vespalib::nbostream& is,
- const document::DocumentTypeRepo&)
+CompactLidSpaceOperation::deserialize(vespalib::nbostream& is, const document::DocumentTypeRepo&)
{
is >> _subDbId;
is >> _lidLimit;
diff --git a/searchcore/src/vespa/searchcore/proton/feedoperation/feedoperation.h b/searchcore/src/vespa/searchcore/proton/feedoperation/feedoperation.h
index 8a00c739126..77b95547bd0 100644
--- a/searchcore/src/vespa/searchcore/proton/feedoperation/feedoperation.h
+++ b/searchcore/src/vespa/searchcore/proton/feedoperation/feedoperation.h
@@ -2,10 +2,10 @@
#pragma once
#include <vespa/searchlib/common/serialnum.h>
-#include <vespa/vespalib/objects/nbostream.h>
+#include <vespa/vespalib/stllike/string.h>
namespace document { class DocumentTypeRepo; }
-
+namespace vespalib { class nbostream; }
namespace proton {
class FeedOperation
diff --git a/searchcore/src/vespa/searchcore/proton/feedoperation/lidvectorcontext.cpp b/searchcore/src/vespa/searchcore/proton/feedoperation/lidvectorcontext.cpp
index 71a701bdb40..67e4d1b4287 100644
--- a/searchcore/src/vespa/searchcore/proton/feedoperation/lidvectorcontext.cpp
+++ b/searchcore/src/vespa/searchcore/proton/feedoperation/lidvectorcontext.cpp
@@ -2,6 +2,7 @@
#include "lidvectorcontext.h"
#include <vespa/searchlib/common/bitvector.h>
+#include <vespa/vespalib/objects/nbostream.h>
#include <vespa/log/log.h>
LOG_SETUP(".proton.feedoperation.lidvectorcontext");
diff --git a/searchcore/src/vespa/searchcore/proton/feedoperation/lidvectorcontext.h b/searchcore/src/vespa/searchcore/proton/feedoperation/lidvectorcontext.h
index b432bbcf20a..b307e50da0a 100644
--- a/searchcore/src/vespa/searchcore/proton/feedoperation/lidvectorcontext.h
+++ b/searchcore/src/vespa/searchcore/proton/feedoperation/lidvectorcontext.h
@@ -2,9 +2,10 @@
#pragma once
#include <vespa/searchlib/query/base.h>
-#include <vespa/vespalib/objects/nbostream.h>
#include <vector>
+namespace vespalib { class nbostream; }
+
namespace proton {
class LidVectorContext
@@ -29,5 +30,4 @@ public:
size_t getNumLids() const { return _result.size(); }
};
-} // namespace proton
-
+}
diff --git a/searchcore/src/vespa/searchcore/proton/feedoperation/newconfigoperation.h b/searchcore/src/vespa/searchcore/proton/feedoperation/newconfigoperation.h
index b7b041135d4..144aa534bf7 100644
--- a/searchcore/src/vespa/searchcore/proton/feedoperation/newconfigoperation.h
+++ b/searchcore/src/vespa/searchcore/proton/feedoperation/newconfigoperation.h
@@ -2,30 +2,29 @@
#pragma once
#include "feedoperation.h"
-#include <vespa/vespalib/objects/nbostream.h>
namespace proton {
+ namespace feedoperation {
+ struct IStreamHandler {
+ virtual ~IStreamHandler() {}
+ virtual void serializeConfig(search::SerialNum serialNum, vespalib::nbostream &os) = 0;
+ virtual void deserializeConfig(search::SerialNum serialNum, vespalib::nbostream &is) = 0;
+ };
+ }
+
class NewConfigOperation : public FeedOperation
{
public:
- struct IStreamHandler {
- virtual ~IStreamHandler() {}
- virtual void serializeConfig(SerialNum serialNum,
- vespalib::nbostream &os) = 0;
- virtual void deserializeConfig(SerialNum serialNum,
- vespalib::nbostream &is) = 0;
- };
+ using IStreamHandler = feedoperation::IStreamHandler;
private:
IStreamHandler &_streamHandler;
public:
- NewConfigOperation(SerialNum serialNum,
- IStreamHandler &streamHandler);
- virtual ~NewConfigOperation() {}
- virtual void serialize(vespalib::nbostream &os) const override;
- virtual void deserialize(vespalib::nbostream &is,
- const document::DocumentTypeRepo &repo) override;
- virtual vespalib::string toString() const override;
+ NewConfigOperation(SerialNum serialNum, IStreamHandler &streamHandler);
+ ~NewConfigOperation() override {}
+ void serialize(vespalib::nbostream &os) const override;
+ void deserialize(vespalib::nbostream &is, const document::DocumentTypeRepo &repo) override;
+ vespalib::string toString() const override;
};
} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/feedoperation/pruneremoveddocumentsoperation.cpp b/searchcore/src/vespa/searchcore/proton/feedoperation/pruneremoveddocumentsoperation.cpp
index eea693e4680..69f754cd594 100644
--- a/searchcore/src/vespa/searchcore/proton/feedoperation/pruneremoveddocumentsoperation.cpp
+++ b/searchcore/src/vespa/searchcore/proton/feedoperation/pruneremoveddocumentsoperation.cpp
@@ -2,6 +2,7 @@
#include "pruneremoveddocumentsoperation.h"
#include <vespa/vespalib/util/stringfmt.h>
+#include <vespa/vespalib/objects/nbostream.h>
#include <cassert>
#include <vespa/log/log.h>
@@ -22,8 +23,7 @@ PruneRemovedDocumentsOperation::PruneRemovedDocumentsOperation()
PruneRemovedDocumentsOperation::
-PruneRemovedDocumentsOperation(DocumentIdT docIdLimit,
- uint32_t subDbId)
+PruneRemovedDocumentsOperation(DocumentIdT docIdLimit, uint32_t subDbId)
: RemoveDocumentsOperation(FeedOperation::PRUNE_REMOVED_DOCUMENTS),
_subDbId(subDbId)
{
@@ -44,8 +44,7 @@ PruneRemovedDocumentsOperation::serialize(vespalib::nbostream &os) const
void
-PruneRemovedDocumentsOperation::deserialize(vespalib::nbostream &is,
- const DocumentTypeRepo &)
+PruneRemovedDocumentsOperation::deserialize(vespalib::nbostream &is, const DocumentTypeRepo &)
{
is >> _subDbId;
deserializeLidsToRemove(is);
diff --git a/searchcore/src/vespa/searchcore/proton/feedoperation/removedocumentsoperation.cpp b/searchcore/src/vespa/searchcore/proton/feedoperation/removedocumentsoperation.cpp
index 6aa479611d1..ef482a19ca3 100644
--- a/searchcore/src/vespa/searchcore/proton/feedoperation/removedocumentsoperation.cpp
+++ b/searchcore/src/vespa/searchcore/proton/feedoperation/removedocumentsoperation.cpp
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "removedocumentsoperation.h"
+#include <vespa/vespalib/objects/nbostream.h>
namespace proton {
@@ -16,11 +17,9 @@ RemoveDocumentsOperation::serializeLidsToRemove(vespalib::nbostream &os) const
{
uint32_t mapSize = _lidsToRemoveMap.size();
os << mapSize;
- for (LidsToRemoveMap::const_iterator
- it = _lidsToRemoveMap.begin(), ite = _lidsToRemoveMap.end();
- it != ite; ++it) {
- os << it->first;
- it->second->serialize(os);
+ for (const auto & entry : _lidsToRemoveMap) {
+ os << entry.first;
+ entry.second->serialize(os);
}
}
diff --git a/searchcore/src/vespa/searchcore/proton/feedoperation/spoolerreplayoperation.cpp b/searchcore/src/vespa/searchcore/proton/feedoperation/spoolerreplayoperation.cpp
index f3ef73f4d90..16ddedc4745 100644
--- a/searchcore/src/vespa/searchcore/proton/feedoperation/spoolerreplayoperation.cpp
+++ b/searchcore/src/vespa/searchcore/proton/feedoperation/spoolerreplayoperation.cpp
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "spoolerreplayoperation.h"
+#include <vespa/vespalib/objects/nbostream.h>
#include <vespa/vespalib/util/stringfmt.h>
using vespalib::make_string;
@@ -14,10 +15,7 @@ SpoolerReplayOperation::SpoolerReplayOperation(Type type)
{
}
-
-SpoolerReplayOperation::SpoolerReplayOperation(Type type,
- SerialNum serialNum,
- SerialNum spoolerSerialNum)
+SpoolerReplayOperation::SpoolerReplayOperation(Type type, SerialNum serialNum, SerialNum spoolerSerialNum)
: FeedOperation(type),
_spoolerSerialNum(spoolerSerialNum)
{
@@ -39,11 +37,8 @@ SpoolerReplayOperation::deserialize(vespalib::nbostream &is)
}
vespalib::string SpoolerReplayOperation::toString() const {
- return make_string(
- "SpoolerReplay%s(spoolerSerialNum=%" PRIu64
- ", serialNum=%" PRIu64 ")",
- getType() == SPOOLER_REPLAY_START ? "Start" : "Complete",
- _spoolerSerialNum, getSerialNum());
+ return make_string("SpoolerReplay%s(spoolerSerialNum=%" PRIu64", serialNum=%" PRIu64 ")",
+ getType() == SPOOLER_REPLAY_START ? "Start" : "Complete", _spoolerSerialNum, getSerialNum());
}
@@ -53,8 +48,7 @@ SpoolerReplayStartOperation::SpoolerReplayStartOperation()
}
-SpoolerReplayStartOperation::SpoolerReplayStartOperation(SerialNum serialNum,
- SerialNum spoolerSerialNum)
+SpoolerReplayStartOperation::SpoolerReplayStartOperation(SerialNum serialNum, SerialNum spoolerSerialNum)
: SpoolerReplayOperation(FeedOperation::SPOOLER_REPLAY_START,
serialNum,
spoolerSerialNum)
@@ -70,12 +64,8 @@ SpoolerReplayCompleteOperation::SpoolerReplayCompleteOperation()
SpoolerReplayCompleteOperation::SpoolerReplayCompleteOperation(SerialNum serialNum,
SerialNum spoolerSerialNum)
- : SpoolerReplayOperation(FeedOperation::SPOOLER_REPLAY_COMPLETE,
- serialNum,
- spoolerSerialNum)
+ : SpoolerReplayOperation(FeedOperation::SPOOLER_REPLAY_COMPLETE, serialNum, spoolerSerialNum)
{
}
-
-
} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/feedoperation/spoolerreplayoperation.h b/searchcore/src/vespa/searchcore/proton/feedoperation/spoolerreplayoperation.h
index 028d01dec20..028ad1c6bfa 100644
--- a/searchcore/src/vespa/searchcore/proton/feedoperation/spoolerreplayoperation.h
+++ b/searchcore/src/vespa/searchcore/proton/feedoperation/spoolerreplayoperation.h
@@ -11,15 +11,12 @@ private:
SerialNum _spoolerSerialNum;
protected:
SpoolerReplayOperation(Type type);
- SpoolerReplayOperation(Type type,
- SerialNum serialNum,
- SerialNum spoolerSerialNum);
+ SpoolerReplayOperation(Type type, SerialNum serialNum, SerialNum spoolerSerialNum);
public:
- virtual ~SpoolerReplayOperation() {}
+ ~SpoolerReplayOperation() override {}
SerialNum getSpoolerSerialNum() const { return _spoolerSerialNum; }
- virtual void serialize(vespalib::nbostream &os) const override;
- virtual void deserialize(vespalib::nbostream &is,
- const document::DocumentTypeRepo &) override {
+ void serialize(vespalib::nbostream &os) const override;
+ void deserialize(vespalib::nbostream &is, const document::DocumentTypeRepo &) override {
deserialize(is);
}
void deserialize(vespalib::nbostream &is);
@@ -38,8 +35,7 @@ public:
* @param serialNum the current serial number of the transaction log.
* @param spoolerSerialNum the serial number of the first entry of the spooler log replay.
*/
- SpoolerReplayStartOperation(SerialNum serialNum,
- SerialNum spoolerSerialNum);
+ SpoolerReplayStartOperation(SerialNum serialNum, SerialNum spoolerSerialNum);
};
@@ -54,8 +50,7 @@ public:
* @param serialNum the current serial number of the transaction log.
* @param spoolerSerialNum the serial number of the last entry of the spooler log replay.
*/
- SpoolerReplayCompleteOperation(SerialNum serialNum,
- SerialNum spoolerSerialNum);
+ SpoolerReplayCompleteOperation(SerialNum serialNum, SerialNum spoolerSerialNum);
};
} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/feedoperation/updateoperation.cpp b/searchcore/src/vespa/searchcore/proton/feedoperation/updateoperation.cpp
index d31f1faec77..dc83848152c 100644
--- a/searchcore/src/vespa/searchcore/proton/feedoperation/updateoperation.cpp
+++ b/searchcore/src/vespa/searchcore/proton/feedoperation/updateoperation.cpp
@@ -51,12 +51,11 @@ UpdateOperation::serializeUpdate(vespalib::nbostream &os) const
}
void
-UpdateOperation::deserializeUpdate(vespalib::nbostream &is, const document::DocumentTypeRepo &repo)
+UpdateOperation::deserializeUpdate(vespalib::nbostream && is, const document::DocumentTypeRepo &repo)
{
- document::ByteBuffer buf(is.peek(), is.size());
- DocumentUpdate::UP update = (getType() == UPDATE_42) ? DocumentUpdate::create42(repo, buf) : DocumentUpdate::createHEAD(repo, buf);
- is.adjustReadPos(buf.getPos());
- _upd = std::move(update);
+ _upd = (getType() == UPDATE_42)
+ ? DocumentUpdate::create42(repo, is)
+ : DocumentUpdate::createHEAD(repo, std::move(is));
}
void
@@ -73,7 +72,7 @@ UpdateOperation::deserialize(vespalib::nbostream &is, const DocumentTypeRepo &re
{
DocumentOperation::deserialize(is, repo);
try {
- deserializeUpdate(is, repo);
+ deserializeUpdate(std::move(is), repo);
} catch (document::DocumentTypeNotFoundException &e) {
LOG(warning, "Failed deserialize update operation using unknown document type '%s'",
e.getDocumentTypeName().c_str());
@@ -83,18 +82,19 @@ UpdateOperation::deserialize(vespalib::nbostream &is, const DocumentTypeRepo &re
}
void
-UpdateOperation::deserializeUpdate(const DocumentTypeRepo &repo)
+UpdateOperation::verifyUpdate(const DocumentTypeRepo &repo)
{
vespalib::nbostream stream;
serializeUpdate(stream);
- deserializeUpdate(stream, repo);
+ deserializeUpdate(std::move(stream), repo);
+ _upd->eagerDeserialize(); // Will trigger exceptions if incompatible
}
-vespalib::string UpdateOperation::toString() const {
+vespalib::string
+UpdateOperation::toString() const {
return make_string("%s(%s, %s)",
((getType() == FeedOperation::UPDATE_42) ? "Update42" : "Update"),
- _upd.get() ?
- _upd->getId().getScheme().toString().c_str() : "NULL",
+ _upd.get() ? _upd->getId().getScheme().toString().c_str() : "NULL",
docArgsToString().c_str());
}
diff --git a/searchcore/src/vespa/searchcore/proton/feedoperation/updateoperation.h b/searchcore/src/vespa/searchcore/proton/feedoperation/updateoperation.h
index 99dcbfbce6c..83e87fea096 100644
--- a/searchcore/src/vespa/searchcore/proton/feedoperation/updateoperation.h
+++ b/searchcore/src/vespa/searchcore/proton/feedoperation/updateoperation.h
@@ -19,7 +19,7 @@ private:
const storage::spi::Timestamp &timestamp,
const DocumentUpdateSP &upd);
void serializeUpdate(vespalib::nbostream &os) const;
- void deserializeUpdate(vespalib::nbostream &is, const document::DocumentTypeRepo &repo);
+ void deserializeUpdate(vespalib::nbostream && is, const document::DocumentTypeRepo &repo);
public:
UpdateOperation();
UpdateOperation(Type type);
@@ -30,7 +30,7 @@ public:
const DocumentUpdateSP &getUpdate() const { return _upd; }
void serialize(vespalib::nbostream &os) const override;
void deserialize(vespalib::nbostream &is, const document::DocumentTypeRepo &repo) override;
- void deserializeUpdate(const document::DocumentTypeRepo &repo);
+ void verifyUpdate(const document::DocumentTypeRepo &repo);
vespalib::string toString() const override;
};
diff --git a/searchcore/src/vespa/searchcore/proton/feedoperation/wipehistoryoperation.cpp b/searchcore/src/vespa/searchcore/proton/feedoperation/wipehistoryoperation.cpp
index d7f38a0cc52..c5e6acd80ed 100644
--- a/searchcore/src/vespa/searchcore/proton/feedoperation/wipehistoryoperation.cpp
+++ b/searchcore/src/vespa/searchcore/proton/feedoperation/wipehistoryoperation.cpp
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "wipehistoryoperation.h"
+#include <vespa/vespalib/objects/nbostream.h>
#include <vespa/vespalib/util/stringfmt.h>
using vespalib::make_string;
@@ -9,29 +10,29 @@ namespace proton {
WipeHistoryOperation::WipeHistoryOperation()
: FeedOperation(FeedOperation::WIPE_HISTORY),
- _wipeTimeLimit(0) {
+ _wipeTimeLimit(0)
+{
}
WipeHistoryOperation::WipeHistoryOperation(SerialNum serialNum,
fastos::TimeStamp wipeTimeLimit)
: FeedOperation(FeedOperation::WIPE_HISTORY),
- _wipeTimeLimit(wipeTimeLimit) {
+ _wipeTimeLimit(wipeTimeLimit)
+{
setSerialNum(serialNum);
}
void WipeHistoryOperation::serialize(vespalib::nbostream &str) const {
str << _wipeTimeLimit;
}
-void WipeHistoryOperation::deserialize(vespalib::nbostream &str,
- const document::DocumentTypeRepo &) {
+void WipeHistoryOperation::deserialize(vespalib::nbostream &str, const document::DocumentTypeRepo &) {
fastos::TimeStamp::TimeT t;
str >> t;
_wipeTimeLimit = t;
}
vespalib::string WipeHistoryOperation::toString() const {
- return make_string("WipeHistory(wipeTimeLimit=%" PRIu64
- ", serialNum=%" PRIu64 ")",
+ return make_string("WipeHistory(wipeTimeLimit=%" PRIu64 ", serialNum=%" PRIu64 ")",
_wipeTimeLimit.ns(), getSerialNum());
}
diff --git a/searchcore/src/vespa/searchcore/proton/feedoperation/wipehistoryoperation.h b/searchcore/src/vespa/searchcore/proton/feedoperation/wipehistoryoperation.h
index 80b551dbd92..0cf7256bf27 100644
--- a/searchcore/src/vespa/searchcore/proton/feedoperation/wipehistoryoperation.h
+++ b/searchcore/src/vespa/searchcore/proton/feedoperation/wipehistoryoperation.h
@@ -12,14 +12,13 @@ class WipeHistoryOperation : public FeedOperation {
public:
WipeHistoryOperation();
WipeHistoryOperation(SerialNum serialNum, fastos::TimeStamp wipeTimeLimit);
- virtual ~WipeHistoryOperation() {}
+ ~WipeHistoryOperation() override {}
fastos::TimeStamp getWipeTimeLimit() const { return _wipeTimeLimit; }
- virtual void serialize(vespalib::nbostream &str) const override;
- virtual void deserialize(vespalib::nbostream &str,
- const document::DocumentTypeRepo &) override;
- virtual vespalib::string toString() const override;
+ void serialize(vespalib::nbostream &str) const override;
+ void deserialize(vespalib::nbostream &str, const document::DocumentTypeRepo &) override;
+ vespalib::string toString() const override;
};
} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/flushengine/flushengine.cpp b/searchcore/src/vespa/searchcore/proton/flushengine/flushengine.cpp
index 95b3008985b..0d2c556b4d6 100644
--- a/searchcore/src/vespa/searchcore/proton/flushengine/flushengine.cpp
+++ b/searchcore/src/vespa/searchcore/proton/flushengine/flushengine.cpp
@@ -8,6 +8,7 @@
#include "tls_stats_factory.h"
#include <vespa/searchcore/proton/common/eventlogger.h>
#include <vespa/vespalib/util/jsonwriter.h>
+#include <thread>
#include <vespa/log/log.h>
LOG_SETUP(".proton.flushengine.flushengine");
@@ -22,8 +23,7 @@ namespace proton {
namespace {
search::SerialNum
-findOldestFlushedSerial(const IFlushTarget::List &lst,
- const IFlushHandler &handler)
+findOldestFlushedSerial(const IFlushTarget::List &lst, const IFlushHandler &handler)
{
search::SerialNum ret(handler.getCurrentSerialNumber());
for (const IFlushTarget::SP & target : lst) {
@@ -33,42 +33,46 @@ findOldestFlushedSerial(const IFlushTarget::List &lst,
return ret;
}
+void
+logTarget(const char * text, const FlushContext & ctx) {
+ LOG(debug, "Target '%s' %s flush of transactions %" PRIu64 " through %" PRIu64 ".",
+ ctx.getName().c_str(), text,
+ ctx.getTarget()->getFlushedSerialNum() + 1,
+ ctx.getHandler()->getCurrentSerialNumber());
+}
+
}
-FlushEngine::FlushMeta::FlushMeta(const vespalib::string & name, fastos::TimeStamp start, uint32_t id) :
- _name(name),
- _start(start),
- _id(id)
+FlushEngine::FlushMeta::FlushMeta(const vespalib::string & name, fastos::TimeStamp start, uint32_t id)
+ : _name(name),
+ _start(start),
+ _id(id)
{ }
-FlushEngine::FlushMeta::~FlushMeta() { }
+FlushEngine::FlushMeta::~FlushMeta() = default;
-FlushEngine::FlushInfo::FlushInfo() :
- FlushMeta("", fastos::ClockSystem::now(), 0),
- _target()
+FlushEngine::FlushInfo::FlushInfo()
+ : FlushMeta("", fastos::ClockSystem::now(), 0),
+ _target()
{
}
-FlushEngine::FlushInfo::~FlushInfo() { }
+FlushEngine::FlushInfo::~FlushInfo() = default;
-FlushEngine::FlushInfo::FlushInfo(uint32_t taskId,
- const IFlushTarget::SP &target,
- const vespalib::string & destination) :
- FlushMeta(destination, fastos::ClockSystem::now(), taskId),
- _target(target)
+FlushEngine::FlushInfo::FlushInfo(uint32_t taskId, const IFlushTarget::SP &target, const vespalib::string & destination)
+ : FlushMeta(destination, fastos::ClockSystem::now(), taskId),
+ _target(target)
{
}
-FlushEngine::FlushEngine(std::shared_ptr<flushengine::ITlsStatsFactory>
- tlsStatsFactory,
- IFlushStrategy::SP strategy, uint32_t numThreads,
- uint32_t idleIntervalMS)
+FlushEngine::FlushEngine(std::shared_ptr<flushengine::ITlsStatsFactory> tlsStatsFactory,
+ IFlushStrategy::SP strategy, uint32_t numThreads, uint32_t idleIntervalMS)
: _closed(false),
_maxConcurrent(numThreads),
_idleIntervalMS(idleIntervalMS),
_taskId(0),
_threadPool(128 * 1024),
- _strategy(strategy),
+ _strategy(std::move(strategy)),
_priorityStrategy(),
_executor(numThreads, 128 * 1024),
_lock(),
@@ -78,11 +82,9 @@ FlushEngine::FlushEngine(std::shared_ptr<flushengine::ITlsStatsFactory>
_setStrategyLock(),
_strategyLock(),
_strategyCond(),
- _tlsStatsFactory(tlsStatsFactory),
+ _tlsStatsFactory(std::move(tlsStatsFactory)),
_pendingPrune()
-{
- // empty
-}
+{ }
FlushEngine::~FlushEngine()
{
@@ -92,7 +94,7 @@ FlushEngine::~FlushEngine()
FlushEngine &
FlushEngine::start()
{
- if (_threadPool.NewThread(this) == NULL) {
+ if (_threadPool.NewThread(this) == nullptr) {
throw vespalib::IllegalStateException("Failed to start engine thread.");
}
return *this;
@@ -148,10 +150,8 @@ FlushEngine::wait(size_t minimumWaitTimeIfReady)
}
void
-FlushEngine::Run(FastOS_ThreadInterface *thread, void *arg)
+FlushEngine::Run(FastOS_ThreadInterface *, void *)
{
- (void)thread;
- (void)arg;
bool shouldIdle = false;
vespalib::string prevFlushName;
while (wait(shouldIdle ? _idleIntervalMS : 0)) {
@@ -161,13 +161,14 @@ FlushEngine::Run(FastOS_ThreadInterface *thread, void *arg)
}
prevFlushName = flushNextTarget(prevFlushName);
if ( ! prevFlushName.empty()) {
- // Sleep at least 10 ms after a successful flush in order to avoid busy loop in case
- // of strategy error or target error.
- FastOS_Thread::Sleep(10);
+ // Sleep 1 ms after a successful flush in order to avoid busy loop in case
+ // of strategy or target error.
+ std::this_thread::sleep_for(1ms);
} else {
shouldIdle = true;
}
- LOG(debug, "Making another wait(idle=%s, timeMS=%d) last was '%s'", shouldIdle ? "true" : "false", shouldIdle ? _idleIntervalMS : 0, prevFlushName.c_str());
+ LOG(debug, "Making another wait(idle=%s, timeMS=%d) last was '%s'",
+ shouldIdle ? "true" : "false", shouldIdle ? _idleIntervalMS : 0, prevFlushName.c_str());
}
_executor.sync();
prune();
@@ -211,18 +212,16 @@ FlushEngine::getTargetList(bool includeFlushingTargets) const
for (const auto & it : _handlers) {
IFlushHandler & handler(*it.second);
search::SerialNum serial(handler.getCurrentSerialNumber());
- LOG(spam, "Checking FlushHandler '%s' current serial = %ld",
- handler.getName().c_str(), serial);
+ LOG(spam, "Checking FlushHandler '%s' current serial = %ld", handler.getName().c_str(), serial);
IFlushTarget::List lst = handler.getFlushTargets();
for (const IFlushTarget::SP & target : lst) {
- LOG(spam, "Checking target '%s' with flushedSerialNum = %ld", target->getName().c_str(), target->getFlushedSerialNum());
+ LOG(spam, "Checking target '%s' with flushedSerialNum = %ld",
+ target->getName().c_str(), target->getFlushedSerialNum());
if (!isFlushing(guard, FlushContext::createName(handler, *target)) || includeFlushingTargets) {
- ret.push_back(FlushContext::SP(new FlushContext(it.second,
- IFlushTarget::SP(new CachedFlushTarget(target)),
- serial)));
+ ret.push_back(std::make_shared<FlushContext>(it.second, std::make_shared<CachedFlushTarget>(target), serial));
} else {
LOG(debug, "Target '%s' with flushedSerialNum = %ld already has a flush going. Local last serial = %ld.",
- target->getName().c_str(), target->getFlushedSerialNum(), serial);
+ target->getName().c_str(), target->getFlushedSerialNum(), serial);
}
}
}
@@ -258,17 +257,12 @@ FlushEngine::initNextFlush(const FlushContext::List &lst)
break;
}
}
- if (ctx.get() != NULL) {
- LOG(debug, "Target '%s' initiated flush of transactions %" PRIu64 " through %" PRIu64 ".",
- ctx->getName().c_str(),
- ctx->getTarget()->getFlushedSerialNum() + 1,
- ctx->getHandler()->getCurrentSerialNumber());
+ if (ctx) {
+ logTarget("initiated", *ctx);
}
return ctx;
}
-
-
void
FlushEngine::flushAll(const FlushContext::List &lst)
{
@@ -276,19 +270,12 @@ FlushEngine::flushAll(const FlushContext::List &lst)
for (const FlushContext::SP & ctx : lst) {
if (wait(0)) {
if (ctx->initFlush()) {
- LOG(debug, "Target '%s' initiated flush of transactions %" PRIu64 " through %" PRIu64 ".",
- ctx->getName().c_str(),
- ctx->getTarget()->getFlushedSerialNum() + 1,
- ctx->getHandler()->getCurrentSerialNumber());
- _executor.execute(Task::UP(new FlushTask(initFlush(*ctx), *this, ctx)));
+ logTarget("initiated", *ctx);
+ _executor.execute(std::make_unique<FlushTask>(initFlush(*ctx), *this, ctx));
} else {
- LOG(debug, "Target '%s' failed to initiate flush of transactions %" PRIu64 " through %" PRIu64 ".",
- ctx->getName().c_str(),
- ctx->getTarget()->getFlushedSerialNum() + 1,
- ctx->getHandler()->getCurrentSerialNumber());
+ logTarget("failed to initiate", *ctx);
}
}
-
}
}
@@ -311,17 +298,17 @@ FlushEngine::flushNextTarget(const vespalib::string & name)
return "";
}
FlushContext::SP ctx = initNextFlush(lst.first);
- if (ctx.get() == NULL) {
+ if ( ! ctx) {
LOG(debug, "All targets refused to flush.");
return "";
}
if ( name == ctx->getName()) {
LOG(info, "The same target %s out of %ld has been asked to flush again. "
- "This might indicate flush logic flaw so I will wait 1s before doing it.",
+ "This might indicate flush logic flaw so I will wait 100 ms before doing it.",
name.c_str(), lst.first.size());
- FastOS_Thread::Sleep(1000);
+ std::this_thread::sleep_for(100ms);
}
- _executor.execute(Task::UP(new FlushTask(initFlush(*ctx), *this, ctx)));
+ _executor.execute(std::make_unique<FlushTask>(initFlush(*ctx), *this, ctx));
return ctx->getName();
}
@@ -330,12 +317,8 @@ FlushEngine::initFlush(const FlushContext &ctx)
{
if (LOG_WOULD_LOG(event)) {
IFlushTarget::MemoryGain mgain(ctx.getTarget()->getApproxMemoryGain());
- EventLogger::flushStart(ctx.getName(),
- mgain.getBefore(),
- mgain.getAfter(),
- mgain.gain(),
- ctx.getTarget()->getFlushedSerialNum() + 1,
- ctx.getHandler()->getCurrentSerialNumber());
+ EventLogger::flushStart(ctx.getName(), mgain.getBefore(), mgain.getAfter(), mgain.gain(),
+ ctx.getTarget()->getFlushedSerialNum() + 1, ctx.getHandler()->getCurrentSerialNumber());
}
return initFlush(ctx.getHandler(), ctx.getTarget());
}
@@ -350,10 +333,7 @@ FlushEngine::flushDone(const FlushContext &ctx, uint32_t taskId)
}
if (LOG_WOULD_LOG(event)) {
FlushStats stats = ctx.getTarget()->getLastFlushStats();
- EventLogger::flushComplete(ctx.getName(),
- duration.ms(),
- stats.getPath(),
- stats.getPathElementsToLog());
+ EventLogger::flushComplete(ctx.getName(), duration.ms(), stats.getPath(), stats.getPathElementsToLog());
}
LOG(debug, "FlushEngine::flushDone(taskId='%d') took '%f' secs", taskId, duration.sec());
std::lock_guard<std::mutex> guard(_lock);
@@ -366,8 +346,7 @@ FlushEngine::flushDone(const FlushContext &ctx, uint32_t taskId)
}
IFlushHandler::SP
-FlushEngine::putFlushHandler(const DocTypeName &docTypeName,
- const IFlushHandler::SP &flushHandler)
+FlushEngine::putFlushHandler(const DocTypeName &docTypeName, const IFlushHandler::SP &flushHandler)
{
std::lock_guard<std::mutex> guard(_lock);
IFlushHandler::SP result(_handlers.putHandler(docTypeName, flushHandler));
@@ -379,13 +358,6 @@ FlushEngine::putFlushHandler(const DocTypeName &docTypeName,
}
IFlushHandler::SP
-FlushEngine::getFlushHandler(const DocTypeName &docTypeName) const
-{
- std::lock_guard<std::mutex> guard(_lock);
- return _handlers.getHandler(docTypeName);
-}
-
-IFlushHandler::SP
FlushEngine::removeFlushHandler(const DocTypeName &docTypeName)
{
std::lock_guard<std::mutex> guard(_lock);
@@ -430,7 +402,7 @@ FlushEngine::setStrategy(IFlushStrategy::SP strategy)
return;
}
assert(!_priorityStrategy);
- _priorityStrategy = strategy;
+ _priorityStrategy = std::move(strategy);
{
std::lock_guard<std::mutex> guard(_lock);
_cond.notify_all();
diff --git a/searchcore/src/vespa/searchcore/proton/flushengine/flushengine.h b/searchcore/src/vespa/searchcore/proton/flushengine/flushengine.h
index 19175f9ce2a..c1be05ba067 100644
--- a/searchcore/src/vespa/searchcore/proton/flushengine/flushengine.h
+++ b/searchcore/src/vespa/searchcore/proton/flushengine/flushengine.h
@@ -15,7 +15,7 @@ namespace proton {
namespace flushengine { class ITlsStatsFactory; }
-class FlushEngine : public FastOS_Runnable
+class FlushEngine final : public FastOS_Runnable
{
public:
class FlushMeta {
@@ -37,9 +37,7 @@ private:
struct FlushInfo : public FlushMeta
{
FlushInfo();
- FlushInfo(uint32_t taskId,
- const IFlushTarget::SP &target,
- const vespalib::string &destination);
+ FlushInfo(uint32_t taskId, const IFlushTarget::SP &target, const vespalib::string &destination);
~FlushInfo();
IFlushTarget::SP _target;
@@ -96,14 +94,13 @@ public:
* @param numThreads The number of worker threads to use.
* @param idleInterval The interval between when flushes are checked whne there are no one progressing.
*/
- FlushEngine(std::shared_ptr<flushengine::ITlsStatsFactory>
- tlsStatsFactory,
+ FlushEngine(std::shared_ptr<flushengine::ITlsStatsFactory> tlsStatsFactory,
IFlushStrategy::SP strategy, uint32_t numThreads, uint32_t idleIntervalMS);
/**
* Destructor. Waits for all pending tasks to complete.
*/
- ~FlushEngine();
+ ~FlushEngine() override;
/**
* Observe and reset internal executor stats
@@ -145,19 +142,8 @@ public:
* @param flushHandler The handler to register.
* @return The replaced handler, if any.
*/
- IFlushHandler::SP
- putFlushHandler(const DocTypeName &docTypeName,
- const IFlushHandler::SP &flushHandler);
+ IFlushHandler::SP putFlushHandler(const DocTypeName &docTypeName, const IFlushHandler::SP &flushHandler);
- /**
- * Returns the flush handler for the given document type. If no handler was
- * registered, this method returns an empty shared pointer.
- *
- * @param docType The document type whose handler to return.
- * @return The registered handler, if any.
- */
- IFlushHandler::SP
- getFlushHandler(const DocTypeName &docTypeName) const;
/**
* Removes and returns the flush handler for the given document type. If no
@@ -166,10 +152,8 @@ public:
* @param docType The document type whose handler to remove.
* @return The removed handler, if any.
*/
- IFlushHandler::SP
- removeFlushHandler(const DocTypeName &docTypeName);
+ IFlushHandler::SP removeFlushHandler(const DocTypeName &docTypeName);
- // Implements FastOS_Runnable.
void Run(FastOS_ThreadInterface *thread, void *arg) override;
FlushMetaSet getCurrentlyFlushingSet() const;
diff --git a/searchcore/src/vespa/searchcore/proton/flushengine/iflushhandler.h b/searchcore/src/vespa/searchcore/proton/flushengine/iflushhandler.h
index 3a24330f8ec..914de9df30c 100644
--- a/searchcore/src/vespa/searchcore/proton/flushengine/iflushhandler.h
+++ b/searchcore/src/vespa/searchcore/proton/flushengine/iflushhandler.h
@@ -30,9 +30,7 @@ public:
*/
IFlushHandler(const vespalib::string &name)
: _name(name)
- {
- // empty
- }
+ { }
/**
* Virtual destructor required for inheritance.
@@ -76,8 +74,7 @@ public:
* This method is called to sync tls to stable media, up to and
* including the given serial number.
*
- * @param syncTo The last serial number that has to be persisted to stable
- * media.
+ * @param syncTo The last serial number that has to be persisted to stable media.
*/
virtual void syncTls(SerialNum syncTo) = 0;
};
diff --git a/searchcore/src/vespa/searchcore/proton/flushengine/tls_stats_map.cpp b/searchcore/src/vespa/searchcore/proton/flushengine/tls_stats_map.cpp
index 088e202c027..c9ba495aa82 100644
--- a/searchcore/src/vespa/searchcore/proton/flushengine/tls_stats_map.cpp
+++ b/searchcore/src/vespa/searchcore/proton/flushengine/tls_stats_map.cpp
@@ -2,6 +2,8 @@
#include "tls_stats_map.h"
#include <vespa/vespalib/stllike/hash_map.hpp>
+#include <vespa/log/log.h>
+LOG_SETUP(".proton.flushengine.tls_stats_map");
namespace proton {
namespace flushengine {
@@ -18,7 +20,7 @@ TlsStatsMap::getTlsStats(const vespalib::string &domain) const {
if (itr != _map.end()) {
return itr->second;
}
- abort();
+ LOG_ABORT("should not be reached");
}
}
diff --git a/searchcore/src/vespa/searchcore/proton/matching/i_match_loop_communicator.h b/searchcore/src/vespa/searchcore/proton/matching/i_match_loop_communicator.h
index 04440831045..c22232d47db 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/i_match_loop_communicator.h
+++ b/searchcore/src/vespa/searchcore/proton/matching/i_match_loop_communicator.h
@@ -2,14 +2,12 @@
#pragma once
-#include <vespa/searchlib/common/feature.h>
#include <vespa/searchlib/queryeval/scores.h>
#include <utility>
#include <cstddef>
#include <vector>
-namespace proton {
-namespace matching {
+namespace proton::matching {
struct IMatchLoopCommunicator {
typedef search::feature_t feature_t;
@@ -31,6 +29,4 @@ struct IMatchLoopCommunicator {
virtual ~IMatchLoopCommunicator() {}
};
-} // namespace matching
-} // namespace proton
-
+}
diff --git a/searchcore/src/vespa/searchcore/proton/matching/match_loop_communicator.cpp b/searchcore/src/vespa/searchcore/proton/matching/match_loop_communicator.cpp
index fa41c73838b..95148ef56e8 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/match_loop_communicator.cpp
+++ b/searchcore/src/vespa/searchcore/proton/matching/match_loop_communicator.cpp
@@ -3,15 +3,14 @@
#include "match_loop_communicator.h"
#include <vespa/vespalib/util/priority_queue.h>
-namespace proton {
-namespace matching {
+namespace proton:: matching {
MatchLoopCommunicator::MatchLoopCommunicator(size_t threads, size_t topN)
: _estimate_match_frequency(threads),
_selectBest(threads, topN),
_rangeCover(threads)
{}
-MatchLoopCommunicator::~MatchLoopCommunicator() {}
+MatchLoopCommunicator::~MatchLoopCommunicator() = default;
void
MatchLoopCommunicator::EstimateMatchFrequency::mingle()
@@ -72,5 +71,4 @@ MatchLoopCommunicator::RangeCover::mingle()
}
}
-} // namespace matching
-} // namespace proton
+}
diff --git a/searchcore/src/vespa/searchcore/proton/matching/match_loop_communicator.h b/searchcore/src/vespa/searchcore/proton/matching/match_loop_communicator.h
index 3b83cb471f7..c1eec37299f 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/match_loop_communicator.h
+++ b/searchcore/src/vespa/searchcore/proton/matching/match_loop_communicator.h
@@ -5,8 +5,7 @@
#include "i_match_loop_communicator.h"
#include <vespa/vespalib/util/rendezvous.h>
-namespace proton {
-namespace matching {
+namespace proton::matching {
class MatchLoopCommunicator : public IMatchLoopCommunicator
{
@@ -14,13 +13,13 @@ private:
struct EstimateMatchFrequency : vespalib::Rendezvous<Matches, double> {
EstimateMatchFrequency(size_t n)
: vespalib::Rendezvous<Matches, double>(n) {}
- virtual void mingle() override;
+ void mingle() override;
};
struct SelectBest : vespalib::Rendezvous<std::vector<feature_t>, size_t> {
size_t topN;
SelectBest(size_t n, size_t topN_in)
: vespalib::Rendezvous<std::vector<feature_t>, size_t>(n), topN(topN_in) {}
- virtual void mingle() override;
+ void mingle() override;
bool cmp(const uint32_t &a, const uint32_t &b) {
return (in(a)[out(a)] > in(b)[out(b)]);
}
@@ -34,8 +33,7 @@ private:
};
struct RangeCover : vespalib::Rendezvous<RangePair, RangePair> {
RangeCover(size_t n)
- : vespalib::Rendezvous<RangePair, RangePair>(n) {}
- virtual void mingle() override;
+ : vespalib::Rendezvous<RangePair, RangePair>(n) {}void mingle() override;
};
EstimateMatchFrequency _estimate_match_frequency;
SelectBest _selectBest;
@@ -45,17 +43,15 @@ public:
MatchLoopCommunicator(size_t threads, size_t topN);
~MatchLoopCommunicator();
- virtual double estimate_match_frequency(const Matches &matches) override {
+ double estimate_match_frequency(const Matches &matches) override {
return _estimate_match_frequency.rendezvous(matches);
}
- virtual size_t selectBest(const std::vector<feature_t> &sortedScores) override {
+ size_t selectBest(const std::vector<feature_t> &sortedScores) override {
return _selectBest.rendezvous(sortedScores);
}
- virtual RangePair rangeCover(const RangePair &ranges) override {
+ RangePair rangeCover(const RangePair &ranges) override {
return _rangeCover.rendezvous(ranges);
}
};
-} // namespace matching
-} // namespace proton
-
+}
diff --git a/searchcore/src/vespa/searchcore/proton/matching/match_master.cpp b/searchcore/src/vespa/searchcore/proton/matching/match_master.cpp
index 370c4b930e1..0eb49aec754 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/match_master.cpp
+++ b/searchcore/src/vespa/searchcore/proton/matching/match_master.cpp
@@ -10,8 +10,7 @@
#include <vespa/log/log.h>
LOG_SETUP(".proton.matching.match_master");
-namespace proton {
-namespace matching {
+namespace proton::matching {
using namespace search::fef;
using search::queryeval::SearchIterator;
@@ -23,15 +22,15 @@ struct TimedMatchLoopCommunicator : IMatchLoopCommunicator {
IMatchLoopCommunicator &communicator;
fastos::StopWatch rerank_time;
TimedMatchLoopCommunicator(IMatchLoopCommunicator &com) : communicator(com) {}
- virtual double estimate_match_frequency(const Matches &matches) override {
+ double estimate_match_frequency(const Matches &matches) override {
return communicator.estimate_match_frequency(matches);
}
- virtual size_t selectBest(const std::vector<feature_t> &sortedScores) override {
+ size_t selectBest(const std::vector<feature_t> &sortedScores) override {
size_t result = communicator.selectBest(sortedScores);
rerank_time.start();
return result;
}
- virtual RangePair rangeCover(const RangePair &ranges) override {
+ RangePair rangeCover(const RangePair &ranges) override {
RangePair result = communicator.rangeCover(ranges);
rerank_time.stop();
return result;
@@ -131,18 +130,15 @@ MatchMaster::getFeatureSet(const MatchToolsFactory &matchToolsFactory,
if (search.seek(docs[i])) {
uint32_t docId = search.getDocId();
search.unpack(docId);
- search::feature_t * f = fs.getFeaturesByIndex(
- fs.addDocId(docId));
+ search::feature_t * f = fs.getFeaturesByIndex(fs.addDocId(docId));
for (uint32_t j = 0; j < featureNames.size(); ++j) {
f[j] = resolver.resolve(j).as_number(docId);
}
} else {
- LOG(debug, "getFeatureSet: Did not find hit for docid '%u'. "
- "Skipping hit", docs[i]);
+ LOG(debug, "getFeatureSet: Did not find hit for docid '%u'. Skipping hit", docs[i]);
}
}
return retval;
}
-} // namespace proton::matching
-} // namespace proton
+}
diff --git a/searchcore/src/vespa/searchcore/proton/matching/match_master.h b/searchcore/src/vespa/searchcore/proton/matching/match_master.h
index b9980023259..4c6463cf75d 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/match_master.h
+++ b/searchcore/src/vespa/searchcore/proton/matching/match_master.h
@@ -8,8 +8,7 @@
namespace vespalib { class ThreadBundle; }
namespace search { class FeatureSet; }
-namespace proton {
-namespace matching {
+namespace proton::matching {
class MatchToolsFactory;
class MatchParams;
@@ -37,6 +36,4 @@ public:
static MatchingStats getStats(MatchMaster && rhs) { return std::move(rhs._stats); }
};
-} // namespace proton::matching
-} // namespace proton
-
+}
diff --git a/searchcore/src/vespa/searchcore/proton/matching/match_thread.h b/searchcore/src/vespa/searchcore/proton/matching/match_thread.h
index 5c78ee59b1d..8384a45e0e2 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/match_thread.h
+++ b/searchcore/src/vespa/searchcore/proton/matching/match_thread.h
@@ -103,7 +103,7 @@ public:
ResultProcessor &rp,
vespalib::DualMergeDirector &md,
uint32_t distributionKey);
- virtual void run() override;
+ void run() override;
const MatchingStats::Partition &get_thread_stats() const { return thread_stats; }
double get_match_time() const { return match_time_s; }
PartialResult::UP extract_result() { return std::move(resultContext->result); }
diff --git a/searchcore/src/vespa/searchcore/proton/matching/match_tools.cpp b/searchcore/src/vespa/searchcore/proton/matching/match_tools.cpp
index 998daed16c0..e7773c94d72 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/match_tools.cpp
+++ b/searchcore/src/vespa/searchcore/proton/matching/match_tools.cpp
@@ -3,9 +3,8 @@
#include "match_tools.h"
#include "querynodes.h"
#include <vespa/searchlib/parsequery/stackdumpiterator.h>
-
#include <vespa/log/log.h>
-LOG_SETUP(".searchcore.matching.match_tools");
+LOG_SETUP(".proton.matching.match_tools");
#include <vespa/searchlib/query/tree/querytreecreator.h>
using search::attribute::IAttributeContext;
diff --git a/searchcore/src/vespa/searchcore/proton/matching/matcher.cpp b/searchcore/src/vespa/searchcore/proton/matching/matcher.cpp
index ed58fcef977..bce13a96da7 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/matcher.cpp
+++ b/searchcore/src/vespa/searchcore/proton/matching/matcher.cpp
@@ -56,9 +56,10 @@ FeatureSet::SP
findFeatureSet(const DocsumRequest &req, MatchToolsFactory &mtf, bool summaryFeatures)
{
std::vector<uint32_t> docs;
- for (size_t i = 0; i < req.hits.size(); ++i) {
- if (req.hits[i].docid != search::endDocId) {
- docs.push_back(req.hits[i].docid);
+ docs.reserve(req.hits.size());
+ for (const auto & hit : req.hits) {
+ if (hit.docid != search::endDocId) {
+ docs.push_back(hit.docid);
}
}
std::sort(docs.begin(), docs.end());
@@ -102,7 +103,7 @@ Matcher::getFeatureSet(const DocsumRequest & req, ISearchContext & searchCtx, IA
bool searchSessionCached = cache_props.lookup("query").found();
if (searchSessionCached) {
SearchSession::SP session(sessionMgr.pickSearch(sessionId));
- if (session.get()) {
+ if (session) {
MatchToolsFactory &mtf = session->getMatchToolsFactory();
FeatureSet::SP result = findFeatureSet(req, mtf, summaryFeatures);
session->releaseEnumGuards();
@@ -117,7 +118,7 @@ Matcher::getFeatureSet(const DocsumRequest & req, ISearchContext & searchCtx, IA
if (!mtf->valid()) {
LOG(warning, "getFeatureSet(%s): query execution failed (invalid query). Returning empty feature set",
(summaryFeatures ? "summary features" : "rank features"));
- return FeatureSet::SP(new FeatureSet());
+ return std::make_shared<FeatureSet>();
}
return findFeatureSet(req, *mtf, summaryFeatures);
}
@@ -224,14 +225,14 @@ Matcher::match(const SearchRequest &request, vespalib::ThreadBundle &threadBundl
shouldCacheSearchSession = cache_props.lookup("query").found();
if (shouldCacheGroupingSession) {
GroupingSession::UP session(sessionMgr.pickGrouping(sessionId));
- if (session.get()) {
+ if (session) {
return handleGroupingSession(sessionMgr, groupingContext, std::move(session));
}
}
}
const Properties *feature_overrides = &request.propertiesMap.featureOverrides();
if (shouldCacheSearchSession) {
- owned_objects.feature_overrides.reset(new Properties(*feature_overrides));
+ owned_objects.feature_overrides = std::make_unique<Properties>(*feature_overrides);
feature_overrides = owned_objects.feature_overrides.get();
}
MatchToolsFactory::UP mtf = create_match_tools_factory(request, searchContext, attrContext,
diff --git a/searchcore/src/vespa/searchcore/proton/matching/matching_stats.cpp b/searchcore/src/vespa/searchcore/proton/matching/matching_stats.cpp
index 0bc1807d714..4fb0e1d72e2 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/matching_stats.cpp
+++ b/searchcore/src/vespa/searchcore/proton/matching/matching_stats.cpp
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "matching_stats.h"
+#include <cmath>
namespace proton::matching {
@@ -13,6 +14,9 @@ MatchingStats::Partition &get_writable_partition(std::vector<MatchingStats::Part
return state[id];
}
+constexpr double MIN_TIMEOUT_SEC = 0.001;
+constexpr double MAX_CHANGE_FACTOR = 5;
+
} // namespace proton::matching::<unnamed>
MatchingStats::MatchingStats()
@@ -62,6 +66,7 @@ MatchingStats::add(const MatchingStats &rhs)
_docsReRanked += rhs._docsReRanked;
_softDoomed += rhs.softDoomed();
+
_queryCollateralTime.add(rhs._queryCollateralTime);
_queryLatency.add(rhs._queryLatency);
_matchTime.add(rhs._matchTime);
@@ -75,10 +80,19 @@ MatchingStats::add(const MatchingStats &rhs)
MatchingStats &
MatchingStats::updatesoftDoomFactor(double hardLimit, double softLimit, double duration) {
- if (duration < softLimit) {
- _softDoomFactor += 0.01*(softLimit - duration)/hardLimit;
- } else {
- _softDoomFactor += 0.02*(softLimit - duration)/hardLimit;
+ // The safety capping here should normally not be necessary as all input numbers
+ // will normally be within reasonable values.
+ // It is merely a safety measure to avoid overflow on bad input as can happen with time senstive stuff
+ // in any soft real time system.
+ if ((hardLimit >= MIN_TIMEOUT_SEC) && (softLimit >= MIN_TIMEOUT_SEC)) {
+ double diff = (softLimit - duration)/hardLimit;
+ if (duration < softLimit) {
+ diff = std::min(diff, _softDoomFactor*MAX_CHANGE_FACTOR);
+ _softDoomFactor += 0.01*diff;
+ } else {
+ diff = std::max(diff, -_softDoomFactor*MAX_CHANGE_FACTOR);
+ _softDoomFactor += 0.02*diff;
+ }
}
return *this;
}
diff --git a/searchcore/src/vespa/searchcore/proton/matching/query.cpp b/searchcore/src/vespa/searchcore/proton/matching/query.cpp
index 38affde6075..e550ad8cad7 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/query.cpp
+++ b/searchcore/src/vespa/searchcore/proton/matching/query.cpp
@@ -28,6 +28,9 @@ using search::query::Node;
using search::query::QueryTreeCreator;
using search::query::Weight;
using search::queryeval::AndBlueprint;
+using search::queryeval::AndNotBlueprint;
+using search::queryeval::RankBlueprint;
+using search::queryeval::IntermediateBlueprint;
using search::queryeval::Blueprint;
using search::queryeval::IRequestContext;
using search::queryeval::SearchIterator;
@@ -73,6 +76,27 @@ void AddLocationNode(const string &location_str, Node::UP &query_tree, Location
}
query_tree = std::move(new_base);
}
+
+IntermediateBlueprint *
+asRankOrAndNot(Blueprint * blueprint) {
+ IntermediateBlueprint * rankOrAndNot = dynamic_cast<RankBlueprint*>(blueprint);
+ if (rankOrAndNot == nullptr) {
+ rankOrAndNot = dynamic_cast<AndNotBlueprint*>(blueprint);
+ }
+ return rankOrAndNot;
+}
+
+IntermediateBlueprint *
+lastConsequtiveRankOrAndNot(Blueprint * blueprint) {
+ IntermediateBlueprint * prev = nullptr;
+ IntermediateBlueprint * curr = asRankOrAndNot(blueprint);
+ while (curr != nullptr) {
+ prev = curr;
+ curr = asRankOrAndNot(&curr->getChild(0));
+ }
+ return prev;
+}
+
} // namespace
Query::Query() = default;
@@ -126,10 +150,18 @@ Query::reserveHandles(const IRequestContext & requestContext, ISearchContext &co
LOG(debug, "original blueprint:\n%s\n", _blueprint->asString().c_str());
if (_whiteListBlueprint) {
auto andBlueprint = std::make_unique<AndBlueprint>();
- (*andBlueprint)
- .addChild(std::move(_blueprint))
- .addChild(std::move(_whiteListBlueprint));
- _blueprint = std::move(andBlueprint);
+ IntermediateBlueprint * rankOrAndNot = lastConsequtiveRankOrAndNot(_blueprint.get());
+ if (rankOrAndNot != nullptr) {
+ (*andBlueprint)
+ .addChild(rankOrAndNot->removeChild(0))
+ .addChild(std::move(_whiteListBlueprint));
+ rankOrAndNot->insertChild(0, std::move(andBlueprint));
+ } else {
+ (*andBlueprint)
+ .addChild(std::move(_blueprint))
+ .addChild(std::move(_whiteListBlueprint));
+ _blueprint = std::move(andBlueprint);
+ }
_blueprint->setDocIdLimit(context.getDocIdLimit());
LOG(debug, "blueprint after white listing:\n%s\n", _blueprint->asString().c_str());
}
diff --git a/searchcore/src/vespa/searchcore/proton/matching/query.h b/searchcore/src/vespa/searchcore/proton/matching/query.h
index a3b512fc2b7..21365f75133 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/query.h
+++ b/searchcore/src/vespa/searchcore/proton/matching/query.h
@@ -100,6 +100,8 @@ public:
* @return estimate of hits produced.
*/
Blueprint::HitEstimate estimate() const;
+
+ const Blueprint * peekRoot() const { return _blueprint.get(); }
};
}
diff --git a/searchcore/src/vespa/searchcore/proton/matching/result_processor.cpp b/searchcore/src/vespa/searchcore/proton/matching/result_processor.cpp
index 325803d5aa6..dedda1504a5 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/result_processor.cpp
+++ b/searchcore/src/vespa/searchcore/proton/matching/result_processor.cpp
@@ -24,7 +24,7 @@ ResultProcessor::Result::Result(std::unique_ptr<search::engine::SearchReply> rep
_numFs4Hits(numFs4Hits)
{ }
-ResultProcessor::Result::~Result() { }
+ResultProcessor::Result::~Result() = default;
ResultProcessor::Sort::Sort(uint32_t partitionId, const vespalib::Doom & doom, IAttributeContext &ac, const vespalib::string &ss)
: sorter(FastS_DefaultResultSorter::instance()),
@@ -43,7 +43,7 @@ ResultProcessor::Context::Context(Sort::UP s, PartialResult::UP r, GroupingConte
groupingSource(grouping.get())
{ }
-ResultProcessor::Context::~Context() { }
+ResultProcessor::Context::~Context() = default;
void
ResultProcessor::GroupingSource::merge(Source &s) {
@@ -75,11 +75,11 @@ ResultProcessor::ResultProcessor(IAttributeContext &attrContext,
_wasMerged(false)
{
if (!_groupingContext.empty()) {
- _groupingSession.reset(new GroupingSession(sessionId, _groupingContext, attrContext));
+ _groupingSession = std::make_unique<GroupingSession>(sessionId, _groupingContext, attrContext);
}
}
-ResultProcessor::~ResultProcessor() { }
+ResultProcessor::~ResultProcessor() = default;
void
ResultProcessor::prepareThreadContextCreation(size_t num_threads)
@@ -95,19 +95,19 @@ ResultProcessor::prepareThreadContextCreation(size_t num_threads)
ResultProcessor::Context::UP
ResultProcessor::createThreadContext(const vespalib::Doom & hardDoom, size_t thread_id, uint32_t distributionKey)
{
- Sort::UP sort(new Sort(distributionKey, hardDoom, _attrContext, _sortSpec));
- PartialResult::UP result(new PartialResult((_offset + _hits), sort->hasSortData()));
+ auto sort = std::make_unique<Sort>(distributionKey, hardDoom, _attrContext, _sortSpec);
+ auto result = std::make_unique<PartialResult>((_offset + _hits), sort->hasSortData());
search::grouping::GroupingContext::UP groupingContext;
- if (_groupingSession.get() != 0) {
+ if (_groupingSession) {
groupingContext = _groupingSession->createThreadContext(thread_id, _attrContext);
}
- return Context::UP(new Context(std::move(sort), std::move(result), std::move(groupingContext)));
+ return std::make_unique<Context>(std::move(sort), std::move(result), std::move(groupingContext));
}
ResultProcessor::Result::UP
ResultProcessor::makeReply(PartialResultUP full_result)
{
- search::engine::SearchReply::UP reply(new search::engine::SearchReply());
+ auto reply = std::make_unique<search::engine::SearchReply>();
const search::IDocumentMetaStore &metaStore = _metaStore;
search::engine::SearchReply &r = *reply;
PartialResult &result = *full_result;
@@ -158,7 +158,7 @@ ResultProcessor::makeReply(PartialResultUP full_result)
assert(sortOffset == sortDataSize);
}
numFs4Hits += reply->hits.size();
- return Result::UP(new Result(std::move(reply), numFs4Hits));
+ return std::make_unique<Result>(std::move(reply), numFs4Hits);
}
}
diff --git a/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp b/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp
index bab3494ca5c..6475efdaabb 100644
--- a/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp
+++ b/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp
@@ -6,7 +6,9 @@
#include <vespa/metrics/loadmetric.h>
#include <vespa/vespalib/stllike/hash_set.h>
#include <vespa/document/fieldvalue/document.h>
+#include <vespa/document/datatype/documenttype.h>
#include <vespa/document/update/documentupdate.h>
+#include <vespa/document/base/exceptions.h>
#include <vespa/log/log.h>
@@ -25,6 +27,7 @@ using storage::spi::Result;
using vespalib::IllegalStateException;
using vespalib::Sequence;
using vespalib::make_string;
+using std::make_unique;
using namespace std::chrono_literals;
@@ -377,6 +380,18 @@ PersistenceEngine::update(const Bucket& b, Timestamp t, const DocumentUpdate::SP
upd->getId().toString().c_str(), state.message().c_str()));
}
}
+ try {
+ upd->eagerDeserialize();
+ } catch (document::FieldNotFoundException & e) {
+ return UpdateResult(Result::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,
+ make_string("Update operation rejected for document '%s' of type '%s'.",
+ upd->getId().toString().c_str(), e.getDocumentTypeName().c_str()));
+
+ }
std::shared_lock<std::shared_timed_mutex> rguard(_rwMutex);
DocTypeName docType(upd->getType());
LOG(spam, "update(%s, %" PRIu64 ", (\"%s\", \"%s\"), createIfNonExistent='%s')",
diff --git a/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.h b/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.h
index f2ab35e0ccf..a6c696d08fb 100644
--- a/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.h
+++ b/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.h
@@ -87,37 +87,36 @@ public:
PersistenceEngine(IPersistenceEngineOwner &owner, const IResourceWriteFilter &writeFilter,
ssize_t defaultSerializedSize, bool ignoreMaxBytes);
- ~PersistenceEngine();
+ ~PersistenceEngine() override;
IPersistenceHandler::SP putHandler(document::BucketSpace bucketSpace, const DocTypeName &docType,
const IPersistenceHandler::SP &handler);
IPersistenceHandler::SP removeHandler(document::BucketSpace bucketSpace, const DocTypeName &docType);
// Implements PersistenceProvider
- virtual Result initialize() override;
- virtual PartitionStateListResult getPartitionStates() const override;
- virtual BucketIdListResult listBuckets(BucketSpace bucketSpace, PartitionId) const override;
- virtual Result setClusterState(BucketSpace bucketSpace, const ClusterState& calc) override;
- virtual Result setActiveState(const Bucket& bucket, BucketInfo::ActiveState newState) override;
- virtual BucketInfoResult getBucketInfo(const Bucket&) const override;
- virtual Result put(const Bucket&, Timestamp, const std::shared_ptr<document::Document>&, Context&) override;
- virtual RemoveResult remove(const Bucket&, Timestamp, const document::DocumentId&, Context&) override;
- virtual UpdateResult update(const Bucket&, Timestamp,
- const std::shared_ptr<document::DocumentUpdate>&, Context&) override;
- virtual GetResult get(const Bucket&, const document::FieldSet&,
- const document::DocumentId&, Context&) const override;
- virtual CreateIteratorResult createIterator(const Bucket&, const document::FieldSet&, const Selection&,
- IncludedVersions, Context&) override;
- virtual IterateResult iterate(IteratorId, uint64_t maxByteSize, Context&) const override;
- virtual Result destroyIterator(IteratorId, Context&) override;
-
- virtual Result createBucket(const Bucket &bucketId, Context &) override ;
- virtual Result deleteBucket(const Bucket&, Context&) override;
- virtual BucketIdListResult getModifiedBuckets(BucketSpace bucketSpace) const override;
- virtual Result split(const Bucket& source, const Bucket& target1, const Bucket& target2, Context&) override;
- virtual Result join(const Bucket& source1, const Bucket& source2, const Bucket& target, Context&) override;
-
- virtual Result maintain(const Bucket&, MaintenanceLevel) override;
+ Result initialize() override;
+ PartitionStateListResult getPartitionStates() const override;
+ BucketIdListResult listBuckets(BucketSpace bucketSpace, PartitionId) const override;
+ Result setClusterState(BucketSpace bucketSpace, const ClusterState& calc) override;
+ Result setActiveState(const Bucket& bucket, BucketInfo::ActiveState newState) override;
+ BucketInfoResult getBucketInfo(const Bucket&) const override;
+ Result put(const Bucket&, Timestamp, const std::shared_ptr<document::Document>&, Context&) override;
+ RemoveResult remove(const Bucket&, Timestamp, const document::DocumentId&, Context&) override;
+ UpdateResult update(const Bucket&, Timestamp,
+ const std::shared_ptr<document::DocumentUpdate>&, Context&) override;
+ GetResult get(const Bucket&, const document::FieldSet&, const document::DocumentId&, Context&) const override;
+ CreateIteratorResult createIterator(const Bucket&, const document::FieldSet&, const Selection&,
+ IncludedVersions, Context&) override;
+ IterateResult iterate(IteratorId, uint64_t maxByteSize, Context&) const override;
+ Result destroyIterator(IteratorId, Context&) override;
+
+ Result createBucket(const Bucket &bucketId, Context &) override ;
+ Result deleteBucket(const Bucket&, Context&) override;
+ BucketIdListResult getModifiedBuckets(BucketSpace bucketSpace) const override;
+ Result split(const Bucket& source, const Bucket& target1, const Bucket& target2, Context&) override;
+ Result join(const Bucket& source1, const Bucket& source2, const Bucket& target, Context&) override;
+
+ Result maintain(const Bucket&, MaintenanceLevel) override;
void destroyIterators();
void propagateSavedClusterState(BucketSpace bucketSpace, IPersistenceHandler &handler);
diff --git a/searchcore/src/vespa/searchcore/proton/reference/gid_to_lid_change_listener.cpp b/searchcore/src/vespa/searchcore/proton/reference/gid_to_lid_change_listener.cpp
index da03adb3fe4..46096fead05 100644
--- a/searchcore/src/vespa/searchcore/proton/reference/gid_to_lid_change_listener.cpp
+++ b/searchcore/src/vespa/searchcore/proton/reference/gid_to_lid_change_listener.cpp
@@ -12,7 +12,7 @@ GidToLidChangeListener::GidToLidChangeListener(search::ISequencedTaskExecutor &a
const vespalib::string &name,
const vespalib::string &docTypeName)
: _attributeFieldWriter(attributeFieldWriter),
- _executorId(_attributeFieldWriter.getExecutorId(attr->getName())),
+ _executorId(_attributeFieldWriter.getExecutorId(attr->getNamePrefix())),
_attr(std::move(attr)),
_refCount(refCount),
_name(name),
diff --git a/searchcore/src/vespa/searchcore/proton/reprocessing/attribute_reprocessing_initializer.cpp b/searchcore/src/vespa/searchcore/proton/reprocessing/attribute_reprocessing_initializer.cpp
index 711f8ebdcaf..1df812b0c61 100644
--- a/searchcore/src/vespa/searchcore/proton/reprocessing/attribute_reprocessing_initializer.cpp
+++ b/searchcore/src/vespa/searchcore/proton/reprocessing/attribute_reprocessing_initializer.cpp
@@ -38,6 +38,9 @@ bool fastPartialUpdateAttribute(BasicType::Type attrType) {
(attrType != BasicType::Type::REFERENCE));
}
+bool isStructFieldAttribute(const vespalib::string &name) {
+ return name.find('.') != vespalib::string::npos;
+}
FilterAttributeManager::AttributeSet
getAttributeSetToPopulate(const ARIConfig &newCfg,
@@ -100,7 +103,8 @@ getFieldsToPopulate(const ARIConfig &newCfg,
// keep the original in order to preserve annotations.
bool wasStringIndexField = oldIndexschemaInspector.isStringIndex(name);
bool populateField = !inNewAttrMgr && unchangedField && !wasStringIndexField &&
- fastPartialUpdateAttribute(attrType.type());
+ fastPartialUpdateAttribute(attrType.type()) &&
+ !isStructFieldAttribute(name);
LOG(debug, "getFieldsToPopulate(): name='%s', inNewAttrMgr=%s, unchangedField=%s, "
"wasStringIndexField=%s, dataType=%s, populate=%s",
name.c_str(), toStr(inNewAttrMgr), toStr(unchangedField),
diff --git a/searchcore/src/vespa/searchcore/proton/server/CMakeLists.txt b/searchcore/src/vespa/searchcore/proton/server/CMakeLists.txt
index b371439dd0e..87aa19fb1b2 100644
--- a/searchcore/src/vespa/searchcore/proton/server/CMakeLists.txt
+++ b/searchcore/src/vespa/searchcore/proton/server/CMakeLists.txt
@@ -13,6 +13,8 @@ vespa_add_library(searchcore_server STATIC
disk_mem_usage_sampler.cpp
disk_mem_usage_forwarder.cpp
docstorevalidator.cpp
+ document_db_config_owner.cpp
+ document_db_directory_holder.cpp
document_db_explorer.cpp
document_db_flush_config.cpp
document_db_maintenance_config.cpp
diff --git a/searchcore/src/vespa/searchcore/proton/server/combiningfeedview.cpp b/searchcore/src/vespa/searchcore/proton/server/combiningfeedview.cpp
index 987218beefa..20306e92ea8 100644
--- a/searchcore/src/vespa/searchcore/proton/server/combiningfeedview.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/combiningfeedview.cpp
@@ -3,7 +3,7 @@
#include "combiningfeedview.h"
#include <vespa/document/fieldvalue/document.h>
#include <vespa/searchcore/proton/documentmetastore/i_document_meta_store.h>
-#include <vespa/searchcore/proton/feedoperation/moveoperation.h>
+#include <vespa/searchcore/proton/feedoperation/operations.h>
#include <vespa/searchlib/common/idestructorcallback.h>
#include <vespa/log/log.h>
@@ -13,22 +13,19 @@ using document::DocumentTypeRepo;
using document::DocumentId;
using search::IDestructorCallback;
-namespace proton
-{
+namespace proton {
-namespace
-{
+namespace {
std::shared_ptr<const DocumentTypeRepo>
getRepo(const std::vector<IFeedView::SP> &views)
{
for (const auto &view : views) {
- if (view.get() == NULL)
+ if (view.get() == nullptr)
continue;
return view->getDocumentTypeRepo();
}
- abort();
- return std::shared_ptr<const DocumentTypeRepo>();
+ LOG_ABORT("should not be reached");
}
};
diff --git a/searchcore/src/vespa/searchcore/proton/server/combiningfeedview.h b/searchcore/src/vespa/searchcore/proton/server/combiningfeedview.h
index 6644f21a3b7..3546fdbea71 100644
--- a/searchcore/src/vespa/searchcore/proton/server/combiningfeedview.h
+++ b/searchcore/src/vespa/searchcore/proton/server/combiningfeedview.h
@@ -3,21 +3,14 @@
#pragma once
#include "ifeedview.h"
-#include <vespa/searchcore/proton/common/feedtoken.h>
-#include <vespa/searchcore/proton/feedoperation/deletebucketoperation.h>
-#include <vespa/searchcore/proton/feedoperation/joinbucketsoperation.h>
-#include <vespa/searchcore/proton/feedoperation/pruneremoveddocumentsoperation.h>
-#include <vespa/searchcore/proton/feedoperation/putoperation.h>
-#include <vespa/searchcore/proton/feedoperation/removeoperation.h>
-#include <vespa/searchcore/proton/feedoperation/splitbucketoperation.h>
-#include <vespa/searchcore/proton/feedoperation/updateoperation.h>
-#include <vespa/searchcore/proton/feedoperation/createbucketoperation.h>
-#include <vespa/searchlib/common/serialnum.h>
#include "replaypacketdispatcher.h"
#include "ibucketstatecalculator.h"
+#include <vespa/searchcore/proton/common/feedtoken.h>
+#include <vespa/searchlib/common/serialnum.h>
namespace proton {
+class DocumentOperation;
class CombiningFeedView : public IFeedView
{
diff --git a/searchcore/src/vespa/searchcore/proton/server/document_db_config_owner.cpp b/searchcore/src/vespa/searchcore/proton/server/document_db_config_owner.cpp
new file mode 100644
index 00000000000..28492c5d39a
--- /dev/null
+++ b/searchcore/src/vespa/searchcore/proton/server/document_db_config_owner.cpp
@@ -0,0 +1,21 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "document_db_config_owner.h"
+#include "document_db_directory_holder.h"
+
+namespace proton {
+
+DocumentDBConfigOwner::DocumentDBConfigOwner()
+ : _holder(std::make_shared<DocumentDBDirectoryHolder>())
+{
+}
+
+DocumentDBConfigOwner::~DocumentDBConfigOwner() = default;
+
+std::shared_ptr<DocumentDBDirectoryHolder>
+DocumentDBConfigOwner::getDocumentDBDirectoryHolder()
+{
+ return _holder;
+};
+
+} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/server/document_db_config_owner.h b/searchcore/src/vespa/searchcore/proton/server/document_db_config_owner.h
new file mode 100644
index 00000000000..cc19bc14fab
--- /dev/null
+++ b/searchcore/src/vespa/searchcore/proton/server/document_db_config_owner.h
@@ -0,0 +1,25 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "i_document_db_config_owner.h"
+
+namespace proton {
+
+class DocumentDBDirectoryHolder;
+
+/*
+ * Abstract class meant to be a base class for DocumentDB where a
+ * directory holder exists until the document db instance is
+ * destroyed.
+ */
+class DocumentDBConfigOwner : public IDocumentDBConfigOwner
+{
+ std::shared_ptr<DocumentDBDirectoryHolder> _holder;
+public:
+ DocumentDBConfigOwner();
+ virtual ~DocumentDBConfigOwner();
+ std::shared_ptr<DocumentDBDirectoryHolder> getDocumentDBDirectoryHolder();
+};
+
+} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/server/document_db_directory_holder.cpp b/searchcore/src/vespa/searchcore/proton/server/document_db_directory_holder.cpp
new file mode 100644
index 00000000000..d0807a3b8a7
--- /dev/null
+++ b/searchcore/src/vespa/searchcore/proton/server/document_db_directory_holder.cpp
@@ -0,0 +1,33 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "document_db_directory_holder.h"
+#include <mutex>
+#include <condition_variable>
+
+namespace proton {
+
+namespace {
+
+std::mutex mutex;
+std::condition_variable cv;
+
+}
+
+DocumentDBDirectoryHolder::DocumentDBDirectoryHolder()
+{
+}
+
+DocumentDBDirectoryHolder::~DocumentDBDirectoryHolder()
+{
+ std::lock_guard guard(mutex);
+ cv.notify_all();
+}
+
+void
+DocumentDBDirectoryHolder::waitUntilDestroyed(const std::weak_ptr<DocumentDBDirectoryHolder> &holder)
+{
+ std::unique_lock guard(mutex);
+ cv.wait(guard, [&]() { return !holder.lock(); });
+}
+
+} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/server/document_db_directory_holder.h b/searchcore/src/vespa/searchcore/proton/server/document_db_directory_holder.h
new file mode 100644
index 00000000000..65df1b7bd8e
--- /dev/null
+++ b/searchcore/src/vespa/searchcore/proton/server/document_db_directory_holder.h
@@ -0,0 +1,20 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "i_document_db_config_owner.h"
+
+namespace proton {
+
+/*
+ * class holding onto a document db directory.
+ */
+class DocumentDBDirectoryHolder
+{
+public:
+ DocumentDBDirectoryHolder();
+ ~DocumentDBDirectoryHolder();
+ static void waitUntilDestroyed(const std::weak_ptr<DocumentDBDirectoryHolder> &holder);
+};
+
+} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/server/documentdb.cpp b/searchcore/src/vespa/searchcore/proton/server/documentdb.cpp
index 61ce0e6dc6f..24d40e15677 100644
--- a/searchcore/src/vespa/searchcore/proton/server/documentdb.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/documentdb.cpp
@@ -17,6 +17,7 @@
#include <vespa/searchcore/proton/attribute/imported_attributes_repo.h>
#include <vespa/searchcore/proton/common/eventlogger.h>
#include <vespa/searchcore/proton/common/statusreport.h>
+#include <vespa/searchcore/proton/feedoperation/noopoperation.h>
#include <vespa/searchcore/proton/index/index_writer.h>
#include <vespa/searchcore/proton/initializer/task_runner.h>
#include <vespa/searchcore/proton/metrics/attribute_metrics_collection.h>
@@ -89,7 +90,7 @@ DocumentDB::DocumentDB(const vespalib::string &baseDir,
ConfigStore::UP config_store,
InitializeThreads initializeThreads,
const HwInfo &hwInfo)
- : IDocumentDBConfigOwner(),
+ : DocumentDBConfigOwner(),
IReplayConfig(),
IFeedHandlerOwner(),
IDocumentSubDBOwner(),
diff --git a/searchcore/src/vespa/searchcore/proton/server/documentdb.h b/searchcore/src/vespa/searchcore/proton/server/documentdb.h
index e25f1fe66fc..1a64a4013de 100644
--- a/searchcore/src/vespa/searchcore/proton/server/documentdb.h
+++ b/searchcore/src/vespa/searchcore/proton/server/documentdb.h
@@ -10,7 +10,7 @@
#include "documentsubdbcollection.h"
#include "executorthreadingservice.h"
#include "feedhandler.h"
-#include "i_document_db_config_owner.h"
+#include "document_db_config_owner.h"
#include "i_document_subdb_owner.h"
#include "i_feed_handler_owner.h"
#include "i_lid_space_compaction_handler.h"
@@ -53,7 +53,7 @@ namespace matching { class SessionManager; }
* to ensure that there are never multiple writers. Unless explicitly stated,
* none of the methods of this class are thread-safe.
*/
-class DocumentDB : public IDocumentDBConfigOwner,
+class DocumentDB : public DocumentDBConfigOwner,
public IReplayConfig,
public IFeedHandlerOwner,
public IDocumentSubDBOwner,
diff --git a/searchcore/src/vespa/searchcore/proton/server/documentdbconfig.cpp b/searchcore/src/vespa/searchcore/proton/server/documentdbconfig.cpp
index d5a9b09ead7..fd1f9f1155d 100644
--- a/searchcore/src/vespa/searchcore/proton/server/documentdbconfig.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/documentdbconfig.cpp
@@ -110,7 +110,7 @@ DocumentDBConfig(const DocumentDBConfig &cfg)
_delayedAttributeAspects(false)
{ }
-DocumentDBConfig::~DocumentDBConfig() { }
+DocumentDBConfig::~DocumentDBConfig() = default;
bool
DocumentDBConfig::operator==(const DocumentDBConfig & rhs) const
diff --git a/searchcore/src/vespa/searchcore/proton/server/documentdbconfigmanager.cpp b/searchcore/src/vespa/searchcore/proton/server/documentdbconfigmanager.cpp
index ec22d3293c4..f2230215c3d 100644
--- a/searchcore/src/vespa/searchcore/proton/server/documentdbconfigmanager.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/documentdbconfigmanager.cpp
@@ -157,13 +157,26 @@ deriveCompression(const T & config) {
return compression;
}
+DocumentStore::Config::UpdateStrategy
+derive(ProtonConfig::Summary::Cache::UpdateStrategy strategy) {
+ switch (strategy) {
+ case ProtonConfig::Summary::Cache::UpdateStrategy::INVALIDATE:
+ return DocumentStore::Config::UpdateStrategy::INVALIDATE;
+ case ProtonConfig::Summary::Cache::UpdateStrategy::UPDATE:
+ return DocumentStore::Config::UpdateStrategy::UPDATE;
+ }
+ return DocumentStore::Config::UpdateStrategy::INVALIDATE;
+}
+
DocumentStore::Config
getStoreConfig(const ProtonConfig::Summary::Cache & cache, const HwInfo & hwInfo)
{
size_t maxBytes = (cache.maxbytes < 0)
? (hwInfo.memory().sizeBytes()*std::min(50l, -cache.maxbytes))/100l
: cache.maxbytes;
- return DocumentStore::Config(deriveCompression(cache.compression), maxBytes, cache.initialentries).allowVisitCaching(cache.allowvisitcaching);
+ return DocumentStore::Config(deriveCompression(cache.compression), maxBytes, cache.initialentries)
+ .allowVisitCaching(cache.allowvisitcaching)
+ .updateStrategy(derive(cache.updateStrategy));
}
LogDocumentStore::Config
@@ -291,8 +304,7 @@ DocumentDBConfigManager::update(const ConfigSnapshot &snapshot)
search::index::Schema schema;
search::index::SchemaBuilder::build(*newIndexschemaConfig, schema);
if (!search::index::SchemaUtil::validateSchema(schema)) {
- LOG(error, "Cannot use bad index schema, validation failed");
- abort();
+ LOG_ABORT("Cannot use bad index schema, validation failed");
}
}
if (snapshot.isChanged<AttributesConfig>(_configId, currentGeneration)) {
diff --git a/searchcore/src/vespa/searchcore/proton/server/fast_access_feed_view.cpp b/searchcore/src/vespa/searchcore/proton/server/fast_access_feed_view.cpp
index 6364e772f94..1c2406b2acf 100644
--- a/searchcore/src/vespa/searchcore/proton/server/fast_access_feed_view.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/fast_access_feed_view.cpp
@@ -5,6 +5,7 @@
#include "operationdonecontext.h"
#include "removedonecontext.h"
#include "putdonecontext.h"
+#include <vespa/searchcore/proton/feedoperation/operations.h>
#include <vespa/searchlib/common/isequencedtaskexecutor.h>
using document::Document;
@@ -71,7 +72,7 @@ FastAccessFeedView::FastAccessFeedView(const StoreOnlyFeedView::Context &storeOn
_docIdLimit(ctx._docIdLimit)
{}
-FastAccessFeedView::~FastAccessFeedView() {}
+FastAccessFeedView::~FastAccessFeedView() = default;
void
FastAccessFeedView::handleCompactLidSpace(const CompactLidSpaceOperation &op)
diff --git a/searchcore/src/vespa/searchcore/proton/server/feedhandler.cpp b/searchcore/src/vespa/searchcore/proton/server/feedhandler.cpp
index 66e721f9222..97cc25e635f 100644
--- a/searchcore/src/vespa/searchcore/proton/server/feedhandler.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/feedhandler.cpp
@@ -14,6 +14,7 @@
#include <vespa/searchcore/proton/bucketdb/ibucketdbhandler.h>
#include <vespa/searchcore/proton/persistenceengine/i_resource_write_filter.h>
#include <vespa/searchcore/proton/persistenceengine/transport_latch.h>
+#include <vespa/searchcore/proton/feedoperation/operations.h>
#include <vespa/searchcore/proton/common/eventlogger.h>
#include <vespa/searchcorespi/index/ithreadingservice.h>
#include <vespa/searchlib/common/gatecallback.h>
@@ -522,7 +523,7 @@ FeedHandler::considerUpdateOperationForRejection(FeedToken &token, UpdateOperati
*/
if (_documentType != &update.getType()) {
try {
- op.deserializeUpdate(*_repo);
+ op.verifyUpdate(*_repo);
} catch (document::FieldNotFoundException &e) {
if (token) {
auto message = make_string("Update operation rejected for document '%s' of type '%s': 'Field not found'",
diff --git a/searchcore/src/vespa/searchcore/proton/server/feedstate.h b/searchcore/src/vespa/searchcore/proton/server/feedstate.h
index 472f5cb224f..fa0a1702499 100644
--- a/searchcore/src/vespa/searchcore/proton/server/feedstate.h
+++ b/searchcore/src/vespa/searchcore/proton/server/feedstate.h
@@ -25,6 +25,7 @@ private:
Type _type;
protected:
+ using FeedOperationUP = std::unique_ptr<FeedOperation>;
void throwExceptionInReceive(const vespalib::string &docType, uint64_t serialRangeFrom,
uint64_t serialRangeTo, size_t packetSize);
void throwExceptionInHandleOperation(const vespalib::string &docType, const FeedOperation &op);
@@ -38,7 +39,7 @@ public:
Type getType() const { return _type; }
vespalib::string getName() const;
- virtual void handleOperation(FeedToken token, std::unique_ptr<FeedOperation> op) = 0;
+ virtual void handleOperation(FeedToken token, FeedOperationUP op) = 0;
virtual void receive(const PacketWrapper::SP &wrap, vespalib::Executor &executor) = 0;
};
diff --git a/searchcore/src/vespa/searchcore/proton/server/feedstates.cpp b/searchcore/src/vespa/searchcore/proton/server/feedstates.cpp
index f0866347f59..ae323bc93df 100644
--- a/searchcore/src/vespa/searchcore/proton/server/feedstates.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/feedstates.cpp
@@ -6,6 +6,7 @@
#include "ireplayconfig.h"
#include "replaypacketdispatcher.h"
#include <vespa/searchcore/proton/bucketdb/ibucketdbhandler.h>
+#include <vespa/searchcore/proton/feedoperation/operations.h>
#include <vespa/searchcore/proton/common/eventlogger.h>
#include <vespa/searchlib/common/idestructorcallback.h>
#include <vespa/vespalib/util/closuretask.h>
diff --git a/searchcore/src/vespa/searchcore/proton/server/feedstates.h b/searchcore/src/vespa/searchcore/proton/server/feedstates.h
index 963a78d0d6b..a9224669d87 100644
--- a/searchcore/src/vespa/searchcore/proton/server/feedstates.h
+++ b/searchcore/src/vespa/searchcore/proton/server/feedstates.h
@@ -28,16 +28,13 @@ public:
{
}
- virtual void handleOperation(FeedToken, FeedOperation::UP op) override {
+ void handleOperation(FeedToken, FeedOperationUP op) override {
throwExceptionInHandleOperation(_doc_type_name, *op);
}
- virtual void
- receive(const PacketWrapper::SP &wrap, vespalib::Executor &) override {
- throwExceptionInReceive(_doc_type_name.c_str(),
- wrap->packet.range().from(),
- wrap->packet.range().to(),
- wrap->packet.size());
+ void receive(const PacketWrapper::SP &wrap, vespalib::Executor &) override {
+ throwExceptionInReceive(_doc_type_name.c_str(), wrap->packet.range().from(),
+ wrap->packet.range().to(), wrap->packet.size());
}
};
@@ -57,12 +54,11 @@ public:
IReplayConfig &replay_config,
FeedConfigStore &config_store);
- virtual void handleOperation(FeedToken, FeedOperation::UP op) override {
+ void handleOperation(FeedToken, FeedOperationUP op) override {
throwExceptionInHandleOperation(_doc_type_name, *op);
}
- virtual void receive(const PacketWrapper::SP &wrap,
- vespalib::Executor &executor) override;
+ void receive(const PacketWrapper::SP &wrap, vespalib::Executor &executor) override;
};
@@ -79,15 +75,13 @@ public:
_handler(handler) {
}
- void handleOperation(FeedToken token, FeedOperation::UP op) override {
+ void handleOperation(FeedToken token, FeedOperationUP op) override {
_handler.performOperation(std::move(token), std::move(op));
}
void receive(const PacketWrapper::SP &wrap, vespalib::Executor &) override {
- throwExceptionInReceive(_handler.getDocTypeName().c_str(),
- wrap->packet.range().from(),
- wrap->packet.range().to(),
- wrap->packet.size());
+ throwExceptionInReceive(_handler.getDocTypeName().c_str(), wrap->packet.range().from(),
+ wrap->packet.range().to(), wrap->packet.size());
}
};
} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/server/i_proton_configurer_owner.h b/searchcore/src/vespa/searchcore/proton/server/i_proton_configurer_owner.h
index 242e99fffbf..fec8430e41d 100644
--- a/searchcore/src/vespa/searchcore/proton/server/i_proton_configurer_owner.h
+++ b/searchcore/src/vespa/searchcore/proton/server/i_proton_configurer_owner.h
@@ -10,7 +10,7 @@ namespace vespalib { class ThreadStackExecutorBase; }
namespace proton {
-class IDocumentDBConfigOwner;
+class DocumentDBConfigOwner;
/*
* Interface class for owner of a proton configurer, with callback methods
@@ -21,12 +21,12 @@ class IProtonConfigurerOwner
using InitializeThreads = std::shared_ptr<vespalib::ThreadStackExecutorBase>;
public:
virtual ~IProtonConfigurerOwner() { }
- virtual IDocumentDBConfigOwner *addDocumentDB(const DocTypeName &docTypeName,
- document::BucketSpace bucketSpace,
- const vespalib::string &configId,
- const std::shared_ptr<BootstrapConfig> &bootstrapConfig,
- const std::shared_ptr<DocumentDBConfig> &documentDBConfig,
- InitializeThreads initializeThreads) = 0;
+ virtual std::shared_ptr<DocumentDBConfigOwner> addDocumentDB(const DocTypeName &docTypeName,
+ document::BucketSpace bucketSpace,
+ const vespalib::string &configId,
+ const std::shared_ptr<BootstrapConfig> &bootstrapConfig,
+ const std::shared_ptr<DocumentDBConfig> &documentDBConfig,
+ InitializeThreads initializeThreads) = 0;
virtual void removeDocumentDB(const DocTypeName &docTypeName) = 0;
virtual void applyConfig(const std::shared_ptr<BootstrapConfig> &bootstrapConfig) = 0;
};
diff --git a/searchcore/src/vespa/searchcore/proton/server/ireplaypackethandler.h b/searchcore/src/vespa/searchcore/proton/server/ireplaypackethandler.h
index 210ece3a36a..e93821e2a36 100644
--- a/searchcore/src/vespa/searchcore/proton/server/ireplaypackethandler.h
+++ b/searchcore/src/vespa/searchcore/proton/server/ireplaypackethandler.h
@@ -2,10 +2,28 @@
#pragma once
-#include <vespa/searchcore/proton/feedoperation/operations.h>
+namespace document { class DocumentTypeRepo; }
namespace proton {
+class PutOperation;
+class RemoveOperation;
+class UpdateOperation;
+class NoopOperation;
+class NewConfigOperation;
+class WipeHistoryOperation;
+class DeleteBucketOperation;
+class SplitBucketOperation;
+class JoinBucketsOperation;
+class PruneRemovedDocumentsOperation;
+class SpoolerReplayStartOperation;
+class SpoolerReplayCompleteOperation;
+class MoveOperation;
+class CreateBucketOperation;
+class CompactLidSpaceOperation;
+
+namespace feedoperation { class IStreamHandler; }
+
/**
* Interface used to handle the various feed operations during
* replay of the transaction log.
@@ -29,7 +47,7 @@ struct IReplayPacketHandler
virtual void replay(const CreateBucketOperation &op) = 0;
virtual void replay(const CompactLidSpaceOperation &op) = 0;
- virtual NewConfigOperation::IStreamHandler &getNewConfigStreamHandler() = 0;
+ virtual feedoperation::IStreamHandler &getNewConfigStreamHandler() = 0;
virtual const document::DocumentTypeRepo &getDeserializeRepo() = 0;
};
diff --git a/searchcore/src/vespa/searchcore/proton/server/matchview.cpp b/searchcore/src/vespa/searchcore/proton/server/matchview.cpp
index 3162f9a1c45..9af648917c4 100644
--- a/searchcore/src/vespa/searchcore/proton/server/matchview.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/matchview.cpp
@@ -44,13 +44,13 @@ MatchView::MatchView(const Matchers::SP &matchers,
_docIdLimit(docIdLimit)
{ }
-MatchView::~MatchView() { }
+MatchView::~MatchView() = default;
Matcher::SP
MatchView::getMatcher(const vespalib::string & rankProfile) const
{
Matcher::SP retval = _matchers->lookup(rankProfile);
- if (retval.get() == NULL) {
+ if ( ! retval) {
throw std::runtime_error(make_string("Failed locating Matcher for rank profile '%s'", rankProfile.c_str()));
}
LOG(debug, "Rankprofile = %s has termwise_limit=%f", rankProfile.c_str(), retval->get_termwise_limit());
@@ -60,8 +60,8 @@ MatchView::getMatcher(const vespalib::string & rankProfile) const
MatchContext::UP MatchView::createContext() const {
IAttributeContext::UP attrCtx = _attrMgr->createContext();
- ISearchContext::UP searchCtx(new SearchContext(_indexSearchable, _docIdLimit.get()));
- return MatchContext::UP(new MatchContext(std::move(attrCtx), std::move(searchCtx)));
+ auto searchCtx = std::make_unique<SearchContext>(_indexSearchable, _docIdLimit.get());
+ return std::make_unique<MatchContext>(std::move(attrCtx), std::move(searchCtx));
}
diff --git a/searchcore/src/vespa/searchcore/proton/server/prepare_restart_handler.cpp b/searchcore/src/vespa/searchcore/proton/server/prepare_restart_handler.cpp
index aef49c51f7a..1c930ef1ddc 100644
--- a/searchcore/src/vespa/searchcore/proton/server/prepare_restart_handler.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/prepare_restart_handler.cpp
@@ -50,8 +50,7 @@ PrepareRestartHandler::performPrepareRestart(const ProtonConfig &protonCfg, std:
{
_running = true;
lock.unlock();
- auto strategy = std::make_shared<PrepareRestartFlushStrategy>(createPrepareRestartConfig(protonCfg));
- _flushEngine.setStrategy(strategy);
+ _flushEngine.setStrategy(std::make_shared<PrepareRestartFlushStrategy>(createPrepareRestartConfig(protonCfg)));
lock.lock();
_running = false;
_cond.notify_all();
diff --git a/searchcore/src/vespa/searchcore/proton/server/proton.cpp b/searchcore/src/vespa/searchcore/proton/server/proton.cpp
index 32dc711d5cf..9a7d5a5141e 100644
--- a/searchcore/src/vespa/searchcore/proton/server/proton.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/proton.cpp
@@ -240,9 +240,8 @@ Proton::init(const BootstrapConfig::SP & configSnapshot)
const HwInfo & hwInfo = configSnapshot->getHwInfo();
setFS4Compression(protonConfig);
- _diskMemUsageSampler = std::make_unique<DiskMemUsageSampler>
- (protonConfig.basedir,
- diskMemUsageSamplerConfig(protonConfig, hwInfo));
+ _diskMemUsageSampler = std::make_unique<DiskMemUsageSampler>(protonConfig.basedir,
+ diskMemUsageSamplerConfig(protonConfig, hwInfo));
_tls = std::make_unique<TLS>(_configUri.createWithNewId(protonConfig.tlsconfigid), _fileHeaderContext);
_metricsEngine->addMetricsHook(_metricsHook);
@@ -253,6 +252,7 @@ Proton::init(const BootstrapConfig::SP & configSnapshot)
_distributionKey = protonConfig.distributionkey;
_summaryEngine= std::make_unique<SummaryEngine>(protonConfig.numsummarythreads);
_docsumBySlime = std::make_unique<DocsumBySlime>(*_summaryEngine);
+
IFlushStrategy::SP strategy;
const ProtonConfig::Flush & flush(protonConfig.flush);
switch (flush.strategy) {
@@ -283,17 +283,15 @@ Proton::init(const BootstrapConfig::SP & configSnapshot)
LOG(debug, "Start proton server with root at %s and cwd at %s",
protonConfig.basedir.c_str(), getcwd(tmp, sizeof(tmp)));
- _persistenceEngine.reset(new PersistenceEngine(*this,
- _diskMemUsageSampler->writeFilter(),
- protonConfig.visit.defaultserializedsize,
- protonConfig.visit.ignoremaxbytes));
-
+ _persistenceEngine = std::make_unique<PersistenceEngine>(*this, _diskMemUsageSampler->writeFilter(),
+ protonConfig.visit.defaultserializedsize,
+ protonConfig.visit.ignoremaxbytes);
vespalib::string fileConfigId;
- _warmupExecutor.reset(new vespalib::ThreadStackExecutor(4, 128*1024));
+ _warmupExecutor = std::make_unique<vespalib::ThreadStackExecutor>(4, 128*1024);
const size_t summaryThreads = deriveCompactionCompressionThreads(protonConfig, hwInfo.cpu());
- _summaryExecutor.reset(new vespalib::BlockingThreadStackExecutor(summaryThreads, 128*1024, summaryThreads*16));
+ _summaryExecutor = std::make_unique<vespalib::BlockingThreadStackExecutor>(summaryThreads, 128*1024, summaryThreads*16);
InitializeThreads initializeThreads;
if (protonConfig.initialize.threads > 0) {
initializeThreads = std::make_shared<vespalib::ThreadStackExecutor>(protonConfig.initialize.threads, 128 * 1024);
@@ -305,7 +303,7 @@ Proton::init(const BootstrapConfig::SP & configSnapshot)
_prepareRestartHandler = std::make_unique<PrepareRestartHandler>(*_flushEngine);
RPCHooks::Params rpcParams(*this, protonConfig.rpcport, _configUri.getConfigId());
rpcParams.slobrok_config = _configUri.createWithNewId(protonConfig.slobrokconfigid);
- _rpcHooks.reset(new RPCHooks(rpcParams));
+ _rpcHooks = std::make_unique<RPCHooks>(rpcParams);
waitForInitDone();
@@ -354,7 +352,7 @@ Proton::applyConfig(const BootstrapConfig::SP & configSnapshot)
}
}
-IDocumentDBConfigOwner *
+std::shared_ptr<DocumentDBConfigOwner>
Proton::addDocumentDB(const DocTypeName &docTypeName,
document::BucketSpace bucketSpace,
const vespalib::string &configId,
@@ -368,21 +366,21 @@ Proton::addDocumentDB(const DocTypeName &docTypeName,
if (docType != NULL) {
LOG(info, "Add document database: doctypename(%s), configid(%s)",
docTypeName.toString().c_str(), configId.c_str());
- return addDocumentDB(*docType, bucketSpace, bootstrapConfig, documentDBConfig, initializeThreads).get();
+ return addDocumentDB(*docType, bucketSpace, bootstrapConfig, documentDBConfig, initializeThreads);
} else {
LOG(warning,
"Did not find document type '%s' in the document manager. "
"Skipping creating document database for this type",
docTypeName.toString().c_str());
- return nullptr;
+ return std::shared_ptr<DocumentDBConfigOwner>();
}
} catch (const document::DocumentTypeNotFoundException & e) {
LOG(warning,
"Did not find document type '%s' in the document manager. "
"Skipping creating document database for this type",
docTypeName.toString().c_str());
- return nullptr;
+ return std::shared_ptr<DocumentDBConfigOwner>();
}
}
@@ -528,23 +526,11 @@ Proton::addDocumentDB(const document::DocumentType &docType,
// 1 thread per document type.
initializeThreads = std::make_shared<vespalib::ThreadStackExecutor>(1, 128 * 1024);
}
- DocumentDB::SP ret(new DocumentDB(config.basedir + "/documents",
- documentDBConfig,
- config.tlsspec,
- _queryLimiter,
- _clock,
- docTypeName,
- bucketSpace,
- config,
- *this,
- *_warmupExecutor,
- *_summaryExecutor,
- *_tls->getTransLogServer(),
- *_metricsEngine,
- _fileHeaderContext,
- std::move(config_store),
- initializeThreads,
- bootstrapConfig->getHwInfo()));
+ auto ret = std::make_shared<DocumentDB>(config.basedir + "/documents", documentDBConfig, config.tlsspec,
+ _queryLimiter, _clock, docTypeName, bucketSpace, config, *this,
+ *_warmupExecutor, *_summaryExecutor, *_tls->getTransLogServer(),
+ *_metricsEngine, _fileHeaderContext, std::move(config_store),
+ initializeThreads, bootstrapConfig->getHwInfo());
try {
ret->start();
} catch (vespalib::Exception &e) {
@@ -571,10 +557,10 @@ Proton::addDocumentDB(const document::DocumentType &docType,
// TODO: Fix race with new cluster state setting.
_persistenceEngine->putHandler(bucketSpace, docTypeName, persistenceHandler);
}
- SearchHandlerProxy::SP searchHandler(new SearchHandlerProxy(ret));
+ auto searchHandler = std::make_shared<SearchHandlerProxy>(ret);
_summaryEngine->putSearchHandler(docTypeName, searchHandler);
_matchEngine->putSearchHandler(docTypeName, searchHandler);
- FlushHandlerProxy::SP flushHandler(new FlushHandlerProxy(ret));
+ auto flushHandler = std::make_shared<FlushHandlerProxy>(ret);
_flushEngine->putFlushHandler(docTypeName, flushHandler);
_diskMemUsageSampler->notifier().addDiskMemUsageListener(ret->diskMemUsageListener());
return ret;
@@ -624,7 +610,7 @@ Proton::MonitorReply::UP
Proton::ping(MonitorRequest::UP request, MonitorClient & client)
{
(void) client;
- MonitorReply::UP reply(new MonitorReply());
+ auto reply = std::make_unique<MonitorReply>();
MonitorReply &ret = *reply;
BootstrapConfig::SP configSnapshot = getActiveConfigSnapshot();
@@ -807,8 +793,8 @@ struct DocumentDBMapExplorer : vespalib::StateExplorer {
std::shared_timed_mutex &mutex;
DocumentDBMapExplorer(const DocumentDBMap &documentDBMap_in, std::shared_timed_mutex &mutex_in)
: documentDBMap(documentDBMap_in), mutex(mutex_in) {}
- virtual void get_state(const vespalib::slime::Inserter &, bool) const override {}
- virtual std::vector<vespalib::string> get_children_names() const override {
+ void get_state(const vespalib::slime::Inserter &, bool) const override {}
+ std::vector<vespalib::string> get_children_names() const override {
std::shared_lock<std::shared_timed_mutex> guard(mutex);
std::vector<vespalib::string> names;
for (const auto &item: documentDBMap) {
@@ -816,14 +802,14 @@ struct DocumentDBMapExplorer : vespalib::StateExplorer {
}
return names;
}
- virtual std::unique_ptr<vespalib::StateExplorer> get_child(vespalib::stringref name) const override {
+ std::unique_ptr<vespalib::StateExplorer> get_child(vespalib::stringref name) const override {
typedef std::unique_ptr<StateExplorer> Explorer_UP;
std::shared_lock<std::shared_timed_mutex> guard(mutex);
auto result = documentDBMap.find(DocTypeName(vespalib::string(name)));
if (result == documentDBMap.end()) {
return Explorer_UP(nullptr);
}
- return Explorer_UP(new DocumentDBExplorer(result->second));
+ return std::make_unique<DocumentDBExplorer>(result->second);
}
};
diff --git a/searchcore/src/vespa/searchcore/proton/server/proton.h b/searchcore/src/vespa/searchcore/proton/server/proton.h
index dd2b42d94cf..a3c596d7b0b 100644
--- a/searchcore/src/vespa/searchcore/proton/server/proton.h
+++ b/searchcore/src/vespa/searchcore/proton/server/proton.h
@@ -130,7 +130,7 @@ private:
std::mutex _nodeUpLock;
std::set<BucketSpace> _nodeUp; // bucketspaces where node is up
- IDocumentDBConfigOwner *
+ std::shared_ptr<DocumentDBConfigOwner>
addDocumentDB(const DocTypeName & docTypeName, BucketSpace bucketSpace, const vespalib::string & configid,
const BootstrapConfig::SP & bootstrapConfig, const std::shared_ptr<DocumentDBConfig> &documentDBConfig,
InitializeThreads initializeThreads) override;
diff --git a/searchcore/src/vespa/searchcore/proton/server/proton_configurer.cpp b/searchcore/src/vespa/searchcore/proton/server/proton_configurer.cpp
index 07ed83fd8c6..f871f36b042 100644
--- a/searchcore/src/vespa/searchcore/proton/server/proton_configurer.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/proton_configurer.cpp
@@ -4,7 +4,8 @@
#include "proton_config_snapshot.h"
#include "bootstrapconfig.h"
#include "i_proton_configurer_owner.h"
-#include "i_document_db_config_owner.h"
+#include "document_db_config_owner.h"
+#include "document_db_directory_holder.h"
#include <vespa/vespalib/util/lambdatask.h>
#include <vespa/vespalib/util/threadstackexecutorbase.h>
#include <vespa/document/bucket/fixed_bucket_spaces.h>
@@ -162,13 +163,15 @@ ProtonConfigurer::configureDocumentDB(const ProtonConfigSnapshot &configSnapshot
const auto &documentDBConfig = cfgitr->second;
auto dbitr(_documentDBs.find(docTypeName));
if (dbitr == _documentDBs.end()) {
- auto *newdb = _owner.addDocumentDB(docTypeName, bucketSpace, configId, bootstrapConfig, documentDBConfig, initializeThreads);
- if (newdb != nullptr) {
- auto insres = _documentDBs.insert(std::make_pair(docTypeName, newdb));
+ auto newdb = _owner.addDocumentDB(docTypeName, bucketSpace, configId, bootstrapConfig, documentDBConfig, initializeThreads);
+ if (newdb) {
+ auto insres = _documentDBs.insert(std::make_pair(docTypeName, std::make_pair(newdb, newdb->getDocumentDBDirectoryHolder())));
assert(insres.second);
}
} else {
- dbitr->second->reconfigure(documentDBConfig);
+ auto documentDB = dbitr->second.first.lock();
+ assert(documentDB);
+ documentDB->reconfigure(documentDBConfig);
}
}
@@ -189,6 +192,7 @@ ProtonConfigurer::pruneDocumentDBs(const ProtonConfigSnapshot &configSnapshot)
auto found(newDocTypes.find(dbitr->first));
if (found == newDocTypes.end()) {
_owner.removeDocumentDB(dbitr->first);
+ DocumentDBDirectoryHolder::waitUntilDestroyed(dbitr->second.second);
dbitr = _documentDBs.erase(dbitr);
} else {
++dbitr;
diff --git a/searchcore/src/vespa/searchcore/proton/server/proton_configurer.h b/searchcore/src/vespa/searchcore/proton/server/proton_configurer.h
index 149be3a9e62..ac0f6197fd7 100644
--- a/searchcore/src/vespa/searchcore/proton/server/proton_configurer.h
+++ b/searchcore/src/vespa/searchcore/proton/server/proton_configurer.h
@@ -12,6 +12,7 @@
namespace proton {
+class DocumentDBDirectoryHolder;
class IDocumentDBConfigOwner;
class IProtonConfigurerOwner;
class BootstrapConfig;
@@ -22,7 +23,7 @@ class BootstrapConfig;
*/
class ProtonConfigurer : public IProtonConfigurer
{
- using DocumentDBs = std::map<DocTypeName, IDocumentDBConfigOwner *>;
+ using DocumentDBs = std::map<DocTypeName, std::pair<std::weak_ptr<IDocumentDBConfigOwner>, std::weak_ptr<DocumentDBDirectoryHolder>>>;
using InitializeThreads = std::shared_ptr<vespalib::ThreadStackExecutorBase>;
ExecutorThreadService _executor;
diff --git a/searchcore/src/vespa/searchcore/proton/server/replaypacketdispatcher.cpp b/searchcore/src/vespa/searchcore/proton/server/replaypacketdispatcher.cpp
index 6b3ee548ad5..42451f08315 100644
--- a/searchcore/src/vespa/searchcore/proton/server/replaypacketdispatcher.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/replaypacketdispatcher.cpp
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "replaypacketdispatcher.h"
+#include <vespa/searchcore/proton/feedoperation/operations.h>
#include <vespa/vespalib/util/exceptions.h>
#include <vespa/document/util/serializableexceptions.h>
@@ -106,9 +107,7 @@ ReplayPacketDispatcher::replayEntry(const Packet::Entry &entry)
}
-ReplayPacketDispatcher::~ReplayPacketDispatcher()
-{
-}
+ReplayPacketDispatcher::~ReplayPacketDispatcher() = default;
void
diff --git a/searchcore/src/vespa/searchcore/proton/server/replaypacketdispatcher.h b/searchcore/src/vespa/searchcore/proton/server/replaypacketdispatcher.h
index fc6f99471d8..771a79ddad9 100644
--- a/searchcore/src/vespa/searchcore/proton/server/replaypacketdispatcher.h
+++ b/searchcore/src/vespa/searchcore/proton/server/replaypacketdispatcher.h
@@ -7,6 +7,7 @@
namespace proton {
+class FeedOperation;
/**
* Utility class that deserializes packet entries into feed operations
* during replay from the transaction log and dispatches the feed operations
@@ -22,14 +23,11 @@ private:
void replay(OperationType &op, vespalib::nbostream &is, const Packet::Entry &entry);
protected:
- virtual void
- store(const FeedOperation &op);
+ virtual void store(const FeedOperation &op);
public:
ReplayPacketDispatcher(IReplayPacketHandler &handler);
-
- virtual
- ~ReplayPacketDispatcher();
+ virtual ~ReplayPacketDispatcher();
void replayEntry(const Packet::Entry &entry);
};
diff --git a/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.cpp b/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.cpp
index 29615b0daf9..22ff1b90de4 100644
--- a/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.cpp
@@ -13,6 +13,7 @@
#include <vespa/searchcore/proton/documentmetastore/ilidreusedelayer.h>
#include <vespa/searchcore/proton/reference/i_gid_to_lid_change_handler.h>
#include <vespa/searchcore/proton/attribute/ifieldupdatecallback.h>
+#include <vespa/searchcore/proton/feedoperation/operations.h>
#include <vespa/searchlib/common/isequencedtaskexecutor.h>
#include <vespa/document/datatype/documenttype.h>
diff --git a/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.h b/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.h
index 825e14b4368..37f67399f4d 100644
--- a/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.h
+++ b/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.h
@@ -13,7 +13,7 @@
#include <vespa/searchcore/proton/common/feeddebugger.h>
#include <vespa/searchcore/proton/documentmetastore/documentmetastore.h>
#include <vespa/searchcore/proton/documentmetastore/documentmetastorecontext.h>
-#include <vespa/searchcore/proton/feedoperation/feedoperation.h>
+#include <vespa/searchcore/proton/feedoperation/lidvectorcontext.h>
#include <vespa/searchcore/proton/persistenceengine/resulthandler.h>
#include <vespa/searchcore/proton/reference/pending_notify_remove_done.h>
#include <vespa/searchcorespi/index/ithreadingservice.h>
@@ -23,7 +23,6 @@
namespace search { class IDestructorCallback; }
-namespace document { class GLobalId; }
namespace proton {
@@ -35,6 +34,8 @@ class RemoveDoneContext;
class CommitTimeTracker;
class IGidToLidChangeHandler;
class IFieldUpdateCallback;
+class RemoveDocumentsOperation;
+class DocumentOperation;
namespace documentmetastore { class ILidReuseDelayer; }
diff --git a/searchcore/src/vespa/searchcore/proton/test/attribute_utils.h b/searchcore/src/vespa/searchcore/proton/test/attribute_utils.h
index 6229e989cba..f1d661a3ad0 100644
--- a/searchcore/src/vespa/searchcore/proton/test/attribute_utils.h
+++ b/searchcore/src/vespa/searchcore/proton/test/attribute_utils.h
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#pragma once
+#include <vespa/vespalib/util/hdr_abort.h>
#include <vespa/searchcommon/attribute/config.h>
#include <vespa/searchlib/attribute/integerbase.h>
@@ -24,7 +25,7 @@ struct AttributeUtils
search::IntegerAttribute &ia = static_cast<search::IntegerAttribute &>(*attr);
while (ia.getNumDocs() < to) {
uint32_t docId;
- if (!ia.addDoc(docId)) { abort(); }
+ if (!ia.addDoc(docId)) { HDR_ABORT("should not be reached"); }
}
for (uint32_t i = from; i < to; ++i) {
ia.update(i, value);
diff --git a/searchcore/src/vespa/searchcore/proton/test/mock_attribute_manager.h b/searchcore/src/vespa/searchcore/proton/test/mock_attribute_manager.h
index 9dad0d32b61..b0b999e0d30 100644
--- a/searchcore/src/vespa/searchcore/proton/test/mock_attribute_manager.h
+++ b/searchcore/src/vespa/searchcore/proton/test/mock_attribute_manager.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/util/hdr_abort.h>
#include <vespa/searchlib/test/mock_attribute_manager.h>
#include <vespa/searchcore/proton/attribute/i_attribute_manager.h>
#include <vespa/searchcore/proton/attribute/imported_attributes_repo.h>
@@ -54,16 +55,16 @@ public:
virtual void pruneRemovedFields(search::SerialNum) override {
}
virtual const IAttributeFactory::SP &getFactory() const override {
- abort();
+ HDR_ABORT("should not be reached");
}
virtual search::ISequencedTaskExecutor &getAttributeFieldWriter() const override {
- abort();
+ HDR_ABORT("should not be reached");
}
virtual search::AttributeVector *getWritableAttribute(const vespalib::string &) const override {
return nullptr;
}
virtual const std::vector<search::AttributeVector *> &getWritableAttributes() const override {
- abort();
+ HDR_ABORT("should not be reached");
}
virtual void asyncForEachAttribute(std::shared_ptr<IAttributeFunctor>) const override {
}
diff --git a/searchlib/pom.xml b/searchlib/pom.xml
index fb9a81b51a5..0202f8510bb 100644
--- a/searchlib/pom.xml
+++ b/searchlib/pom.xml
@@ -17,7 +17,6 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
- <version>13.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
diff --git a/searchlib/src/apps/tests/memoryindexstress_test.cpp b/searchlib/src/apps/tests/memoryindexstress_test.cpp
index 1ba264e0bfe..b911284a1b4 100644
--- a/searchlib/src/apps/tests/memoryindexstress_test.cpp
+++ b/searchlib/src/apps/tests/memoryindexstress_test.cpp
@@ -19,6 +19,7 @@
#include <vespa/document/fieldvalue/document.h>
#include <vespa/document/fieldvalue/stringfieldvalue.h>
#include <vespa/document/repo/configbuilder.h>
+#include <vespa/document/repo/fixedtyperepo.h>
#include <vespa/document/annotation/spanlist.h>
#include <vespa/document/annotation/spantree.h>
#include <vespa/searchlib/util/rand48.h>
diff --git a/searchlib/src/apps/vespa-fileheader-inspect/vespa-fileheader-inspect.cpp b/searchlib/src/apps/vespa-fileheader-inspect/vespa-fileheader-inspect.cpp
index d1f5baa0531..e512cfcdffb 100644
--- a/searchlib/src/apps/vespa-fileheader-inspect/vespa-fileheader-inspect.cpp
+++ b/searchlib/src/apps/vespa-fileheader-inspect/vespa-fileheader-inspect.cpp
@@ -205,7 +205,7 @@ Application::getTypeString(const FileHeader::Tag &tag)
return "string";
default:
LOG_ASSERT(tag.getType() == FileHeader::Tag::TYPE_INTEGER);
- abort();
+ LOG_ABORT("should not be reached");
}
}
diff --git a/searchlib/src/apps/vespa-index-inspect/vespa-index-inspect.cpp b/searchlib/src/apps/vespa-index-inspect/vespa-index-inspect.cpp
index 469c8aeac4e..a746c6c1365 100644
--- a/searchlib/src/apps/vespa-index-inspect/vespa-index-inspect.cpp
+++ b/searchlib/src/apps/vespa-index-inspect/vespa-index-inspect.cpp
@@ -452,7 +452,7 @@ ShowPostingListSubApp::readPostings(const SchemaUtil::IndexIterator &index,
r.read();
}
if (!r.close())
- abort();
+ LOG_ABORT("should not be reached");
}
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/aggregation/AggregationResult.java b/searchlib/src/main/java/com/yahoo/searchlib/aggregation/AggregationResult.java
index 992caedfb51..264a9d4d4e9 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/aggregation/AggregationResult.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/aggregation/AggregationResult.java
@@ -11,7 +11,7 @@ import com.yahoo.vespa.objects.Serializer;
* <p>This is the aggregation super-class from which all types of aggregation inherits.</p>
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class AggregationResult extends ExpressionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/aggregation/AverageAggregationResult.java b/searchlib/src/main/java/com/yahoo/searchlib/aggregation/AverageAggregationResult.java
index 460a93f278e..bee64fd8d5d 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/aggregation/AverageAggregationResult.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/aggregation/AverageAggregationResult.java
@@ -12,7 +12,7 @@ import com.yahoo.vespa.objects.Serializer;
* This is an aggregated result holding the average of all results.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class AverageAggregationResult extends AggregationResult {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/aggregation/CountAggregationResult.java b/searchlib/src/main/java/com/yahoo/searchlib/aggregation/CountAggregationResult.java
index d9d7cd19edf..df92b1eee1d 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/aggregation/CountAggregationResult.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/aggregation/CountAggregationResult.java
@@ -11,7 +11,7 @@ import com.yahoo.vespa.objects.Serializer;
* This is an aggregated result holding the number of aggregated hits.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CountAggregationResult extends AggregationResult {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/aggregation/HitsAggregationResult.java b/searchlib/src/main/java/com/yahoo/searchlib/aggregation/HitsAggregationResult.java
index 64d9c3e2e1b..275f38f7350 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/aggregation/HitsAggregationResult.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/aggregation/HitsAggregationResult.java
@@ -16,7 +16,7 @@ import java.util.List;
*
* @author havardpe
* @author baldersheim
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class HitsAggregationResult extends AggregationResult {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/aggregation/MaxAggregationResult.java b/searchlib/src/main/java/com/yahoo/searchlib/aggregation/MaxAggregationResult.java
index 8831fcf1c02..8555920fb5d 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/aggregation/MaxAggregationResult.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/aggregation/MaxAggregationResult.java
@@ -11,7 +11,7 @@ import com.yahoo.vespa.objects.Serializer;
* This is an aggregated result holding the maximum result of the matching hits.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MaxAggregationResult extends AggregationResult {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/aggregation/MinAggregationResult.java b/searchlib/src/main/java/com/yahoo/searchlib/aggregation/MinAggregationResult.java
index e189c25bbff..95ec3a8aeda 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/aggregation/MinAggregationResult.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/aggregation/MinAggregationResult.java
@@ -11,7 +11,7 @@ import com.yahoo.vespa.objects.Serializer;
* This is an aggregated result holding the minimum result of the matching hits.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MinAggregationResult extends AggregationResult {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/aggregation/RawData.java b/searchlib/src/main/java/com/yahoo/searchlib/aggregation/RawData.java
index 9dd02593097..31e5c45c5fd 100755
--- a/searchlib/src/main/java/com/yahoo/searchlib/aggregation/RawData.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/aggregation/RawData.java
@@ -10,7 +10,7 @@ import java.util.Arrays;
* <p>This class encapsulates a byte array into a cloneable and comparable object. It also implements a sane {@link
* #hashCode()} and {@link #toString()}.</p>
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RawData implements Cloneable, Comparable<RawData> {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/aggregation/SumAggregationResult.java b/searchlib/src/main/java/com/yahoo/searchlib/aggregation/SumAggregationResult.java
index f0e23718b91..d0f03a9cd19 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/aggregation/SumAggregationResult.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/aggregation/SumAggregationResult.java
@@ -11,7 +11,7 @@ import com.yahoo.vespa.objects.Serializer;
* This is an aggregated result holding the sum of the aggregating expression for all matching hits.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class SumAggregationResult extends AggregationResult {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/aggregation/XorAggregationResult.java b/searchlib/src/main/java/com/yahoo/searchlib/aggregation/XorAggregationResult.java
index 3c9edcf1696..b76d32a1ee2 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/aggregation/XorAggregationResult.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/aggregation/XorAggregationResult.java
@@ -11,7 +11,7 @@ import com.yahoo.vespa.objects.Serializer;
* This is an aggregated result holding the xor of the aggregating expression for all matching hits.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class XorAggregationResult extends AggregationResult {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/AddFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/AddFunctionNode.java
index 0ce586579af..e098b80b71a 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/AddFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/AddFunctionNode.java
@@ -5,7 +5,7 @@ package com.yahoo.searchlib.expression;
* This function is an instruction to add all arguments.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class AddFunctionNode extends NumericFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/AndFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/AndFunctionNode.java
index 9ff67ed799a..3148c76c34f 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/AndFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/AndFunctionNode.java
@@ -5,7 +5,7 @@ package com.yahoo.searchlib.expression;
* This function is an instruction to perform bitwise AND on the result of all arguments in order.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class AndFunctionNode extends BitFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/AttributeNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/AttributeNode.java
index c88da9af211..33c2cee22ac 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/AttributeNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/AttributeNode.java
@@ -9,7 +9,7 @@ import com.yahoo.vespa.objects.Serializer;
* This function is an instruction to retrieve the value of a named attribute.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class AttributeNode extends FunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/BitFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/BitFunctionNode.java
index 43a80111761..c8ae724c0fd 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/BitFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/BitFunctionNode.java
@@ -5,7 +5,7 @@ package com.yahoo.searchlib.expression;
* This is an abstract super-class for all non-unary functions that operator on bit values.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class BitFunctionNode extends NumericFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/BucketResultNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/BucketResultNode.java
index d06c2d19da5..659228a9b15 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/BucketResultNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/BucketResultNode.java
@@ -6,7 +6,7 @@ package com.yahoo.searchlib.expression;
*
* @author <a href="mailto:havardpe@yahoo-inc.com">Haavard Pettersen</a>
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
abstract public class BucketResultNode extends ResultNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/CatFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/CatFunctionNode.java
index 8f0c43b9ee4..9f6395bb095 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/CatFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/CatFunctionNode.java
@@ -5,7 +5,7 @@ package com.yahoo.searchlib.expression;
* This function is an instruction to concatenate the bits of all arguments in order.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class CatFunctionNode extends MultiArgFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/ConstantNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/ConstantNode.java
index 5a33a368f5f..f2e91a2d019 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/ConstantNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/ConstantNode.java
@@ -9,7 +9,7 @@ import com.yahoo.vespa.objects.Serializer;
* This abstract expression node represents a function to execute.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ConstantNode extends ExpressionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/DivideFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/DivideFunctionNode.java
index b3ba626962a..1bb97c02b50 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/DivideFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/DivideFunctionNode.java
@@ -5,7 +5,7 @@ package com.yahoo.searchlib.expression;
* This function is an instruction to divide the arguments in order.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DivideFunctionNode extends NumericFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/DocumentAccessorNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/DocumentAccessorNode.java
index 2729ac78447..e9360ba6dfd 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/DocumentAccessorNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/DocumentAccessorNode.java
@@ -6,7 +6,7 @@ package com.yahoo.searchlib.expression;
* implementation of this.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class DocumentAccessorNode extends ExpressionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/DocumentFieldNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/DocumentFieldNode.java
index 46b05eaf3d0..3e23025f9e7 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/DocumentFieldNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/DocumentFieldNode.java
@@ -9,7 +9,7 @@ import com.yahoo.vespa.objects.Serializer;
* The node is a request to retrieve the content of a document field.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class DocumentFieldNode extends DocumentAccessorNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/ExpressionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/ExpressionNode.java
index 2e4519597ef..e415ae9a964 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/ExpressionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/ExpressionNode.java
@@ -13,7 +13,7 @@ import java.io.Serializable;
* happens in the C++ backend. This class hierarchy is for <b>building</b> the expression tree to pass to the backend.
*
* @author baldersheim
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public abstract class ExpressionNode extends Identifiable implements Serializable {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/FixedWidthBucketFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/FixedWidthBucketFunctionNode.java
index 543eaa886f7..6bf7670f7cc 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/FixedWidthBucketFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/FixedWidthBucketFunctionNode.java
@@ -9,7 +9,7 @@ import com.yahoo.vespa.objects.Serializer;
* This function assign a fixed width bucket to each input value
*
* @author <a href="mailto:havardpe@yahoo-inc.com">Haavard Pettersen</a>
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class FixedWidthBucketFunctionNode extends UnaryFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/FloatBucketResultNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/FloatBucketResultNode.java
index d11a2493e84..455a8a42505 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/FloatBucketResultNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/FloatBucketResultNode.java
@@ -9,7 +9,7 @@ import com.yahoo.vespa.objects.Serializer;
* This result holds a float value.
*
* @author <a href="mailto:havardpe@yahoo-inc.com">Haavard Pettersen</a>
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class FloatBucketResultNode extends BucketResultNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/FloatBucketResultNodeVector.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/FloatBucketResultNodeVector.java
index a50e65c0ea7..443358c7628 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/FloatBucketResultNodeVector.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/FloatBucketResultNodeVector.java
@@ -10,7 +10,7 @@ import java.util.ArrayList;
* This result holds nothing.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class FloatBucketResultNodeVector extends ResultNodeVector {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/FloatResultNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/FloatResultNode.java
index 551b20ae480..bcc4f061719 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/FloatResultNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/FloatResultNode.java
@@ -11,7 +11,7 @@ import java.nio.ByteBuffer;
* This result holds a float value.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class FloatResultNode extends NumericResultNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/FloatResultNodeVector.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/FloatResultNodeVector.java
index 25d05e61da3..1cb978303df 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/FloatResultNodeVector.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/FloatResultNodeVector.java
@@ -10,7 +10,7 @@ import java.util.ArrayList;
* This result holds nothing.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class FloatResultNodeVector extends ResultNodeVector {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/FunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/FunctionNode.java
index 45b29e78440..1c2b5a9d72a 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/FunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/FunctionNode.java
@@ -9,7 +9,7 @@ import com.yahoo.vespa.objects.Serializer;
* This abstract expression node represents a function to execute.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class FunctionNode extends ExpressionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/GetDocIdNamespaceSpecificFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/GetDocIdNamespaceSpecificFunctionNode.java
index e549b6b9a27..b9b5707956d 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/GetDocIdNamespaceSpecificFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/GetDocIdNamespaceSpecificFunctionNode.java
@@ -9,7 +9,7 @@ import com.yahoo.vespa.objects.Serializer;
* The node is a request to retrieve the namespace-specific content of a document id.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GetDocIdNamespaceSpecificFunctionNode extends DocumentAccessorNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/GetYMUMChecksumFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/GetYMUMChecksumFunctionNode.java
index e58474c5a0e..9fb47d0401c 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/GetYMUMChecksumFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/GetYMUMChecksumFunctionNode.java
@@ -9,7 +9,7 @@ import com.yahoo.vespa.objects.Serializer;
* This node is a request to retrieve the YMUM checksum of a document.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GetYMUMChecksumFunctionNode extends DocumentAccessorNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/Int16ResultNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/Int16ResultNode.java
index a2103ca0cdd..3e15c35b25e 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/Int16ResultNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/Int16ResultNode.java
@@ -11,7 +11,7 @@ import java.nio.ByteBuffer;
* This result holds an integer value.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Int16ResultNode extends NumericResultNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/Int16ResultNodeVector.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/Int16ResultNodeVector.java
index d7ac541d3aa..2842efe710f 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/Int16ResultNodeVector.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/Int16ResultNodeVector.java
@@ -10,7 +10,7 @@ import java.util.ArrayList;
* This result holds nothing.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Int16ResultNodeVector extends ResultNodeVector {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/Int32ResultNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/Int32ResultNode.java
index ccb54e42d1c..111d3f5c5f6 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/Int32ResultNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/Int32ResultNode.java
@@ -11,7 +11,7 @@ import java.nio.ByteBuffer;
* This result holds an integer value.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Int32ResultNode extends NumericResultNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/Int32ResultNodeVector.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/Int32ResultNodeVector.java
index ad1755490ee..2dd9e577cf8 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/Int32ResultNodeVector.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/Int32ResultNodeVector.java
@@ -10,7 +10,7 @@ import java.util.ArrayList;
* This result holds nothing.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Int32ResultNodeVector extends ResultNodeVector {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/Int8ResultNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/Int8ResultNode.java
index a36ce210dbe..88920323703 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/Int8ResultNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/Int8ResultNode.java
@@ -11,7 +11,7 @@ import java.nio.ByteBuffer;
* This result holds an integer value.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Int8ResultNode extends NumericResultNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/Int8ResultNodeVector.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/Int8ResultNodeVector.java
index b3dfd0849ad..33734c15ff1 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/Int8ResultNodeVector.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/Int8ResultNodeVector.java
@@ -10,7 +10,7 @@ import java.util.ArrayList;
* This result holds nothing.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Int8ResultNodeVector extends ResultNodeVector {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerBucketResultNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerBucketResultNode.java
index e9840d9808d..b8f41d8b065 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerBucketResultNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerBucketResultNode.java
@@ -9,7 +9,7 @@ import com.yahoo.vespa.objects.Serializer;
* This is an integer bucket value
*
* @author <a href="mailto:havardpe@yahoo-inc.com">Haavard Pettersen</a>
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class IntegerBucketResultNode extends BucketResultNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerBucketResultNodeVector.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerBucketResultNodeVector.java
index 226e376abec..c999fdfc6e2 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerBucketResultNodeVector.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerBucketResultNodeVector.java
@@ -10,7 +10,7 @@ import java.util.ArrayList;
* This result holds nothing.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class IntegerBucketResultNodeVector extends ResultNodeVector {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerResultNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerResultNode.java
index 039e52f57af..62534377d38 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerResultNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerResultNode.java
@@ -11,7 +11,7 @@ import java.nio.ByteBuffer;
* This result holds an integer value.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class IntegerResultNode extends NumericResultNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerResultNodeVector.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerResultNodeVector.java
index 14ea5ee76fc..3323460da05 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerResultNodeVector.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerResultNodeVector.java
@@ -10,7 +10,7 @@ import java.util.ArrayList;
* This result holds nothing.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class IntegerResultNodeVector extends ResultNodeVector {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/MD5BitFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/MD5BitFunctionNode.java
index d6d059d2353..7a941a9de4b 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/MD5BitFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/MD5BitFunctionNode.java
@@ -5,7 +5,7 @@ package com.yahoo.searchlib.expression;
* This function is a request to calculate the MD5 of the result of its argument.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MD5BitFunctionNode extends UnaryBitFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/MathFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/MathFunctionNode.java
index 21c612161f0..de3f494196f 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/MathFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/MathFunctionNode.java
@@ -9,7 +9,7 @@ import com.yahoo.vespa.objects.Serializer;
* This function is an instruction to negate its argument.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MathFunctionNode extends MultiArgFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/MaxFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/MaxFunctionNode.java
index ab48a158c68..59a6c0dc708 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/MaxFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/MaxFunctionNode.java
@@ -5,7 +5,7 @@ package com.yahoo.searchlib.expression;
* This function is an instruction to return the maximum value of all its arguments.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MaxFunctionNode extends NumericFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/MinFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/MinFunctionNode.java
index acc4a7ea390..bf131be474c 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/MinFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/MinFunctionNode.java
@@ -5,7 +5,7 @@ package com.yahoo.searchlib.expression;
* This function is an instruction to return the minimum value of all its arguments.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MinFunctionNode extends NumericFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/ModuloFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/ModuloFunctionNode.java
index 02df613428b..7534cf68186 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/ModuloFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/ModuloFunctionNode.java
@@ -5,7 +5,7 @@ package com.yahoo.searchlib.expression;
* This function is an instruction to modulo the arguments in order.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ModuloFunctionNode extends NumericFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/MultiArgFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/MultiArgFunctionNode.java
index ead6fd1ef54..68cc9ba445c 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/MultiArgFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/MultiArgFunctionNode.java
@@ -11,7 +11,7 @@ import java.util.List;
* necessary API for manipulating arguments.</p>
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class MultiArgFunctionNode extends FunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/MultiplyFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/MultiplyFunctionNode.java
index 6a1fbcfd363..21d6cf31ae1 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/MultiplyFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/MultiplyFunctionNode.java
@@ -5,7 +5,7 @@ package com.yahoo.searchlib.expression;
* This function is an instruction to multiply all arguments.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class MultiplyFunctionNode extends NumericFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/NegateFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/NegateFunctionNode.java
index 052cecb4e59..a5678598f25 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/NegateFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/NegateFunctionNode.java
@@ -5,7 +5,7 @@ package com.yahoo.searchlib.expression;
* This function is an instruction to negate its argument.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NegateFunctionNode extends UnaryFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/NormalizeSubjectFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/NormalizeSubjectFunctionNode.java
index 179a6917635..006f8d5e70a 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/NormalizeSubjectFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/NormalizeSubjectFunctionNode.java
@@ -5,7 +5,7 @@ package com.yahoo.searchlib.expression;
* This function is an instruction to negate its argument.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NormalizeSubjectFunctionNode extends UnaryFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/NullResultNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/NullResultNode.java
index 21ee07d7a86..6bbc93b043a 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/NullResultNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/NullResultNode.java
@@ -7,7 +7,7 @@ import com.yahoo.vespa.objects.ObjectVisitor;
* This result holds nothing.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NullResultNode extends ResultNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/NumElemFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/NumElemFunctionNode.java
index 6b79530ac8e..927586050aa 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/NumElemFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/NumElemFunctionNode.java
@@ -5,7 +5,7 @@ package com.yahoo.searchlib.expression;
* This function is an instruction to negate its argument.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class NumElemFunctionNode extends UnaryFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/NumericResultNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/NumericResultNode.java
index 7777e18626c..01a09fc220f 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/NumericResultNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/NumericResultNode.java
@@ -5,7 +5,7 @@ package com.yahoo.searchlib.expression;
* This is a superclass for all numerical results.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
abstract public class NumericResultNode extends SingleResultNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/OrFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/OrFunctionNode.java
index 9d9811e39c9..50b94e6fdf2 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/OrFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/OrFunctionNode.java
@@ -5,7 +5,7 @@ package com.yahoo.searchlib.expression;
* This function is an instruction to perform bitwise OR on the result of all arguments.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class OrFunctionNode extends BitFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/RangeBucketPreDefFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/RangeBucketPreDefFunctionNode.java
index 033338eeff5..5204229d1cf 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/RangeBucketPreDefFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/RangeBucketPreDefFunctionNode.java
@@ -9,7 +9,7 @@ import com.yahoo.vespa.objects.Serializer;
* This function assign a fixed width bucket to each input value
*
* @author <a href="mailto:havardpe@yahoo-inc.com">Haavard Pettersen</a>
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RangeBucketPreDefFunctionNode extends UnaryFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/RawResultNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/RawResultNode.java
index d3986c90255..2ff573218e3 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/RawResultNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/RawResultNode.java
@@ -12,7 +12,7 @@ import java.util.Arrays;
* This result holds a byte array value.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RawResultNode extends SingleResultNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/RawResultNodeVector.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/RawResultNodeVector.java
index d517e033737..4bb9fc78098 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/RawResultNodeVector.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/RawResultNodeVector.java
@@ -10,7 +10,7 @@ import java.util.ArrayList;
* This result holds nothing.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RawResultNodeVector extends ResultNodeVector {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/RelevanceNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/RelevanceNode.java
index 355095f58ca..e4d3328a78b 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/RelevanceNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/RelevanceNode.java
@@ -9,7 +9,7 @@ import com.yahoo.vespa.objects.Serializer;
* This abstract expression node represents a function to execute.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RelevanceNode extends ExpressionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/ResultNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/ResultNode.java
index cb1c2ecc947..f76eb360086 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/ResultNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/ResultNode.java
@@ -7,7 +7,7 @@ import com.yahoo.vespa.objects.Identifiable;
* This abstract expression node represents the result value of execution.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class ResultNode extends Identifiable implements Comparable<ResultNode> {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/ResultNodeVector.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/ResultNodeVector.java
index 743ec1ae41d..99053466307 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/ResultNodeVector.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/ResultNodeVector.java
@@ -5,7 +5,7 @@ package com.yahoo.searchlib.expression;
* This result holds nothing.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class ResultNodeVector extends ResultNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/StrCatFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/StrCatFunctionNode.java
index 260ae51184c..2e2a7d53851 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/StrCatFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/StrCatFunctionNode.java
@@ -5,7 +5,7 @@ package com.yahoo.searchlib.expression;
* This function is an instruction to concatenate the bits of all arguments in order.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class StrCatFunctionNode extends MultiArgFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/StrLenFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/StrLenFunctionNode.java
index 789847ca70a..5c0ee7bc633 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/StrLenFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/StrLenFunctionNode.java
@@ -5,7 +5,7 @@ package com.yahoo.searchlib.expression;
* This function is an instruction to negate its argument.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class StrLenFunctionNode extends UnaryFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/StringBucketResultNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/StringBucketResultNode.java
index fdc56a988c3..279f8b17fcf 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/StringBucketResultNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/StringBucketResultNode.java
@@ -9,7 +9,7 @@ import com.yahoo.vespa.objects.Serializer;
* This is an integer bucket value
*
* @author <a href="mailto:havardpe@yahoo-inc.com">Haavard Pettersen</a>
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class StringBucketResultNode extends BucketResultNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/StringBucketResultNodeVector.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/StringBucketResultNodeVector.java
index 6bab13d0976..9b530164e5e 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/StringBucketResultNodeVector.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/StringBucketResultNodeVector.java
@@ -10,7 +10,7 @@ import java.util.ArrayList;
* This result holds nothing.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class StringBucketResultNodeVector extends ResultNodeVector {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/StringResultNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/StringResultNode.java
index bfc58925c7e..40d424a2759 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/StringResultNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/StringResultNode.java
@@ -10,7 +10,7 @@ import com.yahoo.vespa.objects.Serializer;
* This result holds a string.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class StringResultNode extends SingleResultNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/StringResultNodeVector.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/StringResultNodeVector.java
index 6c0b1664328..2cba466f93a 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/StringResultNodeVector.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/StringResultNodeVector.java
@@ -10,7 +10,7 @@ import java.util.ArrayList;
* This result holds nothing.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class StringResultNodeVector extends ResultNodeVector {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/TimeStampFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/TimeStampFunctionNode.java
index 4f27ddba905..efff9b954b3 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/TimeStampFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/TimeStampFunctionNode.java
@@ -9,7 +9,7 @@ import com.yahoo.vespa.objects.Serializer;
* <p>This function assign a fixed width bucket to each input value.</p>
*
* @author <a href="mailto:havardpe@yahoo-inc.com">Haavard Pettersen</a>
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class TimeStampFunctionNode extends UnaryFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/ToFloatFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/ToFloatFunctionNode.java
index 2c87036143f..f938f33979f 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/ToFloatFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/ToFloatFunctionNode.java
@@ -5,7 +5,7 @@ package com.yahoo.searchlib.expression;
* This function is an instruction to negate its argument.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ToFloatFunctionNode extends UnaryFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/ToIntFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/ToIntFunctionNode.java
index b9111ecfcf7..060480e242e 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/ToIntFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/ToIntFunctionNode.java
@@ -5,7 +5,7 @@ package com.yahoo.searchlib.expression;
* This function is an instruction to negate its argument.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ToIntFunctionNode extends UnaryFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/ToStringFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/ToStringFunctionNode.java
index 42d5aa7ad4b..66cd502758e 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/ToStringFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/ToStringFunctionNode.java
@@ -5,7 +5,7 @@ package com.yahoo.searchlib.expression;
* This function is an instruction to negate its argument.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ToStringFunctionNode extends UnaryFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/UnaryBitFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/UnaryBitFunctionNode.java
index 13b20adc27c..223282293ce 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/UnaryBitFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/UnaryBitFunctionNode.java
@@ -9,7 +9,7 @@ import com.yahoo.vespa.objects.Serializer;
* This is an abstract super-class for all unary functions that operator on bit values.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class UnaryBitFunctionNode extends UnaryFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/UnaryFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/UnaryFunctionNode.java
index 1cd8fd269a2..f2e3cb0f3bb 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/UnaryFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/UnaryFunctionNode.java
@@ -5,7 +5,7 @@ package com.yahoo.searchlib.expression;
* This is an abstract super-class for all functions that accept only a single argument.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class UnaryFunctionNode extends MultiArgFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/XorBitFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/XorBitFunctionNode.java
index 9b4a2868111..8976214c67f 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/XorBitFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/XorBitFunctionNode.java
@@ -8,7 +8,7 @@ package com.yahoo.searchlib.expression;
* the result.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class XorBitFunctionNode extends UnaryBitFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/XorFunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/XorFunctionNode.java
index 63f08ee2c10..1000cfb9ff8 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/expression/XorFunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/XorFunctionNode.java
@@ -5,7 +5,7 @@ package com.yahoo.searchlib.expression;
* This function is an instruction to perform bitwise XOR on the result of all arguments in order.
*
* @author baldersheim
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class XorFunctionNode extends BitFunctionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/gbdt/GbdtConverter.java b/searchlib/src/main/java/com/yahoo/searchlib/gbdt/GbdtConverter.java
index ae562093cc4..3f1df322d73 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/gbdt/GbdtConverter.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/gbdt/GbdtConverter.java
@@ -6,7 +6,7 @@ import com.yahoo.yolean.Exceptions;
import java.io.FileNotFoundException;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GbdtConverter {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/gbdt/GbdtModel.java b/searchlib/src/main/java/com/yahoo/searchlib/gbdt/GbdtModel.java
index cedd156848e..e7cc9642e3c 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/gbdt/GbdtModel.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/gbdt/GbdtModel.java
@@ -14,7 +14,7 @@ import java.util.List;
import java.util.Optional;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GbdtModel {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/gbdt/ResponseNode.java b/searchlib/src/main/java/com/yahoo/searchlib/gbdt/ResponseNode.java
index 61abd73f797..0ff692d3c94 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/gbdt/ResponseNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/gbdt/ResponseNode.java
@@ -6,7 +6,7 @@ import org.w3c.dom.Node;
import java.util.Optional;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ResponseNode extends TreeNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/gbdt/XmlHelper.java b/searchlib/src/main/java/com/yahoo/searchlib/gbdt/XmlHelper.java
index 75c0b667e2c..de2a1053fa4 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/gbdt/XmlHelper.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/gbdt/XmlHelper.java
@@ -20,7 +20,7 @@ import java.util.List;
import java.util.Optional;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
abstract class XmlHelper {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/ExpressionFunction.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/ExpressionFunction.java
index a3ab21a5966..f2239776d48 100755
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/ExpressionFunction.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/ExpressionFunction.java
@@ -14,7 +14,7 @@ import java.util.*;
/**
* A function defined by a ranking expression
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
* @author bratseth
*/
public class ExpressionFunction {
@@ -110,7 +110,7 @@ public class ExpressionFunction {
@Override
public String toString() {
- return name;
+ return "function '" + name + "'";
}
/**
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/FeatureList.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/FeatureList.java
index f0532d9d433..85c7731a298 100755
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/FeatureList.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/FeatureList.java
@@ -15,7 +15,7 @@ import java.util.List;
/**
* Encapsulates the production rule 'featureList()' int the RankingExpressionParser.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
@Beta
public class FeatureList implements Iterable<ReferenceNode> {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/RankingExpression.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/RankingExpression.java
index 6fa5b1196fa..38e642c8145 100755
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/RankingExpression.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/RankingExpression.java
@@ -57,10 +57,10 @@ ArrayContext contextPrototype;
// Create reusable, gbdt optimized expression and context.
// The expression is multithread-safe while the context created is not
try {
- RankingExpression expression=new RankingExpression("10*if(i&gt;35,if(i&gt;one,if(i&gt;=670,4,8),if(i&gt;8000,5,3)),if(i==478,90,91))");
- ArrayContext contextPrototype=new ArrayContext(expression);
- ExpressionOptimizer optimizer=new ExpressionOptimizer(); // Increases evaluation speed of gbdt form expressions by 3-4x
- OptimizationReport triviaAboutTheOptimization=optimizer.optimize(expression,contextPrototype);
+ RankingExpression expression = new RankingExpression("10*if(i&gt;35,if(i&gt;one,if(i&gt;=670,4,8),if(i&gt;8000,5,3)),if(i==478,90,91))");
+ ArrayContext contextPrototype = new ArrayContext(expression);
+ ExpressionOptimizer optimizer = new ExpressionOptimizer(); // Increases evaluation speed of gbdt form expressions by 3-4x
+ OptimizationReport triviaAboutTheOptimization = optimizer.optimize(expression,contextPrototype);
}
catch (ParseException e) {
throw new RuntimeException(e);
@@ -69,12 +69,12 @@ catch (ParseException e) {
...
// Execution (many)
-context=contextPrototype.clone(); // If evaluation is multithreaded - skip this if execution is single-threaded
+context = contextPrototype.clone(); // If evaluation is multithreaded - skip this if execution is single-threaded
context.put("one",1d);
-double result=expression.evaluate(context);
+double result = expression.evaluate(context);
</code></pre>
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
* @author bratseth
*/
public class RankingExpression implements Serializable {
@@ -276,7 +276,7 @@ public class RankingExpression implements Serializable {
* Returns the value of evaluating this expression over the given context.
*
* @param context The variable bindings to use for this evaluation.
- * @return The evaluation result.
+ * @return the evaluation result.
* @throws IllegalArgumentException if there are variables which are not bound in the given map
*/
public Value evaluate(Context context) {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/AbstractArrayContext.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/AbstractArrayContext.java
index ed9fa346c11..41bf827748a 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/AbstractArrayContext.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/AbstractArrayContext.java
@@ -7,8 +7,6 @@ import com.yahoo.searchlib.rankingexpression.rule.CompositeNode;
import com.yahoo.searchlib.rankingexpression.rule.ExpressionNode;
import com.yahoo.searchlib.rankingexpression.rule.ReferenceNode;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
@@ -20,19 +18,15 @@ import java.util.Set;
*
* @author bratseth
*/
-public abstract class AbstractArrayContext extends Context implements Cloneable {
+public abstract class AbstractArrayContext extends Context implements Cloneable, ContextIndex {
private final boolean ignoreUnknownValues;
- /** The mapping from variable name to index */
- private final ImmutableMap<String, Integer> nameToIndex;
-
- /** The current values set, pre-converted to doubles */
- private double[] doubleValues;
-
/** The name of the ranking expression this was created for */
private final String rankingExpressionName;
+ private IndexedBindings indexedBindings;
+
/**
* Create a fast lookup context for an expression.
* This instance should be reused indefinitely by a single thread.
@@ -53,44 +47,51 @@ public abstract class AbstractArrayContext extends Context implements Cloneable
protected AbstractArrayContext(RankingExpression expression, boolean ignoreUnknownValues) {
this.ignoreUnknownValues = ignoreUnknownValues;
this.rankingExpressionName = expression.getName();
- Set<String> variables = new LinkedHashSet<>();
- extractVariables(expression.getRoot(),variables);
+ this.indexedBindings = new IndexedBindings(expression);
+ }
- doubleValues = new double[variables.size()];
+ protected final Map<String, Integer> nameToIndex() { return indexedBindings.nameToIndex(); }
+ protected final double[] doubleValues() { return indexedBindings.doubleValues(); }
+ protected final boolean ignoreUnknownValues() { return ignoreUnknownValues; }
- int i = 0;
- ImmutableMap.Builder<String, Integer> nameToIndexBuilder = new ImmutableMap.Builder<>();
- for (String variable : variables)
- nameToIndexBuilder.put(variable,i++);
- nameToIndex = nameToIndexBuilder.build();
+ @Override
+ public Set<String> names() {
+ return indexedBindings.names();
}
- private void extractVariables(ExpressionNode node,Set<String> variables) {
- if (node instanceof ReferenceNode) {
- ReferenceNode fNode=(ReferenceNode)node;
- if (fNode.getArguments().expressions().size()>0)
- throw new UnsupportedOperationException("Array lookup is not supported with features having arguments)");
- variables.add(fNode.toString());
- }
- else if (node instanceof CompositeNode) {
- CompositeNode cNode=(CompositeNode)node;
- for (ExpressionNode child : cNode.children())
- extractVariables(child,variables);
- }
+ /**
+ * Returns the index from a name.
+ *
+ * @throws NullPointerException is this name is not known to this context
+ */
+ @Override
+ public final int getIndex(String name) { return indexedBindings.nameToIndex.get(name); }
+
+ /** Returns the max number of variables which may be set in this */
+ @Override
+ public int size() { return indexedBindings.size(); }
+
+ /** Perform a fast lookup directly of the value as a double. This is faster than get(index).asDouble() */
+ @Override
+ public double getDouble(int index) {
+ return indexedBindings.getDouble(index);
}
- protected final Map<String, Integer> nameToIndex() { return nameToIndex; }
- protected final double[] doubleValues() { return doubleValues; }
- protected final boolean ignoreUnknownValues() { return ignoreUnknownValues; }
+ @Override
+ public String toString() {
+ return "fast lookup context for ranking expression '" + rankingExpressionName +
+ "' [" + size() + " variables]";
+ }
/**
* Creates a clone of this context suitable for evaluating against the same ranking expression
* in a different thread (i.e, name name to index map, different value set.
*/
+ @Override
public AbstractArrayContext clone() {
try {
- AbstractArrayContext clone=(AbstractArrayContext)super.clone();
- clone.doubleValues=new double[nameToIndex.size()];
+ AbstractArrayContext clone = (AbstractArrayContext)super.clone();
+ clone.indexedBindings = indexedBindings.clone();
return clone;
}
catch (CloneNotSupportedException e) {
@@ -98,34 +99,64 @@ public abstract class AbstractArrayContext extends Context implements Cloneable
}
}
- public Set<String> names() {
- return nameToIndex.keySet();
- }
+ private static class IndexedBindings implements Cloneable {
- /**
- * Returns the index from a name.
- *
- * @throws NullPointerException is this name is not known to this context
- */
- public final int getIndex(String name) {
- return nameToIndex.get(name);
- }
+ /** The mapping from variable name to index */
+ private final ImmutableMap<String, Integer> nameToIndex;
- /** Returns the max number of variables which may be set in this */
- public int size() {
- return doubleValues.length;
- }
+ /** The current values set, pre-converted to doubles */
+ private double[] doubleValues;
- /** Perform a fast lookup directly of the value as a double. This is faster than get(index).asDouble() */
- @Override
- public double getDouble(int index) {
- return doubleValues[index];
- }
+ public IndexedBindings(RankingExpression expression) {
+ Set<String> bindTargets = new LinkedHashSet<>();
+ extractBindTargets(expression.getRoot(), bindTargets);
+
+ doubleValues = new double[bindTargets.size()];
+
+ int i = 0;
+ ImmutableMap.Builder<String, Integer> nameToIndexBuilder = new ImmutableMap.Builder<>();
+ for (String variable : bindTargets)
+ nameToIndexBuilder.put(variable,i++);
+ nameToIndex = nameToIndexBuilder.build();
+ }
+
+ private void extractBindTargets(ExpressionNode node, Set<String> bindTargets) {
+ if (node instanceof ReferenceNode) {
+ if (((ReferenceNode)node).getArguments().expressions().size() > 0)
+ throw new UnsupportedOperationException("Can not bind " + node +
+ ": Array lookup is not supported with features having arguments)");
+ bindTargets.add(node.toString());
+ }
+ else if (node instanceof CompositeNode) {
+ CompositeNode cNode = (CompositeNode)node;
+ for (ExpressionNode child : cNode.children())
+ extractBindTargets(child, bindTargets);
+ }
+ }
+
+ public Map<String, Integer> nameToIndex() { return nameToIndex; }
+ public double[] doubleValues() { return doubleValues; }
+ public Set<String> names() { return nameToIndex.keySet(); }
+ public int getIndex(String name) { return nameToIndex.get(name); }
+ public int size() { return doubleValues.length; }
+ public double getDouble(int index) { return doubleValues[index]; }
+
+ /**
+ * Creates a clone of this context suitable for evaluating against the same ranking expression
+ * in a different thread (i.e, name name to index map, different value set.
+ */
+ @Override
+ public IndexedBindings clone() {
+ try {
+ IndexedBindings clone = (IndexedBindings)super.clone();
+ clone.doubleValues = new double[nameToIndex.size()];
+ return clone;
+ }
+ catch (CloneNotSupportedException e) {
+ throw new RuntimeException("Programming error");
+ }
+ }
- @Override
- public String toString() {
- return "fast lookup context for ranking expression '" + rankingExpressionName +
- "' [" + doubleValues.length + " variables]";
}
}
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/ArrayContext.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/ArrayContext.java
index ee5952d9aea..237c3a1d0b1 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/ArrayContext.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/ArrayContext.java
@@ -119,7 +119,7 @@ public class ArrayContext extends AbstractArrayContext implements Cloneable {
public ArrayContext clone() {
ArrayContext clone = (ArrayContext)super.clone();
clone.values = new Value[nameToIndex().size()];
- Arrays.fill(values,constantZero);
+ Arrays.fill(values, constantZero);
return clone;
}
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/ContextIndex.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/ContextIndex.java
new file mode 100644
index 00000000000..ad6facbf0af
--- /dev/null
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/ContextIndex.java
@@ -0,0 +1,26 @@
+package com.yahoo.searchlib.rankingexpression.evaluation;
+
+/**
+ * Indexed context lookup methods.
+ * Any context which implements these methods supports optimizations where map lookups
+ * are replaced by indexed lookups.
+ *
+ * @author bratseth
+ */
+public interface ContextIndex {
+
+ /** Returns the number of bound variables in this */
+ int size();
+
+ /**
+ * Returns the index from a name.
+ *
+ * @throws NullPointerException is this name is not known to this context
+ */
+ int getIndex(String name);
+
+ Value get(int index);
+
+ double getDouble(int index);
+
+}
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/ExpressionOptimizer.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/ExpressionOptimizer.java
index 836aadd9f70..b82173eabd5 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/ExpressionOptimizer.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/ExpressionOptimizer.java
@@ -44,7 +44,7 @@ public class ExpressionOptimizer {
return null;
}
- public OptimizationReport optimize(RankingExpression expression, AbstractArrayContext arrayContext) {
+ public OptimizationReport optimize(RankingExpression expression, ContextIndex arrayContext) {
OptimizationReport report = new OptimizationReport();
// Note: Order of optimizations matter
gbdtOptimizer.optimize(expression, arrayContext, report);
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/Optimizer.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/Optimizer.java
index fd9c02100f7..044b5b589a5 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/Optimizer.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/Optimizer.java
@@ -18,6 +18,6 @@ public abstract class Optimizer {
/** Returns whether this is enabled */
public boolean isEnabled() { return enabled; }
- public abstract void optimize(RankingExpression expression, AbstractArrayContext context, OptimizationReport report);
+ public abstract void optimize(RankingExpression expression, ContextIndex context, OptimizationReport report);
}
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/Value.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/Value.java
index e5a9e6a5ef1..7809cdd4e1b 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/Value.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/Value.java
@@ -115,4 +115,12 @@ public abstract class Value {
return new DoubleValue(Double.parseDouble(value));
}
+ public static Value of(Tensor tensor) {
+ return new TensorValue(tensor);
+ }
+
+ public static Value of(double scalar) {
+ return new DoubleValue(scalar);
+ }
+
}
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/gbdtoptimization/GBDTForestOptimizer.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/gbdtoptimization/GBDTForestOptimizer.java
index 8999be4745a..bb8b91eecab 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/gbdtoptimization/GBDTForestOptimizer.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/gbdtoptimization/GBDTForestOptimizer.java
@@ -2,8 +2,7 @@
package com.yahoo.searchlib.rankingexpression.evaluation.gbdtoptimization;
import com.yahoo.searchlib.rankingexpression.RankingExpression;
-import com.yahoo.searchlib.rankingexpression.evaluation.AbstractArrayContext;
-import com.yahoo.searchlib.rankingexpression.evaluation.ArrayContext;
+import com.yahoo.searchlib.rankingexpression.evaluation.ContextIndex;
import com.yahoo.searchlib.rankingexpression.evaluation.OptimizationReport;
import com.yahoo.searchlib.rankingexpression.evaluation.Optimizer;
import com.yahoo.searchlib.rankingexpression.rule.ArithmeticNode;
@@ -34,7 +33,7 @@ public class GBDTForestOptimizer extends Optimizer {
* @param report the optimization report to which actions of this is logged
*/
@Override
- public void optimize(RankingExpression expression, AbstractArrayContext context, OptimizationReport report) {
+ public void optimize(RankingExpression expression, ContextIndex context, OptimizationReport report) {
if ( ! isEnabled()) return;
this.report = report;
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/gbdtoptimization/GBDTOptimizer.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/gbdtoptimization/GBDTOptimizer.java
index 74af3e576c1..787818b0f42 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/gbdtoptimization/GBDTOptimizer.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/gbdtoptimization/GBDTOptimizer.java
@@ -33,7 +33,7 @@ public class GBDTOptimizer extends Optimizer {
* @param report the optimization report to which actions of this is logged
*/
@Override
- public void optimize(RankingExpression expression, AbstractArrayContext context, OptimizationReport report) {
+ public void optimize(RankingExpression expression, ContextIndex context, OptimizationReport report) {
if (!isEnabled()) return;
this.report = report;
@@ -60,7 +60,7 @@ public class GBDTOptimizer extends Optimizer {
*
* @return the optimized expression
*/
- private ExpressionNode optimize(ExpressionNode node, AbstractArrayContext context) {
+ private ExpressionNode optimize(ExpressionNode node, ContextIndex context) {
if (node instanceof ArithmeticNode) {
Iterator<ExpressionNode> childIt = ((ArithmeticNode)node).children().iterator();
ExpressionNode ret = optimize(childIt.next(), context);
@@ -77,7 +77,7 @@ public class GBDTOptimizer extends Optimizer {
return node;
}
- private ExpressionNode createGBDTNode(IfNode cNode, AbstractArrayContext context) {
+ private ExpressionNode createGBDTNode(IfNode cNode,ContextIndex context) {
List<Double> values = new ArrayList<>();
try {
consumeNode(cNode, values, context);
@@ -93,7 +93,7 @@ public class GBDTOptimizer extends Optimizer {
/**
* Recursively consume nodes into the value list Returns the number of values produced by this.
*/
- private int consumeNode(ExpressionNode node, List<Double> values, AbstractArrayContext context) {
+ private int consumeNode(ExpressionNode node, List<Double> values, ContextIndex context) {
int beforeIndex = values.size();
if ( node instanceof IfNode) {
IfNode ifNode = (IfNode)node;
@@ -113,7 +113,7 @@ public class GBDTOptimizer extends Optimizer {
}
/** Consumes the if condition and return the size of the values resulting, for convenience */
- private int consumeIfCondition(ExpressionNode condition, List<Double> values, AbstractArrayContext context) {
+ private int consumeIfCondition(ExpressionNode condition, List<Double> values, ContextIndex context) {
if (condition instanceof ComparisonNode) {
ComparisonNode comparison = (ComparisonNode)condition;
if (comparison.getOperator() == TruthOperator.SMALLER)
@@ -138,7 +138,7 @@ public class GBDTOptimizer extends Optimizer {
return values.size();
}
- private double getVariableIndex(ExpressionNode node, AbstractArrayContext context) {
+ private double getVariableIndex(ExpressionNode node, ContextIndex context) {
if (!(node instanceof ReferenceNode)) {
throw new IllegalArgumentException("Contained a left-hand comparison expression " +
"which was not a feature value but was: " + node);
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ConstantNode.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ConstantNode.java
index 64ad4d1ffce..9ae6cc06f29 100755
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ConstantNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ConstantNode.java
@@ -12,7 +12,7 @@ import java.util.Deque;
/**
* A node which holds a constant (frozen) value.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public final class ConstantNode extends ExpressionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/EmbracedNode.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/EmbracedNode.java
index 4844ad82b27..d306e067d16 100755
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/EmbracedNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/EmbracedNode.java
@@ -14,7 +14,7 @@ import java.util.List;
/**
* This class represents another expression enclosed in braces.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public final class EmbracedNode extends CompositeNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ExpressionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ExpressionNode.java
index a02108a8bda..c432e49fe2b 100755
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ExpressionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ExpressionNode.java
@@ -14,7 +14,7 @@ import java.util.Deque;
* Superclass of all expression nodes. Expression nodes have their identity determined by their content.
* All expression nodes are immutable.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public abstract class ExpressionNode implements Serializable {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/FunctionNode.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/FunctionNode.java
index 07efd46e02c..c4f3a75f2f8 100755
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/FunctionNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/FunctionNode.java
@@ -17,7 +17,7 @@ import java.util.List;
/**
* Invocation of a native function.
*
- * @author simon
+ * @author Simon Thoresen Hult
* @author bratseth
*/
public final class FunctionNode extends CompositeNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/IfNode.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/IfNode.java
index 06b899d45ab..28dc623be72 100755
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/IfNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/IfNode.java
@@ -15,7 +15,7 @@ import java.util.List;
/**
* A conditional branch of a ranking expression.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
* @author bratseth
*/
public final class IfNode extends CompositeNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/NameNode.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/NameNode.java
index 6fc1091c557..235e6e592a8 100755
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/NameNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/NameNode.java
@@ -13,7 +13,7 @@ import java.util.Deque;
* An opaque name in a ranking expression. This is used to represent names passed to the context
* and interpreted by the given context in a way which is opaque to the ranking expressions.
*
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
// TODO: This is achieved by ReferenceNode in almost all cases - remove this
public final class NameNode extends ExpressionNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/treenet/TreeNetConverter.java b/searchlib/src/main/java/com/yahoo/searchlib/treenet/TreeNetConverter.java
index 751faf2aa96..578e8c76733 100755
--- a/searchlib/src/main/java/com/yahoo/searchlib/treenet/TreeNetConverter.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/treenet/TreeNetConverter.java
@@ -7,7 +7,7 @@ import java.io.FileNotFoundException;
import java.io.FileReader;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class TreeNetConverter {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/treenet/rule/ComparisonCondition.java b/searchlib/src/main/java/com/yahoo/searchlib/treenet/rule/ComparisonCondition.java
index 174cf21d684..f3f28879bc0 100755
--- a/searchlib/src/main/java/com/yahoo/searchlib/treenet/rule/ComparisonCondition.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/treenet/rule/ComparisonCondition.java
@@ -4,7 +4,7 @@ package com.yahoo.searchlib.treenet.rule;
/**
* Represents a condition which comparing two values
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ComparisonCondition extends Condition {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/treenet/rule/Response.java b/searchlib/src/main/java/com/yahoo/searchlib/treenet/rule/Response.java
index d685d845763..b81d4ae97b9 100755
--- a/searchlib/src/main/java/com/yahoo/searchlib/treenet/rule/Response.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/treenet/rule/Response.java
@@ -2,7 +2,7 @@
package com.yahoo.searchlib.treenet.rule;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Response extends TreeNode {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/treenet/rule/Tree.java b/searchlib/src/main/java/com/yahoo/searchlib/treenet/rule/Tree.java
index 15b4147cc50..33d1e105672 100755
--- a/searchlib/src/main/java/com/yahoo/searchlib/treenet/rule/Tree.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/treenet/rule/Tree.java
@@ -4,7 +4,7 @@ package com.yahoo.searchlib.treenet.rule;
import java.util.Map;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Tree {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/treenet/rule/TreeNet.java b/searchlib/src/main/java/com/yahoo/searchlib/treenet/rule/TreeNet.java
index cfc5529cd77..023ed59bf18 100755
--- a/searchlib/src/main/java/com/yahoo/searchlib/treenet/rule/TreeNet.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/treenet/rule/TreeNet.java
@@ -4,7 +4,7 @@ package com.yahoo.searchlib.treenet.rule;
import java.util.Map;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class TreeNet {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/treenet/rule/TreeNode.java b/searchlib/src/main/java/com/yahoo/searchlib/treenet/rule/TreeNode.java
index 7d1ebe700e3..3e2539d6151 100755
--- a/searchlib/src/main/java/com/yahoo/searchlib/treenet/rule/TreeNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/treenet/rule/TreeNode.java
@@ -2,7 +2,7 @@
package com.yahoo.searchlib.treenet.rule;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class TreeNode {
diff --git a/searchlib/src/main/javacc/TreeNetParser.jj b/searchlib/src/main/javacc/TreeNetParser.jj
index 100c5ce8f00..b2a60af6db1 100755
--- a/searchlib/src/main/javacc/TreeNetParser.jj
+++ b/searchlib/src/main/javacc/TreeNetParser.jj
@@ -2,7 +2,7 @@
/**
* A best-effort treenet parser.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id: TreeNetParser.jj,v 1.1 2009-02-24 10:06:32 arnej Exp $
*/
options {
diff --git a/searchlib/src/test/java/com/yahoo/searchlib/aggregation/GroupTestCase.java b/searchlib/src/test/java/com/yahoo/searchlib/aggregation/GroupTestCase.java
index 1b5ab25f2a7..95ab1c30a3e 100644
--- a/searchlib/src/test/java/com/yahoo/searchlib/aggregation/GroupTestCase.java
+++ b/searchlib/src/test/java/com/yahoo/searchlib/aggregation/GroupTestCase.java
@@ -11,7 +11,7 @@ import java.util.Arrays;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GroupTestCase {
diff --git a/searchlib/src/test/java/com/yahoo/searchlib/aggregation/GroupingTestCase.java b/searchlib/src/test/java/com/yahoo/searchlib/aggregation/GroupingTestCase.java
index f6b324d30be..fe5405ecb6a 100644
--- a/searchlib/src/test/java/com/yahoo/searchlib/aggregation/GroupingTestCase.java
+++ b/searchlib/src/test/java/com/yahoo/searchlib/aggregation/GroupingTestCase.java
@@ -12,7 +12,7 @@ import java.util.Collections;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GroupingTestCase {
diff --git a/searchlib/src/test/java/com/yahoo/searchlib/aggregation/MergeTestCase.java b/searchlib/src/test/java/com/yahoo/searchlib/aggregation/MergeTestCase.java
index 83b7e81a539..322bcb426d5 100755
--- a/searchlib/src/test/java/com/yahoo/searchlib/aggregation/MergeTestCase.java
+++ b/searchlib/src/test/java/com/yahoo/searchlib/aggregation/MergeTestCase.java
@@ -19,7 +19,7 @@ import java.util.List;
import static org.junit.Assert.assertEquals;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class MergeTestCase {
diff --git a/searchlib/src/test/java/com/yahoo/searchlib/expression/FixedWidthBucketFunctionTestCase.java b/searchlib/src/test/java/com/yahoo/searchlib/expression/FixedWidthBucketFunctionTestCase.java
index 8ec83bc1f6e..b8a3e975537 100644
--- a/searchlib/src/test/java/com/yahoo/searchlib/expression/FixedWidthBucketFunctionTestCase.java
+++ b/searchlib/src/test/java/com/yahoo/searchlib/expression/FixedWidthBucketFunctionTestCase.java
@@ -6,7 +6,7 @@ import org.junit.Test;
import static org.junit.Assert.assertSame;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class FixedWidthBucketFunctionTestCase {
diff --git a/searchlib/src/test/java/com/yahoo/searchlib/expression/ObjectVisitorTestCase.java b/searchlib/src/test/java/com/yahoo/searchlib/expression/ObjectVisitorTestCase.java
index 286a6a702db..18753737eb0 100755
--- a/searchlib/src/test/java/com/yahoo/searchlib/expression/ObjectVisitorTestCase.java
+++ b/searchlib/src/test/java/com/yahoo/searchlib/expression/ObjectVisitorTestCase.java
@@ -9,7 +9,7 @@ import java.util.Arrays;
import static org.junit.Assert.assertEquals;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class ObjectVisitorTestCase {
diff --git a/searchlib/src/test/java/com/yahoo/searchlib/expression/RangeBucketPreDefFunctionTestCase.java b/searchlib/src/test/java/com/yahoo/searchlib/expression/RangeBucketPreDefFunctionTestCase.java
index d60b47aec13..d4de0dad062 100644
--- a/searchlib/src/test/java/com/yahoo/searchlib/expression/RangeBucketPreDefFunctionTestCase.java
+++ b/searchlib/src/test/java/com/yahoo/searchlib/expression/RangeBucketPreDefFunctionTestCase.java
@@ -6,7 +6,7 @@ import org.junit.Test;
import static org.junit.Assert.assertSame;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class RangeBucketPreDefFunctionTestCase {
diff --git a/searchlib/src/test/java/com/yahoo/searchlib/expression/TimeStampFunctionTestCase.java b/searchlib/src/test/java/com/yahoo/searchlib/expression/TimeStampFunctionTestCase.java
index 64610c73306..46f13a39a5e 100644
--- a/searchlib/src/test/java/com/yahoo/searchlib/expression/TimeStampFunctionTestCase.java
+++ b/searchlib/src/test/java/com/yahoo/searchlib/expression/TimeStampFunctionTestCase.java
@@ -9,7 +9,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class TimeStampFunctionTestCase {
diff --git a/searchlib/src/test/java/com/yahoo/searchlib/expression/ZCurveFunctionTestCase.java b/searchlib/src/test/java/com/yahoo/searchlib/expression/ZCurveFunctionTestCase.java
index 7bd30fb75c0..10bbe817755 100644
--- a/searchlib/src/test/java/com/yahoo/searchlib/expression/ZCurveFunctionTestCase.java
+++ b/searchlib/src/test/java/com/yahoo/searchlib/expression/ZCurveFunctionTestCase.java
@@ -7,7 +7,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ZCurveFunctionTestCase {
diff --git a/searchlib/src/test/java/com/yahoo/searchlib/gbdt/GbdtConverterTestCase.java b/searchlib/src/test/java/com/yahoo/searchlib/gbdt/GbdtConverterTestCase.java
index 0084180d988..7b278cebb83 100644
--- a/searchlib/src/test/java/com/yahoo/searchlib/gbdt/GbdtConverterTestCase.java
+++ b/searchlib/src/test/java/com/yahoo/searchlib/gbdt/GbdtConverterTestCase.java
@@ -17,7 +17,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class GbdtConverterTestCase {
diff --git a/searchlib/src/test/java/com/yahoo/searchlib/gbdt/GbdtModelTestCase.java b/searchlib/src/test/java/com/yahoo/searchlib/gbdt/GbdtModelTestCase.java
index 1157b9130f0..7f338f2324d 100644
--- a/searchlib/src/test/java/com/yahoo/searchlib/gbdt/GbdtModelTestCase.java
+++ b/searchlib/src/test/java/com/yahoo/searchlib/gbdt/GbdtModelTestCase.java
@@ -11,7 +11,7 @@ import java.io.IOException;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class GbdtModelTestCase {
diff --git a/searchlib/src/test/java/com/yahoo/searchlib/gbdt/ReferenceNodeTestCase.java b/searchlib/src/test/java/com/yahoo/searchlib/gbdt/ReferenceNodeTestCase.java
index f61b283aed3..f9bb63fd119 100644
--- a/searchlib/src/test/java/com/yahoo/searchlib/gbdt/ReferenceNodeTestCase.java
+++ b/searchlib/src/test/java/com/yahoo/searchlib/gbdt/ReferenceNodeTestCase.java
@@ -9,7 +9,7 @@ import java.util.Optional;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ReferenceNodeTestCase {
diff --git a/searchlib/src/test/java/com/yahoo/searchlib/gbdt/ResponseNodeTestCase.java b/searchlib/src/test/java/com/yahoo/searchlib/gbdt/ResponseNodeTestCase.java
index 54bdf794a07..082470a3016 100644
--- a/searchlib/src/test/java/com/yahoo/searchlib/gbdt/ResponseNodeTestCase.java
+++ b/searchlib/src/test/java/com/yahoo/searchlib/gbdt/ResponseNodeTestCase.java
@@ -10,7 +10,7 @@ import java.io.IOException;
import static org.junit.Assert.assertEquals;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class ResponseNodeTestCase {
diff --git a/searchlib/src/test/java/com/yahoo/searchlib/gbdt/TreeNodeTestCase.java b/searchlib/src/test/java/com/yahoo/searchlib/gbdt/TreeNodeTestCase.java
index 6d3b2584d22..27e3ad2651a 100644
--- a/searchlib/src/test/java/com/yahoo/searchlib/gbdt/TreeNodeTestCase.java
+++ b/searchlib/src/test/java/com/yahoo/searchlib/gbdt/TreeNodeTestCase.java
@@ -10,7 +10,7 @@ import java.io.IOException;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class TreeNodeTestCase {
diff --git a/searchlib/src/test/java/com/yahoo/searchlib/gbdt/XmlHelperTestCase.java b/searchlib/src/test/java/com/yahoo/searchlib/gbdt/XmlHelperTestCase.java
index f09e4c04275..60f774ed4e8 100644
--- a/searchlib/src/test/java/com/yahoo/searchlib/gbdt/XmlHelperTestCase.java
+++ b/searchlib/src/test/java/com/yahoo/searchlib/gbdt/XmlHelperTestCase.java
@@ -9,7 +9,7 @@ import java.util.List;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class XmlHelperTestCase {
diff --git a/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/FeatureListTestCase.java b/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/FeatureListTestCase.java
index 9b67c82f802..85dcea3b6e6 100755
--- a/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/FeatureListTestCase.java
+++ b/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/FeatureListTestCase.java
@@ -15,7 +15,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class FeatureListTestCase {
diff --git a/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/RankingExpressionTestCase.java b/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/RankingExpressionTestCase.java
index fe6ac76f32f..7c929ae24b3 100755
--- a/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/RankingExpressionTestCase.java
+++ b/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/RankingExpressionTestCase.java
@@ -32,7 +32,7 @@ import java.util.Map;
import java.util.concurrent.*;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
* @author bratseth
*/
public class RankingExpressionTestCase {
diff --git a/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/evaluation/Benchmark.java b/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/evaluation/Benchmark.java
index 51a1b09b9fa..d394a6a7d3e 100644
--- a/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/evaluation/Benchmark.java
+++ b/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/evaluation/Benchmark.java
@@ -16,7 +16,7 @@ import java.util.LinkedList;
import java.util.List;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public final class Benchmark {
diff --git a/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/integration/ml/VariableConverterTestCase.java b/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/integration/ml/VariableConverterTestCase.java
index f94098e6255..4e3f722fabd 100644
--- a/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/integration/ml/VariableConverterTestCase.java
+++ b/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/integration/ml/VariableConverterTestCase.java
@@ -13,7 +13,7 @@ public class VariableConverterTestCase {
byte[] converted = VariableConverter.importVariable("src/test/files/integration/tensorflow/mnist_softmax/saved",
"Variable_1",
"tensor(d0[10],d1[1])");
- assertEquals("{\"cells\":[{\"address\":{\"d0\":\"0\",\"d1\":\"0\"},\"value\":-0.3546536862850189},{\"address\":{\"d0\":\"1\",\"d1\":\"0\"},\"value\":0.3759574592113495},{\"address\":{\"d0\":\"2\",\"d1\":\"0\"},\"value\":0.06054411828517914},{\"address\":{\"d0\":\"3\",\"d1\":\"0\"},\"value\":-0.251544713973999},{\"address\":{\"d0\":\"4\",\"d1\":\"0\"},\"value\":0.01795101352035999},{\"address\":{\"d0\":\"5\",\"d1\":\"0\"},\"value\":1.289906740188599},{\"address\":{\"d0\":\"6\",\"d1\":\"0\"},\"value\":-0.1038961559534073},{\"address\":{\"d0\":\"7\",\"d1\":\"0\"},\"value\":0.6367976665496826},{\"address\":{\"d0\":\"8\",\"d1\":\"0\"},\"value\":-1.413674473762512},{\"address\":{\"d0\":\"9\",\"d1\":\"0\"},\"value\":-0.2573896050453186}]}",
+ assertEquals("{\"cells\":[{\"address\":{\"d0\":\"0\",\"d1\":\"0\"},\"value\":-0.3546536862850189},{\"address\":{\"d0\":\"1\",\"d1\":\"0\"},\"value\":0.3759574592113495},{\"address\":{\"d0\":\"2\",\"d1\":\"0\"},\"value\":0.06054411828517914},{\"address\":{\"d0\":\"3\",\"d1\":\"0\"},\"value\":-0.251544713973999},{\"address\":{\"d0\":\"4\",\"d1\":\"0\"},\"value\":0.017951013520359993},{\"address\":{\"d0\":\"5\",\"d1\":\"0\"},\"value\":1.2899067401885986},{\"address\":{\"d0\":\"6\",\"d1\":\"0\"},\"value\":-0.10389615595340729},{\"address\":{\"d0\":\"7\",\"d1\":\"0\"},\"value\":0.6367976665496826},{\"address\":{\"d0\":\"8\",\"d1\":\"0\"},\"value\":-1.4136744737625122},{\"address\":{\"d0\":\"9\",\"d1\":\"0\"},\"value\":-0.2573896050453186}]}",
new String(converted, StandardCharsets.UTF_8));
}
diff --git a/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/rule/ArgumentsTestCase.java b/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/rule/ArgumentsTestCase.java
index 303135888d8..1292aba3605 100644
--- a/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/rule/ArgumentsTestCase.java
+++ b/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/rule/ArgumentsTestCase.java
@@ -9,7 +9,7 @@ import java.util.Collections;
import static org.junit.Assert.*;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class ArgumentsTestCase {
diff --git a/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/rule/ReferenceNodeTestCase.java b/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/rule/ReferenceNodeTestCase.java
index 135cc95a209..aabb5e092b5 100644
--- a/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/rule/ReferenceNodeTestCase.java
+++ b/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/rule/ReferenceNodeTestCase.java
@@ -9,7 +9,7 @@ import java.util.List;
import static org.junit.Assert.assertEquals;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class ReferenceNodeTestCase {
diff --git a/searchlib/src/test/java/com/yahoo/searchlib/treenet/TreeNetParserTestCase.java b/searchlib/src/test/java/com/yahoo/searchlib/treenet/TreeNetParserTestCase.java
index 5f48109d4c6..c83429ed1c4 100755
--- a/searchlib/src/test/java/com/yahoo/searchlib/treenet/TreeNetParserTestCase.java
+++ b/searchlib/src/test/java/com/yahoo/searchlib/treenet/TreeNetParserTestCase.java
@@ -15,7 +15,7 @@ import java.io.StringReader;
import static org.junit.Assert.assertEquals;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class TreeNetParserTestCase {
diff --git a/searchlib/src/tests/aggregator/attr_test.cpp b/searchlib/src/tests/aggregator/attr_test.cpp
index f374f117f00..21fc41e3b9d 100644
--- a/searchlib/src/tests/aggregator/attr_test.cpp
+++ b/searchlib/src/tests/aggregator/attr_test.cpp
@@ -8,6 +8,9 @@
#include <vespa/searchlib/expression/arrayatlookupfunctionnode.h>
#include <vespa/searchlib/expression/interpolatedlookupfunctionnode.h>
+#include <vespa/log/log.h>
+LOG_SETUP("attr_test");
+
using namespace search;
using namespace search::expression;
using namespace vespalib;
diff --git a/searchlib/src/tests/aggregator/perdocexpr.cpp b/searchlib/src/tests/aggregator/perdocexpr.cpp
index 8a6e70de09a..25bbd16de07 100644
--- a/searchlib/src/tests/aggregator/perdocexpr.cpp
+++ b/searchlib/src/tests/aggregator/perdocexpr.cpp
@@ -1,4 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
#include <vespa/searchlib/aggregation/aggregation.h>
#include <vespa/searchlib/aggregation/expressioncountaggregationresult.h>
#include <vespa/searchlib/aggregation/perdocexpression.h>
@@ -13,6 +14,9 @@
#include <iostream>
#include <list>
+#include <vespa/log/log.h>
+LOG_SETUP("per_doc_expr_test");
+
#define MU std::make_unique
using namespace search;
diff --git a/searchlib/src/tests/attribute/attribute_test.cpp b/searchlib/src/tests/attribute/attribute_test.cpp
index 9134711773c..32ac836302a 100644
--- a/searchlib/src/tests/attribute/attribute_test.cpp
+++ b/searchlib/src/tests/attribute/attribute_test.cpp
@@ -32,8 +32,7 @@ using search::index::DummyFileHeaderContext;
using search::attribute::BasicType;
using search::attribute::IAttributeVector;
-namespace
-{
+namespace {
vespalib::string empty;
vespalib::string tmpDir("tmp");
@@ -1298,6 +1297,9 @@ AttributeTest::testWeightedSet()
testWeightedSet<IntegerAttribute, AttributeVector::WeightedInt>(ptr, values);
IAttributeVector::EnumHandle e;
EXPECT_TRUE(ptr->findEnum("1", e));
+ EXPECT_EQUAL(1u, ptr->findFoldedEnums("1").size());
+ EXPECT_EQUAL(e, ptr->findFoldedEnums("1")[0]);
+
}
}
{ // FloatingPointAttribute
@@ -1321,6 +1323,8 @@ AttributeTest::testWeightedSet()
testWeightedSet<FloatingPointAttribute, AttributeVector::WeightedFloat>(ptr, values);
IAttributeVector::EnumHandle e;
EXPECT_TRUE(ptr->findEnum("1", e));
+ EXPECT_EQUAL(1u, ptr->findFoldedEnums("1").size());
+ EXPECT_EQUAL(e, ptr->findFoldedEnums("1")[0]);
}
}
{ // StringAttribute
@@ -1346,6 +1350,8 @@ AttributeTest::testWeightedSet()
testWeightedSet<StringAttribute, AttributeVector::WeightedString>(ptr, values);
IAttributeVector::EnumHandle e;
EXPECT_TRUE(ptr->findEnum("string00", e));
+ EXPECT_EQUAL(1u, ptr->findFoldedEnums("StRiNg00").size());
+ EXPECT_EQUAL(e, ptr->findFoldedEnums("StRiNg00")[0]);
}
}
}
@@ -2111,7 +2117,7 @@ AttributeTest::testCompactLidSpace(const Config &config)
}
break;
default:
- abort();
+ LOG_ABORT("should not be reached");
}
}
@@ -2315,6 +2321,23 @@ AttributeTest::testPendingCompaction()
populateSimple(iv, 1, 2); // should not trigger new compaction
}
+void testNamePrefix() {
+ Config cfg(BasicType::INT32, CollectionType::SINGLE);
+ AttributeVector::SP vFlat = createAttribute("sfsint32_pc", cfg);
+ AttributeVector::SP vS1 = createAttribute("sfsint32_pc.abc", cfg);
+ AttributeVector::SP vS2 = createAttribute("sfsint32_pc.xyz", cfg);
+ AttributeVector::SP vSS1 = createAttribute("sfsint32_pc.xyz.abc", cfg);
+ EXPECT_EQUAL("sfsint32_pc", vFlat->getName());
+ EXPECT_EQUAL("sfsint32_pc", vFlat->getNamePrefix());
+ EXPECT_EQUAL("sfsint32_pc.abc", vS1->getName());
+ EXPECT_EQUAL("sfsint32_pc", vS1->getNamePrefix());
+ EXPECT_EQUAL("sfsint32_pc.xyz", vS2->getName());
+ EXPECT_EQUAL("sfsint32_pc", vS2->getNamePrefix());
+ EXPECT_EQUAL("sfsint32_pc.xyz.abc", vSS1->getName());
+ EXPECT_EQUAL("sfsint32_pc", vSS1->getNamePrefix());
+
+}
+
void
deleteDataDirs()
{
@@ -2361,6 +2384,7 @@ int AttributeTest::Main()
TEST_DO(requireThatAddressSpaceUsageIsReported());
testReaderDuringLastUpdate();
TEST_DO(testPendingCompaction());
+ TEST_DO(testNamePrefix());
deleteDataDirs();
TEST_DONE();
diff --git a/searchlib/src/tests/attribute/benchmark/attributebenchmark.cpp b/searchlib/src/tests/attribute/benchmark/attributebenchmark.cpp
index 5722b7c90ca..5f10f5ad631 100644
--- a/searchlib/src/tests/attribute/benchmark/attributebenchmark.cpp
+++ b/searchlib/src/tests/attribute/benchmark/attributebenchmark.cpp
@@ -1,4 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
#include <vespa/searchlib/util/randomgenerator.h>
#include <vespa/searchlib/attribute/attribute.h>
#include <vespa/searchlib/attribute/attributeguard.h>
@@ -8,6 +9,7 @@
#include <vespa/searchlib/attribute/singlestringattribute.h>
#include <vespa/searchlib/attribute/multistringattribute.h>
#include <vespa/searchlib/attribute/attrvector.h>
+#include <vespa/searchlib/attribute/attributevector.hpp>
#include <vespa/fastos/thread.h>
#include <vespa/fastos/app.h>
#include <iostream>
@@ -17,11 +19,8 @@
#include <sys/resource.h>
#include <vespa/log/log.h>
-
LOG_SETUP("attributebenchmark");
-#include <vespa/searchlib/attribute/attributevector.hpp>
-
using std::shared_ptr;
typedef std::vector<uint32_t> NumVector;
diff --git a/searchlib/src/tests/attribute/benchmark/attributeupdater.h b/searchlib/src/tests/attribute/benchmark/attributeupdater.h
index 13360e58b2d..1dad2cb563d 100644
--- a/searchlib/src/tests/attribute/benchmark/attributeupdater.h
+++ b/searchlib/src/tests/attribute/benchmark/attributeupdater.h
@@ -2,6 +2,7 @@
#pragma once
+#include <vespa/vespalib/util/hdr_abort.h>
#include <vespa/searchlib/util/randomgenerator.h>
#include <vespa/searchlib/util/runnable.h>
#include <vespa/searchlib/attribute/attribute.h>
@@ -25,7 +26,7 @@ public:
if (!rc) {
std::cout << "Assert " << _totalCnt << " failed: \"" << str << "\" ("
<< file << ":" << line << ")" << std::endl;
- abort();
+ HDR_ABORT("should not be reached");
}
return true;
}
@@ -39,7 +40,7 @@ public:
std::cout << aStr << ": " << a << std::endl;
std::cout << bStr << ": " << b << std::endl;
std::cout << "(" << file << ":" << line << ")" << std::endl;
- abort();
+ HDR_ABORT("should not be reached");
}
return true;
}
diff --git a/searchlib/src/tests/attribute/document_weight_iterator/document_weight_iterator_test.cpp b/searchlib/src/tests/attribute/document_weight_iterator/document_weight_iterator_test.cpp
index 7b597930d0c..9a7552ce681 100644
--- a/searchlib/src/tests/attribute/document_weight_iterator/document_weight_iterator_test.cpp
+++ b/searchlib/src/tests/attribute/document_weight_iterator/document_weight_iterator_test.cpp
@@ -25,6 +25,9 @@
#include <vespa/searchlib/test/searchiteratorverifier.h>
#include <vespa/searchlib/queryeval/document_weight_search_iterator.h>
+#include <vespa/log/log.h>
+LOG_SETUP("document_weight_iterator_test");
+
using namespace search;
using namespace search::attribute;
diff --git a/searchlib/src/tests/attribute/enumstore/enumstore_test.cpp b/searchlib/src/tests/attribute/enumstore/enumstore_test.cpp
index daff432d68d..89dd1cfdab4 100644
--- a/searchlib/src/tests/attribute/enumstore/enumstore_test.cpp
+++ b/searchlib/src/tests/attribute/enumstore/enumstore_test.cpp
@@ -41,6 +41,7 @@ private:
void testFloatEnumStore(EnumStoreType & es);
void testFloatEnumStore();
+ void testFindFolded();
void testAddEnum();
template <typename EnumStoreType>
void testAddEnum(bool hasPostings);
@@ -275,6 +276,40 @@ EnumStoreTest::testFloatEnumStore()
}
void
+EnumStoreTest::testFindFolded()
+{
+ StringEnumStore ses(100, false);
+ std::vector<EnumIndex> indices;
+ std::vector<std::string> unique({"", "one", "two", "TWO", "Two", "three"});
+ for (std::string &str : unique) {
+ EnumIndex idx;
+ ses.addEnum(str.c_str(), idx);
+ EXPECT_TRUE(ses.getLastEnum() == indices.size());
+ indices.push_back(idx);
+ ses.incRefCount(idx);
+ EXPECT_EQUAL(1u, ses.getRefCount(idx));
+ }
+ ses.freezeTree();
+ for (uint32_t i = 0; i < indices.size(); ++i) {
+ uint32_t e = ses.getEnum(indices[i]);
+ EXPECT_EQUAL(i, e);
+ EnumIndex idx;
+ EXPECT_TRUE(ses.findIndex(unique[i].c_str(), idx));
+ }
+ EXPECT_EQUAL(1u, ses.findFoldedEnums("").size());
+ EXPECT_EQUAL(0u, ses.findFoldedEnums("foo").size());
+ EXPECT_EQUAL(1u, ses.findFoldedEnums("one").size());
+ EXPECT_EQUAL(3u, ses.findFoldedEnums("two").size());
+ EXPECT_EQUAL(3u, ses.findFoldedEnums("TWO").size());
+ EXPECT_EQUAL(3u, ses.findFoldedEnums("tWo").size());
+ const auto v = ses.findFoldedEnums("Two");
+ EXPECT_EQUAL(std::string("TWO"), ses.getValue(v[0]));
+ EXPECT_EQUAL(std::string("Two"), ses.getValue(v[1]));
+ EXPECT_EQUAL(std::string("two"), ses.getValue(v[2]));
+ EXPECT_EQUAL(1u, ses.findFoldedEnums("three").size());
+}
+
+void
EnumStoreTest::testAddEnum()
{
testAddEnum<StringEnumStore>(false);
@@ -319,6 +354,8 @@ EnumStoreTest::testAddEnum(bool hasPostings)
uint32_t e = ses.getEnum(indices[i]);
EXPECT_EQUAL(i, e);
EXPECT_TRUE(ses.findEnum(unique[i].c_str(), e));
+ EXPECT_EQUAL(1u, ses.findFoldedEnums(unique[i].c_str()).size());
+ EXPECT_EQUAL(e, ses.findFoldedEnums(unique[i].c_str())[0]);
EXPECT_TRUE(ses.getEnum(datastore::EntryRef(e)) == i);
EXPECT_TRUE(ses.findIndex(unique[i].c_str(), idx));
EXPECT_TRUE(idx == indices[i]);
@@ -874,6 +911,7 @@ EnumStoreTest::Main()
testStringEntry();
testNumericEntry();
testFloatEnumStore();
+ testFindFolded();
testAddEnum();
testCompaction();
testReset();
diff --git a/searchlib/src/tests/attribute/imported_search_context/imported_search_context_test.cpp b/searchlib/src/tests/attribute/imported_search_context/imported_search_context_test.cpp
index 0a02824e77a..f8d696b3203 100644
--- a/searchlib/src/tests/attribute/imported_search_context/imported_search_context_test.cpp
+++ b/searchlib/src/tests/attribute/imported_search_context/imported_search_context_test.cpp
@@ -348,7 +348,7 @@ TEST_F("Multiple iterators can be created from the same context", SingleValueFix
// implemented at all for (single) numeric attributes. Intentional?
TEST_F("queryTerm() returns term context was created with", WsetValueFixture) {
auto ctx = f.create_context(word_term("helloworld"));
- EXPECT_EQUAL(std::string("helloworld"), std::string(ctx->queryTerm().getTerm()));
+ EXPECT_EQUAL(std::string("helloworld"), std::string(ctx->queryTerm()->getTerm()));
}
struct SearchCacheFixture : Fixture {
diff --git a/searchlib/src/tests/attribute/postinglistattribute/postinglistattribute_test.cpp b/searchlib/src/tests/attribute/postinglistattribute/postinglistattribute_test.cpp
index 9d9aaac9f62..f0496049f0e 100644
--- a/searchlib/src/tests/attribute/postinglistattribute/postinglistattribute_test.cpp
+++ b/searchlib/src/tests/attribute/postinglistattribute/postinglistattribute_test.cpp
@@ -17,6 +17,7 @@
#include <vespa/searchlib/fef/termfieldmatchdata.h>
#include <vespa/fastos/file.h>
#include <iostream>
+
#include <vespa/log/log.h>
LOG_SETUP("postinglistattribute_test");
diff --git a/searchlib/src/tests/attribute/searchable/attribute_searchable_adapter_test.cpp b/searchlib/src/tests/attribute/searchable/attribute_searchable_adapter_test.cpp
index e5af6931977..c7dde172b9d 100644
--- a/searchlib/src/tests/attribute/searchable/attribute_searchable_adapter_test.cpp
+++ b/searchlib/src/tests/attribute/searchable/attribute_searchable_adapter_test.cpp
@@ -1,6 +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/testkit/test_kit.h>
+#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/searchcommon/attribute/iattributecontext.h>
#include <vespa/searchlib/attribute/attribute_blueprint_factory.h>
#include <vespa/searchlib/attribute/attributefactory.h>
@@ -28,6 +28,9 @@
#include <vespa/searchlib/queryeval/wand/parallel_weak_and_search.h>
#include <memory>
+#include <vespa/log/log.h>
+LOG_SETUP("attribute_searchable_adapter_test");
+
using search::AttributeFactory;
using search::AttributeGuard;
using search::AttributeVector;
@@ -517,7 +520,7 @@ TEST("require that attribute parallel wand works") {
} else {
fprintf(stderr, " (fast_search: %s, strict: %s)\n",
as_str(fast_search), as_str(strict));
- assert(false);
+ LOG_ABORT("should not reach here");
}
}
}
diff --git a/searchlib/src/tests/attribute/searchable/attribute_weighted_set_blueprint_test.cpp b/searchlib/src/tests/attribute/searchable/attribute_weighted_set_blueprint_test.cpp
index 39bbb8fb5ad..be64edf89f2 100644
--- a/searchlib/src/tests/attribute/searchable/attribute_weighted_set_blueprint_test.cpp
+++ b/searchlib/src/tests/attribute/searchable/attribute_weighted_set_blueprint_test.cpp
@@ -1,6 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/testkit/testapp.h>
-
#include <vespa/searchlib/attribute/attribute_blueprint_factory.h>
#include <vespa/searchlib/attribute/attribute_weighted_set_blueprint.h>
#include <vespa/searchlib/attribute/attributecontext.h>
@@ -14,10 +13,11 @@
#include <vespa/searchlib/queryeval/fake_result.h>
#include <vespa/searchlib/queryeval/weighted_set_term_search.h>
#include <vespa/searchlib/queryeval/fake_requestcontext.h>
-
-
#include <vespa/searchlib/attribute/enumstore.hpp>
+#include <vespa/log/log.h>
+LOG_SETUP("attribute_weighted_set_blueprint_test");
+
using namespace search;
using namespace search::query;
using namespace search::fef;
diff --git a/searchlib/src/tests/attribute/searchcontext/searchcontext.cpp b/searchlib/src/tests/attribute/searchcontext/searchcontext.cpp
index 77504c4ab3c..85492df7016 100644
--- a/searchlib/src/tests/attribute/searchcontext/searchcontext.cpp
+++ b/searchlib/src/tests/attribute/searchcontext/searchcontext.cpp
@@ -266,6 +266,11 @@ private:
void requireThatOutOfBoundsSearchTermGivesZeroHits(const vespalib::string &name, const Config &cfg, int64_t maxValue);
void requireThatOutOfBoundsSearchTermGivesZeroHits();
+
+ template <typename AttributeType, typename ValueType>
+ void requireThatSearchIteratorExposesSearchContext(const ConfigMap &cfg, ValueType value, const vespalib::string &searchTerm);
+ void requireThatSearchIteratorExposesSearchContext();
+
// init maps with config objects
void initIntegerConfig();
void initFloatConfig();
@@ -1809,6 +1814,46 @@ SearchContextTest::requireThatOutOfBoundsSearchTermGivesZeroHits()
}
}
+void
+assertSearchIteratorExposesSearchContext(search::attribute::ISearchContext &ctx)
+{
+ ASSERT_TRUE(ctx.valid());
+ ctx.fetchPostings(true);
+ TermFieldMatchData dummy;
+ SearchBasePtr itr = ctx.createIterator(&dummy, true);
+ EXPECT_TRUE(itr->getAttributeSearchContext() != nullptr);
+ EXPECT_EQUAL(&ctx, itr->getAttributeSearchContext());
+}
+
+template <typename AttributeType, typename ValueType>
+void
+SearchContextTest::requireThatSearchIteratorExposesSearchContext(const ConfigMap &cfgMap,
+ ValueType value,
+ const vespalib::string &searchTerm)
+{
+ vespalib::string attrSuffix = "-itr-exposes-ctx";
+ std::vector<ValueType> values = {value};
+ for (const auto &cfg : cfgMap) {
+ vespalib::string attrName = cfg.first + attrSuffix;
+ AttributePtr attr = AttributeFactory::createAttribute(attrName, cfg.second);
+ addDocs(*attr, 2);
+ AttributeType &concreteAttr = dynamic_cast<AttributeType &>(*attr);
+ if (attr->hasMultiValue()) {
+ fillAttribute(concreteAttr, values);
+ } else {
+ resetAttribute(concreteAttr, value);
+ }
+ assertSearchIteratorExposesSearchContext(*getSearch(*attr, searchTerm));
+ }
+}
+
+void
+SearchContextTest::requireThatSearchIteratorExposesSearchContext()
+{
+ requireThatSearchIteratorExposesSearchContext<IntegerAttribute, largeint_t>(_integerCfg, 3, "3");
+ requireThatSearchIteratorExposesSearchContext<FloatingPointAttribute, double>(_floatCfg, 5.7, "5.7");
+ requireThatSearchIteratorExposesSearchContext<StringAttribute, vespalib::string>(_stringCfg, "foo", "foo");
+}
void
SearchContextTest::initIntegerConfig()
@@ -1940,6 +1985,7 @@ SearchContextTest::Main()
TEST_DO(requireThatInvalidSearchTermGivesZeroHits());
TEST_DO(requireThatFlagAttributeHandlesTheByteRange());
TEST_DO(requireThatOutOfBoundsSearchTermGivesZeroHits());
+ TEST_DO(requireThatSearchIteratorExposesSearchContext());
TEST_DONE();
}
diff --git a/searchlib/src/tests/attribute/stringattribute/stringattribute_test.cpp b/searchlib/src/tests/attribute/stringattribute/stringattribute_test.cpp
index b25abdfd2e5..2adfdd135df 100644
--- a/searchlib/src/tests/attribute/stringattribute/stringattribute_test.cpp
+++ b/searchlib/src/tests/attribute/stringattribute/stringattribute_test.cpp
@@ -153,6 +153,8 @@ StringAttributeTest::testMultiValue(Attribute & attr, uint32_t numDocs)
EXPECT_TRUE(strcmp(attr.get(doc), uniqueStrings[0].c_str()) == 0);
uint32_t e;
EXPECT_TRUE(attr.findEnum(uniqueStrings[0].c_str(), e));
+ EXPECT_EQUAL(1u, attr.findFoldedEnums(uniqueStrings[0].c_str()).size());
+ EXPECT_EQUAL(e, attr.findFoldedEnums(uniqueStrings[0].c_str())[0]);
EXPECT_TRUE(attr.getEnum(doc) == e);
}
diff --git a/searchlib/src/tests/btree/frozenbtree_test.cpp b/searchlib/src/tests/btree/frozenbtree_test.cpp
index 399c20f53cc..988239a5438 100644
--- a/searchlib/src/tests/btree/frozenbtree_test.cpp
+++ b/searchlib/src/tests/btree/frozenbtree_test.cpp
@@ -306,7 +306,7 @@ FrozenBTreeTest::sortRandomValues()
} else if (*i == prevVal)
okcnt++;
else
- abort();
+ LOG_ABORT("should not be reached");
prevVal = *i;
}
EXPECT_TRUE(okcnt == sorted.size());
diff --git a/searchlib/src/tests/common/sequencedtaskexecutor/sequencedtaskexecutor_test.cpp b/searchlib/src/tests/common/sequencedtaskexecutor/sequencedtaskexecutor_test.cpp
index 805a6d3b962..f8ab03d7710 100644
--- a/searchlib/src/tests/common/sequencedtaskexecutor/sequencedtaskexecutor_test.cpp
+++ b/searchlib/src/tests/common/sequencedtaskexecutor/sequencedtaskexecutor_test.cpp
@@ -122,8 +122,8 @@ TEST_F("require that task with same string component id are serialized", Fixture
std::shared_ptr<TestObj> tv(std::make_shared<TestObj>());
EXPECT_EQUAL(0, tv->_val);
auto test2 = [=]() { tv->modify(14, 42); };
- f._threads.execute("0", [=]() { usleep(2000); tv->modify(0, 14); });
- f._threads.execute("0", test2);
+ f._threads.execute(f._threads.getExecutorId("0"), [=]() { usleep(2000); tv->modify(0, 14); });
+ f._threads.execute(f._threads.getExecutorId("0"), test2);
tv->wait(2);
EXPECT_EQUAL(0, tv->_fail);
EXPECT_EQUAL(42, tv->_val);
@@ -132,8 +132,7 @@ TEST_F("require that task with same string component id are serialized", Fixture
EXPECT_EQUAL(42, tv->_val);
}
-namespace
-{
+namespace {
int detectSerializeFailure(Fixture &f, vespalib::stringref altComponentId, int tryLimit)
{
@@ -141,8 +140,8 @@ int detectSerializeFailure(Fixture &f, vespalib::stringref altComponentId, int t
for (tryCnt = 0; tryCnt < tryLimit; ++tryCnt) {
std::shared_ptr<TestObj> tv(std::make_shared<TestObj>());
EXPECT_EQUAL(0, tv->_val);
- f._threads.execute("0", [=]() { usleep(2000); tv->modify(0, 14); });
- f._threads.execute(altComponentId, [=]() { tv->modify(14, 42); });
+ f._threads.execute(f._threads.getExecutorId("0"), [=]() { usleep(2000); tv->modify(0, 14); });
+ f._threads.execute(f._threads.getExecutorId(altComponentId), [=]() { tv->modify(14, 42); });
tv->wait(2);
if (tv->_fail != 1) {
continue;
diff --git a/searchlib/src/tests/diskindex/fusion/fusion_test.cpp b/searchlib/src/tests/diskindex/fusion/fusion_test.cpp
index 07c399b3092..809688bdb2e 100644
--- a/searchlib/src/tests/diskindex/fusion/fusion_test.cpp
+++ b/searchlib/src/tests/diskindex/fusion/fusion_test.cpp
@@ -18,6 +18,9 @@
#include <vespa/searchlib/util/filekit.h>
#include <vespa/searchlib/common/sequencedtaskexecutor.h>
+#include <vespa/log/log.h>
+LOG_SETUP("fusion_test");
+
namespace search {
using document::Document;
diff --git a/searchlib/src/tests/diskindex/pagedict4/pagedict4_hugeword_cornercase_test.cpp b/searchlib/src/tests/diskindex/pagedict4/pagedict4_hugeword_cornercase_test.cpp
index 400108e91b0..8fb0e48663c 100644
--- a/searchlib/src/tests/diskindex/pagedict4/pagedict4_hugeword_cornercase_test.cpp
+++ b/searchlib/src/tests/diskindex/pagedict4/pagedict4_hugeword_cornercase_test.cpp
@@ -128,8 +128,7 @@ PostingListCounts makeCounts(uint32_t wantLen)
return counts2;
}
}
- LOG(info, "Could not calculate counts with wanted compressed length");
- abort();
+ LOG_ABORT("Could not calculate counts with wanted compressed length");
}
using StartOffset = search::bitcompression::PageDict4StartOffset;
diff --git a/searchlib/src/tests/docstore/logdatastore/logdatastore_test.cpp b/searchlib/src/tests/docstore/logdatastore/logdatastore_test.cpp
index a22c44e843c..34046f551d9 100644
--- a/searchlib/src/tests/docstore/logdatastore/logdatastore_test.cpp
+++ b/searchlib/src/tests/docstore/logdatastore/logdatastore_test.cpp
@@ -282,13 +282,13 @@ TEST("testTruncatedIdxFile"){
{
// Files comes from the 'growing test'.
LogDataStore datastore(executor, TEST_PATH("bug-7257706"), config, GrowStrategy(),
- TuneFileSummary(), fileHeaderContext, tlSyncer, NULL);
+ TuneFileSummary(), fileHeaderContext, tlSyncer, nullptr);
EXPECT_EQUAL(354ul, datastore.lastSyncToken());
}
const char * magic = "mumbo jumbo";
{
LogDataStore datastore(executor, "bug-7257706-truncated", config, GrowStrategy(),
- TuneFileSummary(), fileHeaderContext, tlSyncer, NULL);
+ TuneFileSummary(), fileHeaderContext, tlSyncer, nullptr);
EXPECT_EQUAL(331ul, datastore.lastSyncToken());
datastore.write(332, 7, magic, strlen(magic));
datastore.write(333, 8, magic, strlen(magic));
@@ -296,7 +296,7 @@ TEST("testTruncatedIdxFile"){
}
{
LogDataStore datastore(executor, "bug-7257706-truncated", config, GrowStrategy(),
- TuneFileSummary(), fileHeaderContext, tlSyncer, NULL);
+ TuneFileSummary(), fileHeaderContext, tlSyncer, nullptr);
EXPECT_EQUAL(334ul, datastore.lastSyncToken());
}
}
@@ -308,7 +308,7 @@ TEST("testThatEmptyIdxFilesAndDanglingDatFilesAreRemoved") {
MyTlSyncer tlSyncer;
LogDataStore datastore(executor, "dangling-test", config,
GrowStrategy(), TuneFileSummary(),
- fileHeaderContext, tlSyncer, NULL);
+ fileHeaderContext, tlSyncer, nullptr);
EXPECT_EQUAL(354ul, datastore.lastSyncToken());
EXPECT_EQUAL(4096u + 480u, datastore.getDiskHeaderFootprint());
EXPECT_EQUAL(datastore.getDiskHeaderFootprint() + 94016u, datastore.getDiskFootprint());
@@ -321,7 +321,7 @@ TEST("testThatIncompleteCompactedFilesAreRemoved") {
MyTlSyncer tlSyncer;
LogDataStore datastore(executor, "incompletecompact-test", config,
GrowStrategy(), TuneFileSummary(),
- fileHeaderContext, tlSyncer, NULL);
+ fileHeaderContext, tlSyncer, nullptr);
EXPECT_EQUAL(354ul, datastore.lastSyncToken());
EXPECT_EQUAL(3*(4096u + 480u), datastore.getDiskHeaderFootprint());
LogDataStore::NameIdSet files = datastore.getAllActiveFiles();
@@ -340,7 +340,7 @@ public:
_executor(1, 128*1024),
_tlSyncer(),
_datastore(_executor, _myDir.getDir(), _config, GrowStrategy(),
- TuneFileSummary(), _fileHeaderContext, _tlSyncer, NULL)
+ TuneFileSummary(), _fileHeaderContext, _tlSyncer, nullptr)
{ }
~VisitStore();
IDataStore & getStore() { return _datastore; }
@@ -414,7 +414,7 @@ makeDocTypeRepoConfig()
Document::UP
-makeDoc(const DocumentTypeRepo &repo, uint32_t i, bool extra_field)
+makeDoc(const DocumentTypeRepo &repo, uint32_t i, bool extra_field, size_t numReps=0)
{
asciistream idstr;
idstr << "id:test:test:: " << i;
@@ -424,7 +424,7 @@ makeDoc(const DocumentTypeRepo &repo, uint32_t i, bool extra_field)
ASSERT_TRUE(doc.get());
asciistream mainstr;
mainstr << "static text" << i << " body something";
- for (uint32_t j = 0; j < 10; ++j) {
+ for (uint32_t j = 0; j < 10+numReps; ++j) {
mainstr << (j + i * 1000) << " ";
}
mainstr << " and end field";
@@ -432,7 +432,6 @@ makeDoc(const DocumentTypeRepo &repo, uint32_t i, bool extra_field)
if (extra_field) {
doc->set("extra", "foo");
}
-
return doc;
}
@@ -440,11 +439,15 @@ makeDoc(const DocumentTypeRepo &repo, uint32_t i, bool extra_field)
class VisitCacheStore {
public:
- VisitCacheStore();
+ using UpdateStrategy=DocumentStore::Config::UpdateStrategy;
+ VisitCacheStore(UpdateStrategy strategy);
~VisitCacheStore();
- IDocumentStore & getStore() { return _datastore; }
+ IDocumentStore & getStore() { return *_datastore; }
void write(uint32_t id) {
- write(id, makeDoc(_repo, id, true));
+ write(id, 0);
+ }
+ void write(uint32_t lid, uint32_t numReps) {
+ write(lid, makeDoc(_repo, lid, true, numReps));
}
void rewrite(uint32_t id) {
write(id, makeDoc(_repo, id, false));
@@ -458,7 +461,10 @@ public:
_inserted.erase(id);
}
void verifyRead(uint32_t id) {
- verifyDoc(*_datastore.read(id, _repo), id);
+ verifyDoc(*_datastore->read(id, _repo), id);
+ }
+ void read(uint32_t id) {
+ *_datastore->read(id, _repo);
}
void verifyDoc(const Document & doc, uint32_t id) {
EXPECT_TRUE(doc == *_inserted[id]);
@@ -468,8 +474,10 @@ public:
}
void verifyVisit(const std::vector<uint32_t> & lids, const std::vector<uint32_t> & expected, bool allowCaching) {
VerifyVisitor vv(*this, expected, allowCaching);
- _datastore.visit(lids, _repo, vv);
+ _datastore->visit(lids, _repo, vv);
}
+ void recreate();
+
private:
class VerifyVisitor : public IDocumentVisitor {
public:
@@ -494,7 +502,7 @@ private:
DummyFileHeaderContext _fileHeaderContext;
vespalib::ThreadStackExecutor _executor;
MyTlSyncer _tlSyncer;
- LogDocumentStore _datastore;
+ std::unique_ptr<LogDocumentStore> _datastore;
std::map<uint32_t, Document::UP> _inserted;
SerialNum _serial;
};
@@ -510,21 +518,33 @@ VisitCacheStore::VerifyVisitor::~VerifyVisitor() {
EXPECT_EQUAL(_expected.size(), _actual.size());
}
-VisitCacheStore::VisitCacheStore() :
+
+VisitCacheStore::VisitCacheStore(UpdateStrategy strategy) :
_myDir("visitcache"),
_repo(makeDocTypeRepoConfig()),
- _config(DocumentStore::Config(CompressionConfig::LZ4, 1000000, 0).allowVisitCaching(true),
+ _config(DocumentStore::Config(CompressionConfig::LZ4, 1000000, 0)
+ .allowVisitCaching(true).updateStrategy(strategy),
LogDataStore::Config().setMaxFileSize(50000).setMaxBucketSpread(3.0)
.setFileConfig(WriteableFileChunk::Config(CompressionConfig(), 16384))),
_fileHeaderContext(),
_executor(1, 128*1024),
_tlSyncer(),
- _datastore(_executor, _myDir.getDir(), _config, GrowStrategy(),
- TuneFileSummary(), _fileHeaderContext, _tlSyncer, nullptr),
+ _datastore(std::make_unique<LogDocumentStore>(_executor, _myDir.getDir(), _config, GrowStrategy(),
+ TuneFileSummary(), _fileHeaderContext, _tlSyncer, nullptr)),
_inserted(),
_serial(1)
{ }
-VisitCacheStore::~VisitCacheStore() {}
+
+VisitCacheStore::~VisitCacheStore() = default;
+
+void
+VisitCacheStore::recreate() {
+ _datastore->flush(_datastore->initFlush(_datastore->tentativeLastSyncToken()));
+ _datastore.reset();
+ _datastore = std::make_unique<LogDocumentStore>(_executor, _myDir.getDir(), _config, GrowStrategy(),
+ TuneFileSummary(), _fileHeaderContext, _tlSyncer, nullptr);
+
+}
void
verifyCacheStats(CacheStats cs, size_t hits, size_t misses, size_t elements, size_t memory_used) {
@@ -535,8 +555,64 @@ verifyCacheStats(CacheStats cs, size_t hits, size_t misses, size_t elements, siz
EXPECT_GREATER_EQUAL(memory_used+20, cs.memory_used);
}
+TEST("test the update cache strategy") {
+ VisitCacheStore vcs(DocumentStore::Config::UpdateStrategy::UPDATE);
+ IDocumentStore & ds = vcs.getStore();
+ for (size_t i(1); i <= 10; i++) {
+ vcs.write(i);
+ }
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 0, 0, 0, 0));
+ vcs.verifyRead(7);
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 0, 1, 1, 221));
+ vcs.write(8);
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 0, 1, 1, 221));
+ vcs.write(7, 17);
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 0, 1, 1, 282));
+ vcs.verifyRead(7);
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 1, 1, 1, 282));
+ vcs.remove(8);
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 1, 1, 1, 282));
+ vcs.remove(7);
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 1, 1, 0, 0));
+ vcs.write(7);
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 1, 1, 0, 0));
+ vcs.verifyRead(7);
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 1, 2, 1, 221));
+ vcs.write(7, 17);
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 1, 2, 1, 282));
+ vcs.recreate();
+ IDocumentStore & ds2 = vcs.getStore();
+ vcs.verifyRead(7);
+ TEST_DO(verifyCacheStats(ds2.getCacheStats(), 0, 1, 1, 282));
+}
+
+TEST("test the invalidate cache strategy") {
+ VisitCacheStore vcs(DocumentStore::Config::UpdateStrategy::INVALIDATE);
+ IDocumentStore & ds = vcs.getStore();
+ for (size_t i(1); i <= 10; i++) {
+ vcs.write(i);
+ }
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 0, 0, 0, 0));
+ vcs.verifyRead(7);
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 0, 1, 1, 221));
+ vcs.write(8);
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 0, 1, 1, 221));
+ vcs.write(7);
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 0, 1, 0, 0));
+ vcs.verifyRead(7);
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 0, 2, 1, 221));
+ vcs.remove(8);
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 0, 2, 1, 221));
+ vcs.remove(7);
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 0, 2, 0, 0));
+ vcs.write(7);
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 0, 2, 0, 0));
+ vcs.verifyRead(7);
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 0, 3, 1, 221));
+}
+
TEST("test that the integrated visit cache works.") {
- VisitCacheStore vcs;
+ VisitCacheStore vcs(DocumentStore::Config::UpdateStrategy::INVALIDATE);
IDocumentStore & ds = vcs.getStore();
for (size_t i(1); i <= 100; i++) {
vcs.write(i);
@@ -546,41 +622,42 @@ TEST("test that the integrated visit cache works.") {
for (size_t i(1); i <= 100; i++) {
vcs.verifyRead(i);
}
- TEST_DO(verifyCacheStats(ds.getCacheStats(), 0, 100, 100, 20574));
+ constexpr size_t BASE_SZ = 21374;
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 0, 100, 100, BASE_SZ));
for (size_t i(1); i <= 100; i++) {
vcs.verifyRead(i);
}
- TEST_DO(verifyCacheStats(ds.getCacheStats(), 100, 100, 100, 20574)); // From the individual cache.
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 100, 100, 100, BASE_SZ)); // From the individual cache.
vcs.verifyVisit({7,9,17,19,67,88}, false);
- TEST_DO(verifyCacheStats(ds.getCacheStats(), 100, 100, 100, 20574));
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 100, 100, 100, BASE_SZ));
vcs.verifyVisit({7,9,17,19,67,88}, true);
- TEST_DO(verifyCacheStats(ds.getCacheStats(), 100, 101, 101, 21135));
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 100, 101, 101, BASE_SZ+557));
vcs.verifyVisit({7,9,17,19,67,88}, true);
- TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 101, 101, 21135));
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 101, 101, BASE_SZ+557));
vcs.rewrite(8);
- TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 101, 100, 20922)); // From the individual cache.
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 101, 100, BASE_SZ+328)); // From the individual cache.
vcs.rewrite(7);
- TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 101, 98, 20148)); // From the both caches.
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 101, 98, BASE_SZ-442)); // From the both caches.
vcs.verifyVisit({7,9,17,19,67,88}, true);
- TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 102, 99, 20732));
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 102, 99, BASE_SZ+130));
vcs.verifyVisit({7,9,17,19,67,88,89}, true);
- TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 103, 99, 20783));
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 103, 99, BASE_SZ+201));
vcs.rewrite(17);
- TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 103, 97, 19943));
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 103, 97, BASE_SZ-657));
vcs.verifyVisit({7,9,17,19,67,88,89}, true);
- TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 104, 98, 20587));
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 104, 98, BASE_SZ-3));
vcs.remove(17);
- TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 104, 97, 19943));
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 104, 97, BASE_SZ-657));
vcs.verifyVisit({7,9,17,19,67,88,89}, {7,9,19,67,88,89}, true);
- TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 105, 98, 20526));
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 105, 98, BASE_SZ-64));
vcs.verifyVisit({41, 42}, true);
- TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 106, 99, 20820));
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 106, 99, BASE_SZ+238));
vcs.verifyVisit({43, 44}, true);
- TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 107, 100, 21124));
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 107, 100, BASE_SZ+540));
vcs.verifyVisit({41, 42, 43, 44}, true);
- TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 108, 99, 20944));
+ TEST_DO(verifyCacheStats(ds.getCacheStats(), 101, 108, 99, BASE_SZ+362));
}
TEST("testWriteRead") {
@@ -595,7 +672,7 @@ TEST("testWriteRead") {
vespalib::ThreadStackExecutor executor(1, 128*1024);
MyTlSyncer tlSyncer;
LogDataStore datastore(executor, "empty", config, GrowStrategy(),
- TuneFileSummary(), fileHeaderContext, tlSyncer, NULL);
+ TuneFileSummary(), fileHeaderContext, tlSyncer, nullptr);
ASSERT_TRUE(datastore.lastSyncToken() == 0);
size_t headerFootprint = datastore.getDiskHeaderFootprint();
EXPECT_LESS(0u, headerFootprint);
@@ -606,7 +683,7 @@ TEST("testWriteRead") {
fetchAndTest(datastore, 0, a[0].c_str(), a[0].size());
datastore.write(2, 0, a[1].c_str(), a[1].size());
fetchAndTest(datastore, 0, a[1].c_str(), a[1].size());
- fetchAndTest(datastore, 1, NULL, 0);
+ fetchAndTest(datastore, 1, nullptr, 0);
datastore.remove(3, 0);
fetchAndTest(datastore, 0, "", 0);
@@ -632,7 +709,7 @@ TEST("testWriteRead") {
MyTlSyncer tlSyncer;
LogDataStore datastore(executor, "empty", config,
GrowStrategy(), TuneFileSummary(),
- fileHeaderContext, tlSyncer, NULL);
+ fileHeaderContext, tlSyncer, nullptr);
size_t headerFootprint = datastore.getDiskHeaderFootprint();
EXPECT_LESS(0u, headerFootprint);
EXPECT_EQUAL(4944ul + headerFootprint, datastore.getDiskFootprint());
diff --git a/searchlib/src/tests/features/max_reduce_prod_join_replacer/max_reduce_prod_join_replacer_test.cpp b/searchlib/src/tests/features/max_reduce_prod_join_replacer/max_reduce_prod_join_replacer_test.cpp
index 1c6c224cc79..c9c8124bb94 100644
--- a/searchlib/src/tests/features/max_reduce_prod_join_replacer/max_reduce_prod_join_replacer_test.cpp
+++ b/searchlib/src/tests/features/max_reduce_prod_join_replacer/max_reduce_prod_join_replacer_test.cpp
@@ -8,6 +8,9 @@
#include <vespa/searchlib/fef/test/indexenvironment.h>
#include <vespa/searchlib/fef/blueprint.h>
+#include <vespa/log/log.h>
+LOG_SETUP("max_reduce_prod_join_replacer_test");
+
using search::features::MaxReduceProdJoinReplacer;
using search::features::rankingexpression::ExpressionReplacer;
using search::features::rankingexpression::FeatureNameExtractor;
@@ -36,7 +39,7 @@ struct MyBlueprint : Blueprint {
return true;
}
FeatureExecutor &createExecutor(const IQueryEnvironment &, vespalib::Stash &) const override {
- abort();
+ LOG_ABORT("should not be reached");
}
};
diff --git a/searchlib/src/tests/features/native_dot_product/native_dot_product_test.cpp b/searchlib/src/tests/features/native_dot_product/native_dot_product_test.cpp
index 1da912ccb3a..0cf13443142 100644
--- a/searchlib/src/tests/features/native_dot_product/native_dot_product_test.cpp
+++ b/searchlib/src/tests/features/native_dot_product/native_dot_product_test.cpp
@@ -16,7 +16,8 @@ using namespace search::fef::test;
using namespace search::features;
using CollectionType = FieldInfo::CollectionType;
-const std::string featureName("nativeDotProduct(foo)");
+const std::string fooFeatureName("nativeDotProduct(foo)");
+const std::string anyFeatureName("nativeDotProduct");
struct BlueprintFactoryFixture {
BlueprintFactory factory;
@@ -78,7 +79,8 @@ struct RankFixture : BlueprintFactoryFixture, IndexFixture {
std::vector<TermFieldHandle> fooHandles;
std::vector<TermFieldHandle> barHandles;
RankFixture(const std::vector<uint32_t> &fooWeights,
- const std::vector<uint32_t> &barWeights)
+ const std::vector<uint32_t> &barWeights,
+ const vespalib::string &featureName = fooFeatureName)
: queryEnv(&indexEnv), rankSetup(factory, indexEnv),
mdl(), match_data(), rankProgram(), fooHandles(), barHandles()
{
@@ -152,6 +154,12 @@ TEST_FF("require that setup fails for unknown field", NativeDotProductBlueprint,
EXPECT_TRUE(!((Blueprint&)f1).setup(f2.indexEnv, std::vector<vespalib::string>(1, "unknown")));
}
+TEST_FF("require that setup can be done without field", NativeDotProductBlueprint, IndexFixture) {
+ DummyDependencyHandler deps(f1);
+ f1.setName(vespalib::make_string("%s", f1.getBaseName().c_str()));
+ EXPECT_TRUE(((Blueprint&)f1).setup(f2.indexEnv, std::vector<vespalib::string>()));
+}
+
TEST_F("require that not searching a field will give it 0.0 dot product", RankFixture(vec(), vec(1, 2, 3))) {
EXPECT_EQUAL(0.0, f1.getScore(10));
}
@@ -183,11 +191,18 @@ TEST_F("require that data from other fields is ignored", RankFixture(vec(1, 3),
EXPECT_EQUAL(14, f1.getScore(10));
}
+TEST_F("require that not specifying field includes all term/field combinations", RankFixture(vec(1, 3), vec(5, 7), anyFeatureName)) {
+ f1.setFooWeight(0, 10, 2);
+ f1.setFooWeight(1, 10, 4);
+ f1.setBarWeight(0, 10, 6);
+ f1.setBarWeight(1, 10, 8);
+ EXPECT_EQUAL(100, f1.getScore(10));
+}
+
TEST_F("require that negative weights in the index works", RankFixture(vec(1, 3), vec())) {
f1.setFooWeight(0, 10, 2);
f1.setFooWeight(1, 10, -4);
EXPECT_EQUAL(-10, f1.getScore(10));
}
-
TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/searchlib/src/tests/features/prod_features.cpp b/searchlib/src/tests/features/prod_features.cpp
index 345c66ec672..0e57f520673 100644
--- a/searchlib/src/tests/features/prod_features.cpp
+++ b/searchlib/src/tests/features/prod_features.cpp
@@ -32,6 +32,7 @@ LOG_SETUP("prod_features_test");
#include <vespa/searchlib/features/querytermcountfeature.h>
#include <vespa/searchlib/features/randomfeature.h>
#include <vespa/searchlib/features/random_normal_feature.h>
+#include <vespa/searchlib/features/random_normal_stable_feature.h>
#include <vespa/searchlib/features/rankingexpressionfeature.h>
#include <vespa/searchlib/features/setup.h>
#include <vespa/searchlib/features/termfeature.h>
@@ -105,6 +106,7 @@ Test::Main()
TEST_DO(testQueryTermCount()); TEST_FLUSH();
TEST_DO(testRandom()); TEST_FLUSH();
TEST_DO(testRandomNormal()); TEST_FLUSH();
+ TEST_DO(testRandomNormalStable()); TEST_FLUSH();
TEST_DO(testRankingExpression()); TEST_FLUSH();
TEST_DO(testTerm()); TEST_FLUSH();
TEST_DO(testTermDistance()); TEST_FLUSH();
@@ -1727,17 +1729,16 @@ Test::testRandom()
}
void
-Test::testRandomNormal()
-{
+Test::testRandomNormal() {
{ // Test blueprint.
RandomNormalBlueprint pt;
EXPECT_TRUE(assertCreateInstance(pt, "randomNormal"));
StringList params, in, out;
- FT_SETUP_OK (pt, params, in, out.add("out"));
- FT_SETUP_OK (pt, params.add("0.5").add("1.0"), in, out);
- FT_SETUP_OK (pt, params.add("val1"), in, out);
+ FT_SETUP_OK(pt, params, in, out.add("out"));
+ FT_SETUP_OK(pt, params.add("0.5").add("1.0"), in, out);
+ FT_SETUP_OK(pt, params.add("val1"), in, out);
FT_DUMP_EMPTY(_factory, "randomNormal");
}
@@ -1766,7 +1767,50 @@ Test::testRandomNormal()
for (uint32_t i = 0; i < 5; ++i) {
rr.clear();
ASSERT_TRUE(ft1.executeOnly(rr, i + 1));
- ASSERT_TRUE(ft2.execute(((rr.getScore("randomNormal(0.0,0.1)")-0.0)/0.1) * 0.2 + 1.0, EPS, i + 1));
+ ASSERT_TRUE(ft2.execute(((rr.getScore("randomNormal(0.0,0.1)") - 0.0) / 0.1) * 0.2 + 1.0, EPS, i + 1));
+ }
+ }
+}
+
+void
+Test::testRandomNormalStable() {
+ { // Test blueprint.
+ RandomNormalStableBlueprint pt;
+
+ EXPECT_TRUE(assertCreateInstance(pt, "randomNormalStable"));
+
+ StringList params, in, out;
+ FT_SETUP_OK(pt, params, in, out.add("out"));
+ FT_SETUP_OK(pt, params.add("0.5").add("1.0"), in, out);
+ FT_SETUP_OK(pt, params.add("val1"), in, out);
+
+ FT_DUMP_EMPTY(_factory, "randomNormalStable");
+ }
+
+ { // Test setting of mean and stddev values, and seed
+ FtFeatureTest ft1(_factory, "randomNormalStable(0.0,0.1)");
+ FtFeatureTest ft2(_factory, "randomNormalStable(1.0,0.2)");
+ ft1.getIndexEnv().getProperties().add("randomNormalStable(0.0,0.1).seed", "100");
+ ft2.getIndexEnv().getProperties().add("randomNormalStable(1.0,0.2).seed", "100");
+ ASSERT_TRUE(ft1.setup());
+ ASSERT_TRUE(ft2.setup());
+ RankResult rr;
+ for (uint32_t i = 0; i < 5; ++i) {
+ rr.clear();
+ ASSERT_TRUE(ft1.executeOnly(rr, i + 1));
+ ASSERT_TRUE(ft2.execute(((rr.getScore("randomNormalStable(0.0,0.1)") - 0.0) / 0.1) * 0.2 + 1.0, EPS, i + 1));
+ }
+ }
+ { // Test executor (randomNormalStable)
+ FtFeatureTest ft1(_factory, "randomNormalStable");
+ FtFeatureTest ft2(_factory, "randomNormalStable");
+ ASSERT_TRUE(ft1.setup());
+ ASSERT_TRUE(ft2.setup());
+ RankResult rr;
+ for (uint32_t i = 0; i < 5; ++i) {
+ rr.clear();
+ ASSERT_TRUE(ft1.executeOnly(rr, i + 1));
+ ASSERT_TRUE(ft2.execute(rr.getScore("randomNormalStable"), EPS, i + 1));
}
}
}
diff --git a/searchlib/src/tests/features/prod_features.h b/searchlib/src/tests/features/prod_features.h
index 0d234ca674e..d7bf001bedf 100644
--- a/searchlib/src/tests/features/prod_features.h
+++ b/searchlib/src/tests/features/prod_features.h
@@ -35,6 +35,7 @@ public:
void testQueryTermCount();
void testRandom();
void testRandomNormal();
+ void testRandomNormalStable();
void testRankingExpression();
void testTerm();
void testTermDistance();
diff --git a/searchlib/src/tests/fef/termfieldmodel/termfieldmodel_test.cpp b/searchlib/src/tests/fef/termfieldmodel/termfieldmodel_test.cpp
index 01c72497246..e689e613886 100644
--- a/searchlib/src/tests/fef/termfieldmodel/termfieldmodel_test.cpp
+++ b/searchlib/src/tests/fef/termfieldmodel/termfieldmodel_test.cpp
@@ -192,6 +192,50 @@ TEST("term field model") {
testInvalidId();
}
+TEST("append positions") {
+ TermFieldMatchData tfmd;
+ tfmd.setFieldId(123);
+ EXPECT_EQUAL(0u, tfmd.size());
+ EXPECT_EQUAL(1u, tfmd.capacity());
+ tfmd.reset(7);
+ EXPECT_EQUAL(0u, tfmd.size());
+ EXPECT_EQUAL(1u, tfmd.capacity());
+ TermFieldMatchDataPosition pos(0x01020304, 0x10203040, 0x11223344, 0x12345678);
+ tfmd.appendPosition(pos);
+ EXPECT_EQUAL(1u, tfmd.size());
+ EXPECT_EQUAL(1u, tfmd.capacity());
+ EXPECT_EQUAL(0x01020304u, tfmd.begin()->getElementId());
+ EXPECT_EQUAL(0x10203040u, tfmd.begin()->getPosition());
+ EXPECT_EQUAL(0x11223344, tfmd.begin()->getElementWeight());
+ EXPECT_EQUAL(0x12345678u, tfmd.begin()->getElementLen());
+ tfmd.reset(11);
+ EXPECT_EQUAL(0u, tfmd.size());
+ EXPECT_EQUAL(1u, tfmd.capacity());
+ TermFieldMatchDataPosition pos2(0x21020304, 0x20203040, 0x21223344, 0x22345678);
+ tfmd.appendPosition(pos);
+ tfmd.appendPosition(pos2);
+ EXPECT_EQUAL(2u, tfmd.size());
+ EXPECT_EQUAL(2u, tfmd.capacity());
+ TermFieldMatchDataPosition pos3(0x31020304, 0x30203040, 0x31223344, 0x32345678);
+ tfmd.appendPosition(pos3);
+ EXPECT_EQUAL(3u, tfmd.size());
+ EXPECT_EQUAL(4u, tfmd.capacity());
+ EXPECT_EQUAL(0x01020304u, tfmd.begin()->getElementId());
+ EXPECT_EQUAL(0x10203040u, tfmd.begin()->getPosition());
+ EXPECT_EQUAL(0x11223344, tfmd.begin()->getElementWeight());
+ EXPECT_EQUAL(0x12345678u, tfmd.begin()->getElementLen());
+
+ EXPECT_EQUAL(0x21020304u, tfmd.begin()[1].getElementId());
+ EXPECT_EQUAL(0x20203040u, tfmd.begin()[1].getPosition());
+ EXPECT_EQUAL(0x21223344, tfmd.begin()[1].getElementWeight());
+ EXPECT_EQUAL(0x22345678u, tfmd.begin()[1].getElementLen());
+
+ EXPECT_EQUAL(0x31020304u, tfmd.begin()[2].getElementId());
+ EXPECT_EQUAL(0x30203040u, tfmd.begin()[2].getPosition());
+ EXPECT_EQUAL(0x31223344, tfmd.begin()[2].getElementWeight());
+ EXPECT_EQUAL(0x32345678u, tfmd.begin()[2].getElementLen());
+}
+
TEST("Access subqueries") {
State state;
testSetup(state);
diff --git a/searchlib/src/tests/grouping/grouping_test.cpp b/searchlib/src/tests/grouping/grouping_test.cpp
index 32174ba7647..4cf9eb6f5c6 100644
--- a/searchlib/src/tests/grouping/grouping_test.cpp
+++ b/searchlib/src/tests/grouping/grouping_test.cpp
@@ -13,6 +13,9 @@
#include <cmath>
#include <iostream>
+#include <vespa/log/log.h>
+LOG_SETUP("grouping_test");
+
using namespace vespalib;
using namespace search;
using namespace search::aggregation;
diff --git a/searchlib/src/tests/groupingengine/groupingengine_test.cpp b/searchlib/src/tests/groupingengine/groupingengine_test.cpp
index a90adc5e0c9..a0a6c5cf4c2 100644
--- a/searchlib/src/tests/groupingengine/groupingengine_test.cpp
+++ b/searchlib/src/tests/groupingengine/groupingengine_test.cpp
@@ -14,6 +14,9 @@
#include <cmath>
#include <iostream>
+#include <vespa/log/log.h>
+LOG_SETUP("grouping_engine_test");
+
using namespace vespalib;
using namespace search;
using namespace search::attribute;
diff --git a/searchlib/src/tests/predicate/simple_index_test.cpp b/searchlib/src/tests/predicate/simple_index_test.cpp
index 52cf9c138c7..fbb0a5933c9 100644
--- a/searchlib/src/tests/predicate/simple_index_test.cpp
+++ b/searchlib/src/tests/predicate/simple_index_test.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.
// Unit tests for simple_index.
+
#include <vespa/searchlib/predicate/simple_index.hpp>
#include <vespa/vespalib/testkit/testapp.h>
#include <vespa/searchlib/attribute/predicate_attribute.h>
diff --git a/searchlib/src/tests/queryeval/queryeval.cpp b/searchlib/src/tests/queryeval/queryeval.cpp
index c6dd6a430cc..9f4d8403d76 100644
--- a/searchlib/src/tests/queryeval/queryeval.cpp
+++ b/searchlib/src/tests/queryeval/queryeval.cpp
@@ -1,6 +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/testkit/test_kit.h>
+#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/searchlib/test/initrange.h>
#include <vespa/searchlib/common/bitvectoriterator.h>
#include <vespa/searchlib/queryeval/andnotsearch.h>
@@ -18,9 +18,11 @@
#include <vespa/searchlib/attribute/singlenumericattribute.hpp>
#include <vespa/vespalib/test/insertion_operators.h>
#include <vespa/searchlib/queryeval/isourceselector.h>
-
#include <vespa/searchlib/fef/fef.h>
+#include <vespa/log/log.h>
+LOG_SETUP("query_eval_test");
+
using namespace search::queryeval;
using search::BitVector;
using search::BitVectorIterator;
diff --git a/searchlib/src/tests/queryeval/same_element/same_element_test.cpp b/searchlib/src/tests/queryeval/same_element/same_element_test.cpp
index d89883bc417..45ebdd78fb3 100644
--- a/searchlib/src/tests/queryeval/same_element/same_element_test.cpp
+++ b/searchlib/src/tests/queryeval/same_element/same_element_test.cpp
@@ -5,17 +5,24 @@
#include <vespa/searchlib/queryeval/leaf_blueprints.h>
#include <vespa/searchlib/queryeval/simpleresult.h>
#include <vespa/searchlib/queryeval/same_element_blueprint.h>
+#include <vespa/searchlib/queryeval/same_element_search.h>
+#include <vespa/searchlib/queryeval/emptysearch.h>
+#include <vespa/searchcommon/attribute/i_search_context.h>
+#include <vespa/searchlib/attribute/elementiterator.h>
using namespace search::fef;
using namespace search::queryeval;
+using search::attribute::ElementIterator;
-std::unique_ptr<SameElementBlueprint> make_blueprint(const std::vector<FakeResult> &children) {
+std::unique_ptr<SameElementBlueprint> make_blueprint(const std::vector<FakeResult> &children, bool fake_attr = false) {
auto result = std::make_unique<SameElementBlueprint>();
for (size_t i = 0; i < children.size(); ++i) {
uint32_t field_id = i;
vespalib::string field_name = vespalib::make_string("f%u", field_id);
FieldSpec field = result->getNextChildField(field_name, field_id);
- result->addTerm(std::make_unique<FakeBlueprint>(field, children[i]));
+ auto fake = std::make_unique<FakeBlueprint>(field, children[i]);
+ fake->is_attr(fake_attr);
+ result->addTerm(std::move(fake));
}
return result;
}
@@ -96,4 +103,17 @@ TEST("require that children are sorted") {
EXPECT_EQUAL(dynamic_cast<SameElementBlueprint&>(*bp).terms()[2]->getState().estimate().estHits, 4u);
}
+TEST("require that attribute iterators are wrapped for element unpacking") {
+ auto a = make_result({{5, {1,3,7}}});
+ auto b = make_result({{5, {3,5,10}}});
+ auto bp = finalize(make_blueprint({a,b}, true), true);
+ auto md = MatchData::makeTestInstance(0, 0);
+ auto search = bp->createSearch(*md, false);
+ SameElementSearch *se = dynamic_cast<SameElementSearch*>(search.get());
+ ASSERT_TRUE(se != nullptr);
+ ASSERT_EQUAL(se->children().size(), 2u);
+ EXPECT_TRUE(dynamic_cast<ElementIterator*>(se->children()[0].get()) != nullptr);
+ EXPECT_TRUE(dynamic_cast<ElementIterator*>(se->children()[1].get()) != nullptr);
+}
+
TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/searchlib/src/tests/util/bufferwriter/bm.cpp b/searchlib/src/tests/util/bufferwriter/bm.cpp
index e5ff0f49973..3370860d2b6 100644
--- a/searchlib/src/tests/util/bufferwriter/bm.cpp
+++ b/searchlib/src/tests/util/bufferwriter/bm.cpp
@@ -46,7 +46,7 @@ callWork(size_t size, WorkFuncDispatch dispatch)
workFunctor2(foo, writer);
break;
default:
- abort();
+ LOG_ABORT("should not be reached");
}
double after = getTime();
double delta = (after - before);
diff --git a/searchlib/src/tests/util/ioerrorhandler/ioerrorhandler_test.cpp b/searchlib/src/tests/util/ioerrorhandler/ioerrorhandler_test.cpp
index f6055376907..fb1c1a356f6 100644
--- a/searchlib/src/tests/util/ioerrorhandler/ioerrorhandler_test.cpp
+++ b/searchlib/src/tests/util/ioerrorhandler/ioerrorhandler_test.cpp
@@ -213,8 +213,7 @@ TEST_F("Test that ioerror handler can process read error", Fixture)
injectErrno = EIO;
injectreadErrnoTrigger = 1;
f.file->ReadBuf(buf, fileSize);
- LOG(error, "Should never get here");
- abort();
+ LOG_ABORT("Should never get here");
} catch (std::runtime_error &e) {
LOG(info, "Caught std::runtime_error exception: %s", e.what());
EXPECT_TRUE(strstr(e.what(), "Input/output error") != nullptr);
@@ -253,8 +252,7 @@ TEST_F("Test that ioerror handler can process pread error", Fixture)
injectErrno = EIO;
injectpreadErrnoTrigger = 1;
f.file->ReadBuf(buf, fileSize, 0);
- LOG(error, "Should never get here");
- abort();
+ LOG_ABORT("Should never get here");
} catch (std::runtime_error &e) {
LOG(info, "Caught std::runtime_error exception: %s", e.what());
EXPECT_TRUE(strstr(e.what(), "Input/output error") != nullptr);
@@ -287,8 +285,7 @@ TEST_F("Test that ioerror handler can process write error", Fixture)
injectErrno = EIO;
injectwriteErrnoTrigger = 1;
f.writeTestString();
- LOG(error, "Should never get here");
- abort();
+ LOG_ABORT("Should never get here");
} catch (std::runtime_error &e) {
LOG(info, "Caught std::runtime_error exception: %s", e.what());
EXPECT_TRUE(strstr(e.what(), "Input/output error") != nullptr);
@@ -322,8 +319,7 @@ TEST_F("Test that ioerror handler can process pwrite error", Fixture)
injectErrno = EIO;
injectpwriteErrnoTrigger = 1;
f.writeTestString();
- LOG(error, "Should never get here");
- abort();
+ LOG_ABORT("Should never get here");
} catch (std::runtime_error &e) {
LOG(info, "Caught std::runtime_error exception: %s", e.what());
EXPECT_TRUE(strstr(e.what(), "Input/output error") != nullptr);
diff --git a/searchlib/src/tests/util/sigbushandler/sigbushandler_test.cpp b/searchlib/src/tests/util/sigbushandler/sigbushandler_test.cpp
index c86a1c86dbb..658ad17544a 100644
--- a/searchlib/src/tests/util/sigbushandler/sigbushandler_test.cpp
+++ b/searchlib/src/tests/util/sigbushandler/sigbushandler_test.cpp
@@ -63,8 +63,7 @@ TEST("Test that sigbus handler can trap synthetic sigbus")
if (sigsetjmp(sjb, 1) == 0) {
sbh.setUnwind(&sjb);
kill(getpid(), SIGBUS);
- LOG(error, "Should never get here");
- abort();
+ LOG_ABORT("Should never get here");
}
EXPECT_TRUE(sbh.fired());
{
@@ -100,8 +99,7 @@ TEST("Test that sigbus handler can trap normal sigbus")
if (sigsetjmp(sjb, 1) == 0) {
sbh.setUnwind(&sjb);
r = *p;
- LOG(error, "Should never get here");
- abort();
+ LOG_ABORT("Should never get here");
}
EXPECT_TRUE(sbh.fired());
EXPECT_TRUE(r == '\0');
diff --git a/searchlib/src/vespa/searchlib/aggregation/hitsaggregationresult.cpp b/searchlib/src/vespa/searchlib/aggregation/hitsaggregationresult.cpp
index ac12a7a3ffd..afaf270b795 100644
--- a/searchlib/src/vespa/searchlib/aggregation/hitsaggregationresult.cpp
+++ b/searchlib/src/vespa/searchlib/aggregation/hitsaggregationresult.cpp
@@ -66,7 +66,7 @@ void
HitsAggregationResult::onAggregate(const ResultNode & result)
{
(void) result;
- assert(false);
+ LOG_ABORT("should not reach here");
}
void
diff --git a/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp b/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp
index 712288f9a1c..28310c65862 100644
--- a/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp
@@ -25,8 +25,6 @@
#include <vespa/searchlib/queryeval/weighted_set_term_search.h>
#include <vespa/searchlib/queryeval/weighted_set_term_blueprint.h>
#include <vespa/searchlib/queryeval/get_weight_from_node.h>
-
-
#include <vespa/vespalib/util/regexp.h>
#include <sstream>
diff --git a/searchlib/src/vespa/searchlib/attribute/attribute_header.cpp b/searchlib/src/vespa/searchlib/attribute/attribute_header.cpp
index 4a69e0a827d..828c7c19962 100644
--- a/searchlib/src/vespa/searchlib/attribute/attribute_header.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/attribute_header.cpp
@@ -68,9 +68,7 @@ AttributeHeader::AttributeHeader(const vespalib::string &fileName,
{
}
-AttributeHeader::~AttributeHeader()
-{
-}
+AttributeHeader::~AttributeHeader() = default;
void
AttributeHeader::internalExtractTags(const vespalib::GenericHeader &header)
diff --git a/searchlib/src/vespa/searchlib/attribute/attribute_header.h b/searchlib/src/vespa/searchlib/attribute/attribute_header.h
index 3d00396e171..4a0b1e0bee0 100644
--- a/searchlib/src/vespa/searchlib/attribute/attribute_header.h
+++ b/searchlib/src/vespa/searchlib/attribute/attribute_header.h
@@ -10,8 +10,7 @@
namespace vespalib { class GenericHeader; }
-namespace search {
-namespace attribute {
+namespace search::attribute {
/**
* Attribute header class used by attribute savers and attribute initializer
@@ -71,5 +70,4 @@ public:
void addTags(vespalib::GenericHeader &header) const;
};
-} // namespace search::attribute
-} // namespace search
+}
diff --git a/searchlib/src/vespa/searchlib/attribute/attribute_weighted_set_blueprint.cpp b/searchlib/src/vespa/searchlib/attribute/attribute_weighted_set_blueprint.cpp
index 3ff7db5a184..2eff5a05635 100644
--- a/searchlib/src/vespa/searchlib/attribute/attribute_weighted_set_blueprint.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/attribute_weighted_set_blueprint.cpp
@@ -7,6 +7,8 @@
#include <vespa/searchlib/common/bitvector.h>
#include <vespa/vespalib/stllike/hash_map.h>
#include <vespa/searchlib/fef/matchdatalayout.h>
+#include <vespa/vespalib/objects/visit.h>
+#include <vespa/vespalib/util/stringfmt.h>
namespace search {
@@ -36,14 +38,8 @@ class UseStringEnum : public UseAttr
public:
UseStringEnum(const IAttributeVector & attr)
: UseAttr(attr) {}
- bool mapToken(const ISearchContext &context, int64_t &token) const
- {
- attribute::IAttributeVector::EnumHandle handle;
- if (attribute().findEnum(context.queryTerm().getTerm(), handle)) {
- token = handle;
- return true;
- }
- return false;
+ auto mapToken(const ISearchContext &context) const {
+ return attribute().findFoldedEnums(context.queryTerm()->getTerm());
}
int64_t getToken(uint32_t docId) const {
return attribute().getEnum(docId);
@@ -56,14 +52,13 @@ class UseInteger : public UseAttr
{
public:
UseInteger(const IAttributeVector & attr) : UseAttr(attr) {}
- bool mapToken(const ISearchContext &context, int64_t &token) const
- {
+ std::vector<int64_t> mapToken(const ISearchContext &context) const {
+ std::vector<int64_t> result;
Int64Range range(context.getAsIntegerTerm());
if (range.isPoint()) {
- token = range.lower();
- return true;
+ result.push_back(range.lower());
}
- return false;
+ return result;
}
int64_t getToken(uint32_t docId) const {
return attribute().getInt(docId);
@@ -92,8 +87,7 @@ public:
: _tfmd(tfmd), _attr(attr), _map(), _weight(0)
{
for (size_t i = 0; i < contexts.size(); ++i) {
- int64_t token(0);
- if (_attr.mapToken(*contexts[i], token)) {
+ for (int64_t token : _attr.mapToken(*contexts[i])) {
_map[token] = weights[i];
}
}
@@ -174,11 +168,11 @@ AttributeWeightedSetBlueprint::createLeafSearch(const fef::TermFieldMatchDataArr
assert(isSingleValue);
(void) isSingleValue;
if (isString) {
- return queryeval::SearchIterator::UP(new AttributeFilter<UseStringEnum>(tfmd, _attr, _weights, _contexts));
+ return std::make_unique<AttributeFilter<UseStringEnum>>(tfmd, _attr, _weights, _contexts);
} else {
assert(isInteger);
(void) isInteger;
- return queryeval::SearchIterator::UP(new AttributeFilter<UseInteger>(tfmd, _attr, _weights, _contexts));
+ return std::make_unique<AttributeFilter<UseInteger>>(tfmd, _attr, _weights, _contexts);
}
}
}
@@ -187,10 +181,34 @@ void
AttributeWeightedSetBlueprint::fetchPostings(bool strict)
{
if (strict) {
- for (size_t i = 0; i < _contexts.size(); ++i) {
- _contexts[i]->fetchPostings(true);
+ for (auto * context : _contexts) {
+ context->fetchPostings(true);
+ }
+ }
+}
+
+void
+AttributeWeightedSetBlueprint::visitMembers(vespalib::ObjectVisitor &visitor) const
+{
+ ComplexLeafBlueprint::visitMembers(visitor);
+ visitor.visitString("attribute", _attr.getName());
+ visitor.openStruct("terms", "TermList");
+ for (size_t i = 0; i < _contexts.size(); ++i) {
+ const ISearchContext * context = _contexts[i];
+ visitor.openStruct(vespalib::make_string("[%zu]", i), "Term");
+ visitor.visitBool("valid", context->valid());
+ if (context-> valid()) {
+ bool isString = (_attr.isStringType() && _attr.hasEnum());
+ if (isString) {
+ visitor.visitString("term", context->queryTerm()->getTerm());
+ } else {
+ visitor.visitInt("term", context->getAsIntegerTerm().lower());
+ }
+ visitor.visitInt("weight", _weights[i]);
}
+ visitor.closeStruct();
}
+ visitor.closeStruct();
}
} // namespace search
diff --git a/searchlib/src/vespa/searchlib/attribute/attribute_weighted_set_blueprint.h b/searchlib/src/vespa/searchlib/attribute/attribute_weighted_set_blueprint.h
index 3b4bbd2d916..d66a544e77a 100644
--- a/searchlib/src/vespa/searchlib/attribute/attribute_weighted_set_blueprint.h
+++ b/searchlib/src/vespa/searchlib/attribute/attribute_weighted_set_blueprint.h
@@ -16,21 +16,21 @@ class AttributeWeightedSetBlueprint : public queryeval::ComplexLeafBlueprint
private:
using ISearchContext = attribute::ISearchContext;
using IAttributeVector = attribute::IAttributeVector;
- size_t _numDocs;
- size_t _estHits;
- std::vector<int32_t> _weights;
- const IAttributeVector & _attr;
- std::vector<ISearchContext*> _contexts;
-
- AttributeWeightedSetBlueprint(const AttributeWeightedSetBlueprint &); // disabled
- AttributeWeightedSetBlueprint &operator=(const AttributeWeightedSetBlueprint &); // disabled
+ size_t _numDocs;
+ size_t _estHits;
+ std::vector<int32_t> _weights;
+ const IAttributeVector & _attr;
+ std::vector<ISearchContext*> _contexts;
public:
+ AttributeWeightedSetBlueprint(const AttributeWeightedSetBlueprint &) = delete;
+ AttributeWeightedSetBlueprint &operator=(const AttributeWeightedSetBlueprint &) = delete;
AttributeWeightedSetBlueprint(const queryeval::FieldSpec &field, const IAttributeVector & attr);
~AttributeWeightedSetBlueprint();
void addToken(std::unique_ptr<ISearchContext> context, int32_t weight);
queryeval::SearchIterator::UP createLeafSearch(const fef::TermFieldMatchDataArray &tfmda, bool strict) const override;
void fetchPostings(bool strict) override;
+ void visitMembers(vespalib::ObjectVisitor &visitor) const override;
};
} // namespace search
diff --git a/searchlib/src/vespa/searchlib/attribute/attributefile.cpp b/searchlib/src/vespa/searchlib/attribute/attributefile.cpp
index 6f18dd5fdd1..cf3ed0957b3 100644
--- a/searchlib/src/vespa/searchlib/attribute/attributefile.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/attributefile.cpp
@@ -74,7 +74,7 @@ AttributeFile::OpenReadOnly()
if (!FileSizeCalculator::extractFileSize(datHeader, _datHeaderLen,
_datFile->GetFileName(),
_datFileSize)) {
- abort();
+ LOG_ABORT("should not be reached");
}
if (_idxFile.get()) {
if ( ! _idxFile->OpenReadOnly()) {
@@ -93,7 +93,7 @@ AttributeFile::OpenReadOnly()
if (!FileSizeCalculator::extractFileSize(idxHeader, _idxHeaderLen,
_idxFile->GetFileName(),
_idxFileSize)) {
- abort();
+ LOG_ABORT("should not be reached");
}
if (_weightFile.get()) {
if ( ! _weightFile->OpenReadOnly()) {
diff --git a/searchlib/src/vespa/searchlib/attribute/attributefilewriter.cpp b/searchlib/src/vespa/searchlib/attribute/attributefilewriter.cpp
index b1268aacc4f..415c00cb8fd 100644
--- a/searchlib/src/vespa/searchlib/attribute/attributefilewriter.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/attributefilewriter.cpp
@@ -2,10 +2,10 @@
#include "attributefilewriter.h"
#include "attributefilebufferwriter.h"
+#include "attribute_header.h"
#include <vespa/vespalib/data/fileheader.h>
#include <vespa/searchlib/common/fileheadercontext.h>
#include <vespa/searchlib/common/tunefileinfo.h>
-#include "attribute_header.h"
#include <vespa/fastos/file.h>
#include <vespa/log/log.h>
@@ -14,7 +14,6 @@ LOG_SETUP(".searchlib.attribute.attributefilewriter");
using search::common::FileHeaderContext;
using vespalib::getLastErrorString;
-
namespace search {
namespace {
@@ -23,8 +22,7 @@ const uint32_t headerAlign = 4096;
const uint32_t MIN_ALIGNMENT = 4096;
void
-writeDirectIOAligned(FastOS_FileInterface &file, const void *buf,
- size_t length)
+writeDirectIOAligned(FastOS_FileInterface &file, const void *buf, size_t length)
{
const char * data(static_cast<const char *>(buf));
size_t remaining(length);
@@ -38,7 +36,6 @@ writeDirectIOAligned(FastOS_FileInterface &file, const void *buf,
}
}
-
void
updateHeader(const vespalib::string &name, uint64_t fileBitSize)
{
@@ -63,29 +60,22 @@ class FileBackedBufferWriter : public AttributeFileBufferWriter
{
public:
FileBackedBufferWriter(AttributeFileWriter &fileWriter);
+ ~FileBackedBufferWriter() override;
- virtual ~FileBackedBufferWriter();
-
- virtual void onFlush(size_t nowLen) override;
+ void onFlush(size_t nowLen) override;
};
FileBackedBufferWriter::FileBackedBufferWriter(AttributeFileWriter &fileWriter)
: AttributeFileBufferWriter(fileWriter)
-{
-}
-
-
-FileBackedBufferWriter::~FileBackedBufferWriter()
-{
-}
+{ }
+FileBackedBufferWriter::~FileBackedBufferWriter() = default;
void
FileBackedBufferWriter::onFlush(size_t nowLen) {
// Note: Must use const ptr to indicate that buffer is pre-filled.
- Buffer buf(std::make_unique<BufferBuf>
- ((const char *) _buf->getFree(), nowLen));
+ auto buf(std::make_unique<BufferBuf>(static_cast<const void *>(_buf->getFree()), nowLen));
assert(buf->getDataLen() == nowLen);
assert(buf->getData() == _buf->getFree());
_fileWriter.writeBuf(std::move(buf));
@@ -93,7 +83,6 @@ FileBackedBufferWriter::onFlush(size_t nowLen) {
}
-
AttributeFileWriter::
AttributeFileWriter(const TuneFileAttributes &tuneFileAttributes,
const FileHeaderContext &fileHeaderContext,
@@ -107,9 +96,7 @@ AttributeFileWriter(const TuneFileAttributes &tuneFileAttributes,
_fileBitSize(0)
{ }
-
-AttributeFileWriter::~AttributeFileWriter() { }
-
+AttributeFileWriter::~AttributeFileWriter() = default;
bool
AttributeFileWriter::open(const vespalib::string &fileName)
@@ -130,7 +117,6 @@ AttributeFileWriter::open(const vespalib::string &fileName)
return true;
}
-
void
AttributeFileWriter::writeHeader()
{
@@ -142,7 +128,6 @@ AttributeFileWriter::writeHeader()
_fileBitSize = headerLen * 8;
}
-
void
AttributeFileWriter::addTags(vespalib::GenericHeader &header)
{
@@ -151,14 +136,12 @@ AttributeFileWriter::addTags(vespalib::GenericHeader &header)
header.putTag(Tag("desc", _desc));
}
-
AttributeFileWriter::Buffer
AttributeFileWriter::allocBuf(size_t size)
{
return std::make_unique<BufferBuf>(size, MIN_ALIGNMENT);
}
-
void
AttributeFileWriter::writeBuf(Buffer buf)
{
@@ -168,7 +151,6 @@ AttributeFileWriter::writeBuf(Buffer buf)
_fileBitSize += bufLen * 8;
}
-
void
AttributeFileWriter::close()
{
@@ -179,13 +161,10 @@ AttributeFileWriter::close()
}
}
-
std::unique_ptr<BufferWriter>
AttributeFileWriter::allocBufferWriter()
{
return std::make_unique<FileBackedBufferWriter>(*this);
}
-
-
} // namespace search
diff --git a/searchlib/src/vespa/searchlib/attribute/attributeiterators.cpp b/searchlib/src/vespa/searchlib/attribute/attributeiterators.cpp
index 92c0fe5d37c..2ca92f6a264 100644
--- a/searchlib/src/vespa/searchlib/attribute/attributeiterators.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/attributeiterators.cpp
@@ -3,14 +3,19 @@
#include "attributeiterators.hpp"
#include "postinglistattribute.h"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.attribute_iterators");
+
namespace search {
using queryeval::MinMaxPostingInfo;
using fef::TermFieldMatchData;
-AttributeIteratorBase::AttributeIteratorBase(TermFieldMatchData * matchData) :
- _matchData(matchData),
- _matchPosition(_matchData->populate_fixed())
+AttributeIteratorBase::AttributeIteratorBase(const attribute::ISearchContext &baseSearchCtx,
+ TermFieldMatchData *matchData)
+ : _baseSearchCtx(baseSearchCtx),
+ _matchData(matchData),
+ _matchPosition(_matchData->populate_fixed())
{ }
void
@@ -21,8 +26,9 @@ AttributeIteratorBase::visitMembers(vespalib::ObjectVisitor &visitor) const
visit(visitor, "tfmd.docId", _matchData->getDocId());
}
-FilterAttributeIterator::FilterAttributeIterator(fef::TermFieldMatchData * matchData)
- : AttributeIteratorBase(matchData)
+FilterAttributeIterator::FilterAttributeIterator(const attribute::ISearchContext &baseSearchCtx,
+ fef::TermFieldMatchData *matchData)
+ : AttributeIteratorBase(baseSearchCtx, matchData)
{
_matchPosition->setElementWeight(1);
}
@@ -41,15 +47,17 @@ FlagAttributeIterator::doUnpack(uint32_t docId)
_matchData->resetOnlyDocId(docId);
}
-AttributePostingListIterator:: AttributePostingListIterator(bool hasWeight, TermFieldMatchData *matchData)
- : AttributeIteratorBase(matchData),
+AttributePostingListIterator::AttributePostingListIterator(const attribute::ISearchContext &baseSearchCtx,
+ bool hasWeight,
+ TermFieldMatchData *matchData)
+ : AttributeIteratorBase(baseSearchCtx, matchData),
_hasWeight(hasWeight)
{
}
FilterAttributePostingListIterator::
-FilterAttributePostingListIterator(TermFieldMatchData *matchData)
- : AttributeIteratorBase(matchData)
+FilterAttributePostingListIterator(const attribute::ISearchContext &baseSearchCtx, TermFieldMatchData *matchData)
+ : AttributeIteratorBase(baseSearchCtx, matchData)
{
}
diff --git a/searchlib/src/vespa/searchlib/attribute/attributeiterators.h b/searchlib/src/vespa/searchlib/attribute/attributeiterators.h
index a524f2ce0fa..bb4acdbd732 100644
--- a/searchlib/src/vespa/searchlib/attribute/attributeiterators.h
+++ b/searchlib/src/vespa/searchlib/attribute/attributeiterators.h
@@ -9,6 +9,8 @@
namespace search {
+namespace attribute { class ISearchContext; }
+
namespace fef {
class TermFieldMatchData;
class TermFieldMatchDataPosition;
@@ -28,12 +30,14 @@ protected:
template <typename SC>
std::unique_ptr<BitVector> get_hits(const SC & sc, uint32_t begin_id) const;
void visitMembers(vespalib::ObjectVisitor &visitor) const override;
+ const attribute::ISearchContext &_baseSearchCtx;
fef::TermFieldMatchData * _matchData;
fef::TermFieldMatchDataPosition * _matchPosition;
public:
- AttributeIteratorBase(fef::TermFieldMatchData * matchData);
+ AttributeIteratorBase(const attribute::ISearchContext &baseSearchCtx, fef::TermFieldMatchData *matchData);
Trinary is_strict() const override { return Trinary::False; }
+ const attribute::ISearchContext *getAttributeSearchContext() const override { return &_baseSearchCtx; }
};
@@ -49,8 +53,9 @@ public:
class AttributeIterator : public AttributeIteratorBase
{
public:
- AttributeIterator(fef::TermFieldMatchData * matchData)
- : AttributeIteratorBase(matchData),
+ AttributeIterator(const attribute::ISearchContext &baseSearchCtx,
+ fef::TermFieldMatchData *matchData)
+ : AttributeIteratorBase(baseSearchCtx, matchData),
_weight(1)
{ }
protected:
@@ -62,7 +67,8 @@ protected:
class FilterAttributeIterator : public AttributeIteratorBase
{
public:
- FilterAttributeIterator(fef::TermFieldMatchData * matchData);
+ FilterAttributeIterator(const attribute::ISearchContext &baseSearchCtx,
+ fef::TermFieldMatchData *matchData);
protected:
void doUnpack(uint32_t docId) override;
};
@@ -78,13 +84,11 @@ private:
std::unique_ptr<BitVector> get_hits(uint32_t begin_id) override;
protected:
- const SC & _searchContext;
+ const SC &_concreteSearchCtx;
public:
- AttributeIteratorT(const SC &searchContext, fef::TermFieldMatchData *matchData);
- bool seekFast(uint32_t docId) const { return _searchContext.matches(docId); }
-
- const attribute::ISearchContext * getAttributeSearchContext() const override { return &_searchContext; }
+ AttributeIteratorT(const SC &concreteSearchCtx, fef::TermFieldMatchData *matchData);
+ bool seekFast(uint32_t docId) const { return _concreteSearchCtx.matches(docId); }
};
template <typename SC>
@@ -98,11 +102,11 @@ private:
std::unique_ptr<BitVector> get_hits(uint32_t begin_id) override;
protected:
- const SC & _searchContext;
+ const SC &_concreteSearchCtx;
public:
- FilterAttributeIteratorT(const SC &searchContext, fef::TermFieldMatchData *matchData);
- bool seekFast(uint32_t docId) const { return _searchContext.matches(docId); }
+ FilterAttributeIteratorT(const SC &concreteSearchCtx, fef::TermFieldMatchData *matchData);
+ bool seekFast(uint32_t docId) const { return _concreteSearchCtx.matches(docId); }
};
@@ -118,7 +122,7 @@ template <typename SC>
class AttributeIteratorStrict : public AttributeIteratorT<SC>
{
private:
- using AttributeIteratorT<SC>::_searchContext;
+ using AttributeIteratorT<SC>::_concreteSearchCtx;
using AttributeIteratorT<SC>::setDocId;
using AttributeIteratorT<SC>::setAtEnd;
using AttributeIteratorT<SC>::isAtEnd;
@@ -127,8 +131,8 @@ private:
void doSeek(uint32_t docId) override;
Trinary is_strict() const override { return Trinary::True; }
public:
- AttributeIteratorStrict(const SC &searchContext, fef::TermFieldMatchData * matchData)
- : AttributeIteratorT<SC>(searchContext, matchData)
+ AttributeIteratorStrict(const SC &concreteSearchCtx, fef::TermFieldMatchData * matchData)
+ : AttributeIteratorT<SC>(concreteSearchCtx, matchData)
{ }
};
@@ -137,7 +141,7 @@ template <typename SC>
class FilterAttributeIteratorStrict : public FilterAttributeIteratorT<SC>
{
private:
- using FilterAttributeIteratorT<SC>::_searchContext;
+ using FilterAttributeIteratorT<SC>::_concreteSearchCtx;
using FilterAttributeIteratorT<SC>::setDocId;
using FilterAttributeIteratorT<SC>::setAtEnd;
using FilterAttributeIteratorT<SC>::isAtEnd;
@@ -145,8 +149,8 @@ private:
void doSeek(uint32_t docId) override;
Trinary is_strict() const override { return Trinary::True; }
public:
- FilterAttributeIteratorStrict(const SC &searchContext, fef::TermFieldMatchData * matchData)
- : FilterAttributeIteratorT<SC>(searchContext, matchData)
+ FilterAttributeIteratorStrict(const SC &concreteSearchCtx, fef::TermFieldMatchData *matchData)
+ : FilterAttributeIteratorT<SC>(concreteSearchCtx, matchData)
{ }
};
@@ -163,7 +167,8 @@ public:
class AttributePostingListIterator : public AttributeIteratorBase
{
public:
- AttributePostingListIterator(bool hasWeight, fef::TermFieldMatchData *matchData);
+ AttributePostingListIterator(const attribute::ISearchContext &baseSearchCtx,
+ bool hasWeight, fef::TermFieldMatchData *matchData);
Trinary is_strict() const override { return Trinary::True; }
protected:
bool _hasWeight;
@@ -173,7 +178,7 @@ protected:
class FilterAttributePostingListIterator : public AttributeIteratorBase
{
public:
- FilterAttributePostingListIterator(fef::TermFieldMatchData *matchData);
+ FilterAttributePostingListIterator(const attribute::ISearchContext &baseSearchCtx, fef::TermFieldMatchData *matchData);
Trinary is_strict() const override { return Trinary::True; }
};
@@ -216,7 +221,9 @@ private:
public:
template <typename... Args>
- AttributePostingListIteratorT(bool hasWeight, fef::TermFieldMatchData *matchData, Args &&... args);
+ AttributePostingListIteratorT(const attribute::ISearchContext &baseSearchCtx,
+ bool hasWeight, fef::TermFieldMatchData *matchData,
+ Args &&... args);
};
template <typename PL>
@@ -246,7 +253,7 @@ private:
public:
template <typename... Args>
- FilterAttributePostingListIteratorT(fef::TermFieldMatchData *matchData, Args &&... args);
+ FilterAttributePostingListIteratorT(const attribute::ISearchContext &baseSearchCtx, fef::TermFieldMatchData *matchData, Args &&... args);
};
@@ -316,8 +323,8 @@ FilterAttributePostingListIteratorT<DocIdMinMaxIterator<AttributePosting> >::set
class FlagAttributeIterator : public AttributeIteratorBase
{
public:
- FlagAttributeIterator(fef::TermFieldMatchData * matchData)
- : AttributeIteratorBase(matchData)
+ FlagAttributeIterator(const attribute::ISearchContext &baseSearchCtx, fef::TermFieldMatchData *matchData)
+ : AttributeIteratorBase(baseSearchCtx, matchData)
{ }
protected:
void doUnpack(uint32_t docId) override;
@@ -331,21 +338,21 @@ private:
void doSeek(uint32_t docId) override;
protected:
- const SC & _sc;
+ const SC &_concreteSearchCtx;
void or_hits_into(BitVector &result, uint32_t begin_id) override;
void and_hits_into(BitVector &result, uint32_t begin_id) override;
std::unique_ptr<BitVector> get_hits(uint32_t begin_id) override;
public:
- FlagAttributeIteratorT(const SC &sc, fef::TermFieldMatchData * matchData)
- : FlagAttributeIterator(matchData),
- _sc(sc)
+ FlagAttributeIteratorT(const SC &concreteSearchCtx, fef::TermFieldMatchData *matchData)
+ : FlagAttributeIterator(concreteSearchCtx, matchData),
+ _concreteSearchCtx(concreteSearchCtx)
{ }
void initRange(uint32_t begin, uint32_t end) override {
FlagAttributeIterator::initRange(begin, end);
- if ( _sc._zeroHits ) {
+ if ( _concreteSearchCtx._zeroHits ) {
setAtEnd();
}
}
@@ -356,7 +363,7 @@ template <typename SC>
class FlagAttributeIteratorStrict : public FlagAttributeIteratorT<SC>
{
private:
- using FlagAttributeIteratorT<SC>::_sc;
+ using FlagAttributeIteratorT<SC>::_concreteSearchCtx;
using FlagAttributeIteratorT<SC>::setDocId;
using FlagAttributeIteratorT<SC>::setAtEnd;
using FlagAttributeIteratorT<SC>::isAtEnd;
@@ -366,9 +373,9 @@ private:
Trinary is_strict() const override { return Trinary::True; }
public:
- FlagAttributeIteratorStrict(const SC &sc, fef::TermFieldMatchData *matchData)
- : FlagAttributeIteratorT<SC>(sc, matchData)
+ FlagAttributeIteratorStrict(const SC &concreteSearchCtx, fef::TermFieldMatchData *matchData)
+ : FlagAttributeIteratorT<SC>(concreteSearchCtx, matchData)
{ }
};
-} // namespace search
+}
diff --git a/searchlib/src/vespa/searchlib/attribute/attributeiterators.hpp b/searchlib/src/vespa/searchlib/attribute/attributeiterators.hpp
index 9b1e837beac..e52a393fc11 100644
--- a/searchlib/src/vespa/searchlib/attribute/attributeiterators.hpp
+++ b/searchlib/src/vespa/searchlib/attribute/attributeiterators.hpp
@@ -45,8 +45,10 @@ AttributeIteratorBase::get_hits(const SC & sc, uint32_t begin_id) const {
template <typename PL>
template <typename... Args>
AttributePostingListIteratorT<PL>::
-AttributePostingListIteratorT(bool hasWeight, fef::TermFieldMatchData *matchData, Args &&... args)
- : AttributePostingListIterator(hasWeight, matchData),
+AttributePostingListIteratorT(const attribute::ISearchContext &baseSearchCtx,
+ bool hasWeight, fef::TermFieldMatchData *matchData,
+ Args &&... args)
+ : AttributePostingListIterator(baseSearchCtx, hasWeight, matchData),
_iterator(std::forward<Args>(args)...),
_postingInfo(1, 1),
_postingInfoValid(false)
@@ -68,8 +70,8 @@ void AttributePostingListIteratorT<PL>::initRange(uint32_t begin, uint32_t end)
template <typename PL>
template<typename... Args>
FilterAttributePostingListIteratorT<PL>::
-FilterAttributePostingListIteratorT(fef::TermFieldMatchData *matchData, Args &&... args)
- : FilterAttributePostingListIterator(matchData),
+FilterAttributePostingListIteratorT(const attribute::ISearchContext &baseSearchCtx, fef::TermFieldMatchData *matchData, Args &&... args)
+ : FilterAttributePostingListIterator(baseSearchCtx, matchData),
_iterator(std::forward<Args>(args)...),
_postingInfo(1, 1),
_postingInfoValid(false)
@@ -199,8 +201,8 @@ void
AttributeIteratorT<SC>::visitMembers(vespalib::ObjectVisitor &visitor) const
{
AttributeIterator::visitMembers(visitor);
- visit(visitor, "searchcontext.attribute", _searchContext.attribute().getName());
- visit(visitor, "searchcontext.queryterm", _searchContext.queryTerm());
+ visit(visitor, "searchcontext.attribute", _concreteSearchCtx.attribute().getName());
+ visit(visitor, "searchcontext.queryterm", _concreteSearchCtx.queryTerm());
}
template <typename SC>
@@ -208,21 +210,21 @@ void
FilterAttributeIteratorT<SC>::visitMembers(vespalib::ObjectVisitor &visitor) const
{
FilterAttributeIterator::visitMembers(visitor);
- visit(visitor, "searchcontext.attribute", _searchContext.attribute().getName());
- visit(visitor, "searchcontext.queryterm", _searchContext.queryTerm());
+ visit(visitor, "searchcontext.attribute", _concreteSearchCtx.attribute().getName());
+ visit(visitor, "searchcontext.queryterm", _concreteSearchCtx.queryTerm());
}
template <typename SC>
-AttributeIteratorT<SC>::AttributeIteratorT(const SC &searchContext, fef::TermFieldMatchData *matchData)
- : AttributeIterator(matchData),
- _searchContext(searchContext)
+AttributeIteratorT<SC>::AttributeIteratorT(const SC &concreteSearchCtx, fef::TermFieldMatchData *matchData)
+ : AttributeIterator(concreteSearchCtx, matchData),
+ _concreteSearchCtx(concreteSearchCtx)
{ }
template <typename SC>
-FilterAttributeIteratorT<SC>::FilterAttributeIteratorT(const SC &searchContext, fef::TermFieldMatchData *matchData)
- : FilterAttributeIterator(matchData),
- _searchContext(searchContext)
+FilterAttributeIteratorT<SC>::FilterAttributeIteratorT(const SC &concreteSearchCtx, fef::TermFieldMatchData *matchData)
+ : FilterAttributeIterator(concreteSearchCtx, matchData),
+ _concreteSearchCtx(concreteSearchCtx)
{ }
@@ -230,7 +232,7 @@ template <typename SC>
void
FlagAttributeIteratorStrict<SC>::doSeek(uint32_t docId)
{
- const SC & sc(_sc);
+ const SC & sc(_concreteSearchCtx);
const Attribute &attr = static_cast<const Attribute &>(sc.attribute());
for (int i = sc._low; (i <= sc._high); ++i) {
const BitVector * bv = attr.getBitVector(i);
@@ -259,7 +261,7 @@ template <typename SC>
void
FlagAttributeIteratorT<SC>::doSeek(uint32_t docId)
{
- const SC & sc(_sc);
+ const SC & sc(_concreteSearchCtx);
const Attribute &attr = static_cast<const Attribute &>(sc.attribute());
for (int i = sc._low; (i <= sc._high); ++i) {
const BitVector * bv = attr.getBitVector(i);
@@ -274,7 +276,7 @@ template <typename SC>
void
FlagAttributeIteratorT<SC>::or_hits_into(BitVector &result, uint32_t begin_id) {
(void) begin_id;
- const SC & sc(_sc);
+ const SC & sc(_concreteSearchCtx);
const Attribute &attr = static_cast<const Attribute &>(sc.attribute());
for (int i = sc._low; (i <= sc._high); ++i) {
const BitVector * bv = attr.getBitVector(i);
@@ -287,7 +289,7 @@ FlagAttributeIteratorT<SC>::or_hits_into(BitVector &result, uint32_t begin_id) {
template <typename SC>
void
FlagAttributeIteratorT<SC>::and_hits_into(BitVector &result, uint32_t begin_id) {
- const SC & sc(_sc);
+ const SC & sc(_concreteSearchCtx);
const Attribute &attr = static_cast<const Attribute &>(sc.attribute());
if (sc._low == sc._high) {
const BitVector * bv = attr.getBitVector(sc._low);
@@ -306,7 +308,7 @@ FlagAttributeIteratorT<SC>::and_hits_into(BitVector &result, uint32_t begin_id)
template <typename SC>
std::unique_ptr<BitVector>
FlagAttributeIteratorT<SC>::get_hits(uint32_t begin_id) {
- const SC & sc(_sc);
+ const SC & sc(_concreteSearchCtx);
const Attribute &attr = static_cast<const Attribute &>(sc.attribute());
int i = sc._low;
BitVector::UP result;
@@ -338,7 +340,7 @@ AttributeIteratorT<SC>::doSeek(uint32_t docId)
{
if (isAtEnd(docId)) {
setAtEnd();
- } else if (_searchContext.matches(docId, _weight)) {
+ } else if (_concreteSearchCtx.matches(docId, _weight)) {
setDocId(docId);
}
}
@@ -349,7 +351,7 @@ FilterAttributeIteratorT<SC>::doSeek(uint32_t docId)
{
if (isAtEnd(docId)) {
setAtEnd();
- } else if (_searchContext.matches(docId)) {
+ } else if (_concreteSearchCtx.matches(docId)) {
setDocId(docId);
}
}
@@ -359,7 +361,7 @@ void
AttributeIteratorStrict<SC>::doSeek(uint32_t docId)
{
for (uint32_t nextId = docId; !isAtEnd(nextId); ++nextId) {
- if (_searchContext.matches(nextId, _weight)) {
+ if (_concreteSearchCtx.matches(nextId, _weight)) {
setDocId(nextId);
return;
}
@@ -372,7 +374,7 @@ void
FilterAttributeIteratorStrict<SC>::doSeek(uint32_t docId)
{
for (uint32_t nextId = docId; !isAtEnd(nextId); ++nextId) {
- if (_searchContext.matches(nextId)) {
+ if (_concreteSearchCtx.matches(nextId)) {
setDocId(nextId);
return;
}
@@ -383,37 +385,37 @@ FilterAttributeIteratorStrict<SC>::doSeek(uint32_t docId)
template <typename SC>
void
AttributeIteratorT<SC>::or_hits_into(BitVector & result, uint32_t begin_id) {
- AttributeIteratorBase::or_hits_into(_searchContext, result, begin_id);
+ AttributeIteratorBase::or_hits_into(_concreteSearchCtx, result, begin_id);
}
template <typename SC>
void
FilterAttributeIteratorT<SC>::or_hits_into(BitVector & result, uint32_t begin_id) {
- AttributeIteratorBase::or_hits_into(_searchContext, result, begin_id);
+ AttributeIteratorBase::or_hits_into(_concreteSearchCtx, result, begin_id);
}
template <typename SC>
BitVector::UP
AttributeIteratorT<SC>::get_hits(uint32_t begin_id) {
- return AttributeIteratorBase::get_hits(_searchContext, begin_id);
+ return AttributeIteratorBase::get_hits(_concreteSearchCtx, begin_id);
}
template <typename SC>
BitVector::UP
FilterAttributeIteratorT<SC>::get_hits(uint32_t begin_id) {
- return AttributeIteratorBase::get_hits(_searchContext, begin_id);
+ return AttributeIteratorBase::get_hits(_concreteSearchCtx, begin_id);
}
template <typename SC>
void
AttributeIteratorT<SC>::and_hits_into(BitVector & result, uint32_t begin_id) {
- AttributeIteratorBase::and_hits_into(_searchContext, result, begin_id);
+ AttributeIteratorBase::and_hits_into(_concreteSearchCtx, result, begin_id);
}
template <typename SC>
void
FilterAttributeIteratorT<SC>::and_hits_into(BitVector & result, uint32_t begin_id) {
- AttributeIteratorBase::and_hits_into(_searchContext, result, begin_id);
+ AttributeIteratorBase::and_hits_into(_concreteSearchCtx, result, begin_id);
}
} // namespace search
diff --git a/searchlib/src/vespa/searchlib/attribute/attributevector.cpp b/searchlib/src/vespa/searchlib/attribute/attributevector.cpp
index 1ccc9923c99..fbad06821cd 100644
--- a/searchlib/src/vespa/searchlib/attribute/attributevector.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/attributevector.cpp
@@ -1,10 +1,10 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include "attributevector.h"
#include "attribute_read_guard.h"
#include "attributefilesavetarget.h"
#include "attributeiterators.hpp"
#include "attributesaver.h"
-#include "attributevector.h"
#include "attributevector.hpp"
#include "floatbase.h"
#include "interlock.h"
@@ -216,7 +216,7 @@ AttributeVector::commit(uint64_t firstSyncToken, uint64_t lastSyncToken)
if (firstSyncToken < getStatus().getLastSyncToken()) {
LOG(error, "Expected first token to be >= %" PRIu64 ", got %" PRIu64 ".",
getStatus().getLastSyncToken(), firstSyncToken);
- abort();
+ LOG_ABORT("should not be reached");
}
commit();
_status.setLastSyncToken(lastSyncToken);
@@ -446,7 +446,10 @@ AttributeVector::createAttributeHeader() const {
getVersion());
}
-void AttributeVector::onSave(IAttributeSaveTarget &) { abort(); }
+void AttributeVector::onSave(IAttributeSaveTarget &)
+{
+ LOG_ABORT("should not be reached");
+}
bool
AttributeVector::hasLoadData() const {
@@ -505,6 +508,12 @@ int32_t AttributeVector::getWeight(DocId, uint32_t) const { return 1; }
bool AttributeVector::findEnum(const char *, EnumHandle &) const { return false; }
+std::vector<search::attribute::IAttributeVector::EnumHandle>
+AttributeVector::findFoldedEnums(const char *) const {
+ std::vector<EnumHandle> empty;
+ return empty;
+}
+
const char * AttributeVector::getStringFromEnum(EnumHandle) const { return nullptr; }
AttributeVector::SearchContext::SearchContext(const AttributeVector &attr) :
diff --git a/searchlib/src/vespa/searchlib/attribute/attributevector.h b/searchlib/src/vespa/searchlib/attribute/attributevector.h
index 87ef9a41432..cb83b86001b 100644
--- a/searchlib/src/vespa/searchlib/attribute/attributevector.h
+++ b/searchlib/src/vespa/searchlib/attribute/attributevector.h
@@ -500,6 +500,8 @@ public:
// Implements IAttributeVector
bool findEnum(const char *value, EnumHandle &e) const override;
+ std::vector<EnumHandle> findFoldedEnums(const char *) const override;
+
const char * getStringFromEnum(EnumHandle e) const override;
///// Modify API
@@ -536,16 +538,15 @@ public:
typedef std::unique_ptr<SearchContext> UP;
~SearchContext();
- // Implements attribute::ISearchContext
- virtual unsigned int approximateHits() const override;
- virtual queryeval::SearchIterator::UP createIterator(fef::TermFieldMatchData *matchData, bool strict) override;
- virtual void fetchPostings(bool strict) override;
- virtual bool valid() const override { return false; }
- virtual Int64Range getAsIntegerTerm() const override { return Int64Range(); }
- virtual const QueryTermBase &queryTerm() const override {
- return *static_cast<const QueryTermBase *>(NULL);
+ unsigned int approximateHits() const override;
+ queryeval::SearchIterator::UP createIterator(fef::TermFieldMatchData *matchData, bool strict) override;
+ void fetchPostings(bool strict) override;
+ bool valid() const override { return false; }
+ Int64Range getAsIntegerTerm() const override { return Int64Range(); }
+ const QueryTermBase * queryTerm() const override {
+ return static_cast<const QueryTermBase *>(nullptr);
}
- virtual const vespalib::string &attributeName() const override {
+ const vespalib::string &attributeName() const override {
return _attr.getName();
}
diff --git a/searchlib/src/vespa/searchlib/attribute/attrvector.cpp b/searchlib/src/vespa/searchlib/attribute/attrvector.cpp
index f1be4db4675..41945dff1fe 100644
--- a/searchlib/src/vespa/searchlib/attribute/attrvector.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/attrvector.cpp
@@ -4,6 +4,9 @@
#include "attrvector.hpp"
#include "iattributesavetarget.h"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.attr_vector");
+
namespace search {
StringDirectAttribute::
@@ -48,6 +51,19 @@ bool StringDirectAttribute::findEnum(const char * key, EnumHandle & e) const
return false;
}
+
+// XXX this is not really correct
+std::vector<StringDirectAttribute::EnumHandle>
+StringDirectAttribute::findFoldedEnums(const char *key) const
+{
+ std::vector<EnumHandle> result;
+ EnumHandle handle;
+ if (findEnum(key, handle)) {
+ result.push_back(handle);
+ }
+ return result;
+}
+
void StringDirectAttribute::onSave(IAttributeSaveTarget & saveTarget)
{
assert(!saveTarget.getEnumerated());
@@ -172,7 +188,7 @@ bool StringDirectAttribute::onLoad()
void StringDirectAttribute::onCommit()
{
- abort();
+ LOG_ABORT("should not be reached");
}
bool StringDirectAttribute::addDoc(DocId & doc)
diff --git a/searchlib/src/vespa/searchlib/attribute/attrvector.h b/searchlib/src/vespa/searchlib/attribute/attrvector.h
index a6f9a0ebcee..2ba9ed083f0 100644
--- a/searchlib/src/vespa/searchlib/attribute/attrvector.h
+++ b/searchlib/src/vespa/searchlib/attribute/attrvector.h
@@ -152,6 +152,7 @@ protected:
StringDirectAttribute(const vespalib::string & baseFileName, const Config & c);
~StringDirectAttribute();
bool findEnum(const char * value, EnumHandle & e) const override;
+ std::vector<EnumHandle> findFoldedEnums(const char *) const override;
void getEnumValue(const EnumHandle * v, uint32_t *e, uint32_t sz) const override {
for (size_t i(0); i < sz; i++) {
e[i] = v[i];
diff --git a/searchlib/src/vespa/searchlib/attribute/attrvector.hpp b/searchlib/src/vespa/searchlib/attribute/attrvector.hpp
index 8f67d237a60..565801b1b0c 100644
--- a/searchlib/src/vespa/searchlib/attribute/attrvector.hpp
+++ b/searchlib/src/vespa/searchlib/attribute/attrvector.hpp
@@ -2,6 +2,7 @@
#pragma once
#include "attrvector.h"
+#include <vespa/vespalib/util/hdr_abort.h>
#include <vespa/fastlib/io/bufferedfile.h>
#include <vespa/searchlib/util/filekit.h>
@@ -130,7 +131,7 @@ template <typename B>
void NumericDirectAttribute<B>::onCommit()
{
B::_changes.clear();
- abort();
+ HDR_ABORT("should not be reached");
}
template <typename B>
diff --git a/searchlib/src/vespa/searchlib/attribute/createarraystd.cpp b/searchlib/src/vespa/searchlib/attribute/createarraystd.cpp
index 6b9af6c5ec8..9e2d4d764e7 100644
--- a/searchlib/src/vespa/searchlib/attribute/createarraystd.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/createarraystd.cpp
@@ -7,6 +7,9 @@
#include "multinumericattribute.hpp"
#include "multistringattribute.hpp"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.create_array_std");
+
namespace search {
using attribute::BasicType;
diff --git a/searchlib/src/vespa/searchlib/attribute/createsetstd.cpp b/searchlib/src/vespa/searchlib/attribute/createsetstd.cpp
index 8ea6e49ade6..8ef75de2b44 100644
--- a/searchlib/src/vespa/searchlib/attribute/createsetstd.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/createsetstd.cpp
@@ -7,6 +7,9 @@
#include "multinumericattribute.hpp"
#include "multistringattribute.hpp"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.create_set_std");
+
namespace search {
using attribute::BasicType;
diff --git a/searchlib/src/vespa/searchlib/attribute/createsinglefastsearch.cpp b/searchlib/src/vespa/searchlib/attribute/createsinglefastsearch.cpp
index b02aed39444..ec200124286 100644
--- a/searchlib/src/vespa/searchlib/attribute/createsinglefastsearch.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/createsinglefastsearch.cpp
@@ -12,6 +12,9 @@
#include "enumattribute.hpp"
#include "singleenumattribute.hpp"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.create_single_fast_search");
+
#define INTPOSTING(T) SingleValueNumericPostingAttribute< ENUM_ATTRIBUTE(IntegerAttributeTemplate<T>) >
#define FLOATPOSTING(T) SingleValueNumericPostingAttribute< ENUM_ATTRIBUTE(FloatingPointAttributeTemplate<T>) >
diff --git a/searchlib/src/vespa/searchlib/attribute/createsinglestd.cpp b/searchlib/src/vespa/searchlib/attribute/createsinglestd.cpp
index feaf27da5e4..6fefc7a1852 100644
--- a/searchlib/src/vespa/searchlib/attribute/createsinglestd.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/createsinglestd.cpp
@@ -10,6 +10,9 @@
#include <vespa/searchlib/tensor/generic_tensor_attribute.h>
#include <vespa/searchlib/tensor/dense_tensor_attribute.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.create_single_std");
+
namespace search {
using attribute::BasicType;
diff --git a/searchlib/src/vespa/searchlib/attribute/diversity.cpp b/searchlib/src/vespa/searchlib/attribute/diversity.cpp
index 14e05afa8f0..4c6fe054b12 100644
--- a/searchlib/src/vespa/searchlib/attribute/diversity.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/diversity.cpp
@@ -2,10 +2,6 @@
#include "diversity.hpp"
-namespace search {
-namespace attribute {
-namespace diversity {
+namespace search::attribute::diversity {
}
-}
-}
diff --git a/searchlib/src/vespa/searchlib/attribute/diversity.h b/searchlib/src/vespa/searchlib/attribute/diversity.h
index dff658d99d7..fe2874a65a1 100644
--- a/searchlib/src/vespa/searchlib/attribute/diversity.h
+++ b/searchlib/src/vespa/searchlib/attribute/diversity.h
@@ -14,9 +14,7 @@
* diversified results based on a secondary attribute.
**/
-namespace search {
-namespace attribute {
-namespace diversity {
+namespace search::attribute::diversity {
template <typename ITR>
class ForwardRange
@@ -227,6 +225,4 @@ void diversify(bool forward, const DictItr &lower, const DictItr &upper, const P
}
}
-} // namespace search::attribute::diversity
-} // namespace search::attribute
-} // namespace search
+}
diff --git a/searchlib/src/vespa/searchlib/attribute/diversity.hpp b/searchlib/src/vespa/searchlib/attribute/diversity.hpp
index 9261bb482f9..698f482dec1 100644
--- a/searchlib/src/vespa/searchlib/attribute/diversity.hpp
+++ b/searchlib/src/vespa/searchlib/attribute/diversity.hpp
@@ -4,9 +4,7 @@
#include "diversity.h"
-namespace search {
-namespace attribute {
-namespace diversity {
+namespace search::attribute::diversity {
template <typename ITR>
ForwardRange<ITR>::ForwardRange(const ForwardRange &) = default;
@@ -18,7 +16,7 @@ ForwardRange<ITR>::ForwardRange(const ITR &lower, const ITR &upper)
{}
template <typename ITR>
-ForwardRange<ITR>::~ForwardRange() { }
+ForwardRange<ITR>::~ForwardRange() = default;
template <typename ITR>
ReverseRange<ITR>::ReverseRange(const ReverseRange &) = default;
@@ -31,8 +29,6 @@ ReverseRange<ITR>::ReverseRange(const ITR &lower, const ITR &upper)
template <typename ITR>
-ReverseRange<ITR>::~ReverseRange() { }
+ReverseRange<ITR>::~ReverseRange() = default;
}
-}
-}
diff --git a/searchlib/src/vespa/searchlib/attribute/enumattribute.cpp b/searchlib/src/vespa/searchlib/attribute/enumattribute.cpp
index 94d36a967ea..a894bc63d49 100644
--- a/searchlib/src/vespa/searchlib/attribute/enumattribute.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/enumattribute.cpp
@@ -3,6 +3,9 @@
#include "enumattribute.h"
#include "enumattribute.hpp"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.enum_attribute");
+
namespace search {
} // namespace search
diff --git a/searchlib/src/vespa/searchlib/attribute/enumattribute.hpp b/searchlib/src/vespa/searchlib/attribute/enumattribute.hpp
index 9da670b2bd5..1a2736541fb 100644
--- a/searchlib/src/vespa/searchlib/attribute/enumattribute.hpp
+++ b/searchlib/src/vespa/searchlib/attribute/enumattribute.hpp
@@ -2,6 +2,7 @@
#pragma once
+#include <vespa/vespalib/util/hdr_abort.h>
#include <vespa/searchlib/attribute/enumattribute.h>
#include <vespa/searchlib/attribute/enumstore.hpp>
@@ -108,8 +109,7 @@ EnumAttribute<B>::insertNewUniqueValues(EnumStoreBase::IndexVector & newIndexes)
// fallback to resize strategy
this->_enumStore.fallbackResize(extraBytesNeeded);
if (extraBytesNeeded > this->_enumStore.getRemaining()) {
- fprintf(stderr, "Cannot fallbackResize enumStore\n");
- abort();
+ HDR_ABORT("Cannot fallbackResize enumStore");
}
break; // fallback resize performed instead of compaction.
}
diff --git a/searchlib/src/vespa/searchlib/attribute/enumcomparator.cpp b/searchlib/src/vespa/searchlib/attribute/enumcomparator.cpp
index 314fb781185..bf323d56245 100644
--- a/searchlib/src/vespa/searchlib/attribute/enumcomparator.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/enumcomparator.cpp
@@ -4,6 +4,9 @@
#include "enumstore.hpp"
#include <vespa/searchlib/util/foldedstringcompare.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.enum_comparator");
+
namespace search {
namespace {
diff --git a/searchlib/src/vespa/searchlib/attribute/enumstore.cpp b/searchlib/src/vespa/searchlib/attribute/enumstore.cpp
index 8100fd3f415..3a8f1ad8de5 100644
--- a/searchlib/src/vespa/searchlib/attribute/enumstore.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/enumstore.cpp
@@ -4,6 +4,9 @@
#include "enumstore.hpp"
#include <iomanip>
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.enum_store");
+
namespace search {
template <>
@@ -129,8 +132,7 @@ EnumStoreT<StringEntryType>::deserialize(const void *src,
datastore::BufferState & buffer = _store.getBufferState(activeBufferId);
uint32_t entrySize(alignEntrySize(EntryBase::size() + sz));
if (buffer.remaining() < entrySize) {
- fprintf(stderr, "Out of enumstore bufferspace\n");
- abort(); // not enough space
+ LOG_ABORT("Out of enumstore bufferspace");
}
uint64_t offset = buffer.size();
char *dst(_store.getBufferEntry<char>(activeBufferId, offset));
diff --git a/searchlib/src/vespa/searchlib/attribute/enumstore.h b/searchlib/src/vespa/searchlib/attribute/enumstore.h
index c1f5d278827..3206ea62d73 100644
--- a/searchlib/src/vespa/searchlib/attribute/enumstore.h
+++ b/searchlib/src/vespa/searchlib/attribute/enumstore.h
@@ -166,6 +166,7 @@ public:
ssize_t deserialize(const void *src, size_t available, Index &idx) override;
bool foldedChange(const Index &idx1, const Index &idx2) override;
virtual bool findEnum(Type value, EnumStoreBase::EnumHandle &e) const;
+ virtual std::vector<EnumStoreBase::EnumHandle> findFoldedEnums(Type value) const;
void addEnum(Type value, Index &newIdx);
virtual bool findIndex(Type value, Index &idx) const;
void freeUnusedEnums(bool movePostingidx) override;
diff --git a/searchlib/src/vespa/searchlib/attribute/enumstore.hpp b/searchlib/src/vespa/searchlib/attribute/enumstore.hpp
index 21531e07861..794138c7c89 100644
--- a/searchlib/src/vespa/searchlib/attribute/enumstore.hpp
+++ b/searchlib/src/vespa/searchlib/attribute/enumstore.hpp
@@ -5,6 +5,7 @@
#include "enumstore.h"
#include "enumcomparator.h"
+#include <vespa/vespalib/util/hdr_abort.h>
#include <vespa/searchlib/btree/btreenode.hpp>
#include <vespa/searchlib/btree/btreenodestore.hpp>
#include <vespa/searchlib/btree/btreenodeallocator.hpp>
@@ -151,7 +152,7 @@ EnumStoreT<EntryType>::deserialize(const void *src,
datastore::BufferState & buffer = _store.getBufferState(activeBufferId);
uint32_t entrySize(alignEntrySize(EntryBase::size() + sz));
if (buffer.remaining() < entrySize) {
- abort(); // not enough space
+ HDR_ABORT("not enough space");
}
uint64_t offset = buffer.size();
char *dst(_store.getBufferEntry<char>(activeBufferId, offset));
@@ -199,6 +200,15 @@ EnumStoreT<EntryType>::findEnum(Type value,
}
template <typename EntryType>
+std::vector<EnumStoreBase::EnumHandle>
+EnumStoreT<EntryType>::findFoldedEnums(Type value) const
+{
+ FoldedComparatorType cmp(*this, value);
+ return _enumDict->findMatchingEnums(cmp);
+}
+
+
+template <typename EntryType>
bool
EnumStoreT<EntryType>::findIndex(Type value, Index &idx) const
{
@@ -256,7 +266,7 @@ EnumStoreT<EntryType>::addEnum(Type value,
buffer._deadElems, entrySize);
#endif
if (buffer.remaining() < entrySize) {
- abort(); // not enough space
+ HDR_ABORT("not enough space");
}
// check if already present
diff --git a/searchlib/src/vespa/searchlib/attribute/enumstorebase.cpp b/searchlib/src/vespa/searchlib/attribute/enumstorebase.cpp
index 568b1e7ecb5..61d862b6c4f 100644
--- a/searchlib/src/vespa/searchlib/attribute/enumstorebase.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/enumstorebase.cpp
@@ -9,6 +9,9 @@
#include <vespa/searchlib/util/bufferwriter.h>
#include <vespa/searchlib/common/rcuvector.hpp>
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.enumstorebase");
+
namespace search {
using btree::BTreeNode;
@@ -504,6 +507,20 @@ EnumStoreDict<Dictionary>::findFrozenIndex(const EnumStoreComparator &cmp,
template <typename Dictionary>
+std::vector<EnumStoreBase::EnumHandle>
+EnumStoreDict<Dictionary>::findMatchingEnums(const EnumStoreComparator &cmp) const
+{
+ std::vector<EnumStoreBase::EnumHandle> result;
+ typename Dictionary::ConstIterator itr =
+ _dict.getFrozenView().find(Index(), cmp);
+ while (itr.valid() && !cmp(Index(), itr.getKey())) {
+ result.push_back(itr.getKey().ref());
+ ++itr;
+ }
+ return result;
+}
+
+template <typename Dictionary>
void
EnumStoreDict<Dictionary>::onReset()
{
@@ -572,7 +589,7 @@ template <>
EnumPostingTree &
EnumStoreDict<EnumTree>::getPostingDictionary()
{
- abort();
+ LOG_ABORT("should not be reached");
}
@@ -588,7 +605,7 @@ template <>
const EnumPostingTree &
EnumStoreDict<EnumTree>::getPostingDictionary() const
{
- abort();
+ LOG_ABORT("should not be reached");
}
diff --git a/searchlib/src/vespa/searchlib/attribute/enumstorebase.h b/searchlib/src/vespa/searchlib/attribute/enumstorebase.h
index f74345a8806..9bea2a568e1 100644
--- a/searchlib/src/vespa/searchlib/attribute/enumstorebase.h
+++ b/searchlib/src/vespa/searchlib/attribute/enumstorebase.h
@@ -78,6 +78,9 @@ public:
const EnumStoreComparator *fcmp) = 0;
virtual bool findIndex(const EnumStoreComparator &cmp, Index &idx) const = 0;
virtual bool findFrozenIndex(const EnumStoreComparator &cmp, Index &idx) const = 0;
+ virtual std::vector<attribute::IAttributeVector::EnumHandle>
+ findMatchingEnums(const EnumStoreComparator &cmp) const = 0;
+
virtual void onReset() = 0;
virtual void onTransferHoldLists(generation_t generation) = 0;
virtual void onTrimHoldLists(generation_t firstUsed) = 0;
@@ -131,6 +134,9 @@ public:
bool findIndex(const EnumStoreComparator &cmp, Index &idx) const override;
bool findFrozenIndex(const EnumStoreComparator &cmp, Index &idx) const override;
+ std::vector<attribute::IAttributeVector::EnumHandle>
+ findMatchingEnums(const EnumStoreComparator &cmp) const override;
+
void onReset() override;
void onTransferHoldLists(generation_t generation) override;
void onTrimHoldLists(generation_t firstUsed) override;
diff --git a/searchlib/src/vespa/searchlib/attribute/extendableattributes.cpp b/searchlib/src/vespa/searchlib/attribute/extendableattributes.cpp
index ee984688594..feac5caabd5 100644
--- a/searchlib/src/vespa/searchlib/attribute/extendableattributes.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/extendableattributes.cpp
@@ -3,6 +3,9 @@
#include "extendableattributes.h"
#include "attrvector.hpp"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.extendable_attributes");
+
namespace search {
//******************** CollectionType::SINGLE ********************//
diff --git a/searchlib/src/vespa/searchlib/attribute/fixedsourceselector.cpp b/searchlib/src/vespa/searchlib/attribute/fixedsourceselector.cpp
index b359b74f46c..ae2f0234e60 100644
--- a/searchlib/src/vespa/searchlib/attribute/fixedsourceselector.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/fixedsourceselector.cpp
@@ -3,6 +3,9 @@
#include "fixedsourceselector.h"
#include "singlenumericattribute.hpp"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.fixed_source_selector");
+
namespace search {
namespace {
diff --git a/searchlib/src/vespa/searchlib/attribute/flagattribute.cpp b/searchlib/src/vespa/searchlib/attribute/flagattribute.cpp
index 1df7613013b..0e5027ea46e 100644
--- a/searchlib/src/vespa/searchlib/attribute/flagattribute.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/flagattribute.cpp
@@ -9,7 +9,7 @@
#include <vespa/searchlib/common/bitvectoriterator.h>
#include <vespa/log/log.h>
-LOG_SETUP(".searchlib.attribute.flagattribute");
+LOG_SETUP(".searchlib.attribute.flag_attribute");
namespace search {
diff --git a/searchlib/src/vespa/searchlib/attribute/floatbase.h b/searchlib/src/vespa/searchlib/attribute/floatbase.h
index df156fd9fc5..955b2b252af 100644
--- a/searchlib/src/vespa/searchlib/attribute/floatbase.h
+++ b/searchlib/src/vespa/searchlib/attribute/floatbase.h
@@ -81,6 +81,7 @@ protected:
Change _defaultValue;
private:
bool findEnum(const char *value, EnumHandle &e) const override;
+ std::vector<EnumHandle> findFoldedEnums(const char *value) const override;
bool isUndefined(DocId doc) const override;
virtual T get(DocId doc) const = 0;
virtual T getFromEnum(EnumHandle e) const = 0;
diff --git a/searchlib/src/vespa/searchlib/attribute/floatbase.hpp b/searchlib/src/vespa/searchlib/attribute/floatbase.hpp
index 669c7974e6f..a454fd3e235 100644
--- a/searchlib/src/vespa/searchlib/attribute/floatbase.hpp
+++ b/searchlib/src/vespa/searchlib/attribute/floatbase.hpp
@@ -48,6 +48,18 @@ FloatingPointAttributeTemplate<T>::findEnum(const char *value, EnumHandle &e) co
}
template<typename T>
+std::vector<EnumStoreBase::EnumHandle>
+FloatingPointAttributeTemplate<T>::findFoldedEnums(const char *value) const
+{
+ std::vector<EnumHandle> result;
+ EnumHandle h;
+ if (findEnum(value, h)) {
+ result.push_back(h);
+ }
+ return result;
+}
+
+template<typename T>
bool
FloatingPointAttributeTemplate<T>::isUndefined(DocId doc) const {
return attribute::isUndefined(get(doc));
diff --git a/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector_read_guard.cpp b/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector_read_guard.cpp
index e6dacde3600..428b14671cd 100644
--- a/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector_read_guard.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector_read_guard.cpp
@@ -99,6 +99,11 @@ bool ImportedAttributeVectorReadGuard::findEnum(const char *value, EnumHandle &e
return _target_attribute.findEnum(value, e);
}
+std::vector<ImportedAttributeVectorReadGuard::EnumHandle>
+ImportedAttributeVectorReadGuard::findFoldedEnums(const char *value) const {
+ return _target_attribute.findFoldedEnums(value);
+}
+
const char * ImportedAttributeVectorReadGuard::getStringFromEnum(EnumHandle e) const {
return _target_attribute.getStringFromEnum(e);
}
diff --git a/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector_read_guard.h b/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector_read_guard.h
index 2622f091cf1..4cf4d5b64c1 100644
--- a/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector_read_guard.h
+++ b/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector_read_guard.h
@@ -67,6 +67,8 @@ public:
virtual uint32_t get(DocId docId, WeightedConstChar *buffer, uint32_t sz) const override;
virtual uint32_t get(DocId docId, WeightedEnum *buffer, uint32_t sz) const override;
virtual bool findEnum(const char * value, EnumHandle & e) const override;
+ virtual std::vector<EnumHandle> findFoldedEnums(const char *value) const override;
+
virtual const char * getStringFromEnum(EnumHandle e) const override;
virtual std::unique_ptr<ISearchContext> createSearchContext(std::unique_ptr<QueryTermSimple> term,
const SearchContextParams &params) const override;
diff --git a/searchlib/src/vespa/searchlib/attribute/imported_search_context.cpp b/searchlib/src/vespa/searchlib/attribute/imported_search_context.cpp
index c480eec5e88..44248c97abe 100644
--- a/searchlib/src/vespa/searchlib/attribute/imported_search_context.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/imported_search_context.cpp
@@ -1,13 +1,16 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include "bitvector_search_cache.h"
#include "imported_search_context.h"
+#include "bitvector_search_cache.h"
#include "imported_attribute_vector.h"
#include "reference_attribute.h"
#include <vespa/searchlib/common/bitvectoriterator.h>
#include <vespa/searchlib/queryeval/emptysearch.h>
#include "attributeiterators.hpp"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.imported_search_context");
+
using search::datastore::EntryRef;
using search::queryeval::EmptySearch;
using search::queryeval::SearchIterator;
@@ -64,7 +67,7 @@ ImportedSearchContext::createIterator(fef::TermFieldMatchData* matchData, bool s
DocIt postings;
auto array = _merger.getArray();
postings.set(&array[0], &array[array.size()]);
- return std::make_unique<AttributePostingListIteratorT<DocIt>>(true, matchData, postings);
+ return std::make_unique<AttributePostingListIteratorT<DocIt>>(*this, true, matchData, postings);
}
} else if (_merger.hasBitVector()) {
return BitVectorIterator::create(_merger.getBitVector(), _merger.getDocIdLimit(), *matchData, strict);
@@ -246,7 +249,7 @@ Int64Range ImportedSearchContext::getAsIntegerTerm() const {
return _target_search_context->getAsIntegerTerm();
}
-const QueryTermBase& ImportedSearchContext::queryTerm() const {
+const QueryTermBase * ImportedSearchContext::queryTerm() const {
return _target_search_context->queryTerm();
}
diff --git a/searchlib/src/vespa/searchlib/attribute/imported_search_context.h b/searchlib/src/vespa/searchlib/attribute/imported_search_context.h
index cfb5371dbb9..ae6ce181af0 100644
--- a/searchlib/src/vespa/searchlib/attribute/imported_search_context.h
+++ b/searchlib/src/vespa/searchlib/attribute/imported_search_context.h
@@ -59,7 +59,7 @@ public:
void fetchPostings(bool strict) override;
bool valid() const override;
Int64Range getAsIntegerTerm() const override;
- const QueryTermBase& queryTerm() const override;
+ const QueryTermBase * queryTerm() const override;
const vespalib::string& attributeName() const override;
using DocId = IAttributeVector::DocId;
diff --git a/searchlib/src/vespa/searchlib/attribute/integerbase.h b/searchlib/src/vespa/searchlib/attribute/integerbase.h
index 8d6c5046070..5dec40fd4da 100644
--- a/searchlib/src/vespa/searchlib/attribute/integerbase.h
+++ b/searchlib/src/vespa/searchlib/attribute/integerbase.h
@@ -98,6 +98,8 @@ protected:
Change _defaultValue;
private:
bool findEnum(const char *value, EnumHandle &e) const override;
+ std::vector<EnumHandle> findFoldedEnums(const char *value) const override;
+
virtual T get(DocId doc) const = 0;
virtual T getFromEnum(EnumHandle e) const = 0;
largeint_t getIntFromEnum(EnumHandle e) const override;
diff --git a/searchlib/src/vespa/searchlib/attribute/integerbase.hpp b/searchlib/src/vespa/searchlib/attribute/integerbase.hpp
index 3866e5a4acb..7b23cd51e92 100644
--- a/searchlib/src/vespa/searchlib/attribute/integerbase.hpp
+++ b/searchlib/src/vespa/searchlib/attribute/integerbase.hpp
@@ -32,6 +32,19 @@ IntegerAttributeTemplate<T>::findEnum(const char *value, EnumHandle &e) const {
return findEnum(ivalue, e);
}
+
+template<typename T>
+std::vector<EnumStoreBase::EnumHandle>
+IntegerAttributeTemplate<T>::findFoldedEnums(const char *value) const
+{
+ std::vector<EnumHandle> result;
+ EnumHandle h;
+ if (findEnum(value, h)) {
+ result.push_back(h);
+ }
+ return result;
+}
+
template<typename T>
largeint_t
IntegerAttributeTemplate<T>::getIntFromEnum(EnumHandle e) const {
diff --git a/searchlib/src/vespa/searchlib/attribute/multinumericattribute.cpp b/searchlib/src/vespa/searchlib/attribute/multinumericattribute.cpp
index 4eb8447297e..e16474e45a8 100644
--- a/searchlib/src/vespa/searchlib/attribute/multinumericattribute.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/multinumericattribute.cpp
@@ -3,6 +3,9 @@
#include "multinumericattribute.h"
#include "multinumericattribute.hpp"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.multi_numeric_attribute");
+
namespace search {
} // namespace search
diff --git a/searchlib/src/vespa/searchlib/attribute/multinumericenumattribute.cpp b/searchlib/src/vespa/searchlib/attribute/multinumericenumattribute.cpp
index 3021a37b8cb..0e5efe21de0 100644
--- a/searchlib/src/vespa/searchlib/attribute/multinumericenumattribute.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/multinumericenumattribute.cpp
@@ -3,6 +3,9 @@
#include "multinumericenumattribute.h"
#include "multinumericenumattribute.hpp"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.multi_numeric_enum_attribute");
+
namespace search {
} // namespace search
diff --git a/searchlib/src/vespa/searchlib/attribute/multistringattribute.h b/searchlib/src/vespa/searchlib/attribute/multistringattribute.h
index 5e5d33419aa..eb6a8b630de 100644
--- a/searchlib/src/vespa/searchlib/attribute/multistringattribute.h
+++ b/searchlib/src/vespa/searchlib/attribute/multistringattribute.h
@@ -71,6 +71,11 @@ public:
return this->_enumStore.getValue(indices[0].value());
}
}
+
+ std::vector<EnumHandle> findFoldedEnums(const char *value) const override {
+ return this->_enumStore.findFoldedEnums(value);
+ }
+
const char * getStringFromEnum(EnumHandle e) const override {
return this->_enumStore.getValue(e);
}
diff --git a/searchlib/src/vespa/searchlib/attribute/multistringattribute.hpp b/searchlib/src/vespa/searchlib/attribute/multistringattribute.hpp
index 858fe579764..6c459465b51 100644
--- a/searchlib/src/vespa/searchlib/attribute/multistringattribute.hpp
+++ b/searchlib/src/vespa/searchlib/attribute/multistringattribute.hpp
@@ -119,14 +119,14 @@ StringTemplSearchContext(QueryTermSimpleUP qTerm, const AttrType & toBeSearched)
this->_plsc = static_cast<attribute::IPostingListSearchContext *>(this);
if (this->valid()) {
if (this->isPrefix()) {
- FoldedComparatorType comp(enumStore, queryTerm().getTerm(), true);
+ FoldedComparatorType comp(enumStore, queryTerm()->getTerm(), true);
lookupRange(comp, comp);
} else if (this->isRegex()) {
- vespalib::string prefix(vespalib::Regexp::get_prefix(this->queryTerm().getTerm()));
+ vespalib::string prefix(vespalib::Regexp::get_prefix(this->queryTerm()->getTerm()));
FoldedComparatorType comp(enumStore, prefix.c_str(), true);
lookupRange(comp, comp);
} else {
- FoldedComparatorType comp(enumStore, queryTerm().getTerm());
+ FoldedComparatorType comp(enumStore, queryTerm()->getTerm());
lookupTerm(comp);
}
}
diff --git a/searchlib/src/vespa/searchlib/attribute/multistringpostattribute.cpp b/searchlib/src/vespa/searchlib/attribute/multistringpostattribute.cpp
index 8ea19f26190..75e05d61b13 100644
--- a/searchlib/src/vespa/searchlib/attribute/multistringpostattribute.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/multistringpostattribute.cpp
@@ -3,6 +3,9 @@
#include "multistringpostattribute.h"
#include "multistringpostattribute.hpp"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.multi_string_post_attribute");
+
namespace search {
EnumStoreBase::Index
diff --git a/searchlib/src/vespa/searchlib/attribute/not_implemented_attribute.cpp b/searchlib/src/vespa/searchlib/attribute/not_implemented_attribute.cpp
index 5e6cfa8a847..1dc95c42de8 100644
--- a/searchlib/src/vespa/searchlib/attribute/not_implemented_attribute.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/not_implemented_attribute.cpp
@@ -103,6 +103,12 @@ NotImplementedAttribute::findEnum(const char *, EnumHandle &) const {
return false;
}
+std::vector<NotImplementedAttribute::EnumHandle>
+NotImplementedAttribute::findFoldedEnums(const char *) const {
+ notImplemented();
+ return std::vector<EnumHandle>();
+}
+
long
NotImplementedAttribute::onSerializeForAscendingSort(DocId, void *, long, const common::BlobConverter *) const {
notImplemented();
diff --git a/searchlib/src/vespa/searchlib/attribute/not_implemented_attribute.h b/searchlib/src/vespa/searchlib/attribute/not_implemented_attribute.h
index 98935c1f155..cbd2ff162b2 100644
--- a/searchlib/src/vespa/searchlib/attribute/not_implemented_attribute.h
+++ b/searchlib/src/vespa/searchlib/attribute/not_implemented_attribute.h
@@ -26,6 +26,8 @@ struct NotImplementedAttribute : AttributeVector {
uint32_t get(DocId, WeightedConstChar *, uint32_t) const override;
uint32_t get(DocId, WeightedEnum *, uint32_t) const override;
bool findEnum(const char *, EnumHandle &) const override;
+ std::vector<EnumHandle> findFoldedEnums(const char *value) const override;
+
long onSerializeForAscendingSort(DocId, void *, long, const common::BlobConverter *) const override;
long onSerializeForDescendingSort(DocId, void *, long, const common::BlobConverter *) const override;
uint32_t clearDoc(DocId) override;
diff --git a/searchlib/src/vespa/searchlib/attribute/postinglistsearchcontext.cpp b/searchlib/src/vespa/searchlib/attribute/postinglistsearchcontext.cpp
index f9a638a6090..e17389ea532 100644
--- a/searchlib/src/vespa/searchlib/attribute/postinglistsearchcontext.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/postinglistsearchcontext.cpp
@@ -6,6 +6,8 @@
#include "diversity.hpp"
#include <vespa/searchlib/btree/btreeiterator.hpp>
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.posting_list_search_context");
namespace search {
@@ -21,7 +23,8 @@ PostingListSearchContext(const Dictionary &dictionary,
bool hasWeight,
const EnumStoreBase &esb,
uint32_t minBvDocFreq,
- bool useBitVector)
+ bool useBitVector,
+ const ISearchContext &baseSearchCtx)
: _frozenDictionary(dictionary.getFrozenView()),
_lowerDictItr(BTreeNode::Ref(), dictionary.getAllocator()),
_upperDictItr(BTreeNode::Ref(), dictionary.getAllocator()),
@@ -37,7 +40,8 @@ PostingListSearchContext(const Dictionary &dictionary,
_PLSTC(0.0),
_esb(esb),
_minBvDocFreq(minBvDocFreq),
- _gbv(nullptr)
+ _gbv(nullptr),
+ _baseSearchCtx(baseSearchCtx)
{
}
diff --git a/searchlib/src/vespa/searchlib/attribute/postinglistsearchcontext.h b/searchlib/src/vespa/searchlib/attribute/postinglistsearchcontext.h
index 5bf232cf01f..6a2324cef07 100644
--- a/searchlib/src/vespa/searchlib/attribute/postinglistsearchcontext.h
+++ b/searchlib/src/vespa/searchlib/attribute/postinglistsearchcontext.h
@@ -13,6 +13,8 @@
namespace search::attribute {
+class ISearchContext;
+
/**
* Search context helper for posting list attributes, used to instantiate
* iterators based on posting lists instead of brute force filtering search.
@@ -42,10 +44,11 @@ protected:
const EnumStoreBase &_esb;
uint32_t _minBvDocFreq;
const GrowableBitVector *_gbv; // bitvector if _useBitVector has been set
+ const ISearchContext &_baseSearchCtx;
PostingListSearchContext(const Dictionary &dictionary, uint32_t docIdLimit, uint64_t numValues, bool hasWeight,
- const EnumStoreBase &esb, uint32_t minBvDocFreq, bool useBitVector);
+ const EnumStoreBase &esb, uint32_t minBvDocFreq, bool useBitVector, const ISearchContext &baseSearchCtx);
~PostingListSearchContext();
@@ -111,7 +114,7 @@ protected:
PostingListSearchContextT(const Dictionary &dictionary, uint32_t docIdLimit, uint64_t numValues,
bool hasWeight, const PostingList &postingList, const EnumStoreBase &esb,
- uint32_t minBvCocFreq, bool useBitVector);
+ uint32_t minBvCocFreq, bool useBitVector, const ISearchContext &baseSearchCtx);
~PostingListSearchContextT();
void lookupSingle();
@@ -149,7 +152,7 @@ protected:
PostingListFoldedSearchContextT(const Dictionary &dictionary, uint32_t docIdLimit, uint64_t numValues,
bool hasWeight, const PostingList &postingList, const EnumStoreBase &esb,
- uint32_t minBvCocFreq, bool useBitVector);
+ uint32_t minBvCocFreq, bool useBitVector, const ISearchContext &baseSearchCtx);
unsigned int approximateHits() const override;
};
@@ -253,7 +256,8 @@ PostingSearchContext(QueryTermSimpleUP qTerm, bool useBitVector, const AttrT &to
toBeSearched.getPostingList(),
toBeSearched.getEnumStore(),
toBeSearched._postingList._minBvDocFreq,
- useBitVector),
+ useBitVector,
+ *this),
_toBeSearched(toBeSearched),
_enumStore(_toBeSearched.getEnumStore())
{
@@ -279,14 +283,14 @@ StringPostingSearchContext(QueryTermSimpleUP qTerm, bool useBitVector, const Att
if (this->valid()) {
if (this->isPrefix()) {
- FoldedComparatorType comp(_enumStore, this->queryTerm().getTerm(), true);
+ FoldedComparatorType comp(_enumStore, this->queryTerm()->getTerm(), true);
this->lookupRange(comp, comp);
} else if (this->isRegex()) {
- vespalib::string prefix(Regexp::get_prefix(this->queryTerm().getTerm()));
+ vespalib::string prefix(Regexp::get_prefix(this->queryTerm()->getTerm()));
FoldedComparatorType comp(_enumStore, prefix.c_str(), true);
this->lookupRange(comp, comp);
} else {
- FoldedComparatorType comp(_enumStore, this->queryTerm().getTerm());
+ FoldedComparatorType comp(_enumStore, this->queryTerm()->getTerm());
this->lookupTerm(comp);
}
if (this->_uniqueValues == 1u) {
diff --git a/searchlib/src/vespa/searchlib/attribute/postinglistsearchcontext.hpp b/searchlib/src/vespa/searchlib/attribute/postinglistsearchcontext.hpp
index 7793af8f510..cfad425d7ea 100644
--- a/searchlib/src/vespa/searchlib/attribute/postinglistsearchcontext.hpp
+++ b/searchlib/src/vespa/searchlib/attribute/postinglistsearchcontext.hpp
@@ -21,8 +21,8 @@ template <typename DataT>
PostingListSearchContextT<DataT>::
PostingListSearchContextT(const Dictionary &dictionary, uint32_t docIdLimit, uint64_t numValues, bool hasWeight,
const PostingList &postingList, const EnumStoreBase &esb,
- uint32_t minBvDocFreq, bool useBitVector)
- : PostingListSearchContext(dictionary, docIdLimit, numValues, hasWeight, esb, minBvDocFreq, useBitVector),
+ uint32_t minBvDocFreq, bool useBitVector, const ISearchContext &searchContext)
+ : PostingListSearchContext(dictionary, docIdLimit, numValues, hasWeight, esb, minBvDocFreq, useBitVector, searchContext),
_postingList(postingList),
_merger(docIdLimit),
_fetchPostingsDone(false)
@@ -164,9 +164,9 @@ createPostingIterator(fef::TermFieldMatchData *matchData, bool strict)
vespalib::ConstArrayRef<Posting> array = _merger.getArray();
postings.set(&array[0], &array[array.size()]);
if (_postingList._isFilter) {
- return std::make_unique<FilterAttributePostingListIteratorT<DocIt>>(matchData, postings);
+ return std::make_unique<FilterAttributePostingListIteratorT<DocIt>>(_baseSearchCtx, matchData, postings);
} else {
- return std::make_unique<AttributePostingListIteratorT<DocIt>>(_hasWeight, matchData, postings);
+ return std::make_unique<AttributePostingListIteratorT<DocIt>>(_baseSearchCtx, _hasWeight, matchData, postings);
}
}
if (_merger.hasArray()) {
@@ -192,18 +192,18 @@ createPostingIterator(fef::TermFieldMatchData *matchData, bool strict)
const Posting *array = postingList.getKeyDataEntry(_pidx, clusterSize);
postings.set(array, array + clusterSize);
if (postingList._isFilter) {
- return std::make_unique<FilterAttributePostingListIteratorT<DocIt>>(matchData, postings);
+ return std::make_unique<FilterAttributePostingListIteratorT<DocIt>>(_baseSearchCtx, matchData, postings);
} else {
- return std::make_unique<AttributePostingListIteratorT<DocIt>>(_hasWeight, matchData, postings);
+ return std::make_unique<AttributePostingListIteratorT<DocIt>>(_baseSearchCtx, _hasWeight, matchData, postings);
}
}
typename PostingList::BTreeType::FrozenView frozen(_frozenRoot, postingList.getAllocator());
using DocIt = typename PostingList::ConstIterator;
if (_postingList._isFilter) {
- return std::make_unique<FilterAttributePostingListIteratorT<DocIt>>(matchData, frozen.getRoot(), frozen.getAllocator());
+ return std::make_unique<FilterAttributePostingListIteratorT<DocIt>>(_baseSearchCtx, matchData, frozen.getRoot(), frozen.getAllocator());
} else {
- return std::make_unique<AttributePostingListIteratorT<DocIt>> (_hasWeight, matchData, frozen.getRoot(), frozen.getAllocator());
+ return std::make_unique<AttributePostingListIteratorT<DocIt>> (_baseSearchCtx, _hasWeight, matchData, frozen.getRoot(), frozen.getAllocator());
}
}
// returning nullptr will trigger fallback to filter iterator
@@ -287,8 +287,8 @@ template <typename DataT>
PostingListFoldedSearchContextT<DataT>::
PostingListFoldedSearchContextT(const Dictionary &dictionary, uint32_t docIdLimit, uint64_t numValues,
bool hasWeight, const PostingList &postingList, const EnumStoreBase &esb,
- uint32_t minBvDocFreq, bool useBitVector)
- : Parent(dictionary, docIdLimit, numValues, hasWeight, postingList, esb, minBvDocFreq, useBitVector)
+ uint32_t minBvDocFreq, bool useBitVector, const ISearchContext &searchContext)
+ : Parent(dictionary, docIdLimit, numValues, hasWeight, postingList, esb, minBvDocFreq, useBitVector, searchContext)
{
}
diff --git a/searchlib/src/vespa/searchlib/attribute/postingstore.cpp b/searchlib/src/vespa/searchlib/attribute/postingstore.cpp
index 68eda476df7..a0fa7e35054 100644
--- a/searchlib/src/vespa/searchlib/attribute/postingstore.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/postingstore.cpp
@@ -7,6 +7,9 @@
#include <vespa/searchcommon/attribute/config.h>
#include <vespa/searchcommon/attribute/status.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.posting_store");
+
namespace search::attribute {
using btree::BTreeNoLeafData;
diff --git a/searchlib/src/vespa/searchlib/attribute/predicate_attribute.cpp b/searchlib/src/vespa/searchlib/attribute/predicate_attribute.cpp
index 40405d4f5d9..424405a520e 100644
--- a/searchlib/src/vespa/searchlib/attribute/predicate_attribute.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/predicate_attribute.cpp
@@ -9,7 +9,7 @@
#include "attribute_header.h"
#include <vespa/log/log.h>
-LOG_SETUP(".predicate_attribute");
+LOG_SETUP(".searchlib.attribute.predicate_attribute");
using document::Predicate;
using document::PredicateFieldValue;
diff --git a/searchlib/src/vespa/searchlib/attribute/reference_attribute.cpp b/searchlib/src/vespa/searchlib/attribute/reference_attribute.cpp
index 1566ca47eb1..050954157da 100644
--- a/searchlib/src/vespa/searchlib/attribute/reference_attribute.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/reference_attribute.cpp
@@ -11,6 +11,9 @@
#include <vespa/searchlib/common/i_gid_to_lid_mapper.h>
#include <vespa/vespalib/data/fileheader.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.reference_attribute");
+
namespace search::attribute {
namespace {
diff --git a/searchlib/src/vespa/searchlib/attribute/singleenumattribute.cpp b/searchlib/src/vespa/searchlib/attribute/singleenumattribute.cpp
index b607bd52859..0e122c9d583 100644
--- a/searchlib/src/vespa/searchlib/attribute/singleenumattribute.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/singleenumattribute.cpp
@@ -3,6 +3,9 @@
#include "singleenumattribute.h"
#include "singleenumattribute.hpp"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.single_enum_attribute");
+
namespace search {
using attribute::Config;
diff --git a/searchlib/src/vespa/searchlib/attribute/singlenumericattribute.cpp b/searchlib/src/vespa/searchlib/attribute/singlenumericattribute.cpp
index 661a7f388da..ef92e94aa95 100644
--- a/searchlib/src/vespa/searchlib/attribute/singlenumericattribute.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/singlenumericattribute.cpp
@@ -3,6 +3,9 @@
#include "singlenumericattribute.h"
#include "singlenumericattribute.hpp"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.single_numeric_attribute");
+
namespace search {
} // namespace search
diff --git a/searchlib/src/vespa/searchlib/attribute/singlenumericenumattribute.cpp b/searchlib/src/vespa/searchlib/attribute/singlenumericenumattribute.cpp
index 9eea2773fb4..6c831cade89 100644
--- a/searchlib/src/vespa/searchlib/attribute/singlenumericenumattribute.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/singlenumericenumattribute.cpp
@@ -3,6 +3,9 @@
#include "singlenumericenumattribute.h"
#include "singlenumericenumattribute.hpp"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.single_numeric_enum_attribute");
+
namespace search {
} // namespace search
diff --git a/searchlib/src/vespa/searchlib/attribute/singlenumericpostattribute.cpp b/searchlib/src/vespa/searchlib/attribute/singlenumericpostattribute.cpp
index e47fd93dc65..9de02dbeaf9 100644
--- a/searchlib/src/vespa/searchlib/attribute/singlenumericpostattribute.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/singlenumericpostattribute.cpp
@@ -3,6 +3,9 @@
#include "singlenumericpostattribute.h"
#include "singlenumericpostattribute.hpp"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.single_numeric_post_attribute");
+
namespace search {
} // namespace search
diff --git a/searchlib/src/vespa/searchlib/attribute/singlesmallnumericattribute.cpp b/searchlib/src/vespa/searchlib/attribute/singlesmallnumericattribute.cpp
index 0be4972a4a7..5100e2e5546 100644
--- a/searchlib/src/vespa/searchlib/attribute/singlesmallnumericattribute.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/singlesmallnumericattribute.cpp
@@ -7,6 +7,9 @@
#include <vespa/searchlib/queryeval/emptysearch.h>
#include "iattributesavetarget.h"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.single_small_numeric_attribute");
+
namespace search {
SingleValueSmallNumericAttribute::
diff --git a/searchlib/src/vespa/searchlib/attribute/singlestringattribute.cpp b/searchlib/src/vespa/searchlib/attribute/singlestringattribute.cpp
index 70070fd741b..195926f656f 100644
--- a/searchlib/src/vespa/searchlib/attribute/singlestringattribute.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/singlestringattribute.cpp
@@ -3,6 +3,9 @@
#include "singlestringattribute.h"
#include "singlestringattribute.hpp"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.attribute.single_string_attribute");
+
namespace search {
template class SingleValueStringAttributeT<EnumAttribute<StringAttribute>>;
diff --git a/searchlib/src/vespa/searchlib/attribute/singlestringattribute.h b/searchlib/src/vespa/searchlib/attribute/singlestringattribute.h
index 4993b295b37..8d2efcbdc09 100644
--- a/searchlib/src/vespa/searchlib/attribute/singlestringattribute.h
+++ b/searchlib/src/vespa/searchlib/attribute/singlestringattribute.h
@@ -52,6 +52,9 @@ public:
const char * get(DocId doc) const override {
return this->_enumStore.getValue(this->_enumIndices[doc]);
}
+ std::vector<EnumHandle> findFoldedEnums(const char *value) const override {
+ return this->_enumStore.findFoldedEnums(value);
+ }
const char * getStringFromEnum(EnumHandle e) const override {
return this->_enumStore.getValue(e);
}
diff --git a/searchlib/src/vespa/searchlib/attribute/singlestringattribute.hpp b/searchlib/src/vespa/searchlib/attribute/singlestringattribute.hpp
index 84c2ff277d6..4f028e1a478 100644
--- a/searchlib/src/vespa/searchlib/attribute/singlestringattribute.hpp
+++ b/searchlib/src/vespa/searchlib/attribute/singlestringattribute.hpp
@@ -55,14 +55,14 @@ SingleValueStringAttributeT<B>::StringTemplSearchContext::StringTemplSearchConte
this->_plsc = static_cast<attribute::IPostingListSearchContext *>(this);
if (this->valid()) {
if (this->isPrefix()) {
- FoldedComparatorType comp(enumStore, queryTerm().getTerm(), true);
+ FoldedComparatorType comp(enumStore, queryTerm()->getTerm(), true);
lookupRange(comp, comp);
} else if (this->isRegex()) {
- vespalib::string prefix(vespalib::Regexp::get_prefix(this->queryTerm().getTerm()));
+ vespalib::string prefix(vespalib::Regexp::get_prefix(this->queryTerm()->getTerm()));
FoldedComparatorType comp(enumStore, prefix.c_str(), true);
lookupRange(comp, comp);
} else {
- FoldedComparatorType comp(enumStore, queryTerm().getTerm());
+ FoldedComparatorType comp(enumStore, queryTerm()->getTerm());
lookupTerm(comp);
}
}
diff --git a/searchlib/src/vespa/searchlib/attribute/stringbase.cpp b/searchlib/src/vespa/searchlib/attribute/stringbase.cpp
index 16a05e5f0a9..04ca9d216a3 100644
--- a/searchlib/src/vespa/searchlib/attribute/stringbase.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/stringbase.cpp
@@ -226,7 +226,7 @@ StringAttribute::StringSearchContext::StringSearchContext(QueryTermSimple::UP qT
_isPrefix(qTerm->isPrefix()),
_isRegex(qTerm->isRegex()),
_queryTerm(std::move(qTerm)),
- _termUCS4(queryTerm().getUCS4Term()),
+ _termUCS4(queryTerm()->getUCS4Term()),
_bufferLen(toBeSearched.getMaxValueCount()),
_buffer()
{
@@ -247,9 +247,9 @@ StringAttribute::StringSearchContext::valid() const {
return (_queryTerm.get() && (!_queryTerm->empty()));
}
-const QueryTermBase &
+const QueryTermBase *
StringAttribute::StringSearchContext::queryTerm() const {
- return static_cast<const QueryTermBase &>(*_queryTerm);
+ return static_cast<const QueryTermBase *>(_queryTerm.get());
}
uint32_t StringAttribute::clearDoc(DocId doc)
diff --git a/searchlib/src/vespa/searchlib/attribute/stringbase.h b/searchlib/src/vespa/searchlib/attribute/stringbase.h
index c817332af15..593d8f048f9 100644
--- a/searchlib/src/vespa/searchlib/attribute/stringbase.h
+++ b/searchlib/src/vespa/searchlib/attribute/stringbase.h
@@ -44,6 +44,7 @@ public:
bool apply(DocId doc, const ArithmeticValueUpdate & op);
bool applyWeight(DocId doc, const FieldValue & fv, const ArithmeticValueUpdate & wAdjust) override;
bool findEnum(const char * value, EnumHandle & e) const override = 0;
+ std::vector<EnumHandle> findFoldedEnums(const char *value) const override = 0;
uint32_t get(DocId doc, largeint_t * v, uint32_t sz) const override;
uint32_t get(DocId doc, double * v, uint32_t sz) const override;
uint32_t get(DocId doc, WeightedInt * v, uint32_t sz) const override;
@@ -103,7 +104,7 @@ private:
protected:
bool valid() const override;
- const QueryTermBase & queryTerm() const override;
+ const QueryTermBase * queryTerm() const override;
bool isMatch(const char *src) const {
if (__builtin_expect(isRegex(), false)) {
return getRegex()->match(src);
diff --git a/searchlib/src/vespa/searchlib/bitcompression/pagedict4.cpp b/searchlib/src/vespa/searchlib/bitcompression/pagedict4.cpp
index 50abcca96a7..23fcfb0b719 100644
--- a/searchlib/src/vespa/searchlib/bitcompression/pagedict4.cpp
+++ b/searchlib/src/vespa/searchlib/bitcompression/pagedict4.cpp
@@ -1184,8 +1184,7 @@ lookup(const vespalib::stringref &key)
}
break; // key < counts
}
- LOG(error, "FATAL: Missing L7 entry for overflow entry");
- abort(); // counts < key, should not happen (missing L7 entry)
+ LOG_ABORT("FATAL: Missing L7 entry for overflow entry"); // counts < key, should not happen (missing L7 entry)
} else {
bool l6NotLessThanKey = !(word < key);
if (l6NotLessThanKey)
@@ -1477,7 +1476,7 @@ lookup(const SSReader &ssReader,
bool l3NotLessThanKey = !(word < key);
if (l3NotLessThanKey)
break;
- abort();
+ LOG_ABORT("should not be reached");
_l3Word = word;
}
readStartOffset(dL3,
diff --git a/searchlib/src/vespa/searchlib/bitcompression/posocccompression.cpp b/searchlib/src/vespa/searchlib/bitcompression/posocccompression.cpp
index 739018b99a5..65b7b236525 100644
--- a/searchlib/src/vespa/searchlib/bitcompression/posocccompression.cpp
+++ b/searchlib/src/vespa/searchlib/bitcompression/posocccompression.cpp
@@ -146,7 +146,7 @@ PosOccFieldParams::setSchemaParams(const Schema &schema, uint32_t fieldId)
break;
default:
LOG(error, "Bad collection type");
- abort();
+ LOG_ABORT("should not be reached");
}
_avgElemLen = field.getAvgElemLen();
_name = field.getName();
@@ -179,8 +179,7 @@ PosOccFieldParams::readHeader(const vespalib::GenericHeader &header,
_hasElementWeights = true;
break;
default:
- LOG(error, "Bad collection type when reading field param in header");
- abort();
+ LOG_ABORT("Bad collection type when reading field param in header");
}
_avgElemLen = header.getTag(avgElemLenKey).asInteger();
}
@@ -206,9 +205,7 @@ PosOccFieldParams::writeHeader(vespalib::GenericHeader &header,
ct = schema::CollectionType::WEIGHTEDSET;
break;
default:
- LOG(error,
- "Bad collection type when writing field param in header");
- abort();
+ LOG_ABORT("Bad collection type when writing field param in header");
}
header.putTag(GenericHeader::Tag(collKey, schema::getTypeName(ct)));
header.putTag(GenericHeader::Tag(avgElemLenKey, _avgElemLen));
@@ -285,7 +282,7 @@ PosOccFieldsParams::setSchemaParams(const Schema &schema,
cacheParamsRef();
const Schema::IndexField &field = schema.getIndexField(indexId);
if (!SchemaUtil::validateIndexField(field))
- abort();
+ LOG_ABORT("should not be reached");
_params[0].setSchemaParams(schema, indexId);
}
diff --git a/searchlib/src/vespa/searchlib/btree/btreeinserter.cpp b/searchlib/src/vespa/searchlib/btree/btreeinserter.cpp
index 4607efd6673..f307c474f90 100644
--- a/searchlib/src/vespa/searchlib/btree/btreeinserter.cpp
+++ b/searchlib/src/vespa/searchlib/btree/btreeinserter.cpp
@@ -6,6 +6,9 @@
#include "btreeinserter.hpp"
#include "btreenode.hpp"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.btree.btreeinserter");
+
namespace search::btree {
template class BTreeInserter<uint32_t, uint32_t, NoAggregated>;
diff --git a/searchlib/src/vespa/searchlib/btree/btreeiterator.cpp b/searchlib/src/vespa/searchlib/btree/btreeiterator.cpp
index ae9d27d4c5d..08ad4345289 100644
--- a/searchlib/src/vespa/searchlib/btree/btreeiterator.cpp
+++ b/searchlib/src/vespa/searchlib/btree/btreeiterator.cpp
@@ -1,10 +1,14 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include "btreeiterator.h"
#include "btreeroot.h"
#include "btreenodeallocator.h"
#include "btreeiterator.hpp"
#include "btreenode.hpp"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.btree.breeiterator");
+
namespace search::btree {
template class BTreeIteratorBase<uint32_t, uint32_t, NoAggregated>;
diff --git a/searchlib/src/vespa/searchlib/btree/btreeiterator.hpp b/searchlib/src/vespa/searchlib/btree/btreeiterator.hpp
index 741121aebab..02b73ef53f6 100644
--- a/searchlib/src/vespa/searchlib/btree/btreeiterator.hpp
+++ b/searchlib/src/vespa/searchlib/btree/btreeiterator.hpp
@@ -5,6 +5,7 @@
#include "btreeiterator.h"
#include "btreeaggregator.h"
#include "btreenode.hpp"
+#include <vespa/vespalib/util/hdr_abort.h>
#include <vespa/vespalib/stllike/asciistream.h>
namespace search {
@@ -557,18 +558,15 @@ BTreeIteratorBase<KeyT, DataT, AggrT, INTERNAL_SLOTS, LEAF_SLOTS, PATH_SIZE>::
identical(const BTreeIteratorBase &rhs) const
{
if (_pathSize != rhs._pathSize || _leaf != rhs._leaf) {
- abort();
- return false;
+ HDR_ABORT("should not be reached");
}
for (uint32_t level = 0; level < _pathSize; ++level) {
if (_path[level] != rhs._path[level]) {
- abort();
- return false;
+ HDR_ABORT("should not be reached");
}
}
if (_leafRoot != rhs._leafRoot) {
- abort();
- return false;
+ HDR_ABORT("should not be reached");
}
return true;
}
diff --git a/searchlib/src/vespa/searchlib/btree/btreeroot.cpp b/searchlib/src/vespa/searchlib/btree/btreeroot.cpp
index f233ea324d1..60c44f7973e 100644
--- a/searchlib/src/vespa/searchlib/btree/btreeroot.cpp
+++ b/searchlib/src/vespa/searchlib/btree/btreeroot.cpp
@@ -6,6 +6,9 @@
#include "btreeroot.hpp"
#include "btreenode.hpp"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.btree.btreeroot");
+
namespace search::btree {
template class BTreeRootT<uint32_t, uint32_t, NoAggregated>;
diff --git a/searchlib/src/vespa/searchlib/btree/btreestore.cpp b/searchlib/src/vespa/searchlib/btree/btreestore.cpp
index 06c968eec1f..4467aad15d5 100644
--- a/searchlib/src/vespa/searchlib/btree/btreestore.cpp
+++ b/searchlib/src/vespa/searchlib/btree/btreestore.cpp
@@ -4,6 +4,9 @@
#include "btreestore.hpp"
#include "btreeiterator.hpp"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.btree.breestore");
+
namespace search::btree {
template class BTreeStore<uint32_t, uint32_t,
diff --git a/searchlib/src/vespa/searchlib/common/bitvector.cpp b/searchlib/src/vespa/searchlib/common/bitvector.cpp
index 9f78f6ff222..ece025454c9 100644
--- a/searchlib/src/vespa/searchlib/common/bitvector.cpp
+++ b/searchlib/src/vespa/searchlib/common/bitvector.cpp
@@ -9,6 +9,9 @@
#include <vespa/vespalib/objects/nbostream.h>
#include <vespa/fastos/file.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.common.bitvector");
+
using vespalib::make_string;
using vespalib::IllegalArgumentException;
using vespalib::hwaccelrated::IAccelrated;
@@ -275,12 +278,12 @@ BitVector::hasTrueBitsInternal() const
void
BitVector::resize(Index)
{
- abort();
+ LOG_ABORT("should not be reached");
}
GenerationHeldBase::UP
BitVector::grow(Index, Index )
{
- abort();
+ LOG_ABORT("should not be reached");
}
size_t
diff --git a/searchlib/src/vespa/searchlib/common/bitvector.h b/searchlib/src/vespa/searchlib/common/bitvector.h
index fa8ec5b5cdd..7405688f4f7 100644
--- a/searchlib/src/vespa/searchlib/common/bitvector.h
+++ b/searchlib/src/vespa/searchlib/common/bitvector.h
@@ -120,9 +120,13 @@ public:
}
void setSize(Index sz) {
- clearBit(size());
+ setBit(sz); // Need to place the new stop sign first
+ std::atomic_thread_fence(std::memory_order_release);
+ if (sz > _sz) {
+ // Can only remove the old stopsign if it is ahead of the new.
+ clearBit(_sz);
+ }
_sz = sz;
- setBit(size());
}
void setBit(Index idx) {
_words[wordNum(idx)] |= mask(idx);
diff --git a/searchlib/src/vespa/searchlib/common/isequencedtaskexecutor.h b/searchlib/src/vespa/searchlib/common/isequencedtaskexecutor.h
index 05347e790fb..a8b2a722c01 100644
--- a/searchlib/src/vespa/searchlib/common/isequencedtaskexecutor.h
+++ b/searchlib/src/vespa/searchlib/common/isequencedtaskexecutor.h
@@ -89,14 +89,14 @@ public:
* call sync before tearing down pointed to/referenced data.
* All tasks must be scheduled from same thread.
*
- * @param componentId component id
- * @param function function to be wrapped in a task and later executed
+ * @param id executor id
+ * @param function function to be wrapped in a task and later executed
*/
template <class FunctionType>
- void execute(vespalib::stringref componentId, FunctionType &&function) {
- ExecutorId id = getExecutorId(componentId);
+ void execute(ExecutorId id, FunctionType &&function) {
executeTask(id, vespalib::makeLambdaTask(std::forward<FunctionType>(function)));
}
+
};
} // namespace search
diff --git a/searchlib/src/vespa/searchlib/common/packets.cpp b/searchlib/src/vespa/searchlib/common/packets.cpp
index 5004ba80913..b6b0baea92a 100644
--- a/searchlib/src/vespa/searchlib/common/packets.cpp
+++ b/searchlib/src/vespa/searchlib/common/packets.cpp
@@ -426,7 +426,7 @@ FS4Packet_Shared::Encode(FNET_DataBuffer *dst) {
bool
FS4Packet_Shared::Decode(FNET_DataBuffer *, uint32_t ) {
- abort();
+ LOG_ABORT("should not be reached");
}
vespalib::string
@@ -492,7 +492,7 @@ FS4Packet_PreSerialized::Encode(FNET_DataBuffer *dst)
bool
FS4Packet_PreSerialized::Decode(FNET_DataBuffer *, uint32_t)
{
- abort();
+ LOG_ABORT("should not be reached");
}
vespalib::string
diff --git a/searchlib/src/vespa/searchlib/datastore/datastorebase.cpp b/searchlib/src/vespa/searchlib/datastore/datastorebase.cpp
index b405542292d..68e7155505f 100644
--- a/searchlib/src/vespa/searchlib/datastore/datastorebase.cpp
+++ b/searchlib/src/vespa/searchlib/datastore/datastorebase.cpp
@@ -4,6 +4,9 @@
#include <vespa/vespalib/util/array.hpp>
#include <limits>
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.datastore.datastorebase");
+
using vespalib::GenerationHeldBase;
namespace search::datastore {
@@ -324,7 +327,7 @@ DataStoreBase::getMemStats() const
stats._deadBytes += bState.getDeadElems() * elementSize;
stats._holdBytes += (bState.getHoldElems() * elementSize) + bState.getExtraHoldBytes();
} else {
- abort();
+ LOG_ABORT("should not be reached");
}
}
size_t genHolderHeldBytes = _genHolder.getHeldBytes();
@@ -353,7 +356,7 @@ DataStoreBase::getAddressSpaceUsage() const
} else if (bState.isFree()) {
limitClusters += _maxClusters;
} else {
- abort();
+ LOG_ABORT("should not be reached");
}
}
return AddressSpace(usedClusters, deadClusters, limitClusters);
diff --git a/searchlib/src/vespa/searchlib/diskindex/fileheader.cpp b/searchlib/src/vespa/searchlib/diskindex/fileheader.cpp
index 7e479cb2905..b35c25fcfd3 100644
--- a/searchlib/src/vespa/searchlib/diskindex/fileheader.cpp
+++ b/searchlib/src/vespa/searchlib/diskindex/fileheader.cpp
@@ -90,7 +90,7 @@ FileHeader::taste(const vespalib::string &name,
if (_completed && _fileBitSize > 8 * fileSize) {
LOG(error, "FileHeader::taste(\"%s\"): fleBitSize(%" PRIu64 ") > 8 * fileSize(%" PRIu64 ")",
name.c_str(), _fileBitSize, fileSize);
- abort();
+ LOG_ABORT("should not be reached");
}
} else if (!_allowNoFileBitSize) {
LOG(error, "FileHeader::taste(\"%s\"): Missing fileBitSize tag", name.c_str());
diff --git a/searchlib/src/vespa/searchlib/diskindex/fusion.cpp b/searchlib/src/vespa/searchlib/diskindex/fusion.cpp
index 5dc5af68a38..e61fe7bab17 100644
--- a/searchlib/src/vespa/searchlib/diskindex/fusion.cpp
+++ b/searchlib/src/vespa/searchlib/diskindex/fusion.cpp
@@ -214,7 +214,7 @@ Fusion::mergeField(uint32_t id)
if (!res) {
LOG(error, "Could not merge field postings for field %s dir %s",
indexName.c_str(), indexDir.c_str());
- abort();
+ LOG_ABORT("should not be reached");
}
if (!FileKit::createStamp(indexDir + "/.mergeocc_done"))
return false;
@@ -324,7 +324,7 @@ Fusion::openFieldWriter(const SchemaUtil::IndexIterator &index,
_fileHeaderContext)) {
LOG(error, "Could not open output posocc + dictionary in %s",
dir.c_str());
- abort();
+ LOG_ABORT("should not be reached");
return false;
}
return true;
@@ -375,7 +375,7 @@ Fusion::mergeFieldPostings(const SchemaUtil::IndexIterator &index)
if (!fieldWriter.close()) {
LOG(error, "Could not close output posocc + dictionary in %s/%s",
_outDir.c_str(), indexName.c_str());
- abort();
+ LOG_ABORT("should not be reached");
}
return true;
}
diff --git a/searchlib/src/vespa/searchlib/diskindex/indexbuilder.cpp b/searchlib/src/vespa/searchlib/diskindex/indexbuilder.cpp
index 171e862f064..99eced5f97b 100644
--- a/searchlib/src/vespa/searchlib/diskindex/indexbuilder.cpp
+++ b/searchlib/src/vespa/searchlib/diskindex/indexbuilder.cpp
@@ -297,7 +297,7 @@ FileHandle::open(const vespalib::stringref &dir,
tuneFileWrite, fileHeaderContext)) {
LOG(error, "Could not open term writer %s for write (%s)",
dir.c_str(), getLastErrorString().c_str());
- abort();
+ LOG_ABORT("should not be reached");
}
}
@@ -688,7 +688,7 @@ IndexBuilder::open(uint32_t docIdLimit, uint64_t numWordIds,
vespalib::string schemaFile = appendToPrefix("schema.txt");
if (!_schema.saveToFile(schemaFile)) {
LOG(error, "Cannot save schema to \"%s\"", schemaFile.c_str());
- abort();
+ LOG_ABORT("should not be reached");
}
}
@@ -705,7 +705,7 @@ IndexBuilder::close()
if (!docsummary::DocumentSummary::writeDocIdLimit(_prefix, _docIdLimit)) {
LOG(error, "Could not write docsum count in dir %s: %s",
_prefix.c_str(), getLastErrorString().c_str());
- abort();
+ LOG_ABORT("should not be reached");
}
}
diff --git a/searchlib/src/vespa/searchlib/diskindex/pagedict4file.cpp b/searchlib/src/vespa/searchlib/diskindex/pagedict4file.cpp
index 72fd07e5752..791e41de497 100644
--- a/searchlib/src/vespa/searchlib/diskindex/pagedict4file.cpp
+++ b/searchlib/src/vespa/searchlib/diskindex/pagedict4file.cpp
@@ -23,7 +23,7 @@ void assertOpenWriteOnly(bool ok, const vespalib::string &fileName)
LOG(error, "Could not open %s for write: %s",
fileName.c_str(),
vespalib::getOpenErrorString(osError, fileName.c_str()).c_str());
- abort();
+ LOG_ABORT("should not be reached");
}
}
diff --git a/searchlib/src/vespa/searchlib/diskindex/zcposting.cpp b/searchlib/src/vespa/searchlib/diskindex/zcposting.cpp
index 17d4457a318..688e3ef59e1 100644
--- a/searchlib/src/vespa/searchlib/diskindex/zcposting.cpp
+++ b/searchlib/src/vespa/searchlib/diskindex/zcposting.cpp
@@ -697,7 +697,7 @@ Zc4PostingSeqWrite::readHeader(const vespalib::string &name)
if (!res) {
LOG(error, "Could not open %s for reading file header: %s",
name.c_str(), getLastErrorString().c_str());
- abort();
+ LOG_ABORT("should not be reached");
}
drc.setFile(&file);
diff --git a/searchlib/src/vespa/searchlib/docstore/documentstore.cpp b/searchlib/src/vespa/searchlib/docstore/documentstore.cpp
index 59e92b112fa..6e784bf8c30 100644
--- a/searchlib/src/vespa/searchlib/docstore/documentstore.cpp
+++ b/searchlib/src/vespa/searchlib/docstore/documentstore.cpp
@@ -35,8 +35,7 @@ void
DocumentVisitorAdapter::visit(uint32_t lid, vespalib::ConstBufferRef buf) {
if (buf.size() > 0) {
vespalib::nbostream is(buf.c_str(), buf.size());
- document::Document::UP doc(new document::Document(_repo, is));
- _visitor.visit(lid, std::move(doc));
+ _visitor.visit(lid, std::make_unique<document::Document>(_repo, is));
}
}
@@ -51,34 +50,38 @@ public:
using Alloc = vespalib::alloc::Alloc;
typedef std::unique_ptr<Value> UP;
- Value() : _compressedSize(0), _uncompressedSize(0), _compression(CompressionConfig::NONE) {}
-
- Value(Value &&rhs) :
- _compressedSize(rhs._compressedSize),
- _uncompressedSize(rhs._uncompressedSize),
- _compression(rhs._compression),
- _buf(std::move(rhs._buf)) {}
-
- Value(const Value &rhs) :
- _compressedSize(rhs._compressedSize),
- _uncompressedSize(rhs._uncompressedSize),
- _compression(rhs._compression),
- _buf(Alloc::alloc(rhs.size())) {
+ Value()
+ : _syncToken(0),
+ _compressedSize(0),
+ _uncompressedSize(0),
+ _compression(CompressionConfig::NONE)
+ {}
+
+ Value(uint64_t syncToken)
+ : _syncToken(syncToken),
+ _compressedSize(0),
+ _uncompressedSize(0),
+ _compression(CompressionConfig::NONE)
+ {}
+
+ Value(Value &&rhs) = default;
+ Value &operator=(Value &&rhs) = default;
+
+ Value(const Value &rhs)
+ : _syncToken(rhs._syncToken),
+ _compressedSize(rhs._compressedSize),
+ _uncompressedSize(rhs._uncompressedSize),
+ _compression(rhs._compression),
+ _buf(Alloc::alloc(rhs.size()))
+ {
memcpy(get(), rhs.get(), size());
}
- Value &operator=(Value &&rhs) {
- _buf = std::move(rhs._buf);
- _compressedSize = rhs._compressedSize;
- _uncompressedSize = rhs._uncompressedSize;
- _compression = rhs._compression;
- return *this;
- }
-
void setCompression(CompressionConfig::Type comp, size_t uncompressedSize) {
_compression = comp;
_uncompressedSize = uncompressedSize;
}
+ uint64_t getSyncToken() const { return _syncToken; }
CompressionConfig::Type getCompression() const { return _compression; }
@@ -96,7 +99,8 @@ public:
* Decompress value into temporary buffer and deserialize document from
* the temporary buffer.
*/
- document::Document::UP deserializeDocument(const DocumentTypeRepo &repo);
+ document::Document::UP deserializeDocument(const DocumentTypeRepo &repo) const;
+ vespalib::DataBuffer decompressed() const;
size_t size() const { return _compressedSize; }
bool empty() const { return size() == 0; }
@@ -104,6 +108,7 @@ public:
const void *get() const { return _buf.get(); }
void *get() { return _buf.get(); }
private:
+ uint64_t _syncToken;
size_t _compressedSize;
size_t _uncompressedSize;
CompressionConfig::Type _compression;
@@ -119,7 +124,7 @@ public:
bool read(DocumentIdT key, Value &value) const;
void visit(const IDocumentStore::LidVector &lids, const DocumentTypeRepo &repo, IDocumentVisitor &visitor) const;
- void write(DocumentIdT, const Value &) {}
+ void write(DocumentIdT, const Value &);
void erase(DocumentIdT) {}
const CompressionConfig &getCompression() const { return _compression; }
void reconfigure(const CompressionConfig &compression);
@@ -138,8 +143,7 @@ void
Value::set(vespalib::DataBuffer &&buf, ssize_t len, const CompressionConfig &compression) {
//Underlying buffer must be identical to allow swap.
vespalib::DataBuffer compressed(buf.getData(), 0u);
- CompressionConfig::Type type = compress(compression, vespalib::ConstBufferRef(buf.getData(), len),
- compressed, true);
+ CompressionConfig::Type type = compress(compression, vespalib::ConstBufferRef(buf.getData(), len), compressed, true);
_compressedSize = compressed.getDataLen();
if (buf.getData() == compressed.getData()) {
// Uncompressed so we can just steal the underlying buffer.
@@ -154,16 +158,20 @@ Value::set(vespalib::DataBuffer &&buf, ssize_t len, const CompressionConfig &com
setCompression(type, len);
}
+vespalib::DataBuffer
+Value::decompressed() const {
+ vespalib::DataBuffer uncompressed(_buf.get(), (size_t) 0);
+ decompress(getCompression(), getUncompressedSize(), vespalib::ConstBufferRef(*this, size()), uncompressed, true);
+ return uncompressed;
+}
document::Document::UP
-Value::deserializeDocument(const DocumentTypeRepo &repo) {
- vespalib::DataBuffer uncompressed((char *) _buf.get(), (size_t) 0);
- decompress(getCompression(), getUncompressedSize(), vespalib::ConstBufferRef(*this, size()), uncompressed, true);
+Value::deserializeDocument(const DocumentTypeRepo &repo) const {
+ vespalib::DataBuffer uncompressed(decompressed());
vespalib::nbostream is(uncompressed.getData(), uncompressed.getDataLen());
- return document::Document::UP(new document::Document(repo, is));
+ return std::make_unique<document::Document>(repo, is);
}
-
void
BackingStore::visit(const IDocumentStore::LidVector &lids, const DocumentTypeRepo &repo,
IDocumentVisitor &visitor) const {
@@ -184,6 +192,13 @@ BackingStore::read(DocumentIdT key, Value &value) const {
}
void
+BackingStore::write(DocumentIdT lid, const Value & value)
+{
+ vespalib::DataBuffer buf = value.decompressed();
+ _backingStore.write(value.getSyncToken(), lid, buf.getData(), buf.getDataLen());
+}
+
+void
BackingStore::reconfigure(const CompressionConfig &compression) {
_compression = compression;
}
@@ -194,8 +209,7 @@ using CacheParams = vespalib::CacheParam<
vespalib::LruParam<DocumentIdT, docstore::Value>,
docstore::BackingStore,
vespalib::zero<DocumentIdT>,
- vespalib::size<docstore::Value>
->;
+ vespalib::size<docstore::Value> >;
class Cache : public vespalib::cache<CacheParams> {
public:
@@ -210,6 +224,7 @@ DocumentStore::Config::operator == (const Config &rhs) const {
return (_maxCacheBytes == rhs._maxCacheBytes) &&
(_allowVisitCaching == rhs._allowVisitCaching) &&
(_initialCacheEntries == rhs._initialCacheEntries) &&
+ (_updateStrategy == rhs._updateStrategy) &&
(_compression == rhs._compression);
}
@@ -226,7 +241,7 @@ DocumentStore::DocumentStore(const Config & config, IDataStore & store)
_cache->reserveElements(config.getInitialCacheEntries());
}
-DocumentStore::~DocumentStore() {}
+DocumentStore::~DocumentStore() = default;
void
DocumentStore::reconfigure(const Config & config) {
@@ -282,10 +297,27 @@ DocumentStore::write(uint64_t syncToken, DocumentIdT lid, const document::Docume
void
DocumentStore::write(uint64_t syncToken, DocumentIdT lid, const vespalib::nbostream & stream) {
- _backingStore.write(syncToken, lid, stream.peek(), stream.size());
if (useCache()) {
- _cache->invalidate(lid);
- _visitCache->invalidate(lid);
+ switch (_config.updateStrategy()) {
+ case Config::UpdateStrategy::INVALIDATE:
+ _backingStore.write(syncToken, lid, stream.peek(), stream.size());
+ _cache->invalidate(lid);
+ break;
+ case Config::UpdateStrategy::UPDATE:
+ if (_cache->hasKey(lid)) {
+ Value value(syncToken);
+ vespalib::DataBuffer buf(stream.size());
+ buf.writeBytes(stream.peek(), stream.size());
+ value.set(std::move(buf), stream.size(), _store->getCompression());
+ _cache->write(lid, std::move(value));
+ } else {
+ _backingStore.write(syncToken, lid, stream.peek(), stream.size());
+ }
+ break;
+ }
+ _visitCache->invalidate(lid); // The cost and complexity of this updating this is not worth it.
+ } else {
+ _backingStore.write(syncToken, lid, stream.peek(), stream.size());
}
}
@@ -475,31 +507,22 @@ WrapVisitor(Visitor &visitor,
void
-DocumentStore::accept(IDocumentStoreReadVisitor &visitor,
- IDocumentStoreVisitorProgress &visitorProgress,
+DocumentStore::accept(IDocumentStoreReadVisitor &visitor, IDocumentStoreVisitorProgress &visitorProgress,
const DocumentTypeRepo &repo)
{
- WrapVisitor<IDocumentStoreReadVisitor> wrap(visitor, repo,
- _store->getCompression(),
- *this,
- _backingStore.
- tentativeLastSyncToken());
+ WrapVisitor<IDocumentStoreReadVisitor> wrap(visitor, repo, _store->getCompression(), *this,
+ _backingStore.tentativeLastSyncToken());
WrapVisitorProgress wrapVisitorProgress(visitorProgress);
_backingStore.accept(wrap, wrapVisitorProgress, false);
}
void
-DocumentStore::accept(IDocumentStoreRewriteVisitor &visitor,
- IDocumentStoreVisitorProgress &visitorProgress,
+DocumentStore::accept(IDocumentStoreRewriteVisitor &visitor, IDocumentStoreVisitorProgress &visitorProgress,
const DocumentTypeRepo &repo)
{
- WrapVisitor<IDocumentStoreRewriteVisitor> wrap(visitor,
- repo,
- _store->getCompression(),
- *this,
- _backingStore.
- tentativeLastSyncToken());
+ WrapVisitor<IDocumentStoreRewriteVisitor> wrap(visitor, repo, _store->getCompression(), *this,
+ _backingStore.tentativeLastSyncToken());
WrapVisitorProgress wrapVisitorProgress(visitorProgress);
_backingStore.accept(wrap, wrapVisitorProgress, true);
}
diff --git a/searchlib/src/vespa/searchlib/docstore/documentstore.h b/searchlib/src/vespa/searchlib/docstore/documentstore.h
index 08b042d99c5..7bc3ab1c21d 100644
--- a/searchlib/src/vespa/searchlib/docstore/documentstore.h
+++ b/searchlib/src/vespa/searchlib/docstore/documentstore.h
@@ -27,17 +27,20 @@ class DocumentStore : public IDocumentStore
public:
class Config {
public:
+ enum UpdateStrategy {INVALIDATE, UPDATE };
using CompressionConfig = vespalib::compression::CompressionConfig;
Config() :
_compression(CompressionConfig::LZ4, 9, 70),
_maxCacheBytes(1000000000),
_initialCacheEntries(0),
+ _updateStrategy(INVALIDATE),
_allowVisitCaching(false)
{ }
Config(const CompressionConfig & compression, size_t maxCacheBytes, size_t initialCacheEntries) :
_compression((maxCacheBytes != 0) ? compression : CompressionConfig::NONE),
_maxCacheBytes(maxCacheBytes),
_initialCacheEntries(initialCacheEntries),
+ _updateStrategy(INVALIDATE),
_allowVisitCaching(false)
{ }
const CompressionConfig & getCompression() const { return _compression; }
@@ -45,11 +48,14 @@ public:
size_t getInitialCacheEntries() const { return _initialCacheEntries; }
bool allowVisitCaching() const { return _allowVisitCaching; }
Config & allowVisitCaching(bool allow) { _allowVisitCaching = allow; return *this; }
+ Config & updateStrategy(UpdateStrategy strategy) { _updateStrategy = strategy; return *this; }
+ UpdateStrategy updateStrategy() const { return _updateStrategy; }
bool operator == (const Config &) const;
private:
CompressionConfig _compression;
size_t _maxCacheBytes;
size_t _initialCacheEntries;
+ UpdateStrategy _updateStrategy;
bool _allowVisitCaching;
};
@@ -82,14 +88,10 @@ public:
CacheStats getCacheStats() const override;
size_t memoryMeta() const override { return _backingStore.memoryMeta(); }
const vespalib::string & getBaseDir() const override { return _backingStore.getBaseDir(); }
- void
- accept(IDocumentStoreReadVisitor &visitor,
- IDocumentStoreVisitorProgress &visitorProgress,
- const document::DocumentTypeRepo &repo) override;
- void
- accept(IDocumentStoreRewriteVisitor &visitor,
- IDocumentStoreVisitorProgress &visitorProgress,
- const document::DocumentTypeRepo &repo) override;
+ void accept(IDocumentStoreReadVisitor &visitor, IDocumentStoreVisitorProgress &visitorProgress,
+ const document::DocumentTypeRepo &repo) override;
+ void accept(IDocumentStoreRewriteVisitor &visitor, IDocumentStoreVisitorProgress &visitorProgress,
+ const document::DocumentTypeRepo &repo) override;
double getVisitCost() const override;
DataStoreStorageStats getStorageStats() const override;
MemoryUsage getMemoryUsage() const override;
diff --git a/searchlib/src/vespa/searchlib/docstore/filechunk.cpp b/searchlib/src/vespa/searchlib/docstore/filechunk.cpp
index e9a1ffcda20..39394724853 100644
--- a/searchlib/src/vespa/searchlib/docstore/filechunk.cpp
+++ b/searchlib/src/vespa/searchlib/docstore/filechunk.cpp
@@ -245,7 +245,7 @@ FileChunk::updateLidMap(const LockGuard &guard, ISetLid &ds, uint64_t serialNum,
assert(idxFile.getSize() == 0);
}
} else {
- assert(false);
+ LOG_ABORT("should not reach here");
}
return sz;
}
diff --git a/searchlib/src/vespa/searchlib/docstore/logdatastore.cpp b/searchlib/src/vespa/searchlib/docstore/logdatastore.cpp
index 6674952d202..1f5a904a514 100644
--- a/searchlib/src/vespa/searchlib/docstore/logdatastore.cpp
+++ b/searchlib/src/vespa/searchlib/docstore/logdatastore.cpp
@@ -296,15 +296,17 @@ LogDataStore::compact(uint64_t syncToken)
uint64_t usage = getDiskFootprint();
uint64_t bloat = getDiskBloat();
LOG(debug, "%s", bloatMsg(bloat, usage).c_str());
- if (_fileChunks.size() > 1) {
+ const bool doCompact = (_fileChunks.size() > 1);
+ if (doCompact) {
LOG(info, "%s. Will compact", bloatMsg(bloat, usage).c_str());
compactWorst(_config.getMaxDiskBloatFactor(), _config.getMaxBucketSpread());
+ }
+ flushActiveAndWait(syncToken);
+ if (doCompact) {
usage = getDiskFootprint();
bloat = getDiskBloat();
LOG(info, "Done compacting. %s", bloatMsg(bloat, usage).c_str());
}
-
- flushActiveAndWait(syncToken);
}
size_t
diff --git a/searchlib/src/vespa/searchlib/docstore/logdocumentstore.cpp b/searchlib/src/vespa/searchlib/docstore/logdocumentstore.cpp
index e2b29f6bdd6..c285d4323c2 100644
--- a/searchlib/src/vespa/searchlib/docstore/logdocumentstore.cpp
+++ b/searchlib/src/vespa/searchlib/docstore/logdocumentstore.cpp
@@ -26,7 +26,7 @@ LogDocumentStore::LogDocumentStore(vespalib::ThreadExecutor & executor,
tuneFileSummary, fileHeaderContext, tlSyncer, bucketizer)
{}
-LogDocumentStore::~LogDocumentStore() {}
+LogDocumentStore::~LogDocumentStore() = default;
void
LogDocumentStore::reconfigure(const Config & config) {
diff --git a/searchlib/src/vespa/searchlib/features/CMakeLists.txt b/searchlib/src/vespa/searchlib/features/CMakeLists.txt
index bd847fe35b5..16401a67424 100644
--- a/searchlib/src/vespa/searchlib/features/CMakeLists.txt
+++ b/searchlib/src/vespa/searchlib/features/CMakeLists.txt
@@ -44,6 +44,7 @@ vespa_add_library(searchlib_features OBJECT
queryterm.cpp
querytermcountfeature.cpp
random_normal_feature.cpp
+ random_normal_stable_feature.cpp
randomfeature.cpp
rankingexpressionfeature.cpp
raw_score_feature.cpp
diff --git a/searchlib/src/vespa/searchlib/features/fieldmatch/computer.h b/searchlib/src/vespa/searchlib/features/fieldmatch/computer.h
index ff13c8e1a51..8d1c035e311 100644
--- a/searchlib/src/vespa/searchlib/features/fieldmatch/computer.h
+++ b/searchlib/src/vespa/searchlib/features/fieldmatch/computer.h
@@ -53,7 +53,7 @@ namespace fieldmatch {
* <p>This class is not multithread safe, but is reusable across queries for a single thread.</p>
*
* @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a>
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class Computer {
diff --git a/searchlib/src/vespa/searchlib/features/fieldmatch/metrics.h b/searchlib/src/vespa/searchlib/features/fieldmatch/metrics.h
index d97f574be9d..710211b3895 100644
--- a/searchlib/src/vespa/searchlib/features/fieldmatch/metrics.h
+++ b/searchlib/src/vespa/searchlib/features/fieldmatch/metrics.h
@@ -15,7 +15,7 @@ class Computer;
* The collection of metrics calculated by the string match metric calculator.
*
* @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a>
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class Metrics {
diff --git a/searchlib/src/vespa/searchlib/features/fieldmatch/params.h b/searchlib/src/vespa/searchlib/features/fieldmatch/params.h
index cf56ff9f1f5..21c35f19472 100644
--- a/searchlib/src/vespa/searchlib/features/fieldmatch/params.h
+++ b/searchlib/src/vespa/searchlib/features/fieldmatch/params.h
@@ -13,7 +13,7 @@ namespace fieldmatch {
* The parameters to a string match metric calculator.
*
* @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a>
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class Params {
diff --git a/searchlib/src/vespa/searchlib/features/fieldmatch/segmentstart.h b/searchlib/src/vespa/searchlib/features/fieldmatch/segmentstart.h
index 0c8b27bfc3f..dd4b9406036 100644
--- a/searchlib/src/vespa/searchlib/features/fieldmatch/segmentstart.h
+++ b/searchlib/src/vespa/searchlib/features/fieldmatch/segmentstart.h
@@ -17,7 +17,7 @@ namespace fieldmatch {
* starting point (skipI and previousJ).</p>
*
* @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a>
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
* @version $Id$
*/
class SegmentStart {
diff --git a/searchlib/src/vespa/searchlib/features/internal_max_reduce_prod_join_feature.cpp b/searchlib/src/vespa/searchlib/features/internal_max_reduce_prod_join_feature.cpp
index 90451c01294..9d383e5a03a 100644
--- a/searchlib/src/vespa/searchlib/features/internal_max_reduce_prod_join_feature.cpp
+++ b/searchlib/src/vespa/searchlib/features/internal_max_reduce_prod_join_feature.cpp
@@ -3,16 +3,16 @@
#include "internal_max_reduce_prod_join_feature.h"
#include "valuefeature.h"
#include "weighted_set_parser.h"
+#include "dotproductfeature.h"
-#include <vespa/log/log.h>
#include <vespa/searchlib/attribute/attribute.h>
#include <vespa/searchlib/attribute/imported_attribute_vector_read_guard.h>
#include <vespa/searchlib/attribute/multinumericattribute.h>
-#include <vespa/searchlib/features/dotproductfeature.h>
#include <vespa/searchlib/fef/properties.h>
#include <vespa/searchlib/fef/featureexecutor.h>
#include <vespa/searchcommon/common/datatype.h>
+#include <vespa/log/log.h>
LOG_SETUP(".features.internalmaxreduceprodjoin");
using namespace search::attribute;
@@ -20,8 +20,7 @@ using namespace search::fef;
using search::features::dotproduct::wset::IntegerVector;
-namespace search {
-namespace features {
+namespace search::features {
/**
* Executor used when array can be accessed directly
@@ -38,8 +37,7 @@ public:
};
template <typename BaseType>
-RawExecutor<BaseType>::RawExecutor(const IAttributeVector *attribute,
- const IntegerVector &queryVector) :
+RawExecutor<BaseType>::RawExecutor(const IAttributeVector *attribute, const IntegerVector &queryVector) :
FeatureExecutor(),
_attribute(attribute),
_queryVector(queryVector)
@@ -69,7 +67,7 @@ RawExecutor<BaseType>::execute(uint32_t docId)
{
using A = IntegerAttributeTemplate<BaseType>;
const multivalue::Value<BaseType> *values(nullptr);
- const A *iattr = dynamic_cast<const A *>(_attribute);
+ const A *iattr = static_cast<const A *>(_attribute);
size_t count = iattr->getRawValues(docId, values);
outputs().set_number(0, maxProduct(values, count, _queryVector));
}
@@ -215,8 +213,4 @@ InternalMaxReduceProdJoinBlueprint::createExecutor(const IQueryEnvironment &env,
return stash.create<SingleZeroValueExecutor>();
}
-
}
-}
-
-
diff --git a/searchlib/src/vespa/searchlib/features/internal_max_reduce_prod_join_feature.h b/searchlib/src/vespa/searchlib/features/internal_max_reduce_prod_join_feature.h
index 2c576b58345..65dd0ac2082 100644
--- a/searchlib/src/vespa/searchlib/features/internal_max_reduce_prod_join_feature.h
+++ b/searchlib/src/vespa/searchlib/features/internal_max_reduce_prod_join_feature.h
@@ -4,8 +4,7 @@
#include <vespa/searchlib/fef/blueprint.h>
-namespace search {
-namespace features {
+namespace search::features {
/**
* Feature for the specific replacement of the expression:
@@ -41,4 +40,3 @@ public:
};
}
-}
diff --git a/searchlib/src/vespa/searchlib/features/native_dot_product_feature.cpp b/searchlib/src/vespa/searchlib/features/native_dot_product_feature.cpp
index cb8136e8b7f..7865e32849f 100644
--- a/searchlib/src/vespa/searchlib/features/native_dot_product_feature.cpp
+++ b/searchlib/src/vespa/searchlib/features/native_dot_product_feature.cpp
@@ -8,6 +8,23 @@ using namespace search::fef;
namespace search {
namespace features {
+NativeDotProductExecutor::NativeDotProductExecutor(const search::fef::IQueryEnvironment &env)
+ : FeatureExecutor(),
+ _pairs(),
+ _md(nullptr)
+{
+ for (uint32_t i = 0; i < env.getNumTerms(); ++i) {
+ const search::fef::ITermData *td = env.getTerm(i);
+ auto weight = td->getWeight();
+ for (size_t f = 0; f < td->numFields(); ++f) {
+ auto handle = td->field(f).getHandle();
+ if (handle != search::fef::IllegalHandle) {
+ _pairs.emplace_back(handle, weight);
+ }
+ }
+ }
+}
+
NativeDotProductExecutor::NativeDotProductExecutor(const search::fef::IQueryEnvironment &env, uint32_t fieldId)
: FeatureExecutor(),
_pairs(),
@@ -46,15 +63,21 @@ bool
NativeDotProductBlueprint::setup(const IIndexEnvironment &,
const ParameterList &params)
{
- _field = params[0].asField();
- describeOutput("out", "dot product between query term weights and match weights for the given field");
+ if (params.size() > 0) {
+ _field = params[0].asField();
+ }
+ describeOutput("out", "dot product between query term weights and match weights");
return true;
}
FeatureExecutor &
NativeDotProductBlueprint::createExecutor(const IQueryEnvironment &queryEnv, vespalib::Stash &stash) const
{
- return stash.create<NativeDotProductExecutor>(queryEnv, _field->id());
+ if (_field) {
+ return stash.create<NativeDotProductExecutor>(queryEnv, _field->id());
+ } else {
+ return stash.create<NativeDotProductExecutor>(queryEnv);
+ }
}
} // namespace features
diff --git a/searchlib/src/vespa/searchlib/features/native_dot_product_feature.h b/searchlib/src/vespa/searchlib/features/native_dot_product_feature.h
index a71d23f3158..33c5c89c88b 100644
--- a/searchlib/src/vespa/searchlib/features/native_dot_product_feature.h
+++ b/searchlib/src/vespa/searchlib/features/native_dot_product_feature.h
@@ -20,6 +20,7 @@ private:
void handle_bind_match_data(const fef::MatchData &md) override;
public:
+ NativeDotProductExecutor(const fef::IQueryEnvironment &env);
NativeDotProductExecutor(const fef::IQueryEnvironment &env, uint32_t fieldId);
void execute(uint32_t docId) override;
};
@@ -31,13 +32,13 @@ class NativeDotProductBlueprint : public fef::Blueprint
private:
const fef::FieldInfo *_field;
public:
- NativeDotProductBlueprint() : Blueprint("nativeDotProduct"), _field(0) {}
+ NativeDotProductBlueprint() : Blueprint("nativeDotProduct"), _field(nullptr) {}
void visitDumpFeatures(const fef::IIndexEnvironment &, fef::IDumpFeatureVisitor &) const override {}
fef::Blueprint::UP createInstance() const override {
return Blueprint::UP(new NativeDotProductBlueprint());
}
fef::ParameterDescriptions getDescriptions() const override {
- return fef::ParameterDescriptions().desc().field();
+ return fef::ParameterDescriptions().desc().field().desc();
}
bool setup(const fef::IIndexEnvironment &env, const fef::ParameterList &params) override;
fef::FeatureExecutor &createExecutor(const fef::IQueryEnvironment &env, vespalib::Stash &stash) const override;
diff --git a/searchlib/src/vespa/searchlib/features/queryterm.h b/searchlib/src/vespa/searchlib/features/queryterm.h
index c162b041f38..75902c33022 100644
--- a/searchlib/src/vespa/searchlib/features/queryterm.h
+++ b/searchlib/src/vespa/searchlib/features/queryterm.h
@@ -6,8 +6,7 @@
#include <vespa/searchlib/fef/iqueryenvironment.h>
#include <vespa/searchlib/fef/itermdata.h>
-namespace search {
-namespace features {
+namespace search::features {
/**
* This class represents a query term with the relevant data. Now also
@@ -58,7 +57,4 @@ public:
bool lookupConnectedness = false);
};
-
-} // namespace features
-} // namespace search
-
+}
diff --git a/searchlib/src/vespa/searchlib/features/random_normal_feature.cpp b/searchlib/src/vespa/searchlib/features/random_normal_feature.cpp
index 67f30c4eb93..192fc968324 100644
--- a/searchlib/src/vespa/searchlib/features/random_normal_feature.cpp
+++ b/searchlib/src/vespa/searchlib/features/random_normal_feature.cpp
@@ -4,56 +4,26 @@
#include "utils.h"
#include <vespa/searchlib/fef/properties.h>
#include <vespa/fastos/time.h>
-#include <cmath>
#include <vespa/log/log.h>
LOG_SETUP(".features.randomnormalfeature");
-namespace search {
-namespace features {
+namespace search::features {
RandomNormalExecutor::RandomNormalExecutor(uint64_t seed, double mean, double stddev) :
search::fef::FeatureExecutor(),
- _rnd(),
- _mean(mean),
- _stddev(stddev),
- _hasSpare(false),
- _spare(0.0)
-
+ _rnd(mean, stddev, true)
{
LOG(debug, "RandomNormalExecutor: seed=%zu, mean=%f, stddev=%f", seed, mean, stddev);
- _rnd.srand48(seed);
+ _rnd.seed(seed);
}
-/**
- * Draws a random number from the Gaussian distribution
- * using the Marsaglia polar method.
- */
void
RandomNormalExecutor::execute(uint32_t)
{
- feature_t result = _spare;
- if (_hasSpare) {
- _hasSpare = false;
- } else {
- _hasSpare = true;
-
- feature_t u, v, s;
- do {
- u = (_rnd.lrand48() / (feature_t)0x80000000u) * 2.0 - 1.0;
- v = (_rnd.lrand48() / (feature_t)0x80000000u) * 2.0 - 1.0;
- s = u * u + v * v;
- } while ( (s >= 1.0) || (s == 0.0) );
- s = std::sqrt(-2.0 * std::log(s) / s);
-
- _spare = v * s; // saved for next invocation
- result = u * s;
- }
-
- outputs().set_number(0, _mean + _stddev * result);
+ outputs().set_number(0, _rnd.next());
}
-
RandomNormalBlueprint::RandomNormalBlueprint() :
search::fef::Blueprint("randomNormal"),
_seed(0),
@@ -82,7 +52,6 @@ RandomNormalBlueprint::setup(const search::fef::IIndexEnvironment & env,
if (p.found()) {
_seed = util::strToNum<uint64_t>(p.get());
}
-
if (params.size() > 0) {
_mean = params[0].asDouble();
}
@@ -108,6 +77,4 @@ RandomNormalBlueprint::createExecutor(const search::fef::IQueryEnvironment &, ve
return stash.create<RandomNormalExecutor>(seed, _mean, _stddev);
}
-
-} // namespace features
-} // namespace search
+}
diff --git a/searchlib/src/vespa/searchlib/features/random_normal_feature.h b/searchlib/src/vespa/searchlib/features/random_normal_feature.h
index 92aa3117b91..adf3d3be63f 100644
--- a/searchlib/src/vespa/searchlib/features/random_normal_feature.h
+++ b/searchlib/src/vespa/searchlib/features/random_normal_feature.h
@@ -4,10 +4,9 @@
#include <vespa/searchlib/fef/blueprint.h>
#include <vespa/searchlib/fef/featureexecutor.h>
-#include <vespa/searchlib/util/rand48.h>
+#include <vespa/searchlib/util/random_normal.h>
-namespace search {
-namespace features {
+namespace search::features {
/**
@@ -17,12 +16,7 @@ namespace features {
**/
class RandomNormalExecutor : public fef::FeatureExecutor {
private:
- Rand48 _rnd;
- double _mean;
- double _stddev;
-
- bool _hasSpare;
- double _spare;
+ RandomNormal _rnd; // seeded once per query
public:
RandomNormalExecutor(uint64_t seed, double mean, double stddev);
@@ -65,7 +59,4 @@ public:
fef::FeatureExecutor &createExecutor(const fef::IQueryEnvironment &env, vespalib::Stash &stash) const override;
};
-
-} // namespace features
-} // namespace search
-
+}
diff --git a/searchlib/src/vespa/searchlib/features/random_normal_stable_feature.cpp b/searchlib/src/vespa/searchlib/features/random_normal_stable_feature.cpp
new file mode 100644
index 00000000000..f760f52ee88
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/features/random_normal_stable_feature.cpp
@@ -0,0 +1,80 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "random_normal_stable_feature.h"
+#include "utils.h"
+#include <vespa/searchlib/fef/properties.h>
+#include <vespa/fastos/time.h>
+
+#include <vespa/log/log.h>
+LOG_SETUP(".features.randomnormalstablefeature");
+
+namespace search::features {
+
+RandomNormalStableExecutor::RandomNormalStableExecutor(uint64_t seed, double mean, double stddev) :
+ search::fef::FeatureExecutor(),
+ _rnd(mean, stddev, false), // don't use spares, as we reset seed on every generation
+ _seed(seed)
+{
+ LOG(debug, "RandomNormalStableExecutor: seed=%zu, mean=%f, stddev=%f", seed, mean, stddev);
+}
+
+void
+RandomNormalStableExecutor::execute(uint32_t docId)
+{
+ _rnd.seed(_seed + docId);
+ outputs().set_number(0, _rnd.next());
+}
+
+RandomNormalStableBlueprint::RandomNormalStableBlueprint() :
+ search::fef::Blueprint("randomNormalStable"),
+ _seed(0),
+ _mean(0.0),
+ _stddev(1.0)
+{
+}
+
+void
+RandomNormalStableBlueprint::visitDumpFeatures(const search::fef::IIndexEnvironment &,
+ search::fef::IDumpFeatureVisitor &) const
+{
+}
+
+search::fef::Blueprint::UP
+RandomNormalStableBlueprint::createInstance() const
+{
+ return search::fef::Blueprint::UP(new RandomNormalStableBlueprint());
+}
+
+bool
+RandomNormalStableBlueprint::setup(const search::fef::IIndexEnvironment & env,
+ const search::fef::ParameterList & params)
+{
+ search::fef::Property p = env.getProperties().lookup(getName(), "seed");
+ if (p.found()) {
+ _seed = util::strToNum<uint64_t>(p.get());
+ }
+ if (params.size() > 0) {
+ _mean = params[0].asDouble();
+ }
+ if (params.size() > 1) {
+ _stddev = params[1].asDouble();
+ }
+
+ describeOutput("out" , "A random value drawn from the Gaussian distribution that is stable for a given match (document and query)");
+
+ return true;
+}
+
+search::fef::FeatureExecutor &
+RandomNormalStableBlueprint::createExecutor(const search::fef::IQueryEnvironment &env, vespalib::Stash &stash) const
+{
+ uint64_t seed = _seed;
+ if (seed == 0) {
+ seed = util::strToNum<uint64_t>
+ (env.getProperties().lookup(getName(), "seed").get("1024")); // default seed
+ }
+ return stash.create<RandomNormalStableExecutor>(seed, _mean, _stddev);
+}
+
+
+}
diff --git a/searchlib/src/vespa/searchlib/features/random_normal_stable_feature.h b/searchlib/src/vespa/searchlib/features/random_normal_stable_feature.h
new file mode 100644
index 00000000000..ab9a53a2df3
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/features/random_normal_stable_feature.h
@@ -0,0 +1,63 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/searchlib/fef/blueprint.h>
+#include <vespa/searchlib/fef/featureexecutor.h>
+#include <vespa/searchlib/util/random_normal.h>
+
+namespace search::features {
+
+/**
+ * Implements the executor for the random normal feature outputting a
+ * random number drawn from the Gaussian distribution with the
+ * two arguments 'mean' and 'stddev'.
+ * The same hit always returns the same random number.
+ **/
+class RandomNormalStableExecutor : public fef::FeatureExecutor {
+private:
+ RandomNormal _rnd; // seeded once per match
+ uint64_t _seed;
+
+public:
+ RandomNormalStableExecutor(uint64_t seed, double mean, double stddev);
+ void execute(uint32_t docId) override;
+};
+
+
+/**
+ * Implements the blueprint for the random normal stable feature.
+ */
+class RandomNormalStableBlueprint : public fef::Blueprint {
+private:
+ uint64_t _seed;
+ double _mean;
+ double _stddev;
+
+public:
+ RandomNormalStableBlueprint();
+
+ void visitDumpFeatures(const fef::IIndexEnvironment & env, fef::IDumpFeatureVisitor & visitor) const override;
+ fef::Blueprint::UP createInstance() const override;
+ fef::ParameterDescriptions getDescriptions() const override {
+ return fef::ParameterDescriptions().
+ // Can run without parameters:
+ desc().
+
+ // Can run with two parameters (mean and stddev):
+ desc().
+ number(). // mean
+ number(). // stddev
+
+ // Can run with three parameters:
+ desc().
+ number(). // mean
+ number(). // stddev
+ string(); // in order to name different features
+ }
+
+ bool setup(const fef::IIndexEnvironment & env, const fef::ParameterList & params) override;
+ fef::FeatureExecutor &createExecutor(const fef::IQueryEnvironment &env, vespalib::Stash &stash) const override;
+};
+
+}
diff --git a/searchlib/src/vespa/searchlib/features/randomfeature.cpp b/searchlib/src/vespa/searchlib/features/randomfeature.cpp
index 937fdb23800..cdedbcadc5e 100644
--- a/searchlib/src/vespa/searchlib/features/randomfeature.cpp
+++ b/searchlib/src/vespa/searchlib/features/randomfeature.cpp
@@ -9,8 +9,7 @@
#include <vespa/log/log.h>
LOG_SETUP(".features.randomfeature");
-namespace search {
-namespace features {
+namespace search::features {
RandomExecutor::RandomExecutor(uint64_t seed, uint64_t matchSeed) :
search::fef::FeatureExecutor(),
@@ -81,5 +80,4 @@ RandomBlueprint::createExecutor(const search::fef::IQueryEnvironment &env, vespa
}
-} // namespace features
-} // namespace search
+}
diff --git a/searchlib/src/vespa/searchlib/features/randomfeature.h b/searchlib/src/vespa/searchlib/features/randomfeature.h
index f326606009c..8ed0e403836 100644
--- a/searchlib/src/vespa/searchlib/features/randomfeature.h
+++ b/searchlib/src/vespa/searchlib/features/randomfeature.h
@@ -6,8 +6,7 @@
#include <vespa/searchlib/fef/featureexecutor.h>
#include <vespa/searchlib/util/rand48.h>
-namespace search {
-namespace features {
+namespace search::features {
/**
* Implements the executor for the random feature outputting a number in the interval [0, 1>.
@@ -23,7 +22,6 @@ public:
void execute(uint32_t docId) override;
};
-
/**
* Implements the blueprint for the random feature.
*/
@@ -45,6 +43,4 @@ public:
search::fef::FeatureExecutor &createExecutor(const search::fef::IQueryEnvironment &env, vespalib::Stash &stash) const override;
};
-
-} // namespace features
-} // namespace search
+}
diff --git a/searchlib/src/vespa/searchlib/features/rankingexpressionfeature.cpp b/searchlib/src/vespa/searchlib/features/rankingexpressionfeature.cpp
index 4640170e6b9..72865d042e7 100644
--- a/searchlib/src/vespa/searchlib/features/rankingexpressionfeature.cpp
+++ b/searchlib/src/vespa/searchlib/features/rankingexpressionfeature.cpp
@@ -26,8 +26,7 @@ using search::fef::FeatureType;
using vespalib::ArrayRef;
using vespalib::ConstArrayRef;
-namespace search {
-namespace features {
+namespace search::features {
namespace {
@@ -181,9 +180,7 @@ RankingExpressionBlueprint::RankingExpressionBlueprint(rankingexpression::Expres
{
}
-RankingExpressionBlueprint::~RankingExpressionBlueprint()
-{
-}
+RankingExpressionBlueprint::~RankingExpressionBlueprint() = default;
void
RankingExpressionBlueprint::visitDumpFeatures(const fef::IIndexEnvironment &,
@@ -305,5 +302,4 @@ RankingExpressionBlueprint::createExecutor(const fef::IQueryEnvironment &env, ve
//-----------------------------------------------------------------------------
-} // features
-} // search
+}
diff --git a/searchlib/src/vespa/searchlib/features/rankingexpressionfeature.h b/searchlib/src/vespa/searchlib/features/rankingexpressionfeature.h
index 5da6c9eae87..85514a4def7 100644
--- a/searchlib/src/vespa/searchlib/features/rankingexpressionfeature.h
+++ b/searchlib/src/vespa/searchlib/features/rankingexpressionfeature.h
@@ -8,8 +8,7 @@
#include <vespa/searchlib/features/rankingexpression/expression_replacer.h>
#include <vespa/searchlib/features/rankingexpression/intrinsic_expression.h>
-namespace search {
-namespace features {
+namespace search::features {
//-----------------------------------------------------------------------------
@@ -42,7 +41,4 @@ public:
fef::FeatureExecutor &createExecutor(const fef::IQueryEnvironment &env, vespalib::Stash &stash) const override;
};
-//-----------------------------------------------------------------------------
-
-} // features
-} // search
+}
diff --git a/searchlib/src/vespa/searchlib/features/raw_score_feature.cpp b/searchlib/src/vespa/searchlib/features/raw_score_feature.cpp
index 02e44e781d4..61355581214 100644
--- a/searchlib/src/vespa/searchlib/features/raw_score_feature.cpp
+++ b/searchlib/src/vespa/searchlib/features/raw_score_feature.cpp
@@ -5,14 +5,14 @@
using namespace search::fef;
-namespace search {
-namespace features {
+namespace search::features {
RawScoreExecutor::RawScoreExecutor(const search::fef::IQueryEnvironment &env, uint32_t fieldId)
: FeatureExecutor(),
_handles(),
_md(nullptr)
{
+ _handles.reserve(env.getNumTerms());
for (uint32_t i = 0; i < env.getNumTerms(); ++i) {
search::fef::TermFieldHandle handle = util::getTermFieldHandle(env, i, fieldId);
if (handle != search::fef::IllegalHandle) {
@@ -25,8 +25,8 @@ void
RawScoreExecutor::execute(uint32_t docId)
{
feature_t output = 0.0;
- for (uint32_t i = 0; i < _handles.size(); ++i) {
- const TermFieldMatchData *tfmd = _md->resolveTermField(_handles[i]);
+ for (auto handle : _handles) {
+ const TermFieldMatchData *tfmd = _md->resolveTermField(handle);
if (tfmd->getDocId() == docId) {
output += tfmd->getRawScore();
}
@@ -43,8 +43,7 @@ RawScoreExecutor::handle_bind_match_data(const fef::MatchData &md)
//-----------------------------------------------------------------------------
bool
-RawScoreBlueprint::setup(const IIndexEnvironment &,
- const ParameterList &params)
+RawScoreBlueprint::setup(const IIndexEnvironment &, const ParameterList &params)
{
_field = params[0].asField();
describeOutput("out", "accumulated raw score for the given field");
@@ -57,5 +56,4 @@ RawScoreBlueprint::createExecutor(const IQueryEnvironment &queryEnv, vespalib::S
return stash.create<RawScoreExecutor>(queryEnv, _field->id());
}
-} // namespace features
-} // namespace search
+}
diff --git a/searchlib/src/vespa/searchlib/features/raw_score_feature.h b/searchlib/src/vespa/searchlib/features/raw_score_feature.h
index 0eceba16ffe..db237f036c4 100644
--- a/searchlib/src/vespa/searchlib/features/raw_score_feature.h
+++ b/searchlib/src/vespa/searchlib/features/raw_score_feature.h
@@ -5,8 +5,7 @@
#include <vespa/searchlib/fef/blueprint.h>
#include <vespa/searchlib/fef/featureexecutor.h>
-namespace search {
-namespace features {
+namespace search::features {
class RawScoreExecutor : public fef::FeatureExecutor
{
@@ -40,5 +39,4 @@ public:
fef::FeatureExecutor &createExecutor(const fef::IQueryEnvironment &env, vespalib::Stash &stash) const override;
};
-} // namespace features
-} // namespace search
+}
diff --git a/searchlib/src/vespa/searchlib/features/setup.cpp b/searchlib/src/vespa/searchlib/features/setup.cpp
index 867f058931f..1d3c59f5b3d 100644
--- a/searchlib/src/vespa/searchlib/features/setup.cpp
+++ b/searchlib/src/vespa/searchlib/features/setup.cpp
@@ -38,6 +38,7 @@
#include "querytermcountfeature.h"
#include "randomfeature.h"
#include "random_normal_feature.h"
+#include "random_normal_stable_feature.h"
#include "rankingexpressionfeature.h"
#include "raw_score_feature.h"
#include "reverseproximityfeature.h"
@@ -100,6 +101,7 @@ void setup_search_features(fef::IBlueprintRegistry & registry)
registry.addPrototype(Blueprint::SP(new QueryTermCountBlueprint()));
registry.addPrototype(Blueprint::SP(new RandomBlueprint()));
registry.addPrototype(Blueprint::SP(new RandomNormalBlueprint()));
+ registry.addPrototype(Blueprint::SP(new RandomNormalStableBlueprint()));
registry.addPrototype(Blueprint::SP(new RawScoreBlueprint()));
registry.addPrototype(Blueprint::SP(new SubqueriesBlueprint));
registry.addPrototype(Blueprint::SP(new TensorFromLabelsBlueprint()));
diff --git a/searchlib/src/vespa/searchlib/fef/featureexecutor.h b/searchlib/src/vespa/searchlib/fef/featureexecutor.h
index c8219fada3b..dfc46230e18 100644
--- a/searchlib/src/vespa/searchlib/fef/featureexecutor.h
+++ b/searchlib/src/vespa/searchlib/fef/featureexecutor.h
@@ -4,12 +4,10 @@
#include "handle.h"
#include "matchdata.h"
-#include <memory>
#include "number_or_object.h"
#include <vespa/vespalib/util/arrayref.h>
-namespace search {
-namespace fef {
+namespace search::fef {
class FeatureExecutor;
@@ -181,8 +179,6 @@ vespalib::eval::Value::CREF FeatureExecutor::Inputs::get_object(size_t idx) cons
return _inputs[idx].as_object(_docid);
}
-} // namespace fef
-} // namespace search
-
+}
// LocalWords: param
diff --git a/searchlib/src/vespa/searchlib/fef/featurenamebuilder.cpp b/searchlib/src/vespa/searchlib/fef/featurenamebuilder.cpp
index ec00b7d2f90..0eba912fafd 100644
--- a/searchlib/src/vespa/searchlib/fef/featurenamebuilder.cpp
+++ b/searchlib/src/vespa/searchlib/fef/featurenamebuilder.cpp
@@ -136,7 +136,7 @@ FeatureNameBuilder::buildName() const
vespalib::string ret;
if (!_baseName.empty()) {
ret = _baseName;
- if (!_parameters.empty() > 0) {
+ if (!_parameters.empty()) {
ret += "(";
for (uint32_t i = 0; i < _parameters.size(); ++i) {
if (i > 0) {
diff --git a/searchlib/src/vespa/searchlib/fef/indexproperties.h b/searchlib/src/vespa/searchlib/fef/indexproperties.h
index a2a227b448c..38e0eca7548 100644
--- a/searchlib/src/vespa/searchlib/fef/indexproperties.h
+++ b/searchlib/src/vespa/searchlib/fef/indexproperties.h
@@ -6,10 +6,7 @@
#include <vector>
#include <vespa/searchlib/common/feature.h>
-namespace search {
-namespace fef {
-
-class Properties;
+namespace search::fef { class Properties; }
/**
* This namespace is a placeholder for several structs, each representing
@@ -19,7 +16,7 @@ class Properties;
* instance one should use the property names defined here to perform the lookup.
* If the property is not present the default value is used.
**/
-namespace indexproperties {
+namespace search::fef::indexproperties {
namespace eval {
@@ -357,8 +354,4 @@ struct QueryFeature {
} // namespace type
-
-} // namespace indexproperties
-} // namespace fef
-} // namespace search
-
+}
diff --git a/searchlib/src/vespa/searchlib/fef/iqueryenvironment.h b/searchlib/src/vespa/searchlib/fef/iqueryenvironment.h
index a6ed0058c66..a7f268e5c6b 100644
--- a/searchlib/src/vespa/searchlib/fef/iqueryenvironment.h
+++ b/searchlib/src/vespa/searchlib/fef/iqueryenvironment.h
@@ -3,11 +3,10 @@
#pragma once
#include "iindexenvironment.h"
+#include "objectstore.h"
#include <vespa/searchcommon/attribute/iattributecontext.h>
-#include <vespa/searchlib/fef/objectstore.h>
-namespace search {
-namespace fef {
+namespace search::fef {
class Location;
class Properties;
@@ -90,5 +89,4 @@ private:
ObjectStore _objectStore;
};
-} // namespace fef
-} // namespace search
+}
diff --git a/searchlib/src/vespa/searchlib/fef/itermdata.h b/searchlib/src/vespa/searchlib/fef/itermdata.h
index 94b4ed80cf0..95095dfaff0 100644
--- a/searchlib/src/vespa/searchlib/fef/itermdata.h
+++ b/searchlib/src/vespa/searchlib/fef/itermdata.h
@@ -6,8 +6,7 @@
#include <vespa/searchlib/query/weight.h>
#include <cstddef>
-namespace search {
-namespace fef {
+namespace search::fef {
/**
* Interface to static match data for a single unit (term/phrase/etc).
@@ -84,6 +83,4 @@ public:
void next() { ++_idx; }
};
-} // namespace fef
-} // namespace search
-
+}
diff --git a/searchlib/src/vespa/searchlib/fef/itermfielddata.h b/searchlib/src/vespa/searchlib/fef/itermfielddata.h
index a85998d8d52..bb4ab9c5285 100644
--- a/searchlib/src/vespa/searchlib/fef/itermfielddata.h
+++ b/searchlib/src/vespa/searchlib/fef/itermfielddata.h
@@ -4,8 +4,7 @@
#include "handle.h"
-namespace search {
-namespace fef {
+namespace search::fef {
/**
* Interface to information about a single field that is being
@@ -43,6 +42,4 @@ public:
virtual TermFieldHandle getHandle() const = 0;
};
-} // namespace fef
-} // namespace search
-
+}
diff --git a/searchlib/src/vespa/searchlib/fef/number_or_object.h b/searchlib/src/vespa/searchlib/fef/number_or_object.h
index 59de864961e..259a1622516 100644
--- a/searchlib/src/vespa/searchlib/fef/number_or_object.h
+++ b/searchlib/src/vespa/searchlib/fef/number_or_object.h
@@ -5,8 +5,7 @@
#include <vespa/searchlib/common/feature.h>
#include <vespa/eval/eval/value.h>
-namespace search {
-namespace fef {
+namespace search::fef {
/**
* Storage cell for values passed between feature executors in the
@@ -21,5 +20,4 @@ union NumberOrObject {
~NumberOrObject() {}
};
-} // namespace fef
-} // namespace search
+}
diff --git a/searchlib/src/vespa/searchlib/fef/objectstore.h b/searchlib/src/vespa/searchlib/fef/objectstore.h
index b85ce527b8f..a62ca66486c 100644
--- a/searchlib/src/vespa/searchlib/fef/objectstore.h
+++ b/searchlib/src/vespa/searchlib/fef/objectstore.h
@@ -3,8 +3,7 @@
#include <vespa/vespalib/stllike/hash_map.h>
-namespace search {
-namespace fef {
+namespace search::fef {
class Anything
{
@@ -34,4 +33,3 @@ private:
};
}
-}
diff --git a/searchlib/src/vespa/searchlib/fef/ranksetup.cpp b/searchlib/src/vespa/searchlib/fef/ranksetup.cpp
index 5a16b1b6ba8..eea9188632d 100644
--- a/searchlib/src/vespa/searchlib/fef/ranksetup.cpp
+++ b/searchlib/src/vespa/searchlib/fef/ranksetup.cpp
@@ -20,8 +20,7 @@ public:
};
} // namespace <unnamed>
-namespace search {
-namespace fef {
+namespace search::fef {
using namespace indexproperties;
@@ -61,7 +60,7 @@ RankSetup::RankSetup(const BlueprintFactory &factory, const IIndexEnvironment &i
_softTimeoutTailCost(0.1)
{ }
-RankSetup::~RankSetup() { }
+RankSetup::~RankSetup() = default;
void
RankSetup::configure()
@@ -190,5 +189,4 @@ RankSetup::prepareSharedState(const IQueryEnvironment &queryEnv, IObjectStore &o
}
}
-} // namespace fef
-} // namespace search
+}
diff --git a/searchlib/src/vespa/searchlib/fef/ranksetup.h b/searchlib/src/vespa/searchlib/fef/ranksetup.h
index cccb3d961d5..ef869c87740 100644
--- a/searchlib/src/vespa/searchlib/fef/ranksetup.h
+++ b/searchlib/src/vespa/searchlib/fef/ranksetup.h
@@ -8,8 +8,7 @@
#include "blueprintresolver.h"
#include "rank_program.h"
-namespace search {
-namespace fef {
+namespace search::fef {
/**
* A rank setup contains information about how initial and final rank
@@ -403,5 +402,4 @@ public:
void prepareSharedState(const IQueryEnvironment & queryEnv, IObjectStore & objectStore) const;
};
-} // namespace fef
-} // namespace search
+}
diff --git a/searchlib/src/vespa/searchlib/fef/termfieldmatchdata.cpp b/searchlib/src/vespa/searchlib/fef/termfieldmatchdata.cpp
index 7ff5dcf8b6c..c5c7d2dc574 100644
--- a/searchlib/src/vespa/searchlib/fef/termfieldmatchdata.cpp
+++ b/searchlib/src/vespa/searchlib/fef/termfieldmatchdata.cpp
@@ -40,7 +40,7 @@ TermFieldMatchData & TermFieldMatchData::operator = (const TermFieldMatchData &
TermFieldMatchData::~TermFieldMatchData()
{
if (isRawScore()) {
- } else if (isMultiPos()) {
+ } else if (allocated()) {
delete [] _data._positions._positions;
} else {
getFixed()->~TermFieldMatchDataPosition();
@@ -79,21 +79,30 @@ constexpr size_t MAX_ELEMS = std::numeric_limits<uint16_t>::max();
void
TermFieldMatchData::resizePositionVector(size_t sz)
{
+ assert(allocated());
+ assert(sz >= _sz);
size_t newSize(std::min(MAX_ELEMS, std::max(1ul, sz)));
TermFieldMatchDataPosition * n = new TermFieldMatchDataPosition[newSize];
+ for (size_t i(0); i < _data._positions._allocated; i++) {
+ n[i] = _data._positions._positions[i];
+ }
+ delete [] _data._positions._positions;
+ _data._positions._allocated = newSize;
+ _data._positions._positions = n;
+}
+
+void
+TermFieldMatchData::allocateVector()
+{
+ assert(_sz < 2);
+ assert(!allocated());
+ size_t newSize = 2;
+ TermFieldMatchDataPosition * n = new TermFieldMatchDataPosition[newSize];
if (_sz > 0) {
- if (isMultiPos()) {
- for (size_t i(0); i < _data._positions._allocated; i++) {
- n[i] = _data._positions._positions[i];
- }
- delete [] _data._positions._positions;
- } else {
- assert(_sz == 1);
- n[0] = *getFixed();
- _data._positions._maxElementLength = getFixed()->getElementLen();
- }
+ n[0] = *getFixed();
+ _data._positions._maxElementLength = getFixed()->getElementLen();
}
- _fieldId = _fieldId | 0x4000;
+ _fieldId = _fieldId | 0x4000; // set allocated() flag
_data._positions._allocated = newSize;
_data._positions._positions = n;
}
@@ -101,6 +110,7 @@ TermFieldMatchData::resizePositionVector(size_t sz)
void
TermFieldMatchData::appendPositionToAllocatedVector(const TermFieldMatchDataPosition &pos)
{
+ assert(allocated());
if (__builtin_expect(_sz >= _data._positions._allocated, false)) {
resizePositionVector(_sz*2);
}
diff --git a/searchlib/src/vespa/searchlib/fef/termfieldmatchdata.h b/searchlib/src/vespa/searchlib/fef/termfieldmatchdata.h
index 018af889557..a0a37d9d66c 100644
--- a/searchlib/src/vespa/searchlib/fef/termfieldmatchdata.h
+++ b/searchlib/src/vespa/searchlib/fef/termfieldmatchdata.h
@@ -47,6 +47,7 @@ private:
int32_t getElementWeight() const { return empty() ? 1 : allocated() ? getMultiple()->getElementWeight() : getFixed()->getElementWeight(); }
uint32_t getMaxElementLength() const { return empty() ? 0 : allocated() ? _data._positions._maxElementLength : getFixed()->getElementLen(); }
void appendPositionToAllocatedVector(const TermFieldMatchDataPosition &pos);
+ void allocateVector();
void resizePositionVector(size_t sz) __attribute__((noinline));
enum { FIELDID_MASK = 0x1fff};
@@ -66,6 +67,10 @@ public:
size_t capacity() const { return allocated() ? _data._positions._allocated : 1; }
void reservePositions(size_t sz) {
if (sz > capacity()) {
+ if (!allocated()) {
+ allocateVector();
+ if (sz <= capacity()) return;
+ }
resizePositionVector(sz);
}
}
@@ -224,11 +229,14 @@ public:
* @param pos low-level occurrence information
**/
TermFieldMatchData &appendPosition(const TermFieldMatchDataPosition &pos) {
- if (isMultiPos() || (_sz > 0)) {
- appendPositionToAllocatedVector(pos);
- } else {
+ if (_sz == 0 && !allocated()) {
_sz = 1;
new (_data._position) TermFieldMatchDataPosition(pos);
+ } else {
+ if (!allocated()) {
+ allocateVector();
+ }
+ appendPositionToAllocatedVector(pos);
}
return *this;
}
diff --git a/searchlib/src/vespa/searchlib/index/docbuilder.h b/searchlib/src/vespa/searchlib/index/docbuilder.h
index 1d170483783..73c60304f50 100644
--- a/searchlib/src/vespa/searchlib/index/docbuilder.h
+++ b/searchlib/src/vespa/searchlib/index/docbuilder.h
@@ -4,6 +4,7 @@
#include "doctypebuilder.h"
#include <vespa/document/datatype/datatypes.h>
+#include <vespa/document/repo/fixedtyperepo.h>
#include <vespa/document/fieldvalue/fieldvalues.h>
#include <vespa/document/annotation/annotation.h>
#include <vespa/document/annotation/span.h>
diff --git a/searchlib/src/vespa/searchlib/memoryindex/compact_document_words_store.cpp b/searchlib/src/vespa/searchlib/memoryindex/compact_document_words_store.cpp
index ef942986ca5..b7fb3307de6 100644
--- a/searchlib/src/vespa/searchlib/memoryindex/compact_document_words_store.cpp
+++ b/searchlib/src/vespa/searchlib/memoryindex/compact_document_words_store.cpp
@@ -143,7 +143,7 @@ CompactDocumentWordsStore::insert(const Builder &builder)
if (!insres.second) {
LOG(error, "Failed inserting remove info for docid %u",
builder.docId());
- abort();
+ LOG_ABORT("should not be reached");
}
}
diff --git a/searchlib/src/vespa/searchlib/memoryindex/dictionary.cpp b/searchlib/src/vespa/searchlib/memoryindex/dictionary.cpp
index 6a42570da53..accf227b96c 100644
--- a/searchlib/src/vespa/searchlib/memoryindex/dictionary.cpp
+++ b/searchlib/src/vespa/searchlib/memoryindex/dictionary.cpp
@@ -14,6 +14,9 @@
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/util/exceptions.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.memoryindex.dictionary");
+
namespace search {
diff --git a/searchlib/src/vespa/searchlib/memoryindex/memoryfieldindex.cpp b/searchlib/src/vespa/searchlib/memoryindex/memoryfieldindex.cpp
index f6c3e5b5ea6..733adef97da 100644
--- a/searchlib/src/vespa/searchlib/memoryindex/memoryfieldindex.cpp
+++ b/searchlib/src/vespa/searchlib/memoryindex/memoryfieldindex.cpp
@@ -3,9 +3,7 @@
#include "memoryfieldindex.h"
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/util/exceptions.h>
-
#include <vespa/searchlib/bitcompression/posocccompression.h>
-
#include <vespa/searchlib/btree/btreenode.hpp>
#include <vespa/searchlib/btree/btreenodeallocator.hpp>
#include <vespa/searchlib/btree/btreenodestore.hpp>
@@ -16,6 +14,9 @@
#include "ordereddocumentinserter.h"
#include <vespa/vespalib/util/array.hpp>
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.memoryindex.memory_field_index");
+
namespace search {
using index::DocIdAndFeatures;
diff --git a/searchlib/src/vespa/searchlib/memoryindex/ordereddocumentinserter.cpp b/searchlib/src/vespa/searchlib/memoryindex/ordereddocumentinserter.cpp
index 8cd74b7d630..b1e365c7fd7 100644
--- a/searchlib/src/vespa/searchlib/memoryindex/ordereddocumentinserter.cpp
+++ b/searchlib/src/vespa/searchlib/memoryindex/ordereddocumentinserter.cpp
@@ -16,6 +16,9 @@
#include <vespa/searchlib/btree/btreeroot.hpp>
#include <vespa/searchlib/btree/btree.hpp>
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.memoryindex.ordered_document_inserter");
+
namespace search::memoryindex {
namespace {
diff --git a/searchlib/src/vespa/searchlib/memoryindex/postingiterator.cpp b/searchlib/src/vespa/searchlib/memoryindex/postingiterator.cpp
index 4fdc164ecdb..1f55aa466b1 100644
--- a/searchlib/src/vespa/searchlib/memoryindex/postingiterator.cpp
+++ b/searchlib/src/vespa/searchlib/memoryindex/postingiterator.cpp
@@ -1,13 +1,15 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "postingiterator.h"
-
#include <vespa/searchlib/btree/btreenode.hpp>
#include <vespa/searchlib/btree/btreenodeallocator.hpp>
#include <vespa/searchlib/btree/btreenodestore.hpp>
#include <vespa/searchlib/btree/btreeiterator.hpp>
#include <vespa/searchlib/btree/btreeroot.hpp>
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.memoryindex.postingiterator");
+
namespace search {
namespace memoryindex {
diff --git a/searchlib/src/vespa/searchlib/parsequery/simplequerystack.cpp b/searchlib/src/vespa/searchlib/parsequery/simplequerystack.cpp
index 6f49dc62686..36d76629f87 100644
--- a/searchlib/src/vespa/searchlib/parsequery/simplequerystack.cpp
+++ b/searchlib/src/vespa/searchlib/parsequery/simplequerystack.cpp
@@ -159,6 +159,7 @@ SimpleQueryStack::StackbufToString(const vespalib::stringref &theBuf)
const char *p = theBuf.begin();
const char *ep = theBuf.end();
uint64_t tmp(0);
+ int64_t tmpI64(0);
uint8_t flags(0);
while (p < ep) {
vespalib::string metaStr;
@@ -248,22 +249,20 @@ SimpleQueryStack::StackbufToString(const vespalib::stringref &theBuf)
termRef = p;
p += termRefLen;
result.append(make_string("%c/%d:%.*s/%d:%.*s~", _G_ItemName[type],
- idxRefLen, idxRefLen, idxRef,
- termRefLen, termRefLen, termRef));
+ idxRefLen, idxRefLen, idxRef, termRefLen, termRefLen, termRef));
break;
case ParseItem::ITEM_PURE_WEIGHTED_STRING:
p += vespalib::compress::Integer::decompressPositive(tmp, p);
termRefLen = tmp;
termRef = p;
p += termRefLen;
- result.append(make_string("%c/%d:%.*s~", _G_ItemName[type],
- termRefLen, termRefLen, termRef));
+ result.append(make_string("%c/%d:%.*s~", _G_ItemName[type], termRefLen, termRefLen, termRef));
break;
case ParseItem::ITEM_PURE_WEIGHTED_LONG:
- tmp = vespalib::nbo::n2h(*reinterpret_cast<const uint64_t *>(p));
+ tmpI64 = vespalib::nbo::n2h(*reinterpret_cast<const int64_t *>(p));
p += sizeof(uint64_t);
- result.append(make_string("%c/%lu", _G_ItemName[type], tmp));
+ result.append(make_string("%c/%" PRId64, _G_ItemName[type], tmpI64));
break;
case ParseItem::ITEM_PHRASE:
@@ -281,13 +280,12 @@ SimpleQueryStack::StackbufToString(const vespalib::stringref &theBuf)
uint32_t targetNumHits = tmp;
double scoreThreshold = vespalib::nbo::n2h(*reinterpret_cast<const double *>(p));
p += sizeof(double);
- double thresholdBoostFactor = vespalib::nbo::n2h(*reinterpret_cast<const double *>(p)); // thresholdBoostFactor
+ double thresholdBoostFactor = vespalib::nbo::n2h(*reinterpret_cast<const double *>(p));
p += sizeof(double);
result.append(make_string("%c/%d/%d:%.*s(%u,%f,%f)~", _G_ItemName[type], arity, idxRefLen,
- idxRefLen, idxRef, targetNumHits, scoreThreshold, thresholdBoostFactor));
+ idxRefLen, idxRef, targetNumHits, scoreThreshold, thresholdBoostFactor));
} else {
- result.append(make_string("%c/%d/%d:%.*s~", _G_ItemName[type], arity, idxRefLen,
- idxRefLen, idxRef));
+ result.append(make_string("%c/%d/%d:%.*s~", _G_ItemName[type], arity, idxRefLen, idxRefLen, idxRef));
}
break;
@@ -297,8 +295,7 @@ SimpleQueryStack::StackbufToString(const vespalib::stringref &theBuf)
idxRef = p;
p += idxRefLen;
size_t feature_count = ReadCompressedPositiveInt(p);
- result.append(make_string(
- "%c/%d:%.*s/%zu(", _G_ItemName[type], idxRefLen, idxRefLen, idxRef, feature_count));
+ result.append(make_string("%c/%d:%.*s/%zu(", _G_ItemName[type], idxRefLen, idxRefLen, idxRef, feature_count));
for (size_t i = 0; i < feature_count; ++i) {
vespalib::string key = ReadString(p);
vespalib::string value = ReadString(p);
@@ -326,7 +323,7 @@ SimpleQueryStack::StackbufToString(const vespalib::stringref &theBuf)
default:
LOG(error, "Unhandled type %d", type);
- abort();
+ LOG_ABORT("should not be reached");
}
}
return result;
diff --git a/searchlib/src/vespa/searchlib/parsequery/stackdumpiterator.cpp b/searchlib/src/vespa/searchlib/parsequery/stackdumpiterator.cpp
index 542e7990c1e..1938222b4ac 100644
--- a/searchlib/src/vespa/searchlib/parsequery/stackdumpiterator.cpp
+++ b/searchlib/src/vespa/searchlib/parsequery/stackdumpiterator.cpp
@@ -25,9 +25,9 @@ SimpleQueryStackDumpIterator::SimpleQueryStackDumpIterator(const vespalib::strin
_currArg2(0),
_currArg3(0),
_predicate_query_term(),
- _currIndexName(NULL),
+ _currIndexName(nullptr),
_currIndexNameLen(0),
- _currTerm(NULL),
+ _currTerm(nullptr),
_currTermLen(0),
_generatedTerm(),
_currNum(-1)
@@ -114,9 +114,9 @@ SimpleQueryStackDumpIterator::next()
_currArity = tmp;
if (p > _bufEnd) return false;
_currArg1 = 0;
- _currIndexName = NULL;
+ _currIndexName = nullptr;
_currIndexNameLen = 0;
- _currTerm = NULL;
+ _currTerm = nullptr;
_currTermLen = 0;
break;
@@ -129,9 +129,9 @@ SimpleQueryStackDumpIterator::next()
p += vespalib::compress::Integer::decompressPositive(tmp, p);
_currArg1 = tmp;
if (p > _bufEnd) return false;
- _currIndexName = NULL;
+ _currIndexName = nullptr;
_currIndexNameLen = 0;
- _currTerm = NULL;
+ _currTerm = nullptr;
_currTermLen = 0;
break;
@@ -149,7 +149,7 @@ SimpleQueryStackDumpIterator::next()
_currIndexName = p;
p += _currIndexNameLen;
if (p > _bufEnd) return false;
- _currTerm = NULL;
+ _currTerm = nullptr;
_currTermLen = 0;
break;
case ParseItem::ITEM_SAME_ELEMENT:
@@ -163,7 +163,7 @@ SimpleQueryStackDumpIterator::next()
_currIndexName = p;
p += _currIndexNameLen;
if (p > _bufEnd) return false;
- _currTerm = NULL;
+ _currTerm = nullptr;
_currTermLen = 0;
break;
@@ -180,12 +180,12 @@ SimpleQueryStackDumpIterator::next()
_currArity = 0;
break;
case ParseItem::ITEM_PURE_WEIGHTED_LONG:
- if (p + 8 > _bufEnd) return false;
+ if (p + sizeof(int64_t) > _bufEnd) return false;
_generatedTerm.clear();
- _generatedTerm << vespalib::nbo::n2h(*(const uint64_t *)p);
+ _generatedTerm << vespalib::nbo::n2h(*reinterpret_cast<const int64_t *>(p));
_currTerm = _generatedTerm.c_str();
_currTermLen = _generatedTerm.size();
- p += 8;
+ p += sizeof(int64_t);
if (p > _bufEnd) return false;
_currArg1 = 0;
@@ -197,7 +197,7 @@ SimpleQueryStackDumpIterator::next()
_currIndexName = p;
p += _currIndexNameLen;
_currArity = readCompressedPositiveInt(p);
- _currTerm = NULL;
+ _currTerm = nullptr;
_currTermLen = 0;
if (p > _bufEnd) return false;
} catch (...) {
@@ -281,7 +281,7 @@ SimpleQueryStackDumpIterator::next()
} else {
_currArg1 = 0;
}
- _currTerm = NULL;
+ _currTerm = nullptr;
_currTermLen = 0;
break;
diff --git a/searchlib/src/vespa/searchlib/predicate/document_features_store.cpp b/searchlib/src/vespa/searchlib/predicate/document_features_store.cpp
index 44b13e6bb2a..b5d1d9ec1e0 100644
--- a/searchlib/src/vespa/searchlib/predicate/document_features_store.cpp
+++ b/searchlib/src/vespa/searchlib/predicate/document_features_store.cpp
@@ -10,6 +10,9 @@
#include <unordered_map>
#include <vector>
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.predicate.document_features_store");
+
using search::btree::BTreeNoLeafData;
using search::datastore::EntryRef;
using vespalib::DataBuffer;
diff --git a/searchlib/src/vespa/searchlib/predicate/predicate_index.cpp b/searchlib/src/vespa/searchlib/predicate/predicate_index.cpp
index 2275f079c04..1336f35628e 100644
--- a/searchlib/src/vespa/searchlib/predicate/predicate_index.cpp
+++ b/searchlib/src/vespa/searchlib/predicate/predicate_index.cpp
@@ -3,6 +3,9 @@
#include "predicate_index.h"
#include "predicate_hash.h"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.predicate.predicate_index");
+
using search::datastore::EntryRef;
using vespalib::DataBuffer;
using std::vector;
diff --git a/searchlib/src/vespa/searchlib/predicate/predicate_interval_store.cpp b/searchlib/src/vespa/searchlib/predicate/predicate_interval_store.cpp
index d2459e8d719..a0a8c690b5a 100644
--- a/searchlib/src/vespa/searchlib/predicate/predicate_interval_store.cpp
+++ b/searchlib/src/vespa/searchlib/predicate/predicate_interval_store.cpp
@@ -1,12 +1,14 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "predicate_interval_store.h"
-
#include "predicate_index.h"
#include <vespa/searchlib/datastore/bufferstate.h>
#include <vespa/searchlib/datastore/datastore.hpp>
#include <vespa/searchlib/datastore/entryref.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.predicate.predicate_interval_store");
+
using search::datastore::BufferState;
using search::datastore::EntryRef;
using std::vector;
diff --git a/searchlib/src/vespa/searchlib/predicate/predicate_tree_annotator.cpp b/searchlib/src/vespa/searchlib/predicate/predicate_tree_annotator.cpp
index ddd657cbb4d..3b395f155b8 100644
--- a/searchlib/src/vespa/searchlib/predicate/predicate_tree_annotator.cpp
+++ b/searchlib/src/vespa/searchlib/predicate/predicate_tree_annotator.cpp
@@ -7,7 +7,7 @@
#include <vespa/document/predicate/predicate.h>
#include <vespa/log/log.h>
-LOG_SETUP(".predicate_tree_annotator");
+LOG_SETUP(".searchlib.predicate.predicate_tree_annotator");
using document::Predicate;
using std::map;
diff --git a/searchlib/src/vespa/searchlib/predicate/predicate_zero_constraint_posting_list.cpp b/searchlib/src/vespa/searchlib/predicate/predicate_zero_constraint_posting_list.cpp
index 28b1e2ab877..757bc489179 100644
--- a/searchlib/src/vespa/searchlib/predicate/predicate_zero_constraint_posting_list.cpp
+++ b/searchlib/src/vespa/searchlib/predicate/predicate_zero_constraint_posting_list.cpp
@@ -2,6 +2,9 @@
#include "predicate_zero_constraint_posting_list.h"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.predicate.predicate_zero_constraint_posting_list");
+
namespace search {
namespace predicate {
diff --git a/searchlib/src/vespa/searchlib/predicate/simple_index.cpp b/searchlib/src/vespa/searchlib/predicate/simple_index.cpp
index d78d9d6533f..1ca8fd259c6 100644
--- a/searchlib/src/vespa/searchlib/predicate/simple_index.cpp
+++ b/searchlib/src/vespa/searchlib/predicate/simple_index.cpp
@@ -2,8 +2,9 @@
#include "simple_index.hpp"
#include <vespa/vespalib/util/array.hpp>
+
#include <vespa/log/log.h>
-LOG_SETUP(".searchlib.simple_index");
+LOG_SETUP(".searchlib.predicate.simple_index");
namespace search {
namespace predicate {
diff --git a/searchlib/src/vespa/searchlib/queryeval/andnotsearch.cpp b/searchlib/src/vespa/searchlib/queryeval/andnotsearch.cpp
index 77f8e454035..5646af9e677 100644
--- a/searchlib/src/vespa/searchlib/queryeval/andnotsearch.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/andnotsearch.cpp
@@ -4,8 +4,7 @@
#include "termwise_helper.h"
#include <vespa/searchlib/common/bitvector.h>
-namespace search {
-namespace queryeval {
+namespace search::queryeval {
void
AndNotSearch::doSeek(uint32_t docid)
@@ -160,5 +159,4 @@ AndNotSearch::or_hits_into(BitVector &result, uint32_t begin_id) {
result.orWith(*get_hits(begin_id));
}
-} // namespace queryeval
-} // namespace search
+}
diff --git a/searchlib/src/vespa/searchlib/queryeval/andnotsearch.h b/searchlib/src/vespa/searchlib/queryeval/andnotsearch.h
index e805448ef98..c29ef9407ad 100644
--- a/searchlib/src/vespa/searchlib/queryeval/andnotsearch.h
+++ b/searchlib/src/vespa/searchlib/queryeval/andnotsearch.h
@@ -2,13 +2,11 @@
#pragma once
-#include <vector>
#include "multisearch.h"
#include <vespa/searchlib/attribute/attributeiterators.h>
#include <vespa/searchlib/attribute/singlesmallnumericattribute.h>
-namespace search {
-namespace queryeval {
+namespace search::queryeval {
/**
* A simple implementation of the AndNot search operation.
@@ -97,6 +95,4 @@ private:
void doUnpack(uint32_t docid) override;
};
-} // namespace queryeval
-} // namespace search
-
+}
diff --git a/searchlib/src/vespa/searchlib/queryeval/blueprint.cpp b/searchlib/src/vespa/searchlib/queryeval/blueprint.cpp
index f9b8a9437dc..c99e07cd355 100644
--- a/searchlib/src/vespa/searchlib/queryeval/blueprint.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/blueprint.cpp
@@ -18,7 +18,7 @@ namespace search::queryeval {
void maybe_eliminate_self(Blueprint* &self, Blueprint::UP replacement) {
// replace with replacement
- if (replacement.get() != nullptr) {
+ if (replacement) {
Blueprint *tmp = replacement.release();
tmp->setParent(self->getParent());
tmp->setSourceId(self->getSourceId());
@@ -41,9 +41,9 @@ Blueprint::HitEstimate
Blueprint::max(const std::vector<HitEstimate> &data)
{
HitEstimate est;
- for (size_t i = 0; i < data.size(); ++i) {
- if (est.empty || est.estHits < data[i].estHits) {
- est = data[i];
+ for (const HitEstimate & hitEst : data) {
+ if (est.empty || est.estHits < hitEst.estHits) {
+ est = hitEst;
}
}
return est;
@@ -69,7 +69,7 @@ Blueprint::State::State(const FieldSpecBaseList &fields_in)
{
}
-Blueprint::State::~State() { }
+Blueprint::State::~State() = default;
Blueprint::Blueprint()
: _parent(0),
@@ -79,9 +79,7 @@ Blueprint::Blueprint()
{
}
-Blueprint::~Blueprint()
-{
-}
+Blueprint::~Blueprint() = default;
Blueprint::UP
Blueprint::optimize(Blueprint::UP bp) {
@@ -135,7 +133,6 @@ Blueprint::visitMembers(vespalib::ObjectVisitor &visitor) const
for (size_t i = 0; i < state.numFields(); ++i) {
const FieldSpecBase &spec = state.field(i);
visitor.openStruct(vespalib::make_string("[%zu]", i), "Field");
- // visitor.visitString("name", spec.getName());
visitor.visitInt("fieldId", spec.getFieldId());
visitor.visitInt("handle", spec.getHandle());
visitor.visitBool("isFilter", spec.isFilter());
@@ -180,8 +177,8 @@ void
IntermediateBlueprint::setDocIdLimit(uint32_t limit)
{
Blueprint::setDocIdLimit(limit);
- for (size_t i = 0; i < _children.size(); ++i) {
- _children[i]->setDocIdLimit(limit);
+ for (Blueprint * child : _children) {
+ child->setDocIdLimit(limit);
}
}
@@ -190,8 +187,8 @@ IntermediateBlueprint::calculateEstimate() const
{
std::vector<HitEstimate> estimates;
estimates.reserve(_children.size());
- for (size_t i = 0; i < _children.size(); ++i) {
- estimates.push_back(_children[i]->getState().estimate());
+ for (const Blueprint * child : _children) {
+ estimates.push_back(child->getState().estimate());
}
return combine(estimates);
}
@@ -200,8 +197,8 @@ uint32_t
IntermediateBlueprint::calculate_tree_size() const
{
uint32_t nodes = 1;
- for (size_t i = 0; i < _children.size(); ++i) {
- nodes += _children[i]->getState().tree_size();
+ for (const Blueprint * child : _children) {
+ nodes += child->getState().tree_size();
}
return nodes;
}
@@ -212,8 +209,8 @@ IntermediateBlueprint::infer_allow_termwise_eval() const
if (!supports_termwise_children()) {
return false;
}
- for (size_t i = 0; i < _children.size(); ++i) {
- if (!_children[i]->getState().allow_termwise_eval()) {
+ for (const Blueprint * child : _children) {
+ if (!child->getState().allow_termwise_eval()) {
return false;
}
}
@@ -255,8 +252,8 @@ IntermediateBlueprint::mixChildrenFields() const
Map fieldMap;
FieldSpecBaseList fieldList;
- for (size_t i = 0; i < _children.size(); ++i) {
- const State &childState = _children[i]->getState();
+ for (const Blueprint * child : _children) {
+ const State &childState = child->getState();
if (!childState.isTermLike()) {
return fieldList; // empty: non-term-like child
}
@@ -271,8 +268,8 @@ IntermediateBlueprint::mixChildrenFields() const
}
}
}
- for (MapPos pos = fieldMap.begin(); pos != fieldMap.end(); ++pos) {
- fieldList.add(*(pos->second));
+ for (const auto & entry : fieldMap) {
+ fieldList.add(*entry.second);
}
return fieldList;
}
@@ -306,8 +303,8 @@ IntermediateBlueprint::optimize(Blueprint* &self)
{
assert(self == this);
if (should_optimize_children()) {
- for (size_t i = 0; i < _children.size(); ++i) {
- _children[i]->optimize(_children[i]);
+ for (auto & child : _children) {
+ child->optimize(child);
}
}
optimize_self();
@@ -328,10 +325,7 @@ IntermediateBlueprint::createSearch(fef::MatchData &md, bool strict) const
return createIntermediateSearch(subSearches, strict, md);
}
-IntermediateBlueprint::IntermediateBlueprint()
- : _children()
-{
-}
+IntermediateBlueprint::IntermediateBlueprint() = default;
const Blueprint &
IntermediateBlueprint::getChild(size_t n) const
@@ -396,8 +390,8 @@ IntermediateBlueprint::fetchPostings(bool strict)
void
IntermediateBlueprint::freeze()
{
- for (size_t i = 0; i < _children.size(); ++i) {
- _children[i]->freeze();
+ for (Blueprint * child : _children) {
+ child->freeze();
}
freeze_self();
}
@@ -407,7 +401,7 @@ namespace {
bool
areAnyParentsEquiv(const Blueprint * node)
{
- return (node == NULL)
+ return (node == nullptr)
? false
: node->isEquiv()
? true
@@ -436,7 +430,8 @@ IntermediateBlueprint::calculateUnpackInfo(const fef::MatchData & md) const
const Blueprint & child = getChild(i);
const State &cs = child.getState();
bool canSkipUnpack(canBlueprintSkipUnpack(child, md));
- LOG(debug, "Child[%ld] has %ld fields. canSkipUnpack='%s'.", i, cs.numFields(), canSkipUnpack ? "true" : "false");
+ LOG(debug, "Child[%ld] has %ld fields. canSkipUnpack='%s'.",
+ i, cs.numFields(), canSkipUnpack ? "true" : "false");
for (size_t j = 0; canSkipUnpack && (j < cs.numFields()); ++j) {
if ( ! cs.field(j).resolve(md)->isNotNeeded()) {
LOG(debug, "Child[%ld].field(%ld).fieldId=%d need unpack.", i, j, cs.field(j).getFieldId());
@@ -469,7 +464,7 @@ LeafBlueprint::LeafBlueprint(const FieldSpecBaseList &fields, bool allow_termwis
_state.allow_termwise_eval(allow_termwise_eval);
}
-LeafBlueprint::~LeafBlueprint() { }
+LeafBlueprint::~LeafBlueprint() = default;
void
LeafBlueprint::fetchPostings(bool strict)
diff --git a/searchlib/src/vespa/searchlib/queryeval/blueprint.h b/searchlib/src/vespa/searchlib/queryeval/blueprint.h
index d9033693371..165f592867a 100644
--- a/searchlib/src/vespa/searchlib/queryeval/blueprint.h
+++ b/searchlib/src/vespa/searchlib/queryeval/blueprint.h
@@ -72,9 +72,9 @@ public:
size_t numFields() const { return _fields.size(); }
const FieldSpecBase &field(size_t idx) const { return _fields[idx]; }
const FieldSpecBase *lookupField(uint32_t fieldId) const {
- for (size_t i = 0; i < _fields.size(); ++i) {
- if (_fields[i].getFieldId() == fieldId) {
- return &_fields[i];
+ for (const FieldSpecBase & field : _fields) {
+ if (field.getFieldId() == fieldId) {
+ return &field;
}
}
return nullptr;
@@ -240,7 +240,7 @@ protected:
public:
typedef std::vector<size_t> IndexList;
IntermediateBlueprint();
- virtual ~IntermediateBlueprint();
+ ~IntermediateBlueprint() override;
void setDocIdLimit(uint32_t limit) override final;
@@ -285,7 +285,7 @@ protected:
LeafBlueprint(const FieldSpecBaseList &fields, bool allow_termwise_eval);
public:
- ~LeafBlueprint();
+ ~LeafBlueprint() override;
const State &getState() const override final { return _state; }
void setDocIdLimit(uint32_t limit) override final { Blueprint::setDocIdLimit(limit); }
void fetchPostings(bool strict) override;
diff --git a/searchlib/src/vespa/searchlib/queryeval/fake_search.cpp b/searchlib/src/vespa/searchlib/queryeval/fake_search.cpp
index 226f41a53c0..099e28f552a 100644
--- a/searchlib/src/vespa/searchlib/queryeval/fake_search.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/fake_search.cpp
@@ -4,10 +4,37 @@
#include <vespa/searchlib/fef/termfieldmatchdataposition.h>
#include <vespa/searchlib/fef/termfieldmatchdata.h>
#include <vespa/vespalib/objects/visit.h>
+#include <vespa/searchcommon/attribute/i_search_context.h>
namespace search {
namespace queryeval {
+namespace {
+
+struct FakeContext : search::attribute::ISearchContext {
+ int32_t onFind(DocId, int32_t, int32_t &) const override { return -1; }
+ int32_t onFind(DocId, int32_t) const override { return -1; }
+ unsigned int approximateHits() const override { return 0; }
+ std::unique_ptr<SearchIterator> createIterator(fef::TermFieldMatchData *, bool) override { abort(); }
+ void fetchPostings(bool) override { }
+ bool valid() const override { return true; }
+ search::Int64Range getAsIntegerTerm() const override { abort(); }
+ const search::QueryTermBase * queryTerm() const override { abort(); }
+ const vespalib::string &attributeName() const override { abort(); }
+};
+
+} // namespace search::queryeval::<unnamed>
+
+void
+FakeSearch::is_attr(bool value)
+{
+ if (value) {
+ _ctx = std::make_unique<FakeContext>();
+ } else {
+ _ctx.reset();
+ }
+}
+
void
FakeSearch::doSeek(uint32_t docid)
{
@@ -49,5 +76,5 @@ FakeSearch::visitMembers(vespalib::ObjectVisitor &visitor) const
visit(visitor, "term", _term);
}
-} // namespace queryeval
+} // namespace search::queryeval
} // namespace search
diff --git a/searchlib/src/vespa/searchlib/queryeval/fake_search.h b/searchlib/src/vespa/searchlib/queryeval/fake_search.h
index c320d497edc..aa6df480a21 100644
--- a/searchlib/src/vespa/searchlib/queryeval/fake_search.h
+++ b/searchlib/src/vespa/searchlib/queryeval/fake_search.h
@@ -5,6 +5,7 @@
#include "searchiterator.h"
#include "fake_result.h"
#include <vespa/searchlib/fef/termfieldmatchdataarray.h>
+#include <vespa/searchcommon/attribute/i_search_context.h>
namespace search {
namespace queryeval {
@@ -18,6 +19,7 @@ private:
FakeResult _result;
uint32_t _offset;
fef::TermFieldMatchDataArray _tfmda;
+ std::unique_ptr<attribute::ISearchContext> _ctx;
bool valid() const { return _offset < _result.inspect().size(); }
uint32_t currId() const { return _result.inspect()[_offset].docId; }
@@ -34,10 +36,12 @@ public:
{
assert(_tfmda.size() == 1);
}
+ void is_attr(bool value);
void doSeek(uint32_t docid) override;
void doUnpack(uint32_t docid) override;
const PostingInfo *getPostingInfo() const override { return _result.postingInfo(); }
void visitMembers(vespalib::ObjectVisitor &visitor) const override;
+ const attribute::ISearchContext *getAttributeSearchContext() const override { return _ctx.get(); }
};
} // namespace queryeval
diff --git a/searchlib/src/vespa/searchlib/queryeval/field_spec.h b/searchlib/src/vespa/searchlib/queryeval/field_spec.h
index 7da3d6a8ecc..b274e2c6751 100644
--- a/searchlib/src/vespa/searchlib/queryeval/field_spec.h
+++ b/searchlib/src/vespa/searchlib/queryeval/field_spec.h
@@ -4,12 +4,11 @@
#include <vespa/searchlib/fef/handle.h>
#include <vespa/searchlib/fef/matchdata.h>
-#include <vector>
#include <vespa/vespalib/stllike/string.h>
+#include <vector>
namespace search::queryeval {
-
/**
* Base description of a single field to be searched.
**/
@@ -61,27 +60,22 @@ private:
class FieldSpecBaseList
{
private:
- std::vector<FieldSpecBase> _list;
+ using List = std::vector<FieldSpecBase>;
+ List _list;
public:
+ using const_iterator = List::const_iterator;
FieldSpecBaseList &add(const FieldSpecBase &spec) {
_list.push_back(spec);
return *this;
}
- bool empty() const {
- return _list.empty();
- }
- size_t size() const {
- return _list.size();
- }
- const FieldSpecBase &operator[](size_t i) const {
- return _list[i];
- }
+ bool empty() const { return _list.empty(); }
+ size_t size() const { return _list.size(); }
+ const_iterator begin() const { return _list.begin(); }
+ const_iterator end() const { return _list.end(); }
+ const FieldSpecBase &operator[](size_t i) const { return _list[i]; }
void clear() { _list.clear(); }
-
- void swap(FieldSpecBaseList & rhs) {
- _list.swap(rhs._list);
- }
+ void swap(FieldSpecBaseList & rhs) { _list.swap(rhs._list); }
};
/**
@@ -97,19 +91,11 @@ public:
_list.push_back(spec);
return *this;
}
- bool empty() const {
- return _list.empty();
- }
- size_t size() const {
- return _list.size();
- }
- const FieldSpec &operator[](size_t i) const {
- return _list[i];
- }
+ bool empty() const { return _list.empty(); }
+ size_t size() const { return _list.size(); }
+ const FieldSpec &operator[](size_t i) const { return _list[i]; }
void clear() { _list.clear(); }
- void swap(FieldSpecList & rhs) {
- _list.swap(rhs._list);
- }
+ void swap(FieldSpecList & rhs) { _list.swap(rhs._list); }
};
}
diff --git a/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.cpp b/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.cpp
index 9bc9d3161e6..5cb51aa4fd0 100644
--- a/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.cpp
@@ -302,7 +302,7 @@ OrBlueprint::createIntermediateSearch(const MultiSearch::Children &subSearches,
}
//-----------------------------------------------------------------------------
-WeakAndBlueprint::~WeakAndBlueprint() {}
+WeakAndBlueprint::~WeakAndBlueprint() = default;
Blueprint::HitEstimate
WeakAndBlueprint::combine(const std::vector<HitEstimate> &data) const
diff --git a/searchlib/src/vespa/searchlib/queryeval/leaf_blueprints.cpp b/searchlib/src/vespa/searchlib/queryeval/leaf_blueprints.cpp
index bbfa487ae7d..a140fb146d5 100644
--- a/searchlib/src/vespa/searchlib/queryeval/leaf_blueprints.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/leaf_blueprints.cpp
@@ -64,7 +64,9 @@ SimpleBlueprint::tag(const vespalib::string &t)
SearchIterator::UP
FakeBlueprint::createLeafSearch(const fef::TermFieldMatchDataArray &tfmda, bool) const
{
- return std::make_unique<FakeSearch>(_tag, _field.getName(), _term, _result, tfmda);
+ auto result = std::make_unique<FakeSearch>(_tag, _field.getName(), _term, _result, tfmda);
+ result->is_attr(_is_attr);
+ return result;
}
FakeBlueprint::FakeBlueprint(const FieldSpec &field, const FakeResult &result)
@@ -72,7 +74,8 @@ FakeBlueprint::FakeBlueprint(const FieldSpec &field, const FakeResult &result)
_tag("<tag>"),
_term("<term>"),
_field(field),
- _result(result)
+ _result(result),
+ _is_attr(false)
{
setEstimate(HitEstimate(result.inspect().size(), result.inspect().empty()));
}
diff --git a/searchlib/src/vespa/searchlib/queryeval/leaf_blueprints.h b/searchlib/src/vespa/searchlib/queryeval/leaf_blueprints.h
index 698e2235690..85d30aaf003 100644
--- a/searchlib/src/vespa/searchlib/queryeval/leaf_blueprints.h
+++ b/searchlib/src/vespa/searchlib/queryeval/leaf_blueprints.h
@@ -51,6 +51,7 @@ private:
vespalib::string _term;
FieldSpec _field;
FakeResult _result;
+ bool _is_attr;
protected:
SearchIterator::UP
@@ -66,6 +67,12 @@ public:
}
const vespalib::string &tag() const { return _tag; }
+ FakeBlueprint &is_attr(bool value) {
+ _is_attr = value;
+ return *this;
+ }
+ bool is_attr() const { return _is_attr; }
+
FakeBlueprint &term(const vespalib::string &t) {
_term = t;
return *this;
diff --git a/searchlib/src/vespa/searchlib/queryeval/predicate_blueprint.cpp b/searchlib/src/vespa/searchlib/queryeval/predicate_blueprint.cpp
index a119823c0dd..3e6228053ab 100644
--- a/searchlib/src/vespa/searchlib/queryeval/predicate_blueprint.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/predicate_blueprint.cpp
@@ -7,9 +7,8 @@
#include <vespa/searchlib/predicate/predicate_zstar_compressed_posting_list.h>
#include <vespa/searchlib/predicate/predicate_hash.h>
#include <vespa/searchlib/query/tree/termnodes.h>
-
#include <vespa/log/log.h>
-LOG_SETUP(".predicate_blueprint");
+LOG_SETUP(".searchlib.predicate.predicate_blueprint");
#include <vespa/searchlib/predicate/predicate_range_term_expander.h>
using search::query::PredicateQuery;
diff --git a/searchlib/src/vespa/searchlib/queryeval/same_element_search.h b/searchlib/src/vespa/searchlib/queryeval/same_element_search.h
index 6a116c76e73..1fd381eb1ae 100644
--- a/searchlib/src/vespa/searchlib/queryeval/same_element_search.h
+++ b/searchlib/src/vespa/searchlib/queryeval/same_element_search.h
@@ -39,6 +39,7 @@ public:
void doSeek(uint32_t docid) override;
void doUnpack(uint32_t) override {}
void visitMembers(vespalib::ObjectVisitor &visitor) const override;
+ const std::vector<SearchIterator::UP> &children() const { return _children; }
};
}
diff --git a/searchlib/src/vespa/searchlib/queryeval/scores.h b/searchlib/src/vespa/searchlib/queryeval/scores.h
index 4fb2de5d6fa..e8dae898909 100644
--- a/searchlib/src/vespa/searchlib/queryeval/scores.h
+++ b/searchlib/src/vespa/searchlib/queryeval/scores.h
@@ -4,8 +4,7 @@
#include <vespa/searchlib/common/feature.h>
-namespace search {
-namespace queryeval {
+namespace search::queryeval {
struct Scores {
feature_t low;
@@ -16,6 +15,4 @@ struct Scores {
bool isValid() const { return low <= high; }
};
-} // namespace queryeval
-} // namespace search
-
+}
diff --git a/searchlib/src/vespa/searchlib/tensor/dense_tensor_attribute.cpp b/searchlib/src/vespa/searchlib/tensor/dense_tensor_attribute.cpp
index 18fda4d7d3d..bbd50e468e2 100644
--- a/searchlib/src/vespa/searchlib/tensor/dense_tensor_attribute.cpp
+++ b/searchlib/src/vespa/searchlib/tensor/dense_tensor_attribute.cpp
@@ -8,6 +8,9 @@
#include <vespa/fastlib/io/bufferedfile.h>
#include <vespa/searchlib/attribute/readerbase.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.tensor.dense_tensor_attribute");
+
using vespalib::eval::ValueType;
using vespalib::tensor::MutableDenseTensorView;
using vespalib::tensor::Tensor;
@@ -66,7 +69,7 @@ TensorReader::getNumCells() {
return 0u;
}
if (detect != tensorIsPresent) {
- abort();
+ LOG_ABORT("should not be reached");
}
size_t numCells = _numBoundCells;
if (_numUnboundDims != 0) {
diff --git a/searchlib/src/vespa/searchlib/test/fakedata/fakeegcompr64filterocc.cpp b/searchlib/src/vespa/searchlib/test/fakedata/fakeegcompr64filterocc.cpp
index 67e40fa5061..7eda569924c 100644
--- a/searchlib/src/vespa/searchlib/test/fakedata/fakeegcompr64filterocc.cpp
+++ b/searchlib/src/vespa/searchlib/test/fakedata/fakeegcompr64filterocc.cpp
@@ -5,6 +5,9 @@
#include <vespa/searchlib/queryeval/iterators.h>
#include <vespa/vespalib/util/stringfmt.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.test.fake_eg_compr64_filter_occ");
+
using search::fef::TermFieldMatchData;
using search::fef::TermFieldMatchDataPosition;
@@ -1428,7 +1431,7 @@ FakeFilterOccEGCompressed64SkipArrayIterator<doSkip>::doSeek(uint32_t docId)
oCacheInt,
oPreRead,
_l1SkipDocIdBitsOffset);
- abort();
+ LOG_ABORT("should not be reached");
}
#if DEBUG_EGCOMPR64FILTEROCC_PRINTF
printf("L1DecodeV docId=%d docIdPos=%d L1SkipPos=%d\n",
diff --git a/searchlib/src/vespa/searchlib/test/fakedata/fakememtreeocc.cpp b/searchlib/src/vespa/searchlib/test/fakedata/fakememtreeocc.cpp
index c31a2096a2f..8bbcdf9d359 100644
--- a/searchlib/src/vespa/searchlib/test/fakedata/fakememtreeocc.cpp
+++ b/searchlib/src/vespa/searchlib/test/fakedata/fakememtreeocc.cpp
@@ -10,6 +10,7 @@
#include <vespa/searchlib/btree/btreenodestore.hpp>
#include <vespa/searchlib/memoryindex/postingiterator.h>
#include <vespa/searchlib/util/postingpriorityqueue.h>
+
#include <vespa/log/log.h>
LOG_SETUP(".fakememtreeocc");
@@ -325,7 +326,7 @@ FakeMemTreeOccFactory::make(const FakeWord &fw)
i(_mgr._fw2WordIdx.find(&fw));
if (i == _mgr._fw2WordIdx.end())
- abort();
+ LOG_ABORT("should not be reached");
uint32_t wordIdx = i->second;
@@ -395,7 +396,7 @@ FakeMemTreeOcc2Factory::make(const FakeWord &fw)
i(_mgr._fw2WordIdx.find(&fw));
if (i == _mgr._fw2WordIdx.end())
- abort();
+ LOG_ABORT("should not be reached");
uint32_t wordIdx = i->second;
diff --git a/searchlib/src/vespa/searchlib/transactionlog/domainpart.cpp b/searchlib/src/vespa/searchlib/transactionlog/domainpart.cpp
index 35bdc71c963..47ab81f9b2e 100644
--- a/searchlib/src/vespa/searchlib/transactionlog/domainpart.cpp
+++ b/searchlib/src/vespa/searchlib/transactionlog/domainpart.cpp
@@ -662,7 +662,7 @@ int32_t DomainPart::calcCrc(Crc version, const void * buf, size_t sz)
calculator.process_bytes(buf, sz);
return calculator.checksum();
} else {
- abort();
+ LOG_ABORT("should not be reached");
}
}
diff --git a/searchlib/src/vespa/searchlib/transactionlog/translogserverapp.cpp b/searchlib/src/vespa/searchlib/transactionlog/translogserverapp.cpp
index 3c964c2a04d..0b02d10ffab 100644
--- a/searchlib/src/vespa/searchlib/transactionlog/translogserverapp.cpp
+++ b/searchlib/src/vespa/searchlib/transactionlog/translogserverapp.cpp
@@ -33,7 +33,7 @@ getCrc(searchlib::TranslogserverConfig::Crcmethod crcType)
case searchlib::TranslogserverConfig::xxh64:
return DomainPart::xxh64;
}
- abort();
+ LOG_ABORT("should not be reached");
}
}
diff --git a/searchlib/src/vespa/searchlib/util/random_normal.h b/searchlib/src/vespa/searchlib/util/random_normal.h
new file mode 100644
index 00000000000..74596066312
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/util/random_normal.h
@@ -0,0 +1,67 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/searchlib/util/rand48.h>
+#include <cmath>
+
+namespace search {
+
+/**
+ * Draws a random number from the Gaussian distribution
+ * using the Marsaglia polar method.
+ */
+class RandomNormal
+{
+private:
+ Rand48 _rnd;
+ double _mean;
+ double _stddev;
+
+ bool _useSpare;
+ bool _hasSpare;
+ feature_t _spare;
+
+ feature_t nextUniform() {
+ return (_rnd.lrand48() / (feature_t)0x80000000u) * 2.0 - 1.0;
+ }
+
+public:
+ RandomNormal(double mean, double stddev, bool useSpare = true) :
+ _rnd(),
+ _mean(mean),
+ _stddev(stddev),
+ _useSpare(useSpare),
+ _hasSpare(false),
+ _spare(0.0)
+ {}
+
+ void seed(long seed) {
+ _rnd.srand48(seed);
+ }
+
+ feature_t next() {
+ feature_t result = _spare;
+ if (_useSpare && _hasSpare) {
+ _hasSpare = false;
+ } else {
+ _hasSpare = true;
+
+ feature_t u, v, s;
+ do {
+ u = nextUniform();
+ v = nextUniform();
+ s = u * u + v * v;
+ } while ( (s >= 1.0) || (s == 0.0) );
+ s = std::sqrt(-2.0 * std::log(s) / s);
+
+ _spare = v * s; // saved for next invocation
+ result = u * s;
+ }
+ return _mean + _stddev * result;
+ }
+
+};
+
+} // search
+
diff --git a/searchlib/src/vespa/searchlib/util/statebuf.cpp b/searchlib/src/vespa/searchlib/util/statebuf.cpp
index 27ccb76d289..12d18599a41 100644
--- a/searchlib/src/vespa/searchlib/util/statebuf.cpp
+++ b/searchlib/src/vespa/searchlib/util/statebuf.cpp
@@ -2,6 +2,10 @@
#include "statebuf.h"
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.util.statebuf");
+
+
static const char *hexx = "0123456789abcdef";
namespace search {
@@ -9,7 +13,7 @@ namespace search {
void
StateBuf::overflow() noexcept
{
- abort();
+ LOG_ABORT("should not be reached");
}
@@ -120,7 +124,7 @@ StateBuf::appendDecFraction(unsigned long val, unsigned int width) noexcept
{
char buf[22];
if (width > sizeof(buf)) {
- abort();
+ LOG_ABORT("should not be reached");
}
char *p = buf;
char *pe = buf + width;
@@ -174,7 +178,7 @@ StateBuf::appendTimestamp() noexcept
*/
int gtres = clock_gettime(CLOCK_REALTIME, &ts);
if (gtres != 0) {
- abort();
+ LOG_ABORT("should not be reached");
}
appendTimestamp(ts);
return *this;
diff --git a/searchlib/src/vespa/searchlib/util/statefile.cpp b/searchlib/src/vespa/searchlib/util/statefile.cpp
index 052a18ce97d..5cccc4c6d74 100644
--- a/searchlib/src/vespa/searchlib/util/statefile.cpp
+++ b/searchlib/src/vespa/searchlib/util/statefile.cpp
@@ -8,6 +8,9 @@
#include <fcntl.h>
#include <sys/stat.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.util.statefile");
+
using Mutex = std::mutex;
using Guard = std::lock_guard<Mutex>;
@@ -32,7 +35,7 @@ myopen(const char *name) noexcept
std::error_code ec(errno, std::system_category());
fprintf(stderr,
"Could not open %s: %s\n", name, ec.message().c_str());
- abort();
+ LOG_ABORT("should not be reached");
}
return fd;
}
@@ -45,7 +48,7 @@ myfstat(const char *name, int fd, struct stat &stbuf) noexcept
if (fsres != 0) {
std::error_code ec(errno, std::system_category());
fprintf(stderr, "Could not fstat %s: %s\n", name, ec.message().c_str());
- abort();
+ LOG_ABORT("should not be reached");
}
}
@@ -66,7 +69,7 @@ mypread(const char *name, int fd, void *buf, size_t bufLen, int64_t offset) noex
"Could not read %zu bytes from %s offset %" PRId64 ": %s\n",
bufLen, name, offset, ec.message().c_str());
}
- abort();
+ LOG_ABORT("should not be reached");
}
}
@@ -87,7 +90,7 @@ mypwrite(const char *name, int fd, const void *buf, size_t bufLen,
"Could not write %zu bytes to %s offset %" PRId64 ": %s\n",
bufLen, name, offset, ec.message().c_str());
}
- abort();
+ LOG_ABORT("should not be reached");
}
}
@@ -100,7 +103,7 @@ myclose(const char *name, int fd) noexcept
std::error_code ec(errno, std::system_category());
fprintf(stderr, "Could not close %s: %s\n",
name, ec.message().c_str());
- abort();
+ LOG_ABORT("should not be reached");
}
}
@@ -113,7 +116,7 @@ myfsync(const char *name, int fd) noexcept
std::error_code ec(errno, std::system_category());
fprintf(stderr, "Could not fsync %s: %s\n",
name, ec.message().c_str());
- abort();
+ LOG_ABORT("should not be reached");
}
}
@@ -126,7 +129,7 @@ myunlink(const char *name) noexcept
std::error_code ec(errno, std::system_category());
fprintf(stderr, "Could not unlink %s: %s\n",
name, ec.message().c_str());
- abort();
+ LOG_ABORT("should not be reached");
}
}
@@ -279,7 +282,7 @@ StateFile::trimHistory(std::vector<char> &history, const char *name, int hfd,
std::error_code ec(errno, std::system_category());
fprintf(stderr, "Could not truncate %s: %s\n",
name, ec.message().c_str());
- abort();
+ LOG_ABORT("should not be reached");
}
history.resize(newHistSize);
}
@@ -342,17 +345,17 @@ StateFile::checkState(const char *buf, size_t bufLen) noexcept
if (*p == '\n') {
if (p != buf + bufLen - 1) {
mystderr("statefile state corrupted: early newline\n");
- abort();
+ LOG_ABORT("should not be reached");
}
return;
}
if (*p == '\0') {
mystderr("statefile state corrupted: nul byte found\n");
- abort();
+ LOG_ABORT("should not be reached");
}
}
mystderr("statefile state corrupted: missing newline at end\n");
- abort();
+ LOG_ABORT("should not be reached");
}
@@ -370,22 +373,22 @@ StateFile::internalAddSignalState(const char *buf, size_t bufLen,
0644);
if (fd < 0) {
mystderr(openerr);
- abort();
+ LOG_ABORT("should not be reached");
}
ssize_t wres = write(fd, buf, bufLen);
if (static_cast<size_t>(wres) != bufLen) {
mystderr(writeerr);
- abort();
+ LOG_ABORT("should not be reached");
}
int fsyncres = fsync(fd);
if (fsyncres != 0) {
mystderr(fsyncerr);
- abort();
+ LOG_ABORT("should not be reached");
}
int closeres = close(fd);
if (closeres != 0) {
mystderr(closeerr);
- abort();
+ LOG_ABORT("should not be reached");
}
}
diff --git a/searchsummary/src/tests/docsummary/positionsdfw_test.cpp b/searchsummary/src/tests/docsummary/positionsdfw_test.cpp
index 7e1da35bd75..ce184fd328a 100644
--- a/searchsummary/src/tests/docsummary/positionsdfw_test.cpp
+++ b/searchsummary/src/tests/docsummary/positionsdfw_test.cpp
@@ -63,9 +63,9 @@ public:
return &_attr;
}
virtual const IAttributeVector *
- getAttributeStableEnum(const string &) const override { abort(); }
+ getAttributeStableEnum(const string &) const override { LOG_ABORT("should not be reached"); }
virtual void getAttributeList(vector<const IAttributeVector *> &) const override
- { abort(); }
+ { LOG_ABORT("should not be reached"); }
};
class MyAttributeManager : public IAttributeManager {
@@ -74,13 +74,13 @@ public:
MyAttributeManager(const IAttributeVector &attr) : _attr(attr) {}
virtual AttributeGuard::UP getAttribute(const string &) const override {
- abort();
+ LOG_ABORT("should not be reached");
}
virtual std::unique_ptr<attribute::AttributeReadGuard> getAttributeReadGuard(const string &, bool) const override {
- abort();
+ LOG_ABORT("should not be reached");
}
virtual void getAttributeList(vector<AttributeGuard> &) const override {
- abort();
+ LOG_ABORT("should not be reached");
}
virtual IAttributeContext::UP createContext() const override {
return IAttributeContext::UP(new MyAttributeContext(_attr));
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/rankfeaturesdfw.cpp b/searchsummary/src/vespa/searchsummary/docsummary/rankfeaturesdfw.cpp
index fdc38d8d6f9..7097012c5ea 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/rankfeaturesdfw.cpp
+++ b/searchsummary/src/vespa/searchsummary/docsummary/rankfeaturesdfw.cpp
@@ -8,10 +8,10 @@
namespace search::docsummary {
RankFeaturesDFW::RankFeaturesDFW() :
- _env(NULL)
+ _env(nullptr)
{ }
-RankFeaturesDFW::~RankFeaturesDFW() { }
+RankFeaturesDFW::~RankFeaturesDFW() = default;
void
RankFeaturesDFW::init(IDocsumEnvironment * env)
@@ -23,15 +23,15 @@ void
RankFeaturesDFW::insertField(uint32_t docid, GeneralResult *, GetDocsumsState *state,
ResType type, vespalib::slime::Inserter &target)
{
- if (state->_rankFeatures.get() == NULL) {
+ if (state->_rankFeatures.get() == nullptr) {
state->_callback.FillRankFeatures(state, _env);
- if (state->_rankFeatures.get() == NULL) { // still no rank features to write
+ if (state->_rankFeatures.get() == nullptr) { // still no rank features to write
return;
}
}
const FeatureSet::StringVector & names = state->_rankFeatures->getNames();
const feature_t * values = state->_rankFeatures->getFeaturesByDocId(docid);
- if (type == RES_FEATUREDATA && values != NULL) {
+ if (type == RES_FEATUREDATA && values != nullptr) {
vespalib::slime::Cursor& obj = target.insertObject();
for (uint32_t i = 0; i < names.size(); ++i) {
vespalib::Memory name(names[i].c_str(), names[i].size());
@@ -40,7 +40,7 @@ RankFeaturesDFW::insertField(uint32_t docid, GeneralResult *, GetDocsumsState *s
return;
}
vespalib::JSONStringer & json(state->_jsonStringer);
- if (values != NULL) {
+ if (values != nullptr) {
json.clear();
json.beginObject();
for (uint32_t i = 0; i < names.size(); ++i) {
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/ApplicationHealthMonitor.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/ApplicationHealthMonitor.java
index bd2658db8aa..7c78d61da30 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/ApplicationHealthMonitor.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/ApplicationHealthMonitor.java
@@ -21,6 +21,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.function.Function;
/**
* Responsible for monitoring a whole application using /state/v1/health.
@@ -37,7 +38,13 @@ public class ApplicationHealthMonitor implements ServiceStatusProvider, AutoClos
private final Map<ServiceId, HealthMonitor> healthMonitors;
public static ApplicationHealthMonitor startMonitoring(ApplicationInfo application) {
- return new ApplicationHealthMonitor(makeHealthMonitors(application));
+ return startMonitoring(application, HealthMonitor::new);
+ }
+
+ /** For testing. */
+ static ApplicationHealthMonitor startMonitoring(ApplicationInfo application,
+ Function<HealthEndpoint, HealthMonitor> mapper) {
+ return new ApplicationHealthMonitor(makeHealthMonitors(application, mapper));
}
private ApplicationHealthMonitor(Map<ServiceId, HealthMonitor> healthMonitors) {
@@ -64,7 +71,8 @@ public class ApplicationHealthMonitor implements ServiceStatusProvider, AutoClos
healthMonitors.clear();
}
- private static Map<ServiceId, HealthMonitor> makeHealthMonitors(ApplicationInfo application) {
+ private static Map<ServiceId, HealthMonitor> makeHealthMonitors(
+ ApplicationInfo application, Function<HealthEndpoint, HealthMonitor> monitorFactory) {
Map<ServiceId, HealthMonitor> healthMonitors = new HashMap<>();
for (HostInfo hostInfo : application.getModel().getHosts()) {
for (ServiceInfo serviceInfo : hostInfo.getServices()) {
@@ -73,7 +81,8 @@ public class ApplicationHealthMonitor implements ServiceStatusProvider, AutoClos
application,
hostInfo,
serviceInfo,
- portInfo)
+ portInfo,
+ monitorFactory)
.ifPresent(healthMonitor -> healthMonitors.put(
ApplicationInstanceGenerator.getServiceId(application, serviceInfo),
healthMonitor));
@@ -87,14 +96,14 @@ public class ApplicationHealthMonitor implements ServiceStatusProvider, AutoClos
ApplicationInfo applicationInfo,
HostInfo hostInfo,
ServiceInfo serviceInfo,
- PortInfo portInfo) {
+ PortInfo portInfo,
+ Function<HealthEndpoint, HealthMonitor> monitorFactory) {
if (portInfo.getTags().containsAll(PORT_TAGS_HEALTH)) {
HostName hostname = HostName.from(hostInfo.getHostname());
HealthEndpoint endpoint = HealthEndpoint.forHttp(hostname, portInfo.getPort());
- // todo: make HealthMonitor
- // HealthMonitor healthMonitor = new HealthMonitor(endpoint);
- // healthMonitor.startMonitoring();
- return Optional.empty();
+ HealthMonitor healthMonitor = monitorFactory.apply(endpoint);
+ healthMonitor.startMonitoring();
+ return Optional.of(healthMonitor);
}
return Optional.empty();
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthClient.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthClient.java
index 43a02a385be..c8b2d5d25f7 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthClient.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthClient.java
@@ -14,7 +14,6 @@ import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
-import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.HttpClients;
@@ -23,8 +22,16 @@ import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import javax.net.ssl.SSLContext;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import static com.yahoo.yolean.Exceptions.uncheck;
/**
+ * Health client
+ *
+ * NOT thread-safe.
+ *
* @author hakon
*/
public class HealthClient implements AutoCloseable, ServiceIdentityProvider.Listener {
@@ -47,27 +54,76 @@ public class HealthClient implements AutoCloseable, ServiceIdentityProvider.List
};
private final HealthEndpoint endpoint;
+ private final Supplier<CloseableHttpClient> clientSupplier;
+ private final Function<HttpEntity, String> getContentFunction;
- private volatile CloseableHttpClient httpClient;
+ private CloseableHttpClient httpClient = null;
public HealthClient(HealthEndpoint endpoint) {
+ this(endpoint,
+ () -> makeCloseableHttpClient(endpoint),
+ entity -> uncheck(() -> EntityUtils.toString(entity)));
+ }
+
+ /** For testing. */
+ HealthClient(HealthEndpoint endpoint,
+ Supplier<CloseableHttpClient> clientSupplier,
+ Function<HttpEntity, String> getContentFunction) {
this.endpoint = endpoint;
+ this.clientSupplier = clientSupplier;
+ this.getContentFunction = getContentFunction;
}
public void start() {
- endpoint.getServiceIdentityProvider().ifPresent(provider -> {
- onCredentialsUpdate(provider.getIdentitySslContext(), null);
- provider.addIdentityListener(this);
- });
+ updateHttpClient();
+ endpoint.registerListener(this);
}
@Override
public void onCredentialsUpdate(SSLContext sslContext, AthenzService ignored) {
- SSLConnectionSocketFactory socketFactory =
- new SSLConnectionSocketFactory(sslContext, endpoint.getHostnameVerifier().orElse(null));
+ updateHttpClient();
+ }
+
+ public HealthEndpoint getEndpoint() {
+ return endpoint;
+ }
+ public HealthInfo getHealthInfo() {
+ try {
+ return probeHealth();
+ } catch (Exception e) {
+ return HealthInfo.fromException(e);
+ }
+ }
+
+ @Override
+ public void close() {
+ endpoint.removeListener(this);
+
+ if (httpClient != null) {
+ try {
+ httpClient.close();
+ } catch (Exception e) {
+ // ignore
+ }
+ httpClient = null;
+ }
+ }
+
+ private void updateHttpClient() {
+ CloseableHttpClient httpClient = clientSupplier.get();
+
+ if (this.httpClient != null) {
+ // Note: close() can be called any number of times.
+ uncheck(() -> this.httpClient.close());
+ }
+
+ this.httpClient = httpClient;
+ }
+
+ private static CloseableHttpClient makeCloseableHttpClient(HealthEndpoint endpoint) {
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
- .register("https", socketFactory)
+ .register(endpoint.getStateV1HealthUrl().getProtocol(), endpoint.getConnectionSocketFactory())
.build();
HttpClientConnectionManager connectionManager = new BasicHttpClientConnectionManager(registry);
@@ -78,7 +134,7 @@ public class HealthClient implements AutoCloseable, ServiceIdentityProvider.List
.setSocketTimeout(DEFAULT_TIMEOUT_MILLIS) // waiting for data
.build();
- this.httpClient = HttpClients.custom()
+ return HttpClients.custom()
.setKeepAliveStrategy(KEEP_ALIVE_STRATEGY)
.setConnectionManager(connectionManager)
.disableAutomaticRetries()
@@ -86,54 +142,33 @@ public class HealthClient implements AutoCloseable, ServiceIdentityProvider.List
.build();
}
- public HealthInfo getHealthInfo() {
- try {
- return probeHealth();
- } catch (Exception e) {
- return HealthInfo.fromException(e);
- }
- }
-
- @Override
- public void close() {
- endpoint.getServiceIdentityProvider().ifPresent(provider -> provider.removeIdentityListener(this));
-
- try {
- httpClient.close();
- } catch (Exception e) {
- // ignore
- }
- httpClient = null;
- }
-
private HealthInfo probeHealth() throws Exception {
HttpGet httpget = new HttpGet(endpoint.getStateV1HealthUrl().toString());
- CloseableHttpResponse httpResponse;
CloseableHttpClient httpClient = this.httpClient;
if (httpClient == null) {
- throw new IllegalStateException("HTTP client has closed");
- }
-
- httpResponse = httpClient.execute(httpget);
-
- int httpStatusCode = httpResponse.getStatusLine().getStatusCode();
- if (httpStatusCode < 200 || httpStatusCode >= 300) {
- return HealthInfo.fromBadHttpStatusCode(httpStatusCode);
- }
-
- HttpEntity bodyEntity = httpResponse.getEntity();
- long contentLength = bodyEntity.getContentLength();
- if (contentLength > MAX_CONTENT_LENGTH) {
- throw new IllegalArgumentException("Content too long: " + contentLength + " bytes");
+ throw new IllegalStateException("HTTP client never started or has closed");
}
- String body = EntityUtils.toString(bodyEntity);
- HealthResponse healthResponse = mapper.readValue(body, HealthResponse.class);
- if (healthResponse.status == null || healthResponse.status.code == null) {
- return HealthInfo.fromHealthStatusCode(HealthResponse.Status.DEFAULT_STATUS);
- } else {
- return HealthInfo.fromHealthStatusCode(healthResponse.status.code);
+ try (CloseableHttpResponse httpResponse = httpClient.execute(httpget)) {
+ int httpStatusCode = httpResponse.getStatusLine().getStatusCode();
+ if (httpStatusCode < 200 || httpStatusCode >= 300) {
+ return HealthInfo.fromBadHttpStatusCode(httpStatusCode);
+ }
+
+ HttpEntity bodyEntity = httpResponse.getEntity();
+ long contentLength = bodyEntity.getContentLength();
+ if (contentLength > MAX_CONTENT_LENGTH) {
+ throw new IllegalArgumentException("Content too long: " + contentLength + " bytes");
+ }
+ String body = getContentFunction.apply(bodyEntity);
+ HealthResponse healthResponse = mapper.readValue(body, HealthResponse.class);
+
+ if (healthResponse.status == null || healthResponse.status.code == null) {
+ return HealthInfo.fromHealthStatusCode(HealthResponse.Status.DEFAULT_STATUS);
+ } else {
+ return HealthInfo.fromHealthStatusCode(healthResponse.status.code);
+ }
}
}
}
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthEndpoint.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthEndpoint.java
index e9d17a9ab70..38139d28d7f 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthEndpoint.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthEndpoint.java
@@ -5,25 +5,22 @@ import com.yahoo.config.provision.HostName;
import com.yahoo.vespa.athenz.api.AthenzIdentity;
import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider;
import com.yahoo.vespa.athenz.tls.AthenzIdentityVerifier;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
import javax.net.ssl.HostnameVerifier;
import java.net.URL;
import java.util.Collections;
-import java.util.Optional;
import static com.yahoo.yolean.Exceptions.uncheck;
/**
* @author hakon
*/
-class HealthEndpoint {
- private final URL url;
- private final Optional<HostnameVerifier> hostnameVerifier;
- private final Optional<ServiceIdentityProvider> serviceIdentityProvider;
+public interface HealthEndpoint {
static HealthEndpoint forHttp(HostName hostname, int port) {
URL url = uncheck(() -> new URL("http", hostname.value(), port, "/state/v1/health"));
- return new HealthEndpoint(url, Optional.empty(), Optional.empty());
+ return new HttpHealthEndpoint(url);
}
static HealthEndpoint forHttps(HostName hostname,
@@ -32,26 +29,12 @@ class HealthEndpoint {
AthenzIdentity remoteIdentity) {
URL url = uncheck(() -> new URL("https", hostname.value(), port, "/state/v1/health"));
HostnameVerifier peerVerifier = new AthenzIdentityVerifier(Collections.singleton(remoteIdentity));
- return new HealthEndpoint(url, Optional.of(serviceIdentityProvider), Optional.of(peerVerifier));
+ return new HttpsHealthEndpoint(url, serviceIdentityProvider, peerVerifier);
}
- private HealthEndpoint(URL url,
- Optional<ServiceIdentityProvider> serviceIdentityProvider,
- Optional<HostnameVerifier> hostnameVerifier) {
- this.url = url;
- this.serviceIdentityProvider = serviceIdentityProvider;
- this.hostnameVerifier = hostnameVerifier;
- }
-
- public URL getStateV1HealthUrl() {
- return url;
- }
-
- public Optional<ServiceIdentityProvider> getServiceIdentityProvider() {
- return serviceIdentityProvider;
- }
-
- public Optional<HostnameVerifier> getHostnameVerifier() {
- return hostnameVerifier;
- }
+ URL getStateV1HealthUrl();
+ ConnectionSocketFactory getConnectionSocketFactory();
+ void registerListener(ServiceIdentityProvider.Listener listener);
+ void removeListener(ServiceIdentityProvider.Listener listener);
+ String description();
}
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthInfo.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthInfo.java
index a3fe3cb3106..8b724afba5f 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthInfo.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthInfo.java
@@ -50,7 +50,17 @@ public class HealthInfo {
return healthStatusCode.map(UP_STATUS_CODE::equals).orElse(false);
}
- public ServiceStatus toSerivceStatus() {
+ public ServiceStatus toServiceStatus() {
+ // Bootstrapping ServiceStatus: To avoid thundering herd problem at startup,
+ // the clients will not fetch the health immediately. What should the ServiceStatus
+ // be before the first health has been fetched?
+ //
+ // NOT_CHECKED: Logically the right thing, but if an Orchestrator gets a suspend request
+ // in this window, and another service within the cluster is down, it ends up allowing
+ // suspension when it shouldn't have done so.
+ //
+ // DOWN: Only safe initial value, possibly except if the first initial delay is long,
+ // as that could indicate it has been down for too long.
return isHealthy() ? ServiceStatus.UP : ServiceStatus.DOWN;
}
@@ -65,7 +75,7 @@ public class HealthInfo {
} else if (healthStatusCode.isPresent()) {
return "Bad health status code '" + healthStatusCode.get() + "'";
} else if (exception.isPresent()) {
- return Exceptions.toMessageString(exception.get());
+ return "Exception: " + Exceptions.toMessageString(exception.get());
} else if (httpStatusCode.isPresent()) {
return "Bad HTTP response status code " + httpStatusCode.getAsInt();
} else {
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitor.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitor.java
index fd809b32918..2574f782afb 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitor.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitor.java
@@ -13,40 +13,48 @@ import java.util.logging.Logger;
/**
* Used to monitor the health of a single URL endpoint.
*
+ * <p>Must be closed on successful start of monitoring ({}
+ *
+ * <p>Thread-safe
+ *
* @author hakon
*/
public class HealthMonitor implements AutoCloseable {
private static final Logger logger = Logger.getLogger(HealthMonitor.class.getName());
- private static final Duration DELAY = Duration.ofSeconds(20);
+
+ /** The duration between each health request. */
+ private static final Duration DEFAULT_DELAY = Duration.ofSeconds(10);
+
// About 'static': Javadoc says "Instances of java.util.Random are threadsafe."
private static final Random random = new Random();
private final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
private final HealthClient healthClient;
+ private final Duration delay;
private volatile HealthInfo lastHealthInfo = HealthInfo.empty();
public HealthMonitor(HealthEndpoint stateV1HealthEndpoint) {
- this.healthClient = new HealthClient(stateV1HealthEndpoint);
+ this(new HealthClient(stateV1HealthEndpoint), DEFAULT_DELAY);
}
/** For testing. */
- HealthMonitor(HealthClient healthClient) {
+ HealthMonitor(HealthClient healthClient, Duration delay) {
this.healthClient = healthClient;
+ this.delay = delay;
}
public void startMonitoring() {
healthClient.start();
executor.scheduleWithFixedDelay(
this::updateSynchronously,
- initialDelayInSeconds(DELAY.getSeconds()),
- DELAY.getSeconds(),
- TimeUnit.SECONDS);
+ initialDelayInMillis(delay.toMillis()),
+ delay.toMillis(),
+ TimeUnit.MILLISECONDS);
}
public ServiceStatus getStatus() {
- // todo: return lastHealthInfo.toServiceStatus();
- return ServiceStatus.NOT_CHECKED;
+ return lastHealthInfo.toServiceStatus();
}
@Override
@@ -63,11 +71,21 @@ public class HealthMonitor implements AutoCloseable {
healthClient.close();
}
- private long initialDelayInSeconds(long maxInitialDelayInSeconds) {
- return random.nextLong() % maxInitialDelayInSeconds;
+ private long initialDelayInMillis(long maxInitialDelayInMillis) {
+ if (maxInitialDelayInMillis >= Integer.MAX_VALUE) {
+ throw new IllegalArgumentException("Max initial delay is out of bounds: " + maxInitialDelayInMillis);
+ }
+
+ return (long) random.nextInt((int) maxInitialDelayInMillis);
}
private void updateSynchronously() {
- lastHealthInfo = healthClient.getHealthInfo();
+ try {
+ lastHealthInfo = healthClient.getHealthInfo();
+ } catch (Throwable t) {
+ // An uncaught exception will kill the executor.scheduleWithFixedDelay thread!
+ logger.log(LogLevel.WARNING, "Failed to get health info for " +
+ healthClient.getEndpoint().description(), t);
+ }
}
}
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitorManager.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitorManager.java
index 473ef5e3a94..383cb6961a7 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitorManager.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitorManager.java
@@ -9,17 +9,20 @@ import com.yahoo.vespa.applicationmodel.ClusterId;
import com.yahoo.vespa.applicationmodel.ConfigId;
import com.yahoo.vespa.applicationmodel.ServiceStatus;
import com.yahoo.vespa.applicationmodel.ServiceType;
+import com.yahoo.vespa.service.monitor.application.ConfigServerApplication;
import com.yahoo.vespa.service.monitor.application.ZoneApplication;
import com.yahoo.vespa.service.monitor.internal.MonitorManager;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
/**
+ * Manages all /state/v1/health related monitoring.
+ *
* @author hakon
*/
public class HealthMonitorManager implements MonitorManager {
- private final Map<ApplicationId, ApplicationHealthMonitor> healthMonitors = new HashMap<>();
+ private final ConcurrentHashMap<ApplicationId, ApplicationHealthMonitor> healthMonitors =
+ new ConcurrentHashMap<>();
private final ConfigserverConfig configserverConfig;
@Inject
@@ -29,7 +32,7 @@ public class HealthMonitorManager implements MonitorManager {
@Override
public void applicationActivated(ApplicationInfo application) {
- if (applicationMonitored(application.getApplicationId())) {
+ if (applicationMonitoredForHealth(application.getApplicationId())) {
ApplicationHealthMonitor monitor =
ApplicationHealthMonitor.startMonitoring(application);
healthMonitors.put(application.getApplicationId(), monitor);
@@ -38,11 +41,9 @@ public class HealthMonitorManager implements MonitorManager {
@Override
public void applicationRemoved(ApplicationId id) {
- if (applicationMonitored(id)) {
- ApplicationHealthMonitor monitor = healthMonitors.remove(id);
- if (monitor != null) {
- monitor.close();
- }
+ ApplicationHealthMonitor monitor = healthMonitors.remove(id);
+ if (monitor != null) {
+ monitor.close();
}
}
@@ -58,11 +59,15 @@ public class HealthMonitorManager implements MonitorManager {
return ServiceStatus.UP;
}
- return ServiceStatus.NOT_CHECKED;
+ ApplicationHealthMonitor monitor = healthMonitors.get(applicationId);
+ if (monitor == null) {
+ return ServiceStatus.NOT_CHECKED;
+ }
+
+ return monitor.getStatus(applicationId, clusterId, serviceType, configId);
}
- private boolean applicationMonitored(ApplicationId id) {
- // todo: health-check config server
- return false;
+ private boolean applicationMonitoredForHealth(ApplicationId id) {
+ return id.equals(ConfigServerApplication.CONFIG_SERVER_APPLICATION.getApplicationId());
}
}
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HttpHealthEndpoint.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HttpHealthEndpoint.java
new file mode 100644
index 00000000000..254cb9785e1
--- /dev/null
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HttpHealthEndpoint.java
@@ -0,0 +1,44 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.service.monitor.internal.health;
+
+import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+
+import java.net.URL;
+
+/**
+ * @author hakon
+ */
+class HttpHealthEndpoint implements HealthEndpoint {
+ private final URL url;
+ private final ConnectionSocketFactory socketFactory;
+
+ HttpHealthEndpoint(URL url) {
+ this.url = url;
+ this.socketFactory = PlainConnectionSocketFactory.getSocketFactory();
+ }
+
+ @Override
+ public URL getStateV1HealthUrl() {
+ return url;
+ }
+
+ @Override
+ public ConnectionSocketFactory getConnectionSocketFactory() {
+ return socketFactory;
+ }
+
+ @Override
+ public void registerListener(ServiceIdentityProvider.Listener listener) {
+ }
+
+ @Override
+ public void removeListener(ServiceIdentityProvider.Listener listener) {
+ }
+
+ @Override
+ public String description() {
+ return url.toString();
+ }
+}
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HttpsHealthEndpoint.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HttpsHealthEndpoint.java
new file mode 100644
index 00000000000..f1ebb80f500
--- /dev/null
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HttpsHealthEndpoint.java
@@ -0,0 +1,53 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.service.monitor.internal.health;
+
+import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import java.net.URL;
+
+/**
+ * @author hakon
+ */
+public class HttpsHealthEndpoint implements HealthEndpoint {
+ private final URL url;
+ private final HostnameVerifier hostnameVerifier;
+ private final ServiceIdentityProvider serviceIdentityProvider;
+
+ HttpsHealthEndpoint(URL url,
+ ServiceIdentityProvider serviceIdentityProvider,
+ HostnameVerifier hostnameVerifier) {
+ this.url = url;
+ this.serviceIdentityProvider = serviceIdentityProvider;
+ this.hostnameVerifier = hostnameVerifier;
+ }
+
+ @Override
+ public URL getStateV1HealthUrl() {
+ return url;
+ }
+
+ @Override
+ public ConnectionSocketFactory getConnectionSocketFactory() {
+ SSLContext sslContext = serviceIdentityProvider.getIdentitySslContext();
+ return new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
+ }
+
+ @Override
+ public void registerListener(ServiceIdentityProvider.Listener listener) {
+ serviceIdentityProvider.addIdentityListener(listener);
+ }
+
+ @Override
+ public void removeListener(ServiceIdentityProvider.Listener listener) {
+ serviceIdentityProvider.removeIdentityListener(listener);
+ }
+
+ @Override
+ public String description() {
+ return url.toString();
+ }
+}
diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/ApplicationHealthMonitorTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/ApplicationHealthMonitorTest.java
index 51b0503565f..b0fdb14726f 100644
--- a/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/ApplicationHealthMonitorTest.java
+++ b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/ApplicationHealthMonitorTest.java
@@ -6,19 +6,121 @@ import com.yahoo.vespa.service.monitor.application.ConfigServerApplication;
import com.yahoo.vespa.service.monitor.internal.ConfigserverUtil;
import org.junit.Test;
-import static com.yahoo.vespa.applicationmodel.ServiceStatus.NOT_CHECKED;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Function;
+
import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
public class ApplicationHealthMonitorTest {
@Test
public void sanityCheck() {
- ApplicationHealthMonitor monitor = ApplicationHealthMonitor.startMonitoring(
- ConfigserverUtil.makeExampleConfigServer());
- ServiceStatus status = monitor.getStatus(
+ MonitorFactory monitorFactory = new MonitorFactory();
+
+ HealthMonitor monitor1 = mock(HealthMonitor.class);
+ HealthMonitor monitor2 = mock(HealthMonitor.class);
+ HealthMonitor monitor3 = mock(HealthMonitor.class);
+
+ monitorFactory.expectEndpoint("http://cfg1:19071/state/v1/health", monitor1);
+ monitorFactory.expectEndpoint("http://cfg2:19071/state/v1/health", monitor2);
+ monitorFactory.expectEndpoint("http://cfg3:19071/state/v1/health", monitor3);
+
+ when(monitor1.getStatus()).thenReturn(ServiceStatus.UP);
+ when(monitor2.getStatus()).thenReturn(ServiceStatus.DOWN);
+ when(monitor3.getStatus()).thenReturn(ServiceStatus.NOT_CHECKED);
+
+ ApplicationHealthMonitor applicationMonitor = ApplicationHealthMonitor.startMonitoring(
+ ConfigserverUtil.makeExampleConfigServer(),
+ monitorFactory);
+
+ ServiceStatus status1 = applicationMonitor.getStatus(
ConfigServerApplication.CONFIG_SERVER_APPLICATION.getApplicationId(),
ConfigServerApplication.CLUSTER_ID,
ConfigServerApplication.SERVICE_TYPE,
ConfigServerApplication.configIdFrom(0));
- assertEquals(NOT_CHECKED, status);
+ assertEquals(ServiceStatus.UP, status1);
+
+ ServiceStatus status2 = applicationMonitor.getStatus(
+ ConfigServerApplication.CONFIG_SERVER_APPLICATION.getApplicationId(),
+ ConfigServerApplication.CLUSTER_ID,
+ ConfigServerApplication.SERVICE_TYPE,
+ ConfigServerApplication.configIdFrom(1));
+ assertEquals(ServiceStatus.DOWN, status2);
+
+ ServiceStatus status3 = applicationMonitor.getStatus(
+ ConfigServerApplication.CONFIG_SERVER_APPLICATION.getApplicationId(),
+ ConfigServerApplication.CLUSTER_ID,
+ ConfigServerApplication.SERVICE_TYPE,
+ ConfigServerApplication.configIdFrom(2));
+ assertEquals(ServiceStatus.NOT_CHECKED, status3);
+ }
+
+ private static class MonitorFactory implements Function<HealthEndpoint, HealthMonitor> {
+ private Map<String, EndpointInfo> endpointMonitors = new HashMap<>();
+
+ public void expectEndpoint(String url, HealthMonitor monitorToReturn) {
+ endpointMonitors.put(url, new EndpointInfo(url, monitorToReturn));
+ }
+
+ @Override
+ public HealthMonitor apply(HealthEndpoint endpoint) {
+ String url = endpoint.getStateV1HealthUrl().toString();
+ EndpointInfo info = endpointMonitors.get(url);
+ if (info == null) {
+ throw new IllegalArgumentException("Endpoint not expected: " + url);
+ }
+
+ if (info.isEndpointDiscovered()) {
+ throw new IllegalArgumentException("A HealthMonitor has already been created to " + url);
+ }
+
+ info.setEndpointDiscovered(true);
+
+ return info.getMonitorToReturn();
+ }
+ }
+
+ private static class EndpointInfo {
+ private final String url;
+ private final HealthMonitor monitorToReturn;
+
+ private boolean endpointDiscovered = false;
+
+ private EndpointInfo(String url, HealthMonitor monitorToReturn) {
+ this.url = url;
+ this.monitorToReturn = monitorToReturn;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public boolean isEndpointDiscovered() {
+ return endpointDiscovered;
+ }
+
+ public void setEndpointDiscovered(boolean endpointDiscovered) {
+ this.endpointDiscovered = endpointDiscovered;
+ }
+
+ public HealthMonitor getMonitorToReturn() {
+ return monitorToReturn;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ EndpointInfo that = (EndpointInfo) o;
+ return Objects.equals(url, that.url);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(url);
+ }
}
} \ No newline at end of file
diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/HealthClientTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/HealthClientTest.java
new file mode 100644
index 00000000000..c3e06faaf92
--- /dev/null
+++ b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/HealthClientTest.java
@@ -0,0 +1,165 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.service.monitor.internal.health;
+
+import com.yahoo.config.provision.HostName;
+import com.yahoo.vespa.applicationmodel.ServiceStatus;
+import org.apache.http.HttpEntity;
+import org.apache.http.StatusLine;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.conn.ConnectTimeoutException;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class HealthClientTest {
+ @Test
+ public void successfulRequestResponse() throws IOException {
+ HealthInfo info = getHealthInfoFromJsonResponse("{\n" +
+ " \"metrics\": {\n" +
+ " \"snapshot\": {\n" +
+ " \"from\": 1.528789829249E9,\n" +
+ " \"to\": 1.528789889249E9\n" +
+ " }\n" +
+ " },\n" +
+ " \"status\": {\"code\": \"up\"},\n" +
+ " \"time\": 1528789889364\n" +
+ "}");
+ assertTrue(info.isHealthy());
+ assertEquals(ServiceStatus.UP, info.toServiceStatus());
+ }
+
+ @Test
+ public void notUpResponse() throws IOException {
+ HealthInfo info = getHealthInfoFromJsonResponse("{\n" +
+ " \"metrics\": {\n" +
+ " \"snapshot\": {\n" +
+ " \"from\": 1.528789829249E9,\n" +
+ " \"to\": 1.528789889249E9\n" +
+ " }\n" +
+ " },\n" +
+ " \"status\": {\"code\": \"initializing\"},\n" +
+ " \"time\": 1528789889364\n" +
+ "}");
+ assertFalse(info.isHealthy());
+ assertEquals(ServiceStatus.DOWN, info.toServiceStatus());
+ assertEquals("Bad health status code 'initializing'", info.toString());
+ }
+
+ @Test
+ public void noCodeInResponse() throws IOException {
+ HealthInfo info = getHealthInfoFromJsonResponse("{\n" +
+ " \"metrics\": {\n" +
+ " \"snapshot\": {\n" +
+ " \"from\": 1.528789829249E9,\n" +
+ " \"to\": 1.528789889249E9\n" +
+ " }\n" +
+ " },\n" +
+ " \"status\": {\"foo\": \"bar\"},\n" +
+ " \"time\": 1528789889364\n" +
+ "}");
+ assertFalse(info.isHealthy());
+ assertEquals(ServiceStatus.DOWN, info.toServiceStatus());
+ assertEquals("Bad health status code 'down'", info.toString());
+ }
+
+ @Test
+ public void noStatusInResponse() throws IOException {
+ HealthInfo info = getHealthInfoFromJsonResponse("{\n" +
+ " \"metrics\": {\n" +
+ " \"snapshot\": {\n" +
+ " \"from\": 1.528789829249E9,\n" +
+ " \"to\": 1.528789889249E9\n" +
+ " }\n" +
+ " },\n" +
+ " \"time\": 1528789889364\n" +
+ "}");
+ assertFalse(info.isHealthy());
+ assertEquals(ServiceStatus.DOWN, info.toServiceStatus());
+ assertEquals("Bad health status code 'down'", info.toString());
+ }
+
+ @Test
+ public void badJson() throws IOException {
+ HealthInfo info = getHealthInfoFromJsonResponse("} foo bar");
+ assertFalse(info.isHealthy());
+ assertEquals(ServiceStatus.DOWN, info.toServiceStatus());
+ assertTrue(info.toString().startsWith("Exception: Unexpected close marker '}': "));
+ }
+
+ private HealthInfo getHealthInfoFromJsonResponse(String content)
+ throws IOException {
+ HealthEndpoint endpoint = HealthEndpoint.forHttp(HostName.from("host.com"), 19071);
+ CloseableHttpClient client = mock(CloseableHttpClient.class);
+
+ CloseableHttpResponse response = mock(CloseableHttpResponse.class);
+ when(client.execute(any())).thenReturn(response);
+
+ StatusLine statusLine = mock(StatusLine.class);
+ when(response.getStatusLine()).thenReturn(statusLine);
+
+ when(statusLine.getStatusCode()).thenReturn(200);
+
+ HttpEntity httpEntity = mock(HttpEntity.class);
+ when(response.getEntity()).thenReturn(httpEntity);
+
+ try (HealthClient healthClient = new HealthClient(endpoint, () -> client, entry -> content)) {
+ healthClient.start();
+
+ when(httpEntity.getContentLength()).thenReturn((long) content.length());
+ return healthClient.getHealthInfo();
+ }
+ }
+
+ @Test
+ public void testRequestException() throws IOException {
+ HealthEndpoint endpoint = HealthEndpoint.forHttp(HostName.from("host.com"), 19071);
+ CloseableHttpClient client = mock(CloseableHttpClient.class);
+
+ when(client.execute(any())).thenThrow(new ConnectTimeoutException("exception string"));
+
+ try (HealthClient healthClient = new HealthClient(endpoint, () -> client, entry -> "")) {
+ healthClient.start();
+ HealthInfo info = healthClient.getHealthInfo();
+ assertFalse(info.isHealthy());
+ assertEquals(ServiceStatus.DOWN, info.toServiceStatus());
+ assertEquals("Exception: exception string", info.toString());
+ }
+ }
+
+ @Test
+ public void testBadHttpResponseCode()
+ throws IOException {
+ HealthEndpoint endpoint = HealthEndpoint.forHttp(HostName.from("host.com"), 19071);
+ CloseableHttpClient client = mock(CloseableHttpClient.class);
+
+ CloseableHttpResponse response = mock(CloseableHttpResponse.class);
+ when(client.execute(any())).thenReturn(response);
+
+ StatusLine statusLine = mock(StatusLine.class);
+ when(response.getStatusLine()).thenReturn(statusLine);
+
+ when(statusLine.getStatusCode()).thenReturn(500);
+
+ HttpEntity httpEntity = mock(HttpEntity.class);
+ when(response.getEntity()).thenReturn(httpEntity);
+
+ String content = "{}";
+ try (HealthClient healthClient = new HealthClient(endpoint, () -> client, entry -> content)) {
+ healthClient.start();
+
+ when(httpEntity.getContentLength()).thenReturn((long) content.length());
+ HealthInfo info = healthClient.getHealthInfo();
+ assertFalse(info.isHealthy());
+ assertEquals(ServiceStatus.DOWN, info.toServiceStatus());
+ assertEquals("Bad HTTP response status code 500", info.toString());
+ }
+ }
+} \ No newline at end of file
diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitorTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitorTest.java
index cca1530ad97..2a203027353 100644
--- a/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitorTest.java
+++ b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitorTest.java
@@ -4,18 +4,36 @@ package com.yahoo.vespa.service.monitor.internal.health;
import com.yahoo.vespa.applicationmodel.ServiceStatus;
import org.junit.Test;
-import java.net.MalformedURLException;
+import java.time.Duration;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
public class HealthMonitorTest {
@Test
- public void basicTests() throws MalformedURLException {
+ public void initiallyDown() {
HealthClient healthClient = mock(HealthClient.class);
- try (HealthMonitor monitor = new HealthMonitor(healthClient)) {
+ try (HealthMonitor monitor = new HealthMonitor(healthClient, Duration.ofHours(12))) {
monitor.startMonitoring();
- assertEquals(ServiceStatus.NOT_CHECKED, monitor.getStatus());
+ assertEquals(ServiceStatus.DOWN, monitor.getStatus());
+ }
+ }
+
+ @Test
+ public void eventuallyUp() {
+ HealthClient healthClient = mock(HealthClient.class);
+ when(healthClient.getHealthInfo()).thenReturn(HealthInfo.fromHealthStatusCode(HealthInfo.UP_STATUS_CODE));
+ try (HealthMonitor monitor = new HealthMonitor(healthClient, Duration.ofMillis(10))) {
+ monitor.startMonitoring();
+
+ while (monitor.getStatus() != ServiceStatus.UP) {
+ try {
+ Thread.sleep(1);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
}
}
} \ No newline at end of file
diff --git a/slobrok/src/vespa/slobrok/sbmirror.cpp b/slobrok/src/vespa/slobrok/sbmirror.cpp
index 39f103173c5..d36e456995c 100644
--- a/slobrok/src/vespa/slobrok/sbmirror.cpp
+++ b/slobrok/src/vespa/slobrok/sbmirror.cpp
@@ -319,11 +319,11 @@ MirrorAPI::makeRequest()
if (_target == 0) return;
if (_reqPending) {
LOG(error, "cannot make new request, one is pending already");
- abort();
+ LOG_ABORT("should not be reached");
}
if (_scheduled) {
LOG(error, "cannot make new request, re-schedule is pending");
- abort();
+ LOG_ABORT("should not be reached");
}
_req = _orb.AllocRPCRequest(_req);
@@ -343,7 +343,7 @@ MirrorAPI::reSched(double seconds)
{
if (_scheduled) {
LOG(error, "already scheduled when asked to re-schedule in %f seconds", seconds);
- abort();
+ LOG_ABORT("should not be reached");
}
Schedule(seconds);
_scheduled = true;
diff --git a/slobrok/src/vespa/slobrok/server/cmd.cpp b/slobrok/src/vespa/slobrok/server/cmd.cpp
index bcb8c4ab711..6ba4f9a6e98 100644
--- a/slobrok/src/vespa/slobrok/server/cmd.cpp
+++ b/slobrok/src/vespa/slobrok/server/cmd.cpp
@@ -117,7 +117,7 @@ RegRpcSrvCommand::doneHandler(OkState result)
goto alldone;
}
// no other state should be possible
- abort();
+ LOG_ABORT("should not be reached");
alldone:
cleanupReservation();
delete _data;
diff --git a/socket_test/src/main/java/com/yahoo/socket/test/SocketTestApp.java b/socket_test/src/main/java/com/yahoo/socket/test/SocketTestApp.java
index 2db3b04f107..688c3b1194c 100644
--- a/socket_test/src/main/java/com/yahoo/socket/test/SocketTestApp.java
+++ b/socket_test/src/main/java/com/yahoo/socket/test/SocketTestApp.java
@@ -11,7 +11,7 @@ import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class SocketTestApp {
diff --git a/staging_vespalib/src/tests/stllike/cache_test.cpp b/staging_vespalib/src/tests/stllike/cache_test.cpp
index 142c4065673..6e852644602 100644
--- a/staging_vespalib/src/tests/stllike/cache_test.cpp
+++ b/staging_vespalib/src/tests/stllike/cache_test.cpp
@@ -28,36 +28,10 @@ public:
}
};
-class Test : public TestApp
-{
-public:
- int Main() override;
-private:
- typedef LruParam<uint32_t, string> P;
- typedef Map<uint32_t, string> B;
- void testCache();
- void testCacheSize();
- void testCacheSizeDeep();
- void testCacheEntriesHonoured();
- void testCacheMaxSizeHonoured();
- void testThatMultipleRemoveOnOverflowIsFine();
-};
-
-int
-Test::Main()
-{
- TEST_INIT("cache_test");
- testCache();
- testCacheSize();
- testCacheSizeDeep();
- testCacheEntriesHonoured();
- testCacheMaxSizeHonoured();
- testThatMultipleRemoveOnOverflowIsFine();
- TEST_DONE();
-}
+using P = LruParam<uint32_t, string>;
+using B = Map<uint32_t, string>;
-void Test::testCache()
-{
+TEST("testCache") {
B m;
cache< CacheParam<P, B> > cache(m, -1);
// Verfify start conditions.
@@ -74,24 +48,29 @@ void Test::testCache()
EXPECT_TRUE(cache.size() == 1);
}
-void Test::testCacheSize()
+TEST("testCacheSize")
{
B m;
cache< CacheParam<P, B> > cache(m, -1);
cache.write(1, "10 bytes string");
EXPECT_EQUAL(80u, cache.sizeBytes());
+ cache.write(1, "10 bytes string"); // Still the same size
+ EXPECT_EQUAL(80u, cache.sizeBytes());
}
-void Test::testCacheSizeDeep()
+TEST("testCacheSizeDeep")
{
B m;
cache< CacheParam<P, B, zero<uint32_t>, size<string> > > cache(m, -1);
cache.write(1, "15 bytes string");
EXPECT_EQUAL(95u, cache.sizeBytes());
+ cache.write(1, "10 bytes s");
+ EXPECT_EQUAL(90u, cache.sizeBytes());
+ cache.write(1, "20 bytes string ssss");
+ EXPECT_EQUAL(100u, cache.sizeBytes());
}
-void Test::testCacheEntriesHonoured()
-{
+TEST("testCacheEntriesHonoured") {
B m;
cache< CacheParam<P, B, zero<uint32_t>, size<string> > > cache(m, -1);
cache.maxElements(1);
@@ -105,8 +84,7 @@ void Test::testCacheEntriesHonoured()
EXPECT_EQUAL(96u, cache.sizeBytes());
}
-void Test::testCacheMaxSizeHonoured()
-{
+TEST("testCacheMaxSizeHonoured") {
B m;
cache< CacheParam<P, B, zero<uint32_t>, size<string> > > cache(m, 200);
cache.write(1, "15 bytes string");
@@ -123,8 +101,7 @@ void Test::testCacheMaxSizeHonoured()
EXPECT_EQUAL(291u, cache.sizeBytes());
}
-void Test::testThatMultipleRemoveOnOverflowIsFine()
-{
+TEST("testThatMultipleRemoveOnOverflowIsFine") {
B m;
cache< CacheParam<P, B, zero<uint32_t>, size<string> > > cache(m, 2000);
@@ -159,5 +136,4 @@ void Test::testThatMultipleRemoveOnOverflowIsFine()
EXPECT_EQUAL(2924u, cache.sizeBytes());
}
-
-TEST_APPHOOK(Test)
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/staging_vespalib/src/vespa/vespalib/objects/identifiable.cpp b/staging_vespalib/src/vespa/vespalib/objects/identifiable.cpp
index 52c9def3036..7e14fbc0014 100644
--- a/staging_vespalib/src/vespa/vespalib/objects/identifiable.cpp
+++ b/staging_vespalib/src/vespa/vespalib/objects/identifiable.cpp
@@ -48,7 +48,7 @@ Register::Register() :
_listByName()
{ }
-Register::~Register() { }
+Register::~Register() = default;
bool Register::erase(Identifiable::RuntimeClass * c)
{
diff --git a/staging_vespalib/src/vespa/vespalib/stllike/cache.h b/staging_vespalib/src/vespa/vespalib/stllike/cache.h
index 3d5ab155877..99b601f7c3c 100644
--- a/staging_vespalib/src/vespa/vespalib/stllike/cache.h
+++ b/staging_vespalib/src/vespa/vespalib/stllike/cache.h
@@ -101,7 +101,7 @@ public:
* Update the cache and write through to backing store.
* Object is then put at head of LRU list.
*/
- void write(const K & key, const V & value);
+ void write(const K & key, V value);
/**
* Tell if an object with given key exists in the cache.
@@ -148,6 +148,7 @@ private:
mutable size_t _race;
mutable size_t _insert;
mutable size_t _write;
+ mutable size_t _update;
mutable size_t _erase;
mutable size_t _invalidate;
mutable size_t _lookup;
diff --git a/staging_vespalib/src/vespa/vespalib/stllike/cache.hpp b/staging_vespalib/src/vespa/vespalib/stllike/cache.hpp
index a8c7d16473c..906621d623c 100644
--- a/staging_vespalib/src/vespa/vespalib/stllike/cache.hpp
+++ b/staging_vespalib/src/vespa/vespalib/stllike/cache.hpp
@@ -61,6 +61,7 @@ cache<P>::cache(BackingStore & b, size_t maxBytes) :
_race(0),
_insert(0),
_write(0),
+ _update(0),
_erase(0),
_invalidate(0),
_lookup(0),
@@ -120,16 +121,25 @@ cache<P>::read(const K & key)
template< typename P >
void
-cache<P>::write(const K & key, const V & value)
+cache<P>::write(const K & key, V value)
{
+ size_t newSize = calcSize(key, value);
vespalib::LockGuard storeGuard(getLock(key));
{
vespalib::LockGuard guard(_hashLock);
- (*this)[key] = value;
- _sizeBytes += calcSize(key, value);
- _write++;
+ if (Lru::hasKey(key)) {
+ _sizeBytes -= calcSize(key, (*this)[key]);
+ _update++;
+ }
}
+
_store.write(key, value);
+ {
+ vespalib::LockGuard guard(_hashLock);
+ (*this)[key] = std::move(value);
+ _sizeBytes += newSize;
+ _write++;
+ }
}
template< typename P >
diff --git a/staging_vespalib/src/vespa/vespalib/util/polymorphicarrays.h b/staging_vespalib/src/vespa/vespalib/util/polymorphicarrays.h
index d76b3f09020..721a33ae7da 100644
--- a/staging_vespalib/src/vespa/vespalib/util/polymorphicarrays.h
+++ b/staging_vespalib/src/vespa/vespalib/util/polymorphicarrays.h
@@ -24,9 +24,8 @@ public:
size_t size() const override { return _array.size(); }
iterator erase(iterator it) override { _array.erase(_array.begin() + (it - this->begin())); return it; }
void push_back(const B & v) override {
- size_t sz(_array.size());
- _array.resize(sz + 1);
- _array[sz].assign(v);
+ _array.emplace_back();
+ _array.back().assign(v);
}
private:
std::vector<T> _array;
diff --git a/staging_vespalib/src/vespa/vespalib/util/process_memory_stats.cpp b/staging_vespalib/src/vespa/vespalib/util/process_memory_stats.cpp
index b94e35407a1..138e7a25803 100644
--- a/staging_vespalib/src/vespa/vespalib/util/process_memory_stats.cpp
+++ b/staging_vespalib/src/vespa/vespalib/util/process_memory_stats.cpp
@@ -83,7 +83,7 @@ std::string getLineHeader(const std::string &line)
}
++len;
}
- abort();
+ LOG_ABORT("should not be reached");
}
}
diff --git a/standalone-container/pom.xml b/standalone-container/pom.xml
index f31072aec6e..608d72c72f2 100644
--- a/standalone-container/pom.xml
+++ b/standalone-container/pom.xml
@@ -14,10 +14,6 @@
<packaging>container-plugin</packaging>
<dependencies>
<dependency>
- <groupId>org.scala-lang</groupId>
- <artifactId>scala-library</artifactId>
- </dependency>
- <dependency>
<groupId>com.yahoo.vespa</groupId>
<artifactId>config-provisioning</artifactId>
<version>${project.version}</version>
diff --git a/standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneContainerApplication.java b/standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneContainerApplication.java
index 72937301954..f5c18f2b255 100644
--- a/standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneContainerApplication.java
+++ b/standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneContainerApplication.java
@@ -230,7 +230,7 @@ public class StandaloneContainerApplication implements Application {
ApplicationPackage applicationPackage = rawApplicationPackage.preprocess(Zone.defaultZone(), logger);
validateApplication(applicationPackage);
DeployState deployState = new DeployState.Builder().applicationPackage(applicationPackage).fileRegistry(fileRegistry)
- .deployLogger(logger).configDefinitionRepo(configDefinitionRepo).build(true);
+ .deployLogger(logger).configDefinitionRepo(configDefinitionRepo).build();
VespaModel root = VespaModel.createIncomplete(deployState);
ApplicationConfigProducerRoot vespaRoot = new ApplicationConfigProducerRoot(root, "vespa", deployState.getDocumentModel(),
diff --git a/standalone-container/src/main/sh/standalone-container.sh b/standalone-container/src/main/sh/standalone-container.sh
index dc1f5c7b71e..f9eddafbfbb 100755
--- a/standalone-container/src/main/sh/standalone-container.sh
+++ b/standalone-container/src/main/sh/standalone-container.sh
@@ -157,7 +157,7 @@ StartCommand() {
-XX:+PreserveFramePointer \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath="$VESPA_HOME/var/crash" \
- -XX:OnOutOfMemoryError="kill -9 %p" \
+ -XX:+ExitOnOutOfMemoryError \
-Djava.library.path="$VESPA_HOME/lib64" \
-Djava.awt.headless=true \
-Dsun.rmi.dgc.client.gcInterval=3600000 \
diff --git a/standalone-container/vespa-standalone-container.spec b/standalone-container/vespa-standalone-container.spec
index 05a126e49b3..281516ae552 100644
--- a/standalone-container/vespa-standalone-container.spec
+++ b/standalone-container/vespa-standalone-container.spec
@@ -48,6 +48,7 @@ declare -a modules=(
container-disc
container-jersey2
container-search-and-docproc
+ container-search-gui
defaults
docprocs
jdisc-security-filters
diff --git a/statistics/src/main/java/com/yahoo/statistics/Statistics.java b/statistics/src/main/java/com/yahoo/statistics/Statistics.java
index 8b2ec64d0fb..b29d011147f 100644
--- a/statistics/src/main/java/com/yahoo/statistics/Statistics.java
+++ b/statistics/src/main/java/com/yahoo/statistics/Statistics.java
@@ -7,7 +7,7 @@ import com.yahoo.container.StatisticsConfig;
* Interface used for registering statistics values and counters for logging.
*
* @author steinar
- * @author tonytv
+ * @author Tony Vaagenes
*/
public interface Statistics {
/**
diff --git a/storage/src/tests/bucketdb/bucketmanagertest.cpp b/storage/src/tests/bucketdb/bucketmanagertest.cpp
index d12e6b90b2a..829a080ad01 100644
--- a/storage/src/tests/bucketdb/bucketmanagertest.cpp
+++ b/storage/src/tests/bucketdb/bucketmanagertest.cpp
@@ -723,6 +723,7 @@ public:
auto createUpdateCommand(const document::BucketId& bucket) const {
auto update = std::make_shared<document::DocumentUpdate>(
+ _self._node->getTestDocMan().getTypeRepo(),
*_self._node->getTestDocMan().getTypeRepo()
.getDocumentType("testdoctype1"),
document::DocumentId("id:foo:testdoctype1::bar2"));
diff --git a/storage/src/tests/bucketdb/judymultimaptest.cpp b/storage/src/tests/bucketdb/judymultimaptest.cpp
index db368ac83b5..43b83b16dec 100644
--- a/storage/src/tests/bucketdb/judymultimaptest.cpp
+++ b/storage/src/tests/bucketdb/judymultimaptest.cpp
@@ -10,6 +10,9 @@
#include <ostream>
#include <vector>
+#include <vespa/log/log.h>
+LOG_SETUP(".judy_multi_map_test");
+
namespace storage {
struct JudyMultiMapTest : public CppUnit::TestFixture {
diff --git a/storage/src/tests/bucketdb/lockablemaptest.cpp b/storage/src/tests/bucketdb/lockablemaptest.cpp
index 6f36e0be711..c94121062ec 100644
--- a/storage/src/tests/bucketdb/lockablemaptest.cpp
+++ b/storage/src/tests/bucketdb/lockablemaptest.cpp
@@ -8,6 +8,9 @@
#include <cppunit/extensions/HelperMacros.h>
#include <boost/operators.hpp>
+#include <vespa/log/log.h>
+LOG_SETUP(".lockable_map_test");
+
namespace storage {
struct LockableMapTest : public CppUnit::TestFixture {
diff --git a/storage/src/tests/distributor/externaloperationhandlertest.cpp b/storage/src/tests/distributor/externaloperationhandlertest.cpp
index 81b0293b0c0..54aca78d13d 100644
--- a/storage/src/tests/distributor/externaloperationhandlertest.cpp
+++ b/storage/src/tests/distributor/externaloperationhandlertest.cpp
@@ -200,6 +200,7 @@ std::shared_ptr<api::UpdateCommand> ExternalOperationHandlerTest::makeUpdateComm
const vespalib::string& doc_type,
const vespalib::string& id) const {
auto update = std::make_shared<document::DocumentUpdate>(
+ _testDocMan.getTypeRepo(),
*_testDocMan.getTypeRepo().getDocumentType(doc_type),
document::DocumentId(id));
return std::make_shared<api::UpdateCommand>(
diff --git a/storage/src/tests/distributor/twophaseupdateoperationtest.cpp b/storage/src/tests/distributor/twophaseupdateoperationtest.cpp
index 29d50b1b9b7..ea2cc00c642 100644
--- a/storage/src/tests/distributor/twophaseupdateoperationtest.cpp
+++ b/storage/src/tests/distributor/twophaseupdateoperationtest.cpp
@@ -294,7 +294,7 @@ TwoPhaseUpdateOperationTest::sendUpdate(const std::string& bucketState,
document::DocumentUpdate::SP update;
if (!options._withError) {
update = std::make_shared<document::DocumentUpdate>(
- *_doc_type,
+ *_repo, *_doc_type,
document::DocumentId(document::DocIdString("test", "test")));
document::FieldUpdate fup(_doc_type->getField("headerval"));
fup.addUpdate(ArithmeticValueUpdate(ArithmeticValueUpdate::Add, 10));
@@ -304,7 +304,7 @@ TwoPhaseUpdateOperationTest::sendUpdate(const std::string& bucketState,
// part of the Get. Just a sneaky way to force an eval error.
auto* badDocType = _repo->getDocumentType("testdoctype2");
update = std::make_shared<document::DocumentUpdate>(
- *badDocType,
+ *_repo, *badDocType,
document::DocumentId(document::DocIdString("test", "test")));
document::FieldUpdate fup(badDocType->getField("onlyinchild"));
fup.addUpdate(ArithmeticValueUpdate(ArithmeticValueUpdate::Add, 10));
@@ -350,9 +350,7 @@ TwoPhaseUpdateOperationTest::testSimple()
MessageSenderStub sender;
cb->start(sender, framework::MilliSecTime(0));
- CPPUNIT_ASSERT_EQUAL(
- std::string("Update => 0"),
- sender.getCommands(true));
+ CPPUNIT_ASSERT_EQUAL(std::string("Update => 0"), sender.getCommands(true));
replyToMessage(*cb, sender, 0, 90);
diff --git a/storage/src/tests/distributor/updateoperationtest.cpp b/storage/src/tests/distributor/updateoperationtest.cpp
index fd880effda9..9ce862f5db8 100644
--- a/storage/src/tests/distributor/updateoperationtest.cpp
+++ b/storage/src/tests/distributor/updateoperationtest.cpp
@@ -60,12 +60,8 @@ public:
close();
}
- void replyToMessage(
- UpdateOperation& callback,
- MessageSenderStub& sender,
- uint32_t index,
- uint64_t oldTimestamp,
- api::BucketInfo info = api::BucketInfo(2,4,6));
+ void replyToMessage(UpdateOperation& callback, MessageSenderStub& sender, uint32_t index,
+ uint64_t oldTimestamp, api::BucketInfo info = api::BucketInfo(2,4,6));
std::shared_ptr<UpdateOperation>
sendUpdate(const std::string& bucketState);
@@ -79,8 +75,7 @@ std::shared_ptr<UpdateOperation>
UpdateOperation_Test::sendUpdate(const std::string& bucketState)
{
document::DocumentUpdate::SP update(
- new document::DocumentUpdate(
- *_html_type,
+ new document::DocumentUpdate(*_repo, *_html_type,
document::DocumentId(document::DocIdString("test", "test"))));
_bId = getExternalOperationHandler().getBucketId(update->getId());
@@ -88,26 +83,18 @@ UpdateOperation_Test::sendUpdate(const std::string& bucketState)
addNodesToBucketDB(_bId, bucketState);
std::shared_ptr<api::UpdateCommand> msg(
- new api::UpdateCommand(makeDocumentBucket(document::BucketId(0)),
- update,
- 100));
+ new api::UpdateCommand(makeDocumentBucket(document::BucketId(0)), update, 100));
ExternalOperationHandler& handler = getExternalOperationHandler();
return std::shared_ptr<UpdateOperation>(
- new UpdateOperation(handler,
- getDistributorBucketSpace(),
- msg,
+ new UpdateOperation(handler, getDistributorBucketSpace(), msg,
getDistributor().getMetrics().updates[msg->getLoadType()]));
}
void
-UpdateOperation_Test::replyToMessage(
- UpdateOperation& callback,
- MessageSenderStub& sender,
- uint32_t index,
- uint64_t oldTimestamp,
- api::BucketInfo info)
+UpdateOperation_Test::replyToMessage(UpdateOperation& callback, MessageSenderStub& sender, uint32_t index,
+ uint64_t oldTimestamp, api::BucketInfo info)
{
std::shared_ptr<api::StorageMessage> msg2 = sender.commands[index];
UpdateCommand* updatec = dynamic_cast<UpdateCommand*>(msg2.get());
@@ -116,8 +103,7 @@ UpdateOperation_Test::replyToMessage(
updateR->setOldTimestamp(oldTimestamp);
updateR->setBucketInfo(info);
- callback.onReceive(sender,
- std::shared_ptr<StorageReply>(reply.release()));
+ callback.onReceive(sender, std::shared_ptr<StorageReply>(reply.release()));
}
void
@@ -129,9 +115,7 @@ UpdateOperation_Test::testSimple()
MessageSenderStub sender;
cb->start(sender, framework::MilliSecTime(0));
- CPPUNIT_ASSERT_EQUAL(
- std::string("Update => 0"),
- sender.getCommands(true));
+ CPPUNIT_ASSERT_EQUAL(std::string("Update => 0"), sender.getCommands(true));
replyToMessage(*cb, sender, 0, 90);
@@ -150,9 +134,7 @@ UpdateOperation_Test::testNotFound()
MessageSenderStub sender;
cb->start(sender, framework::MilliSecTime(0));
- CPPUNIT_ASSERT_EQUAL(
- std::string("Update => 0"),
- sender.getCommands(true));
+ CPPUNIT_ASSERT_EQUAL(std::string("Update => 0"), sender.getCommands(true));
replyToMessage(*cb, sender, 0, 0);
@@ -170,9 +152,7 @@ UpdateOperation_Test::testMultiNode()
MessageSenderStub sender;
cb->start(sender, framework::MilliSecTime(0));
- CPPUNIT_ASSERT_EQUAL(
- std::string("Update => 0,Update => 1"),
- sender.getCommands(true));
+ CPPUNIT_ASSERT_EQUAL(std::string("Update => 0,Update => 1"), sender.getCommands(true));
replyToMessage(*cb, sender, 0, 120);
replyToMessage(*cb, sender, 1, 120);
@@ -198,9 +178,7 @@ UpdateOperation_Test::testMultiNodeInconsistentTimestamp()
MessageSenderStub sender;
cb->start(sender, framework::MilliSecTime(0));
- CPPUNIT_ASSERT_EQUAL(
- std::string("Update => 0,Update => 1"),
- sender.getCommands(true));
+ CPPUNIT_ASSERT_EQUAL(std::string("Update => 0,Update => 1"), sender.getCommands(true));
replyToMessage(*cb, sender, 0, 119);
replyToMessage(*cb, sender, 1, 120);
diff --git a/storage/src/tests/persistence/common/filestortestfixture.cpp b/storage/src/tests/persistence/common/filestortestfixture.cpp
index c92687f798b..835b8ef1044 100644
--- a/storage/src/tests/persistence/common/filestortestfixture.cpp
+++ b/storage/src/tests/persistence/common/filestortestfixture.cpp
@@ -18,19 +18,17 @@ spi::LoadType FileStorTestFixture::defaultLoadType = spi::LoadType(0, "default")
const uint32_t FileStorTestFixture::MSG_WAIT_TIME;
void
-FileStorTestFixture::setupDisks(uint32_t diskCount)
+FileStorTestFixture::setupPersistenceThreads(uint32_t threads)
{
std::string rootOfRoot = "todo-make-unique-filestorefixture";
- _config.reset(new vdstestlib::DirConfig(getStandardConfig(true, rootOfRoot)));
-
- _config2.reset(new vdstestlib::DirConfig(*_config));
- _config2->getConfig("stor-server").set("root_folder", (rootOfRoot + "-vdsroot.2"));
- _config2->getConfig("stor-devices").set("root_folder", (rootOfRoot + "-vdsroot.2"));
- _config2->getConfig("stor-server").set("node_index", "1");
-
- _smallConfig.reset(new vdstestlib::DirConfig(*_config));
- _node.reset(new TestServiceLayerApp(DiskCount(diskCount), NodeIndex(1),
- _config->getConfigId()));
+ _config = std::make_unique<vdstestlib::DirConfig>(getStandardConfig(true, rootOfRoot));
+ _config->getConfig("stor-server").set("root_folder", (rootOfRoot + "-vdsroot.2"));
+ _config->getConfig("stor-devices").set("root_folder", (rootOfRoot + "-vdsroot.2"));
+ _config->getConfig("stor-server").set("node_index", "1");
+ _config->getConfig("stor-filestor").set("num_threads", std::to_string(threads));
+
+ _node = std::make_unique<TestServiceLayerApp>(
+ DiskCount(1), NodeIndex(1), _config->getConfigId());
_testdoctype1 = _node->getTypeRepo()->getDocumentType("testdoctype1");
}
@@ -38,16 +36,15 @@ FileStorTestFixture::setupDisks(uint32_t diskCount)
void
FileStorTestFixture::setUp()
{
- setupDisks(1);
+ setupPersistenceThreads(1);
_node->setPersistenceProvider(
- spi::PersistenceProvider::UP(
- new spi::dummy::DummyPersistence(_node->getTypeRepo(), 1)));
+ std::make_unique<spi::dummy::DummyPersistence>(_node->getTypeRepo(), 1));
}
void
FileStorTestFixture::tearDown()
{
- _node.reset(0);
+ _node.reset();
}
void
@@ -91,7 +88,7 @@ FileStorTestFixture::TestFileStorComponents::TestFileStorComponents(
}
api::StorageMessageAddress
-FileStorTestFixture::TestFileStorComponents::makeSelfAddress() const {
+FileStorTestFixture::makeSelfAddress() {
return api::StorageMessageAddress("storage", lib::NodeType::STORAGE, 0);
}
diff --git a/storage/src/tests/persistence/common/filestortestfixture.h b/storage/src/tests/persistence/common/filestortestfixture.h
index c8158d01224..c46f9de24fc 100644
--- a/storage/src/tests/persistence/common/filestortestfixture.h
+++ b/storage/src/tests/persistence/common/filestortestfixture.h
@@ -19,8 +19,6 @@ public:
std::unique_ptr<TestServiceLayerApp> _node;
std::unique_ptr<vdstestlib::DirConfig> _config;
- std::unique_ptr<vdstestlib::DirConfig> _config2;
- std::unique_ptr<vdstestlib::DirConfig> _smallConfig;
const document::DocumentType* _testdoctype1;
static const uint32_t MSG_WAIT_TIME = 60 * 1000;
@@ -30,10 +28,12 @@ public:
void setUp() override;
void tearDown() override;
- void setupDisks(uint32_t diskCount);
+ void setupPersistenceThreads(uint32_t diskCount);
void createBucket(const document::BucketId& bid);
bool bucketExistsInDb(const document::BucketId& bucket) const;
+ static api::StorageMessageAddress makeSelfAddress();
+
api::ReturnCode::Result resultOf(const api::StorageReply& reply) const {
return reply.getResult().getResult();
}
@@ -99,8 +99,6 @@ public:
const char* testName,
const StorageLinkInjector& i = NoOpStorageLinkInjector());
- api::StorageMessageAddress makeSelfAddress() const;
-
void sendDummyGet(const document::BucketId& bid);
void sendPut(const document::BucketId& bid,
uint32_t docIdx,
diff --git a/storage/src/tests/persistence/filestorage/filestormanagertest.cpp b/storage/src/tests/persistence/filestorage/filestormanagertest.cpp
index 838df87662f..369e820f987 100644
--- a/storage/src/tests/persistence/filestorage/filestormanagertest.cpp
+++ b/storage/src/tests/persistence/filestorage/filestormanagertest.cpp
@@ -2725,6 +2725,7 @@ void FileStorManagerTest::update_command_size_is_added_to_metric() {
document::BucketId bucket(16, 4000);
createBucket(bucket, 0);
auto update = std::make_shared<document::DocumentUpdate>(
+ _node->getTestDocMan().getTypeRepo(),
_node->getTestDocMan().createRandomDocument()->getType(),
document::DocumentId("id:foo:testdoctype1::bar"));
auto cmd = std::make_shared<api::UpdateCommand>(
diff --git a/storage/src/tests/persistence/filestorage/mergeblockingtest.cpp b/storage/src/tests/persistence/filestorage/mergeblockingtest.cpp
index 50999f5883e..d4cec415937 100644
--- a/storage/src/tests/persistence/filestorage/mergeblockingtest.cpp
+++ b/storage/src/tests/persistence/filestorage/mergeblockingtest.cpp
@@ -16,7 +16,7 @@ class MergeBlockingTest : public FileStorTestFixture
{
public:
void setupDisks() {
- FileStorTestFixture::setupDisks(1);
+ FileStorTestFixture::setupPersistenceThreads(1);
_node->setPersistenceProvider(
spi::PersistenceProvider::UP(
new spi::dummy::DummyPersistence(_node->getTypeRepo(), 1)));
diff --git a/storage/src/tests/persistence/filestorage/operationabortingtest.cpp b/storage/src/tests/persistence/filestorage/operationabortingtest.cpp
index 64ef48b5719..e12f48bcdea 100644
--- a/storage/src/tests/persistence/filestorage/operationabortingtest.cpp
+++ b/storage/src/tests/persistence/filestorage/operationabortingtest.cpp
@@ -86,9 +86,9 @@ public:
std::unique_ptr<vespalib::Barrier> _queueBarrier;
std::unique_ptr<vespalib::Barrier> _completionBarrier;
- void setupDisks(uint32_t diskCount, uint32_t queueBarrierThreads) {
- FileStorTestFixture::setupDisks(diskCount);
- _dummyProvider.reset(new spi::dummy::DummyPersistence(_node->getTypeRepo(), diskCount));
+ void setupProviderAndBarriers(uint32_t queueBarrierThreads) {
+ FileStorTestFixture::setupPersistenceThreads(1);
+ _dummyProvider.reset(new spi::dummy::DummyPersistence(_node->getTypeRepo(), 1));
_queueBarrier.reset(new vespalib::Barrier(queueBarrierThreads));
_completionBarrier.reset(new vespalib::Barrier(2));
_blockingProvider = new BlockingMockProvider(*_dummyProvider, *_queueBarrier, *_completionBarrier);
@@ -219,7 +219,7 @@ makeAbortCmd(const Container& buckets)
void
OperationAbortingTest::testAbortMessageClearsRelevantQueuedOperations()
{
- setupDisks(1, 2);
+ setupProviderAndBarriers(2);
TestFileStorComponents c(*this, "testAbortMessageClearsRelevantQueuedOperations");
document::BucketId bucket(16, 1);
createBucket(bucket);
@@ -305,7 +305,7 @@ public:
void
OperationAbortingTest::testWaitForCurrentOperationCompletionForAbortedBucket()
{
- setupDisks(1, 3);
+ setupProviderAndBarriers(3);
TestFileStorComponents c(*this, "testWaitForCurrentOperationCompletionForAbortedBucket");
document::BucketId bucket(16, 1);
@@ -386,7 +386,7 @@ OperationAbortingTest::doTestSpecificOperationsNotAborted(const char* testName,
const std::vector<api::StorageMessage::SP>& msgs,
bool shouldCreateBucketInitially)
{
- setupDisks(1, 2);
+ setupProviderAndBarriers(2);
TestFileStorComponents c(*this, testName);
document::BucketId bucket(16, 1);
document::BucketId blockerBucket(16, 2);
diff --git a/storage/src/tests/persistence/persistencequeuetest.cpp b/storage/src/tests/persistence/persistencequeuetest.cpp
index f31623eed61..e96ad013923 100644
--- a/storage/src/tests/persistence/persistencequeuetest.cpp
+++ b/storage/src/tests/persistence/persistencequeuetest.cpp
@@ -15,86 +15,190 @@ using document::test::makeDocumentBucket;
namespace storage {
-class PersistenceQueueTest : public FileStorTestFixture
-{
+class PersistenceQueueTest : public FileStorTestFixture {
public:
void testFetchNextUnlockedMessageIfBucketLocked();
+ void shared_locked_operations_allow_concurrent_bucket_access();
+ void exclusive_locked_operation_not_started_if_shared_op_active();
+ void shared_locked_operation_not_started_if_exclusive_op_active();
+ void exclusive_locked_operation_not_started_if_exclusive_op_active();
+ void operation_batching_not_allowed_across_different_lock_modes();
- std::shared_ptr<api::StorageMessage>
- createPut(uint64_t bucket, uint64_t docIdx);
+ std::shared_ptr<api::StorageMessage> createPut(uint64_t bucket, uint64_t docIdx);
+ std::shared_ptr<api::StorageMessage> createGet(uint64_t bucket) const;
void setUp() override;
- void tearDown() override;
CPPUNIT_TEST_SUITE(PersistenceQueueTest);
CPPUNIT_TEST(testFetchNextUnlockedMessageIfBucketLocked);
+ CPPUNIT_TEST(shared_locked_operations_allow_concurrent_bucket_access);
+ CPPUNIT_TEST(exclusive_locked_operation_not_started_if_shared_op_active);
+ CPPUNIT_TEST(shared_locked_operation_not_started_if_exclusive_op_active);
+ CPPUNIT_TEST(exclusive_locked_operation_not_started_if_exclusive_op_active);
+ CPPUNIT_TEST(operation_batching_not_allowed_across_different_lock_modes);
CPPUNIT_TEST_SUITE_END();
+
+ struct Fixture {
+ FileStorTestFixture& parent;
+ DummyStorageLink top;
+ std::unique_ptr<DummyStorageLink> dummyManager;
+ ForwardingMessageSender messageSender;
+ documentapi::LoadTypeSet loadTypes;
+ FileStorMetrics metrics;
+ std::unique_ptr<FileStorHandler> filestorHandler;
+ uint32_t stripeId;
+
+ explicit Fixture(FileStorTestFixture& parent);
+ ~Fixture();
+ };
+
+ static constexpr uint16_t _disk = 0;
};
CPPUNIT_TEST_SUITE_REGISTRATION(PersistenceQueueTest);
-void
-PersistenceQueueTest::setUp()
+PersistenceQueueTest::Fixture::Fixture(FileStorTestFixture& parent_)
+ : parent(parent_),
+ top(),
+ dummyManager(std::make_unique<DummyStorageLink>()),
+ messageSender(*dummyManager),
+ loadTypes("raw:"),
+ metrics(loadTypes.getMetricLoadTypes())
{
- setupDisks(1);
- _node->setPersistenceProvider(
- spi::PersistenceProvider::UP(
- new spi::dummy::DummyPersistence(_node->getTypeRepo(), 1)));
+ top.push_back(std::move(dummyManager));
+ top.open();
+
+ metrics.initDiskMetrics(parent._node->getPartitions().size(), loadTypes.getMetricLoadTypes(), 1, 1);
+
+ filestorHandler = std::make_unique<FileStorHandler>(messageSender, metrics, parent._node->getPartitions(),
+ parent._node->getComponentRegister());
+ // getNextMessage will time out if no unlocked buckets are present. Choose a timeout
+ // that is large enough to fail tests with high probability if this is not the case,
+ // and small enough to not slow down testing too much.
+ filestorHandler->setGetNextMessageTimeout(20);
+
+ stripeId = filestorHandler->getNextStripeId(0);
}
-void
-PersistenceQueueTest::tearDown()
-{
- _node.reset(0);
+PersistenceQueueTest::Fixture::~Fixture() = default;
+
+void PersistenceQueueTest::setUp() {
+ setupPersistenceThreads(1);
+ _node->setPersistenceProvider(std::make_unique<spi::dummy::DummyPersistence>(_node->getTypeRepo(), 1));
}
-std::shared_ptr<api::StorageMessage>
-PersistenceQueueTest::createPut(uint64_t bucket, uint64_t docIdx)
-{
- std::ostringstream id;
- id << "id:foo:testdoctype1:n=" << bucket << ":" << docIdx;
- document::Document::SP doc(
- _node->getTestDocMan().createDocument("foobar", id.str()));
- std::shared_ptr<api::PutCommand> cmd(
- new api::PutCommand(makeDocumentBucket(document::BucketId(16, bucket)), doc, 1234));
- cmd->setAddress(api::StorageMessageAddress(
- "storage", lib::NodeType::STORAGE, 0));
+std::shared_ptr<api::StorageMessage> PersistenceQueueTest::createPut(uint64_t bucket, uint64_t docIdx) {
+ std::shared_ptr<document::Document> doc = _node->getTestDocMan().createDocument(
+ "foobar", vespalib::make_string("id:foo:testdoctype1:n=%zu:%zu", bucket, docIdx));
+ auto cmd = std::make_shared<api::PutCommand>(makeDocumentBucket(document::BucketId(16, bucket)), doc, 1234);
+ cmd->setAddress(makeSelfAddress());
return cmd;
}
-void
-PersistenceQueueTest::testFetchNextUnlockedMessageIfBucketLocked()
-{
- DummyStorageLink top;
- DummyStorageLink *dummyManager;
- top.push_back(std::unique_ptr<StorageLink>(dummyManager = new DummyStorageLink));
- top.open();
- ForwardingMessageSender messageSender(*dummyManager);
-
- documentapi::LoadTypeSet loadTypes("raw:");
- FileStorMetrics metrics(loadTypes.getMetricLoadTypes());
- metrics.initDiskMetrics(_node->getPartitions().size(), loadTypes.getMetricLoadTypes(), 1, 1);
-
- FileStorHandler filestorHandler(messageSender, metrics, _node->getPartitions(), _node->getComponentRegister());
- uint32_t stripeId = filestorHandler.getNextStripeId(0);
+std::shared_ptr<api::StorageMessage> PersistenceQueueTest::createGet(uint64_t bucket) const {
+ auto cmd = std::make_shared<api::GetCommand>(
+ makeDocumentBucket(document::BucketId(16, bucket)),
+ document::DocumentId(vespalib::make_string("id:foo:testdoctype1:n=%zu:0", bucket)), "[all]");
+ cmd->setAddress(makeSelfAddress());
+ return cmd;
+}
+void PersistenceQueueTest::testFetchNextUnlockedMessageIfBucketLocked() {
+ Fixture f(*this);
// Send 2 puts, 2 to the first bucket, 1 to the second. Calling
// getNextMessage 2 times should then return a lock on the first bucket,
// then subsequently on the second, skipping the already locked bucket.
// Puts all have same pri, so order is well defined.
- filestorHandler.schedule(createPut(1234, 0), 0);
- filestorHandler.schedule(createPut(1234, 1), 0);
- filestorHandler.schedule(createPut(5432, 0), 0);
+ f.filestorHandler->schedule(createPut(1234, 0), _disk);
+ f.filestorHandler->schedule(createPut(1234, 1), _disk);
+ f.filestorHandler->schedule(createPut(5432, 0), _disk);
- auto lock0 = filestorHandler.getNextMessage(0, stripeId);
+ auto lock0 = f.filestorHandler->getNextMessage(_disk, f.stripeId);
CPPUNIT_ASSERT(lock0.first.get());
CPPUNIT_ASSERT_EQUAL(document::BucketId(16, 1234),
dynamic_cast<api::PutCommand&>(*lock0.second).getBucketId());
- auto lock1 = filestorHandler.getNextMessage(0, stripeId);
+ auto lock1 = f.filestorHandler->getNextMessage(_disk, f.stripeId);
CPPUNIT_ASSERT(lock1.first.get());
CPPUNIT_ASSERT_EQUAL(document::BucketId(16, 5432),
dynamic_cast<api::PutCommand&>(*lock1.second).getBucketId());
}
+void PersistenceQueueTest::shared_locked_operations_allow_concurrent_bucket_access() {
+ Fixture f(*this);
+
+ f.filestorHandler->schedule(createGet(1234), _disk);
+ f.filestorHandler->schedule(createGet(1234), _disk);
+
+ auto lock0 = f.filestorHandler->getNextMessage(_disk, f.stripeId);
+ CPPUNIT_ASSERT(lock0.first.get());
+ CPPUNIT_ASSERT_EQUAL(api::LockingRequirements::Shared, lock0.first->lockingRequirements());
+
+ // Even though we already have a lock on the bucket, Gets allow shared locking and we
+ // should therefore be able to get another lock.
+ auto lock1 = f.filestorHandler->getNextMessage(_disk, f.stripeId);
+ CPPUNIT_ASSERT(lock1.first.get());
+ CPPUNIT_ASSERT_EQUAL(api::LockingRequirements::Shared, lock1.first->lockingRequirements());
+}
+
+void PersistenceQueueTest::exclusive_locked_operation_not_started_if_shared_op_active() {
+ Fixture f(*this);
+
+ f.filestorHandler->schedule(createGet(1234), _disk);
+ f.filestorHandler->schedule(createPut(1234, 0), _disk);
+
+ auto lock0 = f.filestorHandler->getNextMessage(_disk, f.stripeId);
+ CPPUNIT_ASSERT(lock0.first.get());
+ CPPUNIT_ASSERT_EQUAL(api::LockingRequirements::Shared, lock0.first->lockingRequirements());
+
+ // Expected to time out
+ auto lock1 = f.filestorHandler->getNextMessage(_disk, f.stripeId);
+ CPPUNIT_ASSERT(!lock1.first.get());
+}
+
+void PersistenceQueueTest::shared_locked_operation_not_started_if_exclusive_op_active() {
+ Fixture f(*this);
+
+ f.filestorHandler->schedule(createPut(1234, 0), _disk);
+ f.filestorHandler->schedule(createGet(1234), _disk);
+
+ auto lock0 = f.filestorHandler->getNextMessage(_disk, f.stripeId);
+ CPPUNIT_ASSERT(lock0.first.get());
+ CPPUNIT_ASSERT_EQUAL(api::LockingRequirements::Exclusive, lock0.first->lockingRequirements());
+
+ // Expected to time out
+ auto lock1 = f.filestorHandler->getNextMessage(_disk, f.stripeId);
+ CPPUNIT_ASSERT(!lock1.first.get());
+}
+
+void PersistenceQueueTest::exclusive_locked_operation_not_started_if_exclusive_op_active() {
+ Fixture f(*this);
+
+ f.filestorHandler->schedule(createPut(1234, 0), _disk);
+ f.filestorHandler->schedule(createPut(1234, 0), _disk);
+
+ auto lock0 = f.filestorHandler->getNextMessage(_disk, f.stripeId);
+ CPPUNIT_ASSERT(lock0.first.get());
+ CPPUNIT_ASSERT_EQUAL(api::LockingRequirements::Exclusive, lock0.first->lockingRequirements());
+
+ // Expected to time out
+ auto lock1 = f.filestorHandler->getNextMessage(_disk, f.stripeId);
+ CPPUNIT_ASSERT(!lock1.first.get());
+}
+
+void PersistenceQueueTest::operation_batching_not_allowed_across_different_lock_modes() {
+ Fixture f(*this);
+
+ f.filestorHandler->schedule(createPut(1234, 0), _disk);
+ f.filestorHandler->schedule(createGet(1234), _disk);
+
+ auto lock0 = f.filestorHandler->getNextMessage(_disk, f.stripeId);
+ CPPUNIT_ASSERT(lock0.first);
+ CPPUNIT_ASSERT(lock0.second);
+ CPPUNIT_ASSERT_EQUAL(api::LockingRequirements::Exclusive, lock0.first->lockingRequirements());
+
+ f.filestorHandler->getNextMessage(_disk, f.stripeId, lock0);
+ CPPUNIT_ASSERT(!lock0.second);
+}
+
} // namespace storage
diff --git a/storage/src/tests/persistence/persistencetestutils.cpp b/storage/src/tests/persistence/persistencetestutils.cpp
index f15921e447d..368eae104e8 100644
--- a/storage/src/tests/persistence/persistencetestutils.cpp
+++ b/storage/src/tests/persistence/persistencetestutils.cpp
@@ -223,16 +223,11 @@ PersistenceTestUtils::doGetOnDisk(
}
document::DocumentUpdate::SP
-PersistenceTestUtils::createBodyUpdate(
- const document::DocumentId& docId,
- const document::FieldValue& updateValue)
+PersistenceTestUtils::createBodyUpdate(const document::DocumentId& docId, const document::FieldValue& updateValue)
{
- const DocumentType* docType(_env->_component.getTypeRepo()
- ->getDocumentType("testdoctype1"));
- document::DocumentUpdate::SP update(
- new document::DocumentUpdate(*docType, docId));
- std::shared_ptr<document::AssignValueUpdate> assignUpdate(
- new document::AssignValueUpdate(updateValue));
+ const DocumentType* docType(_env->_component.getTypeRepo()->getDocumentType("testdoctype1"));
+ document::DocumentUpdate::SP update(new document::DocumentUpdate(*_env->_component.getTypeRepo(), *docType, docId));
+ std::shared_ptr<document::AssignValueUpdate> assignUpdate(new document::AssignValueUpdate(updateValue));
document::FieldUpdate fieldUpdate(docType->getField("content"));
fieldUpdate.addUpdate(*assignUpdate);
update->addUpdate(fieldUpdate);
@@ -240,16 +235,11 @@ PersistenceTestUtils::createBodyUpdate(
}
document::DocumentUpdate::SP
-PersistenceTestUtils::createHeaderUpdate(
- const document::DocumentId& docId,
- const document::FieldValue& updateValue)
+PersistenceTestUtils::createHeaderUpdate(const document::DocumentId& docId, const document::FieldValue& updateValue)
{
- const DocumentType* docType(_env->_component.getTypeRepo()
- ->getDocumentType("testdoctype1"));
- document::DocumentUpdate::SP update(
- new document::DocumentUpdate(*docType, docId));
- std::shared_ptr<document::AssignValueUpdate> assignUpdate(
- new document::AssignValueUpdate(updateValue));
+ const DocumentType* docType(_env->_component.getTypeRepo()->getDocumentType("testdoctype1"));
+ document::DocumentUpdate::SP update(new document::DocumentUpdate(*_env->_component.getTypeRepo(), *docType, docId));
+ std::shared_ptr<document::AssignValueUpdate> assignUpdate(new document::AssignValueUpdate(updateValue));
document::FieldUpdate fieldUpdate(docType->getField("headerval"));
fieldUpdate.addUpdate(*assignUpdate);
update->addUpdate(fieldUpdate);
diff --git a/storage/src/tests/persistence/persistencetestutils.h b/storage/src/tests/persistence/persistencetestutils.h
index 34d4c397f09..8f883115e9d 100644
--- a/storage/src/tests/persistence/persistencetestutils.h
+++ b/storage/src/tests/persistence/persistencetestutils.h
@@ -35,10 +35,9 @@ struct PersistenceTestEnvironment {
};
class PersistenceTestUtils : public CppUnit::TestFixture {
-private:
+public:
std::unique_ptr<PersistenceTestEnvironment> _env;
-public:
PersistenceTestUtils();
virtual ~PersistenceTestUtils();
diff --git a/storage/src/tests/persistence/testandsettest.cpp b/storage/src/tests/persistence/testandsettest.cpp
index c729df1e7eb..686e10ba5ef 100644
--- a/storage/src/tests/persistence/testandsettest.cpp
+++ b/storage/src/tests/persistence/testandsettest.cpp
@@ -189,7 +189,7 @@ std::unique_ptr<api::UpdateCommand> TestAndSetTest::conditional_update_test(
{
putTestDocument(matchingHeader, timestampOne);
- auto docUpdate = std::make_shared<document::DocumentUpdate>(testDoc->getType(), testDocId);
+ auto docUpdate = std::make_shared<document::DocumentUpdate>(_env->_testDocMan.getTypeRepo(), testDoc->getType(), testDocId);
auto fieldUpdate = document::FieldUpdate(testDoc->getField("content"));
fieldUpdate.addUpdate(document::AssignValueUpdate(NEW_CONTENT));
docUpdate->addUpdate(fieldUpdate);
diff --git a/storage/src/tests/storageserver/changedbucketownershiphandlertest.cpp b/storage/src/tests/storageserver/changedbucketownershiphandlertest.cpp
index 7df598bed97..98ad8761736 100644
--- a/storage/src/tests/storageserver/changedbucketownershiphandlertest.cpp
+++ b/storage/src/tests/storageserver/changedbucketownershiphandlertest.cpp
@@ -597,25 +597,19 @@ ChangedBucketOwnershipHandlerTest::testAbortOutdatedPutOperation()
void
ChangedBucketOwnershipHandlerTest::testAbortOutdatedUpdateCommand()
{
- const document::DocumentType* docType(_testDocRepo.getTypeRepo()
- .getDocumentType("testdoctype1"));
+ const document::DocumentType* docType(_testDocRepo.getTypeRepo().getDocumentType("testdoctype1"));
document::DocumentId docId("id:foo:testdoctype1::bar");
- document::DocumentUpdate::SP update(
- std::make_shared<document::DocumentUpdate>(*docType, docId));
- CPPUNIT_ASSERT(changeAbortsMessage<api::UpdateCommand>(
- getBucketToAbort(), update, api::Timestamp(1234)));
- CPPUNIT_ASSERT(!changeAbortsMessage<api::UpdateCommand>(
- getBucketToAllow(), update, api::Timestamp(1234)));
+ auto update(std::make_shared<document::DocumentUpdate>(_testDocRepo.getTypeRepo(), *docType, docId));
+ CPPUNIT_ASSERT(changeAbortsMessage<api::UpdateCommand>(getBucketToAbort(), update, api::Timestamp(1234)));
+ CPPUNIT_ASSERT(!changeAbortsMessage<api::UpdateCommand>(getBucketToAllow(), update, api::Timestamp(1234)));
}
void
ChangedBucketOwnershipHandlerTest::testAbortOutdatedRemoveCommand()
{
document::DocumentId docId("id:foo:testdoctype1::bar");
- CPPUNIT_ASSERT(changeAbortsMessage<api::RemoveCommand>(
- getBucketToAbort(), docId, api::Timestamp(1234)));
- CPPUNIT_ASSERT(!changeAbortsMessage<api::RemoveCommand>(
- getBucketToAllow(), docId, api::Timestamp(1234)));
+ CPPUNIT_ASSERT(changeAbortsMessage<api::RemoveCommand>(getBucketToAbort(), docId, api::Timestamp(1234)));
+ CPPUNIT_ASSERT(!changeAbortsMessage<api::RemoveCommand>(getBucketToAllow(), docId, api::Timestamp(1234)));
}
void
diff --git a/storage/src/tests/storageserver/documentapiconvertertest.cpp b/storage/src/tests/storageserver/documentapiconvertertest.cpp
index 695ae17c5d4..40d561bd589 100644
--- a/storage/src/tests/storageserver/documentapiconvertertest.cpp
+++ b/storage/src/tests/storageserver/documentapiconvertertest.cpp
@@ -181,7 +181,7 @@ void DocumentApiConverterTest::testForwardedPut()
void DocumentApiConverterTest::testUpdate()
{
- auto update = std::make_shared<document::DocumentUpdate>(_html_type, defaultDocId);
+ auto update = std::make_shared<document::DocumentUpdate>(*_repo, _html_type, defaultDocId);
documentapi::UpdateDocumentMessage updateMsg(update);
updateMsg.setOldTimestamp(1234);
updateMsg.setNewTimestamp(5678);
@@ -327,19 +327,19 @@ DocumentApiConverterTest::testBatchDocumentUpdate()
{
document::DocumentId docId(document::UserDocIdString("userdoc:test:1234:test1"));
- auto update = std::make_shared<document::DocumentUpdate>(_html_type, docId);
+ auto update = std::make_shared<document::DocumentUpdate>(*_repo, _html_type, docId);
updates.push_back(update);
}
{
document::DocumentId docId(document::UserDocIdString("userdoc:test:1234:test2"));
- auto update = std::make_shared<document::DocumentUpdate>(_html_type, docId);
+ auto update = std::make_shared<document::DocumentUpdate>(*_repo, _html_type, docId);
updates.push_back(update);
}
{
document::DocumentId docId(document::UserDocIdString("userdoc:test:1234:test3"));
- auto update = std::make_shared<document::DocumentUpdate>(_html_type, docId);
+ auto update = std::make_shared<document::DocumentUpdate>(*_repo, _html_type, docId);
updates.push_back(update);
}
diff --git a/storage/src/tests/visiting/visitortest.cpp b/storage/src/tests/visiting/visitortest.cpp
index 27281d9b95f..4fc577226ca 100644
--- a/storage/src/tests/visiting/visitortest.cpp
+++ b/storage/src/tests/visiting/visitortest.cpp
@@ -62,7 +62,7 @@ private:
CPPUNIT_TEST(testNormalUsage);
CPPUNIT_TEST(testFailedCreateIterator);
CPPUNIT_TEST(testFailedGetIter);
- CPPUNIT_TEST(testMultipleFailedGetIter);
+ CPPUNIT_TEST(iterators_per_bucket_config_is_ignored_and_hardcoded_to_1);
CPPUNIT_TEST(testDocumentAPIClientError);
CPPUNIT_TEST(testNoDocumentAPIResendingForFailedVisitor);
CPPUNIT_TEST(testIteratorCreatedForFailedVisitor);
@@ -90,7 +90,7 @@ public:
void testNormalUsage();
void testFailedCreateIterator();
void testFailedGetIter();
- void testMultipleFailedGetIter();
+ void iterators_per_bucket_config_is_ignored_and_hardcoded_to_1();
void testDocumentAPIClientError();
void testNoDocumentAPIResendingForFailedVisitor();
void testIteratorCreatedForFailedVisitor();
@@ -592,36 +592,31 @@ VisitorTest::testFailedGetIter()
CPPUNIT_ASSERT(waitUntilNoActiveVisitors());
}
-void
-VisitorTest::testMultipleFailedGetIter()
-{
- initializeTest(TestParams().iteratorsPerBucket(2));
- std::shared_ptr<api::CreateVisitorCommand> cmd(
- makeCreateVisitor());
+void VisitorTest::iterators_per_bucket_config_is_ignored_and_hardcoded_to_1() {
+ initializeTest(TestParams().iteratorsPerBucket(20));
+ auto cmd = makeCreateVisitor();
_top->sendDown(cmd);
sendCreateIteratorReply();
- std::vector<GetIterCommand::SP> getIterCmds(
- fetchMultipleCommands<GetIterCommand>(*_bottom, 2));
-
- sendGetIterReply(*getIterCmds[0],
- api::ReturnCode(api::ReturnCode::BUCKET_NOT_FOUND));
-
- // Wait for an "appropriate" amount of time so that wrongful logic
- // will send a DestroyIteratorCommand before all pending GetIters
- // have been replied to.
- std::this_thread::sleep_for(100ms);
+ auto getIterCmd = fetchSingleCommand<GetIterCommand>(*_bottom);
+ CPPUNIT_ASSERT_EQUAL(spi::IteratorId(1234),
+ getIterCmd->getIteratorId());
+ sendGetIterReply(*getIterCmd);
CPPUNIT_ASSERT_EQUAL(size_t(0), _bottom->getNumCommands());
- sendGetIterReply(*getIterCmds[1],
- api::ReturnCode(api::ReturnCode::BUCKET_DELETED));
+ std::vector<document::Document::SP> docs;
+ std::vector<document::DocumentId> docIds;
+ std::vector<std::string> infoMessages;
+ getMessagesAndReply(_documents.size(), getSession(0), docs, docIds, infoMessages);
+ CPPUNIT_ASSERT_EQUAL(size_t(0), infoMessages.size());
+ CPPUNIT_ASSERT_EQUAL(size_t(0), docIds.size());
- DestroyIteratorCommand::SP destroyIterCmd(
- fetchSingleCommand<DestroyIteratorCommand>(*_bottom));
+ auto destroyIterCmd = fetchSingleCommand<DestroyIteratorCommand>(*_bottom);
- verifyCreateVisitorReply(api::ReturnCode::BUCKET_DELETED, 0, 0);
+ verifyCreateVisitorReply(api::ReturnCode::OK);
CPPUNIT_ASSERT(waitUntilNoActiveVisitors());
+ CPPUNIT_ASSERT_EQUAL(0L, getFailedVisitorDestinationReplyCount());
}
void
diff --git a/storage/src/vespa/storage/bucketdb/judymultimap.hpp b/storage/src/vespa/storage/bucketdb/judymultimap.hpp
index 06d553d38bc..0d80bcfb28e 100644
--- a/storage/src/vespa/storage/bucketdb/judymultimap.hpp
+++ b/storage/src/vespa/storage/bucketdb/judymultimap.hpp
@@ -2,6 +2,7 @@
#pragma once
#include "judymultimap.h"
+#include <vespa/vespalib/util/hdr_abort.h>
#include <vespa/vespalib/util/exceptions.h>
#include <vespa/vespalib/util/array.hpp>
#include <set>
@@ -150,7 +151,7 @@ JudyMultiMap<T0, T1, T2, T3>::operator[](key_type key)
case 1: return _values1[getIndex(it.value())];
case 2: return _values2[getIndex(it.value())];
case 3: return _values3[getIndex(it.value())];
- default: assert(false);
+ default: HDR_ABORT("should not be reached");
}
return T0(); // Avoid warning of no return
}
@@ -289,8 +290,7 @@ JudyMultiMap<T0, T1, T2, T3>::ConstIterator::operator*() const
case 3: return value_type(
_iterator.key(), _parent->_values3[getIndex(_iterator.value())]);
default:
- assert(false);
- abort();
+ HDR_ABORT("should not be reached");
}
}
@@ -299,11 +299,12 @@ typename JudyMultiMap<T0, T1, T2, T3>::mapped_type
JudyMultiMap<T0, T1, T2, T3>::ConstIterator::value() const
{
switch (getType(_iterator.value())) {
- default: assert(false);
case 0: return _parent->_values0[getIndex(_iterator.value())];
case 1: return _parent->_values1[getIndex(_iterator.value())];
case 2: return _parent->_values2[getIndex(_iterator.value())];
case 3: return _parent->_values3[getIndex(_iterator.value())];
+ default:
+ HDR_ABORT("should not be reached");
}
}
diff --git a/storage/src/vespa/storage/bucketdb/lockablemap.hpp b/storage/src/vespa/storage/bucketdb/lockablemap.hpp
index f370a792145..6d700cd6049 100644
--- a/storage/src/vespa/storage/bucketdb/lockablemap.hpp
+++ b/storage/src/vespa/storage/bucketdb/lockablemap.hpp
@@ -2,6 +2,7 @@
#pragma once
#include "lockablemap.h"
+#include <vespa/vespalib/util/hdr_abort.h>
#include <vespa/vespalib/stllike/asciistream.h>
#include <vespa/vespalib/stllike/hash_map.hpp>
#include <vespa/vespalib/stllike/hash_set.hpp>
@@ -264,7 +265,8 @@ LockableMap<Map>::handleDecision(key_type& key, mapped_type& val,
break;
case ABORT: return true;
case CONTINUE: break;
- default: assert(false);
+ default:
+ HDR_ABORT("should not be reached");
}
return false;
}
diff --git a/storage/src/vespa/storage/bucketdb/storbucketdb.cpp b/storage/src/vespa/storage/bucketdb/storbucketdb.cpp
index 81d07319358..df9e8e4e5b5 100644
--- a/storage/src/vespa/storage/bucketdb/storbucketdb.cpp
+++ b/storage/src/vespa/storage/bucketdb/storbucketdb.cpp
@@ -3,6 +3,9 @@
#include "storbucketdb.h"
#include "judymultimap.hpp"
+#include <vespa/log/log.h>
+LOG_SETUP(".storage.bucketdb.stor_bucket_db");
+
namespace storage {
namespace bucketdb {
diff --git a/storage/src/vespa/storage/config/stor-communicationmanager.def b/storage/src/vespa/storage/config/stor-communicationmanager.def
index e29540de064..2a2a840dd4e 100644
--- a/storage/src/vespa/storage/config/stor-communicationmanager.def
+++ b/storage/src/vespa/storage/config/stor-communicationmanager.def
@@ -28,3 +28,16 @@ mbus.compress.type enum {NONE, LZ4, ZSTD} default=LZ4
## TTL for rpc target cache
mbus.rpctargetcache.ttl double default = 600
+
+## Number of threads for mbus threadpool
+## Any value below 1 will be 1.
+mbus.num_threads int default=4
+
+## Enable to use above thread pool for encoding replies
+## False will use network(fnet) thread
+mbus.dispatch_on_encode bool default=true
+
+## Enable to use above thread pool for decoding replies
+## False will use network(fnet) thread
+## Todo: Change default once verified in large scale deployment.
+mbus.dispatch_on_decode bool default=false
diff --git a/storage/src/vespa/storage/distributor/maintenance/maintenancescheduler.cpp b/storage/src/vespa/storage/distributor/maintenance/maintenancescheduler.cpp
index c158b499ebb..5481559cf5a 100644
--- a/storage/src/vespa/storage/distributor/maintenance/maintenancescheduler.cpp
+++ b/storage/src/vespa/storage/distributor/maintenance/maintenancescheduler.cpp
@@ -5,6 +5,9 @@
#include <vespa/storage/distributor/operationstarter.h>
#include <vespa/storage/distributor/operations/idealstate/idealstateoperation.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".storage.distributor.maintenance.maintenance_scheduler");
+
namespace storage::distributor {
MaintenanceScheduler::MaintenanceScheduler(
@@ -83,8 +86,7 @@ MaintenanceScheduler::convertToOperationPriority(MaintenancePriority::Priority p
case MaintenancePriority::VERY_HIGH:
return OperationStarter::Priority(0);
default:
- assert(false);
- abort();
+ LOG_ABORT("should not be reached");
}
}
diff --git a/storage/src/vespa/storage/distributor/operations/external/statbucketlistoperation.h b/storage/src/vespa/storage/distributor/operations/external/statbucketlistoperation.h
index 0fbf3bb9e81..c977ea78f67 100644
--- a/storage/src/vespa/storage/distributor/operations/external/statbucketlistoperation.h
+++ b/storage/src/vespa/storage/distributor/operations/external/statbucketlistoperation.h
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#pragma once
+#include <vespa/vespalib/util/hdr_abort.h>
#include <vespa/storage/distributor/operations/operation.h>
#include <vespa/storage/bucketdb/bucketdatabase.h>
#include <vespa/vespalib/util/sync.h>
@@ -30,7 +31,7 @@ public:
void onReceive(DistributorMessageSender&, const std::shared_ptr<api::StorageReply>&) override
{
// Never called.
- assert(false);
+ HDR_ABORT("should not be reached");
}
void onClose(DistributorMessageSender&) override {}
diff --git a/storage/src/vespa/storage/distributor/throttlingoperationstarter.h b/storage/src/vespa/storage/distributor/throttlingoperationstarter.h
index c6e39a083b1..0b6d89e0570 100644
--- a/storage/src/vespa/storage/distributor/throttlingoperationstarter.h
+++ b/storage/src/vespa/storage/distributor/throttlingoperationstarter.h
@@ -2,6 +2,7 @@
#pragma once
#include "operationstarter.h"
+#include <vespa/vespalib/util/hdr_abort.h>
#include <vespa/storage/distributor/operations/operation.h>
namespace storage::distributor {
@@ -49,11 +50,11 @@ class ThrottlingOperationStarter : public OperationStarter
void onStart(DistributorMessageSender&) override {
// Should never be called directly on the throttled operation
// instance, but rather on its wrapped implementation.
- assert(false);
+ HDR_ABORT("should not be reached");
}
void onReceive(DistributorMessageSender&,
const std::shared_ptr<api::StorageReply>&) override {
- assert(false);
+ HDR_ABORT("should not be reached");
}
};
diff --git a/storage/src/vespa/storage/frameworkimpl/thread/appkiller.cpp b/storage/src/vespa/storage/frameworkimpl/thread/appkiller.cpp
index ba1ce957a0f..f2c5c7cf981 100644
--- a/storage/src/vespa/storage/frameworkimpl/thread/appkiller.cpp
+++ b/storage/src/vespa/storage/frameworkimpl/thread/appkiller.cpp
@@ -12,7 +12,7 @@ void RealAppKiller::kill() {
LOG(info, "Aborting the server to dump core, as we're "
"most likely deadlocked and want a core file "
"to view the stack traces.");
- abort();
+ LOG_ABORT("should not be reached");
}
} // storage
diff --git a/storage/src/vespa/storage/persistence/filestorage/filestorhandler.cpp b/storage/src/vespa/storage/persistence/filestorage/filestorhandler.cpp
index 74baecbf026..0da0fd5ce66 100644
--- a/storage/src/vespa/storage/persistence/filestorage/filestorhandler.cpp
+++ b/storage/src/vespa/storage/persistence/filestorage/filestorhandler.cpp
@@ -71,9 +71,9 @@ FileStorHandler::getNextMessage(uint16_t disk, uint32_t stripeId, LockedMessage&
}
FileStorHandler::BucketLockInterface::SP
-FileStorHandler::lock(const document::Bucket& bucket, uint16_t disk)
+FileStorHandler::lock(const document::Bucket& bucket, uint16_t disk, api::LockingRequirements lockReq)
{
- return _impl->lock(bucket, disk);
+ return _impl->lock(bucket, disk, lockReq);
}
void
diff --git a/storage/src/vespa/storage/persistence/filestorage/filestorhandler.h b/storage/src/vespa/storage/persistence/filestorage/filestorhandler.h
index b74765b17d2..02c959df2f0 100644
--- a/storage/src/vespa/storage/persistence/filestorage/filestorhandler.h
+++ b/storage/src/vespa/storage/persistence/filestorage/filestorhandler.h
@@ -58,8 +58,9 @@ public:
typedef std::shared_ptr<BucketLockInterface> SP;
virtual const document::Bucket &getBucket() const = 0;
+ virtual api::LockingRequirements lockingRequirements() const noexcept = 0;
- virtual ~BucketLockInterface() {};
+ virtual ~BucketLockInterface() = default;
};
typedef std::pair<BucketLockInterface::SP, api::StorageMessage::SP> LockedMessage;
@@ -139,7 +140,7 @@ public:
*
*
*/
- BucketLockInterface::SP lock(const document::Bucket&, uint16_t disk);
+ BucketLockInterface::SP lock(const document::Bucket&, uint16_t disk, api::LockingRequirements lockReq);
/**
* Called by FileStorThread::onBucketDiskMove() after moving file, in case
diff --git a/storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.cpp b/storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.cpp
index 06dfc073f61..f9571228ef9 100644
--- a/storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.cpp
+++ b/storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.cpp
@@ -370,16 +370,16 @@ FileStorHandlerImpl::getNextMessage(uint16_t disk, uint32_t stripeId)
}
std::shared_ptr<FileStorHandler::BucketLockInterface>
-FileStorHandlerImpl::Stripe::lock(const document::Bucket &bucket)
-{
+FileStorHandlerImpl::Stripe::lock(const document::Bucket &bucket, api::LockingRequirements lockReq) {
vespalib::MonitorGuard guard(_lock);
- while (isLocked(guard, bucket)) {
- LOG(spam, "Contending for filestor lock for %s", bucket.getBucketId().toString().c_str());
+ while (isLocked(guard, bucket, lockReq)) {
+ LOG(spam, "Contending for filestor lock for %s with %s access",
+ bucket.getBucketId().toString().c_str(), api::to_string(lockReq));
guard.wait(100);
}
- auto locker = std::make_shared<BucketLock>(guard, *this, bucket, 255, api::MessageType::INTERNAL_ID, 0);
+ auto locker = std::make_shared<BucketLock>(guard, *this, bucket, 255, api::MessageType::INTERNAL_ID, 0, lockReq);
guard.broadcast();
return locker;
@@ -388,9 +388,9 @@ FileStorHandlerImpl::Stripe::lock(const document::Bucket &bucket)
namespace {
struct MultiLockGuard {
std::map<uint16_t, vespalib::Monitor*> monitors;
- std::vector<std::shared_ptr<vespalib::MonitorGuard> > guards;
+ std::vector<std::shared_ptr<vespalib::MonitorGuard>> guards;
- MultiLockGuard() {}
+ MultiLockGuard() = default;
void addLock(vespalib::Monitor& monitor, uint16_t index) {
monitors[index] = &monitor;
@@ -419,8 +419,7 @@ namespace {
return static_cast<const api::RemoveCommand&>(msg).getDocumentId();
break;
default:
- assert(false);
- abort();
+ LOG_ABORT("should not be reached");
}
}
uint32_t findCommonBits(document::BucketId a, document::BucketId b) {
@@ -544,7 +543,7 @@ FileStorHandlerImpl::remapMessage(api::StorageMessage& msg, const document::Buck
} else {
LOG(debug, "Did not remap %s with bucket %s from bucket %s",
cmd.toString().c_str(), cmd.getBucketId().toString().c_str(), source.toString().c_str());
- assert(false);
+ LOG_ABORT("should not be reached");
}
break;
}
@@ -932,7 +931,7 @@ FileStorHandlerImpl::Stripe::getNextMessage(uint32_t timeout, Disk & disk)
PriorityIdx& idx(bmi::get<1>(_queue));
PriorityIdx::iterator iter(idx.begin()), end(idx.end());
- while (iter != end && isLocked(guard, iter->_bucket)) {
+ while (iter != end && isLocked(guard, iter->_bucket, iter->_command->lockingRequirements())) {
iter++;
}
if (iter != end) {
@@ -960,6 +959,13 @@ FileStorHandlerImpl::Stripe::getNextMessage(FileStorHandler::LockedMessage& lck)
}
api::StorageMessage & m(*range.first->_command);
+ // For now, don't allow batching of operations across lock requirement modes.
+ // We might relax this requirement later once we're 100% sure it can't trigger
+ // any unfortunate edge cases.
+ if (lck.first->lockingRequirements() != m.lockingRequirements()) {
+ lck.second.reset();
+ return lck;
+ }
uint64_t waitTime(range.first->_timer.stop(_metrics->averageQueueWaitingTime[m.getLoadType()]));
@@ -993,7 +999,8 @@ FileStorHandlerImpl::Stripe::getMessage(vespalib::MonitorGuard & guard, Priority
if (!messageTimedOutInQueue(*msg, waitTime)) {
auto locker = std::make_unique<BucketLock>(guard, *this, bucket, msg->getPriority(),
- msg->getType().getId(), msg->getMsgId());
+ msg->getType().getId(), msg->getMsgId(),
+ msg->lockingRequirements());
guard.unlock();
return FileStorHandler::LockedMessage(std::move(locker), std::move(msg));
} else {
@@ -1091,10 +1098,65 @@ FileStorHandlerImpl::Stripe::flush()
lockGuard.wait(100);
}
}
+
+void FileStorHandlerImpl::Stripe::release(const document::Bucket & bucket,
+ api::LockingRequirements reqOfReleasedLock,
+ api::StorageMessage::Id lockMsgId) {
+ vespalib::MonitorGuard guard(_lock);
+ auto iter = _lockedBuckets.find(bucket);
+ assert(iter != _lockedBuckets.end());
+ auto& entry = iter->second;
+
+ if (reqOfReleasedLock == api::LockingRequirements::Exclusive) {
+ assert(entry._exclusiveLock);
+ assert(entry._exclusiveLock->msgId == lockMsgId);
+ entry._exclusiveLock.reset();
+ } else {
+ assert(!entry._exclusiveLock);
+ auto shared_iter = entry._sharedLocks.find(lockMsgId);
+ assert(shared_iter != entry._sharedLocks.end());
+ entry._sharedLocks.erase(shared_iter);
+ }
+
+ if (!entry._exclusiveLock && entry._sharedLocks.empty()) {
+ _lockedBuckets.erase(iter); // No more locks held
+ }
+ guard.broadcast();
+}
+
+void FileStorHandlerImpl::Stripe::lock(const vespalib::MonitorGuard &, const document::Bucket & bucket,
+ api::LockingRequirements lockReq, const LockEntry & lockEntry) {
+ auto& entry = _lockedBuckets[bucket];
+ assert(!entry._exclusiveLock);
+ if (lockReq == api::LockingRequirements::Exclusive) {
+ assert(entry._sharedLocks.empty());
+ entry._exclusiveLock = lockEntry;
+ } else {
+ // TODO use a hash set with a custom comparator/hasher instead...?
+ auto inserted = entry._sharedLocks.insert(std::make_pair(lockEntry.msgId, lockEntry));
+ (void) inserted;
+ assert(inserted.second);
+ }
+}
+
bool
-FileStorHandlerImpl::Stripe::isLocked(const vespalib::MonitorGuard &, const document::Bucket& bucket) const noexcept
+FileStorHandlerImpl::Stripe::isLocked(const vespalib::MonitorGuard &, const document::Bucket& bucket,
+ api::LockingRequirements lockReq) const noexcept
{
- return (bucket.getBucketId().getRawId() != 0) && (_lockedBuckets.find(bucket) != _lockedBuckets.end());
+ if (bucket.getBucketId().getRawId() == 0) {
+ return false;
+ }
+ auto iter = _lockedBuckets.find(bucket);
+ if (iter == _lockedBuckets.end()) {
+ return false;
+ }
+ if (iter->second._exclusiveLock) {
+ return true;
+ }
+ // Shared locks can be taken alongside other shared locks, but exclusive locks
+ // require that no shared locks are currently present.
+ return ((lockReq == api::LockingRequirements::Exclusive)
+ && !iter->second._sharedLocks.empty());
}
uint32_t
@@ -1115,33 +1177,26 @@ FileStorHandlerImpl::getQueueSize(uint16_t disk) const
FileStorHandlerImpl::BucketLock::BucketLock(const vespalib::MonitorGuard & guard, Stripe& stripe,
const document::Bucket &bucket, uint8_t priority,
- api::MessageType::Id msgType, api::StorageMessage::Id msgId)
+ api::MessageType::Id msgType, api::StorageMessage::Id msgId,
+ api::LockingRequirements lockReq)
: _stripe(stripe),
- _bucket(bucket)
+ _bucket(bucket),
+ _uniqueMsgId(msgId),
+ _lockReq(lockReq)
{
- (void) guard;
if (_bucket.getBucketId().getRawId() != 0) {
- // Lock the bucket and wait until it is not the current operation for
- // the disk itself.
- _stripe.lock(guard, _bucket, Stripe::LockEntry(priority, msgType, msgId));
- LOG(debug, "Locked bucket %s with priority %u",
- bucket.getBucketId().toString().c_str(), priority);
-
- LOG_BUCKET_OPERATION_SET_LOCK_STATE(
- _bucket.getBucketId(), "acquired filestor lock", false,
- debug::BucketOperationLogger::State::BUCKET_LOCKED);
+ _stripe.lock(guard, _bucket, lockReq, Stripe::LockEntry(priority, msgType, msgId));
+ LOG(debug, "Locked bucket %s for message %zu with priority %u in mode %s",
+ bucket.getBucketId().toString().c_str(), msgId, priority, api::to_string(lockReq));
}
}
-FileStorHandlerImpl::BucketLock::~BucketLock()
-{
+FileStorHandlerImpl::BucketLock::~BucketLock() {
if (_bucket.getBucketId().getRawId() != 0) {
- _stripe.release(_bucket);
- LOG(debug, "Unlocked bucket %s", _bucket.getBucketId().toString().c_str());
- LOG_BUCKET_OPERATION_SET_LOCK_STATE(
- _bucket.getBucketId(), "released filestor lock", true,
- debug::BucketOperationLogger::State::BUCKET_UNLOCKED);
+ _stripe.release(_bucket, _lockReq, _uniqueMsgId);
+ LOG(debug, "Unlocked bucket %s for message %zu in mode %s",
+ _bucket.getBucketId().toString().c_str(), _uniqueMsgId, api::to_string(_lockReq));
}
}
@@ -1183,14 +1238,31 @@ FileStorHandlerImpl::Stripe::dumpQueueHtml(std::ostream & os) const
}
}
+namespace {
+
+void dump_lock_entry(const document::BucketId& bucketId, const FileStorHandlerImpl::Stripe::LockEntry& entry,
+ api::LockingRequirements lock_mode, uint32_t now_ts, std::ostream& os) {
+ os << api::MessageType::get(entry.msgType).getName() << ":" << entry.msgId << " ("
+ << bucketId << ", " << api::to_string(lock_mode)
+ << " lock) Running for " << (now_ts - entry.timestamp) << " secs<br/>\n";
+}
+
+}
+
void
FileStorHandlerImpl::Stripe::dumpActiveHtml(std::ostream & os) const
{
uint32_t now = time(nullptr);
vespalib::MonitorGuard guard(_lock);
for (const auto & e : _lockedBuckets) {
- os << api::MessageType::get(e.second.msgType).getName() << ":" << e.second.msgId << " (" << e.first.getBucketId()
- << ") Running for " << (now - e.second.timestamp) << " secs<br/>\n";
+ if (e.second._exclusiveLock) {
+ dump_lock_entry(e.first.getBucketId(), *e.second._exclusiveLock,
+ api::LockingRequirements::Exclusive, now, os);
+ }
+ for (const auto& shared : e.second._sharedLocks) {
+ dump_lock_entry(e.first.getBucketId(), shared.second,
+ api::LockingRequirements::Shared, now, os);
+ }
}
}
@@ -1239,7 +1311,6 @@ FileStorHandlerImpl::getStatus(std::ostream& out, const framework::HttpUrlPath&
}
for (auto & entry : _mergeStates) {
out << "<b>" << entry.first.toString() << "</b><br>\n";
- // << "<p>" << it->second << "</p>\n"; // Gets very spammy with the complete state here..
}
}
}
diff --git a/storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.h b/storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.h
index 45ac5ded47f..f9dcca4315b 100644
--- a/storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.h
+++ b/storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.h
@@ -30,6 +30,7 @@
#include <vespa/storage/common/messagesender.h>
#include <vespa/vespalib/stllike/hash_map.h>
#include <atomic>
+#include <optional>
namespace storage {
@@ -82,13 +83,19 @@ public:
api::MessageType::Id msgType;
api::StorageMessage::Id msgId;
-
LockEntry() : timestamp(0), priority(0), msgType(), msgId(0) { }
LockEntry(uint8_t priority_, api::MessageType::Id msgType_, api::StorageMessage::Id msgId_)
: timestamp(time(nullptr)), priority(priority_), msgType(msgType_), msgId(msgId_)
{ }
};
+
+ struct MultiLockEntry {
+ std::optional<LockEntry> _exclusiveLock;
+ using SharedLocks = vespalib::hash_map<api::StorageMessage::Id, LockEntry>;
+ SharedLocks _sharedLocks;
+ };
+
Stripe(const FileStorHandlerImpl & owner, MessageSender & messageSender);
~Stripe();
void flush();
@@ -105,19 +112,16 @@ public:
vespalib::MonitorGuard guard(_lock);
return _queue.size();
}
- void release(const document::Bucket & bucket){
- vespalib::MonitorGuard guard(_lock);
- _lockedBuckets.erase(bucket);
- guard.broadcast();
- }
+ void release(const document::Bucket & bucket, api::LockingRequirements reqOfReleasedLock,
+ api::StorageMessage::Id lockMsgId);
- bool isLocked(const vespalib::MonitorGuard &, const document::Bucket&) const noexcept;
+ bool isLocked(const vespalib::MonitorGuard &, const document::Bucket&,
+ api::LockingRequirements lockReq) const noexcept;
- void lock(const vespalib::MonitorGuard &, const document::Bucket & bucket, const LockEntry & lockEntry) {
- _lockedBuckets.insert(std::make_pair(bucket, lockEntry));
- }
+ void lock(const vespalib::MonitorGuard &, const document::Bucket & bucket,
+ api::LockingRequirements lockReq, const LockEntry & lockEntry);
- std::shared_ptr<FileStorHandler::BucketLockInterface> lock(const document::Bucket & bucket);
+ std::shared_ptr<FileStorHandler::BucketLockInterface> lock(const document::Bucket & bucket, api::LockingRequirements lockReq);
void failOperations(const document::Bucket & bucket, const api::ReturnCode & code);
FileStorHandler::LockedMessage getNextMessage(uint32_t timeout, Disk & disk);
@@ -131,9 +135,11 @@ public:
void setMetrics(FileStorStripeMetrics * metrics) { _metrics = metrics; }
private:
bool hasActive(vespalib::MonitorGuard & monitor, const AbortBucketOperationsCommand& cmd) const;
+ // Precondition: the bucket used by `iter`s operation is not locked in a way that conflicts
+ // with its locking requirements.
FileStorHandler::LockedMessage getMessage(vespalib::MonitorGuard & guard, PriorityIdx & idx,
PriorityIdx::iterator iter);
- typedef vespalib::hash_map<document::Bucket, LockEntry, document::Bucket::hash> LockedBuckets;
+ using LockedBuckets = vespalib::hash_map<document::Bucket, MultiLockEntry, document::Bucket::hash>;
const FileStorHandlerImpl &_owner;
MessageSender &_messageSender;
FileStorStripeMetrics *_metrics;
@@ -178,8 +184,8 @@ public:
return _stripes[stripeId].getNextMessage(lck);
}
std::shared_ptr<FileStorHandler::BucketLockInterface>
- lock(const document::Bucket & bucket) {
- return stripe(bucket).lock(bucket);
+ lock(const document::Bucket & bucket, api::LockingRequirements lockReq) {
+ return stripe(bucket).lock(bucket, lockReq);
}
void failOperations(const document::Bucket & bucket, const api::ReturnCode & code) {
stripe(bucket).failOperations(bucket, code);
@@ -194,7 +200,7 @@ public:
// Disperse bucket bits by multiplying with the 64-bit FNV-1 prime.
// This avoids an inherent affinity between the LSB of a bucket's bits
// and the stripe an operation ends up on.
- return bucket.getBucketId().getRawId() * 1099511628211ULL;
+ return bucket.getBucketId().getId() * 1099511628211ULL;
}
Stripe & stripe(const document::Bucket & bucket) {
return _stripes[dispersed_bucket_bits(bucket) % _stripes.size()];
@@ -208,15 +214,20 @@ public:
class BucketLock : public FileStorHandler::BucketLockInterface {
public:
+ // TODO refactor, too many params
BucketLock(const vespalib::MonitorGuard & guard, Stripe& disk, const document::Bucket &bucket,
- uint8_t priority, api::MessageType::Id msgType, api::StorageMessage::Id);
+ uint8_t priority, api::MessageType::Id msgType, api::StorageMessage::Id,
+ api::LockingRequirements lockReq);
~BucketLock();
const document::Bucket &getBucket() const override { return _bucket; }
+ api::LockingRequirements lockingRequirements() const noexcept override { return _lockReq; }
private:
Stripe & _stripe;
document::Bucket _bucket;
+ api::StorageMessage::Id _uniqueMsgId;
+ api::LockingRequirements _lockReq;
};
FileStorHandlerImpl(uint32_t numStripes, MessageSender&, FileStorMetrics&,
@@ -253,8 +264,8 @@ public:
uint32_t getNextStripeId(uint32_t disk);
std::shared_ptr<FileStorHandler::BucketLockInterface>
- lock(const document::Bucket & bucket, uint16_t disk) {
- return _diskInfo[disk].lock(bucket);
+ lock(const document::Bucket & bucket, uint16_t disk, api::LockingRequirements lockReq) {
+ return _diskInfo[disk].lock(bucket, lockReq);
}
void addMergeStatus(const document::Bucket&, MergeStatus::SP);
diff --git a/storage/src/vespa/storage/persistence/messages.h b/storage/src/vespa/storage/persistence/messages.h
index ba7f5979569..d0572e7dbf8 100644
--- a/storage/src/vespa/storage/persistence/messages.h
+++ b/storage/src/vespa/storage/persistence/messages.h
@@ -38,6 +38,9 @@ public:
void setMaxByteSize(uint32_t maxByteSize) { _maxByteSize = maxByteSize; }
uint32_t getMaxByteSize() const { return _maxByteSize; }
+ api::LockingRequirements lockingRequirements() const noexcept override {
+ return api::LockingRequirements::Shared;
+ }
void print(std::ostream& out, bool verbose, const std::string& indent) const override;
private:
@@ -105,6 +108,9 @@ public:
spi::ReadConsistency getReadConsistency() const noexcept {
return _readConsistency;
}
+ api::LockingRequirements lockingRequirements() const noexcept override {
+ return api::LockingRequirements::Shared;
+ }
std::unique_ptr<api::StorageReply> makeReply() override;
diff --git a/storage/src/vespa/storage/persistence/persistenceutil.cpp b/storage/src/vespa/storage/persistence/persistenceutil.cpp
index c2dcb8e2a29..888dc93dd82 100644
--- a/storage/src/vespa/storage/persistence/persistenceutil.cpp
+++ b/storage/src/vespa/storage/persistence/persistenceutil.cpp
@@ -122,9 +122,14 @@ PersistenceUtil::lockAndGetDisk(const document::Bucket &bucket,
result.disk = getPreferredAvailableDisk(bucket);
while (true) {
+ // This function is only called in a context where we require exclusive
+ // locking (split/join). Refactor if this no longer the case.
std::shared_ptr<FileStorHandler::BucketLockInterface> lock(
- _fileStorHandler.lock(bucket, result.disk));
+ _fileStorHandler.lock(bucket, result.disk, api::LockingRequirements::Exclusive));
+ // TODO disks are no longer used in practice, can we safely discard this?
+ // Might need it for synchronization purposes if something has taken the
+ // disk lock _and_ the bucket lock...?
StorBucketDatabase::WrappedEntry entry(getBucketDatabase(bucket.getBucketSpace()).get(
bucket.getBucketId(), "join-lockAndGetDisk-1", flags));
if (entry.exist() && entry->disk != result.disk) {
diff --git a/storage/src/vespa/storage/storageserver/CMakeLists.txt b/storage/src/vespa/storage/storageserver/CMakeLists.txt
index 8c9add78fd4..2df3d3a9606 100644
--- a/storage/src/vespa/storage/storageserver/CMakeLists.txt
+++ b/storage/src/vespa/storage/storageserver/CMakeLists.txt
@@ -8,6 +8,7 @@ vespa_add_library(storage_storageserver
communicationmanager.cpp
communicationmanagermetrics.cpp
configurable_bucket_resolver.cpp
+ config_logging.cpp
distributornode.cpp
distributornodecontext.cpp
documentapiconverter.cpp
diff --git a/storage/src/vespa/storage/storageserver/bouncer.cpp b/storage/src/vespa/storage/storageserver/bouncer.cpp
index 2f8838d14eb..8e882fa867e 100644
--- a/storage/src/vespa/storage/storageserver/bouncer.cpp
+++ b/storage/src/vespa/storage/storageserver/bouncer.cpp
@@ -2,6 +2,7 @@
#include "bouncer.h"
#include "bouncer_metrics.h"
+#include "config_logging.h"
#include <vespa/vdslib/state/cluster_state_bundle.h>
#include <vespa/storageapi/message/state.h>
#include <vespa/storageapi/message/persistence.h>
@@ -69,6 +70,7 @@ Bouncer::onClose()
void
Bouncer::configure(std::unique_ptr<vespa::config::content::core::StorBouncerConfig> config)
{
+ log_config_received(*config);
validateConfig(*config);
vespalib::LockGuard lock(_lock);
_config = std::move(config);
diff --git a/storage/src/vespa/storage/storageserver/bucketintegritychecker.cpp b/storage/src/vespa/storage/storageserver/bucketintegritychecker.cpp
index b4c7d1e3e80..01a84177e43 100644
--- a/storage/src/vespa/storage/storageserver/bucketintegritychecker.cpp
+++ b/storage/src/vespa/storage/storageserver/bucketintegritychecker.cpp
@@ -40,8 +40,7 @@ namespace {
case SchedulingOptions::CONTINUE:
return "Continuing any existing run";
default:
- assert(false);
- abort();
+ LOG_ABORT("should not be reached");
}
}
}
diff --git a/storage/src/vespa/storage/storageserver/communicationmanager.cpp b/storage/src/vespa/storage/storageserver/communicationmanager.cpp
index 94a151bcdc1..65523b62c59 100644
--- a/storage/src/vespa/storage/storageserver/communicationmanager.cpp
+++ b/storage/src/vespa/storage/storageserver/communicationmanager.cpp
@@ -79,7 +79,7 @@ StorageTransportContext::StorageTransportContext(std::unique_ptr<RPCRequestWrapp
: _request(std::move(request))
{ }
-StorageTransportContext::~StorageTransportContext() { }
+StorageTransportContext::~StorageTransportContext() = default;
void
CommunicationManager::receiveStorageReply(const std::shared_ptr<api::StorageReply>& reply)
@@ -278,13 +278,13 @@ void CommunicationManager::fail_with_unresolvable_bucket_space(
namespace {
struct PlaceHolderBucketResolver : public BucketResolver {
- virtual document::Bucket bucketFromId(const document::DocumentId &) const override {
+ document::Bucket bucketFromId(const document::DocumentId &) const override {
return document::Bucket(FixedBucketSpaces::default_space(), document::BucketId(0));
}
- virtual document::BucketSpace bucketSpaceFromName(const vespalib::string &) const override {
+ document::BucketSpace bucketSpaceFromName(const vespalib::string &) const override {
return FixedBucketSpaces::default_space();
}
- virtual vespalib::string nameFromBucketSpace(const document::BucketSpace &bucketSpace) const override {
+ vespalib::string nameFromBucketSpace(const document::BucketSpace &bucketSpace) const override {
assert(bucketSpace == FixedBucketSpaces::default_space());
return FixedBucketSpaces::to_string(bucketSpace);
}
@@ -423,6 +423,9 @@ void CommunicationManager::configure(std::unique_ptr<CommunicationManagerConfig>
params.setSlobrokConfig(_configUri);
params.setConnectionExpireSecs(config->mbus.rpctargetcache.ttl);
+ params.setNumThreads(std::max(1, config->mbus.numThreads));
+ params.setDispatchOnDecode(config->mbus.dispatchOnDecode);
+ params.setDispatchOnEncode(config->mbus.dispatchOnEncode);
params.setIdentity(mbus::Identity(_component.getIdentity()));
if (config->mbusport != -1) {
diff --git a/storage/src/vespa/storage/storageserver/config_logging.cpp b/storage/src/vespa/storage/storageserver/config_logging.cpp
new file mode 100644
index 00000000000..0996636fff5
--- /dev/null
+++ b/storage/src/vespa/storage/storageserver/config_logging.cpp
@@ -0,0 +1,22 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "config_logging.h"
+#include <vespa/config/configgen/configinstance.h>
+#include <vespa/config/print/configdatabuffer.h>
+#include <vespa/vespalib/data/slime/slime.h>
+
+#include <vespa/log/log.h>
+
+LOG_SETUP(".storageserver.config_logging");
+
+namespace storage {
+
+void log_config_received(const config::ConfigInstance& cfg) {
+ if (LOG_WOULD_LOG(debug)) {
+ config::ConfigDataBuffer buf;
+ cfg.serialize(buf);
+ LOG(debug, "Received new %s config: %s", cfg.defName().c_str(), buf.slimeObject().toString().c_str());
+ }
+}
+
+}
diff --git a/storage/src/vespa/storage/storageserver/config_logging.h b/storage/src/vespa/storage/storageserver/config_logging.h
new file mode 100644
index 00000000000..94ef27a784c
--- /dev/null
+++ b/storage/src/vespa/storage/storageserver/config_logging.h
@@ -0,0 +1,11 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+namespace config { class ConfigInstance; }
+
+namespace storage {
+
+void log_config_received(const config::ConfigInstance& cfg);
+
+}
diff --git a/storage/src/vespa/storage/storageserver/mergethrottler.cpp b/storage/src/vespa/storage/storageserver/mergethrottler.cpp
index a15b1b98d63..370f1c85241 100644
--- a/storage/src/vespa/storage/storageserver/mergethrottler.cpp
+++ b/storage/src/vespa/storage/storageserver/mergethrottler.cpp
@@ -1211,7 +1211,7 @@ MergeThrottler::handleOutdatedMerges(const api::SetSystemStateCommand& cmd)
markActiveMergesAsAborted(minimumVersion);
} catch (std::exception& e) {
LOG(error, "Received exception during merge aborting: %s", e.what());
- abort();
+ LOG_ABORT("should not be reached");
}
// Rendezvous released on scope exit
diff --git a/storage/src/vespa/storage/storageserver/storagenode.cpp b/storage/src/vespa/storage/storageserver/storagenode.cpp
index 6bb2ca31ec1..ed33f3846c1 100644
--- a/storage/src/vespa/storage/storageserver/storagenode.cpp
+++ b/storage/src/vespa/storage/storageserver/storagenode.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 "config_logging.h"
#include "storagenode.h"
#include "communicationmanager.h"
#include "statemanager.h"
@@ -16,6 +17,7 @@
#include <fcntl.h>
#include <vespa/log/log.h>
+
LOG_SETUP(".node.server");
using vespa::config::content::StorDistributionConfigBuilder;
@@ -466,12 +468,12 @@ StorageNode::shutdown()
LOG(debug, "Done shutting down node");
}
-void StorageNode::configure(std::unique_ptr<StorServerConfig> config)
-{
- // When we get config, we try to grab the config lock to ensure noone
- // else is doing configuration work, and then we write the new config
- // to a variable where we can find it later when processing config
- // updates
+void StorageNode::configure(std::unique_ptr<StorServerConfig> config) {
+ log_config_received(*config);
+ // When we get config, we try to grab the config lock to ensure noone
+ // else is doing configuration work, and then we write the new config
+ // to a variable where we can find it later when processing config
+ // updates
{
vespalib::LockGuard configLockGuard(_configLock);
_newServerConfig = std::move(config);
@@ -482,13 +484,8 @@ void StorageNode::configure(std::unique_ptr<StorServerConfig> config)
}
}
-void
-StorageNode::configure(std::unique_ptr<UpgradingConfig> config)
-{
- // When we get config, we try to grab the config lock to ensure noone
- // else is doing configuration work, and then we write the new config
- // to a variable where we can find it later when processing config
- // updates
+void StorageNode::configure(std::unique_ptr<UpgradingConfig> config) {
+ log_config_received(*config);
{
vespalib::LockGuard configLockGuard(_configLock);
_newClusterConfig = std::move(config);
@@ -499,13 +496,8 @@ StorageNode::configure(std::unique_ptr<UpgradingConfig> config)
}
}
-void
-StorageNode::configure(std::unique_ptr<StorDistributionConfig> config)
-{
- // When we get config, we try to grab the config lock to ensure noone
- // else is doing configuration work, and then we write the new config
- // to a variable where we can find it later when processing config
- // updates
+void StorageNode::configure(std::unique_ptr<StorDistributionConfig> config) {
+ log_config_received(*config);
{
vespalib::LockGuard configLockGuard(_configLock);
_newDistributionConfig = std::move(config);
@@ -516,9 +508,8 @@ StorageNode::configure(std::unique_ptr<StorDistributionConfig> config)
}
}
-void
-StorageNode::configure(std::unique_ptr<StorPrioritymappingConfig> config)
-{
+void StorageNode::configure(std::unique_ptr<StorPrioritymappingConfig> config) {
+ log_config_received(*config);
{
vespalib::LockGuard configLockGuard(_configLock);
_newPriorityConfig = std::move(config);
@@ -533,6 +524,7 @@ void
StorageNode::configure(std::unique_ptr<document::DocumenttypesConfig> config,
bool hasChanged, int64_t generation)
{
+ log_config_received(*config);
(void) generation;
if (!hasChanged)
return;
@@ -546,9 +538,8 @@ StorageNode::configure(std::unique_ptr<document::DocumenttypesConfig> config,
}
}
-void
-StorageNode::configure(std::unique_ptr<BucketspacesConfig> config)
-{
+void StorageNode::configure(std::unique_ptr<BucketspacesConfig> config) {
+ log_config_received(*config);
{
vespalib::LockGuard configLockGuard(_configLock);
_newBucketSpacesConfig = std::move(config);
diff --git a/storage/src/vespa/storage/visiting/stor-visitor.def b/storage/src/vespa/storage/visiting/stor-visitor.def
index 1e80f2993a5..6f16bcb60a2 100644
--- a/storage/src/vespa/storage/visiting/stor-visitor.def
+++ b/storage/src/vespa/storage/visiting/stor-visitor.def
@@ -24,6 +24,7 @@ defaultparalleliterators int default=8
## will be 16 requests to persistence layer, but only 8 will be able to execute
## at the same time, since only one operation can be executed at the same time
## for one bucket)
+## DEPRECATED: ignored by backend, 1 is always used.
iterators_per_bucket int default=1
## Default number of maximum client replies pending.
diff --git a/storage/src/vespa/storage/visiting/visitorthread.cpp b/storage/src/vespa/storage/visiting/visitorthread.cpp
index a8f31514eb1..b12a1eb6e7f 100644
--- a/storage/src/vespa/storage/visiting/visitorthread.cpp
+++ b/storage/src/vespa/storage/visiting/visitorthread.cpp
@@ -637,7 +637,6 @@ VisitorThread::onInternal(const std::shared_ptr<api::InternalCommand>& cmd)
_ignoreNonExistingVisitorTimeLimit
= config.ignorenonexistingvisitortimelimit;
_defaultParallelIterators = config.defaultparalleliterators;
- _iteratorsPerBucket = config.iteratorsPerBucket;
_defaultPendingMessages = config.defaultpendingmessages;
_defaultDocBlockSize = config.defaultdocblocksize;
_visitorMemoryUsageLimit = config.visitorMemoryUsageLimit;
@@ -647,12 +646,6 @@ VisitorThread::onInternal(const std::shared_ptr<api::InternalCommand>& cmd)
LOG(config, "Cannot use value of defaultParallelIterators < 1");
_defaultParallelIterators = 1;
}
- if (_iteratorsPerBucket < 1 && _iteratorsPerBucket > 10) {
- if (_iteratorsPerBucket < 1) _iteratorsPerBucket = 1;
- else _iteratorsPerBucket = 10;
- LOG(config, "Invalid value of iterators per bucket %u using %u",
- config.iteratorsPerBucket, _iteratorsPerBucket);
- }
if (_defaultPendingMessages < 1) {
LOG(config, "Cannot use value of defaultPendingMessages < 1");
_defaultPendingMessages = 1;
diff --git a/storageapi/src/tests/mbusprot/storageprotocoltest.cpp b/storageapi/src/tests/mbusprot/storageprotocoltest.cpp
index a809d80e0f5..cfc69bcde45 100644
--- a/storageapi/src/tests/mbusprot/storageprotocoltest.cpp
+++ b/storageapi/src/tests/mbusprot/storageprotocoltest.cpp
@@ -294,7 +294,7 @@ void
StorageProtocolTest::testUpdate51()
{
ScopedName test("testUpdate51");
- document::DocumentUpdate::SP update(new document::DocumentUpdate(*_testDoc->getDataType(), _testDoc->getId()));
+ document::DocumentUpdate::SP update(new document::DocumentUpdate(_docMan.getTypeRepo(), *_testDoc->getDataType(), _testDoc->getId()));
std::shared_ptr<document::AssignValueUpdate> assignUpdate(new document::AssignValueUpdate(document::IntFieldValue(17)));
document::FieldUpdate fieldUpdate(_testDoc->getField("headerval"));
fieldUpdate.addUpdate(*assignUpdate);
@@ -912,7 +912,7 @@ StorageProtocolTest::testUpdateCommand52()
{
ScopedName test("testUpdateCommand52");
- document::DocumentUpdate::SP update(new document::DocumentUpdate(*_testDoc->getDataType(), _testDoc->getId()));
+ document::DocumentUpdate::SP update(new document::DocumentUpdate(_docMan.getTypeRepo(), *_testDoc->getDataType(), _testDoc->getId()));
UpdateCommand::SP cmd(new UpdateCommand(_bucket, update, 14));
cmd->setCondition(TestAndSetCondition(CONDITION_STRING));
diff --git a/storageapi/src/vespa/storageapi/mbusprot/protocolserialization5_0.cpp b/storageapi/src/vespa/storageapi/mbusprot/protocolserialization5_0.cpp
index 44050264484..47b77af74a5 100644
--- a/storageapi/src/vespa/storageapi/mbusprot/protocolserialization5_0.cpp
+++ b/storageapi/src/vespa/storageapi/mbusprot/protocolserialization5_0.cpp
@@ -228,9 +228,8 @@ ProtocolSerialization5_0::onDecodeUpdateCommand(BBuf& buf) const
uint32_t size = SH::getInt(buf);
if (size != 0) {
- document::ByteBuffer bbuf(buf.getBufferAtPos(), size);
+ update = document::DocumentUpdate::createHEAD(getTypeRepo(), vespalib::nbostream(buf.getBufferAtPos(), size));
buf.incPos(size);
- update = document::DocumentUpdate::createHEAD(getTypeRepo(), bbuf);
}
document::Bucket bucket = getBucket(buf);
diff --git a/storageapi/src/vespa/storageapi/message/persistence.h b/storageapi/src/vespa/storageapi/message/persistence.h
index bda1bd0f038..59934154cf5 100644
--- a/storageapi/src/vespa/storageapi/message/persistence.h
+++ b/storageapi/src/vespa/storageapi/message/persistence.h
@@ -24,7 +24,7 @@ class TestAndSetCommand : public BucketInfoCommand {
TestAndSetCondition _condition;
public:
TestAndSetCommand(const MessageType & messageType, const document::Bucket &bucket);
- ~TestAndSetCommand();
+ ~TestAndSetCommand() override;
void setCondition(const TestAndSetCondition & condition) { _condition = condition; }
const TestAndSetCondition & getCondition() const { return _condition; }
@@ -49,7 +49,7 @@ class PutCommand : public TestAndSetCommand {
public:
PutCommand(const document::Bucket &bucket, const DocumentSP&, Timestamp);
- ~PutCommand();
+ ~PutCommand() override;
void setTimestamp(Timestamp ts) { _timestamp = ts; }
@@ -86,7 +86,7 @@ class PutReply : public BucketInfoReply {
public:
explicit PutReply(const PutCommand& cmd, bool wasFound = true);
- ~PutReply();
+ ~PutReply() override;
const document::DocumentId& getDocumentId() const { return _docId; }
bool hasDocument() const { return _document.get(); }
@@ -116,7 +116,7 @@ class UpdateCommand : public TestAndSetCommand {
public:
UpdateCommand(const document::Bucket &bucket,
const std::shared_ptr<document::DocumentUpdate>&, Timestamp);
- ~UpdateCommand();
+ ~UpdateCommand() override;
void setTimestamp(Timestamp ts) { _timestamp = ts; }
void setOldTimestamp(Timestamp ts) { _oldTimestamp = ts; }
@@ -147,7 +147,7 @@ class UpdateReply : public BucketInfoReply {
public:
UpdateReply(const UpdateCommand& cmd, Timestamp oldTimestamp = 0);
- ~UpdateReply();
+ ~UpdateReply() override;
void setOldTimestamp(Timestamp ts) { _oldTimestamp = ts; }
@@ -189,7 +189,7 @@ class GetCommand : public BucketInfoCommand {
public:
GetCommand(const document::Bucket &bucket, const document::DocumentId&,
const vespalib::stringref & fieldSet, Timestamp before = MAX_TIMESTAMP);
- ~GetCommand();
+ ~GetCommand() override;
void setBeforeTimestamp(Timestamp ts) { _beforeTimestamp = ts; }
const document::DocumentId& getDocumentId() const { return _docId; }
Timestamp getBeforeTimestamp() const { return _beforeTimestamp; }
@@ -199,6 +199,10 @@ public:
vespalib::string getSummary() const override;
void print(std::ostream& out, bool verbose, const std::string& indent) const override;
+ api::LockingRequirements lockingRequirements() const noexcept override {
+ return api::LockingRequirements::Shared;
+ }
+
DECLARE_STORAGECOMMAND(GetCommand, onGet)
};
@@ -219,7 +223,7 @@ public:
GetReply(const GetCommand& cmd,
const DocumentSP& doc = DocumentSP(),
Timestamp lastModified = 0);
- ~GetReply();
+ ~GetReply() override;
const DocumentSP& getDocument() const { return _doc; }
const document::DocumentId& getDocumentId() const { return _docId; }
@@ -245,7 +249,7 @@ class RemoveCommand : public TestAndSetCommand {
public:
RemoveCommand(const document::Bucket &bucket, const document::DocumentId& docId, Timestamp timestamp);
- ~RemoveCommand();
+ ~RemoveCommand() override;
void setTimestamp(Timestamp ts) { _timestamp = ts; }
const document::DocumentId& getDocumentId() const override { return _docId; }
@@ -267,7 +271,7 @@ class RemoveReply : public BucketInfoReply {
Timestamp _oldTimestamp;
public:
explicit RemoveReply(const RemoveCommand& cmd, Timestamp oldTimestamp = 0);
- ~RemoveReply();
+ ~RemoveReply() override;
const document::DocumentId& getDocumentId() const { return _docId; }
Timestamp getTimestamp() { return _timestamp; };
@@ -289,7 +293,7 @@ class RevertCommand : public BucketInfoCommand {
public:
RevertCommand(const document::Bucket &bucket,
const std::vector<Timestamp>& revertTokens);
- ~RevertCommand();
+ ~RevertCommand() override;
const std::vector<Timestamp>& getRevertTokens() const { return _tokens; }
void print(std::ostream& out, bool verbose, const std::string& indent) const override;
DECLARE_STORAGECOMMAND(RevertCommand, onRevert)
@@ -305,7 +309,7 @@ class RevertReply : public BucketInfoReply {
std::vector<Timestamp> _tokens;
public:
explicit RevertReply(const RevertCommand& cmd);
- ~RevertReply();
+ ~RevertReply() override;
const std::vector<Timestamp>& getRevertTokens() const { return _tokens; }
void print(std::ostream& out, bool verbose, const std::string& indent) const override;
DECLARE_STORAGEREPLY(RevertReply, onRevertReply)
diff --git a/storageapi/src/vespa/storageapi/messageapi/storagemessage.cpp b/storageapi/src/vespa/storageapi/messageapi/storagemessage.cpp
index f970091f695..380d846dd93 100644
--- a/storageapi/src/vespa/storageapi/messageapi/storagemessage.cpp
+++ b/storageapi/src/vespa/storageapi/messageapi/storagemessage.cpp
@@ -302,4 +302,19 @@ StorageMessage::getSummary() const {
return toString();
}
+const char* to_string(LockingRequirements req) noexcept {
+ switch (req) {
+ case LockingRequirements::Exclusive:
+ return "Exclusive";
+ case LockingRequirements::Shared:
+ return "Shared";
+ }
+ assert(false);
+}
+
+std::ostream& operator<<(std::ostream& os, LockingRequirements req) {
+ os << to_string(req);
+ return os;
+}
+
}
diff --git a/storageapi/src/vespa/storageapi/messageapi/storagemessage.h b/storageapi/src/vespa/storageapi/messageapi/storagemessage.h
index dadb68c644d..6c561f3af21 100644
--- a/storageapi/src/vespa/storageapi/messageapi/storagemessage.h
+++ b/storageapi/src/vespa/storageapi/messageapi/storagemessage.h
@@ -19,6 +19,7 @@
#include <vespa/document/bucket/bucket.h>
#include <vespa/vespalib/util/printable.h>
#include <map>
+#include <iosfwd>
namespace vespalib {
class asciistream;
@@ -306,6 +307,20 @@ struct TransportContext {
virtual ~TransportContext() = 0;
};
+enum class LockingRequirements : uint8_t {
+ // Operations with exclusive locking can only be executed iff no other
+ // exclusive or shared locks are taken for its bucket.
+ Exclusive = 0,
+ // Operations with shared locking can only be executed iff no exclusive
+ // lock is taken for its bucket. Should only be used for read-only operations
+ // that cannot mutate a bucket's state.
+ Shared
+};
+
+const char* to_string(LockingRequirements req) noexcept;
+
+std::ostream& operator<<(std::ostream&, LockingRequirements);
+
class StorageMessage : public vespalib::Printable
{
friend class StorageMessageTest; // Used for testing only
@@ -421,6 +436,10 @@ public:
virtual document::Bucket getBucket() const { return getDummyBucket(); }
document::BucketId getBucketId() const { return getBucket().getBucketId(); }
virtual bool hasSingleBucketId() const { return false; }
+ virtual LockingRequirements lockingRequirements() const noexcept {
+ // Safe default: assume exclusive locking is required.
+ return LockingRequirements::Exclusive;
+ }
};
}
diff --git a/storageframework/src/vespa/storageframework/defaultimplementation/thread/threadpoolimpl.cpp b/storageframework/src/vespa/storageframework/defaultimplementation/thread/threadpoolimpl.cpp
index 09c805b2b85..affeae44c04 100644
--- a/storageframework/src/vespa/storageframework/defaultimplementation/thread/threadpoolimpl.cpp
+++ b/storageframework/src/vespa/storageframework/defaultimplementation/thread/threadpoolimpl.cpp
@@ -4,6 +4,8 @@
#include "threadimpl.h"
#include <vespa/vespalib/util/exceptions.h>
#include <thread>
+#include <vespa/log/log.h>
+LOG_SETUP(".storageframework.thread_pool_impl");
using namespace std::chrono_literals;
using vespalib::IllegalStateException;
@@ -36,7 +38,7 @@ ThreadPoolImpl::~ThreadPoolImpl()
if (i > 1000) {
fprintf(stderr, "Failed to kill thread pool. Threads won't die. (And if allowing thread pool object"
" to be deleted this will create a segfault later)\n");
- abort();
+ LOG_ABORT("should not be reached");
}
std::this_thread::sleep_for(10ms);
}
diff --git a/storageserver/src/vespa/storageserver/app/servicelayerprocess.cpp b/storageserver/src/vespa/storageserver/app/servicelayerprocess.cpp
index 2ca29223254..377febb6cc2 100644
--- a/storageserver/src/vespa/storageserver/app/servicelayerprocess.cpp
+++ b/storageserver/src/vespa/storageserver/app/servicelayerprocess.cpp
@@ -4,6 +4,9 @@
#include <vespa/storage/storageserver/servicelayernode.h>
#include <vespa/searchvisitor/searchvisitor.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".storageserver.service_layer_process");
+
namespace storage {
ServiceLayerProcess::ServiceLayerProcess(const config::ConfigUri & configUri)
diff --git a/streamingvisitors/src/tests/searchvisitor/searchvisitor.cpp b/streamingvisitors/src/tests/searchvisitor/searchvisitor.cpp
index 67b662fb16b..8d3d291c94f 100644
--- a/streamingvisitors/src/tests/searchvisitor/searchvisitor.cpp
+++ b/streamingvisitors/src/tests/searchvisitor/searchvisitor.cpp
@@ -1,4 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
#include <vespa/document/base/testdocrepo.h>
#include <vespa/document/repo/documenttyperepo.h>
#include <vespa/vespalib/testkit/testapp.h>
@@ -10,6 +11,9 @@
#include <vespa/storage/frameworkimpl/component/storagecomponentregisterimpl.h>
#include <vespa/storageframework/defaultimplementation/clock/fakeclock.h>
+#include <vespa/log/log.h>
+LOG_SETUP("searchvisitor_test");
+
using namespace search;
using namespace search::query;
using namespace document;
diff --git a/vagrant/README.md b/vagrant/README.md
index d81e80e8808..d5212692638 100644
--- a/vagrant/README.md
+++ b/vagrant/README.md
@@ -1,6 +1,6 @@
-<!-- Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
+<!-- Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
-# Create C++ dev environment on CentOS using VirtualBox and Vagrant
+# Create C++ / Java dev environment on CentOS using VirtualBox and Vagrant
## Prerequisites
* [Install VirtualBox](https://www.virtualbox.org/wiki/Downloads)
@@ -12,16 +12,41 @@
cd <vespa-source>/vagrant
-#### 2. Install Vagrant VirtualBox Guest Additions plugin
+#### 2. Choose dev environment
+
+##### a. For a dev environment with plain centos/7 and no GUI:
+
+ export VESPA_VAGRANT_VM_BOX=centos/7
+ export VESPA_VAGRANT_DISABLE_GUI=true
+
+##### b. For a dev environment with GUI and CLion:
+
+Create centos7-desktop box:
+
+* Install packer by following guide at [packer.io](https://www.packer.io/intro/getting-started/install.html)
+
+* Clone boxcutter centos repo and build the box:
+```
+git clone https://github.com/boxcutter/centos.git
+./bin/box build centos7-desktop.json virtualbox
+```
+
+Example exports:
+
+ export VESPA_VAGRANT_VM_BOX="centos7-desktop"
+ export VESPA_VAGRANT_VM_BOX_URL="$HOME/git/boxcutter/centos/box/virtualbox/centos7-desktop-xx.yyyy.z.box"
+
+
+#### 3. Install Vagrant VirtualBox Guest Additions plugin
This is required for mounting shared folders and get mouse pointer integration and seamless windows in the virtual CentOS desktop.
vagrant plugin install vagrant-vbguest
-#### 3. Start and provision the environment
+#### 4. Start and provision the environment
vagrant up
-#### 4. Connect to machine via SSH
+#### 5. Connect to machine via SSH
SSH agent forwarding is enabled to ensure easy interaction with GitHub inside the machine.
vagrant ssh
@@ -31,6 +56,10 @@ This is needed in order to compile and run tests fast on the local file system i
git clone git@github.com:vespa-engine/vespa.git
+## Build Java modules
+Please follow the build instructions described [here](../README.md#build-java-modules).
+
+
## Build C++ modules
Please follow the build instructions described [here](../README.md#build-c-modules).
Skip these steps if doing development with CLion.
diff --git a/vagrant/Vagrantfile b/vagrant/Vagrantfile
index 90b47af3944..f15f45d75a0 100644
--- a/vagrant/Vagrantfile
+++ b/vagrant/Vagrantfile
@@ -1,25 +1,37 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
+disable_gui = ENV['VESPA_VAGRANT_DISABLE_GUI']
-def validate_vm_env_option(name)
+def get_mandatory_env_value(name)
opt = ENV[name]
if opt.nil? or opt.empty?
raise Vagrant::Errors::VagrantError.new, "Environment variable #{name} must be set to a valid value before running vagrant"
end
+ return opt
end
-validate_vm_env_option('VESPA_VAGRANT_VM_BOX')
-validate_vm_env_option('VESPA_VAGRANT_VM_BOX_URL')
+def get_env_value(name, fallback)
+ opt = ENV[name]
+ if opt.nil? or opt.empty?
+ return fallback
+ end
+ return opt
+end
+
+vm_box = get_mandatory_env_value('VESPA_VAGRANT_VM_BOX')
+vm_memory = get_env_value('VESPA_VAGRANT_VM_MEMORY', "8192")
+vm_cpus = get_env_value('VESPA_VAGRANT_VM_CPUS', 4)
-vm_box = ENV['VESPA_VAGRANT_VM_BOX']
-vm_box_url = ENV['VESPA_VAGRANT_VM_BOX_URL']
+unless disable_gui
+ vm_box_url = get_mandatory_env_value('VESPA_VAGRANT_VM_BOX_URL')
+end
# For a complete reference, please see the online documentation at https://docs.vagrantup.com.
Vagrant.configure("2") do |config|
config.vm.box = vm_box
- config.vm.box_url = vm_box_url
+ config.vm.box_url = vm_box_url unless disable_gui
config.ssh.forward_agent = true
@@ -27,11 +39,11 @@ Vagrant.configure("2") do |config|
config.vm.provider "virtualbox" do |vb|
# Display the VirtualBox GUI when booting the machine
- vb.gui = true
+ vb.gui = true unless disable_gui
vb.name = "vespa-dev"
- vb.memory = "8192"
- vb.cpus = 4
+ vb.memory = vm_memory
+ vb.cpus = vm_cpus
end
# Install required and nice-to-have packages
@@ -47,13 +59,33 @@ Vagrant.configure("2") do |config|
valgrind \
sudo \
firefox \
- vim
+ vim \
+ emacs
yum-builddep -y /vagrant/dist/vespa.spec
echo -e "* soft nproc 409600\n* hard nproc 409600" > /etc/security/limits.d/99-nproc.conf
echo -e "* soft nofile 262144\n* hard nofile 262144" > /etc/security/limits.d/99-nofile.conf
- echo -e "fs.inotify.max_user_watches = 524288" > /etc/sysctl.d/clion.conf
- wget -q -O - https://download.jetbrains.com/cpp/CLion-2017.3.3.tar.gz | tar -C /opt -zx
- ln -sf /opt/clion-2017.3.3/bin/clion.sh /usr/bin/clion
+
+ unless disable_gui
+ echo -e "fs.inotify.max_user_watches = 524288" > /etc/sysctl.d/clion.conf
+ wget -q -O - https://download.jetbrains.com/cpp/CLion-2018.1.6.tar.gz | tar -C /opt -zx
+ ln -sf /opt/clion-2018.1.6/bin/clion.sh /usr/bin/clion
+ end
+
yum update -y
+ hostname localhost
SHELL
+
+ # Add settings for Vespa and dev tools as the default user, usually 'vagrant' (privileged: false)
+ # NOTE: adding these settings to .bashrc would break vagrant suspend/resume/provision
+ # due to env vars modified by /opt/rh/devtoolset-7/enable.
+ config.vm.provision "shell", privileged: false, inline: <<-SCRIPT
+ grep -l VESPA_HOME ~/.bash_profile >/dev/null || (\
+ printf "%s\n" \
+ 'export VESPA_HOME=$HOME/vespa' \
+ 'export PATH=$PATH:$VESPA_HOME/bin' \
+ 'source /opt/rh/rh-maven35/enable' \
+ 'source /opt/rh/devtoolset-7/enable' \
+ >> ~/.bash_profile )
+ SCRIPT
+
end
diff --git a/vdslib/src/main/java/com/yahoo/vdslib/BucketDistribution.java b/vdslib/src/main/java/com/yahoo/vdslib/BucketDistribution.java
index 09164ffc2e7..cc35286ce2d 100644
--- a/vdslib/src/main/java/com/yahoo/vdslib/BucketDistribution.java
+++ b/vdslib/src/main/java/com/yahoo/vdslib/BucketDistribution.java
@@ -9,7 +9,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class BucketDistribution {
diff --git a/vdslib/src/test/java/com/yahoo/vdslib/BucketDistributionTestCase.java b/vdslib/src/test/java/com/yahoo/vdslib/BucketDistributionTestCase.java
index 5e1d811ade1..b4f3a58c373 100644
--- a/vdslib/src/test/java/com/yahoo/vdslib/BucketDistributionTestCase.java
+++ b/vdslib/src/test/java/com/yahoo/vdslib/BucketDistributionTestCase.java
@@ -9,7 +9,7 @@ import java.util.Random;
import static org.junit.Assert.assertEquals;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class BucketDistributionTestCase {
diff --git a/vdslib/src/vespa/vdslib/distribution/distribution.cpp b/vdslib/src/vespa/vdslib/distribution/distribution.cpp
index 9b31d10d8e8..5a834929981 100644
--- a/vdslib/src/vespa/vdslib/distribution/distribution.cpp
+++ b/vdslib/src/vespa/vdslib/distribution/distribution.cpp
@@ -124,7 +124,7 @@ namespace {
case ConfigDiskDistribution::MODULO_INDEX : return Distribution::MODULO_INDEX;
case ConfigDiskDistribution::MODULO_KNUTH : return Distribution::MODULO_KNUTH;
}
- abort();
+ LOG_ABORT("should not be reached");
}
ConfigDiskDistribution toConfig(Distribution::DiskDistribution cfg) {
switch (cfg) {
@@ -133,7 +133,7 @@ namespace {
case Distribution::MODULO_INDEX : return ConfigDiskDistribution::MODULO_INDEX;
case Distribution::MODULO_KNUTH : return ConfigDiskDistribution::MODULO_KNUTH;
}
- abort();
+ LOG_ABORT("should not be reached");
}
}
diff --git a/vespa-application-maven-plugin/src/main/java/com/yahoo/container/plugin/mojo/ApplicationMojo.java b/vespa-application-maven-plugin/src/main/java/com/yahoo/container/plugin/mojo/ApplicationMojo.java
index 9002340478b..e70a04d96ed 100644
--- a/vespa-application-maven-plugin/src/main/java/com/yahoo/container/plugin/mojo/ApplicationMojo.java
+++ b/vespa-application-maven-plugin/src/main/java/com/yahoo/container/plugin/mojo/ApplicationMojo.java
@@ -19,7 +19,7 @@ import java.util.Collections;
import java.util.List;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
@Mojo(name = "packageApplication", defaultPhase = LifecyclePhase.PACKAGE, threadSafe = true)
public class ApplicationMojo extends AbstractMojo {
diff --git a/vespa-application-maven-plugin/src/main/java/com/yahoo/container/plugin/mojo/Compression.java b/vespa-application-maven-plugin/src/main/java/com/yahoo/container/plugin/mojo/Compression.java
index 77dc5c69e94..d57e13b6543 100644
--- a/vespa-application-maven-plugin/src/main/java/com/yahoo/container/plugin/mojo/Compression.java
+++ b/vespa-application-maven-plugin/src/main/java/com/yahoo/container/plugin/mojo/Compression.java
@@ -11,7 +11,7 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class Compression {
static public void zipDirectory(File dir) throws Exception {
diff --git a/vespa-athenz/pom.xml b/vespa-athenz/pom.xml
index 7721d1829e5..75116812915 100644
--- a/vespa-athenz/pom.xml
+++ b/vespa-athenz/pom.xml
@@ -111,6 +111,34 @@
</exclusions>
</dependency>
<dependency>
+ <groupId>com.yahoo.athenz</groupId>
+ <artifactId>athenz-zpe-java-client</artifactId>
+ <scope>compile</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk15on</artifactId>
+ </exclusion>
+ <!--Exclude all Jackson bundles provided by JDisc -->
+ <exclusion>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-core</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-annotations</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.1</version>
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzIdentityCertificate.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzIdentityCertificate.java
deleted file mode 100644
index 0e9e9432790..00000000000
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzIdentityCertificate.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.athenz.api;
-
-import java.security.PrivateKey;
-import java.security.cert.X509Certificate;
-
-/**
- * @author bjorncs
- */
-public class AthenzIdentityCertificate {
-
- private final X509Certificate certificate;
- private final PrivateKey privateKey;
-
- public AthenzIdentityCertificate(X509Certificate certificate, PrivateKey privateKey) {
- this.certificate = certificate;
- this.privateKey = privateKey;
- }
-
- public X509Certificate getCertificate() {
- return certificate;
- }
-
- public PrivateKey getPrivateKey() {
- return privateKey;
- }
-}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzPrincipal.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzPrincipal.java
index e96f5bd72d4..2330b1e439f 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzPrincipal.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzPrincipal.java
@@ -2,9 +2,12 @@
package com.yahoo.vespa.athenz.api;
import java.security.Principal;
+import java.util.List;
import java.util.Objects;
import java.util.Optional;
+import static java.util.Collections.emptyList;
+
/**
* @author bjorncs
*/
@@ -12,15 +15,24 @@ public class AthenzPrincipal implements Principal {
private final AthenzIdentity athenzIdentity;
private final NToken nToken;
+ private final List<AthenzRole> roles;
public AthenzPrincipal(AthenzIdentity athenzIdentity) {
- this(athenzIdentity, null);
+ this(athenzIdentity, null, emptyList());
+ }
+
+ public AthenzPrincipal(AthenzIdentity athenzIdentity, NToken nToken) {
+ this(athenzIdentity, nToken, emptyList());
}
- public AthenzPrincipal(AthenzIdentity athenzIdentity,
- NToken nToken) {
+ public AthenzPrincipal(AthenzIdentity identity, List<AthenzRole> roles) {
+ this(identity, null, roles);
+ }
+
+ private AthenzPrincipal(AthenzIdentity athenzIdentity, NToken nToken, List<AthenzRole> roles) {
this.athenzIdentity = athenzIdentity;
this.nToken = nToken;
+ this.roles = roles;
}
public AthenzIdentity getIdentity() {
@@ -40,6 +52,10 @@ public class AthenzPrincipal implements Principal {
return Optional.ofNullable(nToken);
}
+ public List<AthenzRole> getRoles() {
+ return roles;
+ }
+
@Override
public String toString() {
return "AthenzPrincipal{" +
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzPublicKey.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzPublicKey.java
deleted file mode 100644
index 1c810e3e9c9..00000000000
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzPublicKey.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.vespa.athenz.api;
-
-import java.security.PublicKey;
-import java.util.Objects;
-
-/**
- * @author bjorncs
- */
-public class AthenzPublicKey {
-
- private final PublicKey publicKey;
- private final String keyId;
-
- public AthenzPublicKey(PublicKey publicKey, String keyId) {
- this.publicKey = publicKey;
- this.keyId = keyId;
- }
-
- public PublicKey getPublicKey() {
- return publicKey;
- }
-
- public String getKeyId() {
- return keyId;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- AthenzPublicKey that = (AthenzPublicKey) o;
- return Objects.equals(publicKey, that.publicKey) &&
- Objects.equals(keyId, that.keyId);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(publicKey, keyId);
- }
-
- @Override
- public String toString() {
- return "AthenzPublicKey{" +
- "publicKey=" + publicKey +
- ", keyId='" + keyId + '\'' +
- '}';
- }
-}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzResourceName.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzResourceName.java
new file mode 100644
index 00000000000..f7aa2affc86
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzResourceName.java
@@ -0,0 +1,74 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.api;
+
+import java.util.Objects;
+
+/**
+ * Athenz resource name
+ *
+ * @author bjorncs
+ */
+public class AthenzResourceName {
+
+ private final AthenzDomain domain;
+ private final String entityName;
+
+ public AthenzResourceName(AthenzDomain domain, String entityName) {
+ this.domain = domain;
+ this.entityName = entityName;
+ }
+
+ public AthenzResourceName(String domain, String entityName) {
+ this(new AthenzDomain(domain), entityName);
+ }
+
+ /**
+ * @param resourceName A resource name string on format 'domain:entity'
+ * @return the parsed resource name
+ */
+ public static AthenzResourceName fromString(String resourceName) {
+ String[] split = resourceName.split(":");
+ if (split.length != 2 || split[0].isEmpty() || split[1].isEmpty()) {
+ throw new IllegalArgumentException("Invalid resource name: " + resourceName);
+ }
+ return new AthenzResourceName(split[0], split[1]);
+ }
+
+ public AthenzDomain getDomain() {
+ return domain;
+ }
+
+ public String getDomainName() {
+ return domain.getName();
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public String toResourceNameString() {
+ return String.format("%s:%s", domain.getName(), entityName);
+ }
+
+ @Override
+ public String toString() {
+ return "AthenzResourceName{" +
+ "domain=" + domain +
+ ", entityName='" + entityName + '\'' +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ AthenzResourceName that = (AthenzResourceName) o;
+ return Objects.equals(domain, that.domain) &&
+ Objects.equals(entityName, that.entityName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(domain, entityName);
+ }
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzRole.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzRole.java
index b011cc8fbcc..3a81e4a5e17 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzRole.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzRole.java
@@ -15,6 +15,11 @@ public class AthenzRole {
this.roleName = roleName;
}
+ public AthenzRole(String domain, String roleName) {
+ this.domain = new AthenzDomain(domain);
+ this.roleName = roleName;
+ }
+
public AthenzDomain domain() {
return domain;
}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/ZToken.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/ZToken.java
index ae520e66429..36c06132532 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/ZToken.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/ZToken.java
@@ -1,7 +1,14 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.athenz.api;
+import com.yahoo.athenz.auth.token.RoleToken;
+import com.yahoo.vespa.athenz.utils.AthenzIdentities;
+
+import java.util.List;
import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static java.util.stream.Collectors.toList;
/**
* Represents an Athenz ZToken (role token)
@@ -10,27 +17,38 @@ import java.util.Objects;
*/
public class ZToken {
- private final String rawToken;
+ private final RoleToken token;
public ZToken(String rawToken) {
- this.rawToken = rawToken;
+ this.token = new RoleToken(rawToken);
}
public String getRawToken() {
- return rawToken;
+ return token.getSignedToken();
+ }
+
+ public AthenzIdentity getIdentity() {
+ return AthenzIdentities.from(token.getPrincipal());
}
+ public List<AthenzRole> getRoles() {
+ String domain = token.getDomain();
+ return token.getRoles().stream()
+ .map(roleName -> new AthenzRole(domain, roleName))
+ .collect(toList());}
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ZToken zToken = (ZToken) o;
- return Objects.equals(rawToken, zToken.rawToken);
+ return Objects.equals(getRawToken(), zToken.getRawToken());
}
@Override
public int hashCode() {
- return Objects.hash(rawToken);
+ return Objects.hash(getRawToken());
}
+
}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/DefaultZtsClient.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/DefaultZtsClient.java
index dfe49d1b407..8a94518cee7 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/DefaultZtsClient.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/DefaultZtsClient.java
@@ -10,12 +10,17 @@ import com.yahoo.vespa.athenz.api.AthenzRole;
import com.yahoo.vespa.athenz.api.AthenzService;
import com.yahoo.vespa.athenz.api.NToken;
import com.yahoo.vespa.athenz.api.ZToken;
+import com.yahoo.vespa.athenz.client.zts.bindings.ErrorResponseEntity;
+import com.yahoo.vespa.athenz.client.zts.bindings.IdentityRefreshRequestEntity;
+import com.yahoo.vespa.athenz.client.zts.bindings.IdentityResponseEntity;
import com.yahoo.vespa.athenz.client.zts.bindings.InstanceIdentityCredentials;
import com.yahoo.vespa.athenz.client.zts.bindings.InstanceRefreshInformation;
import com.yahoo.vespa.athenz.client.zts.bindings.InstanceRegisterInformation;
import com.yahoo.vespa.athenz.client.zts.bindings.RoleCertificateRequestEntity;
import com.yahoo.vespa.athenz.client.zts.bindings.RoleCertificateResponseEntity;
import com.yahoo.vespa.athenz.client.zts.bindings.RoleTokenResponseEntity;
+import com.yahoo.vespa.athenz.client.zts.bindings.TenantDomainsResponseEntity;
+import com.yahoo.vespa.athenz.client.zts.utils.IdentityCsrGenerator;
import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider;
import com.yahoo.vespa.athenz.tls.Pkcs10Csr;
import com.yahoo.vespa.athenz.tls.Pkcs10CsrBuilder;
@@ -29,7 +34,6 @@ import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
-import org.apache.http.util.EntityUtils;
import org.eclipse.jetty.http.HttpStatus;
import javax.net.ssl.SSLContext;
@@ -40,6 +44,7 @@ import java.net.URI;
import java.security.KeyPair;
import java.security.cert.X509Certificate;
import java.time.Duration;
+import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -47,6 +52,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
import static com.yahoo.vespa.athenz.tls.SignatureAlgorithm.SHA256_WITH_RSA;
import static com.yahoo.vespa.athenz.tls.SubjectAlternativeName.Type.DNS_NAME;
import static com.yahoo.vespa.athenz.tls.SubjectAlternativeName.Type.RFC822_NAME;
+import static java.util.stream.Collectors.toList;
/**
* Default implementation of {@link ZtsClient}
@@ -127,12 +133,37 @@ public class DefaultZtsClient implements ZtsClient {
}
@Override
+ public Identity getServiceIdentity(AthenzService identity, String keyId, Pkcs10Csr csr) {
+ URI uri = ztsUrl.resolve(String.format("instance/%s/%s/refresh", identity.getDomainName(), identity.getName()));
+ HttpUriRequest request = RequestBuilder.post()
+ .setUri(uri)
+ .setEntity(toJsonStringEntity(new IdentityRefreshRequestEntity(csr, keyId)))
+ .build();
+ return withClient(client -> {
+ try (CloseableHttpResponse response = client.execute(request)) {
+ IdentityResponseEntity entity = readEntity(response, IdentityResponseEntity.class);
+ return new Identity(entity.certificate(), entity.caCertificateBundle());
+ }
+ });
+ }
+
+ @Override
+ public Identity getServiceIdentity(AthenzService identity, String keyId, KeyPair keyPair, String dnsSuffix) {
+ Pkcs10Csr csr = new IdentityCsrGenerator(dnsSuffix).generateIdentityCsr(identity, keyPair);
+ return getServiceIdentity(identity, keyId, csr);
+ }
+
+ @Override
public ZToken getRoleToken(AthenzDomain domain) {
return getRoleToken(domain, null);
}
@Override
- public ZToken getRoleToken(AthenzDomain domain, String roleName) {
+ public ZToken getRoleToken(AthenzRole athenzRole) {
+ return getRoleToken(athenzRole.domain(), athenzRole.roleName());
+ }
+
+ private ZToken getRoleToken(AthenzDomain domain, String roleName) {
URI uri = ztsUrl.resolve(String.format("domain/%s/token", domain.getName()));
RequestBuilder requestBuilder = RequestBuilder.get(uri)
.addHeader("Content-Type", "application/json");
@@ -178,6 +209,22 @@ public class DefaultZtsClient implements ZtsClient {
return getRoleCertificate(role, null, keyPair, cloud);
}
+ @Override
+ public List<AthenzDomain> getTenantDomains(AthenzIdentity providerIdentity, AthenzIdentity userIdentity, String roleName) {
+ URI uri = ztsUrl.resolve(
+ String.format("providerdomain/%s/user/%s", providerIdentity.getDomainName(), userIdentity.getFullName()));
+ HttpUriRequest request = RequestBuilder.get(uri)
+ .addParameter("roleName", roleName)
+ .addParameter("serviceName", providerIdentity.getName())
+ .build();
+ return withClient(client -> {
+ try (CloseableHttpResponse response = client.execute(request)) {
+ TenantDomainsResponseEntity entity = readEntity(response, TenantDomainsResponseEntity.class);
+ return entity.tenantDomainNames.stream().map(AthenzDomain::new).collect(toList());
+ }
+ });
+ }
+
private static InstanceIdentity getInstanceIdentity(HttpResponse response) throws IOException {
InstanceIdentityCredentials entity = readEntity(response, InstanceIdentityCredentials.class);
return entity.getServiceToken() != null
@@ -189,10 +236,8 @@ public class DefaultZtsClient implements ZtsClient {
if (HttpStatus.isSuccess(response.getStatusLine().getStatusCode())) {
return objectMapper.readValue(response.getEntity().getContent(), entityType);
} else {
- String message = EntityUtils.toString(response.getEntity());
- throw new ZtsClientException(
- String.format("Unable to get identity. http code/message: %d/%s",
- response.getStatusLine().getStatusCode(), message));
+ ErrorResponseEntity errorEntity = objectMapper.readValue(response.getEntity().getContent(), ErrorResponseEntity.class);
+ throw new ZtsClientException(errorEntity.code, errorEntity.description);
}
}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/Identity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/Identity.java
new file mode 100644
index 00000000000..455f3c06d1d
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/Identity.java
@@ -0,0 +1,29 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.client.zts;
+
+import java.security.cert.X509Certificate;
+import java.util.List;
+
+/**
+ * The identity of a service
+ *
+ * @author bjorncs
+ */
+public class Identity {
+
+ private final X509Certificate certificate;
+ private final List<X509Certificate> caCertificates;
+
+ public Identity(X509Certificate certificate, List<X509Certificate> caCertificates) {
+ this.certificate = certificate;
+ this.caCertificates = caCertificates;
+ }
+
+ public X509Certificate certificate() {
+ return certificate;
+ }
+
+ public List<X509Certificate> caCertificates() {
+ return caCertificates;
+ }
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/ZtsClient.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/ZtsClient.java
index 43378b6507a..5c0e21bfa97 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/ZtsClient.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/ZtsClient.java
@@ -1,7 +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.vespa.athenz.client.zts;
+import com.yahoo.athenz.zts.TenantDomains;
import com.yahoo.vespa.athenz.api.AthenzDomain;
+import com.yahoo.vespa.athenz.api.AthenzIdentity;
import com.yahoo.vespa.athenz.api.AthenzRole;
import com.yahoo.vespa.athenz.api.AthenzService;
import com.yahoo.vespa.athenz.api.ZToken;
@@ -10,6 +12,7 @@ import com.yahoo.vespa.athenz.tls.Pkcs10Csr;
import java.security.KeyPair;
import java.security.cert.X509Certificate;
import java.time.Duration;
+import java.util.List;
/**
* Interface for a ZTS client.
@@ -43,6 +46,25 @@ public interface ZtsClient extends AutoCloseable {
Pkcs10Csr csr);
/**
+ * Get service identity
+ *
+ * @return A x509 certificate with CA certificates
+ */
+ Identity getServiceIdentity(AthenzService identity,
+ String keyId,
+ Pkcs10Csr csr);
+
+ /**
+ * Get service identity
+ *
+ * @return A x509 certificate with CA certificates
+ */
+ Identity getServiceIdentity(AthenzService identity,
+ String keyId,
+ KeyPair keyPair,
+ String dnsSuffix);
+
+ /**
* Fetch a role token for the target domain
*
* @param domain Target domain
@@ -51,13 +73,12 @@ public interface ZtsClient extends AutoCloseable {
ZToken getRoleToken(AthenzDomain domain);
/**
- * Fetch a role token for the target domain and role
+ * Fetch a role token for the target role
*
- * @param domain Target domain
- * @param roleName Target role
+ * @param athenzRole Target role
* @return A role token
*/
- ZToken getRoleToken(AthenzDomain domain, String roleName);
+ ZToken getRoleToken(AthenzRole athenzRole);
/**
* Fetch role certificate for the target domain and role
@@ -85,5 +106,15 @@ public interface ZtsClient extends AutoCloseable {
KeyPair keyPair,
String cloud);
+ /**
+ * For a given provider, get a list of tenant domains that the user is a member of
+ *
+ * @param providerIdentity Provider identity
+ * @param userIdentity User identity
+ * @param roleName Role name
+ * @return List of domains
+ */
+ List<AthenzDomain> getTenantDomains(AthenzIdentity providerIdentity, AthenzIdentity userIdentity, String roleName);
+
void close();
}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/ZtsClientException.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/ZtsClientException.java
index 3d3696ad870..0b0d6914fea 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/ZtsClientException.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/ZtsClientException.java
@@ -8,11 +8,25 @@ package com.yahoo.vespa.athenz.client.zts;
*/
public class ZtsClientException extends RuntimeException {
- public ZtsClientException(String message) {
- super(message);
+ private final int errorCode;
+ private final String description;
+
+ public ZtsClientException(int errorCode, String description) {
+ super(createMessage(errorCode, description));
+ this.errorCode = errorCode;
+ this.description = description;
+ }
+
+ public int getErrorCode() {
+ return errorCode;
}
- public ZtsClientException(String message, Throwable cause) {
- super(message, cause);
+ public String getDescription() {
+ return description;
}
+
+ private static String createMessage(int code, String description) {
+ return String.format("Received error from ZTS: code=%d, message=\"%s\"", code, description);
+ }
+
}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/ErrorResponseEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/ErrorResponseEntity.java
new file mode 100644
index 00000000000..431af084f9f
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/ErrorResponseEntity.java
@@ -0,0 +1,23 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.client.zts.bindings;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * @author bjorncs
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ErrorResponseEntity {
+
+ public final int code;
+ public final String description;
+
+ @JsonCreator
+ public ErrorResponseEntity(@JsonProperty("code") int code,
+ @JsonProperty("message") String description) {
+ this.code = code;
+ this.description = description;
+ }
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/IdentityRefreshRequestEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/IdentityRefreshRequestEntity.java
new file mode 100644
index 00000000000..47ae9cd2d3f
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/IdentityRefreshRequestEntity.java
@@ -0,0 +1,24 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.client.zts.bindings;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.yahoo.vespa.athenz.client.zts.bindings.serializers.Pkcs10CsrSerializer;
+import com.yahoo.vespa.athenz.tls.Pkcs10Csr;
+
+/**
+ * @author bjorncs
+ */
+public class IdentityRefreshRequestEntity {
+
+ @JsonProperty("csr") @JsonSerialize(using = Pkcs10CsrSerializer.class)
+ private final Pkcs10Csr csr;
+
+ @JsonProperty("keyId")
+ private final String keyId;
+
+ public IdentityRefreshRequestEntity(Pkcs10Csr csr, String keyId) {
+ this.csr = csr;
+ this.keyId = keyId;
+ }
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/IdentityResponseEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/IdentityResponseEntity.java
new file mode 100644
index 00000000000..7bd04362599
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/IdentityResponseEntity.java
@@ -0,0 +1,40 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.client.zts.bindings;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.yahoo.vespa.athenz.client.zts.bindings.serializers.X509CertificateDeserializer;
+import com.yahoo.vespa.athenz.client.zts.bindings.serializers.X509CertificateListDeserializer;
+
+import java.security.cert.X509Certificate;
+import java.util.List;
+
+/**
+ * Identity response entity
+ *
+ * @author bjorncs
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class IdentityResponseEntity {
+
+ private final X509Certificate certificate;
+ private final List<X509Certificate> caCertificateBundle;
+
+ @JsonCreator
+ public IdentityResponseEntity(
+ @JsonProperty("certificate") @JsonDeserialize(using = X509CertificateDeserializer.class) X509Certificate certificate,
+ @JsonProperty("caCertBundle") @JsonDeserialize(using = X509CertificateListDeserializer.class) List<X509Certificate> caCertificateBundle) {
+ this.certificate = certificate;
+ this.caCertificateBundle = caCertificateBundle;
+ }
+
+ public X509Certificate certificate() {
+ return certificate;
+ }
+
+ public List<X509Certificate> caCertificateBundle() {
+ return caCertificateBundle;
+ }
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceIdentityCredentials.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceIdentityCredentials.java
index 5c265f14813..0ab697a1c4c 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceIdentityCredentials.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceIdentityCredentials.java
@@ -4,13 +4,9 @@ package com.yahoo.vespa.athenz.client.zts.bindings;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.yahoo.vespa.athenz.tls.X509CertificateUtils;
+import com.yahoo.vespa.athenz.client.zts.bindings.serializers.X509CertificateDeserializer;
-import java.io.IOException;
import java.security.cert.X509Certificate;
/**
@@ -39,11 +35,4 @@ public class InstanceIdentityCredentials {
return serviceToken;
}
- public static class X509CertificateDeserializer extends JsonDeserializer<X509Certificate> {
- @Override
- public X509Certificate deserialize(JsonParser parser, DeserializationContext context) throws IOException {
- return X509CertificateUtils.fromPem(parser.getValueAsString());
- }
- }
-
}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceRefreshInformation.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceRefreshInformation.java
index 6c956ddb410..b842ef43500 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceRefreshInformation.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceRefreshInformation.java
@@ -4,8 +4,9 @@ package com.yahoo.vespa.athenz.client.zts.bindings;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.yahoo.vespa.athenz.client.zts.bindings.serializers.Pkcs10CsrSerializer;
import com.yahoo.vespa.athenz.tls.Pkcs10Csr;
-import com.yahoo.vespa.athenz.tls.Pkcs10CsrUtils;
/**
* @author bjorncs
@@ -15,13 +16,14 @@ import com.yahoo.vespa.athenz.tls.Pkcs10CsrUtils;
public class InstanceRefreshInformation {
@JsonProperty("csr")
- private final String csr;
+ @JsonSerialize(using = Pkcs10CsrSerializer.class)
+ private final Pkcs10Csr csr;
@JsonProperty("token")
private final boolean requestServiceToken;
public InstanceRefreshInformation(Pkcs10Csr csr,
boolean requestServiceToken) {
- this.csr = Pkcs10CsrUtils.toPem(csr);
+ this.csr = csr;
this.requestServiceToken = requestServiceToken;
}
}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/RoleCertificateRequestEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/RoleCertificateRequestEntity.java
index 9c56e5a60d6..f329ffbbd2c 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/RoleCertificateRequestEntity.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/RoleCertificateRequestEntity.java
@@ -8,8 +8,8 @@ import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.yahoo.vespa.athenz.client.zts.bindings.serializers.Pkcs10CsrSerializer;
import com.yahoo.vespa.athenz.tls.Pkcs10Csr;
-import com.yahoo.vespa.athenz.tls.Pkcs10CsrUtils;
import java.io.IOException;
import java.time.Duration;
@@ -20,7 +20,7 @@ import java.time.Duration;
@JsonIgnoreProperties(ignoreUnknown = true)
public class RoleCertificateRequestEntity {
@JsonProperty("csr")
- @JsonSerialize(using = CsrSerializer.class)
+ @JsonSerialize(using = Pkcs10CsrSerializer.class)
public final Pkcs10Csr csr;
@JsonProperty("expiryTime")
@@ -33,15 +33,6 @@ public class RoleCertificateRequestEntity {
this.expiryTime = expiryTime;
}
- public static class CsrSerializer extends JsonSerializer<Pkcs10Csr> {
- @Override
- public void serialize(Pkcs10Csr csr,
- JsonGenerator jsonGenerator,
- SerializerProvider serializerProvider) throws IOException {
- jsonGenerator.writeString(Pkcs10CsrUtils.toPem(csr));
- }
- }
-
public static class ExpirySerializer extends JsonSerializer<Duration> {
@Override
public void serialize(Duration duration,
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/RoleCertificateResponseEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/RoleCertificateResponseEntity.java
index 1b4bd463392..e80f5626843 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/RoleCertificateResponseEntity.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/RoleCertificateResponseEntity.java
@@ -4,13 +4,9 @@ package com.yahoo.vespa.athenz.client.zts.bindings;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.yahoo.vespa.athenz.tls.X509CertificateUtils;
+import com.yahoo.vespa.athenz.client.zts.bindings.serializers.X509CertificateDeserializer;
-import java.io.IOException;
import java.security.cert.X509Certificate;
import java.time.Instant;
@@ -28,11 +24,4 @@ public class RoleCertificateResponseEntity {
this.certificate = certificate;
this.expiry = expiry;
}
-
- public static class X509CertificateDeserializer extends JsonDeserializer<X509Certificate> {
- @Override
- public X509Certificate deserialize(JsonParser parser, DeserializationContext context) throws IOException {
- return X509CertificateUtils.fromPem(parser.getValueAsString());
- }
- }
}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/TenantDomainsResponseEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/TenantDomainsResponseEntity.java
new file mode 100644
index 00000000000..0c69aac318c
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/TenantDomainsResponseEntity.java
@@ -0,0 +1,21 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.client.zts.bindings;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.List;
+
+/**
+ * @author bjorncs
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class TenantDomainsResponseEntity {
+ public final List<String> tenantDomainNames;
+
+ @JsonCreator
+ public TenantDomainsResponseEntity(@JsonProperty("tenantDomainNames") List<String> tenantDomainNames) {
+ this.tenantDomainNames = tenantDomainNames;
+ }
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/Pkcs10CsrSerializer.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/Pkcs10CsrSerializer.java
new file mode 100644
index 00000000000..24825792953
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/Pkcs10CsrSerializer.java
@@ -0,0 +1,20 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.client.zts.bindings.serializers;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.yahoo.vespa.athenz.tls.Pkcs10Csr;
+import com.yahoo.vespa.athenz.tls.Pkcs10CsrUtils;
+
+import java.io.IOException;
+
+/**
+ * @author bjorncs
+ */
+public class Pkcs10CsrSerializer extends JsonSerializer<Pkcs10Csr> {
+ @Override
+ public void serialize(Pkcs10Csr csr, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
+ jsonGenerator.writeString(Pkcs10CsrUtils.toPem(csr));
+ }
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/X509CertificateDeserializer.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/X509CertificateDeserializer.java
new file mode 100644
index 00000000000..5dd6ceb16b4
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/X509CertificateDeserializer.java
@@ -0,0 +1,21 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.client.zts.bindings.serializers;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.yahoo.vespa.athenz.tls.X509CertificateUtils;
+
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+
+/**
+ * @author bjorncs
+ */
+public class X509CertificateDeserializer extends JsonDeserializer<X509Certificate> {
+ @Override
+ public X509Certificate deserialize(JsonParser parser, DeserializationContext context) throws IOException {
+ return X509CertificateUtils.fromPem(parser.getValueAsString());
+ }
+}
+
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/X509CertificateListDeserializer.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/X509CertificateListDeserializer.java
new file mode 100644
index 00000000000..c496031c116
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/X509CertificateListDeserializer.java
@@ -0,0 +1,22 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.client.zts.bindings.serializers;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.yahoo.vespa.athenz.tls.X509CertificateUtils;
+
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+import java.util.List;
+
+/**
+ * @author bjorncs
+ */
+public class X509CertificateListDeserializer extends JsonDeserializer<List<X509Certificate>> {
+
+ @Override
+ public List<X509Certificate> deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException {
+ return X509CertificateUtils.certificateListFromPem(parser.getValueAsString());
+ }
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/package-info.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/package-info.java
new file mode 100644
index 00000000000..4c442617494
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/package-info.java
@@ -0,0 +1,8 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+/**
+ * @author bjorncs
+ */
+@ExportPackage
+package com.yahoo.vespa.athenz.client.zts.bindings.serializers;
+
+import com.yahoo.osgi.annotation.ExportPackage; \ No newline at end of file
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/utils/IdentityCsrGenerator.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/utils/IdentityCsrGenerator.java
new file mode 100644
index 00000000000..2f152fafba8
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/utils/IdentityCsrGenerator.java
@@ -0,0 +1,37 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.client.zts.utils;
+
+import com.yahoo.vespa.athenz.api.AthenzService;
+import com.yahoo.vespa.athenz.client.zts.ZtsClient;
+import com.yahoo.vespa.athenz.tls.Pkcs10Csr;
+import com.yahoo.vespa.athenz.tls.Pkcs10CsrBuilder;
+
+import javax.security.auth.x500.X500Principal;
+import java.security.KeyPair;
+
+import static com.yahoo.vespa.athenz.tls.SignatureAlgorithm.SHA256_WITH_RSA;
+
+/**
+ * Generates a {@link Pkcs10Csr} instance for use with {@link ZtsClient#getServiceIdentity(AthenzService, String, Pkcs10Csr)}
+ *
+ * @author bjorncs
+ */
+public class IdentityCsrGenerator {
+
+ private final String dnsSuffix;
+
+ public IdentityCsrGenerator(String dnsSuffix) {
+ this.dnsSuffix = dnsSuffix;
+ }
+
+ public Pkcs10Csr generateIdentityCsr(AthenzService identity, KeyPair keypair) {
+ return Pkcs10CsrBuilder.fromKeypair(new X500Principal("CN=" + identity.getFullName()), keypair, SHA256_WITH_RSA)
+ .addSubjectAlternativeName(String.format(
+ "%s.%s.%s",
+ identity.getName(),
+ identity.getDomainName().replace(".", "-"),
+ dnsSuffix))
+ .build();
+ }
+
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/utils/package-info.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/utils/package-info.java
new file mode 100644
index 00000000000..baca71bc187
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/utils/package-info.java
@@ -0,0 +1,8 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+/**
+ * @author bjorncs
+ */
+@ExportPackage
+package com.yahoo.vespa.athenz.client.zts.utils;
+
+import com.yahoo.osgi.annotation.ExportPackage; \ No newline at end of file
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapper.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapper.java
index ab127b19bf1..3bfe492a125 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapper.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapper.java
@@ -8,7 +8,6 @@ import com.yahoo.vespa.athenz.api.AthenzService;
import com.yahoo.vespa.athenz.identityprovider.api.bindings.IdentityDocumentEntity;
import com.yahoo.vespa.athenz.identityprovider.api.bindings.SignedIdentityDocumentEntity;
import com.yahoo.vespa.athenz.identityprovider.api.bindings.VespaUniqueInstanceIdEntity;
-import com.yahoo.vespa.athenz.utils.AthenzIdentities;
import java.io.IOException;
import java.io.UncheckedIOException;
@@ -52,12 +51,12 @@ public class EntityBindingsMapper {
public static SignedIdentityDocument toSignedIdentityDocument(SignedIdentityDocumentEntity entity) {
return new SignedIdentityDocument(
- toIdentityDocument(entity.identityDocument),
+ entity.identityDocument != null ? toIdentityDocument(entity.identityDocument) : null,
entity.signature,
entity.signingKeyVersion,
fromDottedString(entity.providerUniqueId),
entity.dnsSuffix,
- (AthenzService) AthenzIdentities.from(entity.providerService),
+ new AthenzService(entity.providerService),
entity.ztsEndpoint,
entity.documentVersion,
entity.configServerHostname,
@@ -84,7 +83,7 @@ public class EntityBindingsMapper {
public static SignedIdentityDocumentEntity toSignedIdentityDocumentEntity(SignedIdentityDocument model) {
try {
- IdentityDocumentEntity identityDocumentEntity = toIdentityDocumentEntity(model.identityDocument());
+ IdentityDocumentEntity identityDocumentEntity = model.identityDocument() != null ? toIdentityDocumentEntity(model.identityDocument()) : null;
String rawDocument = Base64.getEncoder().encodeToString(mapper.writeValueAsString(identityDocumentEntity).getBytes());
return new SignedIdentityDocumentEntity(
rawDocument,
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/SignedIdentityDocument.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/SignedIdentityDocument.java
index 7c64d048944..aa1dbd4dac3 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/SignedIdentityDocument.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/SignedIdentityDocument.java
@@ -58,6 +58,7 @@ public class SignedIdentityDocument {
this.identityType = identityType;
}
+ @Deprecated
public IdentityDocument identityDocument() {
return identityDocument;
}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java
index aa514b3caf3..3d4872549d6 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java
@@ -25,7 +25,7 @@ public class SignedIdentityDocumentEntity {
private static final ObjectMapper mapper = createObjectMapper();
@JsonProperty("identity-document")public final String rawIdentityDocument;
- @JsonIgnore public final IdentityDocumentEntity identityDocument;
+ @JsonIgnore @Deprecated public final IdentityDocumentEntity identityDocument;
@JsonProperty("signature") public final String signature;
@JsonProperty("signing-key-version") public final int signingKeyVersion;
@JsonProperty("provider-unique-id") public final String providerUniqueId; // String representation
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentials.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentials.java
index bb9f512efe6..a1d8a9ca258 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentials.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentials.java
@@ -12,28 +12,21 @@ import java.security.cert.X509Certificate;
*/
class AthenzCredentials {
- private final String nToken;
private final X509Certificate certificate;
private final KeyPair keyPair;
private final SignedIdentityDocument identityDocument;
private final SSLContext identitySslContext;
- AthenzCredentials(String nToken,
- X509Certificate certificate,
+ AthenzCredentials(X509Certificate certificate,
KeyPair keyPair,
SignedIdentityDocument identityDocument,
SSLContext identitySslContext) {
- this.nToken = nToken;
this.certificate = certificate;
this.keyPair = keyPair;
this.identityDocument = identityDocument;
this.identitySslContext = identitySslContext;
}
- String getNToken() {
- return nToken;
- }
-
X509Certificate getCertificate() {
return certificate;
}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java
index 1136106ce19..5567831d49d 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java
@@ -5,6 +5,7 @@ import com.yahoo.container.core.identity.IdentityConfig;
import com.yahoo.vespa.athenz.api.AthenzService;
import com.yahoo.vespa.athenz.client.zts.DefaultZtsClient;
import com.yahoo.vespa.athenz.client.zts.InstanceIdentity;
+import com.yahoo.vespa.athenz.client.zts.ZtsClient;
import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider;
import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper;
import com.yahoo.vespa.athenz.identityprovider.api.IdentityDocumentClient;
@@ -14,13 +15,21 @@ import com.yahoo.vespa.athenz.tls.KeyAlgorithm;
import com.yahoo.vespa.athenz.tls.KeyUtils;
import com.yahoo.vespa.athenz.tls.Pkcs10Csr;
import com.yahoo.vespa.athenz.tls.SslContextBuilder;
+import com.yahoo.vespa.athenz.utils.SiaUtils;
+import com.yahoo.vespa.defaults.Defaults;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
+import java.time.Clock;
+import java.time.Duration;
+import java.util.Optional;
import static com.yahoo.vespa.athenz.tls.KeyStoreType.JKS;
import static java.util.Collections.singleton;
@@ -31,77 +40,114 @@ import static java.util.Collections.singleton;
* @author bjorncs
*/
class AthenzCredentialsService {
- private final IdentityConfig identityConfig;
+ private static final Duration EXPIRATION_MARGIN = Duration.ofDays(2);
+ private static final Path VESPA_SIA_DIRECTORY = Paths.get(Defaults.getDefaults().underVespaHome("var/vespa/sia"));
+ private static final Path IDENTITY_DOCUMENT_FILE = VESPA_SIA_DIRECTORY.resolve("vespa-tenant-identity-document.json");
+
+ private final AthenzService tenantIdentity;
+ private final URI configserverEndpoint;
+ private final URI ztsEndpoint;
+ private final AthenzService configserverIdentity;
private final ServiceIdentityProvider nodeIdentityProvider;
private final File trustStoreJks;
private final String hostname;
private final InstanceCsrGenerator instanceCsrGenerator;
+ private final Clock clock;
AthenzCredentialsService(IdentityConfig identityConfig,
ServiceIdentityProvider nodeIdentityProvider,
File trustStoreJks,
- String hostname) {
- this.identityConfig = identityConfig;
+ String hostname,
+ Clock clock) {
+ this.tenantIdentity = new AthenzService(identityConfig.domain(), identityConfig.service());
+ this.configserverEndpoint = URI.create("https://" + identityConfig.loadBalancerAddress() + ":4443");
+ this.ztsEndpoint = URI.create(identityConfig.ztsUrl());
+ this.configserverIdentity = new AthenzService(identityConfig.configserverIdentityName());
this.nodeIdentityProvider = nodeIdentityProvider;
this.trustStoreJks = trustStoreJks;
this.hostname = hostname;
this.instanceCsrGenerator = new InstanceCsrGenerator(identityConfig.athenzDnsSuffix());
+ this.clock = clock;
}
AthenzCredentials registerInstance() {
+ Optional<AthenzCredentials> athenzCredentialsFromDisk = tryReadCredentialsFromDisk();
+ if (athenzCredentialsFromDisk.isPresent()) {
+ return athenzCredentialsFromDisk.get();
+ }
KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.RSA);
- IdentityDocumentClient identityDocumentClient = createIdentityDocumentClient(identityConfig, nodeIdentityProvider);
+ IdentityDocumentClient identityDocumentClient = createIdentityDocumentClient();
SignedIdentityDocument document = identityDocumentClient.getTenantIdentityDocument(hostname);
- AthenzService tenantIdentity = new AthenzService(identityConfig.domain(), identityConfig.service());
Pkcs10Csr csr = instanceCsrGenerator.generateCsr(
tenantIdentity,
document.providerUniqueId(),
- document.identityDocument().ipAddresses(),
+ document.ipAddresses(),
keyPair);
- try (com.yahoo.vespa.athenz.client.zts.ZtsClient ztsClient =
- new DefaultZtsClient(URI.create(identityConfig.ztsUrl()), nodeIdentityProvider)) {
+ try (ZtsClient ztsClient = new DefaultZtsClient(ztsEndpoint, nodeIdentityProvider)) {
InstanceIdentity instanceIdentity =
ztsClient.registerInstance(
- new AthenzService(identityConfig.configserverIdentityName()),
+ configserverIdentity,
tenantIdentity,
null,
EntityBindingsMapper.toAttestationData(document),
- true,
+ false,
csr);
- return toAthenzCredentials(instanceIdentity, keyPair, document);
+ X509Certificate certificate = instanceIdentity.certificate();
+ SSLContext identitySslContext = createIdentitySslContext(keyPair.getPrivate(), certificate);
+ writeCredentialsToDisk(keyPair.getPrivate(), certificate, document);
+ return new AthenzCredentials(certificate, keyPair, document, identitySslContext);
}
}
AthenzCredentials updateCredentials(SignedIdentityDocument document, SSLContext sslContext) {
- AthenzService tenantIdentity = new AthenzService(identityConfig.domain(), identityConfig.service());
KeyPair newKeyPair = KeyUtils.generateKeypair(KeyAlgorithm.RSA);
Pkcs10Csr csr = instanceCsrGenerator.generateCsr(
tenantIdentity,
document.providerUniqueId(),
- document.identityDocument().ipAddresses(),
+ document.ipAddresses(),
newKeyPair);
- try (com.yahoo.vespa.athenz.client.zts.ZtsClient ztsClient =
- new DefaultZtsClient(URI.create(identityConfig.ztsUrl()), tenantIdentity, sslContext)) {
+ try (ZtsClient ztsClient = new DefaultZtsClient(ztsEndpoint, tenantIdentity, sslContext)) {
InstanceIdentity instanceIdentity =
ztsClient.refreshInstance(
- new AthenzService(identityConfig.configserverIdentityName()),
+ configserverIdentity,
tenantIdentity,
document.providerUniqueId().asDottedString(),
- true,
+ false,
csr);
- return toAthenzCredentials(instanceIdentity, newKeyPair, document);
+ X509Certificate certificate = instanceIdentity.certificate();
+ SSLContext identitySslContext = createIdentitySslContext(newKeyPair.getPrivate(), certificate);
+ writeCredentialsToDisk(newKeyPair.getPrivate(), certificate, document);
+ return new AthenzCredentials(certificate, newKeyPair, document, identitySslContext);
}
}
- private AthenzCredentials toAthenzCredentials(InstanceIdentity instanceIdentity,
- KeyPair keyPair,
- SignedIdentityDocument identityDocument) {
- X509Certificate certificate = instanceIdentity.certificate();
- String serviceToken = instanceIdentity.nToken().get().getRawToken();
- SSLContext identitySslContext = createIdentitySslContext(keyPair.getPrivate(), certificate);
- return new AthenzCredentials(serviceToken, certificate, keyPair, identityDocument, identitySslContext);
+ private Optional<AthenzCredentials> tryReadCredentialsFromDisk() {
+ Optional<PrivateKey> privateKey = SiaUtils.readPrivateKeyFile(VESPA_SIA_DIRECTORY, tenantIdentity);
+ if (!privateKey.isPresent()) return Optional.empty();
+ Optional<X509Certificate> certificate = SiaUtils.readCertificateFile(VESPA_SIA_DIRECTORY, tenantIdentity);
+ if (!certificate.isPresent()) return Optional.empty();
+ if (isExpired(certificate.get())) {
+ return Optional.empty();
+ }
+ if (Files.notExists(IDENTITY_DOCUMENT_FILE)) return Optional.empty();
+ SignedIdentityDocument signedIdentityDocument = EntityBindingsMapper.readSignedIdentityDocumentFromFile(IDENTITY_DOCUMENT_FILE);
+ KeyPair keyPair = new KeyPair(KeyUtils.extractPublicKey(privateKey.get()), privateKey.get());
+ SSLContext sslContext = createIdentitySslContext(privateKey.get(), certificate.get());
+ return Optional.of(new AthenzCredentials(certificate.get(), keyPair, signedIdentityDocument, sslContext));
+ }
+
+ private boolean isExpired(X509Certificate certificate) {
+ return clock.instant().isAfter(certificate.getNotAfter().toInstant().minus(EXPIRATION_MARGIN));
+ }
+
+ private void writeCredentialsToDisk(PrivateKey privateKey,
+ X509Certificate certificate,
+ SignedIdentityDocument identityDocument) {
+ SiaUtils.writePrivateKeyFile(VESPA_SIA_DIRECTORY, tenantIdentity, privateKey);
+ SiaUtils.writeCertificateFile(VESPA_SIA_DIRECTORY, tenantIdentity, certificate);
+ EntityBindingsMapper.writeSignedIdentityDocumentToFile(IDENTITY_DOCUMENT_FILE, identityDocument);
}
private SSLContext createIdentitySslContext(PrivateKey privateKey, X509Certificate certificate) {
@@ -111,11 +157,10 @@ class AthenzCredentialsService {
.build();
}
- private static DefaultIdentityDocumentClient createIdentityDocumentClient(IdentityConfig config,
- ServiceIdentityProvider nodeIdentityProvider) {
+ private DefaultIdentityDocumentClient createIdentityDocumentClient() {
return new DefaultIdentityDocumentClient(
- URI.create(config.loadBalancerAddress()),
+ configserverEndpoint,
nodeIdentityProvider,
- new AthenzIdentityVerifier(singleton(new AthenzService(config.configserverIdentityName()))));
+ new AthenzIdentityVerifier(singleton(configserverIdentity)));
}
}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java
index ce0743021ff..e40a0933002 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java
@@ -14,6 +14,9 @@ import com.yahoo.log.LogLevel;
import com.yahoo.vespa.athenz.api.AthenzDomain;
import com.yahoo.vespa.athenz.api.AthenzRole;
import com.yahoo.vespa.athenz.api.AthenzService;
+import com.yahoo.vespa.athenz.api.ZToken;
+import com.yahoo.vespa.athenz.client.zts.DefaultZtsClient;
+import com.yahoo.vespa.athenz.client.zts.ZtsClient;
import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider;
import com.yahoo.vespa.athenz.identity.ServiceIdentityProviderListenerHelper;
import com.yahoo.vespa.athenz.identity.SiaIdentityProvider;
@@ -25,7 +28,6 @@ import com.yahoo.vespa.defaults.Defaults;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.net.URI;
-import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.time.Clock;
import java.time.Duration;
@@ -33,9 +35,12 @@ import java.time.Instant;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
import java.util.logging.Logger;
/**
+ * A {@link AthenzIdentityProvider} / {@link ServiceIdentityProvider} component that provides the tenant identity.
+ *
* @author mortent
* @author bjorncs
*/
@@ -47,11 +52,12 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen
// TODO These should match the requested expiration
static final Duration UPDATE_PERIOD = Duration.ofDays(1);
static final Duration AWAIT_TERMINTATION_TIMEOUT = Duration.ofSeconds(90);
+ private final static Duration ROLE_SSL_CONTEXT_EXPIRY = Duration.ofHours(24);
+ private final static Duration ROLE_TOKEN_EXPIRY = Duration.ofMinutes(30);
public static final String CERTIFICATE_EXPIRY_METRIC_NAME = "athenz-tenant-cert.expiry.seconds";
private volatile AthenzCredentials credentials;
- private final ZtsClient ztsClient = new ZtsClient();
private final Metric metric;
private final AthenzCredentialsService athenzCredentialsService;
private final ScheduledExecutorService scheduler;
@@ -62,7 +68,8 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen
private final URI ztsEndpoint;
private final LoadingCache<AthenzRole, SSLContext> roleSslContextCache;
- private final static Duration roleSslContextExpiry = Duration.ofHours(24);
+ private final LoadingCache<AthenzRole, ZToken> roleSpecificRoleTokenCache;
+ private final LoadingCache<AthenzDomain, ZToken> domainSpecificRoleTokenCache;
@Inject
public AthenzIdentityProviderImpl(IdentityConfig config, Metric metric) {
@@ -71,7 +78,8 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen
new AthenzCredentialsService(config,
createNodeIdentityProvider(config),
getDefaultTrustStoreLocation(),
- Defaults.getDefaults().vespaHostname()),
+ Defaults.getDefaults().vespaHostname(),
+ Clock.systemUTC()),
new ScheduledThreadPoolExecutor(1),
Clock.systemUTC());
}
@@ -91,14 +99,20 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen
this.listenerHelper = new ServiceIdentityProviderListenerHelper(this.identity);
this.dnsSuffix = config.athenzDnsSuffix();
this.ztsEndpoint = URI.create(config.ztsUrl());
+ roleSslContextCache = createCache(ROLE_SSL_CONTEXT_EXPIRY, this::createRoleSslContext);
+ roleSpecificRoleTokenCache = createCache(ROLE_TOKEN_EXPIRY, this::createRoleToken);
+ domainSpecificRoleTokenCache = createCache(ROLE_TOKEN_EXPIRY, this::createRoleToken);
registerInstance();
- roleSslContextCache = CacheBuilder.newBuilder()
- .refreshAfterWrite(roleSslContextExpiry.dividedBy(2).toMinutes(), TimeUnit.MINUTES)
- .expireAfterWrite(roleSslContextExpiry.toMinutes(), TimeUnit.MINUTES)
- .build(new CacheLoader<AthenzRole, SSLContext>() {
+ }
+
+ private static <KEY, VALUE> LoadingCache<KEY, VALUE> createCache(Duration expiry, Function<KEY, VALUE> cacheLoader) {
+ return CacheBuilder.newBuilder()
+ .refreshAfterWrite(expiry.dividedBy(2).toMinutes(), TimeUnit.MINUTES)
+ .expireAfterWrite(expiry.toMinutes(), TimeUnit.MINUTES)
+ .build(new CacheLoader<KEY, VALUE>() {
@Override
- public SSLContext load(AthenzRole key) throws Exception {
- return createRoleSslContext(key);
+ public VALUE load(KEY key) {
+ return cacheLoader.apply(key);
}
});
}
@@ -149,44 +163,52 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen
try {
return roleSslContextCache.get(new AthenzRole(new AthenzDomain(domain), role));
} catch (Exception e) {
- throw new AthenzIdentityProviderException("Could not retrieve role certificate.", e);
+ throw new AthenzIdentityProviderException("Could not retrieve role certificate: " + e.getMessage(), e);
}
}
- private SSLContext createRoleSslContext(AthenzRole role) {
- PrivateKey privateKey = credentials.getKeyPair().getPrivate();
- X509Certificate roleCertificate = ztsClient.getRoleCertificate(
- role,
- dnsSuffix,
- ztsEndpoint,
- identity,
- privateKey,
- credentials.getIdentitySslContext());
- return new SslContextBuilder()
- .withKeyStore(privateKey, roleCertificate)
- .withTrustStore(getDefaultTrustStoreLocation(), KeyStoreType.JKS)
- .build();
- }
-
@Override
public String getRoleToken(String domain) {
- return ztsClient
- .getRoleToken(
- new AthenzDomain(domain),
- ztsEndpoint,
- credentials.getIdentitySslContext())
- .getRawToken();
+ try {
+ return domainSpecificRoleTokenCache.get(new AthenzDomain(domain)).getRawToken();
+ } catch (Exception e) {
+ throw new AthenzIdentityProviderException("Could not retrieve role token: " + e.getMessage(), e);
+ }
}
@Override
public String getRoleToken(String domain, String role) {
- return ztsClient
- .getRoleToken(
- new AthenzDomain(domain),
- role,
- ztsEndpoint,
- credentials.getIdentitySslContext())
- .getRawToken();
+ try {
+ return roleSpecificRoleTokenCache.get(new AthenzRole(domain, role)).getRawToken();
+ } catch (Exception e) {
+ throw new AthenzIdentityProviderException("Could not retrieve role token: " + e.getMessage(), e);
+ }
+ }
+
+ private SSLContext createRoleSslContext(AthenzRole role) {
+ try (ZtsClient client = createZtsClient()) {
+ X509Certificate roleCertificate = client.getRoleCertificate(role, credentials.getKeyPair(), dnsSuffix);
+ return new SslContextBuilder()
+ .withKeyStore(credentials.getKeyPair().getPrivate(), roleCertificate)
+ .withTrustStore(getDefaultTrustStoreLocation(), KeyStoreType.JKS)
+ .build();
+ }
+ }
+
+ private ZToken createRoleToken(AthenzRole athenzRole) {
+ try (ZtsClient client = createZtsClient()) {
+ return client.getRoleToken(athenzRole);
+ }
+ }
+
+ private ZToken createRoleToken(AthenzDomain domain) {
+ try (ZtsClient client = createZtsClient()) {
+ return client.getRoleToken(domain);
+ }
+ }
+
+ private DefaultZtsClient createZtsClient() {
+ return new DefaultZtsClient(ztsEndpoint, identity(), getIdentitySslContext());
}
@Override
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSigner.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSigner.java
new file mode 100644
index 00000000000..b3b5df0e68b
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSigner.java
@@ -0,0 +1,85 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.identityprovider.client;
+
+import com.yahoo.vespa.athenz.api.AthenzService;
+import com.yahoo.vespa.athenz.identityprovider.api.IdentityType;
+import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument;
+import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId;
+import com.yahoo.vespa.athenz.tls.SignatureAlgorithm;
+
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.time.Instant;
+import java.util.Base64;
+import java.util.Set;
+import java.util.TreeSet;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * Generates and validates the signature for a {@link SignedIdentityDocument}
+ *
+ * @author bjorncs
+ */
+public class IdentityDocumentSigner {
+
+ public String generateSignature(VespaUniqueInstanceId providerUniqueId,
+ AthenzService providerService,
+ String configServerHostname,
+ String instanceHostname,
+ Instant createdAt,
+ Set<String> ipAddresses,
+ IdentityType identityType,
+ PrivateKey privateKey) {
+ try {
+ Signature signer = createSigner();
+ signer.initSign(privateKey);
+ writeToSigner(signer, providerUniqueId, providerService, configServerHostname, instanceHostname, createdAt, ipAddresses, identityType);
+ byte[] signature = signer.sign();
+ return Base64.getEncoder().encodeToString(signature);
+ } catch (GeneralSecurityException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public boolean hasValidSignature(SignedIdentityDocument doc, PublicKey publicKey) {
+ try {
+ Signature signer = createSigner();
+ signer.initVerify(publicKey);
+ writeToSigner(signer, doc.providerUniqueId(), doc.providerService(), doc.configServerHostname(), doc.instanceHostname(), doc.createdAt(), doc.ipAddresses(), doc.identityType());
+ return signer.verify(Base64.getDecoder().decode(doc.signature()));
+ } catch (GeneralSecurityException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static Signature createSigner() throws NoSuchAlgorithmException {
+ return Signature.getInstance(SignatureAlgorithm.SHA512_WITH_RSA.getAlgorithmName());
+ }
+
+ private static void writeToSigner(Signature signer,
+ VespaUniqueInstanceId providerUniqueId,
+ AthenzService providerService,
+ String configServerHostname,
+ String instanceHostname,
+ Instant createdAt,
+ Set<String> ipAddresses,
+ IdentityType identityType) throws SignatureException {
+ signer.update(providerUniqueId.asDottedString().getBytes(UTF_8));
+ signer.update(providerService.getFullName().getBytes(UTF_8));
+ signer.update(configServerHostname.getBytes(UTF_8));
+ signer.update(instanceHostname.getBytes(UTF_8));
+ ByteBuffer timestampAsBuffer = ByteBuffer.allocate(Long.BYTES);
+ timestampAsBuffer.putLong(createdAt.toEpochMilli());
+ signer.update(timestampAsBuffer.array());
+ for (String ipAddress : new TreeSet<>(ipAddresses)) {
+ signer.update(ipAddress.getBytes(UTF_8));
+ }
+ signer.update(identityType.id().getBytes(UTF_8));
+ }
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/ZtsClient.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/ZtsClient.java
deleted file mode 100644
index a3ec55eb815..00000000000
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/ZtsClient.java
+++ /dev/null
@@ -1,64 +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.athenz.identityprovider.client;
-
-import com.yahoo.athenz.zts.RoleCertificateRequest;
-import com.yahoo.athenz.zts.RoleToken;
-import com.yahoo.athenz.zts.ZTSClient;
-import com.yahoo.vespa.athenz.api.AthenzDomain;
-import com.yahoo.vespa.athenz.api.AthenzRole;
-import com.yahoo.vespa.athenz.api.AthenzService;
-import com.yahoo.vespa.athenz.api.ZToken;
-import com.yahoo.vespa.athenz.client.zts.DefaultZtsClient;
-import com.yahoo.vespa.athenz.tls.X509CertificateUtils;
-
-import javax.net.ssl.SSLContext;
-import java.net.URI;
-import java.security.PrivateKey;
-import java.security.cert.X509Certificate;
-import java.time.Duration;
-
-/**
- * @author mortent
- * @author bjorncs
- * @deprecated Will be replaced by {@link DefaultZtsClient} once role token/certificate caching is ready.
- */
-@Deprecated
-class ZtsClient {
-
- ZToken getRoleToken(AthenzDomain domain,
- URI ztsEndpoint,
- SSLContext sslContext) {
- // TODO ztsEndpoint should contain '/zts/v1' as path
- URI correctedZtsEndpoint = ztsEndpoint.resolve("/zts/v1");
- return new ZToken(
- new ZTSClient(correctedZtsEndpoint.toString(), sslContext)
- .getRoleToken(domain.getName()).getToken());
- }
-
- ZToken getRoleToken(AthenzDomain domain,
- String roleName,
- URI ztsEndpoint,
- SSLContext sslContext) {
- // TODO ztsEndpoint should contain '/zts/v1' as path
- URI correctedZtsEndpoint = ztsEndpoint.resolve("/zts/v1");
- return new ZToken(
- new ZTSClient(correctedZtsEndpoint.toString(), sslContext)
- .getRoleToken(domain.getName(), roleName).getToken());
- }
-
- X509Certificate getRoleCertificate(AthenzRole role,
- String dnsSuffix,
- URI ztsEndpoint,
- AthenzService identity,
- PrivateKey privateKey,
- SSLContext sslContext) {
- // TODO ztsEndpoint should contain '/zts/v1' as path
- URI correctedZtsEndpoint = ztsEndpoint.resolve("/zts/v1");
- ZTSClient ztsClient = new ZTSClient(correctedZtsEndpoint.toString(), sslContext);
- RoleCertificateRequest rcr = ZTSClient.generateRoleCertificateRequest(
- identity.getDomain().getName(), identity.getName(), role.domain().getName(), role.roleName(), privateKey, dnsSuffix, (int) Duration.ofHours(1).getSeconds());
- RoleToken pemCert = ztsClient.postRoleCertificateRequest(role.domain().getName(), role.roleName(), rcr);
- return X509CertificateUtils.fromPem(pemCert.token);
- }
-
-}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/AthenzX509CertificateUtils.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/AthenzX509CertificateUtils.java
new file mode 100644
index 00000000000..46aca707be1
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/AthenzX509CertificateUtils.java
@@ -0,0 +1,58 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.tls;
+
+import com.yahoo.vespa.athenz.api.AthenzIdentity;
+import com.yahoo.vespa.athenz.api.AthenzRole;
+import com.yahoo.vespa.athenz.utils.AthenzIdentities;
+
+import java.security.cert.X509Certificate;
+import java.util.List;
+
+import static com.yahoo.vespa.athenz.tls.SubjectAlternativeName.Type.RFC822_NAME;
+
+/**
+ * Utility methods for Athenz issued x509 certificates
+ *
+ * @author bjorncs
+ */
+public class AthenzX509CertificateUtils {
+
+ private static final String COMMON_NAME_ROLE_DELIMITER = ":role.";
+
+ private AthenzX509CertificateUtils() {}
+
+ public static boolean isAthenzRoleCertificate(X509Certificate certificate) {
+ return isAthenzIssuedCertificate(certificate) &&
+ X509CertificateUtils.getSubjectCommonNames(certificate).get(0).contains(COMMON_NAME_ROLE_DELIMITER);
+ }
+
+ public static boolean isAthenzIssuedCertificate(X509Certificate certificate) {
+ return X509CertificateUtils.getIssuerCommonNames(certificate).stream()
+ .anyMatch(cn -> cn.equalsIgnoreCase("Yahoo Athenz CA") || cn.equalsIgnoreCase("Athenz AWS CA"));
+ }
+
+ public static AthenzIdentity getIdentityFromRoleCertificate(X509Certificate certificate) {
+ List<SubjectAlternativeName> sans = X509CertificateUtils.getSubjectAlternativeNames(certificate);
+ return sans.stream()
+ .filter(san -> san.getType() == RFC822_NAME)
+ .map(SubjectAlternativeName::getValue)
+ .map(AthenzX509CertificateUtils::getIdentityFromSanEmail)
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException("Could not find identity in SAN: " + sans));
+ }
+
+ public static AthenzRole getRolesFromRoleCertificate(X509Certificate certificate) {
+ String commonName = X509CertificateUtils.getSubjectCommonNames(certificate).get(0);
+ int delimiterIndex = commonName.indexOf(COMMON_NAME_ROLE_DELIMITER);
+ String domain = commonName.substring(0, delimiterIndex);
+ String roleName = commonName.substring(delimiterIndex + COMMON_NAME_ROLE_DELIMITER.length());
+ return new AthenzRole(domain, roleName);
+ }
+
+ private static AthenzIdentity getIdentityFromSanEmail(String email) {
+ int separator = email.indexOf('@');
+ if (separator == -1) throw new IllegalArgumentException("Invalid SAN email: " + email);
+ return AthenzIdentities.from(email.substring(0, separator));
+ }
+
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/KeyUtils.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/KeyUtils.java
index 563cae80da2..c2be1a40893 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/KeyUtils.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/KeyUtils.java
@@ -2,6 +2,8 @@
package com.yahoo.vespa.athenz.tls;
import com.yahoo.athenz.auth.util.Crypto;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
@@ -70,11 +72,21 @@ public class KeyUtils {
public static String toPem(PrivateKey privateKey) {
try (StringWriter stringWriter = new StringWriter(); JcaPEMWriter pemWriter = new JcaPEMWriter(stringWriter)) {
- pemWriter.writeObject(new PemObject("PRIVATE KEY", privateKey.getEncoded()));
+ // Note: Encoding using PKCS#1 as this is to be read by tools only supporting PKCS#1
+ pemWriter.writeObject(new PemObject("RSA PRIVATE KEY", getPkcs1Bytes(privateKey)));
pemWriter.flush();
return stringWriter.toString();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
+
+ private static byte[] getPkcs1Bytes(PrivateKey privateKey) throws IOException{
+
+ byte[] privBytes = privateKey.getEncoded();
+ PrivateKeyInfo pkInfo = PrivateKeyInfo.getInstance(privBytes);
+ ASN1Encodable encodable = pkInfo.parsePrivateKey();
+ ASN1Primitive primitive = encodable.toASN1Primitive();
+ return primitive.getEncoded();
+ }
}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/SignatureAlgorithm.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/SignatureAlgorithm.java
index 3d85a12f714..2f3e2721751 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/SignatureAlgorithm.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/SignatureAlgorithm.java
@@ -5,7 +5,8 @@ package com.yahoo.vespa.athenz.tls;
* @author bjorncs
*/
public enum SignatureAlgorithm {
- SHA256_WITH_RSA("SHA256withRSA");
+ SHA256_WITH_RSA("SHA256withRSA"),
+ SHA512_WITH_RSA("SHA512withRSA");
private final String algorithmName;
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/X509CertificateUtils.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/X509CertificateUtils.java
index 6ba094ff275..d96ed17765c 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/X509CertificateUtils.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/X509CertificateUtils.java
@@ -21,6 +21,7 @@ import java.io.UncheckedIOException;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -36,16 +37,22 @@ public class X509CertificateUtils {
public static X509Certificate fromPem(String pem) {
try (PEMParser parser = new PEMParser(new StringReader(pem))) {
- Object pemObject = parser.readObject();
- if (pemObject instanceof X509Certificate) {
- return (X509Certificate) pemObject;
- }
- if (pemObject instanceof X509CertificateHolder) {
- return new JcaX509CertificateConverter()
- .setProvider(BouncyCastleProviderHolder.getInstance())
- .getCertificate((X509CertificateHolder) pemObject);
+ return toX509Certificate(parser.readObject());
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ } catch (CertificateException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static List<X509Certificate> certificateListFromPem(String pem) {
+ try (PEMParser parser = new PEMParser(new StringReader(pem))) {
+ List<X509Certificate> list = new ArrayList<>();
+ Object pemObject;
+ while ((pemObject = parser.readObject()) != null) {
+ list.add(toX509Certificate(pemObject));
}
- throw new IllegalArgumentException("Invalid type of PEM object: " + pemObject);
+ return list;
} catch (IOException e) {
throw new UncheckedIOException(e);
} catch (CertificateException e) {
@@ -53,6 +60,18 @@ public class X509CertificateUtils {
}
}
+ private static X509Certificate toX509Certificate(Object pemObject) throws CertificateException {
+ if (pemObject instanceof X509Certificate) {
+ return (X509Certificate) pemObject;
+ }
+ if (pemObject instanceof X509CertificateHolder) {
+ return new JcaX509CertificateConverter()
+ .setProvider(BouncyCastleProviderHolder.getInstance())
+ .getCertificate((X509CertificateHolder) pemObject);
+ }
+ throw new IllegalArgumentException("Invalid type of PEM object: " + pemObject);
+ }
+
public static String toPem(X509Certificate certificate) {
try (StringWriter stringWriter = new StringWriter(); JcaPEMWriter pemWriter = new JcaPEMWriter(stringWriter)) {
pemWriter.writeObject(new PemObject("CERTIFICATE", certificate.getEncoded()));
@@ -65,6 +84,20 @@ public class X509CertificateUtils {
}
}
+ public static String toPem(List<X509Certificate> certificates) {
+ try (StringWriter stringWriter = new StringWriter(); JcaPEMWriter pemWriter = new JcaPEMWriter(stringWriter)) {
+ for (X509Certificate certificate : certificates) {
+ pemWriter.writeObject(new PemObject("CERTIFICATE", certificate.getEncoded()));
+ }
+ pemWriter.flush();
+ return stringWriter.toString();
+ } catch (GeneralSecurityException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
public static List<String> getSubjectCommonNames(X509Certificate certificate) {
return getCommonNames(certificate.getSubjectX500Principal());
}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/SiaUtils.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/SiaUtils.java
index adaafab4617..55e9103b040 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/SiaUtils.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/SiaUtils.java
@@ -2,9 +2,18 @@
package com.yahoo.vespa.athenz.utils;
import com.yahoo.vespa.athenz.api.AthenzService;
+import com.yahoo.vespa.athenz.tls.KeyUtils;
+import com.yahoo.vespa.athenz.tls.X509CertificateUtils;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.Optional;
/**
* Misc utility methods for SIA provided credentials
@@ -36,4 +45,68 @@ public class SiaUtils {
.resolve(String.format("%s.%s.cert.pem", service.getDomainName(), service.getName()));
}
+ public static Optional<PrivateKey> readPrivateKeyFile(AthenzService service) {
+ return readPrivateKeyFile(DEFAULT_SIA_DIRECTORY, service);
+ }
+
+ public static Optional<PrivateKey> readPrivateKeyFile(Path root, AthenzService service) {
+ try {
+ Path privateKeyFile = getPrivateKeyFile(root, service);
+ if (Files.notExists(privateKeyFile)) return Optional.empty();
+ return Optional.of(KeyUtils.fromPemEncodedPrivateKey(new String(Files.readAllBytes(privateKeyFile))));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ public static Optional<X509Certificate> readCertificateFile(AthenzService service) {
+ return readCertificateFile(DEFAULT_SIA_DIRECTORY, service);
+ }
+
+ public static Optional<X509Certificate> readCertificateFile(Path root, AthenzService service) {
+ try {
+ Path certificateFile = getCertificateFile(root, service);
+ if (Files.notExists(certificateFile)) return Optional.empty();
+ return Optional.of(X509CertificateUtils.fromPem(new String(Files.readAllBytes(certificateFile))));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ public static void writePrivateKeyFile(AthenzService service, PrivateKey privateKey) {
+ writePrivateKeyFile(DEFAULT_SIA_DIRECTORY, service, privateKey);
+ }
+
+ public static void writePrivateKeyFile(Path root, AthenzService service, PrivateKey privateKey) {
+ try {
+ Path privateKeyFile = getPrivateKeyFile(root, service);
+ Files.createDirectories(privateKeyFile.getParent());
+ Path tempFile = toTempFile(privateKeyFile);
+ Files.write(tempFile, KeyUtils.toPem(privateKey).getBytes());
+ Files.move(tempFile, privateKeyFile, StandardCopyOption.ATOMIC_MOVE);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ public static void writeCertificateFile(AthenzService service, X509Certificate certificate) {
+ writeCertificateFile(DEFAULT_SIA_DIRECTORY, service, certificate);
+ }
+
+ public static void writeCertificateFile(Path root, AthenzService service, X509Certificate certificate) {
+ try {
+ Path certificateFile = getCertificateFile(root, service);
+ Files.createDirectories(certificateFile.getParent());
+ Path tempFile = toTempFile(certificateFile);
+ Files.write(tempFile, X509CertificateUtils.toPem(certificate).getBytes());
+ Files.move(tempFile, certificateFile, StandardCopyOption.ATOMIC_MOVE);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private static Path toTempFile(Path file) {
+ return Paths.get(file.toAbsolutePath().toString() + ".tmp");
+ }
+
}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/ntoken/AthenzConfTruststore.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/ntoken/AthenzConfTruststore.java
new file mode 100644
index 00000000000..4cb3470635e
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/ntoken/AthenzConfTruststore.java
@@ -0,0 +1,57 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.utils.ntoken;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.yahoo.athenz.auth.util.Crypto;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Path;
+import java.security.PublicKey;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * A {@link AthenzTruststore} that is backed by athenz.conf
+ *
+ * @author bjorncs
+ */
+public class AthenzConfTruststore implements AthenzTruststore {
+
+ private final Map<String, PublicKey> zmsPublicKeys;
+ private final Map<String, PublicKey> ztsPublicKeys;
+
+ public AthenzConfTruststore(Path athenzConfFile) {
+ try {
+ JsonNode root = new ObjectMapper().readTree(athenzConfFile.toFile());
+ this.zmsPublicKeys = loadPublicKeys((ArrayNode) root.get("zmsPublicKeys"));
+ this.ztsPublicKeys = loadPublicKeys((ArrayNode) root.get("ztsPublicKeys"));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private static Map<String, PublicKey> loadPublicKeys(ArrayNode keysArray) {
+ Map<String, PublicKey> publicKeys = new HashMap<>();
+ for (JsonNode keyEntry : keysArray) {
+ String keyId = keyEntry.get("id").textValue();
+ String encodedPublicKey = keyEntry.get("key").textValue();
+ PublicKey publicKey = Crypto.loadPublicKey(Crypto.ybase64DecodeString(encodedPublicKey));
+ publicKeys.put(keyId, publicKey);
+ }
+ return publicKeys;
+ }
+
+ @Override
+ public Optional<PublicKey> getZmsPublicKey(String keyId) {
+ return Optional.ofNullable(zmsPublicKeys.get(keyId));
+ }
+
+ @Override
+ public Optional<PublicKey> getZtsPublicKey(String keyId) {
+ return Optional.ofNullable(ztsPublicKeys.get(keyId));
+ }
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/ntoken/AthenzTruststore.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/ntoken/AthenzTruststore.java
new file mode 100644
index 00000000000..83afa288cf0
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/ntoken/AthenzTruststore.java
@@ -0,0 +1,15 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.utils.ntoken;
+
+import java.security.PublicKey;
+import java.util.Optional;
+
+/**
+ * A truststore that contains all ZMS and ZTS public keys
+ *
+ * @author bjorncs
+ */
+public interface AthenzTruststore {
+ Optional<PublicKey> getZmsPublicKey(String keyId);
+ Optional<PublicKey> getZtsPublicKey(String keyId);
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/ntoken/NTokenValidator.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/ntoken/NTokenValidator.java
new file mode 100644
index 00000000000..f4ec0b168d7
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/ntoken/NTokenValidator.java
@@ -0,0 +1,72 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.utils.ntoken;
+
+import com.yahoo.athenz.auth.token.PrincipalToken;
+import com.yahoo.log.LogLevel;
+import com.yahoo.vespa.athenz.api.AthenzDomain;
+import com.yahoo.vespa.athenz.api.AthenzPrincipal;
+import com.yahoo.vespa.athenz.api.NToken;
+import com.yahoo.vespa.athenz.utils.AthenzIdentities;
+
+import java.nio.file.Path;
+import java.security.PublicKey;
+import java.time.Duration;
+import java.util.logging.Logger;
+
+/**
+ * Validates the content of an NToken:
+ * 1) Verifies that the token is signed by Athenz
+ * 2) Verifies that the token is not expired
+ *
+ * @author bjorncs
+ */
+public class NTokenValidator {
+ // Max allowed skew in token timestamp (only for creation, not expiry timestamp)
+ private static final long ALLOWED_TIMESTAMP_OFFSET = Duration.ofMinutes(5).getSeconds();
+
+ private static final Logger log = Logger.getLogger(NTokenValidator.class.getName());
+ private final AthenzTruststore truststore;
+
+
+ public NTokenValidator(AthenzTruststore truststore) {
+ this.truststore = truststore;
+ }
+
+ public NTokenValidator(Path athenzConfFile) {
+ this(new AthenzConfTruststore(athenzConfFile));
+ }
+
+ public AthenzPrincipal validate(NToken token) throws InvalidTokenException {
+ PrincipalToken principalToken = new PrincipalToken(token.getRawToken());
+ String keyId = principalToken.getKeyId();
+ String keyService = principalToken.getKeyService();
+ PublicKey zmsPublicKey = (keyService == null || keyService.equals("zms") ? truststore.getZmsPublicKey(keyId) : truststore.getZtsPublicKey(keyId))
+ .orElseThrow(() -> {
+ String message = "NToken has an unknown keyId: " + keyId;
+ log.log(LogLevel.WARNING, message);
+ return new InvalidTokenException(message);
+ });
+ validateSignatureAndExpiration(principalToken, zmsPublicKey);
+ return new AthenzPrincipal(
+ AthenzIdentities.from(
+ new AthenzDomain(principalToken.getDomain()),
+ principalToken.getName()),
+ token);
+ }
+
+ private static void validateSignatureAndExpiration(PrincipalToken token, PublicKey zmsPublicKey) throws InvalidTokenException {
+ StringBuilder errorMessageBuilder = new StringBuilder();
+ if (!token.validate(zmsPublicKey, (int) ALLOWED_TIMESTAMP_OFFSET, true, errorMessageBuilder)) {
+ String message = "NToken is expired or has invalid signature: " + errorMessageBuilder.toString();
+ log.log(LogLevel.WARNING, message);
+ throw new InvalidTokenException(message);
+ }
+ }
+
+ public static class InvalidTokenException extends RuntimeException {
+ public InvalidTokenException(String message) {
+ super(message);
+ }
+ }
+
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/ntoken/package-info.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/ntoken/package-info.java
new file mode 100644
index 00000000000..8760c02d27d
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/ntoken/package-info.java
@@ -0,0 +1,8 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+/**
+ * @author bjorncs
+ */
+@ExportPackage
+package com.yahoo.vespa.athenz.utils.ntoken;
+
+import com.yahoo.osgi.annotation.ExportPackage; \ No newline at end of file
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/zpe/AuthorizationResult.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/zpe/AuthorizationResult.java
new file mode 100644
index 00000000000..faf05011af9
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/zpe/AuthorizationResult.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.athenz.zpe;
+
+import com.yahoo.athenz.zpe.AuthZpeClient.AccessCheckStatus;
+
+import java.util.Arrays;
+
+/**
+ * The various types of access control results.
+ *
+ * @author bjorncs
+ */
+public enum AuthorizationResult {
+ ALLOW(AccessCheckStatus.ALLOW),
+ DENY(AccessCheckStatus.DENY),
+ DENY_NO_MATCH(AccessCheckStatus.DENY_NO_MATCH),
+ DENY_ROLETOKEN_EXPIRED(AccessCheckStatus.DENY_ROLETOKEN_EXPIRED),
+ DENY_ROLETOKEN_INVALID(AccessCheckStatus.DENY_ROLETOKEN_INVALID),
+ DENY_DOMAIN_MISMATCH(AccessCheckStatus.DENY_DOMAIN_MISMATCH),
+ DENY_DOMAIN_NOT_FOUND(AccessCheckStatus.DENY_DOMAIN_NOT_FOUND),
+ DENY_DOMAIN_EXPIRED(AccessCheckStatus.DENY_DOMAIN_EXPIRED),
+ DENY_DOMAIN_EMPTY(AccessCheckStatus.DENY_DOMAIN_EMPTY),
+ DENY_INVALID_PARAMETERS(AccessCheckStatus.DENY_INVALID_PARAMETERS),
+ DENY_CERT_MISMATCH_ISSUER(AccessCheckStatus.DENY_CERT_MISMATCH_ISSUER),
+ DENY_CERT_MISSING_SUBJECT(AccessCheckStatus.DENY_CERT_MISSING_SUBJECT),
+ DENY_CERT_MISSING_DOMAIN(AccessCheckStatus.DENY_CERT_MISSING_DOMAIN),
+ DENY_CERT_MISSING_ROLE_NAME(AccessCheckStatus.DENY_CERT_MISSING_ROLE_NAME);
+
+ private final AccessCheckStatus wrappedElement;
+
+ AuthorizationResult(AccessCheckStatus wrappedElement) {
+ this.wrappedElement = wrappedElement;
+ }
+
+ public String getDescription() {
+ return wrappedElement.toString();
+ }
+
+ static AuthorizationResult fromAccessCheckStatus(AccessCheckStatus accessCheckStatus) {
+ return Arrays.stream(values())
+ .filter(value -> value.wrappedElement == accessCheckStatus)
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException("Unknown status: " + accessCheckStatus));
+ }
+
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/zpe/DefaultZpe.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/zpe/DefaultZpe.java
new file mode 100644
index 00000000000..a02d9c7a97a
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/zpe/DefaultZpe.java
@@ -0,0 +1,29 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.zpe;
+
+import com.yahoo.athenz.zpe.AuthZpeClient;
+import com.yahoo.vespa.athenz.api.AthenzResourceName;
+import com.yahoo.vespa.athenz.api.ZToken;
+
+import java.security.cert.X509Certificate;
+
+/**
+ * The default implementation of {@link Zpe}.
+ * This implementation is currently based on the official Athenz ZPE library.
+ *
+ * @author bjorncs
+ */
+public class DefaultZpe implements Zpe {
+ @Override
+ public AuthorizationResult checkAccessAllowed(ZToken roleToken, AthenzResourceName resourceName, String action) {
+ return AuthorizationResult.fromAccessCheckStatus(
+ AuthZpeClient.allowAccess(roleToken.getRawToken(), resourceName.toResourceNameString(), action));
+ }
+
+ @Override
+ public AuthorizationResult checkAccessAllowed(X509Certificate roleCertificate, AthenzResourceName resourceName, String action) {
+ return AuthorizationResult.fromAccessCheckStatus(
+ AuthZpeClient.allowAccess(roleCertificate, resourceName.toResourceNameString(), action));
+ }
+
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/zpe/Zpe.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/zpe/Zpe.java
new file mode 100644
index 00000000000..e22e27f1508
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/zpe/Zpe.java
@@ -0,0 +1,17 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.zpe;
+
+import com.yahoo.vespa.athenz.api.AthenzResourceName;
+import com.yahoo.vespa.athenz.api.ZToken;
+
+import java.security.cert.X509Certificate;
+
+/**
+ * Interface for interacting with ZPE (Authorization Policy Engine)
+ *
+ * @author bjorncs
+ */
+public interface Zpe {
+ AuthorizationResult checkAccessAllowed(ZToken roleToken, AthenzResourceName resourceName, String action);
+ AuthorizationResult checkAccessAllowed(X509Certificate roleCertificate, AthenzResourceName resourceName, String action);
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/zpe/package-info.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/zpe/package-info.java
new file mode 100644
index 00000000000..341eb887021
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/zpe/package-info.java
@@ -0,0 +1,8 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+/**
+ * @author bjorncs
+ */
+@ExportPackage
+package com.yahoo.vespa.athenz.zpe;
+
+import com.yahoo.osgi.annotation.ExportPackage; \ No newline at end of file
diff --git a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/api/AthenzResourceNameTest.java b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/api/AthenzResourceNameTest.java
new file mode 100644
index 00000000000..b9f4bd5369e
--- /dev/null
+++ b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/api/AthenzResourceNameTest.java
@@ -0,0 +1,21 @@
+package com.yahoo.vespa.athenz.api;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author bjorncs
+ */
+public class AthenzResourceNameTest {
+
+ @Test
+ public void can_serialize_and_deserialize_to_string() {
+ AthenzResourceName resourceName = new AthenzResourceName(new AthenzDomain("domain"), "entity");
+ String resourceNameString = resourceName.toResourceNameString();
+ assertEquals("domain:entity", resourceNameString);
+ AthenzResourceName deserializedResourceName = AthenzResourceName.fromString(resourceNameString);
+ assertEquals(deserializedResourceName, resourceName);
+ }
+
+} \ No newline at end of file
diff --git a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImplTest.java b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImplTest.java
index 48781aad651..01dab2dada3 100644
--- a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImplTest.java
+++ b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImplTest.java
@@ -62,12 +62,12 @@ public class AthenzIdentityProviderImplTest {
X509Certificate certificate = getCertificate(getExpirationSupplier(clock));
when(athenzCredentialsService.registerInstance())
- .thenReturn(new AthenzCredentials(null, certificate, null, null, null));
+ .thenReturn(new AthenzCredentials(certificate, null, null, null));
when(athenzCredentialsService.updateCredentials(any(), any()))
.thenThrow(new RuntimeException("#1"))
.thenThrow(new RuntimeException("#2"))
- .thenReturn(new AthenzCredentials(null, certificate, null, null, null));
+ .thenReturn(new AthenzCredentials(certificate, null, null, null));
AthenzIdentityProviderImpl identityProvider =
new AthenzIdentityProviderImpl(IDENTITY_CONFIG, metric, athenzCredentialsService, mock(ScheduledExecutorService.class), clock);
diff --git a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java
new file mode 100644
index 00000000000..9cc500ee241
--- /dev/null
+++ b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java
@@ -0,0 +1,50 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.identityprovider.client;
+
+import com.yahoo.vespa.athenz.api.AthenzService;
+import com.yahoo.vespa.athenz.identityprovider.api.IdentityType;
+import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument;
+import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId;
+import com.yahoo.vespa.athenz.tls.KeyAlgorithm;
+import com.yahoo.vespa.athenz.tls.KeyUtils;
+import org.junit.Test;
+
+import java.net.URI;
+import java.security.KeyPair;
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.HashSet;
+
+import static com.yahoo.vespa.athenz.identityprovider.api.IdentityType.TENANT;
+import static com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument.DEFAULT_DOCUMENT_VERSION;
+import static com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument.DEFAULT_KEY_VERSION;
+import static org.junit.Assert.*;
+
+/**
+ * @author bjorncs
+ */
+public class IdentityDocumentSignerTest {
+
+ @Test
+ public void generates_and_validates_signature() {
+ IdentityDocumentSigner signer = new IdentityDocumentSigner();
+ IdentityType identityType = TENANT;
+ VespaUniqueInstanceId id =
+ new VespaUniqueInstanceId(1, "cluster-id", "instance", "application", "tenant", "region", "environment", identityType);
+ AthenzService providerService = new AthenzService("vespa", "service");
+ KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.RSA);
+ String configserverHostname = "configserverhostname";
+ String instanceHostname = "instancehostname";
+ Instant createdAt = Instant.EPOCH;
+ HashSet<String> ipAddresses = new HashSet<>(Arrays.asList("1.2.3.4", "::1"));
+ String signature =
+ signer.generateSignature(id, providerService, configserverHostname, instanceHostname, createdAt, ipAddresses, identityType, keyPair.getPrivate());
+
+ SignedIdentityDocument signedIdentityDocument = new SignedIdentityDocument(
+ null, signature, DEFAULT_KEY_VERSION, id, "dns-suffix", providerService, URI.create("https://zts"),
+ DEFAULT_DOCUMENT_VERSION, configserverHostname, instanceHostname, createdAt, ipAddresses, identityType);
+
+ assertTrue(signer.hasValidSignature(signedIdentityDocument, keyPair.getPublic()));
+ }
+
+} \ No newline at end of file
diff --git a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/tls/KeyUtilsTest.java b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/tls/KeyUtilsTest.java
index fca4353d400..fbdc6f1e3bd 100644
--- a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/tls/KeyUtilsTest.java
+++ b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/tls/KeyUtilsTest.java
@@ -27,8 +27,8 @@ public class KeyUtilsTest {
public void can_serialize_deserialize_pem() {
KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.RSA);
String pem = KeyUtils.toPem(keyPair.getPrivate());
- assertThat(pem, containsString("BEGIN PRIVATE KEY"));
- assertThat(pem, containsString("END PRIVATE KEY"));
+ assertThat(pem, containsString("BEGIN RSA PRIVATE KEY"));
+ assertThat(pem, containsString("END RSA PRIVATE KEY"));
PrivateKey deserializedKey = KeyUtils.fromPemEncodedPrivateKey(pem);
assertEquals(keyPair.getPrivate(), deserializedKey);
}
diff --git a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/tls/TestUtils.java b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/tls/TestUtils.java
index 64f15408313..2a9b54f9e9e 100644
--- a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/tls/TestUtils.java
+++ b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/tls/TestUtils.java
@@ -24,7 +24,10 @@ class TestUtils {
}
static X509Certificate createCertificate(KeyPair keyPair) {
- X500Principal subject = new X500Principal("CN=mysubject");
+ return createCertificate(keyPair, new X500Principal("CN=mysubject"));
+ }
+
+ static X509Certificate createCertificate(KeyPair keyPair, X500Principal subject) {
return X509CertificateBuilder
.fromKeypair(
keyPair, subject, Instant.now(), Instant.now().plus(1, ChronoUnit.DAYS), SignatureAlgorithm.SHA256_WITH_RSA, 1)
diff --git a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/tls/X509CertificateUtilsTest.java b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/tls/X509CertificateUtilsTest.java
index 718c0e88972..4039bf36a5f 100644
--- a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/tls/X509CertificateUtilsTest.java
+++ b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/tls/X509CertificateUtilsTest.java
@@ -7,6 +7,7 @@ import java.security.KeyPair;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
+import java.util.Arrays;
import java.util.List;
import static com.yahoo.vespa.athenz.tls.SubjectAlternativeName.Type.DNS_NAME;
@@ -24,15 +25,7 @@ public class X509CertificateUtilsTest {
public void can_deserialize_serialized_pem_certificate() {
KeyPair keypair = KeyUtils.generateKeypair(KeyAlgorithm.RSA, 2048);
X500Principal subject = new X500Principal("CN=myservice");
- X509Certificate cert = X509CertificateBuilder
- .fromKeypair(
- keypair,
- subject,
- Instant.now(),
- Instant.now().plus(1, ChronoUnit.DAYS),
- SignatureAlgorithm.SHA256_WITH_RSA,
- 1)
- .build();
+ X509Certificate cert = TestUtils.createCertificate(keypair, subject);
assertEquals(subject, cert.getSubjectX500Principal());
String pem = X509CertificateUtils.toPem(cert);
assertThat(pem, containsString("BEGIN CERTIFICATE"));
@@ -41,6 +34,20 @@ public class X509CertificateUtilsTest {
assertEquals(subject, deserializedCert.getSubjectX500Principal());
}
+ @Test
+ public void can_deserialize_serialized_pem_certificate_list() {
+ KeyPair keypair = KeyUtils.generateKeypair(KeyAlgorithm.RSA, 2048);
+ X500Principal subject1 = new X500Principal("CN=myservice");
+ X509Certificate cert1 = TestUtils.createCertificate(keypair, subject1);
+ X500Principal subject2 = new X500Principal("CN=myservice");
+ X509Certificate cert2 = TestUtils.createCertificate(keypair, subject2);
+ List<X509Certificate> certificateList = Arrays.asList(cert1, cert2);
+ String pem = X509CertificateUtils.toPem(certificateList);
+ List<X509Certificate> deserializedCertificateList = X509CertificateUtils.certificateListFromPem(pem);
+ assertEquals(2, certificateList.size());
+ assertEquals(subject1, deserializedCertificateList.get(0).getSubjectX500Principal());
+ assertEquals(subject2, deserializedCertificateList.get(1).getSubjectX500Principal());
+ }
@Test
public void can_list_subject_alternative_names() {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/NTokenValidatorTest.java b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/utils/ntoken/NTokenValidatorTest.java
index a70c1572c21..22f97ca8b60 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/NTokenValidatorTest.java
+++ b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/utils/ntoken/NTokenValidatorTest.java
@@ -1,24 +1,24 @@
-// 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.controller.athenz.filter;
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.utils.ntoken;
import com.yahoo.athenz.auth.token.PrincipalToken;
import com.yahoo.vespa.athenz.api.AthenzIdentity;
import com.yahoo.vespa.athenz.api.AthenzPrincipal;
import com.yahoo.vespa.athenz.api.AthenzUser;
import com.yahoo.vespa.athenz.api.NToken;
-import com.yahoo.vespa.hosted.controller.api.integration.athenz.InvalidTokenException;
-import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsKeystore;
+import com.yahoo.vespa.athenz.tls.KeyAlgorithm;
+import com.yahoo.vespa.athenz.tls.KeyUtils;
+import com.yahoo.vespa.athenz.utils.ntoken.NTokenValidator.InvalidTokenException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
+import java.security.PublicKey;
import java.time.Instant;
import java.util.Optional;
-import static com.yahoo.vespa.athenz.utils.AthenzIdentities.ZMS_ATHENZ_SERVICE;
import static org.junit.Assert.assertEquals;
/**
@@ -26,16 +26,16 @@ import static org.junit.Assert.assertEquals;
*/
public class NTokenValidatorTest {
- private static final KeyPair TRUSTED_KEY = AthenzTestUtils.generateRsaKeypair();
- private static final KeyPair UNKNOWN_KEY = AthenzTestUtils.generateRsaKeypair();
+ private static final KeyPair TRUSTED_KEY = KeyUtils.generateKeypair(KeyAlgorithm.RSA);
+ private static final KeyPair UNKNOWN_KEY = KeyUtils.generateKeypair(KeyAlgorithm.RSA);
private static final AthenzIdentity IDENTITY = AthenzUser.fromUserId("myuser");
@Rule
public ExpectedException exceptionRule = ExpectedException.none();
@Test
- public void valid_token_is_accepted() throws NoSuchAlgorithmException, InvalidTokenException {
- NTokenValidator validator = new NTokenValidator(createKeystore());
+ public void valid_token_is_accepted() throws InvalidTokenException {
+ NTokenValidator validator = new NTokenValidator(createTruststore());
NToken token = createNToken(IDENTITY, Instant.now(), TRUSTED_KEY.getPrivate(), "0");
AthenzPrincipal principal = validator.validate(token);
assertEquals("user.myuser", principal.getIdentity().getFullName());
@@ -43,7 +43,7 @@ public class NTokenValidatorTest {
@Test
public void invalid_signature_is_not_accepted() throws InvalidTokenException {
- NTokenValidator validator = new NTokenValidator(createKeystore());
+ NTokenValidator validator = new NTokenValidator(createTruststore());
NToken token = createNToken(IDENTITY, Instant.now(), UNKNOWN_KEY.getPrivate(), "0");
exceptionRule.expect(InvalidTokenException.class);
exceptionRule.expectMessage("NToken is expired or has invalid signature");
@@ -52,7 +52,7 @@ public class NTokenValidatorTest {
@Test
public void expired_token_is_not_accepted() throws InvalidTokenException {
- NTokenValidator validator = new NTokenValidator(createKeystore());
+ NTokenValidator validator = new NTokenValidator(createTruststore());
NToken token = createNToken(IDENTITY, Instant.ofEpochMilli(1234) /*long time ago*/, TRUSTED_KEY.getPrivate(), "0");
exceptionRule.expect(InvalidTokenException.class);
exceptionRule.expectMessage("NToken is expired or has invalid signature");
@@ -61,28 +61,25 @@ public class NTokenValidatorTest {
@Test
public void unknown_keyId_is_not_accepted() throws InvalidTokenException {
- NTokenValidator validator = new NTokenValidator(createKeystore());
+ NTokenValidator validator = new NTokenValidator(createTruststore());
NToken token = createNToken(IDENTITY, Instant.now(), TRUSTED_KEY.getPrivate(), "unknown-key-id");
exceptionRule.expect(InvalidTokenException.class);
exceptionRule.expectMessage("NToken has an unknown keyId");
validator.validate(token);
}
- @Test
- public void failing_to_find_key_should_throw_exception() throws InvalidTokenException {
- ZmsKeystore keystore = (athensService, keyId) -> { throw new RuntimeException(); };
- NTokenValidator validator = new NTokenValidator(keystore);
- NToken token = createNToken(IDENTITY, Instant.now(), TRUSTED_KEY.getPrivate(), "0");
- exceptionRule.expect(InvalidTokenException.class);
- exceptionRule.expectMessage("Failed to retrieve public key");
- validator.validate(token);
- }
+ private static AthenzTruststore createTruststore() {
+ return new AthenzTruststore() {
+ @Override
+ public Optional<PublicKey> getZmsPublicKey(String keyId) {
+ return keyId.equals("0") ? Optional.of(TRUSTED_KEY.getPublic()) : Optional.empty();
+ }
- private static ZmsKeystore createKeystore() {
- return (athensService, keyId) ->
- athensService.equals(ZMS_ATHENZ_SERVICE) && keyId.equals("0")
- ? Optional.of(TRUSTED_KEY.getPublic())
- : Optional.empty();
+ @Override
+ public Optional<PublicKey> getZtsPublicKey(String keyId) {
+ return Optional.empty();
+ }
+ };
}
private static NToken createNToken(AthenzIdentity identity, Instant issueTime, PrivateKey privateKey, String keyId) {
@@ -91,6 +88,7 @@ public class NTokenValidatorTest {
.salt("1234")
.host("host")
.ip("1.2.3.4")
+ .keyService("zms")
.issueTime(issueTime.getEpochSecond())
.expirationWindow(1000)
.build();
diff --git a/vespa-documentgen-plugin/pom.xml b/vespa-documentgen-plugin/pom.xml
index 33f2bd9904b..6ce6fef7fce 100644
--- a/vespa-documentgen-plugin/pom.xml
+++ b/vespa-documentgen-plugin/pom.xml
@@ -14,11 +14,18 @@
<name>Vespa DocumentGen Plugin</name>
<dependencies>
<dependency>
+ <!-- TODO: Explicitly list deps instead - container-dev is for bundle development. -->
<groupId>com.yahoo.vespa</groupId>
<artifactId>container-dev</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
+ <!-- TODO: Excluded from container-dev. Remove when deps are explicitly listed. -->
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>linguistics</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
<groupId>com.yahoo.vespa</groupId>
<artifactId>config-model-api</artifactId>
<version>${project.version}</version>
diff --git a/vespa-http-client/pom.xml b/vespa-http-client/pom.xml
index 39e633cdb70..6355f7a56a5 100644
--- a/vespa-http-client/pom.xml
+++ b/vespa-http-client/pom.xml
@@ -26,7 +26,6 @@
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
- <version>4.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
@@ -34,11 +33,6 @@
<version>3.4</version>
</dependency>
<dependency>
- <groupId>org.apache.httpcomponents</groupId>
- <artifactId>httpcore</artifactId>
- <version>4.4.1</version>
- </dependency>
- <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
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 d48a86eea61..c2e94e14486 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
@@ -1,6 +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.http.client.core.communication;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.yahoo.component.Vtag;
import com.yahoo.vespa.http.client.config.ConnectionParams;
import com.yahoo.vespa.http.client.config.Endpoint;
@@ -36,6 +38,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
@@ -48,6 +51,7 @@ import java.util.zip.GZIPOutputStream;
class ApacheGatewayConnection implements GatewayConnection {
private static Logger log = Logger.getLogger(ApacheGatewayConnection.class.getName());
+ private static final ObjectMapper mapper = new ObjectMapper();
private static final String PATH = "/reserved-for-internal-use/feedapi?";
private final List<Integer> SUPPORTED_VERSIONS = new ArrayList<>();
private static final byte[] START_OF_FEED_XML = "<vespafeed>\n".getBytes(StandardCharsets.UTF_8);
@@ -253,7 +257,7 @@ class ApacheGatewayConnection implements GatewayConnection {
throw e;
}
try {
- verifyServerResponseCode(response.getStatusLine());
+ verifyServerResponseCode(response);
verifyServerVersion(response.getFirstHeader(Headers.VERSION));
verifySessionHeader(response.getFirstHeader(Headers.SESSION_ID));
} catch (ServerResponseException e) {
@@ -263,7 +267,8 @@ class ApacheGatewayConnection implements GatewayConnection {
return response.getEntity().getContent();
}
- private void verifyServerResponseCode(StatusLine statusLine) throws ServerResponseException {
+ private void verifyServerResponseCode(HttpResponse response) throws ServerResponseException {
+ StatusLine statusLine = response.getStatusLine();
// We use code 261-299 to report errors related to internal transitive errors that the tenants should not care
// about to avoid masking more serious errors.
int statusCode = statusLine.getStatusCode();
@@ -273,7 +278,22 @@ class ApacheGatewayConnection implements GatewayConnection {
if (statusCode == 299) {
throw new ServerResponseException(429, "Too many requests.");
}
- throw new ServerResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
+ String message = tryGetDetailedErrorMessage(response)
+ .orElseGet(statusLine::getReasonPhrase);
+ throw new ServerResponseException(statusLine.getStatusCode(), message);
+ }
+
+ private static Optional<String> tryGetDetailedErrorMessage(HttpResponse response) {
+ Header contentType = response.getEntity().getContentType();
+ if (contentType == null || !contentType.getValue().equalsIgnoreCase("application/json")) return Optional.empty();
+ try (InputStream in = response.getEntity().getContent()) {
+ JsonNode jsonNode = mapper.readTree(in);
+ JsonNode message = jsonNode.get("message");
+ if (message == null || message.textValue() == null) return Optional.empty();
+ return Optional.of(response.getStatusLine().getReasonPhrase() + " - " + message.textValue());
+ } catch (IOException e) {
+ return Optional.empty();
+ }
}
private void verifySessionHeader(Header serverHeader) throws ServerResponseException {
diff --git a/vespa-http-client/src/test/java/com/yahoo/vespa/http/client/core/communication/ApacheGatewayConnectionTest.java b/vespa-http-client/src/test/java/com/yahoo/vespa/http/client/core/communication/ApacheGatewayConnectionTest.java
index 3136526fd42..1e2d37884ef 100644
--- a/vespa-http-client/src/test/java/com/yahoo/vespa/http/client/core/communication/ApacheGatewayConnectionTest.java
+++ b/vespa-http-client/src/test/java/com/yahoo/vespa/http/client/core/communication/ApacheGatewayConnectionTest.java
@@ -17,7 +17,10 @@ import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.InputStreamEntity;
+import org.apache.http.message.BasicHeader;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
import org.mockito.stubbing.Answer;
import java.io.ByteArrayInputStream;
@@ -25,16 +28,14 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.stub;
@@ -45,6 +46,9 @@ import static org.mockito.Mockito.when;
public class ApacheGatewayConnectionTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
@Test
public void testProtocolV3() throws Exception {
final Endpoint endpoint = Endpoint.create("hostname", 666, false);
@@ -54,7 +58,6 @@ public class ApacheGatewayConnectionTest {
.setEnableV3Protocol(true)
.build();
final List<Document> documents = new ArrayList<>();
- final CountDownLatch verifyContentSentLatch = new CountDownLatch(1);
final String vespaDocContent ="Hello, I a JSON doc.";
final String docId = "42";
@@ -63,7 +66,6 @@ public class ApacheGatewayConnectionTest {
// This is the fake server, takes header client ID and uses this as session Id.
ApacheGatewayConnection.HttpClientFactory mockFactory = mockHttpClientFactory(post -> {
final Header clientIdHeader = post.getFirstHeader(Headers.CLIENT_ID);
- verifyContentSentLatch.countDown();
return httpResponse(clientIdHeader.getValue(), "3");
});
@@ -80,7 +82,6 @@ public class ApacheGatewayConnectionTest {
documents.add(createDoc(docId, vespaDocContent, true));
apacheGatewayConnection.writeOperations(documents);
- assertTrue(verifyContentSentLatch.await(10, TimeUnit.SECONDS));
}
@Test(expected=IllegalArgumentException.class)
@@ -139,8 +140,6 @@ public class ApacheGatewayConnectionTest {
.build();
final List<Document> documents = new ArrayList<>();
- final CountDownLatch verifyContentSentLatch = new CountDownLatch(1);
-
final String vespaDocContent ="Hello, I a JSON doc.";
final String docId = "42";
@@ -157,7 +156,6 @@ public class ApacheGatewayConnectionTest {
assertNotNull(header);
assertThat(header.getValue(), is(FeedParams.DataFormat.JSON_UTF8.name()));
// Test is done.
- verifyContentSentLatch.countDown();
return httpResponse("clientId", "3");
});
@@ -175,7 +173,6 @@ public class ApacheGatewayConnectionTest {
documents.add(createDoc(docId, vespaDocContent, true));
apacheGatewayConnection.writeOperations(documents);
- assertTrue(verifyContentSentLatch.await(10, TimeUnit.SECONDS));
}
@Test
@@ -202,8 +199,6 @@ public class ApacheGatewayConnectionTest {
.build();
final List<Document> documents = new ArrayList<>();
- final CountDownLatch verifyContentSentLatch = new CountDownLatch(1);
-
final String vespaDocContent ="Hello, I am the document data.";
final String docId = "42";
@@ -221,7 +216,6 @@ public class ApacheGatewayConnectionTest {
assertThat(rawContent, is(
doc.getOperationId() + " 38\n" + vespaHeaderText + vespaDocContent + "\n"
+ vespaFooterText));
- verifyContentSentLatch.countDown();
}
return httpResponse("clientId", "3");
@@ -244,7 +238,6 @@ public class ApacheGatewayConnectionTest {
documents.add(doc);
apacheGatewayConnection.writeOperations(documents);
- assertTrue(verifyContentSentLatch.await(10, TimeUnit.SECONDS));
}
@Test
@@ -259,15 +252,12 @@ public class ApacheGatewayConnectionTest {
.addDynamicHeader("foo", headerProvider)
.build();
- CountDownLatch verifyContentSentLatch = new CountDownLatch(1);
-
AtomicInteger counter = new AtomicInteger(1);
ApacheGatewayConnection.HttpClientFactory mockFactory = mockHttpClientFactory(post -> {
Header[] fooHeader = post.getHeaders("foo");
assertEquals(1, fooHeader.length);
assertEquals("foo", fooHeader[0].getName());
assertEquals("v" + counter.getAndIncrement(), fooHeader[0].getValue());
- verifyContentSentLatch.countDown();
return httpResponse("clientId", "3");
});
@@ -287,11 +277,33 @@ public class ApacheGatewayConnectionTest {
documents.add(createDoc("42", "content", true));
apacheGatewayConnection.writeOperations(documents);
apacheGatewayConnection.writeOperations(documents);
- assertTrue(verifyContentSentLatch.await(10, TimeUnit.SECONDS));
verify(headerProvider, times(3)).getHeaderValue(); // 1x connect(), 2x writeOperations()
}
+ @Test
+ public void detailed_error_message_is_extracted_from_error_responses_with_json() throws IOException, ServerResponseException {
+ String reasonPhrase = "Unauthorized";
+ String errorMessage = "Invalid credentials";
+ expectedException.expect(ServerResponseException.class);
+ expectedException.expectMessage(reasonPhrase + " - " + errorMessage);
+
+ ApacheGatewayConnection.HttpClientFactory mockFactory = mockHttpClientFactory(post -> createErrorHttpResponse(401, reasonPhrase, errorMessage));
+
+ ApacheGatewayConnection apacheGatewayConnection =
+ new ApacheGatewayConnection(
+ Endpoint.create("hostname", 666, false),
+ new FeedParams.Builder().build(),
+ "",
+ new ConnectionParams.Builder().build(),
+ mockFactory,
+ "clientId");
+ apacheGatewayConnection.connect();
+ apacheGatewayConnection.handshake();
+
+ apacheGatewayConnection.writeOperations(Collections.singletonList(createDoc("42", "content", true)));
+ }
+
private static ApacheGatewayConnection.HttpClientFactory mockHttpClientFactory(HttpExecuteMock httpExecuteMock) throws IOException {
ApacheGatewayConnection.HttpClientFactory mockFactory =
mock(ApacheGatewayConnection.HttpClientFactory.class);
@@ -355,4 +367,20 @@ public class ApacheGatewayConnectionTest {
when(httpEntityMock.getContent()).thenReturn(inputs);
return httpResponseMock;
}
+
+ private static HttpResponse createErrorHttpResponse(int statusCode, String reasonPhrase, String message) throws IOException {
+ HttpResponse response = mock(HttpResponse.class);
+
+ StatusLine statusLine = mock(StatusLine.class);
+ when(statusLine.getStatusCode()).thenReturn(statusCode);
+ when(statusLine.getReasonPhrase()).thenReturn(reasonPhrase);
+ when(response.getStatusLine()).thenReturn(statusLine);
+
+ HttpEntity httpEntity = mock(HttpEntity.class);
+ when(httpEntity.getContentType()).thenReturn(new BasicHeader("Content-Type", "application/json"));
+ String json = String.format("{\"message\": \"%s\"}", message);
+ when(httpEntity.getContent()).thenReturn(new ByteArrayInputStream(json.getBytes()));
+ when(response.getEntity()).thenReturn(httpEntity);
+ return response;
+ }
}
diff --git a/vespa_feed_perf/src/main/java/com/yahoo/vespa/feed/perf/FeederParams.java b/vespa_feed_perf/src/main/java/com/yahoo/vespa/feed/perf/FeederParams.java
index da7d0f83e3b..af8522f4fc2 100644
--- a/vespa_feed_perf/src/main/java/com/yahoo/vespa/feed/perf/FeederParams.java
+++ b/vespa_feed_perf/src/main/java/com/yahoo/vespa/feed/perf/FeederParams.java
@@ -11,7 +11,7 @@ import java.io.InputStream;
import java.io.PrintStream;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class FeederParams {
diff --git a/vespa_feed_perf/src/main/java/com/yahoo/vespa/feed/perf/SimpleFeeder.java b/vespa_feed_perf/src/main/java/com/yahoo/vespa/feed/perf/SimpleFeeder.java
index f9e4915a74a..e5b243c118a 100644
--- a/vespa_feed_perf/src/main/java/com/yahoo/vespa/feed/perf/SimpleFeeder.java
+++ b/vespa_feed_perf/src/main/java/com/yahoo/vespa/feed/perf/SimpleFeeder.java
@@ -23,7 +23,7 @@ import java.io.PrintStream;
import java.util.concurrent.TimeUnit;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class SimpleFeeder implements ReplyHandler {
diff --git a/vespa_feed_perf/src/test/java/com/yahoo/vespa/feed/perf/FeederParamsTest.java b/vespa_feed_perf/src/test/java/com/yahoo/vespa/feed/perf/FeederParamsTest.java
index 8c8063ad95f..38542c1c6b0 100644
--- a/vespa_feed_perf/src/test/java/com/yahoo/vespa/feed/perf/FeederParamsTest.java
+++ b/vespa_feed_perf/src/test/java/com/yahoo/vespa/feed/perf/FeederParamsTest.java
@@ -16,7 +16,7 @@ import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class FeederParamsTest {
diff --git a/vespa_feed_perf/src/test/java/com/yahoo/vespa/feed/perf/SimpleFeederTest.java b/vespa_feed_perf/src/test/java/com/yahoo/vespa/feed/perf/SimpleFeederTest.java
index 5fd1db1e207..89e52eeee19 100644
--- a/vespa_feed_perf/src/test/java/com/yahoo/vespa/feed/perf/SimpleFeederTest.java
+++ b/vespa_feed_perf/src/test/java/com/yahoo/vespa/feed/perf/SimpleFeederTest.java
@@ -29,7 +29,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class SimpleFeederTest {
diff --git a/vespa_feed_perf/src/test/java/com/yahoo/vespa/feed/perf/SimpleServer.java b/vespa_feed_perf/src/test/java/com/yahoo/vespa/feed/perf/SimpleServer.java
index 2798b8ada9a..058a2636cf0 100644
--- a/vespa_feed_perf/src/test/java/com/yahoo/vespa/feed/perf/SimpleServer.java
+++ b/vespa_feed_perf/src/test/java/com/yahoo/vespa/feed/perf/SimpleServer.java
@@ -19,7 +19,7 @@ import java.io.IOException;
import java.io.PrintWriter;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class SimpleServer {
diff --git a/vespabase/src/rhel-prestart.sh b/vespabase/src/rhel-prestart.sh
index 8061c497454..081d7df18a4 100755
--- a/vespabase/src/rhel-prestart.sh
+++ b/vespabase/src/rhel-prestart.sh
@@ -112,6 +112,7 @@ fixdir ${VESPA_USER} wheel 755 var/db/vespa/index
fixdir ${VESPA_USER} wheel 755 var/db/vespa/logcontrol
fixdir ${VESPA_USER} wheel 755 var/db/vespa/search
fixdir ${VESPA_USER} wheel 755 var/jdisc_core
+fixdir ${VESPA_USER} wheel 755 var/vespa
fixdir ${VESPA_USER} wheel 755 var/vespa/bundlecache
fixdir ${VESPA_USER} wheel 755 var/vespa/bundlecache/configserver
fixdir ${VESPA_USER} wheel 755 var/vespa/cache/config/
diff --git a/vespaclient-java/pom.xml b/vespaclient-java/pom.xml
index dbb48c93f39..a7ac908279b 100644
--- a/vespaclient-java/pom.xml
+++ b/vespaclient-java/pom.xml
@@ -84,18 +84,27 @@
</configuration>
</plugin>
<plugin>
- <artifactId>maven-assembly-plugin</artifactId>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
<configuration>
- <descriptorRefs>
- <descriptorRef>jar-with-dependencies</descriptorRef>
- </descriptorRefs>
+ <finalName>${project.artifactId}-jar-with-dependencies</finalName>
+ <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>
</configuration>
<executions>
<execution>
- <id>make-assembly</id>
<phase>package</phase>
<goals>
- <goal>single</goal>
+ <goal>shade</goal>
</goals>
</execution>
</executions>
diff --git a/vespaclient-java/src/main/sh/vespa-feeder.sh b/vespaclient-java/src/main/sh/vespa-feeder.sh
index 3c1dfaed60d..557a72c1da5 100755
--- a/vespaclient-java/src/main/sh/vespa-feeder.sh
+++ b/vespaclient-java/src/main/sh/vespa-feeder.sh
@@ -75,7 +75,7 @@ export MALLOC_ARENA_MAX=1 # Does not need fast allocation
java \
-server -enableassertions \
-XX:ThreadStackSize=512 \
--XX:MaxJavaStackTraceDepth=-1 \
+-XX:MaxJavaStackTraceDepth=1000000 \
-Djava.library.path=${VESPA_HOME}/libexec64/native:${VESPA_HOME}/lib64 \
-XX:MaxDirectMemorySize=32m -Djava.awt.headless=true \
-Xms128m -Xmx1024m $(getJavaOptionsIPV46) \
diff --git a/vespaclient-java/src/main/sh/vespa-get.sh b/vespaclient-java/src/main/sh/vespa-get.sh
index aef730923de..7c6082f3858 100644
--- a/vespaclient-java/src/main/sh/vespa-get.sh
+++ b/vespaclient-java/src/main/sh/vespa-get.sh
@@ -74,7 +74,7 @@ export MALLOC_ARENA_MAX=1 #Does not need fast allocation
exec java \
-server -enableassertions \
-XX:ThreadStackSize=512 \
--XX:MaxJavaStackTraceDepth=-1 \
+-XX:MaxJavaStackTraceDepth=1000000 \
-Djava.awt.headless=true \
-DVESPA_LOG_LEVEL="all -debug -spam -config -info -event" \
-Xms128m -Xmx1024m $(getJavaOptionsIPV46) \
diff --git a/vespaclient-java/src/main/sh/vespa-stat.sh b/vespaclient-java/src/main/sh/vespa-stat.sh
index d8c33d979d0..de57aece5e4 100644
--- a/vespaclient-java/src/main/sh/vespa-stat.sh
+++ b/vespaclient-java/src/main/sh/vespa-stat.sh
@@ -74,7 +74,7 @@ export MALLOC_ARENA_MAX=1 #Does not need fast allocation
exec java \
-server -enableassertions \
-XX:ThreadStackSize=512 \
--XX:MaxJavaStackTraceDepth=-1 \
+-XX:MaxJavaStackTraceDepth=1000000 \
-Djava.awt.headless=true \
-Xms128m -Xmx1024m $(getJavaOptionsIPV46) \
-cp ${VESPA_HOME}/lib/jars/vespaclient-java-jar-with-dependencies.jar com.yahoo.vespastat.Main "$@"
diff --git a/vespaclient-java/src/main/sh/vespa-summary-benchmark.sh b/vespaclient-java/src/main/sh/vespa-summary-benchmark.sh
index 675b8548d81..3ef5dcd932e 100755
--- a/vespaclient-java/src/main/sh/vespa-summary-benchmark.sh
+++ b/vespaclient-java/src/main/sh/vespa-summary-benchmark.sh
@@ -75,7 +75,7 @@ export MALLOC_ARENA_MAX=1 # Does not need fast allocation
java \
-server -enableassertions \
-XX:ThreadStackSize=512 \
--XX:MaxJavaStackTraceDepth=-1 \
+-XX:MaxJavaStackTraceDepth=1000000 \
-Djava.library.path=${VESPA_HOME}/libexec64/native:${VESPA_HOME}/lib64 \
-XX:MaxDirectMemorySize=32m -Djava.awt.headless=true \
-Xms128m -Xmx1024m $(getJavaOptionsIPV46) \
diff --git a/vespaclient-java/src/main/sh/vespa-visit-target.sh b/vespaclient-java/src/main/sh/vespa-visit-target.sh
index 8e57a70b970..d032ad86a54 100755
--- a/vespaclient-java/src/main/sh/vespa-visit-target.sh
+++ b/vespaclient-java/src/main/sh/vespa-visit-target.sh
@@ -74,7 +74,7 @@ export MALLOC_ARENA_MAX=1 #Does not need fast allocation
exec java \
-server -enableassertions \
-XX:ThreadStackSize=512 \
--XX:MaxJavaStackTraceDepth=-1 \
+-XX:MaxJavaStackTraceDepth=1000000 \
-Djava.library.path=${VESPA_HOME}/libexec64/native:${VESPA_HOME}/lib64 \
-XX:MaxDirectMemorySize=32m -Djava.awt.headless=true $(getJavaOptionsIPV46) \
-cp ${VESPA_HOME}/lib/jars/vespaclient-java-jar-with-dependencies.jar:$CLASSPATH com.yahoo.vespavisit.VdsVisitTarget "$@"
diff --git a/vespaclient-java/src/main/sh/vespa-visit.sh b/vespaclient-java/src/main/sh/vespa-visit.sh
index 5fa9e9451d6..5d47a91ba38 100755
--- a/vespaclient-java/src/main/sh/vespa-visit.sh
+++ b/vespaclient-java/src/main/sh/vespa-visit.sh
@@ -74,7 +74,7 @@ export MALLOC_ARENA_MAX=1 #Does not need fast allocation
exec java \
-server -enableassertions \
-XX:ThreadStackSize=512 \
--XX:MaxJavaStackTraceDepth=-1 \
+-XX:MaxJavaStackTraceDepth=1000000 \
-Djava.library.path=${VESPA_HOME}/libexec64/native:${VESPA_HOME}/lib64 \
-XX:MaxDirectMemorySize=32m -Djava.awt.headless=true \
-Xms128m -Xmx1024m $(getJavaOptionsIPV46) \
diff --git a/vespaclient/src/perl/lib/Yahoo/Vespa/VespaModel.pm b/vespaclient/src/perl/lib/Yahoo/Vespa/VespaModel.pm
index b1675130ec4..fd324540bba 100644
--- a/vespaclient/src/perl/lib/Yahoo/Vespa/VespaModel.pm
+++ b/vespaclient/src/perl/lib/Yahoo/Vespa/VespaModel.pm
@@ -170,10 +170,9 @@ sub retrieveModelConfigDefault { # ()
if (!defined $CONFIG_SERVER_HOST) {
my $temp = `${VESPA_HOME}/bin/vespa-print-default configservers`;
- my @configServerHosts = split(' ', $temp);
- $CONFIG_SERVER_HOST = $configServerHosts[0];
+ chomp($temp);
+ $CONFIG_SERVER_HOST = $temp;
}
- $cmd .= " -s $CONFIG_SERVER_HOST";
if (!defined $CONFIG_SERVER_PORT) {
my $temp = `${VESPA_HOME}/bin/vespa-print-default configserver_rpc_port`;
@@ -182,16 +181,23 @@ sub retrieveModelConfigDefault { # ()
}
$cmd .= " -p $CONFIG_SERVER_PORT";
- printDebug "Fetching model config '$cmd'.\n";
- my @data = `$cmd 2>&1`;
- if ($? != 0 || join(' ', @data) =~ /^error/) {
- printError "Failed to get model config from config command line tool:\n"
- . "Command: $cmd\n"
+ my $errors = "";
+ foreach my $cfshost (split(' ', $CONFIG_SERVER_HOST)) {
+ my $hostcmd = $cmd . " -s $cfshost";
+
+ printDebug "Fetching model config '$hostcmd'.\n";
+ my @data = `$hostcmd 2>&1`;
+ if ($? != 0 || join(' ', @data) =~ /^error/) {
+ $errors .= "Failed to get model config from config command line tool:\n"
+ . "Command: $hostcmd\n"
. "Exit code: $?\n"
. "Output: " . join("\n", @data) . "\n";
- exitApplication(1);
+ } else {
+ return @data;
+ }
}
- return @data;
+ printError $errors;
+ exitApplication(1);
}
sub fetch { # ()
my @data = &$RETRIEVE_MODEL_CONFIG();
diff --git a/vespajlib/pom.xml b/vespajlib/pom.xml
index 81c385c96ab..880d039bc54 100644
--- a/vespajlib/pom.xml
+++ b/vespajlib/pom.xml
@@ -71,6 +71,12 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-exec</artifactId>
</dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>testutil</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
<plugins>
diff --git a/vespajlib/src/main/java/com/yahoo/binaryprefix/BinaryPrefix.java b/vespajlib/src/main/java/com/yahoo/binaryprefix/BinaryPrefix.java
index 4f17b95b074..e01b7d4e142 100644
--- a/vespajlib/src/main/java/com/yahoo/binaryprefix/BinaryPrefix.java
+++ b/vespajlib/src/main/java/com/yahoo/binaryprefix/BinaryPrefix.java
@@ -3,7 +3,7 @@ package com.yahoo.binaryprefix;
/**
* Represents binary prefixes.
- * @author tonytv
+ * @author Tony Vaagenes
*/
public enum BinaryPrefix {
//represents the binary prefix 2^(k*10)
diff --git a/vespajlib/src/main/java/com/yahoo/binaryprefix/BinaryScaledAmount.java b/vespajlib/src/main/java/com/yahoo/binaryprefix/BinaryScaledAmount.java
index 7553c76f257..cecc15e6ced 100644
--- a/vespajlib/src/main/java/com/yahoo/binaryprefix/BinaryScaledAmount.java
+++ b/vespajlib/src/main/java/com/yahoo/binaryprefix/BinaryScaledAmount.java
@@ -8,7 +8,7 @@ package com.yahoo.binaryprefix;
* Examples: 2 kilo, 2 mega, ...
* </p>
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public final class BinaryScaledAmount {
public final double amount;
diff --git a/vespajlib/src/main/java/com/yahoo/collections/CollectionUtil.java b/vespajlib/src/main/java/com/yahoo/collections/CollectionUtil.java
index 8e4a7c7e44b..35af30df64b 100644
--- a/vespajlib/src/main/java/com/yahoo/collections/CollectionUtil.java
+++ b/vespajlib/src/main/java/com/yahoo/collections/CollectionUtil.java
@@ -13,7 +13,7 @@ import java.util.stream.Collectors;
/**
* Utilities for java collections
*
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
* @since 5.1.8
*/
diff --git a/vespajlib/src/main/java/com/yahoo/collections/LazyMap.java b/vespajlib/src/main/java/com/yahoo/collections/LazyMap.java
index 48cf2e03eb5..6e18120d69a 100644
--- a/vespajlib/src/main/java/com/yahoo/collections/LazyMap.java
+++ b/vespajlib/src/main/java/com/yahoo/collections/LazyMap.java
@@ -13,7 +13,7 @@ import java.util.Objects;
import java.util.Set;
/**
- * @author <a href="mailto:simon@hult-thoresen.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public abstract class LazyMap<K, V> implements Map<K, V> {
diff --git a/vespajlib/src/main/java/com/yahoo/collections/LazySet.java b/vespajlib/src/main/java/com/yahoo/collections/LazySet.java
index 0de5509828d..d89a61fd2c4 100644
--- a/vespajlib/src/main/java/com/yahoo/collections/LazySet.java
+++ b/vespajlib/src/main/java/com/yahoo/collections/LazySet.java
@@ -10,7 +10,7 @@ import java.util.NoSuchElementException;
import java.util.Set;
/**
- * @author <a href="mailto:simon@hult-thoresen.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public abstract class LazySet<E> implements Set<E> {
diff --git a/vespajlib/src/main/java/com/yahoo/concurrent/EventBarrier.java b/vespajlib/src/main/java/com/yahoo/concurrent/EventBarrier.java
index 8130c369b75..6289ac1b459 100644
--- a/vespajlib/src/main/java/com/yahoo/concurrent/EventBarrier.java
+++ b/vespajlib/src/main/java/com/yahoo/concurrent/EventBarrier.java
@@ -17,7 +17,7 @@ import java.util.List;
* other threads to complete.
*
* @author Haavard Pettersen
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class EventBarrier {
diff --git a/vespajlib/src/main/java/com/yahoo/concurrent/SystemTimer.java b/vespajlib/src/main/java/com/yahoo/concurrent/SystemTimer.java
index e318bdb1a8f..aab3dd75f39 100644
--- a/vespajlib/src/main/java/com/yahoo/concurrent/SystemTimer.java
+++ b/vespajlib/src/main/java/com/yahoo/concurrent/SystemTimer.java
@@ -6,7 +6,7 @@ import java.util.concurrent.TimeUnit;
/**
* This is an implementation of {@link Timer} that is backed by an actual system timer.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public enum SystemTimer implements Timer {
diff --git a/vespajlib/src/main/java/com/yahoo/concurrent/Timer.java b/vespajlib/src/main/java/com/yahoo/concurrent/Timer.java
index ef1daa040b4..af8054c735d 100644
--- a/vespajlib/src/main/java/com/yahoo/concurrent/Timer.java
+++ b/vespajlib/src/main/java/com/yahoo/concurrent/Timer.java
@@ -5,7 +5,7 @@ package com.yahoo.concurrent;
* This interface wraps access to some timer that can be used to measure elapsed time, in milliseconds. This
* abstraction allows for unit testing the behavior of time-based constructs.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface Timer {
diff --git a/vespajlib/src/main/java/com/yahoo/data/access/simple/JsonRender.java b/vespajlib/src/main/java/com/yahoo/data/access/simple/JsonRender.java
index bc4543011b3..53be6a82dd2 100644
--- a/vespajlib/src/main/java/com/yahoo/data/access/simple/JsonRender.java
+++ b/vespajlib/src/main/java/com/yahoo/data/access/simple/JsonRender.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.data.access.simple;
-import com.yahoo.text.DoubleFormatter;
import com.yahoo.data.access.*;
/**
@@ -52,10 +51,10 @@ public final class JsonRender
}
private void encodeDOUBLE(double value) {
- if (Double.isNaN(value) || Double.isInfinite(value)) {
- out.append("null");
+ if (Double.isFinite(value)) {
+ out.append(String.valueOf(value));
} else {
- out.append(DoubleFormatter.stringValue(value));
+ out.append("null");
}
}
diff --git a/vespajlib/src/main/java/com/yahoo/errorhandling/Results.java b/vespajlib/src/main/java/com/yahoo/errorhandling/Results.java
index 643ebead06e..95f7b00aed1 100644
--- a/vespajlib/src/main/java/com/yahoo/errorhandling/Results.java
+++ b/vespajlib/src/main/java/com/yahoo/errorhandling/Results.java
@@ -9,7 +9,7 @@ import java.util.Collections;
import java.util.List;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class Results<DATA, ERROR> {
diff --git a/vespajlib/src/main/java/com/yahoo/geo/BoundingBoxParser.java b/vespajlib/src/main/java/com/yahoo/geo/BoundingBoxParser.java
index 5cb8c1db2de..69baa119fde 100644
--- a/vespajlib/src/main/java/com/yahoo/geo/BoundingBoxParser.java
+++ b/vespajlib/src/main/java/com/yahoo/geo/BoundingBoxParser.java
@@ -1,9 +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.geo;
-import com.yahoo.text.DoubleParser;
-
-
/**
* Class for parsing a bounding box in text format:
* "n=37.44899,s=37.3323,e=-121.98241,w=-122.06566"
@@ -102,7 +99,7 @@ public class BoundingBoxParser {
if (nsew != 0 && lastNumStartPos > 0) {
String sub = parseString.substring(lastNumStartPos, pos-1);
try {
- double v = DoubleParser.parse(sub);
+ double v = Double.parseDouble(sub);
if (nsew == 'n') {
if (doneN) {
throw new IllegalArgumentException("multiple limits for 'n' boundary");
diff --git a/vespajlib/src/main/java/com/yahoo/io/reader/NamedReader.java b/vespajlib/src/main/java/com/yahoo/io/reader/NamedReader.java
index 76346f8b1f6..0a437e93f41 100644
--- a/vespajlib/src/main/java/com/yahoo/io/reader/NamedReader.java
+++ b/vespajlib/src/main/java/com/yahoo/io/reader/NamedReader.java
@@ -5,6 +5,8 @@ import com.google.common.annotations.Beta;
import java.io.IOException;
import java.io.Reader;
+import java.io.Writer;
+import java.nio.CharBuffer;
import java.util.List;
/**
@@ -35,25 +37,29 @@ public class NamedReader extends Reader {
// The rest is reader method implementations which delegates to the wrapped reader
@Override
- public int read(java.nio.CharBuffer charBuffer) throws java.io.IOException { return reader.read(charBuffer); }
+ public int read(CharBuffer charBuffer) throws IOException { return reader.read(charBuffer); }
@Override
- public int read() throws java.io.IOException { return reader.read(); }
+ public int read() throws IOException { return reader.read(); }
@Override
- public int read(char[] chars) throws java.io.IOException { return reader.read(chars); }
+ public int read(char[] chars) throws IOException { return reader.read(chars); }
@Override
- public int read(char[] chars, int i, int i1) throws java.io.IOException { return reader.read(chars,i,i1); }
+ public int read(char[] chars, int i, int i1) throws IOException { return reader.read(chars,i,i1); }
@Override
- public long skip(long l) throws java.io.IOException { return reader.skip(l); }
+ public long skip(long l) throws IOException { return reader.skip(l); }
@Override
- public boolean ready() throws java.io.IOException { return reader.ready(); }
+ public boolean ready() throws IOException { return reader.ready(); }
@Override
public boolean markSupported() { return reader.markSupported(); }
@Override
- public void mark(int i) throws java.io.IOException { reader.mark(i); }
+ public void mark(int i) throws IOException { reader.mark(i); }
@Override
- public void reset() throws java.io.IOException { reader.reset(); }
+ public void reset() throws IOException { reader.reset(); }
@Override
- public void close() throws java.io.IOException { reader.close(); }
+ public void close() throws IOException { reader.close(); }
+
+ // TODO Java 10: uncomment
+// @Override
+// public long transferTo(Writer out) throws IOException { return reader.transferTo(out); }
/** Convenience method for closing a list of readers. Does nothing if the given reader list is null. */
public static void closeAll(List<NamedReader> readers) {
diff --git a/vespajlib/src/main/java/com/yahoo/javacc/FastCharStream.java b/vespajlib/src/main/java/com/yahoo/javacc/FastCharStream.java
index bc40081c316..4084115eb8f 100644
--- a/vespajlib/src/main/java/com/yahoo/javacc/FastCharStream.java
+++ b/vespajlib/src/main/java/com/yahoo/javacc/FastCharStream.java
@@ -4,7 +4,7 @@ package com.yahoo.javacc;
import java.io.IOException;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class FastCharStream {
diff --git a/vespajlib/src/main/java/com/yahoo/javacc/UnicodeUtilities.java b/vespajlib/src/main/java/com/yahoo/javacc/UnicodeUtilities.java
index e216b0d0fac..7bb6b907f59 100644
--- a/vespajlib/src/main/java/com/yahoo/javacc/UnicodeUtilities.java
+++ b/vespajlib/src/main/java/com/yahoo/javacc/UnicodeUtilities.java
@@ -2,7 +2,7 @@
package com.yahoo.javacc;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class UnicodeUtilities {
diff --git a/vespajlib/src/main/java/com/yahoo/lang/SettableOptional.java b/vespajlib/src/main/java/com/yahoo/lang/SettableOptional.java
index db5dd76c124..e83a6c3f308 100644
--- a/vespajlib/src/main/java/com/yahoo/lang/SettableOptional.java
+++ b/vespajlib/src/main/java/com/yahoo/lang/SettableOptional.java
@@ -19,6 +19,11 @@ public final class SettableOptional<T> {
/** Creates a new settable optional with the given value */
public SettableOptional(T value) { this.value = value; }
+ /** Creates a new settable optional with the given value, or an empty */
+ public SettableOptional(Optional<T> value) {
+ this.value = value.isPresent() ? value.get() : null;
+ }
+
public boolean isPresent() {
return value != null;
}
diff --git a/vespajlib/src/main/java/com/yahoo/net/Url.java b/vespajlib/src/main/java/com/yahoo/net/Url.java
index 0cd808f9432..60a9c98bce8 100644
--- a/vespajlib/src/main/java/com/yahoo/net/Url.java
+++ b/vespajlib/src/main/java/com/yahoo/net/Url.java
@@ -5,7 +5,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class Url {
diff --git a/vespajlib/src/main/java/com/yahoo/net/UrlToken.java b/vespajlib/src/main/java/com/yahoo/net/UrlToken.java
index 680b5da954a..602a9b2014a 100644
--- a/vespajlib/src/main/java/com/yahoo/net/UrlToken.java
+++ b/vespajlib/src/main/java/com/yahoo/net/UrlToken.java
@@ -2,7 +2,7 @@
package com.yahoo.net;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class UrlToken {
diff --git a/vespajlib/src/main/java/com/yahoo/net/UrlTokenizer.java b/vespajlib/src/main/java/com/yahoo/net/UrlTokenizer.java
index aa53b5122b1..77d931b5278 100644
--- a/vespajlib/src/main/java/com/yahoo/net/UrlTokenizer.java
+++ b/vespajlib/src/main/java/com/yahoo/net/UrlTokenizer.java
@@ -7,7 +7,7 @@ import java.util.List;
import java.util.Map;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class UrlTokenizer {
diff --git a/vespajlib/src/main/java/com/yahoo/osgi/maven/ProjectBundleClassPaths.java b/vespajlib/src/main/java/com/yahoo/osgi/maven/ProjectBundleClassPaths.java
index 92ba8b79572..a22bf21ba92 100644
--- a/vespajlib/src/main/java/com/yahoo/osgi/maven/ProjectBundleClassPaths.java
+++ b/vespajlib/src/main/java/com/yahoo/osgi/maven/ProjectBundleClassPaths.java
@@ -20,7 +20,7 @@ import java.util.Objects;
* Represents the bundles in a maven project and the classpath elements
* corresponding to code that would end up in the bundle.
*
- * @author tonytv
+ * @author Tony Vaagenes
* @author bjorncs
*/
diff --git a/vespajlib/src/main/java/com/yahoo/reflection/Casting.java b/vespajlib/src/main/java/com/yahoo/reflection/Casting.java
index f3cc8342c70..49c2a690057 100644
--- a/vespajlib/src/main/java/com/yahoo/reflection/Casting.java
+++ b/vespajlib/src/main/java/com/yahoo/reflection/Casting.java
@@ -5,7 +5,7 @@ import java.util.Optional;
/**
* Utility methods for doing casting
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class Casting {
/**
diff --git a/vespajlib/src/main/java/com/yahoo/reflection/package-info.java b/vespajlib/src/main/java/com/yahoo/reflection/package-info.java
index 604598eb287..c28f2552f3f 100644
--- a/vespajlib/src/main/java/com/yahoo/reflection/package-info.java
+++ b/vespajlib/src/main/java/com/yahoo/reflection/package-info.java
@@ -1,7 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
/**
* Package for reflection utility methods.
- * @author tonytv
+ * @author Tony Vaagenes
*/
@ExportPackage
package com.yahoo.reflection;
diff --git a/vespajlib/src/main/java/com/yahoo/slime/JsonDecoder.java b/vespajlib/src/main/java/com/yahoo/slime/JsonDecoder.java
index 3f73faf289d..71eaa7f8b25 100644
--- a/vespajlib/src/main/java/com/yahoo/slime/JsonDecoder.java
+++ b/vespajlib/src/main/java/com/yahoo/slime/JsonDecoder.java
@@ -210,6 +210,7 @@ public class JsonDecoder {
private static byte[] unicodeStart = {'\\', 'u'};
private long dequoteUtf16() {
+ next();
long codepoint = readHexValue(4);
if (codepoint >= 0xd800) {
if (codepoint < 0xdc00) { // high
diff --git a/vespajlib/src/main/java/com/yahoo/slime/JsonFormat.java b/vespajlib/src/main/java/com/yahoo/slime/JsonFormat.java
index d908359df19..c4c82609df7 100644
--- a/vespajlib/src/main/java/com/yahoo/slime/JsonFormat.java
+++ b/vespajlib/src/main/java/com/yahoo/slime/JsonFormat.java
@@ -6,7 +6,6 @@ import com.yahoo.io.ByteWriter;
import com.yahoo.text.AbstractUtf8Array;
import com.yahoo.text.Utf8;
import com.yahoo.text.Utf8String;
-import com.yahoo.text.DoubleFormatter;
import java.io.*;
@@ -107,10 +106,10 @@ public final class JsonFormat implements SlimeFormat
}
private void encodeDOUBLE(double value) throws IOException {
- if (Double.isNaN(value) || Double.isInfinite(value)) {
- out.write(NULL);
+ if (Double.isFinite(value)) {
+ out.write(String.valueOf(value));
} else {
- out.write(DoubleFormatter.stringValue(value));
+ out.write(NULL);
}
}
diff --git a/vespajlib/src/main/java/com/yahoo/text/Ascii.java b/vespajlib/src/main/java/com/yahoo/text/Ascii.java
index 2b21cae106f..85636234f4e 100644
--- a/vespajlib/src/main/java/com/yahoo/text/Ascii.java
+++ b/vespajlib/src/main/java/com/yahoo/text/Ascii.java
@@ -10,7 +10,7 @@ import java.util.Set;
import java.util.TreeSet;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class Ascii {
diff --git a/vespajlib/src/main/java/com/yahoo/text/DoubleFormatter.java b/vespajlib/src/main/java/com/yahoo/text/DoubleFormatter.java
index 29f038a4cd3..7281f3cd9d6 100644
--- a/vespajlib/src/main/java/com/yahoo/text/DoubleFormatter.java
+++ b/vespajlib/src/main/java/com/yahoo/text/DoubleFormatter.java
@@ -1,532 +1,35 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.text;
-import com.google.common.annotations.Beta;
-
/**
* Utility class to format a double into a String.
* <p>
- * This is intended as a lower-cost replacement for the standard
- * String.valueOf(double) since that method will cause lock
- * contention if it's used too often.
- * <p>
- * Note that this implementation won't always produce the same results
- * as java.lang.* formatting.
+ * This was intended as a lower-cost replacement for the standard
+ * String.valueOf(double) since that method used to cause contention.
* <p>
- * Also, this implementation is very poorly tested at the moment, so
- * it should be used carefully, only in cases where you know the input
- * will be well-defined and you don't need full precision.
+ * The contention issue is fixed in Java 8u96 so this class is now obsolete.
*
* @author arnej27959
*/
-@Beta
+@Deprecated
public final class DoubleFormatter {
- private static void tooSmall(StringBuilder target, long mantissa, int exponent) {
- double carry = 0;
- int prExp = 0;
- while (exponent < 0) {
- while (mantissa < (1L << 53)) {
- carry *= 10.0;
- mantissa *= 10;
- ++prExp;
- }
- carry *= 0.5;
- carry += 0.5*(mantissa & 1);
- mantissa >>= 1;
- ++exponent;
- }
- while (mantissa < (1L << 53)) {
- carry *= 10.0;
- mantissa *= 10;
- ++prExp;
- }
- mantissa += (long)(carry+0.5);
-
- int[] befor = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- int b = 0;
- for (int i = 0; mantissa > 0; i++) {
- befor[i] = (int)(mantissa % 10);
- mantissa /= 10;
- ++b;
- }
- --b;
- target.append((char)('0'+befor[b]));
- target.append('.');
- if (b == 0) {
- target.append('0');
- } else {
- for (int i = b; i-- > 0; ) {
- target.append((char)('0'+befor[i]));
- }
- }
- prExp -= b;
- target.append("E-");
- target.append(String.valueOf(prExp));
- }
-
public static StringBuilder fmt(StringBuilder target, double v) {
- append(target, v);
+ target.append(v);
return target;
}
public static String stringValue(double v) {
- return fmt(new StringBuilder(), v).toString();
+ return String.valueOf(v);
}
-
- //Hardcode some byte arrays to make them quickly available
- public static final char[] INFINITY = {'I','n','f','i','n','i','t','y'};
- public static final char[] NaN = {'N','a','N'};
- public static final char[][] ZEROS = {
- {},
- {'0'},
- {'0','0'},
- {'0','0','0'},
- {'0','0','0','0'},
- {'0','0','0','0','0'},
- {'0','0','0','0','0','0'},
- {'0','0','0','0','0','0','0'},
- {'0','0','0','0','0','0','0','0'},
- {'0','0','0','0','0','0','0','0','0'},
- {'0','0','0','0','0','0','0','0','0','0'},
- {'0','0','0','0','0','0','0','0','0','0','0'},
- {'0','0','0','0','0','0','0','0','0','0','0','0'},
- {'0','0','0','0','0','0','0','0','0','0','0','0','0'},
- {'0','0','0','0','0','0','0','0','0','0','0','0','0','0'},
- {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'},
- {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'},
- {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'},
- {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'},
- {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'},
- {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'},
- };
-
- private static final char[] charForDigit = {
- '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h',
- 'i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'
- };
-
- //And required double related constants.
- private static final long DoubleSignMask = 0x8000000000000000L;
- private static final long DoubleExpMask = 0x7ff0000000000000L;
- private static final long DoubleFractMask= ~(DoubleSignMask|DoubleExpMask);
- private static final int DoubleExpShift = 52;
- private static final int DoubleExpBias = 1023;
-
- private static final double[] d_tenthPowers = {
- 1e-323D, 1e-322D, 1e-321D, 1e-320D, 1e-319D, 1e-318D, 1e-317D, 1e-316D, 1e-315D, 1e-314D,
- 1e-313D, 1e-312D, 1e-311D, 1e-310D, 1e-309D, 1e-308D, 1e-307D, 1e-306D, 1e-305D, 1e-304D,
- 1e-303D, 1e-302D, 1e-301D, 1e-300D, 1e-299D, 1e-298D, 1e-297D, 1e-296D, 1e-295D, 1e-294D,
- 1e-293D, 1e-292D, 1e-291D, 1e-290D, 1e-289D, 1e-288D, 1e-287D, 1e-286D, 1e-285D, 1e-284D,
- 1e-283D, 1e-282D, 1e-281D, 1e-280D, 1e-279D, 1e-278D, 1e-277D, 1e-276D, 1e-275D, 1e-274D,
- 1e-273D, 1e-272D, 1e-271D, 1e-270D, 1e-269D, 1e-268D, 1e-267D, 1e-266D, 1e-265D, 1e-264D,
- 1e-263D, 1e-262D, 1e-261D, 1e-260D, 1e-259D, 1e-258D, 1e-257D, 1e-256D, 1e-255D, 1e-254D,
- 1e-253D, 1e-252D, 1e-251D, 1e-250D, 1e-249D, 1e-248D, 1e-247D, 1e-246D, 1e-245D, 1e-244D,
- 1e-243D, 1e-242D, 1e-241D, 1e-240D, 1e-239D, 1e-238D, 1e-237D, 1e-236D, 1e-235D, 1e-234D,
- 1e-233D, 1e-232D, 1e-231D, 1e-230D, 1e-229D, 1e-228D, 1e-227D, 1e-226D, 1e-225D, 1e-224D,
- 1e-223D, 1e-222D, 1e-221D, 1e-220D, 1e-219D, 1e-218D, 1e-217D, 1e-216D, 1e-215D, 1e-214D,
- 1e-213D, 1e-212D, 1e-211D, 1e-210D, 1e-209D, 1e-208D, 1e-207D, 1e-206D, 1e-205D, 1e-204D,
- 1e-203D, 1e-202D, 1e-201D, 1e-200D, 1e-199D, 1e-198D, 1e-197D, 1e-196D, 1e-195D, 1e-194D,
- 1e-193D, 1e-192D, 1e-191D, 1e-190D, 1e-189D, 1e-188D, 1e-187D, 1e-186D, 1e-185D, 1e-184D,
- 1e-183D, 1e-182D, 1e-181D, 1e-180D, 1e-179D, 1e-178D, 1e-177D, 1e-176D, 1e-175D, 1e-174D,
- 1e-173D, 1e-172D, 1e-171D, 1e-170D, 1e-169D, 1e-168D, 1e-167D, 1e-166D, 1e-165D, 1e-164D,
- 1e-163D, 1e-162D, 1e-161D, 1e-160D, 1e-159D, 1e-158D, 1e-157D, 1e-156D, 1e-155D, 1e-154D,
- 1e-153D, 1e-152D, 1e-151D, 1e-150D, 1e-149D, 1e-148D, 1e-147D, 1e-146D, 1e-145D, 1e-144D,
- 1e-143D, 1e-142D, 1e-141D, 1e-140D, 1e-139D, 1e-138D, 1e-137D, 1e-136D, 1e-135D, 1e-134D,
- 1e-133D, 1e-132D, 1e-131D, 1e-130D, 1e-129D, 1e-128D, 1e-127D, 1e-126D, 1e-125D, 1e-124D,
- 1e-123D, 1e-122D, 1e-121D, 1e-120D, 1e-119D, 1e-118D, 1e-117D, 1e-116D, 1e-115D, 1e-114D,
- 1e-113D, 1e-112D, 1e-111D, 1e-110D, 1e-109D, 1e-108D, 1e-107D, 1e-106D, 1e-105D, 1e-104D,
- 1e-103D, 1e-102D, 1e-101D, 1e-100D, 1e-99D, 1e-98D, 1e-97D, 1e-96D, 1e-95D, 1e-94D,
- 1e-93D, 1e-92D, 1e-91D, 1e-90D, 1e-89D, 1e-88D, 1e-87D, 1e-86D, 1e-85D, 1e-84D,
- 1e-83D, 1e-82D, 1e-81D, 1e-80D, 1e-79D, 1e-78D, 1e-77D, 1e-76D, 1e-75D, 1e-74D,
- 1e-73D, 1e-72D, 1e-71D, 1e-70D, 1e-69D, 1e-68D, 1e-67D, 1e-66D, 1e-65D, 1e-64D,
- 1e-63D, 1e-62D, 1e-61D, 1e-60D, 1e-59D, 1e-58D, 1e-57D, 1e-56D, 1e-55D, 1e-54D,
- 1e-53D, 1e-52D, 1e-51D, 1e-50D, 1e-49D, 1e-48D, 1e-47D, 1e-46D, 1e-45D, 1e-44D,
- 1e-43D, 1e-42D, 1e-41D, 1e-40D, 1e-39D, 1e-38D, 1e-37D, 1e-36D, 1e-35D, 1e-34D,
- 1e-33D, 1e-32D, 1e-31D, 1e-30D, 1e-29D, 1e-28D, 1e-27D, 1e-26D, 1e-25D, 1e-24D,
- 1e-23D, 1e-22D, 1e-21D, 1e-20D, 1e-19D, 1e-18D, 1e-17D, 1e-16D, 1e-15D, 1e-14D,
- 1e-13D, 1e-12D, 1e-11D, 1e-10D, 1e-9D, 1e-8D, 1e-7D, 1e-6D, 1e-5D, 1e-4D,
- 1e-3D, 1e-2D, 1e-1D, 1e0D, 1e1D, 1e2D, 1e3D, 1e4D,
- 1e5D, 1e6D, 1e7D, 1e8D, 1e9D, 1e10D, 1e11D, 1e12D, 1e13D, 1e14D,
- 1e15D, 1e16D, 1e17D, 1e18D, 1e19D, 1e20D, 1e21D, 1e22D, 1e23D, 1e24D,
- 1e25D, 1e26D, 1e27D, 1e28D, 1e29D, 1e30D, 1e31D, 1e32D, 1e33D, 1e34D,
- 1e35D, 1e36D, 1e37D, 1e38D, 1e39D, 1e40D, 1e41D, 1e42D, 1e43D, 1e44D,
- 1e45D, 1e46D, 1e47D, 1e48D, 1e49D, 1e50D, 1e51D, 1e52D, 1e53D, 1e54D,
- 1e55D, 1e56D, 1e57D, 1e58D, 1e59D, 1e60D, 1e61D, 1e62D, 1e63D, 1e64D,
- 1e65D, 1e66D, 1e67D, 1e68D, 1e69D, 1e70D, 1e71D, 1e72D, 1e73D, 1e74D,
- 1e75D, 1e76D, 1e77D, 1e78D, 1e79D, 1e80D, 1e81D, 1e82D, 1e83D, 1e84D,
- 1e85D, 1e86D, 1e87D, 1e88D, 1e89D, 1e90D, 1e91D, 1e92D, 1e93D, 1e94D,
- 1e95D, 1e96D, 1e97D, 1e98D, 1e99D, 1e100D, 1e101D, 1e102D, 1e103D, 1e104D,
- 1e105D, 1e106D, 1e107D, 1e108D, 1e109D, 1e110D, 1e111D, 1e112D, 1e113D, 1e114D,
- 1e115D, 1e116D, 1e117D, 1e118D, 1e119D, 1e120D, 1e121D, 1e122D, 1e123D, 1e124D,
- 1e125D, 1e126D, 1e127D, 1e128D, 1e129D, 1e130D, 1e131D, 1e132D, 1e133D, 1e134D,
- 1e135D, 1e136D, 1e137D, 1e138D, 1e139D, 1e140D, 1e141D, 1e142D, 1e143D, 1e144D,
- 1e145D, 1e146D, 1e147D, 1e148D, 1e149D, 1e150D, 1e151D, 1e152D, 1e153D, 1e154D,
- 1e155D, 1e156D, 1e157D, 1e158D, 1e159D, 1e160D, 1e161D, 1e162D, 1e163D, 1e164D,
- 1e165D, 1e166D, 1e167D, 1e168D, 1e169D, 1e170D, 1e171D, 1e172D, 1e173D, 1e174D,
- 1e175D, 1e176D, 1e177D, 1e178D, 1e179D, 1e180D, 1e181D, 1e182D, 1e183D, 1e184D,
- 1e185D, 1e186D, 1e187D, 1e188D, 1e189D, 1e190D, 1e191D, 1e192D, 1e193D, 1e194D,
- 1e195D, 1e196D, 1e197D, 1e198D, 1e199D, 1e200D, 1e201D, 1e202D, 1e203D, 1e204D,
- 1e205D, 1e206D, 1e207D, 1e208D, 1e209D, 1e210D, 1e211D, 1e212D, 1e213D, 1e214D,
- 1e215D, 1e216D, 1e217D, 1e218D, 1e219D, 1e220D, 1e221D, 1e222D, 1e223D, 1e224D,
- 1e225D, 1e226D, 1e227D, 1e228D, 1e229D, 1e230D, 1e231D, 1e232D, 1e233D, 1e234D,
- 1e235D, 1e236D, 1e237D, 1e238D, 1e239D, 1e240D, 1e241D, 1e242D, 1e243D, 1e244D,
- 1e245D, 1e246D, 1e247D, 1e248D, 1e249D, 1e250D, 1e251D, 1e252D, 1e253D, 1e254D,
- 1e255D, 1e256D, 1e257D, 1e258D, 1e259D, 1e260D, 1e261D, 1e262D, 1e263D, 1e264D,
- 1e265D, 1e266D, 1e267D, 1e268D, 1e269D, 1e270D, 1e271D, 1e272D, 1e273D, 1e274D,
- 1e275D, 1e276D, 1e277D, 1e278D, 1e279D, 1e280D, 1e281D, 1e282D, 1e283D, 1e284D,
- 1e285D, 1e286D, 1e287D, 1e288D, 1e289D, 1e290D, 1e291D, 1e292D, 1e293D, 1e294D,
- 1e295D, 1e296D, 1e297D, 1e298D, 1e299D, 1e300D, 1e301D, 1e302D, 1e303D, 1e304D,
- 1e305D, 1e306D, 1e307D, 1e308D
- };
-
-
- /**
- * Assumes i is positive. Returns the magnitude of i in base 10.
- */
- private static long tenthPower(long i)
- {
- if (i < 10L) return 1;
- else if (i < 100L) return 10L;
- else if (i < 1000L) return 100L;
- else if (i < 10000L) return 1000L;
- else if (i < 100000L) return 10000L;
- else if (i < 1000000L) return 100000L;
- else if (i < 10000000L) return 1000000L;
- else if (i < 100000000L) return 10000000L;
- else if (i < 1000000000L) return 100000000L;
- else if (i < 10000000000L) return 1000000000L;
- else if (i < 100000000000L) return 10000000000L;
- else if (i < 1000000000000L) return 100000000000L;
- else if (i < 10000000000000L) return 1000000000000L;
- else if (i < 100000000000000L) return 10000000000000L;
- else if (i < 1000000000000000L) return 100000000000000L;
- else if (i < 10000000000000000L) return 1000000000000000L;
- else if (i < 100000000000000000L) return 10000000000000000L;
- else if (i < 1000000000000000000L) return 100000000000000000L;
- else return 1000000000000000000L;
+ public static void append(StringBuilder s, double d) {
+ s.append(d);
}
-
- private static int magnitude(double d)
- {
- //It works. What else can I say.
- long doubleToLongBits = Double.doubleToLongBits(d);
- int magnitude =
- (int) ((((doubleToLongBits & DoubleExpMask) >> DoubleExpShift) - DoubleExpBias) * 0.301029995663981);
-
- if (magnitude < -323)
- magnitude = -323;
- else if (magnitude > 308)
- magnitude = 308;
-
- if (d >= d_tenthPowers[magnitude+323])
- {
- while(magnitude < 309 && d >= d_tenthPowers[magnitude+323])
- magnitude++;
- magnitude--;
- return magnitude;
- }
- else
- {
- while(magnitude > -324 && d < d_tenthPowers[magnitude+323])
- magnitude--;
- return magnitude;
- }
+ public static void append(StringBuilder s, int i) {
+ s.append(i);
}
- static long[] l_tenthPowers = {
- 1,
- 10L,
- 100L,
- 1000L,
- 10000L,
- 100000L,
- 1000000L,
- 10000000L,
- 100000000L,
- 1000000000L,
- 10000000000L,
- 100000000000L,
- 1000000000000L,
- 10000000000000L,
- 100000000000000L,
- 1000000000000000L,
- 10000000000000000L,
- 100000000000000000L,
- 1000000000000000000L,
- };
-
- public static long getNthDigit(long l, int n)
- {
- return (l/(tenthPower(l)/l_tenthPowers[n-1]))%10;
- }
-
-
-
-
- public static void append(StringBuilder s, double d)
- {
- if (d == Double.NEGATIVE_INFINITY)
- s.append(NEGATIVE_INFINITY);
- else if (d == Double.POSITIVE_INFINITY)
- s.append(POSITIVE_INFINITY);
- else if (d != d)
- s.append(NaN);
- else if (d == 0.0)
- {
- if ( (Double.doubleToLongBits(d) & DoubleSignMask) != 0)
- s.append('-');
- s.append(DOUBLE_ZERO);
- }
- else
- {
- if (d < 0)
- {
- s.append('-');
- d = -d;
- }
-
- if (d >= 0.001 && d < 0.01)
- {
- long i = (long) (d * 1E20);
- i = i%100 >= 50 ? (i/100) + 1 : i/100;
- s.append(DOUBLE_ZERO2);
- appendFractDigits(s, i,-1);
- }
- else if (d >= 0.01 && d < 0.1)
- {
- long i = (long) (d * 1E19);
- i = i%100 >= 50 ? (i/100) + 1 : i/100;
- s.append(DOUBLE_ZERO);
- appendFractDigits(s, i,-1);
- }
- else if (d >= 0.1 && d < 1)
- {
- long i = (long) (d * 1E18);
- i = i%100 >= 50 ? (i/100) + 1 : i/100;
- s.append(DOUBLE_ZERO0);
- appendFractDigits(s, i,-1);
- }
- else if (d >= 1 && d < 10)
- {
- long i = (long) (d * 1E17);
- i = i%100 >= 50 ? (i/100) + 1 : i/100;
- appendFractDigits(s, i,1);
- }
- else if (d >= 10 && d < 100)
- {
- long i = (long) (d * 1E16);
- i = i%100 >= 50 ? (i/100) + 1 : i/100;
- appendFractDigits(s, i,2);
- }
- else if (d >= 100 && d < 1000)
- {
- long i = (long) (d * 1E15);
- i = i%100 >= 50 ? (i/100) + 1 : i/100;
- appendFractDigits(s, i,3);
- }
- else if (d >= 1000 && d < 10000)
- {
- long i = (long) (d * 1E14);
- i = i%100 >= 50 ? (i/100) + 1 : i/100;
- appendFractDigits(s, i,4);
- }
- else if (d >= 10000 && d < 100000)
- {
- long i = (long) (d * 1E13);
- i = i%100 >= 50 ? (i/100) + 1 : i/100;
- appendFractDigits(s, i,5);
- }
- else if (d >= 100000 && d < 1000000)
- {
- long i = (long) (d * 1E12);
- i = i%100 >= 50 ? (i/100) + 1 : i/100;
- appendFractDigits(s, i,6);
- }
- else if (d >= 1000000 && d < 10000000)
- {
- long i = (long) (d * 1E11);
- i = i%100 >= 50 ? (i/100) + 1 : i/100;
- appendFractDigits(s, i,7);
- }
- else
- {
- int magnitude = magnitude(d);
- long i;
- if (magnitude < -280) {
- long valueBits = Double.doubleToRawLongBits(d);
- long mantissa = valueBits & 0x000fffffffffffffL;
- int exponent = (int)((valueBits >> 52) & 0x7ff);
- if (exponent == 0) {
- tooSmall(s, mantissa, -1074);
- } else {
- mantissa |= 0x0010000000000000L;
- exponent -= 1075;
- tooSmall(s, mantissa, exponent);
- }
- } else {
- i = (long) (d / d_tenthPowers[magnitude + 323 - 17]);
- i = i%100 >= 50 ? (i/100) + 1 : i/100;
- appendFractDigits(s, i, 1);
- s.append('E');
- append(s, magnitude);
- }
- }
- }
- }
- public static void append(StringBuilder s, int i)
- {
- if (i < 0)
- {
- if (i == Integer.MIN_VALUE)
- {
- //cannot make this positive due to integer overflow
- s.append("-2147483648");
- }
- s.append('-');
- i = -i;
- }
- int mag;
- int c;
- if (i < 10)
- {
- //one digit
- s.append(charForDigit[i]);
- }
- else if (i < 100)
- {
- //two digits
- s.append(charForDigit[i/10]);
- s.append(charForDigit[i%10]);
- }
- else if (i < 1000)
- {
- //three digits
- s.append(charForDigit[i/100]);
- s.append(charForDigit[(c=i%100)/10]);
- s.append(charForDigit[c%10]);
- }
- else if (i < 10000)
- {
- //four digits
- s.append(charForDigit[i/1000]);
- s.append(charForDigit[(c=i%1000)/100]);
- s.append(charForDigit[(c%=100)/10]);
- s.append(charForDigit[c%10]);
- }
- else if (i < 100000)
- {
- //five digits
- s.append(charForDigit[i/10000]);
- s.append(charForDigit[(c=i%10000)/1000]);
- s.append(charForDigit[(c%=1000)/100]);
- s.append(charForDigit[(c%=100)/10]);
- s.append(charForDigit[c%10]);
- }
- else if (i < 1000000)
- {
- //six digits
- s.append(charForDigit[i/100000]);
- s.append(charForDigit[(c=i%100000)/10000]);
- s.append(charForDigit[(c%=10000)/1000]);
- s.append(charForDigit[(c%=1000)/100]);
- s.append(charForDigit[(c%=100)/10]);
- s.append(charForDigit[c%10]);
- }
- else if (i < 10000000)
- {
- //seven digits
- s.append(charForDigit[i/1000000]);
- s.append(charForDigit[(c=i%1000000)/100000]);
- s.append(charForDigit[(c%=100000)/10000]);
- s.append(charForDigit[(c%=10000)/1000]);
- s.append(charForDigit[(c%=1000)/100]);
- s.append(charForDigit[(c%=100)/10]);
- s.append(charForDigit[c%10]);
- }
- else if (i < 100000000)
- {
- //eight digits
- s.append(charForDigit[i/10000000]);
- s.append(charForDigit[(c=i%10000000)/1000000]);
- s.append(charForDigit[(c%=1000000)/100000]);
- s.append(charForDigit[(c%=100000)/10000]);
- s.append(charForDigit[(c%=10000)/1000]);
- s.append(charForDigit[(c%=1000)/100]);
- s.append(charForDigit[(c%=100)/10]);
- s.append(charForDigit[c%10]);
- }
- else if (i < 1000000000)
- {
- //nine digits
- s.append(charForDigit[i/100000000]);
- s.append(charForDigit[(c=i%100000000)/10000000]);
- s.append(charForDigit[(c%=10000000)/1000000]);
- s.append(charForDigit[(c%=1000000)/100000]);
- s.append(charForDigit[(c%=100000)/10000]);
- s.append(charForDigit[(c%=10000)/1000]);
- s.append(charForDigit[(c%=1000)/100]);
- s.append(charForDigit[(c%=100)/10]);
- s.append(charForDigit[c%10]);
- }
- else
- {
- //ten digits
- s.append(charForDigit[i/1000000000]);
- s.append(charForDigit[(c=i%1000000000)/100000000]);
- s.append(charForDigit[(c%=100000000)/10000000]);
- s.append(charForDigit[(c%=10000000)/1000000]);
- s.append(charForDigit[(c%=1000000)/100000]);
- s.append(charForDigit[(c%=100000)/10000]);
- s.append(charForDigit[(c%=10000)/1000]);
- s.append(charForDigit[(c%=1000)/100]);
- s.append(charForDigit[(c%=100)/10]);
- s.append(charForDigit[c%10]);
- }
- }
- private static void appendFractDigits(StringBuilder s, long i, int decimalOffset)
- {
- long mag = tenthPower(i);
- long c;
- while ( i > 0 )
- {
- c = i/mag;
- s.append(charForDigit[(int) c]);
- decimalOffset--;
- if (decimalOffset == 0)
- s.append('.');
- c *= mag;
- if ( c <= i)
- i -= c;
- mag = mag/10;
- }
- if (i != 0)
- s.append(charForDigit[(int) i]);
- else if (decimalOffset > 0)
- {
- s.append(ZEROS[decimalOffset]);
- decimalOffset = 1;
- }
-
- decimalOffset--;
- if (decimalOffset == 0)
- s.append(DOT_ZERO);
- else if (decimalOffset == -1)
- s.append('0');
- }
-
- public static final char[] NEGATIVE_INFINITY = {'-','I','n','f','i','n','i','t','y'};
- public static final char[] POSITIVE_INFINITY = {'I','n','f','i','n','i','t','y'};
- public static final char[] DOUBLE_ZERO = {'0','.','0'};
- public static final char[] DOUBLE_ZERO2 = {'0','.','0','0'};
- public static final char[] DOUBLE_ZERO0 = {'0','.'};
- public static final char[] DOT_ZERO = {'.','0'};
-
}
diff --git a/vespajlib/src/main/java/com/yahoo/text/DoubleParser.java b/vespajlib/src/main/java/com/yahoo/text/DoubleParser.java
index 6a1fd8e170c..0a777f3000f 100644
--- a/vespajlib/src/main/java/com/yahoo/text/DoubleParser.java
+++ b/vespajlib/src/main/java/com/yahoo/text/DoubleParser.java
@@ -4,20 +4,15 @@ package com.yahoo.text;
/**
* Utility class to parse a String into a double.
* <p>
- * This is intended as a lower-cost replacement for the standard
- * Double.parseDouble(String) since that method will cause lock
- * contention if it's used too often.
+ * This was intended as a lower-cost replacement for the standard
+ * Double.parseDouble(String) since that method used to cause lock
+ * contention.
* <p>
- * Note that this implementation won't always produce the same results
- * as java.lang.Double (low-order bits may differ), and it doesn't
- * properly support denormalized numbers.
- * <p>
- * Also, this implementation is very poorly tested at the moment, so
- * it should be used carefully, only in cases where you know the input
- * will be well-defined and you don't need full precision.
+ * The contention issue is fixed in Java 8u96 so this class is now obsolete.
*
* @author arnej27959
*/
+@Deprecated
public final class DoubleParser {
/**
@@ -29,171 +24,6 @@ public final class DoubleParser {
* @throws NullPointerException if the string is a null pointer
*/
public static double parse(String data) {
- final int len = data.length();
- double result = 0;
- boolean negative = false;
- int beforePoint = 0;
- int exponent = 0;
- byte[] digits = new byte[25];
- int numDigits = 0;
-
- int i = 0;
- while (i < len && Character.isWhitespace(data.charAt(i))) {
- i++;
- }
- if (data.charAt(i) == '+') {
- i++;
- } else if (data.charAt(i) == '-') {
- negative = true;
- i++;
- }
- if (i + 3 <= len && data.substring(i, i+3).equals("NaN")) {
- i += 3;
- result = Double.NaN;
- } else if (i + 8 <= len && data.substring(i, i+8).equals("Infinity")) {
- i += 8;
- if (negative) {
- result = Double.NEGATIVE_INFINITY;
- } else {
- result = Double.POSITIVE_INFINITY;
- }
- } else {
- while (i < len && Character.isDigit(data.charAt(i))) {
- int dval = Character.digit(data.charAt(i), 10);
- assert dval >= 0;
- assert dval < 10;
- if (numDigits < 25) {
- digits[numDigits++] = (byte)dval;
- }
- ++beforePoint;
- i++;
- }
- if (i < len && data.charAt(i) == '.') {
- i++;
- while (i < len && Character.isDigit(data.charAt(i))) {
- int dval = Character.digit(data.charAt(i), 10);
- assert dval >= 0;
- assert dval < 10;
- if (numDigits < 25) {
- digits[numDigits++] = (byte)dval;
- }
- i++;
- }
- }
- if (numDigits == 0) {
- throw new NumberFormatException("No digits in number: '"+data+"'");
- }
- if (i < len && (data.charAt(i) == 'e' || data.charAt(i) == 'E')) {
- i++;
- boolean expNeg = false;
- int expDigits = 0;
- if (data.charAt(i) == '+') {
- i++;
- } else if (data.charAt(i) == '-') {
- expNeg = true;
- i++;
- }
- while (i < len && Character.isDigit(data.charAt(i))) {
- int dval = Character.digit(data.charAt(i), 10);
- assert dval >= 0;
- assert dval < 10;
- exponent *= 10;
- exponent += dval;
- ++expDigits;
- i++;
- }
- if (expDigits == 0) {
- throw new NumberFormatException("Missing digits in exponent part: "+data);
- }
- if (expNeg) {
- exponent = -exponent;
- }
- }
- // System.out.println("parsed exp: "+exponent);
- // System.out.println("before pt: "+beforePoint);
- exponent += beforePoint;
- exponent -= numDigits;
- // System.out.println("adjusted exp: "+exponent);
- for (int d = numDigits; d > 0; d--) {
- double dv = digits[d-1];
- dv *= powTen(numDigits - d);
- result += dv;
- }
- // System.out.println("digits sum: "+result);
- while (exponent < -99) {
- result *= powTen(-99);
- exponent += 99;
- }
- while (exponent > 99) {
- result *= powTen(99);
- exponent -= 99;
- }
- // System.out.println("digits sum: "+result);
- // System.out.println("exponent multiplier: "+powTen(exponent));
- result *= powTen(exponent);
-
- if (negative) {
- result = -result;
- }
- }
- while (i < len && Character.isWhitespace(data.charAt(i))) {
- i++;
- }
- if (i < len) {
- throw new NumberFormatException("Extra characters after number: "+data.substring(i));
- }
- return result;
- }
-
- private static double[] tens = {
- 1.0e00, 1.0e01, 1.0e02, 1.0e03, 1.0e04, 1.0e05, 1.0e06,
- 1.0e07, 1.0e08, 1.0e09, 1.0e10, 1.0e11, 1.0e12, 1.0e13,
- 1.0e14, 1.0e15, 1.0e16, 1.0e17, 1.0e18, 1.0e19, 1.0e20,
- 1.0e21, 1.0e22, 1.0e23, 1.0e24, 1.0e25, 1.0e26, 1.0e27,
- 1.0e28, 1.0e29, 1.0e30, 1.0e31, 1.0e32, 1.0e33, 1.0e34,
- 1.0e35, 1.0e36, 1.0e37, 1.0e38, 1.0e39, 1.0e40, 1.0e41,
- 1.0e42, 1.0e43, 1.0e44, 1.0e45, 1.0e46, 1.0e47, 1.0e48,
- 1.0e49, 1.0e50, 1.0e51, 1.0e52, 1.0e53, 1.0e54, 1.0e55,
- 1.0e56, 1.0e57, 1.0e58, 1.0e59, 1.0e60, 1.0e61, 1.0e62,
- 1.0e63, 1.0e64, 1.0e65, 1.0e66, 1.0e67, 1.0e68, 1.0e69,
- 1.0e70, 1.0e71, 1.0e72, 1.0e73, 1.0e74, 1.0e75, 1.0e76,
- 1.0e77, 1.0e78, 1.0e79, 1.0e80, 1.0e81, 1.0e82, 1.0e83,
- 1.0e84, 1.0e85, 1.0e86, 1.0e87, 1.0e88, 1.0e89, 1.0e90,
- 1.0e91, 1.0e92, 1.0e93, 1.0e94, 1.0e95, 1.0e96, 1.0e97,
- 1.0e98, 1.0e99
- };
-
- private static double[] tenths = {
- 1.0e-00, 1.0e-01, 1.0e-02, 1.0e-03, 1.0e-04, 1.0e-05,
- 1.0e-06, 1.0e-07, 1.0e-08, 1.0e-09, 1.0e-10, 1.0e-11,
- 1.0e-12, 1.0e-13, 1.0e-14, 1.0e-15, 1.0e-16, 1.0e-17,
- 1.0e-18, 1.0e-19, 1.0e-20, 1.0e-21, 1.0e-22, 1.0e-23,
- 1.0e-24, 1.0e-25, 1.0e-26, 1.0e-27, 1.0e-28, 1.0e-29,
- 1.0e-30, 1.0e-31, 1.0e-32, 1.0e-33, 1.0e-34, 1.0e-35,
- 1.0e-36, 1.0e-37, 1.0e-38, 1.0e-39, 1.0e-40, 1.0e-41,
- 1.0e-42, 1.0e-43, 1.0e-44, 1.0e-45, 1.0e-46, 1.0e-47,
- 1.0e-48, 1.0e-49, 1.0e-50, 1.0e-51, 1.0e-52, 1.0e-53,
- 1.0e-54, 1.0e-55, 1.0e-56, 1.0e-57, 1.0e-58, 1.0e-59,
- 1.0e-60, 1.0e-61, 1.0e-62, 1.0e-63, 1.0e-64, 1.0e-65,
- 1.0e-66, 1.0e-67, 1.0e-68, 1.0e-69, 1.0e-70, 1.0e-71,
- 1.0e-72, 1.0e-73, 1.0e-74, 1.0e-75, 1.0e-76, 1.0e-77,
- 1.0e-78, 1.0e-79, 1.0e-80, 1.0e-81, 1.0e-82, 1.0e-83,
- 1.0e-84, 1.0e-85, 1.0e-86, 1.0e-87, 1.0e-88, 1.0e-89,
- 1.0e-90, 1.0e-91, 1.0e-92, 1.0e-93, 1.0e-94, 1.0e-95,
- 1.0e-96, 1.0e-97, 1.0e-98, 1.0e-99
- };
-
- private static double powTen(int exponent) {
- if (exponent > 0) {
- assert exponent < 100;
- return tens[exponent];
- }
- if (exponent < 0) {
- exponent = -exponent;
- assert exponent < 100;
- return tenths[exponent];
- }
- return 1.0;
+ return Double.parseDouble(data);
}
-
}
diff --git a/vespajlib/src/main/java/com/yahoo/time/TimeBudget.java b/vespajlib/src/main/java/com/yahoo/time/TimeBudget.java
new file mode 100644
index 00000000000..a7963df0208
--- /dev/null
+++ b/vespajlib/src/main/java/com/yahoo/time/TimeBudget.java
@@ -0,0 +1,76 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.time;
+
+import com.google.common.util.concurrent.UncheckedTimeoutException;
+
+import java.time.Clock;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Optional;
+
+/**
+ * A TimeBudget can be used to track the time of an ongoing operation, possibly with a timeout.
+ *
+ * @author hakon
+ */
+public class TimeBudget {
+ private final Clock clock;
+ private final Instant start;
+ private final Optional<Duration> timeout;
+
+ /** Returns a TimeBudget with a start time of now, and with the given timeout. */
+ public static TimeBudget fromNow(Clock clock, Duration timeout) {
+ return new TimeBudget(clock, clock.instant(), Optional.of(timeout));
+ }
+
+ public static TimeBudget from(Clock clock, Instant start, Optional<Duration> timeout) {
+ return new TimeBudget(clock, start, timeout);
+ }
+
+ private TimeBudget(Clock clock, Instant start, Optional<Duration> timeout) {
+ this.clock = clock;
+ this.start = start;
+ this.timeout = timeout.map(TimeBudget::makeNonNegative);
+ }
+
+ /** Returns time since start. */
+ public Duration timePassed() {
+ return nonNegativeBetween(start, clock.instant());
+ }
+
+ /** Returns the original timeout, if any. */
+ public Optional<Duration> originalTimeout() {
+ return timeout;
+ }
+
+ /** Returns the deadline, if present. */
+ public Optional<Instant> deadline() {
+ return timeout.map(start::plus);
+ }
+
+ /**
+ * Returns the time until deadline, if there is one.
+ *
+ * @return time until deadline. It's toMillis() is guaranteed to be positive.
+ * @throws UncheckedTimeoutException if the deadline has been reached or passed.
+ */
+ public Optional<Duration> timeLeftOrThrow() {
+ return timeout.map(timeout -> {
+ Duration passed = timePassed();
+ Duration left = timeout.minus(passed);
+ if (left.toMillis() <= 0) {
+ throw new UncheckedTimeoutException("Time since start " + passed + " exceeds timeout " + this.timeout);
+ }
+
+ return left;
+ });
+ }
+
+ private static Duration nonNegativeBetween(Instant start, Instant end) {
+ return makeNonNegative(Duration.between(start, end));
+ }
+
+ private static Duration makeNonNegative(Duration duration) {
+ return duration.isNegative() ? Duration.ZERO : duration;
+ }
+}
diff --git a/vespajlib/src/main/java/com/yahoo/time/WallClockSource.java b/vespajlib/src/main/java/com/yahoo/time/WallClockSource.java
deleted file mode 100644
index 8a77879adf5..00000000000
--- a/vespajlib/src/main/java/com/yahoo/time/WallClockSource.java
+++ /dev/null
@@ -1,118 +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.time;
-
-import com.google.common.annotations.Beta;
-
-/**
- * A source for high-resolution timestamps.
- *
- * @author arnej27959
- */
-
-@Beta
-public class WallClockSource {
-
- private volatile long offset;
-
- /**
- * Obtain the current time in nanoseconds.
- * The epoch is January 1, 1970 UTC just as for System.currentTimeMillis(),
- * but with greater resolution. Note that the absolute accuracy may be
- * no better than currentTimeMills().
- * @return nanoseconds since the epoch.
- **/
- public final long currentTimeNanos() {
- return System.nanoTime() + offset;
- }
-
- /**
- * Create a source with 1 millisecond accuracy at start.
- **/
- WallClockSource() {
- long actual = System.currentTimeMillis();
- actual *= 1000000;
- this.offset = actual - System.nanoTime();
- initialAdjust();
- }
-
- /** adjust the clock source from currentTimeMillis()
- * to ensure that it is no more than 1 milliseconds off.
- * @return true if we want adjust called again soon
- **/
- boolean adjust() {
- long nanosB = System.nanoTime();
- long actual = System.currentTimeMillis();
- long nanosA = System.nanoTime();
- if (nanosA - nanosB > 100000) {
- return true; // not a good time to adjust, try again soon
- }
- return adjustOffset(nanosB, actual, nanosA);
- }
-
- private boolean adjustOffset(long before, long actual, long after) {
- actual *= 1000000; // convert millis to nanos
- if (actual > after + offset) {
- // System.out.println("WallClockSource adjust UP "+(actual-after-offset));
- offset = actual - after;
- return true;
- }
- if (actual + 999999 < before + offset) {
- // System.out.println("WallClockSource adjust DOWN "+(before+offset-actual-999999));
- offset = actual + 999999 - before;
- return true;
- }
- return false;
- }
-
- private void initialAdjust() {
- for (int i = 0; i < 100; i++) {
- long nanosB = System.nanoTime();
- long actual = System.currentTimeMillis();
- long nanosA = System.nanoTime();
- adjustOffset(nanosB, actual, nanosA);
- }
- }
-
-
- static private WallClockSource autoAdjustingInstance = new WallClockSource();
-
- /**
- * Get a WallClockSource which auto adjusts to wall clock time.
- **/
- static public WallClockSource get() {
- autoAdjustingInstance.startAdjuster();
- return autoAdjustingInstance;
- }
-
- private Thread adjuster;
-
- private synchronized void startAdjuster() {
- if (adjuster == null) {
- adjuster = new AdjustThread();
- adjuster.setDaemon(true);
- adjuster.start();
- }
- }
-
- private class AdjustThread extends Thread {
- public void run() {
- int millis = 0;
- int nanos = 313373; // random number
- while (true) {
- try {
- sleep(millis, nanos);
- if (++millis > 4321) {
- millis = 1000; // do not sleep too long
- }
- } catch (InterruptedException e) {
- return;
- }
- if (adjust()) {
- // adjust more often in case clock jumped
- millis = 0;
- }
- }
- }
- }
-
-}
diff --git a/vespajlib/src/main/java/com/yahoo/time/package-info.java b/vespajlib/src/main/java/com/yahoo/time/package-info.java
new file mode 100644
index 00000000000..1b24da17655
--- /dev/null
+++ b/vespajlib/src/main/java/com/yahoo/time/package-info.java
@@ -0,0 +1,5 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+@ExportPackage
+package com.yahoo.time;
+
+import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/vespajlib/src/main/java/com/yahoo/vespa/objects/Identifiable.java b/vespajlib/src/main/java/com/yahoo/vespa/objects/Identifiable.java
index 8d6fb83efd5..0aee5fcdea5 100644
--- a/vespajlib/src/main/java/com/yahoo/vespa/objects/Identifiable.java
+++ b/vespajlib/src/main/java/com/yahoo/vespa/objects/Identifiable.java
@@ -16,7 +16,7 @@ import java.util.HashMap;
* methods.
*
* @author baldersheim
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class Identifiable extends Selectable implements Cloneable {
diff --git a/vespajlib/src/main/java/com/yahoo/vespa/objects/ObjectDumper.java b/vespajlib/src/main/java/com/yahoo/vespa/objects/ObjectDumper.java
index 57a1faffcdd..73b40222f55 100755
--- a/vespajlib/src/main/java/com/yahoo/vespa/objects/ObjectDumper.java
+++ b/vespajlib/src/main/java/com/yahoo/vespa/objects/ObjectDumper.java
@@ -9,7 +9,7 @@ import java.util.List;
/**
* This is a concrete object visitor that will build up a structured human-readable string representation of an object.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class ObjectDumper extends ObjectVisitor {
diff --git a/vespajlib/src/main/java/com/yahoo/vespa/objects/ObjectOperation.java b/vespajlib/src/main/java/com/yahoo/vespa/objects/ObjectOperation.java
index 926e2cbd5c5..4a26564c2ca 100755
--- a/vespajlib/src/main/java/com/yahoo/vespa/objects/ObjectOperation.java
+++ b/vespajlib/src/main/java/com/yahoo/vespa/objects/ObjectOperation.java
@@ -4,7 +4,7 @@ package com.yahoo.vespa.objects;
/**
* An operation that is able to operate on a generic object.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface ObjectOperation {
diff --git a/vespajlib/src/main/java/com/yahoo/vespa/objects/ObjectPredicate.java b/vespajlib/src/main/java/com/yahoo/vespa/objects/ObjectPredicate.java
index 868742dfe8f..d25d7c58dd0 100755
--- a/vespajlib/src/main/java/com/yahoo/vespa/objects/ObjectPredicate.java
+++ b/vespajlib/src/main/java/com/yahoo/vespa/objects/ObjectPredicate.java
@@ -5,7 +5,7 @@ package com.yahoo.vespa.objects;
* A predicate that is able to say either true or false when presented with a
* generic object.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public interface ObjectPredicate {
diff --git a/vespajlib/src/main/java/com/yahoo/vespa/objects/ObjectVisitor.java b/vespajlib/src/main/java/com/yahoo/vespa/objects/ObjectVisitor.java
index 48aaf36204b..a362cc670ee 100755
--- a/vespajlib/src/main/java/com/yahoo/vespa/objects/ObjectVisitor.java
+++ b/vespajlib/src/main/java/com/yahoo/vespa/objects/ObjectVisitor.java
@@ -6,7 +6,7 @@ package com.yahoo.vespa.objects;
* overridden by subclasses. As an extension to this class, the visit.hpp file contains various versions of the visit
* method that maps visitation of various types into invocations of the basic interface defined by this class.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public abstract class ObjectVisitor {
diff --git a/vespajlib/src/test/java/com/yahoo/binaryprefix/BinaryScaledAmountTestCase.java b/vespajlib/src/test/java/com/yahoo/binaryprefix/BinaryScaledAmountTestCase.java
index 4bfb0441668..dd4a2dee8b2 100644
--- a/vespajlib/src/test/java/com/yahoo/binaryprefix/BinaryScaledAmountTestCase.java
+++ b/vespajlib/src/test/java/com/yahoo/binaryprefix/BinaryScaledAmountTestCase.java
@@ -6,7 +6,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class BinaryScaledAmountTestCase {
diff --git a/vespajlib/src/test/java/com/yahoo/collections/CollectionUtilTest.java b/vespajlib/src/test/java/com/yahoo/collections/CollectionUtilTest.java
index 1c580c34e65..30dd0b99d33 100644
--- a/vespajlib/src/test/java/com/yahoo/collections/CollectionUtilTest.java
+++ b/vespajlib/src/test/java/com/yahoo/collections/CollectionUtilTest.java
@@ -12,7 +12,7 @@ import java.util.List;
import static org.junit.Assert.*;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class CollectionUtilTest {
List<Integer> l1 = Arrays.asList(1, 2, 4, 5, 6, 7);
diff --git a/vespajlib/src/test/java/com/yahoo/collections/LazyMapTest.java b/vespajlib/src/test/java/com/yahoo/collections/LazyMapTest.java
index 59340491bb2..f7ca8ae17b7 100644
--- a/vespajlib/src/test/java/com/yahoo/collections/LazyMapTest.java
+++ b/vespajlib/src/test/java/com/yahoo/collections/LazyMapTest.java
@@ -19,7 +19,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class LazyMapTest {
diff --git a/vespajlib/src/test/java/com/yahoo/collections/LazySetTest.java b/vespajlib/src/test/java/com/yahoo/collections/LazySetTest.java
index 0f10057e87a..a37f61a9198 100644
--- a/vespajlib/src/test/java/com/yahoo/collections/LazySetTest.java
+++ b/vespajlib/src/test/java/com/yahoo/collections/LazySetTest.java
@@ -20,7 +20,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class LazySetTest {
diff --git a/vespajlib/src/test/java/com/yahoo/concurrent/EventBarrierTestCase.java b/vespajlib/src/test/java/com/yahoo/concurrent/EventBarrierTestCase.java
index 7946e8ebc56..ad537237307 100644
--- a/vespajlib/src/test/java/com/yahoo/concurrent/EventBarrierTestCase.java
+++ b/vespajlib/src/test/java/com/yahoo/concurrent/EventBarrierTestCase.java
@@ -7,7 +7,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
- * @author Simon Thoresen
+ * @author Simon Thoresen Hult
*/
public class EventBarrierTestCase {
diff --git a/vespajlib/src/test/java/com/yahoo/io/HexDumpTestCase.java b/vespajlib/src/test/java/com/yahoo/io/HexDumpTestCase.java
index 2be0271cdb8..dcea8101d9c 100644
--- a/vespajlib/src/test/java/com/yahoo/io/HexDumpTestCase.java
+++ b/vespajlib/src/test/java/com/yahoo/io/HexDumpTestCase.java
@@ -15,7 +15,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
* @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
*/
public class HexDumpTestCase {
diff --git a/vespajlib/src/test/java/com/yahoo/io/reader/NamedReaderTestCase.java b/vespajlib/src/test/java/com/yahoo/io/reader/NamedReaderTestCase.java
index fcbc855bf74..3bbbf3ea7de 100644
--- a/vespajlib/src/test/java/com/yahoo/io/reader/NamedReaderTestCase.java
+++ b/vespajlib/src/test/java/com/yahoo/io/reader/NamedReaderTestCase.java
@@ -4,10 +4,11 @@ package com.yahoo.io.reader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
+import java.lang.reflect.Method;
import java.nio.CharBuffer;
import java.util.Collections;
+import java.util.List;
-import com.yahoo.io.reader.NamedReader;
import com.yahoo.protect.ClassValidator;
import org.junit.Test;
diff --git a/vespajlib/src/test/java/com/yahoo/javacc/FastCharStreamTestCase.java b/vespajlib/src/test/java/com/yahoo/javacc/FastCharStreamTestCase.java
index bdafc4bfaf9..d8f7b6e9e7e 100644
--- a/vespajlib/src/test/java/com/yahoo/javacc/FastCharStreamTestCase.java
+++ b/vespajlib/src/test/java/com/yahoo/javacc/FastCharStreamTestCase.java
@@ -9,7 +9,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class FastCharStreamTestCase {
diff --git a/vespajlib/src/test/java/com/yahoo/javacc/UnicodeUtilitiesTestCase.java b/vespajlib/src/test/java/com/yahoo/javacc/UnicodeUtilitiesTestCase.java
index fe8100190aa..abeb42ca2e2 100644
--- a/vespajlib/src/test/java/com/yahoo/javacc/UnicodeUtilitiesTestCase.java
+++ b/vespajlib/src/test/java/com/yahoo/javacc/UnicodeUtilitiesTestCase.java
@@ -9,7 +9,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class UnicodeUtilitiesTestCase {
diff --git a/vespajlib/src/test/java/com/yahoo/net/UrlTestCase.java b/vespajlib/src/test/java/com/yahoo/net/UrlTestCase.java
index 71835963ff8..51a058cabf6 100644
--- a/vespajlib/src/test/java/com/yahoo/net/UrlTestCase.java
+++ b/vespajlib/src/test/java/com/yahoo/net/UrlTestCase.java
@@ -7,7 +7,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class UrlTestCase {
diff --git a/vespajlib/src/test/java/com/yahoo/net/UrlTokenTestCase.java b/vespajlib/src/test/java/com/yahoo/net/UrlTokenTestCase.java
index f7f1a0feb59..5ad81a387fd 100644
--- a/vespajlib/src/test/java/com/yahoo/net/UrlTokenTestCase.java
+++ b/vespajlib/src/test/java/com/yahoo/net/UrlTokenTestCase.java
@@ -6,7 +6,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class UrlTokenTestCase {
diff --git a/vespajlib/src/test/java/com/yahoo/net/UrlTokenizerTestCase.java b/vespajlib/src/test/java/com/yahoo/net/UrlTokenizerTestCase.java
index a1d80e789c6..1e6b27e675b 100644
--- a/vespajlib/src/test/java/com/yahoo/net/UrlTokenizerTestCase.java
+++ b/vespajlib/src/test/java/com/yahoo/net/UrlTokenizerTestCase.java
@@ -10,7 +10,7 @@ import static org.junit.Assert.*;
import static com.yahoo.text.Lowercase.toLowerCase;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen Hult
*/
public class UrlTokenizerTestCase {
diff --git a/vespajlib/src/test/java/com/yahoo/slime/JsonFormatTestCase.java b/vespajlib/src/test/java/com/yahoo/slime/JsonFormatTestCase.java
index d82cb61a08c..44aa4ab2ff7 100644
--- a/vespajlib/src/test/java/com/yahoo/slime/JsonFormatTestCase.java
+++ b/vespajlib/src/test/java/com/yahoo/slime/JsonFormatTestCase.java
@@ -198,6 +198,15 @@ public class JsonFormatTestCase {
}
@Test
+ public void testDecodeUnicodeAmp() {
+ final String json = "{\"body\":\"some text\\u0026more text\"}";
+ Slime slime = new Slime();
+ new JsonDecoder().decode(slime, Utf8.toBytesStd(json));
+ Cursor a = slime.get().field("body");
+ assertThat(a.asString(), is("some text&more text"));
+ }
+
+ @Test
public void testDecodeEncodeUtf8() {
final String json = "{\n" +
" \"rules\": \"# Use unicode equivalents in java source:\\n" +
diff --git a/vespajlib/src/test/java/com/yahoo/text/AsciiTest.java b/vespajlib/src/test/java/com/yahoo/text/AsciiTest.java
index 7a0647ba531..af5528a6c7c 100644
--- a/vespajlib/src/test/java/com/yahoo/text/AsciiTest.java
+++ b/vespajlib/src/test/java/com/yahoo/text/AsciiTest.java
@@ -9,7 +9,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class AsciiTest {
diff --git a/vespajlib/src/test/java/com/yahoo/text/DoubleFormatterTestCase.java b/vespajlib/src/test/java/com/yahoo/text/DoubleFormatterTestCase.java
index 9299c96d902..3d0c623bbf4 100644
--- a/vespajlib/src/test/java/com/yahoo/text/DoubleFormatterTestCase.java
+++ b/vespajlib/src/test/java/com/yahoo/text/DoubleFormatterTestCase.java
@@ -4,6 +4,7 @@ package com.yahoo.text;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
+@SuppressWarnings("deprecation")
/**
* @author arnej27959
*/
@@ -73,19 +74,19 @@ public class DoubleFormatterTestCase {
assertEquals("1.0737418255E9", s);
s = DoubleFormatter.stringValue(1.23456789012345669);
- assertEquals("1.234567890123457", s);
+ assertEquals("1.2345678901234567", s);
s = DoubleFormatter.stringValue(12.3456789012345673);
- assertEquals("12.34567890123457", s);
+ assertEquals("12.345678901234567", s);
s = DoubleFormatter.stringValue(123.456789012345666);
- assertEquals("123.4567890123457", s);
+ assertEquals("123.45678901234567", s);
s = DoubleFormatter.stringValue(1234.56789012345666);
- assertEquals("1234.567890123457", s);
+ assertEquals("1234.5678901234567", s);
s = DoubleFormatter.stringValue(12345.6789012345670);
- assertEquals("12345.67890123457", s);
+ assertEquals("12345.678901234567", s);
s = DoubleFormatter.stringValue(123456.789012345674);
- assertEquals("123456.7890123457", s);
+ assertEquals("123456.78901234567", s);
s = DoubleFormatter.stringValue(1234567.89012345671);
- assertEquals("1234567.890123457", s);
+ assertEquals("1234567.8901234567", s);
s = DoubleFormatter.stringValue(0.99);
// assertEquals("0.99", s);
diff --git a/vespajlib/src/test/java/com/yahoo/text/DoubleParserTestCase.java b/vespajlib/src/test/java/com/yahoo/text/DoubleParserTestCase.java
index 7f20b9b89ec..a48a1b6a943 100644
--- a/vespajlib/src/test/java/com/yahoo/text/DoubleParserTestCase.java
+++ b/vespajlib/src/test/java/com/yahoo/text/DoubleParserTestCase.java
@@ -4,6 +4,7 @@ package com.yahoo.text;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
+@SuppressWarnings("deprecation")
/**
* @author arnej27959
*/
diff --git a/vespajlib/src/test/java/com/yahoo/text/DoubleToStringBenchmark.java b/vespajlib/src/test/java/com/yahoo/text/DoubleToStringBenchmark.java
index ec3eae50aa7..a5925df2caf 100644
--- a/vespajlib/src/test/java/com/yahoo/text/DoubleToStringBenchmark.java
+++ b/vespajlib/src/test/java/com/yahoo/text/DoubleToStringBenchmark.java
@@ -12,6 +12,7 @@ import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
+@SuppressWarnings("deprecation")
/**
* @author arnej27959
*/
diff --git a/vespajlib/src/test/java/com/yahoo/time/TimeBudgetTest.java b/vespajlib/src/test/java/com/yahoo/time/TimeBudgetTest.java
new file mode 100644
index 00000000000..f197cc34b32
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/time/TimeBudgetTest.java
@@ -0,0 +1,53 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.time;
+
+import com.google.common.util.concurrent.UncheckedTimeoutException;
+import com.yahoo.test.ManualClock;
+import org.junit.Test;
+
+import java.time.Clock;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Optional;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+public class TimeBudgetTest {
+ private final Clock clock = mock(Clock.class);
+
+ @Test
+ public void testBasics() {
+ ManualClock clock = new ManualClock();
+ clock.setInstant(Instant.ofEpochSecond(0));
+ TimeBudget timeBudget = TimeBudget.fromNow(clock, Duration.ofSeconds(10));
+
+ clock.advance(Duration.ofSeconds(7));
+ assertEquals(Duration.ofSeconds(3), timeBudget.timeLeftOrThrow().get());
+
+ // Verify that toMillis() of >=1 is fine, but 0 is not.
+
+ clock.setInstant(Instant.ofEpochSecond(9, 999000000));
+ assertEquals(1, timeBudget.timeLeftOrThrow().get().toMillis());
+ clock.setInstant(Instant.ofEpochSecond(9, 999000001));
+ try {
+ timeBudget.timeLeftOrThrow();
+ fail();
+ } catch (UncheckedTimeoutException e) {
+ // OK
+ }
+ }
+
+ @Test
+ public void noDeadline() {
+ ManualClock clock = new ManualClock();
+ clock.setInstant(Instant.ofEpochSecond(0));
+ TimeBudget timeBudget = TimeBudget.from(clock, clock.instant(), Optional.empty());
+
+ assertFalse(timeBudget.originalTimeout().isPresent());
+ assertFalse(timeBudget.timeLeftOrThrow().isPresent());
+ assertFalse(timeBudget.deadline().isPresent());
+ }
+} \ No newline at end of file
diff --git a/vespajlib/src/test/java/com/yahoo/time/WallClockSourceTestCase.java b/vespajlib/src/test/java/com/yahoo/time/WallClockSourceTestCase.java
deleted file mode 100644
index 4c181235a38..00000000000
--- a/vespajlib/src/test/java/com/yahoo/time/WallClockSourceTestCase.java
+++ /dev/null
@@ -1,86 +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.time;
-
-import org.junit.Test;
-import static org.junit.Assert.assertTrue;
-
-public class WallClockSourceTestCase {
-
- @Test
- public void testSimple() {
- long actualBefore = System.currentTimeMillis();
- WallClockSource clock = new WallClockSource();
- long nanos = clock.currentTimeNanos();
- long micros = nanos / 1000;
- long millis = micros / 1000;
- long actualAfter = System.currentTimeMillis();
-
- assertTrue(actualBefore <= millis);
- assertTrue(millis <= actualAfter);
- }
-
- @Test
- public void testWithAdjust() {
- WallClockSource clock = new WallClockSource();
- long diffB = 0;
- long diffA = 0;
- for (int i = 0; i < 66666; i++) {
- long actualB = System.currentTimeMillis();
- clock.adjust();
- long nanos = clock.currentTimeNanos();
- long actualA = System.currentTimeMillis();
- long micros = nanos / 1000;
- long millis = micros / 1000;
- diffB = Math.max(diffB, actualB - millis);
- diffA = Math.max(diffA, millis - actualA);
- // System.out.println("adj Timing values, before: "+actualB+" <= guess: "+millis+" <= after: "+actualA);
- }
- System.out.println("adjust test: biggest difference (beforeTime - guess): "+diffB);
- System.out.println("adjust test: biggest difference (guess - afterTime): "+diffA);
- assertTrue("actual time before sample must be <= wallclocksource, diff: " + diffB, diffB < 2);
- assertTrue("actual time after sample must be >= wallclocksource, diff: " + diffA, diffA < 2);
- }
-
- @Test
- public void testNoAdjust() {
- WallClockSource clock = new WallClockSource();
- long diffB = 0;
- long diffA = 0;
- for (int i = 0; i < 66666; i++) {
- long actualB = System.currentTimeMillis();
- long nanos = clock.currentTimeNanos();
- long actualA = System.currentTimeMillis();
- long micros = nanos / 1000;
- long millis = micros / 1000;
- diffB = Math.max(diffB, actualB - millis);
- diffA = Math.max(diffA, millis - actualA);
- // System.out.println("noadj Timing values, before: "+actualB+" <= guess: "+millis+" <= after: "+actualA);
- }
- System.out.println("noadjust test: biggest difference (beforeTime - guess): "+diffB);
- System.out.println("noadjust test: biggest difference (guess - afterTime): "+diffA);
- assertTrue("actual time before sample must be <= wallclocksource, diff: " + diffB, diffB < 3);
- assertTrue("actual time after sample must be >= wallclocksource, diff: " + diffA, diffA < 3);
- }
-
- @Test
- public void testAutoAdjust() {
- WallClockSource clock = WallClockSource.get();
- long diffB = 0;
- long diffA = 0;
- for (int i = 0; i < 66666; i++) {
- long actualB = System.currentTimeMillis();
- long nanos = clock.currentTimeNanos();
- long actualA = System.currentTimeMillis();
- long micros = nanos / 1000;
- long millis = micros / 1000;
- diffB = Math.max(diffB, actualB - millis);
- diffA = Math.max(diffA, millis - actualA);
- // System.out.println("noadj Timing values, before: "+actualB+" <= guess: "+millis+" <= after: "+actualA);
- }
- System.out.println("autoadjust test: biggest difference (beforeTime - guess): "+diffB);
- System.out.println("autoadjust test: biggest difference (guess - afterTime): "+diffA);
- assertTrue("actual time before sample must be <= wallclocksource, diff: " + diffB, diffB < 3);
- assertTrue("actual time after sample must be >= wallclocksource, diff: " + diffA, diffA < 3);
- }
-
-}
diff --git a/vespalib/src/apps/make_fixture_macros/make_fixture_macros.cpp b/vespalib/src/apps/make_fixture_macros/make_fixture_macros.cpp
index 6e23bd46d71..d15b925d31a 100644
--- a/vespalib/src/apps/make_fixture_macros/make_fixture_macros.cpp
+++ b/vespalib/src/apps/make_fixture_macros/make_fixture_macros.cpp
@@ -4,6 +4,9 @@
#include <stdlib.h>
#include <algorithm>
+#include <vespa/log/log.h>
+LOG_SETUP("make_fixture_macros");
+
void out(const char *str) { fprintf(stdout, "%s", str); }
void out_n(const char *str, int n) { fprintf(stdout, str, n); }
void out_nn(const char *str, int n) { fprintf(stdout, str, n, n); }
@@ -24,7 +27,7 @@ void out_list(const char *pre, const char *str, const char *sep, const char *pos
case 0: out(str); break;
case 1: out_n(str, i + 1); break;
case 2: out_nn(str, i + 1); break;
- default: abort();
+ default: LOG_ABORT("should not be reached");
}
}
out_if(post, n > 0);
diff --git a/vespalib/src/tests/delegatelist/delegatelist.cpp b/vespalib/src/tests/delegatelist/delegatelist.cpp
index 8ee68b80ed0..ba1a2049794 100644
--- a/vespalib/src/tests/delegatelist/delegatelist.cpp
+++ b/vespalib/src/tests/delegatelist/delegatelist.cpp
@@ -290,7 +290,7 @@ Actor::perform(int cnt, int start, const CmdList &cmdList)
return cmdList.size();
break;
default:
- abort(); // that does not seem to work
+ LOG_ABORT("should not be reached"); // that does not seem to work
}
}
}
diff --git a/vespalib/src/tests/objects/nbostream/nbostream_test.cpp b/vespalib/src/tests/objects/nbostream/nbostream_test.cpp
index 8b9ccb8a848..ab7265dfd69 100644
--- a/vespalib/src/tests/objects/nbostream/nbostream_test.cpp
+++ b/vespalib/src/tests/objects/nbostream/nbostream_test.cpp
@@ -7,6 +7,7 @@
#include <ostream>
using vespalib::nbostream;
+using vespalib::alloc::Alloc;
using ExpBuffer = std::vector<uint8_t>;
namespace std
@@ -60,6 +61,44 @@ struct Fixture
}
};
+TEST("test that move of owned buffer does not copy") {
+ Alloc buf = Alloc::allocHeap(1000);
+ const void * ptr = buf.get();
+ nbostream os(std::move(buf), 0);
+ os << static_cast<long>(0x567);
+ EXPECT_EQUAL(ptr, os.peek());
+ EXPECT_EQUAL(8ul, os.size());
+ nbostream moved(std::move(os));
+ EXPECT_TRUE(nullptr == os.peek());
+ EXPECT_EQUAL(0ul, os.size());
+ EXPECT_EQUAL(ptr, moved.peek());
+ EXPECT_EQUAL(8ul, moved.size());
+ long tmp(0);
+ moved >> tmp;
+ EXPECT_EQUAL(0x567l, tmp);
+}
+
+TEST("test that move of non-owned buffer does copy") {
+ Alloc buf = Alloc::allocHeap(1000);
+ const void * ptr = buf.get();
+ nbostream os(std::move(buf), 0);
+ os << static_cast<long>(0x567);
+ EXPECT_EQUAL(ptr, os.peek());
+ EXPECT_EQUAL(8ul, os.size());
+ nbostream refering(os.peek(), os.size());
+ EXPECT_EQUAL(ptr, os.peek());
+ EXPECT_EQUAL(8ul, os.size());
+ EXPECT_EQUAL(ptr, refering.peek());
+ EXPECT_EQUAL(8ul, refering.size());
+ nbostream moved(std::move(refering));
+ EXPECT_TRUE(nullptr == refering.peek());
+ EXPECT_EQUAL(0ul, refering.size());
+ EXPECT_TRUE(ptr != moved.peek());
+ EXPECT_EQUAL(8ul, moved.size());
+ long tmp(0);
+ moved >> tmp;
+ EXPECT_EQUAL(0x567l, tmp);
+}
TEST_F("test serializing 64-bit signed integers", Fixture)
{
diff --git a/vespalib/src/vespa/vespalib/component/.gitignore b/vespalib/src/vespa/vespalib/component/.gitignore
index 75fec0dcbdd..583460ae288 100644
--- a/vespalib/src/vespa/vespalib/component/.gitignore
+++ b/vespalib/src/vespa/vespalib/component/.gitignore
@@ -1,4 +1,3 @@
*.So
.depend
Makefile
-getversion
diff --git a/vespalib/src/vespa/vespalib/data/databuffer.cpp b/vespalib/src/vespa/vespalib/data/databuffer.cpp
index 5558a371836..9b04724b601 100644
--- a/vespalib/src/vespa/vespalib/data/databuffer.cpp
+++ b/vespalib/src/vespa/vespalib/data/databuffer.cpp
@@ -11,11 +11,11 @@ size_t padbefore(size_t alignment, const char *buf) {
DataBuffer::DataBuffer(size_t len, size_t alignment, const Alloc & initial)
: _alignment(alignment),
- _externalBuf(NULL),
- _bufstart(NULL),
- _bufend(NULL),
- _datapt(NULL),
- _freept(NULL),
+ _externalBuf(nullptr),
+ _bufstart(nullptr),
+ _bufend(nullptr),
+ _datapt(nullptr),
+ _freept(nullptr),
_buffer(initial.create(0))
{
assert(_alignment > 0);
@@ -29,7 +29,7 @@ DataBuffer::DataBuffer(size_t len, size_t alignment, const Alloc & initial)
_datapt = _bufstart + padbefore(alignment, _bufstart);
_freept = _datapt;
_bufend = _bufstart + bufsize;
- assert(_bufstart != NULL);
+ assert(_bufstart != nullptr);
}
}
@@ -68,8 +68,8 @@ DataBuffer::shrink(size_t newsize)
if (getBufSize() <= newsize || getDataLen() > newsize) {
return false;
}
- char *newbuf = NULL;
- char *newdata = NULL;
+ char *newbuf = nullptr;
+ char *newdata = nullptr;
newsize += (_alignment - 1);
Alloc newBuf(_buffer.create(newsize));
if (newsize != 0) {
diff --git a/vespalib/src/vespa/vespalib/data/databuffer.h b/vespalib/src/vespa/vespalib/data/databuffer.h
index 28524f373b2..a520ecd58bd 100644
--- a/vespalib/src/vespa/vespalib/data/databuffer.h
+++ b/vespalib/src/vespa/vespalib/data/databuffer.h
@@ -44,6 +44,8 @@ public:
typedef std::unique_ptr<DataBuffer> UP;
DataBuffer(const DataBuffer &) = delete;
DataBuffer &operator=(const DataBuffer &) = delete;
+ DataBuffer(DataBuffer &&) = default;
+ DataBuffer &operator=(DataBuffer &&) = default;
/**
* Construct a databuffer.
@@ -61,19 +63,19 @@ public:
* @param buf pointer to preallocated memory
* @param len length of preallocated memory
**/
- DataBuffer(char *buf, size_t len) :
+ DataBuffer(void *buf, size_t len) :
_alignment(1),
- _externalBuf(buf),
- _bufstart(buf),
- _bufend(buf + len),
+ _externalBuf(static_cast<char *>(buf)),
+ _bufstart(_externalBuf),
+ _bufend(_externalBuf + len),
_datapt(_bufstart),
_freept(_bufstart),
_buffer(Alloc::alloc(0))
{ }
- DataBuffer(const char *buf, size_t len) :
+ DataBuffer(const void *buf, size_t len) :
_alignment(1),
- _externalBuf(const_cast<char *>(buf)),
+ _externalBuf(static_cast<char *>(const_cast<void *>(buf))),
_bufstart(_externalBuf),
_bufend(_bufstart + len),
_datapt(_bufstart),
diff --git a/vespalib/src/vespa/vespalib/data/slime/binary_format.cpp b/vespalib/src/vespa/vespalib/data/slime/binary_format.cpp
index 39ee8a40d4a..2febe3f4405 100644
--- a/vespalib/src/vespa/vespalib/data/slime/binary_format.cpp
+++ b/vespalib/src/vespa/vespalib/data/slime/binary_format.cpp
@@ -4,6 +4,9 @@
#include "slime.h"
#include <vespa/vespalib/data/memory_input.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".vespalib.data.slime.binary_format");
+
namespace vespalib {
namespace slime {
@@ -55,7 +58,7 @@ struct BinaryEncoder : public ArrayTraverser,
case ARRAY::ID: return encodeArray(inspector);
case OBJECT::ID: return encodeObject(inspector);
}
- abort(); // should not be reached
+ LOG_ABORT("should not be reached");
}
void encodeSymbolTable(const Slime &slime) {
size_t numSymbols = slime.symbols();
@@ -170,7 +173,7 @@ struct BinaryDecoder : SymbolHandler<remap_symbols>::type {
case ARRAY::ID: return decodeArray(inserter, meta);
case OBJECT::ID: return decodeObject(inserter, meta);
}
- abort(); // code should not be reached
+ LOG_ABORT("should not be reached");
}
void decodeValue(const Inserter &inserter) {
diff --git a/vespalib/src/vespa/vespalib/data/slime/inject.cpp b/vespalib/src/vespa/vespalib/data/slime/inject.cpp
index de1bd840c5a..7480550ceab 100644
--- a/vespalib/src/vespa/vespalib/data/slime/inject.cpp
+++ b/vespalib/src/vespa/vespalib/data/slime/inject.cpp
@@ -6,6 +6,9 @@
#include "object_traverser.h"
#include <cstdlib>
+#include <vespa/log/log.h>
+LOG_SETUP(".vespalib.data.slime.inject");
+
namespace vespalib {
namespace slime {
@@ -48,7 +51,7 @@ void injectValue(const Inserter &inserter, const Inspector &inspector, const Ins
case ARRAY::ID: return injectArray(inserter, inspector, guard);
case OBJECT::ID: return injectObject(inserter, inspector, guard);
}
- abort(); // should not be reached
+ LOG_ABORT("should not be reached");
}
void
diff --git a/vespalib/src/vespa/vespalib/data/slime/json_format.cpp b/vespalib/src/vespa/vespalib/data/slime/json_format.cpp
index 72b494e2479..3e06529cd5b 100644
--- a/vespalib/src/vespa/vespalib/data/slime/json_format.cpp
+++ b/vespalib/src/vespa/vespalib/data/slime/json_format.cpp
@@ -9,6 +9,9 @@
#include <cmath>
#include <sstream>
+#include <vespa/log/log.h>
+LOG_SETUP(".vespalib.data.slime.json_format");
+
namespace vespalib::slime {
namespace {
@@ -133,7 +136,7 @@ struct JsonEncoder : public ArrayTraverser,
case ARRAY::ID: return encodeARRAY(inspector);
case OBJECT::ID: return encodeOBJECT(inspector);
}
- abort(); // should not be reached
+ LOG_ABORT("should not be reached");
}
void entry(size_t idx, const Inspector &inspector) override;
void field(const Memory &symbol_name, const Inspector &inspector) override;
diff --git a/vespalib/src/vespa/vespalib/hwaccelrated/iaccelrated.cpp b/vespalib/src/vespa/vespalib/hwaccelrated/iaccelrated.cpp
index d2ef5297d72..aae277b48d8 100644
--- a/vespalib/src/vespa/vespalib/hwaccelrated/iaccelrated.cpp
+++ b/vespalib/src/vespa/vespalib/hwaccelrated/iaccelrated.cpp
@@ -7,6 +7,9 @@
#include "avx2.h"
#include "avx512.h"
+#include <vespa/log/log.h>
+LOG_SETUP(".vespalib.hwaccelrated");
+
namespace vespalib::hwaccelrated {
namespace {
@@ -57,7 +60,7 @@ void verifyAccelrator(const IAccelrated & accel)
T hwComputedSum(accel.dotProduct(&a[j], &b[j], testLength - j));
if (sum != hwComputedSum) {
fprintf(stderr, "Accelrator is not computing dotproduct correctly.\n");
- abort();
+ LOG_ABORT("should not be reached");
}
}
delete [] a;
diff --git a/vespalib/src/vespa/vespalib/net/selector.cpp b/vespalib/src/vespa/vespalib/net/selector.cpp
index e59638b6144..5d73396bc7d 100644
--- a/vespalib/src/vespa/vespalib/net/selector.cpp
+++ b/vespalib/src/vespa/vespalib/net/selector.cpp
@@ -7,6 +7,7 @@
#include <cstring>
#include <unistd.h>
#include <fcntl.h>
+#include <vespa/log/log.h>
namespace vespalib {
@@ -19,7 +20,7 @@ uint32_t maybe(uint32_t value, bool yes) { return yes ? value : 0; }
void check(int res) {
if (res == -1) {
if (errno == ENOMEM) {
- abort();
+ LOG_ABORT("out of memory");
}
}
}
diff --git a/vespalib/src/vespa/vespalib/objects/nbostream.cpp b/vespalib/src/vespa/vespalib/objects/nbostream.cpp
index ea9684efe01..871c88b5465 100644
--- a/vespalib/src/vespa/vespalib/objects/nbostream.cpp
+++ b/vespalib/src/vespa/vespalib/objects/nbostream.cpp
@@ -63,6 +63,33 @@ nbostream::nbostream(const nbostream & rhs) :
memcpy(&_wbuf[0], &rhs._rbuf[rhs._rp], _wp);
}
+nbostream::nbostream(nbostream && rhs) noexcept
+ : _wbuf(std::move(rhs._wbuf)),
+ _rbuf(rhs._rbuf),
+ _rp(rhs._rp),
+ _wp(rhs._wp),
+ _state(rhs._state),
+ _longLivedBuffer(rhs._longLivedBuffer)
+{
+ rhs._rp = 0;
+ rhs._wp = 0;
+ rhs._rbuf = ConstBufferRef();
+ if (!_longLivedBuffer && (_wbuf.capacity() == 0)) {
+ _wbuf.resize(roundUp2inN(_rbuf.size()));
+ memcpy(&_wbuf[0], &_rbuf[_rp], size());
+ _wp = size();
+ _rp = 0;
+ _rbuf = ConstBufferRef(&_wbuf[0], _wbuf.capacity());
+ }
+}
+
+nbostream &
+nbostream::operator = (nbostream && rhs) noexcept {
+ nbostream tmp(std::move(rhs));
+ swap(tmp);
+ return *this;
+}
+
nbostream &
nbostream::operator = (const nbostream & rhs) {
if (this != &rhs) {
@@ -134,6 +161,7 @@ void nbostream::swap(nbostream & os)
std::swap(_state, os._state);
_wbuf.swap(os._wbuf);
std::swap(_rbuf, os._rbuf);
+ std::swap(_longLivedBuffer, os._longLivedBuffer);
}
}
diff --git a/vespalib/src/vespa/vespalib/objects/nbostream.h b/vespalib/src/vespa/vespalib/objects/nbostream.h
index 70a590f79d1..b51fff1b7cc 100644
--- a/vespalib/src/vespa/vespalib/objects/nbostream.h
+++ b/vespalib/src/vespa/vespalib/objects/nbostream.h
@@ -28,11 +28,11 @@ public:
nbostream(const void * buf, size_t sz);
nbostream(Alloc && buf, size_t sz);
nbostream(const nbostream & rhs);
- nbostream(nbostream && rhs) noexcept = default;
+ nbostream(nbostream && rhs) noexcept;
~nbostream();
nbostream & operator = (const nbostream & rhs);
- nbostream & operator = (nbostream && rhs) noexcept = default;
+ nbostream & operator = (nbostream && rhs) noexcept;
nbostream & operator << (double v) { double n(nbo::n2h(v)); write8(&n); return *this; }
nbostream & operator >> (double & v) { double n; read8(&n); v = nbo::n2h(n); return *this; }
@@ -228,7 +228,7 @@ public:
size_t _rp;
size_t _wp;
State _state;
- const bool _longLivedBuffer;
+ bool _longLivedBuffer;
};
class nbostream_longlivedbuf : public nbostream {
diff --git a/vespalib/src/vespa/vespalib/stllike/asciistream.cpp b/vespalib/src/vespa/vespalib/stllike/asciistream.cpp
index 7b895f3eb73..1be24175ede 100644
--- a/vespalib/src/vespa/vespalib/stllike/asciistream.cpp
+++ b/vespalib/src/vespa/vespalib/stllike/asciistream.cpp
@@ -11,6 +11,9 @@
#include <stdexcept>
#include <cassert>
+#include <vespa/log/log.h>
+LOG_SETUP(".vespalib.stllike.asciistream");
+
namespace vespalib {
namespace {
@@ -521,7 +524,7 @@ void asciistream::write(const void * buf, size_t len)
if (_wbuf.empty()) {
_wbuf = _rbuf; // Read only to RW
} else {
- abort(); // Impossible
+ LOG_ABORT("should not be reached"); // Impossible
}
}
_wbuf.append(buf, len);
diff --git a/vespalib/src/vespa/vespalib/testkit/test_master.cpp b/vespalib/src/vespa/vespalib/testkit/test_master.cpp
index d5309845dfd..81ac6b65851 100644
--- a/vespalib/src/vespa/vespalib/testkit/test_master.cpp
+++ b/vespalib/src/vespa/vespalib/testkit/test_master.cpp
@@ -4,6 +4,9 @@
#include <vespa/vespalib/util/barrier.h>
#include <cstring>
+#include <vespa/log/log.h>
+LOG_SETUP(".vespalib.testkit.test_master");
+
namespace vespalib {
namespace {
@@ -111,7 +114,7 @@ TestMaster::handleFailure(const vespalib::LockGuard &guard, bool fatal)
}
fprintf(stderr, "%s: ERROR: vital check failed, aborting\n",
_name.c_str());
- abort();
+ LOG_ABORT("should not be reached");
}
}
diff --git a/vespalib/src/vespa/vespalib/testkit/time_bomb.cpp b/vespalib/src/vespa/vespalib/testkit/time_bomb.cpp
index a9191a8fc4f..cbefa285384 100644
--- a/vespalib/src/vespa/vespalib/testkit/time_bomb.cpp
+++ b/vespalib/src/vespa/vespalib/testkit/time_bomb.cpp
@@ -1,6 +1,8 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "time_bomb.h"
+#include <vespa/log/log.h>
+LOG_SETUP(".vespalib.testkit.time_bomb");
namespace vespalib {
@@ -20,7 +22,7 @@ void bomb(Gate &gate, size_t seconds) {
}
}
fprintf(stderr, "BOOM!\n");
- abort();
+ LOG_ABORT("should not be reached");
}
} // namespace vespalib::<unnamed>
diff --git a/vespalib/src/vespa/vespalib/util/CMakeLists.txt b/vespalib/src/vespa/vespalib/util/CMakeLists.txt
index 58739ee4df6..f71a5fa26e9 100644
--- a/vespalib/src/vespa/vespalib/util/CMakeLists.txt
+++ b/vespalib/src/vespa/vespalib/util/CMakeLists.txt
@@ -23,6 +23,7 @@ vespa_add_library(vespalib_vespalib_util OBJECT
generationhandler.cpp
generationholder.cpp
hashmap.cpp
+ hdr_abort.cpp
host_name.cpp
joinable.cpp
left_right_heap.cpp
@@ -42,12 +43,12 @@ vespa_add_library(vespalib_vespalib_util OBJECT
simple_thread_bundle.cpp
slaveproc.cpp
stash.cpp
- string_hash.cpp
stringfmt.cpp
- thread.cpp
+ string_hash.cpp
thread_bundle.cpp
- threadstackexecutor.cpp
+ thread.cpp
threadstackexecutorbase.cpp
+ threadstackexecutor.cpp
time_tracker.cpp
valgrind.cpp
zstdcompressor.cpp
diff --git a/vespalib/src/vespa/vespalib/util/hashmap.h b/vespalib/src/vespa/vespalib/util/hashmap.h
index ed686497458..44db499f372 100644
--- a/vespalib/src/vespa/vespalib/util/hashmap.h
+++ b/vespalib/src/vespa/vespalib/util/hashmap.h
@@ -4,6 +4,7 @@
#include "hashmapdata.h"
#include <cstring>
#include <cstdlib>
+#include <assert.h>
/**
* @brief namespace for generic Vespa library
@@ -350,6 +351,7 @@ HashMap<T>::maxDepth() const
}
if (d > ret) ret = d;
}
+ assert(cnt == _entryCnt);
if (cnt != _entryCnt) abort();
return ret;
}
diff --git a/vespalib/src/vespa/vespalib/util/hdr_abort.cpp b/vespalib/src/vespa/vespalib/util/hdr_abort.cpp
new file mode 100644
index 00000000000..e0c46992f3b
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/util/hdr_abort.cpp
@@ -0,0 +1,23 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "hdr_abort.h"
+#include <cstdlib>
+#include <cstdio>
+
+#include <vespa/log/log.h>
+LOG_SETUP(".vespalib");
+
+namespace vespalib {
+
+void hdr_abort(const char *message,
+ const char *file,
+ unsigned int line)
+{
+ LOG(error, "%s:%d: Abort called. Reason: %s",
+ file, line, message);
+ fprintf(stderr, "%s:%d: Abort called. Reason: %s\n",
+ file, line, message);
+ abort();
+}
+
+} // namespace vespalib
diff --git a/vespalib/src/vespa/vespalib/util/hdr_abort.h b/vespalib/src/vespa/vespalib/util/hdr_abort.h
new file mode 100644
index 00000000000..c2c24ada574
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/util/hdr_abort.h
@@ -0,0 +1,14 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+namespace vespalib {
+
+[[noreturn]] extern void
+hdr_abort(const char *message,
+ const char *file,
+ unsigned int line);
+
+#define HDR_ABORT(msg) \
+ (vespalib::hdr_abort(msg, __FILE__, __LINE__))
+
+} // namespace vespalib
diff --git a/vespalog/src/main/java/com/yahoo/log/LogLevel.java b/vespalog/src/main/java/com/yahoo/log/LogLevel.java
index e20dcbf8b30..2d63e6babe2 100644
--- a/vespalog/src/main/java/com/yahoo/log/LogLevel.java
+++ b/vespalog/src/main/java/com/yahoo/log/LogLevel.java
@@ -95,7 +95,6 @@ public class LogLevel extends Level {
javaToVespa.put(Level.FINEST, SPAM);
// need the VESPA ones too
- javaToVespa.put(UNKNOWN, UNKNOWN);
javaToVespa.put(FATAL, FATAL);
javaToVespa.put(ERROR, ERROR);
javaToVespa.put(EVENT, EVENT);
@@ -144,16 +143,27 @@ public class LogLevel extends Level {
* @param level The Java loglevel we want mapped to its VESPA
* counterpart
* @return The VESPA LogLevel instance representing the corresponding
- * log level or the UNKNOWN instance if the log level was
- * unknown (ie. not contained in the mapping. Should never
- * happen).
+ * log level (or nearest normal level numerically if not in map)
*/
public static Level getVespaLogLevel(Level level) {
Level ll = javaToVespa.get(level);
if (ll != null) {
return ll;
}
- return UNKNOWN;
+ int lv = level.intValue();
+ if (lv > WARNING.intValue()) {
+ return ERROR;
+ }
+ if (lv > INFO.intValue()) {
+ return WARNING;
+ }
+ if (lv > DEBUG.intValue()) {
+ return INFO;
+ }
+ if (lv > FINEST.intValue()) {
+ return DEBUG;
+ }
+ return SPAM;
}
/**
diff --git a/vespalog/src/main/java/com/yahoo/log/UncloseableOutputStream.java b/vespalog/src/main/java/com/yahoo/log/UncloseableOutputStream.java
index 254e436982b..65b1526fbf3 100644
--- a/vespalog/src/main/java/com/yahoo/log/UncloseableOutputStream.java
+++ b/vespalog/src/main/java/com/yahoo/log/UncloseableOutputStream.java
@@ -5,7 +5,7 @@ import java.io.IOException;
import java.io.OutputStream;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
* @since 5.1.14
*/
class UncloseableOutputStream extends OutputStream {
diff --git a/vespalog/src/test/java/com/yahoo/log/UncloseableOutputStreamTestCase.java b/vespalog/src/test/java/com/yahoo/log/UncloseableOutputStreamTestCase.java
index 0a0d9e3ab28..9e462a72e9c 100644
--- a/vespalog/src/test/java/com/yahoo/log/UncloseableOutputStreamTestCase.java
+++ b/vespalog/src/test/java/com/yahoo/log/UncloseableOutputStreamTestCase.java
@@ -8,7 +8,7 @@ import java.io.IOException;
import java.io.OutputStream;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class UncloseableOutputStreamTestCase {
diff --git a/vsm/src/vespa/vsm/searcher/fold.cpp b/vsm/src/vespa/vsm/searcher/fold.cpp
index 8974e669eba..903b1e43f79 100644
--- a/vsm/src/vespa/vsm/searcher/fold.cpp
+++ b/vsm/src/vespa/vsm/searcher/fold.cpp
@@ -48,7 +48,7 @@ const unsigned char * sse2_foldaa(const unsigned char * toFoldOrg, size_t sz, un
folded[i] = __builtin_ia32_por128(_0_9, _a_z);
#else
# warning "Intel's icc compiler does not like __builtin_ia32_pxor128"
- abort();
+ LOG_ABORT("should not be reached");
#endif
}
return toFoldOrg+i*16;
@@ -98,7 +98,7 @@ const unsigned char * sse2_foldua(const unsigned char * toFoldOrg, size_t sz, un
folded[i] = __builtin_ia32_por128(_0_9, _a_z);
#else
# warning "Intel's icc compiler does not like __builtin_ia32_pxor128"
- abort();
+ LOG_ABORT("should not be reached");
#endif
}
return toFoldOrg+i*16;
diff --git a/vsm/src/vespa/vsm/searcher/futf8strchrfieldsearcher.cpp b/vsm/src/vespa/vsm/searcher/futf8strchrfieldsearcher.cpp
index ffbc43104e6..b388507aed5 100644
--- a/vsm/src/vespa/vsm/searcher/futf8strchrfieldsearcher.cpp
+++ b/vsm/src/vespa/vsm/searcher/futf8strchrfieldsearcher.cpp
@@ -95,7 +95,7 @@ inline const char * advance(const char * n, const v16qi zero)
charMap = __builtin_ia32_pmovmskb128(tmp0); // 1 in charMap equals to '\0' in input buffer
#else
# warning "Intel's icc compiler does not like __builtin_ia32_xxxxx"
- abort();
+ LOG_ABORT("should not be reached");
#endif
zeroCountSum += 16;
} while (!charMap);
@@ -112,7 +112,7 @@ inline const char * advance(const char * n, const v16qi zero)
zeroMap = __builtin_ia32_pmovmskb128(tmpCurrent); // 1 in zeroMap equals to word character in input buffer
#else
# warning "Intel's icc compiler does not like __builtin_ia32_xxxxx"
- abort();
+ LOG_ABORT("should not be reached");
#endif
zeroCountSum += 16;
} while(!zeroMap);
diff --git a/yolean/src/main/java/com/yahoo/yolean/chain/After.java b/yolean/src/main/java/com/yahoo/yolean/chain/After.java
index cb2caccc6df..c1385c9259f 100644
--- a/yolean/src/main/java/com/yahoo/yolean/chain/After.java
+++ b/yolean/src/main/java/com/yahoo/yolean/chain/After.java
@@ -11,7 +11,7 @@ import java.lang.annotation.Target;
* The component that is annotated with this must be placed later than the components or phases providing names
* contained in the given list.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
diff --git a/yolean/src/main/java/com/yahoo/yolean/chain/Before.java b/yolean/src/main/java/com/yahoo/yolean/chain/Before.java
index 108fbff2e21..004fa20cdf6 100644
--- a/yolean/src/main/java/com/yahoo/yolean/chain/Before.java
+++ b/yolean/src/main/java/com/yahoo/yolean/chain/Before.java
@@ -11,7 +11,7 @@ import java.lang.annotation.Target;
* The component that is annotated with this must be placed earlier than the components or phases providing names
* contained in the given list.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
diff --git a/yolean/src/main/java/com/yahoo/yolean/chain/Chain.java b/yolean/src/main/java/com/yahoo/yolean/chain/Chain.java
index 537d242374d..e57e83c644f 100644
--- a/yolean/src/main/java/com/yahoo/yolean/chain/Chain.java
+++ b/yolean/src/main/java/com/yahoo/yolean/chain/Chain.java
@@ -15,7 +15,7 @@ import static java.util.Objects.requireNonNull;
/**
* An immutable and ordered list of components
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
public final class Chain<T> implements Iterable<T> {
diff --git a/yolean/src/main/java/com/yahoo/yolean/chain/ChainBuilder.java b/yolean/src/main/java/com/yahoo/yolean/chain/ChainBuilder.java
index b324f51274a..35d37f860e4 100644
--- a/yolean/src/main/java/com/yahoo/yolean/chain/ChainBuilder.java
+++ b/yolean/src/main/java/com/yahoo/yolean/chain/ChainBuilder.java
@@ -10,7 +10,7 @@ import java.util.Map;
import java.util.Set;
/**
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
*/
public final class ChainBuilder<T> {
diff --git a/yolean/src/main/java/com/yahoo/yolean/chain/ChainCycleException.java b/yolean/src/main/java/com/yahoo/yolean/chain/ChainCycleException.java
index 50eff4aabc4..022f8f47b43 100644
--- a/yolean/src/main/java/com/yahoo/yolean/chain/ChainCycleException.java
+++ b/yolean/src/main/java/com/yahoo/yolean/chain/ChainCycleException.java
@@ -8,7 +8,7 @@ import java.util.Collections;
import java.util.List;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ChainCycleException extends RuntimeException {
diff --git a/yolean/src/main/java/com/yahoo/yolean/chain/Dependencies.java b/yolean/src/main/java/com/yahoo/yolean/chain/Dependencies.java
index fca0bac753d..33d6dcd40c4 100644
--- a/yolean/src/main/java/com/yahoo/yolean/chain/Dependencies.java
+++ b/yolean/src/main/java/com/yahoo/yolean/chain/Dependencies.java
@@ -10,7 +10,7 @@ import java.util.Collections;
import java.util.List;
/**
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
*/
public class Dependencies<T> {
diff --git a/yolean/src/main/java/com/yahoo/yolean/chain/DirectedGraph.java b/yolean/src/main/java/com/yahoo/yolean/chain/DirectedGraph.java
index cd1680b5722..ec719178e50 100644
--- a/yolean/src/main/java/com/yahoo/yolean/chain/DirectedGraph.java
+++ b/yolean/src/main/java/com/yahoo/yolean/chain/DirectedGraph.java
@@ -11,7 +11,7 @@ import java.util.Set;
/**
* TODO: prioritize vertices in edge map.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
class DirectedGraph {
diff --git a/yolean/src/main/java/com/yahoo/yolean/chain/EnumeratedIdentitySet.java b/yolean/src/main/java/com/yahoo/yolean/chain/EnumeratedIdentitySet.java
index 6cd316fb5f5..3fd6621bc83 100644
--- a/yolean/src/main/java/com/yahoo/yolean/chain/EnumeratedIdentitySet.java
+++ b/yolean/src/main/java/com/yahoo/yolean/chain/EnumeratedIdentitySet.java
@@ -18,7 +18,7 @@ import java.util.TreeMap;
* A set using identity comparison.
* Keeps track of insertion order, which is available by calling insertionOrderedList.
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
class EnumeratedIdentitySet<T> implements Set<T> {
diff --git a/yolean/src/main/java/com/yahoo/yolean/chain/Provides.java b/yolean/src/main/java/com/yahoo/yolean/chain/Provides.java
index 5b02d3bf4d3..54d0fbcbfbe 100644
--- a/yolean/src/main/java/com/yahoo/yolean/chain/Provides.java
+++ b/yolean/src/main/java/com/yahoo/yolean/chain/Provides.java
@@ -11,7 +11,7 @@ import java.lang.annotation.Target;
* <p>Mark this component as providing some named functionality. Other components can then mark themselves as "before"
* and "after" the string provided here, to impose constraints on ordering.</p>
*
- * @author tonytv
+ * @author Tony Vaagenes
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
diff --git a/yolean/src/main/java/com/yahoo/yolean/chain/Vertex.java b/yolean/src/main/java/com/yahoo/yolean/chain/Vertex.java
index 851fe73ff20..0e3647c7121 100644
--- a/yolean/src/main/java/com/yahoo/yolean/chain/Vertex.java
+++ b/yolean/src/main/java/com/yahoo/yolean/chain/Vertex.java
@@ -2,7 +2,7 @@
package com.yahoo.yolean.chain;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
interface Vertex {
diff --git a/yolean/src/test/java/com/yahoo/yolean/chain/ChainBuilderTest.java b/yolean/src/test/java/com/yahoo/yolean/chain/ChainBuilderTest.java
index 73df09fead7..60629e05636 100644
--- a/yolean/src/test/java/com/yahoo/yolean/chain/ChainBuilderTest.java
+++ b/yolean/src/test/java/com/yahoo/yolean/chain/ChainBuilderTest.java
@@ -15,7 +15,7 @@ import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
import static org.junit.Assert.assertTrue;
/**
- * @author tonytv
+ * @author Tony Vaagenes
* @author gjoranv
*/
public class ChainBuilderTest {
diff --git a/yolean/src/test/java/com/yahoo/yolean/chain/ChainTest.java b/yolean/src/test/java/com/yahoo/yolean/chain/ChainTest.java
index af4b3ff4b83..aa344d88f5a 100644
--- a/yolean/src/test/java/com/yahoo/yolean/chain/ChainTest.java
+++ b/yolean/src/test/java/com/yahoo/yolean/chain/ChainTest.java
@@ -8,7 +8,7 @@ import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertThat;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
public class ChainTest {
diff --git a/yolean/src/test/java/com/yahoo/yolean/chain/ContainsSameElements.java b/yolean/src/test/java/com/yahoo/yolean/chain/ContainsSameElements.java
index 5da67733aab..d3fddeafbb3 100644
--- a/yolean/src/test/java/com/yahoo/yolean/chain/ContainsSameElements.java
+++ b/yolean/src/test/java/com/yahoo/yolean/chain/ContainsSameElements.java
@@ -15,7 +15,7 @@ import java.util.Set;
import static java.util.Collections.sort;
/**
- * @author tonytv
+ * @author Tony Vaagenes
*/
class ContainsSameElements<T> extends TypeSafeMatcher<Collection<? super T>> {
diff --git a/yolean/src/test/java/com/yahoo/yolean/trace/TraceVisitorTestCase.java b/yolean/src/test/java/com/yahoo/yolean/trace/TraceVisitorTestCase.java
index 05f66d5d4e8..86373710045 100644
--- a/yolean/src/test/java/com/yahoo/yolean/trace/TraceVisitorTestCase.java
+++ b/yolean/src/test/java/com/yahoo/yolean/trace/TraceVisitorTestCase.java
@@ -4,7 +4,7 @@ package com.yahoo.yolean.trace;
import org.junit.Test;
/**
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author Simon Thoresen Hult
*/
public class TraceVisitorTestCase {
diff --git a/zkfacade/pom.xml b/zkfacade/pom.xml
index 0aa689d896d..3a4e9232d83 100644
--- a/zkfacade/pom.xml
+++ b/zkfacade/pom.xml
@@ -53,7 +53,6 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
- <version>13.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
diff --git a/zkfacade/src/main/java/com/yahoo/vespa/curator/Lock.java b/zkfacade/src/main/java/com/yahoo/vespa/curator/Lock.java
index 1ab5558e2b9..d71660a990f 100644
--- a/zkfacade/src/main/java/com/yahoo/vespa/curator/Lock.java
+++ b/zkfacade/src/main/java/com/yahoo/vespa/curator/Lock.java
@@ -42,8 +42,9 @@ public class Lock implements Mutex {
throw new RuntimeException("Exception acquiring lock '" + lockPath + "'", e);
}
- if (! acquired) throw new UncheckedTimeoutException("Timed out after waiting " + timeout.toString() +
- " to acquire lock '" + lockPath + "'");
+ if (! acquired)
+ throw new UncheckedTimeoutException("Timed out after waiting " + timeout +
+ " to acquire lock '" + lockPath + "'");
}
@Override