diff --git a/microsphere-annotation-processor/pom.xml b/microsphere-annotation-processor/pom.xml index 26473f3d3..238250266 100644 --- a/microsphere-annotation-processor/pom.xml +++ b/microsphere-annotation-processor/pom.xml @@ -47,18 +47,10 @@ test - + io.github.microsphere-projects - microsphere-jdk-tools - ${revision} - test - - - - - io.github.microsphere-projects - microsphere-java-test + microsphere-annotation-test ${revision} test diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/FilerProcessor.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/FilerProcessor.java index 5a34c0fa5..22d4e0086 100644 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/FilerProcessor.java +++ b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/FilerProcessor.java @@ -97,7 +97,7 @@ public T processInFiler(ThrowableFunction filerCallback, BiFunctio * @return the {@link JavaFileManager} */ public JavaFileManager getJavaFileManager() { - return processInFiler(filer -> getFieldValue(filer, "fileManager")); + return processInFiler(filer -> getFieldValue(true, filer, "fileManager")); } } diff --git a/microsphere-annotation-processor/src/test/resources/logback-test.xml b/microsphere-annotation-processor/src/test/resources/logback-test.xml deleted file mode 100644 index e3375b74c..000000000 --- a/microsphere-annotation-processor/src/test/resources/logback-test.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - ${ENCODER_PATTERN} - - - - - - - - \ No newline at end of file diff --git a/microsphere-annotation-test/pom.xml b/microsphere-annotation-test/pom.xml new file mode 100644 index 000000000..4f2ffcf75 --- /dev/null +++ b/microsphere-annotation-test/pom.xml @@ -0,0 +1,93 @@ + + + + io.github.microsphere-projects + microsphere-java-parent + ${revision} + ../microsphere-java-parent/pom.xml + + 4.0.0 + + io.github.microsphere-projects + microsphere-annotation-test + ${revision} + jar + + Microsphere :: Annotation :: Test + Microsphere Annotation Test + + + + + + io.github.microsphere-projects + microsphere-java-test + ${revision} + + + + + io.github.microsphere-projects + microsphere-jdk-tools + ${revision} + + + + + org.junit.jupiter + junit-jupiter + true + + + + + org.mockito + mockito-core + true + + + + org.junit.jupiter + junit-jupiter-engine + true + + + + + ch.qos.logback + logback-classic + true + + + + + javax.ws.rs + javax.ws.rs-api + true + + + + + javax.xml.ws + jaxws-api + true + + + + + org.springframework + spring-context + true + + + + org.springframework + spring-web + true + + + + + \ No newline at end of file diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AbstractAnnotationProcessingTest.java b/microsphere-annotation-test/src/main/java/io/microsphere/test/annotation/processing/AbstractAnnotationProcessingTest.java similarity index 100% rename from microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AbstractAnnotationProcessingTest.java rename to microsphere-annotation-test/src/main/java/io/microsphere/test/annotation/processing/AbstractAnnotationProcessingTest.java diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java b/microsphere-annotation-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java similarity index 100% rename from microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java rename to microsphere-annotation-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/CompilerInvocationInterceptor.java b/microsphere-annotation-test/src/main/java/io/microsphere/test/annotation/processing/CompilerInvocationInterceptor.java similarity index 100% rename from microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/CompilerInvocationInterceptor.java rename to microsphere-annotation-test/src/main/java/io/microsphere/test/annotation/processing/CompilerInvocationInterceptor.java diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java b/microsphere-annotation-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java similarity index 100% rename from microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java rename to microsphere-annotation-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java diff --git a/microsphere-java-core/pom.xml b/microsphere-java-core/pom.xml index c31c60b8c..a46ea1d93 100644 --- a/microsphere-java-core/pom.xml +++ b/microsphere-java-core/pom.xml @@ -62,6 +62,14 @@ test + + + io.github.microsphere-projects + microsphere-java-test + ${revision} + test + + com.google.code.findbugs diff --git a/microsphere-java-core/src/main/java/io/microsphere/beans/BeanProperty.java b/microsphere-java-core/src/main/java/io/microsphere/beans/BeanProperty.java index b6576518f..a35589ca3 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/beans/BeanProperty.java +++ b/microsphere-java-core/src/main/java/io/microsphere/beans/BeanProperty.java @@ -128,7 +128,7 @@ public static BeanProperty of(@Nonnull Object bean, @Nonnull String propertyName PropertyDescriptor descriptor = new PropertyDescriptor(propertyName, beanClass); BeanProperty beanProperty = new BeanProperty(propertyName, beanClass, descriptor); Method getterMethod = descriptor.getReadMethod(); - Object propertyValue = invokeMethod(bean, getterMethod); + Object propertyValue = invokeMethod(true, bean, getterMethod); beanProperty.value = (propertyValue); return beanProperty; }); diff --git a/microsphere-java-core/src/main/java/io/microsphere/classloading/AbstractURLClassPathHandle.java b/microsphere-java-core/src/main/java/io/microsphere/classloading/AbstractURLClassPathHandle.java index 3a9a4a76d..64a7fcf30 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/classloading/AbstractURLClassPathHandle.java +++ b/microsphere-java-core/src/main/java/io/microsphere/classloading/AbstractURLClassPathHandle.java @@ -122,8 +122,9 @@ public URL[] getURLs(ClassLoader classLoader) { if (classLoader == null) { return EMPTY_URL_ARRAY; } - Object ucp = getFieldValue(classLoader, findUcpField(classLoader)); - return ucp == null ? EMPTY_URL_ARRAY : invokeMethod(ucp, "getURLs"); + + Object ucp = getFieldValue(true, classLoader, findUcpField(classLoader)); + return ucp == null ? EMPTY_URL_ARRAY : invokeMethod(true, ucp, "getURLs"); } @Override @@ -134,10 +135,10 @@ public final boolean removeURL(ClassLoader classLoader, URL url) { initializeLoaders(classLoader); - Object ucp = getFieldValue(classLoader, findUcpField(classLoader)); - Collection urls = getFieldValue(ucp, getUrlsField()); - Collection path = getFieldValue(ucp, getPathField()); - Collection loaders = getFieldValue(ucp, getLoadersField()); + Object ucp = getFieldValue(true, classLoader, findUcpField(classLoader)); + Collection urls = getFieldValue(true, ucp, getUrlsField()); + Collection path = getFieldValue(true, ucp, getPathField()); + Collection loaders = getFieldValue(true, ucp, getLoadersField()); String basePath = resolveBasePath(url); @@ -148,7 +149,7 @@ public final boolean removeURL(ClassLoader classLoader, URL url) { Iterator iterator = loaders.iterator(); while (iterator.hasNext()) { Object loader = iterator.next(); - URL base = getFieldValue(loader, getBaseField()); + URL base = getFieldValue(true, loader, getBaseField()); String basePath_ = resolveBasePath(base); if (Objects.equals(basePath_, basePath)) { if (logger.isTraceEnabled()) { diff --git a/microsphere-java-core/src/main/java/io/microsphere/convert/ByteArrayToObjectConverter.java b/microsphere-java-core/src/main/java/io/microsphere/convert/ByteArrayToObjectConverter.java index e3f5911ea..9b0178f7e 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/convert/ByteArrayToObjectConverter.java +++ b/microsphere-java-core/src/main/java/io/microsphere/convert/ByteArrayToObjectConverter.java @@ -16,11 +16,11 @@ */ package io.microsphere.convert; -import io.microsphere.io.DefaultDeserializer; - import java.io.IOException; import java.io.Serializable; +import static io.microsphere.io.serializer.DefaultDeserializer.DEFAULT_DESERIALIZER; + /** * The class coverts the {@link byte[] byte array} object to be a {@link Object} instance . * @@ -38,7 +38,7 @@ public class ByteArrayToObjectConverter extends AbstractConverterexecutor is null */ protected AbstractEventDispatcher(Executor executor) { - assertNotNull(executor, () -> "executor must not be null"); + assertNotNull(executor, () -> "The 'executor' must not be null"); this.executor = executor; this.loadEventListenerInstances(); } @Override public void addEventListener(EventListener listener) throws NullPointerException, IllegalArgumentException { - Listenable.assertListener(listener); + assertListener(listener); doInListener(listener, listeners -> { addIfAbsent(listeners, listener); }); @@ -121,7 +122,7 @@ public void addEventListener(EventListener listener) throws NullPointerExcept @Override public void removeEventListener(EventListener listener) throws NullPointerException, IllegalArgumentException { - Listenable.assertListener(listener); + assertListener(listener); doInListener(listener, listeners -> listeners.remove(listener)); } @@ -205,15 +206,15 @@ void doInListener(Class eventType, Consumer - * It could be override by the sub-class + * It could be overridden by the sub-class * * @see EventListener * @see ServiceLoader#load(Class) */ protected void loadEventListenerInstances() { - execute(() -> loadServicesList(EventListener.class, getDefaultClassLoader()) + execute(() -> loadServicesList(EventListener.class, getDefaultClassLoader(), true) .stream() .sorted() - .forEach(this::addEventListener), e -> logger.trace(e.getMessage(), e)); + .forEach(this::addEventListener), e -> logger.trace(e.getMessage())); } } diff --git a/microsphere-java-core/src/main/java/io/microsphere/event/EventDispatcher.java b/microsphere-java-core/src/main/java/io/microsphere/event/EventDispatcher.java index 877efa2a1..71350ee09 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/event/EventDispatcher.java +++ b/microsphere-java-core/src/main/java/io/microsphere/event/EventDispatcher.java @@ -16,6 +16,9 @@ */ package io.microsphere.event; +import io.microsphere.annotation.Nonnull; +import io.microsphere.annotation.Nullable; + import java.util.concurrent.Executor; /** @@ -64,6 +67,7 @@ public interface EventDispatcher extends Listenable> { * * @return the default implementation of {@link EventDispatcher} */ + @Nonnull static EventDispatcher newDefault() { return new DirectEventDispatcher(); } @@ -72,12 +76,24 @@ static EventDispatcher newDefault() { * The parallel implementation of {@link EventDispatcher} with the specified {@link Executor} * * @param executor {@link Executor} - * @return the default implementation of {@link EventDispatcher} + * @return the parallel implementation of {@link EventDispatcher} */ - static EventDispatcher parallel(Executor executor) { + @Nonnull + static EventDispatcher parallel(@Nonnull Executor executor) { return new ParallelEventDispatcher(executor); } + /** + * Create an instance of {@link EventDispatcher} with the specified {@link Executor} + * + * @param executor {@link Executor}, can be null + * @return an instance of {@link EventDispatcher} + */ + @Nonnull + static EventDispatcher of(@Nullable Executor executor) { + return executor == null ? newDefault() : parallel(executor); + } + /** * Dispatch a event to the registered {@link EventListener event listeners} * diff --git a/microsphere-java-core/src/main/java/io/microsphere/event/EventListener.java b/microsphere-java-core/src/main/java/io/microsphere/event/EventListener.java index 7e5643784..0c7136f3b 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/event/EventListener.java +++ b/microsphere-java-core/src/main/java/io/microsphere/event/EventListener.java @@ -20,8 +20,10 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.util.List; import java.util.Objects; +import static io.microsphere.lang.invoke.LambdaUtils.resolveLambdaMethodParameterTypes; import static io.microsphere.reflect.TypeUtils.asClass; import static io.microsphere.reflect.TypeUtils.getAllParameterizedTypes; import static io.microsphere.reflect.TypeUtils.isAssignableFrom; @@ -130,14 +132,20 @@ static Class findEventType(Class listenerClass) { Class eventType = null; if (isAssignableFrom(EventListener.class, listenerClass)) { - eventType = getAllParameterizedTypes(listenerClass) - .stream() - .map(EventListener::findEventType) - .filter(Objects::nonNull) - .findAny() - .orElse((Class) findEventType(listenerClass.getSuperclass())); + List> parameterTypes = resolveLambdaMethodParameterTypes(listenerClass, EventListener.class); + if (parameterTypes.size() == 1) { + // Lambda Case + eventType = (Class) parameterTypes.get(0); + } else { + // Normal Case + eventType = getAllParameterizedTypes(listenerClass) + .stream() + .map(EventListener::findEventType) + .filter(Objects::nonNull) + .findAny() + .orElse((Class) findEventType(listenerClass.getSuperclass())); + } } - return eventType; } diff --git a/microsphere-java-core/src/main/java/io/microsphere/event/Listenable.java b/microsphere-java-core/src/main/java/io/microsphere/event/Listenable.java index cbc798d28..9a2ab47f0 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/event/Listenable.java +++ b/microsphere-java-core/src/main/java/io/microsphere/event/Listenable.java @@ -23,10 +23,7 @@ import static io.microsphere.collection.CollectionUtils.addAll; import static io.microsphere.collection.ListUtils.newArrayList; -import static io.microsphere.text.FormatUtils.format; import static io.microsphere.util.Assert.assertNotNull; -import static io.microsphere.util.ClassUtils.getTypeName; -import static java.lang.reflect.Modifier.isFinal; import static java.util.stream.StreamSupport.stream; /** @@ -83,13 +80,13 @@ public interface Listenable> { static void assertListener(EventListener listener) throws IllegalArgumentException { assertNotNull(listener, () -> "The 'listener' must not be null."); - Class listenerClass = listener.getClass(); - - int modifiers = listenerClass.getModifiers(); - - if (isFinal(modifiers)) { - throw new IllegalArgumentException(format("The listener[class : '{}'] must be non-final class", getTypeName(listenerClass))); - } +// Class listenerClass = listener.getClass(); +// +// int modifiers = listenerClass.getModifiers(); +// +// if (isFinal(modifiers)) { +// throw new IllegalArgumentException(format("The listener[class : '{}'] must be non-final class", getTypeName(listenerClass))); +// } } /** diff --git a/microsphere-java-core/src/main/java/io/microsphere/internal/reflect/ConstantPoolUtils.java b/microsphere-java-core/src/main/java/io/microsphere/internal/reflect/ConstantPoolUtils.java new file mode 100644 index 000000000..0e6d04ee2 --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/internal/reflect/ConstantPoolUtils.java @@ -0,0 +1,263 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. + */ + +package io.microsphere.internal.reflect; + +import io.microsphere.annotation.Nonnull; +import io.microsphere.annotation.Nullable; +import io.microsphere.logging.Logger; +import io.microsphere.util.Utils; + +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; + +import static io.microsphere.logging.LoggerFactory.getLogger; +import static io.microsphere.reflect.MethodUtils.findMethod; +import static io.microsphere.reflect.MethodUtils.invokeMethod; +import static io.microsphere.util.ClassLoaderUtils.loadClass; +import static io.microsphere.util.ObjectUtils.defaultIfNull; +import static java.lang.ClassLoader.getSystemClassLoader; + +/** + * The utility class for {@link sun.reflect.ConstantPool} or {@link jdk.internal.reflect.ConstantPool}(JDK 9+ + * + * @author Mercy + * @see sun.reflect.ConstantPool + * @see jdk.internal.reflect.ConstantPool + * @since 1.0.0 + */ +public abstract class ConstantPoolUtils implements Utils { + + private static final Logger logger = getLogger(ConstantPoolUtils.class); + + /** + * The class name of {@link sun.reflect.ConstantPool} + */ + public static final String LEGACY_CONSTANT_POOL_CLASS_NAME = "sun.reflect.ConstantPool"; + + /** + * The class name of {@link jdk.internal.reflect.ConstantPool} + */ + public static final String CONSTANT_POOL_CLASS_NAME = "jdk.internal.reflect.ConstantPool"; + + /** + * The {@link Class} of {@link sun.reflect.ConstantPool} or {@link jdk.internal.reflect.ConstantPool} + */ + @Nonnull + public static final Class CONSTANT_POOL_CLASS = loadConstantPoolClass(); + + /** + * The {@link Method} of {@link sun.reflect.ConstantPool#getSize()} or {@link jdk.internal.reflect.ConstantPool#getSize()} + */ + static final Method getSizeMethod = findMethod(CONSTANT_POOL_CLASS, "getSize"); + + /** + * The {@link Method} of {@link sun.reflect.ConstantPool#getClassAt(int)} or {@link jdk.internal.reflect.ConstantPool#getClassAt(int)} + */ + static final Method getClassAtMethod = findMethod(CONSTANT_POOL_CLASS, "getClassAt", int.class); + + /** + * The {@link Method} of {@link sun.reflect.ConstantPool#getClassAtIfLoaded(int)} or {@link jdk.internal.reflect.ConstantPool#getClassAtIfLoaded(int)} + */ + static final Method getClassAtIfLoadedMethod = findMethod(CONSTANT_POOL_CLASS, "getClassAtIfLoaded", int.class); + + /** + * The {@link Method} of {@link sun.reflect.ConstantPool#getClassRefIndexAt(int)} or {@link jdk.internal.reflect.ConstantPool#getClassRefIndexAt(int)} + */ + static final Method getClassRefIndexAtMethod = findMethod(CONSTANT_POOL_CLASS, "getClassRefIndexAt", int.class); + + /** + * The {@link Method} of {@link sun.reflect.ConstantPool#getMethodAt(int)} or {@link jdk.internal.reflect.ConstantPool#getMethodAt(int)} + */ + static final Method getMethodAtMethod = findMethod(CONSTANT_POOL_CLASS, "getMethodAt", int.class); + + /** + * The {@link Method} of {@link sun.reflect.ConstantPool#getMethodAtIfLoaded(int)} or {@link jdk.internal.reflect.ConstantPool#getMethodAtIfLoaded(int)} + */ + static final Method getMethodAtIfLoadedMethod = findMethod(CONSTANT_POOL_CLASS, "getMethodAtIfLoaded", int.class); + + /** + * The {@link Method} of {@link sun.reflect.ConstantPool#getFieldAt(int)} or {@link jdk.internal.reflect.ConstantPool#getFieldAt(int)} + */ + static final Method getFieldAtMethod = findMethod(CONSTANT_POOL_CLASS, "getFieldAt", int.class); + + /** + * The {@link Method} of {@link sun.reflect.ConstantPool#getFieldAtIfLoaded(int)} or {@link jdk.internal.reflect.ConstantPool#getFieldAtIfLoaded(int)} + */ + static final Method getFieldAtIfLoadedMethod = findMethod(CONSTANT_POOL_CLASS, "getFieldAtIfLoaded", int.class); + + /** + * The {@link Method} of {@link sun.reflect.ConstantPool#getMemberRefInfoAt(int)} or {@link jdk.internal.reflect.ConstantPool#getMemberRefInfoAt(int)} + */ + static final Method getMemberRefInfoAtMethod = findMethod(CONSTANT_POOL_CLASS, "getMemberRefInfoAt", int.class); + + /** + * The {@link Method} of {@link sun.reflect.ConstantPool#getNameAndTypeRefIndexAt(int)} or {@link jdk.internal.reflect.ConstantPool#getNameAndTypeRefIndexAt(int)} + */ + static final Method getNameAndTypeRefIndexAtMethod = findMethod(CONSTANT_POOL_CLASS, "getNameAndTypeRefIndexAt", int.class); + + /** + * The {@link Method} of {@link sun.reflect.ConstantPool#getNameAndTypeRefInfoAt(int)} or {@link jdk.internal.reflect.ConstantPool#getNameAndTypeRefInfoAt(int)} + */ + static final Method getNameAndTypeRefInfoAtMethod = findMethod(CONSTANT_POOL_CLASS, "getNameAndTypeRefInfoAt", int.class); + + /** + * The {@link Method} of {@link sun.reflect.ConstantPool#getIntAt(int)} or {@link jdk.internal.reflect.ConstantPool#getIntAt(int)} + */ + static final Method getIntAtMethod = findMethod(CONSTANT_POOL_CLASS, "getIntAt", int.class); + + /** + * The {@link Method} of {@link sun.reflect.ConstantPool#getLongAt(int)} or {@link jdk.internal.reflect.ConstantPool#getLongAt(int)} + */ + static final Method getLongAtMethod = findMethod(CONSTANT_POOL_CLASS, "getLongAt", int.class); + + /** + * The {@link Method} of {@link sun.reflect.ConstantPool#getFloatAt(int)} or {@link jdk.internal.reflect.ConstantPool#getFloatAt(int)} + */ + static final Method getFloatAtMethod = findMethod(CONSTANT_POOL_CLASS, "getFloatAt", int.class); + + /** + * The {@link Method} of {@link sun.reflect.ConstantPool#getDoubleAt(int)} or {@link jdk.internal.reflect.ConstantPool#getDoubleAt(int)} + */ + static final Method getDoubleAtMethod = findMethod(CONSTANT_POOL_CLASS, "getDoubleAt", int.class); + + /** + * The {@link Method} of {@link sun.reflect.ConstantPool#getStringAt(int)} or {@link jdk.internal.reflect.ConstantPool#getStringAt(int)} + */ + static final Method getStringAtMethod = findMethod(CONSTANT_POOL_CLASS, "getStringAt", int.class); + + /** + * The {@link Method} of {@link sun.reflect.ConstantPool#getUTF8At(int)} or {@link jdk.internal.reflect.ConstantPool#getUTF8At(int)} + */ + static final Method getUTF8AtMethod = findMethod(CONSTANT_POOL_CLASS, "getUTF8At", int.class); + + public static int getSize(Class targetClass) { + return invoke(targetClass, getSizeMethod); + } + + @Nullable + public static Class getClassAt(Class targetClass, int index) { + return invoke(targetClass, getClassAtMethod, index); + } + + @Nullable + public static Class getClassAtIfLoaded(Class targetClass, int index) { + return invoke(targetClass, getClassAtIfLoadedMethod, index); + } + + /** + * Get a class reference index for a method or a field. + * + * @param targetClass the class to inspect + * @param index the index of the class reference + * @return the class reference index + */ + @Nullable + public static Integer getClassRefIndexAt(Class targetClass, int index) { + return invoke(targetClass, getClassRefIndexAtMethod, index); + } + + @Nullable + public static Member getMethodAt(Class targetClass, int index) { + return invoke(targetClass, getMethodAtMethod, index); + } + + @Nullable + public static Member getMethodAtIfLoaded(Class targetClass, int index) { + return invoke(targetClass, getMethodAtIfLoadedMethod, index); + } + + @Nullable + public static Field getFieldAt(Class targetClass, int index) { + return invoke(targetClass, getFieldAtMethod, index); + } + + @Nullable + public static Field getFieldAtIfLoaded(Class targetClass, int index) { + return invoke(targetClass, getFieldAtIfLoadedMethod, index); + } + + @Nonnull + public static String[] getMemberRefInfoAt(Class targetClass, int index) { + return invoke(targetClass, getMemberRefInfoAtMethod, index); + } + + @Nullable + public static Integer getNameAndTypeRefIndexAt(Class targetClass, int index) { + return invoke(targetClass, getNameAndTypeRefIndexAtMethod, index); + } + + @Nonnull + public static String[] getNameAndTypeRefInfoAt(Class targetClass, int index) { + return invoke(targetClass, getNameAndTypeRefInfoAtMethod, index); + } + + @Nullable + public static Integer getIntAt(Class targetClass, int index) { + return invoke(targetClass, getIntAtMethod, index); + } + + @Nullable + public static Long getLongAt(Class targetClass, int index) { + return invoke(targetClass, getLongAtMethod, index); + } + + @Nullable + public static Float getFloatAt(Class targetClass, int index) { + return invoke(targetClass, getFloatAtMethod, index); + } + + public static Double getDoubleAt(Class targetClass, int index) { + return invoke(targetClass, getDoubleAtMethod, index); + } + + @Nullable + public static String getStringAt(Class targetClass, int index) { + return invoke(targetClass, getStringAtMethod, index); + } + + @Nullable + public static String getUTF8At(Class targetClass, int index) { + return invoke(targetClass, getUTF8AtMethod, index); + } + + @Nullable + static T invoke(Class targetClass, Method method, Object... args) { + Object constantPool = getConstantPool(targetClass); + T returnValue = null; + try { + returnValue = invokeMethod(true, constantPool, method, args); + } catch (Throwable e) { + logger.trace(e.getMessage()); + } + return returnValue; + } + + static Object getConstantPool(Class targetClass) { + return invokeMethod(true, (Object) targetClass, "getConstantPool"); + } + + static Class loadConstantPoolClass() { + ClassLoader classLoader = getSystemClassLoader(); + Class javaLangAccessClass = loadClass(classLoader, LEGACY_CONSTANT_POOL_CLASS_NAME); + return defaultIfNull(javaLangAccessClass, () -> loadClass(classLoader, CONSTANT_POOL_CLASS_NAME)); + } + + private ConstantPoolUtils() { + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/main/java/io/microsphere/invoke/MethodHandleUtils.java b/microsphere-java-core/src/main/java/io/microsphere/invoke/MethodHandleUtils.java index 4a63b79f0..5d9872109 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/invoke/MethodHandleUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/invoke/MethodHandleUtils.java @@ -32,6 +32,7 @@ import static io.microsphere.collection.MapUtils.newConcurrentHashMap; import static io.microsphere.invoke.MethodHandleUtils.LookupKey.buildKey; +import static io.microsphere.invoke.MethodHandleUtils.LookupMode.ALL; import static io.microsphere.invoke.MethodHandleUtils.LookupMode.getModes; import static io.microsphere.invoke.MethodHandlesLookupUtils.NOT_FOUND_METHOD_HANDLE; import static io.microsphere.invoke.MethodHandlesLookupUtils.findPublic; @@ -291,7 +292,7 @@ static LookupKey buildKey(Class requestedClass, int allowedModes) { * @return non-null */ public static Lookup lookup(Class requestedClass) { - return lookup(requestedClass, LookupMode.ALL); + return lookup(requestedClass, ALL); } /** @@ -348,7 +349,7 @@ protected static MethodHandle find(Class requestedClass, String methodName, C if (method == null) { return NOT_FOUND_METHOD_HANDLE; } - if (isiCandidateMethod(method)) { + if (isCandidateMethod(method)) { return findPublic(method, function); } Lookup lookup = lookup(requestedClass); @@ -357,12 +358,12 @@ protected static MethodHandle find(Class requestedClass, String methodName, C private static Lookup newLookup(LookupKey key) { if (lookupConstructor3 != null) { - return newInstance(lookupConstructor3, key.requestedClass, key.requestedClass, key.allowedModes); + return newInstance(true, lookupConstructor3, key.requestedClass, key.requestedClass, key.allowedModes); } - return newInstance(lookupConstructor2, key.requestedClass, key.allowedModes); + return newInstance(true, lookupConstructor2, key.requestedClass, key.allowedModes); } - private static boolean isiCandidateMethod(Method method) { + private static boolean isCandidateMethod(Method method) { return isPublic(method) && !isCallerSensitiveMethod(method); } diff --git a/microsphere-java-core/src/main/java/io/microsphere/invoke/MethodHandlesLookupUtils.java b/microsphere-java-core/src/main/java/io/microsphere/invoke/MethodHandlesLookupUtils.java index ed3a4eb73..ff6ec9c72 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/invoke/MethodHandlesLookupUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/invoke/MethodHandlesLookupUtils.java @@ -22,6 +22,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; import java.lang.reflect.Method; @@ -33,7 +34,7 @@ import static java.lang.invoke.MethodType.methodType; /** - * Utilities class providing convenient methods for working with {@link MethodHandles.Lookup}. + * Utilities class providing convenient methods for working with {@link Lookup}. * *

This class offers various static methods to simplify the process of obtaining and using * method handles, particularly for public virtual and static methods. It serves as a central @@ -66,12 +67,12 @@ public abstract class MethodHandlesLookupUtils implements Utils { public static final MethodHandle NOT_FOUND_METHOD_HANDLE = null; /** - * The {@link MethodHandles.Lookup} for {@link MethodHandles#publicLookup()} + * The {@link Lookup} for {@link MethodHandles#publicLookup()} */ - public static final MethodHandles.Lookup PUBLIC_LOOKUP = publicLookup(); + public static final Lookup PUBLIC_LOOKUP = publicLookup(); /** - * The convenient method to find {@link MethodHandles.Lookup#findVirtual(Class, String, MethodType)} for public method + * The convenient method to find {@link Lookup#findVirtual(Class, String, MethodType)} for public method * * @param requestedClass the class to be looked up * @param methodName the target method name @@ -83,7 +84,7 @@ public static MethodHandle findPublicVirtual(Class requestedClass, String met } /** - * The convenient method to find {@link MethodHandles.Lookup#findStatic(Class, String, MethodType)} for public static method + * The convenient method to find {@link Lookup#findStatic(Class, String, MethodType)} for public static method * * @param requestedClass the class to be looked up * @param methodName the target method name @@ -95,22 +96,22 @@ public static MethodHandle findPublicStatic(Class requestedClass, String meth } protected static MethodHandle findPublic(Class requestedClass, String methodName, Class[] parameterTypes, - ThrowableBiFunction function) { + ThrowableBiFunction function) { return find(PUBLIC_LOOKUP, requestedClass, methodName, parameterTypes, function); } - protected static MethodHandle find(MethodHandles.Lookup lookup, Class requestedClass, String methodName, Class[] parameterTypes, - ThrowableBiFunction function) { + protected static MethodHandle find(Lookup lookup, Class requestedClass, String methodName, Class[] parameterTypes, + ThrowableBiFunction function) { Method method = findMethod(requestedClass, methodName, parameterTypes); return find(lookup, method, function); } - protected static MethodHandle findPublic(Method method, ThrowableBiFunction function) { + protected static MethodHandle findPublic(Method method, ThrowableBiFunction function) { return find(PUBLIC_LOOKUP, method, function); } - protected static MethodHandle find(MethodHandles.Lookup lookup, Method method, - ThrowableBiFunction function) { + protected static MethodHandle find(Lookup lookup, Method method, + ThrowableBiFunction function) { if (method == null) { return NOT_FOUND_METHOD_HANDLE; } diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/serializer/AbstractSerializer.java b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/AbstractSerializer.java new file mode 100644 index 000000000..4def99ed8 --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/AbstractSerializer.java @@ -0,0 +1,138 @@ +package io.microsphere.io.serializer; + +import io.microsphere.annotation.Nonnull; +import io.microsphere.io.IOUtils; +import io.microsphere.reflect.JavaType; + +import java.io.IOException; + +import static io.microsphere.reflect.JavaType.from; +import static io.microsphere.util.SizeUtils.UNBOUND_BYTES_SIZE; + +/** + * Convenience base class for {@link Serializer} and {@liink Deserializer} implementations that handles null-safety + * and fixed-length byte-array validation, delegating the actual encode/decode logic to + * {@link #doSerialize(Object)} and {@link #doDeserialize(byte[])}. + * + *

The expected serialized byte-array length is determined by {@link #calcBytesLength()}. + * Sub-classes that always produce arrays of a fixed size (e.g. 4 bytes for {@link Integer}) + * should override this method to return that size; those with variable-length output should + * return {@link IOUtils#UNBOUND_BYTES_SIZE}. + * + *

Example Usage

+ *
{@code
+ *   public class IntegerSerializer extends AbstractSerializer {
+ *
+ *       public static final IntegerSerializer INTEGER_SERIALIZER = new IntegerSerializer();
+ *
+ *       @Override
+ *       protected int calcBytesLength() {
+ *           return INTEGER_BYTES_LENGTH; // 4
+ *       }
+ *
+ *       @Override
+ *       protected byte[] doSerialize(Integer value) {
+ *           ByteBuffer buffer = ByteBuffer.allocate(INTEGER_BYTES_LENGTH);
+ *           buffer.putInt(value);
+ *           return buffer.array();
+ *       }
+ *
+ *       @Override
+ *       protected Integer doDeserialize(byte[] bytes) {
+ *           return ByteBuffer.wrap(bytes).getInt();
+ *       }
+ *   }
+ *
+ *   byte[] bytes = INTEGER_SERIALIZER.serialize(42);  // 4-byte big-endian int
+ *   int value   = INTEGER_SERIALIZER.deserialize(bytes); // 42
+ * }
+ * + * @param Serialized/Deserialized type + * @author Mercy + * @since 1.0.0 + */ +public abstract class AbstractSerializer implements Serializer, Deserializer { + + private final Class targetType; + + private final int bytesLength; + + /** + * Resolves the serializable type parameter {@code T} via reflection and pre-calculates + * the expected serialized byte-array length. + */ + public AbstractSerializer() { + JavaType type = from(getClass()).as(AbstractSerializer.class).getGenericType(0); + this.targetType = type.toClass(); + this.bytesLength = calcBytesLength(); + } + + @Override + public final byte[] serialize(T t) throws IOException { + // null compatible case + if (t == null) { + return null; + } + return doSerialize(t); + } + + @Override + public final T deserialize(byte[] bytes) throws IOException { + // null compatible case + if (bytes == null) { + return null; + } + + // Compatible byte array fixed case + if (bytesLength != UNBOUND_BYTES_SIZE && bytesLength != bytes.length) { + return null; + } + + return doDeserialize(bytes); + } + + /** + * Returns the target type {@code T} that this serializer/deserializer handles. + * + * @return the target type class + */ + public final Class getTargetType() { + return targetType; + } + + /** + * Returns the expected length of the serialized byte array, or {@link IOUtils#UNBOUND_BYTES_SIZE} + * if variable-length serialization is used. + * + * @return the fixed bytes length (e.g. {@code 4} for {@code Integer}), or {@code -1} + */ + public int getBytesLength() { + return bytesLength; + } + + /** + * Calculates the expected fixed serialized byte-array length. + * Sub-classes with a fixed-size representation must override this to return the correct size. + * + * @return the fixed byte length, or {@link IOUtils#UNBOUND_BYTES_SIZE} ({@code -1}) for variable-length + */ + protected abstract int calcBytesLength(); + + /** + * Performs the actual serialization of a non-null {@code t} value. + * + * @param t the value to serialize; never {@code null} + * @return the serialized byte array; must not be {@code null} + * @throws IOException if serialization fails + */ + protected abstract byte[] doSerialize(@Nonnull T t) throws IOException; + + /** + * Performs the actual deserialization of a non-null {@code bytes} array. + * + * @param bytes the byte array to deserialize; never {@code null} + * @return the deserialized value + * @throws IOException if deserialization fails + */ + protected abstract T doDeserialize(@Nonnull byte[] bytes) throws IOException; +} \ No newline at end of file diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/serializer/BooleanSerializer.java b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/BooleanSerializer.java new file mode 100644 index 000000000..8aee5fa13 --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/BooleanSerializer.java @@ -0,0 +1,37 @@ +package io.microsphere.io.serializer; + +import static io.microsphere.util.SizeUtils.BOOLEAN_BYTES_SIZE; + +/** + * Java {@code boolean} or {@link Boolean} type {@link Serializer} and {@link Deserializer} Class + * + * @author Mercy + * @see AbstractSerializer + * @since 1.0.0 + */ +public final class BooleanSerializer extends AbstractSerializer { + + public static final BooleanSerializer BOOLEAN_SERIALIZER = new BooleanSerializer(); + + private static final byte TRUE_VALUE = 1; + + private static final byte FALSE_VALUE = 0; + + @Override + protected int calcBytesLength() { + return BOOLEAN_BYTES_SIZE; + } + + @Override + protected byte[] doSerialize(Boolean booleanValue) { + byte byteValue = booleanValue ? TRUE_VALUE : FALSE_VALUE; + byte[] bytes = new byte[]{byteValue}; + return bytes; + } + + @Override + protected Boolean doDeserialize(byte[] bytes) { + byte byteValue = bytes[0]; + return byteValue == TRUE_VALUE; + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/serializer/ByteSerializer.java b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/ByteSerializer.java new file mode 100644 index 000000000..04be890e5 --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/ByteSerializer.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. + */ + +package io.microsphere.io.serializer; + +import java.io.IOException; + +import static io.microsphere.util.SizeUtils.BYTE_BYTES_SIZE; + +/** + * Java {@code byte} or {@link Byte} type {@link Serializer} and {@link Deserializer} Class + * + * @author Mercy + * @see AbstractSerializer + * @since 1.0.0 + */ +public class ByteSerializer extends AbstractSerializer { + + public static final ByteSerializer BYTE_SERIALIZER = new ByteSerializer(); + + @Override + protected int calcBytesLength() { + return BYTE_BYTES_SIZE; + } + + @Override + protected byte[] doSerialize(Byte b) throws IOException { + return new byte[]{b}; + } + + @Override + protected Byte doDeserialize(byte[] bytes) throws IOException { + return bytes[0]; + } +} diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/serializer/CharacterSerializer.java b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/CharacterSerializer.java new file mode 100644 index 000000000..8c86c2baa --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/CharacterSerializer.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. + */ + +package io.microsphere.io.serializer; + +import static io.microsphere.util.SizeUtils.CHAR_BYTES_SIZE; + +/** + * Java {@code char} or {@link Character} type {@link Serializer} and {@link Deserializer} Class + * + * @author Mercy + * @see AbstractSerializer + * @since 1.0.0 + */ +public class CharacterSerializer extends AbstractSerializer { + + public static final CharacterSerializer CHARACTER_SERIALIZER = new CharacterSerializer(); + + @Override + protected int calcBytesLength() { + return CHAR_BYTES_SIZE; + } + + @Override + protected byte[] doSerialize(Character character) { + char c = character.charValue(); + return new byte[]{ + (byte) (c >>> 8), // High byte + (byte) c // Low byte + }; + } + + @Override + protected Character doDeserialize(byte[] bytes) { + char c = (char) ((bytes[0] << 8) | (bytes[1] & 0xFF)); + return c; + } +} diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/DefaultDeserializer.java b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/DefaultDeserializer.java similarity index 92% rename from microsphere-java-core/src/main/java/io/microsphere/io/DefaultDeserializer.java rename to microsphere-java-core/src/main/java/io/microsphere/io/serializer/DefaultDeserializer.java index 19409d26d..7ddff69e7 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/DefaultDeserializer.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/DefaultDeserializer.java @@ -14,7 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.microsphere.io; +package io.microsphere.io.serializer; + +import io.microsphere.io.FastByteArrayInputStream; import java.io.IOException; import java.io.ObjectInputStream; @@ -48,7 +50,7 @@ */ public class DefaultDeserializer implements Deserializer { - public static final DefaultDeserializer INSTANCE = new DefaultDeserializer(); + public static final DefaultDeserializer DEFAULT_DESERIALIZER = new DefaultDeserializer(); @Override public Object deserialize(byte[] bytes) throws IOException { diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/DefaultSerializer.java b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/DefaultSerializer.java similarity index 92% rename from microsphere-java-core/src/main/java/io/microsphere/io/DefaultSerializer.java rename to microsphere-java-core/src/main/java/io/microsphere/io/serializer/DefaultSerializer.java index 67e3eeb9f..800b03fd3 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/DefaultSerializer.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/DefaultSerializer.java @@ -14,7 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.microsphere.io; +package io.microsphere.io.serializer; + +import io.microsphere.io.FastByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; @@ -44,7 +46,7 @@ */ public class DefaultSerializer implements Serializer { - public static final DefaultSerializer INSTANCE = new DefaultSerializer(); + public static final DefaultSerializer DEFAULT_SERIALIZER = new DefaultSerializer(); @Override public byte[] serialize(Object source) throws IOException { @@ -58,4 +60,4 @@ public byte[] serialize(Object source) throws IOException { } return bytes; } -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/Deserializer.java b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/Deserializer.java similarity index 97% rename from microsphere-java-core/src/main/java/io/microsphere/io/Deserializer.java rename to microsphere-java-core/src/main/java/io/microsphere/io/serializer/Deserializer.java index 609782bfd..a861c5f5c 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/Deserializer.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/Deserializer.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.microsphere.io; +package io.microsphere.io.serializer; import java.io.IOException; diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/Deserializers.java b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/Deserializers.java similarity index 99% rename from microsphere-java-core/src/main/java/io/microsphere/io/Deserializers.java rename to microsphere-java-core/src/main/java/io/microsphere/io/serializer/Deserializers.java index 0904a2ebc..4ddb711a9 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/Deserializers.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/Deserializers.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.microsphere.io; +package io.microsphere.io.serializer; import io.microsphere.annotation.Nonnull; diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/serializer/DoubleSerializer.java b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/DoubleSerializer.java new file mode 100644 index 000000000..c38d6a22f --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/DoubleSerializer.java @@ -0,0 +1,38 @@ +package io.microsphere.io.serializer; + +import java.io.IOException; + +import static io.microsphere.io.serializer.LongSerializer.LONG_SERIALIZER; +import static io.microsphere.util.SizeUtils.DOUBLE_BYTES_SIZE; +import static java.lang.Double.doubleToLongBits; +import static java.lang.Double.longBitsToDouble; + +/** + * Java {@code double} or {@link Double} type {@link Serializer} and {@link Deserializer} Class + * + * @author Mercy + * @since 1.0.0 + */ +public final class DoubleSerializer extends AbstractSerializer { + + public static final DoubleSerializer DOUBLE_SERIALIZER = new DoubleSerializer(); + + @Override + protected int calcBytesLength() { + return DOUBLE_BYTES_SIZE; + } + + @Override + protected byte[] doSerialize(Double aDouble) throws IOException { + double doubleValue = aDouble.doubleValue(); + long longValue = doubleToLongBits(doubleValue); + return LONG_SERIALIZER.serialize(longValue); + } + + @Override + protected Double doDeserialize(byte[] bytes) throws IOException { + long longValue = LONG_SERIALIZER.deserialize(bytes); + double doubleValue = longBitsToDouble(longValue); + return doubleValue; + } +} diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/serializer/EnumSerializer.java b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/EnumSerializer.java new file mode 100644 index 000000000..35a7f7c7c --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/EnumSerializer.java @@ -0,0 +1,84 @@ +package io.microsphere.io.serializer; + +import java.io.IOException; + +import static io.microsphere.io.serializer.ShortSerializer.SHORT_SERIALIZER; +import static io.microsphere.reflect.MethodUtils.invokeStaticMethod; +import static io.microsphere.util.SizeUtils.BYTE_BYTES_SIZE; +import static io.microsphere.util.SizeUtils.SHORT_BYTES_SIZE; +import static java.lang.Byte.MAX_VALUE; + +/** + * {@link Enum} {@link Serializer} and {@link Deserializer} Class + * + * @author Mercy + * @since 1.0.0 + */ +public class EnumSerializer implements Serializer, Deserializer { + + private static final String VALUES_METHOD_NAME = "values"; + + private final Class enumType; + + private final E[] enums; + + private final int bytesLength; + + public EnumSerializer(Class enumType) { + this.enumType = enumType; + this.enums = invokeValues(); + this.bytesLength = calcBytesLength(enums); + } + + static > int calcBytesLength(E[] enums) { + int enumsLength = enums.length; + return enumsLength < MAX_VALUE ? BYTE_BYTES_SIZE : SHORT_BYTES_SIZE; + } + + private E[] invokeValues() { + return invokeStaticMethod(true, enumType, VALUES_METHOD_NAME); + } + + @Override + public byte[] serialize(Enum e) throws IOException { + // null compatible case + if (e == null) { + return null; + } + + int ordinal = e.ordinal(); + final byte[] bytes; + + if (bytesLength == BYTE_BYTES_SIZE) { // Most scenarios match + bytes = new byte[1]; + bytes[0] = (byte) ordinal; + } else { + bytes = SHORT_SERIALIZER.serialize((short) ordinal); + } + + return bytes; + } + + @Override + public E deserialize(byte[] bytes) throws IOException { + // null compatible case + if (bytes == null) { + return null; + } + + int ordinal = bytesLength == BYTE_BYTES_SIZE ? bytes[0] : SHORT_SERIALIZER.deserialize(bytes); + return enums[ordinal]; + } + + public Class getEnumType() { + return enumType; + } + + public int getBytesLength() { + return bytesLength; + } + + public Class getTargetType() { + return enumType; + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/serializer/FloatSerializer.java b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/FloatSerializer.java new file mode 100644 index 000000000..e91beb72c --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/FloatSerializer.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. + */ + +package io.microsphere.io.serializer; + +import static io.microsphere.io.serializer.IntegerSerializer.INTEGER_SERIALIZER; +import static io.microsphere.util.SizeUtils.FLOAT_BYTES_SIZE; +import static java.lang.Float.floatToIntBits; + +/** + * Java {@code char} or {@link Character} type {@link Serializer} and {@link Deserializer} Class + * + * @author Mercy + * @see AbstractSerializer + * @since 1.0.0 + */ +public class FloatSerializer extends AbstractSerializer { + + public static final FloatSerializer FLOAT_SERIALIZER = new FloatSerializer(); + + @Override + protected int calcBytesLength() { + return FLOAT_BYTES_SIZE; + } + + @Override + protected byte[] doSerialize(Float aFloat) { + int intBits = floatToIntBits(aFloat); + return INTEGER_SERIALIZER.doSerialize(intBits); + } + + @Override + protected Float doDeserialize(byte[] bytes) { + int intBits = INTEGER_SERIALIZER.doDeserialize(bytes); + return Float.intBitsToFloat(intBits); + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/serializer/IntegerSerializer.java b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/IntegerSerializer.java new file mode 100644 index 000000000..aa88cc527 --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/IntegerSerializer.java @@ -0,0 +1,41 @@ +package io.microsphere.io.serializer; + +import static io.microsphere.util.SizeUtils.INTEGER_BYTES_SIZE; + +/** + * Java {@code int} or {@link Integer} type {@link Serializer} and {@link Deserializer} Class + * + * @author Mercy + * @since 1.0.0 + */ +public class IntegerSerializer extends AbstractSerializer { + + public static final IntegerSerializer INTEGER_SERIALIZER = new IntegerSerializer(); + + @Override + protected int calcBytesLength() { + return INTEGER_BYTES_SIZE; + } + + @Override + protected byte[] doSerialize(Integer integer) { + int intValue = integer.intValue(); + byte[] bytes = new byte[]{ + (byte) ((intValue >> 24) & 0xff), + (byte) ((intValue >> 16) & 0xff), + (byte) ((intValue >> 8) & 0xff), + (byte) ((intValue >> 0) & 0xff), + }; + return bytes; + } + + @Override + protected Integer doDeserialize(byte[] bytes) { + int intValue = (0xff & bytes[0]) << 24 | + (0xff & bytes[1]) << 16 | + (0xff & bytes[2]) << 8 | + (0xff & bytes[3]) << 0; + return intValue; + } + +} \ No newline at end of file diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/serializer/LongSerializer.java b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/LongSerializer.java new file mode 100644 index 000000000..9d034edbd --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/LongSerializer.java @@ -0,0 +1,48 @@ +package io.microsphere.io.serializer; + +import static io.microsphere.util.SizeUtils.LONG_BYTES_SIZE; + +/** + * Java {@code long} or {@link Long} type {@link Serializer} and {@link Deserializer} Class + * + * @author Mercy + * @since 1.0.0 + */ +public final class LongSerializer extends AbstractSerializer { + + public static final LongSerializer LONG_SERIALIZER = new LongSerializer(); + + @Override + protected int calcBytesLength() { + return LONG_BYTES_SIZE; + } + + @Override + protected byte[] doSerialize(Long aLong) { + long longValue = aLong.longValue(); + byte[] bytes = new byte[]{ + (byte) longValue, + (byte) (longValue >> 8), + (byte) (longValue >> 16), + (byte) (longValue >> 24), + (byte) (longValue >> 32), + (byte) (longValue >> 40), + (byte) (longValue >> 48), + (byte) (longValue >> 56) + }; + return bytes; + } + + @Override + protected Long doDeserialize(byte[] bytes) { + long longValue = ((long) bytes[7] << 56) + | ((long) bytes[6] & 0xff) << 48 + | ((long) bytes[5] & 0xff) << 40 + | ((long) bytes[4] & 0xff) << 32 + | ((long) bytes[3] & 0xff) << 24 + | ((long) bytes[2] & 0xff) << 16 + | ((long) bytes[1] & 0xff) << 8 + | ((long) bytes[0] & 0xff); + return longValue; + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/Serializer.java b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/Serializer.java similarity index 97% rename from microsphere-java-core/src/main/java/io/microsphere/io/Serializer.java rename to microsphere-java-core/src/main/java/io/microsphere/io/serializer/Serializer.java index c03b6ea9d..d74f147c3 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/Serializer.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/Serializer.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.microsphere.io; +package io.microsphere.io.serializer; import java.io.IOException; @@ -40,4 +40,4 @@ public interface Serializer { byte[] serialize(S source) throws IOException; -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/Serializers.java b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/Serializers.java similarity index 98% rename from microsphere-java-core/src/main/java/io/microsphere/io/Serializers.java rename to microsphere-java-core/src/main/java/io/microsphere/io/serializer/Serializers.java index 8d26a36d2..f05887f06 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/Serializers.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/Serializers.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.microsphere.io; +package io.microsphere.io.serializer; import io.microsphere.annotation.Nonnull; @@ -70,7 +70,7 @@ public Serializers() { } public void loadSPI() { - for (Serializer serializer : loadServicesList(Serializer.class, classLoader)) { + for (Serializer serializer : loadServicesList(Serializer.class, classLoader, true)) { List> typeArguments = resolveTypeArgumentClasses(serializer.getClass()); Class targetClass = first(typeArguments); List serializers = typedSerializers.computeIfAbsent(targetClass, k -> new LinkedList()); diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/serializer/ShortSerializer.java b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/ShortSerializer.java new file mode 100644 index 000000000..499a37d44 --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/ShortSerializer.java @@ -0,0 +1,34 @@ +package io.microsphere.io.serializer; + +import static io.microsphere.util.SizeUtils.SHORT_BYTES_SIZE; + +/** + * Java {@code boolean} or {@link Boolean} type {@link Serializer} and {@link Deserializer} Class + * + * @author Mercy + * @since 1.0.0 + */ +public class ShortSerializer extends AbstractSerializer { + + public static final ShortSerializer SHORT_SERIALIZER = new ShortSerializer(); + + @Override + protected int calcBytesLength() { + return SHORT_BYTES_SIZE; + } + + @Override + protected byte[] doSerialize(Short aShort) { + short shortValue = aShort.shortValue(); + byte[] bytes = new byte[]{ + (byte) (shortValue >>> 8), + (byte) (shortValue & 0xFF)}; + return bytes; + } + + @Override + protected Short doDeserialize(byte[] bytes) { + return (short) ((bytes[0] << 8) | (bytes[1] & 0xFF)); + } + +} \ No newline at end of file diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/StringDeserializer.java b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/StringDeserializer.java similarity index 98% rename from microsphere-java-core/src/main/java/io/microsphere/io/StringDeserializer.java rename to microsphere-java-core/src/main/java/io/microsphere/io/serializer/StringDeserializer.java index 815b4bba8..001b8bd4f 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/StringDeserializer.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/StringDeserializer.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.microsphere.io; +package io.microsphere.io.serializer; import java.io.IOException; import java.nio.charset.Charset; diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/StringSerializer.java b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/StringSerializer.java similarity index 98% rename from microsphere-java-core/src/main/java/io/microsphere/io/StringSerializer.java rename to microsphere-java-core/src/main/java/io/microsphere/io/serializer/StringSerializer.java index 066a9bc9c..9740462a6 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/StringSerializer.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/serializer/StringSerializer.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.microsphere.io; +package io.microsphere.io.serializer; import java.io.IOException; import java.nio.charset.Charset; diff --git a/microsphere-java-core/src/main/java/io/microsphere/json/JSONUtils.java b/microsphere-java-core/src/main/java/io/microsphere/json/JSONUtils.java index 747bc2b3e..10d1e7f20 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/json/JSONUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/json/JSONUtils.java @@ -20,8 +20,8 @@ import io.microsphere.annotation.Nonnull; import io.microsphere.annotation.Nullable; import io.microsphere.beans.BeanMetadata; +import io.microsphere.reflect.ConstructorUtils; import io.microsphere.util.CharSequenceUtils; -import io.microsphere.util.ClassUtils; import io.microsphere.util.Utils; import java.lang.reflect.Method; @@ -987,7 +987,7 @@ public static Map readValueAsMap(JSONObject jsonObject) { @Nonnull public static V readValueAsBean(JSONObject jsonObject, Class beanClass) { BeanMetadata beanMetadata = getBeanMetadata(beanClass); - V valueObject = ClassUtils.newInstance(beanClass); + V valueObject = ConstructorUtils.newInstance(true, beanClass); Iterator iterator = jsonObject.keys(); while (iterator.hasNext()) { String key = iterator.next(); diff --git a/microsphere-java-core/src/main/java/io/microsphere/lang/invoke/LambdaUtils.java b/microsphere-java-core/src/main/java/io/microsphere/lang/invoke/LambdaUtils.java new file mode 100644 index 000000000..beec30557 --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/lang/invoke/LambdaUtils.java @@ -0,0 +1,172 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. + */ + +package io.microsphere.lang.invoke; + +import io.microsphere.annotation.Nullable; +import io.microsphere.util.ClassUtils; +import io.microsphere.util.Utils; + +import java.lang.invoke.CallSite; +import java.lang.invoke.LambdaMetafactory; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.util.List; + +import static io.microsphere.collection.Lists.ofList; +import static io.microsphere.internal.reflect.ConstantPoolUtils.getMethodAt; +import static io.microsphere.internal.reflect.ConstantPoolUtils.getSize; +import static io.microsphere.reflect.MemberUtils.isStatic; +import static io.microsphere.reflect.MethodUtils.findFunctionalInterfaceMethod; +import static io.microsphere.util.ArrayUtils.EMPTY_CLASS_ARRAY; +import static io.microsphere.util.ClassUtils.findAllInterfaces; +import static io.microsphere.util.ClassUtils.getType; +import static io.microsphere.util.ClassUtils.isLambdaClass; +import static io.microsphere.util.ClassUtils.resolvePrimitiveType; +import static io.microsphere.util.ClassUtils.tryResolveWrapperType; +import static java.util.Collections.emptyList; + +/** + * The utility class for Java Lambda + * + * @author Mercy + * @see LambdaMetafactory + * @see CallSite + * @see MethodHandles + * @since 1.0.0 + */ +public abstract class LambdaUtils implements Utils { + + /** + * Resolve the parameter types of the lambda method + * + * @param object the lambda object + * @return the parameter types of the lambda method + */ + public static List> resolveLambdaMethodParameterTypes(@Nullable Object object) { + return resolveLambdaMethodParameterTypes(getType(object)); + } + + /** + * Resolve the parameter types of the lambda method + * + * @param type the lambda class type + * @return the parameter types of the lambda method + */ + public static List> resolveLambdaMethodParameterTypes(@Nullable Class type) { + List> allInterfaces = findAllInterfaces(type, ClassUtils::isFunctionalInterface); + if (allInterfaces.size() == 1) { + Class functionalInterface = allInterfaces.get(0); + return resolveLambdaMethodParameterTypes(type, functionalInterface); + } + return emptyList(); + } + + /** + * Resolve the parameter types of the lambda method + * + * @param object the lambda object + * @param functionalInterface the functional interface type + * @return the parameter types of the lambda method + */ + public static List> resolveLambdaMethodParameterTypes(@Nullable Object object, @Nullable Class functionalInterface) { + return resolveLambdaMethodParameterTypes(getType(object), functionalInterface); + } + + /** + * Resolve the parameter types of the lambda method + * + * @param type the lambda class type + * @param functionalInterface the functional interface type + * @return the parameter types of the lambda method + */ + public static List> resolveLambdaMethodParameterTypes(@Nullable Class type, @Nullable Class functionalInterface) { + if (!isLambdaClass(type)) { + return emptyList(); + } + + Method declaredMethod = findFunctionalInterfaceMethod(functionalInterface); + if (declaredMethod == null) { + return emptyList(); + } + + if (!functionalInterface.isAssignableFrom(type)) { + return emptyList(); + } + + Class[] declaredParameterTypes = declaredMethod.getParameterTypes(); + List> actualParameterTypes = emptyList(); + + int size = getSize(type); + for (int i = size - 1; i > -1; i--) { + Member member = getMethodAt(type, i); + if (member instanceof Method) { + Method actualMethod = (Method) member; + Class[] resolvedParameterTypes = resolveParameterTypes(actualMethod, declaredParameterTypes); + if (resolvedParameterTypes != EMPTY_CLASS_ARRAY) { + actualParameterTypes = ofList(resolvedParameterTypes); + break; + } + } + } + + return actualParameterTypes; + } + + static Class[] resolveParameterTypes(Method actualMethod, Class... declaredParameterTypes) { + Class[] actualParameterTypes = actualMethod.getParameterTypes(); + int actualLength = actualParameterTypes.length; + int length = declaredParameterTypes.length; + Class[] matchedParameterTypes = new Class[length]; + if (actualLength < length) { + if (isStatic(actualMethod)) { + // static method must have the same length of parameters with the functional interface method + return EMPTY_CLASS_ARRAY; + } + + matchedParameterTypes[0] = actualMethod.getDeclaringClass(); + for (int i = 1; i < length; i++) { + matchedParameterTypes[i] = tryResolveWrapperType(actualParameterTypes[i - 1]); + } + + } else { + for (int i = 0; i < length; i++) { + int index = length - 1 - i; + Class declaredParameterType = declaredParameterTypes[index]; + Class actualParameterType = actualParameterTypes[actualLength - 1 - i]; + if (declaredParameterType.isPrimitive()) { + actualParameterType = resolvePrimitiveType(actualParameterType); + if (declaredParameterType != actualParameterType) { + return EMPTY_CLASS_ARRAY; + } + } else { + actualParameterType = tryResolveWrapperType(actualParameterType); + if (!declaredParameterType.isAssignableFrom(actualParameterType)) { + return EMPTY_CLASS_ARRAY; + } + } + + matchedParameterTypes[index] = actualParameterType; + } + } + return matchedParameterTypes; + } + + private LambdaUtils() { + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/main/java/io/microsphere/misc/UnsafeUtils.java b/microsphere-java-core/src/main/java/io/microsphere/misc/UnsafeUtils.java index b47ec2e56..de8d7a80d 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/misc/UnsafeUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/misc/UnsafeUtils.java @@ -1,1174 +1,2697 @@ -//package io.microsphere.misc; -// -//import io.microsphere.reflect.ReflectionUtils; -//import sun.misc.Unsafe; -// -//import java.lang.reflect.Field; -//import java.security.AccessController; -//import java.security.PrivilegedExceptionAction; -//import java.util.concurrent.ConcurrentHashMap; -//import java.util.concurrent.ConcurrentMap; -// -//import static io.microsphere.reflect.FieldUtils.findField; -//import static io.microsphere.reflect.FieldUtils.getFieldValue; -//import static io.microsphere.reflect.ReflectionUtils.assertArrayIndex; -//import static io.microsphere.reflect.ReflectionUtils.assertFieldMatchType; -// -/// ** -// * {@link Unsafe} Utility class

Take case to use those utility methods in order to the stability fo -// * JVM -// * -// * @author Mercy -// * @see UnsafeUtils -// * @since 1.0.0 -// */ -//public abstract class UnsafeUtils extends BaseUtils { -// -// final static Unsafe unsafe; -// -// /** -// * long Array base index -// */ -// static final int LONG_ARRAY_BASE_OFFSET; -// -// /** -// * int Array base index -// */ -// static final int INT_ARRAY_BASE_OFFSET; -// -// /** -// * short Array base index -// */ -// static final int SHORT_ARRAY_BASE_OFFSET; -// -// /** -// * byte Array base index -// */ -// static final int BYTE_ARRAY_BASE_OFFSET; -// -// /** -// * boolean Array base index -// */ -// static final int BOOLEAN_ARRAY_BASE_OFFSET; -// -// /** -// * double Array base index -// */ -// static final int DOUBLE_ARRAY_BASE_OFFSET; -// -// /** -// * float Array base index -// */ -// static final int FLOAT_ARRAY_BASE_OFFSET; -// -// /** -// * char Array base index -// */ -// static final int CHAR_ARRAY_BASE_OFFSET; -// -// /** -// * java.lang.Object Array base index -// */ -// static final int OBJECT_ARRAY_BASE_OFFSET; -// -// /** -// * long Array Index scale -// */ -// static final int LONG_ARRAY_INDEX_SCALE; -// -// /** -// * int Array Index scale -// */ -// static final int INT_ARRAY_INDEX_SCALE; -// -// /** -// * short Array Index scale -// */ -// static final int SHORT_ARRAY_INDEX_SCALE; -// -// /** -// * byte Array Index scale -// */ -// static final int BYTE_ARRAY_INDEX_SCALE; -// -// /** -// * boolean Array Index scale -// */ -// static final int BOOLEAN_ARRAY_INDEX_SCALE; -// -// /** -// * double Array Index scale -// */ -// static final int DOUBLE_ARRAY_INDEX_SCALE; -// -// /** -// * float Array Index scale -// */ -// static final int FLOAT_ARRAY_INDEX_SCALE; -// -// /** -// * char Array Index scale -// */ -// static final int CHAR_ARRAY_INDEX_SCALE; -// -// /** -// * java.lang.Object Array Index scale -// */ -// static final int OBJECT_ARRAY_INDEX_SCALE; -// -// /** -// * Offset Cache, -// */ -// private final static ConcurrentMap offsetCache = new ConcurrentHashMap<>(); -// -// static { -// try { -// final PrivilegedExceptionAction action = new PrivilegedExceptionAction() { -// public Unsafe run() throws Exception { -// Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); -// theUnsafe.setAccessible(true); -// return (Unsafe) theUnsafe.get(null); -// } -// }; -// -// unsafe = AccessController.doPrivileged(action); -// -// if (unsafe == null) { -// throw new NullPointerException(); -// } -// -// LONG_ARRAY_BASE_OFFSET = unsafe.arrayBaseOffset(long[].class); -// INT_ARRAY_BASE_OFFSET = unsafe.arrayBaseOffset(int[].class); -// SHORT_ARRAY_BASE_OFFSET = unsafe.arrayBaseOffset(short[].class); -// BYTE_ARRAY_BASE_OFFSET = unsafe.arrayBaseOffset(byte[].class); -// BOOLEAN_ARRAY_BASE_OFFSET = unsafe.arrayBaseOffset(boolean[].class); -// DOUBLE_ARRAY_BASE_OFFSET = unsafe.arrayBaseOffset(double[].class); -// FLOAT_ARRAY_BASE_OFFSET = unsafe.arrayBaseOffset(float[].class); -// CHAR_ARRAY_BASE_OFFSET = unsafe.arrayBaseOffset(char[].class); -// OBJECT_ARRAY_BASE_OFFSET = unsafe.arrayBaseOffset(Object[].class); -// -// LONG_ARRAY_INDEX_SCALE = unsafe.arrayIndexScale(long[].class); -// INT_ARRAY_INDEX_SCALE = unsafe.arrayIndexScale(int[].class); -// SHORT_ARRAY_INDEX_SCALE = unsafe.arrayIndexScale(short[].class); -// BYTE_ARRAY_INDEX_SCALE = unsafe.arrayIndexScale(byte[].class); -// BOOLEAN_ARRAY_INDEX_SCALE = unsafe.arrayIndexScale(boolean[].class); -// DOUBLE_ARRAY_INDEX_SCALE = unsafe.arrayIndexScale(double[].class); -// FLOAT_ARRAY_INDEX_SCALE = unsafe.arrayIndexScale(float[].class); -// CHAR_ARRAY_INDEX_SCALE = unsafe.arrayIndexScale(char[].class); -// OBJECT_ARRAY_INDEX_SCALE = unsafe.arrayIndexScale(Object[].class); -// } catch (Exception e) { -// throw new UnsupportedOperationException("Current JVM does not support sun.misc.Unsafe"); -// } -// } -// -// /** -// * Calculate Array Index Offset -// * -// * @param index Index -// * @param baseOffset {@link Unsafe#arrayBaseOffset(Class)} -// * @param indexScale {@link Unsafe#arrayIndexScale(Class)} -// * @return -// * @see java.util.concurrent.atomic.AtomicIntegerArray -// */ -// protected static long arrayIndexOffset(int index, long baseOffset, long indexScale) { -// if (index < 0) -// throw new IndexOutOfBoundsException("index " + index); -// return baseOffset + (long) index * indexScale; -// } -// -// -// /** -// * Calculate the long type Array Index the relative offset -// * -// * @param index Array Index -// * @return the relative offset -// */ -// protected static long longArrayIndexOffset(int index) { -// return arrayIndexOffset(index, LONG_ARRAY_BASE_OFFSET, LONG_ARRAY_INDEX_SCALE); -// } -// -// /** -// * Calculate int type Array Index the relative offset -// * -// * @param index Array Index -// * @return the relative offset -// */ -// protected static long intArrayIndexOffset(int index) { -// return arrayIndexOffset(index, INT_ARRAY_BASE_OFFSET, INT_ARRAY_INDEX_SCALE); -// } -// -// /** -// * Calculate the short type Array Index the relative offset -// * -// * @param index Array Index -// * @return the relative offset -// */ -// protected static long shortArrayIndexOffset(int index) { -// return arrayIndexOffset(index, SHORT_ARRAY_BASE_OFFSET, SHORT_ARRAY_INDEX_SCALE); -// } -// -// /** -// * Calculate the relative offset of Array Index of type byte -// * -// * @param index Array Index -// * @return the relative offset -// */ -// protected static long byteArrayIndexOffset(int index) { -// return arrayIndexOffset(index, BYTE_ARRAY_BASE_OFFSET, BYTE_ARRAY_INDEX_SCALE); -// } -// -// /** -// * Calculate boolean type Array Index the relative offset -// * -// * @param index Array Index -// * @return the relative offset -// */ -// protected static long booleanArrayIndexOffset(int index) { -// return arrayIndexOffset(index, BOOLEAN_ARRAY_BASE_OFFSET, BOOLEAN_ARRAY_INDEX_SCALE); -// } -// -// /** -// * Calculate the relative offset of Array Index of type double -// * -// * @param index Array Index -// * @return the relative offset -// */ -// protected static long doubleArrayIndexOffset(int index) { -// return arrayIndexOffset(index, DOUBLE_ARRAY_BASE_OFFSET, DOUBLE_ARRAY_INDEX_SCALE); -// } -// -// /** -// * Calculate float type Array Index the relative offset -// * -// * @param index Array Index -// * @return the relative offset -// */ -// protected static long floatArrayIndexOffset(int index) { -// return arrayIndexOffset(index, FLOAT_ARRAY_BASE_OFFSET, FLOAT_ARRAY_INDEX_SCALE); -// } -// -// /** -// * Calculate char type Array Index the relative offset -// * -// * @param index Array Index -// * @return the relative offset -// */ -// protected static long charArrayIndexOffset(int index) { -// return arrayIndexOffset(index, CHAR_ARRAY_BASE_OFFSET, CHAR_ARRAY_INDEX_SCALE); -// } -// -// /** -// * Calculate java.lang.Object type Array Index the relative offset -// * -// * @param index Array Index -// * @return the relative offset -// */ -// protected static long objectArrayIndexOffset(int index) { -// return arrayIndexOffset(index, OBJECT_ARRAY_BASE_OFFSET, OBJECT_ARRAY_INDEX_SCALE); -// } -// -// -// /** -// * Create Offset Cache Key -// * -// * @param type the target type -// * @param fieldName the name of {@link Field} -// * @return The cache key of offset -// */ -// protected static String createOffsetCacheKey(Class type, String fieldName) { -// return type.getName() + "#" + fieldName; -// } -// -// /** -// * Get Offset in cache -// * -// * @param type the target type -// * @param fieldName the name of {@link Field} -// * @return Offset -// */ -// protected static Long getOffsetFromCache(Class type, String fieldName) { -// String key = createOffsetCacheKey(type, fieldName); -// return offsetCache.get(key); -// } -// -// /** -// * Save offset to cache -// * -// * @param type the target type -// * @param fieldName the name of {@link Field} -// * @param offset offset -// */ -// protected static void putOffsetFromCache(Class type, String fieldName, long offset) { -// String key = createOffsetCacheKey(type, fieldName); -// offsetCache.putIfAbsent(key, offset); -// } -// -// -// /** -// * Get the long value of the target Index in the Array field of the object -// * -// * @param object Object -// * @param fieldName the name of {@link Field} -// * @param index the index -// * @return the long value -// */ -// public static long getLongFromArrayVolatile(Object object, String fieldName, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, IllegalAccessException { -// Object array = getFieldValue(object, fieldName); -// long offset = longArrayIndexOffset(index); -// return unsafe.getLongVolatile(array, offset); -// } -// -// /** -// * Get the value of the target Index in the object Array field -// * -// * @param object Object -// * @param fieldName the name of {@link Field} -// * @param index the index -// * @return the int value -// */ -// public static int getIntFromArrayVolatile(Object object, String fieldName, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, IllegalAccessException { -// Object array = getFieldValue(object, fieldName); -// assertArrayIndex(array, index); -// long offset = intArrayIndexOffset(index); -// return unsafe.getIntVolatile(array, offset); -// } -// -// /** -// * Get the short value of the target Index in the Array field of the object -// * -// * @param object Object -// * @param fieldName the name of {@link Field} -// * @param index the index -// * @return the short value -// */ -// public static short getShortFromArrayVolatile(Object object, String fieldName, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, IllegalAccessException { -// Object array = getFieldValue(object, fieldName); -// assertArrayIndex(array, index); -// long offset = shortArrayIndexOffset(index); -// return unsafe.getShortVolatile(array, offset); -// } -// -// /** -// * Get the byte value of the target Index in the object Array field -// * -// * @param object Object -// * @param fieldName the name of {@link Field} -// * @param index the index -// * @return the byte value -// */ -// public static byte getByteFromArrayVolatile(Object object, String fieldName, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, IllegalAccessException { -// Object array = getFieldValue(object, fieldName); -// assertArrayIndex(array, index); -// long offset = byteArrayIndexOffset(index); -// return unsafe.getByteVolatile(array, offset); -// } -// -// /** -// * Get the boolean value of the target Index in the Array field of the object -// * -// * @param object Object -// * @param fieldName the name of {@link Field} -// * @param index the index -// * @return the boolean value -// */ -// public static boolean getBooleanFromArrayVolatile(Object object, String fieldName, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, IllegalAccessException { -// Object array = getFieldValue(object, fieldName); -// assertArrayIndex(array, index); -// long offset = booleanArrayIndexOffset(index); -// return unsafe.getBooleanVolatile(array, offset); -// } -// -// /** -// * Get the double value of the target Index in the Array field of the object -// * -// * @param object Object -// * @param fieldName the name of {@link Field} -// * @param index the index -// * @return the double value -// */ -// public static double getDoubleFromArrayVolatile(Object object, String fieldName, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, IllegalAccessException { -// Object array = getFieldValue(object, fieldName); -// assertArrayIndex(array, index); -// long offset = doubleArrayIndexOffset(index); -// return unsafe.getDoubleVolatile(array, offset); -// } -// -// /** -// * Get the float value of the target Index in the object Array field -// * -// * @param object Object -// * @param fieldName the name of {@link Field} -// * @param index the index -// * @return the target index -// */ -// public static float getFloatFromArrayVolatile(Object object, String fieldName, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, IllegalAccessException { -// Object array = getFieldValue(object, fieldName); -// assertArrayIndex(array, index); -// long offset = floatArrayIndexOffset(index); -// return unsafe.getFloatVolatile(array, offset); -// } -// -// /** -// * Get the char value of the target Index in the object Array field -// * -// * @param object Object -// * @param fieldName the name of {@link Field} -// * @param index the index -// * @return the char value -// * @throws IllegalArgumentException See {@link ReflectionUtils#assertArrayType(Object)} -// * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length -// */ -// public static char getCharFromArrayVolatile(Object object, String fieldName, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, IllegalAccessException { -// Object array = getFieldValue(object, fieldName); -// assertArrayIndex(array, index); -// long offset = charArrayIndexOffset(index); -// return unsafe.getCharVolatile(array, offset); -// } -// -// /** -// * Get the java.lang.Object value of the target Index in the object Array field -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param index the index -// * @return the java.lang.Object value -// * @throws IllegalArgumentException see {@link ReflectionUtils#assertArrayType(Object)} -// * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length -// */ -// public static Object getObjectFromArrayVolatile(Object object, String fieldName, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, IllegalAccessException { -// Object array = getFieldValue(object, fieldName); -// assertArrayIndex(array, index); -// long offset = objectArrayIndexOffset(index); -// return unsafe.getObjectVolatile(array, offset); -// } -// -// -// /** -// * Sets the given double value to the field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param value double value -// */ -// public static void putDouble(Object object, String fieldName, double value) { -// assertFieldMatchType(object, fieldName, double.class); -// long offset = getObjectFieldOffset(object, fieldName); -// unsafe.putDouble(object, offset, value); -// } -// -// /** -// * Sets the given float value to the field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param value float value -// */ -// public static void putFloat(Object object, String fieldName, float value) { -// assertFieldMatchType(object, fieldName, float.class); -// long offset = getObjectFieldOffset(object, fieldName); -// unsafe.putFloat(object, offset, value); -// } -// -// /** -// * Sets the given short value to the field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param value short value -// */ -// public static void putShort(Object object, String fieldName, short value) { -// assertFieldMatchType(object, fieldName, short.class); -// long offset = getObjectFieldOffset(object, fieldName); -// unsafe.putShort(object, offset, value); -// } -// -// /** -// * Sets the given byte value to the field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param value byte value -// */ -// public static void putByte(Object object, String fieldName, byte value) { -// assertFieldMatchType(object, fieldName, byte.class); -// long offset = getObjectFieldOffset(object, fieldName); -// unsafe.putByte(object, offset, value); -// } -// -// /** -// * Sets the given boolean value to the field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param value boolean value -// */ -// public static void putBoolean(Object object, String fieldName, boolean value) { -// assertFieldMatchType(object, fieldName, boolean.class); -// long offset = getObjectFieldOffset(object, fieldName); -// unsafe.putBoolean(object, offset, value); -// } -// -// /** -// * Sets the given char value to the field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param value char value -// */ -// public static void putChar(Object object, String fieldName, char value) { -// assertFieldMatchType(object, fieldName, char.class); -// long offset = getObjectFieldOffset(object, fieldName); -// unsafe.putChar(object, offset, value); -// } -// -// /** -// * Sets the given Object value to the field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param value Object value -// */ -// public static void putObject(Object object, String fieldName, Object value) { -// assertFieldMatchType(object, fieldName, Object.class); -// long offset = getObjectFieldOffset(object, fieldName); -// unsafe.putObject(object, offset, value); -// } -// -// /** -// * Sets the given long value to the field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param value long value -// */ -// public static void putLong(Object object, String fieldName, long value) { -// assertFieldMatchType(object, fieldName, long.class); -// long offset = getObjectFieldOffset(object, fieldName); -// unsafe.putLong(object, offset, value); -// } -// -// -// /** -// * Sets the given int value to the field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param value int value -// */ -// public static void putInt(Object object, String fieldName, int value) { -// assertFieldMatchType(object, fieldName, int.class); -// long offset = getObjectFieldOffset(object, fieldName); -// unsafe.putInt(object, offset, value); -// } -// -// /** -// * Sets the given int value to the field of the specified object (ensures writing order) -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param value int value -// */ -// public static void putOrderedInt(Object object, String fieldName, int value) { -// assertFieldMatchType(object, fieldName, int.class); -// long offset = getObjectFieldOffset(object, fieldName); -// unsafe.putOrderedInt(object, offset, value); -// } -// -// /** -// * Sets the given long value to the field of the specified object (ensures writing order) -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param value long value -// */ -// public static void putOrderedLong(Object object, String fieldName, long value) { -// assertFieldMatchType(object, fieldName, long.class); -// long offset = getObjectFieldOffset(object, fieldName); -// unsafe.putOrderedLong(object, offset, value); -// } -// -// /** -// * Set the given Object value to the field of the specified object (ensure write order) -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param value Object value -// */ -// public static void putOrderedObject(Object object, String fieldName, Object value) { -// assertFieldMatchType(object, fieldName, Object.class); -// long offset = getObjectFieldOffset(object, fieldName); -// unsafe.putOrderedObject(object, offset, value); -// } -// -// /** -// * Sets the given double value to the volatile field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param value double value -// */ -// public static void putDoubleVolatile(Object object, String fieldName, double value) { -// assertFieldMatchType(object, fieldName, double.class); -// long offset = getObjectFieldOffset(object, fieldName); -// unsafe.putDoubleVolatile(object, offset, value); -// } -// -// /** -// * Sets the given float value to the volatile field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param value float value -// */ -// public static void putFloatVolatile(Object object, String fieldName, float value) { -// assertFieldMatchType(object, fieldName, float.class); -// long offset = getObjectFieldOffset(object, fieldName); -// unsafe.putFloatVolatile(object, offset, value); -// } -// -// /** -// * Sets the given short value to the volatile field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param value short value -// */ -// public static void putShortVolatile(Object object, String fieldName, short value) { -// assertFieldMatchType(object, fieldName, short.class); -// long offset = getObjectFieldOffset(object, fieldName); -// unsafe.putShortVolatile(object, offset, value); -// } -// -// /** -// * Sets the given byte value to the volatile field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param value byte value -// */ -// public static void putByteVolatile(Object object, String fieldName, byte value) { -// assertFieldMatchType(object, fieldName, byte.class); -// long offset = getObjectFieldOffset(object, fieldName); -// unsafe.putByteVolatile(object, offset, value); -// } -// -// /** -// * Sets the given boolean value to the volatile field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param value boolean value -// */ -// public static void putBooleanVolatile(Object object, String fieldName, boolean value) { -// assertFieldMatchType(object, fieldName, boolean.class); -// long offset = getObjectFieldOffset(object, fieldName); -// unsafe.putBooleanVolatile(object, offset, value); -// } -// -// /** -// * Sets the given char value to the volatile field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param value char value -// */ -// public static void putCharVolatile(Object object, String fieldName, char value) { -// assertFieldMatchType(object, fieldName, char.class); -// long offset = getObjectFieldOffset(object, fieldName); -// unsafe.putCharVolatile(object, offset, value); -// } -// -// /** -// * Sets the given Object value to the volatile field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param value Object value -// */ -// public static void putObjectVolatile(Object object, String fieldName, Object value) { -// assertFieldMatchType(object, fieldName, Object.class); -// long offset = getObjectFieldOffset(object, fieldName); -// unsafe.putObjectVolatile(object, offset, value); -// } -// -// /** -// * Sets the given long value to the volatile field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param value long value -// */ -// public static void putLongVolatile(Object object, String fieldName, long value) { -// assertFieldMatchType(object, fieldName, long.class); -// long offset = getObjectFieldOffset(object, fieldName); -// unsafe.putLongVolatile(object, offset, value); -// } -// -// /** -// * Sets the given int value to the volatile field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param value int value -// */ -// public static void putIntVolatile(Object object, String fieldName, int value) { -// assertFieldMatchType(object, fieldName, int.class); -// long offset = getObjectFieldOffset(object, fieldName); -// unsafe.putIntVolatile(object, offset, value); -// } -// -// -// /** -// * Sets the given long value to a field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param index the index -// * @param value long value -// * @throws IllegalArgumentException see {@link ReflectionUtils#assertArrayType(Object)} -// * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length -// */ -// public static void putLongIntoArrayVolatile(Object object, String fieldName, int index, long value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, IllegalAccessException { -// Object array = getFieldValue(object, fieldName); -// ReflectionUtils.assertArrayIndex(array, index); -// long offset = longArrayIndexOffset(index); -// unsafe.putLongVolatile(array, offset, value); -// } -// -// /** -// * Sets the given long value to the fields of the specified object (sequential writing) -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param index the index -// * @param value long value -// * @throws IllegalArgumentException see {@link ReflectionUtils#assertArrayType(Object)} -// * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length -// */ -// public static void putOrderedLongIntoArray(Object object, String fieldName, int index, long value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, IllegalAccessException { -// Object array = getFieldValue(object, fieldName); -// assertArrayIndex(array, index); -// long offset = longArrayIndexOffset(index); -// unsafe.putOrderedLong(array, offset, value); -// } -// -// /** -// * Sets the given int value to a field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param index the index -// * @param value int value -// * @throws IllegalArgumentException see {@link ReflectionUtils#assertArrayType(Object)} -// * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length -// */ -// public static void putIntIntoArrayVolatile(Object object, String fieldName, int index, int value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, IllegalAccessException { -// Object array = getFieldValue(object, fieldName); -// assertArrayIndex(array, index); -// long offset = intArrayIndexOffset(index); -// unsafe.putIntVolatile(array, offset, value); -// } -// -// /** -// * Sets the given int value to a field of the specified object (sequential writing) -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param index the index -// * @param value int value -// * @throws IllegalArgumentException see {@link ReflectionUtils#assertArrayType(Object)} -// * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length -// */ -// public static void putOrderedIntIntoArray(Object object, String fieldName, int index, int value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, IllegalAccessException { -// Object array = getFieldValue(object, fieldName); -// assertArrayIndex(array, index); -// long offset = intArrayIndexOffset(index); -// unsafe.putOrderedInt(array, offset, value); -// } -// -// /** -// * Sets the given short value to a field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param index the index -// * @param value short value -// * @throws IllegalArgumentException see {@link ReflectionUtils#assertArrayType(Object)} -// * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length -// */ -// public static void putShortIntoArrayVolatile(Object object, String fieldName, int index, short value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, IllegalAccessException { -// Object array = getFieldValue(object, fieldName); -// assertArrayIndex(array, index); -// long offset = shortArrayIndexOffset(index); -// unsafe.putShortVolatile(array, offset, value); -// } -// -// /** -// * Sets the given byte value to a field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param index the index -// * @param value byte value -// * @throws IllegalArgumentException see {@link ReflectionUtils#assertArrayType(Object)} -// * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length -// */ -// public static void putByteIntoArrayVolatile(Object object, String fieldName, int index, byte value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, IllegalAccessException { -// Object array = getFieldValue(object, fieldName); -// assertArrayIndex(array, index); -// long offset = byteArrayIndexOffset(index); -// unsafe.putByteVolatile(array, offset, value); -// } -// -// /** -// * Sets the given boolean value to a field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param index the index -// * @param value boolean value -// * @throws IllegalArgumentException see {@link ReflectionUtils#assertArrayType(Object)} -// * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length -// */ -// public static void putBooleanIntoArrayVolatile(Object object, String fieldName, int index, boolean value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, IllegalAccessException { -// Object array = getFieldValue(object, fieldName); -// assertArrayIndex(array, index); -// long offset = booleanArrayIndexOffset(index); -// unsafe.putBooleanVolatile(array, offset, value); -// } -// -// /** -// * Sets the given double value to a field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param index the index -// * @param value double value -// * @throws IllegalArgumentException see {@link ReflectionUtils#assertArrayType(Object)} -// * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length -// */ -// public static void putDoubleIntoArrayVolatile(Object object, String fieldName, int index, double value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, IllegalAccessException { -// Object array = getFieldValue(object, fieldName); -// assertArrayIndex(array, index); -// long offset = doubleArrayIndexOffset(index); -// unsafe.putDoubleVolatile(array, offset, value); -// } -// -// /** -// * Sets the given float value to the field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param index the index -// * @param value float value -// * @throws IllegalArgumentException see {@link ReflectionUtils#assertArrayType(Object)} -// * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length -// */ -// public static void putFloatIntoArrayVolatile(Object object, String fieldName, int index, float value) throws IllegalAccessException { -// Object array = getFieldValue(object, fieldName); -// assertArrayIndex(array, index); -// long offset = floatArrayIndexOffset(index); -// unsafe.putFloatVolatile(array, offset, value); -// } -// -// /** -// * Sets the given char value to a field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param index the index -// * @param value char value -// * @throws IllegalArgumentException see {@link ReflectionUtils#assertArrayType(Object)} -// * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length -// */ -// public static void putCharIntoArrayVolatile(Object object, String fieldName, int index, char value) throws IllegalAccessException { -// Object array = getFieldValue(object, fieldName); -// assertArrayIndex(array, index); -// long offset = charArrayIndexOffset(index); -// unsafe.putCharVolatile(array, offset, value); -// } -// -// /** -// * Sets the given java.lang.Object value to a field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param index the index -// * @param value java.lang.Object value -// * @throws IllegalArgumentException see {@link ReflectionUtils#assertArrayType(Object)} -// * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length -// */ -// public static void putObjectIntoArrayVolatile(Object object, String fieldName, int index, Object value) throws IllegalAccessException { -// Object array = getFieldValue(object, fieldName); -// assertArrayIndex(array, index); -// long offset = objectArrayIndexOffset(index); -// unsafe.putObjectVolatile(array, offset, value); -// } -// -// -// /** -// * Sets the given java.lang.Object value to the fields of the specified object (sequential writing) -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @param index the index -// * @param value java.lang.Object value -// * @throws IllegalArgumentException see {@link ReflectionUtils#assertArrayType(Object)} -// * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length -// */ -// public static void putOrderedObjectIntoArray(Object object, String fieldName, int index, Object value) throws IllegalAccessException { -// Object array = getFieldValue(object, fieldName); -// assertArrayIndex(array, index); -// long offset = objectArrayIndexOffset(index); -// unsafe.putOrderedObject(array, offset, value); -// } -// -// /** -// * Get the Object value of the specified object field -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @return Object value -// */ -// public static Object getObject(Object object, String fieldName) { -// long offset = getObjectFieldOffset(object, fieldName); -// return unsafe.getObject(object, offset); -// } -// -// /** -// * Get the long value of the specified object field -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @return long value -// */ -// public static long getLong(Object object, String fieldName) { -// long offset = getObjectFieldOffset(object, fieldName); -// return unsafe.getLong(object, offset); -// } -// -// /** -// * Get the double value of the specified object field -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @return double value -// */ -// public static double getDouble(Object object, String fieldName) { -// long offset = getObjectFieldOffset(object, fieldName); -// return unsafe.getDouble(object, offset); -// } -// -// /** -// * Get the float value of the specified object field -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @return float value -// */ -// public static float getFloat(Object object, String fieldName) { -// long offset = getObjectFieldOffset(object, fieldName); -// return unsafe.getFloat(object, offset); -// } -// -// /** -// * Get the short value of the specified object field -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @return short value -// */ -// public static short getShort(Object object, String fieldName) { -// long offset = getObjectFieldOffset(object, fieldName); -// return unsafe.getShort(object, offset); -// } -// -// /** -// * Get the byte value of the specified object field -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @return byte value -// */ -// public static byte getByte(Object object, String fieldName) { -// long offset = getObjectFieldOffset(object, fieldName); -// return unsafe.getByte(object, offset); -// } -// -// /** -// * Get the boolean value of the specified object field -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @return boolean value -// */ -// public static boolean getBoolean(Object object, String fieldName) { -// long offset = getObjectFieldOffset(object, fieldName); -// return unsafe.getBoolean(object, offset); -// } -// -// /** -// * Get the char value of the specified object field -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @return char value -// */ -// public static char getChar(Object object, String fieldName) { -// long offset = getObjectFieldOffset(object, fieldName); -// return unsafe.getChar(object, offset); -// } -// -// /** -// * Get the int value of the specified object field -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @return int value -// */ -// public static int getInt(Object object, String fieldName) { -// long offset = getObjectFieldOffset(object, fieldName); -// return unsafe.getInt(object, offset); -// } -// -// /** -// * Get the Object value of the specified object volatile field -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @return Object value -// */ -// public static Object getObjectVolatile(Object object, String fieldName) { -// long offset = getObjectFieldOffset(object, fieldName); -// return unsafe.getObjectVolatile(object, offset); -// } -// -// /** -// * Get the long value of the volatile field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @return long value -// */ -// public static long getLongVolatile(Object object, String fieldName) { -// long offset = getObjectFieldOffset(object, fieldName); -// return unsafe.getLongVolatile(object, offset); -// } -// -// /** -// * Get the double value of the volatile field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @return double value -// */ -// public static double getDoubleVolatile(Object object, String fieldName) { -// long offset = getObjectFieldOffset(object, fieldName); -// return unsafe.getDoubleVolatile(object, offset); -// } -// -// /** -// * Get the float value of the volatile field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @return float value -// */ -// public static float getFloatVolatile(Object object, String fieldName) { -// long offset = getObjectFieldOffset(object, fieldName); -// return unsafe.getFloatVolatile(object, offset); -// } -// -// /** -// * Get the short value of the volatile field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @return short value -// */ -// public static short getShortVolatile(Object object, String fieldName) { -// long offset = getObjectFieldOffset(object, fieldName); -// return unsafe.getShortVolatile(object, offset); -// } -// -// /** -// * Get the byte value of the volatile field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @return byte value -// */ -// public static byte getByteVolatile(Object object, String fieldName) { -// long offset = getObjectFieldOffset(object, fieldName); -// return unsafe.getByteVolatile(object, offset); -// } -// -// /** -// * Get the boolean value of the specified object field -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @return boolean value -// */ -// public static boolean getBooleanVolatile(Object object, String fieldName) { -// long offset = getObjectFieldOffset(object, fieldName); -// return unsafe.getBooleanVolatile(object, offset); -// } -// -// /** -// * Get the char value of the volatile field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @return char value -// */ -// public static char getCharVolatile(Object object, String fieldName) { -// long offset = getObjectFieldOffset(object, fieldName); -// return unsafe.getCharVolatile(object, offset); -// } -// -// /** -// * Get the int value of the volatile field of the specified object -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @return int value -// */ -// public static int getIntVolatile(Object object, String fieldName) { -// long offset = getObjectFieldOffset(object, fieldName); -// return unsafe.getIntVolatile(object, offset); -// } -// -// /** -// * get the offset of the object field -// * -// * @param object the target object -// * @param fieldName the name of {@link Field} -// * @return offset -// * @throws IllegalArgumentException If the class is null, or the field name is blank or empty or is matched at multiple places in the inheritance hierarchy -// * @throws NullPointerException If any argument is null -// */ -// public static long getObjectFieldOffset(Object object, String fieldName) throws IllegalArgumentException, NullPointerException { -// Class type = object.getClass(); -// Long offsetFromCache = getOffsetFromCache(type, fieldName); -// if (offsetFromCache != null) { -// return offsetFromCache; -// } -// Field field = findField(type, fieldName); -// long offset = unsafe.objectFieldOffset(field); -// putOffsetFromCache(type, fieldName, offset); -// return offset; -// } -// -// /** -// * Get the offset of a class static field -// * -// * @param type the target type -// * @param fieldName the name of {@link Field} -// * @return offset -// */ -// public static long getStaticFieldOffset(Class type, String fieldName) { -// Field field = findField(type, fieldName); -// return unsafe.staticFieldOffset(field); -// } -//} +package io.microsphere.misc; + +import io.microsphere.util.Assert; +import io.microsphere.util.Utils; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.security.PrivilegedExceptionAction; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import static io.microsphere.lang.function.ThrowableSupplier.execute; +import static io.microsphere.reflect.FieldUtils.findField; +import static io.microsphere.reflect.FieldUtils.getFieldValue; +import static io.microsphere.reflect.FieldUtils.getStaticFieldValue; +import static io.microsphere.reflect.MethodUtils.findMethod; +import static io.microsphere.reflect.MethodUtils.invokeMethod; +import static io.microsphere.util.Assert.assertArrayIndex; +import static io.microsphere.util.Assert.assertFieldMatchType; +import static io.microsphere.util.ClassLoaderUtils.loadClass; +import static java.lang.ClassLoader.getSystemClassLoader; +import static java.security.AccessController.doPrivileged; + +/** + * {@link sun.misc.Unsafe} Utility class

Take case to use those utility methods in order to the stability fo + * JVM + * + * @author Mercy + * @see sun.misc.Unsafe + * @since 1.0.0 + */ +public abstract class UnsafeUtils implements Utils { + + static final String UNSAFE_CLASS_NAME = "sun.misc.Unsafe"; + + static final Class UNSAFE_CLASS = loadClass(getSystemClassLoader(), UNSAFE_CLASS_NAME); + + static final Object unsafe = getUnsafe(); + + // Peek and Poke operations | (compilers should optimize these to memory ops) + // These work on object fields in the Java heap. + // They will not work on elements of packed arrays. + + /** + * @see {@link sun.misc.Unsafe#getInt(Object, long)} + */ + static final Method getIntMethod = findMethod(UNSAFE_CLASS, "getInt", Object.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#putInt(Object, long, int)} + */ + static final Method putIntMethod = findMethod(UNSAFE_CLASS, "putInt", Object.class, long.class, int.class); + + /** + * @see {@link sun.misc.Unsafe#getObject(Object, long)} + */ + static final Method getObjectMethod = findMethod(UNSAFE_CLASS, "getObject", Object.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#putObject(Object, long, Object)} + */ + static final Method putObjectMethod = findMethod(UNSAFE_CLASS, "putObject", Object.class, long.class, Object.class); + + /** + * @see {@link sun.misc.Unsafe#getBoolean(Object, long)} + */ + static final Method getBooleanMethod = findMethod(UNSAFE_CLASS, "getBoolean", Object.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#putBoolean(Object, long, boolean)} + */ + static final Method putBooleanMethod = findMethod(UNSAFE_CLASS, "putBoolean", Object.class, long.class, boolean.class); + + /** + * @see {@link sun.misc.Unsafe#getByte(Object, long)} + */ + static final Method getByteMethod = findMethod(UNSAFE_CLASS, "getByte", Object.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#putByte(Object, long, byte)} + */ + static final Method putByteMethod = findMethod(UNSAFE_CLASS, "putByte", Object.class, long.class, byte.class); + + /** + * @see {@link sun.misc.Unsafe#getShort(Object, long)} + */ + static final Method getShortMethod = findMethod(UNSAFE_CLASS, "getShort", Object.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#putShort(Object, long, short)} + */ + static final Method putShortMethod = findMethod(UNSAFE_CLASS, "putShort", Object.class, long.class, short.class); + + /** + * @see {@link sun.misc.Unsafe#getChar(Object, long)} + */ + static final Method getCharMethod = findMethod(UNSAFE_CLASS, "getChar", Object.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#putChar(Object, long, char)} + */ + static final Method putCharMethod = findMethod(UNSAFE_CLASS, "putChar", Object.class, long.class, char.class); + + /** + * @see {@link sun.misc.Unsafe#getLong(Object, long)} + */ + static final Method getLongMethod = findMethod(UNSAFE_CLASS, "getLong", Object.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#putLong(Object, long, long)} + */ + static final Method putLongMethod = findMethod(UNSAFE_CLASS, "putLong", Object.class, long.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#getFloat(Object, long)} + */ + static final Method getFloatMethod = findMethod(UNSAFE_CLASS, "getFloat", Object.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#putFloat(Object, long, float)} + */ + static final Method putFloatMethod = findMethod(UNSAFE_CLASS, "putFloat", Object.class, long.class, float.class); + + /** + * @see {@link sun.misc.Unsafe#getDouble(Object, long)} + */ + static final Method getDoubleMethod = findMethod(UNSAFE_CLASS, "getDouble", Object.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#putDouble(Object, long, double)} + */ + static final Method putDoubleMethod = findMethod(UNSAFE_CLASS, "putDouble", Object.class, long.class, double.class); + + // These work on values in the C heap. + + /** + * @see {@link sun.misc.Unsafe#getByte(long)} + */ + static final Method getByteFromAddressMethod = findMethod(UNSAFE_CLASS, "getByte", long.class); + + /** + * @see {@link sun.misc.Unsafe#putByte(long, byte)} + */ + static final Method putByteToAddressMethod = findMethod(UNSAFE_CLASS, "putByte", long.class, byte.class); + + /** + * @see {@link sun.misc.Unsafe#getShort(long)} + */ + static final Method getShortFromAddressMethod = findMethod(UNSAFE_CLASS, "getShort", long.class); + + /** + * @see {@link sun.misc.Unsafe#putShort(long, short)} + */ + static final Method putShortToAddressMethod = findMethod(UNSAFE_CLASS, "putShort", long.class, short.class); + + /** + * @see {@link sun.misc.Unsafe#getChar(long)} + */ + static final Method getCharFromAddressMethod = findMethod(UNSAFE_CLASS, "getChar", long.class); + + /** + * @see {@link sun.misc.Unsafe#putChar(long, char)} + */ + static final Method putCharToAddressMethod = findMethod(UNSAFE_CLASS, "putChar", long.class, char.class); + + /** + * @see {@link sun.misc.Unsafe#getInt(long)} + */ + static final Method getIntFromAddressMethod = findMethod(UNSAFE_CLASS, "getInt", long.class); + + /** + * @see {@link sun.misc.Unsafe#putInt(long, int)} + */ + static final Method putIntToAddressMethod = findMethod(UNSAFE_CLASS, "putInt", long.class, int.class); + + /** + * @see {@link sun.misc.Unsafe#getLong(long)} + */ + static final Method getLongFromAddressMethod = findMethod(UNSAFE_CLASS, "getLong", long.class); + + /** + * @see {@link sun.misc.Unsafe#putLong(long, long)} + */ + static final Method putLongToAddressMethod = findMethod(UNSAFE_CLASS, "putLong", long.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#getFloat(long)} + */ + static final Method getFloatFromAddressMethod = findMethod(UNSAFE_CLASS, "getFloat", long.class); + + /** + * @see {@link sun.misc.Unsafe#putFloat(long, float)} + */ + static final Method putFloatToAddressMethod = findMethod(UNSAFE_CLASS, "putFloat", long.class, float.class); + + /** + * @see {@link sun.misc.Unsafe#getDouble(long)} + */ + static final Method getDoubleFromAddressMethod = findMethod(UNSAFE_CLASS, "getDouble", long.class); + + /** + * @see {@link sun.misc.Unsafe#putDouble(long, double)} + */ + static final Method putDoubleToAddressMethod = findMethod(UNSAFE_CLASS, "putDouble", long.class, double.class); + + /** + * @see {@link sun.misc.Unsafe#getAddress(long)} + */ + static final Method getAddressMethod = findMethod(UNSAFE_CLASS, "getAddress", long.class); + + /** + * @see {@link sun.misc.Unsafe#putAddress(long, long)} + */ + static final Method putAddressMethod = findMethod(UNSAFE_CLASS, "putAddress", long.class, long.class); + + // Wrappers for malloc, realloc, free: + + /** + * @see {@link sun.misc.Unsafe#allocateMemory(long)} + */ + static final Method allocateMemoryMethod = findMethod(UNSAFE_CLASS, "allocateMemory", long.class); + + /** + * @see {@link sun.misc.Unsafe#reallocateMemory(long, long)} + */ + static final Method reallocateMemoryMethod = findMethod(UNSAFE_CLASS, "reallocateMemory", long.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#setMemory(Object, long, long, byte)} + */ + static final Method setMemoryMethod = findMethod(UNSAFE_CLASS, "setMemory", Object.class, long.class, long.class, byte.class); + + /** + * @see {@link sun.misc.Unsafe#setMemory(long, long, byte)} + */ + static final Method setMemoryToAddressMethod = findMethod(UNSAFE_CLASS, "setMemory", long.class, long.class, byte.class); + + /** + * @see {@link sun.misc.Unsafe#copyMemory(Object, long, Object, long, long)} + */ + static final Method copyMemoryMethod = findMethod(UNSAFE_CLASS, "copyMemory", Object.class, long.class, Object.class, long.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#copyMemory(long, long, long)} + */ + static final Method copyMemoryFromAddressMethod = findMethod(UNSAFE_CLASS, "copyMemory", long.class, long.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#freeMemory(long)} + */ + static final Method freeMemoryMethod = findMethod(UNSAFE_CLASS, "freeMemory", long.class); + + // Random queries + + /** + * @see {@link sun.misc.Unsafe#objectFieldOffset(Field)} + */ + static final Method objectFieldOffsetMethod = findMethod(UNSAFE_CLASS, "objectFieldOffset", Field.class); + + /** + * @see {@link sun.misc.Unsafe#staticFieldOffset(Field)} + */ + static final Method staticFieldOffsetMethod = findMethod(UNSAFE_CLASS, "staticFieldOffset", Field.class); + + /** + * @see {@link sun.misc.Unsafe#staticFieldBase(Field)} + */ + static final Method staticFieldBaseMethod = findMethod(UNSAFE_CLASS, "staticFieldBase", Field.class); + + /** + * @see {@link sun.misc.Unsafe#arrayBaseOffset(Class)} + */ + static final Method arrayBaseOffsetMethod = findMethod(UNSAFE_CLASS, "arrayBaseOffset", Class.class); + + /** + * @see {@link sun.misc.Unsafe#arrayIndexScale(Class)} + */ + static final Method arrayIndexScaleMethod = findMethod(UNSAFE_CLASS, "arrayIndexScale", Class.class); + + /** + * @see {@link sun.misc.Unsafe#addressSize()} + */ + static final Method addressSizeMethod = findMethod(UNSAFE_CLASS, "addressSize"); + + /** + * @see {@link sun.misc.Unsafe#pageSize()} + */ + static final Method pageSizeMethod = findMethod(UNSAFE_CLASS, "pageSize"); + + // Random trusted operations from JNI: + + /** + * @see {@link sun.misc.Unsafe#allocateInstance(Class)} + */ + static final Method allocateInstanceMethod = findMethod(UNSAFE_CLASS, "allocateInstance", Class.class); + + /** + * @see {@link sun.misc.Unsafe#throwException(Throwable)} + */ + static final Method throwExceptionMethod = findMethod(UNSAFE_CLASS, "throwException", Throwable.class); + + /** + * @see {@link sun.misc.Unsafe#compareAndSwapObject(Object, long, Object, Object)} + */ + static final Method compareAndSwapObjectMethod = findMethod(UNSAFE_CLASS, "compareAndSwapObject", Object.class, long.class, Object.class, Object.class); + + /** + * @see {@link sun.misc.Unsafe#compareAndSwapInt(Object, long, int, int)} + */ + static final Method compareAndSwapIntMethod = findMethod(UNSAFE_CLASS, "compareAndSwapInt", Object.class, long.class, int.class, int.class); + + /** + * @see {@link sun.misc.Unsafe#compareAndSwapLong(Object, long, long, long)} + */ + static final Method compareAndSwapLongMethod = findMethod(UNSAFE_CLASS, "compareAndSwapLong", Object.class, long.class, long.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#getObjectVolatile(Object, long)} + */ + static final Method getObjectVolatileMethod = findMethod(UNSAFE_CLASS, "getObjectVolatile", Object.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#putObjectVolatile(Object, long, Object)} + */ + static final Method putObjectVolatileMethod = findMethod(UNSAFE_CLASS, "putObjectVolatile", Object.class, long.class, Object.class); + + /** + * @see {@link sun.misc.Unsafe#getIntVolatile(Object, long)} + */ + static final Method getIntVolatileMethod = findMethod(UNSAFE_CLASS, "getIntVolatile", Object.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#putIntVolatile(Object, long, int)} + */ + static final Method putIntVolatileMethod = findMethod(UNSAFE_CLASS, "putIntVolatile", Object.class, long.class, int.class); + + /** + * @see {@link sun.misc.Unsafe#getBooleanVolatile(Object, long)} + */ + static final Method getBooleanVolatileMethod = findMethod(UNSAFE_CLASS, "getBooleanVolatile", Object.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#putBooleanVolatile(Object, long, boolean)} + */ + static final Method putBooleanVolatileMethod = findMethod(UNSAFE_CLASS, "putBooleanVolatile", Object.class, long.class, boolean.class); + + /** + * @see {@link sun.misc.Unsafe#getByteVolatile(Object, long)} + */ + static final Method getByteVolatileMethod = findMethod(UNSAFE_CLASS, "getByteVolatile", Object.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#putByteVolatile(Object, long, byte)} + */ + static final Method putByteVolatileMethod = findMethod(UNSAFE_CLASS, "putByteVolatile", Object.class, long.class, byte.class); + + /** + * @see {@link sun.misc.Unsafe#getShortVolatile(Object, long)} + */ + static final Method getShortVolatileMethod = findMethod(UNSAFE_CLASS, "getShortVolatile", Object.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#putShortVolatile(Object, long, short)} + */ + static final Method putShortVolatileMethod = findMethod(UNSAFE_CLASS, "putShortVolatile", Object.class, long.class, short.class); + + /** + * @see {@link sun.misc.Unsafe#getCharVolatile(Object, long)} + */ + static final Method getCharVolatileMethod = findMethod(UNSAFE_CLASS, "getCharVolatile", Object.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#putCharVolatile(Object, long, char)} + */ + static final Method putCharVolatileMethod = findMethod(UNSAFE_CLASS, "putCharVolatile", Object.class, long.class, char.class); + + /** + * @see {@link sun.misc.Unsafe#getLongVolatile(Object, long)} + */ + static final Method getLongVolatileMethod = findMethod(UNSAFE_CLASS, "getLongVolatile", Object.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#putLongVolatile(Object, long, long)} + */ + static final Method putLongVolatileMethod = findMethod(UNSAFE_CLASS, "putLongVolatile", Object.class, long.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#getFloatVolatile(Object, long)} + */ + static final Method getFloatVolatileMethod = findMethod(UNSAFE_CLASS, "getFloatVolatile", Object.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#putFloatVolatile(Object, long, float)} + */ + static final Method putFloatVolatileMethod = findMethod(UNSAFE_CLASS, "putFloatVolatile", Object.class, long.class, float.class); + + /** + * @see {@link sun.misc.Unsafe#getDoubleVolatile(Object, long)} + */ + static final Method getDoubleVolatileMethod = findMethod(UNSAFE_CLASS, "getDoubleVolatile", Object.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#putDoubleVolatile(Object, long, double)} + */ + static final Method putDoubleVolatileMethod = findMethod(UNSAFE_CLASS, "putDoubleVolatile", Object.class, long.class, double.class); + + /** + * @see {@link sun.misc.Unsafe#putOrderedObject(Object, long, Object)} + */ + static final Method putOrderedObjectMethod = findMethod(UNSAFE_CLASS, "putOrderedObject", Object.class, long.class, Object.class); + + /** + * @see {@link sun.misc.Unsafe#putOrderedInt(Object, long, int)} + */ + static final Method putOrderedIntMethod = findMethod(UNSAFE_CLASS, "putOrderedInt", Object.class, long.class, int.class); + + /** + * @see {@link sun.misc.Unsafe#putOrderedLong(Object, long, long)} + */ + static final Method putOrderedLongMethod = findMethod(UNSAFE_CLASS, "putOrderedLong", Object.class, long.class, long.class); + + // The following contain CAS-based Java implementations used on platforms not supporting native instructions + + /** + * @see {@link sun.misc.Unsafe#getAndAddInt(Object, long, int)} + */ + static final Method getAndAddIntMethod = findMethod(UNSAFE_CLASS, "getAndAddInt", Object.class, long.class, int.class); + + /** + * @see {@link sun.misc.Unsafe#getAndAddLong(Object, long, long)} + */ + static final Method getAndAddLongMethod = findMethod(UNSAFE_CLASS, "getAndAddLong", Object.class, long.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#getAndSetInt(Object, long, int)} + */ + static final Method getAndSetIntMethod = findMethod(UNSAFE_CLASS, "getAndSetInt", Object.class, long.class, int.class); + + /** + * @see {@link sun.misc.Unsafe#getAndSetLong(Object, long, long)} + */ + static final Method getAndSetLongMethod = findMethod(UNSAFE_CLASS, "getAndSetLong", Object.class, long.class, long.class); + + /** + * @see {@link sun.misc.Unsafe#getAndSetObject(Object, long, Object)} + */ + static final Method getAndSetObjectMethod = findMethod(UNSAFE_CLASS, "getAndSetObject", Object.class, long.class, Object.class); + + /** + * @see {@link sun.misc.Unsafe#loadFence()} + */ + static final Method loadFenceMethod = findMethod(UNSAFE_CLASS, "loadFence"); + + /** + * @see {@link sun.misc.Unsafe#storeFence()} + */ + static final Method storeFenceMethod = findMethod(UNSAFE_CLASS, "storeFence"); + + /** + * @see {@link sun.misc.Unsafe#fullFence()} + */ + static final Method fullFenceMethod = findMethod(UNSAFE_CLASS, "fullFence"); + + /** + * long Array base index + * + * @see {@link sun.misc.Unsafe#ARRAY_LONG_BASE_OFFSET} + */ + public static final int LONG_ARRAY_BASE_OFFSET = getStaticFieldValue(UNSAFE_CLASS, "ARRAY_LONG_BASE_OFFSET"); + + /** + * int Array base index + */ + public static final int INT_ARRAY_BASE_OFFSET = getStaticFieldValue(UNSAFE_CLASS, "ARRAY_INT_BASE_OFFSET"); + + /** + * short Array base index + */ + public static final int SHORT_ARRAY_BASE_OFFSET = getStaticFieldValue(UNSAFE_CLASS, "ARRAY_SHORT_BASE_OFFSET"); + + /** + * byte Array base index + */ + public static final int BYTE_ARRAY_BASE_OFFSET = getStaticFieldValue(UNSAFE_CLASS, "ARRAY_BYTE_BASE_OFFSET"); + + /** + * boolean Array base index + */ + public static final int BOOLEAN_ARRAY_BASE_OFFSET = getStaticFieldValue(UNSAFE_CLASS, "ARRAY_BOOLEAN_BASE_OFFSET"); + + /** + * double Array base index + */ + public static final int DOUBLE_ARRAY_BASE_OFFSET = getStaticFieldValue(UNSAFE_CLASS, "ARRAY_DOUBLE_BASE_OFFSET"); + + /** + * float Array base index + */ + public static final int FLOAT_ARRAY_BASE_OFFSET = getStaticFieldValue(UNSAFE_CLASS, "ARRAY_FLOAT_BASE_OFFSET"); + + /** + * char Array base index + */ + public static final int CHAR_ARRAY_BASE_OFFSET = getStaticFieldValue(UNSAFE_CLASS, "ARRAY_CHAR_BASE_OFFSET"); + + /** + * java.lang.Object Array base index + */ + public static final int OBJECT_ARRAY_BASE_OFFSET = getStaticFieldValue(UNSAFE_CLASS, "ARRAY_OBJECT_BASE_OFFSET"); + + /** + * long Array Index scale + */ + public static final int LONG_ARRAY_INDEX_SCALE = getStaticFieldValue(UNSAFE_CLASS, "ARRAY_LONG_INDEX_SCALE"); + + /** + * int Array Index scale + */ + public static final int INT_ARRAY_INDEX_SCALE = getStaticFieldValue(UNSAFE_CLASS, "ARRAY_INT_INDEX_SCALE"); + + /** + * short Array Index scale + */ + public static final int SHORT_ARRAY_INDEX_SCALE = getStaticFieldValue(UNSAFE_CLASS, "ARRAY_SHORT_INDEX_SCALE"); + + /** + * byte Array Index scale + */ + public static final int BYTE_ARRAY_INDEX_SCALE = getStaticFieldValue(UNSAFE_CLASS, "ARRAY_BYTE_INDEX_SCALE"); + + /** + * boolean Array Index scale + */ + public static final int BOOLEAN_ARRAY_INDEX_SCALE = getStaticFieldValue(UNSAFE_CLASS, "ARRAY_BOOLEAN_INDEX_SCALE"); + + /** + * double Array Index scale + */ + public static final int DOUBLE_ARRAY_INDEX_SCALE = getStaticFieldValue(UNSAFE_CLASS, "ARRAY_DOUBLE_INDEX_SCALE"); + + /** + * float Array Index scale + */ + public static final int FLOAT_ARRAY_INDEX_SCALE = getStaticFieldValue(UNSAFE_CLASS, "ARRAY_FLOAT_INDEX_SCALE"); + + /** + * char Array Index scale + */ + public static final int CHAR_ARRAY_INDEX_SCALE = getStaticFieldValue(UNSAFE_CLASS, "ARRAY_CHAR_INDEX_SCALE"); + + /** + * java.lang.Object Array Index scale + */ + public static final int OBJECT_ARRAY_INDEX_SCALE = getStaticFieldValue(UNSAFE_CLASS, "ARRAY_OBJECT_INDEX_SCALE"); + + /** + * Offset Cache, + */ + private final static ConcurrentMap offsetCache = new ConcurrentHashMap<>(); + + /** + * @return the {@link sun.misc.Unsafe} instance + */ + static Object getUnsafe() { + final PrivilegedExceptionAction action = () -> { + Field theUnsafe = UNSAFE_CLASS.getDeclaredField("theUnsafe"); + theUnsafe.setAccessible(true); + return theUnsafe.get(null); + }; + return execute(() -> doPrivileged(action), e -> { + throw new UnsupportedOperationException("Current JVM does not support sun.misc.Unsafe", e); + }); + } + + /** + * Calculate Array Index Offset + * + * @param index Index + * @param baseOffset {@link sun.misc.Unsafe#arrayBaseOffset(Class)} + * @param indexScale {@link sun.misc.Unsafe#arrayIndexScale(Class)} + * @return + * @see java.util.concurrent.atomic.AtomicIntegerArray + */ + protected static long arrayIndexOffset(int index, long baseOffset, long indexScale) { + if (index < 0) + throw new IndexOutOfBoundsException("index " + index); + return baseOffset + (long) index * indexScale; + } + + /** + * Calculate the long type Array Index the relative offset + * + * @param index Array Index + * @return the relative offset + */ + protected static long longArrayIndexOffset(int index) { + return arrayIndexOffset(index, LONG_ARRAY_BASE_OFFSET, LONG_ARRAY_INDEX_SCALE); + } + + /** + * Calculate int type Array Index the relative offset + * + * @param index Array Index + * @return the relative offset + */ + protected static long intArrayIndexOffset(int index) { + return arrayIndexOffset(index, INT_ARRAY_BASE_OFFSET, INT_ARRAY_INDEX_SCALE); + } + + /** + * Calculate the short type Array Index the relative offset + * + * @param index Array Index + * @return the relative offset + */ + protected static long shortArrayIndexOffset(int index) { + return arrayIndexOffset(index, SHORT_ARRAY_BASE_OFFSET, SHORT_ARRAY_INDEX_SCALE); + } + + /** + * Calculate the relative offset of Array Index of type byte + * + * @param index Array Index + * @return the relative offset + */ + protected static long byteArrayIndexOffset(int index) { + return arrayIndexOffset(index, BYTE_ARRAY_BASE_OFFSET, BYTE_ARRAY_INDEX_SCALE); + } + + /** + * Calculate boolean type Array Index the relative offset + * + * @param index Array Index + * @return the relative offset + */ + protected static long booleanArrayIndexOffset(int index) { + return arrayIndexOffset(index, BOOLEAN_ARRAY_BASE_OFFSET, BOOLEAN_ARRAY_INDEX_SCALE); + } + + /** + * Calculate the relative offset of Array Index of type double + * + * @param index Array Index + * @return the relative offset + */ + protected static long doubleArrayIndexOffset(int index) { + return arrayIndexOffset(index, DOUBLE_ARRAY_BASE_OFFSET, DOUBLE_ARRAY_INDEX_SCALE); + } + + /** + * Calculate float type Array Index the relative offset + * + * @param index Array Index + * @return the relative offset + */ + protected static long floatArrayIndexOffset(int index) { + return arrayIndexOffset(index, FLOAT_ARRAY_BASE_OFFSET, FLOAT_ARRAY_INDEX_SCALE); + } + + /** + * Calculate char type Array Index the relative offset + * + * @param index Array Index + * @return the relative offset + */ + protected static long charArrayIndexOffset(int index) { + return arrayIndexOffset(index, CHAR_ARRAY_BASE_OFFSET, CHAR_ARRAY_INDEX_SCALE); + } + + /** + * Calculate java.lang.Object type Array Index the relative offset + * + * @param index Array Index + * @return the relative offset + */ + protected static long objectArrayIndexOffset(int index) { + return arrayIndexOffset(index, OBJECT_ARRAY_BASE_OFFSET, OBJECT_ARRAY_INDEX_SCALE); + } + + /** + * Create Offset Cache Key + * + * @param type the target type + * @param fieldName the name of {@link Field} + * @return The cache key of offset + */ + protected static String createOffsetCacheKey(Class type, String fieldName) { + return type.getName() + "#" + fieldName; + } + + /** + * Get Offset in cache + * + * @param type the target type + * @param fieldName the name of {@link Field} + * @return Offset + */ + protected static Long getOffsetFromCache(Class type, String fieldName) { + String key = createOffsetCacheKey(type, fieldName); + return offsetCache.get(key); + } + + /** + * Save offset to cache + * + * @param type the target type + * @param fieldName the name of {@link Field} + * @param offset offset + */ + protected static void putOffsetFromCache(Class type, String fieldName, long offset) { + String key = createOffsetCacheKey(type, fieldName); + offsetCache.putIfAbsent(key, offset); + } + + /** + * Get the long value of the target Index in the Array field of the object + * + * @param object Object + * @param fieldName the name of {@link Field} + * @param index the index + * @return the long value + */ + public static long getLongVolatileFromArray(Object object, String fieldName, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, NullPointerException { + Object array = getFieldValue(true, object, fieldName); + assertArrayIndex(array, index); + long offset = longArrayIndexOffset(index); + return getLongVolatile(array, offset); + } + + /** + * Get the value of the target Index in the object Array field + * + * @param object Object + * @param fieldName the name of {@link Field} + * @param index the index + * @return the int value + */ + public static int getIntVolatileFromArray(Object object, String fieldName, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, NullPointerException { + Object array = getFieldValue(true, object, fieldName); + assertArrayIndex(array, index); + long offset = intArrayIndexOffset(index); + return getIntVolatile(array, offset); + } + + /** + * Get the short value of the target Index in the Array field of the object + * + * @param object Object + * @param fieldName the name of {@link Field} + * @param index the index + * @return the short value + */ + public static short getShortVolatileFromArray(Object object, String fieldName, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, NullPointerException { + Object array = getFieldValue(true, object, fieldName); + assertArrayIndex(array, index); + long offset = shortArrayIndexOffset(index); + return getShortVolatile(array, offset); + } + + /** + * Get the byte value of the target Index in the object Array field + * + * @param object Object + * @param fieldName the name of {@link Field} + * @param index the index + * @return the byte value + */ + public static byte getByteVolatileFromArray(Object object, String fieldName, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, NullPointerException { + Object array = getFieldValue(true, object, fieldName); + assertArrayIndex(array, index); + long offset = byteArrayIndexOffset(index); + return getByteVolatile(array, offset); + } + + /** + * Get the boolean value of the target Index in the Array field of the object + * + * @param object Object + * @param fieldName the name of {@link Field} + * @param index the index + * @return the boolean value + */ + public static boolean getBooleanVolatileFromArray(Object object, String fieldName, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, NullPointerException { + Object array = getFieldValue(true, object, fieldName); + assertArrayIndex(array, index); + long offset = booleanArrayIndexOffset(index); + return getBooleanVolatile(array, offset); + } + + /** + * Get the double value of the target Index in the Array field of the object + * + * @param object Object + * @param fieldName the name of {@link Field} + * @param index the index + * @return the double value + */ + public static double getDoubleVolatileFromArray(Object object, String fieldName, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, NullPointerException { + Object array = getFieldValue(true, object, fieldName); + assertArrayIndex(array, index); + long offset = doubleArrayIndexOffset(index); + return getDoubleVolatile(array, offset); + } + + /** + * Get the float value of the target Index in the object Array field + * + * @param object Object + * @param fieldName the name of {@link Field} + * @param index the index + * @return the target index + */ + public static float getFloatVolatileFromArray(Object object, String fieldName, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, NullPointerException { + Object array = getFieldValue(true, object, fieldName); + assertArrayIndex(array, index); + long offset = floatArrayIndexOffset(index); + return getFloatVolatile(array, offset); + } + + /** + * Get the char value of the target Index in the object Array field + * + * @param object Object + * @param fieldName the name of {@link Field} + * @param index the index + * @return the char value + * @throws IllegalArgumentException See {@link Assert#assertArrayType(Object)} + * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length + */ + public static char getCharVolatileFromArray(Object object, String fieldName, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, NullPointerException { + Object array = getFieldValue(true, object, fieldName); + assertArrayIndex(array, index); + long offset = charArrayIndexOffset(index); + return getCharVolatile(array, offset); + } + + /** + * Get the java.lang.Object value of the target Index in the object Array field + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param index the index + * @return the java.lang.Object value + * @throws IllegalArgumentException see {@link Assert#assertArrayType(Object)} + * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length + */ + public static Object getObjectVolatileFromArray(Object object, String fieldName, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, NullPointerException { + Object array = getFieldValue(true, object, fieldName); + assertArrayIndex(array, index); + long offset = objectArrayIndexOffset(index); + return getObjectVolatile(array, offset); + } + + /** + * Sets the given double value to the field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param value double value + */ + public static void putDouble(Object object, String fieldName, double value) { + assertFieldMatchType(object, fieldName, double.class); + long offset = objectFieldOffset(object, fieldName); + putDouble(object, offset, value); + } + + /** + * Sets the given float value to the field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param value float value + */ + public static void putFloat(Object object, String fieldName, float value) { + assertFieldMatchType(object, fieldName, float.class); + long offset = objectFieldOffset(object, fieldName); + putFloat(object, offset, value); + } + + /** + * Sets the given short value to the field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param value short value + */ + public static void putShort(Object object, String fieldName, short value) { + assertFieldMatchType(object, fieldName, short.class); + long offset = objectFieldOffset(object, fieldName); + putShort(object, offset, value); + } + + /** + * Sets the given byte value to the field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param value byte value + */ + public static void putByte(Object object, String fieldName, byte value) { + assertFieldMatchType(object, fieldName, byte.class); + long offset = objectFieldOffset(object, fieldName); + putByte(object, offset, value); + } + + /** + * Sets the given boolean value to the field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param value boolean value + */ + public static void putBoolean(Object object, String fieldName, boolean value) { + assertFieldMatchType(object, fieldName, boolean.class); + long offset = objectFieldOffset(object, fieldName); + putBoolean(object, offset, value); + } + + /** + * Sets the given char value to the field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param value char value + */ + public static void putChar(Object object, String fieldName, char value) { + assertFieldMatchType(object, fieldName, char.class); + long offset = objectFieldOffset(object, fieldName); + putChar(object, offset, value); + } + + /** + * Sets the given Object value to the field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param value Object value + */ + public static void putObject(Object object, String fieldName, Object value) { + assertFieldMatchType(object, fieldName, Object.class); + long offset = objectFieldOffset(object, fieldName); + putObject(object, offset, value); + } + + /** + * Sets the given long value to the field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param value long value + */ + public static void putLong(Object object, String fieldName, long value) { + assertFieldMatchType(object, fieldName, long.class); + long offset = objectFieldOffset(object, fieldName); + putLong(object, offset, value); + } + + /** + * Sets the given int value to the field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param value int value + */ + public static void putInt(Object object, String fieldName, int value) { + assertFieldMatchType(object, fieldName, int.class); + long offset = objectFieldOffset(object, fieldName); + putInt(object, offset, value); + } + + /** + * Sets the given int value to the field of the specified object (ensures writing order) + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param value int value + */ + public static void putOrderedInt(Object object, String fieldName, int value) { + assertFieldMatchType(object, fieldName, int.class); + long offset = objectFieldOffset(object, fieldName); + putOrderedInt(object, offset, value); + } + + /** + * Sets the given long value to the field of the specified object (ensures writing order) + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param value long value + */ + public static void putOrderedLong(Object object, String fieldName, long value) { + assertFieldMatchType(object, fieldName, long.class); + long offset = objectFieldOffset(object, fieldName); + putOrderedLong(object, offset, value); + } + + /** + * Set the given Object value to the field of the specified object (ensure write order) + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param value Object value + */ + public static void putOrderedObject(Object object, String fieldName, Object value) { + assertFieldMatchType(object, fieldName, Object.class); + long offset = objectFieldOffset(object, fieldName); + putOrderedObject(object, offset, value); + } + + /** + * Sets the given double value to the volatile field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param value double value + */ + public static void putDoubleVolatile(Object object, String fieldName, double value) { + assertFieldMatchType(object, fieldName, double.class); + long offset = objectFieldOffset(object, fieldName); + putDoubleVolatile(object, offset, value); + } + + /** + * Sets the given float value to the volatile field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param value float value + */ + public static void putFloatVolatile(Object object, String fieldName, float value) { + assertFieldMatchType(object, fieldName, float.class); + long offset = objectFieldOffset(object, fieldName); + putFloatVolatile(object, offset, value); + } + + /** + * Sets the given short value to the volatile field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param value short value + */ + public static void putShortVolatile(Object object, String fieldName, short value) { + assertFieldMatchType(object, fieldName, short.class); + long offset = objectFieldOffset(object, fieldName); + putShortVolatile(object, offset, value); + } + + /** + * Sets the given byte value to the volatile field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param value byte value + */ + public static void putByteVolatile(Object object, String fieldName, byte value) { + assertFieldMatchType(object, fieldName, byte.class); + long offset = objectFieldOffset(object, fieldName); + putByteVolatile(object, offset, value); + } + + /** + * Sets the given boolean value to the volatile field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param value boolean value + */ + public static void putBooleanVolatile(Object object, String fieldName, boolean value) { + assertFieldMatchType(object, fieldName, boolean.class); + long offset = objectFieldOffset(object, fieldName); + putBooleanVolatile(object, offset, value); + } + + /** + * Sets the given char value to the volatile field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param value char value + */ + public static void putCharVolatile(Object object, String fieldName, char value) { + assertFieldMatchType(object, fieldName, char.class); + long offset = objectFieldOffset(object, fieldName); + putCharVolatile(object, offset, value); + } + + /** + * Sets the given Object value to the volatile field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param value Object value + */ + public static void putObjectVolatile(Object object, String fieldName, Object value) { + assertFieldMatchType(object, fieldName, Object.class); + long offset = objectFieldOffset(object, fieldName); + putObjectVolatile(object, offset, value); + } + + /** + * Sets the given long value to the volatile field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param value long value + */ + public static void putLongVolatile(Object object, String fieldName, long value) { + assertFieldMatchType(object, fieldName, long.class); + long offset = objectFieldOffset(object, fieldName); + putLongVolatile(object, offset, value); + } + + /** + * Sets the given int value to the volatile field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param value int value + */ + public static void putIntVolatile(Object object, String fieldName, int value) { + assertFieldMatchType(object, fieldName, int.class); + long offset = objectFieldOffset(object, fieldName); + putIntVolatile(object, offset, value); + } + + /** + * Sets the given long value to a field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param index the index + * @param value long value + * @throws IllegalArgumentException see {@link Assert#assertArrayType(Object)} + * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length + */ + public static void putLongVolatileIntoArray(Object object, String fieldName, int index, long value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, NullPointerException { + Object array = getFieldValue(true, object, fieldName); + assertArrayIndex(array, index); + long offset = longArrayIndexOffset(index); + putLongVolatile(array, offset, value); + } + + /** + * Sets the given long value to the fields of the specified object (sequential writing) + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param index the index + * @param value long value + * @throws IllegalArgumentException see {@link Assert#assertArrayType(Object)} + * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length + */ + public static void putOrderedLongIntoArray(Object object, String fieldName, int index, long value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, NullPointerException { + Object array = getFieldValue(true, object, fieldName); + assertArrayIndex(array, index); + long offset = longArrayIndexOffset(index); + putOrderedLong(array, offset, value); + } + + /** + * Sets the given int value to a field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param index the index + * @param value int value + * @throws IllegalArgumentException see {@link Assert#assertArrayType(Object)} + * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length + */ + public static void putIntVolatileIntoArray(Object object, String fieldName, int index, int value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, NullPointerException { + Object array = getFieldValue(true, object, fieldName); + assertArrayIndex(array, index); + long offset = intArrayIndexOffset(index); + putIntVolatile(array, offset, value); + } + + /** + * Sets the given int value to a field of the specified object (sequential writing) + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param index the index + * @param value int value + * @throws IllegalArgumentException see {@link Assert#assertArrayType(Object)} + * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length + */ + public static void putOrderedIntIntoArray(Object object, String fieldName, int index, int value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, NullPointerException { + Object array = getFieldValue(true, object, fieldName); + assertArrayIndex(array, index); + long offset = intArrayIndexOffset(index); + putOrderedInt(array, offset, value); + } + + /** + * Sets the given short value to a field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param index the index + * @param value short value + * @throws IllegalArgumentException see {@link Assert#assertArrayType(Object)} + * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length + */ + public static void putShortVolatileIntoArray(Object object, String fieldName, int index, short value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, NullPointerException { + Object array = getFieldValue(true, object, fieldName); + assertArrayIndex(array, index); + long offset = shortArrayIndexOffset(index); + putShortVolatile(array, offset, value); + } + + /** + * Sets the given byte value to a field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param index the index + * @param value byte value + * @throws IllegalArgumentException see {@link Assert#assertArrayType(Object)} + * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length + */ + public static void putByteVolatileIntoArray(Object object, String fieldName, int index, byte value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, NullPointerException { + Object array = getFieldValue(true, object, fieldName); + assertArrayIndex(array, index); + long offset = byteArrayIndexOffset(index); + putByteVolatile(array, offset, value); + } + + /** + * Sets the given boolean value to a field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param index the index + * @param value boolean value + * @throws IllegalArgumentException see {@link Assert#assertArrayType(Object)} + * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length + */ + public static void putBooleanVolatileIntoArray(Object object, String fieldName, int index, boolean value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, NullPointerException { + Object array = getFieldValue(true, object, fieldName); + assertArrayIndex(array, index); + long offset = booleanArrayIndexOffset(index); + putBooleanVolatile(array, offset, value); + } + + /** + * Sets the given double value to a field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param index the index + * @param value double value + * @throws IllegalArgumentException see {@link Assert#assertArrayType(Object)} + * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length + */ + public static void putDoubleVolatileIntoArray(Object object, String fieldName, int index, double value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, NullPointerException { + Object array = getFieldValue(true, object, fieldName); + assertArrayIndex(array, index); + long offset = doubleArrayIndexOffset(index); + putDoubleVolatile(array, offset, value); + } + + /** + * Sets the given float value to the field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param index the index + * @param value float value + * @throws IllegalArgumentException see {@link Assert#assertArrayType(Object)} + * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length + */ + public static void putFloatVolatileIntoArray(Object object, String fieldName, int index, float value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, NullPointerException { + Object array = getFieldValue(true, object, fieldName); + assertArrayIndex(array, index); + long offset = floatArrayIndexOffset(index); + putFloatVolatile(array, offset, value); + } + + /** + * Sets the given char value to a field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param index the index + * @param value char value + * @throws IllegalArgumentException see {@link Assert#assertArrayType(Object)} + * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length + */ + public static void putCharVolatileIntoArray(Object object, String fieldName, int index, char value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, NullPointerException { + Object array = getFieldValue(true, object, fieldName); + assertArrayIndex(array, index); + long offset = charArrayIndexOffset(index); + putCharVolatile(array, offset, value); + } + + /** + * Sets the given java.lang.Object value to a field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param index the index + * @param value java.lang.Object value + * @throws IllegalArgumentException see {@link Assert#assertArrayType(Object)} + * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length + */ + public static void putObjectVolatileIntoArray(Object object, String fieldName, int index, Object value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, NullPointerException { + Object array = getFieldValue(true, object, fieldName); + assertArrayIndex(array, index); + long offset = objectArrayIndexOffset(index); + putObjectVolatile(array, offset, value); + } + + /** + * Sets the given java.lang.Object value to the fields of the specified object (sequential writing) + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param index the index + * @param value java.lang.Object value + * @throws IllegalArgumentException see {@link Assert#assertArrayType(Object)} + * @throws ArrayIndexOutOfBoundsException If index is less than 0, or greater than or equal to the Array length + */ + public static void putOrderedObjectIntoArray(Object object, String fieldName, int index, Object value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException, NullPointerException { + Object array = getFieldValue(true, object, fieldName); + assertArrayIndex(array, index); + long offset = objectArrayIndexOffset(index); + putOrderedObject(array, offset, value); + } + + /** + * Get the Object value of the specified object field + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @return Object value + */ + public static Object getObject(Object object, String fieldName) { + long offset = objectFieldOffset(object, fieldName); + return getObject(object, offset); + } + + /** + * Get the long value of the specified object field + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @return long value + */ + public static long getLong(Object object, String fieldName) { + long offset = objectFieldOffset(object, fieldName); + return getLong(object, offset); + } + + /** + * Get the double value of the specified object field + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @return double value + */ + public static double getDouble(Object object, String fieldName) { + long offset = objectFieldOffset(object, fieldName); + return getDouble(object, offset); + } + + /** + * Get the float value of the specified object field + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @return float value + */ + public static float getFloat(Object object, String fieldName) { + long offset = objectFieldOffset(object, fieldName); + return getFloat(object, offset); + } + + /** + * Get the short value of the specified object field + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @return short value + */ + public static short getShort(Object object, String fieldName) { + long offset = objectFieldOffset(object, fieldName); + return getShort(object, offset); + } + + /** + * Get the byte value of the specified object field + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @return byte value + */ + public static byte getByte(Object object, String fieldName) { + long offset = objectFieldOffset(object, fieldName); + return getByte(object, offset); + } + + /** + * Get the boolean value of the specified object field + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @return boolean value + */ + public static boolean getBoolean(Object object, String fieldName) { + long offset = objectFieldOffset(object, fieldName); + return getBoolean(object, offset); + } + + /** + * Get the char value of the specified object field + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @return char value + */ + public static char getChar(Object object, String fieldName) { + long offset = objectFieldOffset(object, fieldName); + return getChar(object, offset); + } + + /** + * Get the int value of the specified object field + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @return int value + */ + public static int getInt(Object object, String fieldName) { + long offset = objectFieldOffset(object, fieldName); + return getInt(object, offset); + } + + /** + * Get the Object value of the specified object volatile field + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @return Object value + */ + public static Object getObjectVolatile(Object object, String fieldName) { + long offset = objectFieldOffset(object, fieldName); + return getObjectVolatile(object, offset); + } + + /** + * Get the long value of the volatile field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @return long value + */ + public static long getLongVolatile(Object object, String fieldName) { + long offset = objectFieldOffset(object, fieldName); + return getLongVolatile(object, offset); + } + + /** + * Get the double value of the volatile field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @return double value + */ + public static double getDoubleVolatile(Object object, String fieldName) { + long offset = objectFieldOffset(object, fieldName); + return getDoubleVolatile(object, offset); + } + + /** + * Get the float value of the volatile field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @return float value + */ + public static float getFloatVolatile(Object object, String fieldName) { + long offset = objectFieldOffset(object, fieldName); + return getFloatVolatile(object, offset); + } + + /** + * Get the short value of the volatile field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @return short value + */ + public static short getShortVolatile(Object object, String fieldName) { + long offset = objectFieldOffset(object, fieldName); + return getShortVolatile(object, offset); + } + + /** + * Get the byte value of the volatile field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @return byte value + */ + public static byte getByteVolatile(Object object, String fieldName) { + long offset = objectFieldOffset(object, fieldName); + return getByteVolatile(object, offset); + } + + /** + * Get the boolean value of the specified object field + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @return boolean value + */ + public static boolean getBooleanVolatile(Object object, String fieldName) { + long offset = objectFieldOffset(object, fieldName); + return getBooleanVolatile(object, offset); + } + + /** + * Get the char value of the volatile field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @return char value + */ + public static char getCharVolatile(Object object, String fieldName) { + long offset = objectFieldOffset(object, fieldName); + return getCharVolatile(object, offset); + } + + /** + * Get the int value of the volatile field of the specified object + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @return int value + */ + public static int getIntVolatile(Object object, String fieldName) { + long offset = objectFieldOffset(object, fieldName); + return getIntVolatile(object, offset); + } + + /** + * Atomically sets the field of the specified object to the given updated value + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param expected the expected value + * @param newValue the new value + * @return {@code true} if successful, {@code false} otherwise + */ + public static boolean compareAndSwapObject(Object object, String fieldName, Object expected, Object newValue) { + long offset = objectFieldOffset(object, fieldName); + return compareAndSwapObject(object, offset, expected, newValue); + } + + /** + * Atomically sets the field of the specified object to the given updated value + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param expected the expected value + * @param newValue the new value + * @return {@code true} if successful, {@code false} otherwise + */ + public static boolean compareAndSwapInt(Object object, String fieldName, int expected, int newValue) { + long offset = objectFieldOffset(object, fieldName); + return compareAndSwapInt(object, offset, expected, newValue); + } + + /** + * Atomically sets the field of the specified object to the given updated value + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @param expected the expected value + * @param newValue the new value + * @return {@code true} if successful, {@code false} otherwise + */ + public static boolean compareAndSwapLong(Object object, String fieldName, long expected, long newValue) { + long offset = objectFieldOffset(object, fieldName); + return compareAndSwapLong(object, offset, expected, newValue); + } + + public static int getAndAddInt(Object object, String fieldName, int delta) { + long offset = objectFieldOffset(object, fieldName); + return getAndAddInt(object, offset, delta); + } + + public static long getAndAddLong(Object object, String fieldName, long delta) { + long offset = objectFieldOffset(object, fieldName); + return getAndAddLong(object, offset, delta); + } + + public static int getAndSetInt(Object object, String fieldName, int newValue) { + long offset = objectFieldOffset(object, fieldName); + return getAndSetInt(object, offset, newValue); + } + + public static long getAndSetLong(Object object, String fieldName, long newValue) { + long offset = objectFieldOffset(object, fieldName); + return getAndSetLong(object, offset, newValue); + } + + public static Object getAndSetObject(Object object, String fieldName, Object newValue) { + long offset = objectFieldOffset(object, fieldName); + return getAndSetObject(object, offset, newValue); + } + + //| peek and poke operations + //| (compilers should optimize these to memory ops) + + // These work on object fields in the Java heap. + // They will not work on elements of packed arrays. + + /** + * Fetches a value from a given Java variable. + * More specifically, fetches a field or array element within the given + * object {@code o} at the given offset, or (if {@code o} is null) + * from the memory address whose numerical value is the given offset. + *

+ * The results are undefined unless one of the following cases is true: + *

    + *
  • The offset was obtained from {@link #objectFieldOffset} on + * the {@link java.lang.reflect.Field} of some Java field and the object + * referred to by {@code o} is of a class compatible with that + * field's class. + * + *
  • The offset and object reference {@code o} (either null or + * non-null) were both obtained via {@link #staticFieldOffset} + * and {@link #staticFieldBase} (respectively) from the + * reflective {@link Field} representation of some Java field. + * + *
  • The object referred to by {@code o} is an array, and the offset + * is an integer of the form {@code B+N*S}, where {@code N} is + * a valid index into the array, and {@code B} and {@code S} are + * the values obtained by {@link #arrayBaseOffset} and {@link + * #arrayIndexScale} (respectively) from the array's class. The value + * referred to is the {@code N}th element of the array. + * + *
+ *

+ * If one of the above cases is true, the call references a specific Java + * variable (field or array element). However, the results are undefined + * if that variable is not in fact of the type returned by this method. + *

+ * This method refers to a variable by means of two parameters, and so + * it provides (in effect) a double-register addressing mode + * for Java variables. When the object reference is null, this method + * uses its offset as an absolute address. This is similar in operation + * to methods such as {@link #getInt(long)}, which provide (in effect) a + * single-register addressing mode for non-Java variables. + * However, because Java variables may have a different layout in memory + * from non-Java variables, programmers should not assume that these + * two addressing modes are ever equivalent. Also, programmers should + * remember that offsets from the double-register addressing mode cannot + * be portably confused with longs used in the single-register addressing + * mode. + * + * @param o Java heap object in which the variable resides, if any, else + * null + * @param offset indication of where the variable resides in a Java heap + * object, if any, else a memory address locating the variable + * statically + * @return the value fetched from the indicated Java variable + * @throws RuntimeException No defined exceptions are thrown, not even + * {@link NullPointerException} + * @deprecated Use {@link java.lang.invoke.VarHandle#get(Object...)} or + * {@link java.lang.foreign.java.lang.foreign.MemorySegment#get(java.lang.foreign.java.lang.foreign.ValueLayout.OfInt, long)} instead. + */ + public static int getInt(Object o, long offset) { + return invokeMethod(unsafe, getIntMethod, o, offset); + } + + /** + * Stores a value into a given Java variable. + *

+ * The first two parameters are interpreted exactly as with + * {@link #getInt(Object, long)} to refer to a specific + * Java variable (field or array element). The given value + * is stored into that variable. + *

+ * The variable must be of the same type as the method + * parameter {@code x}. + * + * @param o Java heap object in which the variable resides, if any, else + * null + * @param offset indication of where the variable resides in a Java heap + * object, if any, else a memory address locating the variable + * statically + * @param x the value to store into the indicated Java variable + * @throws RuntimeException No defined exceptions are thrown, not even + * {@link NullPointerException} + * @deprecated Use {@link java.lang.invoke.VarHandle#set(Object...)} or + * {@link java.lang.foreign.java.lang.foreign.MemorySegment#set(java.lang.foreign.java.lang.foreign.ValueLayout.OfInt, long, int)} instead. + */ + public static void putInt(Object o, long offset, int x) { + invokeMethod(unsafe, putIntMethod, o, offset, x); + } + + /** + * Fetches a reference value from a given Java variable. + * + * @deprecated Use {@link java.lang.invoke.VarHandle#get(Object...)} instead. + */ + public static Object getObject(Object o, long offset) { + return invokeMethod(unsafe, getObjectMethod, o, offset); + } + + /** + * Stores a reference value into a given Java variable. + *

+ * Unless the reference {@code x} being stored is either null + * or matches the field type, the results are undefined. + * If the reference {@code o} is non-null, card marks or + * other store barriers for that object (if the VM requires them) + * are updated. + * + * @deprecated Use {@link java.lang.invoke.VarHandle#set(Object...)} instead. + */ + public static void putObject(Object o, long offset, Object x) { + invokeMethod(unsafe, putObjectMethod, o, offset, x); + } + + /** + * @see #getInt(Object, long) + * @deprecated Use {@link java.lang.invoke.VarHandle#get(Object...)} or + * {@link java.lang.foreign.java.lang.foreign.MemorySegment#get(java.lang.foreign.java.lang.foreign.ValueLayout.OfBoolean, long)} instead. + */ + public static boolean getBoolean(Object o, long offset) { + return invokeMethod(unsafe, getBooleanMethod, o, offset); + } + + /** + * @see #putInt(Object, long, int) + * @deprecated Use {@link java.lang.invoke.VarHandle#set(Object...)} or + * {@link java.lang.foreign.java.lang.foreign.MemorySegment#set(java.lang.foreign.java.lang.foreign.ValueLayout.OfBoolean, long, boolean)} instead. + */ + public static void putBoolean(Object o, long offset, boolean x) { + invokeMethod(unsafe, putBooleanMethod, o, offset, x); + } + + /** + * @see #getInt(Object, long) + * @deprecated Use {@link java.lang.invoke.VarHandle#get(Object...)} or + * {@link java.lang.foreign.MemorySegment#get(java.lang.foreign.ValueLayout.OfByte, long)} instead. + */ + public static byte getByte(Object o, long offset) { + return invokeMethod(unsafe, getByteMethod, o, offset); + } + + /** + * @see #putInt(Object, long, int) + * @deprecated Use {@link java.lang.invoke.VarHandle#set(Object...)} or + * {@link java.lang.foreign.MemorySegment#set(java.lang.foreign.ValueLayout.OfByte, long, byte)} instead. + */ + public static void putByte(Object o, long offset, byte x) { + invokeMethod(unsafe, putByteMethod, o, offset, x); + } + + /** + * @see #getInt(Object, long) + * @deprecated Use {@link java.lang.invoke.VarHandle#get(Object...)} or + * {@link java.lang.foreign.MemorySegment#get(java.lang.foreign.ValueLayout.OfShort, long)} instead. + */ + public static short getShort(Object o, long offset) { + return invokeMethod(unsafe, getShortMethod, o, offset); + } + + /** + * @see #putInt(Object, long, int) + * @deprecated Use {@link java.lang.invoke.VarHandle#set(Object...)} or + * {@link java.lang.foreign.MemorySegment#set(java.lang.foreign.ValueLayout.OfShort, long, short)} instead. + */ + public static void putShort(Object o, long offset, short x) { + invokeMethod(unsafe, putShortMethod, o, offset, x); + } + + /** + * @see #getInt(Object, long) + * @deprecated Use {@link java.lang.invoke.VarHandle#get(Object...)} or + * {@link java.lang.foreign.MemorySegment#get(java.lang.foreign.ValueLayout.OfChar, long)} instead. + */ + public static char getChar(Object o, long offset) { + return invokeMethod(unsafe, getCharMethod, o, offset); + } + + /** + * @see #putInt(Object, long, int) + * @deprecated Use {@link java.lang.invoke.VarHandle#set(Object...)} or + * {@link java.lang.foreign.MemorySegment#set(java.lang.foreign.ValueLayout.OfChar, long, char)} instead. + */ + public static void putChar(Object o, long offset, char x) { + invokeMethod(unsafe, putCharMethod, o, offset, x); + } + + /** + * @see #getInt(Object, long) + * @deprecated Use {@link java.lang.invoke.VarHandle#get(Object...)} or + * {@link java.lang.foreign.MemorySegment#get(java.lang.foreign.ValueLayout.OfLong, long)} instead. + */ + public static long getLong(Object o, long offset) { + return invokeMethod(unsafe, getLongMethod, o, offset); + } + + /** + * @see #putInt(Object, long, int) + * @deprecated Use {@link java.lang.invoke.VarHandle#set(Object...)} or + * {@link java.lang.foreign.MemorySegment#set(java.lang.foreign.ValueLayout.OfLong, long, long)} instead. + */ + public static void putLong(Object o, long offset, long x) { + invokeMethod(unsafe, putLongMethod, o, offset, x); + } + + /** + * @see #getInt(Object, long) + * @deprecated Use {@link java.lang.invoke.VarHandle#get(Object...)} or + * {@link java.lang.foreign.MemorySegment#get(java.lang.foreign.ValueLayout.OfFloat, long)} instead. + */ + public static float getFloat(Object o, long offset) { + return invokeMethod(unsafe, getFloatMethod, o, offset); + } + + /** + * @see #putInt(Object, long, int) + * @deprecated Use {@link java.lang.invoke.VarHandle#set(Object...)} or + * {@link java.lang.foreign.MemorySegment#set(java.lang.foreign.ValueLayout.OfFloat, long, float)} instead. + */ + public static void putFloat(Object o, long offset, float x) { + invokeMethod(unsafe, putFloatMethod, o, offset, x); + } + + /** + * @see #getInt(Object, long) + * @deprecated Use {@link java.lang.invoke.VarHandle#get(Object...)} or + * {@link java.lang.foreign.MemorySegment#get(java.lang.foreign.ValueLayout.OfDouble, long)} instead. + */ + public static double getDouble(Object o, long offset) { + return invokeMethod(unsafe, getDoubleMethod, o, offset); + } + + /** + * @see #putInt(Object, long, int) + * @deprecated Use {@link java.lang.invoke.VarHandle#set(Object...)} or + * {@link java.lang.foreign.MemorySegment#set(java.lang.foreign.ValueLayout.OfDouble, long, double)} instead. + */ + public static void putDouble(Object o, long offset, double x) { + invokeMethod(unsafe, putDoubleMethod, o, offset, x); + } + + // These work on values in the C heap. + + /** + * Fetches a value from a given memory address. If the address is zero, or + * does not point into a block obtained from {@link #allocateMemory}, the + * results are undefined. + * + * @see #allocateMemory + * @see {@link sun.misc.Unsafe#getByte(Object, long)} + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + */ + public static byte getByte(long address) { + return invokeMethod(unsafe, getByteFromAddressMethod, address); + } + + /** + * Stores a value into a given memory address. If the address is zero, or + * does not point into a block obtained from {@link #allocateMemory}, the + * results are undefined. + * + * @see #getByte(long) + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + */ + public static void putByte(long address, byte x) { + invokeMethod(unsafe, putByteToAddressMethod, address, x); + } + + /** + * @see #getByte(long) + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + */ + public static short getShort(long address) { + return invokeMethod(unsafe, getShortFromAddressMethod, address); + } + + /** + * @see #putByte(long, byte) + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + */ + public static void putShort(long address, short x) { + invokeMethod(unsafe, putShortToAddressMethod, address, x); + } + + /** + * @see #getByte(long) + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + */ + public static char getChar(long address) { + return invokeMethod(unsafe, getCharFromAddressMethod, address); + } + + /** + * @see #putByte(long, byte) + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + */ + public static void putChar(long address, char x) { + invokeMethod(unsafe, putCharToAddressMethod, address, x); + } + + /** + * @see #getByte(long) + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + */ + public static int getInt(long address) { + return invokeMethod(unsafe, getIntFromAddressMethod, address); + } + + /** + * @see #putByte(long, byte) + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + */ + public static void putInt(long address, int x) { + invokeMethod(unsafe, putIntToAddressMethod, address, x); + } + + /** + * @see #getByte(long) + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + */ + public static long getLong(long address) { + return invokeMethod(unsafe, getLongFromAddressMethod, address); + } + + /** + * @see #putByte(long, byte) + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + */ + public static void putLong(long address, long x) { + invokeMethod(unsafe, putLongToAddressMethod, address, x); + } + + /** + * @see #getByte(long) + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + */ + public static float getFloat(long address) { + return invokeMethod(unsafe, getFloatFromAddressMethod, address); + } + + /** + * @see #putByte(long, byte) + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + */ + public static void putFloat(long address, float x) { + invokeMethod(unsafe, putFloatToAddressMethod, address, x); + } + + /** + * @see #getByte(long) + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + */ + public static double getDouble(long address) { + return invokeMethod(unsafe, getDoubleFromAddressMethod, address); + } + + /** + * @see #putByte(long, byte) + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + */ + public static void putDouble(long address, double x) { + invokeMethod(unsafe, putDoubleToAddressMethod, address, x); + } + + /** + * Fetches a native pointer from a given memory address. If the address is + * zero, or does not point into a block obtained from {@link + * #allocateMemory}, the results are undefined. + * + *

If the native pointer is less than 64 bits wide, it is extended as + * an unsigned number to a Java long. The pointer may be indexed by any + * given byte offset, simply by adding that offset (as a simple integer) to + * the long representing the pointer. The number of bytes actually read + * from the target address may be determined by consulting {@link + * #addressSize}. + * + * @see #allocateMemory + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + */ + public static long getAddress(long address) { + return invokeMethod(unsafe, getAddressMethod, address); + } + + /** + * Stores a native pointer into a given memory address. If the address is + * zero, or does not point into a block obtained from {@link + * #allocateMemory}, the results are undefined. + * + *

The number of bytes actually written at the target address may be + * determined by consulting {@link #addressSize}. + * + * @see #getAddress(long) + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + */ + public static void putAddress(long address, long x) { + invokeMethod(unsafe, putAddressMethod, address, x); + } + + //| wrappers for malloc, realloc, free: + + /** + * Allocates a new block of native memory, of the given size in bytes. The + * contents of the memory are uninitialized; they will generally be + * garbage. The resulting native pointer will be zero if and only if the + * requested size is zero. The resulting native pointer will be aligned for + * all value types. Dispose of this memory by calling {@link #freeMemory} + * or resize it with {@link #reallocateMemory}. + * + * Note: It is the responsibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @throws RuntimeException if the size is negative or too large + * for the native size_t type + * @throws OutOfMemoryError if the allocation is refused by the system + * @see #getByte(long) + * @see #putByte(long, byte) + * @deprecated Use {@link java.lang.foreign} to allocate off-heap memory. + */ + public static long allocateMemory(long bytes) { + return invokeMethod(unsafe, allocateMemoryMethod, bytes); + } + + /** + * Resizes a new block of native memory, to the given size in bytes. The + * contents of the new block past the size of the old block are + * uninitialized; they will generally be garbage. The resulting native + * pointer will be zero if and only if the requested size is zero. The + * resulting native pointer will be aligned for all value types. Dispose + * of this memory by calling {@link #freeMemory}, or resize it with {@link + * #reallocateMemory}. The address passed to this method may be null, in + * which case an allocation will be performed. + * + * Note: It is the responsibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @throws RuntimeException if the size is negative or too large + * for the native size_t type + * @throws OutOfMemoryError if the allocation is refused by the system + * @see #allocateMemory + * @deprecated Use {@link java.lang.foreign} to allocate off-heap memory. + */ + public static long reallocateMemory(long address, long bytes) { + return invokeMethod(unsafe, reallocateMemoryMethod, address, bytes); + } + + /** + * Sets all bytes in a given block of memory to a fixed value + * (usually zero). + * + *

This method determines a block's base address by means of two parameters, + * and so it provides (in effect) a double-register addressing mode, + * as discussed in {@link #getInt(Object, long)}. When the object reference is null, + * the offset supplies an absolute base address. + * + *

The stores are in coherent (atomic) units of a size determined + * by the address and length parameters. If the effective address and + * length are all even modulo 8, the stores take place in 'long' units. + * If the effective address and length are (resp.) even modulo 4 or 2, + * the stores take place in units of 'int' or 'short'. + * + * Note: It is the responsibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @throws RuntimeException if any of the arguments is invalid + * @since JDK 1.7 + * @deprecated {@link java.lang.foreign.java.lang.foreign.MemorySegment#fill(byte)} fills the contents of a memory + * segment with a given value. + */ + public static void setMemory(Object o, long offset, long bytes, byte value) { + invokeMethod(unsafe, setMemoryMethod, o, offset, bytes, value); + } + + /** + * Sets all bytes in a given block of memory to a fixed value + * (usually zero). This provides a single-register addressing mode, + * as discussed in {@link #getInt(Object, long)}. + * + *

Equivalent to {@code setMemory(null, address, bytes, value)}. + * + * @deprecated {@link java.lang.foreign.java.lang.foreign.MemorySegment#fill(byte)} fills the contents of a memory + * segment with a given value. + *

+ * Use {@link java.lang.foreign.java.lang.foreign.MemorySegment} and its bulk copy methods instead. + */ + public static void setMemory(long address, long bytes, byte value) { + invokeMethod(unsafe, setMemoryToAddressMethod, address, bytes, value); + } + + /** + * Sets all bytes in a given block of memory to a copy of another + * block. + * + *

This method determines each block's base address by means of two parameters, + * and so it provides (in effect) a double-register addressing mode, + * as discussed in {@link #getInt(Object, long)}. When the object reference is null, + * the offset supplies an absolute base address. + * + *

The transfers are in coherent (atomic) units of a size determined + * by the address and length parameters. If the effective addresses and + * length are all even modulo 8, the transfer takes place in 'long' units. + * If the effective addresses and length are (resp.) even modulo 4 or 2, + * the transfer takes place in units of 'int' or 'short'. + * + * Note: It is the responsibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @throws RuntimeException if any of the arguments is invalid + * @since JDK 1.7 + * @deprecated Use {@link java.lang.foreign.java.lang.foreign.MemorySegment} and its bulk copy methods instead. + */ + public static void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes) { + invokeMethod(unsafe, copyMemoryMethod, srcBase, srcOffset, destBase, destOffset, bytes); + } + + /** + * Sets all bytes in a given block of memory to a copy of another + * block. This provides a single-register addressing mode, + * as discussed in {@link #getInt(Object, long)}. + *

+ * Equivalent to {@code copyMemory(null, srcAddress, null, destAddress, bytes)}. + * + * @deprecated Use {@link java.lang.foreign.java.lang.foreign.MemorySegment} and its bulk copy methods instead. + */ + public static void copyMemory(long srcAddress, long destAddress, long bytes) { + invokeMethod(unsafe, copyMemoryFromAddressMethod, srcAddress, destAddress, bytes); + } + + /** + * Disposes of a block of native memory, as obtained from {@link + * #allocateMemory} or {@link #reallocateMemory}. The address passed to + * this method may be null, in which case no action is taken. + * + * Note: It is the responsibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @throws RuntimeException if any of the arguments is invalid + * @see #allocateMemory + * @deprecated Use {@link java.lang.foreign} to allocate and free off-heap memory. + */ + public static void freeMemory(long address) { + invokeMethod(unsafe, freeMemoryMethod, address); + } + + /** + * get the offset of the object field + * + * @param object the target object + * @param fieldName the name of {@link Field} + * @return offset + * @throws IllegalArgumentException If the class is null, or the field name is blank or empty or is matched at multiple places in the inheritance hierarchy + * @throws NullPointerException If any argument is null + */ + public static long objectFieldOffset(Object object, String fieldName) throws IllegalArgumentException, NullPointerException { + Class type = object.getClass(); + Long offsetFromCache = getOffsetFromCache(type, fieldName); + if (offsetFromCache != null) { + return offsetFromCache; + } + Field field = findField(type, fieldName); + long offset = invokeMethod(unsafe, objectFieldOffsetMethod, field); + putOffsetFromCache(type, fieldName, offset); + return offset; + } + + /** + * Get the offset of a class static field + * + * @param type the target type + * @param fieldName the name of {@link Field} + * @return offset + */ + public static long staticFieldOffset(Class type, String fieldName) { + Field field = findField(type, fieldName); + return invokeMethod(unsafe, staticFieldOffsetMethod, field); + } + + /** + * Get the base of a class static field + * + * @param type the target type + * @param fieldName the name of {@link Field} + * @return base + */ + public static Object staticFieldBase(Class type, String fieldName) { + Field field = findField(type, fieldName); + return staticFieldBase(field); + } + + /** + * Reports the location of a given static field, in conjunction with {@link + * #staticFieldOffset}. + *

Fetch the base "Object", if any, with which static fields of the + * given class can be accessed via methods like {@link #getInt(Object, + * long)}. This value may be null. This value may refer to an object + * which is a "cookie", not guaranteed to be a real Object, and it should + * not be used in any way except as argument to the get and put routines in + * this class. + * + * @param field + * @return + * @deprecated The guarantee that a field will always have the same offset + * and base may not be true in a future release. The ability to provide an + * offset and object reference to a heap memory accessor will be removed + * in a future release. Use {@link java.lang.invoke.VarHandle} instead. + */ + public static Object staticFieldBase(Field field) { + return invokeMethod(unsafe, staticFieldBaseMethod, field); + } + + /** + * Reports the offset of the first element in the storage allocation of a + * given array class. If {@link #arrayIndexScale} returns a non-zero value + * for the same class, you may use that scale factor, together with this + * base offset, to form new offsets to access elements of arrays of the + * given class. + * + * @see #getInt(Object, long) + * @see #putInt(Object, long, int) + * @deprecated Not needed when using {@link java.lang.invoke.VarHandle} or {@link java.lang.foreign}. + */ + public static int arrayBaseOffset(Class arrayClass) { + return invokeMethod(unsafe, arrayBaseOffsetMethod, arrayClass); + } + + /** + * Reports the scale factor for addressing elements in the storage + * allocation of a given array class. However, arrays of "narrow" types + * will generally not work properly with accessors like {@link + * #getByte(Object, long)}, so the scale factor for such classes is reported + * as zero. + * + * @see #arrayBaseOffset + * @see #getInt(Object, long) + * @see #putInt(Object, long, int) + * @deprecated Not needed when using {@link java.lang.invoke.VarHandle} or {@link java.lang.foreign}. + */ + public static int arrayIndexScale(Class arrayClass) { + return invokeMethod(unsafe, arrayIndexScaleMethod, arrayClass); + } + + /** + * Reports the size in bytes of a native pointer, as stored via {@link + * #putAddress}. This value will be either 4 or 8. Note that the sizes of + * other primitive types (as stored in native memory blocks) is determined + * fully by their information content. + * + * @deprecated Use {@link java.lang.foreign.java.lang.foreign.ValueLayout#ADDRESS}.{@link java.lang.foreign.MemoryLayout#byteSize()} instead. + */ + public static int addressSize() { + return invokeMethod(unsafe, addressSizeMethod); + } + + /** + * Reports the size in bytes of a native memory page (whatever that is). + * This value will always be a power of two. + */ + public static int pageSize() { + return invokeMethod(unsafe, pageSizeMethod); + } + + //| random trusted operations from JNI: + + /** + * Allocates an instance but does not run any constructor. + * Initializes the class if it has not yet been. + */ + public static Object allocateInstance(Class cls) { + return invokeMethod(unsafe, allocateInstanceMethod, cls); + } + + /** + * Throws the exception without telling the verifier. + */ + public static void throwException(Throwable ee) { + invokeMethod(unsafe, throwExceptionMethod, ee); + } + + /** + * Atomically updates Java variable to {@code x} if it is currently + * holding {@code expected}. + * + *

This operation has memory semantics of a {@code volatile} read + * and write. Corresponds to C11 atomic_compare_exchange_strong. + * + * @return {@code true} if successful + * @deprecated Use {@link java.lang.invoke.VarHandle#compareAndExchange(Object...)} instead. + */ + public static boolean compareAndSwapObject(Object o, long offset, Object expected, Object x) { + return invokeMethod(unsafe, compareAndSwapObjectMethod, o, offset, expected, x); + } + + /** + * Atomically updates Java variable to {@code x} if it is currently + * holding {@code expected}. + * + *

This operation has memory semantics of a {@code volatile} read + * and write. Corresponds to C11 atomic_compare_exchange_strong. + * + * @return {@code true} if successful + * @deprecated Use {@link java.lang.invoke.VarHandle#compareAndExchange(Object...)} instead. + */ + public static boolean compareAndSwapInt(Object o, long offset, int expected, int x) { + return invokeMethod(unsafe, compareAndSwapIntMethod, o, offset, expected, x); + } + + /** + * Atomically updates Java variable to {@code x} if it is currently + * holding {@code expected}. + * + *

This operation has memory semantics of a {@code volatile} read + * and write. Corresponds to C11 atomic_compare_exchange_strong. + * + * @return {@code true} if successful + * @deprecated Use {@link java.lang.invoke.VarHandle#compareAndExchange(Object...)} instead. + */ + public static boolean compareAndSwapLong(Object o, long offset, long expected, long x) { + return invokeMethod(unsafe, compareAndSwapLongMethod, o, offset, expected, x); + } + + /** + * Fetches a reference value from a given Java variable, with volatile + * load semantics. Otherwise identical to {@link #getObject(Object, long)} + * + * @deprecated Use {@link java.lang.invoke.VarHandle#getVolatile(Object...)} instead. + */ + public static Object getObjectVolatile(Object o, long offset) { + return invokeMethod(unsafe, getObjectVolatileMethod, o, offset); + } + + /** + * Stores a reference value into a given Java variable, with + * volatile store semantics. Otherwise identical to {@link #putObject(Object, long, Object)} + * + * @deprecated Use {@link java.lang.invoke.VarHandle#setVolatile(Object...)} instead. + */ + public static void putObjectVolatile(Object o, long offset, Object x) { + invokeMethod(unsafe, putObjectVolatileMethod, o, offset, x); + } + + /** + * Volatile version of {@link #getInt(Object, long)}. + * + * @deprecated Use {@link java.lang.invoke.VarHandle#getVolatile(Object...)} instead. + */ + public static int getIntVolatile(Object o, long offset) { + return invokeMethod(unsafe, getIntVolatileMethod, o, offset); + } + + /** + * Volatile version of {@link #putInt(Object, long, int)}. + * + * @deprecated Use {@link java.lang.invoke.VarHandle#setVolatile(Object...)} instead. + */ + public static void putIntVolatile(Object o, long offset, int x) { + invokeMethod(unsafe, putIntVolatileMethod, o, offset, x); + } + + /** + * Volatile version of {@link #getBoolean(Object, long)}. + * + * @deprecated Use {@link java.lang.invoke.VarHandle#getVolatile(Object...)} instead. + */ + public static boolean getBooleanVolatile(Object o, long offset) { + return invokeMethod(unsafe, getBooleanVolatileMethod, o, offset); + } + + /** + * Volatile version of {@link #putBoolean(Object, long, boolean)}. + * + * @deprecated Use {@link java.lang.invoke.VarHandle#setVolatile(Object...)} instead. + */ + public static void putBooleanVolatile(Object o, long offset, boolean x) { + invokeMethod(unsafe, putBooleanVolatileMethod, o, offset, x); + } + + /** + * Volatile version of {@link #getByte(Object, long)}. + * + * @deprecated Use {@link java.lang.invoke.VarHandle#getVolatile(Object...)} + * instead. + */ + public static byte getByteVolatile(Object o, long offset) { + return invokeMethod(unsafe, getByteVolatileMethod, o, offset); + } + + /** + * Volatile version of {@link #putByte(Object, long, byte)}. + * + * @deprecated Use {@link java.lang.invoke.VarHandle#setVolatile(Object...)} instead. + */ + public static void putByteVolatile(Object o, long offset, byte x) { + invokeMethod(unsafe, putByteVolatileMethod, o, offset, x); + } + + /** + * Volatile version of {@link #getShort(Object, long)}. + * + * @deprecated Use {@link java.lang.invoke.VarHandle#getVolatile(Object...)} instead. + */ + public static short getShortVolatile(Object o, long offset) { + return invokeMethod(unsafe, getShortVolatileMethod, o, offset); + } + + /** + * Volatile version of {@link #putShort(Object, long, short)}. + * + * @deprecated Use {@link java.lang.invoke.VarHandle#setVolatile(Object...)} instead. + */ + public static void putShortVolatile(Object o, long offset, short x) { + invokeMethod(unsafe, putShortVolatileMethod, o, offset, x); + } + + /** + * Volatile version of {@link #getChar(Object, long)}. + * + * @deprecated Use {@link java.lang.invoke.VarHandle#getVolatile(Object...)} instead. + */ + public static char getCharVolatile(Object o, long offset) { + return invokeMethod(unsafe, getCharVolatileMethod, o, offset); + } + + /** + * Volatile version of {@link #putChar(Object, long, char)}. + * + * @deprecated Use {@link java.lang.invoke.VarHandle#setVolatile(Object...)} instead. + */ + public static void putCharVolatile(Object o, long offset, char x) { + invokeMethod(unsafe, putCharVolatileMethod, o, offset, x); + } + + /** + * Volatile version of {@link #getLong(Object, long)}. + * + * @deprecated Use {@link java.lang.invoke.VarHandle#getVolatile(Object...)} instead. + */ + public static long getLongVolatile(Object o, long offset) { + return invokeMethod(unsafe, getLongVolatileMethod, o, offset); + } + + /** + * Volatile version of {@link #putLong(Object, long, long)}. + * + * @deprecated Use {@link java.lang.invoke.VarHandle#setVolatile(Object...)} instead. + */ + public static void putLongVolatile(Object o, long offset, long x) { + invokeMethod(unsafe, putLongVolatileMethod, o, offset, x); + } + + /** + * Volatile version of {@link #getFloat(Object, long)}. + * + * @deprecated Use {@link java.lang.invoke.VarHandle#getVolatile(Object...)} instead. + */ + public static float getFloatVolatile(Object o, long offset) { + return invokeMethod(unsafe, getFloatVolatileMethod, o, offset); + } + + /** + * Volatile version of {@link #putFloat(Object, long, float)}. + * + * @deprecated Use {@link java.lang.invoke.VarHandle#setVolatile(Object...)} instead. + */ + public static void putFloatVolatile(Object o, long offset, float x) { + invokeMethod(unsafe, putFloatVolatileMethod, o, offset, x); + } + + /** + * Volatile version of {@link #getDouble(Object, long)}. + * + * @deprecated Use {@link java.lang.invoke.VarHandle#getVolatile(Object...)} instead. + */ + public static double getDoubleVolatile(Object o, long offset) { + return invokeMethod(unsafe, getDoubleVolatileMethod, o, offset); + } + + /** + * Volatile version of {@link #putDouble(Object, long, double)}. + * + * @deprecated Use {@link java.lang.invoke.VarHandle#setVolatile(Object...)} instead. + */ + public static void putDoubleVolatile(Object o, long offset, double x) { + invokeMethod(unsafe, putDoubleVolatileMethod, o, offset, x); + } + + /** + * Version of {@link #putObjectVolatile(Object, long, Object)} + * that does not guarantee immediate visibility of the store to + * other threads. This method is generally only useful if the + * underlying field is a Java volatile (or if an array cell, one + * that is otherwise only accessed using volatile accesses). + *

+ * Corresponds to C11 atomic_store_explicit(..., memory_order_release). + * + * @deprecated Use {@link java.lang.invoke.VarHandle#setRelease(Object...)} instead. + */ + public static void putOrderedObject(Object o, long offset, Object x) { + invokeMethod(unsafe, putOrderedObjectMethod, o, offset, x); + } + + /** + * Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)}. + * + * @deprecated Use {@link java.lang.invoke.VarHandle#setRelease(Object...)} instead. + */ + public static void putOrderedInt(Object o, long offset, int x) { + invokeMethod(unsafe, putOrderedIntMethod, o, offset, x); + } + + /** + * Ordered/Lazy version of {@link #putLongVolatile(Object, long, long)}. + * + * @deprecated Use {@link java.lang.invoke.VarHandle#setRelease(Object...)} instead. + */ + public static void putOrderedLong(Object o, long offset, long x) { + invokeMethod(unsafe, putOrderedLongMethod, o, offset, x); + } + + // The following contain CAS-based Java implementations used on + // platforms not supporting native instructions + + /** + * Atomically adds the given value to the current value of a field + * or array element within the given object {@code o} + * at the given {@code offset}. + * + * @param o object/array to update the field/element in + * @param offset field/element offset + * @param delta the value to add + * @return the previous value + * @since JDK 1.8 + * @deprecated Use {@link java.lang.invoke.VarHandle#getAndAdd(Object...)} instead. + */ + public static int getAndAddInt(Object o, long offset, int delta) { + return invokeMethod(unsafe, getAndAddIntMethod, o, offset, delta); + } + + /** + * Atomically adds the given value to the current value of a field + * or array element within the given object {@code o} + * at the given {@code offset}. + * + * @param o object/array to update the field/element in + * @param offset field/element offset + * @param delta the value to add + * @return the previous value + * @since JDK 1.8 + * @deprecated Use {@link java.lang.invoke.VarHandle#getAndAdd(Object...)} instead. + */ + public static long getAndAddLong(Object o, long offset, long delta) { + return invokeMethod(unsafe, getAndAddLongMethod, o, offset, delta); + } + + /** + * Atomically exchanges the given value with the current value of + * a field or array element within the given object {@code o} + * at the given {@code offset}. + * + * @param o object/array to update the field/element in + * @param offset field/element offset + * @param newValue new value + * @return the previous value + * @since JDK 1.8 + * @deprecated Use {@link java.lang.invoke.VarHandle#getAndAdd(Object...)} instead. + */ + public static int getAndSetInt(Object o, long offset, int newValue) { + return invokeMethod(unsafe, getAndSetIntMethod, o, offset, newValue); + } + + /** + * Atomically exchanges the given value with the current value of + * a field or array element within the given object {@code o} + * at the given {@code offset}. + * + * @param o object/array to update the field/element in + * @param offset field/element offset + * @param newValue new value + * @return the previous value + * @since JDK 1.8 + * @deprecated Use {@link java.lang.invoke.VarHandle#getAndAdd(Object...)} instead. + */ + public static long getAndSetLong(Object o, long offset, long newValue) { + return invokeMethod(unsafe, getAndSetLongMethod, o, offset, newValue); + } + + /** + * Atomically exchanges the given reference value with the current + * reference value of a field or array element within the given + * object {@code o} at the given {@code offset}. + * + * @param o object/array to update the field/element in + * @param offset field/element offset + * @param newValue new value + * @return the previous value + * @since JDK 1.8 + * @deprecated Use {@link java.lang.invoke.VarHandle#getAndAdd(Object...)} instead. + */ + public static Object getAndSetObject(Object o, long offset, Object newValue) { + return invokeMethod(unsafe, getAndSetObjectMethod, o, offset, newValue); + } + + /** + * Ensures that loads before the fence will not be reordered with loads and + * stores after the fence; a "LoadLoad plus LoadStore barrier". + *

+ * Corresponds to C11 atomic_thread_fence(memory_order_acquire) + * (an "acquire fence"). + *

+ * A pure LoadLoad fence is not provided, since the addition of LoadStore + * is almost always desired, and most current hardware instructions that + * provide a LoadLoad barrier also provide a LoadStore barrier for free. + * + * @since JDK 1.8 + * @deprecated Use {@link java.lang.invoke.VarHandle#acquireFence()} instead. + */ + public static void loadFence() { + invokeMethod(unsafe, loadFenceMethod); + } + + /** + * Ensures that loads and stores before the fence will not be reordered with + * stores after the fence; a "StoreStore plus LoadStore barrier". + *

+ * Corresponds to C11 atomic_thread_fence(memory_order_release) + * (a "release fence"). + *

+ * A pure StoreStore fence is not provided, since the addition of LoadStore + * is almost always desired, and most current hardware instructions that + * provide a StoreStore barrier also provide a LoadStore barrier for free. + * + * @since JDK 1.8 + * @deprecated Use {@link java.lang.invoke.VarHandle#releaseFence()} instead. + */ + public static void storeFence() { + invokeMethod(unsafe, storeFenceMethod); + } + + /** + * Ensures that loads and stores before the fence will not be reordered + * with loads and stores after the fence. Implies the effects of both + * loadFence() and storeFence(), and in addition, the effect of a StoreLoad + * barrier. + *

+ * Corresponds to C11 atomic_thread_fence(memory_order_seq_cst). + * + * @since JDK 1.8 + * @deprecated Use {@link java.lang.invoke.VarHandle#fullFence()} instead. + */ + public static void fullFence() { + invokeMethod(unsafe, fullFenceMethod); + } + + private UnsafeUtils() { + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/main/java/io/microsphere/net/StandardURLStreamHandlerFactory.java b/microsphere-java-core/src/main/java/io/microsphere/net/StandardURLStreamHandlerFactory.java index dbcccacb2..9f36ce8ce 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/net/StandardURLStreamHandlerFactory.java +++ b/microsphere-java-core/src/main/java/io/microsphere/net/StandardURLStreamHandlerFactory.java @@ -26,10 +26,10 @@ import static io.microsphere.constants.SymbolConstants.DOT; import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.net.URLUtils.DEFAULT_HANDLER_PACKAGE_PREFIX; +import static io.microsphere.reflect.ConstructorUtils.newInstance; import static io.microsphere.reflect.FieldUtils.findField; import static io.microsphere.reflect.FieldUtils.getStaticFieldValue; import static io.microsphere.util.ClassLoaderUtils.resolveClass; -import static io.microsphere.util.ClassUtils.newInstance; /** * Standard implementation of {@link URLStreamHandlerFactory} that creates a new instance of @@ -78,7 +78,7 @@ URLStreamHandler createURLStreamHandler(Field defaultFactoryField, String protoc String name = DEFAULT_HANDLER_PACKAGE_PREFIX + DOT + protocol + DOT + "Handler"; Class handlerClass = resolveClass(name); if (handlerClass != null) { - handler = (URLStreamHandler) newInstance(handlerClass); + handler = (URLStreamHandler) newInstance(true, handlerClass); } } return handler; @@ -91,7 +91,7 @@ URLStreamHandler createURLStreamHandlerFromDefaultFactory(Field defaultFactoryFi } return null; } - URLStreamHandlerFactory factory = getStaticFieldValue(defaultFactoryField); + URLStreamHandlerFactory factory = getStaticFieldValue(true, defaultFactoryField); URLStreamHandler handler = factory.createURLStreamHandler(protocol); return handler; } diff --git a/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java b/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java index b504c9679..0a12c3422 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java @@ -1422,7 +1422,7 @@ public static void attachURLStreamHandlerFactory(URLStreamHandlerFactory factory */ @Nullable public static URLStreamHandlerFactory getURLStreamHandlerFactory() { - return getStaticFieldValue(URL.class, "factory"); + return getStaticFieldValue(true, URL.class, "factory"); } /** @@ -1639,7 +1639,7 @@ private static MutableURLStreamHandlerFactory findMutableURLStreamHandlerFactory } protected static void clearURLStreamHandlerFactory() { - setStaticFieldValue(URL.class, "factory", null); + setStaticFieldValue(true, URL.class, "factory", null); } protected static String reformProtocol(String protocol, String spec) { diff --git a/microsphere-java-core/src/main/java/io/microsphere/process/VirtualMachineProcessIdResolver.java b/microsphere-java-core/src/main/java/io/microsphere/process/VirtualMachineProcessIdResolver.java index de4c5bead..a7dad062a 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/process/VirtualMachineProcessIdResolver.java +++ b/microsphere-java-core/src/main/java/io/microsphere/process/VirtualMachineProcessIdResolver.java @@ -88,8 +88,8 @@ public boolean supports() { @Override public Long current() { RuntimeMXBean runtimeMXBean = getRuntimeMXBean(); - Object jvm = getFieldValue(runtimeMXBean, JVM_FIELD); - Integer processId = invokeMethod(jvm, GET_PROCESS_ID_METHOD_NAME); + Object jvm = getFieldValue(true, runtimeMXBean, JVM_FIELD); + Integer processId = invokeMethod(true, jvm, GET_PROCESS_ID_METHOD_NAME); if (logger.isTraceEnabled()) { logger.trace("The PID was resolved from the native method 'sun.management.VMManagementImpl#getProcessId()' : {}", processId); } diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/ConstructorUtils.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/ConstructorUtils.java index 17c5a1fbb..405313bc4 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/ConstructorUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/ConstructorUtils.java @@ -23,9 +23,8 @@ import io.microsphere.logging.Logger; import io.microsphere.util.Utils; -import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; import java.util.List; import java.util.function.Predicate; @@ -35,8 +34,9 @@ import static io.microsphere.lang.function.ThrowableSupplier.execute; import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.reflect.AccessibleObjectUtils.trySetAccessible; -import static io.microsphere.reflect.ExecutableUtils.execute; +import static io.microsphere.reflect.ExecutableUtils.matchParameterTypes; import static io.microsphere.reflect.MemberUtils.isPrivate; +import static io.microsphere.text.FormatUtils.format; import static io.microsphere.util.ArrayUtils.arrayToString; import static java.util.Collections.unmodifiableList; @@ -270,44 +270,146 @@ public static Constructor findConstructor(Class type, Class... para }); } + /** + * Creates a new instance of the specified class by inferring constructor parameter types + * from the runtime types of {@code args}. + * + *

+ * This method delegates to {@link #newInstance(boolean, Class, Object...)} with + * {@code forceAccess = false}, so normal Java access checks apply. + *

+ * + *

Example Usage

+ *
{@code
+     * Person person = newInstance(Person.class, "Mercy", 18);
+     * }
+ * + * @param type the target class to instantiate + * @param args the constructor arguments used to locate and invoke a matching constructor + * @param the target type + * @return a new instance of {@code type} + */ + @Nonnull + public static T newInstance(Class type, Object... args) { + return newInstance(false, type, args); + } + + /** + * Creates a new instance of the specified class by locating a declared constructor + * that matches the runtime argument types and invoking it. + * + *

+ * When {@code forceAccess} is {@code true}, this method attempts to make the resolved + * constructor accessible before invocation, which allows use of non-public constructors. + *

+ * + *

Example Usage

+ *

Invoke Public Constructor

+ *
{@code
+     * User user = newInstance(false, User.class, "Alice");
+     * }
+ * + *

Invoke Private Constructor

+ *
{@code
+     * Singleton singleton = newInstance(true, Singleton.class);
+     * }
+ * + * @param forceAccess whether to force constructor accessibility + * @param type the target class to instantiate + * @param args the constructor arguments used to locate and invoke a matching constructor + * @param the target type + * @return a new instance of {@code type} + */ + @Nonnull + public static T newInstance(boolean forceAccess, Class type, Object... args) { + List> constructors = findDeclaredConstructors(type, constructor -> matchParameterTypes(constructor, args)); + if (constructors.isEmpty()) { + String message = format("No constructor['{}'] matches the arguments : {}", type, arrayToString(args)); + throw new IllegalArgumentException(message); + } + Constructor constructor = (Constructor) constructors.get(0); + return newInstance(forceAccess, constructor, args); + } + /** * Creates a new instance by invoking the specified {@link Constructor} with the provided arguments. * *

- * This method makes the constructor accessible (if it is not already) using - * {@link AccessibleObjectUtils#trySetAccessible(AccessibleObject)} and then invokes it using - * the utility method from {@link ExecutableUtils#execute(Executable, ThrowableSupplier)} to handle exceptions uniformly. + * This method uses normal Java access checks and delegates to + * {@link #newInstance(boolean, Constructor, Object...)} with {@code forceAccess = false}. *

* *

Example Usage

- *

Basic Instantiation

*
{@code
      * Constructor constructor = MyClass.class.getConstructor(String.class, int.class);
      * MyClass instance = newInstance(constructor, "hello", 42);
      * }
* - *

Using a Private Constructor

+ * @param constructor the constructor to invoke + * @param args the arguments to pass to the constructor + * @param the type of the object created by the constructor + * @return a new instance of the object constructed by the given constructor + */ + @Nonnull + public static T newInstance(Constructor constructor, Object... args) { + return newInstance(false, constructor, args); + } + + /** + * Creates a new instance by invoking the specified {@link Constructor} with optional forced access. + * + *

+ * When {@code forceAccess} is {@code true}, this method attempts to make the constructor accessible first, + * allowing invocation of non-public constructors. When {@code forceAccess} is {@code false}, normal Java + * access checks apply. + *

+ * + *

Example Usage

+ *

Invoke a Public Constructor Without Forcing Access

+ *
{@code
+     * Constructor constructor = MyClass.class.getConstructor(String.class);
+     * MyClass instance = newInstance(false, constructor, "hello");
+     * }
+ * + *

Invoke a Private Constructor by Forcing Access

*
{@code
-     * Constructor privateConstructor = SingletonClass.class.getDeclaredConstructor();
-     * SingletonClass instance = newInstance(privateConstructor); // Accesses private constructor
+     * Constructor constructor = SingletonClass.class.getDeclaredConstructor();
+     * SingletonClass instance = newInstance(true, constructor);
      * }
* + * @param forceAccess whether to force constructor accessibility before invocation * @param constructor the constructor to invoke * @param args the arguments to pass to the constructor * @param the type of the object created by the constructor - * @return a new instance of the object constructed by the given constructor - * @throws NullPointerException if the constructor is {@code null} - * @throws IllegalStateException if the constructor cannot be accessed or invoked - * @throws IllegalArgumentException if the arguments do not match the expected parameter types - * @throws RuntimeException if the constructor throws an exception during invocation + * @return a new instance created by invoking the given constructor + * @throws IllegalStateException if this {@code Constructor} enforces Java language access control and + * the underlying constructor is inaccessible, or if the declaring class + * is abstract and cannot be instantiated + * @throws IllegalArgumentException if the argument count or argument types are incompatible with the + * constructor parameters, or if this constructor belongs to an enum class + * @throws RuntimeException if the underlying constructor throws an exception */ @Nonnull - public static T newInstance(Constructor constructor, Object... args) { - trySetAccessible(constructor); - return execute(constructor, () -> constructor.newInstance(args)); + public static T newInstance(boolean forceAccess, Constructor constructor, Object... args) { + if (forceAccess) { + trySetAccessible(constructor); + } + T instance = null; + RuntimeException failure = null; + try { + instance = constructor.newInstance(args); + } catch (IllegalAccessException | InstantiationException e) { + failure = new IllegalStateException(e.getMessage(), e); + } catch (InvocationTargetException e) { + failure = new RuntimeException(e.getMessage(), e.getTargetException()); + } + if (failure != null) { + logger.error("It's failed to invoke the constructor[signature : '{}' , arguments : {}]", constructor, arrayToString(args), failure.getCause()); + throw failure; + } + return instance; } private ConstructorUtils() { } - -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/ExecutableUtils.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/ExecutableUtils.java index 49487e6de..482961758 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/ExecutableUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/ExecutableUtils.java @@ -16,6 +16,7 @@ */ package io.microsphere.reflect; +import io.microsphere.annotation.Nonnull; import io.microsphere.lang.function.ThrowableConsumer; import io.microsphere.lang.function.ThrowableFunction; import io.microsphere.lang.function.ThrowableSupplier; @@ -31,6 +32,9 @@ import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.text.FormatUtils.format; +import static io.microsphere.util.ArrayUtils.length; +import static io.microsphere.util.ClassUtils.isPrimitive; +import static io.microsphere.util.ClassUtils.resolveWrapperType; import static io.microsphere.util.ExceptionUtils.wrap; /** @@ -235,6 +239,32 @@ public static R execute(E executableMember, T return result; } + /** + * Matches the parameter types of the given {@link Executable} with the provided arguments. + * + * @param executable The executable member whose parameter types are to be matched. + * @param args The arguments to be matched against the parameter types of the executable. + * @return true if the parameter types of the executable match the provided arguments; false otherwise. + */ + public static boolean matchParameterTypes(@Nonnull Executable executable, @Nonnull Object... args) { + Class[] parameterTypes = executable.getParameterTypes(); + int length = parameterTypes.length; + if (length != length(args)) { + return false; + } + for (int i = 0; i < length; i++) { + Object arg = args[i]; + Class parameterType = parameterTypes[i]; + if (isPrimitive(parameterType)) { + parameterType = resolveWrapperType(parameterType); + } + if (!parameterType.isInstance(arg)) { + return false; + } + } + return true; + } + private ExecutableUtils() { } } diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/FieldDefinition.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/FieldDefinition.java index d9892f319..a3089e4cc 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/FieldDefinition.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/FieldDefinition.java @@ -122,7 +122,7 @@ public Field getResolvedField() { * the underlying field (or a subclass or implementor thereof). */ public T get(Object instance) throws IllegalStateException, IllegalArgumentException { - return getFieldValue(instance, getResolvedField()); + return getFieldValue(true, instance, getResolvedField()); } /** @@ -138,6 +138,6 @@ public T get(Object instance) throws IllegalStateException, IllegalArgumentE * the underlying field (or a subclass or implementor thereof). */ public T set(Object instance, T fieldValue) { - return setFieldValue(instance, getResolvedField(), fieldValue); + return setFieldValue(true, instance, getResolvedField(), fieldValue); } } diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/FieldUtils.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/FieldUtils.java index 102f50d80..b3e7ac418 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/FieldUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/FieldUtils.java @@ -38,7 +38,8 @@ import static io.microsphere.reflect.TypeUtils.isObjectClass; import static io.microsphere.text.FormatUtils.format; import static io.microsphere.util.ClassUtils.getAllInheritedTypes; -import static io.microsphere.util.ClassUtils.getTypeName; +import static io.microsphere.util.ExceptionUtils.wrap; +import static io.microsphere.util.ObjectUtils.defaultIfNull; import static java.util.Collections.unmodifiableSet; /** @@ -74,7 +75,7 @@ public abstract class FieldUtils implements Utils { * @return the {@link Field} object if found; otherwise, {@code null} */ @Nullable - public static Field findField(Object object, String fieldName) { + public static Field findField(@Nonnull Object object, @Nonnull String fieldName) { return findField(object.getClass(), fieldName); } @@ -105,7 +106,7 @@ public static Field findField(Object object, String fieldName) { * typically wrapped as an unchecked exception */ @Nullable - public static Field findField(Class klass, String fieldName) { + public static Field findField(@Nonnull Class klass, @Nonnull String fieldName) { if (klass == null || isObjectClass(klass)) { return null; } @@ -148,7 +149,7 @@ public static Field findField(Class klass, String fieldName) { * @return The found {@link Field}, or {@code null} if no such field exists */ @Nullable - public static Field findField(Class klass, String fieldName, Class fieldType) { + public static Field findField(@Nonnull Class klass, @Nonnull String fieldName, @Nonnull Class fieldType) { return findField(klass, fieldName, field -> Objects.equals(fieldType, field.getType())); } @@ -180,7 +181,7 @@ public static Field findField(Class klass, String fieldName, Class fieldTy * @return The found field that satisfies all the provided predicates, or {@code null} if no such field exists */ @Nullable - public static Field findField(Class klass, String fieldName, Predicate... predicates) { + public static Field findField(@Nonnull Class klass, @Nonnull String fieldName, @Nonnull Predicate... predicates) { Field field = findField(klass, fieldName); return and(predicates).test(field) ? field : null; } @@ -195,6 +196,11 @@ public static Field findField(Class klass, String fieldName, Predicate{@code * class Example { * private static String testField = "Hello, World!"; + * + * void someMethod() { + * String value = FieldUtils.getStaticFieldValue(Example.class, "testField"); + * System.out.println("Field value: " + value); // Output: Field value: Hello, World! + * } * } * * String value = FieldUtils.getStaticFieldValue(Example.class, "testField"); @@ -205,11 +211,50 @@ public static Field findField(Class klass, String fieldName, Predicate The type of the field value * @return The value of the static field, or {@code null} if the field could not be accessed or was not found + * @throws IllegalStateException if this {@code Field} object is enforcing Java language access control and the + * underlying field is inaccessible. + * @throws IllegalArgumentException if the specified object is not an + * instance of the class or interface declaring the underlying field (or a subclass + * or implementor thereof). + */ + @Nullable + public static T getStaticFieldValue(@Nonnull Class klass, @Nonnull String fieldName) throws + IllegalStateException, IllegalArgumentException { + return getStaticFieldValue(false, klass, fieldName); + } + + /** + * Retrieves the value of a static field from the specified class, optionally forcing accessibility. + * + *

When {@code forceAccess} is {@code true}, this method attempts to make the field accessible + * before reading its value. This is useful for private or protected static fields.

+ * + *

Example Usage

+ *
{@code
+     * class Example {
+     *     private static String SECRET = "Hidden";
+     * }
+     *
+     * String value = FieldUtils.getStaticFieldValue(true, Example.class, "SECRET");
+     * System.out.println(value);  // Output: Hidden
+     * }
+ * + * @param The type of the field value + * @param forceAccess Whether to force reflective accessibility when reading the field + * @param klass The class containing the static field + * @param fieldName The name of the static field + * @return The value of the static field, or {@code null} if the field is not found + * @throws IllegalStateException if this {@code Field} object is enforcing Java language access control and the + * underlying field is inaccessible. + * @throws IllegalArgumentException if the specified object is not an + * instance of the class or interface declaring the underlying field (or a subclass + * or implementor thereof). */ @Nullable - public static T getStaticFieldValue(Class klass, String fieldName) { + public static T getStaticFieldValue(boolean forceAccess, @Nonnull Class klass, @Nonnull String fieldName) + throws IllegalStateException, IllegalArgumentException { Field field = findField(klass, fieldName); - return getStaticFieldValue(field); + return getStaticFieldValue(forceAccess, field); } /** @@ -232,10 +277,46 @@ public static T getStaticFieldValue(Class klass, String fieldName) { * @param field The {@link Field} object representing the static field * @param The type of the field value * @return The value of the static field, or {@code null} if the field could not be accessed or was not found + * @throws IllegalStateException if this {@code Field} object is enforcing Java language access control and the + * underlying field is inaccessible. + * @throws IllegalArgumentException if the specified object is not an + * instance of the class or interface declaring the underlying field (or a subclass + * or implementor thereof). */ @Nullable - public static T getStaticFieldValue(Field field) { - return getFieldValue(null, field); + public static T getStaticFieldValue(@Nullable Field field) throws IllegalStateException, IllegalArgumentException { + return getStaticFieldValue(false, field); + } + + /** + * Retrieves the value of a static field, optionally forcing accessibility. + * + *

If {@code forceAccess} is enabled, this method tries to make the field accessible before + * reading the value. Passing {@code null} for {@code field} returns {@code null}.

+ * + *

Example Usage

+ *
{@code
+     * class Example {
+     *     private static int PORT = 8080;
+     * }
+     *
+     * Field portField = FieldUtils.findField(Example.class, "PORT");
+     * Integer port = FieldUtils.getStaticFieldValue(true, portField);
+     * System.out.println(port);  // Output: 8080
+     * }
+ * + * @param The type of the field value + * @param forceAccess Whether to force reflective accessibility when reading the field + * @param field The {@link Field} object representing the static field + * @return The value of the static field, or {@code null} if the field is {@code null} + * @throws IllegalStateException if this Field object is enforcing Java language access control and the underlying + * field is inaccessible + * @throws IllegalArgumentException if the specified object is not an instance of the class or interface declaring + * the underlying field (or a subclass or implementor thereof) + */ + @Nullable + public static T getStaticFieldValue(boolean forceAccess, @Nullable Field field) throws IllegalStateException, IllegalArgumentException { + return getFieldValue(forceAccess, null, field); } /** @@ -265,9 +346,39 @@ public static T getStaticFieldValue(Field field) { * @return The previous value of the static field, or {@code null} if the field could not be accessed or was not found */ @Nullable - public static V setStaticFieldValue(Class klass, String fieldName, V fieldValue) { + public static V setStaticFieldValue(@Nonnull Class klass, @Nonnull String fieldName, @Nullable V fieldValue) { Field field = findField(klass, fieldName); - return setFieldValue(null, field, fieldValue); + return setStaticFieldValue(false, klass, fieldName, fieldValue); + } + + /** + * Sets the value of a static field in the specified class, optionally forcing accessibility. + * + *

When {@code forceAccess} is {@code true}, this method attempts to make the field accessible + * before reading and updating it. The previous field value is returned.

+ * + *

Example Usage

+ *
{@code
+     * class Example {
+     *     private static String MODE = "DEV";
+     * }
+     *
+     * String previous = FieldUtils.setStaticFieldValue(true, Example.class, "MODE", "PROD");
+     * System.out.println(previous); // Output: DEV
+     * System.out.println(FieldUtils.getStaticFieldValue(true, Example.class, "MODE")); // Output: PROD
+     * }
+ * + * @param The type of the field value + * @param forceAccess Whether to force reflective accessibility when writing the field + * @param klass The class containing the static field + * @param fieldName The name of the static field to set + * @param fieldValue The new value to assign to the static field + * @return The previous value of the static field, or {@code null} if the field is not found + */ + @Nullable + public static V setStaticFieldValue(boolean forceAccess, @Nonnull Class klass, @Nonnull String fieldName, @Nullable V fieldValue) { + Field field = findField(klass, fieldName); + return setFieldValue(forceAccess, null, field, fieldValue); } /** @@ -301,7 +412,7 @@ public static V setStaticFieldValue(Class klass, String fieldName, V fiel */ @Nonnull @Immutable - public static Set findAllFields(Class declaredClass, Predicate... fieldFilters) { + public static Set findAllFields(@Nonnull Class declaredClass, @Nonnull Predicate... fieldFilters) { LinkedHashSet allFields = newLinkedHashSet(); addAll(allFields, declaredClass.getFields()); for (Class superType : getAllInheritedTypes(declaredClass)) { @@ -342,7 +453,7 @@ public static Set findAllFields(Class declaredClass, Predicate findAllDeclaredFields(Class declaredClass, Predicate... fieldFilters) { + public static Set findAllDeclaredFields(@Nonnull Class declaredClass, @Nonnull Predicate... fieldFilters) { LinkedHashSet allDeclaredFields = newLinkedHashSet(); addAll(allDeclaredFields, declaredClass.getDeclaredFields()); for (Class superType : getAllInheritedTypes(declaredClass)) { @@ -378,155 +489,355 @@ public static Set findAllDeclaredFields(Class declaredClass, Predicate * typically wrapped as an unchecked exception */ @Nullable - public static Field getDeclaredField(Class declaredClass, String fieldName) { + public static Field getDeclaredField(@Nonnull Class declaredClass, @Nonnull String fieldName) { return execute(() -> declaredClass.getDeclaredField(fieldName)); } /** - * Retrieves the value of a field with the specified name from the given object instance. + * Retrieves the value of a field by name from the given object instance. * - *

This method finds the declared field by its name in the class of the provided instance and retrieves its value. - * If the field is not found or cannot be accessed, an appropriate exception will be thrown.

+ *

This is a convenience overload of {@link #getFieldValue(boolean, Object, String)} + * with {@code forceAccess=false}. It will read public fields directly and may throw an + * access-related exception for non-public fields.

* *

Example Usage

*
{@code
      * class Example {
-     *     private String exampleField = "Hello, Reflection!";
+     *     private String message = "Hello";
      * }
      *
      * Example instance = new Example();
-     * String value = FieldUtils.getFieldValue(instance, "exampleField");
-     * System.out.println("Field value: " + value);  // Output: Field value: Hello, Reflection!
+     * String value = FieldUtils.getFieldValue(instance, "message");
+     * System.out.println(value); // May require forceAccess=true for private fields
      * }
* * @param The type of the field value * @param instance The object instance from which to retrieve the field value * @param fieldName The name of the field whose value is to be retrieved - * @return The value of the field, or {@code null} if the field could not be accessed or was not found + * @return The value of the field, or {@code null} if the field is not found + * @throws IllegalStateException if this {@code Field} object is enforcing Java language access control and the + * underlying field is inaccessible + * @throws IllegalArgumentException if the specified object is not an instance of the class or interface declaring + * the underlying field (or a subclass or implementor thereof) + */ + @Nullable + public static V getFieldValue(@Nonnull Object instance, @Nonnull String fieldName) throws IllegalStateException, IllegalArgumentException { + return getFieldValue(false, instance, fieldName); + } + + /** + * Retrieves the value of a field by name from the given object instance, with optional forced accessibility. + * + *

When {@code forceAccess} is {@code true}, this method attempts to make the target field accessible + * before reading its value, which is useful for private or protected fields.

+ * + *

Example Usage

+ *
{@code
+     * class Example {
+     *     private String secret = "Hidden";
+     * }
+     *
+     * Example instance = new Example();
+     * String value = FieldUtils.getFieldValue(true, instance, "secret");
+     * System.out.println(value);  // Output: Hidden
+     * }
+ * + * @param The type of the field value + * @param forceAccess Whether to force reflective accessibility when reading the field + * @param instance The object instance from which to retrieve the field value + * @param fieldName The name of the field whose value is to be retrieved + * @return The value of the field, or {@code null} if the field is not found * @throws IllegalStateException if this Field object is enforcing Java language access control and the underlying * field is inaccessible * @throws IllegalArgumentException if the specified object is not an instance of the class or interface declaring * the underlying field (or a subclass or implementor thereof) + * @throws NullPointerException if the specified object is null and the field is an instance field. */ @Nullable - public static V getFieldValue(Object instance, String fieldName) throws IllegalStateException, IllegalArgumentException { - return getFieldValue(instance, findField(instance, fieldName)); + public static V getFieldValue(boolean forceAccess, @Nonnull Object instance, @Nonnull String fieldName) throws + IllegalStateException, IllegalArgumentException, NullPointerException { + return getFieldValue(forceAccess, instance, findField(instance, fieldName)); } /** - * Retrieves the value of a field with the specified name from the given object instance, - * returning the provided default value if the field is not found or its value is {@code null}. + * Retrieves the value of a field by name and returns a fallback when the field value is {@code null}. * - *

This method finds the declared field by its name in the class of the provided instance and retrieves its value. - * If the field is not found or cannot be accessed, an appropriate exception will be thrown.

+ *

This method first delegates to {@link #getFieldValue(Object, String)}. If the resolved field value + * is {@code null}, it returns the provided {@code defaultValue} instead.

* *

Example Usage

*
{@code
      * class Example {
-     *     private String exampleField;
+     *     private String label;
+     * }
+     *
+     * Example instance = new Example();
+     * String value = FieldUtils.getFieldValue(instance, "label", "N/A");
+     * System.out.println(value);  // Output: N/A
+     * }
+ * + *

When Field Value Exists

+ * + *
{@code
+     * class Example {
+     *     private String label = "Microsphere";
+     * }
+     *
+     * Example instance = new Example();
+     * String value = FieldUtils.getFieldValue(instance, "label", "N/A");
+     * System.out.println(value);  // Output: Microsphere
+     * }
+ * + * @param The type of the field value + * @param instance The object instance from which to retrieve the field value + * @param fieldName The name of the field whose value is to be retrieved + * @param defaultValue The fallback value to return if the resolved field value is {@code null} + * @return The resolved field value, or {@code defaultValue} if the resolved value is {@code null} + * @throws IllegalStateException if this {@code Field} object is enforcing Java language access control and the + * underlying field is inaccessible + * @throws IllegalArgumentException if the specified object is not an instance of the class or interface declaring + * the underlying field (or a subclass or implementor thereof) + * @throws NullPointerException if the specified object is null and the field is an instance field. + */ + @Nullable + public static V getFieldValue(@Nonnull Object instance, @Nonnull String fieldName, @Nullable V defaultValue) + throws IllegalStateException, IllegalArgumentException, NullPointerException { + return getFieldValue(false, instance, fieldName, defaultValue); + } + + /** + * Retrieves the value of a field by name and returns a fallback when the resolved value is {@code null}, + * optionally forcing accessibility. + * + *

This method is useful when the target field may be private or protected and you want a single call + * that both reads the field and substitutes a default value when the actual field value is {@code null}.

+ * + *

Example Usage

+ *
{@code
+     * class Example {
+     *     private String name;
+     * }
+     *
+     * Example instance = new Example();
+     * String value = FieldUtils.getFieldValue(true, instance, "name", "Unknown");
+     * System.out.println(value);  // Output: Unknown
+     * }
+ * + *

When Field Value Exists

+ * + *
{@code
+     * class Example {
+     *     private String name = "Microsphere";
      * }
      *
      * Example instance = new Example();
-     * String value = FieldUtils.getFieldValue(instance, "exampleField", "default");
-     * System.out.println("Field value: " + value);  // Output: Field value: default
+     * String value = FieldUtils.getFieldValue(true, instance, "name", "Unknown");
+     * System.out.println(value);  // Output: Microsphere
      * }
* * @param The type of the field value + * @param forceAccess Whether to force reflective accessibility when reading the field * @param instance The object instance from which to retrieve the field value * @param fieldName The name of the field whose value is to be retrieved - * @param defaultValue The default value to return if the field's value is {@code null} - * @return The value of the field if found and not {@code null}; otherwise, the {@code defaultValue} - * @throws IllegalStateException If this {@code Field} object is enforcing Java language access control - * and the underlying field is inaccessible - * @throws IllegalArgumentException If the specified object is not an instance of the class or interface - * declaring the underlying field (or a subclass or implementor thereof) + * @param defaultValue The fallback value to return if the resolved field value is {@code null} + * @return The resolved field value, or {@code defaultValue} if the resolved value is {@code null} + * @throws IllegalStateException if this {@code Field} object is enforcing Java language access control and the + * underlying field is inaccessible + * @throws IllegalArgumentException if the specified object is not an instance of the class or interface declaring + * the underlying field (or a subclass or implementor thereof) + * @throws NullPointerException if the specified object is null and the field is an instance field. */ @Nullable - public static V getFieldValue(Object instance, String fieldName, V defaultValue) throws IllegalStateException, IllegalArgumentException { - V value = getFieldValue(instance, fieldName); - return value != null ? value : defaultValue; + public static V getFieldValue(boolean forceAccess, @Nonnull Object instance, @Nonnull String fieldName, @Nullable V defaultValue) + throws IllegalStateException, IllegalArgumentException, NullPointerException { + V value = getFieldValue(forceAccess, instance, fieldName); + return defaultIfNull(value, defaultValue); } /** - * Retrieves the value of a field with the specified name and type from the given object instance. + * Retrieves the value of a field by name and expected type from the given object instance. * - *

This method finds the declared field by its name and ensures it matches the provided field type. - * If the field is not found or its type does not match, {@code null} is returned.

+ *

This is a convenience overload of {@link #getFieldValue(boolean, Object, String, Class)} + * with {@code forceAccess=false}. Use this method when the target field is accessible and you + * want a type-safe lookup by field name and declared type.

* *

Example Usage

*
{@code
      * class Example {
-     *     private String exampleField = "Typed Value";
+     *     public String name = "Microsphere";
      * }
      *
      * Example instance = new Example();
-     * String value = FieldUtils.getFieldValue(instance, "exampleField", String.class);
-     * System.out.println("Field value: " + value);  // Output: Field value: Typed Value
+     * String value = FieldUtils.getFieldValue(instance, "name", String.class);
+     * System.out.println(value);  // Output: Microsphere
+     * }
+ * + *

Type Mismatch Example

+ * + *
{@code
+     * class Example {
+     *     public String name = "Microsphere";
+     * }
+     *
+     * Example instance = new Example();
+     * Integer value = FieldUtils.getFieldValue(instance, "name", Integer.class);
+     * System.out.println(value);  // Output: null (field type does not match)
      * }
* * @param The expected type of the field value * @param instance The object instance from which to retrieve the field value * @param fieldName The name of the field whose value is to be retrieved * @param fieldType The expected type of the field + * @return The value of the field if found and type-matched; otherwise, {@code null} + * @throws IllegalStateException if this {@code Field} object is enforcing Java language access control and the + * underlying field is inaccessible + * @throws IllegalArgumentException if the specified object is not an instance of the class or interface declaring + * the underlying field (or a subclass or implementor thereof) + * @throws NullPointerException if the specified object is null and the field is an instance field. + */ + @Nullable + public static V getFieldValue(@Nonnull Object instance, @Nonnull String fieldName, @Nonnull Class fieldType) + throws IllegalStateException, IllegalArgumentException, NullPointerException { + return getFieldValue(false, instance, fieldName, fieldType); + } + + /** + * Retrieves the value of a field with the specified name and type from the given object instance, + * optionally forcing accessibility. + * + *

This variant is useful when the field may be non-public and you want to verify the field type + * before reading its value.

+ * + *

Example Usage

+ *
{@code
+     * class Example {
+     *     private String secret = "typed";
+     * }
+     *
+     * Example instance = new Example();
+     * String value = FieldUtils.getFieldValue(true, instance, "secret", String.class);
+     * System.out.println(value);  // Output: typed
+     * }
+ * + * @param The expected type of the field value + * @param forceAccess Whether to force reflective accessibility when reading the field + * @param instance The object instance from which to retrieve the field value + * @param fieldName The name of the field whose value is to be retrieved + * @param fieldType The expected type of the field * @return The value of the field if found and of the correct type; otherwise, {@code null} - * @throws IllegalStateException if this Field object is enforcing Java language access control and the underlying - * field is inaccessible + * @throws IllegalStateException if this {@code Field} object is enforcing Java language access control and the + * underlying field is inaccessible * @throws IllegalArgumentException if the specified object is not an instance of the class or interface declaring * the underlying field (or a subclass or implementor thereof) + * @throws NullPointerException if the specified object is null and the field is an instance field. */ @Nullable - public static V getFieldValue(Object instance, String fieldName, Class fieldType) throws IllegalStateException, IllegalArgumentException { + public static V getFieldValue(boolean forceAccess, @Nonnull Object instance, @Nonnull String fieldName, @Nonnull Class fieldType) + throws IllegalStateException, IllegalArgumentException, NullPointerException { Field field = findField(instance.getClass(), fieldName, fieldType); - return getFieldValue(instance, field); + return getFieldValue(forceAccess, instance, field); } /** * Retrieves the value of the specified {@link Field} from the given object instance. * - *

This method accesses the field's value using reflection. If the field is not accessible, - * an attempt will be made to make it accessible. If access fails, an exception will be thrown.

+ *

This is a convenience overload of {@link #getFieldValue(boolean, Object, Field)} + * with {@code forceAccess=false}. Use this method when the field is already accessible, + * or when you are reading public members.

* *

Example Usage

*
{@code
      * class Example {
-     *     private String exampleField = "Reflection Value";
+     *     public String name = "Microsphere";
      * }
      *
      * Example instance = new Example();
-     * Field field = FieldUtils.findField(instance, "exampleField");
+     * Field field = FieldUtils.findField(instance, "name");
      * String value = FieldUtils.getFieldValue(instance, field);
-     * System.out.println("Field value: " + value);  // Output: Field value: Reflection Value
+     * System.out.println(value);  // Output: Microsphere
      * }
* - *

Handling Null Fields

+ *

Null Field Handling

* *
{@code
-     * Field nullField = null;
-     * String value = FieldUtils.getFieldValue(instance, nullField);
-     * System.out.println("Field value: " + value);  // Output: Field value: null
+     * Example instance = new Example();
+     * String value = FieldUtils.getFieldValue(instance, (Field) null);
+     * System.out.println(value);  // Output: null
      * }
* * @param The type of the field value * @param instance The object instance from which to retrieve the field value - * @param field The {@link Field} object representing the field to retrieve - * @return The value of the field if found and accessible; otherwise, {@code null} + * @param field The {@link Field} object representing the field to retrieve (nullable) + * @return The field value, or {@code null} if the field is {@code null} + * @throws IllegalStateException if this {@code Field} object is enforcing Java language access control and the + * underlying field is inaccessible * @throws IllegalArgumentException if the specified object is not an instance of the class or interface declaring * the underlying field (or a subclass or implementor thereof) + * @throws NullPointerException if the specified object is null and the field is an instance field. */ @Nullable - public static V getFieldValue(Object instance, Field field) throws IllegalStateException, IllegalArgumentException { + public static V getFieldValue(@Nullable Object instance, @Nullable Field field) throws IllegalStateException, + IllegalArgumentException, NullPointerException { + return getFieldValue(false, instance, field); + } + + /** + * Retrieves the value of the specified {@link Field} from the given object instance, + * with optional forced accessibility. + * + *

When {@code forceAccess} is {@code true}, this method tries to make the field accessible + * before reading its value. If {@code field} is {@code null}, this method returns {@code null}.

+ * + *

Example Usage

+ *
{@code
+     * class Example {
+     *     private String secret = "Reflection";
+     * }
+     *
+     * Example instance = new Example();
+     * Field field = FieldUtils.findField(instance, "secret");
+     * String value = FieldUtils.getFieldValue(true, instance, field);
+     * System.out.println(value);  // Output: Reflection
+     * }
+ * + * @param The type of the field value + * @param forceAccess Whether to force reflective accessibility when reading the field + * @param instance The object instance from which to retrieve the field value + * @param field The {@link Field} object representing the field to retrieve (nullable) + * @return The value of the field if found and accessible; otherwise, {@code null} + * @throws IllegalStateException if this {@code Field} object is enforcing Java language access control and the + * underlying field is inaccessible. + * @throws IllegalArgumentException if the specified object is not an + * instance of the class or interface declaring the underlying field (or a subclass + * or implementor thereof). + * @throws NullPointerException if the specified object is null and the field is an instance field. + */ + @Nullable + public static V getFieldValue(boolean forceAccess, @Nullable Object instance, @Nullable Field field) throws + IllegalStateException, IllegalArgumentException, NullPointerException { if (field == null) { return null; } V fieldValue = null; - boolean accessible = false; + RuntimeException failure = null; + boolean trySetAccessible = false; try { - accessible = trySetAccessible(field); + if (forceAccess) { + trySetAccessible = trySetAccessible(field); + } fieldValue = (V) field.get(instance); - } catch (IllegalAccessException | IllegalArgumentException e) { - handleFieldException(e, instance, field); - throw new IllegalArgumentException(e); + } catch (IllegalAccessException e) { + failure = new IllegalStateException(e); + } finally { + if (logger.isTraceEnabled()) { + logger.trace("Got the value of field[signature : '{}' , forceAccess : {} , trySetAccessible : {} , instance : {}] : {}", + field, forceAccess, trySetAccessible, instance, fieldValue, failure); + } + } + + if (failure != null) { + throw failure; } return fieldValue; @@ -562,14 +873,56 @@ public static V getFieldValue(Object instance, Field field) throws IllegalSt * @param value The new value to assign to the field * @return The previous value of the field before it was updated, or {@code null} if the field could not be accessed * or was not found - * @throws IllegalStateException if this Field object is enforcing Java language access control and the underlying - * field is inaccessible + * @throws IllegalAccessException if this {@code Field} object is enforcing Java language access control and the + * underlying field is inaccessible or final; or if this {@code Field} object has + * no write access. * @throws IllegalArgumentException if the specified object is not an instance of the class or interface declaring - * the underlying field (or a subclass or implementor thereof) + * the underlying field (or a subclass or implementor thereof), or if an unwrapping + * conversion fails. + * @throws NullPointerException if the specified object is null and the field is an instance field. + */ + @Nullable + public static V setFieldValue(@Nonnull Object instance, @Nonnull String fieldName, @Nullable V value) throws + IllegalStateException, IllegalArgumentException, NullPointerException { + return setFieldValue(false, instance, fieldName, value); + } + + /** + * Sets the value of a field by name in the given object instance, optionally forcing accessibility. + * + *

When {@code forceAccess} is {@code true}, this method attempts to make the field accessible + * before reading and writing its value.

+ * + *

Example Usage

+ *
{@code
+     * class Example {
+     *     private String mode = "DEV";
+     * }
+     *
+     * Example instance = new Example();
+     * String previous = FieldUtils.setFieldValue(true, instance, "mode", "PROD");
+     * System.out.println(previous); // Output: DEV
+     * System.out.println(FieldUtils.getFieldValue(true, instance, "mode")); // Output: PROD
+     * }
+ * + * @param The type of the field value + * @param forceAccess Whether to force reflective accessibility when writing the field + * @param instance The object instance whose field value is to be modified + * @param fieldName The name of the field whose value is to be set + * @param value The new value to assign to the field + * @return The previous value of the field before modification, or {@code null} if the field is not found + * @throws IllegalStateException if this {@code Field} object is enforcing Java language access control and the + * underlying field is inaccessible or final; or if this {@code Field} object has + * no write access. + * @throws IllegalArgumentException if the specified object is not an instance of the class or interface declaring + * the underlying field (or a subclass or implementor thereof), or if an unwrapping + * conversion fails. + * @throws NullPointerException if the specified object is null and the field is an instance field. */ @Nullable - public static V setFieldValue(Object instance, String fieldName, V value) throws IllegalStateException, IllegalArgumentException { - return setFieldValue(instance, findField(instance, fieldName), value); + public static V setFieldValue(boolean forceAccess, @Nullable Object instance, @Nonnull String fieldName, @Nullable V value) + throws IllegalStateException, IllegalArgumentException, NullPointerException { + return setFieldValue(forceAccess, instance, findField(instance, fieldName), value); } /** @@ -610,24 +963,81 @@ public static V setFieldValue(Object instance, String fieldName, V value) th * @param field The {@link Field} object representing the field to modify * @param value The new value to assign to the field * @return The previous value of the field before modification, or {@code null} if the field was not found or inaccessible - * @throws IllegalArgumentException If the specified object is not an instance of the class or interface declaring the underlying field + * @throws IllegalAccessException if this {@code Field} object is enforcing Java language access control and the + * underlying field is inaccessible or final; or if this {@code Field} object has + * no write access. + * @throws IllegalArgumentException if the specified object is not an instance of the class or interface declaring + * the underlying field (or a subclass or implementor thereof), or if an unwrapping + * conversion fails. + * @throws NullPointerException if the specified object is null and the field is an instance field. */ @Nullable - public static V setFieldValue(Object instance, Field field, V value) throws IllegalStateException, IllegalArgumentException { + public static V setFieldValue(@Nullable Object instance, @Nullable Field field, @Nullable V value) throws IllegalStateException, IllegalArgumentException { + return setFieldValue(false, instance, field, value); + } + + /** + * Sets the value of the specified {@link Field} in the given object instance, + * optionally forcing accessibility. + * + *

This method returns the previous field value and only writes when the new value differs + * from the current value.

+ * + *

Example Usage

+ *
{@code
+     * class Example {
+     *     private int port = 8080;
+     * }
+     *
+     * Example instance = new Example();
+     * Field field = FieldUtils.findField(instance, "port");
+     * Integer previous = FieldUtils.setFieldValue(true, instance, field, 9090);
+     * System.out.println(previous); // Output: 8080
+     * }
+ * + * @param The type of the field value + * @param forceAccess Whether to force reflective accessibility when writing the field + * @param instance The object instance whose field value is to be modified + * @param field The {@link Field} object representing the field to modify + * @param value The new value to assign to the field + * @return The previous value of the field before modification, or {@code null} if the field is {@code null} + * @throws IllegalStateException if this {@code Field} object is enforcing Java language access control and the + * underlying field is inaccessible or final; or if this {@code Field} object has + * no write access + * @throws IllegalArgumentException if the specified object is not an instance of the class or interface declaring + * the underlying field (or a subclass or implementor thereof), or if an unwrapping + * conversion fails + * @throws NullPointerException if the specified object is null and the field is an instance field + */ + @Nullable + public static V setFieldValue(boolean forceAccess, @Nullable Object instance, @Nullable Field field, @Nullable V value) + throws IllegalStateException, IllegalArgumentException, NullPointerException { if (field == null) { return null; } V previousValue = null; + RuntimeException failure = null; + boolean trySetAccessible = false; try { - trySetAccessible(field); + if (forceAccess) { + trySetAccessible = trySetAccessible(field); + } previousValue = (V) field.get(instance); if (!Objects.equals(previousValue, value)) { field.set(instance, value); } } catch (IllegalAccessException | IllegalArgumentException e) { - handleFieldException(e, instance, field); - throw new IllegalArgumentException(e); + failure = wrap(e, IllegalArgumentException.class); + } finally { + if (logger.isTraceEnabled()) { + logger.trace("Set the value of field[signature : '{}' , forceAccess : {} , trySetAccessible : {} , instance : {}] : {} -> {}", + field, forceAccess, trySetAccessible, instance, previousValue, value, failure); + } + } + + if (failure != null) { + throw failure; } return previousValue; @@ -676,13 +1086,6 @@ public static void assertFieldMatchType(Object instance, String fieldName, Class } } - static void handleFieldException(Exception e, Object instance, Field field) { - if (logger.isTraceEnabled()) { - logger.trace("The instance[object : {} , class : {}] can't match the field[name : '{}' , type : {}]", - instance, getTypeName(instance.getClass()), field.getName(), getTypeName(field.getType()), e); - } - } - private FieldUtils() { } } diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/MemberUtils.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/MemberUtils.java index 9daa7a243..2cb246e1c 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/MemberUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/MemberUtils.java @@ -16,6 +16,7 @@ */ package io.microsphere.reflect; +import io.microsphere.annotation.Nullable; import io.microsphere.util.Utils; import java.lang.reflect.Constructor; @@ -25,6 +26,8 @@ import java.lang.reflect.Modifier; import java.util.function.Predicate; +import static io.microsphere.util.ClassUtils.isPrimitive; + /** * Java Reflection {@link Member} Utilities class * @@ -336,6 +339,16 @@ public static Member asMember(Object object) { return object instanceof Member ? (Member) object : null; } + /** + * Checks whether the specified declaring class is invalid, which means it is either {@code null} or a primitive type. + * + * @param declaredClass the declaring class to check + * @return true if the declaring class is invalid; false otherwise + */ + public static boolean isInvalidDeclaringClass(@Nullable Class declaredClass) { + return declaredClass == null || isPrimitive(declaredClass); + } + private MemberUtils() { } } diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodDefinition.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodDefinition.java index 74265f946..b76871743 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodDefinition.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodDefinition.java @@ -153,7 +153,7 @@ protected Method resolveMember() { * throws an exception. */ public R invoke(Object instance, Object... args) throws IllegalStateException, IllegalArgumentException, RuntimeException { - return invokeMethod(instance, getMethod(), args); + return invokeMethod(true, instance, getMethod(), args); } @Override diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodUtils.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodUtils.java index f9b90f060..2937e4c6a 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodUtils.java @@ -29,13 +29,14 @@ import java.util.LinkedList; import java.util.List; import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Predicate; import static io.microsphere.annotation.ConfigurationProperty.SYSTEM_PROPERTIES_SOURCE; +import static io.microsphere.collection.ListUtils.first; import static io.microsphere.collection.ListUtils.newLinkedList; import static io.microsphere.collection.ListUtils.of; +import static io.microsphere.collection.MapUtils.newConcurrentHashMap; import static io.microsphere.constants.PropertyConstants.MICROSPHERE_PROPERTY_NAME_PREFIX; import static io.microsphere.constants.SymbolConstants.COMMA_CHAR; import static io.microsphere.constants.SymbolConstants.LEFT_PARENTHESIS; @@ -50,8 +51,14 @@ import static io.microsphere.lang.function.Streams.filterAll; import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.reflect.AccessibleObjectUtils.trySetAccessible; +import static io.microsphere.reflect.ExecutableUtils.matchParameterTypes; +import static io.microsphere.reflect.MemberUtils.isInvalidDeclaringClass; import static io.microsphere.reflect.MemberUtils.isPrivate; import static io.microsphere.reflect.MemberUtils.isStatic; +import static io.microsphere.reflect.Modifier.BRIDGE; +import static io.microsphere.reflect.Modifier.STATIC; +import static io.microsphere.reflect.Modifier.SYNTHETIC; +import static io.microsphere.reflect.Modifier.matchesAny; import static io.microsphere.reflect.TypeUtils.isObjectClass; import static io.microsphere.text.FormatUtils.format; import static io.microsphere.util.AnnotationUtils.CALLER_SENSITIVE_ANNOTATION_CLASS; @@ -60,12 +67,13 @@ import static io.microsphere.util.ArrayUtils.arrayEquals; import static io.microsphere.util.ArrayUtils.arrayToString; import static io.microsphere.util.ArrayUtils.length; +import static io.microsphere.util.Assert.assertNotNull; import static io.microsphere.util.ClassLoaderUtils.resolveClass; import static io.microsphere.util.ClassUtils.getAllInheritedTypes; import static io.microsphere.util.ClassUtils.getTypeName; -import static io.microsphere.util.ClassUtils.getTypes; import static io.microsphere.util.ClassUtils.isArray; -import static io.microsphere.util.ClassUtils.isPrimitive; +import static io.microsphere.util.ClassUtils.isInterface; +import static io.microsphere.util.ExceptionUtils.wrap; import static io.microsphere.util.StringUtils.split; import static io.microsphere.util.StringUtils.startsWith; import static io.microsphere.util.StringUtils.substringBefore; @@ -110,6 +118,10 @@ public abstract class MethodUtils implements Utils { * method = MethodUtils.findMethod(String.class, "substring", int.class); // returns non-null * } */ + @ConfigurationProperty( + type = String[].class, + source = SYSTEM_PROPERTIES_SOURCE + ) public static final String BANNED_METHODS_PROPERTY_NAME = MICROSPHERE_PROPERTY_NAME_PREFIX + "reflect.banned-methods"; /** @@ -156,7 +168,7 @@ public abstract class MethodUtils implements Utils { */ public final static Predicate NON_PRIVATE_METHOD_PREDICATE = MemberUtils::isNonPrivate; - private final static ConcurrentMap methodsCache = new ConcurrentHashMap<>(256); + private final static ConcurrentMap methodsCache = newConcurrentHashMap(256); /** * The cache to store the methods to be banned by the {@link #buildSignature(Class, String, Class[]) signatures}. @@ -169,14 +181,13 @@ public abstract class MethodUtils implements Utils { * method = MethodUtils.findMethod(String.class, "substring", int.class); // returns non-null * } */ - @ConfigurationProperty( - name = BANNED_METHODS_PROPERTY_NAME, - type = String[].class, - source = SYSTEM_PROPERTIES_SOURCE - ) - private final static ConcurrentMap bannedMethodsCache = new ConcurrentHashMap<>(16); + private final static ConcurrentMap bannedMethodsCache = newConcurrentHashMap(16); - private static final ConcurrentMap, Method[]> declaredMethodsCache = new ConcurrentHashMap<>(256); + private static final ConcurrentMap, Method[]> declaredMethodsCache = newConcurrentHashMap(256); + + static { + initBannedMethods(); + } /** * Initializes the banned methods cache based on the system property {@value #BANNED_METHODS_PROPERTY_NAME}. @@ -249,36 +260,14 @@ public static void initBannedMethods() { * @see #initBannedMethods() * @see #bannedMethodsCache */ - public static Method banMethod(Class declaredClass, String methodName, Class... parameterTypes) { + @Nonnull + public static Method banMethod(@Nonnull Class declaredClass, @Nonnull String methodName, @Nonnull Class... parameterTypes) { MethodKey key = buildKey(declaredClass, methodName, parameterTypes); Method method = methodsCache.computeIfAbsent(key, MethodUtils::doFindMethod); bannedMethodsCache.put(key, method); return method; } - /** - * Clears the cache of banned methods. - * - *

This method removes all entries from the {@link #bannedMethodsCache}, effectively - * allowing all previously banned methods to be discoverable again by {@link #findMethod(Class, String, Class[])}.

- * - *

Example Usage

- *
{@code
-     * // Clear all banned methods
-     * MethodUtils.clearBannedMethods();
-     *
-     * // After this call, findMethod will return previously banned methods
-     * Method method = MethodUtils.findMethod(String.class, "substring", int.class, int.class);
-     * // method may now be non-null if it was previously banned
-     * }
- * - * @see #initBannedMethods() - * @see #bannedMethodsCache - */ - public static void clearBannedMethods() { - bannedMethodsCache.clear(); - } - /** * Creates a {@link Predicate} that excludes methods declared by the specified class. * @@ -296,7 +285,7 @@ public static void clearBannedMethods() { * @return a non-null {@link Predicate} that evaluates to {@code true} for methods not declared by the given class */ @Nonnull - public static Predicate excludedDeclaredClass(Class declaredClass) { + public static Predicate excludedDeclaredClass(@Nullable Class declaredClass) { return method -> !Objects.equals(declaredClass, method.getDeclaringClass()); } @@ -318,7 +307,7 @@ public static Predicate excludedDeclaredClass(Class declaredC */ @Nonnull @Immutable - public static List getDeclaredMethods(Class targetClass) { + public static List getDeclaredMethods(@Nullable Class targetClass) { return findDeclaredMethods(targetClass, EMPTY_PREDICATE_ARRAY); } @@ -340,7 +329,7 @@ public static List getDeclaredMethods(Class targetClass) { */ @Nonnull @Immutable - public static List getMethods(Class targetClass) { + public static List getMethods(@Nullable Class targetClass) { return findMethods(targetClass, EMPTY_PREDICATE_ARRAY); } @@ -362,7 +351,7 @@ public static List getMethods(Class targetClass) { */ @Nonnull @Immutable - public static List getAllDeclaredMethods(Class targetClass) { + public static List getAllDeclaredMethods(@Nullable Class targetClass) { return findAllDeclaredMethods(targetClass, EMPTY_PREDICATE_ARRAY); } @@ -387,7 +376,7 @@ public static List getAllDeclaredMethods(Class targetClass) { */ @Nonnull @Immutable - public static List getAllMethods(Class targetClass) { + public static List getAllMethods(@Nullable Class targetClass) { return findAllMethods(targetClass, EMPTY_PREDICATE_ARRAY); } @@ -417,7 +406,7 @@ public static List getAllMethods(Class targetClass) { */ @Nonnull @Immutable - public static List findDeclaredMethods(Class targetClass, Predicate... methodsToFilter) { + public static List findDeclaredMethods(@Nullable Class targetClass, @Nullable Predicate... methodsToFilter) { return findMethods(targetClass, false, false, methodsToFilter); } @@ -447,7 +436,7 @@ public static List findDeclaredMethods(Class targetClass, Predicate findMethods(Class targetClass, Predicate... methodsToFilter) { + public static List findMethods(@Nullable Class targetClass, @Nullable Predicate... methodsToFilter) { return findMethods(targetClass, false, true, methodsToFilter); } @@ -477,7 +466,7 @@ public static List findMethods(Class targetClass, Predicate findAllDeclaredMethods(Class targetClass, Predicate... methodsToFilter) { + public static List findAllDeclaredMethods(@Nullable Class targetClass, @Nullable Predicate... methodsToFilter) { return findMethods(targetClass, true, false, methodsToFilter); } @@ -507,7 +496,7 @@ public static List findAllDeclaredMethods(Class targetClass, Predicat */ @Nonnull @Immutable - public static List findAllMethods(Class targetClass, Predicate... methodsToFilter) { + public static List findAllMethods(@Nullable Class targetClass, @Nullable Predicate... methodsToFilter) { return findMethods(targetClass, true, true, methodsToFilter); } @@ -547,10 +536,10 @@ public static List findAllMethods(Class targetClass, Predicate findMethods(Class targetClass, boolean includeInheritedTypes, boolean publicOnly, - Predicate... methodsToFilter) { + public static List findMethods(@Nullable Class targetClass, boolean includeInheritedTypes, boolean publicOnly, + @Nullable Predicate... methodsToFilter) { - if (targetClass == null || isPrimitive(targetClass)) { + if (isInvalidDeclaringClass(targetClass)) { return emptyList(); } @@ -605,7 +594,7 @@ public static List findMethods(Class targetClass, boolean includeInhe * @return the found method, or {@code null} if no matching method is found */ @Nullable - public static Method findMethod(Class targetClass, String methodName) { + public static Method findMethod(@Nullable Class targetClass, @Nullable String methodName) { return findMethod(targetClass, methodName, EMPTY_CLASS_ARRAY); } @@ -641,7 +630,7 @@ public static Method findMethod(Class targetClass, String methodName) { * @return the found method, or {@code null} if no matching method is found */ @Nullable - public static Method findMethod(Class targetClass, String methodName, Class... parameterTypes) { + public static Method findMethod(@Nullable Class targetClass, @Nullable String methodName, @Nullable Class... parameterTypes) { MethodKey key = buildKey(targetClass, methodName, parameterTypes); return bannedMethodsCache.containsKey(key) ? null : methodsCache.computeIfAbsent(key, MethodUtils::doFindMethod); } @@ -670,21 +659,19 @@ public static Method findMethod(Class targetClass, String methodName, Class.. * @return the found method, or {@code null} if no matching method is found */ @Nullable - public static Method findDeclaredMethod(Class targetClass, String methodName, Class... parameterTypes) { - - if (targetClass == null) { + public static Method findDeclaredMethod(@Nullable Class targetClass, @Nullable String methodName, @Nullable Class... parameterTypes) { + if (isInvalidDeclaringClass(targetClass)) { return null; } - // First, try to find the declared method in directly target class Method method = doFindDeclaredMethod(targetClass, methodName, parameterTypes); - - if (method == null) { // Second, to find the declared method in the super class + // Second, to find the declared method in the super class + if (method == null) { Class superClass = targetClass.isInterface() ? Object.class : targetClass.getSuperclass(); method = findDeclaredMethod(superClass, methodName, parameterTypes); } - - if (method == null) { // Third, to find the declared method in the interfaces + // Third, to find the declared method in the interfaces + if (method == null) { for (Class interfaceClass : targetClass.getInterfaces()) { method = findDeclaredMethod(interfaceClass, methodName, parameterTypes); if (method != null) { @@ -692,14 +679,13 @@ public static Method findDeclaredMethod(Class targetClass, String methodName, } } } - + // Finally, log if the method was not found if (method == null) { if (logger.isTraceEnabled()) { logger.trace("The declared method was not found in the target class[name : '{}'] by name['{}'] and parameter types['{}']", targetClass, methodName, arrayToString(parameterTypes)); } } - return method; } @@ -735,14 +721,129 @@ public static Method findDeclaredMethod(Class targetClass, String methodName, * @param The expected return type of the method. * @return The result of invoking the method, wrapped in the appropriate type. * @throws NullPointerException If the provided object is null. - * @throws IllegalStateException If the method cannot be found or accessed. - * @throws IllegalArgumentException If the arguments do not match the method's parameter types. + * @throws IllegalArgumentException If the method cannot be found, or the arguments do not match the method's + * parameter types or accessed. * @throws RuntimeException If the underlying method throws an exception during invocation. */ @Nullable - public static R invokeMethod(Object object, String methodName, Object... arguments) { - Class type = object.getClass(); - return invokeMethod(object, type, methodName, arguments); + public static R invokeMethod(@Nonnull Object object, @Nonnull String methodName, @Nonnull Object... arguments) { + return invokeMethod(object, object.getClass(), methodName, arguments); + } + + /** + * Invokes a method with the specified name on the given object, optionally forcing accessibility. + * + *

When {@code forceAccess} is {@code true}, this method allows invocation of non-public methods by + * setting accessible flag before invoking.

+ * + *

Example Usage

+ *
{@code
+     * class User {
+     *     private String secret() {
+     *         return "hidden";
+     *     }
+     * }
+     *
+     * User user = new User();
+     * String value = MethodUtils.invokeMethod(true, user, "secret");
+     * System.out.println(value); // Output: hidden
+     * }
+ * + * @param the expected return type + * @param forceAccess whether to force method accessibility + * @param object the target object + * @param methodName the method name + * @param arguments method arguments + * @return the invocation result, or {@code null} if method return type is void + */ + @Nullable + public static R invokeMethod(boolean forceAccess, @Nonnull Object object, @Nonnull String methodName, @Nonnull Object... arguments) { + return invokeMethod(object, forceAccess, object.getClass(), methodName, arguments); + } + + /** + * Invokes a method with the specified name on the given class type, using the provided arguments. + * + *

This method dynamically searches for a method in the specified class that matches the method name + * and argument types, and then invokes it. It supports both instance and static methods.

+ * + *

Example Usage

+ *
{@code
+     * public class ExampleClass {
+     *     public String greet(String name) {
+     *         return "Hello, " + name;
+     *     }
+     * }
+     *
+     * // Create an instance of ExampleClass
+     * ExampleClass exampleInstance = new ExampleClass();
+     *
+     * // Call the 'greet' method using invokeMethod
+     * String result = MethodUtils.invokeMethod(exampleInstance, ExampleClass.class, "greet", "World");
+     * System.out.println(result);  // Output: Hello, World
+     * }
+ * + *

Note: This method internally uses reflection to find and invoke the matching method, + * which may throw exceptions if the method cannot be found or invoked properly.

+ * + * @param object The object on which the method will be invoked. Can be null for static methods. + * @param type The class type to search for the method. Must not be null. + * @param methodName The name of the method to invoke. Must not be null or empty. + * @param arguments The arguments to pass to the method. Can be null or empty if the method requires no parameters. + * @param The expected return type of the method. + * @return The result of invoking the method, wrapped in the appropriate type. + * @throws IllegalArgumentException If the provided object is null or the arguments do not match the method's parameter types or accessed. + * @throws RuntimeException If the underlying method throws an exception during invocation. + */ + @Nullable + public static R invokeMethod(@Nonnull Object object, @Nonnull Class type, @Nonnull String methodName, @Nonnull Object... arguments) { + return invokeMethod(object, false, type, methodName, arguments); + } + + /** + * Invokes a method by name on the specified type, optionally forcing accessibility. + * + *

This overload is useful when the invocation target is represented by a super type or interface, + * or when private/protected methods need to be accessed via reflection.

+ * + *

Example Usage

+ *
{@code
+     * class BaseService {
+     *     protected String ping(String name) {
+     *         return "pong " + name;
+     *     }
+     * }
+     *
+     * BaseService service = new BaseService();
+     * String result = MethodUtils.invokeMethod(service, true, BaseService.class, "ping", "Microsphere");
+     * System.out.println(result); // Output: pong Microsphere
+     * }
+ * + * @param the expected return type + * @param object the target object (can be {@code null} for static methods) + * @param forceAccess whether to force method accessibility + * @param type the type used to resolve the method + * @param methodName the method name + * @param arguments method arguments + * @return the invocation result, or {@code null} if method return type is void + * @throws IllegalArgumentException if the method cannot be accessed with provided arguments + * @throws RuntimeException if the underlying method throws an exception + */ + @Nullable + public static R invokeMethod(@Nonnull Object object, boolean forceAccess, @Nonnull Class type, @Nonnull String methodName, @Nonnull Object... arguments) { + List allDeclaredMethods = findAllDeclaredMethods(type, method -> { + if (Objects.equals(methodName, method.getName()) && matchParameterTypes(method, arguments)) { + return true; + } + return false; + }); + if (allDeclaredMethods.isEmpty()) { + String message = format("No method was not matched by the declared type : {} , name : '{}' , arguments : {}", + type, methodName, arrayToString(arguments)); + throw new IllegalArgumentException(message); + } + Method method = allDeclaredMethods.get(0); + return invokeMethod(forceAccess, object, method, arguments); } /** @@ -770,14 +871,46 @@ public static R invokeMethod(Object object, String methodName, Object... arg * @param arguments the arguments to pass to the method (can be null or empty) * @param the expected return type of the method * @return the result of the method invocation, wrapped in the appropriate type - * @throws NullPointerException if the provided target class or method name is null - * @throws IllegalStateException if the method cannot be found or accessed - * @throws IllegalArgumentException if the arguments do not match the method's parameter types + * @throws NullPointerException if the provided target class or method name is null or the method cannot be found. + * @throws IllegalArgumentException if the arguments do not match the method's parameter types or accessed * @throws RuntimeException if the underlying method throws an exception during invocation */ @Nullable - public static R invokeStaticMethod(Class targetClass, String methodName, Object... arguments) { - return invokeMethod(null, targetClass, methodName, arguments); + public static R invokeStaticMethod(@Nonnull Class targetClass, @Nonnull String methodName, @Nonnull Object... arguments) { + return invokeStaticMethod(false, targetClass, methodName, arguments); + } + + /** + * Invokes a static method of the specified class by name, with optional forced accessibility. + * + *

When {@code forceAccess} is {@code true}, this method attempts to make the target method + * accessible before invocation, allowing reflective access to non-public static methods.

+ * + *

Example Usage

+ *
{@code
+     * class SecretMath {
+     *     private static int sum(int a, int b) {
+     *         return a + b;
+     *     }
+     * }
+     *
+     * Integer value = MethodUtils.invokeStaticMethod(true, SecretMath.class, "sum", 1, 2);
+     * System.out.println(value); // Output: 3
+     * }
+ * + * @param the expected return type + * @param forceAccess whether to force accessibility for non-public methods + * @param targetClass the class declaring the static method + * @param methodName the name of the static method to invoke + * @param arguments the arguments passed to the method + * @return the invocation result, or {@code null} if the method return type is void + * @throws NullPointerException if {@code targetClass} or {@code methodName} is null, or if method lookup fails + * @throws IllegalArgumentException if the arguments do not match the method signature + * @throws RuntimeException if the underlying method throws an exception + */ + @Nullable + public static R invokeStaticMethod(boolean forceAccess, @Nonnull Class targetClass, @Nonnull String methodName, @Nonnull Object... arguments) { + return invokeMethod(null, forceAccess, targetClass, methodName, arguments); } /** @@ -806,61 +939,43 @@ public static R invokeStaticMethod(Class targetClass, String methodName, * @param arguments the arguments to pass to the method (can be null or empty) * @param the expected return type of the method * @return the result of the method invocation, wrapped in the appropriate type - * @throws NullPointerException if the provided method is null - * @throws IllegalStateException if the method cannot be accessed or throws an exception during invocation - * @throws IllegalArgumentException if the arguments do not match the method's parameter types + * @throws IllegalArgumentException if the provided method is null or the arguments do not match the method's parameter types or accessed. */ @Nullable - public static R invokeStaticMethod(Method method, Object... arguments) { - return invokeMethod(null, method, arguments); + public static R invokeStaticMethod(@Nonnull Method method, @Nonnull Object... arguments) { + return invokeStaticMethod(false, method, arguments); } /** - * Invokes a method with the specified name on the given class type, using the provided arguments. + * Invokes a static method represented by a {@link Method} with optional forced accessibility. * - *

This method dynamically searches for a method in the specified class that matches the method name - * and argument types, and then invokes it. It supports both instance and static methods.

+ *

If {@code forceAccess} is {@code true}, this method tries to enable access to non-public + * static methods before invocation.

* *

Example Usage

*
{@code
-     * public class ExampleClass {
-     *     public String greet(String name) {
-     *         return "Hello, " + name;
+     * class Internal {
+     *     private static String echo(String value) {
+     *         return value;
      *     }
      * }
      *
-     * // Create an instance of ExampleClass
-     * ExampleClass exampleInstance = new ExampleClass();
-     *
-     * // Call the 'greet' method using invokeMethod
-     * String result = MethodUtils.invokeMethod(exampleInstance, ExampleClass.class, "greet", "World");
-     * System.out.println(result);  // Output: Hello, World
+     * Method method = Internal.class.getDeclaredMethod("echo", String.class);
+     * String result = MethodUtils.invokeStaticMethod(method, true, "Microsphere");
+     * System.out.println(result); // Output: Microsphere
      * }
* - *

Note: This method internally uses reflection to find and invoke the matching method, - * which may throw exceptions if the method cannot be found or invoked properly.

- * - * @param instance The object on which the method will be invoked. Can be null for static methods. - * @param type The class type to search for the method. Must not be null. - * @param methodName The name of the method to invoke. Must not be null or empty. - * @param arguments The arguments to pass to the method. Can be null or empty if the method requires no parameters. - * @param The expected return type of the method. - * @return The result of invoking the method, wrapped in the appropriate type. - * @throws NullPointerException If the provided type or method name is null. - * @throws IllegalStateException If the method cannot be found or accessed. - * @throws IllegalArgumentException If the arguments do not match the method's parameter types. - * @throws RuntimeException If the underlying method throws an exception during invocation. + * @param the expected return type + * @param forceAccess whether to force accessibility for non-public methods + * @param method the static method to invoke + * @param arguments the arguments passed to the method + * @return the invocation result, or {@code null} if the method return type is void + * @throws IllegalArgumentException if the method is null, non-static, or arguments do not match + * @throws RuntimeException if the underlying method throws an exception */ @Nullable - public static R invokeMethod(Object instance, Class type, String methodName, Object... arguments) { - Class[] parameterTypes = getTypes(arguments); - Method method = findMethod(type, methodName, parameterTypes); - - if (method == null) { - throw new IllegalStateException(format("cannot find method[name : '{}'], class: '{}'", methodName, type.getName())); - } - - return invokeMethod(instance, method, arguments); + public static R invokeStaticMethod(boolean forceAccess, @Nonnull Method method, @Nonnull Object... arguments) { + return invokeMethod(forceAccess, null, method, arguments); } /** @@ -923,11 +1038,7 @@ public static R invokeMethod(Object instance, Class type, String methodNa * @return the result of dispatching the method represented by * this object on {@code instance} with parameters * {@code arguments} - * @throws NullPointerException if this {@link Method} object is null - * @throws IllegalStateException if this {@code Method} object - * is enforcing Java language access control and the underlying - * method is inaccessible. - * @throws IllegalArgumentException if the method is an + * @throws IllegalArgumentException if the method is null or is an * instance method and the specified object argument * is not an instance of the class or interface * declaring the underlying method (or of a subclass @@ -937,29 +1048,78 @@ public static R invokeMethod(Object instance, Class type, String methodNa * after possible unwrapping, a parameter value * cannot be converted to the corresponding formal * parameter type by a method invocation conversion. - * @throws RuntimeException if the underlying method - * throws an exception. + * @throws RuntimeException if the underlying method throws an exception. */ @Nullable - public static R invokeMethod(@Nullable Object instance, Method method, Object... arguments) { - if (method == null) { - throw new NullPointerException("The 'method' must not be null"); - } + public static R invokeMethod(@Nullable Object instance, @Nonnull Method method, @Nonnull Object... arguments) { + return invokeMethod(false, instance, method, arguments); + } + + /** + * Invokes the given {@link Method} on the specified instance, optionally forcing reflective access. + * + *

This overload is the core reflective invocation path used by other utility methods in this class. + * If {@code forceAccess} is {@code true}, it attempts to make the method accessible before invoking, + * which allows calling non-public methods when the runtime permits it.

+ * + *

Example Usage

+ *
{@code
+     * class Demo {
+     *     private String hello(String name) {
+     *         return "Hello, " + name;
+     *     }
+     * }
+     *
+     * Demo demo = new Demo();
+     * Method hello = Demo.class.getDeclaredMethod("hello", String.class);
+     * String value = MethodUtils.invokeMethod(true, demo, hello, "Microsphere");
+     * System.out.println(value); // Output: Hello, Microsphere
+     * }
+ * + *
{@code
+     * class MathUtil {
+     *     static int sum(int a, int b) {
+     *         return a + b;
+     *     }
+     * }
+     *
+     * Method sum = MathUtil.class.getDeclaredMethod("sum", int.class, int.class);
+     * Integer result = MethodUtils.invokeMethod(true, null, sum, 1, 2);
+     * System.out.println(result); // Output: 3
+     * }
+ * + * @param the expected return type + * @param forceAccess whether to force accessibility before invocation + * @param instance the target instance for instance methods; may be {@code null} for static methods + * @param method the method to invoke, must not be {@code null} + * @param arguments the invocation arguments (can be empty) + * @return the invocation result, or {@code null} when the method return type is {@code void} + * @throws IllegalArgumentException if the method cannot be accessed with provided arguments + * @throws RuntimeException if the underlying method throws an exception + */ + @Nullable + public static R invokeMethod(boolean forceAccess, @Nullable Object instance, @Nonnull Method method, @Nonnull Object... arguments) { + assertNotNull(method, () -> "The 'method' must not be null"); R result = null; RuntimeException failure = null; + boolean trySetAccessible = false; try { - trySetAccessible(method); + if (forceAccess) { + trySetAccessible = trySetAccessible(method); + } result = (R) method.invoke(instance, arguments); } catch (IllegalAccessException | IllegalArgumentException e) { - String errorMessage = format("The arguments can't match the method[signature : '{}' , instance : {}] : {}", getSignature(method), instance, arrayToString(arguments)); - failure = new IllegalArgumentException(errorMessage, e); + failure = wrap(e, IllegalArgumentException.class); } catch (InvocationTargetException e) { - String errorMessage = format("It's failed to invoke the method[signature : '{}' , instance : {} , arguments : {}]", getSignature(method), instance, arrayToString(arguments)); - failure = new RuntimeException(errorMessage, e.getTargetException()); + failure = wrap(e.getTargetException(), RuntimeException.class); + } finally { + if (logger.isTraceEnabled()) { + logger.trace("Invoked the method[signature : '{}' , forceAccess : {} , trySetAccessible : {} , instance : {} , arguments : {}] : {}", + getSignature(method), forceAccess, trySetAccessible, instance, arrayToString(arguments), result, failure); + } } if (failure != null) { - logger.error(failure.getMessage(), failure.getCause()); throw failure; } @@ -1006,7 +1166,7 @@ public static R invokeMethod(@Nullable Object instance, Method method, Objec * @jls 8.4.8 Inheritance, Overriding, and Hiding * @jls 9.4.1 Inheritance and Overriding */ - public static boolean overrides(Method overrider, Method overridden) { + public static boolean overrides(@Nullable Method overrider, @Nullable Method overridden) { if (overrider == null || overridden == null || overrider == overridden) { return false; @@ -1096,9 +1256,10 @@ public static boolean overrides(Method overrider, Method overridden) { * * @param overrider the method that potentially overrides another method * @return the overridden method if found; otherwise, {@code null} + * @throws NullPointerException if the provided method is null */ @Nullable - public static Method findNearestOverriddenMethod(Method overrider) { + public static Method findNearestOverriddenMethod(@Nonnull Method overrider) { Class targetClass = overrider.getDeclaringClass(); Method overriddenMethod = null; for (Class inheritedType : getAllInheritedTypes(targetClass)) { @@ -1150,9 +1311,72 @@ public static Method findNearestOverriddenMethod(Method overrider) { * @return the overridden method declared in the target class, or {@code null} if none is found */ @Nullable - public static Method findOverriddenMethod(Method overrider, Class targetClass) { + public static Method findOverriddenMethod(@Nullable Method overrider, @Nullable Class targetClass) { List matchedMethods = findDeclaredMethods(targetClass, method -> overrides(overrider, method)); - return matchedMethods.isEmpty() ? null : matchedMethods.get(0); + return first(matchedMethods); + } + + /** + * Finds the single abstract method (SAM) of a functional interface. + * + *

This method checks if the provided {@code type} is an interface and not annotated with + * {@link FunctionalInterface}. It then searches for a single abstract method that is not static, + * default, synthetic, or a bridge method. If such a method is found, it is returned; otherwise, + * this method returns {@code null}.

+ * + *

Example Usage

+ *
{@code
+     * @FunctionalInterface
+     * interface MyFunctionalInterface {
+     *     void execute();
+     * }
+     *
+     * Method sam = MethodUtils.findFunctionalInterfaceMethod(MyFunctionalInterface.class);
+     * if (sam != null) {
+     *     System.out.println("Found SAM: " + sam.getName());
+     * } else {
+     *     System.out.println("No SAM found.");
+     * }
+     * }
+ * + * @param type the class to inspect for a functional interface method + * @return the single abstract method if found; otherwise, {@code null} + */ + @Nullable + public static Method findFunctionalInterfaceMethod(@Nonnull Class type) { + if (!isInterface(type)) { + return null; + } + + Method[] methods = type.getMethods(); + int length = methods.length; + if (length == 0) { + return null; + } + + int count = 0; + int index = -1; + + for (int i = 0; i < length; i++) { + Method method = methods[i]; + if (isFunctionalInterfaceMethod(method)) { + if (count > 0) { // More than one functional interface method found, return null immediately + return null; + } + count++; + index = i; + } + } + + return index == -1 ? null : methods[index]; + } + + static boolean isFunctionalInterfaceMethod(Method method) { + int modifiers = method.getModifiers(); + if (matchesAny(modifiers, STATIC, BRIDGE, SYNTHETIC) || method.isDefault() || isOverridenObjectMethod(method)) { + return false; + } + return true; } /** @@ -1170,9 +1394,10 @@ public static Method findOverriddenMethod(Method overrider, Class targetClass * * @param method The method for which to generate the signature. * @return A non-null string representing the method signature. + * @throws NullPointerException if the provided method is null */ @Nonnull - public static String getSignature(Method method) { + public static String getSignature(@Nonnull Method method) { return buildSignature(method.getDeclaringClass(), method.getName(), method.getParameterTypes()); } @@ -1197,8 +1422,9 @@ public static String getSignature(Method method) { * @param methodName the name of the method * @param parameterTypes the parameter types of the method * @return a non-null string representing the method signature + * @throws NullPointerException if any of the provided arguments are null */ - public static String buildSignature(Class declaringClass, String methodName, Class... parameterTypes) { + public static String buildSignature(@Nonnull Class declaringClass, @Nonnull String methodName, @Nonnull Class... parameterTypes) { int parameterCount = length(parameterTypes); String[] parameterTypeNames = new String[parameterCount]; String declaringClassName = getTypeName(declaringClass); @@ -1255,13 +1481,38 @@ public static String buildSignature(Class declaringClass, String methodName, * @param method the method to check, may be null * @return true if the method is declared by the {@link Object} class; false otherwise or if the method is null */ - public static boolean isObjectMethod(Method method) { + public static boolean isObjectMethod(@Nullable Method method) { if (method != null) { return isObjectClass(method.getDeclaringClass()); } return false; } + /** + * Checks whether the given method overrides a method declared in the {@link Object} class. + * + *

This utility method is useful for identifying methods that provide custom implementations + * of standard {@code Object} methods (like {@code toString}, {@code equals}, etc.) in subclasses.

+ * + *

Example Usage

+ *
{@code
+     * Method toStringMethod = MyClass.class.getMethod("toString");
+     * boolean isOverridden = MethodUtils.isOverridenObjectMethod(toStringMethod);
+     * System.out.println(isOverridden); // Output: true if MyClass overrides toString, false otherwise
+     * }
+ * + * @param method the method to check, may be null + * @return true if the method overrides a method from the {@link Object} class; false otherwise or if the method is null + */ + public static boolean isOverridenObjectMethod(@Nullable Method method) { + if (method != null && method.getDeclaringClass() != Object.class && !isPrivate(method)) { + String methodName = method.getName(); + Class[] parameterTypes = method.getParameterTypes(); + return findMethod(Object.class, methodName, parameterTypes) != null; + } + return false; + } + /** * Checks if the specified method is annotated with {@link jdk.internal.reflect.CallerSensitive}. * @@ -1279,11 +1530,34 @@ public static boolean isObjectMethod(Method method) { * @return true if the method is non-null and annotated with {@link jdk.internal.reflect.CallerSensitive}; false otherwise * @see jdk.internal.reflect.CallerSensitive */ - public static boolean isCallerSensitiveMethod(Method method) { + public static boolean isCallerSensitiveMethod(@Nullable Method method) { return isAnnotationPresent(method, CALLER_SENSITIVE_ANNOTATION_CLASS); } - public static boolean isIsMethod(Method method) { + /** + * Checks if the specified method is a JavaBean "is" getter method. + * + *

A method is considered an "is" getter if it starts with "is", has no parameters, and returns a boolean type.

+ * + *

Example Usage

+ *
{@code
+     * public class Example {
+     *     private boolean active;
+     *
+     *     public boolean isActive() {
+     *         return active;
+     *     }
+     * }
+     *
+     * Method isActiveMethod = Example.class.getMethod("isActive");
+     * boolean isIsMethod = MethodUtils.isIsMethod(isActiveMethod);
+     * System.out.println(isIsMethod); // Output: true
+     * }
+ * + * @param method the method to check, may be null + * @return true if the method is an "is" getter; false otherwise or if the method is null + */ + public static boolean isIsMethod(@Nullable Method method) { if (startsWith(getMethodName(method), IS_METHOD_NAME_PREFIX)) { if (isNoArgMethod(method)) { return matchesReturnType(method, boolean.class); @@ -1292,7 +1566,30 @@ public static boolean isIsMethod(Method method) { return false; } - public static boolean isGetterMethod(Method method) { + /** + * Checks if the specified method is a JavaBean getter method. + * + *

A method is considered a getter if it starts with "get", has no parameters, and does not return void.

+ * + *

Example Usage

+ *
{@code
+     * public class Example {
+     *     private String name;
+     *
+     *     public String getName() {
+     *         return name;
+     *     }
+     * }
+     *
+     * Method getNameMethod = Example.class.getMethod("getName");
+     * boolean isGetter = MethodUtils.isGetterMethod(getNameMethod);
+     * System.out.println(isGetter); // Output: true
+     * }
+ * + * @param method the method to check, may be null + * @return true if the method is a getter; false otherwise or if the method is null + */ + public static boolean isGetterMethod(@Nullable Method method) { if (startsWith(getMethodName(method), GET_METHOD_NAME_PREFIX)) { if (isNoArgMethod(method)) { return !matchesReturnType(method, void.class); @@ -1301,7 +1598,30 @@ public static boolean isGetterMethod(Method method) { return false; } - public static boolean isSetterMethod(Method method) { + /** + * Checks if the specified method is a JavaBean setter method. + * + *

A method is considered a setter if it starts with "set", has exactly one parameter, and returns void.

+ * + *

Example Usage

+ *
{@code
+     * public class Example {
+     *     private String name;
+     *
+     *     public void setName(String name) {
+     *         this.name = name;
+     *     }
+     * }
+     *
+     * Method setNameMethod = Example.class.getMethod("setName", String.class);
+     * boolean isSetter = MethodUtils.isSetterMethod(setNameMethod);
+     * System.out.println(isSetter); // Output: true
+     * }
+ * + * @param method the method to check, may be null + * @return true if the method is a setter; false otherwise or if the method is null + */ + public static boolean isSetterMethod(@Nullable Method method) { if (startsWith(getMethodName(method), SET_METHOD_NAME_PREFIX)) { if (matchesParameterCount(method, 1)) { return matchesReturnType(method, void.class); @@ -1310,19 +1630,126 @@ public static boolean isSetterMethod(Method method) { return false; } + /** + * Retrieves the name of the specified method. + * + *

This utility method safely retrieves the name of a method, returning {@code null} if the method is {@code null}. + * It is useful for logging or debugging purposes where method names are needed without risking a {@code NullPointerException}.

+ * + *

Example Usage

+ *
{@code
+     * Method method = String.class.getMethod("substring", int.class, int.class);
+     * String methodName = MethodUtils.getMethodName(method);
+     * System.out.println(methodName); // Output: substring
+     *
+     * Method nullMethod = null;
+     * String nullMethodName = MethodUtils.getMethodName(nullMethod);
+     * System.out.println(nullMethodName); // Output: null
+     * }
+ * + * @param method the method from which to retrieve the name, may be null + * @return the name of the method, or {@code null} if the method is {@code null} + */ @Nullable public static String getMethodName(@Nullable Method method) { return method == null ? null : method.getName(); } + /** + * Checks if the specified method has the given number of parameters. + * + *

This utility method is useful for validating method signatures, especially when working with reflection + * to ensure that a method matches expected parameter counts.

+ * + *

Example Usage

+ *
{@code
+     * Method method = String.class.getMethod("substring", int.class, int.class);
+     * boolean hasTwoParameters = MethodUtils.matchesParameterCount(method, 2);
+     * System.out.println(hasTwoParameters); // Output: true
+     *
+     * Method noArgMethod = String.class.getMethod("isEmpty");
+     * boolean hasNoParameters = MethodUtils.matchesParameterCount(noArgMethod, 0);
+     * System.out.println(hasNoParameters); // Output: true
+     *
+     * Method nullMethod = null;
+     * boolean resultForNull = MethodUtils.matchesParameterCount(nullMethod, 1);
+     * System.out.println(resultForNull); // Output: false
+     * }
+ * + * @param method the method to check, may be null + * @param parameterCount the expected number of parameters + * @return true if the method has the specified number of parameters; false if the method is null or does not match + */ public static boolean matchesParameterCount(@Nullable Method method, int parameterCount) { return method == null ? false : method.getParameterCount() == parameterCount; } + /** + * Checks if the specified method has the given return type. + * + *

This utility method is useful for validating method signatures, especially when working with reflection + * to ensure that a method matches expected return types.

+ * + *

Example Usage

+ *
{@code
+     * Method method = String.class.getMethod("substring", int.class, int.class);
+     * boolean returnsString = MethodUtils.matchesReturnType(method, String.class);
+     * System.out.println(returnsString); // Output: true
+     *
+     * Method voidMethod = System.class.getMethod("gc");
+     * boolean returnsVoid = MethodUtils.matchesReturnType(voidMethod, void.class);
+     * System.out.println(returnsVoid); // Output: true
+     *
+     * Method nullMethod = null;
+     * boolean resultForNull = MethodUtils.matchesReturnType(nullMethod, Object.class);
+     * System.out.println(resultForNull); // Output: false
+     * }
+ * + * @param method the method to check, may be null + * @param returnType the expected return type, may be null + * @return true if the method has the specified return type; false if the method is null or does not match + */ public static boolean matchesReturnType(@Nullable Method method, @Nullable Class returnType) { return method == null ? false : Objects.equals(returnType, method.getReturnType()); } + /** + * Clears the cache of discovered methods. + */ + public static void clearMethodsCache() { + methodsCache.clear(); + } + + /** + * Clears the cache of banned methods. + * + *

This method removes all entries from the {@link #bannedMethodsCache}, effectively + * allowing all previously banned methods to be discoverable again by {@link #findMethod(Class, String, Class[])}.

+ * + *

Example Usage

+ *
{@code
+     * // Clear all banned methods
+     * MethodUtils.clearBannedMethodsCache();
+     *
+     * // After this call, findMethod will return previously banned methods
+     * Method method = MethodUtils.findMethod(String.class, "substring", int.class, int.class);
+     * // method may now be non-null if it was previously banned
+     * }
+ * + * @see #initBannedMethods() + * @see #bannedMethodsCache + */ + public static void clearBannedMethodsCache() { + bannedMethodsCache.clear(); + } + + /** + * Clears the cache of declared methods. + */ + public static void clearDeclaredMethodsCache() { + declaredMethodsCache.clear(); + } + static boolean isNoArgMethod(Method method) { return matchesParameterCount(method, 0); } @@ -1334,7 +1761,7 @@ static void filterDeclaredMethodsHierarchically(Class targetClass, Predicate< } } - static void filterDeclaredMethods(Class targetClass, Predicate methodToFilter, List methodsToCollect) { + static void filterDeclaredMethods(@Nullable Class targetClass, Predicate methodToFilter, List methodsToCollect) { for (Method method : doGetDeclaredMethods(targetClass)) { if (methodToFilter.test(method)) { methodsToCollect.add(method); @@ -1342,12 +1769,12 @@ static void filterDeclaredMethods(Class targetClass, Predicate klass, String methodName, Class[] parameterTypes) { + static Method doFindDeclaredMethod(@Nonnull Class klass, @Nullable String methodName, @Nullable Class[] parameterTypes) { Method[] declaredMethods = doGetDeclaredMethods(klass); return doFindMethod(declaredMethods, methodName, parameterTypes); } - static Method doFindMethod(Method[] methods, String methodName, Class[] parameterTypes) { + static Method doFindMethod(@Nonnull Method[] methods, String methodName, Class[] parameterTypes) { Method targetMethod = null; for (Method method : methods) { if (matches(method, methodName, parameterTypes)) { @@ -1362,23 +1789,27 @@ static Method doFindMethod(Method[] methods, String methodName, Class[] param return targetMethod; } - static boolean matches(Method method, String methodName, Class[] parameterTypes) { - int parameterCount = parameterTypes.length; - return parameterCount == method.getParameterCount() - && Objects.equals(method.getName(), methodName) - && matchesParameterTypes(method.getParameterTypes(), parameterTypes, parameterCount); + static boolean matches(@Nonnull Method method, @Nullable String methodName, @Nullable Class[] parameterTypes) { + if (!Objects.equals(method.getName(), methodName)) { + return false; + } + int parameterCount = length(parameterTypes); + if (!(method.getParameterCount() == parameterCount)) { + return false; + } + return matchesParameterTypes(method.getParameterTypes(), parameterTypes, parameterCount); } static boolean matchesParameterTypes(Class[] oneParameterTypes, Class[] anotherParameterTypes, int parameterCount) { for (int i = 0; i < parameterCount; i++) { - if (oneParameterTypes[i] != anotherParameterTypes[i]) { + if (!Objects.equals(oneParameterTypes[i], anotherParameterTypes[i])) { return false; } } return true; } - static Method[] doGetDeclaredMethods(Class klass) { + static Method[] doGetDeclaredMethods(@Nonnull Class klass) { return declaredMethodsCache.computeIfAbsent(klass, c -> c.getDeclaredMethods()); } @@ -1444,3 +1875,4 @@ public String toString() { private MethodUtils() { } } + diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/Modifier.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/Modifier.java index 6ea6789be..424624c72 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/Modifier.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/Modifier.java @@ -16,6 +16,8 @@ */ package io.microsphere.reflect; +import java.util.function.Supplier; + /** * The enumeration class for Java Reflection {@link java.lang.reflect.Modifier} * @@ -27,74 +29,116 @@ public enum Modifier { /** * The {@code public} modifier. + * + * @see java.lang.reflect.Modifier#PUBLIC */ PUBLIC(java.lang.reflect.Modifier.PUBLIC), /** * The {@code private} modifier. + * + * @see java.lang.reflect.Modifier#PRIVATE */ PRIVATE(java.lang.reflect.Modifier.PRIVATE), /** * The {@code protected} modifier. + * + * @see java.lang.reflect.Modifier#PROTECTED */ PROTECTED(java.lang.reflect.Modifier.PROTECTED), /** * The {@code static} modifier. + * + * @see java.lang.reflect.Modifier#STATIC */ STATIC(java.lang.reflect.Modifier.STATIC), /** * The {@code final} modifier. + * + * @see java.lang.reflect.Modifier#FINAL */ FINAL(java.lang.reflect.Modifier.FINAL), /** * The {@code synchronized} modifier. + * + * @see java.lang.reflect.Modifier#SYNCHRONIZED */ SYNCHRONIZED(java.lang.reflect.Modifier.SYNCHRONIZED), /** * The {@code volatile} modifier. + * + * @see java.lang.reflect.Modifier#VOLATILE */ VOLATILE(java.lang.reflect.Modifier.VOLATILE), /** * The {@code transient} modifier. + * + * @see java.lang.reflect.Modifier#TRANSIENT */ TRANSIENT(java.lang.reflect.Modifier.TRANSIENT), /** * The {@code native} modifier. + * + * @see java.lang.reflect.Modifier#NATIVE */ NATIVE(java.lang.reflect.Modifier.NATIVE), /** * The {@code interface} modifier. + * + * @see java.lang.reflect.Modifier#INTERFACE */ INTERFACE(java.lang.reflect.Modifier.INTERFACE), /** * The {@code abstract} modifier. + * + * @see java.lang.reflect.Modifier#ABSTRACT */ ABSTRACT(java.lang.reflect.Modifier.ABSTRACT), /** * The {@code strictfp} modifier. + * + * @see java.lang.reflect.Modifier#STRICT */ STRICT(java.lang.reflect.Modifier.STRICT), + /** + * @see java.lang.reflect.Modifier#BRIDGE + */ BRIDGE(0x00000040), + /** + * @see java.lang.reflect.Modifier#VARARGS + */ VARARGS(0x00000080), + /** + * @see java.lang.reflect.Modifier#SYNTHETIC + */ SYNTHETIC(0x00001000), + /** + * @see java.lang.reflect.Modifier#ANNOTATION + */ ANNOTATION(0x00002000), + /** + * @see java.lang.reflect.Modifier#ENUM + */ ENUM(0x00004000), + /** + * @see java.lang.reflect.Modifier#MANDATED + */ MANDATED(0x00008000); private final int value; @@ -336,4 +380,59 @@ public static boolean isMandated(int modifiers) { return MANDATED.matches(modifiers); } + /** + * Checks if the specified modifiers contain any of the specified modifiers to match. + * + * @param modifiersSupplier a supplier that provides the bit mask of modifiers to check + * @param modifiersToMatch the modifiers to match against + * @return {@code true} if any of the specified modifiers to match are present, otherwise {@code false} + */ + public static boolean matchesAny(Supplier modifiersSupplier, Modifier... modifiersToMatch) { + int modifiers = modifiersSupplier.get(); + return matchesAny(modifiers, modifiersToMatch); + } + + /** + * Checks if the specified modifiers contain any of the specified modifiers to match. + * + * @param modifiers the bit mask of modifiers to check + * @param modifiersToMatch the modifiers to match against + * @return {@code true} if any of the specified modifiers to match are present, otherwise {@code false} + */ + public static boolean matchesAny(int modifiers, Modifier... modifiersToMatch) { + for (Modifier modifier : modifiersToMatch) { + if (modifier.matches(modifiers)) { + return true; + } + } + return false; + } + + /** + * Checks if the specified modifiers contain all of the specified modifiers to match. + * + * @param modifiersSupplier a supplier that provides the bit mask of modifiers to check + * @param modifiersToMatch the modifiers to match against + * @return {@code true} if all of the specified modifiers to match are present, otherwise {@code false} + */ + public static boolean matchesAll(Supplier modifiersSupplier, Modifier... modifiersToMatch) { + int modifiers = modifiersSupplier.get(); + return matchesAll(modifiers, modifiersToMatch); + } + + /** + * Checks if the specified modifiers contain all of the specified modifiers to match. + * + * @param modifiers the bit mask of modifiers to check + * @param modifiersToMatch the modifiers to match against + * @return {@code true} if all of the specified modifiers to match are present, otherwise {@code false} + */ + public static boolean matchesAll(int modifiers, Modifier... modifiersToMatch) { + for (Modifier modifier : modifiersToMatch) { + if (!modifier.matches(modifiers)) { + return false; + } + } + return true; + } } diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java index c94d354b4..9ed3a417d 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java @@ -15,16 +15,18 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import static io.microsphere.collection.ListUtils.newArrayList; import static io.microsphere.collection.MapUtils.newLinkedHashMap; +import static io.microsphere.collection.SetUtils.newHashSet; import static io.microsphere.invoke.MethodHandlesLookupUtils.findPublicStatic; import static io.microsphere.logging.LoggerFactory.getLogger; +import static io.microsphere.reflect.FieldUtils.findAllDeclaredFields; import static io.microsphere.reflect.FieldUtils.getFieldValue; -import static io.microsphere.reflect.MemberUtils.isStatic; import static io.microsphere.reflect.MethodUtils.findMethod; import static io.microsphere.reflect.MethodUtils.invokeMethod; import static io.microsphere.reflect.MethodUtils.invokeStaticMethod; @@ -67,6 +69,9 @@ public abstract class ReflectionUtils implements Utils { */ public static final String SUN_REFLECT_REFLECTION_CLASS_NAME = "sun.reflect.Reflection"; + + public static final String SHARED_SECRETS_CLASS_NAME = "jdk.internal.access.SharedSecrets"; + /** * The {@link Class} of sun.reflect.Reflection */ @@ -442,25 +447,33 @@ public static Map readFieldsAsMap(Object object) { if (object == null) { return emptyMap(); } - Class type = object.getClass(); - Field[] fields = type.getDeclaredFields(); - LinkedHashMap fieldsAsMap = newLinkedHashMap(fields.length); - for (Field field : fields) { - - if (isStatic(field)) { // To filter static fields - continue; - } + Set visitedInstances = newHashSet(); + return readFieldsAsMap(object, visitedInstances); + } + static Map readFieldsAsMap(Object object, Set visitedInstances) { + if (object == null) { + return emptyMap(); + } + Class type = object.getClass(); + Set allDeclaredFields = findAllDeclaredFields(type, MemberUtils::isNonStatic); + visitedInstances.add(object); + LinkedHashMap fieldsAsMap = newLinkedHashMap(allDeclaredFields.size()); + for (Field field : allDeclaredFields) { String fieldName = field.getName(); - Object fieldValue = getFieldValue(object, field); Class fieldValueType = field.getType(); - if (fieldValue != object) { - if (!isPrimitive(fieldValueType) && !isSimpleType(fieldValueType) - && !object.getClass().equals(fieldValueType)) { - fieldValue = readFieldsAsMap(fieldValue); + Object fieldValue = getFieldValue(true, object, field); + if (fieldValue != null) { + if (!visitedInstances.add(fieldValue)) { + continue; } - fieldsAsMap.put(fieldName, fieldValue); } + + if (!isPrimitive(fieldValueType) && !isSimpleType(fieldValueType)) { + fieldValue = readFieldsAsMap(fieldValue, visitedInstances); + } + + fieldsAsMap.put(fieldName, fieldValue); } return unmodifiableMap(fieldsAsMap); } diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java index 6641bb58a..4561e105e 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java @@ -1216,7 +1216,7 @@ public static Set> getAllLoadedClasses(@Nullable ClassLoader classLoade public static Set> getLoadedClasses(@Nullable ClassLoader classLoader) throws UnsupportedOperationException { ClassLoader actualClassLoader = nullSafeClassLoader(classLoader); Field field = findField(classLoaderClass, classesFieldName); - List> classes = getFieldValue(actualClassLoader, field); + List> classes = getFieldValue(true, actualClassLoader, field); return ofSet(classes); } @@ -1850,7 +1850,7 @@ static Class invokeFindLoadedClassMethod(ClassLoader classLoader, String clas Class loadedClass = null; try { Method findLoadedClassMethod = findMethod(ClassLoader.class, findLoadedClassMethodName, String.class); - loadedClass = invokeMethod(classLoader, findLoadedClassMethod, className); + loadedClass = invokeMethod(true, classLoader, findLoadedClassMethod, className); } catch (Throwable e) { logOnFindLoadedClassInvocationFailed(classLoader, className, e); } diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/ClassUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/ClassUtils.java index 7ee674dd2..8bf41404d 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/ClassUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/ClassUtils.java @@ -15,7 +15,6 @@ import io.microsphere.reflect.ConstructorUtils; import java.io.File; -import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; import java.math.BigDecimal; import java.math.BigInteger; @@ -57,12 +56,10 @@ import static io.microsphere.lang.function.Predicates.EMPTY_PREDICATE_ARRAY; import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.net.URLUtils.resolveProtocol; -import static io.microsphere.reflect.ConstructorUtils.findDeclaredConstructors; +import static io.microsphere.reflect.MethodUtils.findFunctionalInterfaceMethod; import static io.microsphere.reflect.Modifier.isAnnotation; import static io.microsphere.reflect.Modifier.isSynthetic; -import static io.microsphere.text.FormatUtils.format; import static io.microsphere.util.ArrayUtils.EMPTY_CLASS_ARRAY; -import static io.microsphere.util.ArrayUtils.arrayToString; import static io.microsphere.util.ArrayUtils.isEmpty; import static io.microsphere.util.ArrayUtils.length; import static io.microsphere.util.ArrayUtils.ofArray; @@ -77,7 +74,6 @@ import static java.lang.Boolean.FALSE; import static java.lang.Boolean.TRUE; import static java.lang.reflect.Modifier.isAbstract; -import static java.lang.reflect.Modifier.isInterface; import static java.util.Collections.addAll; import static java.util.Collections.emptyList; import static java.util.Collections.emptySet; @@ -105,6 +101,11 @@ public abstract class ClassUtils implements Utils { @Immutable public static final String ARRAY_SUFFIX = "[]"; + /** + * Prefix for lambda class names: "$$Lambda" + */ + public static final String LAMBDA_CLASS_NAME_PREFIX = "$$Lambda"; + @Nonnull @Immutable private static final Class[] PRIMITIVE_TYPES_ARRAY = ofArray( @@ -419,6 +420,24 @@ public static boolean isAbstractClass(@Nullable Class type) { return isGeneralClass(type, TRUE); } + /** + * Checks if the specified type is an interface. + * + *

Example Usage

+ *
{@code
+     * boolean result1 = ClassUtils.isInterface(List.class);    // returns true
+     * boolean result2 = ClassUtils.isInterface(String.class);  // returns false
+     * boolean result3 = ClassUtils.isInterface(null);          // returns false
+     * }
+ * + * @param type the type to check, may be {@code null} + * @return {@code true} if the specified type is an interface, {@code false} otherwise + * @see Class#isInterface() + */ + public static boolean isInterface(@Nullable Class type) { + return type != null && type.isInterface(); + } + /** * Checks if the specified type is a general class. *

@@ -471,7 +490,7 @@ protected static boolean isGeneralClass(@Nullable Class type, @Nullable Boole int mod = type.getModifiers(); if (isAnnotation(mod) || io.microsphere.reflect.Modifier.isEnum(mod) - || isInterface(mod) + || io.microsphere.reflect.Modifier.isInterface(mod) || isSynthetic(mod) || isPrimitive(type) || isArray(type)) { @@ -518,6 +537,175 @@ public static boolean isTopLevelClass(@Nullable Class type) { return type != null && !type.isLocalClass() && !type.isMemberClass(); } + /** + * Checks if the specified object is a synthetic class. + *

+ * A synthetic class is a class that is generated by the Java compiler and does not have a corresponding source code representation. + * This method returns {@code true} if the given object's class is marked as synthetic, and {@code false} otherwise. + * It also returns {@code false} if the object is {@code null}. + *

+ * + *

Example Usage

+ *
{@code
+     * // Assuming MyClass has an inner class or lambda expression
+     * Object syntheticObject = new MyClass$1(); // Example of a synthetic class instance
+     *
+     * boolean result1 = ClassUtils.isSyntheticClass(syntheticObject); // returns true
+     * boolean result2 = ClassUtils.isSyntheticClass(new MyClass());   // returns false
+     * boolean result3 = ClassUtils.isSyntheticClass(null);            // returns false
+     * }
+ * + * @param object the object to check, may be {@code null} + * @return {@code true} if the specified object's class is a synthetic class, {@code false} otherwise + */ + public static boolean isSyntheticClass(@Nullable Object object) { + return isSyntheticClass(getType(object)); + } + + /** + * Checks if the specified class is a synthetic class. + *

+ * A synthetic class is a class that is generated by the Java compiler and does not have a corresponding source code representation. + * This method returns {@code true} if the given class is marked as synthetic, and {@code false} otherwise. + * It also returns {@code false} if the class is {@code null}. + *

+ * + *

Example Usage

+ *
{@code
+     * // Assuming MyClass has an inner class or lambda expression
+     * Class syntheticClass = MyClass$1.class; // Example of a synthetic class
+     *
+     * boolean result1 = ClassUtils.isSyntheticClass(syntheticClass); // returns true
+     * boolean result2 = ClassUtils.isSyntheticClass(MyClass.class);  // returns false
+     * boolean result3 = ClassUtils.isSyntheticClass(null);           // returns false
+     * }
+ * + * @param type the class to check, may be {@code null} + * @return {@code true} if the specified class is a synthetic class, {@code false} otherwise + * @see Class#isSynthetic() + */ + public static boolean isSyntheticClass(@Nullable Class type) { + return type != null && type.isSynthetic(); + } + + /** + * Checks if the specified object is a lambda class. + *

+ * A lambda class is a synthetic class generated by the Java compiler to represent a lambda expression. + * This method returns {@code true} if the given object's class is marked as synthetic and its name contains the "$$Lambda/" pattern, + * indicating that it is a lambda class. It returns {@code false} otherwise, including for non-synthetic classes or classes + * that do not follow the naming convention for lambda classes. It also returns {@code false} if the object is {@code null}. + *

+ * + *

Example Usage

+ *
{@code
+     * // Assuming MyClass has a lambda expression
+     * Object lambdaObject = (Runnable) () -> System.out.println("Hello, Lambda!");
+     *
+     * boolean result1 = ClassUtils.isLambdaClass(lambdaObject); // returns true
+     * boolean result2 = ClassUtils.isLambdaClass(new MyClass()); // returns false
+     * boolean result3 = ClassUtils.isLambdaClass(null);          // returns false
+     * }
+ * + * @param object the object to check, may be {@code null} + * @return {@code true} if the specified object's class is a lambda class, {@code false} otherwise + */ + public static boolean isLambdaClass(@Nullable Object object) { + return isLambdaClass(getType(object)); + } + + /** + * Checks if the specified class is a lambda class. + *

+ * A lambda class is a synthetic class generated by the Java compiler to represent a lambda expression. + * This method returns {@code true} if the given class is marked as synthetic and its name contains the "$$Lambda/" pattern, + * indicating that it is a lambda class. It returns {@code false} otherwise, including for non-synthetic classes or classes + * that do not follow the naming convention for lambda classes. It also returns {@code false} if the class is {@code null}. + *

+ * + *

Example Usage

+ *
{@code
+     * // Assuming MyClass has a lambda expression
+     * Class lambdaClass = MyClass$$Lambda$1.class; // Example of a lambda class
+     *
+     * boolean result1 = ClassUtils.isLambdaClass(lambdaClass); // returns true
+     * boolean result2 = ClassUtils.isLambdaClass(MyClass.class); // returns false
+     * boolean result3 = ClassUtils.isLambdaClass(null);          // returns false
+     * }
+ * + * @param type the class to check, may be {@code null} + * @return {@code true} if the specified class is a lambda class, {@code false} otherwise + */ + public static boolean isLambdaClass(@Nullable Class type) { + if (!isSyntheticClass(type)) { + return false; + } + String typeName = type.getName(); + return isLambdaClassName(typeName); + } + + /** + * Checks if the specified class name is a lambda class name. + *

+ * A lambda class name is a synthetic class name generated by the Java compiler to represent a lambda expression. + * This method returns {@code true} if the given class name contains the "$$Lambda/" pattern, + * indicating that it is a lambda class name. It returns {@code false} otherwise, including for non-synthetic class names + * or class names that do not follow the naming convention for lambda classes. It also returns {@code false} if the class name is {@code null}. + *

+ * + *

Example Usage

+ *
{@code
+     * String lambdaClassName = "com.example.MyClass$$Lambda$1"; // Example of a lambda class name
+     *
+     * boolean result1 = ClassUtils.isLambdaClassName(lambdaClassName); // returns true
+     * boolean result2 = ClassUtils.isLambdaClassName("com.example.MyClass"); // returns false
+     * boolean result3 = ClassUtils.isLambdaClassName(null);          // returns false
+     * }
+ * + * @param className the class name to check, may be {@code null} + * @return {@code true} if the specified class name is a lambda class name, {@code false} otherwise + */ + public static boolean isLambdaClassName(@Nullable String className) { + return substringBetween(className, LAMBDA_CLASS_NAME_PREFIX, SLASH) != null; + } + + /** + * Checks if the specified class is a functional interface. + *

+ * A functional interface is an interface that has exactly one abstract method. + * This method returns {@code true} if the given class is an interface and has exactly one abstract method, + * or if it is annotated with {@link FunctionalInterface}. It returns {@code false} otherwise, including for + * non-interface classes or interfaces with more than one abstract method. It also returns {@code false} if + * the class is {@code null}. + *

+ * + *

Example Usage

+ *
{@code
+     * @FunctionalInterface
+     * interface MyFunctionalInterface {
+     *     void doSomething();
+     * }
+     *
+     * boolean result1 = ClassUtils.isFunctionalInterface(MyFunctionalInterface.class); // returns true
+     * boolean result2 = ClassUtils.isFunctionalInterface(Runnable.class);              // returns true
+     * boolean result3 = ClassUtils.isFunctionalInterface(List.class);                  // returns false (not a functional interface)
+     * boolean result4 = ClassUtils.isFunctionalInterface(null);                        // returns false
+     * }
+ * + * @param type the class to check, may be {@code null} + * @return {@code true} if the specified class is a functional interface, {@code false} otherwise + */ + public static boolean isFunctionalInterface(@Nullable Class type) { + if (!isInterface(type)) { + return false; + } + // Trusted by the Java compiler, if the interface is annotated with @FunctionalInterface, it is a functional interface. + if (type.isAnnotationPresent(FunctionalInterface.class)) { + return true; + } + return findFunctionalInterfaceMethod(type) != null; + } + /** * Checks if the specified class is a primitive type. *

@@ -1005,7 +1193,7 @@ public static boolean arrayTypeEquals(@Nullable Class oneArrayType, @Nullable *

*

* Note: This method does not support the "[]" suffix notation for - * primitive arrays; this is only supported by {@link #forName(String)}. + * primitive arrays; this is only supported by {@link Class#forName(String)}. *

* *

Example Usage

@@ -2040,70 +2228,6 @@ public static boolean isDerived(@Nullable Class targetType, @Nullable ClassThis method searches for a declared constructor in the given class that - * matches the number and types of the provided arguments. It handles primitive - * types by resolving their wrapper types for comparison. If no matching constructor - * is found, an {@link IllegalArgumentException} is thrown. If a matching constructor - * is found, it is invoked using {@link ConstructorUtils#newInstance(Constructor, Object...)}.

- * - *

Example Usage

- *
{@code
-     * // Creating an instance of a class with a parameterized constructor
-     * MyClass instance = ClassUtils.newInstance(MyClass.class, "example", 42);
-     *
-     * // Creating an instance of a class with a no-arg constructor
-     * MyClass instance2 = ClassUtils.newInstance(MyClass.class);
-     *
-     * // This will throw an IllegalArgumentException if no matching constructor exists
-     * try {
-     *     ClassUtils.newInstance(MyClass.class, "invalid", "arguments");
-     * } catch (IllegalArgumentException e) {
-     *     System.out.println("No matching constructor found: " + e.getMessage());
-     * }
-     * }
- * - * @param type the class of which to create a new instance - * @param args the arguments to pass to the constructor - * @param the type of the object to create - * @return a new instance of the specified class - * @throws IllegalArgumentException if no constructor matching the provided arguments is found - * @see ConstructorUtils#newInstance(Constructor, Object...) - */ - @Nonnull - public static T newInstance(@Nonnull Class type, Object... args) { - int length = length(args); - - List> constructors = findDeclaredConstructors(type, constructor -> { - Class[] parameterTypes = constructor.getParameterTypes(); - if (length != parameterTypes.length) { - return false; - } - for (int i = 0; i < length; i++) { - Object arg = args[i]; - Class parameterType = parameterTypes[i]; - if (isPrimitive(parameterType)) { - parameterType = resolveWrapperType(parameterType); - } - if (!parameterType.isInstance(arg)) { - return false; - } - } - return true; - }); - - if (constructors.isEmpty()) { - String message = format("No constructor[class : '{}'] matches the arguments : {}", getTypeName(type), arrayToString(args)); - throw new IllegalArgumentException(message); - } - - Constructor constructor = (Constructor) constructors.get(0); - return ConstructorUtils.newInstance(constructor, args); - } - /** * Gets the top-level component type of the given array object. *

diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/ExceptionUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/ExceptionUtils.java index e772a3b75..9f7cce37e 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/ExceptionUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/ExceptionUtils.java @@ -21,9 +21,9 @@ import java.io.PrintWriter; +import static io.microsphere.reflect.ConstructorUtils.newInstance; import static io.microsphere.text.FormatUtils.format; import static io.microsphere.util.ClassUtils.isAssignableFrom; -import static io.microsphere.util.ClassUtils.newInstance; /** * {@link Exception} Utilities class @@ -145,7 +145,7 @@ public static T create(Class throwableClass) { */ @Nonnull public static T create(Class throwableClass, String message) { - return newInstance(throwableClass, message); + return newInstance(true, throwableClass, message); } /** diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/ShutdownHookUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/ShutdownHookUtils.java index e8abe67af..5bb0cb9d5 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/ShutdownHookUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/ShutdownHookUtils.java @@ -411,7 +411,7 @@ private static Map shutdownHookThreadsMap() { * @return the map of shutdown hook threads, or an empty map if the class is {@code null} */ static Map shutdownHookThreadsMap(Class applicationShutdownHooksClass) { - return applicationShutdownHooksClass == null ? emptyMap() : getStaticFieldValue(applicationShutdownHooksClass, HOOKS_FIELD_NAME); + return applicationShutdownHooksClass == null ? emptyMap() : getStaticFieldValue(true, applicationShutdownHooksClass, HOOKS_FIELD_NAME); } private ShutdownHookUtils() { diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/SizeUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/SizeUtils.java new file mode 100644 index 000000000..fd02e6db8 --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/util/SizeUtils.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. + */ + +package io.microsphere.util; + +import java.util.Map; + +import static io.microsphere.collection.MapUtils.ofMap; + +/** + * The utilities class for Size + * + * @author Mercy + * @see Utils + * @since 1.0.0 + */ +public abstract class SizeUtils implements Utils { + + /** + * The size in bytes of an unbounded type. + */ + public static final int UNBOUND_BYTES_SIZE = -1; + + /** + * The size in bytes of boolean type. + */ + public static final int BOOLEAN_BYTES_SIZE = 1; + + /** + * The size in bytes of byte type. + */ + public static final int BYTE_BYTES_SIZE = Byte.BYTES; + + /** + * The size in bytes of short type. + */ + public static final int SHORT_BYTES_SIZE = Short.BYTES; + + /** + * The size in bytes of char type. + */ + public static final int CHAR_BYTES_SIZE = Character.BYTES; + + /** + * The size in bytes of int type. + */ + public static final int INTEGER_BYTES_SIZE = Integer.BYTES; + + /** + * The size in bytes of float type. + */ + public static final int FLOAT_BYTES_SIZE = Float.BYTES; + + /** + * The size in bytes of long type. + */ + public static final int LONG_BYTES_SIZE = Long.BYTES; + + /** + * The size in bytes of double type. + */ + public static final int DOUBLE_BYTES_SIZE = Double.BYTES; + + private static final Map, Integer> BYTES_SIZE_MAP = ofMap( + boolean.class, BOOLEAN_BYTES_SIZE, + byte.class, BYTE_BYTES_SIZE, + short.class, SHORT_BYTES_SIZE, + char.class, CHAR_BYTES_SIZE, + int.class, INTEGER_BYTES_SIZE, + float.class, FLOAT_BYTES_SIZE, + long.class, LONG_BYTES_SIZE, + double.class, DOUBLE_BYTES_SIZE, + Boolean.class, BOOLEAN_BYTES_SIZE, + Byte.class, BYTE_BYTES_SIZE, + Short.class, SHORT_BYTES_SIZE, + Character.class, CHAR_BYTES_SIZE, + Integer.class, INTEGER_BYTES_SIZE, + Float.class, FLOAT_BYTES_SIZE, + Long.class, LONG_BYTES_SIZE, + Double.class, DOUBLE_BYTES_SIZE + ); + + /** + * Returns the size in bytes of the specified type. + * + * @param type the type to get the size in bytes for + * @return the size in bytes of the specified type, or {@link #UNBOUND_BYTES_SIZE} if the type is unbounded + */ + public static int bytesSize(Class type) { + return BYTES_SIZE_MAP.getOrDefault(type, UNBOUND_BYTES_SIZE); + } + + private SizeUtils() { + } +} diff --git a/microsphere-java-core/src/main/resources/META-INF/services/io.microsphere.io.Deserializer b/microsphere-java-core/src/main/resources/META-INF/services/io.microsphere.io.Deserializer deleted file mode 100644 index 965fab6d8..000000000 --- a/microsphere-java-core/src/main/resources/META-INF/services/io.microsphere.io.Deserializer +++ /dev/null @@ -1,2 +0,0 @@ -io.microsphere.io.DefaultDeserializer -io.microsphere.io.StringDeserializer \ No newline at end of file diff --git a/microsphere-java-core/src/main/resources/META-INF/services/io.microsphere.io.Serializer b/microsphere-java-core/src/main/resources/META-INF/services/io.microsphere.io.Serializer deleted file mode 100644 index 9ee5074fb..000000000 --- a/microsphere-java-core/src/main/resources/META-INF/services/io.microsphere.io.Serializer +++ /dev/null @@ -1,2 +0,0 @@ -io.microsphere.io.DefaultSerializer -io.microsphere.io.StringSerializer \ No newline at end of file diff --git a/microsphere-java-core/src/main/resources/META-INF/services/io.microsphere.io.serializer.Deserializer b/microsphere-java-core/src/main/resources/META-INF/services/io.microsphere.io.serializer.Deserializer new file mode 100644 index 000000000..9ce51a12c --- /dev/null +++ b/microsphere-java-core/src/main/resources/META-INF/services/io.microsphere.io.serializer.Deserializer @@ -0,0 +1,10 @@ +io.microsphere.io.serializer.BooleanSerializer +io.microsphere.io.serializer.ByteSerializer +io.microsphere.io.serializer.CharacterSerializer +io.microsphere.io.serializer.ShortSerializer +io.microsphere.io.serializer.IntegerSerializer +io.microsphere.io.serializer.LongSerializer +io.microsphere.io.serializer.FloatSerializer +io.microsphere.io.serializer.DoubleSerializer +io.microsphere.io.serializer.StringDeserializer +io.microsphere.io.serializer.DefaultDeserializer \ No newline at end of file diff --git a/microsphere-java-core/src/main/resources/META-INF/services/io.microsphere.io.serializer.Serializer b/microsphere-java-core/src/main/resources/META-INF/services/io.microsphere.io.serializer.Serializer new file mode 100644 index 000000000..8503aa99a --- /dev/null +++ b/microsphere-java-core/src/main/resources/META-INF/services/io.microsphere.io.serializer.Serializer @@ -0,0 +1,10 @@ +io.microsphere.io.serializer.BooleanSerializer +io.microsphere.io.serializer.ByteSerializer +io.microsphere.io.serializer.CharacterSerializer +io.microsphere.io.serializer.ShortSerializer +io.microsphere.io.serializer.IntegerSerializer +io.microsphere.io.serializer.LongSerializer +io.microsphere.io.serializer.FloatSerializer +io.microsphere.io.serializer.DoubleSerializer +io.microsphere.io.serializer.StringSerializer +io.microsphere.io.serializer.DefaultSerializer \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/MapUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/MapUtilsTest.java index 78758120d..33e6279c6 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/MapUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/MapUtilsTest.java @@ -444,7 +444,7 @@ private void assertNewFixedMap(int size, Function> map.put("G", 7); map.put("H", 8); assertEquals(size, map.size()); - assertEquals(valueOf(size), getFieldValue(map, "threshold")); + assertEquals(valueOf(size), getFieldValue(true, map, "threshold")); } private void assertNewMap(Class mapClass, Map map) { diff --git a/microsphere-java-core/src/test/java/io/microsphere/concurrent/ExecutorUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/concurrent/ExecutorUtilsTest.java index ca2488c38..b50ddbd47 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/concurrent/ExecutorUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/concurrent/ExecutorUtilsTest.java @@ -47,7 +47,7 @@ void tearDown() { @Test void testShutdownOnExit() throws InterruptedException { shutdownOnExit(this.executorService, this.executorService, this.executorService); - PriorityBlockingQueue shutdownHookCallbacks = getStaticFieldValue(ShutdownHookUtils.class, "shutdownHookCallbacks"); + PriorityBlockingQueue shutdownHookCallbacks = getStaticFieldValue(true, ShutdownHookUtils.class, "shutdownHookCallbacks"); assertNotNull(shutdownHookCallbacks); Iterator iterator = shutdownHookCallbacks.iterator(); diff --git a/microsphere-java-core/src/test/java/io/microsphere/convert/ObjectToByteArrayConverterTest.java b/microsphere-java-core/src/test/java/io/microsphere/convert/ObjectToByteArrayConverterTest.java index 089370577..bf7f41ee4 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/convert/ObjectToByteArrayConverterTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/convert/ObjectToByteArrayConverterTest.java @@ -16,7 +16,7 @@ */ package io.microsphere.convert; -import io.microsphere.io.DefaultSerializer; +import io.microsphere.io.serializer.DefaultSerializer; import org.junit.jupiter.api.Test; @@ -44,7 +44,7 @@ protected Object getSource() { @Override protected byte[] getTarget() throws Throwable { - return DefaultSerializer.INSTANCE.serialize(getSource()); + return DefaultSerializer.DEFAULT_SERIALIZER.serialize(getSource()); } @Test diff --git a/microsphere-java-core/src/test/java/io/microsphere/event/DirectEventDispatcherTest.java b/microsphere-java-core/src/test/java/io/microsphere/event/DirectEventDispatcherTest.java index 722d0e0f6..f2a388621 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/event/DirectEventDispatcherTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/event/DirectEventDispatcherTest.java @@ -16,13 +16,17 @@ */ package io.microsphere.event; +import io.microsphere.lang.MutableInteger; +import io.microsphere.util.ValueHolder; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static io.microsphere.collection.Lists.ofList; +import static io.microsphere.util.ValueHolder.of; import static java.util.Collections.emptyList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -145,4 +149,36 @@ void testDispatchEvent() { assertEquals(2, echoEventListener.getEventOccurs()); assertEquals(3, echoEventListener2.getEventOccurs()); } + + @Test + void testAddEventListenerOnLambdaForm() { + ValueHolder eventHolder = of(null); + + MutableInteger counter = new MutableInteger(0); + + EventListener eventListener = event -> { + eventHolder.setValue(event); + counter.incrementAndGet(); + }; + + EventListener echoEventEventListener = event -> { + eventHolder.setValue(event); + counter.incrementAndGet(); + }; + + this.dispatcher.addEventListener(eventListener); + this.dispatcher.addEventListener(echoEventEventListener); + + Event event = new Event("Test") { + }; + + this.dispatcher.dispatch(event); + assertSame(eventHolder.getValue(), event); + assertEquals(1, counter.get()); + + event = new EchoEvent("Hello,World"); + this.dispatcher.dispatch(event); + assertSame(eventHolder.getValue(), event); + assertEquals(3, counter.get()); + } } diff --git a/microsphere-java-core/src/test/java/io/microsphere/event/EchoEvent.java b/microsphere-java-core/src/test/java/io/microsphere/event/EchoEvent.java index 2675c7fb5..0dd3e4f29 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/event/EchoEvent.java +++ b/microsphere-java-core/src/test/java/io/microsphere/event/EchoEvent.java @@ -21,7 +21,7 @@ * * @since 1.0.0 */ -class EchoEvent extends Event { +public class EchoEvent extends Event { public EchoEvent(Object source) { super(source); diff --git a/microsphere-java-core/src/test/java/io/microsphere/event/EventDispatcherTest.java b/microsphere-java-core/src/test/java/io/microsphere/event/EventDispatcherTest.java index e05023003..7cc730576 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/event/EventDispatcherTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/event/EventDispatcherTest.java @@ -21,7 +21,11 @@ import java.util.List; import static io.microsphere.event.EventDispatcher.DIRECT_EXECUTOR; +import static io.microsphere.event.EventDispatcher.newDefault; +import static io.microsphere.event.EventDispatcher.of; +import static io.microsphere.event.EventDispatcher.parallel; import static java.util.Collections.emptyList; +import static java.util.concurrent.ForkJoinPool.commonPool; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; @@ -33,7 +37,7 @@ */ class EventDispatcherTest { - private EventDispatcher defaultInstance = EventDispatcher.newDefault(); + private EventDispatcher defaultInstance = newDefault(); @Test void testDefaultInstance() { @@ -46,6 +50,15 @@ void testDefaultMethods() { assertEquals(2, defaultInstance.getAllEventListeners().size()); } + @Test + void testOf() { + EventDispatcher dispatcher = of(null); + assertEquals(newDefault().getClass(), dispatcher.getClass()); + + dispatcher = of(commonPool()); + assertEquals(parallel(commonPool()).getClass(), dispatcher.getClass()); + } + @Test void testGetExecutor() throws Throwable { assertSame(DIRECT_EXECUTOR, defaultInstance.getExecutor()); diff --git a/microsphere-java-core/src/test/java/io/microsphere/event/EventListenerTest.java b/microsphere-java-core/src/test/java/io/microsphere/event/EventListenerTest.java index 0d7118561..fd1dc18d3 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/event/EventListenerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/event/EventListenerTest.java @@ -16,6 +16,7 @@ */ package io.microsphere.event; +import io.microsphere.io.event.FileChangedEvent; import org.junit.jupiter.api.Test; import static io.microsphere.event.EventListener.findEventType; @@ -31,7 +32,7 @@ class EventListenerTest { @Test - void testFindEventHierarchicalTypes() { + void testFindEventTypeOnHierarchicalClasses() { assertEquals(EchoEvent.class, findEventType(new EchoEventListener())); assertEquals(Event.class, findEventType(new EchoEventListener2())); @@ -39,6 +40,21 @@ void testFindEventHierarchicalTypes() { assertEquals(Event.class, findEventType(EchoEventListener2.class)); } + @Test + void testFindEventTypeOnLambda() { + EventListener listener = (event) -> { + }; + assertEquals(Event.class, findEventType(listener)); + + EventListener listener2 = (event) -> { + }; + assertEquals(EchoEvent.class, findEventType(listener2)); + + EventListener listener3 = (event) -> { + }; + assertEquals(FileChangedEvent.class, findEventType(listener3)); + } + @Test void testFindEventTypeOnNull() { assertNull(findEventType((Class) null)); diff --git a/microsphere-java-core/src/test/java/io/microsphere/event/ListenableTest.java b/microsphere-java-core/src/test/java/io/microsphere/event/ListenableTest.java index 8b58dd395..ee5aaf56d 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/event/ListenableTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/event/ListenableTest.java @@ -19,17 +19,4 @@ void testAssertListenerNoNull() { assertThrows(IllegalArgumentException.class, () -> assertListener(null)); } - @Test - void testAssertListenerOnInterface() { - assertThrows(IllegalArgumentException.class, () -> assertListener(new FinalEventListener())); - } - - static final class FinalEventListener implements EventListener { - - @Override - public void onEvent(Event event) { - - } - } - -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/internal/reflect/ConstantPoolUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/internal/reflect/ConstantPoolUtilsTest.java new file mode 100644 index 000000000..34187127a --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/internal/reflect/ConstantPoolUtilsTest.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. + */ + +package io.microsphere.internal.reflect; + + +import io.microsphere.event.EchoEvent; +import io.microsphere.event.EventListener; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.util.Set; + +import static io.microsphere.internal.reflect.ConstantPoolUtils.CONSTANT_POOL_CLASS; +import static io.microsphere.internal.reflect.ConstantPoolUtils.getClassAt; +import static io.microsphere.internal.reflect.ConstantPoolUtils.getClassAtIfLoaded; +import static io.microsphere.internal.reflect.ConstantPoolUtils.getClassRefIndexAt; +import static io.microsphere.internal.reflect.ConstantPoolUtils.getConstantPool; +import static io.microsphere.internal.reflect.ConstantPoolUtils.getDoubleAt; +import static io.microsphere.internal.reflect.ConstantPoolUtils.getFieldAt; +import static io.microsphere.internal.reflect.ConstantPoolUtils.getFieldAtIfLoaded; +import static io.microsphere.internal.reflect.ConstantPoolUtils.getFloatAt; +import static io.microsphere.internal.reflect.ConstantPoolUtils.getIntAt; +import static io.microsphere.internal.reflect.ConstantPoolUtils.getLongAt; +import static io.microsphere.internal.reflect.ConstantPoolUtils.getMemberRefInfoAt; +import static io.microsphere.internal.reflect.ConstantPoolUtils.getMethodAt; +import static io.microsphere.internal.reflect.ConstantPoolUtils.getMethodAtIfLoaded; +import static io.microsphere.internal.reflect.ConstantPoolUtils.getNameAndTypeRefIndexAt; +import static io.microsphere.internal.reflect.ConstantPoolUtils.getNameAndTypeRefInfoAt; +import static io.microsphere.internal.reflect.ConstantPoolUtils.getSize; +import static io.microsphere.internal.reflect.ConstantPoolUtils.getStringAt; +import static io.microsphere.internal.reflect.ConstantPoolUtils.getUTF8At; +import static io.microsphere.reflect.FieldUtils.findAllDeclaredFields; +import static io.microsphere.reflect.FieldUtils.getStaticFieldValue; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link ConstantPoolUtils} Test + * + * @author Mercy + * @see ConstantPoolUtils + * @since 1.0.0 + */ +class ConstantPoolUtilsTest { + @Test + void testConstants() { + Set allDeclaredFields = findAllDeclaredFields(ConstantPoolUtils.class); + assertTrue(allDeclaredFields.stream().anyMatch(field -> getStaticFieldValue(true, field) != null)); + } + + @Test + void testGetConstantPool() { + assertNotNull(getConstantPool(Object.class)); + assertNotNull(getConstantPool(String.class)); + assertInstanceOf(CONSTANT_POOL_CLASS, getConstantPool(Object.class)); + } + + @Test + void testGetter() { + Class targetClass = ConstantPoolUtils.class; + int size = getSize(targetClass); + assertTrue(size > 0); + + for (int i = 0; i < size; i++) { + int index = i; + assertDoesNotThrow(() -> getClassAt(targetClass, index)); + assertDoesNotThrow(() -> getClassAtIfLoaded(targetClass, index)); + assertDoesNotThrow(() -> getClassRefIndexAt(targetClass, index)); + assertDoesNotThrow(() -> getMethodAt(targetClass, index)); + assertDoesNotThrow(() -> getMethodAtIfLoaded(targetClass, index)); + assertDoesNotThrow(() -> getFieldAt(targetClass, index)); + assertDoesNotThrow(() -> getFieldAtIfLoaded(targetClass, index)); + assertDoesNotThrow(() -> getMemberRefInfoAt(targetClass, index)); + assertDoesNotThrow(() -> getNameAndTypeRefIndexAt(targetClass, index)); + assertDoesNotThrow(() -> getNameAndTypeRefInfoAt(targetClass, index)); + assertDoesNotThrow(() -> getIntAt(targetClass, index)); + assertDoesNotThrow(() -> getLongAt(targetClass, index)); + assertDoesNotThrow(() -> getFloatAt(targetClass, index)); + assertDoesNotThrow(() -> getDoubleAt(targetClass, index)); + assertDoesNotThrow(() -> getStringAt(targetClass, index)); + assertDoesNotThrow(() -> getUTF8At(targetClass, index)); + } + } + + @Test + void testDeduceGenericTypeFromLambda() { + EventListener e = event -> { + }; + + Class targetClass = e.getClass(); + + int count = 0; + + int size = getSize(targetClass); + for (int i = 0; i < size; i++) { + Member member = getMethodAt(targetClass, i); + if (member instanceof Method) { + count++; + } + } + + assertEquals(1, count); + } + +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/serializer/AbstractSerializerTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/AbstractSerializerTest.java new file mode 100644 index 000000000..93484cfea --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/AbstractSerializerTest.java @@ -0,0 +1,64 @@ +package io.microsphere.io.serializer; + +import io.microsphere.reflect.JavaType; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.function.Supplier; + +import static io.microsphere.reflect.JavaType.from; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * Abstract {@link Serializer} Test + * + * @param Serialization type + * @author Mercy + * @see AbstractSerializer + * @since 1.0.0 + */ +abstract class AbstractSerializerTest { + + @Test + void test() throws IOException { + test(this::getValue); + } + + @Test + void testNull() throws IOException { + test(this::getNullValue); + } + + void test(Supplier valueSupplier) throws IOException { + T value = valueSupplier.get(); + AbstractSerializer serializer = getSerializer(); + byte[] bytes = serializer.serialize(value); + T deserialized = serializer.deserialize(bytes); + assertEquals(serializer.calcBytesLength(), serializer.getBytesLength()); + + if (value != null && deserialized != null) { + assertEquals(getTestData(value), getTestData(deserialized)); + } else { + assertEquals(value, deserialized); + } + + Class targetType = serializer.getTargetType(); + JavaType javaType = from(getClass()).as(AbstractSerializerTest.class).getGenericType(0); + Class parameterType = javaType.toClass(); + + assertSame(targetType, parameterType); + } + + protected Object getTestData(T value) { + return value; + } + + protected abstract AbstractSerializer getSerializer(); + + protected abstract T getValue(); + + protected T getNullValue() { + return null; + } +} diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/serializer/BooleanSerializerTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/BooleanSerializerTest.java new file mode 100644 index 000000000..fbc15df83 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/BooleanSerializerTest.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. + */ + +package io.microsphere.io.serializer; + + +import static io.microsphere.io.serializer.BooleanSerializer.BOOLEAN_SERIALIZER; +import static java.lang.Boolean.FALSE; + +/** + * {@link BooleanSerializer} Test + * + * @author Mercy + * @see BooleanSerializer + * @since 1.0.0 + */ +class BooleanSerializerTest extends AbstractSerializerTest { + + @Override + protected AbstractSerializer getSerializer() { + return BOOLEAN_SERIALIZER; + } + + @Override + protected Boolean getValue() { + return FALSE; + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/serializer/ByteSerializerTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/ByteSerializerTest.java new file mode 100644 index 000000000..8a26d4972 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/ByteSerializerTest.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. + */ + +package io.microsphere.io.serializer; + + +import static io.microsphere.io.serializer.ByteSerializer.BYTE_SERIALIZER; + +/** + * {@link ByteSerializer} Test + * + * @author Mercy + * @see ByteSerializer + * @since 1.0.0 + */ +class ByteSerializerTest extends AbstractSerializerTest { + + @Override + protected AbstractSerializer getSerializer() { + return BYTE_SERIALIZER; + } + + @Override + protected Byte getValue() { + return Byte.MAX_VALUE; + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/serializer/CharacterSerializerTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/CharacterSerializerTest.java new file mode 100644 index 000000000..fc813dac5 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/CharacterSerializerTest.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. + */ + +package io.microsphere.io.serializer; + + +import static io.microsphere.io.serializer.CharacterSerializer.CHARACTER_SERIALIZER; +import static java.lang.Character.MAX_VALUE; + +/** + * {@link CharacterSerializer} Test + * + * @author Mercy + * @see CharacterSerializer + * @since 1.0.0 + */ +class CharacterSerializerTest extends AbstractSerializerTest { + + @Override + protected AbstractSerializer getSerializer() { + return CHARACTER_SERIALIZER; + } + + @Override + protected Character getValue() { + return MAX_VALUE; + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/DefaultDeserializerTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/DefaultDeserializerTest.java similarity index 88% rename from microsphere-java-core/src/test/java/io/microsphere/io/DefaultDeserializerTest.java rename to microsphere-java-core/src/test/java/io/microsphere/io/serializer/DefaultDeserializerTest.java index 7de24e867..b901b9e55 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/DefaultDeserializerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/DefaultDeserializerTest.java @@ -1,4 +1,4 @@ -package io.microsphere.io; +package io.microsphere.io.serializer; import org.junit.jupiter.api.Test; @@ -15,7 +15,7 @@ */ class DefaultDeserializerTest { - private DefaultDeserializer deserializer = DefaultDeserializer.INSTANCE; + private DefaultDeserializer deserializer = DefaultDeserializer.DEFAULT_DESERIALIZER; @Test void testDeserializeOnNull() throws IOException { diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/DefaultSerializerAndDeserializerTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/DefaultSerializerAndDeserializerTest.java similarity index 97% rename from microsphere-java-core/src/test/java/io/microsphere/io/DefaultSerializerAndDeserializerTest.java rename to microsphere-java-core/src/test/java/io/microsphere/io/serializer/DefaultSerializerAndDeserializerTest.java index 47f104ce7..248265d51 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/DefaultSerializerAndDeserializerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/DefaultSerializerAndDeserializerTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.microsphere.io; +package io.microsphere.io.serializer; import org.junit.jupiter.api.Test; diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/DeserializersTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/DeserializersTest.java similarity index 98% rename from microsphere-java-core/src/test/java/io/microsphere/io/DeserializersTest.java rename to microsphere-java-core/src/test/java/io/microsphere/io/serializer/DeserializersTest.java index 3e0fbe953..84e338786 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/DeserializersTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/DeserializersTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package io.microsphere.io; +package io.microsphere.io.serializer; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/serializer/DoubleSerializerTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/DoubleSerializerTest.java new file mode 100644 index 000000000..f2487ebbb --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/DoubleSerializerTest.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. + */ + +package io.microsphere.io.serializer; + + +import static io.microsphere.io.serializer.DoubleSerializer.DOUBLE_SERIALIZER; +import static java.lang.Double.MAX_VALUE; + +/** + * {@link DoubleSerializer} Test + * + * @author Mercy + * @see DoubleSerializer + * @since 1.0.0 + */ +class DoubleSerializerTest extends AbstractSerializerTest { + + @Override + protected AbstractSerializer getSerializer() { + return DOUBLE_SERIALIZER; + } + + @Override + protected Double getValue() { + return MAX_VALUE; + } +} diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/serializer/EnumSerializerTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/EnumSerializerTest.java new file mode 100644 index 000000000..59a1ed891 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/EnumSerializerTest.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. + */ + +package io.microsphere.io.serializer; + + +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import static java.util.concurrent.TimeUnit.values; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * {@link EnumSerializer} + * + * @author Mercy + * @see EnumSerializer + * @since 1.0.0 + */ +class EnumSerializerTest { + + @Test + void test() throws IOException { + EnumSerializer serializer = new EnumSerializer<>(TimeUnit.class); + assertSame(TimeUnit.class, serializer.getEnumType()); + assertSame(TimeUnit.class, serializer.getTargetType()); + assertEquals(1, serializer.getBytesLength()); + for (TimeUnit timeUnit : values()) { + String name = timeUnit.name(); + byte[] bytes = serializer.serialize(timeUnit); + TimeUnit deserializedTimeUnit = serializer.deserialize(bytes); + assertEquals(timeUnit, deserializedTimeUnit); + assertEquals(name, deserializedTimeUnit.name()); + } + + assertNull(serializer.serialize(null)); + assertNull(serializer.deserialize(null)); + } + + @Test + void test2() throws IOException { + EnumSerializer serializer = new EnumSerializer<>(E.class); + assertSame(E.class, serializer.getEnumType()); + assertEquals(2, serializer.getBytesLength()); + for (E e : E.values()) { + String name = e.name(); + byte[] bytes = serializer.serialize(e); + E deserializedE = serializer.deserialize(bytes); + assertEquals(e, deserializedE); + assertEquals(name, deserializedE.name()); + } + } + + static enum E { + E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, + E11, E12, E13, E14, E15, E16, E17, E18, E19, E20, + E21, E22, E23, E24, E25, E26, E27, E28, E29, E30, + E31, E32, E33, E34, E35, E36, E37, E38, E39, E40, + E41, E42, E43, E44, E45, E46, E47, E48, E49, E50, + E51, E52, E53, E54, E55, E56, E57, E58, E59, E60, + E61, E62, E63, E64, E65, E66, E67, E68, E69, E70, + E71, E72, E73, E74, E75, E76, E77, E78, E79, E80, + E81, E82, E83, E84, E85, E86, E87, E88, E89, E90, + E91, E92, E93, E94, E95, E96, E97, E98, E99, E100, + E101, E102, E103, E104, E105, E106, E107, E108, E109, E110, + E111, E112, E113, E114, E115, E116, E117, E118, E119, E120, + E121, E122, E123, E124, E125, E126, E127, E128, E129, E130, + E131, E132, E133, E134, E135, E136, E137, E138, E139, E140, + E141, E142, E143, E144, E145, E146, E147, E148, E149, E150, + E151, E152, E153, E154, E155, E156, E157, E158, E159, E160, + E161, E162, E163, E164, E165, E166, E167, E168, E169, E170, + E171, E172, E173, E174, E175, E176, E177, E178, E179, E180, + E181, E182, E183, E184, E185, E186, E187, E188, E189, E190, + E191, E192, E193, E194, E195, E196, E197, E198, E199, E200 + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/serializer/FloatSerializerTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/FloatSerializerTest.java new file mode 100644 index 000000000..b2df7d071 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/FloatSerializerTest.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. + */ + +package io.microsphere.io.serializer; + + +import static io.microsphere.io.serializer.FloatSerializer.FLOAT_SERIALIZER; +import static java.lang.Float.MAX_VALUE; + +/** + * {@link FloatSerializer} Test + * + * @author Mercy + * @see FloatSerializer + * @since 1.0.0 + */ +class FloatSerializerTest extends AbstractSerializerTest { + + @Override + protected AbstractSerializer getSerializer() { + return FLOAT_SERIALIZER; + } + + @Override + protected Float getValue() { + return MAX_VALUE; + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/serializer/IntegerSerializerTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/IntegerSerializerTest.java new file mode 100644 index 000000000..38d159220 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/IntegerSerializerTest.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. + */ + +package io.microsphere.io.serializer; + + +import static io.microsphere.io.serializer.IntegerSerializer.INTEGER_SERIALIZER; +import static java.lang.Integer.MAX_VALUE; + +/** + * {@link IntegerSerializer} Test + * + * @author Mercy + * @see IntegerSerializer + * @since 1.0.0 + */ +class IntegerSerializerTest extends AbstractSerializerTest { + + @Override + protected AbstractSerializer getSerializer() { + return INTEGER_SERIALIZER; + } + + @Override + protected Integer getValue() { + return MAX_VALUE; + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/serializer/LongSerializerTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/LongSerializerTest.java new file mode 100644 index 000000000..6c8fb5ecd --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/LongSerializerTest.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. + */ + +package io.microsphere.io.serializer; + + +import static io.microsphere.io.serializer.LongSerializer.LONG_SERIALIZER; +import static java.lang.Long.MAX_VALUE; + +/** + * {@link LongSerializer} Test + * + * @author Mercy + * @see LongSerializer + * @since 1.0.0 + */ +class LongSerializerTest extends AbstractSerializerTest { + + @Override + protected AbstractSerializer getSerializer() { + return LONG_SERIALIZER; + } + + @Override + protected Long getValue() { + return MAX_VALUE; + } + +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/SerializersAndDeserializersTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/SerializersAndDeserializersTest.java similarity index 98% rename from microsphere-java-core/src/test/java/io/microsphere/io/SerializersAndDeserializersTest.java rename to microsphere-java-core/src/test/java/io/microsphere/io/serializer/SerializersAndDeserializersTest.java index f12f0c86e..3cbb1bf46 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/SerializersAndDeserializersTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/SerializersAndDeserializersTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.microsphere.io; +package io.microsphere.io.serializer; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/SerializersTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/SerializersTest.java similarity index 52% rename from microsphere-java-core/src/test/java/io/microsphere/io/SerializersTest.java rename to microsphere-java-core/src/test/java/io/microsphere/io/serializer/SerializersTest.java index eaf6055a4..3c4d43755 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/SerializersTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/SerializersTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package io.microsphere.io; +package io.microsphere.io.serializer; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -34,6 +34,18 @@ */ class SerializersTest { + private static final Class[] SUPPORTED_CLASSES = { + Boolean.class, + Character.class, + Short.class, + Integer.class, + Long.class, + Float.class, + Double.class, + String.class, + Object.class + }; + private Serializers serializers; @BeforeEach @@ -42,57 +54,54 @@ void setUp() { } @Test - void testLoadSPI() { - assertEquals(emptyList(), this.serializers.get(String.class)); - assertEquals(emptyList(), this.serializers.get(Object.class)); + void testtGet() { + for (Class supportedClass : SUPPORTED_CLASSES) { + assertEquals(emptyList(), this.serializers.get(supportedClass)); + } this.serializers.loadSPI(); - assertNotNull(this.serializers.get(String.class)); - assertNotNull(this.serializers.get(Object.class)); + for (Class supportedClass : SUPPORTED_CLASSES) { + assertNotNull(this.serializers.get(supportedClass)); + } } @Test void testGetMostCompatible() { - assertNull(this.serializers.getMostCompatible(String.class)); - assertNull(this.serializers.getMostCompatible(Object.class)); + for (Class supportedClass : SUPPORTED_CLASSES) { + assertNull(this.serializers.getMostCompatible(supportedClass)); + } this.serializers.loadSPI(); - assertNotNull(this.serializers.getMostCompatible(String.class)); - assertNotNull(this.serializers.getMostCompatible(Object.class)); + for (Class supportedClass : SUPPORTED_CLASSES) { + assertNotNull(this.serializers.getMostCompatible(supportedClass)); + } } @Test void testGetHighestPriority() { - assertNull(this.serializers.getHighestPriority(String.class)); - assertNull(this.serializers.getHighestPriority(Object.class)); + for (Class supportedClass : SUPPORTED_CLASSES) { + assertNull(this.serializers.getHighestPriority(supportedClass)); + } this.serializers.loadSPI(); - assertNotNull(this.serializers.getHighestPriority(String.class)); - assertNotNull(this.serializers.getHighestPriority(Object.class)); + for (Class supportedClass : SUPPORTED_CLASSES) { + assertNotNull(this.serializers.getHighestPriority(supportedClass)); + } } @Test void testGetLowestPriority() { - assertNull(this.serializers.getLowestPriority(String.class)); - assertNull(this.serializers.getLowestPriority(Object.class)); - - this.serializers.loadSPI(); - - assertNotNull(this.serializers.getLowestPriority(String.class)); - assertNotNull(this.serializers.getLowestPriority(Object.class)); - } - - @Test - void testGet() { - assertEquals(emptyList(), this.serializers.get(String.class)); - assertEquals(emptyList(), this.serializers.get(Object.class)); + for (Class supportedClass : SUPPORTED_CLASSES) { + assertNull(this.serializers.getLowestPriority(supportedClass)); + } this.serializers.loadSPI(); - assertNotNull(this.serializers.getMostCompatible(String.class)); - assertNotNull(this.serializers.getMostCompatible(Object.class)); + for (Class supportedClass : SUPPORTED_CLASSES) { + assertNotNull(this.serializers.getLowestPriority(supportedClass)); + } } } diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/serializer/ShortSerializerTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/ShortSerializerTest.java new file mode 100644 index 000000000..8c5304569 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/ShortSerializerTest.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. + */ + +package io.microsphere.io.serializer; + + +import static io.microsphere.io.serializer.ShortSerializer.SHORT_SERIALIZER; +import static java.lang.Short.MAX_VALUE; + +/** + * TODO + * + * @author Mercy + * @see TODO + * @since 1.0.0 + */ +class ShortSerializerTest extends AbstractSerializerTest { + + @Override + protected AbstractSerializer getSerializer() { + return SHORT_SERIALIZER; + } + + @Override + protected Short getValue() { + return MAX_VALUE; + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/StringSerializerAndDeserializerTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/StringSerializerAndDeserializerTest.java similarity index 98% rename from microsphere-java-core/src/test/java/io/microsphere/io/StringSerializerAndDeserializerTest.java rename to microsphere-java-core/src/test/java/io/microsphere/io/serializer/StringSerializerAndDeserializerTest.java index 30be7a780..3c10db5b6 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/StringSerializerAndDeserializerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/serializer/StringSerializerAndDeserializerTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.microsphere.io; +package io.microsphere.io.serializer; import org.junit.jupiter.api.Test; diff --git a/microsphere-java-core/src/test/java/io/microsphere/lang/invoke/LambdaUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/lang/invoke/LambdaUtilsTest.java new file mode 100644 index 000000000..6f00b6870 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/lang/invoke/LambdaUtilsTest.java @@ -0,0 +1,340 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. + */ + +package io.microsphere.lang.invoke; + + +import io.microsphere.event.EchoEvent; +import io.microsphere.event.EventListener; +import io.microsphere.io.event.FileChangedEvent; +import org.junit.jupiter.api.Test; + +import java.lang.invoke.CallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Method; +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.DoubleFunction; +import java.util.function.Function; +import java.util.function.LongFunction; +import java.util.function.ToLongBiFunction; + +import static io.microsphere.lang.invoke.LambdaUtils.resolveLambdaMethodParameterTypes; +import static io.microsphere.lang.invoke.LambdaUtils.resolveParameterTypes; +import static io.microsphere.reflect.MethodUtils.findMethod; +import static io.microsphere.util.ArrayUtils.EMPTY_CLASS_ARRAY; +import static java.lang.Double.valueOf; +import static java.lang.invoke.LambdaMetafactory.metafactory; +import static java.lang.invoke.MethodHandles.lookup; +import static java.lang.invoke.MethodType.methodType; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link LambdaUtils} Test + * + * @author Mercy + * @see LambdaUtils + * @since 1.0.0 + */ +public class LambdaUtilsTest { + + @Test + void testResolveLambdaMethodParameterTypesOnCompiledLambda() { + + String p0 = "0"; + Integer p1 = 1; + + EventListener e = event -> { + }; + + List> parameterTypes = resolveLambdaMethodParameterTypes(e, EventListener.class); + assertEquals(1, parameterTypes.size()); + assertEquals(EchoEvent.class, parameterTypes.get(0)); + + // e instance implements two interfaces of @FunctionalInterface : EventListener and Prioritized + parameterTypes = resolveLambdaMethodParameterTypes(e); + assertTrue(parameterTypes.isEmpty()); + + EventListener fce = event -> { + assertNotNull(p0); + assertNotNull(p1); + }; + + parameterTypes = resolveLambdaMethodParameterTypes(fce, EventListener.class); + assertEquals(1, parameterTypes.size()); + assertEquals(FileChangedEvent.class, parameterTypes.get(0)); + + LongFunction lf = v -> v; + parameterTypes = resolveLambdaMethodParameterTypes(lf, LongFunction.class); + assertEquals(1, parameterTypes.size()); + assertEquals(long.class, parameterTypes.get(0)); + + parameterTypes = resolveLambdaMethodParameterTypes(lf); + assertEquals(1, parameterTypes.size()); + assertEquals(long.class, parameterTypes.get(0)); + + DoubleFunction df = v -> valueOf(v).longValue(); + parameterTypes = resolveLambdaMethodParameterTypes(df, DoubleFunction.class); + assertEquals(1, parameterTypes.size()); + assertEquals(double.class, parameterTypes.get(0)); + + ToLongBiFunction tl = (s, l) -> p1.longValue(); + parameterTypes = resolveLambdaMethodParameterTypes(tl, ToLongBiFunction.class); + assertEquals(2, parameterTypes.size()); + assertEquals(String.class, parameterTypes.get(0)); + assertEquals(Long.class, parameterTypes.get(1)); + } + + @Test + void testResolveLambdaMethodParameterTypesOnLambdaFactory() throws Throwable { + Function upperLambda = toUpperLambda(); + List> parameterTypes = resolveLambdaMethodParameterTypes(upperLambda, Function.class); + assertEquals(1, parameterTypes.size()); + assertEquals(String.class, parameterTypes.get(0)); + + BiFunction sumLambda = sumLambda(); + parameterTypes = resolveLambdaMethodParameterTypes(sumLambda, BiFunction.class); + assertEquals(2, parameterTypes.size()); + assertEquals(Integer.class, parameterTypes.get(0)); + assertEquals(Integer.class, parameterTypes.get(1)); + + TriFunction volumeLambda = volumeLambda(); + parameterTypes = resolveLambdaMethodParameterTypes(volumeLambda, TriFunction.class); + assertEquals(3, parameterTypes.size()); + assertEquals(Integer.class, parameterTypes.get(0)); + assertEquals(Integer.class, parameterTypes.get(1)); + assertEquals(Integer.class, parameterTypes.get(2)); + + TriFunction replaceLambda = replaceLambda(); + parameterTypes = resolveLambdaMethodParameterTypes(replaceLambda, TriFunction.class); + assertEquals(3, parameterTypes.size()); + assertEquals(String.class, parameterTypes.get(0)); + assertEquals(CharSequence.class, parameterTypes.get(1)); + assertEquals(CharSequence.class, parameterTypes.get(2)); + + parameterTypes = resolveLambdaMethodParameterTypes(volumeLambda, Function.class); + assertTrue(parameterTypes.isEmpty()); + + parameterTypes = resolveLambdaMethodParameterTypes(volumeLambda, BiFunction.class); + assertTrue(parameterTypes.isEmpty()); + + parameterTypes = resolveLambdaMethodParameterTypes(volumeLambda, String.class); + assertTrue(parameterTypes.isEmpty()); + + parameterTypes = resolveLambdaMethodParameterTypes(BiFunction.class, String.class); + assertTrue(parameterTypes.isEmpty()); + } + + @Test + void testResolveParameterTypesOnMismatch() { + Method method = findMethod(String.class, "valueOf", int.class); + assertSame(EMPTY_CLASS_ARRAY, resolveParameterTypes(method, Long.class)); + + method = findMethod(String.class, "valueOf", Object.class); + assertSame(EMPTY_CLASS_ARRAY, resolveParameterTypes(method, int.class)); + } + + Function toUpperLambda() throws Throwable { + // 1. Set up the target method lookup context + MethodHandles.Lookup lookup = lookup(); + + // 2. Locate the real method we want the lambda to execute + // We are targeting: String.toUpperCase() + MethodType targetMethodType = methodType(String.class); + MethodHandle implementationHandle = lookup.findVirtual( + String.class, + "toUpperCase", + targetMethodType + ); + + // 3. Define the signatures required by LambdaMetafactory + + // The type signature of the functional interface's abstract method (erased type) + // Function.apply(Object) -> Object + MethodType samMethodType = methodType(Object.class, Object.class); + + // The type signature of the functional interface's abstract method (instantiated type) + // Function.apply(String) -> String + MethodType instantiatedMethodType = methodType(String.class, String.class); + + // 4. Invoke LambdaMetafactory to spin the runtime lambda class + CallSite callSite = metafactory( + lookup, // Caller lookup context + "apply", // Name of the interface method to implement + methodType(Function.class), // Factory signature (returns the functional interface) + samMethodType, // Type signature of the SAM (Single Abstract Method) + implementationHandle, // Direct method handle to the actual implementation + instantiatedMethodType // Specific enforced runtime signature + ); + + // 5. Extract the factory and generate the lambda instance + MethodHandle factory = callSite.getTarget(); + @SuppressWarnings("unchecked") + Function toUpperLambda = (Function) factory.invokeExact(); + return toUpperLambda; + } + + BiFunction sumLambda() throws Throwable { + // 1. Set up the target method lookup context + MethodHandles.Lookup lookup = lookup(); + + // 2. Locate the real method we want the lambda to execute + // We are targeting the static method: Integer.sum(int, int) + MethodType targetMethodType = methodType(int.class, int.class, int.class); + MethodHandle implementationHandle = lookup.findStatic( + Integer.class, + "sum", + targetMethodType + ); + + // 3. Define the signatures required by LambdaMetafactory + + // The type signature of the functional interface's abstract method (erased type) + // BiFunction.apply(Object, Object) -> Object + MethodType samMethodType = methodType(Object.class, Object.class, Object.class); + + // The type signature of the functional interface's abstract method (instantiated type) + // BiFunction.apply(Integer, Integer) -> Integer + MethodType instantiatedMethodType = methodType(Integer.class, Integer.class, Integer.class); + + // 4. Invoke LambdaMetafactory to spin the runtime lambda class + CallSite callSite = metafactory( + lookup, // Caller lookup context + "apply", // Name of the interface method to implement + methodType(BiFunction.class), // Factory signature (returns the functional interface) + samMethodType, // Type signature of the SAM (Single Abstract Method) + implementationHandle, // Direct method handle to the actual implementation + instantiatedMethodType // Specific enforced runtime signature + ); + + // 5. Extract the factory and generate the BiFunction lambda instance + MethodHandle factory = callSite.getTarget(); + @SuppressWarnings("unchecked") + BiFunction sumLambda = (BiFunction) factory.invokeExact(); + return sumLambda; + } + + // Define our custom functional interface + @FunctionalInterface + public interface TriFunction { + R apply(T t, U u, V v); + } + + // The target method we want the lambda to execute + public static int calculateVolume(int length, int width, int height) { + return length * width * height; + } + + TriFunction volumeLambda() throws Throwable { + // 1. Set up the target method lookup context + MethodHandles.Lookup lookup = lookup(); + + // 2. Locate the real static method with 3 parameters + MethodType targetMethodType = methodType(int.class, int.class, int.class, int.class); + MethodHandle implementationHandle = lookup.findStatic( + LambdaUtilsTest.class, + "calculateVolume", + targetMethodType + ); + + // 3. Define the signatures required by LambdaMetafactory + + // The type signature of the SAM (Single Abstract Method) after type erasure. + // TriFunction.apply(Object, Object, Object) -> Object + MethodType samMethodType = methodType( + Object.class, // Return type + Object.class, Object.class, Object.class // 3 Input types + ); + + // The exact generic type signature enforced at runtime. + // TriFunction.apply(Integer, Integer, Integer) -> Integer + MethodType instantiatedMethodType = methodType( + Integer.class, // Return type + Integer.class, Integer.class, Integer.class // 3 Input types + ); + + // 4. Invoke LambdaMetafactory to spin the runtime lambda class + CallSite callSite = metafactory( + lookup, // Caller lookup context + "apply", // Name of the interface method to implement + methodType(TriFunction.class), // Factory signature (returns our TriFunction) + samMethodType, // Type signature of the SAM + implementationHandle, // Direct method handle to the actual implementation + instantiatedMethodType // Enforced runtime signature + ); + + // 5. Extract the factory and generate the TriFunction lambda instance + MethodHandle factory = callSite.getTarget(); + @SuppressWarnings("unchecked") + TriFunction volumeLambda = + (TriFunction) factory.invokeExact(); + return volumeLambda; + } + + TriFunction replaceLambda() throws Throwable { + // 2. Set up the target method lookup context + MethodHandles.Lookup lookup = lookup(); + + // 3. Locate the non-static instance method using findVirtual + // String.replace(CharSequence, CharSequence) returns String + MethodType targetMethodType = methodType(String.class, CharSequence.class, CharSequence.class); + MethodHandle implementationHandle = lookup.findVirtual( + String.class, + "replace", + targetMethodType + ); + + // 4. Define the signatures required by LambdaMetafactory + + // After type erasure, all generics become Object. + // TriFunction.apply(Object, Object, Object) -> Object + MethodType samMethodType = methodType( + Object.class, + Object.class, Object.class, Object.class + ); + + // Enforced runtime types. + // TriFunction.apply(String, CharSequence, CharSequence) -> String + // Note: The first parameter is the instance type (String)! + MethodType instantiatedMethodType = methodType( + String.class, + String.class, CharSequence.class, CharSequence.class + ); + + // 5. Invoke LambdaMetafactory to spin the runtime lambda class + CallSite callSite = metafactory( + lookup, // Caller lookup context + "apply", // Name of the interface method to implement + methodType(TriFunction.class), // Factory signature (returns our TriFunction) + samMethodType, // Type signature of the SAM + implementationHandle, // Direct method handle to the instance method + instantiatedMethodType // Enforced runtime signature + ); + + // 6. Extract the factory and generate the TriFunction lambda instance + MethodHandle factory = callSite.getTarget(); + @SuppressWarnings("unchecked") + TriFunction replaceLambda = + (TriFunction) factory.invokeExact(); + return replaceLambda; + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/logging/LoggerFactoryTest.java b/microsphere-java-core/src/test/java/io/microsphere/logging/LoggerFactoryTest.java index 808572d04..38bfb9eee 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/logging/LoggerFactoryTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/logging/LoggerFactoryTest.java @@ -59,7 +59,7 @@ void testLoadAvailableFactories() { } private void testLoggerFactory(LoggerFactory loggerFactory) { - Logger logger = loggerFactory.createLogger("test"); + Logger logger = loggerFactory.createLogger("io.microsphere"); log(logger); } diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/builder/AbstractMBeanDescribableBuilderTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/builder/AbstractMBeanDescribableBuilderTest.java index d0688efd5..8b25111be 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/management/builder/AbstractMBeanDescribableBuilderTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/management/builder/AbstractMBeanDescribableBuilderTest.java @@ -23,8 +23,8 @@ import javax.management.modelmbean.DescriptorSupport; +import static io.microsphere.reflect.ConstructorUtils.newInstance; import static io.microsphere.reflect.JavaType.from; -import static io.microsphere.util.ClassUtils.newInstance; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; @@ -54,7 +54,7 @@ protected B builder() { .as(AbstractMBeanDescribableBuilderTest.class) .getGenericType(0) .toClass(); - return newInstance(builderClass); + return newInstance(true, builderClass); } @Test diff --git a/microsphere-java-core/src/test/java/io/microsphere/misc/UnsafeUtilTest.java b/microsphere-java-core/src/test/java/io/microsphere/misc/UnsafeUtilTest.java deleted file mode 100644 index 605742d94..000000000 --- a/microsphere-java-core/src/test/java/io/microsphere/misc/UnsafeUtilTest.java +++ /dev/null @@ -1,394 +0,0 @@ -//package io.microsphere.misc; -// -//import sun.misc.Unsafe; -// -//import static org.junit.jupiter.api.Assertions.assertEquals; -//import static org.junit.jupiter.api.Assertions.assertNotNull; -//import static org.junit.jupiter.api.Assertions.assertNull; -// -/// ** -// * {@link UnsafeUtils} Test -// * -// * @author Mercy -// * @see UnsafeUtilTest -// * @since 1.0.0 -// */ -//class UnsafeUtilTest { -// -// private Model model; -// -// public void setUp() throws Exception { -// model = new Model(); -// } -// -// public void testStaticInit() throws Exception { -// assertNotNull(UnsafeUtils.unsafe); -// Unsafe unsafe = UnsafeUtils.unsafe; -// -// assertEquals(UnsafeUtils.LONG_ARRAY_BASE_OFFSET, unsafe.arrayBaseOffset(long[].class)); -// assertEquals(UnsafeUtils.INT_ARRAY_BASE_OFFSET, unsafe.arrayBaseOffset(int[].class)); -// assertEquals(UnsafeUtils.SHORT_ARRAY_BASE_OFFSET, unsafe.arrayBaseOffset(short[].class)); -// assertEquals(UnsafeUtils.BYTE_ARRAY_BASE_OFFSET, unsafe.arrayBaseOffset(byte[].class)); -// assertEquals(UnsafeUtils.BOOLEAN_ARRAY_BASE_OFFSET, unsafe.arrayBaseOffset(boolean[].class)); -// assertEquals(UnsafeUtils.DOUBLE_ARRAY_BASE_OFFSET, unsafe.arrayBaseOffset(double[].class)); -// assertEquals(UnsafeUtils.FLOAT_ARRAY_BASE_OFFSET, unsafe.arrayBaseOffset(float[].class)); -// assertEquals(UnsafeUtils.CHAR_ARRAY_BASE_OFFSET, unsafe.arrayBaseOffset(char[].class)); -// assertEquals(UnsafeUtils.OBJECT_ARRAY_BASE_OFFSET, unsafe.arrayBaseOffset(Object[].class)); -// -// -// assertEquals(UnsafeUtils.LONG_ARRAY_INDEX_SCALE, unsafe.arrayIndexScale(long[].class)); -// assertEquals(UnsafeUtils.INT_ARRAY_INDEX_SCALE, unsafe.arrayIndexScale(int[].class)); -// assertEquals(UnsafeUtils.SHORT_ARRAY_INDEX_SCALE, unsafe.arrayIndexScale(short[].class)); -// assertEquals(UnsafeUtils.BYTE_ARRAY_INDEX_SCALE, unsafe.arrayIndexScale(byte[].class)); -// assertEquals(UnsafeUtils.BOOLEAN_ARRAY_INDEX_SCALE, unsafe.arrayIndexScale(boolean[].class)); -// assertEquals(UnsafeUtils.DOUBLE_ARRAY_INDEX_SCALE, unsafe.arrayIndexScale(double[].class)); -// assertEquals(UnsafeUtils.FLOAT_ARRAY_INDEX_SCALE, unsafe.arrayIndexScale(float[].class)); -// assertEquals(UnsafeUtils.CHAR_ARRAY_INDEX_SCALE, unsafe.arrayIndexScale(char[].class)); -// assertEquals(UnsafeUtils.OBJECT_ARRAY_INDEX_SCALE, unsafe.arrayIndexScale(Object[].class)); -// -// } -// -// public void testPutLongAndGetLong() throws Exception { -// String fieldName = "longValue"; -// long value = Long.MAX_VALUE; -// UnsafeUtils.putLong(model, fieldName, value); -// long returnValue = UnsafeUtils.getLong(model, fieldName); -// assertEquals(returnValue, value); -// assertEquals(model.longValue, returnValue); -// -// value = Long.MIN_VALUE; -// UnsafeUtils.putLongVolatile(model, fieldName, value); -// returnValue = UnsafeUtils.getLongVolatile(model, fieldName); -// assertEquals(returnValue, value); -// assertEquals(model.longValue, returnValue); -// -// value = Long.MAX_VALUE; -// UnsafeUtils.putOrderedLong(model, fieldName, value); -// returnValue = UnsafeUtils.getLongVolatile(model, fieldName); -// assertEquals(returnValue, value); -// assertEquals(model.longValue, returnValue); -// } -// -// public void testPutIntAndGetInt() throws Exception { -// String fieldName = "intValue"; -// int value = 123; -// UnsafeUtils.putInt(model, fieldName, value); -// int returnValue = UnsafeUtils.getInt(model, fieldName); -// assertEquals(returnValue, value); -// assertEquals(model.intValue, returnValue); -// -// value = Integer.MAX_VALUE; -// UnsafeUtils.putIntVolatile(model, fieldName, value); -// returnValue = UnsafeUtils.getIntVolatile(model, fieldName); -// assertEquals(returnValue, value); -// assertEquals(model.intValue, returnValue); -// -// value = Integer.MIN_VALUE; -// UnsafeUtils.putOrderedInt(model, fieldName, value); -// returnValue = UnsafeUtils.getIntVolatile(model, fieldName); -// assertEquals(returnValue, value); -// assertEquals(model.intValue, returnValue); -// } -// -// public void testPutShortAndGetShort() throws Exception { -// String fieldName = "shortValue"; -// short value = Short.MAX_VALUE; -// UnsafeUtils.putShort(model, fieldName, value); -// short returnValue = UnsafeUtils.getShort(model, fieldName); -// assertEquals(returnValue, value); -// assertEquals(model.shortValue, returnValue); -// -// UnsafeUtils.putShortVolatile(model, fieldName, value); -// returnValue = UnsafeUtils.getShortVolatile(model, fieldName); -// assertEquals(returnValue, value); -// assertEquals(model.shortValue, returnValue); -// } -// -// public void testPutByteAndGetByte() throws Exception { -// String fieldName = "byteValue"; -// byte value = Byte.MAX_VALUE; -// UnsafeUtils.putByte(model, fieldName, value); -// byte returnValue = UnsafeUtils.getByte(model, fieldName); -// assertEquals(returnValue, value); -// assertEquals(model.byteValue, returnValue); -// -// UnsafeUtils.putByteVolatile(model, fieldName, value); -// returnValue = UnsafeUtils.getByteVolatile(model, fieldName); -// assertEquals(returnValue, value); -// assertEquals(model.byteValue, returnValue); -// } -// -// public void testPutBooleanAndGetBoolean() throws Exception { -// String fieldName = "booleanValue"; -// boolean value = Boolean.TRUE; -// UnsafeUtils.putBoolean(model, fieldName, value); -// boolean returnValue = UnsafeUtils.getBoolean(model, fieldName); -// assertEquals(returnValue, value); -// assertEquals(model.booleanValue, returnValue); -// -// UnsafeUtils.putBooleanVolatile(model, fieldName, value); -// returnValue = UnsafeUtils.getBooleanVolatile(model, fieldName); -// assertEquals(returnValue, value); -// assertEquals(model.booleanValue, returnValue); -// } -// -// public void testPutDoubleAndGetDouble() throws Exception { -// String fieldName = "doubleValue"; -// double value = Double.MAX_VALUE; -// UnsafeUtils.putDouble(model, fieldName, value); -// double returnValue = UnsafeUtils.getDouble(model, fieldName); -// assertEquals(returnValue, value); -// assertEquals(model.doubleValue, returnValue); -// -// UnsafeUtils.putDoubleVolatile(model, fieldName, value); -// returnValue = UnsafeUtils.getDoubleVolatile(model, fieldName); -// assertEquals(returnValue, value); -// assertEquals(model.doubleValue, returnValue); -// } -// -// public void testPutFloatAndGetFloat() throws Exception { -// String fieldName = "floatValue"; -// float value = Float.MAX_VALUE; -// UnsafeUtils.putFloat(model, fieldName, value); -// float returnValue = UnsafeUtils.getFloat(model, fieldName); -// assertEquals(returnValue, value); -// assertEquals(model.floatValue, returnValue); -// -// UnsafeUtils.putFloatVolatile(model, fieldName, value); -// returnValue = UnsafeUtils.getFloatVolatile(model, fieldName); -// assertEquals(returnValue, value); -// assertEquals(model.floatValue, returnValue); -// } -// -// public void testPutCharAndGetChar() throws Exception { -// String fieldName = "charValue"; -// char value = '@'; -// UnsafeUtils.putChar(model, fieldName, value); -// char returnValue = UnsafeUtils.getChar(model, fieldName); -// assertEquals(returnValue, value); -// assertEquals(model.charValue, returnValue); -// -// UnsafeUtils.putCharVolatile(model, fieldName, value); -// returnValue = UnsafeUtils.getCharVolatile(model, fieldName); -// assertEquals(returnValue, value); -// assertEquals(model.charValue, returnValue); -// -// } -// -// public void testPutObjectAndGetObject() throws Exception { -// String fieldName = "stringValue"; -// Object value = "Test text"; -// UnsafeUtils.putObject(model, fieldName, value); -// Object returnValue = UnsafeUtils.getObject(model, fieldName); -// assertEquals(returnValue, value); -// assertEquals(model.stringValue, returnValue); -// -// value = "AAAAAAAAAAAA"; -// UnsafeUtils.putObjectVolatile(model, fieldName, value); -// returnValue = UnsafeUtils.getObjectVolatile(model, fieldName); -// assertEquals(returnValue, value); -// assertEquals(model.stringValue, returnValue); -// -// value = "BBBBBB"; -// UnsafeUtils.putOrderedObject(model, fieldName, value); -// returnValue = UnsafeUtils.getObject(model, fieldName); -// assertEquals(returnValue, value); -// assertEquals(model.stringValue, returnValue); -// } -// -// public void testPutLongIntoArrayVolatileAndGetLongFromArrayVolatile() throws Exception { -// String fieldName = "longArrayValue"; -// long value = 123; -// int index = 2; -// UnsafeUtils.putLongIntoArrayVolatile(model, fieldName, index, value); -// long returnValue = UnsafeUtils.getLongFromArrayVolatile(model, fieldName, index); -// assertEquals(returnValue, value); -// assertEquals(model.longArrayValue[index], returnValue); -// -// value = Integer.MAX_VALUE; -// UnsafeUtils.putOrderedLongIntoArray(model, fieldName, index, value); -// returnValue = UnsafeUtils.getLongFromArrayVolatile(model, fieldName, index); -// assertEquals(returnValue, value); -// assertEquals(model.longArrayValue[index], returnValue); -// } -// -// public void testPutIntIntoArrayVolatileAndGetIntFromArrayVolatile() throws Exception { -// String fieldName = "intArrayValue"; -// int value = 123; -// int index = 1; -// UnsafeUtils.putIntIntoArrayVolatile(model, fieldName, index, value); -// int returnValue = UnsafeUtils.getIntFromArrayVolatile(model, fieldName, index); -// assertEquals(returnValue, value); -// assertEquals(model.intArrayValue[index], returnValue); -// -// value = Integer.MAX_VALUE; -// UnsafeUtils.putOrderedIntIntoArray(model, fieldName, index, value); -// returnValue = UnsafeUtils.getIntFromArrayVolatile(model, fieldName, index); -// assertEquals(returnValue, value); -// assertEquals(model.intArrayValue[index], returnValue); -// } -// -// public void testPutShortIntoArrayVolatileAndGetShortFromArrayVolatile() throws Exception { -// String fieldName = "shortArrayValue"; -// short value = 123; -// int index = 5; -// UnsafeUtils.putShortIntoArrayVolatile(model, fieldName, index, value); -// short returnValue = UnsafeUtils.getShortFromArrayVolatile(model, fieldName, index); -// assertEquals(returnValue, value); -// assertEquals(model.shortArrayValue[index], returnValue); -// } -// -// public void testPutByteIntoArrayVolatileAndGetByteFromArrayVolatile() throws Exception { -// String fieldName = "byteArrayValue"; -// byte value = 123; -// int index = 5; -// UnsafeUtils.putByteIntoArrayVolatile(model, fieldName, index, value); -// byte returnValue = UnsafeUtils.getByteFromArrayVolatile(model, fieldName, index); -// assertEquals(returnValue, value); -// assertEquals(model.byteArrayValue[index], returnValue); -// } -// -// public void testPutBooleanIntoArrayVolatileAndGetBooleanFromArrayVolatile() throws Exception { -// String fieldName = "booleanArrayValue"; -// boolean value = Boolean.TRUE; -// int index = 3; -// UnsafeUtils.putBooleanIntoArrayVolatile(model, fieldName, index, value); -// boolean returnValue = UnsafeUtils.getBooleanFromArrayVolatile(model, fieldName, index); -// assertEquals(returnValue, value); -// assertEquals(model.booleanArrayValue[index], returnValue); -// } -// -// public void testPutDoubleIntoArrayVolatileAndGetDoubleFromArrayVolatile() throws Exception { -// String fieldName = "doubleArrayValue"; -// double value = Double.MAX_VALUE; -// int index = 8; -// UnsafeUtils.putDoubleIntoArrayVolatile(model, fieldName, index, value); -// double returnValue = UnsafeUtils.getDoubleFromArrayVolatile(model, fieldName, index); -// assertEquals(returnValue, value); -// assertEquals(model.doubleArrayValue[index], returnValue); -// } -// -// public void testPutFloatIntoArrayVolatileAndGetFloatFromArrayVolatile() throws Exception { -// String fieldName = "floatArrayValue"; -// float value = Float.MAX_VALUE; -// int index = 7; -// UnsafeUtils.putFloatIntoArrayVolatile(model, fieldName, index, value); -// float returnValue = UnsafeUtils.getFloatFromArrayVolatile(model, fieldName, index); -// assertEquals(returnValue, value); -// assertEquals(model.floatArrayValue[index], returnValue); -// } -// -// public void testPutCharIntoArrayVolatileAndGetCharFromArrayVolatile() throws Exception { -// String fieldName = "charArrayValue"; -// char value = '@'; -// int index = 9; -// UnsafeUtils.putCharIntoArrayVolatile(model, fieldName, index, value); -// char returnValue = UnsafeUtils.getCharFromArrayVolatile(model, fieldName, index); -// assertEquals(returnValue, value); -// assertEquals(model.charArrayValue[index], returnValue); -// } -// -// public void testPutObjectIntoArrayVolatileAndGetObjectFromArrayVolatile() throws Exception { -// String fieldName = "objectArrayValue"; -// Object value = "Test"; -// int index = 5; -// UnsafeUtils.putObjectIntoArrayVolatile(model, fieldName, index, value); -// Object returnValue = UnsafeUtils.getObjectFromArrayVolatile(model, fieldName, index); -// assertEquals(returnValue, value); -// assertEquals(model.objectArrayValue[index], returnValue); -// -// UnsafeUtils.putOrderedObjectIntoArray(model, fieldName, index, value); -// returnValue = UnsafeUtils.getObjectFromArrayVolatile(model, fieldName, index); -// assertEquals(returnValue, value); -// assertEquals(model.objectArrayValue[index], returnValue); -// -// } -// -// /** -// * 测试当存储更高范围类型的值,然后获取 -// */ -// public void tesPutOnInvalidTypeValue() throws Exception { -// String fieldName = "intValue"; -// int value = Integer.MAX_VALUE; -// int index = 1; -// IllegalArgumentException exception; -// -// exception = null; -// assertNull(exception); -// try { -// UnsafeUtils.putLong(model, fieldName, value); -// } catch (IllegalArgumentException e) { -// exception = e; -// } -// assertNotNull(exception); -// -// exception = null; -// assertNull(exception); -// try { -// UnsafeUtils.putShort(model, fieldName, (short) 1); -// } catch (IllegalArgumentException e) { -// exception = e; -// } -// assertNotNull(exception); -// -// exception = null; -// assertNull(exception); -// try { -// UnsafeUtils.putChar(model, fieldName, (char) 1); -// } catch (IllegalArgumentException e) { -// exception = e; -// } -// assertNotNull(exception); -// -// -// } -// -// -// public void testOnObjectIsNull() throws Exception { -// String fieldName = "aaaa"; -// Object value = "value"; -// -// NullPointerException exception = null; -// try { -// UnsafeUtils.putObject(null, fieldName, value); -// } catch (NullPointerException e) { -// exception = e; -// } -// assertNotNull(exception); -// } -// -// public void testOnIllegalArgumentException() throws Exception { -// String fieldName = "aaaa"; -// Object value = "value"; -// -// NullPointerException exception = null; -// try { -// UnsafeUtils.putObject(model, fieldName, value); -// } catch (NullPointerException e) { -// exception = e; -// } -// assertNotNull(exception); -// } -// -// private static class Model { -// private long longValue; -// private int intValue; -// private short shortValue; -// private byte byteValue; -// private boolean booleanValue; -// private float floatValue; -// private double doubleValue; -// private char charValue; -// private String stringValue; -// private long[] longArrayValue = new long[10]; -// private int[] intArrayValue = new int[10]; -// private short[] shortArrayValue = new short[10]; -// private byte[] byteArrayValue = new byte[10]; -// private boolean[] booleanArrayValue = new boolean[10]; -// private double[] doubleArrayValue = new double[10]; -// private float[] floatArrayValue = new float[10]; -// private char[] charArrayValue = new char[10]; -// private Object[] objectArrayValue = new Object[10]; -// } -// -//} diff --git a/microsphere-java-core/src/test/java/io/microsphere/misc/UnsafeUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/misc/UnsafeUtilsTest.java new file mode 100644 index 000000000..7373b0a16 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/misc/UnsafeUtilsTest.java @@ -0,0 +1,751 @@ +package io.microsphere.misc; + +import io.microsphere.io.serializer.Serializer; +import io.microsphere.io.serializer.Serializers; +import io.microsphere.reflect.MemberUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Function; + +import static io.microsphere.misc.UnsafeUtils.BOOLEAN_ARRAY_BASE_OFFSET; +import static io.microsphere.misc.UnsafeUtils.BOOLEAN_ARRAY_INDEX_SCALE; +import static io.microsphere.misc.UnsafeUtils.BYTE_ARRAY_BASE_OFFSET; +import static io.microsphere.misc.UnsafeUtils.BYTE_ARRAY_INDEX_SCALE; +import static io.microsphere.misc.UnsafeUtils.CHAR_ARRAY_BASE_OFFSET; +import static io.microsphere.misc.UnsafeUtils.CHAR_ARRAY_INDEX_SCALE; +import static io.microsphere.misc.UnsafeUtils.DOUBLE_ARRAY_BASE_OFFSET; +import static io.microsphere.misc.UnsafeUtils.DOUBLE_ARRAY_INDEX_SCALE; +import static io.microsphere.misc.UnsafeUtils.FLOAT_ARRAY_BASE_OFFSET; +import static io.microsphere.misc.UnsafeUtils.FLOAT_ARRAY_INDEX_SCALE; +import static io.microsphere.misc.UnsafeUtils.INT_ARRAY_BASE_OFFSET; +import static io.microsphere.misc.UnsafeUtils.INT_ARRAY_INDEX_SCALE; +import static io.microsphere.misc.UnsafeUtils.LONG_ARRAY_BASE_OFFSET; +import static io.microsphere.misc.UnsafeUtils.LONG_ARRAY_INDEX_SCALE; +import static io.microsphere.misc.UnsafeUtils.OBJECT_ARRAY_BASE_OFFSET; +import static io.microsphere.misc.UnsafeUtils.OBJECT_ARRAY_INDEX_SCALE; +import static io.microsphere.misc.UnsafeUtils.SHORT_ARRAY_BASE_OFFSET; +import static io.microsphere.misc.UnsafeUtils.SHORT_ARRAY_INDEX_SCALE; +import static io.microsphere.misc.UnsafeUtils.addressSize; +import static io.microsphere.misc.UnsafeUtils.allocateInstance; +import static io.microsphere.misc.UnsafeUtils.allocateMemory; +import static io.microsphere.misc.UnsafeUtils.arrayBaseOffset; +import static io.microsphere.misc.UnsafeUtils.arrayIndexOffset; +import static io.microsphere.misc.UnsafeUtils.arrayIndexScale; +import static io.microsphere.misc.UnsafeUtils.compareAndSwapInt; +import static io.microsphere.misc.UnsafeUtils.compareAndSwapLong; +import static io.microsphere.misc.UnsafeUtils.compareAndSwapObject; +import static io.microsphere.misc.UnsafeUtils.copyMemory; +import static io.microsphere.misc.UnsafeUtils.freeMemory; +import static io.microsphere.misc.UnsafeUtils.getAddress; +import static io.microsphere.misc.UnsafeUtils.getAndAddInt; +import static io.microsphere.misc.UnsafeUtils.getAndAddLong; +import static io.microsphere.misc.UnsafeUtils.getAndSetInt; +import static io.microsphere.misc.UnsafeUtils.getAndSetLong; +import static io.microsphere.misc.UnsafeUtils.getAndSetObject; +import static io.microsphere.misc.UnsafeUtils.getBoolean; +import static io.microsphere.misc.UnsafeUtils.getBooleanVolatile; +import static io.microsphere.misc.UnsafeUtils.getBooleanVolatileFromArray; +import static io.microsphere.misc.UnsafeUtils.getByte; +import static io.microsphere.misc.UnsafeUtils.getByteVolatile; +import static io.microsphere.misc.UnsafeUtils.getByteVolatileFromArray; +import static io.microsphere.misc.UnsafeUtils.getChar; +import static io.microsphere.misc.UnsafeUtils.getCharVolatile; +import static io.microsphere.misc.UnsafeUtils.getCharVolatileFromArray; +import static io.microsphere.misc.UnsafeUtils.getDouble; +import static io.microsphere.misc.UnsafeUtils.getDoubleVolatile; +import static io.microsphere.misc.UnsafeUtils.getDoubleVolatileFromArray; +import static io.microsphere.misc.UnsafeUtils.getFloat; +import static io.microsphere.misc.UnsafeUtils.getFloatVolatile; +import static io.microsphere.misc.UnsafeUtils.getFloatVolatileFromArray; +import static io.microsphere.misc.UnsafeUtils.getInt; +import static io.microsphere.misc.UnsafeUtils.getIntVolatile; +import static io.microsphere.misc.UnsafeUtils.getIntVolatileFromArray; +import static io.microsphere.misc.UnsafeUtils.getLong; +import static io.microsphere.misc.UnsafeUtils.getLongVolatile; +import static io.microsphere.misc.UnsafeUtils.getLongVolatileFromArray; +import static io.microsphere.misc.UnsafeUtils.getObject; +import static io.microsphere.misc.UnsafeUtils.getObjectVolatile; +import static io.microsphere.misc.UnsafeUtils.getObjectVolatileFromArray; +import static io.microsphere.misc.UnsafeUtils.getShort; +import static io.microsphere.misc.UnsafeUtils.getShortVolatile; +import static io.microsphere.misc.UnsafeUtils.getShortVolatileFromArray; +import static io.microsphere.misc.UnsafeUtils.pageSize; +import static io.microsphere.misc.UnsafeUtils.putAddress; +import static io.microsphere.misc.UnsafeUtils.putBoolean; +import static io.microsphere.misc.UnsafeUtils.putBooleanVolatile; +import static io.microsphere.misc.UnsafeUtils.putBooleanVolatileIntoArray; +import static io.microsphere.misc.UnsafeUtils.putByte; +import static io.microsphere.misc.UnsafeUtils.putByteVolatile; +import static io.microsphere.misc.UnsafeUtils.putByteVolatileIntoArray; +import static io.microsphere.misc.UnsafeUtils.putChar; +import static io.microsphere.misc.UnsafeUtils.putCharVolatile; +import static io.microsphere.misc.UnsafeUtils.putCharVolatileIntoArray; +import static io.microsphere.misc.UnsafeUtils.putDouble; +import static io.microsphere.misc.UnsafeUtils.putDoubleVolatile; +import static io.microsphere.misc.UnsafeUtils.putDoubleVolatileIntoArray; +import static io.microsphere.misc.UnsafeUtils.putFloat; +import static io.microsphere.misc.UnsafeUtils.putFloatVolatile; +import static io.microsphere.misc.UnsafeUtils.putFloatVolatileIntoArray; +import static io.microsphere.misc.UnsafeUtils.putInt; +import static io.microsphere.misc.UnsafeUtils.putIntVolatile; +import static io.microsphere.misc.UnsafeUtils.putIntVolatileIntoArray; +import static io.microsphere.misc.UnsafeUtils.putLong; +import static io.microsphere.misc.UnsafeUtils.putLongVolatile; +import static io.microsphere.misc.UnsafeUtils.putLongVolatileIntoArray; +import static io.microsphere.misc.UnsafeUtils.putObject; +import static io.microsphere.misc.UnsafeUtils.putObjectVolatile; +import static io.microsphere.misc.UnsafeUtils.putObjectVolatileIntoArray; +import static io.microsphere.misc.UnsafeUtils.putOrderedInt; +import static io.microsphere.misc.UnsafeUtils.putOrderedIntIntoArray; +import static io.microsphere.misc.UnsafeUtils.putOrderedLong; +import static io.microsphere.misc.UnsafeUtils.putOrderedLongIntoArray; +import static io.microsphere.misc.UnsafeUtils.putOrderedObject; +import static io.microsphere.misc.UnsafeUtils.putOrderedObjectIntoArray; +import static io.microsphere.misc.UnsafeUtils.putShort; +import static io.microsphere.misc.UnsafeUtils.putShortVolatile; +import static io.microsphere.misc.UnsafeUtils.putShortVolatileIntoArray; +import static io.microsphere.misc.UnsafeUtils.reallocateMemory; +import static io.microsphere.misc.UnsafeUtils.setMemory; +import static io.microsphere.misc.UnsafeUtils.staticFieldBase; +import static io.microsphere.misc.UnsafeUtils.staticFieldOffset; +import static io.microsphere.misc.UnsafeUtils.throwException; +import static io.microsphere.reflect.FieldUtils.findAllDeclaredFields; +import static io.microsphere.reflect.FieldUtils.getStaticFieldValue; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link UnsafeUtils} Test + * + * @author Mercy + * @see UnsafeUtilsTest + * @since 1.0.0 + */ +class UnsafeUtilsTest { + + private Model model; + + private Serializers serializers; + + @BeforeEach + void setUp() { + this.model = new Model(); + this.serializers = new Serializers(); + this.serializers.loadSPI(); + } + + @Test + void testStaticFields() { + assertEquals(16, LONG_ARRAY_BASE_OFFSET); + assertEquals(16, INT_ARRAY_BASE_OFFSET); + assertEquals(16, SHORT_ARRAY_BASE_OFFSET); + assertEquals(16, BYTE_ARRAY_BASE_OFFSET); + assertEquals(16, BOOLEAN_ARRAY_BASE_OFFSET); + assertEquals(16, DOUBLE_ARRAY_BASE_OFFSET); + assertEquals(16, FLOAT_ARRAY_BASE_OFFSET); + assertEquals(16, CHAR_ARRAY_BASE_OFFSET); + assertEquals(16, OBJECT_ARRAY_BASE_OFFSET); + + assertEquals(8, LONG_ARRAY_INDEX_SCALE); + assertEquals(4, INT_ARRAY_INDEX_SCALE); + assertEquals(2, SHORT_ARRAY_INDEX_SCALE); + assertEquals(1, BYTE_ARRAY_INDEX_SCALE); + assertEquals(1, BOOLEAN_ARRAY_INDEX_SCALE); + assertEquals(8, DOUBLE_ARRAY_INDEX_SCALE); + assertEquals(4, FLOAT_ARRAY_INDEX_SCALE); + assertEquals(2, CHAR_ARRAY_INDEX_SCALE); + assertEquals(4, OBJECT_ARRAY_INDEX_SCALE); + + Class unsafeUtilsClass = UnsafeUtils.class; + Set allDeclaredFields = findAllDeclaredFields(unsafeUtilsClass, MemberUtils::isStatic); + for (Field field : allDeclaredFields) { + Object value = getStaticFieldValue(true, field); + assertNotNull(value, "The static field value of " + field + " should not be null"); + } + } + + @Test + void testLongOps() { + String fieldName = "longValue"; + long value = Long.MAX_VALUE; + putLong(model, fieldName, value); + long returnValue = getLong(model, fieldName); + assertEquals(returnValue, value); + assertEquals(model.longValue, returnValue); + + value = Long.MIN_VALUE; + putLongVolatile(model, fieldName, value); + returnValue = getLongVolatile(model, fieldName); + assertEquals(returnValue, value); + assertEquals(model.longValue, returnValue); + + value = Long.MAX_VALUE; + putOrderedLong(model, fieldName, value); + returnValue = getLongVolatile(model, fieldName); + assertEquals(returnValue, value); + assertEquals(model.longValue, returnValue); + + assertTrue(compareAndSwapLong(model, fieldName, value, Long.MIN_VALUE)); + returnValue = getLong(model, fieldName); + assertEquals(returnValue, Long.MIN_VALUE); + + assertFalse(compareAndSwapLong(model, fieldName, value, Long.MAX_VALUE)); + + long oldValue = getAndAddLong(model, fieldName, 1); + assertEquals(oldValue, returnValue); + returnValue = getLong(model, fieldName); + assertEquals(returnValue, Long.MIN_VALUE + 1); + + assertEquals(returnValue, getAndSetLong(model, fieldName, Long.MIN_VALUE)); + returnValue = getLong(model, fieldName); + assertEquals(returnValue, Long.MIN_VALUE); + } + + @Test + void testIntOps() { + String fieldName = "intValue"; + int value = 123; + putInt(model, fieldName, value); + int returnValue = getInt(model, fieldName); + assertEquals(returnValue, value); + assertEquals(model.intValue, returnValue); + + value = Integer.MAX_VALUE; + putIntVolatile(model, fieldName, value); + returnValue = getIntVolatile(model, fieldName); + assertEquals(returnValue, value); + assertEquals(model.intValue, returnValue); + + value = Integer.MIN_VALUE; + putOrderedInt(model, fieldName, value); + returnValue = getIntVolatile(model, fieldName); + assertEquals(returnValue, value); + assertEquals(model.intValue, returnValue); + + assertTrue(compareAndSwapInt(model, fieldName, value, Integer.MAX_VALUE)); + returnValue = getInt(model, fieldName); + assertEquals(returnValue, Integer.MAX_VALUE); + + assertFalse(compareAndSwapInt(model, fieldName, value, Integer.MIN_VALUE)); + + int oldValue = getAndAddInt(model, fieldName, 1); + assertEquals(oldValue, returnValue); + returnValue = getInt(model, fieldName); + assertEquals(returnValue, Integer.MAX_VALUE + 1); + + assertEquals(returnValue, getAndSetInt(model, fieldName, Integer.MIN_VALUE)); + returnValue = getInt(model, fieldName); + assertEquals(returnValue, Integer.MIN_VALUE); + } + + @Test + void testShortOps() { + String fieldName = "shortValue"; + short value = Short.MAX_VALUE; + putShort(model, fieldName, value); + short returnValue = getShort(model, fieldName); + assertEquals(returnValue, value); + assertEquals(model.shortValue, returnValue); + + putShortVolatile(model, fieldName, value); + returnValue = getShortVolatile(model, fieldName); + assertEquals(returnValue, value); + assertEquals(model.shortValue, returnValue); + } + + @Test + void testByteOps() { + String fieldName = "byteValue"; + byte value = Byte.MAX_VALUE; + putByte(model, fieldName, value); + byte returnValue = getByte(model, fieldName); + assertEquals(returnValue, value); + assertEquals(model.byteValue, returnValue); + + putByteVolatile(model, fieldName, value); + returnValue = getByteVolatile(model, fieldName); + assertEquals(returnValue, value); + assertEquals(model.byteValue, returnValue); + } + + @Test + void testBooleanOps() { + String fieldName = "booleanValue"; + boolean value = true; + putBoolean(model, fieldName, value); + boolean returnValue = getBoolean(model, fieldName); + assertEquals(returnValue, value); + assertEquals(model.booleanValue, returnValue); + + putBooleanVolatile(model, fieldName, value); + returnValue = getBooleanVolatile(model, fieldName); + assertEquals(returnValue, value); + assertEquals(model.booleanValue, returnValue); + } + + @Test + void testDoubleOps() { + String fieldName = "doubleValue"; + double value = Double.MAX_VALUE; + putDouble(model, fieldName, value); + double returnValue = getDouble(model, fieldName); + assertEquals(returnValue, value); + assertEquals(model.doubleValue, returnValue); + + putDoubleVolatile(model, fieldName, value); + returnValue = getDoubleVolatile(model, fieldName); + assertEquals(returnValue, value); + assertEquals(model.doubleValue, returnValue); + } + + @Test + void testFloatOps() { + String fieldName = "floatValue"; + float value = Float.MAX_VALUE; + putFloat(model, fieldName, value); + float returnValue = getFloat(model, fieldName); + assertEquals(returnValue, value); + assertEquals(model.floatValue, returnValue); + + putFloatVolatile(model, fieldName, value); + returnValue = getFloatVolatile(model, fieldName); + assertEquals(returnValue, value); + assertEquals(model.floatValue, returnValue); + } + + @Test + void testCharOps() { + String fieldName = "charValue"; + char value = '@'; + putChar(model, fieldName, value); + char returnValue = getChar(model, fieldName); + assertEquals(returnValue, value); + assertEquals(model.charValue, returnValue); + + putCharVolatile(model, fieldName, value); + returnValue = getCharVolatile(model, fieldName); + assertEquals(returnValue, value); + assertEquals(model.charValue, returnValue); + } + + @Test + void testObjectOps() { + String fieldName = "stringValue"; + Object value = "Test text"; + putObject(model, fieldName, value); + Object returnValue = getObject(model, fieldName); + assertEquals(returnValue, value); + assertEquals(model.stringValue, returnValue); + + value = "A"; + putObjectVolatile(model, fieldName, value); + returnValue = getObjectVolatile(model, fieldName); + assertEquals(returnValue, value); + assertEquals(model.stringValue, returnValue); + + value = "B"; + putOrderedObject(model, fieldName, value); + returnValue = getObject(model, fieldName); + assertEquals(returnValue, value); + assertEquals(model.stringValue, returnValue); + + value = "C"; + assertTrue(compareAndSwapObject(model, fieldName, returnValue, value)); + returnValue = getObject(model, fieldName); + assertEquals(returnValue, value); + + assertFalse(compareAndSwapObject(model, fieldName, "returnValue", value)); + + value = "D"; + assertEquals(returnValue, getAndSetObject(model, fieldName, value)); + returnValue = getObject(model, fieldName); + assertEquals(returnValue, value); + } + + @Test + void testLongArrayVolatileOps() { + String fieldName = "longArrayValue"; + long value = 123; + int index = 2; + putLongVolatileIntoArray(model, fieldName, index, value); + long returnValue = getLongVolatileFromArray(model, fieldName, index); + assertEquals(returnValue, value); + assertEquals(model.longArrayValue[index], returnValue); + + value = Integer.MAX_VALUE; + putOrderedLongIntoArray(model, fieldName, index, value); + returnValue = getLongVolatileFromArray(model, fieldName, index); + assertEquals(returnValue, value); + assertEquals(model.longArrayValue[index], returnValue); + } + + @Test + void testIntArrayVolatileOps() { + String fieldName = "intArrayValue"; + int value = 123; + int index = 1; + putIntVolatileIntoArray(model, fieldName, index, value); + int returnValue = getIntVolatileFromArray(model, fieldName, index); + assertEquals(returnValue, value); + assertEquals(model.intArrayValue[index], returnValue); + + value = Integer.MAX_VALUE; + putOrderedIntIntoArray(model, fieldName, index, value); + returnValue = getIntVolatileFromArray(model, fieldName, index); + assertEquals(returnValue, value); + assertEquals(model.intArrayValue[index], returnValue); + } + + @Test + void testShortArrayVolatileOps() { + String fieldName = "shortArrayValue"; + short value = 123; + int index = 5; + putShortVolatileIntoArray(model, fieldName, index, value); + short returnValue = getShortVolatileFromArray(model, fieldName, index); + assertEquals(returnValue, value); + assertEquals(model.shortArrayValue[index], returnValue); + } + + @Test + void testByteArrayVolatileOps() { + String fieldName = "byteArrayValue"; + byte value = 123; + int index = 5; + putByteVolatileIntoArray(model, fieldName, index, value); + byte returnValue = getByteVolatileFromArray(model, fieldName, index); + assertEquals(returnValue, value); + assertEquals(model.byteArrayValue[index], returnValue); + } + + @Test + void testBooleanArrayVolatileOps() { + String fieldName = "booleanArrayValue"; + boolean value = true; + int index = 3; + putBooleanVolatileIntoArray(model, fieldName, index, value); + boolean returnValue = getBooleanVolatileFromArray(model, fieldName, index); + assertEquals(returnValue, value); + assertEquals(model.booleanArrayValue[index], returnValue); + } + + @Test + void testDoubleArrayVolatileOps() { + String fieldName = "doubleArrayValue"; + double value = Double.MAX_VALUE; + int index = 8; + putDoubleVolatileIntoArray(model, fieldName, index, value); + double returnValue = getDoubleVolatileFromArray(model, fieldName, index); + assertEquals(returnValue, value); + assertEquals(model.doubleArrayValue[index], returnValue); + } + + @Test + void testFloatArrayVolatileOps() { + String fieldName = "floatArrayValue"; + float value = Float.MAX_VALUE; + int index = 7; + putFloatVolatileIntoArray(model, fieldName, index, value); + float returnValue = getFloatVolatileFromArray(model, fieldName, index); + assertEquals(returnValue, value); + assertEquals(model.floatArrayValue[index], returnValue); + } + + @Test + void testCharArrayVolatileOps() { + String fieldName = "charArrayValue"; + char value = '@'; + int index = 9; + putCharVolatileIntoArray(model, fieldName, index, value); + char returnValue = getCharVolatileFromArray(model, fieldName, index); + assertEquals(returnValue, value); + assertEquals(model.charArrayValue[index], returnValue); + } + + @Test + void testObjectArrayVolatileOps() { + String fieldName = "objectArrayValue"; + Object value = "Test"; + int index = 5; + putObjectVolatileIntoArray(model, fieldName, index, value); + Object returnValue = getObjectVolatileFromArray(model, fieldName, index); + assertEquals(returnValue, value); + assertEquals(model.objectArrayValue[index], returnValue); + + putOrderedObjectIntoArray(model, fieldName, index, value); + returnValue = getObjectVolatileFromArray(model, fieldName, index); + assertEquals(returnValue, value); + assertEquals(model.objectArrayValue[index], returnValue); + } + + @Test + void tesPutOnInvalidTypeValue() { + String fieldName = "intValue"; + int value = Integer.MAX_VALUE; + int index = 1; + IllegalArgumentException exception; + + exception = null; + assertNull(exception); + try { + putLong(model, fieldName, value); + } catch (IllegalArgumentException e) { + exception = e; + } + assertNotNull(exception); + + exception = null; + assertNull(exception); + try { + putShort(model, fieldName, (short) 1); + } catch (IllegalArgumentException e) { + exception = e; + } + assertNotNull(exception); + + exception = null; + assertNull(exception); + try { + putChar(model, fieldName, (char) 1); + } catch (IllegalArgumentException e) { + exception = e; + } + assertNotNull(exception); + } + + @Test + void testOnObjectIsNull() { + String fieldName = "aaaa"; + Object value = "value"; + + NullPointerException exception = null; + try { + putObject(null, fieldName, value); + } catch (NullPointerException e) { + exception = e; + } + assertNotNull(exception); + } + + @Test + void testOnIllegalArgumentException() { + String fieldName = "aaaa"; + Object value = "value"; + + NullPointerException exception = null; + try { + putObject(model, fieldName, value); + } catch (NullPointerException e) { + exception = e; + } + assertNotNull(exception); + } + + @Test + void testStaticFieldOffset() { + String fieldName = "MIN_VALUE"; + long offset = staticFieldOffset(Integer.class, fieldName); + assertTrue(offset > 0L); + } + + @Test + void testStaticFieldBase() { + String fieldName = "MIN_VALUE"; + Object base = staticFieldBase(Integer.class, fieldName); + assertNotNull(base); + } + + @Test + void testByteOpsInOffHeap() { + testInOffHeap(Byte.MAX_VALUE, UnsafeUtils::putByte, UnsafeUtils::getByte); + } + + @Test + void testShortOpsInOffHeap() { + testInOffHeap(Short.MAX_VALUE, UnsafeUtils::putShort, UnsafeUtils::getShort); + } + + @Test + void testCharOpsInOffHeap() { + testInOffHeap(Character.MAX_VALUE, UnsafeUtils::putChar, UnsafeUtils::getChar); + } + + @Test + void testIntegerOpsInOffHeap() { + testInOffHeap(Integer.MAX_VALUE, UnsafeUtils::putInt, UnsafeUtils::getInt); + } + + @Test + void testLongOpsInOffHeap() { + testInOffHeap(Long.MAX_VALUE, UnsafeUtils::putLong, UnsafeUtils::getLong); + } + + @Test + void testFloatOpsInOffHeap() { + testInOffHeap(Float.MAX_VALUE, UnsafeUtils::putFloat, UnsafeUtils::getFloat); + } + + @Test + void testDoubleOpsInOffHeap() { + testInOffHeap(Double.MAX_VALUE, UnsafeUtils::putDouble, UnsafeUtils::getDouble); + } + + void testInOffHeap(N number, BiConsumer addressConsumer, Function addressFunction) { + long address = -1L; + Class numberType = (Class) number.getClass(); + Serializer serializer = (Serializer) this.serializers.getMostCompatible(numberType); + try { + byte[] bytes = serializer.serialize(number); + address = allocateMemory(bytes.length); + addressConsumer.accept(address, number); + N returnValue = addressFunction.apply(address); + assertEquals(number, returnValue); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + if (address != -1L) { + freeMemory(address); + } + } + } + + @Test + void testArrayBaseOffset() { + assertEquals(LONG_ARRAY_BASE_OFFSET, arrayBaseOffset(long[].class)); + assertEquals(INT_ARRAY_BASE_OFFSET, arrayBaseOffset(int[].class)); + assertEquals(SHORT_ARRAY_BASE_OFFSET, arrayBaseOffset(short[].class)); + assertEquals(BYTE_ARRAY_BASE_OFFSET, arrayBaseOffset(byte[].class)); + assertEquals(BOOLEAN_ARRAY_BASE_OFFSET, arrayBaseOffset(boolean[].class)); + assertEquals(DOUBLE_ARRAY_BASE_OFFSET, arrayBaseOffset(double[].class)); + assertEquals(FLOAT_ARRAY_BASE_OFFSET, arrayBaseOffset(float[].class)); + assertEquals(CHAR_ARRAY_BASE_OFFSET, arrayBaseOffset(char[].class)); + assertEquals(OBJECT_ARRAY_BASE_OFFSET, arrayBaseOffset(Object[].class)); + } + + @Test + void testArrayIndexScale() { + assertEquals(LONG_ARRAY_INDEX_SCALE, arrayIndexScale(long[].class)); + assertEquals(INT_ARRAY_INDEX_SCALE, arrayIndexScale(int[].class)); + assertEquals(SHORT_ARRAY_INDEX_SCALE, arrayIndexScale(short[].class)); + assertEquals(BYTE_ARRAY_INDEX_SCALE, arrayIndexScale(byte[].class)); + assertEquals(BOOLEAN_ARRAY_INDEX_SCALE, arrayIndexScale(boolean[].class)); + assertEquals(DOUBLE_ARRAY_INDEX_SCALE, arrayIndexScale(double[].class)); + assertEquals(FLOAT_ARRAY_INDEX_SCALE, arrayIndexScale(float[].class)); + assertEquals(CHAR_ARRAY_INDEX_SCALE, arrayIndexScale(char[].class)); + assertEquals(OBJECT_ARRAY_INDEX_SCALE, arrayIndexScale(Object[].class)); + } + + @Test + void testAddressSize() { + assertTrue(addressSize() > 0); + } + + @Test + void testPageSize() { + assertTrue(pageSize() > 0); + } + + @Test + void testAllocateInstance() { + assertNotNull(allocateInstance(Model.class)); + } + + @Test + void testThrowException() { + assertThrows(RuntimeException.class, () -> throwException(new RuntimeException())); + } + + @Test + void testMemoryOps() { + long length = 8; + long address = allocateMemory(length); + length = 16; + assertEquals(address, reallocateMemory(address, length)); + + long newAddress = allocateMemory(length); + + setMemory(address, length, (byte) 1); + + for (int i = 0; i < length; i++) { + assertEquals((byte) 1, getByte(address + i)); + } + + copyMemory(address, newAddress, length); + + for (int i = 0; i < length; i++) { + assertEquals((byte) 1, getByte(newAddress + i)); + } + + setMemory(null, newAddress, length, (byte) 2); + + for (int i = 0; i < length; i++) { + assertEquals((byte) 2, getByte(newAddress + i)); + } + + long value = Long.MAX_VALUE; + + putAddress(address, value); + assertEquals(value, getAddress(address)); + + freeMemory(address); + freeMemory(newAddress); + } + + @Test + void testObjectMemoryOps() throws IOException { + + long value = Long.MAX_VALUE; + + Serializer serializer = (Serializer) this.serializers.getMostCompatible(Long.class); + byte[] bytes = serializer.serialize(value); + byte[] bytesCopy = new byte[bytes.length]; + + copyMemory(bytes, BOOLEAN_ARRAY_BASE_OFFSET, bytesCopy, BOOLEAN_ARRAY_BASE_OFFSET, 8); + + assertArrayEquals(bytes, bytesCopy); + } + + @Test + void testFenceOps() { + assertDoesNotThrow(UnsafeUtils::loadFence); + assertDoesNotThrow(UnsafeUtils::storeFence); + assertDoesNotThrow(UnsafeUtils::fullFence); + } + + @Test + void testArrayIndexOffsetOnIndexOutOfBoundsException() { + assertThrows(IndexOutOfBoundsException.class, () -> arrayIndexOffset(-1, 0, 0)); + } + + private static class Model { + private long longValue; + private int intValue; + private short shortValue; + private byte byteValue; + private boolean booleanValue; + private float floatValue; + private double doubleValue; + private char charValue; + private String stringValue; + private long[] longArrayValue = new long[10]; + private int[] intArrayValue = new int[10]; + private short[] shortArrayValue = new short[10]; + private byte[] byteArrayValue = new byte[10]; + private boolean[] booleanArrayValue = new boolean[10]; + private double[] doubleArrayValue = new double[10]; + private float[] floatArrayValue = new float[10]; + private char[] charArrayValue = new char[10]; + private Object[] objectArrayValue = new Object[10]; + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/process/ModernProcessIdResolverTest.java b/microsphere-java-core/src/test/java/io/microsphere/process/ModernProcessIdResolverTest.java index 8156ada8a..872fc5bfe 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/process/ModernProcessIdResolverTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/process/ModernProcessIdResolverTest.java @@ -40,7 +40,7 @@ void testCurrent() { if (isGEJava9) { assertNotNull(resolver.current()); } else { - assertThrows(NullPointerException.class, resolver::current); + assertThrows(IllegalArgumentException.class, resolver::current); } } diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/ConstructorUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/ConstructorUtilsTest.java index 5bcbb4c6a..348ec580b 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/ConstructorUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/ConstructorUtilsTest.java @@ -2,6 +2,7 @@ import io.microsphere.LoggingTest; +import io.microsphere.util.BaseUtils; import org.junit.jupiter.api.Test; import static io.microsphere.reflect.ConstructorUtils.findConstructor; @@ -96,7 +97,33 @@ void testFindConstructor() { } @Test - void testNewInstance() { + void testNewInstanceWithType() { + assertEquals("test", newInstance(String.class, "test")); + assertNotNull(newInstance(true, PrivateConstructorClass.class)); + assertNotNull(newInstance(PackagePrivateConstructorClass.class)); + assertNotNull(newInstance(ProtectedConstructorClass.class)); + assertNotNull(newInstance(PublicConstructorClass.class, "test")); + } + + @Test + void testNewInstanceWithTypeOnIllegalArgumentException() { + assertThrows(IllegalArgumentException.class, () -> newInstance(PrivateConstructorClass.class, "test")); + assertThrows(IllegalArgumentException.class, () -> newInstance(PackagePrivateConstructorClass.class, "test")); + assertThrows(IllegalArgumentException.class, () -> newInstance(ProtectedConstructorClass.class, "test")); + } + + @Test + void testNewInstanceWithTypeOnIllegalStateException() { + assertThrows(IllegalStateException.class, () -> newInstance(PrivateConstructorClass.class)); + } + + @Test + void testNewInstanceWithTypeOnRuntimeException() { + assertThrows(RuntimeException.class, () -> newInstance(MyUtils.class)); + } + + @Test + void testNewInstanceWithConstructor() { assertNotNull(newInstance(getConstructor(PublicConstructorClass.class))); assertThrows(RuntimeException.class, () -> newInstance(getConstructor(ProtectedConstructorClass.class))); assertThrows(RuntimeException.class, () -> newInstance(getConstructor(PackagePrivateConstructorClass.class))); @@ -126,4 +153,11 @@ static class PrivateConstructorClass { private PrivateConstructorClass() { } } + + static class MyUtils extends BaseUtils { + + protected MyUtils() { + super(); + } + } } diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/ExecutableUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/ExecutableUtilsTest.java index 56439f5f3..e7ef6d40b 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/ExecutableUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/ExecutableUtilsTest.java @@ -21,10 +21,14 @@ import java.lang.reflect.Method; import static io.microsphere.reflect.ExecutableUtils.execute; +import static io.microsphere.reflect.ExecutableUtils.matchParameterTypes; import static io.microsphere.reflect.MethodUtils.findMethod; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link ExecutableUtils} Test @@ -98,4 +102,17 @@ void testExecuteWithThrowableConsumerOnClassCastException() { })); } -} + @Test + void testExecuteWithThrowableSupplier() { + Method method = findMethod(ReflectionTest.class, "publicMethod", int.class); + assertNull(execute(method, () -> null)); + } + + @Test + void testMatchParameterTypes() { + Method method = findMethod(ReflectionTest.class, "publicMethod", int.class); + assertTrue(matchParameterTypes(method, 1)); + assertFalse(matchParameterTypes(method, 1L)); + assertFalse(matchParameterTypes(method)); + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/FieldUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/FieldUtilsTest.java index 4dbc8c77a..d5f76072b 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/FieldUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/FieldUtilsTest.java @@ -30,13 +30,11 @@ import static io.microsphere.reflect.FieldUtils.getDeclaredField; import static io.microsphere.reflect.FieldUtils.getFieldValue; import static io.microsphere.reflect.FieldUtils.getStaticFieldValue; -import static io.microsphere.reflect.FieldUtils.handleFieldException; import static io.microsphere.reflect.FieldUtils.setFieldValue; import static io.microsphere.reflect.FieldUtils.setStaticFieldValue; import static io.microsphere.util.VersionUtils.CURRENT_JAVA_VERSION; import static io.microsphere.util.VersionUtils.JAVA_VERSION_8; import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -187,46 +185,54 @@ void testGetStaticFieldValueOnField() { void testGetFieldValue() { String value = "Hello,World"; if (CURRENT_JAVA_VERSION.le(JAVA_VERSION_8)) { - assertArrayEquals(value.toCharArray(), getFieldValue(value, "value", char[].class)); + assertArrayEquals(value.toCharArray(), getFieldValue(true, value, "value", char[].class)); } else { - assertArrayEquals(value.getBytes(), getFieldValue(value, "value", byte[].class)); + assertArrayEquals(value.getBytes(), getFieldValue(true, value, "value", byte[].class)); } - assertGetFieldValue(test, "privateField"); - assertGetFieldValue(test, "packagePrivateField"); - assertGetFieldValue(test, "protectedField"); - assertGetFieldValue(test, "publicField"); + assertGetFieldValue(test, "privateField", true); + assertGetFieldValue(test, "packagePrivateField", false); + assertGetFieldValue(test, "protectedField", false); + assertGetFieldValue(test, "publicField", false); + + assertNotNull(getFieldValue(test, "packagePrivateField", String.class)); + assertNotNull(getFieldValue(test, "protectedField", String.class)); + assertNotNull(getFieldValue(test, "publicField", String.class)); } @Test void testGetFieldValueWithDefaultValue() { ReflectionTestExt testExt = new ReflectionTestExt(); - assertGetFieldValue(testExt, "integerField", 0); - assertGetFieldValue(testExt, "stringField", "test"); + assertGetFieldValue(testExt, "integerField", 0, true); + assertGetFieldValue(testExt, "stringField", "test", true); } @Test void testGetFieldValueOnIllegalArgumentException() { Field field = findField(test, "privateField"); - assertThrows(IllegalArgumentException.class, () -> getFieldValue("test", field)); + assertThrows(IllegalArgumentException.class, () -> getFieldValue(true, "test", field)); + } + + @Test + void testGetFieldValueOnIllegalStateException() { + assertThrows(IllegalStateException.class, () -> getFieldValue(this, "value", String.class)); } @Test void testGetFieldValueOnNull() { - Object fieldValue = getFieldValue(null, (Field) null); - assertNull(fieldValue); + assertThrows(NullPointerException.class, () -> getFieldValue(null, (String) null)); } @Test void testSetFieldValue() { Integer value = 999; - setFieldValue(value, "value", 2); - assertEquals(value.intValue(), setFieldValue(value, "value", 2)); + setFieldValue(true, value, "value", 2); + assertEquals(value.intValue(), setFieldValue(true, value, "value", 2)); - assertSetFieldValue(test, "privateField", "test"); - assertSetFieldValue(test, "packagePrivateField", "test"); - assertSetFieldValue(test, "protectedField", "test"); - assertSetFieldValue(test, "publicField", "test"); + assertSetFieldValue(test, "privateField", "test", true); + assertSetFieldValue(test, "packagePrivateField", "test", false); + assertSetFieldValue(test, "protectedField", "test", false); + assertSetFieldValue(test, "publicField", "test", false); } @Test @@ -241,10 +247,10 @@ void testSetFieldValueOnIllegalArgumentException() { @Test void testSetStaticFieldValue() { - setStaticFieldValue(getClass(), "value", "abc"); + setStaticFieldValue(true, getClass(), "value", "abc"); assertEquals("abc", value); - assertSetStaticFieldValue(ReflectionTest.class, "staticField", "test"); + assertSetStaticFieldValue(ReflectionTest.class, "staticField", "test", false); } @Test @@ -263,12 +269,6 @@ void testAssertFieldMatchTypeOnIllegalArgumentException() { assertThrows(IllegalArgumentException.class, () -> assertFieldMatchType(test, "privateField", Integer.class)); } - @Test - void testHandleFieldException() { - Field field = findField(ReflectionTest.class, "staticField"); - assertDoesNotThrow(() -> handleFieldException(new IllegalAccessException(), test, field)); - } - private void assertFindField(Object object, String fieldName) { assertNotNull(findField(object, fieldName)); } @@ -289,21 +289,41 @@ private void assertGetStaticFieldValue(Class klass, String fieldName) { assertEquals(fieldName, getStaticFieldValue(klass, fieldName)); } - private void assertGetFieldValue(ReflectionTest test, String fieldName) { - assertEquals(fieldName, getFieldValue(test, fieldName)); - assertEquals(fieldName, getFieldValue(test, fieldName, (Object) null)); + private void assertGetFieldValue(ReflectionTest test, String fieldName, boolean forceAccess) { + if (forceAccess) { + assertEquals(fieldName, getFieldValue(true, test, fieldName)); + assertEquals(fieldName, getFieldValue(true, test, fieldName, (Object) null)); + } else { + assertEquals(fieldName, getFieldValue(test, fieldName)); + assertEquals(fieldName, getFieldValue(test, fieldName, (Object) null)); + } } - private void assertGetFieldValue(ReflectionTest test, String fieldName, Object defaultValue) { - assertEquals(defaultValue, getFieldValue(test, fieldName, defaultValue)); + private void assertGetFieldValue(ReflectionTest test, String fieldName, Object defaultValue, boolean forceAccess) { + if (forceAccess) { + assertEquals(defaultValue, getFieldValue(true, test, fieldName, defaultValue)); + } else { + assertEquals(defaultValue, getFieldValue(test, fieldName, defaultValue)); + } } - private void assertSetFieldValue(ReflectionTest test, String fieldName, String fieldValue) { - assertEquals(fieldName, setFieldValue(test, fieldName, fieldValue)); + private void assertSetFieldValue(ReflectionTest test, String fieldName, String fieldValue, boolean forceAccess) { + Field field = findField(test, fieldName); + if (forceAccess) { + assertEquals(fieldName, setFieldValue(true, test, fieldName, fieldValue)); + setFieldValue(true, test, field, fieldValue); + } else { + assertEquals(fieldName, setFieldValue(test, fieldName, fieldValue)); + setFieldValue(test, field, fieldValue); + } } - private void assertSetStaticFieldValue(Class klass, String fieldName, String fieldValue) { - assertEquals(fieldName, setStaticFieldValue(klass, fieldName, fieldValue)); + private void assertSetStaticFieldValue(Class klass, String fieldName, String fieldValue, boolean forceAccess) { + if (forceAccess) { + assertEquals(fieldName, setStaticFieldValue(true, klass, fieldName, fieldValue)); + } else { + assertEquals(fieldName, setStaticFieldValue(klass, fieldName, fieldValue)); + } } private void assertGetDeclaredField(Class klass, String fieldName) { diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/JavaTypeTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/JavaTypeTest.java index 359479cd7..a20d41416 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/JavaTypeTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/JavaTypeTest.java @@ -30,6 +30,7 @@ import java.util.List; import java.util.Map; +import static io.microsphere.reflect.ConstructorUtils.newInstance; import static io.microsphere.reflect.JavaType.EMPTY_JAVA_TYPE_ARRAY; import static io.microsphere.reflect.JavaType.Kind.CLASS; import static io.microsphere.reflect.JavaType.NULL_JAVA_TYPE; @@ -39,7 +40,6 @@ import static io.microsphere.reflect.JavaType.fromMethodParameters; import static io.microsphere.reflect.JavaType.fromMethodReturnType; import static io.microsphere.reflect.MethodUtils.findMethod; -import static io.microsphere.util.ClassUtils.newInstance; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/MemberUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/MemberUtilsTest.java index 2568e6261..1bf48fc49 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/MemberUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/MemberUtilsTest.java @@ -13,6 +13,7 @@ import static io.microsphere.reflect.MemberUtils.asMember; import static io.microsphere.reflect.MemberUtils.isAbstract; import static io.microsphere.reflect.MemberUtils.isFinal; +import static io.microsphere.reflect.MemberUtils.isInvalidDeclaringClass; import static io.microsphere.reflect.MemberUtils.isNonPrivate; import static io.microsphere.reflect.MemberUtils.isNonStatic; import static io.microsphere.reflect.MemberUtils.isPrivate; @@ -181,4 +182,11 @@ void testAsMemberOnNotMember() { void testAsMemberOnNull() { assertNull(asMember(null)); } + + @Test + void testIsInvalidDeclaringClass() { + assertTrue(isInvalidDeclaringClass(null)); + assertTrue(isInvalidDeclaringClass(int.class)); + assertFalse(isInvalidDeclaringClass(ReflectionTest.class)); + } } diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java index 11bfa33fa..82c6bbe91 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java @@ -29,6 +29,7 @@ import java.util.AbstractList; import java.util.ArrayList; import java.util.Collection; +import java.util.EventListener; import java.util.List; import java.util.Objects; import java.util.function.Predicate; @@ -51,11 +52,14 @@ import static io.microsphere.reflect.MethodUtils.banMethod; import static io.microsphere.reflect.MethodUtils.buildKey; import static io.microsphere.reflect.MethodUtils.buildSignature; -import static io.microsphere.reflect.MethodUtils.clearBannedMethods; +import static io.microsphere.reflect.MethodUtils.clearBannedMethodsCache; +import static io.microsphere.reflect.MethodUtils.clearDeclaredMethodsCache; +import static io.microsphere.reflect.MethodUtils.clearMethodsCache; import static io.microsphere.reflect.MethodUtils.excludedDeclaredClass; import static io.microsphere.reflect.MethodUtils.findAllDeclaredMethods; import static io.microsphere.reflect.MethodUtils.findAllMethods; import static io.microsphere.reflect.MethodUtils.findDeclaredMethods; +import static io.microsphere.reflect.MethodUtils.findFunctionalInterfaceMethod; import static io.microsphere.reflect.MethodUtils.findMethod; import static io.microsphere.reflect.MethodUtils.findMethods; import static io.microsphere.reflect.MethodUtils.findNearestOverriddenMethod; @@ -73,6 +77,7 @@ import static io.microsphere.reflect.MethodUtils.isGetterMethod; import static io.microsphere.reflect.MethodUtils.isIsMethod; import static io.microsphere.reflect.MethodUtils.isObjectMethod; +import static io.microsphere.reflect.MethodUtils.isOverridenObjectMethod; import static io.microsphere.reflect.MethodUtils.isSetterMethod; import static io.microsphere.reflect.MethodUtils.matchesParameterCount; import static io.microsphere.reflect.MethodUtils.matchesReturnType; @@ -113,7 +118,9 @@ void setUp() { @AfterAll static void afterAll() { System.getProperties().remove(BANNED_METHODS_PROPERTY_NAME); - clearBannedMethods(); + clearMethodsCache(); + clearBannedMethodsCache(); + clearDeclaredMethodsCache(); } @Test @@ -207,8 +214,12 @@ void testGetDeclaredMethods() { methods = getDeclaredMethods(TestClass.class); assertEquals(7 + JACOCO_ADDED_METHOD_COUNT, methods.size()); + + methods = getDeclaredMethods(null); + assertTrue(methods.isEmpty()); } + @Test void testGetMethods() { List methods = getMethods(Object.class); @@ -222,6 +233,9 @@ void testGetMethods() { methods = getMethods(TestClass.class); assertEquals(1, methods.size()); + + methods = getMethods(null); + assertTrue(methods.isEmpty()); } @Test @@ -231,6 +245,9 @@ void testGetAllDeclaredMethods() { methods = getAllDeclaredMethods(TestClass.class); assertEquals(OBJECT_DECLARED_METHODS.size() + 7 + JACOCO_ADDED_METHOD_COUNT, methods.size()); + + methods = getAllDeclaredMethods(null); + assertTrue(methods.isEmpty()); } @Test @@ -243,6 +260,9 @@ void testGetAllMethods() { methods = getAllMethods(TestClass.class); assertEquals(OBJECT_PUBLIC_METHODS.size() + 1, methods.size()); + + methods = getAllMethods(null); + assertTrue(methods.isEmpty()); } @Test @@ -255,6 +275,9 @@ void testFindDeclaredMethods() { methods = findDeclaredMethods(TestClass.class, PUBLIC_METHOD_PREDICATE); assertEquals(1, methods.size()); + + methods = findDeclaredMethods(null); + assertTrue(methods.isEmpty()); } @Test @@ -270,6 +293,9 @@ void testFindMethods() { methods = findMethods(TestClass.class, PUBLIC_METHOD_PREDICATE); assertEquals(1, methods.size()); + + methods = findMethods(null); + assertTrue(methods.isEmpty()); } @Test @@ -282,6 +308,9 @@ void testFindAllDeclaredMethods() { methods = findAllDeclaredMethods(TestClass.class, PUBLIC_METHOD_PREDICATE); assertEquals(OBJECT_PUBLIC_METHODS.size() + 1, methods.size()); + + methods = findAllDeclaredMethods(null); + assertTrue(methods.isEmpty()); } @Test @@ -292,8 +321,11 @@ void testFindAllMethods() { methods = findAllMethods(Object.class, PUBLIC_METHOD_PREDICATE); assertEquals(OBJECT_PUBLIC_METHODS, methods); - methods = getAllMethods(TestClass.class); + methods = findAllMethods(TestClass.class); assertEquals(OBJECT_PUBLIC_METHODS.size() + 1, methods.size()); + + methods = findAllMethods(null); + assertTrue(methods.isEmpty()); } @Test @@ -435,24 +467,32 @@ void testFindMethodOnSuperInterfacesOnNotFound() { assertNull(findMethod(TestSubInterface.class, "notFound")); } + @Test + void testFindMethodOnNull() { + assertNull(findMethod(null, "equals", int.class)); + assertNull(findMethod(TestInterface.class, null, Object.class)); + assertNull(findMethod(TestInterface.class, "equals", null)); + } + + @Test + void testFindMethodOnPrimitiveType() { + assertNull(findMethod(int.class, "equals", int.class)); + } + @Test void testInvokeMethod() { String test = "test"; assertEquals(test, invokeMethod(test, "toString")); TestClass testClass = new TestClass(); - assertEquals(valueOf(0), invokeMethod(testClass, "intMethod")); - assertEquals(testClass, invokeMethod(testClass, "objectMethod")); + assertEquals(valueOf(0), invokeMethod(true, testClass, "intMethod")); + assertEquals(testClass, invokeMethod(true, testClass, "objectMethod")); } @Test void testInvokeMethodOnNullPointerException() { - assertThrows(NullPointerException.class, () -> invokeMethod("test", (Method) null)); - } - - @Test - void testInvokeMethodOnIllegalStateException() { - assertThrows(IllegalStateException.class, () -> invokeMethod("test", "notFound")); + assertThrows(IllegalArgumentException.class, () -> invokeMethod("test", (Method) null)); + assertThrows(IllegalArgumentException.class, () -> invokeMethod("test", "notFound")); } @Test @@ -470,7 +510,8 @@ void testInvokeMethodOnInvocationTargetException() { void testInvokeStaticMethod() { Method method = findMethod(Integer.class, "valueOf", int.class); assertEquals(valueOf(0), (Integer) invokeStaticMethod(method, 0)); - assertEquals(valueOf(0), (Integer) invokeStaticMethod(TestClass.class, "value", 0)); + assertEquals(valueOf(0), (Integer) invokeStaticMethod(true, TestClass.class, "value", 0)); + assertEquals(valueOf(0), (Integer) invokeStaticMethod(Integer.class, "parseInt", "0")); } @Test @@ -591,10 +632,18 @@ void testFindNearestOverriddenMethodOnObject() { } @Test - void test() { - Method[] methods = TestSubInterface.class.getDeclaredMethods(); - methods = TestInterface.class.getDeclaredMethods(); - assertNotNull(methods); + void testFindFunctionalInterfaceMethod() { + Method method = findMethod(Runnable.class, "run"); + assertEquals(method, findFunctionalInterfaceMethod(Runnable.class)); + + // non-interface type + assertNull(findFunctionalInterfaceMethod(Object.class)); + // No method was declared on the interface + assertNull(findFunctionalInterfaceMethod(EventListener.class)); + // More than one methods were declared on the interface + assertNull(findFunctionalInterfaceMethod(CharSequence.class)); + // Only default method(s) were declared on the interface + assertNull(findFunctionalInterfaceMethod(OnlyDefaultMethodsInterface.class)); } /** @@ -631,6 +680,15 @@ void testIsObjectMethod() { assertIsObjectMethod(false, String.class, "notFound"); } + @Test + void testIsOverridenObjectMethod() { + assertFalse(isOverridenObjectMethod(null)); + assertIsOverridenObjectMethod(false, Object.class, "hashCode"); + assertIsOverridenObjectMethod(true, String.class, "hashCode"); + assertIsOverridenObjectMethod(true, Collection.class, "equals", Object.class); + assertIsOverridenObjectMethod(false, MethodUtilsTest.class, "getMethod", String.class, Class[].class); + } + @Test void testIsIsMethod() { Method method = findMethod(RuntimeMXBean.class, "isBootClassPathSupported"); @@ -708,6 +766,31 @@ void testMatchesReturnType() { assertMatchesReturnType(void.class, "not-found-method"); } + @Test + void testBuildKey() { + assertMethodKey(String.class, "toString"); + assertMethodKey(ReflectionTest.class, "publicMethod", int.class); + assertMethodKey(Appendable.class, "append", CharSequence.class, int.class, int.class); + + MethodKey key = new MethodKey(String.class, "valueOf", boolean.class); + MethodKey key1 = new MethodKey(String.class, "valueOf", char.class); + MethodKey key2 = new MethodKey(String.class, "equals", Object.class); + MethodKey key3 = new MethodKey(Object.class, "equals", Object.class); + + assertNotEquals(key, key1); + assertNotEquals(key1, key2); + assertNotEquals(key1, key3); + assertFalse(key1.equals(this)); + } + + @Test + void testIsCallerSensitiveMethod() { + Method[] methods = MethodUtilsTest.class.getMethods(); + for (Method method : methods) { + assertFalse(isCallerSensitiveMethod(method)); + } + } + private void assertMatchesReturnType(Class expectedReturnType, String methodName, Class... parameterTypes) { Method method = getMethod(methodName, parameterTypes); if (method == null) { @@ -717,7 +800,7 @@ private void assertMatchesReturnType(Class expectedReturnType, String methodN } } - void assertMatchesParameterCount(int execptedCount, String methodName, Class... parameterTypes) { + private void assertMatchesParameterCount(int execptedCount, String methodName, Class... parameterTypes) { Method method = getMethod(methodName, parameterTypes); int parameterCount = parameterTypes.length; if (method == null) { @@ -740,7 +823,7 @@ void assertGetMethodName(String methodName) { } } - Method getMethod(String methodName, Class... parameterTypes) { + private Method getMethod(String methodName, Class... parameterTypes) { return findMethod(MethodUtilsTest.class, methodName, parameterTypes); } @@ -749,29 +832,9 @@ private void assertIsObjectMethod(boolean expected, Class declaredClass, Stri assertEquals(expected, isObjectMethod(method)); } - @Test - void testBuildKey() { - assertMethodKey(String.class, "toString"); - assertMethodKey(ReflectionTest.class, "publicMethod", int.class); - assertMethodKey(Appendable.class, "append", CharSequence.class, int.class, int.class); - - MethodKey key = new MethodKey(String.class, "valueOf", boolean.class); - MethodKey key1 = new MethodKey(String.class, "valueOf", char.class); - MethodKey key2 = new MethodKey(String.class, "equals", Object.class); - MethodKey key3 = new MethodKey(Object.class, "equals", Object.class); - - assertNotEquals(key, key1); - assertNotEquals(key1, key2); - assertNotEquals(key1, key3); - assertFalse(key1.equals(this)); - } - - @Test - void testIsCallerSensitiveMethod() { - Method[] methods = MethodUtilsTest.class.getMethods(); - for (Method method : methods) { - assertFalse(isCallerSensitiveMethod(method)); - } + private void assertIsOverridenObjectMethod(boolean expected, Class declaredClass, String methodName, Class... parameterTypes) { + Method method = findMethod(declaredClass, methodName, parameterTypes); + assertEquals(expected, isOverridenObjectMethod(method)); } private void assertMethodKey(Class declaredClass, String methodName, Class... parameterTypes) { @@ -837,6 +900,17 @@ interface TestSubInterface extends TestInterface { void subMethod(); } + interface OnlyDefaultMethodsInterface { + default void method1() { + } + + default void method2() { + } + + default void method3() { + } + } + void isMethod() { } diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/ModifierTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/ModifierTest.java index 91d23f87b..b76e3919e 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/ModifierTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/ModifierTest.java @@ -24,7 +24,11 @@ import java.util.concurrent.locks.AbstractQueuedSynchronizer; import static io.microsphere.reflect.FieldUtils.getStaticFieldValue; +import static io.microsphere.reflect.Modifier.FINAL; import static io.microsphere.reflect.Modifier.MANDATED; +import static io.microsphere.reflect.Modifier.PUBLIC; +import static io.microsphere.reflect.Modifier.SYNCHRONIZED; +import static io.microsphere.reflect.Modifier.VOLATILE; import static io.microsphere.reflect.Modifier.isAbstract; import static io.microsphere.reflect.Modifier.isAnnotation; import static io.microsphere.reflect.Modifier.isBridge; @@ -43,6 +47,8 @@ import static io.microsphere.reflect.Modifier.isTransient; import static io.microsphere.reflect.Modifier.isVarArgs; import static io.microsphere.reflect.Modifier.isVolatile; +import static io.microsphere.reflect.Modifier.matchesAll; +import static io.microsphere.reflect.Modifier.matchesAny; import static io.microsphere.reflect.Modifier.values; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -196,8 +202,32 @@ void testIsMandated() { assertFalse(isMandated(Object.class.getModifiers())); } + @Test + void testMatchesAny() { + for (Modifier modifier : values()) { + assertFalse(matchesAny(modifier::getValue)); + } + + assertTrue(matchesAny(String.class::getModifiers, PUBLIC)); + assertTrue(matchesAny(String.class::getModifiers, FINAL)); + assertTrue(matchesAny(Object.class::getModifiers, PUBLIC, FINAL)); + assertTrue(matchesAny(Object.class::getModifiers, FINAL, PUBLIC)); + assertFalse(matchesAny(String.class::getModifiers, SYNCHRONIZED)); + assertFalse(matchesAny(String.class::getModifiers, SYNCHRONIZED, VOLATILE)); + } + + @Test + void testMatchesAll() { + for (Modifier modifier : values()) { + assertTrue(matchesAll(modifier::getValue)); + } + + assertTrue(matchesAll(String.class::getModifiers, PUBLIC, FINAL)); + assertFalse(matchesAll(Object.class::getModifiers, PUBLIC, FINAL)); + } + private int findModifierValue(String name) { - return getStaticFieldValue(java.lang.reflect.Modifier.class, name); + return getStaticFieldValue(true, java.lang.reflect.Modifier.class, name); } } diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java index 75d66bdba..190e57e41 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java @@ -91,7 +91,7 @@ void testGetCallerClassNameOnStackWalkerSupportedForTesting() { @Test void testGetCallerClassNamesInStackWalker() { if (testCurrentJavaVersion("<", JAVA_VERSION_9)) { - assertThrows(NullPointerException.class, () -> getCallerClassNamesInStackWalker()); + assertThrows(IllegalArgumentException.class, () -> getCallerClassNamesInStackWalker()); } else { assertTrue(getCallerClassNamesInStackWalker().contains(CALLER_CLASS_NAME)); } diff --git a/microsphere-java-core/src/test/java/io/microsphere/test/Data.java b/microsphere-java-core/src/test/java/io/microsphere/test/Data.java index fedf7b861..dcb33afe4 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/test/Data.java +++ b/microsphere-java-core/src/test/java/io/microsphere/test/Data.java @@ -179,16 +179,16 @@ && compare(weight, data.weight) == 0 @Override public int hashCode() { - int result = name.hashCode(); - result = 31 * result + age; + int result = Objects.hashCode(name); + result = 31 * result + Integer.hashCode(age); result = 31 * result + Boolean.hashCode(male); result = 31 * result + Double.hashCode(height); result = 31 * result + Float.hashCode(weight); result = 31 * result + Long.hashCode(birth); - result = 31 * result + index; - result = 31 * result + grade; - result = 31 * result + sex; - result = 31 * result + object.hashCode(); + result = 31 * result + Short.hashCode(index); + result = 31 * result + Byte.hashCode(grade); + result = 31 * result + Character.hashCode(sex); + result = 31 * result + Objects.hashCode(object); result = 31 * result + Arrays.hashCode(names); return result; } diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java index 782789ab6..5c67b5acd 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java @@ -7,6 +7,7 @@ import io.microsphere.lang.ClassDataRepository; import io.microsphere.test.A; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.provider.ValueSource; import javax.annotation.Nonnull; import java.io.File; @@ -20,14 +21,17 @@ import java.util.AbstractMap; import java.util.Collection; import java.util.Date; +import java.util.EventListener; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.NavigableMap; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; +import java.util.function.BinaryOperator; import java.util.function.Predicate; import static io.microsphere.AbstractTestCase.createRandomTempFile; @@ -35,6 +39,8 @@ import static io.microsphere.collection.Lists.ofList; import static io.microsphere.collection.MapUtils.ofEntry; import static io.microsphere.constants.SymbolConstants.SPACE; +import static io.microsphere.event.EventDispatcher.DIRECT_EXECUTOR; +import static io.microsphere.reflect.ConstructorUtils.newInstance; import static io.microsphere.util.ArrayUtils.EMPTY_BOOLEAN_ARRAY; import static io.microsphere.util.ArrayUtils.EMPTY_BYTE_ARRAY; import static io.microsphere.util.ArrayUtils.EMPTY_CHAR_ARRAY; @@ -48,6 +54,7 @@ import static io.microsphere.util.ArrayUtils.ofArray; import static io.microsphere.util.ClassLoaderUtils.getResource; import static io.microsphere.util.ClassUtils.ARRAY_SUFFIX; +import static io.microsphere.util.ClassUtils.LAMBDA_CLASS_NAME_PREFIX; import static io.microsphere.util.ClassUtils.PRIMITIVE_TYPES; import static io.microsphere.util.ClassUtils.arrayTypeEquals; import static io.microsphere.util.ClassUtils.cast; @@ -82,13 +89,17 @@ import static io.microsphere.util.ClassUtils.isDerived; import static io.microsphere.util.ClassUtils.isEnum; import static io.microsphere.util.ClassUtils.isFinal; +import static io.microsphere.util.ClassUtils.isFunctionalInterface; import static io.microsphere.util.ClassUtils.isGeneralClass; +import static io.microsphere.util.ClassUtils.isInterface; +import static io.microsphere.util.ClassUtils.isLambdaClass; +import static io.microsphere.util.ClassUtils.isLambdaClassName; import static io.microsphere.util.ClassUtils.isNumber; import static io.microsphere.util.ClassUtils.isPrimitive; import static io.microsphere.util.ClassUtils.isSimpleType; +import static io.microsphere.util.ClassUtils.isSyntheticClass; import static io.microsphere.util.ClassUtils.isTopLevelClass; import static io.microsphere.util.ClassUtils.isWrapperType; -import static io.microsphere.util.ClassUtils.newInstance; import static io.microsphere.util.ClassUtils.resolveClassName; import static io.microsphere.util.ClassUtils.resolvePackageName; import static io.microsphere.util.ClassUtils.resolvePrimitiveClassForName; @@ -120,6 +131,7 @@ class ClassUtilsTest extends LoggingTest { @Test void testConstants() { assertEquals("[]", ARRAY_SUFFIX); + assertEquals("$$Lambda", LAMBDA_CLASS_NAME_PREFIX); } @Test @@ -188,6 +200,13 @@ void testIsAbstractClass() { assertFalse(isAbstractClass(Object.class)); } + @Test + void testIsInterface() { + assertTrue(isInterface(List.class)); + assertFalse(isInterface(String.class)); + assertFalse(isInterface(null)); + } + @Test void testIsGeneralClass() { // test null @@ -218,7 +237,7 @@ void testIsTopLevelClass() { assertFalse(isTopLevelClass(null)); assertTrue(isTopLevelClass(Object.class)); assertTrue(isTopLevelClass(String.class)); - assertFalse(isTopLevelClass(Map.Entry.class)); + assertFalse(isTopLevelClass(Entry.class)); class A { @@ -227,6 +246,56 @@ class A { assertFalse(isTopLevelClass(A.class)); } + @Test + void testIsSyntheticClass() { + Runnable r = () -> { + }; + assertTrue(isSyntheticClass(r)); + assertFalse(isSyntheticClass(new Object())); + assertFalse(isSyntheticClass(null)); + } + + @Test + void testIsLambdaClass() { + Runnable r = () -> { + }; + assertTrue(isLambdaClass(r)); + assertTrue(isLambdaClass(DIRECT_EXECUTOR)); + assertFalse(isLambdaClass(new Object())); + assertFalse(isLambdaClass(null)); + + ValueSource valueSource = LoggingTest.class.getAnnotation(ValueSource.class); + assertFalse(isLambdaClass(valueSource)); + } + + @Test + void testIsLambdaClassName() { + assertFalse(isLambdaClassName(null)); + assertFalse(isLambdaClassName("io.microsphere.util.ClassUtilsTest")); + assertFalse(isLambdaClassName("io.microsphere.util.ClassUtilsTest$$Lambda")); + assertFalse(isLambdaClassName("io.microsphere.util.ClassUtilsTest/")); + assertTrue(isLambdaClassName("io.microsphere.util.ClassUtilsTest$$Lambda$1/0x0000000800b00440")); + } + + @Test + void testIsFunctionalInterface() { + assertFalse(isFunctionalInterface(null)); + assertFalse(isFunctionalInterface(Object.class)); + assertFalse(isFunctionalInterface(Serializable.class)); + assertFalse(isFunctionalInterface(EventListener.class)); + assertFalse(isFunctionalInterface(Collection.class)); + // Runnable is annotated with @FunctionalInterface + assertTrue(isFunctionalInterface(Runnable.class)); + // Comparable is not annotated with @FunctionalInterface, and it has only one abstract method compareTo(T o) + assertTrue(isFunctionalInterface(Comparable.class)); + // Annotation + assertFalse(isFunctionalInterface(ValueSource.class)); + + assertTrue(isFunctionalInterface(StringDescriptive.class)); + // + assertFalse(isFunctionalInterface(Calculator.class)); + } + @Test void testIsPrimitive() { assertTrue(isPrimitive(void.class)); @@ -754,7 +823,7 @@ void testGetTypeNameWithClass() { assertEquals("java.lang.String", getTypeName(String.class)); // b) Nested classes (static member classes) - assertEquals("java.util.Map$Entry", getTypeName(Map.Entry.class)); + assertEquals("java.util.Map$Entry", getTypeName(Entry.class)); // c) Inner classes (non-static member classes) assertEquals("java.lang.Thread$State", getTypeName(Thread.State.class)); @@ -795,7 +864,7 @@ void testGetSimpleName() { assertEquals("String", getSimpleName(String.class)); // b) Nested classes (static member classes) - assertEquals("Entry", getSimpleName(Map.Entry.class)); + assertEquals("Entry", getSimpleName(Entry.class)); // c) Inner classes (non-static member classes) assertEquals("State", getSimpleName(Thread.State.class)); @@ -972,4 +1041,27 @@ private void assertFindClassNamesInClassPath(URL location) { Set classNamesInClassPath = findClassNamesInClassPath(path, true); assertFalse(classNamesInClassPath.isEmpty()); } + + interface Descriptive { + + CharSequence getDescription(); + } + + interface StringDescriptive extends Descriptive { + + @Override + String getDescription(); + + static String name() { + return "StringDescriptive"; + } + } + + interface Calculator { + // A default method containing a lambda expression + default int getAdder() { + BinaryOperator adder = (a, b) -> a + b; + return adder.apply(5, 10); + } + } } diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/SizeUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/SizeUtilsTest.java new file mode 100644 index 000000000..467ec75f4 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/util/SizeUtilsTest.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. + */ + +package io.microsphere.util; + + +import org.junit.jupiter.api.Test; + + +import static io.microsphere.util.SizeUtils.BOOLEAN_BYTES_SIZE; +import static io.microsphere.util.SizeUtils.BYTE_BYTES_SIZE; +import static io.microsphere.util.SizeUtils.CHAR_BYTES_SIZE; +import static io.microsphere.util.SizeUtils.DOUBLE_BYTES_SIZE; +import static io.microsphere.util.SizeUtils.FLOAT_BYTES_SIZE; +import static io.microsphere.util.SizeUtils.INTEGER_BYTES_SIZE; +import static io.microsphere.util.SizeUtils.LONG_BYTES_SIZE; +import static io.microsphere.util.SizeUtils.SHORT_BYTES_SIZE; +import static io.microsphere.util.SizeUtils.UNBOUND_BYTES_SIZE; +import static io.microsphere.util.SizeUtils.bytesSize; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * {@link SizeUtils} Test + * + * @author Mercy + * @see SizeUtils + * @since 1.0.0 + */ +class SizeUtilsTest { + + @Test + void testConstants() { + assertEquals(-1, UNBOUND_BYTES_SIZE); + assertEquals(1, BOOLEAN_BYTES_SIZE); + assertEquals(1, BYTE_BYTES_SIZE); + assertEquals(2, SHORT_BYTES_SIZE); + assertEquals(2, CHAR_BYTES_SIZE); + assertEquals(4, INTEGER_BYTES_SIZE); + assertEquals(4, FLOAT_BYTES_SIZE); + assertEquals(8, LONG_BYTES_SIZE); + assertEquals(8, DOUBLE_BYTES_SIZE); + } + + @Test + void testBytesSize() { + assertEquals(UNBOUND_BYTES_SIZE, bytesSize(Object.class)); + assertEquals(BOOLEAN_BYTES_SIZE, bytesSize(boolean.class)); + assertEquals(BOOLEAN_BYTES_SIZE, bytesSize(Boolean.class)); + assertEquals(BYTE_BYTES_SIZE, bytesSize(byte.class)); + assertEquals(BYTE_BYTES_SIZE, bytesSize(Byte.class)); + assertEquals(SHORT_BYTES_SIZE, bytesSize(short.class)); + assertEquals(SHORT_BYTES_SIZE, bytesSize(Short.class)); + assertEquals(CHAR_BYTES_SIZE, bytesSize(char.class)); + assertEquals(CHAR_BYTES_SIZE, bytesSize(Character.class)); + assertEquals(INTEGER_BYTES_SIZE, bytesSize(int.class)); + assertEquals(INTEGER_BYTES_SIZE, bytesSize(Integer.class)); + assertEquals(FLOAT_BYTES_SIZE, bytesSize(float.class)); + assertEquals(FLOAT_BYTES_SIZE, bytesSize(Float.class)); + assertEquals(LONG_BYTES_SIZE, bytesSize(long.class)); + assertEquals(LONG_BYTES_SIZE, bytesSize(Long.class)); + assertEquals(DOUBLE_BYTES_SIZE, bytesSize(double.class)); + assertEquals(DOUBLE_BYTES_SIZE, bytesSize(Double.class)); + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/resources/junit-platform.properties b/microsphere-java-core/src/test/resources/junit-platform.properties deleted file mode 100644 index 5c558927b..000000000 --- a/microsphere-java-core/src/test/resources/junit-platform.properties +++ /dev/null @@ -1,3 +0,0 @@ -junit.jupiter.extensions.autodetection.enabled = true - -junit.jupiter.execution.parallel.enabled = false \ No newline at end of file diff --git a/microsphere-java-core/src/test/resources/logback-test.xml b/microsphere-java-core/src/test/resources/logback-test.xml deleted file mode 100644 index e3375b74c..000000000 --- a/microsphere-java-core/src/test/resources/logback-test.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - ${ENCODER_PATTERN} - - - - - - - - \ No newline at end of file diff --git a/microsphere-java-dependencies/pom.xml b/microsphere-java-dependencies/pom.xml index aba268cdb..f8393f34f 100644 --- a/microsphere-java-dependencies/pom.xml +++ b/microsphere-java-dependencies/pom.xml @@ -45,6 +45,12 @@ ${revision} + + io.github.microsphere-projects + microsphere-annotation-test + ${revision} + + io.github.microsphere-projects microsphere-lang-model diff --git a/microsphere-java-parent/pom.xml b/microsphere-java-parent/pom.xml index 5bde8ae19..25f250f42 100644 --- a/microsphere-java-parent/pom.xml +++ b/microsphere-java-parent/pom.xml @@ -19,7 +19,7 @@ Microsphere Java Parent - 0.3.4 + 0.3.8 7.0.8 diff --git a/microsphere-java-test/pom.xml b/microsphere-java-test/pom.xml index 40504c98e..fc82e8388 100644 --- a/microsphere-java-test/pom.xml +++ b/microsphere-java-test/pom.xml @@ -20,17 +20,10 @@ - + io.github.microsphere-projects - microsphere-java-core - ${revision} - - - - - io.github.microsphere-projects - microsphere-jdk-tools + microsphere-java-annotations ${revision} true diff --git a/microsphere-java-test/src/main/resources/logback-test.xml b/microsphere-java-test/src/main/resources/logback-test.xml index 888b2ff1b..24649d370 100644 --- a/microsphere-java-test/src/main/resources/logback-test.xml +++ b/microsphere-java-test/src/main/resources/logback-test.xml @@ -2,6 +2,19 @@ + + test.log + false + false + + ${ENCODER_PATTERN} + + + + + + + ${ENCODER_PATTERN} @@ -13,7 +26,7 @@ - + \ No newline at end of file diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/model/CollectionTypeModelTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/model/CollectionTypeModelTest.java index c235e4c32..9dd66bfea 100644 --- a/microsphere-java-test/src/test/java/io/microsphere/test/model/CollectionTypeModelTest.java +++ b/microsphere-java-test/src/test/java/io/microsphere/test/model/CollectionTypeModelTest.java @@ -9,7 +9,6 @@ import java.util.LinkedList; import java.util.List; -import static io.microsphere.collection.SetUtils.newHashSet; import static io.microsphere.test.model.Color.BLUE; import static io.microsphere.test.model.Color.RED; import static io.microsphere.test.model.Color.YELLOW; @@ -91,7 +90,7 @@ void testSetGetModels() { @Test void testSetGetModelArrays() { - HashSet testSet = newHashSet(); + HashSet testSet = new HashSet<>(); testSet.add(new Model[]{new Model(), new Model()}); testSet.add(new Model[]{new Model()}); diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/model/MapTypeModelTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/model/MapTypeModelTest.java index 0194682a8..9dbe44289 100644 --- a/microsphere-java-test/src/test/java/io/microsphere/test/model/MapTypeModelTest.java +++ b/microsphere-java-test/src/test/java/io/microsphere/test/model/MapTypeModelTest.java @@ -6,8 +6,6 @@ import java.util.HashMap; import java.util.TreeMap; -import static io.microsphere.collection.MapUtils.newHashMap; -import static io.microsphere.collection.MapUtils.newTreeMap; import static io.microsphere.test.model.Color.BLUE; import static io.microsphere.test.model.Color.RED; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -39,7 +37,7 @@ void testDefaultValues() { @Test void testSetGetStrings() { - HashMap testMap = newHashMap(); + HashMap testMap = new HashMap<>(); testMap.put("key1", "value1"); testMap.put("key2", "value2"); @@ -52,7 +50,7 @@ void testSetGetStrings() { @Test void testSetGetColors() { - TreeMap testMap = newTreeMap(); + TreeMap testMap = new TreeMap<>(); testMap.put("red_key", RED); testMap.put("blue_key", BLUE); @@ -65,7 +63,7 @@ void testSetGetColors() { @Test void testSetGetPrimitiveTypeModels() { - TreeMap testMap = newTreeMap(); + TreeMap testMap = new TreeMap<>(); testMap.put(RED, new PrimitiveTypeModel()); testMap.put(BLUE, new PrimitiveTypeModel()); @@ -78,7 +76,7 @@ void testSetGetPrimitiveTypeModels() { @Test void testSetGetModels() { - HashMap testMap = newHashMap(); + HashMap testMap = new HashMap<>(); testMap.put("model1", new Model()); testMap.put("model2", new Model()); @@ -123,7 +121,7 @@ void testSetNullValues() { @Test void testMapMutability() { - HashMap originalMap = newHashMap(); + HashMap originalMap = new HashMap<>(); originalMap.put("initial", "value"); model.setStrings(originalMap); diff --git a/microsphere-jdk-tools/pom.xml b/microsphere-jdk-tools/pom.xml index b0822a8de..a99cf3301 100644 --- a/microsphere-jdk-tools/pom.xml +++ b/microsphere-jdk-tools/pom.xml @@ -40,6 +40,14 @@ test + + + io.github.microsphere-projects + microsphere-java-test + ${revision} + test + + org.mockito diff --git a/microsphere-jdk-tools/src/main/java/io/microsphere/jdk/tools/compiler/Compiler.java b/microsphere-jdk-tools/src/main/java/io/microsphere/jdk/tools/compiler/Compiler.java index 595bc9355..c6fa97846 100644 --- a/microsphere-jdk-tools/src/main/java/io/microsphere/jdk/tools/compiler/Compiler.java +++ b/microsphere-jdk-tools/src/main/java/io/microsphere/jdk/tools/compiler/Compiler.java @@ -16,6 +16,7 @@ */ package io.microsphere.jdk.tools.compiler; +import io.microsphere.io.StringBuilderWriter; import io.microsphere.logging.Logger; import javax.annotation.processing.Processor; @@ -72,7 +73,8 @@ public class Compiler { private static final Logger logger = getLogger(Compiler.class); - public static final String[] DEFAULT_OPTIONS = ofArray("-parameters", "-Xlint:unchecked", "-nowarn", "-Xlint:deprecation"); + public static final String[] DEFAULT_OPTIONS = ofArray("-parameters", "-Xlint:-unchecked", "-nowarn", + "-Xlint:deprecation"); private final Set sourcePaths; @@ -153,11 +155,14 @@ public Compiler charset(Charset charset) { public boolean compile(Class... sourceClasses) throws IOException { JavaCompiler javaCompiler = getJavaCompiler(); + StringBuilderWriter writer = new StringBuilderWriter(); try (StandardJavaFileManager javaFileManager = getJavaFileManager()) { - CompilationTask task = javaCompiler.getTask(null, javaFileManager, + CompilationTask task = javaCompiler.getTask(writer, javaFileManager, getDiagnosticListener(), getOptions(), null, getJavaFileObjects(javaFileManager, sourceClasses)); task.setProcessors(this.getProcessors()); return task.call(); + } finally { + logger.trace("The output of the Java compiler:\n{}", writer); } } diff --git a/microsphere-jdk-tools/src/test/resources/logback-test.xml b/microsphere-jdk-tools/src/test/resources/logback-test.xml deleted file mode 100644 index e3375b74c..000000000 --- a/microsphere-jdk-tools/src/test/resources/logback-test.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - ${ENCODER_PATTERN} - - - - - - - - \ No newline at end of file diff --git a/microsphere-lang-model/pom.xml b/microsphere-lang-model/pom.xml index 101084931..d2f238518 100644 --- a/microsphere-lang-model/pom.xml +++ b/microsphere-lang-model/pom.xml @@ -48,10 +48,10 @@ test - + io.github.microsphere-projects - microsphere-java-test + microsphere-annotation-test ${revision} test diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitor.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitor.java index 6e921a2c5..8301f8c1d 100644 --- a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitor.java +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitor.java @@ -170,7 +170,7 @@ public Object visitAnnotation(AnnotationMirror a, ExecutableElement attributeMet } Class annotationClass = loadClass(a.getAnnotationType()); - return invokeStaticMethod(annotationForMapMethod, annotationClass, attributesMap); + return invokeStaticMethod(true, annotationForMapMethod, annotationClass, attributesMap); } @Override diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/TypeUtils.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/TypeUtils.java index 6b363b167..94acbbb0e 100644 --- a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/TypeUtils.java +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/TypeUtils.java @@ -2463,7 +2463,7 @@ static String getTypeName(TypeMirror type) { List typeParameterElements = element.getTypeParameters(); int typeParameterElementsSize = typeParameterElements.size(); if (typeParameterElementsSize > 0) { - List typeMirrors = invokeMethod(type, "getTypeArguments"); + List typeMirrors = invokeMethod(true, type, "getTypeArguments"); int size = typeMirrors.size(); if (size > 0) { StringBuilder typeBuilder = new StringBuilder(element.toString()); diff --git a/pom.xml b/pom.xml index b7c8190aa..b5eb35fb3 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.microsphere-projects microsphere-build - 0.3.8 + 0.3.11 io.github.microsphere-projects @@ -51,7 +51,7 @@ - 0.3.15-SNAPSHOT + 0.3.16-SNAPSHOT @@ -61,6 +61,7 @@ microsphere-java-core microsphere-jdk-tools microsphere-java-test + microsphere-annotation-test microsphere-lang-model microsphere-annotation-processor diff --git a/release-notes.md b/release-notes.md index 36d2f1417..f789006f8 100644 --- a/release-notes.md +++ b/release-notes.md @@ -354,4 +354,33 @@ For complete details, see the [full changelog](#). ## Other Changes - Bumped version to the next patch after publishing `0.3.13`. ([155bb4fc](#)) -**Full Changelog**: https://github.com/microsphere-projects/microsphere-java/compare/0.3.13...0.3.14 \ No newline at end of file +**Full Changelog**: https://github.com/microsphere-projects/microsphere-java/compare/0.3.13...0.3.14## v0.3.15 + +# Release Notes - Version 0.3.15 + +## Dependency Updates +- Upgraded Microsphere BOM to version `0.3.4`. +- Updated Microsphere Build parent to version `0.3.8`. + +## Build and Workflow Enhancements +- Merged `main` branch into `release`, ensuring parity. [skip-ci] + +## Test Improvements +- Adjusted spacing and formatting in various test classes: + - `MessagerUtilsTest` + - `MapTypeModelTest` + - `FormatBeforeTest` super call. + +## Code Quality Improvements +- Standardized indentation and formatting in utility classes: + - `SetUtils` + - `QueueUtils` factory methods. +- Trimmed unnecessary whitespace in `StringUtils`. +- Aligned wrapped `assertTrue` message indentation. +- Improved Javadoc `@param` formatting in `Compatible`. +- Aligned parameter indentation in `Maps` methods. +- Removed unused `FILE_SEPARATOR` import. + +--- + +**Full Changelog**: https://github.com/microsphere-projects/microsphere-java/compare/0.3.14...0.3.15 \ No newline at end of file