1 /*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002, 2013 Oracle and/or its affiliates. All rights reserved. 5 * 6 */ 7 8 package com.sleepycat.je.rep.utilint.net; 9 10 import java.lang.reflect.Constructor; 11 import java.lang.reflect.InvocationTargetException; 12 import java.util.concurrent.atomic.AtomicInteger; 13 import java.util.logging.Formatter; 14 import java.util.logging.Level; 15 import java.util.logging.Logger; 16 17 import com.sleepycat.je.dbi.EnvironmentImpl; 18 import com.sleepycat.je.rep.ReplicationNetworkConfig; 19 import com.sleepycat.je.rep.net.DataChannelFactory; 20 import com.sleepycat.je.rep.net.InstanceContext; 21 import com.sleepycat.je.rep.net.InstanceLogger; 22 import com.sleepycat.je.rep.net.InstanceParams; 23 import com.sleepycat.je.rep.net.LoggerFactory; 24 import com.sleepycat.je.utilint.LoggerUtils; 25 import com.sleepycat.je.utilint.TracerFormatter; 26 27 /** 28 * Class for creating DataChannel instances. 29 */ 30 public class DataChannelFactoryBuilder { 31 32 /** 33 * A count of the number of factories for which construction was attempted. 34 */ 35 private static final AtomicInteger factoryCount = new AtomicInteger(0); 36 37 /** 38 * Construct the "default" DataChannelFactory that arises from an empty 39 * DataChannelFactory configuration. 40 */ constructDefault()41 public static DataChannelFactory constructDefault() { 42 return new SimpleChannelFactory(); 43 } 44 45 /** 46 * Construct a DataChannelFactory from the specified network 47 * configuration. 48 * The choice of DataChannelFactory type is determined by the setting 49 * of {@link ReplicationNetworkConfig#CHANNEL_TYPE je.rep.channelType}. 50 * 51 * If set to <code>ssl</code>then the internal SSL implementation is 52 * is used. If set to <code>custom</code> then a custom channel 53 * factory is constructed based on the setting of 54 * {@link ReplicationNetworkConfig#CHANNEL_FACTORY_CLASS je.rep.dataChannelFactoryClass} 55 * 56 * If set to <code>basic</code> or not set, SimpleChannelFactory 57 * is instantiated. 58 * 59 * @param repNetConfig The configuration to control factory building 60 * @return a DataChannelFactory 61 * @throws IllegalArgumentException if an invalid configuration 62 * property value or combination of values was specified. 63 */ construct( ReplicationNetworkConfig repNetConfig)64 public static DataChannelFactory construct( 65 ReplicationNetworkConfig repNetConfig) 66 throws IllegalArgumentException { 67 68 return construct(repNetConfig, (String) null); 69 } 70 71 /** 72 * Construct a DataChannelFactory from the specified access 73 * configuration. 74 * The choice of DataChannelFactory type is determined by the setting 75 * of {@link ReplicationNetworkConfig#CHANNEL_TYPE je.rep.channelType}. 76 * 77 * If set to <code>ssl</code>then the internal SSL implementation is 78 * is used. If set to <code>custom</code> then a custom channel 79 * factory is constructed based on the setting of 80 * {@link ReplicationNetworkConfig#CHANNEL_FACTORY_CLASS je.rep.dataChannelFactoryClass} 81 * 82 * If set to <code>basic</code> or not set, SimpleChannelFactory 83 * is instantiated. 84 * 85 * @param repNetConfig The configuration to control factory building 86 * @param logContext A null-allowable String that contributes to the 87 * logging identifier for the factory. 88 * @return a DataChannelFactory 89 * @throws IllegalArgumentException if an invalid configuration 90 * property value or combination of values was specified. 91 */ construct( ReplicationNetworkConfig repNetConfig, String logContext)92 public static DataChannelFactory construct( 93 ReplicationNetworkConfig repNetConfig, String logContext) 94 throws IllegalArgumentException { 95 96 final String logName = repNetConfig.getLogName(); 97 if (logName.isEmpty() && (logContext == null || logContext.isEmpty())) { 98 return construct(repNetConfig, (LoggerFactory) null); 99 } 100 101 final String logId; 102 if (logName.isEmpty()) { 103 logId = logContext; 104 } else if (logContext == null || logContext.isEmpty()) { 105 logId = logName; 106 } else { 107 logId = logName + ":" + logContext; 108 } 109 final LoggerFactory loggerFactory = makeLoggerFactory(logId); 110 111 return construct(repNetConfig, loggerFactory); 112 } 113 114 /** 115 * Construct a DataChannelFactory from the specified access 116 * configuration. 117 * The choice of DataChannelFactory type is determined by the setting 118 * of {@link ReplicationNetworkConfig#CHANNEL_TYPE je.rep.channelType}. 119 * 120 * If set to <code>ssl</code>then the internal SSL implementation is 121 * is used. If set to <code>custom</code> then a custom channel 122 * factory is constructed based on the setting of 123 * {@link ReplicationNetworkConfig#CHANNEL_FACTORY_CLASS je.rep.dataChannelFactoryClass} 124 * 125 * If set to <code>basic</code> or not set, SimpleChannelFactory 126 * is instantiated. 127 * 128 * @param repNetConfig The configuration to control factory building 129 * @param loggerFactory A null-allowable LoggerFactory for use in channel 130 * factory construction 131 * @return a DataChannelFactory 132 * @throws IllegalArgumentException if an invalid configuration 133 * property value or combination of values was specified. 134 */ construct( ReplicationNetworkConfig repNetConfig, LoggerFactory loggerFactory)135 public static DataChannelFactory construct( 136 ReplicationNetworkConfig repNetConfig, 137 LoggerFactory loggerFactory) 138 throws IllegalArgumentException { 139 140 final String channelType = repNetConfig.getChannelType(); 141 final int factoryIndex = factoryCount.getAndIncrement(); 142 143 /* 144 * Build the LoggerFactory if not provided by the caller 145 */ 146 if (loggerFactory == null) { 147 String logName = repNetConfig.getLogName(); 148 if (logName.isEmpty()) { 149 logName = Integer.toString(factoryIndex); 150 } 151 loggerFactory = makeLoggerFactory(logName); 152 } 153 154 final InstanceContext context = 155 new InstanceContext(repNetConfig, loggerFactory); 156 157 final String factoryClass = repNetConfig.getChannelFactoryClass(); 158 if (factoryClass == null || factoryClass.isEmpty()) { 159 if (channelType.equalsIgnoreCase("basic")) { 160 return new SimpleChannelFactory( 161 new InstanceParams(context, null)); 162 } 163 164 if (channelType.equalsIgnoreCase("ssl")) { 165 return new SSLChannelFactory(new InstanceParams(context, null)); 166 } 167 168 throw new IllegalArgumentException( 169 "The channelType setting '" + channelType + "' is not valid"); 170 } 171 172 final String classParams = repNetConfig.getChannelFactoryParams(); 173 final InstanceParams factoryParams = 174 new InstanceParams(context, classParams); 175 return construct(factoryClass, factoryParams); 176 } 177 178 /** 179 * Constructs a DataChannelFactory implementation. 180 * @param factoryClassName the name of the class to instantiate, 181 * which must implement DataChannelFactory 182 * @param factoryParams the context and factory arguments 183 * @return a newly constructed instance 184 * @throws IllegalArgumentException if the arguments are invalid 185 */ construct( String factoryClassName, InstanceParams factoryParams)186 private static DataChannelFactory construct( 187 String factoryClassName, InstanceParams factoryParams) 188 throws IllegalArgumentException { 189 190 return (DataChannelFactory) constructObject( 191 factoryClassName, DataChannelFactory.class, 192 "data channel factory", 193 new CtorArgSpec(new Class<?>[] { InstanceParams.class }, 194 new Object[] { factoryParams })); 195 } 196 197 /** 198 * Instantiates a class based on a configuration specification. This method 199 * looks up a class of the specified name, then finds a constructor with 200 * an argument list that matches the caller's specification, and constructs 201 * an instance using that constructor and validates that the instance 202 * extends or implements the mustImplement class specified. 203 * 204 * @param instClassName the name of the class to instantiate 205 * @param mustImplement a class denoting a required base class or 206 * required implemented interface of the class whose name is 207 * specified by instClassName. 208 * @param miDesc a descriptive term for the mustImplement class 209 * @param ctorArgSpec specifies the required constructor signature and 210 * the values to be passed 211 * @return an instance of the specified class 212 * @throws IllegalArgumentException if any of the input arguments are 213 * invalid 214 */ constructObject(String instClassName, Class<?> mustImplement, String miDesc, CtorArgSpec ctorArgSpec)215 static Object constructObject(String instClassName, 216 Class<?> mustImplement, 217 String miDesc, 218 CtorArgSpec ctorArgSpec) 219 throws IllegalArgumentException { 220 221 /* 222 * Resolve the class 223 */ 224 Class<?> instClass = null; 225 try { 226 instClass = Class.forName(instClassName); 227 } catch (ClassNotFoundException cnfe) { 228 throw new IllegalArgumentException( 229 "Error resolving " + miDesc + " class " + 230 instClassName, cnfe); 231 } 232 233 /* 234 * Find an appropriate constructor for the class. 235 */ 236 final Constructor<?> constructor; 237 try { 238 constructor = instClass.getConstructor(ctorArgSpec.argTypes); 239 } catch (NoSuchMethodException nsme) { 240 throw new IllegalArgumentException( 241 "Unable to find an appropriate constructor for " + miDesc + 242 " class " + instClassName); 243 } 244 245 /* 246 * Get an instance of the class. 247 */ 248 final Object instObject; 249 try { 250 instObject = constructor.newInstance(ctorArgSpec.argValues); 251 } catch (IllegalAccessException iae) { 252 /* Constructor is not accessible */ 253 throw new IllegalArgumentException( 254 "Error instantiating " + miDesc + " class " + instClassName + 255 ". Not accessible?", 256 iae); 257 } catch (IllegalArgumentException iae) { 258 /* Wrong arguments - should not be possible here */ 259 throw new IllegalArgumentException( 260 "Error instantiating " + miDesc + " class " + instClassName, 261 iae); 262 } catch (InstantiationException ie) { 263 /* Class is abstract */ 264 throw new IllegalArgumentException( 265 "Error instantiating " + miDesc + " class " + instClassName + 266 ". Class is abstract?", 267 ie); 268 } catch (InvocationTargetException ite) { 269 /* Exception thrown within constructor */ 270 throw new IllegalArgumentException( 271 "Error instantiating " + miDesc + " class " + instClassName + 272 ". Exception within constructor", 273 ite); 274 } 275 276 /* 277 * In this context, the class must implement the specified 278 * interface. 279 */ 280 if (! (mustImplement.isAssignableFrom(instObject.getClass()))) { 281 throw new IllegalArgumentException( 282 "The " + miDesc + " class " + instClassName + 283 " does not implement " + mustImplement.getName()); 284 } 285 286 return instObject; 287 } 288 289 /** 290 * Creates a logger factory based on an EnvironmentImpl 291 * 292 * @param envImpl a non-null EnvironmentImpl 293 */ makeLoggerFactory(EnvironmentImpl envImpl)294 public static LoggerFactory makeLoggerFactory(EnvironmentImpl envImpl) { 295 if (envImpl == null) { 296 throw new IllegalArgumentException("envImpl must not be null"); 297 } 298 299 return new ChannelLoggerFactory(envImpl, null /* formatter */); 300 } 301 302 /** 303 * Creates a logger factory based on a fixed string 304 * 305 * @param prefix a fixed string to be used as logger prefix 306 */ makeLoggerFactory(String prefix)307 public static LoggerFactory makeLoggerFactory(String prefix) { 308 if (prefix == null) { 309 throw new IllegalArgumentException("prefix must not be null"); 310 } 311 312 final Formatter formatter = new ChannelFormatter(prefix); 313 314 return new ChannelLoggerFactory(null, /* envImpl */ formatter); 315 } 316 317 318 /** 319 * A simple class that captures the proposed formal and actual argument 320 * lists to match against possible constructors. 321 */ 322 static class CtorArgSpec { 323 private final Class<?>[] argTypes; 324 private final Object[] argValues; 325 CtorArgSpec(Class<?>[] argTypes, Object[] argValues)326 CtorArgSpec(Class<?>[] argTypes, Object[] argValues) { 327 this.argTypes = argTypes; 328 this.argValues = argValues; 329 } 330 } 331 332 /** 333 * A simple implementation of LoggerFactory that encapsulates the 334 * necessary information to do JE environment-friendly logging without 335 * needing to know JE HA internal logging. 336 */ 337 static class ChannelLoggerFactory implements LoggerFactory { 338 private final EnvironmentImpl envImpl; 339 private final Formatter formatter; 340 341 /** 342 * Creates a LoggerFactory for use in construction of channel 343 * objects. The caller should supply either an EnvironmentImpl or a 344 * Formatter object. 345 * 346 * @param envImpl a possibly-null EnvironmentImpl 347 * @param formatter a possible null formatter 348 */ ChannelLoggerFactory(EnvironmentImpl envImpl, Formatter formatter)349 ChannelLoggerFactory(EnvironmentImpl envImpl, 350 Formatter formatter) { 351 this.envImpl = envImpl; 352 this.formatter = formatter; 353 } 354 355 /** 356 * @see LoggerFactory#getLogger(Class) 357 */ 358 @Override getLogger(Class<?> clazz)359 public InstanceLogger getLogger(Class<?> clazz) { 360 final Logger logger; 361 if (envImpl == null) { 362 logger = LoggerUtils.getLoggerFormatterNeeded(clazz); 363 } else { 364 logger = LoggerUtils.getLogger(clazz); 365 } 366 return new ChannelInstanceLogger(envImpl, formatter, logger); 367 } 368 } 369 370 /** 371 * A simple implementation of InstanceLogger that encapuslates the 372 * necessary information to do JE environment-friendly logging without 373 * needing to know JE logging rules. 374 */ 375 static class ChannelInstanceLogger implements InstanceLogger { 376 private final EnvironmentImpl envImpl; 377 private final Formatter formatter; 378 private final Logger logger; 379 380 /** 381 * Creates a ChannelInstanceLogger for use in construction of channel 382 * objects. The caller should supply either an EnvironmentImpl or a 383 * Formatter object. 384 * 385 * @param envImpl a possibly-null EnvironmentImpl 386 * @param formatter a possible null formatter 387 * @param logger a logger created via LoggerUtils.getLogger() 388 */ ChannelInstanceLogger(EnvironmentImpl envImpl, Formatter formatter, Logger logger)389 ChannelInstanceLogger(EnvironmentImpl envImpl, 390 Formatter formatter, 391 Logger logger) { 392 this.envImpl = envImpl; 393 this.formatter = formatter; 394 this.logger = logger; 395 } 396 397 /** 398 * @see InstanceLogger#log(Level, String) 399 */ 400 @Override log(Level logLevel, String msg)401 public void log(Level logLevel, String msg) { 402 LoggerUtils.logMsg(logger, envImpl, formatter, logLevel, msg); 403 } 404 } 405 406 /** 407 * Formatter for log messages 408 */ 409 static class ChannelFormatter extends TracerFormatter { 410 private final String id; 411 ChannelFormatter(String id)412 ChannelFormatter(String id) { 413 super(); 414 this.id = id; 415 } 416 417 @Override appendEnvironmentName(StringBuilder sb)418 protected void appendEnvironmentName(StringBuilder sb) { 419 sb.append(" [" + id + "]"); 420 } 421 } 422 } 423