Skip to content

Commit

Permalink
Add generator classes to generate platform-specific wrappers (#15)
Browse files Browse the repository at this point in the history
* Add generator classes to generate platform-specific wrappers

* Address PR comments

* Rebase to latest master; Address PR comments
  • Loading branch information
shardulm94 authored and rdsr committed Apr 2, 2019
1 parent 3d3974f commit 35a3ffc
Show file tree
Hide file tree
Showing 32 changed files with 967 additions and 77 deletions.
5 changes: 3 additions & 2 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ rootProject.name = 'transportable-udfs'

def modules = [
'transportable-udfs-api',
'transportable-udfs-annotation-processor',
'transportable-udfs-avro',
'transportable-udfs-codegen',
'transportable-udfs-compile-utils',
'transportable-udfs-hive',
'transportable-udfs-presto',
'transportable-udfs-annotation-processor',
'transportable-udfs-spark',
'transportable-udfs-test:transportable-udfs-test-api',
'transportable-udfs-test:transportable-udfs-test-generic',
Expand All @@ -24,4 +26,3 @@ def modules = [
modules.each { module ->
include "${module}"
}

3 changes: 2 additions & 1 deletion transportable-udfs-annotation-processor/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ apply plugin: 'java'

dependencies {
compile project(':transportable-udfs-api')
compile project(':transportable-udfs-compile-utils')
compile('com.google.guava:guava:24.1-jre')
compile('com.google.code.gson:gson:2.8.5')

Expand All @@ -10,4 +11,4 @@ dependencies {

license {
exclude '**/*.json'
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import com.linkedin.transport.api.udf.StdUDF;
import com.linkedin.transport.api.udf.TopLevelStdUDF;
import com.linkedin.transport.compile.TransportUDFMetadata;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
Expand Down Expand Up @@ -54,7 +55,7 @@ public class TransportProcessor extends AbstractProcessor {
private Elements _elements;
private TypeMirror _topLevelStdUDFInterfaceType;
private TypeMirror _stdUDFClassType;
private UDFProperties _udfProperties;
private TransportUDFMetadata _transportUdfMetadata;

@Override
public SourceVersion getSupportedSourceVersion() {
Expand All @@ -68,7 +69,7 @@ public synchronized void init(ProcessingEnvironment processingEnv) {
_elements = processingEnv.getElementUtils();
_topLevelStdUDFInterfaceType = _elements.getTypeElement(TopLevelStdUDF.class.getName()).asType();
_stdUDFClassType = _elements.getTypeElement(StdUDF.class.getName()).asType();
_udfProperties = new UDFProperties();
_transportUdfMetadata = new TransportUDFMetadata();
}

@Override
Expand Down Expand Up @@ -132,7 +133,7 @@ private void processUDFClass(TypeElement udfClassElement) {
String topLevelStdUdfClassName =
elementsOverridingTopLevelStdUDFMethods.iterator().next().getQualifiedName().toString();
debug(String.format("TopLevelStdUDF class found: %s", topLevelStdUdfClassName));
_udfProperties.addUDF(topLevelStdUdfClassName, udfClassElement.getQualifiedName().toString());
_transportUdfMetadata.addUDF(topLevelStdUdfClassName, udfClassElement.getQualifiedName().toString());
}
}

Expand Down Expand Up @@ -184,7 +185,7 @@ private void generateUDFPropertiesFile() {
try {
FileObject fileObject = filer.createResource(StandardLocation.CLASS_OUTPUT, "", Constants.UDF_RESOURCE_FILE_PATH);
try (Writer writer = fileObject.openWriter()) {
_udfProperties.toJson(writer);
_transportUdfMetadata.toJson(writer);
}
debug("Wrote Transport UDF properties file to: " + fileObject.toUri());
} catch (IOException e) {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"udfs": [
{
"topLevelStdUDFClass": "udfs.OverloadedUDF1",
"implementations": [
"udfs.OverloadedUDFString",
"udfs.OverloadedUDFInt"
"topLevelClass": "udfs.OverloadedUDF1",
"stdUDFImplementations": [
"udfs.OverloadedUDFInt",
"udfs.OverloadedUDFString"
]
}
]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"udfs": [
{
"topLevelStdUDFClass": "udfs.SimpleUDF",
"implementations": [
"topLevelClass": "udfs.SimpleUDF",
"stdUDFImplementations": [
"udfs.SimpleUDF"
]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"udfs": [
{
"topLevelStdUDFClass": "udfs.UDFExtendingAbstractUDF",
"implementations": [
"topLevelClass": "udfs.UDFExtendingAbstractUDF",
"stdUDFImplementations": [
"udfs.UDFExtendingAbstractUDF"
]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"udfs": [
{
"topLevelStdUDFClass": "udfs.AbstractUDFImplementingInterface",
"implementations": [
"topLevelClass": "udfs.AbstractUDFImplementingInterface",
"stdUDFImplementations": [
"udfs.UDFExtendingAbstractUDFImplementingInterface"
]
}
Expand Down
16 changes: 16 additions & 0 deletions transportable-udfs-codegen/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
apply plugin: 'java'


dependencies {
compile project(':transportable-udfs-api')
compile project(':transportable-udfs-compile-utils')
compile ('com.google.guava:guava:24.1-jre')
compile ('org.apache.commons:commons-io:1.3.2')
compile ('org.apache.commons:commons-text:1.6')
compile ('com.squareup:javapoet:1.11.1')
}

licenseTest {
exclude 'inputs/**'
exclude 'outputs/**'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
* Copyright 2019 LinkedIn Corporation. All rights reserved.
* Licensed under the BSD-2 Clause license.
* See LICENSE in the project root for license information.
*/
package com.linkedin.transport.codegen;

import com.google.common.base.Preconditions;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import org.apache.commons.lang3.StringUtils;


class CodegenUtils {

private CodegenUtils() {
}

/**
* Writes a source code file for a class to the provided output directory creating package directories if required
*
* @param outputDir Output directory
* @param packageName Package name of the class (can be {@code null} for the default package)
* @param className Class name
* @param extension Extension of the file (can be {@code null} for no extension)
* @param code Source code of the class
* @throws IOException
*/
static void writeCodeFile(Path outputDir, String packageName, String className, String extension,
String code) throws IOException {
Preconditions.checkArgument(Files.notExists(outputDir) || Files.isDirectory(outputDir),
"Output directory should not exist or must be a directory", outputDir);
Preconditions.checkArgument(!StringUtils.isBlank(className), "Class name should not be null or an empty string");
Preconditions.checkNotNull(code, "Code should not be null");

final Path packageDir;
if (!StringUtils.isBlank(packageName)) {
packageDir = Paths.get(outputDir.toString(), packageName.split("\\."));
} else {
packageDir = outputDir;
}
Files.createDirectories(packageDir);

String fileName = className + (StringUtils.isBlank(extension) ? "" : "." + extension);
Path outputPath = packageDir.resolve(fileName);
Files.write(outputPath, code.getBytes());
}

/**
* Writes a list of services to a specified service file
*
* @param outputDir Output directory
* @param serviceFilePath Path of the service file relative to the output directory
* @param services List of services
* @throws IOException
*/
static void writeServiceFile(Path outputDir, Path serviceFilePath, Collection<String> services)
throws IOException {
Preconditions.checkArgument(Files.notExists(outputDir) || Files.isDirectory(outputDir),
"Output directory should not exist or must be a directory", outputDir);
Preconditions.checkNotNull(serviceFilePath, "Service file path should not be null");

Path resolvedServiceFilePath = outputDir.resolve(serviceFilePath);
Files.createDirectories(resolvedServiceFilePath.getParent());

try (BufferedWriter writer = Files.newBufferedWriter(resolvedServiceFilePath)) {
for (String service : services) {
writer.write(service + "\n");
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/**
* Copyright 2019 LinkedIn Corporation. All rights reserved.
* Licensed under the BSD-2 Clause license.
* See LICENSE in the project root for license information.
*/
package com.linkedin.transport.codegen;

import com.google.common.collect.ImmutableList;
import com.linkedin.transport.api.udf.StdUDF;
import com.linkedin.transport.api.udf.TopLevelStdUDF;
import com.linkedin.transport.compile.TransportUDFMetadata;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.WildcardTypeName;
import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import javax.lang.model.element.Modifier;


public class HiveWrapperGenerator implements WrapperGenerator {

private static final String HIVE_PACKAGE_SUFFIX = "hive";
private static final String GET_TOP_LEVEL_UDF_CLASS_METHOD = "getTopLevelUdfClass";
private static final String GET_STD_UDF_IMPLEMENTATIONS_METHOD = "getStdUdfImplementations";
private static final ClassName HIVE_STD_UDF_WRAPPER_CLASS_NAME =
ClassName.bestGuess("com.linkedin.transport.hive.StdUdfWrapper");

@Override
public void generateWrappers(WrapperGeneratorContext context) {
TransportUDFMetadata udfMetadata = context.getTransportUdfMetadata();
for (String topLevelClass : udfMetadata.getTopLevelClasses()) {
generateWrapper(topLevelClass, udfMetadata.getStdUDFImplementations(topLevelClass),
context.getSourcesOutputDir());
}
}

private void generateWrapper(String topLevelClass, Collection<String> implementationClasses, File outputDir) {

ClassName topLevelClassName = ClassName.bestGuess(topLevelClass);
ClassName wrapperClassName = ClassName.get(topLevelClassName.packageName() + "." + HIVE_PACKAGE_SUFFIX,
topLevelClassName.simpleName());

/*
Generates ->
@Override
protected Class<? extends TopLevelStdUDF> getTopLevelUdfClass() {
return ${topLevelClass}.class;
}
*/
MethodSpec getTopLevelUdfClassMethod = MethodSpec.methodBuilder(GET_TOP_LEVEL_UDF_CLASS_METHOD)
.addAnnotation(Override.class)
.returns(
ParameterizedTypeName.get(ClassName.get(Class.class), WildcardTypeName.subtypeOf(TopLevelStdUDF.class)))
.addModifiers(Modifier.PROTECTED)
.addStatement("return $T.class", topLevelClassName)
.build();

/*
Generates ->
@Override
protected List<? extends StdUDF> getStdUdfImplementations() {
return ImmutableList.of(
new ${implementationClasses(0)}(),
new ${implementationClasses(1)}(),
.
.
.
);
}
*/
MethodSpec getStdUdfImplementationsMethod = MethodSpec.methodBuilder(GET_STD_UDF_IMPLEMENTATIONS_METHOD)
.addAnnotation(Override.class)
.returns(ParameterizedTypeName.get(ClassName.get(List.class), WildcardTypeName.subtypeOf(StdUDF.class)))
.addModifiers(Modifier.PROTECTED)
.addStatement("return $T.of($L)", ImmutableList.class, implementationClasses.stream()
.map(clazz -> "new " + clazz + "()")
.collect(Collectors.joining(", ")))
.build();

/*
Generates ->
public class ${wrapperClassName} extends StdUdfWrapper {
.
.
.
}
*/
TypeSpec wrapperClass = TypeSpec.classBuilder(wrapperClassName)
.addModifiers(Modifier.PUBLIC)
.superclass(HIVE_STD_UDF_WRAPPER_CLASS_NAME)
.addMethod(getTopLevelUdfClassMethod)
.addMethod(getStdUdfImplementationsMethod)
.build();

JavaFile javaFile = JavaFile.builder(wrapperClassName.packageName(), wrapperClass).build();

try {
javaFile.writeTo(outputDir);
} catch (Exception e) {
throw new RuntimeException("Error writing wrapper to file", e);
}
}
}
Loading

0 comments on commit 35a3ffc

Please sign in to comment.