Remove spring-cloud-function-compiler module
Fixes gh-805
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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._
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
java -jar ../spring-cloud-function-compiler/target/spring-cloud-function-compiler-2.0.0.BUILD-SNAPSHOT.jar
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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><Flux<Object>></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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
org.springframework.context.ApplicationListener=\
|
||||
org.springframework.cloud.function.compiler.config.FunctionProxyApplicationListener
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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')
|
||||
}
|
||||
Binary file not shown.
@@ -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
|
||||
@@ -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 "$@"
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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!!!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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')
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user