Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0b8da6d
Removed Sun depedencies.
HmSebastianH May 4, 2020
5b61e01
Changed java target version because of depedency
HmSebastianH May 4, 2020
35777d9
Added Envvar lookup as maven home fallback.
HmSebastianH May 4, 2020
8af896c
Minor fixes including: * Better identificaiton of injected ctor
HmSebastianH May 12, 2020
6a69400
Changed generated files to be java 11
HmSebastianH May 14, 2020
68b054f
Changed some depedencie to avoid aws-create problems.
HmSebastianH May 14, 2020
6e197c1
Added some notes for later implementations.
HmSebastianH May 19, 2020
314cf6e
Changed lambda invocation to be blocking.
HmSebastianH May 26, 2020
4ea12b6
Fixed retun values for invoked void methods.
HmSebastianH May 26, 2020
20f84c5
Added Util class which allows async dispatching of (lambda) functions.
HmSebastianH May 26, 2020
c0adc21
Made default handling of invocations synchronous.
HmSebastianH May 26, 2020
ec202fc
Updated usage of lambda api to avoid deprecated methods.
HmSebastianH May 26, 2020
c932735
Updated usage of lambda api to avoid deprecated methods.
HmSebastianH May 26, 2020
c11d4a2
Some wip utlities to analyze the depedencies of classes.
HmSebastianH Jun 7, 2020
0c0861a
Removed unecesarry code and finished depedency analysis.
HmSebastianH Jun 7, 2020
40cb2f6
Removal of dead code.
HmSebastianH Jun 7, 2020
74f1ec8
Added copying of required CUs to lambda writing.
HmSebastianH Jun 7, 2020
28bf79e
Fixed a null pointer bug in compiler if no package deklaration is used.
HmSebastianH Jun 9, 2020
3e1529c
Added imports to input and output type if required.
HmSebastianH Jun 9, 2020
cf6fa37
Small style impÃrovements.
HmSebastianH Jun 9, 2020
475e6a3
reenabled upload (was disbaled for testing purposes.
HmSebastianH Jun 9, 2020
2105a4f
Small adjustments to file generation and fixed bug with geneerated so…
HmSebastianH Jun 9, 2020
8636b04
Minor logging changes.
HmSebastianH Jun 9, 2020
4cecff5
Improved behaviour if multiple annotation processing runs are performed.
HmSebastianH Jun 9, 2020
cef2356
Added basic handling of non static methods, this reference does not y…
HmSebastianH Jun 9, 2020
5d65783
Fixed handling of non static methods and added serialization for priv…
HmSebastianH Jun 12, 2020
ff25864
Improved matching of lambda annotation to avoid deleting too much code.
HmSebastianH Jun 12, 2020
d5438f4
Made logging less verbose.
HmSebastianH Jun 12, 2020
1bbacd3
Minor changes to fallback behaviour.
HmSebastianH Jun 12, 2020
b66ab74
Fixed a bug with generics not being imported correctlyÂ
HmSebastianH Jun 23, 2020
f7dd7f1
Added filtering of builtins to import List
HmSebastianH Jun 23, 2020
7476107
Removed unused code.
HmSebastianH Jun 30, 2020
fad4962
Delete Termite.iml
HmSebastianH Jun 30, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 7 additions & 13 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.source-target.version>1.8</java.source-target.version>
<aspectj.version>1.8.2</aspectj.version>
<java.source-target.version>11</java.source-target.version>
<aspectj.version>1.9.5</aspectj.version>
</properties>

<dependencies>
Expand All @@ -25,13 +25,6 @@
<artifactId>maven-model</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>com.sun.tools</groupId>
<artifactId>tools</artifactId>
<version>${java.version}</version>
<scope>system</scope>
<systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
Expand All @@ -56,20 +49,21 @@
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-lambda</artifactId>
<version>1.11.60</version>
<version>1.11.774</version>
</dependency>


</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.0</version>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<source>11</source>
<target>11</target>
<!-- Disable annotation processing for ourselves. -->
<compilerArgument>-proc:none</compilerArgument>
</configuration>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import ch.zhaw.splab.podilizerproc.awslambda.Functions;
import ch.zhaw.splab.podilizerproc.awslambda.LambdaFunction;
import ch.zhaw.splab.podilizerproc.depdencies.CompilationUnitInfo;
import ch.zhaw.splab.podilizerproc.depdencies.DependencyResolver;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.MethodTree;
Expand All @@ -10,51 +12,73 @@
import com.sun.source.util.TreePathScanner;
import com.sun.source.util.Trees;

import javax.annotation.processing.*;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.util.Types;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.Writer;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
* Processor of {@link Lambda} annotation
*/
@SupportedAnnotationTypes({"ch.zhaw.splab.podilizerproc.annotations.Lambda"})
public class LambdaProcessor extends AbstractProcessor {

private Trees trees;
private Types typeUtils;

/**
* Initialization of {@link ProcessEnvironment} object and {@link Trees} object
* Initialization.
*
* @param processingEnv
*/
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
trees = Trees.instance(processingEnv);
typeUtils = processingEnv.getTypeUtils();

}


public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
;
// TODO: For a normal maven compile call this class / method is called twice.
// This method should somehow detect if it was already run and should avoid executing twice.
if (roundEnv.errorRaised()) {
System.out.println("[TERMITE] Code not compilable, skipping processing for now...");
return false;
}
if (roundEnv.processingOver()) {
System.out.println("[TERMITE] Nothing left todo, skipping processing...");
return false;
}


List<LambdaFunction> functions = new ArrayList<>();

Set<? extends Element> annotatedMethods = roundEnv.getElementsAnnotatedWith(Lambda.class);
if (annotatedMethods.size() == 0) {
if (annotatedMethods.isEmpty()) {
return true;
}
DependencyResolver dependencyResolver = new DependencyResolver(trees, roundEnv);

for (Element element :
annotatedMethods) {
MethodScanner methodScanner = new MethodScanner();
TypeScanner typeScanner = new TypeScanner();
CUVisitor cuVisitor = new CUVisitor();

Set<CompilationUnitInfo> requiredCompilationUnits = dependencyResolver.resolveDependencies(element);

TreePath tp = trees.getPath(element);
methodScanner.scan(tp, trees);
Expand All @@ -63,15 +87,24 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
TreePath tp1 = trees.getPath(getMostExternalType(element));
cuVisitor.visit(tp1, trees);

ExecutableType emeth = (ExecutableType) element.asType();

Lambda lambda = element.getAnnotation(Lambda.class);
LambdaFunction lambdaFunction =
new LambdaFunction(methodScanner.getMethod(), typeScanner.getClazz(), cuVisitor.getCu(), lambda);
new LambdaFunction(methodScanner.getMethod(),
typeScanner.getClazz(),
cuVisitor.getCu(),
lambda,
requiredCompilationUnits,
emeth.getParameterTypes(),
emeth.getReturnType());
functions.add(lambdaFunction);

try {
String packageName = lambdaFunction.generateInputPackage();
System.out.println("[TERMITE] Generating local Data Classes. For Package: " + packageName);
String generatedClassPath = packageName.substring(8, packageName.length() - 1);
JavaFileObject inputType = processingEnv.getFiler().createSourceFile(generatedClassPath +".InputType", null);
JavaFileObject inputType = processingEnv.getFiler().createSourceFile(generatedClassPath + ".InputType", null);
JavaFileObject outputType = processingEnv.getFiler().createSourceFile(generatedClassPath + ".OutputType", null);
Writer writer = inputType.openWriter();
Writer writer1 = outputType.openWriter();
Expand All @@ -81,8 +114,11 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
writer1.append(lambdaFunction.createOutputType());
writer.flush();
writer1.flush();
writer.close();
writer1.close();
System.out.println("[TERMITE] Successfully generated local sources.");
} catch (IOException e) {
e.printStackTrace();
System.out.println("[TERMITE] Failed to generate local sources. They probably already exist.");
}

}
Expand Down
191 changes: 186 additions & 5 deletions src/main/java/ch/zhaw/splab/podilizerproc/aspect/Invoke.java
Original file line number Diff line number Diff line change
@@ -1,25 +1,206 @@
package ch.zhaw.splab.podilizerproc.aspect;


import ch.zhaw.splab.podilizerproc.awslambda.InvokeThread;
import ch.zhaw.splab.podilizerproc.annotations.Lambda;
import ch.zhaw.splab.podilizerproc.awslambda.AwsCredentialsReader;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.lambda.AWSLambda;
import com.amazonaws.services.lambda.AWSLambdaClientBuilder;
import com.amazonaws.services.lambda.model.InvokeRequest;
import com.amazonaws.services.lambda.model.InvokeResult;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Aspect
public class Invoke {

@Around("@annotation(lambda)")
public Object anyExec(ProceedingJoinPoint joinPoint,
ch.zhaw.splab.podilizerproc.annotations.Lambda lambda) throws Throwable {
System.out.println("[TERMITE] Annotation invoked");
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();

InvokeThread invokeThread = new InvokeThread(method, lambda, joinPoint);
invokeThread.start();
return null;
return invokeOnLambda(method, lambda, joinPoint);
}

private Object invokeOnLambda(Method method, Lambda lambda, ProceedingJoinPoint joinPoint) throws Throwable {
Class inClazz = null;
Class outClazz = null;
try {
inClazz = Class.forName(getInputPackage(method));
outClazz = Class.forName(getOutputPackage(method));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
AwsCredentialsReader credentialsReader = new AwsCredentialsReader();
credentialsReader.read();
String awsAccessKeyId = credentialsReader.getAwsAccessKeyId();
String awsSecretKeyAccessKey = credentialsReader.getAwsSecretAccessKey();
String regionName = lambda.region();
String functionName = getFunctionName(method);

AWSCredentials credentials = new BasicAWSCredentials(awsAccessKeyId, awsSecretKeyAccessKey);
Regions region = Regions.fromName(regionName);


AWSLambdaClientBuilder clientBuilder = AWSLambdaClientBuilder
.standard()
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withRegion(region);

if (!lambda.endPoint().equals("")) {
clientBuilder = clientBuilder.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(lambda.endPoint(), regionName));
}
AWSLambda awsLambda = clientBuilder.build();

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
String json = "";
try {
List<Object> ctorArgs = new ArrayList<>();
if (!Modifier.isStatic(joinPoint.getSignature().getModifiers())) {
ctorArgs.add(joinPoint.getTarget());
}
ctorArgs.addAll(Arrays.asList(joinPoint.getArgs()));


Constructor<Object> correctCtor = null;
for (Constructor<Object> constructor : inClazz.getConstructors()) {
if (constructor.getParameterCount() == ctorArgs.size()) {
correctCtor = constructor;
break;
}
}

Object inputObj = correctCtor.newInstance(ctorArgs.toArray());
json = objectMapper.writeValueAsString(inputObj);
// TODO: Remove these verbose printouts (Or make log level configurable)
System.out.println("[TERMITE] Sending json input.");
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | JsonProcessingException e) {
e.printStackTrace();
}
Object outObj = null;
Object methodResult = null;

try {
InvokeRequest invokeRequest = new InvokeRequest();
invokeRequest.setFunctionName(functionName);
invokeRequest.setPayload(json);
System.out.println("[TERMITE] Starting ");
InvokeResult invokeResult = awsLambda.invoke(invokeRequest);
String resultJson = byteBufferToString(invokeResult.getPayload(), StandardCharsets.UTF_8);
System.out.println("[TERMITE] Received json output."
+ " Size: " + invokeResult.getSdkHttpMetadata().getAllHttpHeaders().get("Content-Length"));
System.out.flush();
try {
outObj = objectMapper.readValue(resultJson, outClazz);
objectMapper.readerForUpdating(outObj).readValue(resultJson);
} catch (Throwable t) {
System.out.println("[TERMITE] Failed to deserialize result.");
t.printStackTrace();
}
// TODO: Remove these verbose printouts (Or make log level configurable)

MethodSignature signature = (MethodSignature) joinPoint.getSignature();
if (outObj != null && signature != null && !void.class.equals(signature.getReturnType())) {
// GetResult is only generated for non void methods
Method getResultMethod = outObj.getClass().getDeclaredMethod("getResult");
methodResult = getResultMethod.invoke(outObj);
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("Function " + method.getName() + " is unreachable. Processing locally...");

// Note: While using the joinPoint to proceed normal execution, the code will sometimes be executed twice.
// This is a known AspectJ Bug documented here: https://github.com/spring-projects/spring-framework/issues/24046
methodResult = joinPoint.proceed();
}
awsLambda.shutdown();

try {
if (outObj != null) {
String functionReport = "Thread of Function " + method.getName() + " invocation was finished. " +
"Function performed at - " + outObj.getClass().getDeclaredMethod("getDefaultReturn", null).invoke(outObj) +
" - for " + outObj.getClass().getDeclaredMethod("getTime", null).invoke(outObj) + " ms";
System.out.println(functionReport);
} else {
System.out.println("Processed " + method.getName() + " locally with result " + methodResult);
}
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}

return methodResult;
}


/**
* Generates package name for input type based on method signature
*
* @param method is method signature object
* @return {@link String} package name of InputType
*/
private String getInputPackage(Method method) {
String fullClassName = method.getDeclaringClass().getName();
return "aws." + fullClassName + "." + method.getName() + method.getParameterCount() + ".InputType";
}

/**
* Generates package name for output type based on method signature
*
* @param method is method signature object
* @return {@link String} package name of OutputType
*/
private String getOutputPackage(Method method) {
String fullClassName = method.getDeclaringClass().getName();
return "aws." + fullClassName + "." + method.getName() + method.getParameterCount() + ".OutputType";
}

/**
* Generates function name for annotated method over the load process
*
* @param method is annotated method to generate lambda function name for
* @return {@link String} name of format 'package_class_method_#argsNumber'
*/
private String getFunctionName(Method method) {
String result = method.getDeclaringClass().getName().replace('.', '_');
result += "_" + method.getName();
result += method.getParameterCount();
return result;
}

private static String byteBufferToString(ByteBuffer buffer, Charset charset) {
byte[] bytes;
if (buffer.hasArray()) {
bytes = buffer.array();
} else {
bytes = new byte[buffer.remaining()];
buffer.get(bytes);
}
return new String(bytes, charset);
}
}
Loading