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

com.tangosol.coherence.config.builder.StaticFactoryInstanceBuilder Maven / Gradle / Ivy

There is a newer version: 24.09
Show newest version
/*
 * Copyright (c) 2000, 2022, 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.coherence.config.builder;

import com.tangosol.coherence.config.ParameterList;
import com.tangosol.coherence.config.SimpleParameterList;

import com.tangosol.config.annotation.Injectable;
import com.tangosol.config.expression.Expression;
import com.tangosol.config.expression.LiteralExpression;
import com.tangosol.config.expression.Parameter;
import com.tangosol.config.expression.ParameterResolver;

import com.tangosol.io.ExternalizableLite;
import com.tangosol.io.pof.PofReader;
import com.tangosol.io.pof.PofWriter;
import com.tangosol.io.pof.PortableObject;

import com.tangosol.util.Base;
import com.tangosol.util.ExternalizableHelper;

import static com.tangosol.coherence.config.builder.ParameterizedBuilderHelper.getAssignableValue;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import javax.json.bind.annotation.JsonbProperty;

/**
 * A {@link StaticFactoryInstanceBuilder} is a {@link ParameterizedBuilder} 
 * that has been configured to realize objects based on the properties defined 
 * by an <instance> configuration element that uses the static
 * <class-factory-name> approach.
 *
 * @author bo  2011.06.24
 * @since Coherence 12.1.2
 */
public class StaticFactoryInstanceBuilder
        implements ParameterizedBuilder, ParameterizedBuilder.ReflectionSupport,
            ExternalizableLite, PortableObject
    {
    // ----- constructors ---------------------------------------------------

    /**
     * Construct a {@link StaticFactoryInstanceBuilder}.
     */
    public StaticFactoryInstanceBuilder()
        {
        m_exprFactoryClassName  = new LiteralExpression("undefined");
        m_exprFactoryMethodName = new LiteralExpression("undefined");
        m_listMethodParameters  = new SimpleParameterList();
        }

    // ----- StaticFactoryInstanceBuilder methods ---------------------------

    /**
     * Return the {@link Expression} that when evaluated will produce the name of the class
     * containing a static factory method that will realize instances
     * for this {@link ParameterizedBuilder}.
     *
     * @return the factory class name {@link Expression}
     */
    public Expression getFactoryClassName()
        {
        return m_exprFactoryClassName;
        }

    /**
     * Sets the {@link Expression} that when evaluated will produce the name of the class
     * containing a static factory method that will realize instances
     * for this {@link ParameterizedBuilder}.
     *
     * @param exprFactoryClassName  the {@link Expression}
     */
    @Injectable("class-factory-name")
    public void setFactoryClassName(Expression exprFactoryClassName)
        {
        m_exprFactoryClassName = exprFactoryClassName;
        }

    /**
     * Return the {@link Expression} that when evaluated will produce the name of the factory class
     * static  method that will realize instances for this {@link ParameterizedBuilder}.
     *
     * @return the factory method name {@link Expression}
     */
    public Expression getFactoryMethodName()
        {
        return m_exprFactoryMethodName;
        }

    /**
     * Set the {@link Expression} that when evaluated will produce the name of the factory class
     * static  method that will realize instances for this {@link ParameterizedBuilder}.
     *
     * @param exprFactoryMethodName  the {@link Expression}
     */
    @Injectable("method-name")
    public void setFactoryMethodName(Expression exprFactoryMethodName)
        {
        m_exprFactoryMethodName = exprFactoryMethodName;
        }

    /**
     * Returns the {@link ParameterList} to use to resolve factory method parameters when realizing the class.
     *
     * @return  the {@link ParameterList} for method parameters
     */
    public ParameterList getFactoryMethodParameters()
        {
        return m_listMethodParameters;
        }

    /**
     * Sets the {@link ParameterList} to use to resolve factory method parameters when realizing the class.
     *
     * @param listParameters  the {@link ParameterList} for method parameters
     */
    @Injectable("init-params")
    public void setFactoryMethodParameters(ParameterList listParameters)
        {
        if (listParameters != null)
            {
            m_listMethodParameters = listParameters;
            }
        }

    /**
     * Ensures we have a non-null {@link ClassLoader} that we can use for loading classes.
     *
     * @param loader  the proposed {@link ClassLoader}, which may be null
     *
     * @return a non-null {@link ClassLoader}
     */
    protected ClassLoader ensureClassLoader(ClassLoader loader)
        {
        return loader == null ? getClass().getClassLoader() : loader;
        }

    // ----- ParameterizedBuilder Interface --------------------------------

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
	@Override
    public T realize(ParameterResolver resolver, ClassLoader loader, ParameterList listMethodParameters)
        {
        try
            {
            // ensure we have a classloader
            loader = ensureClassLoader(loader);

            // load the factory class
            String   sClassName = m_exprFactoryClassName.evaluate(resolver);
            Class clzFactory = loader.loadClass(sClassName);

            // determine which parameter list we should use
            ParameterList listParameters = listMethodParameters == null ? m_listMethodParameters : listMethodParameters;

            // find a static method with the required name and compatible parameter types
            String   sMethodName       = m_exprFactoryMethodName.evaluate(resolver);
            int      cMethodParameters = listParameters.size();
            Method   compatibleMethod  = null;
            Object[] aMethodParameters = cMethodParameters == 0 ? null : new Object[cMethodParameters];
            Method[] aMethods          = clzFactory.getMethods();

            for (int i = 0; i < aMethods.length && compatibleMethod == null; i++)
                {
                // only consider static methods with the required number of parameters, the required name and a return type
                if (aMethods[i].getName().equals(sMethodName)
                    && aMethods[i].getParameterTypes().length == cMethodParameters
                    && Modifier.isStatic(aMethods[i].getModifiers()) && (aMethods[i].getReturnType() != null))
                    {
                    // determine the compatible method parameter values
                    Class[] aMethodParameterTypes = aMethods[i].getParameterTypes();
                    boolean    fIsCompatible         = true;

                    try
                        {
                        int j = 0;

                        for (Parameter parameter : listParameters)
                            {
                            aMethodParameters[j] = getAssignableValue(aMethodParameterTypes[j], parameter, resolver,
                                loader);
                            j++;
                            }
                        }
                    catch (Exception exception)
                        {
                        // this method is incompatible as parameter types don't match
                        fIsCompatible = false;
                        }

                    if (fIsCompatible)
                        {
                        compatibleMethod = aMethods[i];
                        }
                    }
                }

            // did we find a compatible method?
            if (compatibleMethod == null)
                {
                throw new NoSuchMethodException(String.format(
                    "Unable to find a compatible method for [%s] with the parameters [%s]", sMethodName,
                    listParameters));
                }
            else
                {
                return (T)compatibleMethod.invoke(clzFactory, aMethodParameters);
                }
            }
        catch (Exception e)
            {
            throw Base.ensureRuntimeException(e);
            }
        }

    // ----- ParameterizedBuilder.ReflectionSupport interface ---------------

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean realizes(Class clzClass, ParameterResolver resolver, ClassLoader loader)
        {
        try
            {
            // ensure we have a classloader
            loader = ensureClassLoader(loader);

            // attempt to factory load the class name, but don't initialize it
            String   sClassName = m_exprFactoryClassName.evaluate(resolver);
            Class clzFactory = loader.loadClass(sClassName);

            // determine if there is a static method on the factory that has the correct name
            // NOTE: we don't attempt to use the types to find an exact match as the parameters may not be provided)
            String sMethodName      = m_exprFactoryMethodName.evaluate(resolver);
            Method compatibleMethod = null;

            for (Method method : clzFactory.getMethods())
                {
                if (method.getName().equals(sMethodName) && Modifier.isStatic(method.getModifiers())
                    && method.getReturnType() != null && clzClass.isAssignableFrom(method.getReturnType()))
                    {
                    compatibleMethod = method;

                    break;
                    }
                }

            return compatibleMethod != null;
            }
        catch (ClassNotFoundException e)
            {
            return false;
            }
        }

    // ----- ExternalizableLite interface -----------------------------------

    /**
     * {@inheritDoc}
     */
    @Override
    public void readExternal(DataInput in) throws IOException
        {
        m_exprFactoryClassName  = (Expression) ExternalizableHelper.readObject(in, null);
        m_exprFactoryMethodName = (Expression) ExternalizableHelper.readObject(in, null);
        m_listMethodParameters  = (ParameterList) ExternalizableHelper.readObject(in, null);
        }

    /**
     * {@inheritDoc}
     */
    @Override
    public void writeExternal(DataOutput out) throws IOException
        {
        ExternalizableHelper.writeObject(out, m_exprFactoryClassName);
        ExternalizableHelper.writeObject(out, m_exprFactoryMethodName);
        ExternalizableHelper.writeObject(out, m_listMethodParameters);
        }

    // ----- PortableObject interface ---------------------------------------

    /**
     * {@inheritDoc}
     */
    @Override
    public void readExternal(PofReader reader) throws IOException
        {
        m_exprFactoryClassName  = (Expression) reader.readObject(0);
        m_exprFactoryMethodName = (Expression) reader.readObject(1);
        m_listMethodParameters  = (ParameterList) reader.readObject(2);
        }

    /**
     * {@inheritDoc}
     */
    @Override
    public void writeExternal(PofWriter writer) throws IOException
        {
        writer.writeObject(0, m_exprFactoryClassName);
        writer.writeObject(1, m_exprFactoryMethodName);
        writer.writeObject(2, m_listMethodParameters);
        }

    // ----- data members ---------------------------------------------------

    /**
     * An {@link Expression} that when evaluated will produce the name of a class
     * providing a factory method to use when realizing instances for this {@link ParameterizedBuilder}.
     */
    @JsonbProperty("exprFactoryClassName")
    private Expression m_exprFactoryClassName;

    /**
     * An {@link Expression} that when evaluated will produce the name of the static
     * method on the factory class that can be used to realize instances for this {@link ParameterizedBuilder}.
     */
    @JsonbProperty("exprFactoryMethodName")
    private Expression m_exprFactoryMethodName;

    /**
     * The {@link ParameterList} to use for resolving {@link Parameter}s and calling the factory method.
     */
    @JsonbProperty("methodParameters")
    private ParameterList m_listMethodParameters;
    }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy