Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial version of Serverless client #656

Merged
merged 3 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 4 additions & 4 deletions config/checkstyle/checkstyle_suppressions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@

<suppressions>
<!-- Do not enforce line length on generated code -->
<suppress files="client[/\\]src[/\\]main[/\\]java[/\\]co[/\\]elastic[/\\]clients[/\\]elasticsearch[^.]*\.java" checks="LineLength" />
<suppress files="client[/\\]src[/\\]main[/\\]java[/\\]co[/\\]elastic[/\\]clients[/\\]elasticsearch[^.]*\.java" checks="UnusedImports" />
<suppress files="client[/\\]src[/\\]main[/\\]java[/\\]co[/\\]elastic[/\\]clients[/\\]elasticsearch[/\\]Elasticsearch[^.]*\.java" checks="LineLength" />
<suppress files="client[/\\]src[/\\]main[/\\]java[/\\]co[/\\]elastic[/\\]clients[/\\]elasticsearch[/\\]Elasticsearch[^.]*\.java" checks="UnusedImports" />
<suppress files="[/\\]src[/\\]main[/\\]java[/\\]co[/\\]elastic[/\\]clients[/\\]elasticsearch[^.]*\.java" checks="LineLength" />
<suppress files="[/\\]src[/\\]main[/\\]java[/\\]co[/\\]elastic[/\\]clients[/\\]elasticsearch[^.]*\.java" checks="UnusedImports" />
<suppress files="[/\\]src[/\\]main[/\\]java[/\\]co[/\\]elastic[/\\]clients[/\\]elasticsearch[/\\]Elasticsearch[^.]*\.java" checks="LineLength" />
<suppress files="[/\\]src[/\\]main[/\\]java[/\\]co[/\\]elastic[/\\]clients[/\\]elasticsearch[/\\]Elasticsearch[^.]*\.java" checks="UnusedImports" />
<!-- Do not check temp files: 'temp' packages and 'FooTemp.java' files -->
<suppress files="[/\\]temp[/\\]" checks="." />
<suppress files="Temp.java*" checks="." />
Expand Down
1 change: 1 addition & 0 deletions config/version-serverless.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
8.11.0
39 changes: 38 additions & 1 deletion java-client-serverless/README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,39 @@
Home for the Serverless Java client
# Java API client for the Elasticsearch Serverless Service.

A flavor of the Java API client for the upcoming [Elasticsearch Serverless Service](https://www.elastic.co/search-labs/stateless-your-new-state-of-find-with-elasticsearch).


The major difference is that Elasticsearch Serverless will abstract all infrastructure concerns from users, and as such has a reduced API surface. This new flavor of the Elasticsearch Java API client uses the same base framework and same package names as the "regular" client, but with a reduced number of APIs.

It is meant for users who want to ensure their code will work on the current Elasticsearch and the upcoming Serverless flavor.

## Usage

In the initial preview phase, this new flavor of the Java API client is published on the [GitHub project's Maven repository](https://github.com/elastic/elasticsearch-java/packages/1934787).

To install it, you need to add this repository to your project. This requires to authenticate to GitHub with an application token. Example with Gradle:

```
repositories {
maven {
name = "GitHubElasticsearchJava"
url = uri("https://maven.pkg.github.com/elastic/elasticsearch-java")
// See https://docs.gradle.org/current/samples/sample_publishing_credentials.html
credentials(PasswordCredentials::class)
}
}

dependencies {
implementation("co.elastic.clients", "elasticsearch-java-serverless", "1.0.0-20231031-SNAPSHOT")
}
```

## Implementation details

This project is parallel to [java-client](../java-client). The Java packages that are common to both projects are shared using symbolic links from this project to [java-client](../java-client). This ensures minimal disruption in the code layout and build system while avoiding code duplication.

In the future, the Java API client will be split into different modules, and there will be parent modules for the two flavors with dependencies on child modules.




316 changes: 316 additions & 0 deletions java-client-serverless/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,316 @@
/*
* 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.
*/

import com.github.jk1.license.ProjectData
import com.github.jk1.license.render.LicenseDataCollector
import com.github.jk1.license.render.ReportRenderer
import java.io.File
import java.io.FileWriter

plugins {
java
`java-library`
checkstyle
`maven-publish`
id("com.github.jk1.dependency-license-report") version "2.2"
id("de.thetaphi.forbiddenapis") version "3.4"
}

// GitHub Maven repo doesn't like 1.0.0+20231031-SNAPSHOT
version = "1.0.0-20231031-SNAPSHOT"

java {
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_1_8

withJavadocJar()
withSourcesJar()
}

sourceSets {
main {
java.srcDir("src/main-flavored/java")
}
}

forbiddenApis {
signaturesFiles = files(File(rootProject.projectDir, "config/forbidden-apis.txt"))
suppressAnnotations = setOf("co.elastic.clients.util.AllowForbiddenApis")
}

tasks.forbiddenApisMain {
bundledSignatures = setOf("jdk-system-out")
}

tasks.getByName<ProcessResources>("processResources") {
eachFile {
if (name != "apis.json") {
// Only process main source-set resources (test files are large)
expand(
"version" to version,
"git_revision" to (if (rootProject.extra.has("gitHashFull")) rootProject.extra["gitHashFull"] else "unknown")
)
}
}
}

tasks.withType<Test> {
useJUnitPlatform()
}

tasks.withType<Jar> {
doFirst {
if (rootProject.extra.has("gitHashFull")) {
val jar = this as Jar
jar.manifest.attributes["X-Git-Revision"] = rootProject.extra["gitHashFull"]
jar.manifest.attributes["X-Git-Commit-Time"] = rootProject.extra["gitCommitTime"]
} else {
throw GradleException("No git information available")
}
}

manifest {
attributes["Implementation-Title"] = "Elasticsearch Java client (Serverless edition)"
attributes["Implementation-Vendor"] = "Elastic"
attributes["Implementation-URL"] = "https://github.com/elastic/elasticsearch-java/"
attributes["Build-Date"] = rootProject.extra["buildTime"]
}

metaInf {
from("../LICENSE.txt")
from("../NOTICE.txt")
}
}

tasks.withType<Javadoc> {
val opt = options as StandardJavadocDocletOptions
// Gradle calls javadoc with a list of file and not a path. This prevents doc-files from being copied.
opt.addStringOption("sourcepath", project.projectDir.path + "/src/main/java")
opt.docFilesSubDirs(true)
opt.addBooleanOption("Xdoclint:-missing", true)

doLast {
// Javadoc adds its decoration to html doc files, including quite some JS. This slows down the api spec
// redirector that doesn't need it. So overwrite the target file with the original one.
val specFile = "co/elastic/clients/elasticsearch/doc-files/api-spec.html"
val source = File(project.projectDir, "src/main/java/" + specFile)
val target = File(project.projectDir, "build/docs/javadoc/" + specFile)
source.copyTo(target, overwrite = true)
}
}

publishing {
repositories {
maven {
// See https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-gradle-registry
// and https://docs.gradle.org/current/samples/sample_publishing_credentials.html
name = "ESJavaGithubPackages"
url = uri("https://maven.pkg.github.com/elastic/elasticsearch-java")
credentials(PasswordCredentials::class)
}

maven {
name = "Build"
url = uri("${rootProject.buildDir}/repository")
}
}

publications {
register<MavenPublication>("maven") {
from(components["java"])
pom {
name.set("Elasticsearch Java API Client (Serverless edition)")
artifactId = "elasticsearch-java-serverless"
description.set("Java API Client for Elasticsearch Serverless")
url.set("https://github.com/elastic/elasticsearch-java/")
licenses {
license {
name.set("The Apache Software License, Version 2.0")
url.set("https://www.apache.org/licenses/LICENSE-2.0.txt")
}
}
developers {
developer {
name.set("Elastic")
url.set("https://www.elastic.co")
inceptionYear.set("2020")
}
}
scm {
connection.set("scm:git:https://github.com/elastic/elasticsearch-java.git")
developerConnection.set("scm:git:ssh://git@github.com:elastic/elasticsearch-java.git")
url.set("https://github.com/elastic/elasticsearch-java/")
}

// Fixed version in Serverless.
// withXml {
// // Set the version of dependencies of the org.elasticsearch.client group to the one that we are building.
// // Since the unified release process releases everything at once, this ensures all published artifacts depend
// // on the exact same version. This assumes of course that the binary API and the behavior of these dependencies
// // are the same as the one used in the dependency section below.
// val xPathFactory = javax.xml.xpath.XPathFactory.newInstance()
// val depSelector = xPathFactory.newXPath()
// .compile("/project/dependencies/dependency[groupId/text() = 'org.elasticsearch.client']")
// val versionSelector = xPathFactory.newXPath().compile("version")
//
// var foundVersion = false;
//
// val deps = depSelector.evaluate(asElement().ownerDocument, javax.xml.xpath.XPathConstants.NODESET)
// as org.w3c.dom.NodeList
//
// for (i in 0 until deps.length) {
// val dep = deps.item(i)
// val version = versionSelector.evaluate(dep, javax.xml.xpath.XPathConstants.NODE) as org.w3c.dom.Element
// foundVersion = true;
// version.textContent = project.version.toString()
// }
//
// if (!foundVersion) {
// throw GradleException("Could not find a 'org.elasticsearch.client' to update dependency version in the POM.")
// }
// }
}
}
}
}

dependencies {
val elasticsearchVersion = "8.9.0"
val jacksonVersion = "2.13.3"
val openTelemetryVersion = "1.29.0"

// Apache 2.0
// https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-low.html
api("org.elasticsearch.client", "elasticsearch-rest-client", elasticsearchVersion)

// Apache 2.0
// https://search.maven.org/artifact/com.google.code.findbugs/jsr305
api("com.google.code.findbugs:jsr305:3.0.2")

// EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
// https://github.com/eclipse-ee4j/jsonp
api("jakarta.json:jakarta.json-api:2.0.1")

// Needed even if using Jackson to have an implementation of the Jsonp object model
// EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
// https://github.com/eclipse-ee4j/parsson
api("org.eclipse.parsson:parsson:1.0.0")

// OpenTelemetry API for native instrumentation of the client.
// Apache 2.0
// https://github.com/open-telemetry/opentelemetry-java
implementation("io.opentelemetry", "opentelemetry-api", openTelemetryVersion)
// Use it once it's stable (see Instrumentation.java). Limited to tests for now.
testImplementation("io.opentelemetry", "opentelemetry-semconv", "$openTelemetryVersion-alpha")

// EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
// https://github.com/eclipse-ee4j/jsonb-api
compileOnly("jakarta.json.bind", "jakarta.json.bind-api", "2.0.0")
testImplementation("jakarta.json.bind", "jakarta.json.bind-api", "2.0.0")

// Apache 2.0
// https://github.com/FasterXML/jackson
compileOnly("com.fasterxml.jackson.core", "jackson-core", jacksonVersion)
compileOnly("com.fasterxml.jackson.core", "jackson-databind", jacksonVersion)
testImplementation("com.fasterxml.jackson.core", "jackson-core", jacksonVersion)
testImplementation("com.fasterxml.jackson.core", "jackson-databind", jacksonVersion)

// EPL-2.0 OR BSD-3-Clause
// https://eclipse-ee4j.github.io/yasson/
testImplementation("org.eclipse", "yasson", "2.0.4") {
// Exclude Glassfish as we use Parsson (basically Glassfish renamed in the Jakarta namespace).
exclude(group = "org.glassfish", module = "jakarta.json")
}

// Apache-2.0
testImplementation("commons-io:commons-io:2.11.0")

// EPL-2.0
// https://junit.org/junit5/
testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2")

// MIT
// https://github.com/classgraph/classgraph
testImplementation("io.github.classgraph:classgraph:4.8.147")

// MIT
// https://www.testcontainers.org/
testImplementation("org.testcontainers", "testcontainers", "1.17.3")
testImplementation("org.testcontainers", "elasticsearch", "1.17.3")


testImplementation("io.opentelemetry", "opentelemetry-sdk", openTelemetryVersion)
}


licenseReport {
renderers = arrayOf(SpdxReporter(File(rootProject.buildDir, "release/dependencies.csv")))
excludeGroups = arrayOf("org.elasticsearch.client")
}

class SpdxReporter(val dest: File) : ReportRenderer {
// License names to their SPDX identifier
val spdxIds = mapOf(
"The Apache License, Version 2.0" to "Apache-2.0",
"Apache License, Version 2.0" to "Apache-2.0",
"The Apache Software License, Version 2.0" to "Apache-2.0",
"BSD Zero Clause License" to "0BSD",
"Eclipse Public License 2.0" to "EPL-2.0",
"Eclipse Public License v. 2.0" to "EPL-2.0",
"Eclipse Public License - v 2.0" to "EPL-2.0",
"GNU General Public License, version 2 with the GNU Classpath Exception" to "GPL-2.0 WITH Classpath-exception-2.0",
"COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0" to "CDDL-1.0"
)

private fun quote(str: String): String {
return if (str.contains(',') || str.contains("\"")) {
"\"" + str.replace("\"", "\"\"") + "\""
} else {
str
}
}

override fun render(data: ProjectData?) {
dest.parentFile.mkdirs()
FileWriter(dest).use { out ->
out.append("name,url,version,revision,license\n")
data?.allDependencies?.forEach { dep ->

val depVersion = dep.version
val depName = dep.group + ":" + dep.name

val info = LicenseDataCollector.multiModuleLicenseInfo(dep)
val depUrl = info.moduleUrls.first()

val licenseIds = info.licenses.mapNotNull { license ->
license.name?.let {
checkNotNull(spdxIds[it]) { "No SPDX identifier for $license" }
}
}.toSet()

// Combine multiple licenses.
// See https://spdx.github.io/spdx-spec/appendix-IV-SPDX-license-expressions/#composite-license-expressions
val licenseId = licenseIds.joinToString(" OR ")

out.append("${quote(depName)},${quote(depUrl)},${quote(depVersion)},,${quote(licenseId)}\n")
}
}
}
}
Loading
Loading