diff --git a/docs/docset.yml b/docs/docset.yml index 0c29a1da5..73b405a45 100644 --- a/docs/docset.yml +++ b/docs/docset.yml @@ -2,7 +2,6 @@ project: 'Java API Client' exclude: - external-resources.md - design/* - - local/README.md cross_links: - docs-content - elasticsearch @@ -10,482 +9,7 @@ toc: - toc: reference - toc: release-notes subs: - ref: "https://www.elastic.co/guide/en/elasticsearch/reference/current" - ref-bare: "https://www.elastic.co/guide/en/elasticsearch/reference" - ref-8x: "https://www.elastic.co/guide/en/elasticsearch/reference/8.1" - ref-80: "https://www.elastic.co/guide/en/elasticsearch/reference/8.0" - ref-7x: "https://www.elastic.co/guide/en/elasticsearch/reference/7.17" - ref-70: "https://www.elastic.co/guide/en/elasticsearch/reference/7.0" - ref-60: "https://www.elastic.co/guide/en/elasticsearch/reference/6.0" - ref-64: "https://www.elastic.co/guide/en/elasticsearch/reference/6.4" - xpack-ref: "https://www.elastic.co/guide/en/x-pack/6.2" - logstash-ref: "https://www.elastic.co/guide/en/logstash/current" - kibana-ref: "https://www.elastic.co/guide/en/kibana/current" - kibana-ref-all: "https://www.elastic.co/guide/en/kibana" - beats-ref-root: "https://www.elastic.co/guide/en/beats" - beats-ref: "https://www.elastic.co/guide/en/beats/libbeat/current" - beats-ref-60: "https://www.elastic.co/guide/en/beats/libbeat/6.0" - beats-ref-63: "https://www.elastic.co/guide/en/beats/libbeat/6.3" - beats-devguide: "https://www.elastic.co/guide/en/beats/devguide/current" - auditbeat-ref: "https://www.elastic.co/guide/en/beats/auditbeat/current" - packetbeat-ref: "https://www.elastic.co/guide/en/beats/packetbeat/current" - metricbeat-ref: "https://www.elastic.co/guide/en/beats/metricbeat/current" - filebeat-ref: "https://www.elastic.co/guide/en/beats/filebeat/current" - functionbeat-ref: "https://www.elastic.co/guide/en/beats/functionbeat/current" - winlogbeat-ref: "https://www.elastic.co/guide/en/beats/winlogbeat/current" - heartbeat-ref: "https://www.elastic.co/guide/en/beats/heartbeat/current" - journalbeat-ref: "https://www.elastic.co/guide/en/beats/journalbeat/current" - ingest-guide: "https://www.elastic.co/guide/en/ingest/current" - fleet-guide: "https://www.elastic.co/guide/en/fleet/current" - apm-guide-ref: "https://www.elastic.co/guide/en/apm/guide/current" - apm-guide-7x: "https://www.elastic.co/guide/en/apm/guide/7.17" - apm-app-ref: "https://www.elastic.co/guide/en/kibana/current" - apm-agents-ref: "https://www.elastic.co/guide/en/apm/agent" - apm-android-ref: "https://www.elastic.co/guide/en/apm/agent/android/current" - apm-py-ref: "https://www.elastic.co/guide/en/apm/agent/python/current" - apm-py-ref-3x: "https://www.elastic.co/guide/en/apm/agent/python/3.x" - apm-node-ref-index: "https://www.elastic.co/guide/en/apm/agent/nodejs" - apm-node-ref: "https://www.elastic.co/guide/en/apm/agent/nodejs/current" - apm-node-ref-1x: "https://www.elastic.co/guide/en/apm/agent/nodejs/1.x" - apm-rum-ref: "https://www.elastic.co/guide/en/apm/agent/rum-js/current" - apm-ruby-ref: "https://www.elastic.co/guide/en/apm/agent/ruby/current" - apm-java-ref: "https://www.elastic.co/guide/en/apm/agent/java/current" - apm-go-ref: "https://www.elastic.co/guide/en/apm/agent/go/current" - apm-dotnet-ref: "https://www.elastic.co/guide/en/apm/agent/dotnet/current" - apm-php-ref: "https://www.elastic.co/guide/en/apm/agent/php/current" - apm-ios-ref: "https://www.elastic.co/guide/en/apm/agent/swift/current" - apm-lambda-ref: "https://www.elastic.co/guide/en/apm/lambda/current" - apm-attacher-ref: "https://www.elastic.co/guide/en/apm/attacher/current" - docker-logging-ref: "https://www.elastic.co/guide/en/beats/loggingplugin/current" - esf-ref: "https://www.elastic.co/guide/en/esf/current" - kinesis-firehose-ref: "https://www.elastic.co/guide/en/kinesis/{{kinesis_version}}" - estc-welcome-current: "https://www.elastic.co/guide/en/starting-with-the-elasticsearch-platform-and-its-solutions/current" - estc-welcome: "https://www.elastic.co/guide/en/starting-with-the-elasticsearch-platform-and-its-solutions/current" - estc-welcome-all: "https://www.elastic.co/guide/en/starting-with-the-elasticsearch-platform-and-its-solutions" - hadoop-ref: "https://www.elastic.co/guide/en/elasticsearch/hadoop/current" - stack-ref: "https://www.elastic.co/guide/en/elastic-stack/current" - stack-ref-67: "https://www.elastic.co/guide/en/elastic-stack/6.7" - stack-ref-68: "https://www.elastic.co/guide/en/elastic-stack/6.8" - stack-ref-70: "https://www.elastic.co/guide/en/elastic-stack/7.0" - stack-ref-80: "https://www.elastic.co/guide/en/elastic-stack/8.0" - stack-ov: "https://www.elastic.co/guide/en/elastic-stack-overview/current" - stack-gs: "https://www.elastic.co/guide/en/elastic-stack-get-started/current" - stack-gs-current: "https://www.elastic.co/guide/en/elastic-stack-get-started/current" - javaclient: "https://www.elastic.co/guide/en/elasticsearch/client/java-api/current" - java-api-client: "https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current" - java-rest: "https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current" - jsclient: "https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current" - jsclient-current: "https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current" - es-ruby-client: "https://www.elastic.co/guide/en/elasticsearch/client/ruby-api/current" - es-dotnet-client: "https://www.elastic.co/guide/en/elasticsearch/client/net-api/current" - es-php-client: "https://www.elastic.co/guide/en/elasticsearch/client/php-api/current" - es-python-client: "https://www.elastic.co/guide/en/elasticsearch/client/python-api/current" - defguide: "https://www.elastic.co/guide/en/elasticsearch/guide/2.x" - painless: "https://www.elastic.co/guide/en/elasticsearch/painless/current" - plugins: "https://www.elastic.co/guide/en/elasticsearch/plugins/current" - plugins-8x: "https://www.elastic.co/guide/en/elasticsearch/plugins/8.1" - plugins-7x: "https://www.elastic.co/guide/en/elasticsearch/plugins/7.17" - plugins-6x: "https://www.elastic.co/guide/en/elasticsearch/plugins/6.8" - glossary: "https://www.elastic.co/guide/en/elastic-stack-glossary/current" - upgrade_guide: "https://www.elastic.co/products/upgrade_guide" - blog-ref: "https://www.elastic.co/blog/" - curator-ref: "https://www.elastic.co/guide/en/elasticsearch/client/curator/current" - curator-ref-current: "https://www.elastic.co/guide/en/elasticsearch/client/curator/current" - metrics-ref: "https://www.elastic.co/guide/en/metrics/current" - metrics-guide: "https://www.elastic.co/guide/en/metrics/guide/current" - logs-ref: "https://www.elastic.co/guide/en/logs/current" - logs-guide: "https://www.elastic.co/guide/en/logs/guide/current" - uptime-guide: "https://www.elastic.co/guide/en/uptime/current" - observability-guide: "https://www.elastic.co/guide/en/observability/current" - observability-guide-all: "https://www.elastic.co/guide/en/observability" - siem-guide: "https://www.elastic.co/guide/en/siem/guide/current" - security-guide: "https://www.elastic.co/guide/en/security/current" - security-guide-all: "https://www.elastic.co/guide/en/security" - endpoint-guide: "https://www.elastic.co/guide/en/endpoint/current" - sql-odbc: "https://www.elastic.co/guide/en/elasticsearch/sql-odbc/current" - ecs-ref: "https://www.elastic.co/guide/en/ecs/current" - ecs-logging-ref: "https://www.elastic.co/guide/en/ecs-logging/overview/current" - ecs-logging-go-logrus-ref: "https://www.elastic.co/guide/en/ecs-logging/go-logrus/current" - ecs-logging-go-zap-ref: "https://www.elastic.co/guide/en/ecs-logging/go-zap/current" - ecs-logging-go-zerolog-ref: "https://www.elastic.co/guide/en/ecs-logging/go-zap/current" - ecs-logging-java-ref: "https://www.elastic.co/guide/en/ecs-logging/java/current" - ecs-logging-dotnet-ref: "https://www.elastic.co/guide/en/ecs-logging/dotnet/current" - ecs-logging-nodejs-ref: "https://www.elastic.co/guide/en/ecs-logging/nodejs/current" - ecs-logging-php-ref: "https://www.elastic.co/guide/en/ecs-logging/php/current" - ecs-logging-python-ref: "https://www.elastic.co/guide/en/ecs-logging/python/current" - ecs-logging-ruby-ref: "https://www.elastic.co/guide/en/ecs-logging/ruby/current" - ml-docs: "https://www.elastic.co/guide/en/machine-learning/current" - eland-docs: "https://www.elastic.co/guide/en/elasticsearch/client/eland/current" - eql-ref: "https://eql.readthedocs.io/en/latest/query-guide" - extendtrial: "https://www.elastic.co/trialextension" - wikipedia: "https://en.wikipedia.org/wiki" - forum: "https://discuss.elastic.co/" - xpack-forum: "https://discuss.elastic.co/c/50-x-pack" - security-forum: "https://discuss.elastic.co/c/x-pack/shield" - watcher-forum: "https://discuss.elastic.co/c/x-pack/watcher" - monitoring-forum: "https://discuss.elastic.co/c/x-pack/marvel" - graph-forum: "https://discuss.elastic.co/c/x-pack/graph" - apm-forum: "https://discuss.elastic.co/c/apm" - enterprise-search-ref: "https://www.elastic.co/guide/en/enterprise-search/current" - app-search-ref: "https://www.elastic.co/guide/en/app-search/current" - workplace-search-ref: "https://www.elastic.co/guide/en/workplace-search/current" - enterprise-search-node-ref: "https://www.elastic.co/guide/en/enterprise-search-clients/enterprise-search-node/current" - enterprise-search-php-ref: "https://www.elastic.co/guide/en/enterprise-search-clients/php/current" - enterprise-search-python-ref: "https://www.elastic.co/guide/en/enterprise-search-clients/python/current" - enterprise-search-ruby-ref: "https://www.elastic.co/guide/en/enterprise-search-clients/ruby/current" - elastic-maps-service: "https://maps.elastic.co" - integrations-docs: "https://docs.elastic.co/en/integrations" - integrations-devguide: "https://www.elastic.co/guide/en/integrations-developer/current" - time-units: "https://www.elastic.co/guide/en/elasticsearch/reference/current/api-conventions.html#time-units" - byte-units: "https://www.elastic.co/guide/en/elasticsearch/reference/current/api-conventions.html#byte-units" - apm-py-ref-v: "https://www.elastic.co/guide/en/apm/agent/python/current" - apm-node-ref-v: "https://www.elastic.co/guide/en/apm/agent/nodejs/current" - apm-rum-ref-v: "https://www.elastic.co/guide/en/apm/agent/rum-js/current" - apm-ruby-ref-v: "https://www.elastic.co/guide/en/apm/agent/ruby/current" - apm-java-ref-v: "https://www.elastic.co/guide/en/apm/agent/java/current" - apm-go-ref-v: "https://www.elastic.co/guide/en/apm/agent/go/current" - apm-ios-ref-v: "https://www.elastic.co/guide/en/apm/agent/swift/current" - apm-dotnet-ref-v: "https://www.elastic.co/guide/en/apm/agent/dotnet/current" - apm-php-ref-v: "https://www.elastic.co/guide/en/apm/agent/php/current" - ecloud: "Elastic Cloud" - esf: "Elastic Serverless Forwarder" - ess: "Elasticsearch Service" - ece: "Elastic Cloud Enterprise" - eck: "Elastic Cloud on Kubernetes" - serverless-full: "Elastic Cloud Serverless" - serverless-short: "Serverless" - es-serverless: "Elasticsearch Serverless" - es3: "Elasticsearch Serverless" - obs-serverless: "Elastic Observability Serverless" - sec-serverless: "Elastic Security Serverless" - serverless-docs: "https://docs.elastic.co/serverless" - cloud: "https://www.elastic.co/guide/en/cloud/current" - ess-utm-params: "?page=docs&placement=docs-body" - ess-baymax: "?page=docs&placement=docs-body" - ess-trial: "https://cloud.elastic.co/registration?page=docs&placement=docs-body" - ess-product: "https://www.elastic.co/cloud/elasticsearch-service?page=docs&placement=docs-body" - ess-console: "https://cloud.elastic.co?page=docs&placement=docs-body" - ess-console-name: "Elasticsearch Service Console" - ess-deployments: "https://cloud.elastic.co/deployments?page=docs&placement=docs-body" - ece-ref: "https://www.elastic.co/guide/en/cloud-enterprise/current" - eck-ref: "https://www.elastic.co/guide/en/cloud-on-k8s/current" - ess-leadin: "You can run Elasticsearch on your own hardware or use our hosted Elasticsearch Service that is available on AWS, GCP, and Azure. https://cloud.elastic.co/registration{ess-utm-params}[Try the Elasticsearch Service for free]." - ess-leadin-short: "Our hosted Elasticsearch Service is available on AWS, GCP, and Azure, and you can https://cloud.elastic.co/registration{ess-utm-params}[try it for free]." - ess-icon: "image:https://doc-icons.s3.us-east-2.amazonaws.com/logo_cloud.svg[link=\"https://cloud.elastic.co/registration{ess-utm-params}\", title=\"Supported on Elasticsearch Service\"]" - ece-icon: "image:https://doc-icons.s3.us-east-2.amazonaws.com/logo_cloud_ece.svg[link=\"https://cloud.elastic.co/registration{ess-utm-params}\", title=\"Supported on Elastic Cloud Enterprise\"]" - cloud-only: "This feature is designed for indirect use by https://cloud.elastic.co/registration{ess-utm-params}[Elasticsearch Service], https://www.elastic.co/guide/en/cloud-enterprise/{ece-version-link}[Elastic Cloud Enterprise], and https://www.elastic.co/guide/en/cloud-on-k8s/current[Elastic Cloud on Kubernetes]. Direct use is not supported." - ess-setting-change: "image:https://doc-icons.s3.us-east-2.amazonaws.com/logo_cloud.svg[link=\"{ess-trial}\", title=\"Supported on {ess}\"] indicates a change to a supported https://www.elastic.co/guide/en/cloud/current/ec-add-user-settings.html[user setting] for Elasticsearch Service." - ess-skip-section: "If you use Elasticsearch Service, skip this section. Elasticsearch Service handles these changes for you." - api-cloud: "https://www.elastic.co/docs/api/doc/cloud" - api-ece: "https://www.elastic.co/docs/api/doc/cloud-enterprise" - api-kibana-serverless: "https://www.elastic.co/docs/api/doc/serverless" - es-feature-flag: "This feature is in development and not yet available for use. This documentation is provided for informational purposes only." - es-ref-dir: "'{{elasticsearch-root}}/docs/reference'" - apm-app: "APM app" - uptime-app: "Uptime app" - synthetics-app: "Synthetics app" - logs-app: "Logs app" - metrics-app: "Metrics app" - infrastructure-app: "Infrastructure app" - siem-app: "SIEM app" - security-app: "Elastic Security app" - ml-app: "Machine Learning" - dev-tools-app: "Dev Tools" - ingest-manager-app: "Ingest Manager" - stack-manage-app: "Stack Management" - stack-monitor-app: "Stack Monitoring" - alerts-ui: "Alerts and Actions" - rules-ui: "Rules" - rac-ui: "Rules and Connectors" - connectors-ui: "Connectors" - connectors-feature: "Actions and Connectors" - stack-rules-feature: "Stack Rules" - user-experience: "User Experience" - ems: "Elastic Maps Service" - ems-init: "EMS" - hosted-ems: "Elastic Maps Server" - ipm-app: "Index Pattern Management" - ingest-pipelines: "ingest pipelines" - ingest-pipelines-app: "Ingest Pipelines" - ingest-pipelines-cap: "Ingest pipelines" - ls-pipelines: "Logstash pipelines" - ls-pipelines-app: "Logstash Pipelines" - maint-windows: "maintenance windows" - maint-windows-app: "Maintenance Windows" - maint-windows-cap: "Maintenance windows" - custom-roles-app: "Custom Roles" - data-source: "data view" - data-sources: "data views" - data-source-caps: "Data View" - data-sources-caps: "Data Views" - data-source-cap: "Data view" - data-sources-cap: "Data views" - project-settings: "Project settings" - manage-app: "Management" - index-manage-app: "Index Management" - data-views-app: "Data Views" - rules-app: "Rules" - saved-objects-app: "Saved Objects" - tags-app: "Tags" - api-keys-app: "API keys" - transforms-app: "Transforms" - connectors-app: "Connectors" - files-app: "Files" - reports-app: "Reports" - maps-app: "Maps" - alerts-app: "Alerts" - crawler: "Enterprise Search web crawler" - ents: "Enterprise Search" - app-search-crawler: "App Search web crawler" - agent: "Elastic Agent" - agents: "Elastic Agents" - fleet: "Fleet" - fleet-server: "Fleet Server" - integrations-server: "Integrations Server" - ingest-manager: "Ingest Manager" - ingest-management: "ingest management" - package-manager: "Elastic Package Manager" - integrations: "Integrations" - package-registry: "Elastic Package Registry" - artifact-registry: "Elastic Artifact Registry" - aws: "AWS" - stack: "Elastic Stack" - xpack: "X-Pack" es: "Elasticsearch" - kib: "Kibana" - esms: "Elastic Stack Monitoring Service" - esms-init: "ESMS" - ls: "Logstash" - beats: "Beats" - auditbeat: "Auditbeat" - filebeat: "Filebeat" - heartbeat: "Heartbeat" - metricbeat: "Metricbeat" - packetbeat: "Packetbeat" - winlogbeat: "Winlogbeat" - functionbeat: "Functionbeat" - journalbeat: "Journalbeat" - es-sql: "Elasticsearch SQL" - esql: "ES|QL" - elastic-agent: "Elastic Agent" - k8s: "Kubernetes" - log-driver-long: "Elastic Logging Plugin for Docker" - security: "X-Pack security" - security-features: "security features" - operator-feature: "operator privileges feature" - es-security-features: "Elasticsearch security features" - stack-security-features: "Elastic Stack security features" - endpoint-sec: "Endpoint Security" - endpoint-cloud-sec: "Endpoint and Cloud Security" - elastic-defend: "Elastic Defend" - elastic-sec: "Elastic Security" - elastic-endpoint: "Elastic Endpoint" - swimlane: "Swimlane" - sn: "ServiceNow" - sn-itsm: "ServiceNow ITSM" - sn-itom: "ServiceNow ITOM" - sn-sir: "ServiceNow SecOps" - jira: "Jira" - ibm-r: "IBM Resilient" - webhook: "Webhook" - webhook-cm: "Webhook - Case Management" - opsgenie: "Opsgenie" - bedrock: "Amazon Bedrock" - gemini: "Google Gemini" - hive: "TheHive" - monitoring: "X-Pack monitoring" - monitor-features: "monitoring features" - stack-monitor-features: "Elastic Stack monitoring features" - watcher: "Watcher" - alert-features: "alerting features" - reporting: "X-Pack reporting" - report-features: "reporting features" - graph: "X-Pack graph" - graph-features: "graph analytics features" - searchprofiler: "Search Profiler" - xpackml: "X-Pack machine learning" - ml: "machine learning" - ml-cap: "Machine learning" - ml-init: "ML" - ml-features: "machine learning features" - stack-ml-features: "Elastic Stack machine learning features" - ccr: "cross-cluster replication" - ccr-cap: "Cross-cluster replication" - ccr-init: "CCR" - ccs: "cross-cluster search" - ccs-cap: "Cross-cluster search" - ccs-init: "CCS" - ilm: "index lifecycle management" - ilm-cap: "Index lifecycle management" - ilm-init: "ILM" - dlm: "data lifecycle management" - dlm-cap: "Data lifecycle management" - dlm-init: "DLM" - search-snap: "searchable snapshot" - search-snaps: "searchable snapshots" - search-snaps-cap: "Searchable snapshots" - slm: "snapshot lifecycle management" - slm-cap: "Snapshot lifecycle management" - slm-init: "SLM" - rollup-features: "data rollup features" - ipm: "index pattern management" - ipm-cap: "Index pattern" - rollup: "rollup" - rollup-cap: "Rollup" - rollups: "rollups" - rollups-cap: "Rollups" - rollup-job: "rollup job" - rollup-jobs: "rollup jobs" - rollup-jobs-cap: "Rollup jobs" - dfeed: "datafeed" - dfeeds: "datafeeds" - dfeed-cap: "Datafeed" - dfeeds-cap: "Datafeeds" - ml-jobs: "machine learning jobs" - ml-jobs-cap: "Machine learning jobs" - anomaly-detect: "anomaly detection" - anomaly-detect-cap: "Anomaly detection" - anomaly-job: "anomaly detection job" - anomaly-jobs: "anomaly detection jobs" - anomaly-jobs-cap: "Anomaly detection jobs" - dataframe: "data frame" - dataframes: "data frames" - dataframe-cap: "Data frame" - dataframes-cap: "Data frames" - watcher-transform: "payload transform" - watcher-transforms: "payload transforms" - watcher-transform-cap: "Payload transform" - watcher-transforms-cap: "Payload transforms" - transform: "transform" - transforms: "transforms" - transform-cap: "Transform" - transforms-cap: "Transforms" - dataframe-transform: "transform" - dataframe-transform-cap: "Transform" - dataframe-transforms: "transforms" - dataframe-transforms-cap: "Transforms" - dfanalytics-cap: "Data frame analytics" - dfanalytics: "data frame analytics" - dataframe-analytics-config: "'{dataframe} analytics config'" - dfanalytics-job: "'{dataframe} analytics job'" - dfanalytics-jobs: "'{dataframe} analytics jobs'" - dfanalytics-jobs-cap: "'{dataframe-cap} analytics jobs'" - cdataframe: "continuous data frame" - cdataframes: "continuous data frames" - cdataframe-cap: "Continuous data frame" - cdataframes-cap: "Continuous data frames" - cdataframe-transform: "continuous transform" - cdataframe-transforms: "continuous transforms" - cdataframe-transforms-cap: "Continuous transforms" - ctransform: "continuous transform" - ctransform-cap: "Continuous transform" - ctransforms: "continuous transforms" - ctransforms-cap: "Continuous transforms" - oldetection: "outlier detection" - oldetection-cap: "Outlier detection" - olscore: "outlier score" - olscores: "outlier scores" - fiscore: "feature influence score" - evaluatedf-api: "evaluate {dataframe} analytics API" - evaluatedf-api-cap: "Evaluate {dataframe} analytics API" - binarysc: "binary soft classification" - binarysc-cap: "Binary soft classification" - regression: "regression" - regression-cap: "Regression" - reganalysis: "regression analysis" - reganalysis-cap: "Regression analysis" - depvar: "dependent variable" - feature-var: "feature variable" - feature-vars: "feature variables" - feature-vars-cap: "Feature variables" - classification: "classification" - classification-cap: "Classification" - classanalysis: "classification analysis" - classanalysis-cap: "Classification analysis" - infer-cap: "Inference" - infer: "inference" - lang-ident-cap: "Language identification" - lang-ident: "language identification" - data-viz: "Data Visualizer" - file-data-viz: "File Data Visualizer" - feat-imp: "feature importance" - feat-imp-cap: "Feature importance" - nlp: "natural language processing" - nlp-cap: "Natural language processing" - apm-agent: "APM agent" - apm-go-agent: "Elastic APM Go agent" - apm-go-agents: "Elastic APM Go agents" - apm-ios-agent: "Elastic APM iOS agent" - apm-ios-agents: "Elastic APM iOS agents" - apm-java-agent: "Elastic APM Java agent" - apm-java-agents: "Elastic APM Java agents" - apm-dotnet-agent: "Elastic APM .NET agent" - apm-dotnet-agents: "Elastic APM .NET agents" - apm-node-agent: "Elastic APM Node.js agent" - apm-node-agents: "Elastic APM Node.js agents" - apm-php-agent: "Elastic APM PHP agent" - apm-php-agents: "Elastic APM PHP agents" - apm-py-agent: "Elastic APM Python agent" - apm-py-agents: "Elastic APM Python agents" - apm-ruby-agent: "Elastic APM Ruby agent" - apm-ruby-agents: "Elastic APM Ruby agents" - apm-rum-agent: "Elastic APM Real User Monitoring (RUM) JavaScript agent" - apm-rum-agents: "Elastic APM RUM JavaScript agents" - apm-lambda-ext: "Elastic APM AWS Lambda extension" - project-monitors: "project monitors" - project-monitors-cap: "Project monitors" - private-location: "Private Location" - private-locations: "Private Locations" - pwd: "YOUR_PASSWORD" - esh: "ES-Hadoop" - default-dist: "default distribution" - oss-dist: "OSS-only distribution" - observability: "Observability" - api-request-title: "Request" - api-prereq-title: "Prerequisites" - api-description-title: "Description" - api-path-parms-title: "Path parameters" - api-query-parms-title: "Query parameters" - api-request-body-title: "Request body" - api-response-codes-title: "Response codes" - api-response-body-title: "Response body" - api-example-title: "Example" - api-examples-title: "Examples" - api-definitions-title: "Properties" - multi-arg: "†footnoteref:[multi-arg,This parameter accepts multiple arguments.]" - multi-arg-ref: "†footnoteref:[multi-arg]" - yes-icon: "image:https://doc-icons.s3.us-east-2.amazonaws.com/icon-yes.png[Yes,20,15]" - no-icon: "image:https://doc-icons.s3.us-east-2.amazonaws.com/icon-no.png[No,20,15]" - es-repo: "https://github.com/elastic/elasticsearch/" - es-issue: "https://github.com/elastic/elasticsearch/issues/" - es-pull: "https://github.com/elastic/elasticsearch/pull/" - es-commit: "https://github.com/elastic/elasticsearch/commit/" - kib-repo: "https://github.com/elastic/kibana/" - kib-issue: "https://github.com/elastic/kibana/issues/" - kibana-issue: "'{kib-repo}issues/'" - kib-pull: "https://github.com/elastic/kibana/pull/" - kibana-pull: "'{kib-repo}pull/'" - kib-commit: "https://github.com/elastic/kibana/commit/" - ml-repo: "https://github.com/elastic/ml-cpp/" - ml-issue: "https://github.com/elastic/ml-cpp/issues/" - ml-pull: "https://github.com/elastic/ml-cpp/pull/" - ml-commit: "https://github.com/elastic/ml-cpp/commit/" - apm-repo: "https://github.com/elastic/apm-server/" - apm-issue: "https://github.com/elastic/apm-server/issues/" - apm-pull: "https://github.com/elastic/apm-server/pull/" - kibana-blob: "https://github.com/elastic/kibana/blob/current/" - apm-get-started-ref: "https://www.elastic.co/guide/en/apm/get-started/current" - apm-server-ref: "https://www.elastic.co/guide/en/apm/server/current" - apm-server-ref-v: "https://www.elastic.co/guide/en/apm/server/current" - apm-server-ref-m: "https://www.elastic.co/guide/en/apm/server/master" - apm-server-ref-62: "https://www.elastic.co/guide/en/apm/server/6.2" - apm-server-ref-64: "https://www.elastic.co/guide/en/apm/server/6.4" - apm-server-ref-70: "https://www.elastic.co/guide/en/apm/server/7.0" - apm-overview-ref-v: "https://www.elastic.co/guide/en/apm/get-started/current" - apm-overview-ref-70: "https://www.elastic.co/guide/en/apm/get-started/7.0" - apm-overview-ref-m: "https://www.elastic.co/guide/en/apm/get-started/master" - infra-guide: "https://www.elastic.co/guide/en/infrastructure/guide/current" - a-data-source: "a data view" - icon-bug: "pass:[]" - icon-checkInCircleFilled: "pass:[]" - icon-warningFilled: "pass:[]" + version: "9.0.0" + # Not used by docs-builder, but present in `% :::{include-bloc}` preprocessing directives (see IncludeExpander.java) + doc-tests-src: "../../java-client/src/test/java/co/elastic/clients/documentation" diff --git a/docs/local/README.md b/docs/local/README.md deleted file mode 100644 index a68e64521..000000000 --- a/docs/local/README.md +++ /dev/null @@ -1 +0,0 @@ -This directory contains stubs that allow building the Java client docs in isolation following the instructions in "[Building documentation for a local repo](https://github.com/elastic/docs#building-documentation)". diff --git a/docs/reference/_snippets/doc-tests-blurb.md b/docs/reference/_snippets/doc-tests-blurb.md new file mode 100644 index 000000000..4bbd549e2 --- /dev/null +++ b/docs/reference/_snippets/doc-tests-blurb.md @@ -0,0 +1 @@ +The source code for the examples above can be found in the [Java API Client tests](https://github.com/elastic/elasticsearch-java/tree/main/java-client/src/test/java/co/elastic/clients/documentation). diff --git a/docs/reference/_snippets/legacy-rest-client.md b/docs/reference/_snippets/legacy-rest-client.md new file mode 100644 index 000000000..8f7c3d328 --- /dev/null +++ b/docs/reference/_snippets/legacy-rest-client.md @@ -0,0 +1,3 @@ +:::{note} +This is the legacy RestClient. Please migrate to [Rest5Client](/reference/transport/rest5-client/index.md), a drop-in replacement with an up-to-date http library. +::: diff --git a/docs/reference/api-conventions.md b/docs/reference/api-conventions.md deleted file mode 100644 index 629a150cc..000000000 --- a/docs/reference/api-conventions.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -mapped_pages: - - https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/api-conventions.html ---- - -# API conventions [api-conventions] - -The Java API Client uses a very consistent code structure, using modern code patterns that make complex requests easier to write and complex responses easier to process. The sections below explain these in details. - -* [Package structure and namespace clients](/reference/package-structure.md) -* [Method naming conventions](/reference/method-naming.md) -* [Blocking and asynchronous clients](/reference/blocking-async.md) -* [Building API objects](/reference/building-objects.md) -* [Lists and maps](/reference/lists-maps.md) -* [Variant types](/reference/variant-types.md) -* [Object life cycles and thread safety](/reference/object-lifecycles.md) -* [Creating API objects from JSON data](/reference/loading-json.md) -* [Exceptions](/reference/exception-conventions.md) - - - - - - - - - - diff --git a/docs/reference/blocking-async.md b/docs/reference/api-conventions/blocking-async.md similarity index 74% rename from docs/reference/blocking-async.md rename to docs/reference/api-conventions/blocking-async.md index d7456d3b3..41b6044eb 100644 --- a/docs/reference/blocking-async.md +++ b/docs/reference/api-conventions/blocking-async.md @@ -9,13 +9,12 @@ API clients come in two flavors: blocking and asynchronous. All methods on async Both flavors can be used at the same time depending on your needs, sharing the same transport object: +% :::{include-code} src={{doc-tests-src}}/api_conventions/ApiConventionsTest.java tag=blocking-and-async ```java -ElasticsearchTransport transport = ... - // Synchronous blocking client -ElasticsearchClient client = new ElasticsearchClient(transport); +ElasticsearchClient esClient = new ElasticsearchClient(transport); -if (client.exists(b -> b.index("products").id("foo")).value()) { +if (esClient.exists(b -> b.index("products").id("foo")).value()) { logger.info("product exists"); } @@ -36,5 +35,6 @@ asyncClient Although we won’t go in deeper details on asynchronous programming in Java, remember to handle failures of asynchronous tasks. It’s easy to overlook them and have errors go unnoticed. -The source code for the examples above can be found in the [Java API Client tests](https://github.com/elastic/elasticsearch-java/tree/master/java-client/src/test/java/co/elastic/clients/documentation). +:::{include} /reference/_snippets/doc-tests-blurb.md +::: diff --git a/docs/reference/building-objects.md b/docs/reference/api-conventions/building-objects.md similarity index 78% rename from docs/reference/building-objects.md rename to docs/reference/api-conventions/building-objects.md index 2c8f4d713..684c518c3 100644 --- a/docs/reference/building-objects.md +++ b/docs/reference/api-conventions/building-objects.md @@ -10,9 +10,10 @@ mapped_pages: All data types in the Java API Client are immutable. Object creation uses the [builder pattern](https://www.informit.com/articles/article.aspx?p=1216151&seqNum=2) that was popularized in **Effective Java** in 2008. +% :::{include-code} src={{doc-tests-src}}/api_conventions/ApiConventionsTest.java tag=builders ```java -ElasticsearchClient client = ... -CreateIndexResponse createResponse = client.indices().create( +ElasticsearchClient esClient = createClient(); +CreateIndexResponse createResponse = esClient.indices().create( new CreateIndexRequest.Builder() .index("my-index") .aliases("foo", @@ -29,9 +30,10 @@ Note that a builder should not be reused after its `build()` method has been cal Although this works nicely, having to instantiate builder classes and call the `build()` method is a bit verbose. So every property setter in the Java API Client also accepts a lambda expression that takes a newly created builder as a parameter and returns a populated builder. The snippet above can also be written as: +% :::{include-code} src={{doc-tests-src}}/api_conventions/ApiConventionsTest.java tag=builder-lambdas ```java -ElasticsearchClient client = ... -CreateIndexResponse createResponse = client.indices() +ElasticsearchClient esClient = createClient(); +CreateIndexResponse createResponse = esClient.indices() .create(createIndexBuilder -> createIndexBuilder .index("my-index") .aliases("foo", aliasBuilder -> aliasBuilder @@ -44,9 +46,10 @@ This approach allows for much more concise code, and also avoids importing class Note in the above example that builder variables are only used to start a chain of property setters. The names of these variables are therefore unimportant and can be shortened to improve readability: +% :::{include-code} src={{doc-tests-src}}/api_conventions/ApiConventionsTest.java tag=builder-lambdas-short ```java -ElasticsearchClient client = ... -CreateIndexResponse createResponse = client.indices() +ElasticsearchClient esClient = createClient(); +CreateIndexResponse createResponse = esClient.indices() .create(c -> c .index("my-index") .aliases("foo", a -> a @@ -55,13 +58,14 @@ CreateIndexResponse createResponse = client.indices() ); ``` -Builder lambdas become particularly useful with complex nested queries like the one below, taken from the [intervals query API documentation](elasticsearch://docs/reference/query-languages/query-dsl-intervals-query.md). +Builder lambdas become particularly useful with complex nested queries like the one below, taken from the [intervals query API documentation](elasticsearch://reference/query-languages/query-dsl/query-dsl-intervals-query.md). This example also highlights a useful naming convention for builder parameters in deeply nested structures. For lambda expressions with a single argument, Kotlin provides the implicit `it` parameter and Scala allows use of `_`. This can be approximated in Java by using an underscore or a single letter prefix followed by a number representing the depth level (i.e. `_0`, `_1`, or `b0`, `b1` and so on). Not only does this remove the need to create throw-away variable names, but it also improves code readability. Correct indentation also allows the structure of the query to stand out. +% :::{include-code} src={{doc-tests-src}}/api_conventions/ApiConventionsTest.java tag=builder-intervals ```java -ElasticsearchClient client = ... -SearchResponse results = client +ElasticsearchClient esClient = createClient(); +SearchResponse results = esClient .search(b0 -> b0 .query(b1 -> b1 .intervals(b2 -> b2 @@ -92,12 +96,13 @@ SearchResponse results = client ) ) ), - SomeApplicationData.class <1> + SomeApplicationData.class // <1> ); ``` 1. Search results will be mapped to `SomeApplicationData` instances to be readily available to the application. -The source code for the examples above can be found in the [Java API Client tests](https://github.com/elastic/elasticsearch-java/tree/master/java-client/src/test/java/co/elastic/clients/documentation). +:::{include} /reference/_snippets/doc-tests-blurb.md +::: diff --git a/docs/reference/exception-conventions.md b/docs/reference/api-conventions/exception-conventions.md similarity index 100% rename from docs/reference/exception-conventions.md rename to docs/reference/api-conventions/exception-conventions.md diff --git a/docs/reference/api-conventions/index.md b/docs/reference/api-conventions/index.md new file mode 100644 index 000000000..e36e5978c --- /dev/null +++ b/docs/reference/api-conventions/index.md @@ -0,0 +1,28 @@ +--- +mapped_pages: + - https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/api-conventions.html +--- + +# API conventions [api-conventions] + +The Java API Client uses a very consistent code structure, using modern code patterns that make complex requests easier to write and complex responses easier to process. The sections below explain these in details. + +* [Package structure and namespace clients](package-structure.md) +* [Method naming conventions](method-naming.md) +* [Blocking and asynchronous clients](blocking-async.md) +* [Building API objects](building-objects.md) +* [Lists and maps](lists-maps.md) +* [Variant types](variant-types.md) +* [Object life cycles and thread safety](object-lifecycles.md) +* [Creating API objects from JSON data](loading-json.md) +* [Exceptions](exception-conventions.md) + + + + + + + + + + diff --git a/docs/reference/lists-maps.md b/docs/reference/api-conventions/lists-maps.md similarity index 92% rename from docs/reference/lists-maps.md rename to docs/reference/api-conventions/lists-maps.md index b31308fd3..a5e891841 100644 --- a/docs/reference/lists-maps.md +++ b/docs/reference/api-conventions/lists-maps.md @@ -12,6 +12,7 @@ Properties of type `List` and `Map` are exposed by object builders as a set of o Object builders create immutable objects, and this also applies to list and map properties that are made immutable at object construction time. +% :::{include-code} src={{doc-tests-src}}/api_conventions/ApiConventionsTest.java tag=collections ```java // Prepare a list of index names List names = Arrays.asList("idx-a", "idx-b", "idx-c"); @@ -48,7 +49,6 @@ SearchRequest search = SearchRequest.of(r -> r ); ``` - ## List and map values are never `null` [_list_and_map_values_are_never_null] The {{es}} API has a lot of optional properties. For single-valued properties, the Java API Client represents missing optional values as `null`. Applications therefore have to null-check optional values before using them. @@ -57,6 +57,7 @@ For lists and maps however, applications often only care about whether they’re If you ever need to distinguish between a missing (undefined) optional collection and an effectively-empty collection returned by {{es}}, the `ApiTypeHelper` class provides a utility method to distinguish them: +% :::{include-code} src={{doc-tests-src}}/api_conventions/ApiConventionsTest.java tag=optional-collections ```java NodeStatistics stats = NodeStatistics.of(b -> b .total(1) @@ -73,5 +74,6 @@ assertEquals(0, stats.failures().size()); assertFalse(ApiTypeHelper.isDefined(stats.failures())); ``` -The source code for the examples above can be found in the [Java API Client tests](https://github.com/elastic/elasticsearch-java/tree/master/java-client/src/test/java/co/elastic/clients/documentation). +:::{include} /reference/_snippets/doc-tests-blurb.md +::: diff --git a/docs/reference/loading-json.md b/docs/reference/api-conventions/loading-json.md similarity index 83% rename from docs/reference/loading-json.md rename to docs/reference/api-conventions/loading-json.md index 4a4227528..7cda36d71 100644 --- a/docs/reference/loading-json.md +++ b/docs/reference/api-conventions/loading-json.md @@ -31,16 +31,17 @@ Consider a resource file `some-index.json` containing an index definition: You can create an index from that definition as follows: +% :::{include-code} src={{doc-tests-src}}/api_conventions/LoadingJsonTest.java tag=load-index ```java InputStream input = this.getClass() - .getResourceAsStream("some-index.json"); <1> + .getResourceAsStream("some-index.json"); // <1> CreateIndexRequest req = CreateIndexRequest.of(b -> b .index("some-index") - .withJson(input) <2> + .withJson(input) // <2> ); -boolean created = client.indices().create(req).acknowledged(); +boolean created = esClient.indices().create(req).acknowledged(); ``` 1. open an input stream for the JSON resource file. @@ -52,17 +53,18 @@ boolean created = client.indices().create(req).acknowledged(); Similarly, you can read documents to be stored in {{es}} from data files: +% :::{include-code} src={{doc-tests-src}}/api_conventions/LoadingJsonTest.java tag=ingest-data ```java FileReader file = new FileReader(new File(dataDir, "document-1.json")); -IndexRequest req; <1> +IndexRequest req; // <1> req = IndexRequest.of(b -> b .index("some-index") .withJson(file) ); -client.index(req); +esClient.index(req); ``` 1. when calling `withJson()` on data structures that have generic type parameters, these generic types will be considered to be `JsonData`. @@ -73,6 +75,7 @@ client.index(req); You can combine `withJson()` with regular calls to setter methods. The example below loads the query part of a search request from a `String` and programmatically adds an aggregation. +% :::{include-code} src={{doc-tests-src}}/api_conventions/LoadingJsonTest.java tag=query ```java Reader queryJson = new StringReader( "{" + @@ -86,8 +89,8 @@ Reader queryJson = new StringReader( "}"); SearchRequest aggRequest = SearchRequest.of(b -> b - .withJson(queryJson) <1> - .aggregations("max-cpu", a1 -> a1 <2> + .withJson(queryJson) // <1> + .aggregations("max-cpu", a1 -> a1 // <2> .dateHistogram(h -> h .field("@timestamp") .calendarInterval(CalendarInterval.Hour) @@ -99,8 +102,8 @@ SearchRequest aggRequest = SearchRequest.of(b -> b .size(0) ); -Map aggs = client - .search(aggRequest, Void.class) <3> +Map aggs = esClient + .search(aggRequest, Void.class) // <3> .aggregations(); ``` @@ -114,6 +117,7 @@ Map aggs = client The `withJson()` methods are partial deserializers: the properties loaded from the JSON will set property values or replace the previous ones, but will not reset other properties not found in the JSON input. You can use this to combine multiple JSON snippets to build complex search requests. In the example below, we combine separate definitions of a query that selects some documents and an aggregation that is run on the results of this query. +% :::{include-code} src={{doc-tests-src}}/api_conventions/LoadingJsonTest.java tag=query-and-agg ```java Reader queryJson = new StringReader( "{" + @@ -124,12 +128,12 @@ Reader queryJson = new StringReader( " }" + " }" + " }," + - " \"size\": 100" + <1> + " \"size\": 100" + // <1> "}"); Reader aggregationJson = new StringReader( "{" + - " \"size\": 0, " + <2> + " \"size\": 0, " + // <2> " \"aggregations\": {" + " \"hours\": {" + " \"date_histogram\": {" + @@ -148,12 +152,12 @@ Reader aggregationJson = new StringReader( "}"); SearchRequest aggRequest = SearchRequest.of(b -> b - .withJson(queryJson) <3> - .withJson(aggregationJson) <4> - .ignoreUnavailable(true) <5> + .withJson(queryJson) // <3> + .withJson(aggregationJson) // <4> + .ignoreUnavailable(true) // <5> ); -Map aggs = client +Map aggs = esClient .search(aggRequest, Void.class) .aggregations(); ``` @@ -167,5 +171,6 @@ Map aggs = client Notice that order matters when the JSON snippets have some common properties: just as when setting property values programmatically, the last value that is set for a property overwrites the previous one. -The source code for the examples above can be found in the [Java API Client tests](https://github.com/elastic/elasticsearch-java/tree/master/java-client/src/test/java/co/elastic/clients/documentation). +:::{include} /reference/_snippets/doc-tests-blurb.md +::: diff --git a/docs/reference/method-naming.md b/docs/reference/api-conventions/method-naming.md similarity index 100% rename from docs/reference/method-naming.md rename to docs/reference/api-conventions/method-naming.md diff --git a/docs/reference/object-lifecycles.md b/docs/reference/api-conventions/object-lifecycles.md similarity index 100% rename from docs/reference/object-lifecycles.md rename to docs/reference/api-conventions/object-lifecycles.md diff --git a/docs/reference/package-structure.md b/docs/reference/api-conventions/package-structure.md similarity index 86% rename from docs/reference/package-structure.md rename to docs/reference/api-conventions/package-structure.md index 632415f89..744a2d9e5 100644 --- a/docs/reference/package-structure.md +++ b/docs/reference/api-conventions/package-structure.md @@ -5,13 +5,13 @@ mapped_pages: # Package structure and namespace clients [package-structure] -The {{es}} API is large and is organized into feature groups, as can be seen in the [{{es}} API documentation](elasticsearch://docs/reference/elasticsearch/rest-apis/index.md). +The {{es}} API is large and is organized into feature groups, as can be seen in the [{{es}} API documentation](elasticsearch://reference/elasticsearch/rest-apis/index.md). The Java API Client follows this structure: feature groups are called “namespaces”, and each namespace is located in a subpackage of `co.elastic.clients.elasticsearch`. Each of the namespace clients can be accessed from the top level {{es}} client. The only exceptions are the “search” and “document” APIs which are located in the `core` subpackage and can be accessed on the main {{es}} client object. -The snippet below shows how to use the indices namespace client to create an index (the lambda syntax is explained in [Building API objects](/reference/building-objects.md)): +The snippet below shows how to use the indices namespace client to create an index (the lambda syntax is explained in [Building API objects](building-objects.md)): ```java // Create the "products" index diff --git a/docs/reference/variant-types.md b/docs/reference/api-conventions/variant-types.md similarity index 81% rename from docs/reference/variant-types.md rename to docs/reference/api-conventions/variant-types.md index 9540ac463..170eab438 100644 --- a/docs/reference/variant-types.md +++ b/docs/reference/api-conventions/variant-types.md @@ -13,13 +13,14 @@ This is because variant objects in the Java API Client are implementations of a Variant builders have setter methods for every available implementation. They use the same conventions as regular properties and accept both a builder lambda expression and a ready-made object of the actual type of the variant. Here’s an example to build a term query: +% :::{include-code} src={{doc-tests-src}}/api_conventions/ApiConventionsTest.java tag=variant-creation ```java Query query = new Query.Builder() - .term(t -> t <1> - .field("name") <2> + .term(t -> t // <1> + .field("name") // <2> .value(v -> v.stringValue("foo")) ) - .build(); <3> + .build(); // <3> ``` 1. Choose the `term` variant to build a term query. @@ -29,6 +30,7 @@ Query query = new Query.Builder() Variant objects have getter methods for every available implementation. These methods check that the object actually holds a variant of that kind and return the value downcasted to the correct type. They throw an `IllegalStateException` otherwise. This approach allows writing fluent code to traverse variants. +% :::{include-code} src={{doc-tests-src}}/api_conventions/ApiConventionsTest.java tag=variant-navigation ```java assertEquals("foo", query.term().value().stringValue()); ``` @@ -40,12 +42,13 @@ Variant objects also provide information on the variant kind they currently hold This information can then be used to navigate down into specific variants after checking their actual kind: +% :::{include-code} src={{doc-tests-src}}/api_conventions/ApiConventionsTest.java tag=variant-kind ```java -if (query.isTerm()) { <1> +if (query.isTerm()) { // <1> doSomething(query.term()); } -switch(query._kind()) { <2> +switch(query._kind()) { // <2> case Term: doSomething(query.term()); break; @@ -53,7 +56,7 @@ switch(query._kind()) { <2> doSomething(query.intervals()); break; default: - doSomething(query._kind(), query._get()); <3> + doSomething(query._kind(), query._get()); // <3> } ``` @@ -61,8 +64,6 @@ switch(query._kind()) { <2> 2. Test a larger set of variant kinds. 3. Get the kind and value held by the variant object. - - ## Custom extensions provided by {{es}} plugins [variant-types-custom] {{es}} accepts plugins that can extend the available variants for a number of types. This includes queries, aggregations, text analyzers and tokenizers, ingest processors, etc. @@ -73,8 +74,9 @@ In the examples below we use a hypothetical plugin that adds a `sphere-distance` To create a custom aggregation, use the `_custom()` aggregation type and provide its identifier, defined by the plugin, and parameters. The parameters can be any object or value that can be serialized to JSON. In the example below we use a simple map: +% :::{include-code} src={{doc-tests-src}}/api_conventions/ApiConventionsTest.java tag=custom-variant-creation ```java -Map params = new HashMap<>(); <1> +Map params = new HashMap<>(); // <1> params.put("interval", 10); params.put("scale", "log"); params.put("origin", new Double[]{145.0, 12.5, 1649.0}); @@ -82,7 +84,7 @@ params.put("origin", new Double[]{145.0, 12.5, 1649.0}); SearchRequest request = SearchRequest.of(r -> r .index("stars") .aggregations("neighbors", agg -> agg - ._custom("sphere-distance", params) <2> + ._custom("sphere-distance", params) // <2> ) ); ``` @@ -95,14 +97,15 @@ The results of custom variants are returned as raw JSON represented by a `JsonDa Traversing the JSON tree: +% :::{include-code} src={{doc-tests-src}}/api_conventions/ApiConventionsTest.java tag=custom-variant-navigation-json ```java -SearchResponse response = esClient.search(request, Void.class); <1> +SearchResponse response = esClient.search(request, Void.class); // <1> JsonData neighbors = response .aggregations().get("neighbors") - ._custom(); <2> + ._custom(); // <2> -JsonArray buckets = neighbors.toJson() <3> +JsonArray buckets = neighbors.toJson() // <3> .asJsonObject() .getJsonArray("buckets"); @@ -114,20 +117,21 @@ for (JsonValue item : buckets) { } ``` -1. Use `Void` if you’re only interested in aggregation results, not search hits (see also [Aggregations](/reference/aggregations.md)). +1. Use `Void` if you’re only interested in aggregation results, not search hits (see also [Aggregations](/reference/usage/aggregations.md)). 2. Get the `neighbors` aggregation result as custom JSON result. 3. Traverse the JSON tree to extract the result data. Using a class that represents the custom aggregation results: +% :::{include-code} src={{doc-tests-src}}/api_conventions/ApiConventionsTest.java tag=custom-variant-navigation-typed ```java SearchResponse response = esClient.search(request, Void.class); SphereDistanceAggregate neighbors = response .aggregations().get("neighbors") ._custom() - .to(SphereDistanceAggregate.class); <1> + .to(SphereDistanceAggregate.class); // <1> for (Bucket bucket : neighbors.buckets()) { doSomething(bucket.key(), bucket.docCount()); @@ -139,6 +143,7 @@ for (Bucket bucket : neighbors.buckets()) { Where `SphereDistanceAggregate` can be defined as follows: +% :::{include-code} src={{doc-tests-src}}/api_conventions/ApiConventionsTest.java tag=custom-variant-types ```java public static class SphereDistanceAggregate { private final List buckets; @@ -172,5 +177,5 @@ public static class Bucket { } ``` -The source code for the examples above can be found in the [Java API Client tests](https://github.com/elastic/elasticsearch-java/tree/master/java-client/src/test/java/co/elastic/clients/documentation). - +:::{include} /reference/_snippets/doc-tests-blurb.md +::: diff --git a/docs/reference/getting-started.md b/docs/reference/getting-started.md index cce1876f8..153f22ec4 100644 --- a/docs/reference/getting-started.md +++ b/docs/reference/getting-started.md @@ -3,73 +3,61 @@ mapped_pages: - https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/getting-started-java.html --- -# Getting started [getting-started-java] +# Getting started This page guides you through the installation process of the Java client, shows you how to instantiate the client, and how to perform basic Elasticsearch operations with it. +### Requirements -### Requirements [_requirements] +Java 17 or later. -* Java 8 or later. -* A JSON object mapping library to allow seamless integration of your application classes with the Elasticsearch API. The examples below show usage with Jackson. +### Installation -### Installation [_installation] +#### Installation in a Gradle project - -#### Installation in a Gradle project by using Jackson [_installation_in_a_gradle_project_by_using_jackson] - -```groovy +```groovy subs=true dependencies { - implementation 'co.elastic.clients:elasticsearch-java:9.0.0-beta1' + implementation 'co.elastic.clients:elasticsearch-java:{{version}}' } ``` -#### Installation in a Maven project by using Jackson [_installation_in_a_maven_project_by_using_jackson] +#### Installation in a Maven project In the `pom.xml` of your project, add the following repository definition and dependencies: -```xml +```xml subs=true co.elastic.clients elasticsearch-java - 9.0.0-beta1 + {{version}} ``` -Refer to the [Installation](/reference/installation.md) page to learn more. +Refer to the [Installation](setup/installation.md) page to learn more. -### Connecting [_connecting] +### Connecting You can connect to the Elastic Cloud using an API key and the Elasticsearch endpoint. +% :::{include-code} src={{doc-tests-src}}/getting_started/ConnectingTest.java tag=create-client ```java // URL and API key String serverUrl = "https://localhost:9200"; String apiKey = "VnVhQ2ZHY0JDZGJrU..."; -// Create the low-level client -RestClient restClient = RestClient - .builder(HttpHost.create(serverUrl)) - .setDefaultHeaders(new Header[]{ - new BasicHeader("Authorization", "ApiKey " + apiKey) - }) - .build(); - -// Create the transport with a Jackson mapper -ElasticsearchTransport transport = new RestClientTransport( - restClient, new JacksonJsonpMapper()); - -// And create the API client -ElasticsearchClient esClient = new ElasticsearchClient(transport); +ElasticsearchClient esClient = ElasticsearchClient.of(b -> b + .host(serverUrl) + .apiKey(apiKey) +); // Use the client... @@ -79,28 +67,29 @@ esClient.close(); Your Elasticsearch endpoint can be found on the **My deployment** page of your deployment: -:::{image} ../images/es-endpoint.jpg +:::{image} images/es-endpoint.jpg :alt: Finding Elasticsearch endpoint ::: You can generate an API key on the **Management** page under Security. -:::{image} ../images/create-api-key.png +:::{image} images/create-api-key.png :alt: Create API key ::: -For other connection options, refer to the [Connecting](/reference/connecting.md) section. +For other connection options, refer to the [Connecting](setup/connecting.md) section. ### Operations [_operations] -Time to use Elasticsearch! This section walks you through the basic, and most important, operations of Elasticsearch. For more operations and more advanced examples, refer to the [*Using the Java API Client*](/reference/using-java-api-client.md) page. +Time to use Elasticsearch! This section walks you through the basic, and most important, operations of Elasticsearch. For more operations and more advanced examples, refer to the [*Using the Java API Client*](usage/index.md) page. #### Creating an index [_creating_an_index] This is how you create the `product` index: +% :::{include-code} src={{doc-tests-src}}/usage/IndexingTest.java tag=create-products-index ```java esClient.indices().create(c -> c .index("products") @@ -112,6 +101,7 @@ esClient.indices().create(c -> c This is a simple way of indexing a document, here a `Product` application object: +% :::{include-code} src={{doc-tests-src}}/usage/IndexingTest.java tag=single-doc-dsl ```java Product product = new Product("bk-1", "City bike", 123.0); @@ -124,16 +114,16 @@ IndexResponse response = esClient.index(i -> i logger.info("Indexed with version " + response.version()); ``` - #### Getting documents [_getting_documents] You can get documents by using the following code: +% :::{include-code} src={{doc-tests-src}}/usage/ReadingTest.java tag=get-by-id ```java GetResponse response = esClient.get(g -> g - .index("products") <1> + .index("products") // <1> .id("bk-1"), - Product.class <2> + Product.class // <2> ); if (response.found()) { @@ -147,12 +137,11 @@ if (response.found()) { 1. The get request, with the index name and identifier. 2. The target class, here `Product`. - - #### Searching documents [_searching_documents] This is how you can create a single match query with the Java client: +% :::{include-code} src={{doc-tests-src}}/usage/SearchingTest.java tag=search-getting-started ```java String searchText = "bike"; @@ -168,11 +157,11 @@ SearchResponse response = esClient.search(s -> s ); ``` - #### Updating documents [_updating_documents] This is how you can update a document, for example to add a new field: +% :::{include-code} src={{doc-tests-src}}/usage/IndexingTest.java tag=single-doc-update ```java Product product = new Product("bk-1", "City bike", 123.0); @@ -184,23 +173,22 @@ esClient.update(u -> u ); ``` - #### Deleting documents [_deleting_documents] +% :::{include-code} src={{doc-tests-src}}/usage/IndexingTest.java tag=single-doc-delete ```java esClient.delete(d -> d.index("products").id("bk-1")); ``` - #### Deleting an index [_deleting_an_index] +% :::{include-code} src={{doc-tests-src}}/usage/IndexingTest.java tag=delete-products-index ```java esClient.indices().delete(d -> d .index("products") ); ``` - ## Examples [_examples] The [examples](https://github.com/elastic/elasticsearch-java/tree/main/examples) folder in the Github repository contains full working examples showing how to set up and use the client. @@ -208,4 +196,4 @@ The [examples](https://github.com/elastic/elasticsearch-java/tree/main/examples) ## Further reading [_further_reading] -* Learn more about the [*API conventions*](/reference/api-conventions.md) of the Java client. +* Learn more about the [*API conventions*](api-conventions/index.md) of the Java client. diff --git a/docs/images/create-api-key.png b/docs/reference/images/create-api-key.png similarity index 100% rename from docs/images/create-api-key.png rename to docs/reference/images/create-api-key.png diff --git a/docs/images/es-endpoint.jpg b/docs/reference/images/es-endpoint.jpg similarity index 100% rename from docs/images/es-endpoint.jpg rename to docs/reference/images/es-endpoint.jpg diff --git a/docs/images/otel-waterfall-instrumented-without-http.jpg b/docs/reference/images/otel-waterfall-instrumented-without-http.jpg similarity index 100% rename from docs/images/otel-waterfall-instrumented-without-http.jpg rename to docs/reference/images/otel-waterfall-instrumented-without-http.jpg diff --git a/docs/images/otel-waterfall-instrumented.jpg b/docs/reference/images/otel-waterfall-instrumented.jpg similarity index 100% rename from docs/images/otel-waterfall-instrumented.jpg rename to docs/reference/images/otel-waterfall-instrumented.jpg diff --git a/docs/images/otel-waterfall-retries.jpg b/docs/reference/images/otel-waterfall-retries.jpg similarity index 100% rename from docs/images/otel-waterfall-retries.jpg rename to docs/reference/images/otel-waterfall-retries.jpg diff --git a/docs/reference/index.md b/docs/reference/index.md index 8f9fdc876..099edb0ae 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -15,7 +15,7 @@ This is the documentation for the official Java API Client for {{es}}. The clien * Blocking and asynchronous versions of all APIs. * Use of fluent builders and functional patterns to allow writing concise yet readable code when creating complex nested structures. * Seamless integration of application classes by using an object mapper such as Jackson or any JSON-B implementation. -* Delegates protocol handling to an http client such as the [Java Low Level REST Client](/reference/java-low-level-rest-client.md) that takes care of all transport-level concerns: HTTP connection pooling, retries, node discovery, and so on. +* Delegates protocol handling to an http client such as the [Java Low Level REST Client](transport/rest-client/index.md) that takes care of all transport-level concerns: HTTP connection pooling, retries, node discovery, and so on. ## Elasticsearch server compatibility policy [_elasticsearch_server_compatibility_policy] @@ -23,8 +23,8 @@ This is the documentation for the official Java API Client for {{es}}. The clien The {{es}} Java client is forward compatible; meaning that the client supports communicating with greater or equal minor versions of {{es}} without breaking. It does not mean that the client automatically supports new features of newer {{es}} versions; it is only possible after a release of a new client version. For example, a 8.12 client version won’t automatically support the new features of the 8.13 version of {{es}}, the 8.13 client version is required for that. {{es}} language clients are only backwards compatible with default distributions and without guarantees made. | Elasticsearch Version | Elasticsearch-Java Branch | Supported | -| --- | --- | --- | -| main | main | | -| 8.x | 8.x | 8.x | -| 7.x | 7.x | 7.17 | +|-----------------------|---------------------------|-----------| +| main | main | | +| 9.x | 9.x | 9.x | +| 8.x | 8.x | 8.17 | diff --git a/docs/reference/java-rest-low-config.md b/docs/reference/java-rest-low-config.md deleted file mode 100644 index 52574079d..000000000 --- a/docs/reference/java-rest-low-config.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -mapped_pages: - - https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/java-rest-low-config.html ---- - -# Common configuration [java-rest-low-config] - -As explained in [Initialization](/reference/java-rest-low-usage-initialization.md), the `RestClientBuilder` supports providing both a `RequestConfigCallback` and an `HttpClientConfigCallback` which allow for any customization that the Apache Async Http Client exposes. Those callbacks make it possible to modify some specific behaviour of the client without overriding every other default configuration that the `RestClient` is initialized with. This section describes some common scenarios that require additional configuration for the low-level Java REST Client. - - - - - - - - diff --git a/docs/reference/javadoc-source-code.md b/docs/reference/javadoc-source-code.md index abcdd4183..808267fb0 100644 --- a/docs/reference/javadoc-source-code.md +++ b/docs/reference/javadoc-source-code.md @@ -5,7 +5,7 @@ mapped_pages: # Javadoc and source code [java-client-javadoc] -The javadoc for the Java API Client can be found at [https://snapshots.elastic.co/javadoc/co/elastic/clients/elasticsearch-java/9.0.0-beta1-SNAPSHOT/index.html](https://snapshots.elastic.co/javadoc/co/elastic/clients/elasticsearch-java/9.0.0-beta1-SNAPSHOT/index.md). +The javadoc for the Java API Client can be found at [https://snapshots.elastic.co/javadoc/co/elastic/clients/elasticsearch-java/{{version}}/](https://snapshots.elastic.co/javadoc/co/elastic/clients/elasticsearch-java/{{version}}/). The source code is at [https://github.com/elastic/elasticsearch-java/](https://github.com/elastic/elasticsearch-java/) and is licensed under the Apache 2.0 License. diff --git a/docs/reference/_license.md b/docs/reference/license.md similarity index 100% rename from docs/reference/_license.md rename to docs/reference/license.md diff --git a/docs/reference/migrate-hlrc.md b/docs/reference/migrate-hlrc.md deleted file mode 100644 index 66ab070e7..000000000 --- a/docs/reference/migrate-hlrc.md +++ /dev/null @@ -1,74 +0,0 @@ ---- -mapped_pages: - - https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/migrate-hlrc.html ---- - -# Migrating from the High Level Rest Client [migrate-hlrc] - -The {{es}} Java API Client is an entirely new client library that has no relation to the older High Level Rest Client (HLRC). This was a deliberate choice to provide a library that is independent from the {{es}} server code and that provides a very consistent and easier to use API for all {{es}} features. - -Migrating from the HLRC therefore requires some code rewrite in your application. This transition can however happen progressively as the two client libraries can coexist in a single application with no operational overhead. - - -## Compatibility mode: using a 7.17 client with {{es}} 8.x [_compatibility_mode_using_a_7_17_client_with_es_8_x] - -The HLRC version `7.17` can be used with {{es}} version `8.x` by enabling HLRC’s compatibility mode (see code sample below). In this mode HLRC sends additional headers that instruct {{es}} `8.x` to behave like a `7.x` server. - -The Java API Client doesn’t need this setting as compatibility mode is always enabled. - -You can use the HLRC version `7.x` with the Java API Client version `8.x`: - -```groovy -dependencies { - implementation 'co.elastic.clients:elasticsearch-java:9.0.0-beta1' - implementation 'org.elasticsearch.client:elasticsearch-rest-high-level-client:7.17.4' - // other dependencies <1> -} -``` - -1. See [Installation](/reference/installation.md) - - - -## Using the same http client with the HLRC and the Java API Client [_using_the_same_http_client_with_the_hlrc_and_the_java_api_client] - -To avoid any operational overhead during the transition phase where an application would use both the HLRC and the new Java API Client, both clients can share the same Low Level Rest Client, which is the network layer that manages all connections, round-robin strategies, node sniffing, and so on. - -The code below shows how to initialize both clients with the same HTTP client: - -```java -// Create the low-level client -RestClient httpClient = RestClient.builder( - new HttpHost("localhost", 9200) -).build(); - -// Create the HLRC -RestHighLevelClient hlrc = new RestHighLevelClientBuilder(httpClient) - .setApiCompatibilityMode(true) <1> - .build(); - -// Create the Java API Client with the same low level client -ElasticsearchTransport transport = new RestClientTransport( - httpClient, - new JacksonJsonpMapper() -); - -ElasticsearchClient esClient = new ElasticsearchClient(transport); - -// hlrc and esClient share the same httpClient -``` - -1. Enables compatibility mode that allows HLRC `7.17` to work with {{es}} `8.x`. - - - -## Transition strategies [_transition_strategies] - -There are different ways you can start transitioning away from the HLRC in your application code. - -For example: - -* keep the existing code as-is and use the new Java API Client for new features in your application, then later migrate the existing code, -* rewrite the parts of your application where the new Java API Client is much easier to use than that of the HLRC, like everything related to search, -* rewrite those parts where you need to map application objects to/from JSON, by leveraging the tight integration of the new Java API Client with JSON object mappers. - diff --git a/docs/reference/setup.md b/docs/reference/setup.md deleted file mode 100644 index 55bb77419..000000000 --- a/docs/reference/setup.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -mapped_pages: - - https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/_setup.html ---- - -# Setup [_setup] - -* [Installation](/reference/installation.md) -* [Connecting](/reference/connecting.md) -* [Migrating from the High Level Rest Client](/reference/migrate-hlrc.md) -* [Using OpenTelemetry](/reference/opentelemetry.md) -* [Java Low Level REST Client](/reference/java-low-level-rest-client.md) - - - - - diff --git a/docs/reference/connecting.md b/docs/reference/setup/connecting.md similarity index 68% rename from docs/reference/connecting.md rename to docs/reference/setup/connecting.md index 0974a6bd7..a8c3e5a4f 100644 --- a/docs/reference/connecting.md +++ b/docs/reference/setup/connecting.md @@ -8,30 +8,21 @@ mapped_pages: The Java API Client is structured around three main components: * **API client classes**. These provide strongly typed data structures and methods for {{es}} APIs. Since the {{es}} API is large, it is structured in feature groups (also called “namespaces”), each having its own client class. {{es}} core features are implemented in the `ElasticsearchClient` class. -* **A JSON object mapper**. This maps your application classes to JSON and seamlessly integrates them with the API client. -* **A transport layer implementation**. This is where all HTTP request handling takes place. +* **A JSON object mapper**. This maps your application classes to JSON and seamlessly integrates them with the API client. The default implementation uses Jackson. +* **A transport layer implementation**. This is where all HTTP request handling takes place. The [default implementation](/reference/transport/rest5-client/index.md) is based on the [Apache http client library](https://hc.apache.org/). -This code snippet creates and wires together these three components: +This code snippet uses the default configurations and only needs the location and credentials to connect to Elasticsearch: +% :::{include-code} src={{doc-tests-src}}/getting_started/ConnectingTest.java tag=create-client ```java // URL and API key String serverUrl = "https://localhost:9200"; String apiKey = "VnVhQ2ZHY0JDZGJrU..."; -// Create the low-level client -RestClient restClient = RestClient - .builder(HttpHost.create(serverUrl)) - .setDefaultHeaders(new Header[]{ - new BasicHeader("Authorization", "ApiKey " + apiKey) - }) - .build(); - -// Create the transport with a Jackson mapper -ElasticsearchTransport transport = new RestClientTransport( - restClient, new JacksonJsonpMapper()); - -// And create the API client -ElasticsearchClient esClient = new ElasticsearchClient(transport); +ElasticsearchClient esClient = ElasticsearchClient.of(b -> b + .host(serverUrl) + .apiKey(apiKey) +); // Use the client... @@ -39,15 +30,16 @@ ElasticsearchClient esClient = new ElasticsearchClient(transport); esClient.close(); ``` -Authentication is managed by the [Java Low Level REST Client](/reference/java-low-level-rest-client.md). For further details on configuring authentication, refer to [its documentation](/reference/_basic_authentication.md). +Authentication is managed by the [Java Low Level REST Client](/reference/transport/rest5-client/index.md). For further details on configuring authentication, refer to [its documentation](/reference/transport/rest5-client/config/basic_authentication.md). ## Your first request [_your_first_request] The code snippet below searches all items from a “product” index whose name matches “bicycle” and return them as instances of a `Product` application class. -It illustrates the use of fluent functional builders to write search queries as concise DSL-like code. This pattern is explained in more detail in [*API conventions*](/reference/api-conventions.md). +It illustrates the use of fluent functional builders to write search queries as concise DSL-like code. This pattern is explained in more detail in [*API conventions*](/reference/api-conventions/index.md). +% :::{include-code} src={{doc-tests-src}}/getting_started/ConnectingTest.java tag=first-request ```java SearchResponse search = esClient.search(s -> s .index("products") @@ -63,16 +55,15 @@ for (Hit hit: search.hits().hits()) { } ``` - ## Using a secure connection [using-a-secure-connection] -The [Java Low Level REST Client](/reference/java-low-level-rest-client.md) documentation explains how to set up encrypted communications in detail. +The [Java Low Level REST Client](/reference/transport/rest-client/index.md) documentation explains how to set up encrypted communications in detail. In self-managed installations, Elasticsearch will start with security features like authentication and TLS enabled. To connect to the Elasticsearch cluster you’ll need to configure the Java API Client to use HTTPS with the generated CA certificate in order to make requests successfully. When you start Elasticsearch for the first time you’ll see a distinct block like the one below in the output from Elasticsearch (you may have to scroll up if it’s been a while): -```xml +``` -> Elasticsearch security features have been automatically configured! -> Authentication is enabled and cluster connections are encrypted. @@ -93,29 +84,19 @@ Depending on the context, you have two options for verifying the HTTPS connectio This method of verifying the HTTPS connection uses the certificate fingerprint value noted down earlier. +% :::{include-code} src={{doc-tests-src}}/getting_started/ConnectingTest.java tag=create-secure-client-fingerprint ```java String fingerprint = ""; SSLContext sslContext = TransportUtils - .sslContextFromCaFingerprint(fingerprint); <1> + .sslContextFromCaFingerprint(fingerprint); // <1> -BasicCredentialsProvider credsProv = new BasicCredentialsProvider(); <2> -credsProv.setCredentials( - AuthScope.ANY, new UsernamePasswordCredentials(login, password) +ElasticsearchClient esClient = ElasticsearchClient.of(b -> b + .host(url) // <3> + .usernameAndPassword(login, password) // <2> + .sslContext(sslContext) // <4> ); -RestClient restClient = RestClient - .builder(new HttpHost(host, port, "https")) <3> - .setHttpClientConfigCallback(hc -> hc - .setSSLContext(sslContext) <4> - .setDefaultCredentialsProvider(credsProv) - ) - .build(); - -// Create the transport and the API client -ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper()); -ElasticsearchClient esClient = new ElasticsearchClient(transport); - // Use the client... // Close the client, also closing the underlying transport object and network connections. @@ -148,29 +129,19 @@ The generated root CA certificate can be found in the `certs` directory in your Once you have made the `http_ca.crt` file available to your application, you can use it to set up the client: +% :::{include-code} src={{doc-tests-src}}/getting_started/ConnectingTest.java tag=create-secure-client-cert ```java File certFile = new File("/path/to/http_ca.crt"); SSLContext sslContext = TransportUtils - .sslContextFromHttpCaCrt(certFile); <1> + .sslContextFromHttpCaCrt(certFile); // <1> -BasicCredentialsProvider credsProv = new BasicCredentialsProvider(); <2> -credsProv.setCredentials( - AuthScope.ANY, new UsernamePasswordCredentials(login, password) +ElasticsearchClient esClient = ElasticsearchClient.of(b -> b + .host(url) // <3> + .usernameAndPassword(login, password) // <2> + .sslContext(sslContext) // <4> ); -RestClient restClient = RestClient - .builder(new HttpHost(host, port, "https")) <3> - .setHttpClientConfigCallback(hc -> hc - .setSSLContext(sslContext) <4> - .setDefaultCredentialsProvider(credsProv) - ) - .build(); - -// Create the transport and the API client -ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper()); -ElasticsearchClient esClient = new ElasticsearchClient(transport); - // Use the client... // Close the client, also closing the underlying transport object and network connections. @@ -183,4 +154,5 @@ esClient.close(); 4. Configure the http client with the SSL and authentication configurations. -The source code for the examples above can be found in the [Java API Client tests](https://github.com/elastic/elasticsearch-java/tree/master/java-client/src/test/java/co/elastic/clients/documentation). +:::{include} /reference/_snippets/doc-tests-blurb.md +::: diff --git a/docs/reference/setup/index.md b/docs/reference/setup/index.md new file mode 100644 index 000000000..ede57f1a9 --- /dev/null +++ b/docs/reference/setup/index.md @@ -0,0 +1,15 @@ +--- +mapped_pages: + - https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/_setup.html +--- + +# Setup [_setup] + +* [](installation.md) +* [](connecting.md) +* [](opentelemetry.md) + + + + + diff --git a/docs/reference/installation.md b/docs/reference/setup/installation.md similarity index 82% rename from docs/reference/installation.md rename to docs/reference/setup/installation.md index dacc9767d..a535d919e 100644 --- a/docs/reference/installation.md +++ b/docs/reference/setup/installation.md @@ -13,27 +13,27 @@ Requirements: Releases are hosted on [Maven Central](https://search.maven.org/search?q=g:co.elastic.clients). If you are looking for a SNAPSHOT version, the Elastic Maven Snapshot repository is available at [https://snapshots.elastic.co/maven/](https://snapshots.elastic.co/maven/). -## Installation in a Gradle project by using Jackson [gradle] +## Installation in a Gradle project [gradle] -```groovy +```groovy subs=true dependencies { - implementation 'co.elastic.clients:elasticsearch-java:9.0.0-beta1' + implementation 'co.elastic.clients:elasticsearch-java:{{version}}' } ``` -## Installation in a Maven project by using Jackson [maven] +## Installation in a Maven project [maven] In the `pom.xml` of your project, add the following repository definition and dependencies: -```xml +```xml subs=true co.elastic.clients elasticsearch-java - 9.0.0-beta1 + {{version}} @@ -50,7 +50,7 @@ If this happens, you have to explicitly add the `jakarta.json:jakarta.json-api:2 ```groovy dependencies { ... - implementation 'jakarta.json:jakarta.json-api:2.0.1' + implementation 'jakarta.json:jakarta.json-api:2.1.3' } ``` @@ -61,7 +61,7 @@ dependencies { jakarta.json jakarta.json-api - 2.0.1 + 2.1.3 @@ -74,8 +74,6 @@ Some frameworks like Spring Boot or Helidon come with their Gradle and Maven plu One of these libraries can be `jakarta.json:json-api` that defines the standard Java JSON API. In version `1.x` this library used the `javax.json` package, while in version `2.x` it uses the `jakarta.json` package after [the transition from JavaEE to JakartaEE](https://blogs.oracle.com/javamagazine/post/transition-from-java-ee-to-jakarta-ee). -The Java API Client depends on version `2.0.1` of this library, in order to use the newer and future-proof `jakarta.json` package. But some build plugins and BOMs override the Java API Client’s dependency to use version `1.x` in the older `javax.json` namespace, resulting in `ClassNotFoundException: jakarta.json.spi.JsonProvider`. +The Java API Client depends on version `2.1.3` of this library, in order to use the newer and future-proof `jakarta.json` package. But some build plugins and BOMs override the Java API Client’s dependency to use version `1.x` in the older `javax.json` namespace, resulting in `ClassNotFoundException: jakarta.json.spi.JsonProvider`. Adding the correct version as top-level project dependency solves the problem. - -If your application also requires `javax.json` you can add the `javax.json:javax.json-api:1.1.4` dependency, which is equivalent to `jakarta.json:jakarta.json-api:1.1.6`. diff --git a/docs/reference/opentelemetry.md b/docs/reference/setup/opentelemetry.md similarity index 92% rename from docs/reference/opentelemetry.md rename to docs/reference/setup/opentelemetry.md index 8f9368305..0a528ae61 100644 --- a/docs/reference/opentelemetry.md +++ b/docs/reference/setup/opentelemetry.md @@ -40,18 +40,12 @@ When using the [OpenTelemetry Java SDK manually](https://opentelemetry.io/docs/i In case you are using [manual OpenTelemetry instrumentation](https://opentelemetry.io/docs/instrumentation/java/manual/#example) with a custom OpenTelemetry SDK instance that is *not registered globally*, you can create the Java API Client using a custom OpenTelemetry instance. The following code snippet shows an example of using a custom OpenTelemetry instance. +% :::{include-code} src={{doc-tests-src}}/getting_started/ConnectingTest.java tag=create-client-otel ```java // URL and API key String serverUrl = "https://localhost:9200"; String apiKey = "VnVhQ2ZHY0JDZGJrU..."; -// Create the low-level client -RestClient restClient = RestClient - .builder(HttpHost.create(serverUrl)) - .setDefaultHeaders(new Header[]{ - new BasicHeader("Authorization", "ApiKey " + apiKey) - }) - .build(); // Create and configure custom OpenTelemetry instance OpenTelemetry customOtel = OpenTelemetrySdk.builder().build(); @@ -60,21 +54,18 @@ OpenTelemetry customOtel = OpenTelemetrySdk.builder().build(); OpenTelemetryForElasticsearch esOtelInstrumentation = new OpenTelemetryForElasticsearch(customOtel, false); -// Create the transport with the custom Instrumentation instance -ElasticsearchTransport transport = new RestClientTransport( - restClient, new JacksonJsonpMapper(), null, esOtelInstrumentation +ElasticsearchClient esClient = ElasticsearchClient.of(b -> b + .host(serverUrl) + .apiKey(apiKey) + .instrumentation(esOtelInstrumentation) ); -// And create the API client -ElasticsearchClient esClient = new ElasticsearchClient(transport); - // Use the client... // Close the client, also closing the underlying transport object and network connections. esClient.close(); ``` - ## Configuring the OpenTelemetry instrumentation [_configuring_the_opentelemetry_instrumentation] You can configure the OpenTelemetry instrumentation either through Java System properties or Environment Variables. The following configuration options are available. diff --git a/docs/reference/toc.yml b/docs/reference/toc.yml index 282c0f690..89d47d2df 100644 --- a/docs/reference/toc.yml +++ b/docs/reference/toc.yml @@ -1,14 +1,20 @@ +# Note: source code snippets in all the docs are pulled from test source files, do ensure their correctness. +# To update these snippets, run `./gradlew :tools:expand-includes` from the main elasticsearch-java directory. + toc: - file: index.md - file: getting-started.md - - file: setup.md + + - folder: setup children: + - file: index.md - file: installation.md - file: connecting.md - - file: migrate-hlrc.md - file: opentelemetry.md - - file: api-conventions.md + + - folder: api-conventions children: + - file: index.md - file: package-structure.md - file: method-naming.md - file: blocking-async.md @@ -18,42 +24,86 @@ toc: - file: object-lifecycles.md - file: loading-json.md - file: exception-conventions.md - - file: using-java-api-client.md + + - folder: usage children: - - file: esql.md + - file: index.md - file: indexing.md - file: indexing-bulk.md - file: reading.md - file: searching.md - file: aggregations.md - - file: javadoc-source-code.md - - file: external-resources.md - - file: java-low-level-rest-client.md + - file: esql.md + + - folder: troubleshooting children: - - file: java-rest-low-usage.md - children: - - file: java-rest-low-javadoc.md - - file: java-rest-low-usage-maven.md - - file: java-rest-low-usage-dependencies.md - - file: java-rest-low-usage-shading.md - - file: java-rest-low-usage-initialization.md - - file: java-rest-low-usage-requests.md - - file: java-rest-low-usage-responses.md - - file: java-rest-low-usage-logging.md - - file: java-rest-low-config.md + - file: index.md + - file: missing-required-property.md + - file: no-such-method-request-options.md + - file: io-reactor-errors.md + - file: serialize-without-typed-keys.md + + - folder: transport + children: + - file: index.md + + - folder: rest5-client children: - - file: _timeouts.md - - file: _number_of_threads.md - - file: _basic_authentication.md - - file: _other_authentication_methods.md - - file: _encrypted_communication.md - - file: _others.md - - file: _node_selector.md - - file: sniffer.md + - file: index.md + - folder: usage + children: + - file: index.md + - file: initialization.md + - file: requests.md + - file: responses.md + - file: logging.md + - folder: config + children: + - file: index.md + - file: timeouts.md + - file: number_of_threads.md + - file: basic_authentication.md + - file: other_authentication_methods.md + - file: encrypted_communication.md + - file: others.md + - file: node_selector.md + - folder: sniffer + children: + - file: index.md + + - folder: rest-client children: - - file: java-rest-sniffer-javadoc.md - - file: _maven_repository.md - - file: _usage.md + - file: index.md + - folder: usage + children: + - file: index.md + - file: javadoc.md + - file: maven.md + - file: dependencies.md + - file: shading.md + - file: initialization.md + - file: requests.md + - file: responses.md + - file: logging.md + - folder: config + children: + - file: index.md + - file: timeouts.md + - file: number_of_threads.md + - file: basic_authentication.md + - file: other_authentication_methods.md + - file: encrypted_communication.md + - file: others.md + - file: node_selector.md + - folder: sniffer + children: + - file: index.md + - file: javadoc.md + - file: maven_repository.md + - file: usage.md + + - file: javadoc-source-code.md + - file: external-resources.md - file: breaking-changes-policy.md - file: release-highlights.md - - file: _license.md \ No newline at end of file + - file: license.md diff --git a/docs/reference/transport/index.md b/docs/reference/transport/index.md new file mode 100644 index 000000000..afcd049ab --- /dev/null +++ b/docs/reference/transport/index.md @@ -0,0 +1,33 @@ +--- +navigation_title: Transport layer +--- + +# The transport layer + +`ElasticsearchClient` exposes the Elasticsearch APIs using high level objects and data structures. It relies on a `Transport` implementation for lower level concerns that include: + +* JSON serialization and deserialization, by delegating to a `JsonpMapper`. +* network communication: HTTP, TLS, load-balancing among cluster nodes, etc. + +## JSON mappers + +The Java API client comes with two JSON mapping implementations: + +* `JacksonJsonpMapper`, the default implementation based on the popular [Jackson](https://github.com/FasterXML/jackson) library. +* `JsonbJsonpMapper`, based on the JakartaEE JSONP specification, which allows using any implementation of this specification such as [Eclipse Parsson](https://github.com/eclipse-ee4j/parsson) + +Which implementation should you use? + +Unless you have specific requirements, use the default Jackson implementation. + +## HTTP implementations + +The Java API client comes with two HTTP implementations: + +* [](rest5-client/index.md), the default implementation, based on the Apache http client library version 5. It's a direct port of the previous version to this new library. +* [Rest Client](rest-client/index.md), the legacy implementation that has existed since Elasticsearch version 7, based on the Apache http client library version 4. + +Which implementation should you use? +* if you're starting a new application, use the default Rest 5 Client. +* if you're upgrading an existing application, consider upgrading to Rest 5 Client. Adapting your client creation code to the newer Apache http library should be releatively straightforward. +* if you're upgrading an application with a heavily customized Rest Client configuration, then consider staying with this legacy version. Be mindful however that it may be removed in the next major release. diff --git a/docs/reference/_basic_authentication.md b/docs/reference/transport/rest-client/config/basic_authentication.md similarity index 88% rename from docs/reference/_basic_authentication.md rename to docs/reference/transport/rest-client/config/basic_authentication.md index ff376ff33..9fb1a35ac 100644 --- a/docs/reference/_basic_authentication.md +++ b/docs/reference/transport/rest-client/config/basic_authentication.md @@ -5,7 +5,10 @@ mapped_pages: # Basic authentication [_basic_authentication] -Configuring basic authentication can be done by providing an `HttpClientConfigCallback` while building the `RestClient` through its builder. The interface has one method that receives an instance of [`org.apache.http.impl.nio.client.HttpAsyncClientBuilder`](https://hc.apache.org/httpcomponents-asyncclient-4.1.x/current/httpasyncclient/apidocs/org/apache/http/impl/nio/client/HttpAsyncClientBuilder.md) as an argument and has the same return type. The http client builder can be modified and then returned. In the following example we set a default credentials provider that requires basic authentication. +:::{include} /reference/_snippets/legacy-rest-client.md +::: + +Configuring basic authentication can be done by providing an `HttpClientConfigCallback` while building the `RestClient` through its builder. The interface has one method that receives an instance of [`org.apache.http.impl.nio.client.HttpAsyncClientBuilder`](https://hc.apache.org/httpcomponents-asyncclient-4.1.x/current/httpasyncclient/apidocs/org/apache/http/impl/nio/client/HttpAsyncClientBuilder.html) as an argument and has the same return type. The http client builder can be modified and then returned. In the following example we set a default credentials provider that requires basic authentication. ```java final CredentialsProvider credentialsProvider = diff --git a/docs/reference/_encrypted_communication.md b/docs/reference/transport/rest-client/config/encrypted_communication.md similarity index 93% rename from docs/reference/_encrypted_communication.md rename to docs/reference/transport/rest-client/config/encrypted_communication.md index 4018f7c98..ac18c4b69 100644 --- a/docs/reference/_encrypted_communication.md +++ b/docs/reference/transport/rest-client/config/encrypted_communication.md @@ -5,7 +5,10 @@ mapped_pages: # Encrypted communication [_encrypted_communication] -Encrypted communication using TLS can also be configured through the `HttpClientConfigCallback`. The [`org.apache.http.impl.nio.client.HttpAsyncClientBuilder`](https://hc.apache.org/httpcomponents-asyncclient-4.1.x/current/httpasyncclient/apidocs/org/apache/http/impl/nio/client/HttpAsyncClientBuilder.md) received as an argument exposes multiple methods to configure encrypted communication: `setSSLContext`, `setSSLSessionStrategy` and `setConnectionManager`, in order of precedence from the least important. +:::{include} /reference/_snippets/legacy-rest-client.md +::: + +Encrypted communication using TLS can also be configured through the `HttpClientConfigCallback`. The [`org.apache.http.impl.nio.client.HttpAsyncClientBuilder`](https://hc.apache.org/httpcomponents-asyncclient-4.1.x/current/httpasyncclient/apidocs/org/apache/http/impl/nio/client/HttpAsyncClientBuilder.html) received as an argument exposes multiple methods to configure encrypted communication: `setSSLContext`, `setSSLSessionStrategy` and `setConnectionManager`, in order of precedence from the least important. When accessing an Elasticsearch cluster that is setup for TLS on the HTTP layer, the client needs to trust the certificate that Elasticsearch is using. The following is an example of setting up the client to trust the CA that has signed the certificate that Elasticsearch is using, when that CA certificate is available in a PKCS#12 keystore: diff --git a/docs/reference/transport/rest-client/config/index.md b/docs/reference/transport/rest-client/config/index.md new file mode 100644 index 000000000..d2ddf09f1 --- /dev/null +++ b/docs/reference/transport/rest-client/config/index.md @@ -0,0 +1,19 @@ +--- +mapped_pages: + - https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/java-rest-low-config.html +--- + +# Common configuration [java-rest-low-config] + +:::{include} /reference/_snippets/legacy-rest-client.md +::: + +As explained in [Initialization](../usage/initialization.md), the `RestClientBuilder` supports providing both a `RequestConfigCallback` and an `HttpClientConfigCallback` which allow for any customization that the Apache Async Http Client exposes. Those callbacks make it possible to modify some specific behaviour of the client without overriding every other default configuration that the `RestClient` is initialized with. This section describes some common scenarios that require additional configuration for the low-level Java REST Client. + + + + + + + + diff --git a/docs/reference/_node_selector.md b/docs/reference/transport/rest-client/config/node_selector.md similarity index 97% rename from docs/reference/_node_selector.md rename to docs/reference/transport/rest-client/config/node_selector.md index 1cd0f3408..702dc3c55 100644 --- a/docs/reference/_node_selector.md +++ b/docs/reference/transport/rest-client/config/node_selector.md @@ -5,6 +5,9 @@ mapped_pages: # Node selector [_node_selector] +:::{include} /reference/_snippets/legacy-rest-client.md +::: + The client sends each request to one of the configured nodes in round-robin fashion. Nodes can optionally be filtered through a node selector that needs to be provided when initializing the client. This is useful when sniffing is enabled, in case no dedicated master nodes should be hit by HTTP requests. For each request the client will run the eventually configured node selector to filter the node candidates, then select the next one in the list out of the remaining ones. ```java diff --git a/docs/reference/_number_of_threads.md b/docs/reference/transport/rest-client/config/number_of_threads.md similarity index 94% rename from docs/reference/_number_of_threads.md rename to docs/reference/transport/rest-client/config/number_of_threads.md index 318c2d407..8589f2837 100644 --- a/docs/reference/_number_of_threads.md +++ b/docs/reference/transport/rest-client/config/number_of_threads.md @@ -5,6 +5,9 @@ mapped_pages: # Number of threads [_number_of_threads] +:::{include} /reference/_snippets/legacy-rest-client.md +::: + The Apache Http Async Client starts by default one dispatcher thread, and a number of worker threads used by the connection manager, as many as the number of locally detected processors (depending on what `Runtime.getRuntime().availableProcessors()` returns). The number of threads can be modified as follows: ```java diff --git a/docs/reference/_other_authentication_methods.md b/docs/reference/transport/rest-client/config/other_authentication_methods.md similarity index 96% rename from docs/reference/_other_authentication_methods.md rename to docs/reference/transport/rest-client/config/other_authentication_methods.md index ef3e88fb0..a03984c8e 100644 --- a/docs/reference/_other_authentication_methods.md +++ b/docs/reference/transport/rest-client/config/other_authentication_methods.md @@ -5,6 +5,9 @@ mapped_pages: # Other authentication methods [_other_authentication_methods] +:::{include} /reference/_snippets/legacy-rest-client.md +::: + ## Elasticsearch Token Service tokens [_elasticsearch_token_service_tokens] If you want the client to authenticate with an Elasticsearch access token, set the relevant HTTP request header. If the client makes requests on behalf of a single user only, you can set the necessary `Authorization` header as a default header as shown in the following example: diff --git a/docs/reference/_others.md b/docs/reference/transport/rest-client/config/others.md similarity index 94% rename from docs/reference/_others.md rename to docs/reference/transport/rest-client/config/others.md index 6232fb867..61eafa1df 100644 --- a/docs/reference/_others.md +++ b/docs/reference/transport/rest-client/config/others.md @@ -5,6 +5,9 @@ mapped_pages: # Others [_others] +:::{include} /reference/_snippets/legacy-rest-client.md +::: + For any other required configuration needed, the Apache HttpAsyncClient docs should be consulted: [https://hc.apache.org/httpcomponents-asyncclient-4.1.x/](https://hc.apache.org/httpcomponents-asyncclient-4.1.x/) . ::::{note} diff --git a/docs/reference/_timeouts.md b/docs/reference/transport/rest-client/config/timeouts.md similarity index 81% rename from docs/reference/_timeouts.md rename to docs/reference/transport/rest-client/config/timeouts.md index 1e265ac80..b62e8901e 100644 --- a/docs/reference/_timeouts.md +++ b/docs/reference/transport/rest-client/config/timeouts.md @@ -5,7 +5,10 @@ mapped_pages: # Timeouts [_timeouts] -Configuring requests timeouts can be done by providing an instance of `RequestConfigCallback` while building the `RestClient` through its builder. The interface has one method that receives an instance of [`org.apache.http.client.config.RequestConfig.Builder`](https://hc.apache.org/httpcomponents-client-4.5.x/current/httpclient/apidocs/org/apache/http/client/config/RequestConfig.Builder.md) as an argument and has the same return type. The request config builder can be modified and then returned. In the following example we increase the connect timeout (defaults to 1 second) and the socket timeout (defaults to 30 seconds). +:::{include} /reference/_snippets/legacy-rest-client.md +::: + +Configuring requests timeouts can be done by providing an instance of `RequestConfigCallback` while building the `RestClient` through its builder. The interface has one method that receives an instance of [`org.apache.http.client.config.RequestConfig.Builder`](https://hc.apache.org/httpcomponents-client-4.5.x/current/httpclient/apidocs/org/apache/http/client/config/RequestConfig.Builder.html) as an argument and has the same return type. The request config builder can be modified and then returned. In the following example we increase the connect timeout (defaults to 1 second) and the socket timeout (defaults to 30 seconds). ```java RestClientBuilder builder = RestClient.builder( diff --git a/docs/reference/java-low-level-rest-client.md b/docs/reference/transport/rest-client/index.md similarity index 77% rename from docs/reference/java-low-level-rest-client.md rename to docs/reference/transport/rest-client/index.md index 05e7eeea2..dff81e2c1 100644 --- a/docs/reference/java-low-level-rest-client.md +++ b/docs/reference/transport/rest-client/index.md @@ -3,7 +3,10 @@ mapped_pages: - https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/java-rest-low.html --- -# Java Low Level REST Client [java-rest-low] +# Legacy REST Client [java-rest-low] + +:::{include} /reference/_snippets/legacy-rest-client.md +::: The low-level client’s features include: @@ -13,5 +16,5 @@ The low-level client’s features include: * failed connection penalization (whether a failed node is retried depends on how many consecutive times it failed; the more failed attempts the longer the client will wait before trying that same node again) * persistent connections * trace logging of requests and responses -* optional automatic [discovery of cluster nodes](/reference/sniffer.md) +* optional automatic [discovery of cluster nodes](sniffer/index.md) diff --git a/docs/reference/sniffer.md b/docs/reference/transport/rest-client/sniffer/index.md similarity index 87% rename from docs/reference/sniffer.md rename to docs/reference/transport/rest-client/sniffer/index.md index cbb67a7f4..61ee38a3d 100644 --- a/docs/reference/sniffer.md +++ b/docs/reference/transport/rest-client/sniffer/index.md @@ -5,9 +5,11 @@ mapped_pages: # Sniffer [sniffer] +:::{include} /reference/_snippets/legacy-rest-client.md +::: + Minimal library that allows to automatically discover nodes from a running Elasticsearch cluster and set them to an existing `RestClient` instance. It retrieves by default the nodes that belong to the cluster using the Nodes Info api and uses jackson to parse the obtained json response. -Compatible with Elasticsearch 2.x and onwards. diff --git a/docs/reference/java-rest-sniffer-javadoc.md b/docs/reference/transport/rest-client/sniffer/javadoc.md similarity index 56% rename from docs/reference/java-rest-sniffer-javadoc.md rename to docs/reference/transport/rest-client/sniffer/javadoc.md index a24c16f1c..3a1f9b5e9 100644 --- a/docs/reference/java-rest-sniffer-javadoc.md +++ b/docs/reference/transport/rest-client/sniffer/javadoc.md @@ -5,5 +5,8 @@ mapped_pages: # Javadoc [java-rest-sniffer-javadoc] -The javadoc for the REST client sniffer can be found at [https://snapshots.elastic.co/javadoc/org/elasticsearch/client/elasticsearch-rest-client-sniffer/9.0.0-beta1-SNAPSHOT/index.html](https://snapshots.elastic.co/javadoc/org/elasticsearch/client/elasticsearch-rest-client-sniffer/9.0.0-beta1-SNAPSHOT/index.md). +:::{include} /reference/_snippets/legacy-rest-client.md +::: + +The javadoc for the REST client sniffer can be found at [https://snapshots.elastic.co/javadoc/org/elasticsearch/client/elasticsearch-rest-client-sniffer/{{version}}/index.html](https://snapshots.elastic.co/javadoc/org/elasticsearch/client/elasticsearch-rest-client-sniffer/{{version}}/index.md). diff --git a/docs/reference/_maven_repository.md b/docs/reference/transport/rest-client/sniffer/maven_repository.md similarity index 90% rename from docs/reference/_maven_repository.md rename to docs/reference/transport/rest-client/sniffer/maven_repository.md index 2f5324972..d86caaddf 100644 --- a/docs/reference/_maven_repository.md +++ b/docs/reference/transport/rest-client/sniffer/maven_repository.md @@ -5,6 +5,9 @@ mapped_pages: # Maven Repository [_maven_repository] +:::{include} /reference/_snippets/legacy-rest-client.md +::: + The REST client sniffer is subject to the same release cycle as Elasticsearch. Replace the version with the desired sniffer version, first released with `5.0.0-alpha4`. There is no relation between the sniffer version and the Elasticsearch version that the client can communicate with. Sniffer supports fetching the nodes list from Elasticsearch 2.x and onwards. If you are looking for a SNAPSHOT version, the Elastic Maven Snapshot repository is available at [https://snapshots.elastic.co/maven/](https://snapshots.elastic.co/maven/). @@ -13,11 +16,11 @@ If you are looking for a SNAPSHOT version, the Elastic Maven Snapshot repository Here is how you can configure the dependency using maven as a dependency manager. Add the following to your `pom.xml` file: -```xml +```xml subs=true org.elasticsearch.client elasticsearch-rest-client-sniffer - 9.0.0-beta1 + {{version}} ``` @@ -28,7 +31,7 @@ Here is how you can configure the dependency using gradle as a dependency manage ```groovy dependencies { - compile 'org.elasticsearch.client:elasticsearch-rest-client-sniffer:9.0.0-beta1' + compile 'org.elasticsearch.client:elasticsearch-rest-client-sniffer:{{version}}' } ``` diff --git a/docs/reference/_usage.md b/docs/reference/transport/rest-client/sniffer/usage.md similarity index 93% rename from docs/reference/_usage.md rename to docs/reference/transport/rest-client/sniffer/usage.md index d38511e87..73dbd58f3 100644 --- a/docs/reference/_usage.md +++ b/docs/reference/transport/rest-client/sniffer/usage.md @@ -5,7 +5,10 @@ mapped_pages: # Usage [_usage] -Once a `RestClient` instance has been created as shown in [Initialization](/reference/java-rest-low-usage-initialization.md), a `Sniffer` can be associated to it. The `Sniffer` will make use of the provided `RestClient` to periodically (every 5 minutes by default) fetch the list of current nodes from the cluster and update them by calling `RestClient#setNodes`. +:::{include} /reference/_snippets/legacy-rest-client.md +::: + +Once a `RestClient` instance has been created as shown in [Initialization](../usage/initialization.md), a `Sniffer` can be associated to it. The `Sniffer` will make use of the provided `RestClient` to periodically (every 5 minutes by default) fetch the list of current nodes from the cluster and update them by calling `RestClient#setNodes`. ```java RestClient restClient = RestClient.builder( diff --git a/docs/reference/java-rest-low-usage-dependencies.md b/docs/reference/transport/rest-client/usage/dependencies.md similarity index 91% rename from docs/reference/java-rest-low-usage-dependencies.md rename to docs/reference/transport/rest-client/usage/dependencies.md index 3bc944a11..70a9cd85e 100644 --- a/docs/reference/java-rest-low-usage-dependencies.md +++ b/docs/reference/transport/rest-client/usage/dependencies.md @@ -5,6 +5,9 @@ mapped_pages: # Dependencies [java-rest-low-usage-dependencies] +:::{include} /reference/_snippets/legacy-rest-client.md +::: + The low-level Java REST client internally uses the [Apache Http Async Client](https://hc.apache.org/httpcomponents-asyncclient-4.1.x/) to send http requests. It depends on the following artifacts, namely the async http client and its own transitive dependencies: * org.apache.httpcomponents:httpasyncclient diff --git a/docs/reference/java-rest-low-usage.md b/docs/reference/transport/rest-client/usage/index.md similarity index 83% rename from docs/reference/java-rest-low-usage.md rename to docs/reference/transport/rest-client/usage/index.md index 8c87b0d4e..dd5897ca4 100644 --- a/docs/reference/java-rest-low-usage.md +++ b/docs/reference/transport/rest-client/usage/index.md @@ -5,6 +5,9 @@ mapped_pages: # Getting started [java-rest-low-usage] +:::{include} /reference/_snippets/legacy-rest-client.md +::: + This section describes how to get started with the low-level REST client from getting the artifact to using it in an application. diff --git a/docs/reference/java-rest-low-usage-initialization.md b/docs/reference/transport/rest-client/usage/initialization.md similarity index 95% rename from docs/reference/java-rest-low-usage-initialization.md rename to docs/reference/transport/rest-client/usage/initialization.md index 30e3bca66..dae72fdd9 100644 --- a/docs/reference/java-rest-low-usage-initialization.md +++ b/docs/reference/transport/rest-client/usage/initialization.md @@ -5,7 +5,10 @@ mapped_pages: # Initialization [java-rest-low-usage-initialization] -A `RestClient` instance can be built through the corresponding `RestClientBuilder` class, created via `RestClient#builder(HttpHost...)` static method. The only required argument is one or more hosts that the client will communicate with, provided as instances of [HttpHost](https://hc.apache.org/httpcomponents-core-4.4.x/current/httpcore/apidocs/org/apache/http/HttpHost.md) as follows: +:::{include} /reference/_snippets/legacy-rest-client.md +::: + +A `RestClient` instance can be built through the corresponding `RestClientBuilder` class, created via `RestClient#builder(HttpHost...)` static method. The only required argument is one or more hosts that the client will communicate with, provided as instances of [HttpHost](https://hc.apache.org/httpcomponents-core-4.4.x/current/httpcore/apidocs/org/apache/http/HttpHost.html) as follows: ```java RestClient restClient = RestClient.builder( @@ -67,7 +70,7 @@ builder.setRequestConfigCallback( }); ``` -1. Set a callback that allows to modify the default request configuration (e.g. request timeouts, authentication, or anything that the [`org.apache.http.client.config.RequestConfig.Builder`](https://hc.apache.org/httpcomponents-client-4.5.x/current/httpclient/apidocs/org/apache/http/client/config/RequestConfig.Builder.md) allows to set) +1. Set a callback that allows to modify the default request configuration (e.g. request timeouts, authentication, or anything that the [`org.apache.http.client.config.RequestConfig.Builder`](https://hc.apache.org/httpcomponents-client-4.5.x/current/httpclient/apidocs/org/apache/http/client/config/RequestConfig.Builder.html) allows to set) ```java @@ -83,6 +86,6 @@ builder.setHttpClientConfigCallback(new HttpClientConfigCallback() { }); ``` -1. Set a callback that allows to modify the http client configuration (e.g. encrypted communication over ssl, or anything that the [`org.apache.http.impl.nio.client.HttpAsyncClientBuilder`](https://hc.apache.org/httpcomponents-asyncclient-4.1.x/current/httpasyncclient/apidocs/org/apache/http/impl/nio/client/HttpAsyncClientBuilder.md) allows to set) +1. Set a callback that allows to modify the http client configuration (e.g. encrypted communication over ssl, or anything that the [`org.apache.http.impl.nio.client.HttpAsyncClientBuilder`](https://hc.apache.org/httpcomponents-asyncclient-4.1.x/current/httpasyncclient/apidocs/org/apache/http/impl/nio/client/HttpAsyncClientBuilder.html) allows to set) diff --git a/docs/reference/java-rest-low-javadoc.md b/docs/reference/transport/rest-client/usage/javadoc.md similarity index 59% rename from docs/reference/java-rest-low-javadoc.md rename to docs/reference/transport/rest-client/usage/javadoc.md index e80307d97..a427a05b4 100644 --- a/docs/reference/java-rest-low-javadoc.md +++ b/docs/reference/transport/rest-client/usage/javadoc.md @@ -5,5 +5,8 @@ mapped_pages: # Javadoc [java-rest-low-javadoc] -The javadoc for the low level REST client can be found at [https://snapshots.elastic.co/javadoc/org/elasticsearch/client/elasticsearch-rest-client/9.0.0-beta1-SNAPSHOT/index.html](https://snapshots.elastic.co/javadoc/org/elasticsearch/client/elasticsearch-rest-client/9.0.0-beta1-SNAPSHOT/index.md). +:::{include} /reference/_snippets/legacy-rest-client.md +::: + +The javadoc for the low level REST client can be found at [https://snapshots.elastic.co/javadoc/org/elasticsearch/client/elasticsearch-rest-client/{{version}}/](https://snapshots.elastic.co/javadoc/org/elasticsearch/client/elasticsearch-rest-client/{{version}}/). diff --git a/docs/reference/java-rest-low-usage-logging.md b/docs/reference/transport/rest-client/usage/logging.md similarity index 94% rename from docs/reference/java-rest-low-usage-logging.md rename to docs/reference/transport/rest-client/usage/logging.md index 9d2e44739..bd6dcd80d 100644 --- a/docs/reference/java-rest-low-usage-logging.md +++ b/docs/reference/transport/rest-client/usage/logging.md @@ -5,6 +5,9 @@ mapped_pages: # Logging [java-rest-low-usage-logging] +:::{include} /reference/_snippets/legacy-rest-client.md +::: + The Java REST client uses the same logging library that the Apache Async Http Client uses: [Apache Commons Logging](https://commons.apache.org/proper/commons-logging/), which comes with support for a number of popular logging implementations. The java packages to enable logging for are `org.elasticsearch.client` for the client itself and `org.elasticsearch.client.sniffer` for the sniffer. The request tracer logging can also be enabled to log every request and corresponding response in curl format. That comes handy when debugging, for instance in case a request needs to be manually executed to check whether it still yields the same response as it did. Enable trace logging for the `tracer` package to have such log lines printed out. Do note that this type of logging is expensive and should not be enabled at all times in production environments, but rather temporarily used only when needed. @@ -13,7 +16,7 @@ The request tracer logging can also be enabled to log every request and correspo ### Trace Logs [_trace_logs] -In order to enable trace logs for logback, we have to use [jcl-over-slf4j bridging module](https://www.slf4j.org/legacy.md#jclOverSLF4J). +In order to enable trace logs for logback, we have to use [jcl-over-slf4j bridging module](https://www.slf4j.org/legacy.html#jclOverSLF4J). 1. Add the following to your Gradle setting: diff --git a/docs/reference/java-rest-low-usage-maven.md b/docs/reference/transport/rest-client/usage/maven.md similarity index 92% rename from docs/reference/java-rest-low-usage-maven.md rename to docs/reference/transport/rest-client/usage/maven.md index e6c97b5df..df88bea06 100644 --- a/docs/reference/java-rest-low-usage-maven.md +++ b/docs/reference/transport/rest-client/usage/maven.md @@ -5,6 +5,9 @@ mapped_pages: # Maven Repository [java-rest-low-usage-maven] +:::{include} /reference/_snippets/legacy-rest-client.md +::: + The low-level Java REST client is hosted on [Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.elasticsearch.client%22). The minimum Java version required is `1.8`. The low-level REST client is subject to the same release cycle as Elasticsearch. Replace the version with the desired client version, first released with `5.0.0-alpha4`. There is no relation between the client version and the Elasticsearch version that the client can communicate with. The low-level REST client is compatible with all Elasticsearch versions. @@ -15,11 +18,11 @@ If you are looking for a SNAPSHOT version, the Elastic Maven Snapshot repository Here is how you can configure the dependency using maven as a dependency manager. Add the following to your `pom.xml` file: -```xml +```xml subs=true org.elasticsearch.client elasticsearch-rest-client - 9.0.0-beta1 + {{version}} ``` @@ -30,7 +33,7 @@ Here is how you can configure the dependency using gradle as a dependency manage ```groovy dependencies { - compile 'org.elasticsearch.client:elasticsearch-rest-client:9.0.0-beta1' + compile 'org.elasticsearch.client:elasticsearch-rest-client:{{version}}' } ``` diff --git a/docs/reference/java-rest-low-usage-requests.md b/docs/reference/transport/rest-client/usage/requests.md similarity index 99% rename from docs/reference/java-rest-low-usage-requests.md rename to docs/reference/transport/rest-client/usage/requests.md index 0bf474b6f..5632259e5 100644 --- a/docs/reference/java-rest-low-usage-requests.md +++ b/docs/reference/transport/rest-client/usage/requests.md @@ -5,6 +5,9 @@ mapped_pages: # Performing requests [java-rest-low-usage-requests] +:::{include} /reference/_snippets/legacy-rest-client.md +::: + Once the `RestClient` has been created, requests can be sent by calling either `performRequest` or `performRequestAsync`. `performRequest` is synchronous and will block the calling thread and return the `Response` when the request is successful or throw an exception if it fails. `performRequestAsync` is asynchronous and accepts a `ResponseListener` argument that it calls with a `Response` when the request is successful or with an `Exception` if it fails. This is synchronous: diff --git a/docs/reference/java-rest-low-usage-responses.md b/docs/reference/transport/rest-client/usage/responses.md similarity index 80% rename from docs/reference/java-rest-low-usage-responses.md rename to docs/reference/transport/rest-client/usage/responses.md index eb929cfd3..5ac4ee9dd 100644 --- a/docs/reference/java-rest-low-usage-responses.md +++ b/docs/reference/transport/rest-client/usage/responses.md @@ -5,6 +5,9 @@ mapped_pages: # Reading responses [java-rest-low-usage-responses] +:::{include} /reference/_snippets/legacy-rest-client.md +::: + The `Response` object, either returned by the synchronous `performRequest` methods or received as an argument in `ResponseListener#onSuccess(Response)`, wraps the response object returned by the http client and exposes some additional information. ```java @@ -20,7 +23,7 @@ String responseBody = EntityUtils.toString(response.getEntity()); <5> 2. The host that returned the response 3. The response status line, from which you can for instance retrieve the status code 4. The response headers, which can also be retrieved by name though `getHeader(String)` -5. The response body enclosed in an [`org.apache.http.HttpEntity`](https://hc.apache.org/httpcomponents-core-4.4.x/current/httpcore/apidocs/org/apache/http/HttpEntity.md) object +5. The response body enclosed in an [`org.apache.http.HttpEntity`](https://hc.apache.org/httpcomponents-core-4.4.x/current/httpcore/apidocs/org/apache/http/HttpEntity.html) object When performing a request, an exception is thrown (or received as an argument in `ResponseListener#onFailure(Exception)` in the following scenarios: @@ -38,4 +41,4 @@ A `ResponseException` is **not** thrown for `HEAD` requests that return a `404` Note that the low-level client doesn’t expose any helper for json marshalling and un-marshalling. Users are free to use the library that they prefer for that purpose. -The underlying Apache Async Http Client ships with different [`org.apache.http.HttpEntity`](https://hc.apache.org/httpcomponents-core-4.4.x/current/httpcore/apidocs/org/apache/http/HttpEntity.md) implementations that allow to provide the request body in different formats (stream, byte array, string etc.). As for reading the response body, the `HttpEntity#getContent` method comes handy which returns an `InputStream` reading from the previously buffered response body. As an alternative, it is possible to provide a custom [`org.apache.http.nio.protocol.HttpAsyncResponseConsumer`](https://hc.apache.org/httpcomponents-core-4.4.x/current/httpcore-nio/apidocs/org/apache/http/nio/protocol/HttpAsyncResponseConsumer.md) that controls how bytes are read and buffered. +The underlying Apache Async Http Client ships with different [`org.apache.http.HttpEntity`](https://hc.apache.org/httpcomponents-core-4.4.x/current/httpcore/apidocs/org/apache/http/HttpEntity.html) implementations that allow to provide the request body in different formats (stream, byte array, string etc.). As for reading the response body, the `HttpEntity#getContent` method comes handy which returns an `InputStream` reading from the previously buffered response body. As an alternative, it is possible to provide a custom [`org.apache.http.nio.protocol.HttpAsyncResponseConsumer`](https://hc.apache.org/httpcomponents-core-4.4.x/current/httpcore-nio/apidocs/org/apache/http/nio/protocol/HttpAsyncResponseConsumer.html) that controls how bytes are read and buffered. diff --git a/docs/reference/java-rest-low-usage-shading.md b/docs/reference/transport/rest-client/usage/shading.md similarity index 95% rename from docs/reference/java-rest-low-usage-shading.md rename to docs/reference/transport/rest-client/usage/shading.md index 766cb7a65..78f594056 100644 --- a/docs/reference/java-rest-low-usage-shading.md +++ b/docs/reference/transport/rest-client/usage/shading.md @@ -5,13 +5,16 @@ mapped_pages: # Shading [java-rest-low-usage-shading] +:::{include} /reference/_snippets/legacy-rest-client.md +::: + In order to avoid version conflicts, the dependencies can be shaded and packaged within the client in a single JAR file (sometimes called an "uber JAR" or "fat JAR"). Shading a dependency consists of taking its content (resources files and Java class files) and renaming some of its packages before putting them in the same JAR file as the low-level Java REST client. Shading a JAR can be accomplished by 3rd-party plugins for Gradle and Maven. Be advised that shading a JAR also has implications. Shading the Commons Logging layer, for instance, means that 3rd-party logging backends need to be shaded as well. ## Maven configuration [java-rest-low-usage-shading-maven] -Here is a configuration using the Maven [Shade](https://maven.apache.org/plugins/maven-shade-plugin/index.md) plugin. Add the following to your `pom.xml` file: +Here is a configuration using the Maven [Shade](https://maven.apache.org/plugins/maven-shade-plugin/index.html) plugin. Add the following to your `pom.xml` file: ```xml diff --git a/docs/reference/transport/rest5-client/config/basic_authentication.md b/docs/reference/transport/rest5-client/config/basic_authentication.md new file mode 100644 index 000000000..077fbee17 --- /dev/null +++ b/docs/reference/transport/rest5-client/config/basic_authentication.md @@ -0,0 +1,41 @@ + +# Basic authentication + +Configuring basic authentication can be done by providing an `HttpClientConfigCallback` while building the `RestClient` through its builder. The interface has one method that receives an instance of [`org.apache.http.impl.nio.client.HttpAsyncClientBuilder`](https://hc.apache.org/httpcomponents-asyncclient-4.1.x/current/httpasyncclient/apidocs/org/apache/http/impl/nio/client/HttpAsyncClientBuilder.html) as an argument and has the same return type. The http client builder can be modified and then returned. In the following example we set a default credentials provider that requires basic authentication. + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-config-basic-auth +```java +var creds = Base64.getEncoder() + .encodeToString("user:test-user-password".getBytes()); + +Rest5ClientBuilder restClient = Rest5Client + .builder(new HttpHost("https", "localhost", 9200)) + .setDefaultHeaders(new Header[]{ + new BasicHeader("Authorization", "Basic " + creds) + }); + +``` + +Preemptive Authentication can be disabled, which means that every request will be sent without authorization headers to see if it is accepted and, upon receiving an HTTP 401 response, it will resend the exact same request with the basic authentication header. If you wish to do this, then you can do so by disabling it via the `HttpAsyncClientBuilder`: + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-config-disable-preemptive-auth +```java +HttpHost host = new HttpHost("http", "localhost", 9200); + +var creds = Base64.getEncoder().encodeToString("user:test-user-password".getBytes()); + +CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom() + .disableAuthCaching() // <1> + .build(); + +Rest5ClientBuilder restClient = Rest5Client + .builder(new HttpHost("https", "localhost", 9200)) + .setHttpClient(httpclient) + .setDefaultHeaders(new Header[]{ + new BasicHeader("Authorization", "Basic " + creds) + }); +``` + +1. Disable preemptive authentication + + diff --git a/docs/reference/transport/rest5-client/config/encrypted_communication.md b/docs/reference/transport/rest5-client/config/encrypted_communication.md new file mode 100644 index 000000000..d978106dc --- /dev/null +++ b/docs/reference/transport/rest5-client/config/encrypted_communication.md @@ -0,0 +1,84 @@ + +# Encrypted communication + +Encrypted communication using TLS can also be configured through the `HttpClientConfigCallback`. The [`org.apache.http.impl.nio.client.HttpAsyncClientBuilder`](https://hc.apache.org/httpcomponents-asyncclient-4.1.x/current/httpasyncclient/apidocs/org/apache/http/impl/nio/client/HttpAsyncClientBuilder.html) received as an argument exposes multiple methods to configure encrypted communication: `setSSLContext`, `setSSLSessionStrategy` and `setConnectionManager`, in order of precedence from the least important. + +When accessing an Elasticsearch cluster that is setup for TLS on the HTTP layer, the client needs to trust the certificate that Elasticsearch is using. The following is an example of setting up the client to trust the CA that has signed the certificate that Elasticsearch is using, when that CA certificate is available in a PKCS#12 keystore: + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-config-encrypted-communication +```java +Path trustStorePath = Paths.get("/path/to/truststore.p12"); +KeyStore truststore = KeyStore.getInstance("pkcs12"); +try (InputStream is = Files.newInputStream(trustStorePath)) { + truststore.load(is, keyStorePass.toCharArray()); +} + +SSLContext sslContext = SSLContexts.custom() + .loadTrustMaterial(truststore, null) + .build(); + +Rest5ClientBuilder builder = Rest5Client + .builder(new HttpHost("https", "localhost", 9200)) + .setSSLContext(sslContext); +``` + +The following is an example of setting up the client to trust the CA that has signed the certificate that Elasticsearch is using, when that CA certificate is available as a PEM encoded file. + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-config-trust-ca-pem +```java +Path caCertificatePath = Paths.get("/path/to/ca.crt"); +CertificateFactory factory = CertificateFactory.getInstance("X.509"); +Certificate trustedCa; +try (InputStream is = Files.newInputStream(caCertificatePath)) { + trustedCa = factory.generateCertificate(is); +} + +KeyStore trustStore = KeyStore.getInstance("pkcs12"); +trustStore.load(null, null); +trustStore.setCertificateEntry("ca", trustedCa); + +SSLContext sslContext = SSLContexts.custom() + .loadTrustMaterial(trustStore, null) + .build(); + +Rest5Client + .builder(new HttpHost("https", "localhost", 9200)) + .setSSLContext(sslContext); +``` + +When Elasticsearch is configured to require client TLS authentication, for example when a PKI realm is configured, the client needs to provide a client certificate during the TLS handshake in order to authenticate. The following is an example of setting up the client for TLS authentication with a certificate and a private key that are stored in a PKCS#12 keystore. + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-config-mutual-tls-authentication +```java +Path trustStorePath = Paths.get("/path/to/your/truststore.p12"); +Path keyStorePath = Paths.get("/path/to/your/keystore.p12"); +KeyStore trustStore = KeyStore.getInstance("pkcs12"); +KeyStore keyStore = KeyStore.getInstance("pkcs12"); + +try (InputStream is = Files.newInputStream(trustStorePath)) { + trustStore.load(is, trustStorePass.toCharArray()); +} + +try (InputStream is = Files.newInputStream(keyStorePath)) { + keyStore.load(is, keyStorePass.toCharArray()); +} + +SSLContext sslContext = SSLContexts.custom() + .loadTrustMaterial(trustStore, null) + .loadKeyMaterial(keyStore, keyStorePass.toCharArray()) + .build(); + +Rest5Client + .builder(new HttpHost("https", "localhost", 9200)) + .setSSLContext(sslContext); +``` + +If the client certificate and key are not available in a keystore but rather as PEM encoded files, you cannot use them directly to build an SSLContext. You must rely on external libraries to parse the PEM key into a PrivateKey instance. Alternatively, you can use external tools to build a keystore from your PEM files, as shown in the following example: + +```bash +openssl pkcs12 -export -in client.crt -inkey private_key.pem \ + -name "client" -out client.p12 +``` + +If no explicit configuration is provided, the [system default configuration](https://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/JSSERefGuide.md#CustomizingStores) will be used. + diff --git a/docs/reference/transport/rest5-client/config/index.md b/docs/reference/transport/rest5-client/config/index.md new file mode 100644 index 000000000..0e9b5f77d --- /dev/null +++ b/docs/reference/transport/rest5-client/config/index.md @@ -0,0 +1,14 @@ + +# Common configuration + +As explained in [Initialization](../usage/initialization.md), the `RestClientBuilder` supports providing both a `RequestConfigCallback` and an `HttpClientConfigCallback` which allow for any customization that the Apache Async Http Client exposes. Those callbacks make it possible to modify some specific behaviour of the client without overriding every other default configuration that the `Rest5Client` is initialized with. This section describes some common scenarios that require additional configuration for the low-level Java REST Client. + + + + + + + + diff --git a/docs/reference/transport/rest5-client/config/node_selector.md b/docs/reference/transport/rest5-client/config/node_selector.md new file mode 100644 index 000000000..e06f9f1b1 --- /dev/null +++ b/docs/reference/transport/rest5-client/config/node_selector.md @@ -0,0 +1,45 @@ + +# Node selector + +The client sends each request to one of the configured nodes in round-robin fashion. Nodes can optionally be filtered through a node selector that needs to be provided when initializing the client. This is useful when sniffing is enabled, in case no dedicated master nodes should be hit by HTTP requests. For each request the client will run the eventually configured node selector to filter the node candidates, then select the next one in the list out of the remaining ones. + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-init-allocation-aware-selector +```java +Rest5ClientBuilder builder = Rest5Client + .builder(new HttpHost("http", "localhost", 9200)); + +builder.setNodeSelector(nodes -> { // <1> + /* + * Prefer any node that belongs to rack_one. If none is around + * we will go to another rack till it's time to try and revive + * some of the nodes that belong to rack_one. + */ + boolean foundOne = false; + for (Node node : nodes) { + String rackId = node.getAttributes().get("rack_id").get(0); + if ("rack_one".equals(rackId)) { + foundOne = true; + break; + } + } + if (foundOne) { + Iterator nodesIt = nodes.iterator(); + while (nodesIt.hasNext()) { + Node node = nodesIt.next(); + String rackId = node.getAttributes().get("rack_id").get(0); + if (!"rack_one".equals(rackId)) { + nodesIt.remove(); + } + } + } +}); +``` + +1. Set an allocation aware node selector that allows to pick a node in the local rack if any available, otherwise go to any other node in any rack. It acts as a preference rather than a strict requirement, given that it goes to another rack if none of the local nodes are available, rather than returning no nodes in such case which would make the client forcibly revive a local node whenever none of the nodes from the preferred rack is available. + + +::::{warning} +Node selectors that do not consistently select the same set of nodes will make round-robin behaviour unpredictable and possibly unfair. The preference example above is fine as it reasons about availability of nodes which already affects the predictability of round-robin. Node selection should not depend on other external factors or round-robin will not work properly. +:::: + + diff --git a/docs/reference/transport/rest5-client/config/number_of_threads.md b/docs/reference/transport/rest5-client/config/number_of_threads.md new file mode 100644 index 000000000..a5be90de1 --- /dev/null +++ b/docs/reference/transport/rest5-client/config/number_of_threads.md @@ -0,0 +1,17 @@ + +# Number of threads + +The Apache Http Async Client starts by default one dispatcher thread, and a number of worker threads used by the connection manager, as many as the number of locally detected processors (depending on what `Runtime.getRuntime().availableProcessors()` returns). The number of threads can be modified as follows: + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-config-threads +```java +CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom() + .setIOReactorConfig( + IOReactorConfig.custom().setIoThreadCount(1).build() + ) + .build(); + +Rest5ClientBuilder builder = Rest5Client + .builder(new HttpHost("localhost", 9200)) + .setHttpClient(httpclient); +``` diff --git a/docs/reference/transport/rest5-client/config/other_authentication_methods.md b/docs/reference/transport/rest5-client/config/other_authentication_methods.md new file mode 100644 index 000000000..584cc1b6a --- /dev/null +++ b/docs/reference/transport/rest5-client/config/other_authentication_methods.md @@ -0,0 +1,45 @@ + +# Other authentication methods + +## Elasticsearch Token Service tokens + +If you want the client to authenticate with an Elasticsearch access token, set the relevant HTTP request header. If the client makes requests on behalf of a single user only, you can set the necessary `Authorization` header as a default header as shown in the following example: + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-auth-bearer-token +```java +Rest5ClientBuilder builder = Rest5Client + .builder(new HttpHost("https", "localhost", 9200)); + +Header[] defaultHeaders = new Header[]{ new BasicHeader( + "Authorization", + "Bearer u6iuAxZ0RG1Kcm5jVFI4eU4tZU9aVFEwT2F3" +)}; + +builder.setDefaultHeaders(defaultHeaders); +``` + + +## Elasticsearch API keys + +If you want the client to authenticate with an Elasticsearch API key, set the relevant HTTP request header. If the client makes requests on behalf of a single user only, you can set the necessary `Authorization` header as a default header as shown in the following example: + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-auth-api-key +```java +String apiKeyId = "uqlEyn8B_gQ_jlvwDIvM"; +String apiKeySecret = "HxHWk2m4RN-V_qg9cDpuX"; +String apiKeyAuth = Base64.getEncoder().encodeToString( + (apiKeyId + ":" + apiKeySecret).getBytes(StandardCharsets.UTF_8) +); + +Rest5ClientBuilder builder = Rest5Client + .builder(new HttpHost("https", "localhost", 9200)); + +Header[] defaultHeaders = new Header[]{ new BasicHeader( + "Authorization", + "ApiKey " + apiKeyAuth +)}; + +builder.setDefaultHeaders(defaultHeaders); +``` + + diff --git a/docs/reference/transport/rest5-client/config/others.md b/docs/reference/transport/rest5-client/config/others.md new file mode 100644 index 000000000..e99b6870f --- /dev/null +++ b/docs/reference/transport/rest5-client/config/others.md @@ -0,0 +1,10 @@ + +# Others + +For any other required configuration needed, the Apache HttpAsyncClient docs should be consulted: [https://hc.apache.org/httpcomponents-asyncclient-4.1.x/](https://hc.apache.org/httpcomponents-asyncclient-4.1.x/) . + +::::{note} +If your application runs under the security manager you might be subject to the JVM default policies of caching positive hostname resolutions indefinitely and negative hostname resolutions for ten seconds. If the resolved addresses of the hosts to which you are connecting the client to vary with time then you might want to modify the default JVM behavior. These can be modified by adding [`networkaddress.cache.ttl=`](https://docs.oracle.com/javase/8/docs/technotes/guides/net/properties.md) and [`networkaddress.cache.negative.ttl=`](https://docs.oracle.com/javase/8/docs/technotes/guides/net/properties.md) to your [Java security policy](https://docs.oracle.com/javase/8/docs/technotes/guides/security/PolicyFiles.md). +:::: + + diff --git a/docs/reference/transport/rest5-client/config/timeouts.md b/docs/reference/transport/rest5-client/config/timeouts.md new file mode 100644 index 000000000..5b43b9b19 --- /dev/null +++ b/docs/reference/transport/rest5-client/config/timeouts.md @@ -0,0 +1,33 @@ + +# Timeouts + +Configuring requests timeouts can be done by providing an instance of `RequestConfigCallback` while building the `RestClient` through its builder. The interface has one method that receives an instance of [`org.apache.http.client.config.RequestConfig.Builder`](https://hc.apache.org/httpcomponents-client-4.5.x/current/httpclient/apidocs/org/apache/http/client/config/RequestConfig.Builder.html) as an argument and has the same return type. The request config builder can be modified and then returned. In the following example we increase the connect timeout (defaults to 1 second) and the socket timeout (defaults to 30 seconds). + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-config-timeouts +```java +RequestConfig.Builder requestConfigBuilder = RequestConfig.custom() + .setConnectTimeout(Timeout.of(5000, TimeUnit.MILLISECONDS)); + +CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom() + .setDefaultRequestConfig(requestConfigBuilder.build()) + .build(); + +Rest5ClientBuilder builder = Rest5Client + .builder(new HttpHost("localhost", 9200)) + .setHttpClient(httpclient); +``` + +Timeouts also can be set per request with RequestOptions, which overrides RestClient customizeRequestConfig. + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-config-request-options-timeouts +```java +RequestConfig requestConfig = RequestConfig.custom() + .setConnectTimeout(Timeout.ofMilliseconds(5000)) + .setConnectionRequestTimeout(Timeout.ofMilliseconds(60000)) + .build(); + +RequestOptions options = RequestOptions.DEFAULT.toBuilder() + .setRequestConfig(requestConfig) + .build(); +``` + diff --git a/docs/reference/transport/rest5-client/index.md b/docs/reference/transport/rest5-client/index.md new file mode 100644 index 000000000..d6afed595 --- /dev/null +++ b/docs/reference/transport/rest5-client/index.md @@ -0,0 +1,13 @@ + +# REST 5 Client + +The low-level client’s features include: + +* minimal dependencies +* load balancing across all available nodes +* failover in case of node failures and upon specific response codes +* failed connection penalization (whether a failed node is retried depends on how many consecutive times it failed; the more failed attempts the longer the client will wait before trying that same node again) +* persistent connections +* trace logging of requests and responses +* optional automatic [discovery of cluster nodes](sniffer/index.md) + diff --git a/docs/reference/transport/rest5-client/sniffer/index.md b/docs/reference/transport/rest5-client/sniffer/index.md new file mode 100644 index 000000000..74b43e4d4 --- /dev/null +++ b/docs/reference/transport/rest5-client/sniffer/index.md @@ -0,0 +1,120 @@ + +# Sniffer + +Minimal library that allows to automatically discover nodes from a running Elasticsearch cluster and set them to an existing `RestClient` instance. It retrieves by default the nodes that belong to the cluster using the Nodes Info api and uses jackson to parse the obtained json response. + +# Usage + +Once a `RestClient` instance has been created as shown in [Initialization](../usage/initialization.md), a `Sniffer` can be associated to it. The `Sniffer` will make use of the provided `RestClient` to periodically (every 5 minutes by default) fetch the list of current nodes from the cluster and update them by calling `RestClient#setNodes`. + +% :::{include-code} src={{doc-tests-src}}/rest5_client/SnifferDocumentation.java tag=sniffer-init +```java +Rest5Client restClient = Rest5Client + .builder(HttpHost.create("http://localhost:9200")) + .build(); + +Sniffer sniffer = Sniffer.builder(restClient).build(); +``` + +It is important to close the `Sniffer` so that its background thread gets properly shutdown and all of its resources are released. The `Sniffer` object should have the same lifecycle as the `RestClient` and get closed right before the client: + +% :::{include-code} src={{doc-tests-src}}/rest5_client/SnifferDocumentation.java tag=sniffer-close +```java +sniffer.close(); +restClient.close(); +``` + +The `Sniffer` updates the nodes by default every 5 minutes. This interval can be customized by providing it (in milliseconds) as follows: + +% :::{include-code} src={{doc-tests-src}}/rest5_client/SnifferDocumentation.java tag=sniffer-interval +```java +Rest5Client restClient = Rest5Client + .builder(new HttpHost("localhost", 9200)) + .build(); + +Sniffer sniffer = Sniffer.builder(restClient) + .setSniffIntervalMillis(60000).build(); +``` + +It is also possible to enable sniffing on failure, meaning that after each failure the nodes list gets updated straightaway rather than at the following ordinary sniffing round. In this case a `SniffOnFailureListener` needs to be created at first and provided at `RestClient` creation. Also once the `Sniffer` is later created, it needs to be associated with that same `SniffOnFailureListener` instance, which will be notified at each failure and use the `Sniffer` to perform the additional sniffing round as described. + +% :::{include-code} src={{doc-tests-src}}/rest5_client/SnifferDocumentation.java tag=sniff-on-failure +```java +SniffOnFailureListener sniffOnFailureListener = + new SniffOnFailureListener(); + +Rest5Client restClient = Rest5Client + .builder(new HttpHost("localhost", 9200)) + .setFailureListener(sniffOnFailureListener) // <1> + .build(); + +Sniffer sniffer = Sniffer.builder(restClient) + .setSniffAfterFailureDelayMillis(30000) // <2> + .build(); + +sniffOnFailureListener.setSniffer(sniffer); // <3> +``` + +1. Set the failure listener to the `RestClient` instance +2. When sniffing on failure, not only do the nodes get updated after each failure, but an additional sniffing round is also scheduled sooner than usual, by default one minute after the failure, assuming that things will go back to normal and we want to detect that as soon as possible. Said interval can be customized at `Sniffer` creation time through the `setSniffAfterFailureDelayMillis` method. Note that this last configuration parameter has no effect in case sniffing on failure is not enabled like explained above. +3. Set the `Sniffer` instance to the failure listener + + +The Elasticsearch Nodes Info api doesn’t return the protocol to use when connecting to the nodes but only their `host:port` key-pair, hence `http` is used by default. In case `https` should be used instead, the `ElasticsearchNodesSniffer` instance has to be manually created and provided as follows: + +% :::{include-code} src={{doc-tests-src}}/rest5_client/SnifferDocumentation.java tag=sniffer-https +```java +Rest5Client restClient = Rest5Client.builder( + new HttpHost("localhost", 9200)) + .build(); + +NodesSniffer nodesSniffer = new ElasticsearchNodesSniffer( + restClient, + ElasticsearchNodesSniffer.DEFAULT_SNIFF_REQUEST_TIMEOUT, + ElasticsearchNodesSniffer.Scheme.HTTPS +); + +Sniffer sniffer = Sniffer.builder(restClient) + .setNodesSniffer(nodesSniffer).build(); +``` + +In the same way it is also possible to customize the `sniffRequestTimeout`, which defaults to one second. That is the `timeout` parameter provided as a query string parameter when calling the Nodes Info api, so that when the timeout expires on the server side, a valid response is still returned although it may contain only a subset of the nodes that are part of the cluster, the ones that have responded until then. + +% :::{include-code} src={{doc-tests-src}}/rest5_client/SnifferDocumentation.java tag=sniff-request-timeout +```java +Rest5Client restClient = Rest5Client.builder( + new HttpHost("localhost", 9200)) + .build(); + +NodesSniffer nodesSniffer = new ElasticsearchNodesSniffer( + restClient, + TimeUnit.SECONDS.toMillis(5), + ElasticsearchNodesSniffer.Scheme.HTTP +); + +Sniffer sniffer = Sniffer.builder(restClient) + .setNodesSniffer(nodesSniffer).build(); +``` + +Also, a custom `NodesSniffer` implementation can be provided for advanced use cases that may require fetching the nodes from external sources rather than from Elasticsearch: + +% :::{include-code} src={{doc-tests-src}}/rest5_client/SnifferDocumentation.java tag=custom-nodes-sniffer +```java +Rest5Client restClient = Rest5Client + .builder(URI.create("http://localhost:9200")) + .build(); + +NodesSniffer nodesSniffer = new NodesSniffer() { + @Override + public List sniff() throws IOException { + return null; // <1> + } +}; + +Sniffer sniffer = Sniffer.builder(restClient) + .setNodesSniffer(nodesSniffer).build(); +``` + +1. Fetch the hosts from the external source + + diff --git a/docs/reference/transport/rest5-client/usage/index.md b/docs/reference/transport/rest5-client/usage/index.md new file mode 100644 index 000000000..aa1bb6631 --- /dev/null +++ b/docs/reference/transport/rest5-client/usage/index.md @@ -0,0 +1,13 @@ + +# Getting started + +This section describes how to get started with the low-level REST client from getting the artifact to using it in an application. + + + + + + + + + diff --git a/docs/reference/transport/rest5-client/usage/initialization.md b/docs/reference/transport/rest5-client/usage/initialization.md new file mode 100644 index 000000000..363e9e21d --- /dev/null +++ b/docs/reference/transport/rest5-client/usage/initialization.md @@ -0,0 +1,96 @@ + +# Initialization + +A `Rest5Client` instance can be built through the corresponding `Rest5ClientBuilder` class, created via `Rest5Client#builder(...)` static method. The only required argument is one or more URLs or `HttpHost` that the client will communicate with as follows: + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-init +```java +Rest5Client restClient = Rest5Client.builder( + URI.create("http://localhost:9200"), + URI.create("http://localhost:9201") +).build(); +``` + +The `RestClient` class is thread-safe and ideally has the same lifecycle as the application that uses it. It is important that it gets closed when no longer needed so that all the resources used by it get properly released, as well as the underlying http client instance and its threads: + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-close +```java +restClient.close(); +``` + +`RestClientBuilder` also allows to optionally set the following configuration parameters while building the `RestClient` instance: + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-init-default-headers +```java +Rest5ClientBuilder builder = Rest5Client + .builder(new HttpHost("http", "localhost", 9200)); + +Header[] defaultHeaders = new Header[]{new BasicHeader("header", "value")}; +builder.setDefaultHeaders(defaultHeaders); // <1> +``` + +1. Set the default headers that need to be sent with each request, to prevent having to specify them with each single request + + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-init-failure-listener +```java +Rest5ClientBuilder builder = Rest5Client + .builder(new HttpHost("http", "localhost", 9200)); + +builder.setFailureListener(new Rest5Client.FailureListener() { + @Override + public void onFailure(Node node) { + // <1> + } +}); +``` + +1. Set a listener that gets notified every time a node fails, in case actions need to be taken. Used internally when sniffing on failure is enabled. + + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-init-node-selector +```java +Rest5ClientBuilder builder = Rest5Client + .builder(new HttpHost("http", "localhost", 9200)); + +builder.setNodeSelector(NodeSelector.SKIP_DEDICATED_MASTERS); // <1> +``` + +1. Set the node selector to be used to filter the nodes the client will send requests to among the ones that are set to the client itself. This is useful for instance to prevent sending requests to dedicated master nodes when sniffing is enabled. By default the client sends requests to every configured node. + + + + diff --git a/docs/reference/transport/rest5-client/usage/logging.md b/docs/reference/transport/rest5-client/usage/logging.md new file mode 100644 index 000000000..28a79acd8 --- /dev/null +++ b/docs/reference/transport/rest5-client/usage/logging.md @@ -0,0 +1,9 @@ +# Logging + +The Java REST 5 client uses the same logging library that the Apache Async Http Client uses: [Apache Commons Logging](https://commons.apache.org/proper/commons-logging/), which comes with support for a number of popular logging implementations. The java packages to enable logging for is `co.elastic.clients.transport.rest5_client`. + +The request tracer logging can also be enabled to log every request and corresponding response in curl format. That comes handy when debugging, for instance in case a request needs to be manually executed to check whether it still yields the same response as it did. Enable trace logging for the `co.elastic.clients.transport.rest5_client.Request` class to have such log lines printed out. Do note that this type of logging is expensive and should not be enabled at all times in production environments, but rather temporarily used only when needed. + + + + diff --git a/docs/reference/transport/rest5-client/usage/requests.md b/docs/reference/transport/rest5-client/usage/requests.md new file mode 100644 index 000000000..a7ba8a3d3 --- /dev/null +++ b/docs/reference/transport/rest5-client/usage/requests.md @@ -0,0 +1,191 @@ + +# Performing requests + +Once the `RestClient` has been created, requests can be sent by calling either `performRequest` or `performRequestAsync`. `performRequest` is synchronous and will block the calling thread and return the `Response` when the request is successful or throw an exception if it fails. `performRequestAsync` is asynchronous and accepts a `ResponseListener` argument that it calls with a `Response` when the request is successful or with an `Exception` if it fails. + +This is synchronous: + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-sync +```java +Request request = new Request( + "GET", // <1> + "/"); // <2> + +Response response = restClient.performRequest(request); +``` + +1. The HTTP method (`GET`, `POST`, `HEAD`, etc) +2. The endpoint on the server + + +And this is asynchronous: + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-async +```java +Request request = new Request( + "GET", // <1> + "/"); // <2> + +Cancellable cancellable = restClient.performRequestAsync( + request, + new ResponseListener() { + @Override + public void onSuccess(Response response) { + // <3> + } + + @Override + public void onFailure(Exception exception) { + // <4> + } + }); +``` + +1. The HTTP method (`GET`, `POST`, `HEAD`, etc) +2. The endpoint on the server +3. Handle the response +4. Handle the failure + + +You can add request parameters to the request object: + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-parameters +```java +request.addParameter("pretty", "true"); +``` + +You can set the body of the request to any `HttpEntity`: + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-body +```java +request.setEntity(new StringEntity( + "{\"json\":\"text\"}", + ContentType.APPLICATION_JSON)); +``` + +::::{important} +The `ContentType` specified for the `HttpEntity` is important because it will be used to set the `Content-Type` header so that Elasticsearch can properly parse the content. +:::: + + +You can also set it to a `String` which will default to a `ContentType` of `application/json`. + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-body-shorter +```java +request.setJsonEntity("{\"json\":\"text\"}"); +``` + +## RequestOptions [java-rest-low-usage-request-options] + +The `RequestOptions` class holds parts of the request that should be shared between many requests in the same application. You can make a singleton instance and share it between all requests: + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-options-singleton +```java +private static final RequestOptions COMMON_OPTIONS; + +static { + RequestOptions.Builder builder = RequestOptions.DEFAULT + .toBuilder() + .addHeader("Authorization", "Bearer " + TOKEN) // <1> + .setHttpAsyncResponseConsumerFactory( // <2> + HttpAsyncResponseConsumerFactory.DEFAULT + ); + + COMMON_OPTIONS = builder.build(); +} +``` + +1. Add any headers needed by all requests. +2. Customize the response consumer. + + +`addHeader` is for headers that are required for authorization or to work with a proxy in front of Elasticsearch. There is no need to set the `Content-Type` header because the client will automatically set that from the `HttpEntity` attached to the request. + +You can set the `NodeSelector` which controls which nodes will receive requests. `NodeSelector.SKIP_DEDICATED_MASTERS` is a good choice. + +You can also customize the response consumer used to buffer the asynchronous responses. The default consumer will buffer up to 100MB of response on the JVM heap. If the response is larger then the request will fail. You could, for example, lower the maximum size which might be useful if you are running in a heap constrained environment like the example above. + +Once you’ve created the singleton you can use it when making requests: + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-options-set-singleton +```java +request.setOptions(COMMON_OPTIONS); +``` + +You can also customize these options on a per request basis. For example, this adds an extra header: + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-options-customize-header +```java +RequestOptions.Builder options = COMMON_OPTIONS.toBuilder(); +options.addHeader("cats", "knock things off of other things"); +request.setOptions(options); +``` + + +## Multiple parallel asynchronous actions + +The client is quite happy to execute many actions in parallel. The following example indexes many documents in parallel. In a real world scenario you’d probably want to use the `_bulk` API instead, but the example is illustrative. + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-async-example +```java +final CountDownLatch latch = new CountDownLatch(documents.length); +for (int i = 0; i < documents.length; i++) { + Request request = new Request("PUT", "/posts/doc/" + i); + //let's assume that the documents are stored in an HttpEntity array + request.setEntity(documents[i]); + restClient.performRequestAsync( + request, + new ResponseListener() { + @Override + public void onSuccess(Response response) { + // <1> + latch.countDown(); + } + + @Override + public void onFailure(Exception exception) { + // <2> + latch.countDown(); + } + } + ); +} +latch.await(); +``` + +1. Process the returned response +2. Handle the returned exception, due to communication error or a response with status code that indicates an error + + + +## Cancelling asynchronous requests + +The `performRequestAsync` method returns a `Cancellable` that exposes a single public method called `cancel`. Such method can be called to cancel the on-going request. Cancelling a request will result in aborting the http request through the underlying http client. On the server side, this does not automatically translate to the execution of that request being cancelled, which needs to be specifically implemented in the API itself. + +The use of the `Cancellable` instance is optional and you can safely ignore this if you don’t need it. A typical usecase for this would be using this together with frameworks like Rx Java or the Kotlin’s `suspendCancellableCoRoutine`. Cancelling no longer needed requests is a good way to avoid putting unnecessary load on Elasticsearch. + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-async-cancel +```java +Request request = new Request("GET", "/posts/_search"); +Cancellable cancellable = restClient.performRequestAsync( + request, + new ResponseListener() { + @Override + public void onSuccess(Response response) { + // <1> + } + + @Override + public void onFailure(Exception exception) { + // <2> + } + } +); +cancellable.cancel(); +``` + +1. Process the returned response, in case it was ready before the request got cancelled +2. Handle the returned exception, which will most likely be a `CancellationException` as the request got cancelled + + + diff --git a/docs/reference/transport/rest5-client/usage/responses.md b/docs/reference/transport/rest5-client/usage/responses.md new file mode 100644 index 000000000..566dc63b6 --- /dev/null +++ b/docs/reference/transport/rest5-client/usage/responses.md @@ -0,0 +1,43 @@ + +# Reading responses + +The `Response` object, either returned by the synchronous `performRequest` methods or received as an argument in `ResponseListener#onSuccess(Response)`, wraps the response object returned by the http client and exposes some additional information. + +% :::{include-code} src={{doc-tests-src}}/rest5_client/RestClientDocumentation.java tag=rest-client-response2 +```java +Response response = restClient.performRequest(new Request("GET", "/")); + +RequestLine requestLine = response.getRequestLine(); // <1> +HttpHost host = response.getHost(); // <2> +int statusCode = response.getStatusCode(); // <3> +Header[] headers = response.getHeaders(); // <4> +String responseBody = EntityUtils.toString(response.getEntity()); // <5> +``` + +1. Information about the performed request +2. The host that returned the response +3. The response status line, from which you can for instance retrieve the status code +4. The response headers, which can also be retrieved by name though `getHeader(String)` +5. The response body enclosed in an [`org.apache.http.HttpEntity`](https://hc.apache.org/httpcomponents-core-4.4.x/current/httpcore/apidocs/org/apache/http/HttpEntity.html) object + + +When performing a request, an exception is thrown (or received as an argument in `ResponseListener#onFailure(Exception)` in the following scenarios: + +`IOException` +: communication problem (e.g. SocketTimeoutException) + +`ResponseException` +: a response was returned, but its status code indicated an error (not `2xx`). A `ResponseException` originates from a valid http response, hence it exposes its corresponding `Response` object which gives access to the returned response. + +::::{note} +A `ResponseException` is **not** thrown for `HEAD` requests that return a `404` status code because it is an expected `HEAD` response that simply denotes that the resource is not found. All other HTTP methods (e.g., `GET`) throw a `ResponseException` for `404` responses unless the `ignore` parameter contains `404`. `ignore` is a special client parameter that doesn’t get sent to Elasticsearch and contains a comma separated list of error status codes. It allows to control whether some error status code should be treated as an expected response rather than as an exception. This is useful for instance with the get api as it can return `404` when the document is missing, in which case the response body will not contain an error but rather the usual get api response, just without the document as it was not found. +:::: + + +Note that the low-level client doesn’t expose any helper for json marshalling and un-marshalling. Users are free to use the library that they prefer for that purpose. + +The underlying Apache Async Http Client ships with different `HttpEntity` implementations that allow to provide the request body in different formats (stream, byte array, string etc.). As for reading the response body, the `HttpEntity#getContent` method comes handy which returns an `InputStream` reading from the previously buffered response body. As an alternative, it is possible to provide a custom `HttpAsyncResponseConsumer` that controls how bytes are read and buffered. + + diff --git a/docs/reference/troubleshooting/index.md b/docs/reference/troubleshooting/index.md new file mode 100644 index 000000000..9922f95d4 --- /dev/null +++ b/docs/reference/troubleshooting/index.md @@ -0,0 +1,16 @@ +--- +mapped_pages: + - https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/troubleshooting.html +--- + +# Troubleshooting + +## Exceptions + +* [](missing-required-property.md) +* [](no-such-method-request-options.md) +* [](io-reactor-errors.md) + +## Miscellaneous + +* [](serialize-without-typed-keys.md) diff --git a/docs/reference/troubleshooting/io-reactor-errors.md b/docs/reference/troubleshooting/io-reactor-errors.md new file mode 100644 index 000000000..9315cca17 --- /dev/null +++ b/docs/reference/troubleshooting/io-reactor-errors.md @@ -0,0 +1,37 @@ +--- +navigation_title: IOReactor errors +mapped_pages: + - https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/io-reactor-errors.html +--- + +# Apache http-client I/O reactor errors + +Sending requests can sometimes fail with one of the following errors, coming from the Apache http-client library: + +* `Request cannot be executed; I/O reactor status: STOPPED` +* `I/O reactor terminated abnormally` +* `I/O reactor has been shut down` + +The I/O Reactor is the internal event loop in the http client library. It can terminate when an application callback throws an `Error`, like an `OutOfMemoryError` or a `StackOverflowError`. Remember that `Error` is different from a regular `Exception` and – [quoting the Java documentation](https://docs.oracle.com/javase/8/docs/api/?java/lang/Error.md) – *indicates serious problems that a reasonable application should not try to catch*. + +In the context of the Elasticsearch Java clients, this can happen on two occasions: + +* the application calls the low level `RestClient` directly, using the asynchronous `performRequestAsync` method, and an `Error` is thrown in the `ResponseListener` provided by the application. +* an `OutOfMemoryError` happens while buffering the body of an http response. + +In the first case, it is the application’s responsibility to catch `Error` in its `ResponseListener` and decide what to do when these errors happen. + +The second case is taken care of in the Java API Client since version 8.12: the error is wrapped in a `RuntimeException` that is reported to the application. + +In previous versions of the Java API Client, you can copy/paste the `SafeResponseConsumer` class in your project and initialize the `RestClientTransport` as follows: + +```java +RestClient restClient = ... +JsonpMapper mapper = ... +RestClientOptions options = new RestClientOptions( + SafeResponseConsumer.DEFAULT_REQUEST_OPTIONS +); +RestClientTransport transport = new RestClientTransport( + restClient, mapper, options +); +``` diff --git a/docs/reference/troubleshooting/missing-required-property.md b/docs/reference/troubleshooting/missing-required-property.md new file mode 100644 index 000000000..b9f1070a9 --- /dev/null +++ b/docs/reference/troubleshooting/missing-required-property.md @@ -0,0 +1,43 @@ +--- +navigation_title: Missing required property +mapped_pages: +- https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/missing-required-property.html +--- + +# Missing required property exception in a response + +The Java API Client distinguishes optional and required properties. Optional properties are marked with the `@Nullable` annotation. + +When an API object is built and a required property hasn’t been set, a `MissingRequiredPropertyException` is thrown. This applies both to request object built by your application and to response objects returned by Elasticsearch, so that you can be assured that a property that does not have the `@Nullable` annotation will never be `null`. + +However, there may be bugs in the [Elasticsearch API specification](https://github.com/elastic/elasticsearch-specification) where a response object’s property is incorrectly required, leading to a `MissingRequiredPropertyException` when deserializing a response. If this happens, here’s how you can work around it: + +* Make sure you use the latest release of the Java API Client. The issue may already have been fixed. +* If the issue is still present on the latest version, [open an issue](https://github.com/elastic/elasticsearch-java/issues/new/choose) so that we can fix it in the next release. Please help us to improve the Java API Client. +* Temporarily disable required property checks for the offending request: + +::::{warning} +This is a workaround. Do not consider this as a permanent solution, and please [open an issue](https://github.com/elastic/elasticsearch-java/issues/new/choose) so that the problem can be fixed in a future release. +:::: + + +```java + ApiTypeHelper.DANGEROUS_disableRequiredPropertiesCheck(true); + SomeRequest request = SomeRequest.of(...); + SomeResponse response = esClient.someApi(request); + ApiTypeHelper.DANGEROUS_disableRequiredPropertiesCheck(false); + // Do something with response +``` + +The `DANGEROUS_disableRequiredPropertiesCheck` method disables required property checks on the current thread, and for response deserialization in asynchronous requests. As its name implies, it is dangerous as it removes the guarantees of properties that are not `@Nullable`. This is a temporary workaround until the issue is fixed. + +Note that the result of this method is an `AutoCloseable` object that resets required property checks to its previous setting. You can therefore use it in a try-with-resource block as follows: + +```java +try (ApiTypeHelper.DisabledChecksHandle h = + ApiTypeHelper.DANGEROUS_disableRequiredPropertiesCheck(true)) { + SomeRequest request = SomeRequest.of(...); + SomeResponse response = esClient.someApi(request); + // Do something with response +} +``` diff --git a/docs/reference/troubleshooting/no-such-method-request-options.md b/docs/reference/troubleshooting/no-such-method-request-options.md new file mode 100644 index 000000000..db80a52f1 --- /dev/null +++ b/docs/reference/troubleshooting/no-such-method-request-options.md @@ -0,0 +1,35 @@ +--- +navigation_title: "NoSuchMethodError: removeHeader" +mapped_pages: + - https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/no-such-method-request-options.html +--- + +# `NoSuchMethodError` when creating a client + +In certain contexts you may encounter an error when creating the `ElasticsearchClient` saying that the method `RequestOptions$Builder.removeHeader` does not exist: + +```java +java.lang.NoSuchMethodError: 'org.elasticsearch.client.RequestOptions$Builder org.elasticsearch.client.RequestOptions$Builder.removeHeader(java.lang.String)' +``` + +This method was introduced in `elasticsearch-rest-client` version 7.16.0. The error happens because your project is using an older version of this dependency. + +This happens in particular when the project is using the [Spring Boot Maven Plugin](https://docs.spring.io/spring-boot/docs/current/maven-plugin/reference/htmlsingle/), as this plugin [defines versions for commonly used libraries](https://github.com/spring-projects/spring-boot/blob/main/spring-boot-project/spring-boot-dependencies/build.gradle), including `elasticsearch-rest-client`. Depending on the version of Spring Boot used in the project, that version may be outdated. + +To solve this issue, you have to add the `elasticsearch-rest-client` dependency explicitly in your project, with the same version as `elasticsearch-java` (see also [Installation](/reference/setup/installation.md)). + +Using Gradle: + +```groovy subs=true +implementation 'org.elasticsearch.client:elasticsearch-rest-client:{{version}}' +``` + +Using Maven: + +```xml subs=true + + org.elasticsearch.client + elasticsearch-rest-client + {{version}} + +``` diff --git a/docs/reference/troubleshooting/serialize-without-typed-keys.md b/docs/reference/troubleshooting/serialize-without-typed-keys.md new file mode 100644 index 000000000..5542e846a --- /dev/null +++ b/docs/reference/troubleshooting/serialize-without-typed-keys.md @@ -0,0 +1,47 @@ +--- +navigation_title: Serializing without typed keys +mapped_pages: + - https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/serialize-without-typed-keys.html +--- + +# Serializing aggregations and suggestions without typed keys + +{{es}} search requests accept a `typed_key` parameter that allow returning type information along with the name in aggregation and suggestion results (see Elasticsearch's aggregation documentation for additional details). + +The Java API Client always adds this parameter to search requests, as type information is needed to know the concrete class that should be used to deserialize aggregation and suggestion results. + +Symmetrically, the Java API Client also serializes aggregation and suggestion results using this `typed_keys` format, so that it can correctly deserialize the results of its own serialization. + +% :::{include-code} src={{doc-tests-src}}/troubleshooting/TroubleShootingTests.java tag=aggregation-typed-keys +```java +JsonpMapper mapper = esClient._jsonpMapper(); + +StringWriter writer = new StringWriter(); +try (JsonGenerator generator = mapper.jsonProvider().createGenerator(writer)) { + mapper.serialize(searchResponse, generator); +} +String result = writer.toString(); + +// The aggregation property provides the "avg" type and "price" name +assertTrue(result.contains("\"aggregations\":{\"avg#price\":{\"value\":3.14}}}")); +``` + +However, in some use cases serializing objects in the `typed_keys` format may not be desirable, for example when the Java API Client is used in an application that acts as a front-end to other services that expect the default format for aggregations and suggestions. + +You can disable `typed_keys` serialization by setting the `JsonpMapperFeatures.SERIALIZE_TYPED_KEYS` attribute to `false` on your mapper object: + +% :::{include-code} src={{doc-tests-src}}/troubleshooting/TroubleShootingTests.java tag=aggregation-no-typed-keys +```java +// Create a new mapper with the typed_keys feature disabled +JsonpMapper mapper = esClient._jsonpMapper() + .withAttribute(JsonpMapperFeatures.SERIALIZE_TYPED_KEYS, false); + +StringWriter writer = new StringWriter(); +try (JsonGenerator generator = mapper.jsonProvider().createGenerator(writer)) { + mapper.serialize(searchResponse, generator); +} +String result = writer.toString(); + +// The aggregation only provides the "price" name +assertTrue(result.contains("\"aggregations\":{\"price\":{\"value\":3.14}}}")); +``` diff --git a/docs/reference/aggregations.md b/docs/reference/usage/aggregations.md similarity index 75% rename from docs/reference/aggregations.md rename to docs/reference/usage/aggregations.md index 3934f13ee..269463c1d 100644 --- a/docs/reference/aggregations.md +++ b/docs/reference/usage/aggregations.md @@ -8,19 +8,20 @@ mapped_pages: An aggregation summarizes your data as metrics, statistics, or other analytics. ::::{note} -See the [{{es}} documentation](elasticsearch://docs/reference/data-analysis/aggregations/index.md) for a full explanation of aggregations. +See the [{{es}} documentation](elasticsearch://reference/aggregations/index.md) for a full explanation of aggregations. :::: ## A simple aggregation [_a_simple_aggregation] -In the example below we run an aggregation that creates a price histogram from a product index, for the products whose name match a user-provided text. To achieve this, we use a search request that has a query (explained in [Searching for documents](/reference/searching.md)) and an aggregation definition. +In the example below we run an aggregation that creates a price histogram from a product index, for the products whose name match a user-provided text. To achieve this, we use a search request that has a query (explained in [Searching for documents](searching.md)) and an aggregation definition. This example is an analytics-type aggregation where we do not want to use the matching documents. A general pattern for search requests used for analytics is to set the result `size` to zero and the target class for search results to `Void`. If that same aggregation was used for to display products and the price histogram as drill-down facets, we would have set `size` to a non-zero value and used `Product` as the target class to process the results. +% :::{include-code} src={{doc-tests-src}}/usage/AggregationsTest.java tag=price-histo-request ```java String searchText = "bike"; @@ -31,15 +32,15 @@ Query query = MatchQuery.of(m -> m SearchResponse response = esClient.search(b -> b .index("products") - .size(0) <1> - .query(query) <2> - .aggregations("price-histogram", a -> a <3> - .histogram(h -> h <4> + .size(0) // <1> + .query(query) // <2> + .aggregations("price-histogram", a -> a // <3> + .histogram(h -> h // <4> .field("price") .interval(50.0) ) ), - Void.class <5> + Void.class // <5> ); ``` @@ -52,16 +53,18 @@ SearchResponse response = esClient.search(b -> b The response contains an aggregation result for each aggregation in the request. +% :::{include-code} src={{doc-tests-src}}/usage/AggregationsTest.java tag=price-histo-response ```java List buckets = response.aggregations() - .get("price-histogram") <1> - .histogram() <2> - .buckets().array(); <3> + .get("price-histogram") // <1> + .histogram() // <2> + .buckets().array(); // <3> for (HistogramBucket bucket: buckets) { logger.info("There are " + bucket.docCount() + " bikes under " + bucket.key()); } + ``` 1. Get the results for the "price-histogram" aggregation. @@ -69,5 +72,6 @@ for (HistogramBucket bucket: buckets) { 3. Buckets can be expressed as arrays or maps. This casts down to the array variant (the default). -The source code for the examples above can be found in the [Java API Client tests](https://github.com/elastic/elasticsearch-java/tree/master/java-client/src/test/java/co/elastic/clients/documentation). +:::{include} /reference/_snippets/doc-tests-blurb.md +::: diff --git a/docs/reference/esql.md b/docs/reference/usage/esql.md similarity index 100% rename from docs/reference/esql.md rename to docs/reference/usage/esql.md diff --git a/docs/reference/using-java-api-client.md b/docs/reference/usage/index.md similarity index 53% rename from docs/reference/using-java-api-client.md rename to docs/reference/usage/index.md index 2cf7e63ab..567d33ae5 100644 --- a/docs/reference/using-java-api-client.md +++ b/docs/reference/usage/index.md @@ -7,16 +7,16 @@ mapped_pages: The sections below provide tutorials on the most frequently used and some less obvious features of {{es}}. -For a full reference, see the [Elasticsearch documentation](docs-content://get-started/index.md) and in particular the [REST APIs](elasticsearch://docs/reference/elasticsearch/rest-apis/index.md) section. The Java API Client follows closely the JSON structures described there, using the [Java API conventions](/reference/api-conventions.md). +For a full reference, see the [Elasticsearch documentation](docs-content://get-started/index.md) and in particular the [REST APIs](elasticsearch://reference/elasticsearch/rest-apis/index.md) section. The Java API Client follows closely the JSON structures described there, using the [Java API conventions](/reference/api-conventions/index.md). If you’re new to Elasticsearch, make sure also to read [Elasticsearch’s quick start](docs-content://solutions/search/get-started.md) that provides a good introduction. -* [ES|QL in the Java client](/reference/esql.md) -* [Indexing single documents](/reference/indexing.md) -* [Bulk: indexing multiple documents](/reference/indexing-bulk.md) -* [Reading documents by id](/reference/reading.md) -* [Searching for documents](/reference/searching.md) -* [Aggregations](/reference/aggregations.md) +* [ES|QL in the Java client](/reference/usage/esql.md) +* [Indexing single documents](/reference/usage/indexing.md) +* [Bulk: indexing multiple documents](/reference/usage/indexing-bulk.md) +* [Reading documents by id](/reference/usage/reading.md) +* [Searching for documents](/reference/usage/searching.md) +* [Aggregations](/reference/usage/aggregations.md) ::::{note} This is still a work in progress, more sections will be added in the near future. diff --git a/docs/reference/indexing-bulk.md b/docs/reference/usage/indexing-bulk.md similarity index 84% rename from docs/reference/indexing-bulk.md rename to docs/reference/usage/indexing-bulk.md index 0b3eb882d..1b40cf227 100644 --- a/docs/reference/indexing-bulk.md +++ b/docs/reference/usage/indexing-bulk.md @@ -18,23 +18,22 @@ A bulk request can contain several kinds of operations: See the [{{es}} API documentation](https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-bulk) for a full explanation of bulk requests. :::: - - ## Indexing application objects [_indexing_application_objects] -A `BulkRequest` contains a collection of operations, each operation being a [type with several variants](/reference/variant-types.md). To create this request, it is convenient to use a builder object for the main request, and the fluent DSL for each operation. +A `BulkRequest` contains a collection of operations, each operation being a [type with several variants](/reference/api-conventions/variant-types.md). To create this request, it is convenient to use a builder object for the main request, and the fluent DSL for each operation. The example below shows how to index a list or application objects. +% :::{include-code} src={{doc-tests-src}}/usage/IndexingBulkTest.java tag=bulk-objects ```java List products = fetchProducts(); BulkRequest.Builder br = new BulkRequest.Builder(); for (Product product : products) { - br.operations(op -> op <1> - .index(idx -> idx <2> - .index("products") <3> + br.operations(op -> op // <1> + .index(idx -> idx // <2> + .index("products") // <3> .id(product.getSku()) .document(product) ) @@ -54,11 +53,9 @@ if (result.errors()) { } ``` -1. Adds an operation (remember that [list properties are additive](/reference/lists-maps.md)). `op` is is a builder for `BulkOperation` which is a [variant type](/reference/variant-types.md). This type has `index`, `create`, `update` and `delete` variants. +1. Adds an operation (remember that [list properties are additive](/reference/api-conventions/lists-maps.md)). `op` is is a builder for `BulkOperation` which is a [variant type](/reference/api-conventions/variant-types.md). This type has `index`, `create`, `update` and `delete` variants. 2. Selects the `index` operation variant, `idx` is a builder for `IndexOperation`. -3. Sets the properties for the index operation, similar to [single document indexing](/reference/indexing.md): index name, identifier and document. - - +3. Sets the properties for the index operation, similar to [single document indexing](indexing.md): index name, identifier and document. ## Indexing raw JSON data [indexing-raw-json-data] @@ -66,6 +63,7 @@ The `document` property of a bulk index request can be any object that can be se In the example below we will use the Java API Client’s `BinaryData` to read json files from a log directory and send them in a bulk request. +% :::{include-code} src={{doc-tests-src}}/usage/IndexingBulkTest.java tag=bulk-json ```java // List json log files in the log directory File[] logFiles = logDir.listFiles( @@ -87,7 +85,6 @@ for (File file: logFiles) { } ``` - ## Streaming ingestion with the Bulk Ingester [_streaming_ingestion_with_the_bulk_ingester] The `BulkIngester` simplifies the usage of the Bulk API by providing a utility class that allows index/update/delete operations to be transparently grouped in bulk requests. You only have to `add()` bulk operations to the ingester and it will take care of grouping and sending them in bulk according to its configuration. @@ -100,18 +97,19 @@ The ingester will send a bulk request when one of the following criteria is met: Additionally, you can define a maximum number of concurrent request waiting to be executed by {{es}} (defaults to 1). When that maximum is reached and the maximum number of operations have been collected, adding a new operation to the indexer will block. This is avoids overloading the {{es}} server by putting backpressure on the client application. +% :::{include-code} src={{doc-tests-src}}/usage/IndexingBulkTest.java tag=bulk-ingester-setup ```java BulkIngester ingester = BulkIngester.of(b -> b - .client(esClient) <1> - .maxOperations(100) <2> - .flushInterval(1, TimeUnit.SECONDS) <3> + .client(esClient) // <1> + .maxOperations(100) // <2> + .flushInterval(1, TimeUnit.SECONDS) // <3> ); for (File file: logFiles) { FileInputStream input = new FileInputStream(file); BinaryData data = BinaryData.of(IOUtils.toByteArray(input), ContentType.APPLICATION_JSON); - ingester.add(op -> op <4> + ingester.add(op -> op // <4> .index(idx -> idx .index("logs") .document(data) @@ -119,7 +117,7 @@ for (File file: logFiles) { ); } -ingester.close(); <5> +ingester.close(); // <5> ``` 1. Sets the {{es}} client used to send bulk requests. @@ -133,8 +131,9 @@ Additionally, the bulk ingester accepts a listener so that your application can The following example shows how you can use context values to implement a bulk ingestion listener: as previously it sends JSON log files in bulk, but tracks bulk request errors and failed operations. When an operation fails, depending on the error type you may want to re-add it to the ingester. +% :::{include-code} src={{doc-tests-src}}/usage/IndexingBulkTest.java tag=bulk-ingester-context ```java -BulkListener listener = new BulkListener() { <1> +BulkListener listener = new BulkListener() { // <1> @Override public void beforeBulk(long executionId, BulkRequest request, List contexts) { } @@ -164,7 +163,7 @@ BulkIngester ingester = BulkIngester.of(b -> b .client(esClient) .maxOperations(100) .flushInterval(1, TimeUnit.SECONDS) - .listener(listener) <2> + .listener(listener) // <2> ); for (File file: logFiles) { @@ -176,7 +175,7 @@ for (File file: logFiles) { .index("logs") .document(data) ), - file.getName() <3> + file.getName() // <3> ); } @@ -195,5 +194,6 @@ The bulk ingest also exposes statistic information that allows monitoring the in * number of bulk requests sent, * number of bulk requests that were blocked because the maximum number of concurrent requests was reached. -The source code for the examples above can be found in the [Java API Client tests](https://github.com/elastic/elasticsearch-java/tree/master/java-client/src/test/java/co/elastic/clients/documentation). +:::{include} /reference/_snippets/doc-tests-blurb.md +::: diff --git a/docs/reference/indexing.md b/docs/reference/usage/indexing.md similarity index 85% rename from docs/reference/indexing.md rename to docs/reference/usage/indexing.md index f95d91fac..3a4b114c6 100644 --- a/docs/reference/indexing.md +++ b/docs/reference/usage/indexing.md @@ -19,6 +19,7 @@ See the [{{es}} API documentation](https://www.elastic.co/docs/api/doc/elasticse The most direct way to build requests is using the fluent DSL. In the example below we index a product description in the `products` index, using the product’s SKU as the document identifier in the index. The `product` object will be mapped to JSON using the object mapper configured on the {{es}} client. +% :::{include-code} src={{doc-tests-src}}/usage/IndexingTest.java tag=single-doc-dsl ```java Product product = new Product("bk-1", "City bike", 123.0); @@ -33,6 +34,7 @@ logger.info("Indexed with version " + response.version()); You can also assign objects created with the DSL to variables. Java API Client classes have a static `of()` method for this, that creates an object with the DSL syntax. +% :::{include-code} src={{doc-tests-src}}/usage/IndexingTest.java tag=single-doc-dsl-of ```java Product product = new Product("bk-1", "City bike", 123.0); @@ -47,11 +49,11 @@ IndexResponse response = esClient.index(request); logger.info("Indexed with version " + response.version()); ``` - ## Using classic builders [_using_classic_builders] If you’re more used to the classic builder pattern, it is also available. Builder objects are used under the hood by the fluent DSL syntax. +% :::{include-code} src={{doc-tests-src}}/usage/IndexingTest.java tag=single-doc-builder ```java Product product = new Product("bk-1", "City bike", 123.0); @@ -65,11 +67,11 @@ IndexResponse response = esClient.index(indexReqBuilder.build()); logger.info("Indexed with version " + response.version()); ``` - ## Using the asynchronous client [_using_the_asynchronous_client] -The examples above used the synchronous {{es}} client. All {{es}} APIs are also available in the asynchronous client, using the same request and response types. See also [Blocking and asynchronous clients](/reference/blocking-async.md) for additional details. +The examples above used the synchronous {{es}} client. All {{es}} APIs are also available in the asynchronous client, using the same request and response types. See also [Blocking and asynchronous clients](/reference/api-conventions/blocking-async.md) for additional details. +% :::{include-code} src={{doc-tests-src}}/usage/IndexingTest.java tag=single-doc-dsl-async ```java ElasticsearchAsyncClient esAsyncClient = new ElasticsearchAsyncClient(transport); @@ -88,13 +90,13 @@ esAsyncClient.index(i -> i }); ``` - ## Using raw JSON data [_using_raw_json_data] When the data you want to index comes from external sources, having to create domain objects may be cumbersome or outright impossible with semi-structured data. -You can index data from an arbitrary source using `withJson()`. Using this method will read the source and use it for the index request’s `document` property. See [Creating API objects from JSON data](/reference/loading-json.md) for additional details. +You can index data from an arbitrary source using `withJson()`. Using this method will read the source and use it for the index request’s `document` property. See [Creating API objects from JSON data](/reference/api-conventions/loading-json.md) for additional details. +% :::{include-code} src={{doc-tests-src}}/usage/IndexingTest.java tag=single-doc-json ```java Reader input = new StringReader( "{'@timestamp': '2022-04-08T13:55:32Z', 'level': 'warn', 'message': 'Some log message'}" @@ -110,5 +112,6 @@ IndexResponse response = esClient.index(request); logger.info("Indexed with version " + response.version()); ``` -The source code for the examples above can be found in the [Java API Client tests](https://github.com/elastic/elasticsearch-java/tree/master/java-client/src/test/java/co/elastic/clients/documentation). +:::{include} /reference/_snippets/doc-tests-blurb.md +::: diff --git a/docs/reference/reading.md b/docs/reference/usage/reading.md similarity index 86% rename from docs/reference/reading.md rename to docs/reference/usage/reading.md index e6f27305b..1d1311899 100644 --- a/docs/reference/reading.md +++ b/docs/reference/usage/reading.md @@ -22,11 +22,12 @@ The `get` request has two parameters: * the first parameter is the actual request, built below with the fluent DSL * the second parameter is the class we want the document’s JSON to be mapped to. +% :::{include-code} src={{doc-tests-src}}/usage/ReadingTest.java tag=get-by-id ```java GetResponse response = esClient.get(g -> g - .index("products") <1> + .index("products") // <1> .id("bk-1"), - Product.class <2> + Product.class // <2> ); if (response.found()) { @@ -48,11 +49,12 @@ When your index contains semi-structured data or if you don’t have a domain ob Raw JSON data is just another class that you can use as the result type for the get request. In the example below we use Jackson’s `ObjectNode`. We could also have used any JSON representation that can be deserialized by the JSON mapper associated to the `ElasticsearchClient`. +% :::{include-code} src={{doc-tests-src}}/usage/ReadingTest.java tag=get-by-id-json ```java GetResponse response = esClient.get(g -> g .index("products") .id("bk-1"), - ObjectNode.class <1> + ObjectNode.class // <1> ); if (response.found()) { @@ -67,5 +69,6 @@ if (response.found()) { 1. The target class is a raw JSON object. -The source code for the examples above can be found in the [Java API Client tests](https://github.com/elastic/elasticsearch-java/tree/master/java-client/src/test/java/co/elastic/clients/documentation). +:::{include} /reference/_snippets/doc-tests-blurb.md +::: diff --git a/docs/reference/searching.md b/docs/reference/usage/searching.md similarity index 77% rename from docs/reference/searching.md rename to docs/reference/usage/searching.md index f2eb732d1..8724c6b51 100644 --- a/docs/reference/searching.md +++ b/docs/reference/usage/searching.md @@ -23,18 +23,20 @@ The total value comes with a relation that indicates if the total is exact (`eq` Each returned document comes with its relevance score and additional information about its location in the index. +% :::{include-code} src={{doc-tests-src}}/usage/SearchingTest.java tag=search-simple ```java + String searchText = "bike"; SearchResponse response = esClient.search(s -> s - .index("products") <1> - .query(q -> q <2> - .match(t -> t <3> - .field("name") <4> + .index("products") // <1> + .query(q -> q // <2> + .match(t -> t // <3> + .field("name") // <4> .query(searchText) ) ), - Product.class <5> + Product.class // <5> ); TotalHits total = response.hits().total(); @@ -54,42 +56,43 @@ for (Hit hit: hits) { ``` 1. Name of the index we want to search. -2. The query part of the search request (a search request can also have other components like [aggregations](/reference/aggregations.md)). +2. The query part of the search request (a search request can also have other components like [aggregations](aggregations.md)). 3. Choose a query variant among the many available. We choose here the match query (full text search). 4. Configure the match query: we search for a term in the `name` field. -5. The target class for the matching documents. We use `Product` here, just like in [get request](/reference/reading.md) examples. +5. The target class for the matching documents. We use `Product` here, just like in [get request](reading.md) examples. -Similarly to [get](/reference/reading.md) operations, you can fetch documents matching your query as raw JSON by using a corresponding target class instead of `Product`, like JSON-P’s `JsonValue` or Jackson’s `ObjectNode`. +Similarly to [get](reading.md) operations, you can fetch documents matching your query as raw JSON by using a corresponding target class instead of `Product`, like JSON-P’s `JsonValue` or Jackson’s `ObjectNode`. ## Nested search queries [_nested_search_queries] {{es}} allows individual queries to be combined to build more complex search requests. In the example below we will search for bikes with a maximum price of 200. +% :::{include-code} src={{doc-tests-src}}/usage/SearchingTest.java tag=search-nested ```java String searchText = "bike"; double maxPrice = 200.0; // Search by product name -Query byName = MatchQuery.of(m -> m <1> +Query byName = MatchQuery.of(m -> m // <1> .field("name") .query(searchText) -)._toQuery(); <2> +)._toQuery(); // <2> // Search by max price Query byMaxPrice = RangeQuery.of(r -> r .number(n -> n .field("price") - .gte(maxPrice)) <3> + .gte(maxPrice)) // <3> )._toQuery(); // Combine name and price queries to search the product index SearchResponse response = esClient.search(s -> s .index("products") .query(q -> q - .bool(b -> b <4> - .must(byName) <5> + .bool(b -> b // <4> + .must(byName) // <5> .must(byMaxPrice) ) ), @@ -104,7 +107,7 @@ for (Hit hit: hits) { ``` 1. We’re creating the queries for individual criteria separately. -2. A `MatchQuery` is a query *variant* that we have to turn into the `Query` *union type*. See [variant types](/reference/variant-types.md) for additional details. +2. A `MatchQuery` is a query *variant* that we have to turn into the `Query` *union type*. See [variant types](/reference/api-conventions/variant-types.md) for additional details. 3. {{es}} range query accepts a large range of value types. We create here a JSON representation of the maximum price. 4. The search query is a boolean query that combines the text search and max price queries. 5. Both queries are added as `must` as we want results to match all criteria. @@ -117,13 +120,14 @@ A search template is a stored search that you can run with different variables. Before running a template search, you first have to create the template. This is a stored script that returns the search request body, and is usually defined as a Mustache template. This stored script can be created outside the application, and also with the Java API Client: +% :::{include-code} src={{doc-tests-src}}/usage/SearchingTest.java tag=search-template-script ```java // Create a script esClient.putScript(r -> r - .id("query-script") <1> + .id("query-script") // <1> .script(s -> s .lang("mustache") - .source("{\"query\":{\"match\":{\"{{field}}\":\"{{value}}\"}}}") + .source(so -> so.scriptString("{\"query\":{\"match\":{\"{{field}}\":\"{{value}}\"}}}")) )); ``` @@ -132,11 +136,12 @@ esClient.putScript(r -> r To use the search template, use the `searchTemplate` method to refer to the script and provide values for its parameters: +% :::{include-code} src={{doc-tests-src}}/usage/SearchingTest.java tag=search-template-query ```java SearchTemplateResponse response = esClient.searchTemplate(r -> r .index("some-index") - .id("query-script") <1> - .params("field", JsonData.of("some-field")) <2> + .id("query-script") // <1> + .params("field", JsonData.of("some-field")) // <2> .params("value", JsonData.of("some-data")), Product.class ); @@ -154,5 +159,6 @@ for (Hit hit: hits) { For more in-depth information, see the [{{es}} search template documentation](docs-content://solutions/search/search-templates.md). -The source code for the examples above can be found in the [Java API Client tests](https://github.com/elastic/elasticsearch-java/tree/master/java-client/src/test/java/co/elastic/clients/documentation). +:::{include} /reference/_snippets/doc-tests-blurb.md +::: diff --git a/java-client/src/test/java/co/elastic/clients/documentation/api_conventions/ApiConventionsTest.java b/java-client/src/test/java/co/elastic/clients/documentation/api_conventions/ApiConventionsTest.java index 17ac641c6..bc2d5e2d5 100644 --- a/java-client/src/test/java/co/elastic/clients/documentation/api_conventions/ApiConventionsTest.java +++ b/java-client/src/test/java/co/elastic/clients/documentation/api_conventions/ApiConventionsTest.java @@ -35,8 +35,6 @@ import co.elastic.clients.util.ApiTypeHelper; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.PropertyNamingStrategies; import jakarta.json.JsonArray; import jakarta.json.JsonObject; import jakarta.json.JsonValue; @@ -62,9 +60,9 @@ public void blockingAndAsync() throws Exception { //tag::blocking-and-async // Synchronous blocking client - ElasticsearchClient client = new ElasticsearchClient(transport); + ElasticsearchClient esClient = new ElasticsearchClient(transport); - if (client.exists(b -> b.index("products").id("foo")).value()) { + if (esClient.exists(b -> b.index("products").id("foo")).value()) { logger.info("product exists"); } @@ -85,11 +83,15 @@ public void blockingAndAsync() throws Exception { } + private ElasticsearchClient createClient() { + return new ElasticsearchClient(transport); + } + public void builders() throws Exception { - ElasticsearchClient client = new ElasticsearchClient(transport); //tag::builders - CreateIndexResponse createResponse = client.indices().create( + ElasticsearchClient esClient = createClient(); + CreateIndexResponse createResponse = esClient.indices().create( new CreateIndexRequest.Builder() .index("my-index") .aliases("foo", @@ -101,10 +103,10 @@ public void builders() throws Exception { } public void builderLambdas() throws Exception { - ElasticsearchClient client = new ElasticsearchClient(transport); //tag::builder-lambdas - CreateIndexResponse createResponse = client.indices() + ElasticsearchClient esClient = createClient(); + CreateIndexResponse createResponse = esClient.indices() .create(createIndexBuilder -> createIndexBuilder .index("my-index") .aliases("foo", aliasBuilder -> aliasBuilder @@ -115,10 +117,10 @@ public void builderLambdas() throws Exception { } public void builderLambdasShort() throws Exception { - ElasticsearchClient client = new ElasticsearchClient(transport); //tag::builder-lambdas-short - CreateIndexResponse createResponse = client.indices() + ElasticsearchClient esClient = createClient(); + CreateIndexResponse createResponse = esClient.indices() .create(c -> c .index("my-index") .aliases("foo", a -> a @@ -129,12 +131,9 @@ public void builderLambdasShort() throws Exception { } public void builderIntervals() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); - ElasticsearchClient client = new ElasticsearchClient(transport); - //tag::builder-intervals - SearchResponse results = client + ElasticsearchClient esClient = createClient(); + SearchResponse results = esClient .search(b0 -> b0 .query(b1 -> b1 .intervals(b2 -> b2 diff --git a/java-client/src/test/java/co/elastic/clients/documentation/api_conventions/LoadingJsonTest.java b/java-client/src/test/java/co/elastic/clients/documentation/api_conventions/LoadingJsonTest.java index f0a579ea6..af61cff32 100644 --- a/java-client/src/test/java/co/elastic/clients/documentation/api_conventions/LoadingJsonTest.java +++ b/java-client/src/test/java/co/elastic/clients/documentation/api_conventions/LoadingJsonTest.java @@ -47,7 +47,7 @@ public class LoadingJsonTest extends ModelTestCase { private final DocTestsTransport transport = new DocTestsTransport(); - private final ElasticsearchClient client = new ElasticsearchClient(transport); + private final ElasticsearchClient esClient = new ElasticsearchClient(transport); private static final SearchResponse searchResponse = SearchResponse.of(b -> b .aggregations(new HashMap<>()) @@ -75,34 +75,34 @@ public void loadIndexDefinition() throws IOException { //tag::load-index InputStream input = this.getClass() - .getResourceAsStream("some-index.json"); //<1> + .getResourceAsStream("some-index.json"); // <1> CreateIndexRequest req = CreateIndexRequest.of(b -> b .index("some-index") - .withJson(input) //<2> + .withJson(input) // <2> ); - boolean created = client.indices().create(req).acknowledged(); + boolean created = esClient.indices().create(req).acknowledged(); //end::load-index } @Disabled @Test public void ingestDocument() throws IOException { - + File dataDir = null; //tag::ingest-data FileReader file = new FileReader(new File(dataDir, "document-1.json")); - IndexRequest req; //<1> + IndexRequest req; // <1> req = IndexRequest.of(b -> b .index("some-index") .withJson(file) ); - client.index(req); + esClient.index(req); //end::ingest-data } @@ -123,8 +123,8 @@ public void query1() throws IOException { "}"); SearchRequest aggRequest = SearchRequest.of(b -> b - .withJson(queryJson) //<1> - .aggregations("max-cpu", a1 -> a1 //<2> + .withJson(queryJson) // <1> + .aggregations("max-cpu", a1 -> a1 // <2> .dateHistogram(h -> h .field("@timestamp") .calendarInterval(CalendarInterval.Hour) @@ -136,8 +136,8 @@ public void query1() throws IOException { .size(0) ); - Map aggs = client - .search(aggRequest, Void.class) //<3> + Map aggs = esClient + .search(aggRequest, Void.class) // <3> .aggregations(); //end::query } @@ -156,12 +156,12 @@ public void query2() throws IOException { " }" + " }" + " }," + - " \"size\": 100" + //<1> + " \"size\": 100" + // <1> "}"); Reader aggregationJson = new StringReader( "{" + - " \"size\": 0, " + //<2> + " \"size\": 0, " + // <2> " \"aggregations\": {" + " \"hours\": {" + " \"date_histogram\": {" + @@ -180,12 +180,12 @@ public void query2() throws IOException { "}"); SearchRequest aggRequest = SearchRequest.of(b -> b - .withJson(queryJson) //<3> - .withJson(aggregationJson) //<4> - .ignoreUnavailable(true) //<5> + .withJson(queryJson) // <3> + .withJson(aggregationJson) // <4> + .ignoreUnavailable(true) // <5> ); - Map aggs = client + Map aggs = esClient .search(aggRequest, Void.class) .aggregations(); //end::query-and-agg diff --git a/java-client/src/test/java/co/elastic/clients/documentation/getting_started/ConnectingTest.java b/java-client/src/test/java/co/elastic/clients/documentation/getting_started/ConnectingTest.java index b8fef1ecf..1d779b30c 100644 --- a/java-client/src/test/java/co/elastic/clients/documentation/getting_started/ConnectingTest.java +++ b/java-client/src/test/java/co/elastic/clients/documentation/getting_started/ConnectingTest.java @@ -23,7 +23,6 @@ import co.elastic.clients.elasticsearch.ElasticsearchClient; import co.elastic.clients.elasticsearch.core.SearchResponse; import co.elastic.clients.elasticsearch.core.search.Hit; -import co.elastic.clients.json.jackson.JacksonJsonpMapper; import co.elastic.clients.transport.TransportUtils; import co.elastic.clients.transport.instrumentation.OpenTelemetryForElasticsearch; import io.opentelemetry.api.OpenTelemetry; @@ -47,8 +46,6 @@ public void createClient() throws Exception { ElasticsearchClient esClient = ElasticsearchClient.of(b -> b .host(serverUrl) .apiKey(apiKey) - // Use the Jackson mapper to deserialize JSON to application objects - .jsonMapper(new JacksonJsonpMapper()) ); // Use the client... diff --git a/java-client/src/test/java/co/elastic/clients/transport/rest5_client/low_level/documentation/RestClientDocumentation.java b/java-client/src/test/java/co/elastic/clients/documentation/rest5_client/RestClientDocumentation.java similarity index 80% rename from java-client/src/test/java/co/elastic/clients/transport/rest5_client/low_level/documentation/RestClientDocumentation.java rename to java-client/src/test/java/co/elastic/clients/documentation/rest5_client/RestClientDocumentation.java index e9c030f7e..fb8756033 100644 --- a/java-client/src/test/java/co/elastic/clients/transport/rest5_client/low_level/documentation/RestClientDocumentation.java +++ b/java-client/src/test/java/co/elastic/clients/documentation/rest5_client/RestClientDocumentation.java @@ -17,7 +17,7 @@ * under the License. */ -package co.elastic.clients.transport.rest5_client.low_level.documentation; +package co.elastic.clients.documentation.rest5_client; import co.elastic.clients.transport.rest5_client.low_level.Cancellable; import co.elastic.clients.transport.rest5_client.low_level.HttpAsyncResponseConsumerFactory; @@ -44,13 +44,13 @@ import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.reactor.IOReactorConfig; -import org.apache.hc.core5.ssl.SSLContextBuilder; import org.apache.hc.core5.ssl.SSLContexts; import org.apache.hc.core5.util.Timeout; import javax.net.ssl.SSLContext; import java.io.IOException; import java.io.InputStream; +import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -85,25 +85,29 @@ public class RestClientDocumentation { private static final String TOKEN = "DUMMY"; - // tag::rest-client-options-singleton + //tag::rest-client-options-singleton private static final RequestOptions COMMON_OPTIONS; static { - RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder(); - builder.addHeader("Authorization", "Bearer " + TOKEN); // <1> - builder.setHttpAsyncResponseConsumerFactory( // <2> - HttpAsyncResponseConsumerFactory.DEFAULT); + RequestOptions.Builder builder = RequestOptions.DEFAULT + .toBuilder() + .addHeader("Authorization", "Bearer " + TOKEN) // <1> + .setHttpAsyncResponseConsumerFactory( // <2> + HttpAsyncResponseConsumerFactory.DEFAULT + ); + COMMON_OPTIONS = builder.build(); } - // end::rest-client-options-singleton + //end::rest-client-options-singleton @SuppressWarnings("unused") public void usage() throws IOException, InterruptedException, ParseException { //tag::rest-client-init Rest5Client restClient = Rest5Client.builder( - new HttpHost("http", "localhost", 9200), - new HttpHost("http", "localhost", 9201)).build(); + URI.create("http://localhost:9200"), + URI.create("http://localhost:9201") + ).build(); //end::rest-client-init //tag::rest-client-close @@ -112,24 +116,26 @@ public void usage() throws IOException, InterruptedException, ParseException { { //tag::rest-client-init-default-headers - Rest5ClientBuilder builder = Rest5Client.builder( - new HttpHost("http", "localhost", 9200)); + Rest5ClientBuilder builder = Rest5Client + .builder(new HttpHost("http", "localhost", 9200)); + Header[] defaultHeaders = new Header[]{new BasicHeader("header", "value")}; builder.setDefaultHeaders(defaultHeaders); // <1> //end::rest-client-init-default-headers } { //tag::rest-client-init-node-selector - Rest5ClientBuilder builder = Rest5Client.builder( - new HttpHost("http", "localhost", 9200)); + Rest5ClientBuilder builder = Rest5Client + .builder(new HttpHost("http", "localhost", 9200)); + builder.setNodeSelector(NodeSelector.SKIP_DEDICATED_MASTERS); // <1> //end::rest-client-init-node-selector } { //tag::rest-client-init-allocation-aware-selector - Rest5ClientBuilder builder = Rest5Client.builder( - new HttpHost("http", "localhost", 9200)); - // <1> + Rest5ClientBuilder builder = Rest5Client + .builder(new HttpHost("http", "localhost", 9200)); + builder.setNodeSelector(nodes -> { // <1> /* * Prefer any node that belongs to rack_one. If none is around @@ -149,7 +155,7 @@ public void usage() throws IOException, InterruptedException, ParseException { while (nodesIt.hasNext()) { Node node = nodesIt.next(); String rackId = node.getAttributes().get("rack_id").get(0); - if ("rack_one".equals(rackId) == false) { + if (!"rack_one".equals(rackId)) { nodesIt.remove(); } } @@ -159,8 +165,9 @@ public void usage() throws IOException, InterruptedException, ParseException { } { //tag::rest-client-init-failure-listener - Rest5ClientBuilder builder = Rest5Client.builder( - new HttpHost("http", "localhost", 9200)); + Rest5ClientBuilder builder = Rest5Client + .builder(new HttpHost("http", "localhost", 9200)); + builder.setFailureListener(new Rest5Client.FailureListener() { @Override public void onFailure(Node node) { @@ -179,9 +186,10 @@ public void onFailure(Node node) { CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom() .setConnectionManager(connectionManager) .build(); - Rest5ClientBuilder builder = Rest5Client.builder( - new HttpHost("http", "localhost", 9200)); - builder.setHttpClient(httpclient); + + Rest5ClientBuilder builder = Rest5Client + .builder(new HttpHost("http", "localhost", 9200)) + .setHttpClient(httpclient); //end::rest-client-init-request-config-callback } @@ -190,6 +198,7 @@ public void onFailure(Node node) { Request request = new Request( "GET", // <1> "/"); // <2> + Response response = restClient.performRequest(request); //end::rest-client-sync } @@ -198,7 +207,9 @@ public void onFailure(Node node) { Request request = new Request( "GET", // <1> "/"); // <2> - Cancellable cancellable = restClient.performRequestAsync(request, + + Cancellable cancellable = restClient.performRequestAsync( + request, new ResponseListener() { @Override public void onSuccess(Response response) { @@ -214,17 +225,21 @@ public void onFailure(Exception exception) { } { Request request = new Request("GET", "/"); + //tag::rest-client-parameters request.addParameter("pretty", "true"); //end::rest-client-parameters + //tag::rest-client-body request.setEntity(new StringEntity( "{\"json\":\"text\"}", ContentType.APPLICATION_JSON)); //end::rest-client-body + //tag::rest-client-body-shorter request.setJsonEntity("{\"json\":\"text\"}"); //end::rest-client-body-shorter + //tag::rest-client-options-set-singleton request.setOptions(COMMON_OPTIONS); //end::rest-client-options-set-singleton @@ -287,6 +302,7 @@ public void onFailure(Exception exception) { { //tag::rest-client-response2 Response response = restClient.performRequest(new Request("GET", "/")); + RequestLine requestLine = response.getRequestLine(); // <1> HttpHost host = response.getHost(); // <2> int statusCode = response.getStatusCode(); // <3> @@ -307,8 +323,8 @@ public void commonConfiguration() throws Exception { .setDefaultRequestConfig(requestConfigBuilder.build()) .build(); - Rest5ClientBuilder builder = Rest5Client.builder( - new HttpHost("localhost", 9200)) + Rest5ClientBuilder builder = Rest5Client + .builder(new HttpHost("localhost", 9200)) .setHttpClient(httpclient); //end::rest-client-config-timeouts } @@ -318,6 +334,7 @@ public void commonConfiguration() throws Exception { .setConnectTimeout(Timeout.ofMilliseconds(5000)) .setConnectionRequestTimeout(Timeout.ofMilliseconds(60000)) .build(); + RequestOptions options = RequestOptions.DEFAULT.toBuilder() .setRequestConfig(requestConfig) .build(); @@ -326,22 +343,23 @@ public void commonConfiguration() throws Exception { { //tag::rest-client-config-threads CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom() - .setIOReactorConfig(IOReactorConfig.custom() - .setIoThreadCount(1).build()) + .setIOReactorConfig( + IOReactorConfig.custom().setIoThreadCount(1).build() + ) .build(); - Rest5ClientBuilder builder = Rest5Client.builder( - new HttpHost("localhost", 9200)) + Rest5ClientBuilder builder = Rest5Client + .builder(new HttpHost("localhost", 9200)) .setHttpClient(httpclient); //end::rest-client-config-threads } { //tag::rest-client-config-basic-auth + var creds = Base64.getEncoder() + .encodeToString("user:test-user-password".getBytes()); - var creds = Base64.getEncoder().encodeToString("user:test-user-password".getBytes()); - - Rest5ClientBuilder restClient = Rest5Client.builder(new HttpHost("https", "localhost", - 9200)) + Rest5ClientBuilder restClient = Rest5Client + .builder(new HttpHost("https", "localhost", 9200)) .setDefaultHeaders(new Header[]{ new BasicHeader("Authorization", "Basic " + creds) }); @@ -355,11 +373,11 @@ public void commonConfiguration() throws Exception { var creds = Base64.getEncoder().encodeToString("user:test-user-password".getBytes()); CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom() - .disableAuthCaching() + .disableAuthCaching() // <1> .build(); - Rest5ClientBuilder restClient = Rest5Client.builder(new HttpHost("https", "localhost", - 9200)) + Rest5ClientBuilder restClient = Rest5Client + .builder(new HttpHost("https", "localhost", 9200)) .setHttpClient(httpclient) .setDefaultHeaders(new Header[]{ new BasicHeader("Authorization", "Basic " + creds) @@ -374,33 +392,35 @@ public void commonConfiguration() throws Exception { try (InputStream is = Files.newInputStream(trustStorePath)) { truststore.load(is, keyStorePass.toCharArray()); } - SSLContextBuilder sslBuilder = SSLContexts.custom() - .loadTrustMaterial(truststore, null); - final SSLContext sslContext = sslBuilder.build(); - Rest5ClientBuilder builder = Rest5Client.builder( - new HttpHost("https", "localhost", - 9200)) + + SSLContext sslContext = SSLContexts.custom() + .loadTrustMaterial(truststore, null) + .build(); + + Rest5ClientBuilder builder = Rest5Client + .builder(new HttpHost("https", "localhost", 9200)) .setSSLContext(sslContext); //end::rest-client-config-encrypted-communication } { //tag::rest-client-config-trust-ca-pem Path caCertificatePath = Paths.get("/path/to/ca.crt"); - CertificateFactory factory = - CertificateFactory.getInstance("X.509"); + CertificateFactory factory = CertificateFactory.getInstance("X.509"); Certificate trustedCa; try (InputStream is = Files.newInputStream(caCertificatePath)) { trustedCa = factory.generateCertificate(is); } + KeyStore trustStore = KeyStore.getInstance("pkcs12"); trustStore.load(null, null); trustStore.setCertificateEntry("ca", trustedCa); - SSLContextBuilder sslContextBuilder = SSLContexts.custom() - .loadTrustMaterial(trustStore, null); - final SSLContext sslContext = sslContextBuilder.build(); - Rest5Client.builder( - new HttpHost("https", "localhost", - 9200)) + + SSLContext sslContext = SSLContexts.custom() + .loadTrustMaterial(trustStore, null) + .build(); + + Rest5Client + .builder(new HttpHost("https", "localhost", 9200)) .setSSLContext(sslContext); //end::rest-client-config-trust-ca-pem } @@ -412,30 +432,35 @@ public void commonConfiguration() throws Exception { Path keyStorePath = Paths.get("/path/to/your/keystore.p12"); KeyStore trustStore = KeyStore.getInstance("pkcs12"); KeyStore keyStore = KeyStore.getInstance("pkcs12"); + try (InputStream is = Files.newInputStream(trustStorePath)) { trustStore.load(is, trustStorePass.toCharArray()); } + try (InputStream is = Files.newInputStream(keyStorePath)) { keyStore.load(is, keyStorePass.toCharArray()); } - SSLContextBuilder sslBuilder = SSLContexts.custom() + + SSLContext sslContext = SSLContexts.custom() .loadTrustMaterial(trustStore, null) - .loadKeyMaterial(keyStore, keyStorePass.toCharArray()); - final SSLContext sslContext = sslBuilder.build(); - Rest5Client.builder( - new HttpHost("https", "localhost", - 9200)) + .loadKeyMaterial(keyStore, keyStorePass.toCharArray()) + .build(); + + Rest5Client + .builder(new HttpHost("https", "localhost", 9200)) .setSSLContext(sslContext); //end::rest-client-config-mutual-tls-authentication } { //tag::rest-client-auth-bearer-token - Rest5ClientBuilder builder = Rest5Client.builder( - new HttpHost("https", "localhost", - 9200)); - Header[] defaultHeaders = - new Header[]{new BasicHeader("Authorization", - "Bearer u6iuAxZ0RG1Kcm5jVFI4eU4tZU9aVFEwT2F3")}; + Rest5ClientBuilder builder = Rest5Client + .builder(new HttpHost("https", "localhost", 9200)); + + Header[] defaultHeaders = new Header[]{ new BasicHeader( + "Authorization", + "Bearer u6iuAxZ0RG1Kcm5jVFI4eU4tZU9aVFEwT2F3" + )}; + builder.setDefaultHeaders(defaultHeaders); //end::rest-client-auth-bearer-token } @@ -443,19 +468,20 @@ public void commonConfiguration() throws Exception { //tag::rest-client-auth-api-key String apiKeyId = "uqlEyn8B_gQ_jlvwDIvM"; String apiKeySecret = "HxHWk2m4RN-V_qg9cDpuX"; - String apiKeyAuth = - Base64.getEncoder().encodeToString( - (apiKeyId + ":" + apiKeySecret) - .getBytes(StandardCharsets.UTF_8)); - Rest5ClientBuilder builder = Rest5Client.builder( - new HttpHost("https", "localhost", - 9200)); - Header[] defaultHeaders = - new Header[]{new BasicHeader("Authorization", - "ApiKey " + apiKeyAuth)}; + String apiKeyAuth = Base64.getEncoder().encodeToString( + (apiKeyId + ":" + apiKeySecret).getBytes(StandardCharsets.UTF_8) + ); + + Rest5ClientBuilder builder = Rest5Client + .builder(new HttpHost("https", "localhost", 9200)); + + Header[] defaultHeaders = new Header[]{ new BasicHeader( + "Authorization", + "ApiKey " + apiKeyAuth + )}; + builder.setDefaultHeaders(defaultHeaders); //end::rest-client-auth-api-key } - } } diff --git a/java-client/src/test/java/co/elastic/clients/transport/rest5_client/low_level/sniffer/documentation/SnifferDocumentation.java b/java-client/src/test/java/co/elastic/clients/documentation/rest5_client/SnifferDocumentation.java similarity index 85% rename from java-client/src/test/java/co/elastic/clients/transport/rest5_client/low_level/sniffer/documentation/SnifferDocumentation.java rename to java-client/src/test/java/co/elastic/clients/documentation/rest5_client/SnifferDocumentation.java index 995d16670..3db0bd7a1 100644 --- a/java-client/src/test/java/co/elastic/clients/transport/rest5_client/low_level/sniffer/documentation/SnifferDocumentation.java +++ b/java-client/src/test/java/co/elastic/clients/documentation/rest5_client/SnifferDocumentation.java @@ -17,7 +17,7 @@ * under the License. */ -package co.elastic.clients.transport.rest5_client.low_level.sniffer.documentation; +package co.elastic.clients.documentation.rest5_client; import org.apache.hc.core5.http.HttpHost; import co.elastic.clients.transport.rest5_client.low_level.Node; @@ -28,6 +28,7 @@ import co.elastic.clients.transport.rest5_client.low_level.sniffer.Sniffer; import java.io.IOException; +import java.net.URI; import java.net.URISyntaxException; import java.util.List; import java.util.concurrent.TimeUnit; @@ -56,9 +57,10 @@ public class SnifferDocumentation { public void usage() throws IOException, URISyntaxException { { //tag::sniffer-init - Rest5Client restClient = Rest5Client.builder( - HttpHost.create("http://localhost:9200")) + Rest5Client restClient = Rest5Client + .builder(HttpHost.create("http://localhost:9200")) .build(); + Sniffer sniffer = Sniffer.builder(restClient).build(); //end::sniffer-init @@ -69,9 +71,10 @@ public void usage() throws IOException, URISyntaxException { } { //tag::sniffer-interval - Rest5Client restClient = Rest5Client.builder( - new HttpHost("localhost", 9200)) + Rest5Client restClient = Rest5Client + .builder(new HttpHost("localhost", 9200)) .build(); + Sniffer sniffer = Sniffer.builder(restClient) .setSniffIntervalMillis(60000).build(); //end::sniffer-interval @@ -80,13 +83,16 @@ public void usage() throws IOException, URISyntaxException { //tag::sniff-on-failure SniffOnFailureListener sniffOnFailureListener = new SniffOnFailureListener(); - Rest5Client restClient = Rest5Client.builder( - new HttpHost("localhost", 9200)) + + Rest5Client restClient = Rest5Client + .builder(new HttpHost("localhost", 9200)) .setFailureListener(sniffOnFailureListener) // <1> .build(); + Sniffer sniffer = Sniffer.builder(restClient) .setSniffAfterFailureDelayMillis(30000) // <2> .build(); + sniffOnFailureListener.setSniffer(sniffer); // <3> //end::sniff-on-failure } @@ -95,10 +101,13 @@ public void usage() throws IOException, URISyntaxException { Rest5Client restClient = Rest5Client.builder( new HttpHost("localhost", 9200)) .build(); + NodesSniffer nodesSniffer = new ElasticsearchNodesSniffer( restClient, ElasticsearchNodesSniffer.DEFAULT_SNIFF_REQUEST_TIMEOUT, - ElasticsearchNodesSniffer.Scheme.HTTPS); + ElasticsearchNodesSniffer.Scheme.HTTPS + ); + Sniffer sniffer = Sniffer.builder(restClient) .setNodesSniffer(nodesSniffer).build(); //end::sniffer-https @@ -108,25 +117,30 @@ public void usage() throws IOException, URISyntaxException { Rest5Client restClient = Rest5Client.builder( new HttpHost("localhost", 9200)) .build(); + NodesSniffer nodesSniffer = new ElasticsearchNodesSniffer( restClient, TimeUnit.SECONDS.toMillis(5), - ElasticsearchNodesSniffer.Scheme.HTTP); + ElasticsearchNodesSniffer.Scheme.HTTP + ); + Sniffer sniffer = Sniffer.builder(restClient) .setNodesSniffer(nodesSniffer).build(); //end::sniff-request-timeout } { //tag::custom-nodes-sniffer - Rest5Client restClient = Rest5Client.builder( - HttpHost.create("http://localhost:9200")) + Rest5Client restClient = Rest5Client + .builder(URI.create("http://localhost:9200")) .build(); + NodesSniffer nodesSniffer = new NodesSniffer() { - @Override - public List sniff() throws IOException { - return null; // <1> - } - }; + @Override + public List sniff() throws IOException { + return null; // <1> + } + }; + Sniffer sniffer = Sniffer.builder(restClient) .setNodesSniffer(nodesSniffer).build(); //end::custom-nodes-sniffer diff --git a/java-client/src/test/java/co/elastic/clients/documentation/usage/IndexingBulkTest.java b/java-client/src/test/java/co/elastic/clients/documentation/usage/IndexingBulkTest.java index 1e6006978..33997effd 100644 --- a/java-client/src/test/java/co/elastic/clients/documentation/usage/IndexingBulkTest.java +++ b/java-client/src/test/java/co/elastic/clients/documentation/usage/IndexingBulkTest.java @@ -71,9 +71,9 @@ public void indexBulk() throws Exception { BulkRequest.Builder br = new BulkRequest.Builder(); for (Product product : products) { - br.operations(op -> op //<1> - .index(idx -> idx //<2> - .index("products") //<3> + br.operations(op -> op // <1> + .index(idx -> idx // <2> + .index("products") // <3> .id(product.getSku()) .document(product) ) diff --git a/settings.gradle.kts b/settings.gradle.kts index 8e5d8b347..868426732 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -22,3 +22,4 @@ import java.nio.file.Paths rootProject.name = "elasticsearch-java" include("java-client") +include("tools") diff --git a/tools/build.gradle.kts b/tools/build.gradle.kts new file mode 100644 index 000000000..c758a5622 --- /dev/null +++ b/tools/build.gradle.kts @@ -0,0 +1,52 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +plugins { + java + `java-test-fixtures` +} + +tasks.withType { + useJUnitPlatform() +} + +java { + targetCompatibility = JavaVersion.VERSION_17 +} + +dependencies { + // EPL-2.0 + // https://junit.org/junit5/ + testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.0") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.0") +} + +tasks.register("expand-includes") { + workingDir = rootDir + group = "application" + mainClass.set("co.elastic.clients.tools.docs.IncludeExpander") + args = listOf( + "docs/reference", + ) + classpath = sourceSets["main"].runtimeClasspath +} + +repositories { + mavenCentral() +} diff --git a/tools/src/main/java/co/elastic/clients/tools/docs/IncludeExpander.java b/tools/src/main/java/co/elastic/clients/tools/docs/IncludeExpander.java new file mode 100644 index 000000000..899b20f5f --- /dev/null +++ b/tools/src/main/java/co/elastic/clients/tools/docs/IncludeExpander.java @@ -0,0 +1,222 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package co.elastic.clients.tools.docs; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.LineNumberReader; +import java.io.StringReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * Processes the {@code .md} files in a directory expanding code blocks following a custom directive + * (expressed as MyST comment): + *
+ *     % :::{include-code} src={{doc-tests-src}}/path/to/SomeCode.java tag=some-marker
+ *     ```java
+ *       this is replaced by the section of SomeCode.java delimited
+ *       with `tag::some-marker` and `end::some-marker`
+ *     ```
+ * 
+ * + * Note: the MyST format has the {@code {literalinclude}} directive, which is currently only partially supported + * by docs-builder (the whole file is included, ignoring {@code start-after} and {@code end-before} parameters). + *

+ * See MyST docs + */ +public class IncludeExpander { + + public static void main(String[] args) throws IOException { + + File dir = new File(args.length == 0 ? "docs/reference" : args[0]); + + if (!dir.isDirectory()) { + throw new IllegalArgumentException(dir.getAbsolutePath() + " is not a directory"); + } + + // FIXME: we should read this from `docset.yml` in the `dir` directory. + processDirectory(dir, Map.of("doc-tests-src", "java-client/src/test/java/co/elastic/clients/documentation")); + } + + public static void processDirectory(File dir, Map subst) throws IOException { + //System.out.println("Processing directory " + dir); + // Traverse all files + for (File file : dir.listFiles()) { + if (file.isDirectory()) { + processDirectory(file, subst); + + } else if (file.getName().endsWith(".md")) { + processFile(file, subst); + } + } + } + + public static void processFile(File file, Map subst) throws IOException { + Path path = file.toPath(); + String text = Files.readString(path); + + String result = expandText(text, subst, path.toString()); + if (result == null) { + // No include directives + return; + } + + if (result.equals(text)) { + System.out.println("No change in " + path); + return; + } + + // File changed, write it. + System.out.println("Expanded " + path); + Files.writeString(path, result); + } + + enum State { + NORMAL_TEXT, + INCLUDE_CODE_DIRECTIVE, + REPLACED_CODE_BLOCK, + } + + public static void fail(String message, String path, LineNumberReader reader, Throwable e) { + throw new RuntimeException(message + " at " + path + ":" + reader.getLineNumber(), e); + } + + public static String expandText(String input, Map subst, String path) throws IOException { + if (!input.contains("% :::{include-code}")) { + // Nothing to do + return null; + } + + LineNumberReader reader = new LineNumberReader(new StringReader(input)); + StringBuilder output = new StringBuilder(); + State state = State.NORMAL_TEXT; + + String includeCodeLine = null; + + String line; + while ((line = reader.readLine()) != null) { + + switch (state) { + case NORMAL_TEXT -> { + if (line.startsWith("% :::{include-code}")) { + output.append(line).append("\n"); + includeCodeLine = line; + state = State.INCLUDE_CODE_DIRECTIVE; + + } else { + // Regular text line + output.append(line).append("\n"); + } + } + + case INCLUDE_CODE_DIRECTIVE -> { + if (!line.startsWith("```")) { + fail("The '% :::{include-code}' should be followed by a code block", path, reader, null); + } + output.append(line).append("\n"); + state = State.REPLACED_CODE_BLOCK; + try { + expandIncludeCodeDirective(includeCodeLine, subst, output); + } catch (Exception e) { + fail("Failed to expand include directive", path, reader, e); + } + } + + case REPLACED_CODE_BLOCK -> { + // Skip existing code until we reach the end + if (line.startsWith("```")) { + output.append(line).append("\n"); + state = State.NORMAL_TEXT; + } + } + } + } + + if (state != State.NORMAL_TEXT) { + throw new RuntimeException("Premature end of file in " + state); + } + + return output.toString(); + } + + public static void expandIncludeCodeDirective(String command, Map subst, StringBuilder output) throws IOException { + String[] s = command.split(" "); + Map args = new HashMap<>(); + for (int i = 2; i < s.length; i++) { + var kv = s[i].split("="); + args.put(kv[0], kv[1]); + } + + var src = Objects.requireNonNull(args.get("src"), "Missing 'src' attribute"); + var tag = Objects.requireNonNull(args.get("tag"), "Missing 'tag' attribute"); + + // Brute force replacement of placeholders. We can do better. + for (var kv: subst.entrySet()) { + src = src.replace("{{" + kv.getKey() + "}}", kv.getValue()); + } + + expandTaggedFile(src, tag, output); + } + + public static void expandTaggedFile(String path, String tag, StringBuilder output) throws IOException { + File file = new File(path); + String content = Files.readString(file.toPath()); + var reader = new BufferedReader(new StringReader(content)); + + String startTag = "tag::" + tag; + String endTag = "end::" + tag; + + String line; + boolean found = false; + while ((line = reader.readLine()) != null) { + if (line.contains(startTag)) { + // Find indentation level + int start = 0; + while (Character.isWhitespace(line.charAt(start))) { + start++; + } + + found = true; + while((line = reader.readLine()) != null) { + if (line.contains(endTag)) { + return; + } + // If the line has more characters than the tag's initial position, + // assume it's whitespace and truncate it to remove indentation. + if (line.length() > start) { + line = line.substring(start); + } + output.append(line).append("\n"); + } + } + } + + if (found) { + throw new RuntimeException("Missing end tag '" + tag + "' in " + path); + } else { + throw new RuntimeException("Missing start tag '" + tag + "' in " + path); + } + } +}