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

com.sleepycat.je.rep.utilint.net.DataChannelFactoryBuilder Maven / Gradle / Ivy

The newest version!
/*-
 * Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
 *
 * This file was distributed by Oracle as part of a version of Oracle Berkeley
 * DB Java Edition made available at:
 *
 * http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
 *
 * Please see the LICENSE file included in the top-level directory of the
 * appropriate version of Oracle Berkeley DB Java Edition for a copy of the
 * license and additional information.
 */

package com.sleepycat.je.rep.utilint.net;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.sleepycat.je.dbi.EnvironmentImpl;
import com.sleepycat.je.rep.ReplicationNetworkConfig;
import com.sleepycat.je.rep.net.DataChannelFactory;
import com.sleepycat.je.rep.net.InstanceContext;
import com.sleepycat.je.rep.net.InstanceLogger;
import com.sleepycat.je.rep.net.InstanceParams;
import com.sleepycat.je.rep.net.LoggerFactory;
import com.sleepycat.je.utilint.LoggerUtils;
import com.sleepycat.je.utilint.TracerFormatter;

/**
 * Class for creating DataChannel instances.
 */
public class DataChannelFactoryBuilder {

    /**
     * A count of the number of factories for which construction was attempted.
     */
    private static final AtomicInteger factoryCount = new AtomicInteger(0);

    /**
     * Construct the "default" DataChannelFactory that arises from an empty
     * DataChannelFactory configuration.
     */
    public static DataChannelFactory constructDefault() {
        return new SimpleChannelFactory();
    }

    /**
     * Construct a DataChannelFactory from the specified network
     * configuration.
     * The choice of DataChannelFactory type is determined by the setting
     * of {@link ReplicationNetworkConfig#CHANNEL_TYPE je.rep.channelType}.
     *
     * If set to sslthen the internal SSL implementation is
     * is used.  If set to custom then a custom channel
     * factory is constructed based on the setting of
     *   {@link ReplicationNetworkConfig#CHANNEL_FACTORY_CLASS je.rep.dataChannelFactoryClass}
     *
     * If set to basic or not set, SimpleChannelFactory
     * is instantiated.
     *
     * @param repNetConfig The configuration to control factory building
     * @return a DataChannelFactory
     * @throws IllegalArgumentException if an invalid configuration
     * property value or combination of values was specified.
     */
    public static DataChannelFactory construct(
            ReplicationNetworkConfig repNetConfig)
        throws IllegalArgumentException {

        return construct(repNetConfig, (String) null);
    }

    /**
     * Construct a DataChannelFactory from the specified access
     * configuration.
     * The choice of DataChannelFactory type is determined by the setting
     * of {@link ReplicationNetworkConfig#CHANNEL_TYPE je.rep.channelType}.
     *
     * If set to sslthen the internal SSL implementation is
     * is used.  If set to custom then a custom channel
     * factory is constructed based on the setting of
     *   {@link ReplicationNetworkConfig#CHANNEL_FACTORY_CLASS je.rep.dataChannelFactoryClass}
     *
     * If set to basic or not set, SimpleChannelFactory
     * is instantiated.
     *
     * @param repNetConfig The configuration to control factory building
     * @param logContext A null-allowable String that contributes to the
     * logging identifier for the factory.
     * @return a DataChannelFactory
     * @throws IllegalArgumentException if an invalid configuration
     * property value or combination of values was specified.
     */
    public static DataChannelFactory construct(
            ReplicationNetworkConfig repNetConfig, String logContext)
        throws IllegalArgumentException {

        final String logName = repNetConfig.getLogName();
        if (logName.isEmpty() && (logContext == null || logContext.isEmpty())) {
            return construct(repNetConfig, (LoggerFactory) null);
        }

        final String logId;
        if (logName.isEmpty()) {
            logId = logContext;
        } else if (logContext == null || logContext.isEmpty()) {
            logId = logName;
        } else {
            logId = logName + ":" + logContext;
        }
        final LoggerFactory loggerFactory = makeLoggerFactory(logId);

        return construct(repNetConfig, loggerFactory);
    }

    /**
     * Construct a DataChannelFactory from the specified access
     * configuration.
     * The choice of DataChannelFactory type is determined by the setting
     * of {@link ReplicationNetworkConfig#CHANNEL_TYPE je.rep.channelType}.
     *
     * If set to sslthen the internal SSL implementation is
     * is used.  If set to custom then a custom channel
     * factory is constructed based on the setting of
     *   {@link ReplicationNetworkConfig#CHANNEL_FACTORY_CLASS je.rep.dataChannelFactoryClass}
     *
     * If set to basic or not set, SimpleChannelFactory
     * is instantiated.
     *
     * @param repNetConfig The configuration to control factory building
     * @param loggerFactory A null-allowable LoggerFactory for use in channel
     * factory construction
     * @return a DataChannelFactory
     * @throws IllegalArgumentException if an invalid configuration
     * property value or combination of values was specified.
     */
    public static DataChannelFactory construct(
            ReplicationNetworkConfig repNetConfig,
            LoggerFactory loggerFactory)
        throws IllegalArgumentException {

        final String channelType = repNetConfig.getChannelType();
        final int factoryIndex = factoryCount.getAndIncrement();

        /*
         * Build the LoggerFactory if not provided by the caller
         */
        if (loggerFactory == null) {
            String logName = repNetConfig.getLogName();
            if (logName.isEmpty()) {
                logName = Integer.toString(factoryIndex);
            }
            loggerFactory = makeLoggerFactory(logName);
        }

        final InstanceContext context =
            new InstanceContext(repNetConfig, loggerFactory);

        final String factoryClass = repNetConfig.getChannelFactoryClass();
        if (factoryClass == null || factoryClass.isEmpty()) {
            if (channelType.equalsIgnoreCase("basic")) {
                return new SimpleChannelFactory(
                    new InstanceParams(context, null));
            }

            if (channelType.equalsIgnoreCase("ssl")) {
                return new SSLChannelFactory(new InstanceParams(context, null));
            }

            throw new IllegalArgumentException(
                "The channelType setting '" + channelType + "' is not valid");
        }

        final String classParams = repNetConfig.getChannelFactoryParams();
        final InstanceParams factoryParams =
            new InstanceParams(context, classParams);
        return construct(factoryClass, factoryParams);
    }

    /**
     * Constructs a DataChannelFactory implementation.
     * @param factoryClassName the name of the class to instantiate,
     * which must implement DataChannelFactory
     * @param factoryParams the context and factory arguments
     * @return a newly constructed instance
     * @throws IllegalArgumentException if the arguments are invalid
     */
    private static DataChannelFactory construct(
        String factoryClassName, InstanceParams factoryParams)
        throws IllegalArgumentException {

        return (DataChannelFactory) constructObject(
            factoryClassName, DataChannelFactory.class,
            "data channel factory",
            new CtorArgSpec(new Class[] { InstanceParams.class },
                            new Object[] { factoryParams }));
    }

    /**
     * Instantiates a class based on a configuration specification. This method
     * looks up a class of the specified name, then finds a constructor with
     * an argument list that matches the caller's specification, and constructs
     * an instance using that constructor and validates that the instance
     * extends or implements the mustImplement class specified.
     *
     * @param instClassName the name of the class to instantiate
     * @param mustImplement a class denoting a required base class or
     * required implemented interface of the class whose name is
     * specified by instClassName.
     * @param miDesc a descriptive term for the mustImplement class
     * @param ctorArgSpec specifies the required constructor signature and
     * the values to be passed
     * @return an instance of the specified class
     * @throws IllegalArgumentException if any of the input arguments are
     * invalid
     */
    static Object constructObject(String instClassName,
                                  Class mustImplement,
                                  String miDesc,
                                  CtorArgSpec ctorArgSpec)
        throws IllegalArgumentException {

        /*
         * Resolve the class
         */
        Class instClass = null;
        try {
            instClass = Class.forName(instClassName);
        } catch (ClassNotFoundException cnfe) {
            throw new IllegalArgumentException(
                "Error resolving " + miDesc + " class " +
                instClassName, cnfe);
        }

        /*
         * Find an appropriate constructor for the class.
         */
        final Constructor constructor;
        try {
            constructor = instClass.getConstructor(ctorArgSpec.argTypes);
        } catch (NoSuchMethodException nsme) {
            throw new IllegalArgumentException(
                "Unable to find an appropriate constructor for " + miDesc +
                " class " + instClassName);
        }

        /*
         * Get an instance of the class.
         */
        final Object instObject;
        try {
            instObject = constructor.newInstance(ctorArgSpec.argValues);
        } catch (IllegalAccessException iae) {
            /* Constructor is not accessible */
            throw new IllegalArgumentException(
                "Error instantiating " + miDesc + " class " + instClassName +
                ".  Not accessible?",
                iae);
        } catch (IllegalArgumentException iae) {
            /* Wrong arguments - should not be possible here */
            throw new IllegalArgumentException(
                "Error instantiating " + miDesc + " class " + instClassName,
                iae);
        } catch (InstantiationException ie) {
            /* Class is abstract */
            throw new IllegalArgumentException(
                "Error instantiating " + miDesc + " class " + instClassName +
                ". Class is abstract?",
                ie);
        } catch (InvocationTargetException ite) {
            /* Exception thrown within constructor */
            throw new IllegalArgumentException(
                "Error instantiating " + miDesc + " class " + instClassName +
                ". Exception within constructor",
                ite);
        }

        /*
         * In this context, the class must implement the specified
         * interface.
         */
        if (! (mustImplement.isAssignableFrom(instObject.getClass()))) {
            throw new IllegalArgumentException(
                "The " + miDesc + " class " +  instClassName +
                " does not implement " + mustImplement.getName());
        }

        return instObject;
    }

    /**
     * Creates a logger factory based on an EnvironmentImpl
     *
     * @param envImpl a non-null EnvironmentImpl
     */
    public static LoggerFactory makeLoggerFactory(EnvironmentImpl envImpl) {
        if (envImpl == null) {
            throw new IllegalArgumentException("envImpl must not be null");
        }

        return new ChannelLoggerFactory(envImpl, null /* formatter */);
    }

    /**
     * Creates a logger factory based on a fixed string
     *
     * @param prefix a fixed string to be used as logger prefix
     */
    public static LoggerFactory makeLoggerFactory(String prefix) {
        if (prefix == null) {
            throw new IllegalArgumentException("prefix must not be null");
        }

        final Formatter formatter = new ChannelFormatter(prefix);

        return new ChannelLoggerFactory(null, /* envImpl */ formatter);
    }


    /**
     * A simple class that captures the proposed formal and actual argument
     * lists to match against possible constructors.
     */
    static class CtorArgSpec {
        private final Class[] argTypes;
        private final Object[] argValues;

        CtorArgSpec(Class[] argTypes, Object[] argValues) {
            this.argTypes = argTypes;
            this.argValues = argValues;
        }
    }

    /**
     * A simple implementation of LoggerFactory that encapsulates the 
     * necessary information to do JE environment-friendly logging without
     * needing to know JE HA internal logging.
     */
    static class ChannelLoggerFactory implements LoggerFactory {
        private final EnvironmentImpl envImpl;
        private final Formatter formatter;

        /**
         * Creates a LoggerFactory for use in construction of channel
         * objects. The caller should supply either an EnvironmentImpl or a
         * Formatter object.
         * 
         * @param envImpl a possibly-null EnvironmentImpl
         * @param formatter a possible null formatter
         */
        ChannelLoggerFactory(EnvironmentImpl envImpl,
                             Formatter formatter) {
            this.envImpl = envImpl;
            this.formatter = formatter;
        }

        /**
         * @see LoggerFactory#getLogger(Class)
         */
        @Override
        public InstanceLogger getLogger(Class clazz) {
            final Logger logger;
            if (envImpl == null) {
                logger = LoggerUtils.getLoggerFormatterNeeded(clazz);
            } else {
                logger = LoggerUtils.getLogger(clazz);
            }
            return new ChannelInstanceLogger(envImpl, formatter, logger);
        }
    }

    /**
     * A simple implementation of InstanceLogger that encapuslates the 
     * necessary information to do JE environment-friendly logging without
     * needing to know JE logging rules.
     */
    static class ChannelInstanceLogger implements InstanceLogger {
        private final EnvironmentImpl envImpl;
        private final Formatter formatter;
        private final Logger logger;

        /**
         * Creates a ChannelInstanceLogger for use in construction of channel
         * objects. The caller should supply either an EnvironmentImpl or a
         * Formatter object.
         * 
         * @param envImpl a possibly-null EnvironmentImpl
         * @param formatter a possible null formatter
         * @param logger a logger created via LoggerUtils.getLogger()
         */
        ChannelInstanceLogger(EnvironmentImpl envImpl,
                              Formatter formatter,
                              Logger logger) {
            this.envImpl = envImpl;
            this.formatter = formatter;
            this.logger = logger;
        }

        /**
         * @see InstanceLogger#log(Level, String)
         */
        @Override
        public void log(Level logLevel, String msg) {
            LoggerUtils.logMsg(logger, envImpl, formatter, logLevel, msg);
        }
    }

    /**
     * Formatter for log messages
     */
    static class ChannelFormatter extends TracerFormatter {
        private final String id;

        ChannelFormatter(String id) {
            super();
            this.id = id;
        }

        @Override
        protected void appendEnvironmentName(StringBuilder sb) {
            sb.append(" [" + id + "]");
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy