Skip to content

Commit 3f533d5

Browse files
committed
more lenient osgi.ee for unknown JDKs
For a yet to bnd unknown JDK (e.g. JDK-10000) Instead of ``` Require-Capability: osgi.ee;filter:="(osgi.ee=UNKNOWN)" ``` we now create Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=9955))" for a .class file with u2 major_version 10000 (9955 == 10000 - 45) Signed-off-by: Christoph Rueger <chrisrueger@gmail.com>
1 parent 1c40c8c commit 3f533d5

File tree

3 files changed

+133
-7
lines changed

3 files changed

+133
-7
lines changed

biz.aQute.bndlib.tests/test/test/ClazzTest.java

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,20 @@
66
import static org.junit.jupiter.api.Assertions.assertTrue;
77
import static org.junit.jupiter.api.Assertions.fail;
88

9+
import java.io.ByteArrayInputStream;
910
import java.io.File;
1011
import java.io.FileInputStream;
1112
import java.io.Serializable;
1213
import java.lang.annotation.ElementType;
1314
import java.lang.annotation.Target;
15+
import java.nio.file.Files;
1416
import java.util.ArrayList;
1517
import java.util.EnumSet;
1618
import java.util.List;
1719
import java.util.Set;
1820
import java.util.concurrent.atomic.AtomicReference;
21+
import java.util.jar.Attributes;
22+
import java.util.jar.Manifest;
1923

2024
import org.junit.jupiter.api.Test;
2125
import org.junit.jupiter.api.condition.EnabledForJreRange;
@@ -30,16 +34,21 @@
3034
import aQute.bnd.osgi.ClassDataCollector;
3135
import aQute.bnd.osgi.Clazz;
3236
import aQute.bnd.osgi.Clazz.FieldDef;
37+
import aQute.bnd.osgi.Clazz.JAVA;
3338
import aQute.bnd.osgi.Clazz.MethodDef;
3439
import aQute.bnd.osgi.Clazz.QUERY;
40+
import aQute.bnd.osgi.Constants;
3541
import aQute.bnd.osgi.Descriptors;
3642
import aQute.bnd.osgi.Descriptors.PackageRef;
43+
import aQute.bnd.osgi.EmbeddedResource;
3744
import aQute.bnd.osgi.FileResource;
3845
import aQute.bnd.osgi.Instruction;
3946
import aQute.bnd.osgi.Jar;
4047
import aQute.bnd.osgi.Macro;
48+
import aQute.bnd.osgi.Resource;
4149
import aQute.bnd.xmlattribute.XMLAttributeFinder;
4250
import aQute.lib.io.IO;
51+
import aQute.lib.manifest.ManifestUtil;
4352

4453
public class ClazzTest {
4554

@@ -1111,4 +1120,77 @@ public void kotlin_LambdaClass() throws Exception {
11111120
}
11121121
}
11131122

1123+
1124+
/**
1125+
* This test creates a fake java .class file with an imaginary large major
1126+
* version (10000) to test how our Analyzer handles class files of a yet
1127+
* unknown future JDK.
1128+
*/
1129+
@Test
1130+
void testUnknownJDKClass() throws Exception {
1131+
1132+
1133+
File file = IO.getFile("bin_test/test/ClazzTest$Inner.class");
1134+
byte[] fakeClassBytes = Files.readAllBytes(file.toPath());
1135+
1136+
int newMajor = 10_000;
1137+
int expected = 9955; // newMajor - 45;
1138+
// Patch major version (u2 big-endian)
1139+
// https://docs.oracle.com/javase/specs/jvms/se21/html/jvms-4.html
1140+
fakeClassBytes[6] = (byte) ((newMajor >> 8) & 0xFF);
1141+
fakeClassBytes[7] = (byte) (newMajor & 0xFF);
1142+
1143+
try (Jar jar = new Jar("future");
1144+
Analyzer a = new Analyzer(jar)) {
1145+
1146+
Resource r = new EmbeddedResource(fakeClassBytes, fakeClassBytes.length);
1147+
1148+
Clazz c = new Clazz(a, "", r);
1149+
c.parseClassFile(new ByteArrayInputStream(fakeClassBytes), new ClassDataCollector() {});
1150+
a.getClassspace()
1151+
.put(c.getClassName(), c);
1152+
1153+
assertThat(c.getMajorVersion()).isEqualTo(newMajor);
1154+
1155+
Manifest calcManifest = a.calcManifest();
1156+
ManifestUtil.write(calcManifest, System.out);
1157+
Attributes mainAttributes = calcManifest.getMainAttributes();
1158+
assertThat(mainAttributes.getValue(Constants.REQUIRE_CAPABILITY))
1159+
.contains("(&(osgi.ee=JavaSE)(version=" + expected + "))");
1160+
assertThat(a.getEEs()).containsExactly(JAVA.UNKNOWN);
1161+
1162+
1163+
}
1164+
catch (Exception e) {
1165+
e.printStackTrace();
1166+
throw e;
1167+
}
1168+
1169+
}
1170+
1171+
@Test
1172+
void testBuildEEFilter_lenientKnown() {
1173+
// lenient = true but version within known range
1174+
String result = Clazz.JAVA.buildEEFilterLenient(55); // within range
1175+
assertEquals("(&(osgi.ee=JavaSE)(version=11))", result);
1176+
}
1177+
1178+
1179+
@Test
1180+
void testBuildEEFilter_lenientTooLow() {
1181+
// version < 0 -> UNKNOWN
1182+
String result = Clazz.JAVA.buildEEFilterLenient(40);
1183+
assertEquals("(&(osgi.ee=JavaSE)(version=UNKNOWN))", result);
1184+
}
1185+
1186+
@Test
1187+
void testBuildEEFilter_lenientTooHigh() {
1188+
// version >= JAVA.values.length - 1 -> eeFilter(version)
1189+
int maxmajor = Clazz.JAVA.values()[Clazz.JAVA.values().length - 2].getMajor();
1190+
int tooHigh = maxmajor + 10;
1191+
int expected = tooHigh - 45;
1192+
String result = Clazz.JAVA.buildEEFilterLenient(tooHigh);
1193+
// version = max. known version + 1
1194+
assertEquals("(&(osgi.ee=JavaSE)(version=" + expected + "))", result);
1195+
}
11141196
}

biz.aQute.bndlib/src/aQute/bnd/osgi/Analyzer.java

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ public class Analyzer extends Processor {
118118
private final static Logger logger = LoggerFactory.getLogger(Analyzer.class);
119119
private final static Version frameworkR7 = new Version("1.9");
120120
private final SortedSet<Clazz.JAVA> ees = new TreeSet<>();
121+
private int highestMajorJavaVersion = 0;
121122

122123
// Bundle parameters
123124
private Jar dot;
@@ -231,8 +232,14 @@ private void analyze0() throws Exception {
231232
classspace.values()
232233
.stream()
233234
.filter(c -> !c.isModule())
234-
.map(Clazz::getFormat)
235-
.forEach(ees::add);
235+
.forEach(c -> {
236+
ees.add(c.getFormat());
237+
int majorVersion = c.getMajorVersion();
238+
if(majorVersion > highestMajorJavaVersion) {
239+
highestMajorJavaVersion = majorVersion;
240+
}
241+
242+
});
236243

237244
try (ClassDataCollectors cds = new ClassDataCollectors(this)) {
238245
List<ClassParser> parsers = getPlugins(ClassParser.class);
@@ -445,6 +452,8 @@ public void reset() {
445452
packagesVisited.clear();
446453
nonClassReferences.clear();
447454
bcpTypes.clear();
455+
highestMajorJavaVersion = 0;
456+
ees.clear();
448457
}
449458

450459
private void analyzeContent() throws Exception {
@@ -1301,9 +1310,17 @@ public Jar findClasspathEntry(String bsn, String r) {
13011310
* highest found profile is added. This only works for java packages.
13021311
*/
13031312
private String doEEProfiles(JAVA highest) throws IOException {
1313+
1314+
String highestFilter;
1315+
if (highest == aQute.bnd.osgi.Clazz.JAVA.UNKNOWN) {
1316+
highestFilter = aQute.bnd.osgi.Clazz.JAVA.buildEEFilterLenient(highestMajorJavaVersion);
1317+
} else {
1318+
highestFilter = highest.getFilter();
1319+
}
1320+
13041321
String ee = getProperty(EEPROFILE);
13051322
if (ee == null)
1306-
return highest.getFilter();
1323+
return highestFilter;
13071324

13081325
ee = ee.trim();
13091326

@@ -1312,7 +1329,7 @@ private String doEEProfiles(JAVA highest) throws IOException {
13121329
if (ee.equals(EEPROFILE_AUTO_ATTRIBUTE)) {
13131330
profiles = highest.getProfiles();
13141331
if (profiles == null)
1315-
return highest.getFilter();
1332+
return highestFilter;
13161333
} else {
13171334
profiles = OSGiHeader.parseProperties(ee)
13181335
.stream()
@@ -1349,11 +1366,11 @@ private String doEEProfiles(JAVA highest) throws IOException {
13491366
//
13501367
// Ouch, outside any profile
13511368
//
1352-
return highest.getFilter();
1369+
return highestFilter;
13531370
}
13541371
}
13551372

1356-
String filter = highest.getFilter();
1373+
String filter = highestFilter;
13571374
if (!found.isEmpty())
13581375
filter = filter.replaceAll("JavaSE", "JavaSE/" + found.last());
13591376
// TODO a more elegant way to build the filter, we now assume JavaSE

biz.aQute.bndlib/src/aQute/bnd/osgi/Clazz.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ public Map<String, Set<String>> getProfiles() throws IOException {
178178
this.major = ordinal() + 45;
179179
String version = Integer.toString(ordinal() + 1);
180180
this.ee = "JavaSE-" + version;
181-
this.filter = "(&(osgi.ee=JavaSE)(version=" + version + "))";
181+
this.filter = eeFilter(version);
182182
}
183183

184184
JAVA(String ee, String filter) {
@@ -228,6 +228,28 @@ public String getEE() {
228228
return ee;
229229
}
230230

231+
/**
232+
* Returns an eeFilter String also supporting yet unknown JDKs (lenient)
233+
*/
234+
public static String buildEEFilterLenient(int major) {
235+
236+
// lenient: We try to return a useful filter
237+
// even for yet unknown JDKs
238+
int version = major - 45;
239+
if ((version < 0)) {
240+
return eeFilter("UNKNOWN");
241+
} else if (version >= (JAVA.values.length - 1)) {
242+
// yet UNKNOWN
243+
return eeFilter(Integer.toString(version));
244+
}
245+
246+
return format(major).getFilter();
247+
}
248+
249+
private static String eeFilter(String version) {
250+
return "(&(osgi.ee=JavaSE)(version=" + version + "))";
251+
}
252+
231253
public String getFilter() {
232254
return filter;
233255
}
@@ -1916,6 +1938,11 @@ public JAVA getFormat() {
19161938

19171939
}
19181940

1941+
public int getMajorVersion() {
1942+
return classFile.major_version;
1943+
1944+
}
1945+
19191946
public static String objectDescriptorToFQN(String string) {
19201947
if ((string.startsWith("L") || string.startsWith("T")) && string.endsWith(";"))
19211948
return string.substring(1, string.length() - 1)

0 commit comments

Comments
 (0)