Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2016-2020 chronicle.software
*
* https://chronicle.software
*
* Licensed 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 net.openhft.chronicle.wire;
import net.openhft.chronicle.bytes.Bytes;
import net.openhft.chronicle.bytes.MethodId;
import net.openhft.chronicle.bytes.MethodReader;
import net.openhft.chronicle.bytes.MethodReaderInterceptorReturns;
import net.openhft.chronicle.core.Jvm;
import net.openhft.chronicle.core.Maths;
import net.openhft.chronicle.core.annotation.UsedViaReflection;
import net.openhft.chronicle.core.io.Closeable;
import net.openhft.chronicle.core.util.InvocationTargetRuntimeException;
import net.openhft.chronicle.core.util.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
import static net.openhft.chronicle.wire.VanillaWireParser.SKIP_READABLE_BYTES;
/**
* This is the VanillaMethodReader class implementing the MethodReader interface.
* The class primarily handles reading methods from a MarshallableIn source and provides related utilities.
* It works with WireParselet, MethodReaderInterceptorReturns, and other constructs to facilitate the reading process.
*/
@SuppressWarnings({"rawtypes","this-escape"})
public class VanillaMethodReader implements MethodReader {
// beware enabling DEBUG_ENABLED as logMessage will not work unless Wire marshalling used - https://github.com/ChronicleEnterprise/Chronicle-Services/issues/240
public static final boolean DEBUG_ENABLED = Jvm.isDebugEnabled(VanillaMethodReader.class) && Jvm.getBoolean("wire.mr.debug");
static final Object[] NO_ARGS = {};
static final Object IGNORED = new Object(); // object used to flag that the call should be ignored.
private final MarshallableIn in;
@NotNull
private final WireParser metaWireParser;
private final WireParser dataWireParser;
private final MethodReaderInterceptorReturns methodReaderInterceptorReturns;
private final Predicate predicate;
private MessageHistory messageHistory;
private boolean closeIn = false;
private boolean closed;
/**
* Constructor for creating an instance of VanillaMethodReader with specified parameters.
* It uses default values for certain parameters like SKIP_READABLE_BYTES and creates the instance accordingly.
*
* @param in The MarshallableIn source from which methods are read.
* @param ignoreDefault Flag to determine if defaults should be ignored.
* @param defaultParselet The default parselet to use.
* @param methodReaderInterceptorReturns The interceptor for method reading.
* @param metaDataHandler Metadata handler array.
* @param objects Varargs for additional parameters.
*/
@UsedViaReflection
public VanillaMethodReader(MarshallableIn in,
boolean ignoreDefault,
WireParselet defaultParselet,
MethodReaderInterceptorReturns methodReaderInterceptorReturns,
Object[] metaDataHandler,
@NotNull Object... objects) {
this(in, ignoreDefault, defaultParselet, SKIP_READABLE_BYTES, methodReaderInterceptorReturns, metaDataHandler, objects);
}
@UsedViaReflection
public VanillaMethodReader(MarshallableIn in,
boolean ignoreDefault,
WireParselet defaultParselet,
FieldNumberParselet fieldNumberParselet,
MethodReaderInterceptorReturns methodReaderInterceptorReturns,
@NotNull Object... objects) {
this(in, ignoreDefault, defaultParselet, fieldNumberParselet, methodReaderInterceptorReturns, null, objects);
}
/**
* This constructor is an overloaded version of the primary VanillaMethodReader constructor.
* It initializes the reader with default predicate logic (always true) and delegates the initialization
* to the primary constructor.
*
* @param in The MarshallableIn source from which methods are read.
* @param ignoreDefault Flag to determine if defaults should be ignored.
* @param defaultParselet The default parselet to be used for parsing.
* @param fieldNumberParselet Custom parselet for handling field numbers.
* @param methodReaderInterceptorReturns Interceptor for reading methods.
* @param metaDataHandler Metadata handler array.
* @param objects Varargs for additional parameters, including custom parselets.
*/
public VanillaMethodReader(MarshallableIn in,
boolean ignoreDefault,
WireParselet defaultParselet,
FieldNumberParselet fieldNumberParselet,
MethodReaderInterceptorReturns methodReaderInterceptorReturns,
Object[] metaDataHandler,
@NotNull Object... objects) {
this(in,
ignoreDefault,
defaultParselet,
fieldNumberParselet,
methodReaderInterceptorReturns, metaDataHandler,
x -> true, objects);
}
/**
* This is the primary constructor for the VanillaMethodReader class. It initializes the reader
* with a provided input source, parsing strategies, interceptors, metadata handlers, and other configurations.
* It supports a comprehensive configuration by accepting multiple parameters and sets up parsers accordingly.
*
* @param in The MarshallableIn source from which methods are read.
* @param ignoreDefault Flag to determine if defaults should be ignored.
* @param defaultParselet The default parselet to be used for parsing.
* @param fieldNumberParselet Custom parselet for handling field numbers.
* @param methodReaderInterceptorReturns Interceptor for reading methods.
* @param metaDataHandler Metadata handler array.
* @param predicate Predicate for filtering.
* @param objects Varargs for additional parameters, including custom parselets.
*/
public VanillaMethodReader(MarshallableIn in,
boolean ignoreDefault,
WireParselet defaultParselet,
FieldNumberParselet fieldNumberParselet,
MethodReaderInterceptorReturns methodReaderInterceptorReturns,
Object[] metaDataHandler,
Predicate predicate,
@NotNull Object... objects) {
this.in = in;
this.methodReaderInterceptorReturns = methodReaderInterceptorReturns;
this.predicate = predicate;
// If the first object in the varargs is of type WireParselet, set it as the default parselet
if (objects[0] instanceof WireParselet)
defaultParselet = (WireParselet) objects[0];
// Set up wire parsers with default actions and strategies
metaWireParser = WireParser.wireParser((s, in0) -> in0.skipValue());
dataWireParser = WireParser.wireParser(defaultParselet, fieldNumberParselet);
// Add parsers for components based on provided configurations and objects
addParsersForComponents(metaWireParser, ignoreDefault,
addObjectsToMetaDataHandlers(metaDataHandler, objects));
addParsersForComponents(dataWireParser, ignoreDefault, objects);
// Add a parser for the message history if not already present
if (dataWireParser.lookup(HISTORY) == null) {
dataWireParser.register(new MethodWireKey(HISTORY, MESSAGE_HISTORY_METHOD_ID), (s, v) -> v.marshallable(messageHistory));
}
}
/**
* Fetches the LongConversion annotation for the first parameter of the provided method.
* If no such annotation exists, it returns null.
*
* @param m The method whose parameter annotations are to be checked.
* @return The LongConversion annotation for the first parameter or null if not present.
*/
private static LongConversion longConversionForFirstParam(Method m) {
Annotation[][] annotations = m.getParameterAnnotations();
// Check if there are any annotations for the first parameter
if (annotations.length < 1 || annotations[0].length < 1)
return null;
// Loop through all annotations of the first parameter
for (Annotation annotation : annotations[0]) {
if (annotation instanceof LongConversion)
return (LongConversion) annotation;
}
return null;
}
/**
* Invokes the provided method with a single long argument, which is parsed or converted based on certain conditions.
* Provides support for debug logging, context updates, method interception, and exception handling.
*
* @param o The object on which the method is to be invoked.
* @param context The context of the method invocation.
* @param m The method to be invoked.
* @param name Name of the method (currently unused but might be useful for future logging or debugging).
* @param mh Optional MethodHandle for invoking the method (may be null).
* @param argArr Arguments array for the method.
* @param s The CharSequence input (used for logging in debug mode).
* @param v ValueIn from which the long argument is extracted.
* @param methodReaderInterceptor Interceptor for the method invocation.
*/
private static void invokeMethodWithOneLong(Object o, Object[] context, @NotNull Method m, String name, MethodHandle mh, Object[] argArr, CharSequence s, ValueIn v, MethodReaderInterceptorReturns methodReaderInterceptor) {
try {
// Log the message if debugging is enabled
if (Jvm.isDebug())
logMessage(s, v);
// Update the context if it's null
if (context[0] == null)
updateContext(context, o);
// Parse or convert the argument from the input ValueIn
long arg = 0;
if (v.isBinary()) {
arg = v.int64();
} else {
LongConversion lc = longConversionForFirstParam(m);
if (lc == null) {
arg = v.int64();
} else {
String text = v.text();
if (text != null && !text.isEmpty()) {
LongConverter longConverter = (LongConverter) ObjectUtils.newInstance(lc.value());
arg = longConverter.parse(text);
}
}
}
// Handle method interception
if (methodReaderInterceptor != null) {
argArr[0] = arg;
Object intercept = methodReaderInterceptor.intercept(m, context[0], argArr, VanillaMethodReader::actualInvoke);
updateContext(context, intercept);
} else {
if (mh == null) {
argArr[0] = arg;
updateContext(context, m.invoke(context[0], argArr));
} else {
try {
if (m.getReturnType() == void.class) {
mh.invokeExact(arg);
updateContext(context, null);
} else {
updateContext(context, mh.invokeExact(arg));
}
} catch (Throwable t) {
throw new InvocationTargetException(t);
}
}
}
} catch (InvocationTargetException e) {
throw new InvocationTargetRuntimeException(e);
} catch (Throwable e) {
String msg = "Failure to dispatch message: " + m.getName() + " " + Arrays.asList(argArr);
Jvm.warn().on(o.getClass(), msg, e);
}
}
/**
* Updates the provided context with the intercepted object.
*
* @param context The array containing the context that will be updated.
* @param intercept The intercepted object that will be set as the new context.
*/
private static void updateContext(Object[] context, Object intercept) {
// System.err.println("context: " + (intercept == null ? null : intercept.getClass()));
context[0] = intercept;
}
/**
* Invokes the specified method on the provided object with the given arguments.
*
* @param method The method to be invoked.
* @param o The object on which the method is to be invoked.
* @param objects The arguments for the method.
* @return The result of the method invocation.
* @throws InvocationTargetException if the method invocation fails.
*/
private static Object actualInvoke(Method method, Object o, Object[] objects) throws InvocationTargetException {
try {
return method.invoke(o, objects);
} catch (IllegalAccessException iae) {
throw Jvm.rethrow(iae);
}
}
/**
* Logs a message if the debug mode is enabled.
*
* @param s A CharSequence representing part of the message.
* @param v The ValueIn associated with the message.
*/
public static void logMessage(@NotNull CharSequence s, @NotNull ValueIn v) {
if (!DEBUG_ENABLED) {
return;
}
Jvm.debug().on(VanillaMethodReader.class, logMessage0(s, v));
}
/**
* Constructs a log message based on the provided CharSequence and ValueIn.
* Converts binary messages to text for better logging.
*
* @param s A CharSequence representing part of the message.
* @param v The ValueIn associated with the message.
* @return A constructed log message.
*/
// package local for testing
static @NotNull String logMessage0(@NotNull CharSequence s, @NotNull ValueIn v) {
try {
String rest;
// Convert binary to text for logging, or retrieve the string representation
if (v.wireIn().isBinary()) {
final Bytes> bytes0 = v.wireIn().bytes();
Bytes> bytes = Bytes.allocateElasticOnHeap((int) (bytes0.readRemaining() * 3 / 2 + 64));
long pos = bytes0.readPosition();
try {
v.wireIn().copyTo(WireType.TEXT.apply(bytes));
rest = bytes.toString();
} catch (Exception t) {
rest = bytes0.toHexString(pos, bytes0.readLimit() - pos);
} finally {
bytes0.readPosition(pos);
bytes.releaseLast();
}
} else {
rest = v.toString();
}
// Remove any newline characters from the end of the text representation
if (rest.endsWith("\n"))
rest = rest.substring(0, rest.length() - 1);
return "read " + s + " - " + rest;
} catch (Exception e) {
return "read " + s + " - " + e;
}
}
/**
* Merges the given metaDataHandler and objects arrays, ensuring no duplicates and
* maintaining the original order. If the metaDataHandler is null, it returns the objects array.
*
* @param metaDataHandler The initial metadata handlers to be merged.
* @param objects The objects to be added to the metadata handlers.
* @return A merged array containing unique entries from both arrays.
*/
private Object[] addObjectsToMetaDataHandlers(Object[] metaDataHandler, @NotNull Object @NotNull [] objects) {
if (metaDataHandler == null) {
metaDataHandler = objects;
} else {
Set