All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tangosol.internal.util.invoke.Lambdas Maven / Gradle / Ivy

There is a newer version: 24.03
Show newest version
/*
 * Copyright (c) 2000, 2020, Oracle and/or its affiliates.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * http://oss.oracle.com/licenses/upl.
 */
package com.tangosol.internal.util.invoke;

import com.oracle.coherence.common.internal.util.CanonicalNames;

import com.tangosol.coherence.config.Config;

import com.tangosol.internal.util.invoke.lambda.AbstractRemotableLambda;
import com.tangosol.internal.util.invoke.lambda.LambdaIdentity;
import com.tangosol.internal.util.invoke.lambda.RemotableLambdaGenerator;
import com.tangosol.internal.util.invoke.lambda.MethodReferenceIdentity;
import com.tangosol.internal.util.invoke.lambda.AnonymousLambdaIdentity;
import com.tangosol.internal.util.invoke.lambda.StaticLambdaInfo;

import com.tangosol.net.CacheFactory;

import com.tangosol.util.Base;

import java.io.Serializable;

import java.lang.invoke.MethodHandleInfo;
import java.lang.invoke.SerializedLambda;

import java.lang.reflect.Method;

/**
 * Helpers for lambda remoting.
 *
 * @author as  2015.02.18
 * @since 12.2.1
 */
public abstract class Lambdas
    {
    /**
     * Return true if the specified object is a raw lambda.
     *
     * @param o  object to inspect
     *
     * @return true if object is a raw lambda
     */
    public static boolean isLambda(Object o)
        {
        return o != null && isLambdaClass(o.getClass());
        }

    /**
     * Return true if the specified Class represents a raw lambda.
     *
     * @param clz  class to inspect
     *
     * @return true if the class represents a raw lambda
     */
    public static boolean isLambdaClass(Class clz)
        {
        return clz != null && clz.getName().contains("$$Lambda$");
        }

    /**
     * Return a {@link SerializedLambda} based upon the provided lambda, or
     * throw an IllegalArgumentException if the provided lambda is deemed to
     * not be a lambda.
     *
     * @param oLambda  the lambda that the returned SerializedLambda represents
     *
     * @return SerializedLambda representing the provided lambda
     *
     * @throws IllegalArgumentException  if the provided lambda is deemed not to be a lambda
     * @throws IllegalStateException     if an issue arose in creating the SerializedLambda
     */
    public static SerializedLambda getSerializedLambda(Object oLambda)
        {
        if (oLambda == null || !isLambda(oLambda) || !(oLambda instanceof Serializable))
            {
            throw new IllegalArgumentException(
                "Specified object is not an instance of a serializable lambda");
            }

        try
            {
            Class clzLambda = oLambda.getClass();
            Method   method    = clzLambda.getDeclaredMethod("writeReplace");
            method.setAccessible(true);

            return (SerializedLambda) method.invoke(oLambda);
            }
        catch (Exception e)
            {
            throw new IllegalStateException(
                "Unable to extract SerializedLambda from lambda: " + oLambda, e);
            }
        }

    /**
     * Return whether the provided method name is a name used by the JDK to
     * generate lambda implementations in the capturing class.
     *
     * @param sMethodName  the method name to inspect
     *
     * @return whether the provided method name represents a lambda implementation
     */
    public static boolean isLambdaMethod(String sMethodName)
        {
        return sMethodName.startsWith("lambda$");
        }

    /**
     * Return true if the provided {@link SerializedLambda} represents a method
     * reference.
     *
     * @param lambda  the SerializedLambda to inspect
     *
     * @return whether the SerializedLambda represents a method reference
     */
    public static boolean isMethodReference(SerializedLambda lambda)
        {
        return lambda.getImplMethodKind() == MethodHandleInfo.REF_newInvokeSpecial ||
               (!isLambdaMethod(lambda.getImplMethodName()) && lambda.getCapturedArgCount() == 0);
        }

    /**
     * Create a {@link ClassDefinition} for the provided raw Serializable lambda.
     *
     * @param id      the remote class identity
     * @param lambda  the raw Serializable lambda
     * @param loader  the ClassLoader used to load the capturing class containing
     *                the lambda implementation
     *
     * @return a LambdaDefinition that represents the Serializable lambda
     */
    public static ClassDefinition createDefinition(ClassIdentity id, Serializable lambda, ClassLoader loader)
        {
        SerializedLambda lambdaMetadata = getSerializedLambda(lambda);

        if (lambdaMetadata.getImplMethodKind() == MethodHandleInfo.REF_invokeStatic ||
            isMethodReference(lambdaMetadata))
            {
            ClassDefinition definition = new ClassDefinition(
                    id,
                    RemotableLambdaGenerator.createRemoteLambdaClass(id.getName(), lambdaMetadata, loader));

            definition.dumpClass(DUMP_LAMBDAS);

            return definition;
            }
        throw new IllegalArgumentException(
                "The specified lambda is referring to the enclosing class instance " +
                "or its fields and therefore cannot be marshalled across network boundaries (" +
                lambdaMetadata + ")");
        }

    /**
     * Return a {@link LambdaIdentity} based upon the provided
     * {@link SerializedLambda function metadata}.
     *
     * @param lambdaMetadata  function metadata
     * @param loader          ClassLoader used to load the capturing class
     *
     * @return an AbstractLambdaIdentity based upon the provided function metadata
     */
    public static LambdaIdentity createIdentity(SerializedLambda lambdaMetadata, ClassLoader loader)
        {
        return isMethodReference(lambdaMetadata)
               ? new MethodReferenceIdentity(lambdaMetadata, loader)
               : new AnonymousLambdaIdentity(lambdaMetadata, loader);
        }

    /**
     * Return captured arguments as an object array.
     *
     * @param lambdaMetadata  function metadata
     *
     * @return captured arguments
     */
    public static Object[] getCapturedArguments(SerializedLambda lambdaMetadata)
        {
        int      c      = lambdaMetadata.getCapturedArgCount();
        Object[] aoArgs = new Object[c];

        for (int i = 0; i < c; i++)
            {
            aoArgs[i] = lambdaMetadata.getCapturedArg(i);
            }

        return aoArgs;
        }

    /**
     * Ensure that the specified function is an instance of a {@link Remotable}
     * lambda.
     * 

* If the specified function is not a lambda or if {@link #isDynamicLambdas() dynamic lambdas} * are not enabled, it will be ignored and returned as is, which makes this method safe to use * in the cases where the argument could be either a lambda or an instance of a concrete class. * Of course, this assumes that the specified {@code function} does not need to implement * {@code Remotable} interface because it provides native support for * serialization and can be marshaled across process boundaries on its own. * * @param the type of the function * @param function the function to convert into {@code Remotable} lambda * if necessary * * @return a {@code Remotable} lambda for the specified function, or the * function itself if the specified function is not a lambda or dynamic * lambdas are not enabled */ public static T ensureRemotable(T function) { if (!isDynamicLambdas() || function instanceof AbstractRemotableLambda || !isLambda(function)) { return function; } RemotableSupport support = RemotableSupport.get(function.getClass().getClassLoader()); return support.realize(support.createRemoteConstructor(function)); } /** * Return lambda serialization wire format based on {@link #LAMBDAS_SERIALIZATION_MODE} value. * * @param oFunction raw lambda to convert into serializable wire format * * @return a dynamic or static lambda serialization wire format for lambda oFunction or just * oFunction itself if the specified oFunction is not a lambda * * @see #LAMBDAS_SERIALIZATION_MODE * * @since 14.1.1.0.2 */ public static Object ensureSerializable(Object oFunction) { return isLambda(oFunction) ? isDynamicLambdas() ? ensureRemotable((Serializable) oFunction) : new StaticLambdaInfo(oFunction.getClass(), oFunction) : oFunction; } /** * Return the canonical name for a given lambda object. If the object is not a lambda, * return null. This is for the Coherence ValueExtractor. * * @param oLambda the lambda object * * @return the lambda's canonical name, or null if the supplied object is not a lambda * * @since 12.2.1.4 */ public static String getValueExtractorCanonicalName(Object oLambda) { if (oLambda instanceof AbstractRemotableLambda) { AbstractRemotableLambda lambda = (AbstractRemotableLambda) oLambda; return CanonicalNames.computeValueExtractorCanonicalName(((MethodReferenceIdentity) lambda.getId()) .getImplMethod() + CanonicalNames.VALUE_EXTRACTOR_METHOD_SUFFIX, null); } return null; } /** * Return {@code true} only if lambdas are serialized using dynamic lambdas. * * @return {@code true} only if lambdas are serialized using dynamic lambdas * * @since 14.1.1.0.2 */ public static boolean isDynamicLambdas() { return LAMBDAS_SERIALIZATION_MODE == SerializationMode.DYNAMIC; } /** * Return {@code true} only if lambdas are serialized using static lambdas. * * @return {@code true} only if lambdas are serialized using static lambdas * * @since 14.1.1.0.2 */ public static boolean isStaticLambdas() { return LAMBDAS_SERIALIZATION_MODE == SerializationMode.STATIC; } // ----- inner enum: SerializationMode ---------------------------------- /** * Specify the serialization mode for lambdas. * * @since 14.1.1.0.2 */ public static enum SerializationMode { /** * Serialize lambdas as dynamic lambdas. */ DYNAMIC, /** * Serialize lambdas as static lambdas. */ STATIC }; // ---- static members -------------------------------------------------- /** * Path to the file system where generated lambdas should be written; purely for debug. */ private static final String DUMP_LAMBDAS = Config.getProperty("coherence.remotable.dumpLambdas", Config.getProperty("coherence.remotable.dumpAll")); /** * System property to control lambda serialization. Valid values are defined by enumeration {@link SerializationMode}. * * @since 14.1.1.0.2 */ public static final String LAMBDAS_SERIALIZATION_MODE_PROPERTY = "coherence.lambdas"; /** * Specifies {@link SerializationMode} used for lambdas. * System property {@link #LAMBDAS_SERIALIZATION_MODE_PROPERTY} configures this setting. * The default configuration {@link SerializationMode#DYNAMIC} enables dynamic lambdas serialization. * * @since 14.1.1.0.2 */ protected static final SerializationMode LAMBDAS_SERIALIZATION_MODE; static { String sPropValue = null; SerializationMode mode = SerializationMode.DYNAMIC; try { sPropValue = Config.getProperty(LAMBDAS_SERIALIZATION_MODE_PROPERTY, SerializationMode.DYNAMIC.name()); mode = SerializationMode.valueOf(sPropValue.toUpperCase()); } catch (IllegalArgumentException e) { StringBuilder sbValues = new StringBuilder("["); for (SerializationMode m : SerializationMode.values()) { sbValues.append(m).append("|"); } sbValues.setCharAt(sbValues.length() - 1, ']'); final String f_sPropValue = sPropValue; final SerializationMode f_mode = mode; CacheFactory.log( "System property \"" + LAMBDAS_SERIALIZATION_MODE_PROPERTY + "\" is set to invalid value of \"" + f_sPropValue + "\"" + "; valid values are: " + sbValues.toString() + ". Reverting to default mode of " + f_mode.name() + ".", Base.LOG_WARN); } LAMBDAS_SERIALIZATION_MODE = mode; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy