Skip to content

Commit fefc9d8

Browse files
First tests with Java 21
1 parent ef752d0 commit fefc9d8

File tree

3 files changed

+185
-4
lines changed

3 files changed

+185
-4
lines changed

spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/access/modifier/FieldAccessModifier.java

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,36 @@
1616
package software.xdev.spring.data.eclipse.store.repository.access.modifier;
1717

1818
import java.lang.reflect.Field;
19+
import java.lang.reflect.InvocationTargetException;
1920
import java.util.Objects;
21+
import java.util.concurrent.atomic.AtomicInteger;
2022

2123

2224
/**
2325
* Provides a way to open member variables (fields) to changes.
2426
*/
2527
public interface FieldAccessModifier<E> extends AutoCloseable
2628
{
29+
AtomicInteger javaVersion = new AtomicInteger(0);
30+
2731
static <T> FieldAccessModifierToEditable<T> makeFieldEditable(final Field field, final T sourceObject)
32+
throws InvocationTargetException, IllegalAccessException
2833
{
29-
return new FieldEditableMakerForJavaLowerThan18<>(
34+
if(javaVersion.get() == 0)
35+
{
36+
javaVersion.set(getJavaVersion());
37+
}
38+
if(javaVersion.get() < 18)
39+
{
40+
return new FieldEditableMakerForJavaLowerThan18<>(
41+
Objects.requireNonNull(field),
42+
Objects.requireNonNull(sourceObject)
43+
);
44+
}
45+
return new FieldEditableMakerForJavaHigherOrEqualTo18<>(
3046
Objects.requireNonNull(field),
31-
Objects.requireNonNull(sourceObject));
47+
Objects.requireNonNull(sourceObject)
48+
);
3249
}
3350

3451
static <T> FieldAccessModifier<T> makeFieldReadable(final Field field, final T sourceObject)
@@ -37,4 +54,22 @@ static <T> FieldAccessModifier<T> makeFieldReadable(final Field field, final T s
3754
}
3855

3956
Object getValueOfField(E objectOfFieldToRead) throws IllegalAccessException;
57+
58+
private static int getJavaVersion()
59+
{
60+
String version = System.getProperty("java.version");
61+
if(version.startsWith("1."))
62+
{
63+
version = version.substring(2, 3);
64+
}
65+
else
66+
{
67+
final int dot = version.indexOf(".");
68+
if(dot != -1)
69+
{
70+
version = version.substring(0, dot);
71+
}
72+
}
73+
return Integer.parseInt(version);
74+
}
4075
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*
2+
* Copyright © 2023 XDEV Software (https://xdev.software)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package software.xdev.spring.data.eclipse.store.repository.access.modifier;
17+
18+
import java.lang.reflect.Field;
19+
import java.lang.reflect.InvocationTargetException;
20+
import java.lang.reflect.Method;
21+
import java.lang.reflect.Modifier;
22+
import java.util.Arrays;
23+
import java.util.Objects;
24+
25+
import org.slf4j.Logger;
26+
import org.slf4j.LoggerFactory;
27+
28+
29+
/**
30+
* {@link Field}s are made <b>modifiable/editable</b> with this class. Should be created through
31+
* {@link FieldAccessModifier}.
32+
* <p>
33+
* Can be used like this because it is {@link AutoCloseable}:
34+
* <p>
35+
* <code>
36+
* try(final FieldAccessModifier fem = FieldAccessModifier.makeFieldEditable(field,sourceObject))<br/> {<br/> // Do
37+
* something with the field.<br/> }<br/>
38+
* </code>
39+
* <p>
40+
* This is a quite shaky way to make final variables "unfinal". Should work for java versions lower than 18.
41+
* <p>
42+
* <a href="https://stackoverflow.com/a/56043252/2351407">https://stackoverflow.com/a/56043252/2351407</a>
43+
*/
44+
@SuppressWarnings("java:S3011")
45+
public class FieldEditableMakerForJavaHigherOrEqualTo18<E> implements FieldAccessModifierToEditable<E>
46+
{
47+
private static final Logger LOG = LoggerFactory.getLogger(FieldEditableMakerForJavaHigherOrEqualTo18.class);
48+
private final Field field;
49+
private final Field modifiersField;
50+
private final boolean wasFinal;
51+
private final boolean wasAccessible;
52+
53+
FieldEditableMakerForJavaHigherOrEqualTo18(final Field field, final E sourceObject)
54+
throws InvocationTargetException, IllegalAccessException
55+
{
56+
this.field = Objects.requireNonNull(field);
57+
58+
final Method[] classMethods = Class.class.getDeclaredMethods();
59+
60+
final Method declaredFieldMethod = Arrays.stream(classMethods)
61+
.filter(x -> Objects.equals(x.getName(), "getDeclaredFields0"))
62+
.findAny()
63+
.orElseThrow();
64+
65+
declaredFieldMethod.setAccessible(true);
66+
67+
final Field[] declaredFieldsOfField = (Field[])declaredFieldMethod.invoke(Field.class, false);
68+
69+
this.modifiersField = Arrays.stream(declaredFieldsOfField)
70+
.filter(x -> Objects.equals(x.getName(), "modifiers"))
71+
.findAny()
72+
.orElseThrow();
73+
this.modifiersField.setAccessible(true);
74+
75+
final int fieldModifiers = field.getModifiers();
76+
this.wasAccessible = field.canAccess(Objects.requireNonNull(sourceObject));
77+
this.wasFinal = Modifier.isFinal(fieldModifiers);
78+
this.startMakingEditable();
79+
}
80+
81+
private void startMakingEditable()
82+
{
83+
if(!this.wasAccessible)
84+
{
85+
this.field.setAccessible(true);
86+
}
87+
if(this.wasFinal)
88+
{
89+
if(LOG.isTraceEnabled())
90+
{
91+
LOG.trace(
92+
"Make field {}#{} editable.",
93+
this.field.getDeclaringClass().getSimpleName(),
94+
this.field.getName());
95+
}
96+
try
97+
{
98+
this.modifiersField.setInt(this.field, this.field.getModifiers() & ~Modifier.FINAL);
99+
}
100+
catch(final IllegalAccessException e)
101+
{
102+
throw new UnsupportedOperationException(e);
103+
}
104+
}
105+
}
106+
107+
@Override
108+
public void close()
109+
{
110+
if(this.wasFinal)
111+
{
112+
if(LOG.isTraceEnabled())
113+
{
114+
LOG.trace(
115+
"Make field {}#{} immutable.",
116+
this.field.getDeclaringClass().getSimpleName(),
117+
this.field.getName());
118+
}
119+
try
120+
{
121+
this.modifiersField.setInt(this.field, this.field.getModifiers() | Modifier.FINAL);
122+
}
123+
catch(final IllegalAccessException e)
124+
{
125+
throw new UnsupportedOperationException(e);
126+
}
127+
}
128+
if(!this.wasAccessible)
129+
{
130+
this.field.setAccessible(this.wasAccessible);
131+
}
132+
}
133+
134+
@Override
135+
public Object getValueOfField(final E objectOfFieldToRead) throws IllegalAccessException
136+
{
137+
return this.field.get(objectOfFieldToRead);
138+
}
139+
140+
@Override
141+
public void writeValueOfField(final E objectOfFieldToWriteTo, final Object valueToWrite)
142+
throws IllegalAccessException
143+
{
144+
this.field.set(objectOfFieldToWriteTo, valueToWrite);
145+
}
146+
}

spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/access/modifier/FieldEditableMakerForJavaLowerThan18.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public class FieldEditableMakerForJavaLowerThan18<E> implements FieldAccessModif
7979
private void startMakingEditable()
8080
{
8181
this.field.setAccessible(true);
82-
if(!this.wasFinal)
82+
if(this.wasFinal)
8383
{
8484
if(LOG.isTraceEnabled())
8585
{
@@ -95,7 +95,7 @@ private void startMakingEditable()
9595
@Override
9696
public void close()
9797
{
98-
if(!this.wasFinal)
98+
if(this.wasFinal)
9999
{
100100
if(LOG.isTraceEnabled())
101101
{

0 commit comments

Comments
 (0)