From 2b5c1551f68e3d94ae828affa2a254c68468db63 Mon Sep 17 00:00:00 2001 From: Simon Wimmesberger Date: Sun, 27 Sep 2020 19:10:21 +0200 Subject: [PATCH 01/10] Added bnd plugin --- build.gradle | 9 +++++++-- okhttp-brotli/build.gradle | 9 ++++++--- okhttp-dnsoverhttps/build.gradle | 9 ++++++--- okhttp-logging-interceptor/build.gradle | 9 ++++++--- okhttp-sse/build.gradle | 10 +++++++--- okhttp-tls/build.gradle | 10 +++++++--- okhttp-urlconnection/build.gradle | 16 +++++++++++++--- okhttp/build.gradle | 19 ++++++++++++++++--- 8 files changed, 68 insertions(+), 23 deletions(-) diff --git a/build.gradle b/build.gradle index a9597c38cb19..6d42f78b14c9 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,8 @@ buildscript { 'okio': '2.8.0', 'ktlint': '0.38.0', 'picocli': '4.2.0', - 'openjsse': '1.1.0' + 'openjsse': '1.1.0', + 'bndPlugin': '5.1.2' ] ext.deps = [ @@ -43,13 +44,15 @@ buildscript { 'moshi': "com.squareup.moshi:moshi:${versions.moshi}", 'moshiKotlin': "com.squareup.moshi:moshi-kotlin-codegen:${versions.moshi}", 'okio': "com.squareup.okio:okio:${versions.okio}", - 'openjsse': "org.openjsse:openjsse:${versions.openjsse}" + 'openjsse': "org.openjsse:openjsse:${versions.openjsse}", + 'bnd': "biz.aQute.bnd:biz.aQute.bnd.gradle:${versions.bndPlugin}" ] dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.10" classpath "org.jetbrains.dokka:dokka-gradle-plugin:0.10.1" classpath "com.android.tools.build:gradle:4.0.1" + classpath deps.bnd } repositories { @@ -118,6 +121,8 @@ subprojects { project -> apply plugin: 'checkstyle' apply plugin: 'ru.vyarus.animalsniffer' apply plugin: 'org.jetbrains.dokka' + apply plugin: 'biz.aQute.bnd.builder' + sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 diff --git a/okhttp-brotli/build.gradle b/okhttp-brotli/build.gradle index 2199662f9099..f99622b7cc48 100644 --- a/okhttp-brotli/build.gradle +++ b/okhttp-brotli/build.gradle @@ -1,7 +1,10 @@ jar { - manifest { - attributes('Automatic-Module-Name': 'okhttp3.brotli') - } + // modify these lines for MANIFEST.MF properties or for specific bnd instructions + bnd ''' + Export-Package: okhttp3.brotli + Automatic-Module-Name: okhttp3.brotli + Bundle-SymbolicName: com.squareup.okhttp3.brotli + ''' } dependencies { diff --git a/okhttp-dnsoverhttps/build.gradle b/okhttp-dnsoverhttps/build.gradle index 87479d7cf5af..87ec9c45878c 100644 --- a/okhttp-dnsoverhttps/build.gradle +++ b/okhttp-dnsoverhttps/build.gradle @@ -1,7 +1,10 @@ jar { - manifest { - attributes('Automatic-Module-Name': 'okhttp3.dnsoverhttps') - } + // modify these lines for MANIFEST.MF properties or for specific bnd instructions + bnd ''' + Export-Package: okhttp3.dnsoverhttps + Automatic-Module-Name: okhttp3.dnsoverhttps + Bundle-SymbolicName: com.squareup.okhttp3.dnsoverhttps + ''' } dependencies { diff --git a/okhttp-logging-interceptor/build.gradle b/okhttp-logging-interceptor/build.gradle index fae4fd07fb2a..1d5e7d50c1c0 100644 --- a/okhttp-logging-interceptor/build.gradle +++ b/okhttp-logging-interceptor/build.gradle @@ -1,9 +1,12 @@ apply plugin: 'me.champeau.gradle.japicmp' jar { - manifest { - attributes('Automatic-Module-Name': 'okhttp3.logging') - } + // modify these lines for MANIFEST.MF properties or for specific bnd instructions + bnd ''' + Export-Package: okhttp3.logging + Automatic-Module-Name: okhttp3.logging + Bundle-SymbolicName: com.squareup.okhttp3.logging + ''' } dependencies { diff --git a/okhttp-sse/build.gradle b/okhttp-sse/build.gradle index 6b0916d8aa81..3cbdbd9ca3ec 100644 --- a/okhttp-sse/build.gradle +++ b/okhttp-sse/build.gradle @@ -1,9 +1,13 @@ apply plugin: 'me.champeau.gradle.japicmp' jar { - manifest { - attributes('Automatic-Module-Name': 'okhttp3.sse') - } + // modify these lines for MANIFEST.MF properties or for specific bnd instructions + // is okhttp3.internal.sse package used in dependent libraries? + bnd ''' + Export-Package: okhttp3.sse + Automatic-Module-Name: okhttp3.sse + Bundle-SymbolicName: com.squareup.okhttp3.sse + ''' } dependencies { diff --git a/okhttp-tls/build.gradle b/okhttp-tls/build.gradle index 47dbf62ea1d6..a13dd2e5ef9e 100644 --- a/okhttp-tls/build.gradle +++ b/okhttp-tls/build.gradle @@ -1,9 +1,13 @@ apply plugin: 'me.champeau.gradle.japicmp' jar { - manifest { - attributes('Automatic-Module-Name': 'okhttp3.tls') - } + // modify these lines for MANIFEST.MF properties or for specific bnd instructions + // are okhttp3.tls.internal.* packages used in dependent libraries? + bnd ''' + Export-Package: okhttp3.tls + Automatic-Module-Name: okhttp3.tls + Bundle-SymbolicName: com.squareup.okhttp3.tls + ''' } dependencies { diff --git a/okhttp-urlconnection/build.gradle b/okhttp-urlconnection/build.gradle index 16eeb1b70f17..0a75e7e82d3b 100644 --- a/okhttp-urlconnection/build.gradle +++ b/okhttp-urlconnection/build.gradle @@ -1,9 +1,19 @@ apply plugin: 'me.champeau.gradle.japicmp' jar { - manifest { - attributes('Automatic-Module-Name': 'okhttp3.urlconnection') - } + // modify these lines for MANIFEST.MF properties or for specific bnd instructions + // because this project uses the same package name as the default okhttp project (which is mostly "invalid" for OSGi) + // we have to remove okhttp from the bnd classpath and import it's packages manually + bnd """ + Export-Package: okhttp3 + Import-Package: \ + okhttp3;version="\${range;[==,+);\${version_cleanup;${project.version}}}",\ + okhttp3.internal.*;version="\${range;[==,+);\${version_cleanup;${project.version}}}",\ + * + Automatic-Module-Name: okhttp3.urlconnection + Bundle-SymbolicName: com.squareup.okhttp3.urlconnection + """ + setClasspath(files([])) } dependencies { diff --git a/okhttp/build.gradle b/okhttp/build.gradle index df42f42810a2..c7fe98089752 100644 --- a/okhttp/build.gradle +++ b/okhttp/build.gradle @@ -1,9 +1,22 @@ apply plugin: 'me.champeau.gradle.japicmp' jar { - manifest { - attributes('Automatic-Module-Name': 'okhttp3') - } + // modify these lines for MANIFEST.MF properties or for specific bnd instructions + // internal package needs to be exported too because interceptors use internal packages + bnd ''' + Export-Package: \ + okhttp3,\ + okhttp3.internal.* + Import-Package: \ + android.*;resolution:=optional,\ + dalvik.system;resolution:=optional,\ + org.conscrypt;resolution:=optional,\ + org.bouncycastle.*;resolution:=optional,\ + org.openjsse.*;resolution:=optional,\ + * + Automatic-Module-Name: okhttp3 + Bundle-SymbolicName: com.squareup.okhttp3 + ''' } sourceSets { From 8d2a9797a9873ea042a17e60594591c3298fb792 Mon Sep 17 00:00:00 2001 From: Simon Wimmesberger Date: Mon, 28 Sep 2020 16:24:47 +0200 Subject: [PATCH 02/10] Fixed comment --- okhttp/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/okhttp/build.gradle b/okhttp/build.gradle index c7fe98089752..422d7471ae9d 100644 --- a/okhttp/build.gradle +++ b/okhttp/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'me.champeau.gradle.japicmp' jar { // modify these lines for MANIFEST.MF properties or for specific bnd instructions - // internal package needs to be exported too because interceptors use internal packages + // internal package needs to be exported too because sibling libraries use internal packages bnd ''' Export-Package: \ okhttp3,\ From e02bd10974a16c5c04e77c0873abdee4717f0d26 Mon Sep 17 00:00:00 2001 From: Simon Wimmesberger Date: Tue, 29 Sep 2020 12:33:44 +0200 Subject: [PATCH 03/10] Added OSGi test, only apply bnd plugin to selected projects --- build.gradle | 32 ++++++- okhttp-brotli/build.gradle | 2 + okhttp-dnsoverhttps/build.gradle | 2 + okhttp-logging-interceptor/build.gradle | 1 + okhttp-sse/build.gradle | 1 + okhttp-tls/build.gradle | 2 +- okhttp-urlconnection/build.gradle | 15 ++- okhttp/build.gradle | 19 ++++ .../java/okhttp3/osgi/OkHttpOsgiTest.java | 96 +++++++++++++++++++ .../resources/okhttp3/osgi/resolveTest.bndrun | 13 +++ .../resources/okhttp3/osgi/ws1/cnf/build.bnd | 5 + .../okhttp3/osgi/ws1/cnf/local/.keep | 0 12 files changed, 173 insertions(+), 15 deletions(-) create mode 100644 okhttp/src/test/java/okhttp3/osgi/OkHttpOsgiTest.java create mode 100644 okhttp/src/test/resources/okhttp3/osgi/resolveTest.bndrun create mode 100644 okhttp/src/test/resources/okhttp3/osgi/ws1/cnf/build.bnd create mode 100644 okhttp/src/test/resources/okhttp3/osgi/ws1/cnf/local/.keep diff --git a/build.gradle b/build.gradle index 6d42f78b14c9..d19ee6c92919 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,3 @@ -import net.ltgt.gradle.errorprone.CheckSeverity - buildscript { ext.versions = [ 'animalSniffer': '1.18', @@ -21,7 +19,8 @@ buildscript { 'ktlint': '0.38.0', 'picocli': '4.2.0', 'openjsse': '1.1.0', - 'bndPlugin': '5.1.2' + 'bnd': '5.1.2', + 'equinox': '3.16.0' ] ext.deps = [ @@ -45,7 +44,10 @@ buildscript { 'moshiKotlin': "com.squareup.moshi:moshi-kotlin-codegen:${versions.moshi}", 'okio': "com.squareup.okio:okio:${versions.okio}", 'openjsse': "org.openjsse:openjsse:${versions.openjsse}", - 'bnd': "biz.aQute.bnd:biz.aQute.bnd.gradle:${versions.bndPlugin}" + 'bnd': "biz.aQute.bnd:biz.aQute.bnd.gradle:${versions.bnd}", + 'bndResolve': "biz.aQute.bnd:biz.aQute.resolve:${versions.bnd}", + 'equinox': "org.eclipse.platform:org.eclipse.osgi:${versions.equinox}", + 'kotlinStdlibOsgi': "org.jetbrains.kotlin:kotlin-osgi-bundle:${versions.kotlin}" ] dependencies { @@ -110,6 +112,26 @@ allprojects { } } +ext.applyOsgi = { project -> + project.apply plugin: 'biz.aQute.bnd.builder' + + project.sourceSets { + // own source set for OSGi specific dependencies + osgi + } + + project.jar { t -> + // this sets only the source set for OSGi + // setSourceSet is a method provided by bnd + t.setClasspath(project.sourceSets.osgi['compileClasspath'] + project.sourceSets.main['compileClasspath']) + } + + project.dependencies { + // when we provide the OSGi version for the kotlin standard library BND can infer the bundle version for the import package statements + osgiApi deps.kotlinStdlibOsgi + } +} + /** Configure building for Java+Kotlin projects. */ subprojects { project -> if (project.name == 'android-test') return @@ -121,7 +143,7 @@ subprojects { project -> apply plugin: 'checkstyle' apply plugin: 'ru.vyarus.animalsniffer' apply plugin: 'org.jetbrains.dokka' - apply plugin: 'biz.aQute.bnd.builder' + sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 diff --git a/okhttp-brotli/build.gradle b/okhttp-brotli/build.gradle index f99622b7cc48..75d9bd0789d3 100644 --- a/okhttp-brotli/build.gradle +++ b/okhttp-brotli/build.gradle @@ -1,3 +1,5 @@ +applyOsgi(this) + jar { // modify these lines for MANIFEST.MF properties or for specific bnd instructions bnd ''' diff --git a/okhttp-dnsoverhttps/build.gradle b/okhttp-dnsoverhttps/build.gradle index 87ec9c45878c..c6835a02e98a 100644 --- a/okhttp-dnsoverhttps/build.gradle +++ b/okhttp-dnsoverhttps/build.gradle @@ -1,3 +1,5 @@ +applyOsgi(this) + jar { // modify these lines for MANIFEST.MF properties or for specific bnd instructions bnd ''' diff --git a/okhttp-logging-interceptor/build.gradle b/okhttp-logging-interceptor/build.gradle index 1d5e7d50c1c0..b880494fcd48 100644 --- a/okhttp-logging-interceptor/build.gradle +++ b/okhttp-logging-interceptor/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'me.champeau.gradle.japicmp' +applyOsgi(this) jar { // modify these lines for MANIFEST.MF properties or for specific bnd instructions diff --git a/okhttp-sse/build.gradle b/okhttp-sse/build.gradle index 3cbdbd9ca3ec..236c59c68a27 100644 --- a/okhttp-sse/build.gradle +++ b/okhttp-sse/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'me.champeau.gradle.japicmp' +applyOsgi(this) jar { // modify these lines for MANIFEST.MF properties or for specific bnd instructions diff --git a/okhttp-tls/build.gradle b/okhttp-tls/build.gradle index a13dd2e5ef9e..804c5803ada1 100644 --- a/okhttp-tls/build.gradle +++ b/okhttp-tls/build.gradle @@ -1,8 +1,8 @@ apply plugin: 'me.champeau.gradle.japicmp' +applyOsgi(this) jar { // modify these lines for MANIFEST.MF properties or for specific bnd instructions - // are okhttp3.tls.internal.* packages used in dependent libraries? bnd ''' Export-Package: okhttp3.tls Automatic-Module-Name: okhttp3.tls diff --git a/okhttp-urlconnection/build.gradle b/okhttp-urlconnection/build.gradle index 0a75e7e82d3b..c6cb4c8c3a48 100644 --- a/okhttp-urlconnection/build.gradle +++ b/okhttp-urlconnection/build.gradle @@ -1,23 +1,20 @@ apply plugin: 'me.champeau.gradle.japicmp' +applyOsgi(this) +def mainProj = project(':okhttp') jar { // modify these lines for MANIFEST.MF properties or for specific bnd instructions - // because this project uses the same package name as the default okhttp project (which is mostly "invalid" for OSGi) - // we have to remove okhttp from the bnd classpath and import it's packages manually + // urlconnection is a OSGi fragment because the package name is the same as the okhttp3 module bnd """ - Export-Package: okhttp3 - Import-Package: \ - okhttp3;version="\${range;[==,+);\${version_cleanup;${project.version}}}",\ - okhttp3.internal.*;version="\${range;[==,+);\${version_cleanup;${project.version}}}",\ - * + Fragment-Host: com.squareup.okhttp3; bundle-version="\${range;[==,+);\${version_cleanup;${mainProj.version}}}" Automatic-Module-Name: okhttp3.urlconnection Bundle-SymbolicName: com.squareup.okhttp3.urlconnection + -removeheaders: Private-Package """ - setClasspath(files([])) } dependencies { - api project(':okhttp') + api mainProj compileOnly deps.jsr305 compileOnly deps.animalSniffer diff --git a/okhttp/build.gradle b/okhttp/build.gradle index 422d7471ae9d..d276dd669c75 100644 --- a/okhttp/build.gradle +++ b/okhttp/build.gradle @@ -1,8 +1,10 @@ apply plugin: 'me.champeau.gradle.japicmp' +applyOsgi(this) jar { // modify these lines for MANIFEST.MF properties or for specific bnd instructions // internal package needs to be exported too because sibling libraries use internal packages + // if a dependency is added which is not necessarily required at runtime add the packages to the optional list bnd ''' Export-Package: \ okhttp3,\ @@ -13,6 +15,7 @@ jar { org.conscrypt;resolution:=optional,\ org.bouncycastle.*;resolution:=optional,\ org.openjsse.*;resolution:=optional,\ + sun.security.ssl;resolution:=optional,\ * Automatic-Module-Name: okhttp3 Bundle-SymbolicName: com.squareup.okhttp3 @@ -34,6 +37,16 @@ task copyJavaTemplates(type: Copy) { filteringCharset = 'UTF-8' } +// make osgiTestImplementation configuration jars available to the test environment +configurations { + osgiTestImplementation +} +task copyOsgiTestDeployment(type: Copy) { + from configurations.osgiTestImplementation + into "${buildDir}/resources/test/okhttp3/osgi/deployments" +} +tasks.test.dependsOn(copyOsgiTestDeployment) + dependencies { api deps.okio api deps.kotlinStdlib @@ -50,10 +63,16 @@ dependencies { testImplementation project(':okhttp-urlconnection') testImplementation project(':mockwebserver') testImplementation project(':okhttp-logging-interceptor') + testImplementation project(':okhttp-brotli') + testImplementation project(':okhttp-dnsoverhttps') + testImplementation project(':okhttp-sse') testImplementation deps.conscrypt testImplementation deps.junit testImplementation deps.assertj testImplementation deps.openjsse + testImplementation deps.bndResolve + osgiTestImplementation deps.equinox + osgiTestImplementation deps.kotlinStdlibOsgi testCompileOnly deps.jsr305 } diff --git a/okhttp/src/test/java/okhttp3/osgi/OkHttpOsgiTest.java b/okhttp/src/test/java/okhttp3/osgi/OkHttpOsgiTest.java new file mode 100644 index 000000000000..cb9608eceb6b --- /dev/null +++ b/okhttp/src/test/java/okhttp3/osgi/OkHttpOsgiTest.java @@ -0,0 +1,96 @@ +package okhttp3.osgi; + +import aQute.bnd.build.Workspace; +import aQute.bnd.service.RepositoryPlugin; +import aQute.lib.io.IO; +import biz.aQute.resolve.Bndrun; +import org.junit.Test; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.stream.Stream; + +/** + * Tries to resolve the OSGi metadata specified in the + */ +public class OkHttpOsgiTest { + @Test + public void testMainModuleWithSiblings() throws Exception { + File workspaceDir = getWorkspaceDirectory().toFile(); + Path resourceDir = getResourceDirectory(); + Workspace workspace = new Workspace(workspaceDir); + + RepositoryPlugin localRepo = workspace.getRepository("Local"); + // deploy the bundles in the deployments test directory + deployDirectory(localRepo, resourceDir.resolve("deployments")); + deployClassPath(localRepo); + + try (Bndrun bndRun = new Bndrun(workspace, resourceDir.resolve("resolveTest.bndrun").toFile())) { + // this will fail when something is wrong + bndRun.resolve(false, true); + } + } + + private Path getResourceDirectory() throws URISyntaxException { + return getWorkspaceDirectory().getParent(); + } + + private Path getWorkspaceDirectory() throws URISyntaxException { + URL bndWorkspaceURL = OkHttpOsgiTest.class.getResource("ws1/cnf/build.bnd"); + Path bndWorkspaceFile = Paths.get(bndWorkspaceURL.toURI()); + return bndWorkspaceFile.getParent().getParent(); + } + + private void deployDirectory(RepositoryPlugin repository, Path directory) throws IOException { + if (!Files.exists(directory)) + return; + + try (Stream files = Files.list(directory)) { + files.forEach(f -> { + deployFile(repository, f); + }); + } + } + + private void deployClassPath(RepositoryPlugin repository) { + String classpath = System.getProperty("java.class.path"); + String[] classpathEntries = classpath.split(File.pathSeparator); + for (String classPathEntry : classpathEntries) { + Path classPathFile = Paths.get(classPathEntry); + deployFile(repository, classPathFile); + } + } + + private void deployFile(RepositoryPlugin repository, Path file) { + if (Files.isRegularFile(file)) { + String fileName = file.getFileName().toString(); + try { + deploy(repository, file); + successDeployment(fileName); + } catch (Exception e) { + failedDeployment(fileName, e); + } + } + } + + private void deploy(RepositoryPlugin repository, Path bundleFile) throws Exception { + try (InputStream stream = new BufferedInputStream(IO.stream(bundleFile))) { + repository.put(stream, new RepositoryPlugin.PutOptions()); + } + } + + private void successDeployment(String fileName) { + System.out.println("Deployed " + fileName); + } + + private void failedDeployment(String fileName, Exception error) { + System.out.println("Failed to deploy " + fileName); + } +} diff --git a/okhttp/src/test/resources/okhttp3/osgi/resolveTest.bndrun b/okhttp/src/test/resources/okhttp3/osgi/resolveTest.bndrun new file mode 100644 index 000000000000..5f588f545372 --- /dev/null +++ b/okhttp/src/test/resources/okhttp3/osgi/resolveTest.bndrun @@ -0,0 +1,13 @@ +-runfw: org.eclipse.osgi +-runee: JavaSE-1.8 +-runsystemcapabilities: ${native_capability} +-resolve.effective: active;skip:="osgi.service" + +-runrequires: \ + bnd.identity;version='latest';id='com.squareup.okhttp3',\ + bnd.identity;version='latest';id='com.squareup.okhttp3.brotli',\ + bnd.identity;version='latest';id='com.squareup.okhttp3.dnsoverhttps',\ + bnd.identity;version='latest';id='com.squareup.okhttp3.logging',\ + bnd.identity;version='latest';id='com.squareup.okhttp3.sse',\ + bnd.identity;version='latest';id='com.squareup.okhttp3.tls',\ + bnd.identity;version='latest';id='com.squareup.okhttp3.urlconnection' \ No newline at end of file diff --git a/okhttp/src/test/resources/okhttp3/osgi/ws1/cnf/build.bnd b/okhttp/src/test/resources/okhttp3/osgi/ws1/cnf/build.bnd new file mode 100644 index 000000000000..1464db4d1abb --- /dev/null +++ b/okhttp/src/test/resources/okhttp3/osgi/ws1/cnf/build.bnd @@ -0,0 +1,5 @@ +-plugin.1.Local: \ + aQute.bnd.deployer.repository.LocalIndexedRepo; \ + name = Local; \ + pretty = true; \ + local = ${build}/local \ No newline at end of file diff --git a/okhttp/src/test/resources/okhttp3/osgi/ws1/cnf/local/.keep b/okhttp/src/test/resources/okhttp3/osgi/ws1/cnf/local/.keep new file mode 100644 index 000000000000..e69de29bb2d1 From c749a35fd2c97fb544028842350e7dda7ce6cc8a Mon Sep 17 00:00:00 2001 From: Simon Wimmesberger Date: Wed, 30 Sep 2020 10:04:16 +0200 Subject: [PATCH 04/10] Renamed osgi test deployment configuration for better clarity --- okhttp/build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/okhttp/build.gradle b/okhttp/build.gradle index d276dd669c75..ff70cc61ac7d 100644 --- a/okhttp/build.gradle +++ b/okhttp/build.gradle @@ -39,10 +39,10 @@ task copyJavaTemplates(type: Copy) { // make osgiTestImplementation configuration jars available to the test environment configurations { - osgiTestImplementation + osgiTestDeploy } task copyOsgiTestDeployment(type: Copy) { - from configurations.osgiTestImplementation + from configurations.osgiTestDeploy into "${buildDir}/resources/test/okhttp3/osgi/deployments" } tasks.test.dependsOn(copyOsgiTestDeployment) @@ -71,8 +71,8 @@ dependencies { testImplementation deps.assertj testImplementation deps.openjsse testImplementation deps.bndResolve - osgiTestImplementation deps.equinox - osgiTestImplementation deps.kotlinStdlibOsgi + osgiTestDeploy deps.equinox + osgiTestDeploy deps.kotlinStdlibOsgi testCompileOnly deps.jsr305 } From ef432d9472a5fcd2ac414a108e26c934293db90c Mon Sep 17 00:00:00 2001 From: Simon Wimmesberger Date: Wed, 30 Sep 2020 10:05:09 +0200 Subject: [PATCH 05/10] Moved workspace/bndrun definition into code instead of text files --- .../java/okhttp3/osgi/OkHttpOsgiTest.java | 128 +++++++++++++++--- .../resources/okhttp3/osgi/resolveTest.bndrun | 13 -- .../resources/okhttp3/osgi/ws1/cnf/build.bnd | 5 - .../okhttp3/osgi/ws1/cnf/local/.keep | 0 4 files changed, 109 insertions(+), 37 deletions(-) delete mode 100644 okhttp/src/test/resources/okhttp3/osgi/resolveTest.bndrun delete mode 100644 okhttp/src/test/resources/okhttp3/osgi/ws1/cnf/build.bnd delete mode 100644 okhttp/src/test/resources/okhttp3/osgi/ws1/cnf/local/.keep diff --git a/okhttp/src/test/java/okhttp3/osgi/OkHttpOsgiTest.java b/okhttp/src/test/java/okhttp3/osgi/OkHttpOsgiTest.java index cb9608eceb6b..7f47dfd38a6e 100644 --- a/okhttp/src/test/java/okhttp3/osgi/OkHttpOsgiTest.java +++ b/okhttp/src/test/java/okhttp3/osgi/OkHttpOsgiTest.java @@ -1,51 +1,127 @@ package okhttp3.osgi; +import aQute.bnd.build.Project; import aQute.bnd.build.Workspace; +import aQute.bnd.build.model.BndEditModel; +import aQute.bnd.deployer.repository.LocalIndexedRepo; +import aQute.bnd.osgi.Constants; import aQute.bnd.service.RepositoryPlugin; +import aQute.bnd.stream.MapStream; import aQute.lib.io.IO; import biz.aQute.resolve.Bndrun; +import org.junit.BeforeClass; import org.junit.Test; import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.net.URISyntaxException; -import java.net.URL; import java.nio.file.Files; +import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.*; +import java.util.stream.Collectors; import java.util.stream.Stream; /** - * Tries to resolve the OSGi metadata specified in the + * Tries to resolve the OSGi metadata of the okhttp3 main module and all siblings. + * If some required modules do not have OSGi metadata the test will fail. */ public class OkHttpOsgiTest { + private static final List RUN_REQ = Arrays.asList( + "com.squareup.okhttp3", + "com.squareup.okhttp3.brotli", + "com.squareup.okhttp3.dnsoverhttps", + "com.squareup.okhttp3.logging", + "com.squareup.okhttp3.sse", + "com.squareup.okhttp3.tls", + "com.squareup.okhttp3.urlconnection" + ); + // equinox + // the framework defined here must also be on the testing classpath + private static final String RESOLVE_OSGI_FRAMEWORK = "org.eclipse.osgi"; + private static final String RESOLVE_JAVA_VERSION = "JavaSE-1.8"; + private static final String LOCAL_REPO_NAME = "Local"; + + private static Path TEST_RESOURCE_DIR; + private static Path WORKING_DIR; + + @BeforeClass + public static void setup() throws IOException { + TEST_RESOURCE_DIR = Paths.get("./build/resources/test/okhttp3/osgi"); + TEST_RESOURCE_DIR = TEST_RESOURCE_DIR.toRealPath(LinkOption.NOFOLLOW_LINKS); + WORKING_DIR = TEST_RESOURCE_DIR.resolve("tmp"); + // ensure we start from scratch + deleteDirectory(WORKING_DIR); + Files.createDirectories(WORKING_DIR); + } + @Test public void testMainModuleWithSiblings() throws Exception { - File workspaceDir = getWorkspaceDirectory().toFile(); - Path resourceDir = getResourceDirectory(); - Workspace workspace = new Workspace(workspaceDir); + try (Workspace workspace = createWorkspace(); Bndrun bndRun = createBndRun(workspace)) { + // this will fail when something is wrong + bndRun.resolve(false, false); + } + } + + private Workspace createWorkspace() throws Exception { + Path workingDir = WORKING_DIR; + Path cnfDir = workingDir.resolve("cnf"); + Path localRepoDir = cnfDir.resolve("local"); + Files.createDirectories(localRepoDir); + + Workspace workspace = new Workspace(workingDir.toFile(), cnfDir.getFileName().toString()); + // add local repository plugin to which we deploy the bundles to + addPlugin(workspace, LOCAL_REPO_NAME, LocalIndexedRepo.class, LocalIndexedRepo.PROP_NAME, LOCAL_REPO_NAME, LocalIndexedRepo.PROP_LOCAL_DIR, localRepoDir.toString()); + workspace.refresh(); + prepareWorkspace(workspace); + return workspace; + } - RepositoryPlugin localRepo = workspace.getRepository("Local"); + private void prepareWorkspace(Workspace workspace) throws Exception { + Path testResDir = TEST_RESOURCE_DIR; + Path deploymentsDir = testResDir.resolve("deployments"); + + RepositoryPlugin localRepo = workspace.getRepository(LOCAL_REPO_NAME); // deploy the bundles in the deployments test directory - deployDirectory(localRepo, resourceDir.resolve("deployments")); + deployDirectory(localRepo, deploymentsDir); deployClassPath(localRepo); + } - try (Bndrun bndRun = new Bndrun(workspace, resourceDir.resolve("resolveTest.bndrun").toFile())) { - // this will fail when something is wrong - bndRun.resolve(false, true); - } + private void addPlugin(Workspace workspace, String alias, Class pluginType, String key1, String value1, String key2, String value2) { + Map props = new HashMap<>(); + props.put(key1, value1); + props.put(key2, value2); + addPlugin(workspace, alias, pluginType, props); } - private Path getResourceDirectory() throws URISyntaxException { - return getWorkspaceDirectory().getParent(); + private void addPlugin(Workspace workspace, String alias, Class pluginType, Map properties) { + try (Formatter setup = new Formatter()) { + setup.format("%s", pluginType.getName()); + MapStream.of(properties) + .forEachOrdered((k, v) -> setup.format("; %s = '%s'", k, v)); + workspace.setProperty(Constants.PLUGIN + "." + alias, setup.toString()); + } } - private Path getWorkspaceDirectory() throws URISyntaxException { - URL bndWorkspaceURL = OkHttpOsgiTest.class.getResource("ws1/cnf/build.bnd"); - Path bndWorkspaceFile = Paths.get(bndWorkspaceURL.toURI()); - return bndWorkspaceFile.getParent().getParent(); + private Bndrun createBndRun(Workspace workspace) throws Exception { + // creating the run require sting + // it will always use the latest version of each bundle available in the repository + String runRequireString = RUN_REQ.stream().map( + s -> "osgi.identity;filter:='(osgi.identity=" + s + ")'" + ).collect( + Collectors.joining(",") + ); + + BndEditModel runEditModel = new BndEditModel(workspace); + // tmp project to satisfy bnd API + runEditModel.setProject(new Project(workspace, WORKING_DIR.toFile())); + Bndrun bndrun = new Bndrun(runEditModel); + bndrun.setRunfw(RESOLVE_OSGI_FRAMEWORK); + bndrun.setRunee(RESOLVE_JAVA_VERSION); + bndrun.setRunRequires(runRequireString); + return bndrun; } private void deployDirectory(RepositoryPlugin repository, Path directory) throws IOException { @@ -90,7 +166,21 @@ private void successDeployment(String fileName) { System.out.println("Deployed " + fileName); } + // called when e.g. module is no OSGi bundle private void failedDeployment(String fileName, Exception error) { - System.out.println("Failed to deploy " + fileName); + if (error instanceof IllegalArgumentException || !(error instanceof RuntimeException)) { + System.out.println("Failed to deploy " + fileName); + } else { + throw (RuntimeException) error; + } + } + + private static void deleteDirectory(Path dir) throws IOException { + if (!Files.exists(dir)) return; + + Files.walk(dir) + .filter(Files::isRegularFile) + .map(Path::toFile) + .forEach(File::delete); } } diff --git a/okhttp/src/test/resources/okhttp3/osgi/resolveTest.bndrun b/okhttp/src/test/resources/okhttp3/osgi/resolveTest.bndrun deleted file mode 100644 index 5f588f545372..000000000000 --- a/okhttp/src/test/resources/okhttp3/osgi/resolveTest.bndrun +++ /dev/null @@ -1,13 +0,0 @@ --runfw: org.eclipse.osgi --runee: JavaSE-1.8 --runsystemcapabilities: ${native_capability} --resolve.effective: active;skip:="osgi.service" - --runrequires: \ - bnd.identity;version='latest';id='com.squareup.okhttp3',\ - bnd.identity;version='latest';id='com.squareup.okhttp3.brotli',\ - bnd.identity;version='latest';id='com.squareup.okhttp3.dnsoverhttps',\ - bnd.identity;version='latest';id='com.squareup.okhttp3.logging',\ - bnd.identity;version='latest';id='com.squareup.okhttp3.sse',\ - bnd.identity;version='latest';id='com.squareup.okhttp3.tls',\ - bnd.identity;version='latest';id='com.squareup.okhttp3.urlconnection' \ No newline at end of file diff --git a/okhttp/src/test/resources/okhttp3/osgi/ws1/cnf/build.bnd b/okhttp/src/test/resources/okhttp3/osgi/ws1/cnf/build.bnd deleted file mode 100644 index 1464db4d1abb..000000000000 --- a/okhttp/src/test/resources/okhttp3/osgi/ws1/cnf/build.bnd +++ /dev/null @@ -1,5 +0,0 @@ --plugin.1.Local: \ - aQute.bnd.deployer.repository.LocalIndexedRepo; \ - name = Local; \ - pretty = true; \ - local = ${build}/local \ No newline at end of file diff --git a/okhttp/src/test/resources/okhttp3/osgi/ws1/cnf/local/.keep b/okhttp/src/test/resources/okhttp3/osgi/ws1/cnf/local/.keep deleted file mode 100644 index e69de29bb2d1..000000000000 From 3d3a006d32e1aed57c571ab2d609ca66c16f88ea Mon Sep 17 00:00:00 2001 From: Simon Wimmesberger Date: Wed, 30 Sep 2020 13:18:40 +0200 Subject: [PATCH 06/10] Better comments, cleanup --- build.gradle | 2 -- okhttp/build.gradle | 6 ++++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index d19ee6c92919..089928bd041a 100644 --- a/build.gradle +++ b/build.gradle @@ -143,8 +143,6 @@ subprojects { project -> apply plugin: 'checkstyle' apply plugin: 'ru.vyarus.animalsniffer' apply plugin: 'org.jetbrains.dokka' - - sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 diff --git a/okhttp/build.gradle b/okhttp/build.gradle index ff70cc61ac7d..169a9387e622 100644 --- a/okhttp/build.gradle +++ b/okhttp/build.gradle @@ -4,7 +4,7 @@ applyOsgi(this) jar { // modify these lines for MANIFEST.MF properties or for specific bnd instructions // internal package needs to be exported too because sibling libraries use internal packages - // if a dependency is added which is not necessarily required at runtime add the packages to the optional list + // to prevent requiring certain android/ssl packages at runtime we explicitly define those as optional here bnd ''' Export-Package: \ okhttp3,\ @@ -37,7 +37,7 @@ task copyJavaTemplates(type: Copy) { filteringCharset = 'UTF-8' } -// make osgiTestImplementation configuration jars available to the test environment +// make osgiTestDeploy configuration jars available to the test environment configurations { osgiTestDeploy } @@ -47,6 +47,8 @@ task copyOsgiTestDeployment(type: Copy) { } tasks.test.dependsOn(copyOsgiTestDeployment) +// ensure that if a dependency is added here which is not necessarily required at runtime (compileOnly!) +// is also added as a optional package import to the bnd definition above dependencies { api deps.okio api deps.kotlinStdlib From 6992eeddabcfc2a5bf8882f46dd722568ebed20d Mon Sep 17 00:00:00 2001 From: Simon Wimmesberger Date: Wed, 30 Sep 2020 13:19:38 +0200 Subject: [PATCH 07/10] Removed cleared comment --- okhttp-sse/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/okhttp-sse/build.gradle b/okhttp-sse/build.gradle index 236c59c68a27..5c81363be5a9 100644 --- a/okhttp-sse/build.gradle +++ b/okhttp-sse/build.gradle @@ -3,7 +3,6 @@ applyOsgi(this) jar { // modify these lines for MANIFEST.MF properties or for specific bnd instructions - // is okhttp3.internal.sse package used in dependent libraries? bnd ''' Export-Package: okhttp3.sse Automatic-Module-Name: okhttp3.sse From 3accc409cd42ca132877aecd58d966d73757c3f0 Mon Sep 17 00:00:00 2001 From: Simon Wimmesberger Date: Wed, 30 Sep 2020 13:21:26 +0200 Subject: [PATCH 08/10] Preserving attr order --- okhttp/src/test/java/okhttp3/osgi/OkHttpOsgiTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/okhttp/src/test/java/okhttp3/osgi/OkHttpOsgiTest.java b/okhttp/src/test/java/okhttp3/osgi/OkHttpOsgiTest.java index 7f47dfd38a6e..3810e459c851 100644 --- a/okhttp/src/test/java/okhttp3/osgi/OkHttpOsgiTest.java +++ b/okhttp/src/test/java/okhttp3/osgi/OkHttpOsgiTest.java @@ -90,7 +90,7 @@ private void prepareWorkspace(Workspace workspace) throws Exception { } private void addPlugin(Workspace workspace, String alias, Class pluginType, String key1, String value1, String key2, String value2) { - Map props = new HashMap<>(); + Map props = new LinkedHashMap<>(); props.put(key1, value1); props.put(key2, value2); addPlugin(workspace, alias, pluginType, props); From bb104b37181cc38c4506df4140e2d4389960fb56 Mon Sep 17 00:00:00 2001 From: Simon Wimmesberger Date: Wed, 30 Sep 2020 15:50:10 +0200 Subject: [PATCH 09/10] Added OSGi documentation to CONTRIBUTING.md --- CONTRIBUTING.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 973d385648ac..6967ab331b09 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,6 +46,14 @@ Running Android Tests $ ANDROID_SDK_ROOT=PATH_TO_ANDROID_HOME/sdk ./gradlew :android-test:connectedCheck +OSGi +--------------------- + +Ensure that added dependencies are OSGi compatible. +If added dependencies are optional the package imports in [okhttp/build.gradle][okhttp_build] need to +be adjusted. + + Committer's Guides ------------------ @@ -59,3 +67,4 @@ Committer's Guides [releasing]: http://square.github.io/okhttp/releasing/ [security]: http://square.github.io/okhttp/security/ [works_with_okhttp]: http://square.github.io/okhttp/works_with_okhttp/ + [okhttp_build]: https://github.com/square/okhttp/blob/master/okhttp/build.gradle \ No newline at end of file From 1d215e3f5fc9e8aab43886863c742bc4afedd3e7 Mon Sep 17 00:00:00 2001 From: Simon Wimmesberger Date: Wed, 30 Sep 2020 15:50:44 +0200 Subject: [PATCH 10/10] Added okhttpinternal package attribute to export package statement --- okhttp/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/okhttp/build.gradle b/okhttp/build.gradle index 169a9387e622..8aa3615d3eae 100644 --- a/okhttp/build.gradle +++ b/okhttp/build.gradle @@ -8,7 +8,7 @@ jar { bnd ''' Export-Package: \ okhttp3,\ - okhttp3.internal.* + okhttp3.internal.*;okhttpinternal=true;mandatory:=okhttpinternal Import-Package: \ android.*;resolution:=optional,\ dalvik.system;resolution:=optional,\