blob: f529db097565d129fc8968705d719794e78f9e38 [file] [log] [blame]
/*-
* 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 <code>ssl</code>then the internal SSL implementation is
* is used. If set to <code>custom</code> then a custom channel
* factory is constructed based on the setting of
* {@link ReplicationNetworkConfig#CHANNEL_FACTORY_CLASS je.rep.dataChannelFactoryClass}
*
* If set to <code>basic</code> 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 <code>ssl</code>then the internal SSL implementation is
* is used. If set to <code>custom</code> then a custom channel
* factory is constructed based on the setting of
* {@link ReplicationNetworkConfig#CHANNEL_FACTORY_CLASS je.rep.dataChannelFactoryClass}
*
* If set to <code>basic</code> 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 <code>ssl</code>then the internal SSL implementation is
* is used. If set to <code>custom</code> then a custom channel
* factory is constructed based on the setting of
* {@link ReplicationNetworkConfig#CHANNEL_FACTORY_CLASS je.rep.dataChannelFactoryClass}
*
* If set to <code>basic</code> 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 + "]");
}
}
}