Skip to content

Commit 6d47ba6

Browse files
committed
Provide access to LazyConstant via internal fallback module
- introduces "JDK Fallbacks" module for internal use only - module delegates to JDK implementations if available and uses fallbacks if not - the accessors are meant to be temporary friend API with expiration date, only to bridge the gap between lower and upper runtime requirements
1 parent ac407de commit 6d47ba6

8 files changed

Lines changed: 288 additions & 0 deletions

File tree

nbbuild/cluster.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ nb.cluster.platform=\
215215
o.n.html.ko4j,\
216216
o.n.html.presenters.spi,\
217217
o.n.html.xhr4j,\
218+
o.n.jdk.fallback,\
218219
o.n.swing.laf.dark,\
219220
o.n.swing.laf.flatlaf,\
220221
o.n.swing.outline,\
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
Licensed to the Apache Software Foundation (ASF) under one
5+
or more contributor license agreements. See the NOTICE file
6+
distributed with this work for additional information
7+
regarding copyright ownership. The ASF licenses this file
8+
to you under the Apache License, Version 2.0 (the
9+
"License"); you may not use this file except in compliance
10+
with the License. You may obtain a copy of the License at
11+
12+
http://www.apache.org/licenses/LICENSE-2.0
13+
14+
Unless required by applicable law or agreed to in writing,
15+
software distributed under the License is distributed on an
16+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
KIND, either express or implied. See the License for the
18+
specific language governing permissions and limitations
19+
under the License.
20+
21+
-->
22+
<project basedir="." default="build" name="platform/o.n.jdk.fallback">
23+
<description>Builds, tests, and runs the project org.netbeans.jdk.fallback</description>
24+
<import file="../../nbbuild/templates/projectized.xml"/>
25+
</project>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Manifest-Version: 1.0
2+
AutoUpdate-Show-In-Client: false
3+
OpenIDE-Module: o.n.jdk.fallback/0
4+
OpenIDE-Module-Localizing-Bundle: org/netbeans/jdk/fallback/Bundle.properties
5+
OpenIDE-Module-Specification-Version: 0.1
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
javac.compilerargs=-Xlint
2+
javac.release=17
3+
4+
# javadoc.arch=${basedir}/arch.xml
5+
# javadoc.apichanges=${basedir}/apichanges.xml
6+
# javadoc.arch=${basedir}/arch.xml
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
Licensed to the Apache Software Foundation (ASF) under one
5+
or more contributor license agreements. See the NOTICE file
6+
distributed with this work for additional information
7+
regarding copyright ownership. The ASF licenses this file
8+
to you under the Apache License, Version 2.0 (the
9+
"License"); you may not use this file except in compliance
10+
with the License. You may obtain a copy of the License at
11+
12+
http://www.apache.org/licenses/LICENSE-2.0
13+
14+
Unless required by applicable law or agreed to in writing,
15+
software distributed under the License is distributed on an
16+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
KIND, either express or implied. See the License for the
18+
specific language governing permissions and limitations
19+
under the License.
20+
21+
-->
22+
<project xmlns="http://www.netbeans.org/ns/project/1">
23+
<type>org.netbeans.modules.apisupport.project</type>
24+
<configuration>
25+
<data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
26+
<code-name-base>o.n.jdk.fallback</code-name-base>
27+
<module-dependencies/>
28+
<test-dependencies>
29+
<test-type>
30+
<name>unit</name>
31+
<test-dependency>
32+
<code-name-base>org.netbeans.libs.junit4</code-name-base>
33+
<compile-dependency/>
34+
</test-dependency>
35+
<test-dependency>
36+
<code-name-base>org.netbeans.modules.nbjunit</code-name-base>
37+
<compile-dependency/>
38+
</test-dependency>
39+
</test-type>
40+
</test-dependencies>
41+
<!-- internal API-->
42+
<friend-packages>
43+
<friend>org.netbeans.modules.maven.indexer</friend>
44+
<package>org.netbeans.jdk.fallback.lang</package>
45+
</friend-packages>
46+
</data>
47+
</configuration>
48+
</project>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
OpenIDE-Module-Name=Simple JDK Fallbacks
19+
OpenIDE-Module-Display-Category=Libraries
20+
OpenIDE-Module-Short-Description=Provides internal, non-permanent and possibly incomplete fallbacks for JDK APIs\
21+
which are useful for NetBeans but not covered by the minimal run requirements yet.
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
2+
package org.netbeans.jdk.fallback.lang;
3+
4+
import java.lang.invoke.MethodHandle;
5+
import java.lang.invoke.MethodHandles;
6+
import java.lang.invoke.MethodType;
7+
import java.util.Objects;
8+
import java.util.function.Supplier;
9+
import java.util.logging.Level;
10+
import java.util.logging.Logger;
11+
12+
/**
13+
* Delegates to JDK's LazyConstant and provides a fallback implementation if not available.
14+
*
15+
* Internal API, may be removed when no longer needed.
16+
*
17+
* @author mbien
18+
*/
19+
public final class NBLazyConstant {
20+
21+
private static final MethodHandle lazyConstantFactory;
22+
23+
static {
24+
Logger log = Logger.getLogger(NBLazyConstant.class.getName());
25+
MethodHandle mh = null;
26+
try {
27+
if (Boolean.getBoolean("nb.jdk.LazyConstant.usefallback")) {
28+
mh = null;
29+
log.log(Level.INFO, "using fallback");
30+
} else if (Runtime.version().feature() >= 26) {
31+
Class<?> entryPoint = Class.forName("java.lang.LazyConstant");
32+
mh = MethodHandles.lookup().findStatic(entryPoint, "of", MethodType.methodType(entryPoint, Supplier.class))
33+
.asType(MethodType.methodType(Supplier.class, Supplier.class));
34+
} else if (Runtime.version().feature() == 25) {
35+
Class<?> entryPoint = Class.forName("java.lang.StableValue");
36+
mh = MethodHandles.lookup().findStatic(entryPoint, "supplier", MethodType.methodType(Supplier.class, Supplier.class));
37+
}
38+
// dryrun - just to be sure
39+
if (mh != null) {
40+
Supplier<?> probe = () -> true;
41+
((Supplier<?>)mh.invokeExact(probe)).get();
42+
}
43+
} catch (Throwable ex) {
44+
mh = null;
45+
log.log(Level.FINE, "using fallback", ex);
46+
}
47+
lazyConstantFactory = mh;
48+
log.log(Level.FINE, () -> "impl=" + String.valueOf(lazyConstantFactory));
49+
}
50+
51+
private NBLazyConstant() {}
52+
53+
/**
54+
* Create a {@link Supplier} for a lazily initializing constant.
55+
* @param computingFunction Factory to create the constant, only called once.
56+
* @return Returns the constant, never null.
57+
*/
58+
@SuppressWarnings("unchecked")
59+
public static <T> Supplier<T> of(Supplier<? extends T> computingFunction) {
60+
Objects.requireNonNull(computingFunction);
61+
if (computingFunction instanceof DoubleCheckedFallback<? extends T> lc) {
62+
return (Supplier<T>) lc;
63+
}
64+
if (lazyConstantFactory != null) {
65+
try {
66+
return (Supplier<T>) lazyConstantFactory.invokeExact(computingFunction);
67+
} catch (Throwable ex) {
68+
throw new RuntimeException(ex);
69+
}
70+
} else {
71+
return new DoubleCheckedFallback<>(computingFunction);
72+
}
73+
}
74+
75+
private static class DoubleCheckedFallback<T> implements Supplier<T> {
76+
77+
private volatile T constant;
78+
private Supplier<? extends T> factory;
79+
80+
private DoubleCheckedFallback(Supplier<? extends T> factory) {
81+
this.factory = factory;
82+
}
83+
84+
@Override
85+
public T get() {
86+
T c = constant;
87+
if (c == null) {
88+
synchronized (this) {
89+
c = constant;
90+
if (c == null) {
91+
c = factory.get();
92+
Objects.requireNonNull(c);
93+
constant = c;
94+
factory = null;
95+
}
96+
}
97+
}
98+
return c;
99+
}
100+
101+
@Override
102+
public String toString() {
103+
return getClass().getSimpleName() + "{factory=" + factory + ", constant=" + constant + '}';
104+
}
105+
}
106+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.netbeans.jdk.fallback.lang;
20+
21+
import java.util.function.Supplier;
22+
import org.junit.Test;
23+
24+
import static org.junit.Assert.*;
25+
import static org.junit.Assume.assumeTrue;
26+
27+
public class NBLazyConstantTest {
28+
29+
@Test
30+
public void testImpl() {
31+
32+
Supplier<Boolean> lazy = NBLazyConstant.of(() -> true);
33+
assertNotNull(lazy);
34+
assertTrue(lazy.get());
35+
36+
String impl;
37+
if (Runtime.version().feature() >= 26) {
38+
impl = "LazyConstant";
39+
} else if (Runtime.version().feature() == 25) {
40+
impl = "StableSupplier";
41+
} else {
42+
impl = "DoubleCheckedFallback";
43+
}
44+
assertTrue(impl + " expected but got " + lazy.getClass(), lazy.getClass().getSimpleName().contains(impl));
45+
}
46+
47+
@Test
48+
public void testConstant() {
49+
Supplier<Double> lazy = NBLazyConstant.of(() -> Math.random());
50+
assertNotNull(lazy);
51+
assertEquals(lazy.get(), lazy.get());
52+
assertEquals(lazy.get(), lazy.get());
53+
assertEquals(lazy.get(), lazy.get());
54+
}
55+
56+
@Test
57+
public void testNPEonNullValue() {
58+
59+
// StableValue allows null, we use the LazyConstant spec
60+
assumeTrue(Runtime.version().feature() != 25);
61+
62+
Supplier<Object> lazy = NBLazyConstant.of(() -> null);
63+
assertNotNull(lazy);
64+
65+
try {
66+
lazy.get();
67+
fail();
68+
} catch (NullPointerException good) {}
69+
70+
try {
71+
lazy.get();
72+
fail();
73+
} catch (NullPointerException stillGood) {}
74+
}
75+
76+
}

0 commit comments

Comments
 (0)