/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.pinterest.secor.util; import com.pinterest.secor.common.LogFilePath; import com.pinterest.secor.common.SecorConfig; import com.pinterest.secor.io.FileReader; import com.pinterest.secor.io.FileReaderWriterFactory; import com.pinterest.secor.io.FileWriter; import com.pinterest.secor.monitoring.MetricCollector; import com.pinterest.secor.parser.MessageParser; import com.pinterest.secor.transformer.MessageTransformer; import com.pinterest.secor.uploader.UploadManager; import com.pinterest.secor.uploader.Uploader; import com.pinterest.secor.util.orc.schema.ORCScehmaProvider; import org.apache.hadoop.io.compress.CompressionCodec; /** * ReflectionUtil implements utility methods to construct objects of classes * specified by name. * * @author Pawel Garbacki (pawel@pinterest.com) * @author Silas Davis (github-code@silasdavis.net) */ public class ReflectionUtil { /** * Create an UploadManager from its fully qualified class name. * * The class passed in by name must be assignable to UploadManager * and have 1-parameter constructor accepting a SecorConfig. * * See the secor.upload.manager.class config option. * * @param className The class name of a subclass of UploadManager * @param config The SecorCondig to initialize the UploadManager with * @return an UploadManager instance with the runtime type of the class passed by name * @throws Exception */ public static UploadManager createUploadManager(String className, SecorConfig config) throws Exception { Class<?> clazz = Class.forName(className); if (!UploadManager.class.isAssignableFrom(clazz)) { throw new IllegalArgumentException(String.format("The class '%s' is not assignable to '%s'.", className, UploadManager.class.getName())); } // Assume that subclass of UploadManager has a constructor with the same signature as UploadManager return (UploadManager) clazz.getConstructor(SecorConfig.class).newInstance(config); } /** * Create an Uploader from its fully qualified class name. * * The class passed in by name must be assignable to Uploader. * See the secor.upload.class config option. * * @param className The class name of a subclass of Uploader * @return an UploadManager instance with the runtime type of the class passed by name * @throws Exception */ public static Uploader createUploader(String className) throws Exception { Class<?> clazz = Class.forName(className); if (!Uploader.class.isAssignableFrom(clazz)) { throw new IllegalArgumentException(String.format("The class '%s' is not assignable to '%s'.", className, Uploader.class.getName())); } return (Uploader) clazz.newInstance(); } /** * Create a MessageParser from it's fully qualified class name. * The class passed in by name must be assignable to MessageParser and have 1-parameter constructor accepting a SecorConfig. * Allows the MessageParser to be pluggable by providing the class name of a desired MessageParser in config. * * See the secor.message.parser.class config option. * * @param className The class name of a subclass of MessageParser * @param config The SecorCondig to initialize the MessageParser with * @return a MessageParser instance with the runtime type of the class passed by name * @throws Exception */ public static MessageParser createMessageParser(String className, SecorConfig config) throws Exception { Class<?> clazz = Class.forName(className); if (!MessageParser.class.isAssignableFrom(clazz)) { throw new IllegalArgumentException(String.format("The class '%s' is not assignable to '%s'.", className, MessageParser.class.getName())); } // Assume that subclass of MessageParser has a constructor with the same signature as MessageParser return (MessageParser) clazz.getConstructor(SecorConfig.class).newInstance(config); } /** * Create a FileReaderWriterFactory that is able to read and write a specific type of output log file. * The class passed in by name must be assignable to FileReaderWriterFactory. * Allows for pluggable FileReader and FileWriter instances to be constructed for a particular type of log file. * * See the secor.file.reader.writer.factory config option. * * @param className the class name of a subclass of FileReaderWriterFactory * @param config The SecorCondig to initialize the FileReaderWriterFactory with * @return a FileReaderWriterFactory with the runtime type of the class passed by name * @throws Exception */ private static FileReaderWriterFactory createFileReaderWriterFactory(String className, SecorConfig config) throws Exception { Class<?> clazz = Class.forName(className); if (!FileReaderWriterFactory.class.isAssignableFrom(clazz)) { throw new IllegalArgumentException(String.format("The class '%s' is not assignable to '%s'.", className, FileReaderWriterFactory.class.getName())); } try { // Try to load constructor that accepts single parameter - secor // configuration instance return (FileReaderWriterFactory) clazz.getConstructor(SecorConfig.class).newInstance(config); } catch (NoSuchMethodException e) { // Fallback to parameterless constructor return (FileReaderWriterFactory) clazz.newInstance(); } } /** * Use the FileReaderWriterFactory specified by className to build a FileWriter * * @param className the class name of a subclass of FileReaderWriterFactory to create a FileWriter from * @param logFilePath the LogFilePath that the returned FileWriter should write to * @param codec an instance CompressionCodec to compress the file written with, or null for no compression * @param config The SecorCondig to initialize the FileWriter with * @return a FileWriter specialised to write the type of files supported by the FileReaderWriterFactory * @throws Exception */ public static FileWriter createFileWriter(String className, LogFilePath logFilePath, CompressionCodec codec, SecorConfig config) throws Exception { return createFileReaderWriterFactory(className, config).BuildFileWriter(logFilePath, codec); } /** * Use the FileReaderWriterFactory specified by className to build a FileReader * * @param className the class name of a subclass of FileReaderWriterFactory to create a FileReader from * @param logFilePath the LogFilePath that the returned FileReader should read from * @param codec an instance CompressionCodec to decompress the file being read, or null for no compression * @param config The SecorCondig to initialize the FileReader with * @return a FileReader specialised to read the type of files supported by the FileReaderWriterFactory * @throws Exception */ public static FileReader createFileReader(String className, LogFilePath logFilePath, CompressionCodec codec, SecorConfig config) throws Exception { return createFileReaderWriterFactory(className, config).BuildFileReader(logFilePath, codec); } /** * Create a MessageTransformer from it's fully qualified class name. The * class passed in by name must be assignable to MessageTransformers and have * 1-parameter constructor accepting a SecorConfig. Allows the MessageTransformers * to be pluggable by providing the class name of a desired MessageTransformers in * config. * * See the secor.message.transformer.class config option. * * @param className * @param config * @return * @throws Exception */ public static MessageTransformer createMessageTransformer( String className, SecorConfig config) throws Exception { Class<?> clazz = Class.forName(className); if (!MessageTransformer.class.isAssignableFrom(clazz)) { throw new IllegalArgumentException(String.format( "The class '%s' is not assignable to '%s'.", className, MessageTransformer.class.getName())); } return (MessageTransformer) clazz.getConstructor(SecorConfig.class) .newInstance(config); } /** * Create an MetricCollector from its fully qualified class name. * <p> * The class passed in by name must be assignable to MetricCollector. * See the secor.monitoring.metrics.collector.class config option. * * @param className The class name of a subclass of MetricCollector * @return a MetricCollector with the runtime type of the class passed by name * @throws ClassNotFoundException if class with the {@code className} is not found in classpath * @throws IllegalAccessException if the class or its nullary * constructor is not accessible. * @throws InstantiationException if this {@code Class} represents an abstract class, * an interface, an array class, a primitive type, or void; * or if the class has no nullary constructor; * or if the instantiation fails for some other reason. */ public static MetricCollector createMetricCollector(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException { Class<?> clazz = Class.forName(className); if (!MetricCollector.class.isAssignableFrom(clazz)) { throw new IllegalArgumentException(String.format("The class '%s' is not assignable to '%s'.", className, MetricCollector.class.getName())); } return (MetricCollector) clazz.newInstance(); } /** * Create a ORCScehmaProvider from it's fully qualified class name. The * class passed in by name must be assignable to ORCScehmaProvider and have * 1-parameter constructor accepting a SecorConfig. Allows the ORCScehmaProvider * to be pluggable by providing the class name of a desired ORCScehmaProvider in * config. * * See the secor.orc.schema.provider config option. * * @param className * @param config * @return * @throws Exception */ public static ORCScehmaProvider createORCSchemaProvider( String className, SecorConfig config) throws Exception { Class<?> clazz = Class.forName(className); if (!ORCScehmaProvider.class.isAssignableFrom(clazz)) { throw new IllegalArgumentException(String.format( "The class '%s' is not assignable to '%s'.", className, ORCScehmaProvider.class.getName())); } return (ORCScehmaProvider) clazz.getConstructor(SecorConfig.class) .newInstance(config); } }