Remove spring-cloud-function-compiler module

Fixes gh-805
This commit is contained in:
onobc
2022-02-11 20:29:54 -06:00
committed by Oleg Zhurakousky
parent 3fa021b4fa
commit ea1e85bda2
75 changed files with 0 additions and 7110 deletions

View File

@@ -62,7 +62,6 @@ endpoints and/or message stream listeners/publishers with RabbitMQ, Kafka etc.
* _Packaging functions for deployments, specific to the target platform (e.g., Project Riff, AWS Lambda and more)_
* _Adapters to expose function to the outside world as HTTP endpoints etc._
* _Deploying a JAR file containing such an application context with an isolated classloader, so that you can pack them together in a single JVM._
* _Compiling strings which are Java function bodies into bytecode, and then turning them into `@Beans` that can be wrapped as above._
* _Adapters for https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-aws[AWS Lambda], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-azure[Azure], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp[Google Cloud Functions], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk[Apache OpenWhisk] and possibly other "serverless" service providers._
== Getting Started

View File

@@ -49,5 +49,4 @@ endpoints and/or message stream listeners/publishers with RabbitMQ, Kafka etc.
* _Packaging functions for deployments, specific to the target platform (e.g., Project Riff, AWS Lambda and more)_
* _Adapters to expose function to the outside world as HTTP endpoints etc._
* _Deploying a JAR file containing such an application context with an isolated classloader, so that you can pack them together in a single JVM._
* _Compiling strings which are Java function bodies into bytecode, and then turning them into `@Beans` that can be wrapped as above._
* _Adapters for https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-aws[AWS Lambda], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-azure[Azure], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp[Google Cloud Functions], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk[Apache OpenWhisk] and possibly other "serverless" service providers._

View File

@@ -18,7 +18,6 @@ Spring Cloud Function features:
* _Packaging functions for deployments, specific to the target platform (e.g., Project Riff, AWS Lambda and more)_
* _Adapters to expose function to the outside world as HTTP endpoints etc._
* _Deploying a JAR file containing such an application context with an isolated classloader, so that you can pack them together in a single JVM._
* _Compiling strings which are Java function bodies into bytecode, and then turning them into `@Beans` that can be wrapped as above._
* _Adapters for https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-aws[AWS Lambda], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-azure[Microsoft Azure], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp[Google Cloud Functions], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk[Apache OpenWhisk] and possibly other "serverless" service providers._
Here's a complete, executable, testable Spring Boot application (implementing a simple string manipulation):

View File

@@ -1,3 +0,0 @@
#!/bin/bash
java -jar ../spring-cloud-function-compiler/target/spring-cloud-function-compiler-2.0.0.BUILD-SNAPSHOT.jar

View File

@@ -1,17 +0,0 @@
#!/bin/bash
while getopts ":n:f:t:" opt; do
case $opt in
n)
NAME=$OPTARG
;;
f)
FUNC=$OPTARG
;;
t)
TYPE=$OPTARG
;;
esac
done
curl -X POST -H "Content-Type: text/plain" -d $FUNC localhost:8080/consumer/$NAME?type=$TYPE

View File

@@ -1,21 +0,0 @@
#!/bin/bash
while getopts ":n:f:i:o:" opt; do
case $opt in
n)
NAME=$OPTARG
;;
f)
FUNC=$OPTARG
;;
i)
INTYPE=$OPTARG
;;
o)
OUTTYPE=$OPTARG
;;
esac
done
curl -X POST -H "Content-Type: text/plain" -d $FUNC "localhost:8080/function/$NAME?inputType=$INTYPE&outputType=$OUTTYPE"

View File

@@ -1,17 +0,0 @@
#!/bin/bash
while getopts ":n:f:t:" opt; do
case $opt in
n)
NAME=$OPTARG
;;
f)
FUNC=$OPTARG
;;
t)
TYPE=$OPTARG
;;
esac
done
curl -X POST -H "Content-Type: text/plain" -d $FUNC localhost:8080/supplier/$NAME?type=$TYPE

View File

@@ -1,56 +0,0 @@
#!/bin/bash
PREFIX="--spring.cloud.function.import"
DIR="file:///tmp/function-registry"
tokenize() {
local IFS=,
local TOKENS=($1)
echo ${TOKENS[@]}
}
DURATION=0
while getopts ":i:s:f:c:o:p:d:" opt; do
case $opt in
i)
IN=--spring.cloud.stream.bindings.input.destination=$OPTARG
;;
s)
FUNC=$OPTARG
TYPE="$PREFIX.$FUNC.type=supplier"
RESOURCE="$PREFIX.$FUNC.location=$DIR/suppliers/$FUNC.fun"
;;
f)
FUNC=$OPTARG
for i in `tokenize $OPTARG`; do
RESOURCE="$RESOURCE $PREFIX.${i}.location=$DIR/functions/${i}.fun"
TYPE="$TYPE $PREFIX.${i}.type=function"
done
;;
c)
FUNC=$OPTARG
TYPE="$PREFIX.$FUNC.type=consumer"
RESOURCE="$PREFIX.$FUNC.location=$DIR/consumers/$FUNC.fun"
;;
o)
OUT=--spring.cloud.stream.bindings.output.destination=$OPTARG
;;
p)
PORT=$OPTARG
;;
d)
DURATION=$OPTARG
;;
esac
done
java -jar ../spring-cloud-function-samples/function-sample-compiler/target/function-sample-compiler-2.0.0.BUILD-SNAPSHOT.jar\
--management.security.enabled=false\
--server.port=$PORT\
--spring.cloud.function.stream.endpoint=$FUNC\
--spring.cloud.function.stream.interval=$DURATION\
$IN\
$OUT\
$RESOURCE\
$TYPE

View File

@@ -1,18 +0,0 @@
#!/bin/bash
while getopts ":s:f:c:" opt; do
case $opt in
s)
SUPP=$OPTARG
;;
f)
FUNC=$OPTARG
;;
c)
CONS=$OPTARG
;;
esac
done
java -noverify -XX:TieredStopAtLevel=1 -Xss256K -Xms16M -Xmx256M -XX:MaxMetaspaceSize=128M -jar ../spring-cloud-function-task/target/spring-cloud-function-task-2.0.0.BUILD-SNAPSHOT.jar\
--lambda.supplier=$SUPP --lambda.function=$FUNC --lambda.consumer=$CONS

View File

@@ -1,27 +0,0 @@
#!/bin/bash
while getopts ":s:f:c:p:" opt; do
case $opt in
s)
FUNC=$OPTARG
TYPE=supplier
;;
f)
FUNC=$OPTARG
TYPE=function
;;
c)
FUNC=$OPTARG
TYPE=consumer
;;
p)
PORT=$OPTARG
;;
esac
done
java -jar ../spring-cloud-function-samples/function-sample-compiler/target/function-sample-compiler-2.0.0.BUILD-SNAPSHOT.jar\
--spring.cloud.function.import.$FUNC.type=$TYPE\
--spring.cloud.function.import.$FUNC.location=file:///tmp/function-registry/$TYPE's'/$FUNC.fun\
--management.security.enabled=false\
--server.port=$PORT

View File

@@ -1,189 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler;
import java.util.List;
import java.util.regex.Matcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.function.compiler.java.CompilationFailedException;
import org.springframework.cloud.function.compiler.java.CompilationMessage;
import org.springframework.cloud.function.compiler.java.CompilationResult;
import org.springframework.cloud.function.compiler.java.RuntimeJavaCompiler;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* @param <F> result type
* @author Andy Clement
* @author Mark Fisher
* @author Oleg Zhurakousky
*/
public abstract class AbstractFunctionCompiler<F> {
// Newlines in the property are escaped
private static final String NEWLINE_ESCAPE = Matcher.quoteReplacement("\\n");
// Individual double-quote characters are represented by two double quotes in the DSL
private static final String DOUBLE_DOUBLE_QUOTE = Matcher.quoteReplacement("\"\"");
private static Logger logger = LoggerFactory
.getLogger(AbstractFunctionCompiler.class);
/**
* The user supplied code snippet is inserted into the template and then the result is
* compiled.
*/
// @formatter:off
private static String SOURCE_CODE_TEMPLATE = "package "
+ AbstractFunctionCompiler.class.getPackage().getName() + ";\n"
+ "import java.util.*;\n" // Helpful to include this
+ "import java.time.*;\n" // Helpful to include this
+ "import java.util.function.*;\n"
+ "import reactor.core.publisher.Flux;\n"
+ "public class %s implements CompilationResultFactory<%s> {\n"
+ " public %s<%s> getResult() {\n"
+ " %s\n"
+ " }\n"
+ "}\n";
// @formatter:on
private final ResultType resultType;
private final String[] defaultResultTypeParameterizations;
private final RuntimeJavaCompiler compiler = new RuntimeJavaCompiler();
AbstractFunctionCompiler(ResultType type,
String... defaultResultTypeParameterizations) {
this.resultType = type;
this.defaultResultTypeParameterizations = defaultResultTypeParameterizations;
}
private static String decode(String input) {
return input.replaceAll(NEWLINE_ESCAPE, "\n").replaceAll(DOUBLE_DOUBLE_QUOTE,
"\"");
}
/**
* Produce a factory instance by:
* <ul>
* <li>Decoding the code String to process any newlines/double-double-quotes
* <li>Insert the code into the source code template for a class
* <li>Compiling the class using the JDK provided Java Compiler
* <li>Loading the compiled class
* <li>Invoking a well known method on the factory class to produce a Consumer,
* Function, or Supplier instance
* <li>Returning that instance.
* </ul>
* @param name - name of the function
* @param code - code of the function
* @param resultTypeParameterizations - result types
* @return a factory instance
*/
public final CompiledFunctionFactory<F> compile(String name, String code,
String... resultTypeParameterizations) {
if (name == null || name.length() == 0) {
throw new IllegalArgumentException("name must not be empty");
}
logger.info("Initial code property value :'{}'", code);
String[] parameterizedTypes = (!ObjectUtils.isEmpty(resultTypeParameterizations))
? resultTypeParameterizations : this.defaultResultTypeParameterizations;
code = decode(code);
if (code.startsWith("\"") && code.endsWith("\"")) {
code = code.substring(1, code.length() - 1);
}
if (!code.startsWith("return ") && !code.endsWith(";")) {
code = String.format("return (%s<%s> & java.io.Serializable) %s;",
this.resultType,
StringUtils.arrayToCommaDelimitedString(parameterizedTypes), code);
}
logger.info("Processed code property value :\n{}\n", code);
String firstLetter = name.substring(0, 1).toUpperCase();
name = (name.length() > 1) ? firstLetter + name.substring(1) : firstLetter;
String className = String.format("%s.%s%sFactory",
this.getClass().getPackage().getName(), name, this.resultType);
CompilationResult compilationResult = buildAndCompileSourceCode(className, code,
parameterizedTypes);
if (compilationResult.wasSuccessful()) {
CompiledFunctionFactory<F> factory = new CompiledFunctionFactory<>(className,
compilationResult);
return this.postProcessCompiledFunctionFactory(factory);
}
List<CompilationMessage> compilationMessages = compilationResult
.getCompilationMessages();
throw new CompilationFailedException(compilationMessages);
}
/**
* Implementing subclasses may override this, e.g. to set the input and/or output
* types.
* @param factory the {@link CompiledFunctionFactory} produced by
* {@link #compile(String, String, String...)}
* @return the post-processed {@link CompiledFunctionFactory}
*/
protected CompiledFunctionFactory<F> postProcessCompiledFunctionFactory(
CompiledFunctionFactory<F> factory) {
return factory;
}
/**
* Create the source for and then compile and load a class that embodies the supplied
* methodBody. The methodBody is inserted into a class template that returns the
* specified parameterized type. This method can return more than one class if the
* method body includes local class declarations. An example methodBody would be
* <tt>return input -> input.buffer(5).map(list->list.get(0));</tt>.
* @param className the name of the class
* @param methodBody the source code for a method
* @param parameterizedTypes the array of String representations for the parameterized
* input and/or output types, e.g.: <tt>&lt;Flux&lt;Object&gt;&gt;</tt>
* @return the list of Classes produced by compiling and then loading the snippet of
* code
*/
private CompilationResult buildAndCompileSourceCode(String className,
String methodBody, String[] parameterizedTypes) {
String sourceCode = makeSourceClassDefinition(className, methodBody,
parameterizedTypes);
return this.compiler.compile(className, sourceCode);
}
/**
* Make a full source code definition for a class by applying the specified method
* body to the Reactive template.
* @param className the name of the class
* @param methodBody the code to insert into the Reactive source class template
* @param types the parameterized input and/or output types as Strings
* @return a complete Java Class definition
*/
private String makeSourceClassDefinition(String className, String methodBody,
String[] types) {
String shortClassName = className.substring(className.lastIndexOf('.') + 1);
String s = String.format(SOURCE_CODE_TEMPLATE, shortClassName, this.resultType,
this.resultType, StringUtils.arrayToCommaDelimitedString(types),
methodBody);
logger.info("\n" + s);
return s;
}
enum ResultType {
Consumer, Function, Supplier
}
}

View File

@@ -1,27 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler;
/**
* @param <T> result type
* @author Mark Fisher
*/
public interface CompilationResultFactory<T> {
T getResult();
}

View File

@@ -1,110 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler;
import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import org.springframework.cloud.function.compiler.java.CompilationResult;
import org.springframework.util.ReflectionUtils;
/**
* @param <T> result type
* @author Mark Fisher
*/
public class CompiledFunctionFactory<T> implements CompilationResultFactory<T> {
private final T result;
private final byte[] generatedClassBytes;
private String inputType;
private String outputType;
private Method method;
public CompiledFunctionFactory(String className,
CompilationResult compilationResult) {
List<Class<?>> clazzes = compilationResult.getCompiledClasses();
T result = null;
Method method = null;
for (Class<?> clazz : clazzes) {
if (clazz.getName().equals(className)) {
try {
@SuppressWarnings("unchecked")
CompilationResultFactory<T> factory = (CompilationResultFactory<T>) clazz
.newInstance();
result = factory.getResult();
method = findFactoryMethod(clazz);
}
catch (Exception e) {
throw new IllegalArgumentException(
"Unexpected problem during retrieval of Function from compiled class",
e);
}
}
}
if (result == null) {
throw new IllegalArgumentException("Failed to extract compilation result.");
}
this.result = result;
this.method = method;
this.generatedClassBytes = compilationResult.getClassBytes(className);
}
private Method findFactoryMethod(Class<?> clazz) {
AtomicReference<Method> method = new AtomicReference<>();
ReflectionUtils.doWithLocalMethods(clazz, m -> {
if (m.getName().equals("getResult")
&& m.getReturnType().getName().startsWith("java.util.function")) {
method.set(m);
}
});
return method.get();
}
public T getResult() {
return this.result;
}
public Method getFactoryMethod() {
return this.method;
}
public String getInputType() {
return this.inputType;
}
public void setInputType(String inputType) {
this.inputType = inputType;
}
public String getOutputType() {
return this.outputType;
}
public void setOutputType(String outputType) {
this.outputType = outputType;
}
public byte[] getGeneratedClassBytes() {
return this.generatedClassBytes;
}
}

View File

@@ -1,45 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler;
import java.util.function.Consumer;
/**
* @param <T> result type
* @author Mark Fisher
*/
public class ConsumerCompiler<T> extends AbstractFunctionCompiler<Consumer<T>> {
private final String inputType;
public ConsumerCompiler() {
this("Flux<Object>");
}
public ConsumerCompiler(String inputType) {
super(ResultType.Consumer, inputType);
this.inputType = inputType;
}
@Override
protected CompiledFunctionFactory<Consumer<T>> postProcessCompiledFunctionFactory(
CompiledFunctionFactory<Consumer<T>> factory) {
factory.setInputType(this.inputType);
return factory;
}
}

View File

@@ -1,55 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler;
import java.util.function.Function;
/**
* @param <T> function input type
* @param <R> function output type
* @author Mark Fisher
*/
public class FunctionCompiler<T, R> extends AbstractFunctionCompiler<Function<T, R>> {
private final String inputType;
private final String outputType;
public FunctionCompiler() {
this("Flux<Object>");
}
public FunctionCompiler(String type) {
this(type, type);
}
public FunctionCompiler(String inputType, String outputType) {
super(ResultType.Function, inputType, outputType);
this.inputType = inputType;
this.outputType = outputType;
}
@Override
protected CompiledFunctionFactory<Function<T, R>> postProcessCompiledFunctionFactory(
CompiledFunctionFactory<Function<T, R>> factory) {
factory.setInputType(this.inputType);
factory.setOutputType(this.outputType);
return factory;
}
}

View File

@@ -1,45 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler;
import java.util.function.Supplier;
/**
* @param <T> input type
* @author Mark Fisher
*/
public class SupplierCompiler<T> extends AbstractFunctionCompiler<Supplier<T>> {
private final String outputType;
public SupplierCompiler() {
this("Flux<Object>");
}
public SupplierCompiler(String outputType) {
super(ResultType.Supplier, outputType);
this.outputType = outputType;
}
@Override
protected CompiledFunctionFactory<Supplier<T>> postProcessCompiledFunctionFactory(
CompiledFunctionFactory<Supplier<T>> factory) {
factory.setOutputType(this.outputType);
return factory;
}
}

View File

@@ -1,112 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.app;
import java.io.File;
import java.io.IOException;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import reactor.core.publisher.Flux;
import org.springframework.cloud.function.compiler.AbstractFunctionCompiler;
import org.springframework.cloud.function.compiler.CompiledFunctionFactory;
import org.springframework.cloud.function.compiler.ConsumerCompiler;
import org.springframework.cloud.function.compiler.FunctionCompiler;
import org.springframework.cloud.function.compiler.SupplierCompiler;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
/**
* @author Mark Fisher
* @author Oleg Zhurakousky
*/
public class CompiledFunctionRegistry {
private static final String SUPPLIER_DIRECTORY = "suppliers";
private static final String FUNCTION_DIRECTORY = "functions";
private static final String CONSUMER_DIRECTORY = "consumers";
private final File supplierDirectory;
private final File functionDirectory;
private final File consumerDirectory;
private final AbstractFunctionCompiler<Supplier<Flux<?>>> supplierCompiler = new SupplierCompiler<>();
private final AbstractFunctionCompiler<Function<Flux<?>, Flux<?>>> functionCompiler = new FunctionCompiler<>();
private final AbstractFunctionCompiler<Consumer<Flux<?>>> consumerCompiler = new ConsumerCompiler<>();
public CompiledFunctionRegistry() {
this(new File("/tmp/function-registry"));
}
public CompiledFunctionRegistry(File directory) {
Assert.notNull(directory, "Directory must not be null");
if (!directory.exists()) {
directory.mkdirs();
}
else {
Assert.isTrue(directory.isDirectory(),
String.format("%s is not a directory.", directory.getAbsolutePath()));
}
this.supplierDirectory = new File(directory, SUPPLIER_DIRECTORY);
this.functionDirectory = new File(directory, FUNCTION_DIRECTORY);
this.consumerDirectory = new File(directory, CONSUMER_DIRECTORY);
this.supplierDirectory.mkdir();
this.functionDirectory.mkdir();
this.consumerDirectory.mkdir();
}
public void registerSupplier(String name, String lambda, String type) {
this.doRegister(this.supplierCompiler, this.supplierDirectory, name, lambda,
type);
}
public void registerFunction(String name, String lambda, String... types) {
this.doRegister(this.functionCompiler, this.functionDirectory, name, lambda,
types);
}
public void registerConsumer(String name, String lambda, String type) {
this.doRegister(this.consumerCompiler, this.consumerDirectory, name, lambda,
type);
}
private void doRegister(AbstractFunctionCompiler<?> compiler, File directory,
String name, String lambda, String... types) {
CompiledFunctionFactory<?> factory = compiler.compile(name, lambda, types);
File file = new File(directory, fileName(name));
try {
FileCopyUtils.copy(factory.getGeneratedClassBytes(), file);
}
catch (IOException e) {
throw new IllegalArgumentException(
String.format("failed to register '%s'", name), e);
}
}
private String fileName(String functionName) {
return String.format("%s.%s", functionName, "fun");
}
}

View File

@@ -1,31 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// @checkstyle:off
@SpringBootApplication
public class CompilerApplication {
public static void main(String[] args) {
SpringApplication.run(CompilerApplication.class, args);
}
}
// @checkstyle:on

View File

@@ -1,52 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.app;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Mark Fisher
*/
@RestController
public class CompilerController {
private final CompiledFunctionRegistry registry = new CompiledFunctionRegistry();
@PostMapping(path = "/supplier/{name}")
public void registerSupplier(@PathVariable String name, @RequestBody String lambda,
@RequestParam(defaultValue = "Flux<String>") String type) {
this.registry.registerSupplier(name, lambda, type);
}
@PostMapping(path = "/function/{name}")
public void registerFunction(@PathVariable String name, @RequestBody String lambda,
@RequestParam(defaultValue = "Flux<String>") String inputType,
@RequestParam(defaultValue = "Flux<String>") String outputType) {
this.registry.registerFunction(name, lambda, inputType, outputType);
}
@PostMapping(path = "/consumer/{name}")
public void registerConsumer(@PathVariable String name, @RequestBody String lambda,
@RequestParam(defaultValue = "Flux<String>") String type) {
this.registry.registerConsumer(name, lambda, type);
}
}

View File

@@ -1,183 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.config;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.cloud.function.compiler.ConsumerCompiler;
import org.springframework.cloud.function.compiler.FunctionCompiler;
import org.springframework.cloud.function.compiler.SupplierCompiler;
import org.springframework.cloud.function.compiler.proxy.ByteCodeLoadingConsumer;
import org.springframework.cloud.function.compiler.proxy.ByteCodeLoadingFunction;
import org.springframework.cloud.function.compiler.proxy.ByteCodeLoadingSupplier;
import org.springframework.cloud.function.compiler.proxy.LambdaCompilingConsumer;
import org.springframework.cloud.function.compiler.proxy.LambdaCompilingFunction;
import org.springframework.cloud.function.compiler.proxy.LambdaCompilingSupplier;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
/**
* @author Mark Fisher
*/
@ConfigurationProperties("spring.cloud.function")
public class FunctionProxyApplicationListener
implements ApplicationListener<ApplicationPreparedEvent> {
private final SupplierCompiler<?> supplierCompiler = new SupplierCompiler<>();
private final FunctionCompiler<?, ?> functionCompiler = new FunctionCompiler<>();
private final ConsumerCompiler<?> consumerCompiler = new ConsumerCompiler<>();
/**
* Configuration for function bodies, which will be compiled. The key in the map is
* the function name and the value is a map containing a key "lambda" which is the
* body to compile, and optionally a "type" (defaults to "function"). Can also contain
* "inputType" and "outputType" in case it is ambiguous.
*/
private final Map<String, Object> compile = new HashMap<>();
/**
* Configuration for a set of files containing function bodies, which will be imported
* and compiled. The key in the map is the function name and the value is another map,
* containing a "location" of the file to compile and (optionally) a "type" (defaults
* to "function").
*/
private final Map<String, Object> imports = new HashMap<>();
public Map<String, Object> getCompile() {
return this.compile;
}
public Map<String, Object> getImports() {
return this.imports;
}
@Override
public void onApplicationEvent(ApplicationPreparedEvent event) {
ConfigurableApplicationContext context = event.getApplicationContext();
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context
.getBeanFactory();
bind(context, beanFactory);
for (Map.Entry<String, Object> entry : this.compile.entrySet()) {
String name = entry.getKey();
@SuppressWarnings("unchecked")
Map<String, String> properties = (Map<String, String>) entry.getValue();
String type = (properties.get("type") != null) ? properties.get("type")
: "function";
String lambda = properties.get("lambda");
Assert.notNull(lambda, () -> String.format(
"The 'lambda' property is required for compiling Function: %s",
name));
String inputType = properties.get("inputType");
String outputType = properties.get("outputType");
registerLambdaCompilingProxy(name, type, inputType, outputType, lambda,
beanFactory);
}
for (Map.Entry<String, Object> entry : this.imports.entrySet()) {
String name = entry.getKey();
@SuppressWarnings("unchecked")
Map<String, String> properties = (Map<String, String>) entry.getValue();
String type = (properties.get("type") != null) ? properties.get("type")
: "function";
String location = properties.get("location");
Assert.notNull(location, String.format(
"The 'location' property is required for importing Function: %s",
name));
registerByteCodeLoadingProxy(name, type, context.getResource(location),
beanFactory);
}
}
private void bind(ConfigurableApplicationContext application,
DefaultListableBeanFactory context) {
Binder.get(application.getEnvironment()).bind("spring.cloud.function",
Bindable.ofInstance(this));
}
private void registerByteCodeLoadingProxy(String name, String type, Resource resource,
DefaultListableBeanFactory beanFactory) {
Class<?> proxyClass = null;
if ("supplier".equals(type.toLowerCase())) {
proxyClass = ByteCodeLoadingSupplier.class;
}
else if ("consumer".equals(type.toLowerCase())) {
proxyClass = ByteCodeLoadingConsumer.class;
}
else {
proxyClass = ByteCodeLoadingFunction.class;
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(proxyClass);
ConstructorArgumentValues args = new ConstructorArgumentValues();
args.addGenericArgumentValue(resource);
beanDefinition.setConstructorArgumentValues(args);
beanFactory.registerBeanDefinition(name, beanDefinition);
}
private void registerLambdaCompilingProxy(String name, String type, String inputType,
String outputType, String lambda, DefaultListableBeanFactory beanFactory) {
Resource resource = new ByteArrayResource(lambda.getBytes());
ConstructorArgumentValues args = new ConstructorArgumentValues();
MutablePropertyValues props = new MutablePropertyValues();
args.addGenericArgumentValue(resource);
Class<?> proxyClass = null;
if ("supplier".equals(type.toLowerCase())) {
proxyClass = LambdaCompilingSupplier.class;
args.addGenericArgumentValue(this.supplierCompiler);
if (outputType != null) {
props.add("typeParameterizations", outputType);
}
}
else if ("consumer".equals(type.toLowerCase())) {
proxyClass = LambdaCompilingConsumer.class;
args.addGenericArgumentValue(this.consumerCompiler);
if (inputType != null) {
props.add("typeParameterizations", inputType);
}
}
else {
proxyClass = LambdaCompilingFunction.class;
args.addGenericArgumentValue(this.functionCompiler);
if ((inputType == null && outputType != null)
|| (outputType == null && inputType != null)) {
throw new IllegalArgumentException(
"if either input or output type is set, the other is also required");
}
if (inputType != null) {
props.add("typeParameterizations",
new String[] { inputType, outputType });
}
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(proxyClass);
beanDefinition.setConstructorArgumentValues(args);
beanDefinition.setPropertyValues(props);
beanFactory.registerBeanDefinition(name, beanDefinition);
}
}

View File

@@ -1,124 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.java;
import java.io.File;
import javax.tools.JavaFileObject;
import org.springframework.cloud.function.compiler.java.MemoryBasedJavaFileManager.CompilationInfoCache;
/**
* Common superclass for iterables that need to handle closing when finished with and that
* need to handle possible constraints on the values that are iterated over.
*
* @author Andy Clement
*/
public abstract class CloseableFilterableJavaFileObjectIterable
implements Iterable<JavaFileObject> {
// private final static Logger logger =
// LoggerFactory.getLogger(CloseableFilterableJavaFileObjectIterable.class);
private final static boolean BOOT_PACKAGING_AWARE = true;
private final static String BOOT_PACKAGING_PREFIX_FOR_CLASSES = "BOOT-INF/classes/";
// If set specifies the package the iterator consumer is interested in. Only
// return results in this package. Will have a trailing separator to speed
// matching. '/' on its own represents the default package
protected String packageNameFilter;
// Indicates whether the consumer of the iterator wants to see classes
// that are in subpackages of those matching the filter.
protected boolean includeSubpackages;
protected CompilationInfoCache compilationInfoCache;
public CloseableFilterableJavaFileObjectIterable(
CompilationInfoCache compilationInfoCache, String packageNameFilter,
boolean includeSubpackages) {
if (packageNameFilter != null && packageNameFilter.contains(File.separator)) {
throw new IllegalArgumentException(
"Package name filters should use dots to separate components: "
+ packageNameFilter);
}
this.compilationInfoCache = compilationInfoCache;
// Normalize filter to forward slashes
this.packageNameFilter = packageNameFilter == null ? null
: packageNameFilter.replace('.', '/') + '/';
this.includeSubpackages = includeSubpackages;
}
/**
* Used by subclasses to check values against any specified constraints.
* @param name the name to check against the criteria
* @return true if the name is a valid iterator result based on the specified criteria
*/
protected boolean accept(String name) {
// logger.debug("checking {} against constraints packageNameFilter={}
// includeSubpackages={}",name,packageNameFilter,includeSubpackages);
if (!name.endsWith(".class")) {
return false;
}
if (this.packageNameFilter == null) {
return true;
}
boolean accept;
// Normalize to forward slashes (some jars are producing paths with forward
// slashes, some with backward slashes)
name = name.replace('\\', '/');
if (this.packageNameFilter.length() == 1 && this.packageNameFilter.equals("/")) {
// This is the 'default package' filter representation
if (name.indexOf('/') == -1) {
accept = true;
}
else if (BOOT_PACKAGING_AWARE) {
accept = name.startsWith(BOOT_PACKAGING_PREFIX_FOR_CLASSES) && name
.indexOf('/', BOOT_PACKAGING_PREFIX_FOR_CLASSES.length()) == -1;
}
return accept;
}
if (this.includeSubpackages) {
accept = name.startsWith(this.packageNameFilter);
if (!accept && BOOT_PACKAGING_AWARE) {
accept = name.startsWith(BOOT_PACKAGING_PREFIX_FOR_CLASSES)
&& name.indexOf(
this.packageNameFilter) == BOOT_PACKAGING_PREFIX_FOR_CLASSES
.length();
}
}
else {
accept = name.startsWith(this.packageNameFilter)
&& name.indexOf("/", this.packageNameFilter.length()) == -1;
if (!accept && BOOT_PACKAGING_AWARE) {
accept = name.startsWith(BOOT_PACKAGING_PREFIX_FOR_CLASSES)
&& name.indexOf(
this.packageNameFilter) == BOOT_PACKAGING_PREFIX_FOR_CLASSES
.length()
&& name.indexOf("/", BOOT_PACKAGING_PREFIX_FOR_CLASSES.length()
+ this.packageNameFilter.length()) == -1;
}
}
return accept;
}
abstract void close();
abstract void reset();
}

View File

@@ -1,42 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.java;
import java.util.List;
/**
* @author Mark Fisher
*/
@SuppressWarnings("serial")
public class CompilationFailedException extends RuntimeException {
public CompilationFailedException(List<CompilationMessage> messages) {
super(consolidateMessages(messages));
}
private static String consolidateMessages(List<CompilationMessage> messages) {
if (messages == null || messages.isEmpty()) {
return "";
}
StringBuilder sb = new StringBuilder();
for (CompilationMessage message : messages) {
sb.append(message.toString());
}
return sb.toString();
}
}

View File

@@ -1,137 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.java;
/**
* Encapsulate information produced during compilation. A message may be an error or
* something less serious (warning/informational). The <tt>toString()</tt> method will
* produce a formatted error include source context indicating the precise location of the
* problem.
*
* @author Andy Clement
*/
public class CompilationMessage {
private Kind kind;
private String message;
private String sourceCode;
private int startPosition;
private int endPosition;
public CompilationMessage(Kind kind, String message, String sourceCode,
int startPosition, int endPosition) {
this.kind = kind;
this.message = message;
this.sourceCode = sourceCode;
this.startPosition = startPosition;
this.endPosition = endPosition;
}
/**
* @return the type of message
*/
public Kind getKind() {
return this.kind;
}
/**
* @return the message text
*/
public String getMessage() {
return this.message;
}
/**
* @return the source code for the file associated with the message
*/
public String getSourceCode() {
return this.sourceCode;
}
/**
* @return offset from start of source file where the error begins
*/
public int getStartPosition() {
return this.startPosition;
}
/**
* @return offset from start of source file where the error ends
*/
public int getEndPosition() {
return this.endPosition;
}
public String toString() {
StringBuilder s = new StringBuilder();
s.append("==========\n");
if (this.sourceCode != null) { // Cannot include source context if no source
// available
int[] lineStartEnd = getLineStartEnd(this.startPosition);
s.append(this.sourceCode.substring(lineStartEnd[0], lineStartEnd[1]))
.append("\n");
int col = lineStartEnd[0];
// When inserting the whitespace, ensure tabs in the source line are respected
while ((col) < this.startPosition) {
s.append(this.sourceCode.charAt(col++) == '\t' ? "\t" : " ");
}
// Want at least one ^
s.append("^");
col++;
while ((col++) < this.endPosition) {
s.append("^");
}
s.append("\n");
}
s.append(this.kind).append(":").append(this.message).append("\n");
s.append("==========\n");
return s.toString();
}
/**
* For a given position in the source code this method returns a pair of int that
* indicate the start and end of the line within the source code that contain the
* position.
* @param searchPos the position of interest in the source code
* @return an int array of length 2 containing the start and end positions of the line
*/
private int[] getLineStartEnd(int searchPos) {
int previousPos = -1;
int pos = 0;
do {
pos = this.sourceCode.indexOf('\n', previousPos + 1);
if (searchPos < pos) {
return new int[] { previousPos + 1, pos };
}
previousPos = pos;
}
while (pos != -1);
return new int[] { previousPos + 1, this.sourceCode.length() };
}
enum Kind {
ERROR, OTHER
}
// TODO test coverage for first line/last line situations
}

View File

@@ -1,72 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.java;
import java.util.ArrayList;
import java.util.List;
import javax.tools.FileObject;
import javax.tools.JavaFileManager.Location;
import javax.tools.JavaFileObject.Kind;
/**
* During compilation instances of this class will collect up the output files from the
* compilation process. Any kind of file is collected upon but access is only currently
* provided to retrieve classes produced during compilation. Annotation processors that
* run may create other kinds of artifact.
*
* @author Andy Clement
*/
public class CompilationOutputCollector {
private List<InMemoryJavaFileObject> outputFiles = new ArrayList<>();
/**
* Retrieve compiled classes that have been collected since this collector was built.
* Due to annotation processing it is possible other source files or metadata files
* may be produced during compilation - those are not included in the returned list.
* @return list of compiled classes
*/
public List<CompiledClassDefinition> getCompiledClasses() {
List<CompiledClassDefinition> compiledClassDefinitions = new ArrayList<>();
for (InMemoryJavaFileObject outputFile : this.outputFiles) {
if (outputFile.getKind() == Kind.CLASS) {
CompiledClassDefinition compiledClassDefinition = new CompiledClassDefinition(
outputFile.getName(), outputFile.getBytes());
compiledClassDefinitions.add(compiledClassDefinition);
}
}
return compiledClassDefinitions;
}
public InMemoryJavaFileObject getJavaFileForOutput(Location location,
String className, Kind kind, FileObject sibling) {
InMemoryJavaFileObject jfo = InMemoryJavaFileObject.getJavaFileObject(location,
className, kind, sibling);
this.outputFiles.add(jfo);
return jfo;
}
public InMemoryJavaFileObject getFileForOutput(Location location, String packageName,
String relativeName, FileObject sibling) {
InMemoryJavaFileObject ojfo = InMemoryJavaFileObject.getFileObject(location,
packageName, relativeName, sibling);
this.outputFiles.add(ojfo);
return ojfo;
}
}

View File

@@ -1,101 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.java;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Holder for the results of compilation. If compilation was successful the set of classes
* that resulted from compilation will be available. If compilation was not successful the
* error messages should provide information about why. Note that compilation may succeed
* and yet there will still be informational or warning messages collected.
*
* @author Andy Clement
* @author Mark Fisher
*/
public class CompilationResult {
List<CompilationMessage> compilationMessages = new ArrayList<>();
List<Class<?>> compiledClasses = new ArrayList<>();
private boolean successfulCompilation;
private Map<String, byte[]> classBytes = new HashMap<>();
private List<File> resolvedAdditionalDependencies = new ArrayList<>();
public CompilationResult(boolean successfulCompilation) {
this.successfulCompilation = successfulCompilation;
}
public void addClassBytes(String name, byte[] bytes) {
this.classBytes.put(name, bytes);
}
public List<File> getResolvedAdditionalDependencies() {
return this.resolvedAdditionalDependencies;
}
public void setResolvedAdditionalDependencies(
List<File> resolvedAdditionalDependencies) {
this.resolvedAdditionalDependencies = resolvedAdditionalDependencies;
}
public byte[] getClassBytes(String classname) {
return this.classBytes.get(classname);
}
public boolean wasSuccessful() {
return this.successfulCompilation;
}
public List<Class<?>> getCompiledClasses() {
return this.compiledClasses;
}
public void setCompiledClasses(List<Class<?>> compiledClasses) {
this.compiledClasses = compiledClasses;
}
public List<CompilationMessage> getCompilationMessages() {
return Collections.unmodifiableList(this.compilationMessages);
}
public void recordCompilationMessage(CompilationMessage message) {
this.compilationMessages.add(message);
}
public void recordCompilationMessages(List<CompilationMessage> messages) {
this.compilationMessages.addAll(messages);
}
public String toString() {
StringBuilder s = new StringBuilder();
s.append("Compilation result: #classes=" + this.compiledClasses.size()
+ " #messages=" + this.compilationMessages.size() + "\n");
s.append("Compiled classes:\n").append(this.compiledClasses).append("\n");
s.append("Compilation messages:\n").append(this.compilationMessages).append("\n");
return s.toString();
}
}

View File

@@ -1,63 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.java;
/**
* Encapsulates a name with the bytes for its class definition.
*
* @author Andy Clement
*/
public class CompiledClassDefinition {
private byte[] bytes;
private String filename;
private String classname;
public CompiledClassDefinition(String filename, byte[] bytes) {
this.filename = filename;
this.bytes = bytes;
this.classname = filename;
if (this.classname.startsWith("/")) {
this.classname = this.classname.substring(1);
}
this.classname = this.classname.replace('/', '.').substring(0,
this.classname.length() - 6); // strip
// off
// .class
}
public String getName() {
return this.filename;
}
public byte[] getBytes() {
return this.bytes;
}
@Override
public String toString() {
return "CompiledClassDefinition(name=" + getName() + ",#bytes="
+ getBytes().length + ")";
}
public String getClassName() {
return this.classname;
}
}

View File

@@ -1,50 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.java;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.aether.repository.Proxy;
import org.eclipse.aether.repository.ProxySelector;
import org.eclipse.aether.repository.RemoteRepository;
/**
* Composite {@link ProxySelector}.
*
* @author Dave Syer
*/
public class CompositeProxySelector implements ProxySelector {
private List<ProxySelector> selectors = new ArrayList<ProxySelector>();
public CompositeProxySelector(List<ProxySelector> selectors) {
this.selectors = selectors;
}
@Override
public Proxy getProxy(RemoteRepository repository) {
for (ProxySelector selector : this.selectors) {
Proxy proxy = selector.getProxy(repository);
if (proxy != null) {
return proxy;
}
}
return null;
}
}

View File

@@ -1,485 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.java;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import javax.inject.Singleton;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.name.Named;
import com.google.inject.name.Names;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
import org.apache.maven.artifact.repository.MavenArtifactRepository;
import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.DefaultModelReader;
import org.apache.maven.model.io.ModelReader;
import org.apache.maven.model.locator.DefaultModelLocator;
import org.apache.maven.model.locator.ModelLocator;
import org.apache.maven.model.validation.DefaultModelValidator;
import org.apache.maven.model.validation.ModelValidator;
import org.apache.maven.project.DefaultProjectBuildingRequest;
import org.apache.maven.project.DependencyResolutionResult;
import org.apache.maven.project.ProjectBuilder;
import org.apache.maven.project.ProjectBuildingException;
import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.project.ProjectBuildingRequest.RepositoryMerging;
import org.apache.maven.project.ProjectBuildingResult;
import org.apache.maven.repository.internal.DefaultArtifactDescriptorReader;
import org.apache.maven.repository.internal.DefaultVersionRangeResolver;
import org.apache.maven.repository.internal.DefaultVersionResolver;
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
import org.apache.maven.repository.internal.SnapshotMetadataGeneratorFactory;
import org.apache.maven.repository.internal.VersionsMetadataGeneratorFactory;
import org.apache.maven.settings.Profile;
import org.apache.maven.settings.Repository;
import org.codehaus.plexus.ContainerConfiguration;
import org.codehaus.plexus.DefaultContainerConfiguration;
import org.codehaus.plexus.DefaultPlexusContainer;
import org.codehaus.plexus.MutablePlexusContainer;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.classworlds.ClassWorld;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.impl.ArtifactDescriptorReader;
import org.eclipse.aether.impl.MetadataGeneratorFactory;
import org.eclipse.aether.impl.VersionRangeResolver;
import org.eclipse.aether.impl.VersionResolver;
import org.eclipse.aether.impl.guice.AetherModule;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.NoLocalRepositoryManagerException;
import org.eclipse.aether.repository.ProxySelector;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.repository.RepositoryPolicy;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
import org.eclipse.aether.spi.connector.transport.TransporterFactory;
import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory;
import org.eclipse.aether.transport.file.FileTransporterFactory;
import org.eclipse.aether.transport.http.HttpTransporterFactory;
import org.eclipse.aether.util.repository.JreProxySelector;
import org.eclipse.sisu.inject.DefaultBeanLocator;
import org.eclipse.sisu.plexus.ClassRealmManager;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;
/**
* Dependency resolver utility class.
*
* @author Andy Clement
*/
public final class DependencyResolver {
private static DependencyResolver instance = new DependencyResolver();
private static Properties globals;
private final Object lock = new Object();
private LocalRepositoryManagerFactory localRepositoryManagerFactory;
private PlexusContainer container;
private ProjectBuilder projectBuilder;
private RepositorySystem repositorySystem;
private MavenSettings settings;
private DependencyResolver() {
}
public static DependencyResolver instance() {
return instance;
}
public static void close() {
instance = new DependencyResolver();
}
static Properties getGlobals() {
return globals;
}
private void initialize() {
if (this.container == null) {
synchronized (this.lock) {
if (this.container == null) {
ClassWorld classWorld = new ClassWorld("plexus.core",
Thread.currentThread().getContextClassLoader());
ContainerConfiguration config = new DefaultContainerConfiguration()
.setClassWorld(classWorld)
.setRealm(classWorld.getClassRealm("plexus.core"))
.setClassPathScanning(PlexusConstants.SCANNING_INDEX)
.setAutoWiring(true).setName("maven");
PlexusContainer container;
try {
container = new DefaultPlexusContainer(config, new AetherModule(),
new DependencyResolutionModule());
this.localRepositoryManagerFactory = container
.lookup(LocalRepositoryManagerFactory.class);
container.addComponent(
new ClassRealmManager((MutablePlexusContainer) container,
new DefaultBeanLocator()),
ClassRealmManager.class.getName());
this.projectBuilder = container.lookup(ProjectBuilder.class);
this.repositorySystem = container.lookup(RepositorySystem.class);
}
catch (Exception e) {
throw new IllegalStateException("Cannot create container", e);
}
this.container = container;
this.settings = new MavenSettingsReader().readSettings();
}
}
}
}
public List<Dependency> dependencies(Resource resource) {
return dependencies(resource, new Properties());
}
public List<Dependency> dependencies(final Resource resource,
final Properties properties) {
initialize();
try {
ProjectBuildingRequest request = getProjectBuildingRequest(properties);
request.setResolveDependencies(true);
synchronized (DependencyResolver.class) {
ProjectBuildingResult result = this.projectBuilder
.build(new PropertiesModelSource(properties, resource), request);
DependencyResolver.globals = null;
DependencyResolutionResult dependencies = result
.getDependencyResolutionResult();
if (!dependencies.getUnresolvedDependencies().isEmpty()) {
StringBuilder builder = new StringBuilder();
for (Dependency dependency : dependencies
.getUnresolvedDependencies()) {
List<Exception> errors = dependencies
.getResolutionErrors(dependency);
for (Exception exception : errors) {
if (builder.length() > 0) {
builder.append("\n");
}
builder.append(exception.getMessage());
}
}
throw new RuntimeException(builder.toString());
}
return runtime(dependencies.getDependencies());
}
}
catch (ProjectBuildingException | NoLocalRepositoryManagerException e) {
throw new IllegalStateException("Cannot build model", e);
}
}
public File resolve(Dependency dependency) {
initialize();
return collectNonTransitive(Arrays.asList(dependency)).iterator().next()
.getArtifact().getFile();
}
private List<Dependency> runtime(List<Dependency> dependencies) {
List<Dependency> list = new ArrayList<>();
for (Dependency dependency : dependencies) {
if (!"test".equals(dependency.getScope())
&& !"provided".equals(dependency.getScope())) {
list.add(dependency);
}
}
return list;
}
private ProjectBuildingRequest getProjectBuildingRequest(Properties properties)
throws NoLocalRepositoryManagerException {
DefaultProjectBuildingRequest projectBuildingRequest = new DefaultProjectBuildingRequest();
DefaultRepositorySystemSession session = createSession(properties);
projectBuildingRequest.setRepositoryMerging(RepositoryMerging.REQUEST_DOMINANT);
projectBuildingRequest.setRemoteRepositories(mavenRepositories(properties));
projectBuildingRequest.getRemoteRepositories()
.addAll(mavenRepositories(this.settings));
projectBuildingRequest.setRepositorySession(session);
projectBuildingRequest.setProcessPlugins(false);
projectBuildingRequest.setBuildStartTime(new Date());
projectBuildingRequest.setUserProperties(properties);
projectBuildingRequest.setSystemProperties(System.getProperties());
return projectBuildingRequest;
}
private Collection<? extends ArtifactRepository> mavenRepositories(
MavenSettings settings) {
List<ArtifactRepository> list = new ArrayList<>();
for (Profile profile : settings.getActiveProfiles()) {
for (Repository repository : profile.getRepositories()) {
addRepositoryIfMissing(list, repository.getId(), repository.getUrl(),
repository.getReleases() != null
? repository.getReleases().isEnabled() : true,
repository.getSnapshots() != null
? repository.getSnapshots().isEnabled() : false);
}
}
return list;
}
private List<ArtifactRepository> mavenRepositories(Properties properties) {
List<ArtifactRepository> list = new ArrayList<>();
addRepositoryIfMissing(list, "spring-snapshots",
"https://repo.spring.io/libs-snapshot", true, true);
addRepositoryIfMissing(list, "central", "https://repo1.maven.org/maven2", true,
false);
return list;
}
private List<RemoteRepository> aetherRepositories(Properties properties) {
List<RemoteRepository> list = new ArrayList<>();
for (ArtifactRepository input : mavenRepositories(properties)) {
list.add(remote(input));
}
return list;
}
private RemoteRepository remote(ArtifactRepository input) {
return new RemoteRepository.Builder(input.getId(), input.getLayout().getId(),
input.getUrl()).setSnapshotPolicy(policy(input.getSnapshots()))
.setReleasePolicy(policy(input.getReleases())).build();
}
private RepositoryPolicy policy(ArtifactRepositoryPolicy input) {
RepositoryPolicy policy = new RepositoryPolicy(input.isEnabled(),
RepositoryPolicy.UPDATE_POLICY_DAILY,
RepositoryPolicy.CHECKSUM_POLICY_WARN);
return policy;
}
private void addRepositoryIfMissing(List<ArtifactRepository> list, String id,
String url, boolean releases, boolean snapshots) {
for (ArtifactRepository repo : list) {
if (url.equals(repo.getUrl())) {
return;
}
if (id.equals(repo.getId())) {
return;
}
}
list.add(repo(id, url, releases, snapshots));
}
private ArtifactRepository repo(String id, String url, boolean releases,
boolean snapshots) {
MavenArtifactRepository repository = new MavenArtifactRepository();
repository.setLayout(new DefaultRepositoryLayout());
repository.setId(id);
repository.setUrl(url);
ArtifactRepositoryPolicy enabled = new ArtifactRepositoryPolicy();
enabled.setEnabled(true);
ArtifactRepositoryPolicy disabled = new ArtifactRepositoryPolicy();
disabled.setEnabled(false);
repository.setReleaseUpdatePolicy(releases ? enabled : disabled);
repository.setSnapshotUpdatePolicy(snapshots ? enabled : disabled);
return repository;
}
private DefaultRepositorySystemSession createSession(Properties properties)
throws NoLocalRepositoryManagerException {
DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
LocalRepository repository = localRepository(properties);
session.setLocalRepositoryManager(
this.localRepositoryManagerFactory.newInstance(session, repository));
applySettings(session);
ProxySelector existing = session.getProxySelector();
if (existing == null || !(existing instanceof CompositeProxySelector)) {
JreProxySelector fallback = new JreProxySelector();
ProxySelector selector = existing == null ? fallback
: new CompositeProxySelector(Arrays.asList(existing, fallback));
session.setProxySelector(selector);
}
return session;
}
private void applySettings(DefaultRepositorySystemSession session) {
MavenSettingsReader.applySettings(this.settings, session);
}
private LocalRepository localRepository(Properties properties) {
return new LocalRepository(getM2RepoDirectory());
}
public Model readModel(Resource resource) {
return readModel(resource, new Properties());
}
public Model readModel(final Resource resource, final Properties properties) {
initialize();
try {
ProjectBuildingRequest request = getProjectBuildingRequest(properties);
request.setResolveDependencies(false);
ProjectBuildingResult result = this.projectBuilder
.build(new PropertiesModelSource(properties, resource), request);
return result.getProject().getModel();
}
catch (Exception e) {
throw new IllegalStateException("Failed to build model from effective pom",
e);
}
}
private File getM2RepoDirectory() {
return new File(getDefaultM2HomeDirectory(), "repository");
}
private File getDefaultM2HomeDirectory() {
String mavenRoot = System.getProperty("maven.home");
if (StringUtils.hasLength(mavenRoot)) {
return new File(mavenRoot);
}
return new File(System.getProperty("user.home"), ".m2");
}
private List<ArtifactResult> collectNonTransitive(List<Dependency> dependencies) {
try {
List<ArtifactRequest> artifactRequests = getArtifactRequests(dependencies);
List<ArtifactResult> result = this.repositorySystem
.resolveArtifacts(createSession(new Properties()), artifactRequests);
return result;
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
private List<ArtifactRequest> getArtifactRequests(List<Dependency> dependencies) {
List<ArtifactRequest> list = new ArrayList<>();
for (Dependency dependency : dependencies) {
ArtifactRequest request = new ArtifactRequest(dependency.getArtifact(), null,
null);
request.setRepositories(aetherRepositories(new Properties()));
list.add(request);
}
return list;
}
@SuppressWarnings("deprecation")
private static final class PropertiesModelSource
implements org.apache.maven.model.building.ModelSource {
private final Properties properties;
private final Resource resource;
private PropertiesModelSource(Properties properties, Resource resource) {
this.properties = properties;
this.resource = resource;
}
@Override
public InputStream getInputStream() throws IOException {
DependencyResolver.globals = this.properties;
return new BufferedInputStream(this.resource.getInputStream()) {
@Override
public void close() throws IOException {
DependencyResolver.globals = null;
super.close();
}
};
}
@Override
public String getLocation() {
return this.resource.getDescription();
}
}
}
class DependencyResolutionModule extends AbstractModule {
@Override
protected void configure() {
bind(ModelLocator.class).to(DefaultModelLocator.class).in(Singleton.class);
bind(ModelReader.class).to(DefaultModelReader.class).in(Singleton.class);
bind(ModelValidator.class).to(DefaultModelValidator.class).in(Singleton.class);
bind(RepositoryConnectorFactory.class).to(BasicRepositoryConnectorFactory.class)
.in(Singleton.class);
bind(ArtifactDescriptorReader.class) //
.to(DefaultArtifactDescriptorReader.class).in(Singleton.class);
bind(VersionResolver.class) //
.to(DefaultVersionResolver.class).in(Singleton.class);
bind(VersionRangeResolver.class) //
.to(DefaultVersionRangeResolver.class).in(Singleton.class);
bind(MetadataGeneratorFactory.class).annotatedWith(Names.named("snapshot")) //
.to(SnapshotMetadataGeneratorFactory.class).in(Singleton.class);
bind(MetadataGeneratorFactory.class).annotatedWith(Names.named("versions")) //
.to(VersionsMetadataGeneratorFactory.class).in(Singleton.class);
bind(TransporterFactory.class).annotatedWith(Names.named("http"))
.to(HttpTransporterFactory.class).in(Singleton.class);
bind(TransporterFactory.class).annotatedWith(Names.named("file"))
.to(FileTransporterFactory.class).in(Singleton.class);
}
@Provides
@Singleton
Set<MetadataGeneratorFactory> provideMetadataGeneratorFactories(
@Named("snapshot") MetadataGeneratorFactory snapshot,
@Named("versions") MetadataGeneratorFactory versions) {
Set<MetadataGeneratorFactory> factories = new HashSet<>();
factories.add(snapshot);
factories.add(versions);
return Collections.unmodifiableSet(factories);
}
@Provides
@Singleton
Set<RepositoryConnectorFactory> provideRepositoryConnectorFactories(
RepositoryConnectorFactory factory) {
return Collections.singleton(factory);
}
@Provides
@Singleton
Set<TransporterFactory> provideTransporterFactories(
@Named("file") TransporterFactory file,
@Named("http") TransporterFactory http) {
// Order is decided elsewhere (by priority)
Set<TransporterFactory> factories = new HashSet<TransporterFactory>();
factories.add(file);
factories.add(http);
return Collections.unmodifiableSet(factories);
}
}

View File

@@ -1,143 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.java;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URI;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.NestingKind;
import javax.tools.JavaFileObject;
/**
* A JavaFileObject that represents a file in a directory.
*
* @author Andy Clement
*/
public class DirEntryJavaFileObject implements JavaFileObject {
private File file;
private File basedir;
public DirEntryJavaFileObject(File basedir, File file) {
this.basedir = basedir;
this.file = file;
}
@Override
public URI toUri() {
return this.file.toURI();
}
/**
* @return the path of the file relative to the base directory, for example:
* a/b/c/D.class
*/
@Override
public String getName() {
String basedirPath = this.basedir.getPath();
String filePath = this.file.getPath();
return filePath.substring(basedirPath.length() + 1);
}
@Override
public InputStream openInputStream() throws IOException {
return new FileInputStream(this.file);
}
@Override
public OutputStream openOutputStream() throws IOException {
throw new IllegalStateException("Only expected to be used for input");
}
@Override
public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
// It is bytecode
throw new UnsupportedOperationException(
"openReader() not supported on class file: " + getName());
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
// It is bytecode
throw new UnsupportedOperationException(
"getCharContent() not supported on class file: " + getName());
}
@Override
public Writer openWriter() throws IOException {
throw new IllegalStateException("only expected to be used for input");
}
@Override
public long getLastModified() {
return this.file.lastModified();
}
@Override
public boolean delete() {
return false; // This object is for read only access to a class
}
@Override
public Kind getKind() {
return Kind.CLASS;
}
@Override
public boolean isNameCompatible(String simpleName, Kind kind) {
if (kind != Kind.CLASS) {
return false;
}
String name = getName();
int lastSlash = name.lastIndexOf('/');
return name.substring(lastSlash + 1).equals(simpleName + ".class");
}
@Override
public NestingKind getNestingKind() {
return null;
}
@Override
public Modifier getAccessLevel() {
return null;
}
@Override
public int hashCode() {
return this.file.getName().hashCode() * 37 + this.basedir.getName().hashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof DirEntryJavaFileObject)) {
return false;
}
DirEntryJavaFileObject that = (DirEntryJavaFileObject) obj;
return (this.basedir.getName().equals(that.basedir.getName()))
&& (this.file.getName().equals(that.file.getName()));
}
}

View File

@@ -1,112 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.java;
import java.io.File;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.NoSuchElementException;
/**
* Walks a directory hierarchy from some base directory discovering files.
*
* @author Andy Clement
*/
public class DirEnumeration implements Enumeration<File> {
// The starting point
private File basedir;
// Candidates collected so far
private List<File> filesToReturn;
// Places still to explore for candidates
private List<File> directoriesToExplore;
public DirEnumeration(File basedir) {
this.basedir = basedir;
}
private void computeValue() {
if (this.filesToReturn == null) { // Indicates we haven't started yet
this.filesToReturn = new ArrayList<>();
this.directoriesToExplore = new ArrayList<>();
visitDirectory(this.basedir);
}
if (this.filesToReturn.size() == 0) {
while (this.filesToReturn.size() == 0
&& this.directoriesToExplore.size() != 0) {
File nextDir = this.directoriesToExplore.get(0);
this.directoriesToExplore.remove(0);
visitDirectory(nextDir);
}
}
}
@Override
public boolean hasMoreElements() {
computeValue();
return this.filesToReturn.size() != 0;
}
@Override
public File nextElement() {
computeValue();
if (this.filesToReturn.size() == 0) {
throw new NoSuchElementException();
}
File toReturn = this.filesToReturn.get(0);
this.filesToReturn.remove(0);
return toReturn;
}
private void visitDirectory(File dir) {
File[] files = dir.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
this.directoriesToExplore.add(file);
}
else {
this.filesToReturn.add(file);
}
}
}
}
public File getDirectory() {
return this.basedir;
}
/**
* Return the relative path of this file to the base directory that the directory
* enumeration was started for.
* @param file a file discovered returned by this enumeration
* @return the relative path of the file (for example: a/b/c/D.class)
*/
public String getName(File file) {
String basedirPath = this.basedir.getPath();
String filePath = file.getPath();
if (!filePath.startsWith(basedirPath)) {
throw new IllegalStateException("The file '" + filePath
+ "' is not nested below the base directory '" + basedirPath + "'");
}
return filePath.substring(basedirPath.length() + 1);
}
}

View File

@@ -1,239 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.java;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.CharArrayWriter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.NestingKind;
import javax.tools.FileObject;
import javax.tools.JavaFileManager.Location;
import javax.tools.JavaFileObject;
import javax.tools.StandardLocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A JavaFileObject that represents a source artifact created for compilation or an output
* artifact producing during compilation (a .class file or some other thing if an
* annotation processor has run). In order to be clear what it is being used for there are
* static factory methods that ask for specific types of file.
*
* @author Andy Clement
*/
public final class InMemoryJavaFileObject implements JavaFileObject {
private final static Logger logger = LoggerFactory
.getLogger(InMemoryJavaFileObject.class);
private Location location;
private String packageName;
private String relativeName;
private FileObject sibling;
private String className;
private Kind kind;
private byte[] content = null;
private long lastModifiedTime = 0;
private URI uri = null;
private InMemoryJavaFileObject() {
}
public static InMemoryJavaFileObject getFileObject(Location location,
String packageName, String relativeName, FileObject sibling) {
InMemoryJavaFileObject retval = new InMemoryJavaFileObject();
retval.kind = Kind.OTHER;
retval.location = location;
retval.packageName = packageName;
retval.relativeName = relativeName;
retval.sibling = sibling;
return retval;
}
public static InMemoryJavaFileObject getJavaFileObject(Location location,
String className, Kind kind, FileObject sibling) {
InMemoryJavaFileObject retval = new InMemoryJavaFileObject();
retval.location = location;
retval.className = className;
retval.kind = kind;
retval.sibling = sibling;
return retval;
}
public static InMemoryJavaFileObject getSourceJavaFileObject(String className,
String content) {
InMemoryJavaFileObject retval = new InMemoryJavaFileObject();
retval.location = StandardLocation.SOURCE_PATH;
retval.className = className;
retval.kind = Kind.SOURCE;
retval.content = content.getBytes();
return retval;
}
public byte[] getBytes() {
return this.content;
}
public String toString() {
return "OutputJavaFileObject: Location=" + this.location + ",className="
+ this.className + ",kind=" + this.kind + ",relativeName="
+ this.relativeName + ",sibling=" + this.sibling + ",packageName="
+ this.packageName;
}
@Override
public URI toUri() {
// These memory based output files 'pretend' to be relative to the file system
// root
if (this.uri == null) {
String name = null;
if (this.className != null) {
name = this.className.replace('.', '/');
}
else if (this.packageName != null && this.packageName.length() != 0) {
name = this.packageName.replace('.', '/') + '/' + this.relativeName;
}
else {
name = this.relativeName;
}
String uriString = null;
try {
uriString = "file:/" + name + this.kind.extension;
this.uri = new URI(uriString);
}
catch (URISyntaxException e) {
throw new IllegalStateException(
"Unexpected URISyntaxException for string '" + uriString + "'",
e);
}
}
return this.uri;
}
@Override
public String getName() {
return toUri().getPath();
}
@Override
public InputStream openInputStream() throws IOException {
if (this.content == null) {
throw new FileNotFoundException();
}
logger.debug("opening input stream for {}", getName());
return new ByteArrayInputStream(this.content);
}
@Override
public OutputStream openOutputStream() throws IOException {
logger.debug("opening output stream for {}", getName());
return new ByteArrayOutputStream() {
@Override
public void close() throws IOException {
super.close();
InMemoryJavaFileObject.this.lastModifiedTime = System.currentTimeMillis();
InMemoryJavaFileObject.this.content = this.toByteArray();
}
};
}
@Override
public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
return new InputStreamReader(openInputStream(), Charset.defaultCharset());
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
if (this.kind != Kind.SOURCE) {
throw new UnsupportedOperationException(
"getCharContent() not supported on file object: " + getName());
}
// Not yet supporting encodings
return (this.content == null ? null : new String(this.content));
}
@Override
public Writer openWriter() throws IOException {
// Let's not enforce this restriction right now
// if (kind == Kind.CLASS) {
// throw new UnsupportedOperationException("openWriter() not supported on file
// object: " + getName());
// }
return new CharArrayWriter() {
@Override
public void close() {
InMemoryJavaFileObject.this.lastModifiedTime = System.currentTimeMillis();
InMemoryJavaFileObject.this.content = new String(toCharArray())
.getBytes(); // Ignoring encoding...
}
};
}
@Override
public long getLastModified() {
return this.lastModifiedTime;
}
@Override
public boolean delete() {
return false;
}
@Override
public Kind getKind() {
return this.kind;
}
public boolean isNameCompatible(String simpleName, Kind kind) {
String baseName = simpleName + kind.extension;
return kind.equals(getKind()) && (baseName.equals(toUri().getPath())
|| toUri().getPath().endsWith("/" + baseName));
}
@Override
public NestingKind getNestingKind() {
return null;
}
@Override
public Modifier getAccessLevel() {
return null;
}
}

View File

@@ -1,298 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.java;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import javax.tools.JavaFileObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.function.compiler.java.MemoryBasedJavaFileManager.CompilationInfoCache;
import org.springframework.cloud.function.compiler.java.MemoryBasedJavaFileManager.CompilationInfoCache.ArchiveInfo;
/**
* Iterable that will produce an iterator that returns classes found on a specified
* classpath that meet specified criteria. For jars it finds, the iterator will go into
* nested jars - this handles the situation with a spring boot uberjar.
*
* @author Andy Clement
*/
public class IterableClasspath extends CloseableFilterableJavaFileObjectIterable {
private static Logger logger = LoggerFactory.getLogger(IterableClasspath.class);
private List<File> classpathEntries = new ArrayList<>();
private List<ZipFile> openArchives = new ArrayList<>();
/**
* @param compilationInfoCache cache of info that may help accelerate compilation
* @param classpath a classpath of jars/directories
* @param packageNameFilter an optional package name if choosing to filter (e.g.
* com.example)
* @param includeSubpackages if true, include results in subpackages of the specified
* package filter
*/
IterableClasspath(CompilationInfoCache compilationInfoCache, String classpath,
String packageNameFilter, boolean includeSubpackages) {
super(compilationInfoCache, packageNameFilter, includeSubpackages);
StringTokenizer tokenizer = new StringTokenizer(classpath, File.pathSeparator);
while (tokenizer.hasMoreElements()) {
String nextEntry = tokenizer.nextToken();
File f = new File(nextEntry);
if (f.exists()) {
// Skip iterating over archives that cannot possibly match the filter
if (this.packageNameFilter != null
&& this.packageNameFilter.length() > 0) {
ArchiveInfo archiveInfo = compilationInfoCache.getArchiveInfoFor(f);
if (archiveInfo != null
&& !archiveInfo.containsPackage(this.packageNameFilter,
this.includeSubpackages)) {
continue;
}
}
this.classpathEntries.add(f);
}
else {
logger.debug("path element does not exist {}", f);
}
}
}
public void close() {
for (ZipFile openArchive : this.openArchives) {
try {
openArchive.close();
}
catch (IOException ioe) {
logger.debug("Unexpected error closing archive {}", openArchive, ioe);
}
}
this.openArchives.clear();
}
public Iterator<JavaFileObject> iterator() {
return new ClasspathEntriesIterator();
}
public void reset() {
close();
}
static class ZipEnumerator implements Enumeration<ZipEntry> {
private ZipInputStream zis;
private ZipEntry nextEntry = null;
ZipEnumerator(ZipInputStream zis) {
this.zis = zis;
}
@Override
public boolean hasMoreElements() {
try {
this.nextEntry = this.zis.getNextEntry();
}
catch (IOException ioe) {
this.nextEntry = null;
}
return this.nextEntry != null;
}
@Override
public ZipEntry nextElement() {
ZipEntry retval = this.nextEntry;
this.nextEntry = null;
return retval;
}
}
class ClasspathEntriesIterator implements Iterator<JavaFileObject> {
private int currentClasspathEntriesIndex = 0;
// Walking one of three possible things: directory tree, zip, or Java runtime
// packaged in JDK9+ form
private File openDirectory = null;
private DirEnumeration openDirectoryEnumeration = null;
private ZipFile openArchive = null;
private File openFile = null;
private ZipEntry nestedZip = null;
private Stack<Enumeration<? extends ZipEntry>> openArchiveEnumeration = null;
private File openJrt;
private JrtFsEnumeration openJrtEnumeration = null;
private JavaFileObject nextEntry = null;
private void findNext() {
if (this.nextEntry == null) {
try {
while (this.openArchive != null || this.openDirectory != null
|| this.openJrt != null
|| this.currentClasspathEntriesIndex < IterableClasspath.this.classpathEntries
.size()) {
if (this.openArchive == null && this.openDirectory == null
&& this.openJrt == null) {
// Open the next item
File nextFile = IterableClasspath.this.classpathEntries
.get(this.currentClasspathEntriesIndex);
if (nextFile.isDirectory()) {
this.openDirectory = nextFile;
this.openDirectoryEnumeration = new DirEnumeration(
nextFile);
}
else if (nextFile.getName().endsWith("jrt-fs.jar")) {
this.openJrt = nextFile;
this.openJrtEnumeration = new JrtFsEnumeration(nextFile,
null);
}
else {
this.openFile = nextFile;
this.openArchive = new ZipFile(nextFile);
IterableClasspath.this.openArchives.add(this.openArchive);
this.openArchiveEnumeration = new Stack<Enumeration<? extends ZipEntry>>();
this.openArchiveEnumeration
.push(this.openArchive.entries());
}
this.currentClasspathEntriesIndex++;
}
if (this.openArchiveEnumeration != null) {
while (!this.openArchiveEnumeration.isEmpty()) {
while (this.openArchiveEnumeration.peek()
.hasMoreElements()) {
ZipEntry entry = this.openArchiveEnumeration.peek()
.nextElement();
String entryName = entry.getName();
if (accept(entryName)) {
if (this.nestedZip != null) {
this.nextEntry = new NestedZipEntryJavaFileObject(
this.openFile, this.openArchive,
this.nestedZip, entry);
}
else {
this.nextEntry = new ZipEntryJavaFileObject(
this.openFile, this.openArchive,
entry);
}
return;
}
else if (this.nestedZip == null
&& entryName.startsWith(
MemoryBasedJavaFileManager.BOOT_PACKAGING_PREFIX_FOR_LIBRARIES)
&& entryName.endsWith(".jar")) {
// nested jar in uber jar
logger.debug("opening nested archive {}",
entry.getName());
ZipInputStream zis = new ZipInputStream(
this.openArchive.getInputStream(entry));
Enumeration<? extends ZipEntry> nestedZipEnumerator = new ZipEnumerator(
zis);
this.nestedZip = entry;
this.openArchiveEnumeration
.push(nestedZipEnumerator);
}
}
this.openArchiveEnumeration.pop();
if (this.nestedZip == null) {
this.openArchive = null;
this.openFile = null;
}
else {
this.nestedZip = null;
}
}
this.openArchiveEnumeration = null;
this.openArchive = null;
this.openFile = null;
}
else if (this.openDirectoryEnumeration != null) {
while (this.openDirectoryEnumeration.hasMoreElements()) {
File entry = this.openDirectoryEnumeration.nextElement();
String name = this.openDirectoryEnumeration
.getName(entry);
if (accept(name)) {
this.nextEntry = new DirEntryJavaFileObject(
this.openDirectoryEnumeration.getDirectory(),
entry);
return;
}
}
this.openDirectoryEnumeration = null;
this.openDirectory = null;
}
else if (this.openJrtEnumeration != null) {
while (this.openJrtEnumeration.hasMoreElements()) {
JrtEntryJavaFileObject jrtEntry = this.openJrtEnumeration
.nextElement();
String name = this.openJrtEnumeration.getName(jrtEntry);
if (accept(name)) {
this.nextEntry = jrtEntry;
return;
}
}
this.openJrtEnumeration = null;
this.openJrt = null;
}
}
}
catch (IOException ioe) {
logger.debug("Unexpected error whilst processing classpath entries",
ioe);
}
}
}
public boolean hasNext() {
findNext();
return this.nextEntry != null;
}
public JavaFileObject next() {
findNext();
if (this.nextEntry == null) {
throw new NoSuchElementException();
}
JavaFileObject retval = this.nextEntry;
this.nextEntry = null;
return retval;
}
}
}

View File

@@ -1,114 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.java;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import javax.tools.JavaFileObject;
import org.springframework.cloud.function.compiler.java.MemoryBasedJavaFileManager.CompilationInfoCache;
/**
* Iterable that will produce an iterator that returns classes found in a specific module
* tree within the Java runtime image that exists in Java 9 and later.
*
* @author Andy Clement
*/
public class IterableJrtModule extends CloseableFilterableJavaFileObjectIterable {
// private static Logger logger = LoggerFactory.getLogger(IterableJrtModule.class);
Map<String, JrtFsEnumeration> walkers = new HashMap<>();
private Path moduleRootPath;
/**
* @param compilationInfoCache cache of info that may help accelerate compilation
* @param moduleRootPath path to the base of the relevant module within the JRT image
* @param packageNameFilter an optional package name if choosing to filter (e.g.
* com.example)
* @param includeSubpackages if true, include results in subpackages of the specified
* package filter
*/
public IterableJrtModule(CompilationInfoCache compilationInfoCache,
Path moduleRootPath, String packageNameFilter, boolean includeSubpackages) {
super(compilationInfoCache, packageNameFilter, includeSubpackages);
this.moduleRootPath = moduleRootPath;
}
public Iterator<JavaFileObject> iterator() {
JrtFsEnumeration jrtFsWalker = this.walkers.get(this.moduleRootPath.toString());
if (jrtFsWalker == null) {
jrtFsWalker = new JrtFsEnumeration(null, this.moduleRootPath);
this.walkers.put(this.moduleRootPath.toString(), jrtFsWalker);
}
jrtFsWalker.reset();
return new IteratorOverJrtFsEnumeration(jrtFsWalker);
}
public void close() {
}
public void reset() {
close();
}
class IteratorOverJrtFsEnumeration implements Iterator<JavaFileObject> {
private JavaFileObject nextEntry = null;
private JrtFsEnumeration jrtEnumeration;
IteratorOverJrtFsEnumeration(JrtFsEnumeration jrtFsWalker) {
this.jrtEnumeration = jrtFsWalker;
}
private void findNext() {
if (this.nextEntry == null) {
while (this.jrtEnumeration.hasMoreElements()) {
JrtEntryJavaFileObject jrtEntry = this.jrtEnumeration.nextElement();
String name = this.jrtEnumeration.getName(jrtEntry);
if (accept(name)) {
this.nextEntry = jrtEntry;
return;
}
}
}
}
public boolean hasNext() {
findNext();
return this.nextEntry != null;
}
public JavaFileObject next() {
findNext();
if (this.nextEntry == null) {
throw new NoSuchElementException();
}
JavaFileObject retval = this.nextEntry;
this.nextEntry = null;
return retval;
}
}
}

View File

@@ -1,159 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.java;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.NestingKind;
import javax.tools.JavaFileObject;
/**
* A JavaFileObject that represents a class from the Java runtime as packaged in Java 9
* and later.
*
* @author Andy Clement
*/
public class JrtEntryJavaFileObject implements JavaFileObject {
private String pathToClassString;
private Path path;
/**
* @param path entry in the Java runtime filesystem, for example
* '/modules/java.base/java/lang/Object.class'
*/
public JrtEntryJavaFileObject(Path path) {
this.pathToClassString = path.subpath(2, path.getNameCount()).toString(); // e.g.
// java/lang/Object.class
this.path = path;
}
@Override
public URI toUri() {
return this.path.toUri();
}
/**
* @return the path of the file relative to the base directory, for example:
* a/b/c/D.class
*/
@Override
public String getName() {
return this.pathToClassString;
}
@Override
public InputStream openInputStream() throws IOException {
byte[] bytes = Files.readAllBytes(this.path);
return new ByteArrayInputStream(bytes);
}
@Override
public OutputStream openOutputStream() throws IOException {
throw new IllegalStateException("Only expected to be used for input");
}
@Override
public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
// It is bytecode
throw new UnsupportedOperationException(
"openReader() not supported on class file: " + getName());
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
// It is bytecode
throw new UnsupportedOperationException(
"getCharContent() not supported on class file: " + getName());
}
@Override
public Writer openWriter() throws IOException {
throw new IllegalStateException("only expected to be used for input");
}
@Override
public long getLastModified() {
try {
return Files.getLastModifiedTime(this.path).toMillis();
}
catch (IOException ioe) {
throw new RuntimeException(
"Unable to determine last modified time of " + this.pathToClassString,
ioe);
}
}
@Override
public boolean delete() {
return false; // This object is for read only access to a class
}
@Override
public Kind getKind() {
return Kind.CLASS;
}
@Override
public boolean isNameCompatible(String simpleName, Kind kind) {
if (kind != Kind.CLASS) {
return false;
}
String name = getName();
int lastSlash = name.lastIndexOf('/');
return name.substring(lastSlash + 1).equals(simpleName + ".class");
}
@Override
public NestingKind getNestingKind() {
return null;
}
@Override
public Modifier getAccessLevel() {
return null;
}
@Override
public int hashCode() {
return getName().hashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof JrtEntryJavaFileObject)) {
return false;
}
JrtEntryJavaFileObject that = (JrtEntryJavaFileObject) obj;
return (getName().equals(that.getName()));
}
public String getPathToClassString() {
return this.pathToClassString;
}
}

View File

@@ -1,140 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.java;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.NoSuchElementException;
/**
* Walks a JrtFS treating it like a directory (to avoid overcomplicating the walking logic
* in IterableClasspath).
*
* @author Andy Clement
*/
public class JrtFsEnumeration implements Enumeration<JrtEntryJavaFileObject> {
// private final static Logger logger =
// LoggerFactory.getLogger(JrtFsEnumeration.class);
private static URI JRT_URI = URI.create("jrt:/"); //$NON-NLS-1$
private final static FileSystem fs = FileSystems.getFileSystem(JRT_URI);
private Path pathWithinJrt;
private List<JrtEntryJavaFileObject> jfos = new ArrayList<>();
private Integer counter = 0;
private Boolean initialized = false;
public JrtFsEnumeration(File jrtFsFile, Path pathWithinJrt) {
this.pathWithinJrt = pathWithinJrt;
ensureInitialized();
}
private void ensureInitialized() {
synchronized (this.initialized) {
if (this.initialized) {
return;
}
FileCacheBuilderVisitor visitor = new FileCacheBuilderVisitor();
if (this.pathWithinJrt != null) {
try {
Files.walkFileTree(this.pathWithinJrt, visitor);
// System.out.println("JrtFs enumeration for '"+pathWithinJrt+"' with
// #"+jfos.size()+" entries");
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
else {
Iterable<java.nio.file.Path> roots = fs.getRootDirectories();
try {
for (java.nio.file.Path path : roots) {
Files.walkFileTree(path, visitor);
}
// System.out.println("JrtFs enumeration initialized with
// #"+jfos.size()+" entries");
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
this.initialized = true;
}
}
@Override
public boolean hasMoreElements() {
return this.counter < this.jfos.size();
}
@Override
public JrtEntryJavaFileObject nextElement() {
if (this.counter >= this.jfos.size()) {
throw new NoSuchElementException();
}
JrtEntryJavaFileObject toReturn = this.jfos.get(this.counter++);
return toReturn;
}
/**
* Return the relative path of this file to the base directory that the directory
* enumeration was started for.
* @param file a file discovered returned by this enumeration
* @return the relative path of the file (for example: a/b/c/D.class)
*/
public String getName(JrtEntryJavaFileObject file) {
return file.getPathToClassString();
}
public void reset() {
this.counter = 0;
}
class FileCacheBuilderVisitor extends SimpleFileVisitor<Path> {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
int fnc = file.getNameCount();
if (fnc >= 3 && file.toString().endsWith(".class")) { // There is a preceeding
// module name - e.g.
// /modules/java.base/java/lang/Object.class
// file.subpath(2, fnc); // e.g. java/lang/Object.class
JrtFsEnumeration.this.jfos.add(new JrtEntryJavaFileObject(file));
}
return FileVisitResult.CONTINUE;
}
}
}

View File

@@ -1,325 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.java;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.maven.model.ActivationFile;
import org.apache.maven.model.ActivationOS;
import org.apache.maven.model.ActivationProperty;
import org.apache.maven.model.building.ModelProblemCollector;
import org.apache.maven.model.building.ModelProblemCollectorRequest;
import org.apache.maven.model.path.DefaultPathTranslator;
import org.apache.maven.model.profile.DefaultProfileSelector;
import org.apache.maven.model.profile.ProfileActivationContext;
import org.apache.maven.model.profile.activation.FileProfileActivator;
import org.apache.maven.model.profile.activation.JdkVersionProfileActivator;
import org.apache.maven.model.profile.activation.OperatingSystemProfileActivator;
import org.apache.maven.model.profile.activation.PropertyProfileActivator;
import org.apache.maven.settings.Activation;
import org.apache.maven.settings.Mirror;
import org.apache.maven.settings.Profile;
import org.apache.maven.settings.Proxy;
import org.apache.maven.settings.Server;
import org.apache.maven.settings.Settings;
import org.apache.maven.settings.crypto.SettingsDecryptionResult;
import org.eclipse.aether.repository.Authentication;
import org.eclipse.aether.repository.AuthenticationSelector;
import org.eclipse.aether.repository.MirrorSelector;
import org.eclipse.aether.repository.ProxySelector;
import org.eclipse.aether.util.repository.AuthenticationBuilder;
import org.eclipse.aether.util.repository.ConservativeAuthenticationSelector;
import org.eclipse.aether.util.repository.DefaultAuthenticationSelector;
import org.eclipse.aether.util.repository.DefaultMirrorSelector;
import org.eclipse.aether.util.repository.DefaultProxySelector;
/**
* An encapsulation of settings read from a user's Maven settings.xml.
*
* @author Andy Wilkinson
* @see MavenSettingsReader
*/
public class MavenSettings {
private final boolean offline;
private final MirrorSelector mirrorSelector;
private final AuthenticationSelector authenticationSelector;
private final ProxySelector proxySelector;
private final String localRepository;
private final List<Profile> activeProfiles;
/**
* Create a new {@link MavenSettings} instance.
* @param settings the source settings
* @param decryptedSettings the decrypted settings
*/
public MavenSettings(Settings settings, SettingsDecryptionResult decryptedSettings) {
this.offline = settings.isOffline();
this.mirrorSelector = createMirrorSelector(settings);
this.authenticationSelector = createAuthenticationSelector(decryptedSettings);
this.proxySelector = createProxySelector(decryptedSettings);
this.localRepository = settings.getLocalRepository();
this.activeProfiles = determineActiveProfiles(settings);
}
private MirrorSelector createMirrorSelector(Settings settings) {
DefaultMirrorSelector selector = new DefaultMirrorSelector();
for (Mirror mirror : settings.getMirrors()) {
selector.add(mirror.getId(), mirror.getUrl(), mirror.getLayout(), false,
mirror.getMirrorOf(), mirror.getMirrorOfLayouts());
}
return selector;
}
private AuthenticationSelector createAuthenticationSelector(
SettingsDecryptionResult decryptedSettings) {
DefaultAuthenticationSelector selector = new DefaultAuthenticationSelector();
for (Server server : decryptedSettings.getServers()) {
AuthenticationBuilder auth = new AuthenticationBuilder();
auth.addUsername(server.getUsername()).addPassword(server.getPassword());
auth.addPrivateKey(server.getPrivateKey(), server.getPassphrase());
selector.add(server.getId(), auth.build());
}
return new ConservativeAuthenticationSelector(selector);
}
private ProxySelector createProxySelector(
SettingsDecryptionResult decryptedSettings) {
DefaultProxySelector selector = new DefaultProxySelector();
for (Proxy proxy : decryptedSettings.getProxies()) {
Authentication authentication = new AuthenticationBuilder()
.addUsername(proxy.getUsername()).addPassword(proxy.getPassword())
.build();
selector.add(
new org.eclipse.aether.repository.Proxy(proxy.getProtocol(),
proxy.getHost(), proxy.getPort(), authentication),
proxy.getNonProxyHosts());
}
return selector;
}
private List<Profile> determineActiveProfiles(Settings settings) {
SpringBootCliModelProblemCollector problemCollector = new SpringBootCliModelProblemCollector();
List<org.apache.maven.model.Profile> activeModelProfiles = createProfileSelector()
.getActiveProfiles(createModelProfiles(settings.getProfiles()),
new SpringBootCliProfileActivationContext(
settings.getActiveProfiles()),
problemCollector);
if (!problemCollector.getProblems().isEmpty()) {
throw new IllegalStateException(createFailureMessage(problemCollector));
}
List<Profile> activeProfiles = new ArrayList<Profile>();
Map<String, Profile> profiles = settings.getProfilesAsMap();
for (org.apache.maven.model.Profile modelProfile : activeModelProfiles) {
activeProfiles.add(profiles.get(modelProfile.getId()));
}
return activeProfiles;
}
private String createFailureMessage(
SpringBootCliModelProblemCollector problemCollector) {
StringWriter message = new StringWriter();
PrintWriter printer = new PrintWriter(message);
printer.println("Failed to determine active profiles:");
for (ModelProblemCollectorRequest problem : problemCollector.getProblems()) {
printer.println(" " + problem.getMessage() + (problem.getLocation() != null
? " at " + problem.getLocation() : ""));
if (problem.getException() != null) {
printer.println(indentStackTrace(problem.getException(), " "));
}
}
return message.toString();
}
private String indentStackTrace(Exception ex, String indent) {
return indentLines(printStackTrace(ex), indent);
}
private String printStackTrace(Exception ex) {
StringWriter stackTrace = new StringWriter();
PrintWriter printer = new PrintWriter(stackTrace);
ex.printStackTrace(printer);
return stackTrace.toString();
}
private String indentLines(String input, String indent) {
StringWriter indented = new StringWriter();
PrintWriter writer = new PrintWriter(indented);
String line;
BufferedReader reader = new BufferedReader(new StringReader(input));
try {
while ((line = reader.readLine()) != null) {
writer.println(indent + line);
}
}
catch (IOException ex) {
return input;
}
return indented.toString();
}
private DefaultProfileSelector createProfileSelector() {
DefaultProfileSelector selector = new DefaultProfileSelector();
selector.addProfileActivator(new FileProfileActivator()
.setPathTranslator(new DefaultPathTranslator()));
selector.addProfileActivator(new JdkVersionProfileActivator());
selector.addProfileActivator(new PropertyProfileActivator());
selector.addProfileActivator(new OperatingSystemProfileActivator());
return selector;
}
private List<org.apache.maven.model.Profile> createModelProfiles(
List<Profile> profiles) {
List<org.apache.maven.model.Profile> modelProfiles = new ArrayList<org.apache.maven.model.Profile>();
for (Profile profile : profiles) {
org.apache.maven.model.Profile modelProfile = new org.apache.maven.model.Profile();
modelProfile.setId(profile.getId());
if (profile.getActivation() != null) {
modelProfile
.setActivation(createModelActivation(profile.getActivation()));
}
modelProfiles.add(modelProfile);
}
return modelProfiles;
}
private org.apache.maven.model.Activation createModelActivation(
Activation activation) {
org.apache.maven.model.Activation modelActivation = new org.apache.maven.model.Activation();
modelActivation.setActiveByDefault(activation.isActiveByDefault());
if (activation.getFile() != null) {
ActivationFile activationFile = new ActivationFile();
activationFile.setExists(activation.getFile().getExists());
activationFile.setMissing(activation.getFile().getMissing());
modelActivation.setFile(activationFile);
}
modelActivation.setJdk(activation.getJdk());
if (activation.getOs() != null) {
ActivationOS os = new ActivationOS();
os.setArch(activation.getOs().getArch());
os.setFamily(activation.getOs().getFamily());
os.setName(activation.getOs().getName());
os.setVersion(activation.getOs().getVersion());
modelActivation.setOs(os);
}
if (activation.getProperty() != null) {
ActivationProperty property = new ActivationProperty();
property.setName(activation.getProperty().getName());
property.setValue(activation.getProperty().getValue());
modelActivation.setProperty(property);
}
return modelActivation;
}
public boolean getOffline() {
return this.offline;
}
public MirrorSelector getMirrorSelector() {
return this.mirrorSelector;
}
public AuthenticationSelector getAuthenticationSelector() {
return this.authenticationSelector;
}
public ProxySelector getProxySelector() {
return this.proxySelector;
}
public String getLocalRepository() {
return this.localRepository;
}
public List<Profile> getActiveProfiles() {
return this.activeProfiles;
}
private static final class SpringBootCliProfileActivationContext
implements ProfileActivationContext {
private final List<String> activeProfiles;
SpringBootCliProfileActivationContext(List<String> activeProfiles) {
this.activeProfiles = activeProfiles;
}
@Override
public List<String> getActiveProfileIds() {
return this.activeProfiles;
}
@Override
public List<String> getInactiveProfileIds() {
return Collections.emptyList();
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public Map<String, String> getSystemProperties() {
return (Map) System.getProperties();
}
@Override
public Map<String, String> getUserProperties() {
return Collections.emptyMap();
}
@Override
public File getProjectDirectory() {
return new File(".");
}
@Override
public Map<String, String> getProjectProperties() {
return Collections.emptyMap();
}
}
private static final class SpringBootCliModelProblemCollector
implements ModelProblemCollector {
private final List<ModelProblemCollectorRequest> problems = new ArrayList<ModelProblemCollectorRequest>();
@Override
public void add(ModelProblemCollectorRequest req) {
this.problems.add(req);
}
List<ModelProblemCollectorRequest> getProblems() {
return this.problems;
}
}
}

View File

@@ -1,157 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.java;
import java.io.File;
import java.lang.reflect.Field;
import org.apache.maven.settings.Settings;
import org.apache.maven.settings.building.DefaultSettingsBuilderFactory;
import org.apache.maven.settings.building.DefaultSettingsBuildingRequest;
import org.apache.maven.settings.building.SettingsBuildingException;
import org.apache.maven.settings.building.SettingsBuildingRequest;
import org.apache.maven.settings.crypto.DefaultSettingsDecrypter;
import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
import org.apache.maven.settings.crypto.SettingsDecrypter;
import org.apache.maven.settings.crypto.SettingsDecryptionResult;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.NoLocalRepositoryManagerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonatype.plexus.components.cipher.DefaultPlexusCipher;
import org.sonatype.plexus.components.cipher.PlexusCipherException;
import org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher;
/**
* {@code MavenSettingsReader} reads settings from a user's Maven settings.xml file,
* decrypting them if necessary using settings-security.xml.
*
* @author Andy Wilkinson
*/
public class MavenSettingsReader {
private static final Logger log = LoggerFactory.getLogger(MavenSettingsReader.class);
private final String homeDir;
public MavenSettingsReader() {
this(System.getProperty("user.home"));
}
public MavenSettingsReader(String homeDir) {
this.homeDir = homeDir;
}
public static void applySettings(MavenSettings settings,
DefaultRepositorySystemSession session) {
if (settings.getLocalRepository() != null) {
try {
session.setLocalRepositoryManager(
new SimpleLocalRepositoryManagerFactory().newInstance(session,
new LocalRepository(settings.getLocalRepository())));
}
catch (NoLocalRepositoryManagerException e) {
throw new IllegalStateException(
"Cannot set local repository to " + settings.getLocalRepository(),
e);
}
}
session.setOffline(settings.getOffline());
session.setMirrorSelector(settings.getMirrorSelector());
session.setAuthenticationSelector(settings.getAuthenticationSelector());
session.setProxySelector(settings.getProxySelector());
}
public MavenSettings readSettings() {
Settings settings = loadSettings();
SettingsDecryptionResult decrypted = decryptSettings(settings);
if (!decrypted.getProblems().isEmpty()) {
log.error(
"Maven settings decryption failed. Some Maven repositories may be inaccessible");
// Continue - the encrypted credentials may not be used
}
return new MavenSettings(settings, decrypted);
}
private Settings loadSettings() {
File settingsFile = new File(this.homeDir, ".m2/settings.xml");
if (settingsFile.exists()) {
log.info("Reading settings from: " + settingsFile);
}
else {
log.info("No settings found at: " + settingsFile);
}
SettingsBuildingRequest request = new DefaultSettingsBuildingRequest();
request.setUserSettingsFile(settingsFile);
request.setSystemProperties(System.getProperties());
try {
return new DefaultSettingsBuilderFactory().newInstance().build(request)
.getEffectiveSettings();
}
catch (SettingsBuildingException ex) {
throw new IllegalStateException(
"Failed to build settings from " + settingsFile, ex);
}
}
private SettingsDecryptionResult decryptSettings(Settings settings) {
DefaultSettingsDecryptionRequest request = new DefaultSettingsDecryptionRequest(
settings);
return createSettingsDecrypter().decrypt(request);
}
private SettingsDecrypter createSettingsDecrypter() {
SettingsDecrypter settingsDecrypter = new DefaultSettingsDecrypter();
setField(DefaultSettingsDecrypter.class, "securityDispatcher", settingsDecrypter,
new SpringBootSecDispatcher());
return settingsDecrypter;
}
private void setField(Class<?> sourceClass, String fieldName, Object target,
Object value) {
try {
Field field = sourceClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(target, value);
}
catch (Exception ex) {
throw new IllegalStateException(
"Failed to set field '" + fieldName + "' on '" + target + "'", ex);
}
}
private class SpringBootSecDispatcher extends DefaultSecDispatcher {
private static final String SECURITY_XML = ".m2/settings-security.xml";
SpringBootSecDispatcher() {
File file = new File(MavenSettingsReader.this.homeDir, SECURITY_XML);
this._configurationFile = file.getAbsolutePath();
try {
this._cipher = new DefaultPlexusCipher();
}
catch (PlexusCipherException e) {
throw new IllegalStateException(e);
}
}
}
}

View File

@@ -1,769 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.java;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import javax.tools.FileObject;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import javax.tools.StandardLocation;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.graph.Dependency;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.function.compiler.java.IterableClasspath.ZipEnumerator;
/**
* A file manager that serves source code from in memory and ensures output results are
* kept in memory rather than being flushed out to disk. The JavaFileManager is also used
* as a lookup mechanism for resolving types.
*
* @author Andy Clement
* @author Oleg Zhurakousky
*/
public class MemoryBasedJavaFileManager implements JavaFileManager {
final static String BOOT_PACKAGING_PREFIX_FOR_CLASSES = "BOOT-INF/classes/";
final static String BOOT_PACKAGING_PREFIX_FOR_LIBRARIES = "BOOT-INF/lib/";
private static Logger logger = LoggerFactory
.getLogger(MemoryBasedJavaFileManager.class);
private static URI JRT_URI = URI.create("jrt:/");
private static FileSystem fs;
private CompilationOutputCollector outputCollector;
private Map<String, File> resolvedAdditionalDependencies = new LinkedHashMap<>();
private String platformClasspath;
private String classpath;
private CompilationInfoCache compilationInfoCache;
private Map<Key, CloseableFilterableJavaFileObjectIterable> iterables = new HashMap<>();
private String jrtFsFilePath = null;
private boolean checkedForJrtFsPath = false;
public MemoryBasedJavaFileManager() {
this.outputCollector = new CompilationOutputCollector();
this.compilationInfoCache = new CompilationInfoCache();
}
private static FileSystem getJrtFs() {
if (fs == null) {
fs = FileSystems.getFileSystem(JRT_URI);
}
return fs;
}
@Override
public int isSupportedOption(String option) {
logger.debug("isSupportedOption({})", option);
return -1; // Not yet supporting options
}
@Override
public ClassLoader getClassLoader(Location location) {
// Do not simply return the context classloader as it may get closed and then
// be unusable for loading any further classes
logger.debug("getClassLoader({})", location);
return null; // Do not currently need to load plugins
}
private String getPlatformClassPath() {
if (this.platformClasspath == null) {
this.platformClasspath = System.getProperty("sun.boot.class.path");
}
if (this.platformClasspath == null) {
this.platformClasspath = "";
}
return this.platformClasspath;
}
@Override
public Iterable<JavaFileObject> list(Location location, String packageName,
Set<Kind> kinds, boolean recurse) throws IOException {
logger.debug("list({},{},{},{})", location, packageName, kinds, recurse);
String classpath = "";
Path moduleRootPath = null;
if (location instanceof JDKModuleLocation
&& (kinds == null || kinds.contains(Kind.CLASS))) {
// list(org.springframework.cloud.function.compiler.java.MemoryBasedJavaFileManager$JDKModuleLocation@550a1967,
// java.lang,[SOURCE, CLASS, HTML, OTHER],false)
moduleRootPath = ((JDKModuleLocation) location).getModuleRootPath();
logger.debug("For JDKModuleLocation " + location.toString() + " root path is "
+ moduleRootPath);
}
else if (location == StandardLocation.PLATFORM_CLASS_PATH
&& (kinds == null || kinds.contains(Kind.CLASS))) {
classpath = getPlatformClassPath();
// if (classpath.length() == 0) {
// if (hasJrtFsPath()) {
// classpath = getJrtFsPath();
// }
// }
logger.debug("Creating iterable for boot class path: {}", classpath);
}
else if (location == StandardLocation.CLASS_PATH
&& (kinds == null || kinds.contains(Kind.CLASS))) {
String javaClassPath = getClassPath();
if (!this.resolvedAdditionalDependencies.isEmpty()) {
for (File resolvedAdditionalDependency : this.resolvedAdditionalDependencies
.values()) {
javaClassPath += File.pathSeparatorChar + resolvedAdditionalDependency
.toURI().toString().substring("file:".length());
}
}
classpath = javaClassPath;
logger.debug("Creating iterable for class path: {}", classpath);
}
Key k = new Key(location, classpath, packageName, kinds, recurse);
CloseableFilterableJavaFileObjectIterable resultIterable = this.iterables.get(k);
if (resultIterable == null) {
if (moduleRootPath != null) {
resultIterable = new IterableJrtModule(this.compilationInfoCache,
moduleRootPath, packageName, recurse);
}
else {
resultIterable = new IterableClasspath(this.compilationInfoCache,
classpath, packageName, recurse);
}
this.iterables.put(k, resultIterable);
}
resultIterable.reset();
return resultIterable;
}
private String getClassPath() {
if (this.classpath == null) {
ClassLoader loader = InMemoryJavaFileObject.class.getClassLoader();
String cp = null;
if (loader instanceof URLClassLoader) {
cp = classPath((URLClassLoader) loader, cp);
}
if (cp == null) {
cp = System.getProperty("java.class.path");
}
if (hasJrtFsPath()) {
cp = cp + File.pathSeparator + getJrtFsPath();
}
this.classpath = pathWithPlatformClassPathRemoved(cp);
}
return this.classpath;
}
private String classPath(URLClassLoader loader, String cp) {
URL[] urls = loader.getURLs();
if (urls.length > 1) { // heuristic that catches Maven surefire tests
if (!urls[0].toString().startsWith("jar:file:")) { // heuristic for
// Spring Boot fat
// jar
StringBuilder builder = new StringBuilder();
for (URL url : urls) {
if (builder.length() > 0) {
builder.append(File.pathSeparator);
}
String path = url.toString();
if (path.startsWith("file:")) {
path = path.substring("file:".length());
}
builder.append(path);
}
cp = builder.toString();
}
}
return cp;
}
// remove the platform classpath entries, they will be search separately (and earlier)
private String pathWithPlatformClassPathRemoved(String classpath) {
Set<String> pcps = toList(getPlatformClassPath());
Set<String> cps = toList(classpath);
cps.removeAll(pcps);
StringBuilder builder = new StringBuilder();
for (String cpe : cps) {
if (builder.length() > 0) {
builder.append(File.pathSeparator);
}
builder.append(cpe);
}
return builder.toString();
}
private Set<String> toList(String path) {
Set<String> result = new LinkedHashSet<>();
StringTokenizer tokenizer = new StringTokenizer(path, File.pathSeparator);
while (tokenizer.hasMoreTokens()) {
result.add(tokenizer.nextToken());
}
return result;
}
@Override
public boolean hasLocation(Location location) {
logger.debug("hasLocation({})", location);
return (location == StandardLocation.SOURCE_PATH
|| location == StandardLocation.CLASS_PATH
|| location == StandardLocation.PLATFORM_CLASS_PATH);
}
@Override
public String inferBinaryName(Location location, JavaFileObject file) {
if (location == StandardLocation.SOURCE_PATH) {
return null;
}
// Kind of ignoring location here... assuming we want basically the FQ type name
// Example value from getName(): javax/validation/bootstrap/GenericBootstrap.class
String classname = file.getName().replace('/', '.').replace('\\', '.');
return classname.substring(0, classname.lastIndexOf(".class"));
}
@Override
public boolean isSameFile(FileObject a, FileObject b) {
logger.debug("isSameFile({},{})", a, b);
return a.equals(b);
}
@Override
public boolean handleOption(String current, Iterator<String> remaining) {
logger.debug("handleOption({},{})", current, remaining);
return false; // This file manager does not manage any options
}
@Override
public JavaFileObject getJavaFileForInput(Location location, String className,
Kind kind) throws IOException {
logger.debug("getJavaFileForInput({},{},{})", location, className, kind);
// getJavaFileForInput(SOURCE_PATH,module-info,SOURCE)
if (className.equals("module-info")) {
return null;
}
throw new IllegalStateException("Not expected to be used in this context");
}
@Override
public JavaFileObject getJavaFileForOutput(Location location, String className,
Kind kind, FileObject sibling) throws IOException {
logger.debug("getJavaFileForOutput({},{},{},{})", location, className, kind,
sibling);
// Example parameters: CLASS_OUTPUT, Foo, CLASS,
// StringBasedJavaSourceFileObject[string:///a/b/c/Foo.java]
return this.outputCollector.getJavaFileForOutput(location, className, kind,
sibling);
}
@Override
public FileObject getFileForInput(Location location, String packageName,
String relativeName) throws IOException {
logger.debug("getFileForInput({},{},{})", location, packageName, relativeName);
throw new IllegalStateException("Not expected to be used in this context");
}
@Override
public FileObject getFileForOutput(Location location, String packageName,
String relativeName, FileObject sibling) throws IOException {
logger.debug("getFileForOutput({},{},{},{})", location, packageName, relativeName,
sibling);
// This can be called when the annotation config processor runs
// Example parameters: CLASS_OUTPUT, ,
// META-INF/spring-configuration-metadata.json, null
return this.outputCollector.getFileForOutput(location, packageName, relativeName,
sibling);
}
@Override
public void flush() throws IOException {
}
@Override
public void close() throws IOException {
Collection<CloseableFilterableJavaFileObjectIterable> toClose = this.iterables
.values();
for (CloseableFilterableJavaFileObjectIterable icp : toClose) {
icp.close();
}
}
public List<CompiledClassDefinition> getCompiledClasses() {
return this.outputCollector.getCompiledClasses();
}
public List<CompilationMessage> addAndResolveDependencies(String[] dependencies) {
List<CompilationMessage> resolutionMessages = new ArrayList<>();
for (String dependency : dependencies) {
if (dependency.startsWith("maven:")) {
// Resolving an explicit external archive
String coordinates = dependency.replaceFirst("maven:\\/*", "");
DependencyResolver engine = DependencyResolver.instance();
try {
File resolved = engine.resolve(
new Dependency(new DefaultArtifact(coordinates), "runtime"));
// Example:
// dependency =
// maven://org.springframework:spring-expression:4.3.9.RELEASE
// resolved.toURI() =
// file:/Users/aclement/.m2/repository/
// org/springframework/spring-expression/4.3.9.RELEASE/spring-expression-4.3.9.RELEASE.jar
this.resolvedAdditionalDependencies.put(dependency, resolved);
}
catch (RuntimeException re) {
CompilationMessage compilationMessage = new CompilationMessage(
CompilationMessage.Kind.ERROR, re.getMessage(), null, 0, 0);
resolutionMessages.add(compilationMessage);
}
}
else if (dependency.startsWith("file:")) {
this.resolvedAdditionalDependencies.put(dependency,
new File(URI.create(dependency)));
}
else {
resolutionMessages.add(new CompilationMessage(
CompilationMessage.Kind.ERROR,
"Unrecognized dependency: " + dependency
+ " (expected something of the form: maven://groupId:artifactId:version)",
null, 0, 0));
}
}
return resolutionMessages;
}
public Map<String, File> getResolvedAdditionalDependencies() {
return this.resolvedAdditionalDependencies;
}
public String inferModuleName(Location location) throws IOException {
if (location instanceof JDKModuleLocation) {
JDKModuleLocation m = (JDKModuleLocation) location;
return m.getModuleName();
}
throw new IllegalStateException(
"Asked to inferModuleName from a " + location.getClass().getName());
}
private boolean hasJrtFsPath() {
return getJrtFsPath() != null;
}
private String getJrtFsPath() {
if (!this.checkedForJrtFsPath) {
String javaHome = System.getProperty("java.home");
String jrtFsFilePath = javaHome + File.separator + "lib" + File.separator
+ "jrt-fs.jar";
File jrtFsFile = new File(jrtFsFilePath);
if (jrtFsFile.exists()) {
this.jrtFsFilePath = jrtFsFilePath;
}
this.checkedForJrtFsPath = true;
}
return this.jrtFsFilePath;
}
public Iterable<Set<Location>> listLocationsForModules(Location location)
throws IOException {
if (getJrtFsPath() != null
&& location == StandardLocation.valueOf("SYSTEM_MODULES")) {
Set<Set<Location>> ss = new HashSet<>();
HashSet<Location> moduleLocations = new HashSet<>();
ModuleIdentifierVisitor visitor = new ModuleIdentifierVisitor();
Iterable<Path> roots = getJrtFs().getRootDirectories();
try {
for (Path path : roots) {
Files.walkFileTree(path, visitor);
}
moduleLocations.addAll(visitor.getModuleLocations());
}
catch (IOException ioe) {
throw new RuntimeException(ioe);
}
ss.add(moduleLocations);
return ss;
}
else {
return Collections.emptySet();
}
}
// Holds information that may help speed up compilation
static class CompilationInfoCache {
private Map<File, ArchiveInfo> archivePackageCache;
private boolean packageCacheInitialized = false;
private Map<String, Path> packageCache = new HashMap<String, Path>();
private ArchiveInfo moduleArchiveInfo;
ArchiveInfo getArchiveInfoFor(File archive) {
if (!archive.isFile() || !(archive.getName().endsWith(".zip")
|| archive.getName().endsWith(".jar"))) {
// it is not an archive
return null;
}
if (this.archivePackageCache == null) {
this.archivePackageCache = new HashMap<>();
}
try {
ArchiveInfo result = this.archivePackageCache.get(archive);
if (result == null) {
result = buildArchiveInfo(archive);
this.archivePackageCache.put(archive, result);
}
return result;
}
catch (Exception e) {
throw new IllegalStateException(
"Unexpected problem caching entries from " + archive.getName(),
e);
}
}
private synchronized ArchiveInfo buildPackageMap() {
if (!this.packageCacheInitialized) {
this.packageCacheInitialized = true;
Iterable<java.nio.file.Path> roots = getJrtFs().getRootDirectories();
PackageCacheBuilderVisitor visitor = new PackageCacheBuilderVisitor();
try {
for (java.nio.file.Path path : roots) {
Files.walkFileTree(path, visitor);
}
}
catch (IOException e) {
throw new RuntimeException(e);
}
List<String> ls = new ArrayList<>();
ls.addAll(this.packageCache.keySet());
Collections.sort(ls);
this.moduleArchiveInfo = new ArchiveInfo(ls, false);
}
return this.moduleArchiveInfo;
}
/**
* Walk the specified archive and collect up the package names of any .class files
* encountered. If the archive contains nested jars packaged in a BOOT style way
* (under a BOOT-INF/lib folder) then walk those too and include relevant
* packages.
* @param file archive file to discover packages from
* @return an ArchiveInfo encapsulating package info from the archive
*/
private ArchiveInfo buildArchiveInfo(File file) {
if (file.toString().endsWith("jrt-fs.jar")) {
// Special treatment for >=JDK9 - treat this as intention to use modules
return buildPackageMap();
}
List<String> packageNames = new ArrayList<>();
boolean isBootJar = false;
try (ZipFile openArchive = new ZipFile(file)) {
Enumeration<? extends ZipEntry> entries = openArchive.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String name = entry.getName();
if (name.endsWith(".class")) {
if (name.startsWith(BOOT_PACKAGING_PREFIX_FOR_CLASSES)) {
isBootJar = true;
int idx = name.lastIndexOf('/') + 1;
if (idx != 0) {
if (idx == BOOT_PACKAGING_PREFIX_FOR_CLASSES.length()) {
// default package
packageNames.add("/");
}
else {
// Normalize to forward slashes
name = name.substring(
BOOT_PACKAGING_PREFIX_FOR_CLASSES.length(),
idx);
name = name.replace('\\', '/');
packageNames.add(name);
}
}
}
else {
int idx = name.lastIndexOf('/') + 1;
if (idx != 0) {
// Normalize to forward slashes
name = name.replace('\\', '/');
name = name.substring(0, idx);
packageNames.add(name);
}
else if (idx == 0) {
// default package entries in here
packageNames.add("/");
}
}
}
else if (name.startsWith(BOOT_PACKAGING_PREFIX_FOR_LIBRARIES)
&& name.endsWith(".jar")) {
isBootJar = true;
try (ZipInputStream zis = new ZipInputStream(
openArchive.getInputStream(entry))) {
Enumeration<? extends ZipEntry> nestedZipEnumerator = new ZipEnumerator(
zis);
while (nestedZipEnumerator.hasMoreElements()) {
ZipEntry innerEntry = nestedZipEnumerator.nextElement();
String innerEntryName = innerEntry.getName();
if (innerEntryName.endsWith(".class")) {
int idx = innerEntryName.lastIndexOf('/') + 1;
if (idx != 0) {
// Normalize to forward slashes
innerEntryName = innerEntryName.replace('\\',
'/');
innerEntryName = innerEntryName.substring(0, idx);
packageNames.add(innerEntryName);
}
else if (idx == 0) {
// default package entries in here
packageNames.add("/");
}
}
}
}
}
}
}
catch (IOException ioe) {
throw new IllegalStateException(
"Unexpected problem determining packages in " + file, ioe);
}
return new ArchiveInfo(packageNames, isBootJar);
}
static class ArchiveInfo {
// The packages identified in a particular archive
private List<String> packageNames;
private boolean isBootJar = false;
ArchiveInfo(List<String> packageNames, boolean isBootJar) {
this.packageNames = packageNames;
Collections.sort(this.packageNames);
this.isBootJar = isBootJar;
}
public List<String> getPackageNames() {
return this.packageNames;
}
public boolean isBootJar() {
return this.isBootJar;
}
public boolean containsPackage(String packageName,
boolean subpackageMatchesAllowed) {
if (subpackageMatchesAllowed) {
for (String candidatePackageName : this.packageNames) {
if (candidatePackageName.startsWith(packageName)) {
return true;
}
}
return false;
}
else {
// Must be an exact match, fast binary search:
int pos = Collections.binarySearch(this.packageNames, packageName);
return (pos >= 0);
}
}
}
private class PackageCacheBuilderVisitor extends SimpleFileVisitor<Path> {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
if (file.getNameCount() > 3 && file.toString().endsWith(".class")) {
int fnc = file.getNameCount();
if (fnc > 3) { // There is a package name - e.g.
// /modules/java.base/java/lang/Object.class
Path packagePath = file.subpath(2, fnc - 1); // e.g. java/lang
String packagePathString = packagePath.toString() + "/";
CompilationInfoCache.this.packageCache.put(packagePathString,
file.subpath(0, fnc - 1)); // java/lang
// ->
// /modules/java.base/java/lang
}
}
return FileVisitResult.CONTINUE;
}
}
}
static class Key {
private Location location;
private String classpath;
private String packageName;
private Set<Kind> kinds;
private boolean recurse;
Key(Location location, String classpath, String packageName, Set<Kind> kinds,
boolean recurse) {
this.location = location;
this.classpath = classpath;
this.packageName = packageName;
this.kinds = kinds;
this.recurse = recurse;
}
@Override
public int hashCode() {
return (((this.location.hashCode() * 37) + this.classpath.hashCode() * 37
+ (this.packageName == null ? 0 : this.packageName.hashCode())) * 37
+ this.kinds.hashCode()) * 37 + (this.recurse ? 1 : 0);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Key)) {
return false;
}
Key that = (Key) obj;
return this.location.equals(that.location)
&& this.classpath.equals(that.classpath)
&& this.kinds.equals(that.kinds) && (this.recurse == that.recurse)
&& (this.packageName == null ? (that.packageName == null)
: this.packageName.equals(that.packageName));
}
}
static class JDKModuleLocation implements Location {
private String moduleName;
private Path moduleRootPath;
JDKModuleLocation(String moduleName, Path moduleRootPath) {
this.moduleName = moduleName;
this.moduleRootPath = moduleRootPath;
}
@Override
public String getName() {
return "MODULE";
}
@Override
public boolean isOutputLocation() {
return false;
}
public String getModuleName() {
return this.moduleName;
}
public Path getModuleRootPath() {
return this.moduleRootPath;
}
public String toString() {
return "JDKModuleLocation(" + this.moduleName + ")";
}
public int hashCode() {
return this.moduleName.hashCode();
}
public boolean equals(Object other) {
if (!(other instanceof JDKModuleLocation)) {
return false;
}
return this.hashCode() == ((JDKModuleLocation) other).hashCode();
}
}
static class ModuleIdentifierVisitor extends SimpleFileVisitor<Path> {
private Map<String, Path> modules = new HashMap<>();
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
if (file.getNameCount() > 2 && file.toString().endsWith(".class")) {
// /modules/jdk.rmic/sun/tools/tree/CaseStatement.class
String moduleName = file.getName(1).toString(); // jdk.rmic
Path moduleRootPath = file.subpath(0, 2); // /modules/jdk.rmic
if (!this.modules.containsKey(moduleName)) {
this.modules.put(moduleName, moduleRootPath);
}
}
return FileVisitResult.CONTINUE;
}
public Set<Location> getModuleLocations() {
if (this.modules.size() == 0) {
return Collections.emptySet();
}
else {
Set<Location> locations = new HashSet<>();
for (Map.Entry<String, Path> moduleEntry : this.modules.entrySet()) {
locations.add(new JDKModuleLocation(moduleEntry.getKey(),
moduleEntry.getValue()));
}
return locations;
}
}
}
}

View File

@@ -1,187 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.java;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.NestingKind;
import javax.tools.JavaFileObject;
/**
* Represents an element inside in zip which is itself inside a zip. These objects are not
* initially created with the content of the file they represent, only enough information
* to find that content because many will typically be created but only few will be
* opened.
*
* @author Andy Clement
*/
public class NestedZipEntryJavaFileObject implements JavaFileObject {
private File outerFile;
private ZipFile outerZipFile;
private ZipEntry innerZipFile;
private ZipEntry innerZipFileEntry;
private URI uri;
public NestedZipEntryJavaFileObject(File outerFile, ZipFile outerZipFile,
ZipEntry innerZipFile, ZipEntry innerZipFileEntry) {
this.outerFile = outerFile;
this.outerZipFile = outerZipFile;
this.innerZipFile = innerZipFile;
this.innerZipFileEntry = innerZipFileEntry;
}
@Override
public String getName() {
return this.innerZipFileEntry.getName(); // Example: a/b/C.class
}
@Override
public URI toUri() {
if (this.uri == null) {
String uriString = null;
try {
uriString = "zip:" + this.outerFile.getAbsolutePath() + "!"
+ this.innerZipFile.getName() + "!"
+ this.innerZipFileEntry.getName();
this.uri = new URI(uriString);
}
catch (URISyntaxException e) {
throw new IllegalStateException(
"Unexpected URISyntaxException for string '" + uriString + "'",
e);
}
}
return this.uri;
}
@Override
public InputStream openInputStream() throws IOException {
// Find the inner zip file inside the outer zip file, then
// find the relevant entry, then return the stream.
InputStream innerZipFileInputStream = this.outerZipFile
.getInputStream(this.innerZipFile);
ZipInputStream innerZipInputStream = new ZipInputStream(innerZipFileInputStream);
ZipEntry nextEntry = innerZipInputStream.getNextEntry();
while (nextEntry != null) {
if (nextEntry.getName().equals(this.innerZipFileEntry.getName())) {
return innerZipInputStream;
}
nextEntry = innerZipInputStream.getNextEntry();
}
throw new IllegalStateException(
"Unable to locate nested zip entry " + this.innerZipFileEntry.getName()
+ " in zip " + this.innerZipFile.getName() + " inside zip "
+ this.outerZipFile.getName());
}
@Override
public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
// It is bytecode
throw new UnsupportedOperationException(
"getCharContent() not supported on class file: " + getName());
}
@Override
public long getLastModified() {
return this.innerZipFileEntry.getTime();
}
@Override
public Kind getKind() {
// The filtering before this object was created ensure it is only used for classes
return Kind.CLASS;
}
@Override
public boolean delete() {
return false; // Cannot delete entries inside nested zips
}
@Override
public OutputStream openOutputStream() throws IOException {
throw new IllegalStateException("cannot write to nested zip entry: " + toUri());
}
@Override
public Writer openWriter() throws IOException {
throw new IllegalStateException("cannot write to nested zip entry: " + toUri());
}
@Override
public boolean isNameCompatible(String simpleName, Kind kind) {
if (kind != Kind.CLASS) {
return false;
}
String name = getName();
int lastSlash = name.lastIndexOf('/');
return name.substring(lastSlash + 1).equals(simpleName + ".class");
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
// It is bytecode
throw new UnsupportedOperationException(
"getCharContent() not supported on class file: " + getName());
}
@Override
public NestingKind getNestingKind() {
return null; // nesting level not known
}
@Override
public Modifier getAccessLevel() {
return null; // access level not known
}
@Override
public int hashCode() {
int hc = this.outerFile.getName().hashCode();
hc = hc * 37 + this.innerZipFile.getName().hashCode();
hc = hc * 37 + this.innerZipFileEntry.getName().hashCode();
return hc;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof NestedZipEntryJavaFileObject)) {
return false;
}
NestedZipEntryJavaFileObject that = (NestedZipEntryJavaFileObject) obj;
return (this.outerFile.getName().equals(that.outerFile.getName()))
&& (this.innerZipFile.getName().equals(that.innerZipFile.getName()))
&& (this.innerZipFileEntry.getName()
.equals(that.innerZipFileEntry.getName()));
}
}

View File

@@ -1,128 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.java;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.Diagnostic.Kind;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.ToolProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Compile Java source at runtime and load it.
*
* @author Andy Clement
*/
public class RuntimeJavaCompiler {
private static Logger logger = LoggerFactory.getLogger(RuntimeJavaCompiler.class);
private JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
/**
* Compile the named class consisting of the supplied source code. If successful load
* the class and return it. Multiple classes may get loaded if the source code
* included anonymous/inner/local classes.
* @param className the name of the class (dotted form, e.g. com.foo.bar.Goo)
* @param classSourceCode the full source code for the class
* @param dependencies optional coordinates for dependencies, maven
* 'maven://groupId:artifactId:version', or 'file:' URIs for local files
* @return a CompilationResult that encapsulates what happened during compilation
* (classes/messages produced)
*/
public CompilationResult compile(String className, String classSourceCode,
String... dependencies) {
logger.info("Compiling source for class {} using compiler {}", className,
this.compiler.getClass().getName());
DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<JavaFileObject>();
MemoryBasedJavaFileManager fileManager = new MemoryBasedJavaFileManager();
List<CompilationMessage> resolutionMessages = fileManager
.addAndResolveDependencies(dependencies);
JavaFileObject sourceFile = InMemoryJavaFileObject
.getSourceJavaFileObject(className, classSourceCode);
Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(sourceFile);
List<String> options = new ArrayList<>();
options.add("-source");
options.add("1.8");
CompilationTask task = this.compiler.getTask(null, fileManager,
diagnosticCollector, options, null, compilationUnits);
boolean success = task.call();
CompilationResult compilationResult = new CompilationResult(success);
compilationResult.recordCompilationMessages(resolutionMessages);
compilationResult.setResolvedAdditionalDependencies(new ArrayList<>(
fileManager.getResolvedAdditionalDependencies().values()));
// If successful there may be no errors but there might be info/warnings
for (Diagnostic<? extends JavaFileObject> diagnostic : diagnosticCollector
.getDiagnostics()) {
CompilationMessage.Kind kind = (diagnostic.getKind() == Kind.ERROR
? CompilationMessage.Kind.ERROR : CompilationMessage.Kind.OTHER);
// String sourceCode =
// ((StringBasedJavaSourceFileObject)diagnostic.getSource()).getSourceCode();
String sourceCode = null;
try {
sourceCode = (String) diagnostic.getSource().getCharContent(true);
}
catch (IOException ioe) {
// Unexpected, but leave sourceCode null to indicate it was not
// retrievable
}
catch (NullPointerException npe) {
// TODO: should we skip warning diagnostics in the loop altogether?
}
int startPosition = (int) diagnostic.getPosition();
if (startPosition == Diagnostic.NOPOS) {
startPosition = (int) diagnostic.getStartPosition();
}
CompilationMessage compilationMessage = new CompilationMessage(kind,
diagnostic.getMessage(null), sourceCode, startPosition,
(int) diagnostic.getEndPosition());
compilationResult.recordCompilationMessage(compilationMessage);
}
if (success) {
List<CompiledClassDefinition> ccds = fileManager.getCompiledClasses();
List<Class<?>> classes = new ArrayList<>();
try (SimpleClassLoader ccl = new SimpleClassLoader(
this.getClass().getClassLoader())) {
for (CompiledClassDefinition ccd : ccds) {
Class<?> clazz = ccl.defineClass(ccd.getClassName(), ccd.getBytes());
classes.add(clazz);
compilationResult.addClassBytes(ccd.getClassName(), ccd.getBytes());
}
}
catch (IOException ioe) {
logger.debug("Unexpected exception defining classes", ioe);
}
compilationResult.setCompiledClasses(classes);
}
return compilationResult;
}
}

View File

@@ -1,59 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.java;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;
/**
* Very simple classloader that can be used to load the compiled types.
*
* @author Andy Clement
*/
public class SimpleClassLoader extends URLClassLoader {
private static final URL[] NO_URLS = new URL[0];
public SimpleClassLoader(ClassLoader classLoader) {
super(NO_URLS, classLoader);
}
public SimpleClassLoader(List<File> resolvedAdditionalDependencies,
ClassLoader classLoader) {
super(toUrls(resolvedAdditionalDependencies), classLoader);
}
private static URL[] toUrls(List<File> resolvedAdditionalDependencies) {
URL[] urls = new URL[resolvedAdditionalDependencies.size()];
for (int i = 0, max = resolvedAdditionalDependencies.size(); i < max; i++) {
try {
urls[i] = resolvedAdditionalDependencies.get(i).toURI().toURL();
}
catch (Exception e) {
throw new IllegalStateException(e);
}
}
return urls;
}
public Class<?> defineClass(String name, byte[] bytes) {
return super.defineClass(name, bytes, 0, bytes.length);
}
}

View File

@@ -1,159 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.java;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.NestingKind;
import javax.tools.JavaFileObject;
/**
* A {@link JavaFileObject} that works on a ZIP entry.
*
* @author Mark Fisher
*/
public class ZipEntryJavaFileObject implements JavaFileObject {
private File containingFile;
private ZipFile zf;
private ZipEntry ze;
private URI uri;
public ZipEntryJavaFileObject(File containingFile, ZipFile zipFile, ZipEntry entry) {
this.containingFile = containingFile;
this.zf = zipFile;
this.ze = entry;
}
@Override
public URI toUri() {
if (this.uri == null) {
String uriString = null;
try {
uriString = "zip:" + this.containingFile.getAbsolutePath() + "!"
+ this.ze.getName();
this.uri = new URI(uriString);
}
catch (URISyntaxException e) {
throw new IllegalStateException(
"Unexpected URISyntaxException for string '" + uriString + "'",
e);
}
}
return this.uri;
}
@Override
public String getName() {
return this.ze.getName(); // a/b/C.class
}
@Override
public InputStream openInputStream() throws IOException {
return this.zf.getInputStream(this.ze);
}
@Override
public OutputStream openOutputStream() throws IOException {
throw new IllegalStateException("only expected to be used for input");
}
@Override
public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
// It is bytecode
throw new UnsupportedOperationException(
"openReader() not supported on class file: " + getName());
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
// It is bytecode
throw new UnsupportedOperationException(
"getCharContent() not supported on class file: " + getName());
}
@Override
public Writer openWriter() throws IOException {
throw new IllegalStateException("only expected to be used for input");
}
@Override
public long getLastModified() {
return this.ze.getTime();
}
@Override
public boolean delete() {
return false; // Cannot delete entries inside zips
}
@Override
public Kind getKind() {
return Kind.CLASS;
}
@Override
public boolean isNameCompatible(String simpleName, Kind kind) {
if (kind != Kind.CLASS) {
return false;
}
String name = getName();
int lastSlash = name.lastIndexOf('/');
return name.substring(lastSlash + 1).equals(simpleName + ".class");
}
@Override
public NestingKind getNestingKind() {
return null;
}
@Override
public Modifier getAccessLevel() {
return null;
}
@Override
public int hashCode() {
int hc = this.containingFile.getName().hashCode();
hc = hc * 37 + this.ze.getName().hashCode();
return hc;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ZipEntryJavaFileObject)) {
return false;
}
ZipEntryJavaFileObject that = (ZipEntryJavaFileObject) obj;
return (this.containingFile.getName().equals(that.containingFile.getName()))
&& (this.ze.getName().equals(that.ze.getName()));
}
}

View File

@@ -1,88 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.proxy;
import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicReference;
import org.springframework.asm.ClassReader;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cloud.function.compiler.CompilationResultFactory;
import org.springframework.cloud.function.compiler.java.SimpleClassLoader;
import org.springframework.cloud.function.core.FunctionFactoryMetadata;
import org.springframework.core.io.Resource;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.ReflectionUtils;
/**
* @author Mark Fisher
* @author Oleg Zhurakousky
*/
abstract class AbstractByteCodeLoadingProxy<T>
implements InitializingBean, FunctionFactoryMetadata<T> {
private final Resource resource;
private final SimpleClassLoader classLoader = new SimpleClassLoader(
AbstractByteCodeLoadingProxy.class.getClassLoader());
private T target;
private Method method;
AbstractByteCodeLoadingProxy(Resource resource) {
this.resource = resource;
}
@Override
@SuppressWarnings("unchecked")
public void afterPropertiesSet() throws Exception {
byte[] bytes = FileCopyUtils.copyToByteArray(this.resource.getInputStream());
String className = new ClassReader(bytes).getClassName().replace("/", ".");
Class<?> factoryClass = this.classLoader.defineClass(className, bytes);
try {
this.target = ((CompilationResultFactory<T>) factoryClass.newInstance())
.getResult();
this.method = findFactoryMethod(factoryClass);
}
catch (InstantiationException | IllegalAccessException e) {
throw new IllegalArgumentException("failed to load Function byte code", e);
}
}
@Override
public final T getTarget() {
return this.target;
}
@Override
public Method getFactoryMethod() {
return this.method;
}
private Method findFactoryMethod(Class<?> clazz) {
AtomicReference<Method> method = new AtomicReference<>();
ReflectionUtils.doWithLocalMethods(clazz, m -> {
if (m.getName().equals("getResult")
&& m.getReturnType().getName().startsWith("java.util.function")) {
method.set(m);
}
});
return method.get();
}
}

View File

@@ -1,83 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.proxy;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cloud.function.compiler.AbstractFunctionCompiler;
import org.springframework.cloud.function.compiler.CompiledFunctionFactory;
import org.springframework.cloud.function.core.FunctionFactoryMetadata;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
/**
* @param <T> target type
* @author Mark Fisher
*/
public class AbstractLambdaCompilingProxy<T>
implements InitializingBean, BeanNameAware, FunctionFactoryMetadata<T> {
private final Resource resource;
private final AbstractFunctionCompiler<T> compiler;
private String beanName;
private CompiledFunctionFactory<T> factory;
private String[] typeParameterizations;
public AbstractLambdaCompilingProxy(Resource resource,
AbstractFunctionCompiler<T> compiler) {
Assert.notNull(resource, "Resource must not be null");
Assert.notNull(compiler, "Compiler must not be null");
this.resource = resource;
this.compiler = compiler;
}
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
}
public void setTypeParameterizations(String... typeParameterizations) {
this.typeParameterizations = typeParameterizations;
}
@Override
public void afterPropertiesSet() throws Exception {
String lambda = FileCopyUtils
.copyToString(new InputStreamReader(this.resource.getInputStream()));
this.factory = this.compiler.compile(this.beanName, lambda,
this.typeParameterizations);
}
@Override
public final T getTarget() {
return this.factory.getResult();
}
@Override
public Method getFactoryMethod() {
return this.factory.getFactoryMethod();
}
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.proxy;
import java.util.function.Consumer;
import org.springframework.cloud.function.core.FunctionFactoryMetadata;
import org.springframework.core.io.Resource;
/**
* @param <T> type
* @author Mark Fisher
* @author Oleg Zhurakousky
*/
public class ByteCodeLoadingConsumer<T> extends AbstractByteCodeLoadingProxy<Consumer<T>>
implements FunctionFactoryMetadata<Consumer<T>>, Consumer<T> {
public ByteCodeLoadingConsumer(Resource resource) {
super(resource);
}
@Override
public void accept(T t) {
this.getTarget().accept(t);
}
}

View File

@@ -1,43 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.proxy;
import java.util.function.Function;
import org.springframework.cloud.function.core.FunctionFactoryMetadata;
import org.springframework.core.io.Resource;
/**
* @param <T> Function input type
* @param <R> Function result type
* @author Mark Fisher
* @author Oleg Zhurakousky
*/
public class ByteCodeLoadingFunction<T, R>
extends AbstractByteCodeLoadingProxy<Function<T, R>>
implements FunctionFactoryMetadata<Function<T, R>>, Function<T, R> {
public ByteCodeLoadingFunction(Resource resource) {
super(resource);
}
@Override
public R apply(T input) {
return this.getTarget().apply(input);
}
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.proxy;
import java.util.function.Supplier;
import org.springframework.cloud.function.core.FunctionFactoryMetadata;
import org.springframework.core.io.Resource;
/**
* @param <T> type
* @author Mark Fisher
* @author Oleg Zhurakousky
*/
public class ByteCodeLoadingSupplier<T> extends AbstractByteCodeLoadingProxy<Supplier<T>>
implements FunctionFactoryMetadata<Supplier<T>>, Supplier<T> {
public ByteCodeLoadingSupplier(Resource resource) {
super(resource);
}
@Override
public T get() {
return this.getTarget().get();
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.proxy;
import java.util.function.Consumer;
import org.springframework.cloud.function.compiler.ConsumerCompiler;
import org.springframework.core.io.Resource;
/**
* @param <T> input argument type
* @author Mark Fisher
*/
public class LambdaCompilingConsumer<T> extends AbstractLambdaCompilingProxy<Consumer<T>>
implements Consumer<T> {
public LambdaCompilingConsumer(Resource resource, ConsumerCompiler<T> compiler) {
super(resource, compiler);
}
@Override
public void accept(T input) {
this.getTarget().accept(input);
}
}

View File

@@ -1,43 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.proxy;
import java.util.function.Function;
import org.springframework.cloud.function.compiler.FunctionCompiler;
import org.springframework.cloud.function.core.FunctionFactoryMetadata;
import org.springframework.core.io.Resource;
/**
* @param <T> input argument type
* @param <R> output argument type
* @author Mark Fisher
*/
public class LambdaCompilingFunction<T, R>
extends AbstractLambdaCompilingProxy<Function<T, R>>
implements FunctionFactoryMetadata<Function<T, R>>, Function<T, R> {
public LambdaCompilingFunction(Resource resource, FunctionCompiler<T, R> compiler) {
super(resource, compiler);
}
@Override
public R apply(T input) {
return this.getTarget().apply(input);
}
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.proxy;
import java.util.function.Supplier;
import org.springframework.cloud.function.compiler.SupplierCompiler;
import org.springframework.cloud.function.core.FunctionFactoryMetadata;
import org.springframework.core.io.Resource;
/**
* @param <T> target type
* @author Mark Fisher
*/
public class LambdaCompilingSupplier<T> extends AbstractLambdaCompilingProxy<Supplier<T>>
implements FunctionFactoryMetadata<Supplier<T>>, Supplier<T> {
public LambdaCompilingSupplier(Resource resource, SupplierCompiler<T> compiler) {
super(resource, compiler);
}
@Override
public T get() {
return this.getTarget().get();
}
}

View File

@@ -1,2 +0,0 @@
org.springframework.context.ApplicationListener=\
org.springframework.cloud.function.compiler.config.FunctionProxyApplicationListener

View File

@@ -1,466 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import org.junit.jupiter.api.Test;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.function.compiler.java.CompilationResult;
import org.springframework.cloud.function.compiler.java.RuntimeJavaCompiler;
import org.springframework.cloud.function.core.FunctionFactoryUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests that verify dependency resolution. Dependencies can be resolved against simple
* classpath entries or against classes under BOOT-INF/classes or in a nested jar under
* under BOOT-INF/lib. Finding classes in those locations enables compilation against a
* packaged boot jar.
*
* @author Andy Clement
*/
public class CompilerDependencyResolutionTests {
@Test
public void compilingTestClass() throws Exception {
ClassDescriptor t1 = compile("Test1",
"package com.test;\npublic class Test1 { public static String doit() { return \"T1\";}}\n");
String result = (String) t1.clazz.getDeclaredMethod("doit").invoke(null);
assertThat(result).isEqualTo("T1");
}
@Test
public void packagingClassesIntoJar() {
ClassDescriptor t1 = getTestClass("1");
ClassDescriptor t2 = getTestClass("2");
File jar = JarBuilder.create().addEntries(t1, t2).getJar();
assertJarContents(jar, t1, t2);
}
/**
* Doesn't actually verify the caching helps but can be useful to run to see current
* numbers.
*/
@Test
public void speedtest() {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
Logger rootLogger = loggerContext
.getLogger("org.springframework.cloud.function.compiler");
rootLogger.setLevel(Level.ERROR);
// 10 uses of a single function compiler:
long stime = System.currentTimeMillis();
FunctionCompiler<String, String> fc = new FunctionCompiler<String, String>(
String.class.getName());
for (int i = 0; i < 5; i++) {
stime = System.currentTimeMillis();
CompiledFunctionFactory<Function<String, String>> result = fc.compile("foos",
"flux -> flux.map(v -> v.toUpperCase())", "Flux<String>",
"Flux<String>");
assertThat(FunctionFactoryUtils.isFluxFunction(result.getFactoryMethod()))
.isTrue();
System.out.println("Reusing FunctionCompiler: #" + (i + 1) + " = "
+ (System.currentTimeMillis() - stime) + "ms");
}
// 3 separate FunctionCompilers:
stime = System.currentTimeMillis();
CompiledFunctionFactory<Function<String, String>> compiled = new FunctionCompiler<String, String>(
String.class.getName()).compile("foos",
"flux -> flux.map(v -> v.toUpperCase())", "Flux<String>",
"Flux<String>");
assertThat(FunctionFactoryUtils.isFluxFunction(compiled.getFactoryMethod()))
.isTrue();
long etime = System.currentTimeMillis();
long time1 = (etime - stime);
System.out.println("New FunctionCompiler: " + time1 + "ms");
stime = System.currentTimeMillis();
compiled = new FunctionCompiler<String, String>(String.class.getName()).compile(
"foos", "flux -> flux.map(v -> v.toUpperCase())", "Flux<String>",
"Flux<String>");
assertThat(FunctionFactoryUtils.isFluxFunction(compiled.getFactoryMethod()))
.isTrue();
etime = System.currentTimeMillis();
long time2 = (etime - stime);
System.out.println("New FunctionCompiler: " + time2 + "ms");
stime = System.currentTimeMillis();
compiled = new FunctionCompiler<String, String>(String.class.getName()).compile(
"foos", "flux -> flux.map(v -> v.toUpperCase())", "Flux<String>",
"Flux<String>");
assertThat(FunctionFactoryUtils.isFluxFunction(compiled.getFactoryMethod()))
.isTrue();
etime = System.currentTimeMillis();
long time3 = (etime - stime);
System.out.println("New FunctionCompiler: " + time3 + "ms");
}
@Test
public void usingJarNoPackageDecl() throws Exception {
ClassDescriptor tx = compile("TestX",
"public class TestX { public static String doit() { return \"TX\";}}\n");
File jar = JarBuilder.create().addEntry(tx).getJar();
assertJarContents(jar, tx);
CompilationResult result = new RuntimeJavaCompiler().compile("A",
"public class A {\n" + " public static Object run() {\n"
+ " return new TestX();\n" + " }\n" + "}",
jar.toURI().toString());
assertThat(result.getCompilationMessages().isEmpty())
.as("Should be no problems: " + result.getCompilationMessages()).isTrue();
try (URLClassLoader cl = new TestClassLoader(tx, descriptorFromResult(result))) {
Class<?> class1 = cl.loadClass("A");
Object invoke = class1.getDeclaredMethod("run").invoke(null);
assertThat(invoke.getClass().getName()).isEqualTo(tx.name);
}
}
// A class with no package declaration is placed under BOOT-INF/classes/ in a jar that
// is then used for resolution
@Test
public void usingJarNoPackageDeclBootInfClasses() throws Exception {
ClassDescriptor t1 = compile("TestX",
"public class TestX { public static String doit() { return \"TX\";}}\n");
File jar = JarBuilder.create().addEntryWithPrefix("BOOT-INF/classes/", t1)
.getJar();
assertJarContents(jar, "BOOT-INF/classes/", t1);
CompilationResult result = new RuntimeJavaCompiler().compile("A",
"public class A {\n" + " public static Object run() {\n"
+ " return new TestX();\n" + " }\n" + "}",
jar.toURI().toString());
assertThat(result.getCompilationMessages().isEmpty())
.as("Should be no problems: " + result.getCompilationMessages()).isTrue();
try (URLClassLoader cl = new TestClassLoader(t1, descriptorFromResult(result))) {
Class<?> class1 = cl.loadClass("A");
Object invoke = class1.getDeclaredMethod("run").invoke(null);
assertThat(invoke.getClass().getName()).isEqualTo(t1.name);
}
}
// A class with no package declaration is placed in a jar which is then placed under
// under BOOT-INF/lib/ in a jar that is then used for resolution
@Test
public void usingJarNoPackageDeclNestedBootInfLib() throws Exception {
ClassDescriptor t1 = compile("TestX",
"public class TestX { public static String doit() { return \"TX\";}}\n");
File jar = JarBuilder.create().addEntry(t1).getJar();
assertJarContents(jar, t1);
// Now stick that jar in another jar!
File jar2 = JarBuilder.create().addEntry("BOOT-INF/lib/inner.jar", jar).getJar();
CompilationResult result = new RuntimeJavaCompiler().compile("A",
"public class A {\n" + " public static Object run() {\n"
+ " return new TestX();\n" + " }\n" + "}",
jar2.toURI().toString());
assertThat(result.getCompilationMessages().isEmpty())
.as("Should be no problems: " + result.getCompilationMessages()).isTrue();
try (URLClassLoader cl = new TestClassLoader(t1, descriptorFromResult(result))) {
Class<?> class1 = cl.loadClass("A");
Object invoke = class1.getDeclaredMethod("run").invoke(null);
assertThat(invoke.getClass().getName()).isEqualTo(t1.name);
}
}
// Build a jar containing a type with a package declaration and building against it
@Test
public void usingJarWithPackageDecl() throws Exception {
ClassDescriptor t1 = getTestClass("1");
File jar = JarBuilder.create().addEntry(t1).getJar();
assertJarContents(jar, t1);
CompilationResult result = new RuntimeJavaCompiler().compile("A",
"import " + t1.name.replace('$', '.') + ";\n" + "public class A {\n"
+ " public static Object run() {\n" + " return new Test1();\n"
+ " }\n" + "}",
jar.toURI().toString());
assertThat(result.getCompilationMessages().isEmpty())
.as("Should be no problems: " + result.getCompilationMessages()).isTrue();
try (URLClassLoader cl = new TestClassLoader(t1, descriptorFromResult(result))) {
Class<?> class1 = cl.loadClass("A");
Object invoke = class1.getDeclaredMethod("run").invoke(null);
assertThat(invoke.getClass().getName()).isEqualTo(t1.name);
}
}
@Test
public void usingJarWithPackageDeclBootInfClasses() throws Exception {
// Here the dependencies are under BOOT-INF/classes in the jar
ClassDescriptor t1 = getTestClass("1");
File jar = JarBuilder.create().addEntryWithPrefix("BOOT-INF/classes/", t1)
.getJar();
assertJarContents(jar, "BOOT-INF/classes/", t1);
CompilationResult result = new RuntimeJavaCompiler().compile("A",
"import " + t1.name.replace('$', '.') + ";\n" + "public class A {\n"
+ " public static Object run() {\n" + " return new Test1();\n"
+ " }\n" + "}",
jar.toURI().toString());
assertThat(result.getCompilationMessages().isEmpty())
.as("Should be no problems: " + result.getCompilationMessages()).isTrue();
try (URLClassLoader cl = new TestClassLoader(t1, descriptorFromResult(result))) {
Class<?> class1 = cl.loadClass("A");
Object invoke = class1.getDeclaredMethod("run").invoke(null);
assertThat(invoke.getClass().getName()).isEqualTo(t1.name);
}
}
@Test
public void usingJarWithPackageDeclNestedBootInfLib() throws Exception {
// Here the dependencies are under BOOT-INF/lib/nested.jar
ClassDescriptor t1 = getTestClass("1");
File jar = JarBuilder.create().addEntry(t1).getJar();
assertJarContents(jar, t1);
// Now stick that jar in another jar!
File jar2 = JarBuilder.create().addEntry("BOOT-INF/lib/inner.jar", jar).getJar();
CompilationResult result = new RuntimeJavaCompiler().compile("A",
"import " + t1.name.replace('$', '.') + ";\n" + "public class A {\n"
+ " public static Object run() {\n" + " return new Test1();\n"
+ " }\n" + "}",
jar2.toURI().toString());
assertThat(result.getCompilationMessages().isEmpty())
.as("Should be no problems: " + result.getCompilationMessages()).isTrue();
try (URLClassLoader cl = new TestClassLoader(t1, descriptorFromResult(result))) {
Class<?> class1 = cl.loadClass("A");
Object invoke = class1.getDeclaredMethod("run").invoke(null);
assertThat(invoke.getClass().getName()).isEqualTo(t1.name);
}
}
// ---
private ClassDescriptor descriptorFromResult(CompilationResult result) {
Class<?> clazz = result.getCompiledClasses().get(0);
return new ClassDescriptor(clazz.getName(), result.getClassBytes(clazz.getName()),
clazz);
}
private ClassDescriptor compile(String className, String classSourceCode) {
CompilationResult compile = new RuntimeJavaCompiler().compile(className,
classSourceCode);
assertThat(compile.getCompilationMessages().isEmpty())
.as("Should be empty: \n" + compile.getCompilationMessages()).isTrue();
Class<?> clazz = compile.getCompiledClasses().get(0);
return new ClassDescriptor(clazz.getName(),
compile.getClassBytes(clazz.getName()),
compile.getCompiledClasses().get(0));
}
private ClassDescriptor getTestClass(String suffix) {
try {
return compile("Test" + suffix,
"package com.test;\npublic class Test" + suffix
+ " { public static String doit() { return \"T" + suffix
+ "\";}}\n");
}
catch (Exception e) {
throw new IllegalStateException(e);
}
}
private void assertJarContents(File jar, ClassDescriptor... classdescriptors) {
assertJarContents(jar, "", classdescriptors);
}
private void assertJarContents(File jar, String prefix,
ClassDescriptor... classDescriptors) {
List<String> clazzes = new ArrayList<>();
for (ClassDescriptor classDescriptor : classDescriptors) {
clazzes.add(prefix + classDescriptor.name.replace('.', '/') + ".class");
}
walkJar(jar, (entry) -> clazzes.remove(entry.getName()));
assertThat(clazzes.isEmpty()).as("Should be empty: " + clazzes).isTrue();
}
private void walkJar(File jar, Consumer<JarEntry> fn) {
try {
JarInputStream jarInputStream = new JarInputStream(new FileInputStream(jar));
while (true) {
JarEntry nextJarEntry = jarInputStream.getNextJarEntry();
if (nextJarEntry == null) {
break;
}
fn.accept(nextJarEntry);
}
jarInputStream.close();
}
catch (IOException ioe) {
ioe.printStackTrace();
}
}
@SuppressWarnings("unused")
private void printJar(File jar) {
System.out.println("Contents of jar: " + jar);
walkJar(jar, (entry) -> {
System.out.println("- " + entry.getName());
});
}
// Simple holder for the result of compilation
static class ClassDescriptor {
final String name;
final byte[] bytes;
final Class<?> clazz;
ClassDescriptor(String name, byte[] bytes, Class<?> clazz) {
this.name = name;
this.bytes = bytes;
this.clazz = clazz;
}
}
static final class JarBuilder {
File jarFile;
JarOutputStream jos;
private JarBuilder() {
try {
File newJar = File.createTempFile("test", ".jar");
this.jarFile = newJar.getAbsoluteFile();
newJar.delete();
this.jos = new JarOutputStream(new FileOutputStream(this.jarFile));
this.jarFile.deleteOnExit();
}
catch (IOException e) {
throw new IllegalStateException("Unexpected problem creating file", e);
}
}
public static JarBuilder create() {
return new JarBuilder();
}
public JarBuilder addEntry(String entryName, File entryContentFile) {
try {
ZipEntry ze = new ZipEntry(entryName);
this.jos.putNextEntry(ze);
this.jos.write(loadBytes(entryContentFile));
this.jos.closeEntry();
return this;
}
catch (IOException e) {
throw new IllegalStateException(e);
}
}
private byte[] loadBytes(File f) {
try (InputStream is = new FileInputStream(f)) {
byte[] bs = null;
byte[] buf = new byte[10000];
int readCount;
while ((readCount = is.read(buf)) != -1) {
if (bs == null) {
bs = new byte[readCount];
System.arraycopy(buf, 0, bs, 0, readCount);
}
else {
byte[] newbs = new byte[bs.length + readCount];
System.arraycopy(bs, 0, newbs, 0, bs.length);
System.arraycopy(buf, 0, newbs, bs.length, readCount);
bs = newbs;
}
}
return bs;
}
catch (IOException ioe) {
throw new IllegalStateException(ioe);
}
}
public JarBuilder addEntries(ClassDescriptor... classes) {
for (ClassDescriptor clazz : classes) {
addEntry(clazz);
}
return this;
}
public JarBuilder addEntry(ClassDescriptor clazz) {
return addEntryWithPrefix("", clazz);
}
public JarBuilder addEntryWithPrefix(String prefix, ClassDescriptor holder) {
try {
String n = holder.name.replace('.', '/') + ".class";
ZipEntry ze = new ZipEntry(prefix + n);
this.jos.putNextEntry(ze);
this.jos.write(holder.bytes);
this.jos.closeEntry();
return this;
}
catch (IOException e) {
throw new IllegalStateException(e);
}
}
private File getJar() {
try {
this.jos.close();
}
catch (IOException e) {
throw new IllegalStateException("Unable to close jar", e);
}
return this.jarFile;
}
}
// Simple classloader that can load from descriptors
class TestClassLoader extends URLClassLoader {
ClassDescriptor[] descriptors;
TestClassLoader(ClassDescriptor... descriptors) {
super(new URL[0], TestClassLoader.class.getClassLoader());
this.descriptors = descriptors;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
for (ClassDescriptor descriptor : this.descriptors) {
if (descriptor.name.equals(name)) {
return defineClass(descriptor.name, descriptor.bytes, 0,
descriptor.bytes.length);
}
}
return super.findClass(name);
}
}
}

View File

@@ -1,50 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler;
import java.util.function.Consumer;
import org.junit.jupiter.api.Test;
import org.springframework.cloud.function.core.FunctionFactoryUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Dave Syer
*
*/
public class ConsumerCompilerTests {
@Test
public void consumesFluxString() {
CompiledFunctionFactory<Consumer<String>> compiled = new ConsumerCompiler<String>(
String.class.getName()).compile("foos",
"flux -> flux.subscribe(System.out::println)", "Flux<String>");
assertThat(FunctionFactoryUtils.isFluxConsumer(compiled.getFactoryMethod()))
.isTrue();
}
@Test
public void consumesString() {
CompiledFunctionFactory<Consumer<String>> compiled = new ConsumerCompiler<String>(
String.class.getName()).compile("foos", "System.out::println", "String");
assertThat(FunctionFactoryUtils.isFluxConsumer(compiled.getFactoryMethod()))
.isFalse();
}
}

View File

@@ -1,53 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler;
import java.util.function.Function;
import org.junit.jupiter.api.Test;
import org.springframework.cloud.function.core.FunctionFactoryUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Dave Syer
*
*/
public class FunctionCompilerTests {
@Test
public void transformsFluxString() {
CompiledFunctionFactory<Function<String, String>> compiled = new FunctionCompiler<String, String>(
String.class.getName()).compile("foos",
"flux -> flux.map(v -> v.toUpperCase())", "Flux<String>",
"Flux<String>");
assertThat(FunctionFactoryUtils.isFluxFunction(compiled.getFactoryMethod()))
.isTrue();
}
@Test
public void transformsString() {
CompiledFunctionFactory<Function<String, String>> compiled = new FunctionCompiler<String, String>(
String.class.getName()).compile("foos", "v -> v.toUpperCase()", "String",
"String");
assertThat(FunctionFactoryUtils.isFluxFunction(compiled.getFactoryMethod()))
.isFalse();
assertThat(compiled.getResult().apply("hello")).isEqualTo("HELLO");
}
}

View File

@@ -1,63 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler;
import java.util.function.Supplier;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import org.springframework.cloud.function.core.FunctionFactoryUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Dave Syer
*
*/
public class SupplierCompilerTests {
@Test
public void supppliesFluxString() {
CompiledFunctionFactory<Supplier<String>> compiled = new SupplierCompiler<String>(
String.class.getName()).compile("foos",
"() -> Flux.just(\"foo\", \"bar\")", "Flux<String>");
assertThat(FunctionFactoryUtils.isFluxSupplier(compiled.getFactoryMethod()))
.isTrue();
}
@Test
public void supppliesString() {
CompiledFunctionFactory<Supplier<String>> compiled = new SupplierCompiler<String>(
String.class.getName()).compile("foos", "() -> \"foo\"", "String");
assertThat(FunctionFactoryUtils.isFluxSupplier(compiled.getFactoryMethod()))
.isFalse();
assertThat(compiled.getResult().get()).isEqualTo("foo");
}
@Test
public void supppliesFluxStreamString() {
CompiledFunctionFactory<Supplier<Flux<String>>> compiled = new SupplierCompiler<Flux<String>>(
String.class.getName()).compile("foos",
"() -> Flux.interval(Duration.ofMillis(1000)).map(Object::toString)",
"Flux<String>");
assertThat(FunctionFactoryUtils.isFluxSupplier(compiled.getFactoryMethod()))
.isTrue();
assertThat(compiled.getResult().get().blockFirst()).isEqualTo("0");
}
}

View File

@@ -1,174 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.java;
import java.io.File;
import java.util.List;
import java.util.Locale;
import java.util.function.Supplier;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Andy Clement
*/
@SuppressWarnings("unchecked")
public class RuntimeJavaCompilerTests {
@Test
public void basicCompilation() {
RuntimeJavaCompiler rjc = new RuntimeJavaCompiler();
CompilationResult cr = rjc.compile("A", "public class A {}");
List<CompilationMessage> compilationMessages = cr.getCompilationMessages();
assertThat(compilationMessages.isEmpty()).isTrue();
}
@Test
public void missingType() throws Exception {
Locale.setDefault(Locale.ENGLISH);
RuntimeJavaCompiler rjc = new RuntimeJavaCompiler();
CompilationResult cr = rjc.compile("A",
"public class A implements java.util.function.Supplier { "
+ " public String get() {\n"
+ " ExpressionParser parser = new SpelExpressionParser();\n"
+ " Expression exp = parser.parseExpression(\"'Hello World'\");\n"
+ " String message = (String) exp.getValue();"
+ " return message;\n" + " }\n" + "}");
List<CompilationMessage> compilationMessages = cr.getCompilationMessages();
assertThat(compilationMessages.size()).isEqualTo(3);
assertThat(compilationMessages.get(0).getMessage().contains("cannot find symbol"))
.isTrue();
assertThat(compilationMessages.get(0).getMessage()
.contains("class ExpressionParser")).isTrue();
assertThat(compilationMessages.get(1).getMessage().contains("cannot find symbol"))
.isTrue();
assertThat(compilationMessages.get(1).getMessage()
.contains("class SpelExpressionParser")).isTrue();
assertThat(compilationMessages.get(2).getMessage().contains("cannot find symbol"))
.isTrue();
assertThat(compilationMessages.get(2).getMessage().contains("class Expression"))
.isTrue();
}
@Test
public void okWithImportedDependencies() throws Exception {
RuntimeJavaCompiler rjc = new RuntimeJavaCompiler();
CompilationResult cr = rjc.compile("A",
"import org.springframework.expression.*;\n"
+ "import org.springframework.expression.spel.standard.*;\n"
+ "public class A implements java.util.function.Supplier {\n"
+ " public String get() {\n"
+ " ExpressionParser parser = new SpelExpressionParser();\n"
+ " Expression exp = parser.parseExpression(\"'Hello World'\");\n"
+ " String message = (String) exp.getValue();\n"
+ " return message;\n" + " }\n" + "}",
"maven://org.springframework:spring-expression:4.3.9.RELEASE");
List<CompilationMessage> compilationMessages = cr.getCompilationMessages();
assertThat(compilationMessages.isEmpty()).isTrue();
try (SimpleClassLoader cl = new SimpleClassLoader(
this.getClass().getClassLoader())) {
Class<?> clazz = cl.defineClass("A", cr.getClassBytes("A"));
Supplier<String> supplier = (Supplier<String>) clazz.newInstance();
assertThat(supplier.get()).isEqualTo("Hello World");
}
}
@Test
public void okWithImportedDependencies2() throws Exception {
RuntimeJavaCompiler rjc = new RuntimeJavaCompiler();
String source = "import org.joda.time.*;\n"
+ "public class A implements java.util.function.Supplier {\n"
+ " public String get() {\n" + " DateTime dt = new DateTime();\n"
+ " int month = dt.getMonthOfYear();\n"
+ " return String.valueOf(month>0);\n" + " }\n" + "}";
CompilationResult cr = rjc.compile("A", source,
"maven://joda-time:joda-time:2.9.9");
List<CompilationMessage> compilationMessages = cr.getCompilationMessages();
assertThat(compilationMessages.isEmpty()).isTrue();
List<File> resolvedAdditionalDependencies = cr
.getResolvedAdditionalDependencies();
try (SimpleClassLoader cl = new SimpleClassLoader(resolvedAdditionalDependencies,
this.getClass().getClassLoader())) {
Class<?> clazz = cl.defineClass("A", cr.getClassBytes("A"));
Supplier<String> supplier = (Supplier<String>) clazz.newInstance();
assertThat(supplier.get()).isEqualTo("true");
}
cr = rjc.compile("A", source,
"maven://org.springframework:spring-expression:4.3.9.RELEASE",
"maven://joda-time:joda-time:2.9.9");
compilationMessages = cr.getCompilationMessages();
assertThat(compilationMessages.isEmpty()).isTrue();
resolvedAdditionalDependencies = cr.getResolvedAdditionalDependencies();
try (SimpleClassLoader cl = new SimpleClassLoader(resolvedAdditionalDependencies,
this.getClass().getClassLoader())) {
Class<?> clazz = cl.defineClass("A", cr.getClassBytes("A"));
Supplier<String> supplier = (Supplier<String>) clazz.newInstance();
assertThat(supplier.get()).isEqualTo("true");
}
}
@Test
public void dependencyResolution() throws Exception {
// Failure:
RuntimeJavaCompiler rjc = new RuntimeJavaCompiler();
CompilationResult cr = rjc.compile("A", "public class A {}",
"maven://org.springframework:spring-expression2:4.3.9.RELEASE"); // extra
// '2'
// in
// there
List<CompilationMessage> compilationMessages = cr.getCompilationMessages();
assertThat(compilationMessages.size()).isEqualTo(1);
// ERROR:org.eclipse.aether.resolution.ArtifactResolutionException: Could not find
// artifact org.springframework:spring-expression2:jar:4.3.9.RELEASE in
// spring-snapshots (https://repo.spring.io/libs-snapshot)
assertThat(compilationMessages.get(0).getMessage().contains(
"Could not find artifact org.springframework:spring-expression2:jar:4.3.9.RELEASE"))
.isTrue();
// Failure:
rjc = new RuntimeJavaCompiler();
cr = rjc.compile("A", "public class A {}",
"trouble://org.springframework:spring-expression:4.3.9.RELEASE"); // rogue
// prefix
// (should
// be
// "maven:")
compilationMessages = cr.getCompilationMessages();
assertThat(compilationMessages.size()).isEqualTo(1);
assertThat(compilationMessages.get(0).getMessage()
.contains("Unrecognized dependency: "))
.as(compilationMessages.get(0).toString()).isTrue();
// Success
rjc = new RuntimeJavaCompiler();
cr = rjc.compile("A", "public class A {}", "maven://joda-time:joda-time:2.9.9");
compilationMessages = cr.getCompilationMessages();
assertThat(compilationMessages.size()).isEqualTo(0);
List<File> resolvedAdditionalDependencies = cr
.getResolvedAdditionalDependencies();
assertThat(resolvedAdditionalDependencies.size()).isEqualTo(1);
assertThat(resolvedAdditionalDependencies.get(0).toString()
.endsWith("joda-time-2.9.9.jar"))
.as("Expected this to end with 'joda-time-2.9.9.jar': "
+ resolvedAdditionalDependencies.get(0).toString())
.isTrue();
}
}

View File

@@ -1,106 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.compiler.proxy;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import org.springframework.cloud.function.compiler.CompiledFunctionFactory;
import org.springframework.cloud.function.compiler.ConsumerCompiler;
import org.springframework.cloud.function.compiler.FunctionCompiler;
import org.springframework.cloud.function.compiler.SupplierCompiler;
import org.springframework.cloud.function.core.FunctionFactoryMetadata;
import org.springframework.cloud.function.core.FunctionFactoryUtils;
import org.springframework.core.io.ByteArrayResource;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Dave Syer
* @author Oleg Zhurakousky
*/
public class ByteCodeLoadingFunctionTests {
@Test
public void compileConsumer() throws Exception {
CompiledFunctionFactory<Consumer<String>> compiled = new ConsumerCompiler<String>(
String.class.getName()).compile("foos", "System.out::println", "String");
ByteArrayResource resource = new ByteArrayResource(
compiled.getGeneratedClassBytes(), "foos");
ByteCodeLoadingConsumer<String> consumer = new ByteCodeLoadingConsumer<>(
resource);
consumer.afterPropertiesSet();
assertThat(consumer instanceof FunctionFactoryMetadata);
assertThat(FunctionFactoryUtils.isFluxConsumer(consumer.getFactoryMethod()))
.isFalse();
consumer.accept("foo");
}
@Test
public void compileSupplier() throws Exception {
CompiledFunctionFactory<Supplier<String>> compiled = new SupplierCompiler<String>(
String.class.getName()).compile("foos", "() -> \"foo\"", "String");
ByteArrayResource resource = new ByteArrayResource(
compiled.getGeneratedClassBytes(), "foos");
ByteCodeLoadingSupplier<String> supplier = new ByteCodeLoadingSupplier<>(
resource);
supplier.afterPropertiesSet();
assertThat(supplier instanceof FunctionFactoryMetadata);
assertThat(FunctionFactoryUtils.isFluxSupplier(supplier.getFactoryMethod()))
.isFalse();
assertThat(supplier.get()).isEqualTo("foo");
}
@Test
public void compileFunction() throws Exception {
CompiledFunctionFactory<Function<String, String>> compiled = new FunctionCompiler<String, String>(
String.class.getName()).compile("foos", "v -> v.toUpperCase()", "String",
"String");
ByteArrayResource resource = new ByteArrayResource(
compiled.getGeneratedClassBytes(), "foos");
ByteCodeLoadingFunction<String, String> function = new ByteCodeLoadingFunction<>(
resource);
function.afterPropertiesSet();
assertThat(function instanceof FunctionFactoryMetadata);
assertThat(FunctionFactoryUtils.isFluxFunction(function.getFactoryMethod()))
.isFalse();
assertThat(function.apply("foo")).isEqualTo("FOO");
}
@Test
public void compileFluxFunction() throws Exception {
CompiledFunctionFactory<Function<Flux<String>, Flux<String>>> compiled = null;
compiled = new FunctionCompiler<Flux<String>, Flux<String>>(
String.class.getName()).compile("foos",
"flux -> flux.map(v -> v.toUpperCase())", "Flux<String>",
"Flux<String>");
ByteArrayResource resource = new ByteArrayResource(
compiled.getGeneratedClassBytes(), "foos");
ByteCodeLoadingFunction<Flux<String>, Flux<String>> function = new ByteCodeLoadingFunction<>(
resource);
function.afterPropertiesSet();
assertThat(function instanceof FunctionFactoryMetadata);
assertThat(FunctionFactoryUtils.isFluxFunction(function.getFactoryMethod()))
.isTrue();
assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO");
}
}

View File

@@ -1,146 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.core;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
/**
* <p>
* Miscellaneous utility operations to interrogate functional components (beans)
* configured in BeanFactory.
* </p>
* <p>
* It is important to understand that it is not a general purpose utility to interrogate
* "any" functional component. Certain operations may/will not work as expected due to
* java type erasure. While BeanFactory is not the requirement, this utility is targeting
* only the components defined in such way where they could be configured beans within
* BeanFactory.
* </p>
* It is primarily used internally by the framework.
*
* @author Mark Fisher
* @author Oleg Zhurakousky
*/
public abstract class FunctionFactoryUtils {
private static final String FLUX_CLASS_NAME = Flux.class.getName();
private static final String PUBLISHER_CLASS_NAME = Publisher.class.getName();
private FunctionFactoryUtils() {
}
public static boolean isFluxConsumer(Consumer<?> consumer) {
return consumer instanceof FunctionFactoryMetadata
? isFluxConsumer(
((FunctionFactoryMetadata<?>) consumer).getFactoryMethod())
: isFlux(1, getParameterizedTypeNames(consumer, Consumer.class));
}
public static boolean isFluxConsumer(Method method) {
return isFlux(1, getParameterizedTypeNamesForMethod(method, Consumer.class));
}
public static boolean isFluxSupplier(Supplier<?> supplier) {
return supplier instanceof FunctionFactoryMetadata
? isFluxSupplier(
((FunctionFactoryMetadata<?>) supplier).getFactoryMethod())
: isFlux(1, getParameterizedTypeNames(supplier, Supplier.class));
}
public static boolean isFluxSupplier(Method method) {
return isFlux(1, getParameterizedTypeNamesForMethod(method, Supplier.class));
}
public static boolean isFluxFunction(Function<?, ?> function) {
return function instanceof FunctionFactoryMetadata
? isFluxFunction(
((FunctionFactoryMetadata<?>) function).getFactoryMethod())
: isFlux(1, getParameterizedTypeNames(function, Function.class));
}
public static boolean isFluxFunction(Method method) {
return isFlux(2, getParameterizedTypeNamesForMethod(method, Function.class));
}
private static String[] getParameterizedTypeNamesForMethod(Method method,
Class<?> interfaceClass) {
String[] types = retrieveTypes(method.getGenericReturnType(), interfaceClass);
return types == null ? new String[0] : types;
}
private static String[] getParameterizedTypeNames(Object source,
Class<?> interfaceClass) {
return Stream.of(source.getClass().getGenericInterfaces())
.map(gi -> retrieveTypes(gi, interfaceClass)).filter(s -> s != null)
.findFirst().orElse(getSerializedLambdaParameterizedTypeNames(source));
}
private static String[] retrieveTypes(Type genericInterface,
Class<?> interfaceClass) {
if ((genericInterface instanceof ParameterizedType) && interfaceClass
.getTypeName().equals(((ParameterizedType) genericInterface).getRawType()
.getTypeName())) {
ParameterizedType type = (ParameterizedType) genericInterface;
Type[] args = type.getActualTypeArguments();
if (args != null) {
return Stream.of(args).map(arg -> arg.getTypeName())
.toArray(String[]::new);
}
}
return null;
}
private static String[] getSerializedLambdaParameterizedTypeNames(Object source) {
Method method = ReflectionUtils.findMethod(source.getClass(), "writeReplace");
if (method == null) {
return null;
}
ReflectionUtils.makeAccessible(method);
SerializedLambda serializedLambda = (SerializedLambda) ReflectionUtils
.invokeMethod(method, source);
String signature = serializedLambda.getImplMethodSignature().replaceAll("[()]",
"");
List<String> typeNames = Stream.of(signature.split(";"))
.map(t -> t.substring(1).replace('/', '.')).collect(Collectors.toList());
return typeNames.toArray(new String[typeNames.size()]);
}
private static boolean isFlux(int length, String... types) {
return !ObjectUtils.isEmpty(types) && types.length == length
&& Stream.of(types).allMatch(type -> type.startsWith(FLUX_CLASS_NAME)
|| type.startsWith(PUBLISHER_CLASS_NAME));
}
}

View File

@@ -1,94 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.function.core;
import java.lang.reflect.Method;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import org.junit.jupiter.api.Test;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Dave Syer
*
*/
public class FunctionFactoryUtilsTests {
@Test
public void isFluxConsumer() {
Method method = ReflectionUtils.findMethod(FunctionFactoryUtilsTests.class,
"fluxConsumer");
assertThat(FunctionFactoryUtils.isFluxConsumer(method)).isTrue();
assertThat(FunctionFactoryUtils.isFluxSupplier(method)).isFalse();
assertThat(FunctionFactoryUtils.isFluxFunction(method)).isFalse();
}
@Test
public void isFluxSupplier() {
Method method = ReflectionUtils.findMethod(FunctionFactoryUtilsTests.class,
"fluxSupplier");
assertThat(FunctionFactoryUtils.isFluxSupplier(method)).isTrue();
assertThat(FunctionFactoryUtils.isFluxConsumer(method)).isFalse();
assertThat(FunctionFactoryUtils.isFluxFunction(method)).isFalse();
}
@Test
public void isFluxFunction() {
Method method = ReflectionUtils.findMethod(FunctionFactoryUtilsTests.class,
"fluxFunction");
assertThat(FunctionFactoryUtils.isFluxFunction(method)).isTrue();
assertThat(FunctionFactoryUtils.isFluxSupplier(method)).isFalse();
assertThat(FunctionFactoryUtils.isFluxConsumer(method)).isFalse();
}
@Test
public void isReactiveFunction() {
Method method = ReflectionUtils.findMethod(FunctionFactoryUtilsTests.class,
"reactiveFunction");
assertThat(FunctionFactoryUtils.isFluxFunction(method)).isTrue();
assertThat(FunctionFactoryUtils.isFluxSupplier(method)).isFalse();
assertThat(FunctionFactoryUtils.isFluxConsumer(method)).isFalse();
}
public Function<Flux<Foo>, Flux<Foo>> fluxFunction() {
return foos -> foos.map(foo -> new Foo());
}
public Function<Publisher<Foo>, Publisher<Foo>> reactiveFunction() {
return foos -> Flux.from(foos).map(foo -> new Foo());
}
public Supplier<Flux<Foo>> fluxSupplier() {
return () -> Flux.just(new Foo());
}
public Consumer<Flux<Foo>> fluxConsumer() {
return flux -> flux.subscribe(System.out::println);
}
class Foo {
}
}

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<logger name="org.springframework.boot.autoconfigure.logging" level="INFO"/>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>

View File

@@ -1,51 +0,0 @@
buildscript {
ext {
springBootVersion = '1.5.12.RELEASE'
wrapperVersion = '1.0.11.RELEASE'
}
repositories {
mavenLocal()
mavenCentral()
maven { url "https://repo.spring.io/plugins-snapshot" }
maven { url "https://repo.spring.io/plugins-milestone" }
}
dependencies {
classpath("org.springframework.boot.experimental:spring-boot-thin-gradle-plugin:${wrapperVersion}")
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'eclipse'
apply plugin: 'spring-boot'
apply plugin: 'org.springframework.boot.experimental.thin-launcher'
group = 'io.spring.sample'
version = '2.0.0.RELEASE'
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenLocal()
mavenCentral()
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
}
ext {
springCloudFunctionVersion = "2.0.0.BUILD-SNAPSHOT"
}
ext['reactor.version'] = "3.1.7.RELEASE"
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-function-dependencies:${springCloudFunctionVersion}"
}
}
dependencies {
compile('org.springframework.cloud:spring-cloud-starter-function-web')
compile('org.springframework.cloud:spring-cloud-function-compiler')
testCompile('org.springframework.boot:spring-boot-starter-test')
}

View File

@@ -1,5 +0,0 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-bin.zip

View File

@@ -1,164 +0,0 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

View File

@@ -1,90 +0,0 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -1,31 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// @checkstyle:off
@SpringBootApplication
public class SampleApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(SampleApplication.class, args);
}
}
// @checkstyle:on

View File

@@ -1,58 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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.example;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Mark Fisher
*/
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {
"spring.cloud.function.compile.test.lambda=com.example.SampleCompiledConsumerTests.Reference::set",
"spring.cloud.function.compile.test.inputType=String",
"spring.cloud.function.compile.test.type=consumer"})
public class SampleCompiledConsumerTests {
@LocalServerPort
private int port;
@Test
public void print() {
assertThat(new TestRestTemplate().postForObject(
"http://localhost:" + this.port + "/test", "it works", String.class))
.isNull();
assertThat(Reference.instance).isEqualTo("it works");
}
public static class Reference {
private static Object instance;
public static void set(Object o) {
instance = o;
}
}
}

View File

@@ -1,47 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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.example;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Mark Fisher
*/
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {
"spring.cloud.function.compile.test.lambda=f->f.map(s->s+\"!!!\")",
"spring.cloud.function.compile.test.inputType=Flux<String>",
"spring.cloud.function.compile.test.outputType=Flux<String>"})
public class SampleCompiledFunctionTests {
@LocalServerPort
private int port;
@Test
public void lowercase() {
assertThat(new TestRestTemplate().postForObject(
"http://localhost:" + this.port + "/test", "it works", String.class))
.contains("it works!!!");
}
}

View File

@@ -46,6 +46,5 @@ dependencyManagement {
dependencies {
compile('org.springframework.cloud:spring-cloud-starter-function-web')
compile('org.springframework.cloud:spring-cloud-function-compiler')
testCompile('org.springframework.boot:spring-boot-starter-test')
}

View File

@@ -46,6 +46,5 @@ dependencyManagement {
dependencies {
compile('org.springframework.cloud:spring-cloud-starter-function-web')
compile('org.springframework.cloud:spring-cloud-function-compiler')
testCompile('org.springframework.boot:spring-boot-starter-test')
}

View File

@@ -46,6 +46,5 @@ dependencyManagement {
dependencies {
compile('org.springframework.cloud:spring-cloud-function-task')
compile('org.springframework.cloud:spring-cloud-function-compiler')
testCompile('org.springframework.boot:spring-boot-starter-test')
}

View File

@@ -34,10 +34,6 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-function-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-compiler</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>

View File

@@ -45,6 +45,5 @@ dependencyManagement {
dependencies {
compile('org.springframework.cloud:spring-cloud-starter-function-web')
compile('org.springframework.cloud:spring-cloud-function-compiler')
testCompile('org.springframework.boot:spring-boot-starter-test')
}