Turned on checkstyle
This commit is contained in:
14
.editorconfig
Normal file
14
.editorconfig
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# EditorConfig is awesome: http://EditorConfig.org
|
||||||
|
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.yml]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<settings>
|
<settings>
|
||||||
<servers>
|
<servers>
|
||||||
<server>
|
<server>
|
||||||
<id>sonatype-nexus-staging</id>
|
<id>sonatype-nexus-staging</id>
|
||||||
<username>${env.sonatype_username}</username>
|
<username>${env.sonatype_username}</username>
|
||||||
<password>${env.sonatype_password}</password>
|
<password>${env.sonatype_password}</password>
|
||||||
</server>
|
</server>
|
||||||
<server>
|
<server>
|
||||||
<id>repo.spring.io</id>
|
<id>repo.spring.io</id>
|
||||||
<username>${env.spring_username}</username>
|
<username>${env.spring_username}</username>
|
||||||
<password>${env.spring_password}</password>
|
<password>${env.spring_password}</password>
|
||||||
</server>
|
</server>
|
||||||
</servers>
|
</servers>
|
||||||
</settings>
|
</settings>
|
||||||
|
|||||||
0
.springformat
Normal file
0
.springformat
Normal file
31
docs/pom.xml
31
docs/pom.xml
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>spring-cloud-function-docs</artifactId>
|
<artifactId>spring-cloud-function-docs</artifactId>
|
||||||
@@ -66,24 +66,33 @@
|
|||||||
<target>
|
<target>
|
||||||
<java classname="org.jruby.Main"
|
<java classname="org.jruby.Main"
|
||||||
failonerror="yes">
|
failonerror="yes">
|
||||||
<arg value="${docs.resources.dir}/ruby/generate_readme.sh"/>
|
<arg
|
||||||
|
value="${docs.resources.dir}/ruby/generate_readme.sh"/>
|
||||||
<arg value="-o"/>
|
<arg value="-o"/>
|
||||||
<arg value="${adapters.path}/spring-cloud-function-adapter-aws/README.adoc"/>
|
<arg
|
||||||
<arg value="${basedir}/src/main/asciidoc/adapters/aws-readme.adoc"/>
|
value="${adapters.path}/spring-cloud-function-adapter-aws/README.adoc"/>
|
||||||
|
<arg
|
||||||
|
value="${basedir}/src/main/asciidoc/adapters/aws-readme.adoc"/>
|
||||||
</java>
|
</java>
|
||||||
<java classname="org.jruby.Main"
|
<java classname="org.jruby.Main"
|
||||||
failonerror="yes">
|
failonerror="yes">
|
||||||
<arg value="${docs.resources.dir}/ruby/generate_readme.sh"/>
|
<arg
|
||||||
|
value="${docs.resources.dir}/ruby/generate_readme.sh"/>
|
||||||
<arg value="-o"/>
|
<arg value="-o"/>
|
||||||
<arg value="${adapters.path}/spring-cloud-function-adapter-azure/README.adoc"/>
|
<arg
|
||||||
<arg value="${basedir}/src/main/asciidoc/adapters/azure-readme.adoc"/>
|
value="${adapters.path}/spring-cloud-function-adapter-azure/README.adoc"/>
|
||||||
|
<arg
|
||||||
|
value="${basedir}/src/main/asciidoc/adapters/azure-readme.adoc"/>
|
||||||
</java>
|
</java>
|
||||||
<java classname="org.jruby.Main"
|
<java classname="org.jruby.Main"
|
||||||
failonerror="yes">
|
failonerror="yes">
|
||||||
<arg value="${docs.resources.dir}/ruby/generate_readme.sh"/>
|
<arg
|
||||||
|
value="${docs.resources.dir}/ruby/generate_readme.sh"/>
|
||||||
<arg value="-o"/>
|
<arg value="-o"/>
|
||||||
<arg value="${adapters.path}/spring-cloud-function-adapter-openwhisk/README.adoc"/>
|
<arg
|
||||||
<arg value="${basedir}/src/main/asciidoc/adapters/openwhisk-readme.adoc"/>
|
value="${adapters.path}/spring-cloud-function-adapter-openwhisk/README.adoc"/>
|
||||||
|
<arg
|
||||||
|
value="${basedir}/src/main/asciidoc/adapters/openwhisk-readme.adoc"/>
|
||||||
</java>
|
</java>
|
||||||
</target>
|
</target>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
110
pipeline.yml
110
pipeline.yml
@@ -2,75 +2,75 @@
|
|||||||
# fly --target cloud set-pipeline --config pipeline.yml --pipeline spring-cloud-function --load-vars-from credentials.yml
|
# fly --target cloud set-pipeline --config pipeline.yml --pipeline spring-cloud-function --load-vars-from credentials.yml
|
||||||
---
|
---
|
||||||
jobs:
|
jobs:
|
||||||
- name: build
|
- name: build
|
||||||
plan:
|
plan:
|
||||||
- get: source
|
- get: source
|
||||||
trigger: true
|
trigger: true
|
||||||
- task: maven
|
- task: maven
|
||||||
config:
|
config:
|
||||||
platform: linux
|
platform: linux
|
||||||
image_resource:
|
image_resource:
|
||||||
type: docker-image
|
type: docker-image
|
||||||
source:
|
source:
|
||||||
repository: springio/maven-base
|
repository: springio/maven-base
|
||||||
inputs:
|
inputs:
|
||||||
- name: source
|
- name: source
|
||||||
caches:
|
caches:
|
||||||
- path: source/.m2
|
- path: source/.m2
|
||||||
run:
|
run:
|
||||||
dir: source
|
dir: source
|
||||||
path: sh
|
path: sh
|
||||||
args:
|
args:
|
||||||
- -c
|
- -c
|
||||||
- |
|
- |
|
||||||
rm -rf ~/.m2
|
rm -rf ~/.m2
|
||||||
ln -s $(pwd)/.m2 ~/.m2
|
ln -s $(pwd)/.m2 ~/.m2
|
||||||
./mvnw deploy -s .mvn/settings.xml -Dgpg.passphrase="${passphrase}"
|
./mvnw deploy -s .mvn/settings.xml -Dgpg.passphrase="${passphrase}"
|
||||||
params:
|
params:
|
||||||
TERM: -dumb
|
TERM: -dumb
|
||||||
passphrase: {{passphrase}}
|
passphrase: {{passphrase}}
|
||||||
sonatype_username: {{sonatype-username}}
|
sonatype_username: {{sonatype-username}}
|
||||||
sonatype_password: {{sonatype-password}}
|
sonatype_password: {{sonatype-password}}
|
||||||
spring_username: {{spring-username}}
|
spring_username: {{spring-username}}
|
||||||
spring_password: {{spring-password}}
|
spring_password: {{spring-password}}
|
||||||
on_failure: *slack-failure
|
on_failure: *slack-failure
|
||||||
on_success: *slack-success
|
on_success: *slack-success
|
||||||
|
|
||||||
slack-failure: &slack-failure
|
slack-failure: &slack-failure
|
||||||
put: slack
|
put: slack
|
||||||
params:
|
params:
|
||||||
channel: spring-cloud-firehose
|
channel: spring-cloud-firehose
|
||||||
attachments:
|
attachments:
|
||||||
- color: danger
|
- color: danger
|
||||||
fallback: "$BUILD_PIPELINE_NAME/$BUILD_JOB_NAME #$BUILD_NAME <FAILURE>"
|
fallback: "$BUILD_PIPELINE_NAME/$BUILD_JOB_NAME #$BUILD_NAME <FAILURE>"
|
||||||
text: "Build has failed"
|
text: "Build has failed"
|
||||||
title: "$BUILD_PIPELINE_NAME/$BUILD_JOB_NAME #$BUILD_NAME"
|
title: "$BUILD_PIPELINE_NAME/$BUILD_JOB_NAME #$BUILD_NAME"
|
||||||
title_link: $ATC_EXTERNAL_URL/teams/$BUILD_TEAM_NAME/pipelines/$BUILD_PIPELINE_NAME/jobs/$BUILD_JOB_NAME/builds/$BUILD_NAME
|
title_link: $ATC_EXTERNAL_URL/teams/$BUILD_TEAM_NAME/pipelines/$BUILD_PIPELINE_NAME/jobs/$BUILD_JOB_NAME/builds/$BUILD_NAME
|
||||||
|
|
||||||
slack-success: &slack-success
|
slack-success: &slack-success
|
||||||
put: slack
|
put: slack
|
||||||
params:
|
params:
|
||||||
channel: spring-cloud-firehose
|
channel: spring-cloud-firehose
|
||||||
attachments:
|
attachments:
|
||||||
- color: good
|
- color: good
|
||||||
fallback: "$BUILD_PIPELINE_NAME/$BUILD_JOB_NAME #$BUILD_NAME <SUCCESS>"
|
fallback: "$BUILD_PIPELINE_NAME/$BUILD_JOB_NAME #$BUILD_NAME <SUCCESS>"
|
||||||
text: "Build has succeeded"
|
text: "Build has succeeded"
|
||||||
title: "$BUILD_PIPELINE_NAME/$BUILD_JOB_NAME #$BUILD_NAME"
|
title: "$BUILD_PIPELINE_NAME/$BUILD_JOB_NAME #$BUILD_NAME"
|
||||||
title_link: $ATC_EXTERNAL_URL/teams/$BUILD_TEAM_NAME/pipelines/$BUILD_PIPELINE_NAME/jobs/$BUILD_JOB_NAME/builds/$BUILD_NAME
|
title_link: $ATC_EXTERNAL_URL/teams/$BUILD_TEAM_NAME/pipelines/$BUILD_PIPELINE_NAME/jobs/$BUILD_JOB_NAME/builds/$BUILD_NAME
|
||||||
|
|
||||||
resources:
|
resources:
|
||||||
- name: source
|
- name: source
|
||||||
type: git
|
type: git
|
||||||
source:
|
source:
|
||||||
uri: https://github.com/spring-cloud/spring-cloud-function.git
|
uri: https://github.com/spring-cloud/spring-cloud-function.git
|
||||||
- name: slack
|
- name: slack
|
||||||
type: slack-notification
|
type: slack-notification
|
||||||
source:
|
source:
|
||||||
url: {{slack-url}}
|
url: {{slack-url}}
|
||||||
|
|
||||||
resource_types:
|
resource_types:
|
||||||
- name: slack-notification
|
- name: slack-notification
|
||||||
type: docker-image
|
type: docker-image
|
||||||
source:
|
source:
|
||||||
repository: nebhale/slack-notification-resource
|
repository: nebhale/slack-notification-resource
|
||||||
|
|
||||||
|
|||||||
31
pom.xml
31
pom.xml
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<artifactId>spring-cloud-function-parent</artifactId>
|
<artifactId>spring-cloud-function-parent</artifactId>
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-build</artifactId>
|
<artifactId>spring-cloud-build</artifactId>
|
||||||
<version>2.1.3.BUILD-SNAPSHOT</version>
|
<version>2.1.3.BUILD-SNAPSHOT</version>
|
||||||
<relativePath />
|
<relativePath/>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
@@ -23,6 +23,9 @@
|
|||||||
<spring-cloud-task.version>2.1.0.RELEASE</spring-cloud-task.version>
|
<spring-cloud-task.version>2.1.0.RELEASE</spring-cloud-task.version>
|
||||||
<wrapper.version>1.0.15.RELEASE</wrapper.version>
|
<wrapper.version>1.0.15.RELEASE</wrapper.version>
|
||||||
<docs.main>spring-cloud-function</docs.main>
|
<docs.main>spring-cloud-function</docs.main>
|
||||||
|
<maven-checkstyle-plugin.failsOnError>true</maven-checkstyle-plugin.failsOnError>
|
||||||
|
<maven-checkstyle-plugin.failsOnViolation>true
|
||||||
|
</maven-checkstyle-plugin.failsOnViolation>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
@@ -91,6 +94,14 @@
|
|||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>io.spring.javaformat</groupId>
|
||||||
|
<artifactId>spring-javaformat-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
<pluginManagement>
|
<pluginManagement>
|
||||||
<plugins>
|
<plugins>
|
||||||
@@ -99,7 +110,8 @@
|
|||||||
<inherited>false</inherited>
|
<inherited>false</inherited>
|
||||||
<configuration>
|
<configuration>
|
||||||
<aggregate>true</aggregate>
|
<aggregate>true</aggregate>
|
||||||
<excludePackageNames>com.example,functions,example</excludePackageNames>
|
<excludePackageNames>com.example,functions,example
|
||||||
|
</excludePackageNames>
|
||||||
</configuration>
|
</configuration>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
@@ -216,6 +228,15 @@
|
|||||||
</pluginRepository>
|
</pluginRepository>
|
||||||
</pluginRepositories>
|
</pluginRepositories>
|
||||||
|
|
||||||
|
<reporting>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</reporting>
|
||||||
|
|
||||||
<profiles>
|
<profiles>
|
||||||
<profile>
|
<profile>
|
||||||
<id>java11+</id>
|
<id>java11+</id>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<artifactId>spring-cloud-function-adapter-parent</artifactId>
|
<artifactId>spring-cloud-function-adapter-parent</artifactId>
|
||||||
@@ -18,6 +19,6 @@
|
|||||||
<module>spring-cloud-function-adapter-aws</module>
|
<module>spring-cloud-function-adapter-aws</module>
|
||||||
<module>spring-cloud-function-adapter-openwhisk</module>
|
<module>spring-cloud-function-adapter-openwhisk</module>
|
||||||
<module>spring-cloud-function-adapter-azure</module>
|
<module>spring-cloud-function-adapter-azure</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<artifactId>spring-cloud-function-adapter-aws</artifactId>
|
<artifactId>spring-cloud-function-adapter-aws</artifactId>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2018 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -32,7 +32,6 @@ import org.springframework.messaging.MessageHeaders;
|
|||||||
import org.springframework.messaging.support.GenericMessage;
|
import org.springframework.messaging.support.GenericMessage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
* @author Oleg Zhurakousky
|
* @author Oleg Zhurakousky
|
||||||
*
|
*
|
||||||
@@ -66,12 +65,12 @@ public class SpringBootApiGatewayRequestHandler extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean functionAcceptsMessage() {
|
private boolean functionAcceptsMessage() {
|
||||||
return inspector.isMessage(function());
|
return this.inspector.isMessage(function());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object deserializeBody(String json) {
|
private Object deserializeBody(String json) {
|
||||||
try {
|
try {
|
||||||
return mapper.readValue(json, getInputType());
|
return this.mapper.readValue(json, getInputType());
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
throw new IllegalStateException("Cannot convert event", e);
|
throw new IllegalStateException("Cannot convert event", e);
|
||||||
@@ -91,13 +90,15 @@ public class SpringBootApiGatewayRequestHandler extends
|
|||||||
protected APIGatewayProxyResponseEvent convertOutput(Object output) {
|
protected APIGatewayProxyResponseEvent convertOutput(Object output) {
|
||||||
if (functionReturnsMessage(output)) {
|
if (functionReturnsMessage(output)) {
|
||||||
Message<?> message = (Message<?>) output;
|
Message<?> message = (Message<?>) output;
|
||||||
return new APIGatewayProxyResponseEvent().withStatusCode(
|
return new APIGatewayProxyResponseEvent()
|
||||||
(Integer) message.getHeaders().getOrDefault("statuscode", HttpStatus.OK.value()))
|
.withStatusCode((Integer) message.getHeaders()
|
||||||
|
.getOrDefault("statuscode", HttpStatus.OK.value()))
|
||||||
.withHeaders(toResponseHeaders(message.getHeaders()))
|
.withHeaders(toResponseHeaders(message.getHeaders()))
|
||||||
.withBody(serializeBody(message.getPayload()));
|
.withBody(serializeBody(message.getPayload()));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return new APIGatewayProxyResponseEvent().withStatusCode(HttpStatus.OK.value())
|
return new APIGatewayProxyResponseEvent()
|
||||||
|
.withStatusCode(HttpStatus.OK.value())
|
||||||
.withBody(serializeBody(output));
|
.withBody(serializeBody(output));
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -116,7 +117,7 @@ public class SpringBootApiGatewayRequestHandler extends
|
|||||||
|
|
||||||
private String serializeBody(Object body) {
|
private String serializeBody(Object body) {
|
||||||
try {
|
try {
|
||||||
return mapper.writeValueAsString(body);
|
return this.mapper.writeValueAsString(body);
|
||||||
}
|
}
|
||||||
catch (JsonProcessingException e) {
|
catch (JsonProcessingException e) {
|
||||||
throw new IllegalStateException("Cannot convert output", e);
|
throw new IllegalStateException("Cannot convert output", e);
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.function.adapter.aws;
|
package org.springframework.cloud.function.adapter.aws;
|
||||||
|
|
||||||
import static java.util.stream.Collectors.toList;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -32,7 +30,11 @@ import org.springframework.cloud.function.context.catalog.FunctionInspector;
|
|||||||
import org.springframework.messaging.Message;
|
import org.springframework.messaging.Message;
|
||||||
import org.springframework.messaging.support.GenericMessage;
|
import org.springframework.messaging.support.GenericMessage;
|
||||||
|
|
||||||
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param <E> payload type
|
||||||
|
* @param <O> response type
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
* @author Halvdan Hoem Grelland
|
* @author Halvdan Hoem Grelland
|
||||||
*/
|
*/
|
||||||
@@ -65,28 +67,26 @@ public class SpringBootKinesisEventHandler<E, O>
|
|||||||
|
|
||||||
if (functionAcceptsMessage()) {
|
if (functionAcceptsMessage()) {
|
||||||
return wrapInMessages(payloads);
|
return wrapInMessages(payloads);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
return payloads;
|
return payloads;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Message<E>> wrapInMessages(List<E> payloads) {
|
private List<Message<E>> wrapInMessages(List<E> payloads) {
|
||||||
return payloads.stream()
|
return payloads.stream().map(GenericMessage::new).collect(Collectors.toList());
|
||||||
.map(GenericMessage::new)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<E> deserializePayloads(List<KinesisEvent.KinesisEventRecord> records) {
|
private List<E> deserializePayloads(List<KinesisEvent.KinesisEventRecord> records) {
|
||||||
return RecordDeaggregator.deaggregate(records).stream()
|
return RecordDeaggregator.deaggregate(records).stream()
|
||||||
.map(this::deserializeUserRecord)
|
.map(this::deserializeUserRecord).collect(toList());
|
||||||
.collect(toList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private E deserializeUserRecord(UserRecord userRecord) {
|
private E deserializeUserRecord(UserRecord userRecord) {
|
||||||
try {
|
try {
|
||||||
byte[] jsonBytes = userRecord.getData().array();
|
byte[] jsonBytes = userRecord.getData().array();
|
||||||
return (E) mapper.readValue(jsonBytes, getInputType());
|
return (E) this.mapper.readValue(jsonBytes, getInputType());
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
throw new IllegalStateException("Cannot convert event", e);
|
throw new IllegalStateException("Cannot convert event", e);
|
||||||
@@ -94,6 +94,7 @@ public class SpringBootKinesisEventHandler<E, O>
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean functionAcceptsMessage() {
|
private boolean functionAcceptsMessage() {
|
||||||
return inspector.isMessage(function());
|
return this.inspector.isMessage(function());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -22,12 +22,12 @@ import java.util.List;
|
|||||||
|
|
||||||
import com.amazonaws.services.lambda.runtime.Context;
|
import com.amazonaws.services.lambda.runtime.Context;
|
||||||
import com.amazonaws.services.lambda.runtime.RequestHandler;
|
import com.amazonaws.services.lambda.runtime.RequestHandler;
|
||||||
|
|
||||||
import org.reactivestreams.Publisher;
|
import org.reactivestreams.Publisher;
|
||||||
|
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param <E> event type
|
||||||
|
* @param <O> result types
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
*/
|
*/
|
||||||
public class SpringBootRequestHandler<E, O> extends SpringFunctionInitializer
|
public class SpringBootRequestHandler<E, O> extends SpringFunctionInitializer
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017-1018 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -26,12 +26,11 @@ import java.util.List;
|
|||||||
import com.amazonaws.services.lambda.runtime.Context;
|
import com.amazonaws.services.lambda.runtime.Context;
|
||||||
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
|
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
import org.reactivestreams.Publisher;
|
import org.reactivestreams.Publisher;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
|
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
* @author Oleg Zhurakousky
|
* @author Oleg Zhurakousky
|
||||||
@@ -39,7 +38,7 @@ import reactor.core.publisher.Flux;
|
|||||||
public class SpringBootStreamHandler extends SpringFunctionInitializer
|
public class SpringBootStreamHandler extends SpringFunctionInitializer
|
||||||
implements RequestStreamHandler {
|
implements RequestStreamHandler {
|
||||||
|
|
||||||
@Autowired(required=false)
|
@Autowired(required = false)
|
||||||
private ObjectMapper mapper;
|
private ObjectMapper mapper;
|
||||||
|
|
||||||
public SpringBootStreamHandler() {
|
public SpringBootStreamHandler() {
|
||||||
@@ -64,7 +63,7 @@ public class SpringBootStreamHandler extends SpringFunctionInitializer
|
|||||||
initialize();
|
initialize();
|
||||||
Object value = convertStream(input);
|
Object value = convertStream(input);
|
||||||
Publisher<?> flux = apply(extract(value));
|
Publisher<?> flux = apply(extract(value));
|
||||||
mapper.writeValue(output, result(value, flux));
|
this.mapper.writeValue(output, result(value, flux));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object result(Object input, Publisher<?> flux) {
|
private Object result(Object input, Publisher<?> flux) {
|
||||||
@@ -72,7 +71,7 @@ public class SpringBootStreamHandler extends SpringFunctionInitializer
|
|||||||
for (Object value : Flux.from(flux).toIterable()) {
|
for (Object value : Flux.from(flux).toIterable()) {
|
||||||
result.add(value);
|
result.add(value);
|
||||||
}
|
}
|
||||||
if (isSingleValue(input) && result.size()==1) {
|
if (isSingleValue(input) && result.size() == 1) {
|
||||||
return result.get(0);
|
return result.get(0);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -91,10 +90,11 @@ public class SpringBootStreamHandler extends SpringFunctionInitializer
|
|||||||
|
|
||||||
private Object convertStream(InputStream input) {
|
private Object convertStream(InputStream input) {
|
||||||
try {
|
try {
|
||||||
return mapper.readValue(input, getInputType());
|
return this.mapper.readValue(input, getInputType());
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
throw new IllegalStateException("Cannot convert event", e);
|
throw new IllegalStateException("Cannot convert event", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -29,6 +29,7 @@ import java.util.jar.Manifest;
|
|||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.reactivestreams.Publisher;
|
import org.reactivestreams.Publisher;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
@@ -38,8 +39,6 @@ import org.springframework.cloud.function.context.catalog.FunctionInspector;
|
|||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
import reactor.core.publisher.Flux;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
*/
|
*/
|
||||||
@@ -69,71 +68,6 @@ public class SpringFunctionInitializer implements Closeable {
|
|||||||
this(getStartClass());
|
this(getStartClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
if (this.context != null) {
|
|
||||||
this.context.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected void initialize() {
|
|
||||||
if (!this.initialized.compareAndSet(false, true)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
logger.info("Initializing: " + configurationClass);
|
|
||||||
SpringApplication builder = springApplication();
|
|
||||||
ConfigurableApplicationContext context = builder.run();
|
|
||||||
context.getAutowireCapableBeanFactory().autowireBean(this);
|
|
||||||
String name = context.getEnvironment().getProperty("function.name");
|
|
||||||
if (name == null) {
|
|
||||||
name = "function";
|
|
||||||
}
|
|
||||||
if (this.catalog == null) {
|
|
||||||
if (context.containsBean(name)) {
|
|
||||||
this.function = context.getBean(name, Function.class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Set<String> functionNames = this.catalog.getNames(Function.class);
|
|
||||||
if (functionNames.size() == 1) {
|
|
||||||
this.function = this.catalog.lookup(Function.class,
|
|
||||||
functionNames.iterator().next());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.function = this.catalog.lookup(Function.class, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.context = context;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private SpringApplication springApplication() {
|
|
||||||
Class<?> sourceClass = configurationClass;
|
|
||||||
SpringApplication application = new org.springframework.cloud.function.context.FunctionalSpringApplication(
|
|
||||||
sourceClass);
|
|
||||||
application.setWebApplicationType(WebApplicationType.NONE);
|
|
||||||
return application;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Class<?> getInputType() {
|
|
||||||
if (inspector != null) {
|
|
||||||
return inspector.getInputType(function());
|
|
||||||
}
|
|
||||||
return Object.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Object function() {
|
|
||||||
return this.function;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Publisher<?> apply(Publisher<?> input) {
|
|
||||||
if (this.function != null) {
|
|
||||||
return Flux.from(function.apply(input));
|
|
||||||
}
|
|
||||||
throw new IllegalStateException("No function defined");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Class<?> getStartClass() {
|
private static Class<?> getStartClass() {
|
||||||
ClassLoader classLoader = SpringFunctionInitializer.class.getClassLoader();
|
ClassLoader classLoader = SpringFunctionInitializer.class.getClassLoader();
|
||||||
if (System.getenv("MAIN_CLASS") != null) {
|
if (System.getenv("MAIN_CLASS") != null) {
|
||||||
@@ -180,4 +114,69 @@ public class SpringFunctionInitializer implements Closeable {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
if (this.context != null) {
|
||||||
|
this.context.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected void initialize() {
|
||||||
|
if (!this.initialized.compareAndSet(false, true)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logger.info("Initializing: " + this.configurationClass);
|
||||||
|
SpringApplication builder = springApplication();
|
||||||
|
ConfigurableApplicationContext context = builder.run();
|
||||||
|
context.getAutowireCapableBeanFactory().autowireBean(this);
|
||||||
|
String name = context.getEnvironment().getProperty("function.name");
|
||||||
|
if (name == null) {
|
||||||
|
name = "function";
|
||||||
|
}
|
||||||
|
if (this.catalog == null) {
|
||||||
|
if (context.containsBean(name)) {
|
||||||
|
this.function = context.getBean(name, Function.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Set<String> functionNames = this.catalog.getNames(Function.class);
|
||||||
|
if (functionNames.size() == 1) {
|
||||||
|
this.function = this.catalog.lookup(Function.class,
|
||||||
|
functionNames.iterator().next());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.function = this.catalog.lookup(Function.class, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.context = context;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private SpringApplication springApplication() {
|
||||||
|
Class<?> sourceClass = this.configurationClass;
|
||||||
|
SpringApplication application = new org.springframework.cloud.function.context.FunctionalSpringApplication(
|
||||||
|
sourceClass);
|
||||||
|
application.setWebApplicationType(WebApplicationType.NONE);
|
||||||
|
return application;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Class<?> getInputType() {
|
||||||
|
if (this.inspector != null) {
|
||||||
|
return this.inspector.getInputType(function());
|
||||||
|
}
|
||||||
|
return Object.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object function() {
|
||||||
|
return this.function;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Publisher<?> apply(Publisher<?> input) {
|
||||||
|
if (this.function != null) {
|
||||||
|
return Flux.from(this.function.apply(input));
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("No function defined");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
package org.springframework.cloud.function.adapter.aws;
|
package org.springframework.cloud.function.adapter.aws;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
|
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
|
||||||
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
|
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||||
import org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration;
|
import org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@@ -11,10 +16,6 @@ import org.springframework.context.annotation.Import;
|
|||||||
import org.springframework.messaging.Message;
|
import org.springframework.messaging.Message;
|
||||||
import org.springframework.messaging.support.GenericMessage;
|
import org.springframework.messaging.support.GenericMessage;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
public class SpringBootApiGatewayRequestHandlerTests {
|
public class SpringBootApiGatewayRequestHandlerTests {
|
||||||
@@ -23,71 +24,83 @@ public class SpringBootApiGatewayRequestHandlerTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void functionBean() {
|
public void functionBean() {
|
||||||
handler = new SpringBootApiGatewayRequestHandler(FunctionConfig.class);
|
this.handler = new SpringBootApiGatewayRequestHandler(FunctionConfig.class);
|
||||||
handler.initialize();
|
this.handler.initialize();
|
||||||
|
|
||||||
APIGatewayProxyRequestEvent request = new APIGatewayProxyRequestEvent();
|
APIGatewayProxyRequestEvent request = new APIGatewayProxyRequestEvent();
|
||||||
request.setBody("{\"value\":\"foo\"}");
|
request.setBody("{\"value\":\"foo\"}");
|
||||||
|
|
||||||
Object output = handler.handleRequest(request, null);
|
Object output = this.handler.handleRequest(request, null);
|
||||||
assertThat(output).isInstanceOf(APIGatewayProxyResponseEvent.class);
|
assertThat(output).isInstanceOf(APIGatewayProxyResponseEvent.class);
|
||||||
assertThat(((APIGatewayProxyResponseEvent) output).getStatusCode()).isEqualTo(200);
|
assertThat(((APIGatewayProxyResponseEvent) output).getStatusCode())
|
||||||
assertThat(((APIGatewayProxyResponseEvent) output).getBody()).isEqualTo("{\"value\":\"FOO\"}");
|
.isEqualTo(200);
|
||||||
}
|
assertThat(((APIGatewayProxyResponseEvent) output).getBody())
|
||||||
|
.isEqualTo("{\"value\":\"FOO\"}");
|
||||||
@Configuration
|
|
||||||
@Import({ContextFunctionCatalogAutoConfiguration.class,
|
|
||||||
JacksonAutoConfiguration.class})
|
|
||||||
protected static class FunctionConfig {
|
|
||||||
@Bean
|
|
||||||
public Function<Foo, Bar> function() {
|
|
||||||
return foo -> new Bar(foo.getValue().toUpperCase());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void functionMessageBean() {
|
public void functionMessageBean() {
|
||||||
handler = new SpringBootApiGatewayRequestHandler(FunctionMessageConfig.class);
|
this.handler = new SpringBootApiGatewayRequestHandler(
|
||||||
handler.initialize();
|
FunctionMessageConfig.class);
|
||||||
|
this.handler.initialize();
|
||||||
|
|
||||||
APIGatewayProxyRequestEvent request = new APIGatewayProxyRequestEvent();
|
APIGatewayProxyRequestEvent request = new APIGatewayProxyRequestEvent();
|
||||||
request.setBody("{\"value\":\"foo\"}");
|
request.setBody("{\"value\":\"foo\"}");
|
||||||
|
|
||||||
Object output = handler.handleRequest(request, null);
|
Object output = this.handler.handleRequest(request, null);
|
||||||
assertThat(output).isInstanceOf(APIGatewayProxyResponseEvent.class);
|
assertThat(output).isInstanceOf(APIGatewayProxyResponseEvent.class);
|
||||||
assertThat(((APIGatewayProxyResponseEvent) output).getStatusCode()).isEqualTo(200);
|
assertThat(((APIGatewayProxyResponseEvent) output).getStatusCode())
|
||||||
assertThat(((APIGatewayProxyResponseEvent) output).getHeaders().get("spring")).isEqualTo("cloud");
|
.isEqualTo(200);
|
||||||
assertThat(((APIGatewayProxyResponseEvent) output).getBody()).isEqualTo("{\"value\":\"FOO\"}");
|
assertThat(((APIGatewayProxyResponseEvent) output).getHeaders().get("spring"))
|
||||||
|
.isEqualTo("cloud");
|
||||||
|
assertThat(((APIGatewayProxyResponseEvent) output).getBody())
|
||||||
|
.isEqualTo("{\"value\":\"FOO\"}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@Import({ContextFunctionCatalogAutoConfiguration.class,
|
@Import({ ContextFunctionCatalogAutoConfiguration.class,
|
||||||
JacksonAutoConfiguration.class})
|
JacksonAutoConfiguration.class })
|
||||||
|
protected static class FunctionConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Function<Foo, Bar> function() {
|
||||||
|
return foo -> new Bar(foo.getValue().toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Import({ ContextFunctionCatalogAutoConfiguration.class,
|
||||||
|
JacksonAutoConfiguration.class })
|
||||||
protected static class FunctionMessageConfig {
|
protected static class FunctionMessageConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Function<Message<Foo>, Message<Bar>> function() {
|
public Function<Message<Foo>, Message<Bar>> function() {
|
||||||
return (foo -> {
|
return (foo -> {
|
||||||
Map<String, Object> headers = Collections.singletonMap("spring", "cloud");
|
Map<String, Object> headers = Collections.singletonMap("spring", "cloud");
|
||||||
return new GenericMessage<>(
|
return new GenericMessage<>(
|
||||||
new Bar(foo.getPayload().getValue().toUpperCase()),
|
new Bar(foo.getPayload().getValue().toUpperCase()), headers);
|
||||||
headers);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class Foo {
|
protected static class Foo {
|
||||||
|
|
||||||
private String value;
|
private String value;
|
||||||
|
|
||||||
public String getValue() {
|
public String getValue() {
|
||||||
return value;
|
return this.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setValue(String value) {
|
public void setValue(String value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class Bar {
|
protected static class Bar {
|
||||||
|
|
||||||
private String value;
|
private String value;
|
||||||
|
|
||||||
public Bar() {
|
public Bar() {
|
||||||
@@ -98,11 +111,13 @@ public class SpringBootApiGatewayRequestHandlerTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getValue() {
|
public String getValue() {
|
||||||
return value;
|
return this.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setValue(String value) {
|
public void setValue(String value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -16,11 +16,6 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.function.adapter.aws;
|
package org.springframework.cloud.function.adapter.aws;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
|
||||||
import static java.util.Collections.singletonList;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.assertj.core.api.Assertions.fail;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -41,6 +36,11 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.messaging.Message;
|
import org.springframework.messaging.Message;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.fail;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Halvdan Hoem Grelland
|
* @author Halvdan Hoem Grelland
|
||||||
*/
|
*/
|
||||||
@@ -50,122 +50,6 @@ public class SpringBootKinesisEventHandlerTests {
|
|||||||
|
|
||||||
private SpringBootKinesisEventHandler<Foo, Bar> handler;
|
private SpringBootKinesisEventHandler<Foo, Bar> handler;
|
||||||
|
|
||||||
@Test
|
|
||||||
public void functionBeanHandlesKinesisEvent() throws Exception {
|
|
||||||
handler = new SpringBootKinesisEventHandler<>(FunctionConfig.class);
|
|
||||||
handler.initialize();
|
|
||||||
|
|
||||||
KinesisEvent event = asKinesisEvent(singletonList(new Foo("foo")));
|
|
||||||
|
|
||||||
List<Bar> output = handler.handleRequest(event, null);
|
|
||||||
|
|
||||||
assertThat(output).containsExactly(new Bar("FOO"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void functionBeanHandlesAggregatedKinesisEvent() throws Exception {
|
|
||||||
handler = new SpringBootKinesisEventHandler<>(FunctionConfig.class);
|
|
||||||
handler.initialize();
|
|
||||||
|
|
||||||
List<Foo> events = asList(new Foo("foo"), new Foo("bar"), new Foo("baz"));
|
|
||||||
KinesisEvent aggregatedEvent = asAggregatedKinesisEvent(events);
|
|
||||||
|
|
||||||
List<Bar> output = handler.handleRequest(aggregatedEvent, null);
|
|
||||||
|
|
||||||
assertThat(output)
|
|
||||||
.containsExactly(new Bar("FOO"), new Bar("BAR"), new Bar("BAZ"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void functionMessageBean() throws Exception {
|
|
||||||
handler = new SpringBootKinesisEventHandler<>(FunctionMessageConfig.class);
|
|
||||||
handler.initialize();
|
|
||||||
|
|
||||||
KinesisEvent event = asKinesisEvent(asList(new Foo("foo"), new Foo("bar")));
|
|
||||||
|
|
||||||
List<Bar> output = handler.handleRequest(event, null);
|
|
||||||
|
|
||||||
assertThat(output)
|
|
||||||
.containsExactly(new Bar("FOO"), new Bar("BAR"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@Import({
|
|
||||||
ContextFunctionCatalogAutoConfiguration.class,
|
|
||||||
JacksonAutoConfiguration.class
|
|
||||||
})
|
|
||||||
protected static class FunctionConfig {
|
|
||||||
@Bean
|
|
||||||
public Function<Foo, Bar> function() {
|
|
||||||
return foo -> new Bar(foo.getValue().toUpperCase());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@Import({
|
|
||||||
ContextFunctionCatalogAutoConfiguration.class,
|
|
||||||
JacksonAutoConfiguration.class
|
|
||||||
})
|
|
||||||
protected static class FunctionMessageConfig {
|
|
||||||
@Bean
|
|
||||||
public Function<Message<Foo>, Bar> function() {
|
|
||||||
return foo -> new Bar(foo.getPayload().getValue().toUpperCase());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static class Foo {
|
|
||||||
|
|
||||||
private String value;
|
|
||||||
|
|
||||||
public Foo() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public Foo(String value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(String value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static class Bar {
|
|
||||||
|
|
||||||
private String value;
|
|
||||||
|
|
||||||
public Bar() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public Bar(String value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(String value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
Bar bar = (Bar) o;
|
|
||||||
return Objects.equals(value, bar.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static KinesisEvent asKinesisEvent(List<Object> payloads) {
|
private static KinesisEvent asKinesisEvent(List<Object> payloads) {
|
||||||
KinesisEvent kinesisEvent = new KinesisEvent();
|
KinesisEvent kinesisEvent = new KinesisEvent();
|
||||||
|
|
||||||
@@ -175,8 +59,7 @@ public class SpringBootKinesisEventHandlerTests {
|
|||||||
KinesisEvent.Record record = new KinesisEvent.Record();
|
KinesisEvent.Record record = new KinesisEvent.Record();
|
||||||
record.setData(asJsonByteBuffer(payload));
|
record.setData(asJsonByteBuffer(payload));
|
||||||
|
|
||||||
KinesisEvent.KinesisEventRecord kinesisEventRecord =
|
KinesisEvent.KinesisEventRecord kinesisEventRecord = new KinesisEvent.KinesisEventRecord();
|
||||||
new KinesisEvent.KinesisEventRecord();
|
|
||||||
kinesisEventRecord.setKinesis(record);
|
kinesisEventRecord.setKinesis(record);
|
||||||
|
|
||||||
kinesisEventRecords.add(kinesisEventRecord);
|
kinesisEventRecords.add(kinesisEventRecord);
|
||||||
@@ -190,12 +73,12 @@ public class SpringBootKinesisEventHandlerTests {
|
|||||||
private static KinesisEvent asAggregatedKinesisEvent(List<?> payloads) {
|
private static KinesisEvent asAggregatedKinesisEvent(List<?> payloads) {
|
||||||
RecordAggregator aggregator = new RecordAggregator();
|
RecordAggregator aggregator = new RecordAggregator();
|
||||||
|
|
||||||
payloads.stream()
|
payloads.stream().map(SpringBootKinesisEventHandlerTests::asJsonByteBuffer)
|
||||||
.map(SpringBootKinesisEventHandlerTests::asJsonByteBuffer)
|
|
||||||
.forEach(buffer -> {
|
.forEach(buffer -> {
|
||||||
try {
|
try {
|
||||||
aggregator.addUserRecord("fakePartitionKey", buffer.array());
|
aggregator.addUserRecord("fakePartitionKey", buffer.array());
|
||||||
} catch (Exception e) {
|
}
|
||||||
|
catch (Exception e) {
|
||||||
fail("Creating aggregated record failed");
|
fail("Creating aggregated record failed");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -218,9 +101,132 @@ public class SpringBootKinesisEventHandlerTests {
|
|||||||
private static ByteBuffer asJsonByteBuffer(Object object) {
|
private static ByteBuffer asJsonByteBuffer(Object object) {
|
||||||
try {
|
try {
|
||||||
return ByteBuffer.wrap(mapper.writeValueAsBytes(object));
|
return ByteBuffer.wrap(mapper.writeValueAsBytes(object));
|
||||||
} catch (JsonProcessingException e) {
|
}
|
||||||
|
catch (JsonProcessingException e) {
|
||||||
fail("Setting up test data failed", e);
|
fail("Setting up test data failed", e);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void functionBeanHandlesKinesisEvent() throws Exception {
|
||||||
|
this.handler = new SpringBootKinesisEventHandler<>(FunctionConfig.class);
|
||||||
|
this.handler.initialize();
|
||||||
|
|
||||||
|
KinesisEvent event = asKinesisEvent(singletonList(new Foo("foo")));
|
||||||
|
|
||||||
|
List<Bar> output = this.handler.handleRequest(event, null);
|
||||||
|
|
||||||
|
assertThat(output).containsExactly(new Bar("FOO"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void functionBeanHandlesAggregatedKinesisEvent() throws Exception {
|
||||||
|
this.handler = new SpringBootKinesisEventHandler<>(FunctionConfig.class);
|
||||||
|
this.handler.initialize();
|
||||||
|
|
||||||
|
List<Foo> events = asList(new Foo("foo"), new Foo("bar"), new Foo("baz"));
|
||||||
|
KinesisEvent aggregatedEvent = asAggregatedKinesisEvent(events);
|
||||||
|
|
||||||
|
List<Bar> output = this.handler.handleRequest(aggregatedEvent, null);
|
||||||
|
|
||||||
|
assertThat(output).containsExactly(new Bar("FOO"), new Bar("BAR"),
|
||||||
|
new Bar("BAZ"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void functionMessageBean() throws Exception {
|
||||||
|
this.handler = new SpringBootKinesisEventHandler<>(FunctionMessageConfig.class);
|
||||||
|
this.handler.initialize();
|
||||||
|
|
||||||
|
KinesisEvent event = asKinesisEvent(asList(new Foo("foo"), new Foo("bar")));
|
||||||
|
|
||||||
|
List<Bar> output = this.handler.handleRequest(event, null);
|
||||||
|
|
||||||
|
assertThat(output).containsExactly(new Bar("FOO"), new Bar("BAR"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Import({ ContextFunctionCatalogAutoConfiguration.class,
|
||||||
|
JacksonAutoConfiguration.class })
|
||||||
|
protected static class FunctionConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Function<Foo, Bar> function() {
|
||||||
|
return foo -> new Bar(foo.getValue().toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Import({ ContextFunctionCatalogAutoConfiguration.class,
|
||||||
|
JacksonAutoConfiguration.class })
|
||||||
|
protected static class FunctionMessageConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Function<Message<Foo>, Bar> function() {
|
||||||
|
return foo -> new Bar(foo.getPayload().getValue().toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static class Foo {
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
public Foo() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Foo(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static class Bar {
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
public Bar() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bar(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Bar bar = (Bar) o;
|
||||||
|
return Objects.equals(this.value, bar.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(this.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -38,9 +38,9 @@ public class SpringBootRequestHandlerTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void functionBean() throws Exception {
|
public void functionBean() throws Exception {
|
||||||
handler = new SpringBootRequestHandler<Foo, Bar>(FunctionConfig.class);
|
this.handler = new SpringBootRequestHandler<Foo, Bar>(FunctionConfig.class);
|
||||||
handler.initialize();
|
this.handler.initialize();
|
||||||
Object output = handler.handleRequest(new Foo("foo"), null);
|
Object output = this.handler.handleRequest(new Foo("foo"), null);
|
||||||
assertThat(output).isInstanceOf(Bar.class);
|
assertThat(output).isInstanceOf(Bar.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,13 +48,16 @@ public class SpringBootRequestHandlerTests {
|
|||||||
@Import({ ContextFunctionCatalogAutoConfiguration.class,
|
@Import({ ContextFunctionCatalogAutoConfiguration.class,
|
||||||
JacksonAutoConfiguration.class })
|
JacksonAutoConfiguration.class })
|
||||||
protected static class FunctionConfig {
|
protected static class FunctionConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Function<Foo, Bar> function() {
|
public Function<Foo, Bar> function() {
|
||||||
return foo -> new Bar(foo.getValue().toUpperCase());
|
return foo -> new Bar(foo.getValue().toUpperCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class Foo {
|
protected static class Foo {
|
||||||
|
|
||||||
private String value;
|
private String value;
|
||||||
|
|
||||||
public Foo(String value) {
|
public Foo(String value) {
|
||||||
@@ -62,15 +65,17 @@ public class SpringBootRequestHandlerTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getValue() {
|
public String getValue() {
|
||||||
return value;
|
return this.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setValue(String value) {
|
public void setValue(String value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class Bar {
|
protected static class Bar {
|
||||||
|
|
||||||
private String value;
|
private String value;
|
||||||
|
|
||||||
public Bar() {
|
public Bar() {
|
||||||
@@ -81,11 +86,13 @@ public class SpringBootRequestHandlerTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getValue() {
|
public String getValue() {
|
||||||
return value;
|
return this.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setValue(String value) {
|
public void setValue(String value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2018 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -40,21 +40,21 @@ public class SpringBootStreamHandlerTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void functionBeanWithJacksonConfig() throws Exception {
|
public void functionBeanWithJacksonConfig() throws Exception {
|
||||||
handler = new SpringBootStreamHandler(FunctionConfigWithJackson.class);
|
this.handler = new SpringBootStreamHandler(FunctionConfigWithJackson.class);
|
||||||
handler.initialize();
|
this.handler.initialize();
|
||||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||||
handler.handleRequest(new ByteArrayInputStream("{\"value\":\"foo\"}".getBytes()),
|
this.handler.handleRequest(
|
||||||
output, null);
|
new ByteArrayInputStream("{\"value\":\"foo\"}".getBytes()), output, null);
|
||||||
assertThat(output.toString()).isEqualTo("{\"value\":\"FOO\"}");
|
assertThat(output.toString()).isEqualTo("{\"value\":\"FOO\"}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void functionBeanWithoutJacksonConfig() throws Exception {
|
public void functionBeanWithoutJacksonConfig() throws Exception {
|
||||||
handler = new SpringBootStreamHandler(FunctionConfigWithoutJackson.class);
|
this.handler = new SpringBootStreamHandler(FunctionConfigWithoutJackson.class);
|
||||||
handler.initialize();
|
this.handler.initialize();
|
||||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||||
handler.handleRequest(new ByteArrayInputStream("{\"value\":\"foo\"}".getBytes()),
|
this.handler.handleRequest(
|
||||||
output, null);
|
new ByteArrayInputStream("{\"value\":\"foo\"}".getBytes()), output, null);
|
||||||
assertThat(output.toString()).isEqualTo("{\"value\":\"FOO\"}");
|
assertThat(output.toString()).isEqualTo("{\"value\":\"FOO\"}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,34 +62,41 @@ public class SpringBootStreamHandlerTests {
|
|||||||
@Import({ ContextFunctionCatalogAutoConfiguration.class,
|
@Import({ ContextFunctionCatalogAutoConfiguration.class,
|
||||||
JacksonAutoConfiguration.class })
|
JacksonAutoConfiguration.class })
|
||||||
protected static class FunctionConfigWithJackson {
|
protected static class FunctionConfigWithJackson {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Function<Foo, Bar> function() {
|
public Function<Foo, Bar> function() {
|
||||||
return foo -> new Bar(foo.getValue().toUpperCase());
|
return foo -> new Bar(foo.getValue().toUpperCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@Import({ ContextFunctionCatalogAutoConfiguration.class})
|
@Import({ ContextFunctionCatalogAutoConfiguration.class })
|
||||||
protected static class FunctionConfigWithoutJackson {
|
protected static class FunctionConfigWithoutJackson {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Function<Foo, Bar> function() {
|
public Function<Foo, Bar> function() {
|
||||||
return foo -> new Bar(foo.getValue().toUpperCase());
|
return foo -> new Bar(foo.getValue().toUpperCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class Foo {
|
protected static class Foo {
|
||||||
|
|
||||||
private String value;
|
private String value;
|
||||||
|
|
||||||
public String getValue() {
|
public String getValue() {
|
||||||
return value;
|
return this.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setValue(String value) {
|
public void setValue(String value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class Bar {
|
protected static class Bar {
|
||||||
|
|
||||||
private String value;
|
private String value;
|
||||||
|
|
||||||
public Bar() {
|
public Bar() {
|
||||||
@@ -100,11 +107,13 @@ public class SpringBootStreamHandlerTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getValue() {
|
public String getValue() {
|
||||||
return value;
|
return this.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setValue(String value) {
|
public void setValue(String value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -23,6 +23,7 @@ import java.util.stream.Collectors;
|
|||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
import org.springframework.cloud.function.context.FunctionRegistration;
|
import org.springframework.cloud.function.context.FunctionRegistration;
|
||||||
import org.springframework.cloud.function.context.FunctionType;
|
import org.springframework.cloud.function.context.FunctionType;
|
||||||
@@ -35,8 +36,6 @@ import org.springframework.context.support.GenericApplicationContext;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
import reactor.core.publisher.Flux;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
*
|
*
|
||||||
@@ -48,74 +47,78 @@ public class SpringFunctionInitializerTests {
|
|||||||
@After
|
@After
|
||||||
public void after() {
|
public void after() {
|
||||||
System.clearProperty("function.name");
|
System.clearProperty("function.name");
|
||||||
if (initializer != null) {
|
if (this.initializer != null) {
|
||||||
initializer.close();
|
this.initializer.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void functionBean() {
|
public void functionBean() {
|
||||||
initializer = new SpringFunctionInitializer(FluxFunctionConfig.class);
|
this.initializer = new SpringFunctionInitializer(FluxFunctionConfig.class);
|
||||||
initializer.initialize();
|
this.initializer.initialize();
|
||||||
Flux<?> result = Flux.from(initializer.apply(Flux.just(new Foo())));
|
Flux<?> result = Flux.from(this.initializer.apply(Flux.just(new Foo())));
|
||||||
assertThat(result.blockFirst()).isInstanceOf(Bar.class);
|
assertThat(result.blockFirst()).isInstanceOf(Bar.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void functionApp() {
|
public void functionApp() {
|
||||||
initializer = new SpringFunctionInitializer(FluxFunctionApp.class);
|
this.initializer = new SpringFunctionInitializer(FluxFunctionApp.class);
|
||||||
initializer.initialize();
|
this.initializer.initialize();
|
||||||
Flux<?> result = Flux.from(initializer.apply(Flux.just(new Foo())));
|
Flux<?> result = Flux.from(this.initializer.apply(Flux.just(new Foo())));
|
||||||
assertThat(result.blockFirst()).isInstanceOf(Bar.class);
|
assertThat(result.blockFirst()).isInstanceOf(Bar.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void functionCatalog() {
|
public void functionCatalog() {
|
||||||
initializer = new SpringFunctionInitializer(FunctionConfig.class);
|
this.initializer = new SpringFunctionInitializer(FunctionConfig.class);
|
||||||
initializer.initialize();
|
this.initializer.initialize();
|
||||||
Flux<?> result = Flux.from(initializer.apply(Flux.just(new Foo())));
|
Flux<?> result = Flux.from(this.initializer.apply(Flux.just(new Foo())));
|
||||||
assertThat(result.blockFirst()).isInstanceOf(Bar.class);
|
assertThat(result.blockFirst()).isInstanceOf(Bar.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore // related to boot 2.1 no bean override change
|
@Ignore // related to boot 2.1 no bean override change
|
||||||
public void functionRegistrar() {
|
public void functionRegistrar() {
|
||||||
initializer = new SpringFunctionInitializer(FunctionRegistrar.class);
|
this.initializer = new SpringFunctionInitializer(FunctionRegistrar.class);
|
||||||
initializer.initialize();
|
this.initializer.initialize();
|
||||||
Flux<?> result = Flux.from(initializer.apply(Flux.just(new Foo())));
|
Flux<?> result = Flux.from(this.initializer.apply(Flux.just(new Foo())));
|
||||||
assertThat(result.blockFirst()).isInstanceOf(Bar.class);
|
assertThat(result.blockFirst()).isInstanceOf(Bar.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void namedFunctionCatalog() {
|
public void namedFunctionCatalog() {
|
||||||
initializer = new SpringFunctionInitializer(NamedFunctionConfig.class);
|
this.initializer = new SpringFunctionInitializer(NamedFunctionConfig.class);
|
||||||
System.setProperty("function.name", "other");
|
System.setProperty("function.name", "other");
|
||||||
initializer.initialize();
|
this.initializer.initialize();
|
||||||
Flux<?> result = Flux.from(initializer.apply(Flux.just(new Foo())));
|
Flux<?> result = Flux.from(this.initializer.apply(Flux.just(new Foo())));
|
||||||
assertThat(result.blockFirst()).isInstanceOf(Bar.class);
|
assertThat(result.blockFirst()).isInstanceOf(Bar.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void consumerCatalog() {
|
public void consumerCatalog() {
|
||||||
initializer = new SpringFunctionInitializer(ConsumerConfig.class);
|
this.initializer = new SpringFunctionInitializer(ConsumerConfig.class);
|
||||||
initializer.initialize();
|
this.initializer.initialize();
|
||||||
Flux<?> result = Flux.from(initializer.apply(Flux.just(new Foo())));
|
Flux<?> result = Flux.from(this.initializer.apply(Flux.just(new Foo())));
|
||||||
assertThat(result.toStream().collect(Collectors.toList())).isEmpty();
|
assertThat(result.toStream().collect(Collectors.toList())).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
protected static class FluxFunctionConfig {
|
protected static class FluxFunctionConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Function<Flux<Foo>, Flux<Bar>> function() {
|
public Function<Flux<Foo>, Flux<Bar>> function() {
|
||||||
return flux -> flux.map(foo -> new Bar());
|
return flux -> flux.map(foo -> new Bar());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class FluxFunctionApp implements Function<Flux<Foo>, Flux<Bar>> {
|
protected static class FluxFunctionApp implements Function<Flux<Foo>, Flux<Bar>> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Flux<Bar> apply(Flux<Foo> flux) {
|
public Flux<Bar> apply(Flux<Foo> flux) {
|
||||||
return flux.map(foo -> new Bar());
|
return flux.map(foo -> new Bar());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class FunctionRegistrar
|
protected static class FunctionRegistrar
|
||||||
@@ -133,39 +136,49 @@ public class SpringFunctionInitializerTests {
|
|||||||
.type(FunctionType.from(Foo.class).to(Bar.class)
|
.type(FunctionType.from(Foo.class).to(Bar.class)
|
||||||
.wrap(Flux.class).getType()));
|
.wrap(Flux.class).getType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@Import(ContextFunctionCatalogAutoConfiguration.class)
|
@Import(ContextFunctionCatalogAutoConfiguration.class)
|
||||||
protected static class FunctionConfig {
|
protected static class FunctionConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Function<Foo, Bar> function() {
|
public Function<Foo, Bar> function() {
|
||||||
return foo -> new Bar();
|
return foo -> new Bar();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@Import(ContextFunctionCatalogAutoConfiguration.class)
|
@Import(ContextFunctionCatalogAutoConfiguration.class)
|
||||||
protected static class NamedFunctionConfig {
|
protected static class NamedFunctionConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Function<Foo, Bar> other() {
|
public Function<Foo, Bar> other() {
|
||||||
return foo -> new Bar();
|
return foo -> new Bar();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@Import(ContextFunctionCatalogAutoConfiguration.class)
|
@Import(ContextFunctionCatalogAutoConfiguration.class)
|
||||||
protected static class ConsumerConfig {
|
protected static class ConsumerConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Consumer<Foo> consumer() {
|
public Consumer<Foo> consumer() {
|
||||||
return foo -> {
|
return foo -> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class Foo {
|
protected static class Foo {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class Bar {
|
protected static class Bar {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<artifactId>spring-cloud-function-adapter-azure</artifactId>
|
<artifactId>spring-cloud-function-adapter-azure</artifactId>
|
||||||
@@ -20,7 +20,8 @@
|
|||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
<java.version>1.8</java.version>
|
<java.version>1.8</java.version>
|
||||||
<azure.functions.java.core.version>1.0.0-beta-5</azure.functions.java.core.version>
|
<azure.functions.java.core.version>1.0.0-beta-5
|
||||||
|
</azure.functions.java.core.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -24,12 +24,12 @@ import java.util.function.Function;
|
|||||||
|
|
||||||
import com.microsoft.azure.functions.ExecutionContext;
|
import com.microsoft.azure.functions.ExecutionContext;
|
||||||
import com.microsoft.azure.functions.OutputBinding;
|
import com.microsoft.azure.functions.OutputBinding;
|
||||||
|
|
||||||
import org.reactivestreams.Publisher;
|
import org.reactivestreams.Publisher;
|
||||||
|
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param <I> input type
|
||||||
|
* @param <O> result type
|
||||||
* @author Soby Chacko
|
* @author Soby Chacko
|
||||||
*/
|
*/
|
||||||
public class AzureSpringBootRequestHandler<I, O> extends AzureSpringFunctionInitializer {
|
public class AzureSpringBootRequestHandler<I, O> extends AzureSpringFunctionInitializer {
|
||||||
@@ -118,4 +118,5 @@ public class AzureSpringBootRequestHandler<I, O> extends AzureSpringFunctionInit
|
|||||||
protected O convertOutput(Object output) {
|
protected O convertOutput(Object output) {
|
||||||
return (O) output;
|
return (O) output;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -29,7 +29,6 @@ import java.util.function.Function;
|
|||||||
import java.util.jar.Manifest;
|
import java.util.jar.Manifest;
|
||||||
|
|
||||||
import com.microsoft.azure.functions.ExecutionContext;
|
import com.microsoft.azure.functions.ExecutionContext;
|
||||||
|
|
||||||
import org.reactivestreams.Publisher;
|
import org.reactivestreams.Publisher;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -46,20 +45,20 @@ import org.springframework.util.ClassUtils;
|
|||||||
*/
|
*/
|
||||||
public class AzureSpringFunctionInitializer implements Closeable {
|
public class AzureSpringFunctionInitializer implements Closeable {
|
||||||
|
|
||||||
|
private volatile static ConfigurableApplicationContext context;
|
||||||
|
|
||||||
|
private final Class<?> configurationClass;
|
||||||
|
|
||||||
private volatile Function<Publisher<?>, Publisher<?>> function;
|
private volatile Function<Publisher<?>, Publisher<?>> function;
|
||||||
|
|
||||||
private AtomicBoolean initialized = new AtomicBoolean();
|
private AtomicBoolean initialized = new AtomicBoolean();
|
||||||
|
|
||||||
private final Class<?> configurationClass;
|
|
||||||
|
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
private volatile FunctionCatalog catalog;
|
private volatile FunctionCatalog catalog;
|
||||||
|
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
private volatile FunctionInspector inspector;
|
private volatile FunctionInspector inspector;
|
||||||
|
|
||||||
private volatile static ConfigurableApplicationContext context;
|
|
||||||
|
|
||||||
public AzureSpringFunctionInitializer(Class<?> configurationClass) {
|
public AzureSpringFunctionInitializer(Class<?> configurationClass) {
|
||||||
this.configurationClass = configurationClass == null ? getClass()
|
this.configurationClass = configurationClass == null ? getClass()
|
||||||
: configurationClass;
|
: configurationClass;
|
||||||
@@ -69,6 +68,55 @@ public class AzureSpringFunctionInitializer implements Closeable {
|
|||||||
this(getStartClass());
|
this(getStartClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Class<?> getStartClass() {
|
||||||
|
ClassLoader classLoader = org.springframework.cloud.function.adapter.azure.AzureSpringFunctionInitializer.class
|
||||||
|
.getClassLoader();
|
||||||
|
if (System.getenv("MAIN_CLASS") != null) {
|
||||||
|
return ClassUtils.resolveClassName(System.getenv("MAIN_CLASS"), classLoader);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Class<?> result = getStartClass(
|
||||||
|
Collections.list(classLoader.getResources("META-INF/MANIFEST.MF")));
|
||||||
|
if (result == null) {
|
||||||
|
result = getStartClass(Collections
|
||||||
|
.list(classLoader.getResources("meta-inf/manifest.mf")));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Class<?> getStartClass(List<URL> list) {
|
||||||
|
for (URL url : list) {
|
||||||
|
try {
|
||||||
|
InputStream inputStream = url.openStream();
|
||||||
|
try {
|
||||||
|
Manifest manifest = new Manifest(inputStream);
|
||||||
|
String startClass = manifest.getMainAttributes()
|
||||||
|
.getValue("Main-Class");
|
||||||
|
if (startClass != null) {
|
||||||
|
Class<?> aClass = ClassUtils.forName(startClass,
|
||||||
|
org.springframework.cloud.function.adapter.azure.AzureSpringFunctionInitializer.class
|
||||||
|
.getClassLoader());
|
||||||
|
SpringBootApplication declaredAnnotation = aClass
|
||||||
|
.getDeclaredAnnotation(SpringBootApplication.class);
|
||||||
|
if (declaredAnnotation != null) {
|
||||||
|
return aClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
inputStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
if (AzureSpringFunctionInitializer.context != null) {
|
if (AzureSpringFunctionInitializer.context != null) {
|
||||||
@@ -132,7 +180,7 @@ public class AzureSpringFunctionInitializer implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private SpringApplication springApplication() {
|
private SpringApplication springApplication() {
|
||||||
Class<?> sourceClass = configurationClass;
|
Class<?> sourceClass = this.configurationClass;
|
||||||
SpringApplication application = new org.springframework.cloud.function.context.FunctionalSpringApplication(
|
SpringApplication application = new org.springframework.cloud.function.context.FunctionalSpringApplication(
|
||||||
sourceClass);
|
sourceClass);
|
||||||
application.setWebApplicationType(WebApplicationType.NONE);
|
application.setWebApplicationType(WebApplicationType.NONE);
|
||||||
@@ -144,7 +192,8 @@ public class AzureSpringFunctionInitializer implements Closeable {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (this.inspector != null) {
|
if (this.inspector != null) {
|
||||||
return Collection.class.isAssignableFrom(inspector.getInputType(function));
|
return Collection.class
|
||||||
|
.isAssignableFrom(this.inspector.getInputType(function));
|
||||||
}
|
}
|
||||||
return ((Collection<?>) input).size() <= 1;
|
return ((Collection<?>) input).size() <= 1;
|
||||||
}
|
}
|
||||||
@@ -154,7 +203,8 @@ public class AzureSpringFunctionInitializer implements Closeable {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (this.inspector != null) {
|
if (this.inspector != null) {
|
||||||
return Collection.class.isAssignableFrom(inspector.getOutputType(function));
|
return Collection.class
|
||||||
|
.isAssignableFrom(this.inspector.getOutputType(function));
|
||||||
}
|
}
|
||||||
return ((Collection<?>) output).size() <= 1;
|
return ((Collection<?>) output).size() <= 1;
|
||||||
}
|
}
|
||||||
@@ -174,64 +224,16 @@ public class AzureSpringFunctionInitializer implements Closeable {
|
|||||||
throw new IllegalStateException("No function defined with name=" + name);
|
throw new IllegalStateException("No function defined with name=" + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Class<?> getStartClass() {
|
|
||||||
ClassLoader classLoader = org.springframework.cloud.function.adapter.azure.AzureSpringFunctionInitializer.class
|
|
||||||
.getClassLoader();
|
|
||||||
if (System.getenv("MAIN_CLASS") != null) {
|
|
||||||
return ClassUtils.resolveClassName(System.getenv("MAIN_CLASS"), classLoader);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
Class<?> result = getStartClass(
|
|
||||||
Collections.list(classLoader.getResources("META-INF/MANIFEST.MF")));
|
|
||||||
if (result == null) {
|
|
||||||
result = getStartClass(Collections
|
|
||||||
.list(classLoader.getResources("meta-inf/manifest.mf")));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Class<?> getStartClass(List<URL> list) {
|
|
||||||
for (URL url : list) {
|
|
||||||
try {
|
|
||||||
InputStream inputStream = url.openStream();
|
|
||||||
try {
|
|
||||||
Manifest manifest = new Manifest(inputStream);
|
|
||||||
String startClass = manifest.getMainAttributes()
|
|
||||||
.getValue("Main-Class");
|
|
||||||
if (startClass != null) {
|
|
||||||
Class<?> aClass = ClassUtils.forName(startClass,
|
|
||||||
org.springframework.cloud.function.adapter.azure.AzureSpringFunctionInitializer.class
|
|
||||||
.getClassLoader());
|
|
||||||
SpringBootApplication declaredAnnotation = aClass
|
|
||||||
.getDeclaredAnnotation(SpringBootApplication.class);
|
|
||||||
if (declaredAnnotation != null) {
|
|
||||||
return aClass;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
inputStream.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Function<Publisher<?>, Publisher<?>> getFunction() {
|
public Function<Publisher<?>, Publisher<?>> getFunction() {
|
||||||
return function;
|
return this.function;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionCatalog getCatalog() {
|
public FunctionCatalog getCatalog() {
|
||||||
return catalog;
|
return this.catalog;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionInspector getInspector() {
|
public FunctionInspector getInspector() {
|
||||||
return inspector;
|
return this.inspector;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2018 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -24,6 +24,7 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@@ -31,8 +32,6 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
import reactor.core.publisher.Flux;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
*
|
*
|
||||||
@@ -114,50 +113,60 @@ public class AzureSpringBootRequestHandlerTests {
|
|||||||
|
|
||||||
@After
|
@After
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
if (handler != null)
|
if (this.handler != null) {
|
||||||
handler.close();
|
this.handler.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
protected static class BareConfig {
|
protected static class BareConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Function<Flux<Foo>, Flux<Bar>> function() {
|
public Function<Flux<Foo>, Flux<Bar>> function() {
|
||||||
return foos -> foos.map(foo -> new Bar(foo.getValue().toUpperCase()));
|
return foos -> foos.map(foo -> new Bar(foo.getValue().toUpperCase()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
protected static class AutoConfig {
|
protected static class AutoConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Function<Foo, Bar> uppercase() {
|
public Function<Foo, Bar> uppercase() {
|
||||||
return foo -> new Bar(foo.getValue().toUpperCase());
|
return foo -> new Bar(foo.getValue().toUpperCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
protected static class ListConfig {
|
protected static class ListConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Function<List<Foo>, List<Bar>> uppercase() {
|
public Function<List<Foo>, List<Bar>> uppercase() {
|
||||||
return foos -> foos.stream().map(foo -> new Bar(foo.getValue().toUpperCase()))
|
return foos -> foos.stream().map(foo -> new Bar(foo.getValue().toUpperCase()))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
protected static class CollectConfig {
|
protected static class CollectConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Function<List<Foo>, Bar> uppercase() {
|
public Function<List<Foo>, Bar> uppercase() {
|
||||||
return foos -> new Bar(foos.stream().map(foo -> foo.getValue().toUpperCase())
|
return foos -> new Bar(foos.stream().map(foo -> foo.getValue().toUpperCase())
|
||||||
.collect(Collectors.joining(",")));
|
.collect(Collectors.joining(",")));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
protected static class MultiConfig {
|
protected static class MultiConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Function<Foo, Bar> uppercase() {
|
public Function<Foo, Bar> uppercase() {
|
||||||
return foo -> new Bar(foo.getValue().toUpperCase());
|
return foo -> new Bar(foo.getValue().toUpperCase());
|
||||||
@@ -167,6 +176,7 @@ public class AzureSpringBootRequestHandlerTests {
|
|||||||
public Function<Bar, Foo> lowercase() {
|
public Function<Bar, Foo> lowercase() {
|
||||||
return bar -> new Foo(bar.getValue().toLowerCase());
|
return bar -> new Foo(bar.getValue().toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -178,25 +188,26 @@ class Foo {
|
|||||||
Foo() {
|
Foo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String lowercase() {
|
|
||||||
return value.toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Foo(String value) {
|
public Foo(String value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String lowercase() {
|
||||||
|
return this.value.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
public String uppercase() {
|
public String uppercase() {
|
||||||
return value.toUpperCase();
|
return this.value.toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getValue() {
|
public String getValue() {
|
||||||
return value;
|
return this.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setValue(String value) {
|
public void setValue(String value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Bar {
|
class Bar {
|
||||||
@@ -211,7 +222,7 @@ class Bar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getValue() {
|
public String getValue() {
|
||||||
return value;
|
return this.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setValue(String value) {
|
public void setValue(String value) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -20,6 +20,7 @@ import java.util.function.Function;
|
|||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
import org.springframework.boot.SpringBootConfiguration;
|
import org.springframework.boot.SpringBootConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
@@ -31,8 +32,6 @@ import org.springframework.context.support.GenericApplicationContext;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
import reactor.core.publisher.Flux;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
*
|
*
|
||||||
@@ -50,8 +49,9 @@ public class AzureSpringFunctionInitializerTests {
|
|||||||
|
|
||||||
@After
|
@After
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
if (handler != null)
|
if (this.handler != null) {
|
||||||
handler.close();
|
this.handler.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -84,18 +84,22 @@ public class AzureSpringFunctionInitializerTests {
|
|||||||
@SpringBootConfiguration
|
@SpringBootConfiguration
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
protected static class BareConfig {
|
protected static class BareConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Function<Foo, Bar> function() {
|
public Function<Foo, Bar> function() {
|
||||||
return foo -> new Bar(foo.getValue().toUpperCase());
|
return foo -> new Bar(foo.getValue().toUpperCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SpringBootConfiguration
|
@SpringBootConfiguration
|
||||||
protected static class FunctionConfig implements Function<Foo, Bar> {
|
protected static class FunctionConfig implements Function<Foo, Bar> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Bar apply(Foo foo) {
|
public Bar apply(Foo foo) {
|
||||||
return new Bar(foo.getValue().toUpperCase());
|
return new Bar(foo.getValue().toUpperCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SpringBootConfiguration
|
@SpringBootConfiguration
|
||||||
@@ -113,5 +117,7 @@ public class AzureSpringFunctionInitializerTests {
|
|||||||
"uppercase")
|
"uppercase")
|
||||||
.type(FunctionType.from(Foo.class).to(Bar.class)));
|
.type(FunctionType.from(Foo.class).to(Bar.class)));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2018 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -22,6 +22,7 @@ import java.util.logging.Logger;
|
|||||||
import com.microsoft.azure.functions.ExecutionContext;
|
import com.microsoft.azure.functions.ExecutionContext;
|
||||||
|
|
||||||
public class TestExecutionContext implements ExecutionContext {
|
public class TestExecutionContext implements ExecutionContext {
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
public TestExecutionContext(String name) {
|
public TestExecutionContext(String name) {
|
||||||
@@ -40,6 +41,7 @@ public class TestExecutionContext implements ExecutionContext {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getFunctionName() {
|
public String getFunctionName() {
|
||||||
return name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<artifactId>spring-cloud-function-adapter-openwhisk</artifactId>
|
<artifactId>spring-cloud-function-adapter-openwhisk</artifactId>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -29,7 +29,7 @@ public class FunctionProperties {
|
|||||||
private String type = "function";
|
private String type = "function";
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
@@ -37,10 +37,11 @@ public class FunctionProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getType() {
|
public String getType() {
|
||||||
return type;
|
return this.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setType(String type) {
|
public void setType(String type) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -23,6 +23,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
|||||||
/**
|
/**
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
*/
|
*/
|
||||||
|
// @checkstyle:off
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableConfigurationProperties(FunctionProperties.class)
|
@EnableConfigurationProperties(FunctionProperties.class)
|
||||||
public class OpenWhiskActionApplication {
|
public class OpenWhiskActionApplication {
|
||||||
@@ -30,4 +31,6 @@ public class OpenWhiskActionApplication {
|
|||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(OpenWhiskActionApplication.class, args);
|
SpringApplication.run(OpenWhiskActionApplication.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// @checkstyle:on
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -23,18 +23,16 @@ import java.util.Map;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.reactivestreams.Publisher;
|
import org.reactivestreams.Publisher;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import reactor.core.publisher.Flux;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
* @author Kamesh Sampath
|
* @author Kamesh Sampath
|
||||||
@@ -43,6 +41,7 @@ import reactor.core.publisher.Flux;
|
|||||||
public class OpenWhiskActionHandler extends OpenWhiskFunctionInitializer {
|
public class OpenWhiskActionHandler extends OpenWhiskFunctionInitializer {
|
||||||
|
|
||||||
private static final String NO_INPUT_PROVIDED = "No input provided";
|
private static final String NO_INPUT_PROVIDED = "No input provided";
|
||||||
|
|
||||||
private static Log logger = LogFactory.getLog(OpenWhiskFunctionInitializer.class);
|
private static Log logger = LogFactory.getLog(OpenWhiskFunctionInitializer.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@@ -61,7 +60,7 @@ public class OpenWhiskActionHandler extends OpenWhiskFunctionInitializer {
|
|||||||
public Object run(@RequestBody OpenWhiskActionRequest request) {
|
public Object run(@RequestBody OpenWhiskActionRequest request) {
|
||||||
Object input = convertEvent(request.getValue());
|
Object input = convertEvent(request.getValue());
|
||||||
Object result = NO_INPUT_PROVIDED;
|
Object result = NO_INPUT_PROVIDED;
|
||||||
if(input !=null ) {
|
if (input != null) {
|
||||||
Publisher<?> output = apply(extract(input));
|
Publisher<?> output = apply(extract(input));
|
||||||
result = result(input, output);
|
result = result(input, output);
|
||||||
}
|
}
|
||||||
@@ -104,17 +103,20 @@ public class OpenWhiskActionHandler extends OpenWhiskFunctionInitializer {
|
|||||||
|
|
||||||
private Object convertToFunctionParamType(Object payload) {
|
private Object convertToFunctionParamType(Object payload) {
|
||||||
try {
|
try {
|
||||||
return mapper.convertValue(payload, getInputType());
|
return this.mapper.convertValue(payload, getInputType());
|
||||||
} catch (Exception e) {
|
}
|
||||||
|
catch (Exception e) {
|
||||||
throw new IllegalStateException("Cannot convert event payload", e);
|
throw new IllegalStateException("Cannot convert event payload", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String serializeBody(Object body) {
|
private String serializeBody(Object body) {
|
||||||
try {
|
try {
|
||||||
return "{\"result\":" + mapper.writeValueAsString(body) + "}";
|
return "{\"result\":" + this.mapper.writeValueAsString(body) + "}";
|
||||||
} catch (JsonProcessingException e) {
|
}
|
||||||
|
catch (JsonProcessingException e) {
|
||||||
throw new IllegalStateException("Cannot convert output", e);
|
throw new IllegalStateException("Cannot convert output", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -41,7 +41,7 @@ public class OpenWhiskActionRequest {
|
|||||||
private Map<String, Object> value;
|
private Map<String, Object> value;
|
||||||
|
|
||||||
public String getActionName() {
|
public String getActionName() {
|
||||||
return actionName;
|
return this.actionName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setActionName(String actionName) {
|
public void setActionName(String actionName) {
|
||||||
@@ -49,7 +49,7 @@ public class OpenWhiskActionRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getActivationId() {
|
public String getActivationId() {
|
||||||
return activationId;
|
return this.activationId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setActivationId(String activationId) {
|
public void setActivationId(String activationId) {
|
||||||
@@ -57,7 +57,7 @@ public class OpenWhiskActionRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getApiKey() {
|
public String getApiKey() {
|
||||||
return apiKey;
|
return this.apiKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setApiKey(String apiKey) {
|
public void setApiKey(String apiKey) {
|
||||||
@@ -65,7 +65,7 @@ public class OpenWhiskActionRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getDeadline() {
|
public String getDeadline() {
|
||||||
return deadline;
|
return this.deadline;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDeadline(String deadline) {
|
public void setDeadline(String deadline) {
|
||||||
@@ -73,7 +73,7 @@ public class OpenWhiskActionRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getNamespace() {
|
public String getNamespace() {
|
||||||
return namespace;
|
return this.namespace;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setNamespace(String namespace) {
|
public void setNamespace(String namespace) {
|
||||||
@@ -81,10 +81,11 @@ public class OpenWhiskActionRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> getValue() {
|
public Map<String, Object> getValue() {
|
||||||
return value;
|
return this.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setValue(Map<String, Object> value) {
|
public void setValue(Map<String, Object> value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -24,13 +24,12 @@ import java.util.function.Supplier;
|
|||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.reactivestreams.Publisher;
|
import org.reactivestreams.Publisher;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.cloud.function.context.FunctionCatalog;
|
import org.springframework.cloud.function.context.FunctionCatalog;
|
||||||
import org.springframework.cloud.function.context.catalog.FunctionInspector;
|
import org.springframework.cloud.function.context.catalog.FunctionInspector;
|
||||||
|
|
||||||
import reactor.core.publisher.Flux;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
@@ -76,8 +75,8 @@ public class OpenWhiskFunctionInitializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Class<?> getInputType() {
|
protected Class<?> getInputType() {
|
||||||
if (inspector != null) {
|
if (this.inspector != null) {
|
||||||
return inspector.getInputType(function());
|
return this.inspector.getInputType(function());
|
||||||
}
|
}
|
||||||
return Object.class;
|
return Object.class;
|
||||||
}
|
}
|
||||||
@@ -89,7 +88,7 @@ public class OpenWhiskFunctionInitializer {
|
|||||||
|
|
||||||
protected Publisher<?> apply(Publisher<?> input) {
|
protected Publisher<?> apply(Publisher<?> input) {
|
||||||
if (this.function != null) {
|
if (this.function != null) {
|
||||||
return function.apply(input);
|
return this.function.apply(input);
|
||||||
}
|
}
|
||||||
if (this.consumer != null) {
|
if (this.consumer != null) {
|
||||||
this.consumer.accept(input);
|
this.consumer.accept(input);
|
||||||
@@ -100,4 +99,5 @@ public class OpenWhiskFunctionInitializer {
|
|||||||
}
|
}
|
||||||
throw new IllegalStateException("No function defined");
|
throw new IllegalStateException("No function defined");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -28,7 +28,7 @@ public class OpenWhiskInitRequest {
|
|||||||
private String main;
|
private String main;
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
@@ -36,7 +36,7 @@ public class OpenWhiskInitRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isBinary() {
|
public boolean isBinary() {
|
||||||
return binary;
|
return this.binary;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBinary(boolean binary) {
|
public void setBinary(boolean binary) {
|
||||||
@@ -44,10 +44,11 @@ public class OpenWhiskInitRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getMain() {
|
public String getMain() {
|
||||||
return main;
|
return this.main;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMain(String main) {
|
public void setMain(String main) {
|
||||||
this.main = main;
|
this.main = main;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -21,7 +21,6 @@ import java.util.Map;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
@@ -61,11 +60,11 @@ public class OpenWhiskActionHandlerTest {
|
|||||||
testData.put("name", "Spring");
|
testData.put("name", "Spring");
|
||||||
Map<String, Object> eventData = new HashMap<>();
|
Map<String, Object> eventData = new HashMap<>();
|
||||||
eventData.put("payload", testData);
|
eventData.put("payload", testData);
|
||||||
actionHandler.init(new OpenWhiskInitRequest());
|
this.actionHandler.init(new OpenWhiskInitRequest());
|
||||||
OpenWhiskActionRequest actionRequest = new OpenWhiskActionRequest();
|
OpenWhiskActionRequest actionRequest = new OpenWhiskActionRequest();
|
||||||
actionRequest.setActionName("test_action");
|
actionRequest.setActionName("test_action");
|
||||||
actionRequest.setValue(eventData);
|
actionRequest.setValue(eventData);
|
||||||
Object result = actionHandler.run(actionRequest);
|
Object result = this.actionHandler.run(actionRequest);
|
||||||
assertNotNull(result);
|
assertNotNull(result);
|
||||||
assertEquals("{\"result\":{\"name\":\"Spring\",\"message\":\"Hello, Spring\"}}",
|
assertEquals("{\"result\":{\"name\":\"Spring\",\"message\":\"Hello, Spring\"}}",
|
||||||
result);
|
result);
|
||||||
@@ -75,10 +74,10 @@ public class OpenWhiskActionHandlerTest {
|
|||||||
public void testHandlerWithoutPayload() {
|
public void testHandlerWithoutPayload() {
|
||||||
Map<String, String> testData = new HashMap<>();
|
Map<String, String> testData = new HashMap<>();
|
||||||
testData.put("name", "Spring");
|
testData.put("name", "Spring");
|
||||||
actionHandler.init(new OpenWhiskInitRequest());
|
this.actionHandler.init(new OpenWhiskInitRequest());
|
||||||
OpenWhiskActionRequest actionRequest = new OpenWhiskActionRequest();
|
OpenWhiskActionRequest actionRequest = new OpenWhiskActionRequest();
|
||||||
actionRequest.setActionName("test_action");
|
actionRequest.setActionName("test_action");
|
||||||
Object result = actionHandler.run(actionRequest);
|
Object result = this.actionHandler.run(actionRequest);
|
||||||
assertNotNull(result);
|
assertNotNull(result);
|
||||||
assertEquals("{\"result\":\"No input provided\"}", result);
|
assertEquals("{\"result\":\"No input provided\"}", result);
|
||||||
}
|
}
|
||||||
@@ -111,6 +110,7 @@ public class OpenWhiskActionHandlerTest {
|
|||||||
private final String GREETINGS_FORMAT = "Hello, %s";
|
private final String GREETINGS_FORMAT = "Hello, %s";
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
private String message;
|
private String message;
|
||||||
|
|
||||||
public Greetings() {
|
public Greetings() {
|
||||||
@@ -122,21 +122,22 @@ public class OpenWhiskActionHandlerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
return message;
|
return this.message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMessage(String message) {
|
public void setMessage(String message) {
|
||||||
this.message = String.format(GREETINGS_FORMAT,
|
this.message = String.format(this.GREETINGS_FORMAT,
|
||||||
this.name != null ? name : "nobody");
|
this.name != null ? this.name : "nobody");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
function.name: greeter
|
function.name:greeter
|
||||||
function.type: function
|
function.type:function
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<artifactId>spring-cloud-function-compiler</artifactId>
|
<artifactId>spring-cloud-function-compiler</artifactId>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -30,24 +30,25 @@ import org.springframework.util.ObjectUtils;
|
|||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param <F> result type
|
||||||
* @author Andy Clement
|
* @author Andy Clement
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
* @author Oleg Zhurakousky
|
* @author Oleg Zhurakousky
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractFunctionCompiler<F> {
|
public abstract class AbstractFunctionCompiler<F> {
|
||||||
|
|
||||||
private static Logger logger = LoggerFactory
|
|
||||||
.getLogger(AbstractFunctionCompiler.class);
|
|
||||||
|
|
||||||
// Newlines in the property are escaped
|
// Newlines in the property are escaped
|
||||||
private static final String NEWLINE_ESCAPE = Matcher.quoteReplacement("\\n");
|
private static final String NEWLINE_ESCAPE = Matcher.quoteReplacement("\\n");
|
||||||
|
|
||||||
// Individual double-quote characters are represented by two double quotes in the DSL
|
// Individual double-quote characters are represented by two double quotes in the DSL
|
||||||
private static final String DOUBLE_DOUBLE_QUOTE = Matcher.quoteReplacement("\"\"");
|
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
|
* The user supplied code snippet is inserted into the template and then the result is
|
||||||
* compiled
|
* compiled.
|
||||||
*/
|
*/
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
private static String SOURCE_CODE_TEMPLATE = "package "
|
private static String SOURCE_CODE_TEMPLATE = "package "
|
||||||
@@ -62,11 +63,6 @@ public abstract class AbstractFunctionCompiler<F> {
|
|||||||
+ " }\n"
|
+ " }\n"
|
||||||
+ "}\n";
|
+ "}\n";
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|
||||||
static enum ResultType {
|
|
||||||
Consumer, Function, Supplier
|
|
||||||
}
|
|
||||||
|
|
||||||
private final ResultType resultType;
|
private final ResultType resultType;
|
||||||
|
|
||||||
private final String[] defaultResultTypeParameterizations;
|
private final String[] defaultResultTypeParameterizations;
|
||||||
@@ -79,6 +75,11 @@ public abstract class AbstractFunctionCompiler<F> {
|
|||||||
this.defaultResultTypeParameterizations = defaultResultTypeParameterizations;
|
this.defaultResultTypeParameterizations = defaultResultTypeParameterizations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String decode(String input) {
|
||||||
|
return input.replaceAll(NEWLINE_ESCAPE, "\n").replaceAll(DOUBLE_DOUBLE_QUOTE,
|
||||||
|
"\"");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produce a factory instance by:
|
* Produce a factory instance by:
|
||||||
* <ul>
|
* <ul>
|
||||||
@@ -90,7 +91,9 @@ public abstract class AbstractFunctionCompiler<F> {
|
|||||||
* Function, or Supplier instance
|
* Function, or Supplier instance
|
||||||
* <li>Returning that instance.
|
* <li>Returning that instance.
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
* @param name - name of the function
|
||||||
|
* @param code - code of the function
|
||||||
|
* @param resultTypeParameterizations - result types
|
||||||
* @return a factory instance
|
* @return a factory instance
|
||||||
*/
|
*/
|
||||||
public final CompiledFunctionFactory<F> compile(String name, String code,
|
public final CompiledFunctionFactory<F> compile(String name, String code,
|
||||||
@@ -106,14 +109,15 @@ public abstract class AbstractFunctionCompiler<F> {
|
|||||||
code = code.substring(1, code.length() - 1);
|
code = code.substring(1, code.length() - 1);
|
||||||
}
|
}
|
||||||
if (!code.startsWith("return ") && !code.endsWith(";")) {
|
if (!code.startsWith("return ") && !code.endsWith(";")) {
|
||||||
code = String.format("return (%s<%s> & java.io.Serializable) %s;", resultType,
|
code = String.format("return (%s<%s> & java.io.Serializable) %s;",
|
||||||
|
this.resultType,
|
||||||
StringUtils.arrayToCommaDelimitedString(parameterizedTypes), code);
|
StringUtils.arrayToCommaDelimitedString(parameterizedTypes), code);
|
||||||
}
|
}
|
||||||
logger.info("Processed code property value :\n{}\n", code);
|
logger.info("Processed code property value :\n{}\n", code);
|
||||||
String firstLetter = name.substring(0, 1).toUpperCase();
|
String firstLetter = name.substring(0, 1).toUpperCase();
|
||||||
name = (name.length() > 1) ? firstLetter + name.substring(1) : firstLetter;
|
name = (name.length() > 1) ? firstLetter + name.substring(1) : firstLetter;
|
||||||
String className = String.format("%s.%s%sFactory",
|
String className = String.format("%s.%s%sFactory",
|
||||||
this.getClass().getPackage().getName(), name, resultType);
|
this.getClass().getPackage().getName(), name, this.resultType);
|
||||||
CompilationResult compilationResult = buildAndCompileSourceCode(className, code,
|
CompilationResult compilationResult = buildAndCompileSourceCode(className, code,
|
||||||
parameterizedTypes);
|
parameterizedTypes);
|
||||||
if (compilationResult.wasSuccessful()) {
|
if (compilationResult.wasSuccessful()) {
|
||||||
@@ -129,7 +133,6 @@ public abstract class AbstractFunctionCompiler<F> {
|
|||||||
/**
|
/**
|
||||||
* Implementing subclasses may override this, e.g. to set the input and/or output
|
* Implementing subclasses may override this, e.g. to set the input and/or output
|
||||||
* types.
|
* types.
|
||||||
*
|
|
||||||
* @param factory the {@link CompiledFunctionFactory} produced by
|
* @param factory the {@link CompiledFunctionFactory} produced by
|
||||||
* {@link #compile(String, String, String...)}
|
* {@link #compile(String, String, String...)}
|
||||||
* @return the post-processed {@link CompiledFunctionFactory}
|
* @return the post-processed {@link CompiledFunctionFactory}
|
||||||
@@ -145,7 +148,6 @@ public abstract class AbstractFunctionCompiler<F> {
|
|||||||
* specified parameterized type. This method can return more than one class if 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
|
* method body includes local class declarations. An example methodBody would be
|
||||||
* <tt>return input -> input.buffer(5).map(list->list.get(0));</tt>.
|
* <tt>return input -> input.buffer(5).map(list->list.get(0));</tt>.
|
||||||
*
|
|
||||||
* @param className the name of the class
|
* @param className the name of the class
|
||||||
* @param methodBody the source code for a method
|
* @param methodBody the source code for a method
|
||||||
* @param parameterizedTypes the array of String representations for the parameterized
|
* @param parameterizedTypes the array of String representations for the parameterized
|
||||||
@@ -157,18 +159,12 @@ public abstract class AbstractFunctionCompiler<F> {
|
|||||||
String methodBody, String[] parameterizedTypes) {
|
String methodBody, String[] parameterizedTypes) {
|
||||||
String sourceCode = makeSourceClassDefinition(className, methodBody,
|
String sourceCode = makeSourceClassDefinition(className, methodBody,
|
||||||
parameterizedTypes);
|
parameterizedTypes);
|
||||||
return compiler.compile(className, sourceCode);
|
return this.compiler.compile(className, sourceCode);
|
||||||
}
|
|
||||||
|
|
||||||
private static String decode(String input) {
|
|
||||||
return input.replaceAll(NEWLINE_ESCAPE, "\n").replaceAll(DOUBLE_DOUBLE_QUOTE,
|
|
||||||
"\"");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a full source code definition for a class by applying the specified method
|
* Make a full source code definition for a class by applying the specified method
|
||||||
* body to the Reactive template.
|
* body to the Reactive template.
|
||||||
*
|
|
||||||
* @param className the name of the class
|
* @param className the name of the class
|
||||||
* @param methodBody the code to insert into the Reactive source class template
|
* @param methodBody the code to insert into the Reactive source class template
|
||||||
* @param types the parameterized input and/or output types as Strings
|
* @param types the parameterized input and/or output types as Strings
|
||||||
@@ -177,9 +173,17 @@ public abstract class AbstractFunctionCompiler<F> {
|
|||||||
private String makeSourceClassDefinition(String className, String methodBody,
|
private String makeSourceClassDefinition(String className, String methodBody,
|
||||||
String[] types) {
|
String[] types) {
|
||||||
String shortClassName = className.substring(className.lastIndexOf('.') + 1);
|
String shortClassName = className.substring(className.lastIndexOf('.') + 1);
|
||||||
String s = String.format(SOURCE_CODE_TEMPLATE, shortClassName, resultType,
|
String s = String.format(SOURCE_CODE_TEMPLATE, shortClassName, this.resultType,
|
||||||
resultType, StringUtils.arrayToCommaDelimitedString(types), methodBody);
|
this.resultType, StringUtils.arrayToCommaDelimitedString(types),
|
||||||
|
methodBody);
|
||||||
logger.info("\n" + s);
|
logger.info("\n" + s);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ResultType {
|
||||||
|
|
||||||
|
Consumer, Function, Supplier
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
package org.springframework.cloud.function.compiler;
|
package org.springframework.cloud.function.compiler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param <T> result type
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
*/
|
*/
|
||||||
public interface CompilationResultFactory<T> {
|
public interface CompilationResultFactory<T> {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -24,6 +24,7 @@ import org.springframework.cloud.function.compiler.java.CompilationResult;
|
|||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param <T> result type
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
*/
|
*/
|
||||||
public class CompiledFunctionFactory<T> implements CompilationResultFactory<T> {
|
public class CompiledFunctionFactory<T> implements CompilationResultFactory<T> {
|
||||||
@@ -79,15 +80,15 @@ public class CompiledFunctionFactory<T> implements CompilationResultFactory<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public T getResult() {
|
public T getResult() {
|
||||||
return result;
|
return this.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Method getFactoryMethod() {
|
public Method getFactoryMethod() {
|
||||||
return method;
|
return this.method;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getInputType() {
|
public String getInputType() {
|
||||||
return inputType;
|
return this.inputType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setInputType(String inputType) {
|
public void setInputType(String inputType) {
|
||||||
@@ -95,7 +96,7 @@ public class CompiledFunctionFactory<T> implements CompilationResultFactory<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getOutputType() {
|
public String getOutputType() {
|
||||||
return outputType;
|
return this.outputType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOutputType(String outputType) {
|
public void setOutputType(String outputType) {
|
||||||
@@ -103,6 +104,7 @@ public class CompiledFunctionFactory<T> implements CompilationResultFactory<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getGeneratedClassBytes() {
|
public byte[] getGeneratedClassBytes() {
|
||||||
return generatedClassBytes;
|
return this.generatedClassBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -19,6 +19,7 @@ package org.springframework.cloud.function.compiler;
|
|||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param <T> result type
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
*/
|
*/
|
||||||
public class ConsumerCompiler<T> extends AbstractFunctionCompiler<Consumer<T>> {
|
public class ConsumerCompiler<T> extends AbstractFunctionCompiler<Consumer<T>> {
|
||||||
@@ -35,8 +36,10 @@ public class ConsumerCompiler<T> extends AbstractFunctionCompiler<Consumer<T>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected CompiledFunctionFactory<Consumer<T>> postProcessCompiledFunctionFactory(CompiledFunctionFactory<Consumer<T>> factory) {
|
protected CompiledFunctionFactory<Consumer<T>> postProcessCompiledFunctionFactory(
|
||||||
|
CompiledFunctionFactory<Consumer<T>> factory) {
|
||||||
factory.setInputType(this.inputType);
|
factory.setInputType(this.inputType);
|
||||||
return factory;
|
return factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -19,6 +19,8 @@ package org.springframework.cloud.function.compiler;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param <T> function input type
|
||||||
|
* @param <R> function output type
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
*/
|
*/
|
||||||
public class FunctionCompiler<T, R> extends AbstractFunctionCompiler<Function<T, R>> {
|
public class FunctionCompiler<T, R> extends AbstractFunctionCompiler<Function<T, R>> {
|
||||||
@@ -42,10 +44,12 @@ public class FunctionCompiler<T, R> extends AbstractFunctionCompiler<Function<T,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected CompiledFunctionFactory<Function<T, R>> postProcessCompiledFunctionFactory(CompiledFunctionFactory<Function<T, R>> factory) {
|
protected CompiledFunctionFactory<Function<T, R>> postProcessCompiledFunctionFactory(
|
||||||
|
CompiledFunctionFactory<Function<T, R>> factory) {
|
||||||
factory.setInputType(this.inputType);
|
factory.setInputType(this.inputType);
|
||||||
factory.setOutputType(this.outputType);
|
factory.setOutputType(this.outputType);
|
||||||
|
|
||||||
return factory;
|
return factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -19,6 +19,7 @@ package org.springframework.cloud.function.compiler;
|
|||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param <T> input type
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
*/
|
*/
|
||||||
public class SupplierCompiler<T> extends AbstractFunctionCompiler<Supplier<T>> {
|
public class SupplierCompiler<T> extends AbstractFunctionCompiler<Supplier<T>> {
|
||||||
@@ -35,8 +36,10 @@ public class SupplierCompiler<T> extends AbstractFunctionCompiler<Supplier<T>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected CompiledFunctionFactory<Supplier<T>> postProcessCompiledFunctionFactory(CompiledFunctionFactory<Supplier<T>> factory) {
|
protected CompiledFunctionFactory<Supplier<T>> postProcessCompiledFunctionFactory(
|
||||||
|
CompiledFunctionFactory<Supplier<T>> factory) {
|
||||||
factory.setOutputType(this.outputType);
|
factory.setOutputType(this.outputType);
|
||||||
return factory;
|
return factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -22,6 +22,8 @@ import java.util.function.Consumer;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
import org.springframework.cloud.function.compiler.AbstractFunctionCompiler;
|
import org.springframework.cloud.function.compiler.AbstractFunctionCompiler;
|
||||||
import org.springframework.cloud.function.compiler.CompiledFunctionFactory;
|
import org.springframework.cloud.function.compiler.CompiledFunctionFactory;
|
||||||
import org.springframework.cloud.function.compiler.ConsumerCompiler;
|
import org.springframework.cloud.function.compiler.ConsumerCompiler;
|
||||||
@@ -30,8 +32,6 @@ import org.springframework.cloud.function.compiler.SupplierCompiler;
|
|||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.FileCopyUtils;
|
import org.springframework.util.FileCopyUtils;
|
||||||
|
|
||||||
import reactor.core.publisher.Flux;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
* @author Oleg Zhurakousky
|
* @author Oleg Zhurakousky
|
||||||
@@ -78,29 +78,35 @@ public class CompiledFunctionRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void registerSupplier(String name, String lambda, String type) {
|
public void registerSupplier(String name, String lambda, String type) {
|
||||||
this.doRegister(this.supplierCompiler, this.supplierDirectory, name, lambda, type);
|
this.doRegister(this.supplierCompiler, this.supplierDirectory, name, lambda,
|
||||||
|
type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerFunction(String name, String lambda, String... types) {
|
public void registerFunction(String name, String lambda, String... types) {
|
||||||
this.doRegister(this.functionCompiler, this.functionDirectory, name, lambda, types);
|
this.doRegister(this.functionCompiler, this.functionDirectory, name, lambda,
|
||||||
|
types);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerConsumer(String name, String lambda, String type) {
|
public void registerConsumer(String name, String lambda, String type) {
|
||||||
this.doRegister(this.consumerCompiler, this.consumerDirectory, name, lambda, type);
|
this.doRegister(this.consumerCompiler, this.consumerDirectory, name, lambda,
|
||||||
|
type);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doRegister(AbstractFunctionCompiler<?> compiler, File directory, String name, String lambda, String... types) {
|
private void doRegister(AbstractFunctionCompiler<?> compiler, File directory,
|
||||||
|
String name, String lambda, String... types) {
|
||||||
CompiledFunctionFactory<?> factory = compiler.compile(name, lambda, types);
|
CompiledFunctionFactory<?> factory = compiler.compile(name, lambda, types);
|
||||||
File file = new File(directory, fileName(name));
|
File file = new File(directory, fileName(name));
|
||||||
try {
|
try {
|
||||||
FileCopyUtils.copy(factory.getGeneratedClassBytes(), file);
|
FileCopyUtils.copy(factory.getGeneratedClassBytes(), file);
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
throw new IllegalArgumentException(String.format("failed to register '%s'", name), e);
|
throw new IllegalArgumentException(
|
||||||
|
String.format("failed to register '%s'", name), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String fileName(String functionName) {
|
private String fileName(String functionName) {
|
||||||
return String.format("%s.%s", functionName, "fun");
|
return String.format("%s.%s", functionName, "fun");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -19,10 +19,13 @@ package org.springframework.cloud.function.compiler.app;
|
|||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
// @checkstyle:off
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
public class CompilerApplication {
|
public class CompilerApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(CompilerApplication.class, args);
|
SpringApplication.run(CompilerApplication.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// @checkstyle:on
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -32,20 +32,21 @@ public class CompilerController {
|
|||||||
|
|
||||||
@PostMapping(path = "/supplier/{name}")
|
@PostMapping(path = "/supplier/{name}")
|
||||||
public void registerSupplier(@PathVariable String name, @RequestBody String lambda,
|
public void registerSupplier(@PathVariable String name, @RequestBody String lambda,
|
||||||
@RequestParam(defaultValue="Flux<String>") String type) {
|
@RequestParam(defaultValue = "Flux<String>") String type) {
|
||||||
this.registry.registerSupplier(name, lambda, type);
|
this.registry.registerSupplier(name, lambda, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(path = "/function/{name}")
|
@PostMapping(path = "/function/{name}")
|
||||||
public void registerFunction(@PathVariable String name, @RequestBody String lambda,
|
public void registerFunction(@PathVariable String name, @RequestBody String lambda,
|
||||||
@RequestParam(defaultValue="Flux<String>") String inputType,
|
@RequestParam(defaultValue = "Flux<String>") String inputType,
|
||||||
@RequestParam(defaultValue="Flux<String>") String outputType) {
|
@RequestParam(defaultValue = "Flux<String>") String outputType) {
|
||||||
this.registry.registerFunction(name, lambda, inputType, outputType);
|
this.registry.registerFunction(name, lambda, inputType, outputType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(path = "/consumer/{name}")
|
@PostMapping(path = "/consumer/{name}")
|
||||||
public void registerConsumer(@PathVariable String name, @RequestBody String lambda,
|
public void registerConsumer(@PathVariable String name, @RequestBody String lambda,
|
||||||
@RequestParam(defaultValue="Flux<String>") String type) {
|
@RequestParam(defaultValue = "Flux<String>") String type) {
|
||||||
this.registry.registerConsumer(name, lambda, type);
|
this.registry.registerConsumer(name, lambda, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -73,11 +73,11 @@ public class FunctionProxyApplicationListener
|
|||||||
private final Map<String, Object> imports = new HashMap<>();
|
private final Map<String, Object> imports = new HashMap<>();
|
||||||
|
|
||||||
public Map<String, Object> getCompile() {
|
public Map<String, Object> getCompile() {
|
||||||
return compile;
|
return this.compile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> getImports() {
|
public Map<String, Object> getImports() {
|
||||||
return imports;
|
return this.imports;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -86,7 +86,7 @@ public class FunctionProxyApplicationListener
|
|||||||
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context
|
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context
|
||||||
.getBeanFactory();
|
.getBeanFactory();
|
||||||
bind(context);
|
bind(context);
|
||||||
for (Map.Entry<String, Object> entry : compile.entrySet()) {
|
for (Map.Entry<String, Object> entry : this.compile.entrySet()) {
|
||||||
String name = entry.getKey();
|
String name = entry.getKey();
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Map<String, String> properties = (Map<String, String>) entry.getValue();
|
Map<String, String> properties = (Map<String, String>) entry.getValue();
|
||||||
@@ -101,7 +101,7 @@ public class FunctionProxyApplicationListener
|
|||||||
registerLambdaCompilingProxy(name, type, inputType, outputType, lambda,
|
registerLambdaCompilingProxy(name, type, inputType, outputType, lambda,
|
||||||
beanFactory);
|
beanFactory);
|
||||||
}
|
}
|
||||||
for (Map.Entry<String, Object> entry : imports.entrySet()) {
|
for (Map.Entry<String, Object> entry : this.imports.entrySet()) {
|
||||||
String name = entry.getKey();
|
String name = entry.getKey();
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Map<String, String> properties = (Map<String, String>) entry.getValue();
|
Map<String, String> properties = (Map<String, String>) entry.getValue();
|
||||||
@@ -132,7 +132,8 @@ public class FunctionProxyApplicationListener
|
|||||||
ConfigurationPropertiesBindingPostProcessor post) {
|
ConfigurationPropertiesBindingPostProcessor post) {
|
||||||
StaticApplicationContext other = new StaticApplicationContext();
|
StaticApplicationContext other = new StaticApplicationContext();
|
||||||
other.setEnvironment(context.getEnvironment());
|
other.setEnvironment(context.getEnvironment());
|
||||||
other.registerSingleton(ConfigurationBeanFactoryMetadata.class.getName(), ConfigurationBeanFactoryMetadata.class);
|
other.registerSingleton(ConfigurationBeanFactoryMetadata.class.getName(),
|
||||||
|
ConfigurationBeanFactoryMetadata.class);
|
||||||
other.setParent(context);
|
other.setParent(context);
|
||||||
post.setApplicationContext(other);
|
post.setApplicationContext(other);
|
||||||
}
|
}
|
||||||
@@ -195,4 +196,5 @@ public class FunctionProxyApplicationListener
|
|||||||
beanDefinition.setPropertyValues(props);
|
beanDefinition.setPropertyValues(props);
|
||||||
beanFactory.registerBeanDefinition(name, beanDefinition);
|
beanFactory.registerBeanDefinition(name, beanDefinition);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2018 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -23,17 +23,19 @@ import javax.tools.JavaFileObject;
|
|||||||
import org.springframework.cloud.function.compiler.java.MemoryBasedJavaFileManager.CompilationInfoCache;
|
import org.springframework.cloud.function.compiler.java.MemoryBasedJavaFileManager.CompilationInfoCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common superclass for iterables that need to handle closing when finished
|
* Common superclass for iterables that need to handle closing when finished with and that
|
||||||
* with and that need to handle possible constraints on the values that
|
* need to handle possible constraints on the values that are iterated over.
|
||||||
* are iterated over.
|
|
||||||
*
|
*
|
||||||
* @author Andy Clement
|
* @author Andy Clement
|
||||||
*/
|
*/
|
||||||
public abstract class CloseableFilterableJavaFileObjectIterable implements Iterable<JavaFileObject> {
|
public abstract class CloseableFilterableJavaFileObjectIterable
|
||||||
|
implements Iterable<JavaFileObject> {
|
||||||
|
|
||||||
// private final static Logger logger = LoggerFactory.getLogger(CloseableFilterableJavaFileObjectIterable.class);
|
// private final static Logger logger =
|
||||||
|
// LoggerFactory.getLogger(CloseableFilterableJavaFileObjectIterable.class);
|
||||||
|
|
||||||
private final static boolean BOOT_PACKAGING_AWARE = true;
|
private final static boolean BOOT_PACKAGING_AWARE = true;
|
||||||
|
|
||||||
private final static String BOOT_PACKAGING_PREFIX_FOR_CLASSES = "BOOT-INF/classes/";
|
private final static String BOOT_PACKAGING_PREFIX_FOR_CLASSES = "BOOT-INF/classes/";
|
||||||
|
|
||||||
// If set specifies the package the iterator consumer is interested in. Only
|
// If set specifies the package the iterator consumer is interested in. Only
|
||||||
@@ -47,55 +49,69 @@ public abstract class CloseableFilterableJavaFileObjectIterable implements Itera
|
|||||||
|
|
||||||
protected CompilationInfoCache compilationInfoCache;
|
protected CompilationInfoCache compilationInfoCache;
|
||||||
|
|
||||||
public CloseableFilterableJavaFileObjectIterable(CompilationInfoCache compilationInfoCache, String packageNameFilter, boolean includeSubpackages) {
|
public CloseableFilterableJavaFileObjectIterable(
|
||||||
if (packageNameFilter!=null && packageNameFilter.contains(File.separator)) {
|
CompilationInfoCache compilationInfoCache, String packageNameFilter,
|
||||||
throw new IllegalArgumentException("Package name filters should use dots to separate components: "+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;
|
this.compilationInfoCache = compilationInfoCache;
|
||||||
// Normalize filter to forward slashes
|
// Normalize filter to forward slashes
|
||||||
this.packageNameFilter = packageNameFilter==null?null:packageNameFilter.replace('.', '/') + '/';
|
this.packageNameFilter = packageNameFilter == null ? null
|
||||||
|
: packageNameFilter.replace('.', '/') + '/';
|
||||||
this.includeSubpackages = includeSubpackages;
|
this.includeSubpackages = includeSubpackages;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by subclasses to check values against any specified constraints.
|
* Used by subclasses to check values against any specified constraints.
|
||||||
*
|
|
||||||
* @param name the name to check against the criteria
|
* @param name the name to check against the criteria
|
||||||
* @return true if the name is a valid iterator result based on the specified criteria
|
* @return true if the name is a valid iterator result based on the specified criteria
|
||||||
*/
|
*/
|
||||||
protected boolean accept(String name) {
|
protected boolean accept(String name) {
|
||||||
// logger.debug("checking {} against constraints packageNameFilter={} includeSubpackages={}",name,packageNameFilter,includeSubpackages);
|
// logger.debug("checking {} against constraints packageNameFilter={}
|
||||||
|
// includeSubpackages={}",name,packageNameFilter,includeSubpackages);
|
||||||
if (!name.endsWith(".class")) {
|
if (!name.endsWith(".class")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (packageNameFilter == null) {
|
if (this.packageNameFilter == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
boolean accept;
|
boolean accept;
|
||||||
// Normalize to forward slashes (some jars are producing paths with forward slashes, some with backward slashes)
|
// Normalize to forward slashes (some jars are producing paths with forward
|
||||||
|
// slashes, some with backward slashes)
|
||||||
name = name.replace('\\', '/');
|
name = name.replace('\\', '/');
|
||||||
if (packageNameFilter.length() == 1 && packageNameFilter.equals("/")) {
|
if (this.packageNameFilter.length() == 1 && this.packageNameFilter.equals("/")) {
|
||||||
// This is the 'default package' filter representation
|
// This is the 'default package' filter representation
|
||||||
if (name.indexOf('/') == -1) {
|
if (name.indexOf('/') == -1) {
|
||||||
accept = true;
|
accept = true;
|
||||||
} else if (BOOT_PACKAGING_AWARE) {
|
}
|
||||||
accept = name.startsWith(BOOT_PACKAGING_PREFIX_FOR_CLASSES) &&
|
else if (BOOT_PACKAGING_AWARE) {
|
||||||
name.indexOf('/',BOOT_PACKAGING_PREFIX_FOR_CLASSES.length()) == -1;
|
accept = name.startsWith(BOOT_PACKAGING_PREFIX_FOR_CLASSES) && name
|
||||||
|
.indexOf('/', BOOT_PACKAGING_PREFIX_FOR_CLASSES.length()) == -1;
|
||||||
}
|
}
|
||||||
return accept;
|
return accept;
|
||||||
}
|
}
|
||||||
if (includeSubpackages == true) {
|
if (this.includeSubpackages) {
|
||||||
accept = name.startsWith(packageNameFilter);
|
accept = name.startsWith(this.packageNameFilter);
|
||||||
if (!accept && BOOT_PACKAGING_AWARE) {
|
if (!accept && BOOT_PACKAGING_AWARE) {
|
||||||
accept = name.startsWith(BOOT_PACKAGING_PREFIX_FOR_CLASSES) &&
|
accept = name.startsWith(BOOT_PACKAGING_PREFIX_FOR_CLASSES)
|
||||||
name.indexOf(packageNameFilter)==BOOT_PACKAGING_PREFIX_FOR_CLASSES.length();
|
&& name.indexOf(
|
||||||
|
this.packageNameFilter) == BOOT_PACKAGING_PREFIX_FOR_CLASSES
|
||||||
|
.length();
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
accept = name.startsWith(packageNameFilter) && name.indexOf("/",packageNameFilter.length())==-1;
|
else {
|
||||||
|
accept = name.startsWith(this.packageNameFilter)
|
||||||
|
&& name.indexOf("/", this.packageNameFilter.length()) == -1;
|
||||||
if (!accept && BOOT_PACKAGING_AWARE) {
|
if (!accept && BOOT_PACKAGING_AWARE) {
|
||||||
accept = name.startsWith(BOOT_PACKAGING_PREFIX_FOR_CLASSES) &&
|
accept = name.startsWith(BOOT_PACKAGING_PREFIX_FOR_CLASSES)
|
||||||
name.indexOf(packageNameFilter)==BOOT_PACKAGING_PREFIX_FOR_CLASSES.length() &&
|
&& name.indexOf(
|
||||||
name.indexOf("/",BOOT_PACKAGING_PREFIX_FOR_CLASSES.length()+packageNameFilter.length())==-1;
|
this.packageNameFilter) == BOOT_PACKAGING_PREFIX_FOR_CLASSES
|
||||||
|
.length()
|
||||||
|
&& name.indexOf("/", BOOT_PACKAGING_PREFIX_FOR_CLASSES.length()
|
||||||
|
+ this.packageNameFilter.length()) == -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return accept;
|
return accept;
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -38,4 +38,5 @@ public class CompilationFailedException extends RuntimeException {
|
|||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -17,32 +17,33 @@
|
|||||||
package org.springframework.cloud.function.compiler.java;
|
package org.springframework.cloud.function.compiler.java;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulate information produced during compilation. A message may be an error
|
* Encapsulate information produced during compilation. A message may be an error or
|
||||||
* or something less serious (warning/informational). The <tt>toString()</tt> method
|
* something less serious (warning/informational). The <tt>toString()</tt> method will
|
||||||
* will produce a formatted error include source context indicating the precise
|
* produce a formatted error include source context indicating the precise location of the
|
||||||
* location of the problem.
|
* problem.
|
||||||
*
|
*
|
||||||
* @author Andy Clement
|
* @author Andy Clement
|
||||||
*/
|
*/
|
||||||
public class CompilationMessage {
|
public class CompilationMessage {
|
||||||
|
|
||||||
private Kind kind;
|
private Kind kind;
|
||||||
|
|
||||||
private String message;
|
private String message;
|
||||||
|
|
||||||
private String sourceCode;
|
private String sourceCode;
|
||||||
|
|
||||||
private int startPosition;
|
private int startPosition;
|
||||||
|
|
||||||
private int endPosition;
|
private int endPosition;
|
||||||
|
|
||||||
enum Kind {
|
public CompilationMessage(Kind kind, String message, String sourceCode,
|
||||||
ERROR, OTHER
|
int startPosition, int endPosition) {
|
||||||
};
|
|
||||||
|
|
||||||
public CompilationMessage(Kind kind, String message, String sourceCode, int startPosition, int endPosition) {
|
|
||||||
this.kind = kind;
|
this.kind = kind;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.sourceCode = sourceCode;
|
this.sourceCode = sourceCode;
|
||||||
this.startPosition = startPosition;
|
this.startPosition = startPosition;
|
||||||
this.endPosition = endPosition;
|
this.endPosition = endPosition;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the type of message
|
* @return the type of message
|
||||||
@@ -82,32 +83,33 @@ public class CompilationMessage {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder s = new StringBuilder();
|
StringBuilder s = new StringBuilder();
|
||||||
s.append("==========\n");
|
s.append("==========\n");
|
||||||
if (sourceCode != null) { // Cannot include source context if no source available
|
if (this.sourceCode != null) { // Cannot include source context if no source
|
||||||
int[] lineStartEnd = getLineStartEnd(startPosition);
|
// available
|
||||||
s.append(sourceCode.substring(lineStartEnd[0], lineStartEnd[1])).append("\n");
|
int[] lineStartEnd = getLineStartEnd(this.startPosition);
|
||||||
|
s.append(this.sourceCode.substring(lineStartEnd[0], lineStartEnd[1]))
|
||||||
|
.append("\n");
|
||||||
int col = lineStartEnd[0];
|
int col = lineStartEnd[0];
|
||||||
// When inserting the whitespace, ensure tabs in the source line are respected
|
// When inserting the whitespace, ensure tabs in the source line are respected
|
||||||
while ((col) < startPosition) {
|
while ((col) < this.startPosition) {
|
||||||
s.append(sourceCode.charAt(col++)=='\t'?"\t":" ");
|
s.append(this.sourceCode.charAt(col++) == '\t' ? "\t" : " ");
|
||||||
}
|
}
|
||||||
// Want at least one ^
|
// Want at least one ^
|
||||||
s.append("^");
|
s.append("^");
|
||||||
col++;
|
col++;
|
||||||
while ((col++) < endPosition) {
|
while ((col++) < this.endPosition) {
|
||||||
s.append("^");
|
s.append("^");
|
||||||
}
|
}
|
||||||
s.append("\n");
|
s.append("\n");
|
||||||
}
|
}
|
||||||
s.append(kind).append(":").append(message).append("\n");
|
s.append(this.kind).append(":").append(this.message).append("\n");
|
||||||
s.append("==========\n");
|
s.append("==========\n");
|
||||||
return s.toString();
|
return s.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For a given position in the source code this method returns a pair of int
|
* For a given position in the source code this method returns a pair of int that
|
||||||
* that indicate the start and end of the line within the source code that
|
* indicate the start and end of the line within the source code that contain the
|
||||||
* contain the position.
|
* position.
|
||||||
*
|
|
||||||
* @param searchPos the position of interest in the source code
|
* @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
|
* @return an int array of length 2 containing the start and end positions of the line
|
||||||
*/
|
*/
|
||||||
@@ -115,13 +117,20 @@ public class CompilationMessage {
|
|||||||
int previousPos = -1;
|
int previousPos = -1;
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
do {
|
do {
|
||||||
pos = sourceCode.indexOf('\n', previousPos + 1);
|
pos = this.sourceCode.indexOf('\n', previousPos + 1);
|
||||||
if (searchPos < pos) {
|
if (searchPos < pos) {
|
||||||
return new int[] { previousPos + 1, pos };
|
return new int[] { previousPos + 1, pos };
|
||||||
}
|
}
|
||||||
previousPos = pos;
|
previousPos = pos;
|
||||||
} while (pos != -1);
|
}
|
||||||
return new int[] { previousPos + 1, sourceCode.length() };
|
while (pos != -1);
|
||||||
|
return new int[] { previousPos + 1, this.sourceCode.length() };
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Kind {
|
||||||
|
|
||||||
|
ERROR, OTHER
|
||||||
|
|
||||||
}
|
}
|
||||||
// TODO test coverage for first line/last line situations
|
// TODO test coverage for first line/last line situations
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -24,9 +24,10 @@ import javax.tools.JavaFileManager.Location;
|
|||||||
import javax.tools.JavaFileObject.Kind;
|
import javax.tools.JavaFileObject.Kind;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* During compilation instances of this class will collect up the output files from the compilation process.
|
* During compilation instances of this class will collect up the output files from the
|
||||||
* Any kind of file is collected upon but access is only currently provided to retrieve classes produced
|
* compilation process. Any kind of file is collected upon but access is only currently
|
||||||
* during compilation. Annotation processors that run may create other kinds of artifact.
|
* provided to retrieve classes produced during compilation. Annotation processors that
|
||||||
|
* run may create other kinds of artifact.
|
||||||
*
|
*
|
||||||
* @author Andy Clement
|
* @author Andy Clement
|
||||||
*/
|
*/
|
||||||
@@ -35,34 +36,36 @@ public class CompilationOutputCollector {
|
|||||||
private List<InMemoryJavaFileObject> outputFiles = new ArrayList<>();
|
private List<InMemoryJavaFileObject> outputFiles = new ArrayList<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve compiled classes that have been collected since this collector
|
* Retrieve compiled classes that have been collected since this collector was built.
|
||||||
* was built. Due to annotation processing it is possible other source files
|
* Due to annotation processing it is possible other source files or metadata files
|
||||||
* or metadata files may be produced during compilation - those are not included
|
* may be produced during compilation - those are not included in the returned list.
|
||||||
* in the returned list.
|
|
||||||
*
|
|
||||||
* @return list of compiled classes
|
* @return list of compiled classes
|
||||||
*/
|
*/
|
||||||
public List<CompiledClassDefinition> getCompiledClasses() {
|
public List<CompiledClassDefinition> getCompiledClasses() {
|
||||||
List<CompiledClassDefinition> compiledClassDefinitions = new ArrayList<>();
|
List<CompiledClassDefinition> compiledClassDefinitions = new ArrayList<>();
|
||||||
for (InMemoryJavaFileObject outputFile : outputFiles) {
|
for (InMemoryJavaFileObject outputFile : this.outputFiles) {
|
||||||
if (outputFile.getKind() == Kind.CLASS) {
|
if (outputFile.getKind() == Kind.CLASS) {
|
||||||
CompiledClassDefinition compiledClassDefinition = new CompiledClassDefinition(outputFile.getName(),
|
CompiledClassDefinition compiledClassDefinition = new CompiledClassDefinition(
|
||||||
outputFile.getBytes());
|
outputFile.getName(), outputFile.getBytes());
|
||||||
compiledClassDefinitions.add(compiledClassDefinition);
|
compiledClassDefinitions.add(compiledClassDefinition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return compiledClassDefinitions;
|
return compiledClassDefinitions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InMemoryJavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) {
|
public InMemoryJavaFileObject getJavaFileForOutput(Location location,
|
||||||
InMemoryJavaFileObject jfo = InMemoryJavaFileObject.getJavaFileObject(location, className, kind, sibling);
|
String className, Kind kind, FileObject sibling) {
|
||||||
outputFiles.add(jfo);
|
InMemoryJavaFileObject jfo = InMemoryJavaFileObject.getJavaFileObject(location,
|
||||||
|
className, kind, sibling);
|
||||||
|
this.outputFiles.add(jfo);
|
||||||
return jfo;
|
return jfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InMemoryJavaFileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) {
|
public InMemoryJavaFileObject getFileForOutput(Location location, String packageName,
|
||||||
InMemoryJavaFileObject ojfo = InMemoryJavaFileObject.getFileObject(location, packageName, relativeName, sibling);
|
String relativeName, FileObject sibling) {
|
||||||
outputFiles.add(ojfo);
|
InMemoryJavaFileObject ojfo = InMemoryJavaFileObject.getFileObject(location,
|
||||||
|
packageName, relativeName, sibling);
|
||||||
|
this.outputFiles.add(ojfo);
|
||||||
return ojfo;
|
return ojfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -24,23 +24,22 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holder for the results of compilation. If compilation was successful the set
|
* Holder for the results of compilation. If compilation was successful the set of classes
|
||||||
* of classes that resulted from compilation will be available. If compilation
|
* that resulted from compilation will be available. If compilation was not successful the
|
||||||
* was not successful the error messages should provide information about why.
|
* error messages should provide information about why. Note that compilation may succeed
|
||||||
* Note that compilation may succeed and yet there will still be informational or
|
* and yet there will still be informational or warning messages collected.
|
||||||
* warning messages collected.
|
|
||||||
*
|
*
|
||||||
* @author Andy Clement
|
* @author Andy Clement
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
*/
|
*/
|
||||||
public class CompilationResult {
|
public class CompilationResult {
|
||||||
|
|
||||||
private boolean successfulCompilation;
|
|
||||||
|
|
||||||
List<CompilationMessage> compilationMessages = new ArrayList<>();
|
List<CompilationMessage> compilationMessages = new ArrayList<>();
|
||||||
|
|
||||||
List<Class<?>> compiledClasses = new ArrayList<>();
|
List<Class<?>> compiledClasses = new ArrayList<>();
|
||||||
|
|
||||||
|
private boolean successfulCompilation;
|
||||||
|
|
||||||
private Map<String, byte[]> classBytes = new HashMap<>();
|
private Map<String, byte[]> classBytes = new HashMap<>();
|
||||||
|
|
||||||
private List<File> resolvedAdditionalDependencies = new ArrayList<>();
|
private List<File> resolvedAdditionalDependencies = new ArrayList<>();
|
||||||
@@ -53,28 +52,33 @@ public class CompilationResult {
|
|||||||
this.classBytes.put(name, bytes);
|
this.classBytes.put(name, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setResolvedAdditionalDependencies(List<File> resolvedAdditionalDependencies) {
|
|
||||||
this.resolvedAdditionalDependencies = resolvedAdditionalDependencies;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<File> getResolvedAdditionalDependencies() {
|
public List<File> getResolvedAdditionalDependencies() {
|
||||||
return this.resolvedAdditionalDependencies;
|
return this.resolvedAdditionalDependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setResolvedAdditionalDependencies(
|
||||||
|
List<File> resolvedAdditionalDependencies) {
|
||||||
|
this.resolvedAdditionalDependencies = resolvedAdditionalDependencies;
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] getClassBytes(String classname) {
|
public byte[] getClassBytes(String classname) {
|
||||||
return this.classBytes.get(classname);
|
return this.classBytes.get(classname);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean wasSuccessful() {
|
public boolean wasSuccessful() {
|
||||||
return successfulCompilation;
|
return this.successfulCompilation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Class<?>> getCompiledClasses() {
|
public List<Class<?>> getCompiledClasses() {
|
||||||
return compiledClasses;
|
return this.compiledClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCompiledClasses(List<Class<?>> compiledClasses) {
|
||||||
|
this.compiledClasses = compiledClasses;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<CompilationMessage> getCompilationMessages() {
|
public List<CompilationMessage> getCompilationMessages() {
|
||||||
return Collections.unmodifiableList(compilationMessages);
|
return Collections.unmodifiableList(this.compilationMessages);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void recordCompilationMessage(CompilationMessage message) {
|
public void recordCompilationMessage(CompilationMessage message) {
|
||||||
@@ -85,15 +89,12 @@ public class CompilationResult {
|
|||||||
this.compilationMessages.addAll(messages);
|
this.compilationMessages.addAll(messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCompiledClasses(List<Class<?>> compiledClasses) {
|
|
||||||
this.compiledClasses = compiledClasses;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder s = new StringBuilder();
|
StringBuilder s = new StringBuilder();
|
||||||
s.append("Compilation result: #classes="+compiledClasses.size()+" #messages="+compilationMessages.size()+"\n");
|
s.append("Compilation result: #classes=" + this.compiledClasses.size()
|
||||||
s.append("Compiled classes:\n").append(compiledClasses).append("\n");
|
+ " #messages=" + this.compilationMessages.size() + "\n");
|
||||||
s.append("Compilation messages:\n").append(compilationMessages).append("\n");
|
s.append("Compiled classes:\n").append(this.compiledClasses).append("\n");
|
||||||
|
s.append("Compilation messages:\n").append(this.compilationMessages).append("\n");
|
||||||
return s.toString();
|
return s.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -24,25 +24,30 @@ package org.springframework.cloud.function.compiler.java;
|
|||||||
public class CompiledClassDefinition {
|
public class CompiledClassDefinition {
|
||||||
|
|
||||||
private byte[] bytes;
|
private byte[] bytes;
|
||||||
|
|
||||||
private String filename;
|
private String filename;
|
||||||
|
|
||||||
private String classname;
|
private String classname;
|
||||||
|
|
||||||
public CompiledClassDefinition(String filename, byte[] bytes) {
|
public CompiledClassDefinition(String filename, byte[] bytes) {
|
||||||
this.filename = filename;
|
this.filename = filename;
|
||||||
this.bytes = bytes;
|
this.bytes = bytes;
|
||||||
this.classname = filename;
|
this.classname = filename;
|
||||||
if (classname.startsWith("/")) {
|
if (this.classname.startsWith("/")) {
|
||||||
classname = classname.substring(1);
|
this.classname = this.classname.substring(1);
|
||||||
}
|
}
|
||||||
classname = classname.replace('/', '.').substring(0, classname.length() - 6); // strip off .class
|
this.classname = this.classname.replace('/', '.').substring(0,
|
||||||
|
this.classname.length() - 6); // strip
|
||||||
|
// off
|
||||||
|
// .class
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return filename;
|
return this.filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getBytes() {
|
public byte[] getBytes() {
|
||||||
return bytes;
|
return this.bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -36,7 +36,6 @@ import com.google.inject.AbstractModule;
|
|||||||
import com.google.inject.Provides;
|
import com.google.inject.Provides;
|
||||||
import com.google.inject.name.Named;
|
import com.google.inject.name.Named;
|
||||||
import com.google.inject.name.Names;
|
import com.google.inject.name.Names;
|
||||||
|
|
||||||
import org.apache.maven.artifact.repository.ArtifactRepository;
|
import org.apache.maven.artifact.repository.ArtifactRepository;
|
||||||
import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
|
import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
|
||||||
import org.apache.maven.artifact.repository.MavenArtifactRepository;
|
import org.apache.maven.artifact.repository.MavenArtifactRepository;
|
||||||
@@ -98,24 +97,32 @@ import org.eclipse.sisu.plexus.ClassRealmManager;
|
|||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
public class DependencyResolver {
|
/**
|
||||||
|
* Dependency resolver utility class.
|
||||||
|
*
|
||||||
|
* @author Andy Clement
|
||||||
|
*/
|
||||||
|
public final class DependencyResolver {
|
||||||
|
|
||||||
private static DependencyResolver instance = new DependencyResolver();
|
private static DependencyResolver instance = new DependencyResolver();
|
||||||
|
|
||||||
private static Properties globals;
|
private static Properties globals;
|
||||||
|
|
||||||
|
private final Object lock = new Object();
|
||||||
|
|
||||||
private LocalRepositoryManagerFactory localRepositoryManagerFactory;
|
private LocalRepositoryManagerFactory localRepositoryManagerFactory;
|
||||||
|
|
||||||
private PlexusContainer container;
|
private PlexusContainer container;
|
||||||
|
|
||||||
private final Object lock = new Object();
|
|
||||||
|
|
||||||
private ProjectBuilder projectBuilder;
|
private ProjectBuilder projectBuilder;
|
||||||
|
|
||||||
private RepositorySystem repositorySystem;
|
private RepositorySystem repositorySystem;
|
||||||
|
|
||||||
private MavenSettings settings;
|
private MavenSettings settings;
|
||||||
|
|
||||||
|
private DependencyResolver() {
|
||||||
|
}
|
||||||
|
|
||||||
public static DependencyResolver instance() {
|
public static DependencyResolver instance() {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
@@ -124,12 +131,13 @@ public class DependencyResolver {
|
|||||||
instance = new DependencyResolver();
|
instance = new DependencyResolver();
|
||||||
}
|
}
|
||||||
|
|
||||||
private DependencyResolver() {
|
static Properties getGlobals() {
|
||||||
|
return globals;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initialize() {
|
private void initialize() {
|
||||||
if (this.container == null) {
|
if (this.container == null) {
|
||||||
synchronized (lock) {
|
synchronized (this.lock) {
|
||||||
if (this.container == null) {
|
if (this.container == null) {
|
||||||
ClassWorld classWorld = new ClassWorld("plexus.core",
|
ClassWorld classWorld = new ClassWorld("plexus.core",
|
||||||
Thread.currentThread().getContextClassLoader());
|
Thread.currentThread().getContextClassLoader());
|
||||||
@@ -142,14 +150,14 @@ public class DependencyResolver {
|
|||||||
try {
|
try {
|
||||||
container = new DefaultPlexusContainer(config, new AetherModule(),
|
container = new DefaultPlexusContainer(config, new AetherModule(),
|
||||||
new DependencyResolutionModule());
|
new DependencyResolutionModule());
|
||||||
localRepositoryManagerFactory = container
|
this.localRepositoryManagerFactory = container
|
||||||
.lookup(LocalRepositoryManagerFactory.class);
|
.lookup(LocalRepositoryManagerFactory.class);
|
||||||
container.addComponent(
|
container.addComponent(
|
||||||
new ClassRealmManager((MutablePlexusContainer) container,
|
new ClassRealmManager((MutablePlexusContainer) container,
|
||||||
new DefaultBeanLocator()),
|
new DefaultBeanLocator()),
|
||||||
ClassRealmManager.class.getName());
|
ClassRealmManager.class.getName());
|
||||||
projectBuilder = container.lookup(ProjectBuilder.class);
|
this.projectBuilder = container.lookup(ProjectBuilder.class);
|
||||||
repositorySystem = container.lookup(RepositorySystem.class);
|
this.repositorySystem = container.lookup(RepositorySystem.class);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
throw new IllegalStateException("Cannot create container", e);
|
throw new IllegalStateException("Cannot create container", e);
|
||||||
@@ -172,7 +180,7 @@ public class DependencyResolver {
|
|||||||
ProjectBuildingRequest request = getProjectBuildingRequest(properties);
|
ProjectBuildingRequest request = getProjectBuildingRequest(properties);
|
||||||
request.setResolveDependencies(true);
|
request.setResolveDependencies(true);
|
||||||
synchronized (DependencyResolver.class) {
|
synchronized (DependencyResolver.class) {
|
||||||
ProjectBuildingResult result = projectBuilder
|
ProjectBuildingResult result = this.projectBuilder
|
||||||
.build(new PropertiesModelSource(properties, resource), request);
|
.build(new PropertiesModelSource(properties, resource), request);
|
||||||
DependencyResolver.globals = null;
|
DependencyResolver.globals = null;
|
||||||
DependencyResolutionResult dependencies = result
|
DependencyResolutionResult dependencies = result
|
||||||
@@ -224,7 +232,7 @@ public class DependencyResolver {
|
|||||||
projectBuildingRequest.setRepositoryMerging(RepositoryMerging.REQUEST_DOMINANT);
|
projectBuildingRequest.setRepositoryMerging(RepositoryMerging.REQUEST_DOMINANT);
|
||||||
projectBuildingRequest.setRemoteRepositories(mavenRepositories(properties));
|
projectBuildingRequest.setRemoteRepositories(mavenRepositories(properties));
|
||||||
projectBuildingRequest.getRemoteRepositories()
|
projectBuildingRequest.getRemoteRepositories()
|
||||||
.addAll(mavenRepositories(settings));
|
.addAll(mavenRepositories(this.settings));
|
||||||
projectBuildingRequest.setRepositorySession(session);
|
projectBuildingRequest.setRepositorySession(session);
|
||||||
projectBuildingRequest.setProcessPlugins(false);
|
projectBuildingRequest.setProcessPlugins(false);
|
||||||
projectBuildingRequest.setBuildStartTime(new Date());
|
projectBuildingRequest.setBuildStartTime(new Date());
|
||||||
@@ -250,8 +258,10 @@ public class DependencyResolver {
|
|||||||
|
|
||||||
private List<ArtifactRepository> mavenRepositories(Properties properties) {
|
private List<ArtifactRepository> mavenRepositories(Properties properties) {
|
||||||
List<ArtifactRepository> list = new ArrayList<>();
|
List<ArtifactRepository> list = new ArrayList<>();
|
||||||
addRepositoryIfMissing(list, "spring-snapshots", "https://repo.spring.io/libs-snapshot", true, true);
|
addRepositoryIfMissing(list, "spring-snapshots",
|
||||||
addRepositoryIfMissing(list, "central", "https://repo1.maven.org/maven2", true, false);
|
"https://repo.spring.io/libs-snapshot", true, true);
|
||||||
|
addRepositoryIfMissing(list, "central", "https://repo1.maven.org/maven2", true,
|
||||||
|
false);
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,7 +319,7 @@ public class DependencyResolver {
|
|||||||
DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
|
DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
|
||||||
LocalRepository repository = localRepository(properties);
|
LocalRepository repository = localRepository(properties);
|
||||||
session.setLocalRepositoryManager(
|
session.setLocalRepositoryManager(
|
||||||
localRepositoryManagerFactory.newInstance(session, repository));
|
this.localRepositoryManagerFactory.newInstance(session, repository));
|
||||||
applySettings(session);
|
applySettings(session);
|
||||||
ProxySelector existing = session.getProxySelector();
|
ProxySelector existing = session.getProxySelector();
|
||||||
if (existing == null || !(existing instanceof CompositeProxySelector)) {
|
if (existing == null || !(existing instanceof CompositeProxySelector)) {
|
||||||
@@ -322,7 +332,7 @@ public class DependencyResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void applySettings(DefaultRepositorySystemSession session) {
|
private void applySettings(DefaultRepositorySystemSession session) {
|
||||||
MavenSettingsReader.applySettings(settings, session);
|
MavenSettingsReader.applySettings(this.settings, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LocalRepository localRepository(Properties properties) {
|
private LocalRepository localRepository(Properties properties) {
|
||||||
@@ -338,7 +348,7 @@ public class DependencyResolver {
|
|||||||
try {
|
try {
|
||||||
ProjectBuildingRequest request = getProjectBuildingRequest(properties);
|
ProjectBuildingRequest request = getProjectBuildingRequest(properties);
|
||||||
request.setResolveDependencies(false);
|
request.setResolveDependencies(false);
|
||||||
ProjectBuildingResult result = projectBuilder
|
ProjectBuildingResult result = this.projectBuilder
|
||||||
.build(new PropertiesModelSource(properties, resource), request);
|
.build(new PropertiesModelSource(properties, resource), request);
|
||||||
return result.getProject().getModel();
|
return result.getProject().getModel();
|
||||||
}
|
}
|
||||||
@@ -383,13 +393,10 @@ public class DependencyResolver {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Properties getGlobals() {
|
|
||||||
return globals;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
private static final class PropertiesModelSource
|
private static final class PropertiesModelSource
|
||||||
implements org.apache.maven.model.building.ModelSource {
|
implements org.apache.maven.model.building.ModelSource {
|
||||||
|
|
||||||
private final Properties properties;
|
private final Properties properties;
|
||||||
|
|
||||||
private final Resource resource;
|
private final Resource resource;
|
||||||
@@ -401,8 +408,8 @@ public class DependencyResolver {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream getInputStream() throws IOException {
|
public InputStream getInputStream() throws IOException {
|
||||||
DependencyResolver.globals = properties;
|
DependencyResolver.globals = this.properties;
|
||||||
return new BufferedInputStream(resource.getInputStream()) {
|
return new BufferedInputStream(this.resource.getInputStream()) {
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
DependencyResolver.globals = null;
|
DependencyResolver.globals = null;
|
||||||
@@ -413,8 +420,9 @@ public class DependencyResolver {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getLocation() {
|
public String getLocation() {
|
||||||
return resource.getDescription();
|
return this.resource.getDescription();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -37,6 +37,7 @@ import javax.tools.JavaFileObject;
|
|||||||
public class DirEntryJavaFileObject implements JavaFileObject {
|
public class DirEntryJavaFileObject implements JavaFileObject {
|
||||||
|
|
||||||
private File file;
|
private File file;
|
||||||
|
|
||||||
private File basedir;
|
private File basedir;
|
||||||
|
|
||||||
public DirEntryJavaFileObject(File basedir, File file) {
|
public DirEntryJavaFileObject(File basedir, File file) {
|
||||||
@@ -46,22 +47,23 @@ public class DirEntryJavaFileObject implements JavaFileObject {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URI toUri() {
|
public URI toUri() {
|
||||||
return file.toURI();
|
return this.file.toURI();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the path of the file relative to the base directory, for example: a/b/c/D.class
|
* @return the path of the file relative to the base directory, for example:
|
||||||
|
* a/b/c/D.class
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
String basedirPath = basedir.getPath();
|
String basedirPath = this.basedir.getPath();
|
||||||
String filePath = file.getPath();
|
String filePath = this.file.getPath();
|
||||||
return filePath.substring(basedirPath.length()+1);
|
return filePath.substring(basedirPath.length() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream openInputStream() throws IOException {
|
public InputStream openInputStream() throws IOException {
|
||||||
return new FileInputStream(file);
|
return new FileInputStream(this.file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -72,13 +74,15 @@ public class DirEntryJavaFileObject implements JavaFileObject {
|
|||||||
@Override
|
@Override
|
||||||
public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
|
public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
|
||||||
// It is bytecode
|
// It is bytecode
|
||||||
throw new UnsupportedOperationException("openReader() not supported on class file: " + getName());
|
throw new UnsupportedOperationException(
|
||||||
|
"openReader() not supported on class file: " + getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
|
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
|
||||||
// It is bytecode
|
// It is bytecode
|
||||||
throw new UnsupportedOperationException("getCharContent() not supported on class file: " + getName());
|
throw new UnsupportedOperationException(
|
||||||
|
"getCharContent() not supported on class file: " + getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -88,7 +92,7 @@ public class DirEntryJavaFileObject implements JavaFileObject {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getLastModified() {
|
public long getLastModified() {
|
||||||
return file.lastModified();
|
return this.file.lastModified();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -123,7 +127,7 @@ public class DirEntryJavaFileObject implements JavaFileObject {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return file.getName().hashCode()*37+basedir.getName().hashCode();
|
return this.file.getName().hashCode() * 37 + this.basedir.getName().hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -131,8 +135,9 @@ public class DirEntryJavaFileObject implements JavaFileObject {
|
|||||||
if (!(obj instanceof DirEntryJavaFileObject)) {
|
if (!(obj instanceof DirEntryJavaFileObject)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
DirEntryJavaFileObject that = (DirEntryJavaFileObject)obj;
|
DirEntryJavaFileObject that = (DirEntryJavaFileObject) obj;
|
||||||
return (basedir.getName().equals(that.basedir.getName())) && (file.getName().equals(that.file.getName()));
|
return (this.basedir.getName().equals(that.basedir.getName()))
|
||||||
|
&& (this.file.getName().equals(that.file.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -43,15 +43,16 @@ public class DirEnumeration implements Enumeration<File> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void computeValue() {
|
private void computeValue() {
|
||||||
if (filesToReturn == null) { // Indicates we haven't started yet
|
if (this.filesToReturn == null) { // Indicates we haven't started yet
|
||||||
filesToReturn = new ArrayList<>();
|
this.filesToReturn = new ArrayList<>();
|
||||||
directoriesToExplore = new ArrayList<>();
|
this.directoriesToExplore = new ArrayList<>();
|
||||||
visitDirectory(basedir);
|
visitDirectory(this.basedir);
|
||||||
}
|
}
|
||||||
if (filesToReturn.size() == 0) {
|
if (this.filesToReturn.size() == 0) {
|
||||||
while (filesToReturn.size() == 0 && directoriesToExplore.size() != 0) {
|
while (this.filesToReturn.size() == 0
|
||||||
File nextDir = directoriesToExplore.get(0);
|
&& this.directoriesToExplore.size() != 0) {
|
||||||
directoriesToExplore.remove(0);
|
File nextDir = this.directoriesToExplore.get(0);
|
||||||
|
this.directoriesToExplore.remove(0);
|
||||||
visitDirectory(nextDir);
|
visitDirectory(nextDir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,50 +61,52 @@ public class DirEnumeration implements Enumeration<File> {
|
|||||||
@Override
|
@Override
|
||||||
public boolean hasMoreElements() {
|
public boolean hasMoreElements() {
|
||||||
computeValue();
|
computeValue();
|
||||||
return filesToReturn.size() != 0;
|
return this.filesToReturn.size() != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public File nextElement() {
|
public File nextElement() {
|
||||||
computeValue();
|
computeValue();
|
||||||
if (filesToReturn.size()==0) {
|
if (this.filesToReturn.size() == 0) {
|
||||||
throw new NoSuchElementException();
|
throw new NoSuchElementException();
|
||||||
}
|
}
|
||||||
File toReturn = filesToReturn.get(0);
|
File toReturn = this.filesToReturn.get(0);
|
||||||
filesToReturn.remove(0);
|
this.filesToReturn.remove(0);
|
||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void visitDirectory(File dir) {
|
private void visitDirectory(File dir) {
|
||||||
File[] files = dir.listFiles();
|
File[] files = dir.listFiles();
|
||||||
if (files != null) {
|
if (files != null) {
|
||||||
for (File file: files) {
|
for (File file : files) {
|
||||||
if (file.isDirectory()) {
|
if (file.isDirectory()) {
|
||||||
directoriesToExplore.add(file);
|
this.directoriesToExplore.add(file);
|
||||||
} else {
|
}
|
||||||
filesToReturn.add(file);
|
else {
|
||||||
|
this.filesToReturn.add(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public File getDirectory() {
|
public File getDirectory() {
|
||||||
return basedir;
|
return this.basedir;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the relative path of this file to the base directory that the directory enumeration was
|
* Return the relative path of this file to the base directory that the directory
|
||||||
* started for.
|
* enumeration was started for.
|
||||||
* @param file a file discovered returned by this enumeration
|
* @param file a file discovered returned by this enumeration
|
||||||
* @return the relative path of the file (for example: a/b/c/D.class)
|
* @return the relative path of the file (for example: a/b/c/D.class)
|
||||||
*/
|
*/
|
||||||
public String getName(File file) {
|
public String getName(File file) {
|
||||||
String basedirPath = basedir.getPath();
|
String basedirPath = this.basedir.getPath();
|
||||||
String filePath = file.getPath();
|
String filePath = file.getPath();
|
||||||
if (!filePath.startsWith(basedirPath)) {
|
if (!filePath.startsWith(basedirPath)) {
|
||||||
throw new IllegalStateException("The file '"+filePath+"' is not nested below the base directory '"+basedirPath+"'");
|
throw new IllegalStateException("The file '" + filePath
|
||||||
|
+ "' is not nested below the base directory '" + basedirPath + "'");
|
||||||
}
|
}
|
||||||
return filePath.substring(basedirPath.length()+1);
|
return filePath.substring(basedirPath.length() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -42,30 +42,40 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A JavaFileObject that represents a source artifact created for compilation or an output
|
* 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
|
* artifact producing during compilation (a .class file or some other thing if an
|
||||||
* processor has run). In order to be clear what it is being used for there are static factory
|
* annotation processor has run). In order to be clear what it is being used for there are
|
||||||
* methods that ask for specific types of file.
|
* static factory methods that ask for specific types of file.
|
||||||
*
|
*
|
||||||
* @author Andy Clement
|
* @author Andy Clement
|
||||||
*/
|
*/
|
||||||
public class InMemoryJavaFileObject implements JavaFileObject {
|
public final class InMemoryJavaFileObject implements JavaFileObject {
|
||||||
|
|
||||||
private final static Logger logger = LoggerFactory.getLogger(InMemoryJavaFileObject.class);
|
private final static Logger logger = LoggerFactory
|
||||||
|
.getLogger(InMemoryJavaFileObject.class);
|
||||||
|
|
||||||
private Location location;
|
private Location location;
|
||||||
|
|
||||||
private String packageName;
|
private String packageName;
|
||||||
|
|
||||||
private String relativeName;
|
private String relativeName;
|
||||||
|
|
||||||
private FileObject sibling;
|
private FileObject sibling;
|
||||||
|
|
||||||
private String className;
|
private String className;
|
||||||
|
|
||||||
private Kind kind;
|
private Kind kind;
|
||||||
|
|
||||||
private byte[] content = null;
|
private byte[] content = null;
|
||||||
|
|
||||||
private long lastModifiedTime = 0;
|
private long lastModifiedTime = 0;
|
||||||
|
|
||||||
private URI uri = null;
|
private URI uri = null;
|
||||||
|
|
||||||
private InMemoryJavaFileObject() {}
|
private InMemoryJavaFileObject() {
|
||||||
|
}
|
||||||
|
|
||||||
public static InMemoryJavaFileObject getFileObject(Location location, String packageName, String relativeName, FileObject sibling) {
|
public static InMemoryJavaFileObject getFileObject(Location location,
|
||||||
|
String packageName, String relativeName, FileObject sibling) {
|
||||||
InMemoryJavaFileObject retval = new InMemoryJavaFileObject();
|
InMemoryJavaFileObject retval = new InMemoryJavaFileObject();
|
||||||
retval.kind = Kind.OTHER;
|
retval.kind = Kind.OTHER;
|
||||||
retval.location = location;
|
retval.location = location;
|
||||||
@@ -75,7 +85,8 @@ public class InMemoryJavaFileObject implements JavaFileObject {
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InMemoryJavaFileObject getJavaFileObject(Location location, String className, Kind kind, FileObject sibling) {
|
public static InMemoryJavaFileObject getJavaFileObject(Location location,
|
||||||
|
String className, Kind kind, FileObject sibling) {
|
||||||
InMemoryJavaFileObject retval = new InMemoryJavaFileObject();
|
InMemoryJavaFileObject retval = new InMemoryJavaFileObject();
|
||||||
retval.location = location;
|
retval.location = location;
|
||||||
retval.className = className;
|
retval.className = className;
|
||||||
@@ -84,7 +95,8 @@ public class InMemoryJavaFileObject implements JavaFileObject {
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InMemoryJavaFileObject getSourceJavaFileObject(String className, String content) {
|
public static InMemoryJavaFileObject getSourceJavaFileObject(String className,
|
||||||
|
String content) {
|
||||||
InMemoryJavaFileObject retval = new InMemoryJavaFileObject();
|
InMemoryJavaFileObject retval = new InMemoryJavaFileObject();
|
||||||
retval.location = StandardLocation.SOURCE_PATH;
|
retval.location = StandardLocation.SOURCE_PATH;
|
||||||
retval.className = className;
|
retval.className = className;
|
||||||
@@ -94,35 +106,44 @@ public class InMemoryJavaFileObject implements JavaFileObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getBytes() {
|
public byte[] getBytes() {
|
||||||
return content;
|
return this.content;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "OutputJavaFileObject: Location="+location+",className="+className+",kind="+kind+",relativeName="+relativeName+",sibling="+sibling+",packageName="+packageName;
|
return "OutputJavaFileObject: Location=" + this.location + ",className="
|
||||||
|
+ this.className + ",kind=" + this.kind + ",relativeName="
|
||||||
|
+ this.relativeName + ",sibling=" + this.sibling + ",packageName="
|
||||||
|
+ this.packageName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URI toUri() {
|
public URI toUri() {
|
||||||
// These memory based output files 'pretend' to be relative to the file system root
|
// These memory based output files 'pretend' to be relative to the file system
|
||||||
if (uri == null) {
|
// root
|
||||||
|
if (this.uri == null) {
|
||||||
String name = null;
|
String name = null;
|
||||||
if (className != null) {
|
if (this.className != null) {
|
||||||
name = className.replace('.', '/');
|
name = this.className.replace('.', '/');
|
||||||
} else if (packageName !=null && packageName.length()!=0) {
|
}
|
||||||
name = packageName.replace('.', '/')+'/'+relativeName;
|
else if (this.packageName != null && this.packageName.length() != 0) {
|
||||||
} else {
|
name = this.packageName.replace('.', '/') + '/' + this.relativeName;
|
||||||
name = relativeName;
|
}
|
||||||
|
else {
|
||||||
|
name = this.relativeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
String uriString = null;
|
String uriString = null;
|
||||||
try {
|
try {
|
||||||
uriString = "file:/"+name+kind.extension;
|
uriString = "file:/" + name + this.kind.extension;
|
||||||
uri = new URI(uriString);
|
this.uri = new URI(uriString);
|
||||||
} catch (URISyntaxException e) {
|
}
|
||||||
throw new IllegalStateException("Unexpected URISyntaxException for string '" + uriString + "'", e);
|
catch (URISyntaxException e) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Unexpected URISyntaxException for string '" + uriString + "'",
|
||||||
|
e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return uri;
|
return this.uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -132,22 +153,22 @@ public class InMemoryJavaFileObject implements JavaFileObject {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream openInputStream() throws IOException {
|
public InputStream openInputStream() throws IOException {
|
||||||
if (content == null) {
|
if (this.content == null) {
|
||||||
throw new FileNotFoundException();
|
throw new FileNotFoundException();
|
||||||
}
|
}
|
||||||
logger.debug("opening input stream for {}",getName());
|
logger.debug("opening input stream for {}", getName());
|
||||||
return new ByteArrayInputStream(content);
|
return new ByteArrayInputStream(this.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OutputStream openOutputStream() throws IOException {
|
public OutputStream openOutputStream() throws IOException {
|
||||||
logger.debug("opening output stream for {}",getName());
|
logger.debug("opening output stream for {}", getName());
|
||||||
return new ByteArrayOutputStream() {
|
return new ByteArrayOutputStream() {
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
super.close();
|
super.close();
|
||||||
lastModifiedTime = System.currentTimeMillis();
|
InMemoryJavaFileObject.this.lastModifiedTime = System.currentTimeMillis();
|
||||||
content = this.toByteArray();
|
InMemoryJavaFileObject.this.content = this.toByteArray();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -159,31 +180,34 @@ public class InMemoryJavaFileObject implements JavaFileObject {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
|
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
|
||||||
if (kind!=Kind.SOURCE) {
|
if (this.kind != Kind.SOURCE) {
|
||||||
throw new UnsupportedOperationException("getCharContent() not supported on file object: " + getName());
|
throw new UnsupportedOperationException(
|
||||||
|
"getCharContent() not supported on file object: " + getName());
|
||||||
}
|
}
|
||||||
// Not yet supporting encodings
|
// Not yet supporting encodings
|
||||||
return (content==null?null:new String(content));
|
return (this.content == null ? null : new String(this.content));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Writer openWriter() throws IOException {
|
public Writer openWriter() throws IOException {
|
||||||
// Let's not enforce this restriction right now
|
// Let's not enforce this restriction right now
|
||||||
// if (kind == Kind.CLASS) {
|
// if (kind == Kind.CLASS) {
|
||||||
// throw new UnsupportedOperationException("openWriter() not supported on file object: " + getName());
|
// throw new UnsupportedOperationException("openWriter() not supported on file
|
||||||
// }
|
// object: " + getName());
|
||||||
|
// }
|
||||||
return new CharArrayWriter() {
|
return new CharArrayWriter() {
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
lastModifiedTime = System.currentTimeMillis();
|
InMemoryJavaFileObject.this.lastModifiedTime = System.currentTimeMillis();
|
||||||
content = new String(toCharArray()).getBytes(); // Ignoring encoding...
|
InMemoryJavaFileObject.this.content = new String(toCharArray())
|
||||||
|
.getBytes(); // Ignoring encoding...
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getLastModified() {
|
public long getLastModified() {
|
||||||
return lastModifiedTime;
|
return this.lastModifiedTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -193,15 +217,14 @@ public class InMemoryJavaFileObject implements JavaFileObject {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Kind getKind() {
|
public Kind getKind() {
|
||||||
return kind;
|
return this.kind;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNameCompatible(String simpleName, Kind kind) {
|
public boolean isNameCompatible(String simpleName, Kind kind) {
|
||||||
String baseName = simpleName + kind.extension;
|
String baseName = simpleName + kind.extension;
|
||||||
return kind.equals(getKind())
|
return kind.equals(getKind()) && (baseName.equals(toUri().getPath())
|
||||||
&& (baseName.equals(toUri().getPath())
|
|| toUri().getPath().endsWith("/" + baseName));
|
||||||
|| toUri().getPath().endsWith("/" + baseName));
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NestingKind getNestingKind() {
|
public NestingKind getNestingKind() {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -33,14 +33,14 @@ import javax.tools.JavaFileObject;
|
|||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import org.springframework.cloud.function.compiler.java.MemoryBasedJavaFileManager.CompilationInfoCache;
|
import org.springframework.cloud.function.compiler.java.MemoryBasedJavaFileManager.CompilationInfoCache;
|
||||||
import org.springframework.cloud.function.compiler.java.MemoryBasedJavaFileManager.CompilationInfoCache.ArchiveInfo;
|
import org.springframework.cloud.function.compiler.java.MemoryBasedJavaFileManager.CompilationInfoCache.ArchiveInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterable that will produce an iterator that returns classes found
|
* Iterable that will produce an iterator that returns classes found on a specified
|
||||||
* on a specified classpath that meet specified criteria. For jars it finds, the
|
* classpath that meet specified criteria. For jars it finds, the iterator will go into
|
||||||
* iterator will go into nested jars - this handles the situation with a
|
* nested jars - this handles the situation with a spring boot uberjar.
|
||||||
* spring boot uberjar.
|
|
||||||
*
|
*
|
||||||
* @author Andy Clement
|
* @author Andy Clement
|
||||||
*/
|
*/
|
||||||
@@ -55,10 +55,13 @@ public class IterableClasspath extends CloseableFilterableJavaFileObjectIterable
|
|||||||
/**
|
/**
|
||||||
* @param compilationInfoCache cache of info that may help accelerate compilation
|
* @param compilationInfoCache cache of info that may help accelerate compilation
|
||||||
* @param classpath a classpath of jars/directories
|
* @param classpath a classpath of jars/directories
|
||||||
* @param packageNameFilter an optional package name if choosing to filter (e.g. com.example)
|
* @param packageNameFilter an optional package name if choosing to filter (e.g.
|
||||||
* @param includeSubpackages if true, include results in subpackages of the specified package filter
|
* com.example)
|
||||||
|
* @param includeSubpackages if true, include results in subpackages of the specified
|
||||||
|
* package filter
|
||||||
*/
|
*/
|
||||||
IterableClasspath(CompilationInfoCache compilationInfoCache, String classpath, String packageNameFilter, boolean includeSubpackages) {
|
IterableClasspath(CompilationInfoCache compilationInfoCache, String classpath,
|
||||||
|
String packageNameFilter, boolean includeSubpackages) {
|
||||||
super(compilationInfoCache, packageNameFilter, includeSubpackages);
|
super(compilationInfoCache, packageNameFilter, includeSubpackages);
|
||||||
StringTokenizer tokenizer = new StringTokenizer(classpath, File.pathSeparator);
|
StringTokenizer tokenizer = new StringTokenizer(classpath, File.pathSeparator);
|
||||||
while (tokenizer.hasMoreElements()) {
|
while (tokenizer.hasMoreElements()) {
|
||||||
@@ -66,177 +69,230 @@ public class IterableClasspath extends CloseableFilterableJavaFileObjectIterable
|
|||||||
File f = new File(nextEntry);
|
File f = new File(nextEntry);
|
||||||
if (f.exists()) {
|
if (f.exists()) {
|
||||||
// Skip iterating over archives that cannot possibly match the filter
|
// Skip iterating over archives that cannot possibly match the filter
|
||||||
if (this.packageNameFilter != null && this.packageNameFilter.length() > 0) {
|
if (this.packageNameFilter != null
|
||||||
|
&& this.packageNameFilter.length() > 0) {
|
||||||
ArchiveInfo archiveInfo = compilationInfoCache.getArchiveInfoFor(f);
|
ArchiveInfo archiveInfo = compilationInfoCache.getArchiveInfoFor(f);
|
||||||
if (archiveInfo != null && !archiveInfo.containsPackage(this.packageNameFilter, this.includeSubpackages)) {
|
if (archiveInfo != null
|
||||||
|
&& !archiveInfo.containsPackage(this.packageNameFilter,
|
||||||
|
this.includeSubpackages)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
classpathEntries.add(f);
|
this.classpathEntries.add(f);
|
||||||
} else {
|
}
|
||||||
logger.debug("path element does not exist {}",f);
|
else {
|
||||||
|
logger.debug("path element does not exist {}", f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
for (ZipFile openArchive : openArchives) {
|
for (ZipFile openArchive : this.openArchives) {
|
||||||
try {
|
try {
|
||||||
openArchive.close();
|
openArchive.close();
|
||||||
} catch (IOException ioe) {
|
}
|
||||||
logger.debug("Unexpected error closing archive {}",openArchive,ioe);
|
catch (IOException ioe) {
|
||||||
|
logger.debug("Unexpected error closing archive {}", openArchive, ioe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
openArchives.clear();
|
this.openArchives.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Iterator<JavaFileObject> iterator() {
|
public Iterator<JavaFileObject> iterator() {
|
||||||
return new ClasspathEntriesIterator();
|
return new ClasspathEntriesIterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClasspathEntriesIterator implements Iterator<JavaFileObject> {
|
public void reset() {
|
||||||
private int currentClasspathEntriesIndex = 0;
|
close();
|
||||||
|
|
||||||
// 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 (nextEntry == null) {
|
|
||||||
try {
|
|
||||||
while (openArchive!=null || openDirectory!=null || openJrt != null || currentClasspathEntriesIndex < classpathEntries.size()) {
|
|
||||||
if (openArchive == null && openDirectory == null && openJrt == null) {
|
|
||||||
// Open the next item
|
|
||||||
File nextFile = classpathEntries.get(currentClasspathEntriesIndex);
|
|
||||||
if (nextFile.isDirectory()) {
|
|
||||||
openDirectory = nextFile;
|
|
||||||
openDirectoryEnumeration = new DirEnumeration(nextFile);
|
|
||||||
} else if (nextFile.getName().endsWith("jrt-fs.jar")) {
|
|
||||||
openJrt = nextFile;
|
|
||||||
openJrtEnumeration = new JrtFsEnumeration(nextFile,null);
|
|
||||||
} else {
|
|
||||||
openFile = nextFile;
|
|
||||||
openArchive = new ZipFile(nextFile);
|
|
||||||
openArchives.add(openArchive);
|
|
||||||
openArchiveEnumeration = new Stack<Enumeration<? extends ZipEntry>>();
|
|
||||||
openArchiveEnumeration.push(openArchive.entries());
|
|
||||||
}
|
|
||||||
currentClasspathEntriesIndex++;
|
|
||||||
}
|
|
||||||
if (openArchiveEnumeration != null) {
|
|
||||||
while (!openArchiveEnumeration.isEmpty()) {
|
|
||||||
while (openArchiveEnumeration.peek().hasMoreElements()) {
|
|
||||||
ZipEntry entry = openArchiveEnumeration.peek().nextElement();
|
|
||||||
String entryName = entry.getName();
|
|
||||||
if (accept(entryName)) {
|
|
||||||
if (nestedZip!=null) {
|
|
||||||
nextEntry = new NestedZipEntryJavaFileObject(openFile, openArchive,nestedZip, entry);
|
|
||||||
} else {
|
|
||||||
nextEntry = new ZipEntryJavaFileObject(openFile, openArchive, entry);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
} else if (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(openArchive.getInputStream(entry));
|
|
||||||
Enumeration<? extends ZipEntry> nestedZipEnumerator = new ZipEnumerator(zis);
|
|
||||||
nestedZip = entry;
|
|
||||||
openArchiveEnumeration.push(nestedZipEnumerator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
openArchiveEnumeration.pop();
|
|
||||||
if (nestedZip ==null) { openArchive = null; openFile = null; }
|
|
||||||
else nestedZip = null;
|
|
||||||
}
|
|
||||||
openArchiveEnumeration = null;
|
|
||||||
openArchive = null;
|
|
||||||
openFile = null;
|
|
||||||
} else if (openDirectoryEnumeration != null) {
|
|
||||||
while (openDirectoryEnumeration.hasMoreElements()) {
|
|
||||||
File entry = openDirectoryEnumeration.nextElement();
|
|
||||||
String name = openDirectoryEnumeration.getName(entry);
|
|
||||||
if (accept(name)) {
|
|
||||||
nextEntry = new DirEntryJavaFileObject(openDirectoryEnumeration.getDirectory(), entry);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
openDirectoryEnumeration = null;
|
|
||||||
openDirectory = null;
|
|
||||||
} else if (openJrtEnumeration != null) {
|
|
||||||
while (openJrtEnumeration.hasMoreElements()) {
|
|
||||||
JrtEntryJavaFileObject jrtEntry = openJrtEnumeration.nextElement();
|
|
||||||
String name = openJrtEnumeration.getName(jrtEntry);
|
|
||||||
if (accept(name)) {
|
|
||||||
nextEntry = jrtEntry;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
openJrtEnumeration = null;
|
|
||||||
openJrt = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
logger.debug("Unexpected error whilst processing classpath entries",ioe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasNext() {
|
|
||||||
findNext();
|
|
||||||
return nextEntry != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JavaFileObject next() {
|
|
||||||
findNext();
|
|
||||||
if (nextEntry == null) {
|
|
||||||
throw new NoSuchElementException();
|
|
||||||
}
|
|
||||||
JavaFileObject retval = nextEntry;
|
|
||||||
nextEntry = null;
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ZipEnumerator implements Enumeration<ZipEntry>{
|
static class ZipEnumerator implements Enumeration<ZipEntry> {
|
||||||
|
|
||||||
private ZipInputStream zis;
|
private ZipInputStream zis;
|
||||||
|
|
||||||
private ZipEntry nextEntry = null;
|
private ZipEntry nextEntry = null;
|
||||||
|
|
||||||
public ZipEnumerator(ZipInputStream zis) {
|
ZipEnumerator(ZipInputStream zis) {
|
||||||
this.zis = zis;
|
this.zis = zis;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasMoreElements() {
|
public boolean hasMoreElements() {
|
||||||
try {
|
try {
|
||||||
nextEntry = zis.getNextEntry();
|
this.nextEntry = this.zis.getNextEntry();
|
||||||
} catch (IOException ioe) {
|
|
||||||
nextEntry=null;
|
|
||||||
}
|
}
|
||||||
return nextEntry!=null;
|
catch (IOException ioe) {
|
||||||
|
this.nextEntry = null;
|
||||||
|
}
|
||||||
|
return this.nextEntry != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ZipEntry nextElement() {
|
public ZipEntry nextElement() {
|
||||||
ZipEntry retval = nextEntry;
|
ZipEntry retval = this.nextEntry;
|
||||||
nextEntry = null;
|
this.nextEntry = null;
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset() {
|
class ClasspathEntriesIterator implements Iterator<JavaFileObject> {
|
||||||
close();
|
|
||||||
|
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,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2018 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -27,85 +27,88 @@ import javax.tools.JavaFileObject;
|
|||||||
import org.springframework.cloud.function.compiler.java.MemoryBasedJavaFileManager.CompilationInfoCache;
|
import org.springframework.cloud.function.compiler.java.MemoryBasedJavaFileManager.CompilationInfoCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterable that will produce an iterator that returns classes found
|
* Iterable that will produce an iterator that returns classes found in a specific module
|
||||||
* in a specific module tree within the Java runtime image that exists in
|
* tree within the Java runtime image that exists in Java 9 and later.
|
||||||
* Java 9 and later.
|
|
||||||
*
|
*
|
||||||
* @author Andy Clement
|
* @author Andy Clement
|
||||||
*/
|
*/
|
||||||
public class IterableJrtModule extends CloseableFilterableJavaFileObjectIterable {
|
public class IterableJrtModule extends CloseableFilterableJavaFileObjectIterable {
|
||||||
|
|
||||||
// private static Logger logger = LoggerFactory.getLogger(IterableJrtModule.class);
|
// private static Logger logger = LoggerFactory.getLogger(IterableJrtModule.class);
|
||||||
|
|
||||||
private Path moduleRootPath;
|
|
||||||
|
|
||||||
Map<String, JrtFsEnumeration> walkers = new HashMap<>();
|
Map<String, JrtFsEnumeration> walkers = new HashMap<>();
|
||||||
|
|
||||||
|
private Path moduleRootPath;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param compilationInfoCache cache of info that may help accelerate compilation
|
* @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 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 packageNameFilter an optional package name if choosing to filter (e.g.
|
||||||
* @param includeSubpackages if true, include results in subpackages of the specified package filter
|
* com.example)
|
||||||
|
* @param includeSubpackages if true, include results in subpackages of the specified
|
||||||
|
* package filter
|
||||||
*/
|
*/
|
||||||
public IterableJrtModule(CompilationInfoCache compilationInfoCache, Path moduleRootPath, String packageNameFilter,
|
public IterableJrtModule(CompilationInfoCache compilationInfoCache,
|
||||||
boolean includeSubpackages) {
|
Path moduleRootPath, String packageNameFilter, boolean includeSubpackages) {
|
||||||
super(compilationInfoCache, packageNameFilter, includeSubpackages);
|
super(compilationInfoCache, packageNameFilter, includeSubpackages);
|
||||||
this.moduleRootPath = moduleRootPath;
|
this.moduleRootPath = moduleRootPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Iterator<JavaFileObject> iterator() {
|
public Iterator<JavaFileObject> iterator() {
|
||||||
JrtFsEnumeration jrtFsWalker = walkers.get(moduleRootPath.toString());
|
JrtFsEnumeration jrtFsWalker = this.walkers.get(this.moduleRootPath.toString());
|
||||||
if (jrtFsWalker == null) {
|
if (jrtFsWalker == null) {
|
||||||
jrtFsWalker = new JrtFsEnumeration(null, moduleRootPath);
|
jrtFsWalker = new JrtFsEnumeration(null, this.moduleRootPath);
|
||||||
walkers.put(moduleRootPath.toString(), jrtFsWalker);
|
this.walkers.put(this.moduleRootPath.toString(), jrtFsWalker);
|
||||||
}
|
}
|
||||||
jrtFsWalker.reset();
|
jrtFsWalker.reset();
|
||||||
return new IteratorOverJrtFsEnumeration(jrtFsWalker);
|
return new IteratorOverJrtFsEnumeration(jrtFsWalker);
|
||||||
}
|
}
|
||||||
|
|
||||||
class IteratorOverJrtFsEnumeration implements Iterator<JavaFileObject> {
|
|
||||||
|
|
||||||
private JavaFileObject nextEntry = null;
|
|
||||||
|
|
||||||
private JrtFsEnumeration jrtEnumeration;
|
|
||||||
|
|
||||||
public IteratorOverJrtFsEnumeration(JrtFsEnumeration jrtFsWalker) {
|
|
||||||
this.jrtEnumeration = jrtFsWalker;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void findNext() {
|
|
||||||
if (nextEntry == null) {
|
|
||||||
while (jrtEnumeration.hasMoreElements()) {
|
|
||||||
JrtEntryJavaFileObject jrtEntry = jrtEnumeration.nextElement();
|
|
||||||
String name = jrtEnumeration.getName(jrtEntry);
|
|
||||||
if (accept(name)) {
|
|
||||||
nextEntry = jrtEntry;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasNext() {
|
|
||||||
findNext();
|
|
||||||
return nextEntry != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JavaFileObject next() {
|
|
||||||
findNext();
|
|
||||||
if (nextEntry == null) {
|
|
||||||
throw new NoSuchElementException();
|
|
||||||
}
|
|
||||||
JavaFileObject retval = nextEntry;
|
|
||||||
nextEntry = null;
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset() {
|
public void reset() {
|
||||||
close();
|
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,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2018 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -31,39 +31,44 @@ import javax.lang.model.element.NestingKind;
|
|||||||
import javax.tools.JavaFileObject;
|
import javax.tools.JavaFileObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A JavaFileObject that represents a class from the Java runtime as packaged in Java 9 and later.
|
* A JavaFileObject that represents a class from the Java runtime as packaged in Java 9
|
||||||
|
* and later.
|
||||||
*
|
*
|
||||||
* @author Andy Clement
|
* @author Andy Clement
|
||||||
*/
|
*/
|
||||||
public class JrtEntryJavaFileObject implements JavaFileObject {
|
public class JrtEntryJavaFileObject implements JavaFileObject {
|
||||||
|
|
||||||
private String pathToClassString;
|
private String pathToClassString;
|
||||||
|
|
||||||
private Path path;
|
private Path path;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param path entry in the Java runtime filesystem, for example '/modules/java.base/java/lang/Object.class'
|
* @param path entry in the Java runtime filesystem, for example
|
||||||
|
* '/modules/java.base/java/lang/Object.class'
|
||||||
*/
|
*/
|
||||||
public JrtEntryJavaFileObject(Path path) {
|
public JrtEntryJavaFileObject(Path path) {
|
||||||
this.pathToClassString = path.subpath(2, path.getNameCount()).toString(); // e.g. java/lang/Object.class
|
this.pathToClassString = path.subpath(2, path.getNameCount()).toString(); // e.g.
|
||||||
|
// java/lang/Object.class
|
||||||
this.path = path;
|
this.path = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URI toUri() {
|
public URI toUri() {
|
||||||
return path.toUri();
|
return this.path.toUri();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the path of the file relative to the base directory, for example: a/b/c/D.class
|
* @return the path of the file relative to the base directory, for example:
|
||||||
|
* a/b/c/D.class
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return pathToClassString;
|
return this.pathToClassString;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream openInputStream() throws IOException {
|
public InputStream openInputStream() throws IOException {
|
||||||
byte[] bytes = Files.readAllBytes(path);
|
byte[] bytes = Files.readAllBytes(this.path);
|
||||||
return new ByteArrayInputStream(bytes);
|
return new ByteArrayInputStream(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,13 +80,15 @@ public class JrtEntryJavaFileObject implements JavaFileObject {
|
|||||||
@Override
|
@Override
|
||||||
public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
|
public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
|
||||||
// It is bytecode
|
// It is bytecode
|
||||||
throw new UnsupportedOperationException("openReader() not supported on class file: " + getName());
|
throw new UnsupportedOperationException(
|
||||||
|
"openReader() not supported on class file: " + getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
|
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
|
||||||
// It is bytecode
|
// It is bytecode
|
||||||
throw new UnsupportedOperationException("getCharContent() not supported on class file: " + getName());
|
throw new UnsupportedOperationException(
|
||||||
|
"getCharContent() not supported on class file: " + getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -92,9 +99,12 @@ public class JrtEntryJavaFileObject implements JavaFileObject {
|
|||||||
@Override
|
@Override
|
||||||
public long getLastModified() {
|
public long getLastModified() {
|
||||||
try {
|
try {
|
||||||
return Files.getLastModifiedTime(path).toMillis();
|
return Files.getLastModifiedTime(this.path).toMillis();
|
||||||
} catch (IOException ioe) {
|
}
|
||||||
throw new RuntimeException("Unable to determine last modified time of "+pathToClassString, ioe);
|
catch (IOException ioe) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Unable to determine last modified time of " + this.pathToClassString,
|
||||||
|
ioe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,12 +148,12 @@ public class JrtEntryJavaFileObject implements JavaFileObject {
|
|||||||
if (!(obj instanceof JrtEntryJavaFileObject)) {
|
if (!(obj instanceof JrtEntryJavaFileObject)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
JrtEntryJavaFileObject that = (JrtEntryJavaFileObject)obj;
|
JrtEntryJavaFileObject that = (JrtEntryJavaFileObject) obj;
|
||||||
return (getName().equals(that.getName()));
|
return (getName().equals(that.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPathToClassString() {
|
public String getPathToClassString() {
|
||||||
return pathToClassString;
|
return this.pathToClassString;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2018 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.function.compiler.java;
|
package org.springframework.cloud.function.compiler.java;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -31,14 +32,15 @@ import java.util.List;
|
|||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Walks a JrtFS treating it like a directory (to avoid overcomplicating the walking
|
* Walks a JrtFS treating it like a directory (to avoid overcomplicating the walking logic
|
||||||
* logic in IterableClasspath)
|
* in IterableClasspath).
|
||||||
*
|
*
|
||||||
* @author Andy Clement
|
* @author Andy Clement
|
||||||
*/
|
*/
|
||||||
public class JrtFsEnumeration implements Enumeration<JrtEntryJavaFileObject> {
|
public class JrtFsEnumeration implements Enumeration<JrtEntryJavaFileObject> {
|
||||||
|
|
||||||
// private final static Logger logger = LoggerFactory.getLogger(JrtFsEnumeration.class);
|
// private final static Logger logger =
|
||||||
|
// LoggerFactory.getLogger(JrtFsEnumeration.class);
|
||||||
|
|
||||||
private static URI JRT_URI = URI.create("jrt:/"); //$NON-NLS-1$
|
private static URI JRT_URI = URI.create("jrt:/"); //$NON-NLS-1$
|
||||||
|
|
||||||
@@ -57,63 +59,56 @@ public class JrtFsEnumeration implements Enumeration<JrtEntryJavaFileObject> {
|
|||||||
ensureInitialized();
|
ensureInitialized();
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|
||||||
jfos.add(new JrtEntryJavaFileObject(file));
|
|
||||||
}
|
|
||||||
return FileVisitResult.CONTINUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ensureInitialized() {
|
private void ensureInitialized() {
|
||||||
synchronized (initialized) {
|
synchronized (this.initialized) {
|
||||||
if (initialized) {
|
if (this.initialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
FileCacheBuilderVisitor visitor = new FileCacheBuilderVisitor();
|
FileCacheBuilderVisitor visitor = new FileCacheBuilderVisitor();
|
||||||
if (pathWithinJrt != null) {
|
if (this.pathWithinJrt != null) {
|
||||||
try {
|
try {
|
||||||
Files.walkFileTree(pathWithinJrt, visitor);
|
Files.walkFileTree(this.pathWithinJrt, visitor);
|
||||||
// System.out.println("JrtFs enumeration for '"+pathWithinJrt+"' with #"+jfos.size()+" entries");
|
// System.out.println("JrtFs enumeration for '"+pathWithinJrt+"' with
|
||||||
} catch (IOException e) {
|
// #"+jfos.size()+" entries");
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
Iterable<java.nio.file.Path> roots = fs.getRootDirectories();
|
Iterable<java.nio.file.Path> roots = fs.getRootDirectories();
|
||||||
try {
|
try {
|
||||||
for (java.nio.file.Path path : roots) {
|
for (java.nio.file.Path path : roots) {
|
||||||
Files.walkFileTree(path, visitor);
|
Files.walkFileTree(path, visitor);
|
||||||
}
|
}
|
||||||
// System.out.println("JrtFs enumeration initialized with #"+jfos.size()+" entries");
|
// System.out.println("JrtFs enumeration initialized with
|
||||||
} catch (IOException e) {
|
// #"+jfos.size()+" entries");
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
initialized = true;
|
this.initialized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasMoreElements() {
|
public boolean hasMoreElements() {
|
||||||
return counter < jfos.size();
|
return this.counter < this.jfos.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JrtEntryJavaFileObject nextElement() {
|
public JrtEntryJavaFileObject nextElement() {
|
||||||
if (counter>=jfos.size()) {
|
if (this.counter >= this.jfos.size()) {
|
||||||
throw new NoSuchElementException();
|
throw new NoSuchElementException();
|
||||||
}
|
}
|
||||||
JrtEntryJavaFileObject toReturn = jfos.get(counter++);
|
JrtEntryJavaFileObject toReturn = this.jfos.get(this.counter++);
|
||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the relative path of this file to the base directory that the directory enumeration was
|
* Return the relative path of this file to the base directory that the directory
|
||||||
* started for.
|
* enumeration was started for.
|
||||||
* @param file a file discovered returned by this enumeration
|
* @param file a file discovered returned by this enumeration
|
||||||
* @return the relative path of the file (for example: a/b/c/D.class)
|
* @return the relative path of the file (for example: a/b/c/D.class)
|
||||||
*/
|
*/
|
||||||
@@ -122,7 +117,24 @@ public class JrtFsEnumeration implements Enumeration<JrtEntryJavaFileObject> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void reset() {
|
public void reset() {
|
||||||
counter = 0;
|
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,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -58,17 +58,6 @@ public class MavenSettingsReader {
|
|||||||
this.homeDir = homeDir;
|
this.homeDir = homeDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void applySettings(MavenSettings settings,
|
public static void applySettings(MavenSettings settings,
|
||||||
DefaultRepositorySystemSession session) {
|
DefaultRepositorySystemSession session) {
|
||||||
if (settings.getLocalRepository() != null) {
|
if (settings.getLocalRepository() != null) {
|
||||||
@@ -89,6 +78,17 @@ public class MavenSettingsReader {
|
|||||||
session.setProxySelector(settings.getProxySelector());
|
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() {
|
private Settings loadSettings() {
|
||||||
File settingsFile = new File(this.homeDir, ".m2/settings.xml");
|
File settingsFile = new File(this.homeDir, ".m2/settings.xml");
|
||||||
if (settingsFile.exists()) {
|
if (settingsFile.exists()) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -55,6 +55,7 @@ import org.eclipse.aether.artifact.DefaultArtifact;
|
|||||||
import org.eclipse.aether.graph.Dependency;
|
import org.eclipse.aether.graph.Dependency;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import org.springframework.cloud.function.compiler.java.IterableClasspath.ZipEnumerator;
|
import org.springframework.cloud.function.compiler.java.IterableClasspath.ZipEnumerator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,6 +75,10 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
|
|||||||
private static Logger logger = LoggerFactory
|
private static Logger logger = LoggerFactory
|
||||||
.getLogger(MemoryBasedJavaFileManager.class);
|
.getLogger(MemoryBasedJavaFileManager.class);
|
||||||
|
|
||||||
|
private static URI JRT_URI = URI.create("jrt:/");
|
||||||
|
|
||||||
|
private static FileSystem fs;
|
||||||
|
|
||||||
private CompilationOutputCollector outputCollector;
|
private CompilationOutputCollector outputCollector;
|
||||||
|
|
||||||
private Map<String, File> resolvedAdditionalDependencies = new LinkedHashMap<>();
|
private Map<String, File> resolvedAdditionalDependencies = new LinkedHashMap<>();
|
||||||
@@ -86,9 +91,20 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
|
|||||||
|
|
||||||
private Map<Key, CloseableFilterableJavaFileObjectIterable> iterables = new HashMap<>();
|
private Map<Key, CloseableFilterableJavaFileObjectIterable> iterables = new HashMap<>();
|
||||||
|
|
||||||
|
private String jrtFsFilePath = null;
|
||||||
|
|
||||||
|
private boolean checkedForJrtFsPath = false;
|
||||||
|
|
||||||
public MemoryBasedJavaFileManager() {
|
public MemoryBasedJavaFileManager() {
|
||||||
outputCollector = new CompilationOutputCollector();
|
this.outputCollector = new CompilationOutputCollector();
|
||||||
compilationInfoCache = new CompilationInfoCache();
|
this.compilationInfoCache = new CompilationInfoCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FileSystem getJrtFs() {
|
||||||
|
if (fs == null) {
|
||||||
|
fs = FileSystems.getFileSystem(JRT_URI);
|
||||||
|
}
|
||||||
|
return fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -105,228 +121,14 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
|
|||||||
return null; // Do not currently need to load plugins
|
return null; // Do not currently need to load plugins
|
||||||
}
|
}
|
||||||
|
|
||||||
// Holds information that may help speed up compilation
|
|
||||||
static class CompilationInfoCache {
|
|
||||||
|
|
||||||
private Map<File, ArchiveInfo> archivePackageCache;
|
|
||||||
|
|
||||||
static class ArchiveInfo {
|
|
||||||
|
|
||||||
// The packages identified in a particular archive
|
|
||||||
private List<String> packageNames;
|
|
||||||
|
|
||||||
private boolean isBootJar = false;
|
|
||||||
|
|
||||||
public ArchiveInfo(List<String> packageNames, boolean isBootJar) {
|
|
||||||
this.packageNames = packageNames;
|
|
||||||
Collections.sort(this.packageNames);
|
|
||||||
this.isBootJar = isBootJar;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getPackageNames() {
|
|
||||||
return packageNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isBootJar() {
|
|
||||||
return isBootJar;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean containsPackage(String packageName, boolean subpackageMatchesAllowed) {
|
|
||||||
if (subpackageMatchesAllowed) {
|
|
||||||
for (String candidatePackageName: packageNames) {
|
|
||||||
if (candidatePackageName.startsWith(packageName)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
// Must be an exact match, fast binary search:
|
|
||||||
int pos = Collections.binarySearch(packageNames, packageName);
|
|
||||||
return (pos >= 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ArchiveInfo getArchiveInfoFor(File archive) {
|
|
||||||
if (!archive.isFile() || !(archive.getName().endsWith(".zip") || archive.getName().endsWith(".jar"))) {
|
|
||||||
// it is not an archive
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (archivePackageCache == null) {
|
|
||||||
archivePackageCache = new HashMap<>();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
ArchiveInfo result = archivePackageCache.get(archive);
|
|
||||||
if (result == null) {
|
|
||||||
result = buildArchiveInfo(archive);
|
|
||||||
archivePackageCache.put(archive, result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new IllegalStateException("Unexpected problem caching entries from "+archive.getName(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean packageCacheInitialized = false;
|
|
||||||
private Map<String, Path> packageCache = new HashMap<String, Path>();
|
|
||||||
private ArchiveInfo moduleArchiveInfo;
|
|
||||||
|
|
||||||
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()+"/";
|
|
||||||
packageCache.put(packagePathString, file.subpath(0, fnc-1)); // java/lang -> /modules/java.base/java/lang
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return FileVisitResult.CONTINUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized ArchiveInfo buildPackageMap() {
|
|
||||||
if (!packageCacheInitialized) {
|
|
||||||
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(packageCache.keySet());
|
|
||||||
Collections.sort(ls);
|
|
||||||
moduleArchiveInfo = new ArchiveInfo(ls, false);
|
|
||||||
}
|
|
||||||
return 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 Key {
|
|
||||||
private Location location;
|
|
||||||
private String classpath;
|
|
||||||
private String packageName;
|
|
||||||
private Set<Kind> kinds;
|
|
||||||
private boolean recurse;
|
|
||||||
|
|
||||||
public 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 (((location.hashCode()*37)+classpath.hashCode()*37+(packageName==null?0:packageName.hashCode()))*37+kinds.hashCode())*37+(recurse?1:0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (!(obj instanceof Key)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Key that = (Key)obj;
|
|
||||||
return location.equals(that.location) &&
|
|
||||||
classpath.equals(that.classpath) &&
|
|
||||||
kinds.equals(that.kinds) &&
|
|
||||||
(recurse==that.recurse) &&
|
|
||||||
(packageName==null?(that.packageName==null):this.packageName.equals(that.packageName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getPlatformClassPath() {
|
private String getPlatformClassPath() {
|
||||||
if (platformClasspath == null) {
|
if (this.platformClasspath == null) {
|
||||||
platformClasspath = System.getProperty("sun.boot.class.path");
|
this.platformClasspath = System.getProperty("sun.boot.class.path");
|
||||||
}
|
}
|
||||||
if (platformClasspath == null) {
|
if (this.platformClasspath == null) {
|
||||||
platformClasspath = "";
|
this.platformClasspath = "";
|
||||||
}
|
}
|
||||||
return platformClasspath;
|
return this.platformClasspath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -335,26 +137,29 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
|
|||||||
logger.debug("list({},{},{},{})", location, packageName, kinds, recurse);
|
logger.debug("list({},{},{},{})", location, packageName, kinds, recurse);
|
||||||
String classpath = "";
|
String classpath = "";
|
||||||
Path moduleRootPath = null;
|
Path moduleRootPath = null;
|
||||||
if (location instanceof JDKModuleLocation && (kinds == null || kinds.contains(Kind.CLASS))) {
|
if (location instanceof JDKModuleLocation
|
||||||
|
&& (kinds == null || kinds.contains(Kind.CLASS))) {
|
||||||
// list(org.springframework.cloud.function.compiler.java.MemoryBasedJavaFileManager$JDKModuleLocation@550a1967,
|
// list(org.springframework.cloud.function.compiler.java.MemoryBasedJavaFileManager$JDKModuleLocation@550a1967,
|
||||||
// java.lang,[SOURCE, CLASS, HTML, OTHER],false)
|
// java.lang,[SOURCE, CLASS, HTML, OTHER],false)
|
||||||
moduleRootPath = ((JDKModuleLocation)location).getModuleRootPath();
|
moduleRootPath = ((JDKModuleLocation) location).getModuleRootPath();
|
||||||
logger.debug("For JDKModuleLocation "+location.toString()+" root path is "+moduleRootPath);
|
logger.debug("For JDKModuleLocation " + location.toString() + " root path is "
|
||||||
} else if (location == StandardLocation.PLATFORM_CLASS_PATH
|
+ moduleRootPath);
|
||||||
|
}
|
||||||
|
else if (location == StandardLocation.PLATFORM_CLASS_PATH
|
||||||
&& (kinds == null || kinds.contains(Kind.CLASS))) {
|
&& (kinds == null || kinds.contains(Kind.CLASS))) {
|
||||||
classpath = getPlatformClassPath();
|
classpath = getPlatformClassPath();
|
||||||
// if (classpath.length() == 0) {
|
// if (classpath.length() == 0) {
|
||||||
// if (hasJrtFsPath()) {
|
// if (hasJrtFsPath()) {
|
||||||
// classpath = getJrtFsPath();
|
// classpath = getJrtFsPath();
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
logger.debug("Creating iterable for boot class path: {}", classpath);
|
logger.debug("Creating iterable for boot class path: {}", classpath);
|
||||||
}
|
}
|
||||||
else if (location == StandardLocation.CLASS_PATH
|
else if (location == StandardLocation.CLASS_PATH
|
||||||
&& (kinds == null || kinds.contains(Kind.CLASS))) {
|
&& (kinds == null || kinds.contains(Kind.CLASS))) {
|
||||||
String javaClassPath = getClassPath();
|
String javaClassPath = getClassPath();
|
||||||
if (!resolvedAdditionalDependencies.isEmpty()) {
|
if (!this.resolvedAdditionalDependencies.isEmpty()) {
|
||||||
for (File resolvedAdditionalDependency : resolvedAdditionalDependencies
|
for (File resolvedAdditionalDependency : this.resolvedAdditionalDependencies
|
||||||
.values()) {
|
.values()) {
|
||||||
javaClassPath += File.pathSeparatorChar + resolvedAdditionalDependency
|
javaClassPath += File.pathSeparatorChar + resolvedAdditionalDependency
|
||||||
.toURI().toString().substring("file:".length());
|
.toURI().toString().substring("file:".length());
|
||||||
@@ -364,41 +169,28 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
|
|||||||
logger.debug("Creating iterable for class path: {}", classpath);
|
logger.debug("Creating iterable for class path: {}", classpath);
|
||||||
}
|
}
|
||||||
Key k = new Key(location, classpath, packageName, kinds, recurse);
|
Key k = new Key(location, classpath, packageName, kinds, recurse);
|
||||||
CloseableFilterableJavaFileObjectIterable resultIterable = iterables.get(k);
|
CloseableFilterableJavaFileObjectIterable resultIterable = this.iterables.get(k);
|
||||||
if (resultIterable == null) {
|
if (resultIterable == null) {
|
||||||
if (moduleRootPath != null) {
|
if (moduleRootPath != null) {
|
||||||
resultIterable = new IterableJrtModule(compilationInfoCache, moduleRootPath, packageName, recurse);
|
resultIterable = new IterableJrtModule(this.compilationInfoCache,
|
||||||
} else {
|
moduleRootPath, packageName, recurse);
|
||||||
resultIterable = new IterableClasspath(compilationInfoCache, classpath, packageName, recurse);
|
|
||||||
}
|
}
|
||||||
iterables.put(k, resultIterable);
|
else {
|
||||||
|
resultIterable = new IterableClasspath(this.compilationInfoCache,
|
||||||
|
classpath, packageName, recurse);
|
||||||
|
}
|
||||||
|
this.iterables.put(k, resultIterable);
|
||||||
}
|
}
|
||||||
resultIterable.reset();
|
resultIterable.reset();
|
||||||
return resultIterable;
|
return resultIterable;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getClassPath() {
|
private String getClassPath() {
|
||||||
if (classpath == null) {
|
if (this.classpath == null) {
|
||||||
ClassLoader loader = InMemoryJavaFileObject.class.getClassLoader();
|
ClassLoader loader = InMemoryJavaFileObject.class.getClassLoader();
|
||||||
String cp = null;
|
String cp = null;
|
||||||
if (loader instanceof URLClassLoader) {
|
if (loader instanceof URLClassLoader) {
|
||||||
URL[] urls = ((URLClassLoader) loader).getURLs();
|
cp = classPath((URLClassLoader) loader, cp);
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (cp == null) {
|
if (cp == null) {
|
||||||
cp = System.getProperty("java.class.path");
|
cp = System.getProperty("java.class.path");
|
||||||
@@ -406,9 +198,32 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
|
|||||||
if (hasJrtFsPath()) {
|
if (hasJrtFsPath()) {
|
||||||
cp = cp + File.pathSeparator + getJrtFsPath();
|
cp = cp + File.pathSeparator + getJrtFsPath();
|
||||||
}
|
}
|
||||||
classpath = pathWithPlatformClassPathRemoved(cp);
|
this.classpath = pathWithPlatformClassPathRemoved(cp);
|
||||||
}
|
}
|
||||||
return classpath;
|
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)
|
// remove the platform classpath entries, they will be search separately (and earlier)
|
||||||
@@ -417,7 +232,7 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
|
|||||||
Set<String> cps = toList(classpath);
|
Set<String> cps = toList(classpath);
|
||||||
cps.removeAll(pcps);
|
cps.removeAll(pcps);
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
for (String cpe: cps) {
|
for (String cpe : cps) {
|
||||||
if (builder.length() > 0) {
|
if (builder.length() > 0) {
|
||||||
builder.append(File.pathSeparator);
|
builder.append(File.pathSeparator);
|
||||||
}
|
}
|
||||||
@@ -428,7 +243,7 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
|
|||||||
|
|
||||||
private Set<String> toList(String path) {
|
private Set<String> toList(String path) {
|
||||||
Set<String> result = new LinkedHashSet<>();
|
Set<String> result = new LinkedHashSet<>();
|
||||||
StringTokenizer tokenizer = new StringTokenizer(path,File.pathSeparator);
|
StringTokenizer tokenizer = new StringTokenizer(path, File.pathSeparator);
|
||||||
while (tokenizer.hasMoreTokens()) {
|
while (tokenizer.hasMoreTokens()) {
|
||||||
result.add(tokenizer.nextToken());
|
result.add(tokenizer.nextToken());
|
||||||
}
|
}
|
||||||
@@ -484,7 +299,8 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
|
|||||||
sibling);
|
sibling);
|
||||||
// Example parameters: CLASS_OUTPUT, Foo, CLASS,
|
// Example parameters: CLASS_OUTPUT, Foo, CLASS,
|
||||||
// StringBasedJavaSourceFileObject[string:///a/b/c/Foo.java]
|
// StringBasedJavaSourceFileObject[string:///a/b/c/Foo.java]
|
||||||
return outputCollector.getJavaFileForOutput(location, className, kind, sibling);
|
return this.outputCollector.getJavaFileForOutput(location, className, kind,
|
||||||
|
sibling);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -502,7 +318,7 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
|
|||||||
// This can be called when the annotation config processor runs
|
// This can be called when the annotation config processor runs
|
||||||
// Example parameters: CLASS_OUTPUT, ,
|
// Example parameters: CLASS_OUTPUT, ,
|
||||||
// META-INF/spring-configuration-metadata.json, null
|
// META-INF/spring-configuration-metadata.json, null
|
||||||
return outputCollector.getFileForOutput(location, packageName, relativeName,
|
return this.outputCollector.getFileForOutput(location, packageName, relativeName,
|
||||||
sibling);
|
sibling);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,14 +328,15 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
Collection<CloseableFilterableJavaFileObjectIterable> toClose = iterables.values();
|
Collection<CloseableFilterableJavaFileObjectIterable> toClose = this.iterables
|
||||||
for (CloseableFilterableJavaFileObjectIterable icp: toClose) {
|
.values();
|
||||||
|
for (CloseableFilterableJavaFileObjectIterable icp : toClose) {
|
||||||
icp.close();
|
icp.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<CompiledClassDefinition> getCompiledClasses() {
|
public List<CompiledClassDefinition> getCompiledClasses() {
|
||||||
return outputCollector.getCompiledClasses();
|
return this.outputCollector.getCompiledClasses();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<CompilationMessage> addAndResolveDependencies(String[] dependencies) {
|
public List<CompilationMessage> addAndResolveDependencies(String[] dependencies) {
|
||||||
@@ -536,16 +353,19 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
|
|||||||
// dependency =
|
// dependency =
|
||||||
// maven://org.springframework:spring-expression:4.3.9.RELEASE
|
// maven://org.springframework:spring-expression:4.3.9.RELEASE
|
||||||
// resolved.toURI() =
|
// resolved.toURI() =
|
||||||
// file:/Users/aclement/.m2/repository/org/springframework/spring-expression/4.3.9.RELEASE/spring-expression-4.3.9.RELEASE.jar
|
// file:/Users/aclement/.m2/repository/
|
||||||
resolvedAdditionalDependencies.put(dependency, resolved);
|
// org/springframework/spring-expression/4.3.9.RELEASE/spring-expression-4.3.9.RELEASE.jar
|
||||||
|
this.resolvedAdditionalDependencies.put(dependency, resolved);
|
||||||
}
|
}
|
||||||
catch (RuntimeException re) {
|
catch (RuntimeException re) {
|
||||||
CompilationMessage compilationMessage = new CompilationMessage(
|
CompilationMessage compilationMessage = new CompilationMessage(
|
||||||
CompilationMessage.Kind.ERROR, re.getMessage(), null, 0, 0);
|
CompilationMessage.Kind.ERROR, re.getMessage(), null, 0, 0);
|
||||||
resolutionMessages.add(compilationMessage);
|
resolutionMessages.add(compilationMessage);
|
||||||
}
|
}
|
||||||
} else if (dependency.startsWith("file:")) {
|
}
|
||||||
resolvedAdditionalDependencies.put(dependency, new File(URI.create(dependency)));
|
else if (dependency.startsWith("file:")) {
|
||||||
|
this.resolvedAdditionalDependencies.put(dependency,
|
||||||
|
new File(URI.create(dependency)));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
resolutionMessages.add(new CompilationMessage(
|
resolutionMessages.add(new CompilationMessage(
|
||||||
@@ -559,18 +379,320 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, File> getResolvedAdditionalDependencies() {
|
public Map<String, File> getResolvedAdditionalDependencies() {
|
||||||
return resolvedAdditionalDependencies;
|
return this.resolvedAdditionalDependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static URI JRT_URI = URI.create("jrt:/");
|
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 static FileSystem fs;
|
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 {
|
static class JDKModuleLocation implements Location {
|
||||||
|
|
||||||
private String moduleName;
|
private String moduleName;
|
||||||
|
|
||||||
private Path moduleRootPath;
|
private Path moduleRootPath;
|
||||||
|
|
||||||
public JDKModuleLocation(String moduleName, Path moduleRootPath) {
|
JDKModuleLocation(String moduleName, Path moduleRootPath) {
|
||||||
this.moduleName = moduleName;
|
this.moduleName = moduleName;
|
||||||
this.moduleRootPath = moduleRootPath;
|
this.moduleRootPath = moduleRootPath;
|
||||||
}
|
}
|
||||||
@@ -586,114 +708,62 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getModuleName() {
|
public String getModuleName() {
|
||||||
return moduleName;
|
return this.moduleName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Path getModuleRootPath() {
|
public Path getModuleRootPath() {
|
||||||
return moduleRootPath;
|
return this.moduleRootPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "JDKModuleLocation(" + moduleName + ")";
|
return "JDKModuleLocation(" + this.moduleName + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return moduleName.hashCode();
|
return this.moduleName.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean equals(Object other) {
|
public boolean equals(Object other) {
|
||||||
if (!(other instanceof JDKModuleLocation)) {
|
if (!(other instanceof JDKModuleLocation)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return this.hashCode() == ((JDKModuleLocation)other).hashCode();
|
return this.hashCode() == ((JDKModuleLocation) other).hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
static class ModuleIdentifierVisitor extends SimpleFileVisitor<Path> {
|
static class ModuleIdentifierVisitor extends SimpleFileVisitor<Path> {
|
||||||
|
|
||||||
private Map<String, Path> modules = new HashMap<>();
|
private Map<String, Path> modules = new HashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
|
||||||
|
throws IOException {
|
||||||
if (file.getNameCount() > 2 && file.toString().endsWith(".class")) {
|
if (file.getNameCount() > 2 && file.toString().endsWith(".class")) {
|
||||||
// /modules/jdk.rmic/sun/tools/tree/CaseStatement.class
|
// /modules/jdk.rmic/sun/tools/tree/CaseStatement.class
|
||||||
String moduleName = file.getName(1).toString(); // jdk.rmic
|
String moduleName = file.getName(1).toString(); // jdk.rmic
|
||||||
Path moduleRootPath = file.subpath(0, 2); // /modules/jdk.rmic
|
Path moduleRootPath = file.subpath(0, 2); // /modules/jdk.rmic
|
||||||
if (!modules.containsKey(moduleName)) {
|
if (!this.modules.containsKey(moduleName)) {
|
||||||
modules.put(moduleName, moduleRootPath);
|
this.modules.put(moduleName, moduleRootPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return FileVisitResult.CONTINUE;
|
return FileVisitResult.CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<Location> getModuleLocations() {
|
public Set<Location> getModuleLocations() {
|
||||||
if (modules.size()==0) {
|
if (this.modules.size() == 0) {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
Set<Location> locations = new HashSet<>();
|
Set<Location> locations = new HashSet<>();
|
||||||
for (Map.Entry<String,Path> moduleEntry: modules.entrySet()) {
|
for (Map.Entry<String, Path> moduleEntry : this.modules.entrySet()) {
|
||||||
locations.add(new JDKModuleLocation(moduleEntry.getKey(),moduleEntry.getValue()));
|
locations.add(new JDKModuleLocation(moduleEntry.getKey(),
|
||||||
|
moduleEntry.getValue()));
|
||||||
}
|
}
|
||||||
return locations;
|
return locations;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String jrtFsFilePath = null;
|
|
||||||
|
|
||||||
private boolean checkedForJrtFsPath = false;
|
|
||||||
|
|
||||||
private boolean hasJrtFsPath() {
|
|
||||||
return getJrtFsPath() != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getJrtFsPath() {
|
|
||||||
if (!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;
|
|
||||||
}
|
|
||||||
checkedForJrtFsPath = true;
|
|
||||||
}
|
|
||||||
return 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static FileSystem getJrtFs() {
|
|
||||||
if (fs == null) {
|
|
||||||
fs = FileSystems.getFileSystem(JRT_URI);
|
|
||||||
}
|
|
||||||
return fs;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -33,23 +33,27 @@ import javax.lang.model.element.NestingKind;
|
|||||||
import javax.tools.JavaFileObject;
|
import javax.tools.JavaFileObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an element inside in zip which is itself inside a zip. These objects are
|
* Represents an element inside in zip which is itself inside a zip. These objects are not
|
||||||
* not initially created with the content of the file they represent,
|
* initially created with the content of the file they represent, only enough information
|
||||||
* only enough information to find that content because many will
|
* to find that content because many will typically be created but only few will be
|
||||||
* typically be created but only few will be opened.
|
* opened.
|
||||||
*
|
*
|
||||||
* @author Andy Clement
|
* @author Andy Clement
|
||||||
*/
|
*/
|
||||||
public class NestedZipEntryJavaFileObject implements JavaFileObject {
|
public class NestedZipEntryJavaFileObject implements JavaFileObject {
|
||||||
|
|
||||||
private File outerFile;
|
private File outerFile;
|
||||||
|
|
||||||
private ZipFile outerZipFile;
|
private ZipFile outerZipFile;
|
||||||
|
|
||||||
private ZipEntry innerZipFile;
|
private ZipEntry innerZipFile;
|
||||||
|
|
||||||
private ZipEntry innerZipFileEntry;
|
private ZipEntry innerZipFileEntry;
|
||||||
|
|
||||||
private URI uri;
|
private URI uri;
|
||||||
|
|
||||||
public NestedZipEntryJavaFileObject(File outerFile, ZipFile outerZipFile, ZipEntry innerZipFile, ZipEntry innerZipFileEntry) {
|
public NestedZipEntryJavaFileObject(File outerFile, ZipFile outerZipFile,
|
||||||
|
ZipEntry innerZipFile, ZipEntry innerZipFileEntry) {
|
||||||
this.outerFile = outerFile;
|
this.outerFile = outerFile;
|
||||||
this.outerZipFile = outerZipFile;
|
this.outerZipFile = outerZipFile;
|
||||||
this.innerZipFile = innerZipFile;
|
this.innerZipFile = innerZipFile;
|
||||||
@@ -58,48 +62,58 @@ public class NestedZipEntryJavaFileObject implements JavaFileObject {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return innerZipFileEntry.getName(); // Example: a/b/C.class
|
return this.innerZipFileEntry.getName(); // Example: a/b/C.class
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URI toUri() {
|
public URI toUri() {
|
||||||
if (uri == null) {
|
if (this.uri == null) {
|
||||||
String uriString = null;
|
String uriString = null;
|
||||||
try {
|
try {
|
||||||
uriString = "zip:"+outerFile.getAbsolutePath()+"!"+innerZipFile.getName()+"!"+innerZipFileEntry.getName();
|
uriString = "zip:" + this.outerFile.getAbsolutePath() + "!"
|
||||||
uri = new URI(uriString);
|
+ this.innerZipFile.getName() + "!"
|
||||||
} catch (URISyntaxException e) {
|
+ this.innerZipFileEntry.getName();
|
||||||
throw new IllegalStateException("Unexpected URISyntaxException for string '"+uriString+"'",e);
|
this.uri = new URI(uriString);
|
||||||
|
}
|
||||||
|
catch (URISyntaxException e) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Unexpected URISyntaxException for string '" + uriString + "'",
|
||||||
|
e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return uri;
|
return this.uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream openInputStream() throws IOException {
|
public InputStream openInputStream() throws IOException {
|
||||||
// Find the inner zip file inside the outer zip file, then
|
// Find the inner zip file inside the outer zip file, then
|
||||||
// find the relevant entry, then return the stream.
|
// find the relevant entry, then return the stream.
|
||||||
InputStream innerZipFileInputStream = this.outerZipFile.getInputStream(innerZipFile);
|
InputStream innerZipFileInputStream = this.outerZipFile
|
||||||
|
.getInputStream(this.innerZipFile);
|
||||||
ZipInputStream innerZipInputStream = new ZipInputStream(innerZipFileInputStream);
|
ZipInputStream innerZipInputStream = new ZipInputStream(innerZipFileInputStream);
|
||||||
ZipEntry nextEntry = innerZipInputStream.getNextEntry();
|
ZipEntry nextEntry = innerZipInputStream.getNextEntry();
|
||||||
while (nextEntry != null) {
|
while (nextEntry != null) {
|
||||||
if (nextEntry.getName().equals(innerZipFileEntry.getName())) {
|
if (nextEntry.getName().equals(this.innerZipFileEntry.getName())) {
|
||||||
return innerZipInputStream;
|
return innerZipInputStream;
|
||||||
}
|
}
|
||||||
nextEntry = innerZipInputStream.getNextEntry();
|
nextEntry = innerZipInputStream.getNextEntry();
|
||||||
}
|
}
|
||||||
throw new IllegalStateException("Unable to locate nested zip entry "+innerZipFileEntry.getName()+" in zip "+innerZipFile.getName()+" inside zip "+outerZipFile.getName());
|
throw new IllegalStateException(
|
||||||
|
"Unable to locate nested zip entry " + this.innerZipFileEntry.getName()
|
||||||
|
+ " in zip " + this.innerZipFile.getName() + " inside zip "
|
||||||
|
+ this.outerZipFile.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
|
public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
|
||||||
// It is bytecode
|
// It is bytecode
|
||||||
throw new UnsupportedOperationException("getCharContent() not supported on class file: " + getName());
|
throw new UnsupportedOperationException(
|
||||||
|
"getCharContent() not supported on class file: " + getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getLastModified() {
|
public long getLastModified() {
|
||||||
return innerZipFileEntry.getTime();
|
return this.innerZipFileEntry.getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -115,12 +129,12 @@ public class NestedZipEntryJavaFileObject implements JavaFileObject {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OutputStream openOutputStream() throws IOException {
|
public OutputStream openOutputStream() throws IOException {
|
||||||
throw new IllegalStateException("cannot write to nested zip entry: "+toUri());
|
throw new IllegalStateException("cannot write to nested zip entry: " + toUri());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Writer openWriter() throws IOException {
|
public Writer openWriter() throws IOException {
|
||||||
throw new IllegalStateException("cannot write to nested zip entry: "+toUri());
|
throw new IllegalStateException("cannot write to nested zip entry: " + toUri());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -130,13 +144,14 @@ public class NestedZipEntryJavaFileObject implements JavaFileObject {
|
|||||||
}
|
}
|
||||||
String name = getName();
|
String name = getName();
|
||||||
int lastSlash = name.lastIndexOf('/');
|
int lastSlash = name.lastIndexOf('/');
|
||||||
return name.substring(lastSlash+1).equals(simpleName+".class");
|
return name.substring(lastSlash + 1).equals(simpleName + ".class");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
|
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
|
||||||
// It is bytecode
|
// It is bytecode
|
||||||
throw new UnsupportedOperationException("getCharContent() not supported on class file: " + getName());
|
throw new UnsupportedOperationException(
|
||||||
|
"getCharContent() not supported on class file: " + getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -151,9 +166,9 @@ public class NestedZipEntryJavaFileObject implements JavaFileObject {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int hc = outerFile.getName().hashCode();
|
int hc = this.outerFile.getName().hashCode();
|
||||||
hc = hc * 37 + innerZipFile.getName().hashCode();
|
hc = hc * 37 + this.innerZipFile.getName().hashCode();
|
||||||
hc = hc * 37 + innerZipFileEntry.getName().hashCode();
|
hc = hc * 37 + this.innerZipFileEntry.getName().hashCode();
|
||||||
return hc;
|
return hc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,11 +177,11 @@ public class NestedZipEntryJavaFileObject implements JavaFileObject {
|
|||||||
if (!(obj instanceof NestedZipEntryJavaFileObject)) {
|
if (!(obj instanceof NestedZipEntryJavaFileObject)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
NestedZipEntryJavaFileObject that = (NestedZipEntryJavaFileObject)obj;
|
NestedZipEntryJavaFileObject that = (NestedZipEntryJavaFileObject) obj;
|
||||||
return (outerFile.getName().equals(that.outerFile.getName())) &&
|
return (this.outerFile.getName().equals(that.outerFile.getName()))
|
||||||
(innerZipFile.getName().equals(that.innerZipFile.getName())) &&
|
&& (this.innerZipFile.getName().equals(that.innerZipFile.getName()))
|
||||||
(innerZipFileEntry.getName().equals(that.innerZipFileEntry.getName()));
|
&& (this.innerZipFileEntry.getName()
|
||||||
|
.equals(that.innerZipFileEntry.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -39,73 +39,90 @@ import org.slf4j.LoggerFactory;
|
|||||||
*/
|
*/
|
||||||
public class RuntimeJavaCompiler {
|
public class RuntimeJavaCompiler {
|
||||||
|
|
||||||
private JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
|
||||||
|
|
||||||
private static Logger logger = LoggerFactory.getLogger(RuntimeJavaCompiler.class);
|
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
|
* Compile the named class consisting of the supplied source code. If successful load
|
||||||
* and return it. Multiple classes may get loaded if the source code included anonymous/inner/local
|
* the class and return it. Multiple classes may get loaded if the source code
|
||||||
* classes.
|
* included anonymous/inner/local classes.
|
||||||
* @param className the name of the class (dotted form, e.g. com.foo.bar.Goo)
|
* @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 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
|
* @param dependencies optional coordinates for dependencies, maven
|
||||||
* @return a CompilationResult that encapsulates what happened during compilation (classes/messages produced)
|
* '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) {
|
public CompilationResult compile(String className, String classSourceCode,
|
||||||
logger.info("Compiling source for class {} using compiler {}",className,compiler.getClass().getName());
|
String... dependencies) {
|
||||||
|
logger.info("Compiling source for class {} using compiler {}", className,
|
||||||
|
this.compiler.getClass().getName());
|
||||||
|
|
||||||
DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<JavaFileObject>();
|
DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<JavaFileObject>();
|
||||||
MemoryBasedJavaFileManager fileManager = new MemoryBasedJavaFileManager();
|
MemoryBasedJavaFileManager fileManager = new MemoryBasedJavaFileManager();
|
||||||
List<CompilationMessage> resolutionMessages = fileManager.addAndResolveDependencies(dependencies);
|
List<CompilationMessage> resolutionMessages = fileManager
|
||||||
JavaFileObject sourceFile = InMemoryJavaFileObject.getSourceJavaFileObject(className, classSourceCode);
|
.addAndResolveDependencies(dependencies);
|
||||||
|
JavaFileObject sourceFile = InMemoryJavaFileObject
|
||||||
|
.getSourceJavaFileObject(className, classSourceCode);
|
||||||
|
|
||||||
Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(sourceFile);
|
Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(sourceFile);
|
||||||
List<String> options = new ArrayList<>();
|
List<String> options = new ArrayList<>();
|
||||||
options.add("-source");
|
options.add("-source");
|
||||||
options.add("1.8");
|
options.add("1.8");
|
||||||
CompilationTask task = compiler.getTask(null, fileManager , diagnosticCollector, options, null, compilationUnits);
|
CompilationTask task = this.compiler.getTask(null, fileManager,
|
||||||
|
diagnosticCollector, options, null, compilationUnits);
|
||||||
|
|
||||||
boolean success = task.call();
|
boolean success = task.call();
|
||||||
CompilationResult compilationResult = new CompilationResult(success);
|
CompilationResult compilationResult = new CompilationResult(success);
|
||||||
compilationResult.recordCompilationMessages(resolutionMessages);
|
compilationResult.recordCompilationMessages(resolutionMessages);
|
||||||
compilationResult.setResolvedAdditionalDependencies(new ArrayList<>(fileManager.getResolvedAdditionalDependencies().values()));
|
compilationResult.setResolvedAdditionalDependencies(new ArrayList<>(
|
||||||
|
fileManager.getResolvedAdditionalDependencies().values()));
|
||||||
|
|
||||||
// If successful there may be no errors but there might be info/warnings
|
// If successful there may be no errors but there might be info/warnings
|
||||||
for (Diagnostic<? extends JavaFileObject> diagnostic : diagnosticCollector.getDiagnostics()) {
|
for (Diagnostic<? extends JavaFileObject> diagnostic : diagnosticCollector
|
||||||
CompilationMessage.Kind kind = (diagnostic.getKind()==Kind.ERROR?CompilationMessage.Kind.ERROR:CompilationMessage.Kind.OTHER);
|
.getDiagnostics()) {
|
||||||
// String sourceCode = ((StringBasedJavaSourceFileObject)diagnostic.getSource()).getSourceCode();
|
CompilationMessage.Kind kind = (diagnostic.getKind() == Kind.ERROR
|
||||||
String sourceCode =null;
|
? CompilationMessage.Kind.ERROR : CompilationMessage.Kind.OTHER);
|
||||||
|
// String sourceCode =
|
||||||
|
// ((StringBasedJavaSourceFileObject)diagnostic.getSource()).getSourceCode();
|
||||||
|
String sourceCode = null;
|
||||||
try {
|
try {
|
||||||
sourceCode = (String)diagnostic.getSource().getCharContent(true);
|
sourceCode = (String) diagnostic.getSource().getCharContent(true);
|
||||||
}
|
}
|
||||||
catch (IOException ioe) {
|
catch (IOException ioe) {
|
||||||
// Unexpected, but leave sourceCode null to indicate it was not retrievable
|
// Unexpected, but leave sourceCode null to indicate it was not
|
||||||
|
// retrievable
|
||||||
}
|
}
|
||||||
catch (NullPointerException npe) {
|
catch (NullPointerException npe) {
|
||||||
// TODO: should we skip warning diagnostics in the loop altogether?
|
// TODO: should we skip warning diagnostics in the loop altogether?
|
||||||
}
|
}
|
||||||
int startPosition = (int)diagnostic.getPosition();
|
int startPosition = (int) diagnostic.getPosition();
|
||||||
if (startPosition == Diagnostic.NOPOS) {
|
if (startPosition == Diagnostic.NOPOS) {
|
||||||
startPosition = (int)diagnostic.getStartPosition();
|
startPosition = (int) diagnostic.getStartPosition();
|
||||||
}
|
}
|
||||||
CompilationMessage compilationMessage = new CompilationMessage(kind,diagnostic.getMessage(null),sourceCode,startPosition,(int)diagnostic.getEndPosition());
|
CompilationMessage compilationMessage = new CompilationMessage(kind,
|
||||||
|
diagnostic.getMessage(null), sourceCode, startPosition,
|
||||||
|
(int) diagnostic.getEndPosition());
|
||||||
compilationResult.recordCompilationMessage(compilationMessage);
|
compilationResult.recordCompilationMessage(compilationMessage);
|
||||||
}
|
}
|
||||||
if (success) {
|
if (success) {
|
||||||
List<CompiledClassDefinition> ccds = fileManager.getCompiledClasses();
|
List<CompiledClassDefinition> ccds = fileManager.getCompiledClasses();
|
||||||
List<Class<?>> classes = new ArrayList<>();
|
List<Class<?>> classes = new ArrayList<>();
|
||||||
try (SimpleClassLoader ccl = new SimpleClassLoader(this.getClass().getClassLoader())) {
|
try (SimpleClassLoader ccl = new SimpleClassLoader(
|
||||||
for (CompiledClassDefinition ccd: ccds) {
|
this.getClass().getClassLoader())) {
|
||||||
|
for (CompiledClassDefinition ccd : ccds) {
|
||||||
Class<?> clazz = ccl.defineClass(ccd.getClassName(), ccd.getBytes());
|
Class<?> clazz = ccl.defineClass(ccd.getClassName(), ccd.getBytes());
|
||||||
classes.add(clazz);
|
classes.add(clazz);
|
||||||
compilationResult.addClassBytes(ccd.getClassName(), ccd.getBytes());
|
compilationResult.addClassBytes(ccd.getClassName(), ccd.getBytes());
|
||||||
}
|
}
|
||||||
} catch (IOException ioe) {
|
}
|
||||||
logger.debug("Unexpected exception defining classes",ioe);
|
catch (IOException ioe) {
|
||||||
|
logger.debug("Unexpected exception defining classes", ioe);
|
||||||
}
|
}
|
||||||
compilationResult.setCompiledClasses(classes);
|
compilationResult.setCompiledClasses(classes);
|
||||||
}
|
}
|
||||||
return compilationResult;
|
return compilationResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -34,16 +34,18 @@ public class SimpleClassLoader extends URLClassLoader {
|
|||||||
super(NO_URLS, classLoader);
|
super(NO_URLS, classLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SimpleClassLoader(List<File> resolvedAdditionalDependencies, ClassLoader classLoader) {
|
public SimpleClassLoader(List<File> resolvedAdditionalDependencies,
|
||||||
|
ClassLoader classLoader) {
|
||||||
super(toUrls(resolvedAdditionalDependencies), classLoader);
|
super(toUrls(resolvedAdditionalDependencies), classLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static URL[] toUrls(List<File> resolvedAdditionalDependencies) {
|
private static URL[] toUrls(List<File> resolvedAdditionalDependencies) {
|
||||||
URL[] urls = new URL[resolvedAdditionalDependencies.size()];
|
URL[] urls = new URL[resolvedAdditionalDependencies.size()];
|
||||||
for (int i=0,max=resolvedAdditionalDependencies.size();i<max;i++) {
|
for (int i = 0, max = resolvedAdditionalDependencies.size(); i < max; i++) {
|
||||||
try {
|
try {
|
||||||
urls[i] = resolvedAdditionalDependencies.get(i).toURI().toURL();
|
urls[i] = resolvedAdditionalDependencies.get(i).toURI().toURL();
|
||||||
} catch (Exception e) {
|
}
|
||||||
|
catch (Exception e) {
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,4 +55,5 @@ public class SimpleClassLoader extends URLClassLoader {
|
|||||||
public Class<?> defineClass(String name, byte[] bytes) {
|
public Class<?> defineClass(String name, byte[] bytes) {
|
||||||
return super.defineClass(name, bytes, 0, bytes.length);
|
return super.defineClass(name, bytes, 0, bytes.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -31,10 +31,17 @@ import javax.lang.model.element.Modifier;
|
|||||||
import javax.lang.model.element.NestingKind;
|
import javax.lang.model.element.NestingKind;
|
||||||
import javax.tools.JavaFileObject;
|
import javax.tools.JavaFileObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link JavaFileObject} that works on a ZIP entry.
|
||||||
|
*
|
||||||
|
* @author Mark Fisher
|
||||||
|
*/
|
||||||
public class ZipEntryJavaFileObject implements JavaFileObject {
|
public class ZipEntryJavaFileObject implements JavaFileObject {
|
||||||
|
|
||||||
private File containingFile;
|
private File containingFile;
|
||||||
|
|
||||||
private ZipFile zf;
|
private ZipFile zf;
|
||||||
|
|
||||||
private ZipEntry ze;
|
private ZipEntry ze;
|
||||||
|
|
||||||
private URI uri;
|
private URI uri;
|
||||||
@@ -47,26 +54,30 @@ public class ZipEntryJavaFileObject implements JavaFileObject {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URI toUri() {
|
public URI toUri() {
|
||||||
if (uri == null) {
|
if (this.uri == null) {
|
||||||
String uriString = null;
|
String uriString = null;
|
||||||
try {
|
try {
|
||||||
uriString = "zip:" + containingFile.getAbsolutePath() + "!" + ze.getName();
|
uriString = "zip:" + this.containingFile.getAbsolutePath() + "!"
|
||||||
uri = new URI(uriString);
|
+ this.ze.getName();
|
||||||
} catch (URISyntaxException e) {
|
this.uri = new URI(uriString);
|
||||||
throw new IllegalStateException("Unexpected URISyntaxException for string '" + uriString + "'", e);
|
}
|
||||||
|
catch (URISyntaxException e) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Unexpected URISyntaxException for string '" + uriString + "'",
|
||||||
|
e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return uri;
|
return this.uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return ze.getName(); // a/b/C.class
|
return this.ze.getName(); // a/b/C.class
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream openInputStream() throws IOException {
|
public InputStream openInputStream() throws IOException {
|
||||||
return zf.getInputStream(ze);
|
return this.zf.getInputStream(this.ze);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -77,13 +88,15 @@ public class ZipEntryJavaFileObject implements JavaFileObject {
|
|||||||
@Override
|
@Override
|
||||||
public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
|
public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
|
||||||
// It is bytecode
|
// It is bytecode
|
||||||
throw new UnsupportedOperationException("openReader() not supported on class file: " + getName());
|
throw new UnsupportedOperationException(
|
||||||
|
"openReader() not supported on class file: " + getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
|
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
|
||||||
// It is bytecode
|
// It is bytecode
|
||||||
throw new UnsupportedOperationException("getCharContent() not supported on class file: " + getName());
|
throw new UnsupportedOperationException(
|
||||||
|
"getCharContent() not supported on class file: " + getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -93,7 +106,7 @@ public class ZipEntryJavaFileObject implements JavaFileObject {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getLastModified() {
|
public long getLastModified() {
|
||||||
return ze.getTime();
|
return this.ze.getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -128,8 +141,8 @@ public class ZipEntryJavaFileObject implements JavaFileObject {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int hc = containingFile.getName().hashCode();
|
int hc = this.containingFile.getName().hashCode();
|
||||||
hc = hc * 37 + ze.getName().hashCode();
|
hc = hc * 37 + this.ze.getName().hashCode();
|
||||||
return hc;
|
return hc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,9 +151,9 @@ public class ZipEntryJavaFileObject implements JavaFileObject {
|
|||||||
if (!(obj instanceof ZipEntryJavaFileObject)) {
|
if (!(obj instanceof ZipEntryJavaFileObject)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ZipEntryJavaFileObject that = (ZipEntryJavaFileObject)obj;
|
ZipEntryJavaFileObject that = (ZipEntryJavaFileObject) obj;
|
||||||
return (containingFile.getName().equals(that.containingFile.getName())) &&
|
return (this.containingFile.getName().equals(that.containingFile.getName()))
|
||||||
(ze.getName().equals(that.ze.getName()));
|
&& (this.ze.getName().equals(that.ze.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -32,11 +32,13 @@ import org.springframework.util.ReflectionUtils;
|
|||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
* @author Oleg Zhurakousky
|
* @author Oleg Zhurakousky
|
||||||
*/
|
*/
|
||||||
abstract class AbstractByteCodeLoadingProxy<T> implements InitializingBean, FunctionFactoryMetadata<T> {
|
abstract class AbstractByteCodeLoadingProxy<T>
|
||||||
|
implements InitializingBean, FunctionFactoryMetadata<T> {
|
||||||
|
|
||||||
private final Resource resource;
|
private final Resource resource;
|
||||||
|
|
||||||
private final SimpleClassLoader classLoader = new SimpleClassLoader(AbstractByteCodeLoadingProxy.class.getClassLoader());
|
private final SimpleClassLoader classLoader = new SimpleClassLoader(
|
||||||
|
AbstractByteCodeLoadingProxy.class.getClassLoader());
|
||||||
|
|
||||||
private T target;
|
private T target;
|
||||||
|
|
||||||
@@ -53,7 +55,8 @@ abstract class AbstractByteCodeLoadingProxy<T> implements InitializingBean, Func
|
|||||||
String className = new ClassReader(bytes).getClassName().replace("/", ".");
|
String className = new ClassReader(bytes).getClassName().replace("/", ".");
|
||||||
Class<?> factoryClass = this.classLoader.defineClass(className, bytes);
|
Class<?> factoryClass = this.classLoader.defineClass(className, bytes);
|
||||||
try {
|
try {
|
||||||
this.target = ((CompilationResultFactory<T>) factoryClass.newInstance()).getResult();
|
this.target = ((CompilationResultFactory<T>) factoryClass.newInstance())
|
||||||
|
.getResult();
|
||||||
this.method = findFactoryMethod(factoryClass);
|
this.method = findFactoryMethod(factoryClass);
|
||||||
}
|
}
|
||||||
catch (InstantiationException | IllegalAccessException e) {
|
catch (InstantiationException | IllegalAccessException e) {
|
||||||
@@ -81,4 +84,5 @@ abstract class AbstractByteCodeLoadingProxy<T> implements InitializingBean, Func
|
|||||||
});
|
});
|
||||||
return method.get();
|
return method.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -29,9 +29,11 @@ import org.springframework.util.Assert;
|
|||||||
import org.springframework.util.FileCopyUtils;
|
import org.springframework.util.FileCopyUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param <T> target type
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
*/
|
*/
|
||||||
public class AbstractLambdaCompilingProxy<T> implements InitializingBean, BeanNameAware, FunctionFactoryMetadata<T> {
|
public class AbstractLambdaCompilingProxy<T>
|
||||||
|
implements InitializingBean, BeanNameAware, FunctionFactoryMetadata<T> {
|
||||||
|
|
||||||
private final Resource resource;
|
private final Resource resource;
|
||||||
|
|
||||||
@@ -43,7 +45,8 @@ public class AbstractLambdaCompilingProxy<T> implements InitializingBean, BeanNa
|
|||||||
|
|
||||||
private String[] typeParameterizations;
|
private String[] typeParameterizations;
|
||||||
|
|
||||||
public AbstractLambdaCompilingProxy(Resource resource, AbstractFunctionCompiler<T> compiler) {
|
public AbstractLambdaCompilingProxy(Resource resource,
|
||||||
|
AbstractFunctionCompiler<T> compiler) {
|
||||||
Assert.notNull(resource, "Resource must not be null");
|
Assert.notNull(resource, "Resource must not be null");
|
||||||
Assert.notNull(compiler, "Compiler must not be null");
|
Assert.notNull(compiler, "Compiler must not be null");
|
||||||
this.resource = resource;
|
this.resource = resource;
|
||||||
@@ -61,8 +64,10 @@ public class AbstractLambdaCompilingProxy<T> implements InitializingBean, BeanNa
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
String lambda = FileCopyUtils.copyToString(new InputStreamReader(this.resource.getInputStream()));
|
String lambda = FileCopyUtils
|
||||||
this.factory = this.compiler.compile(this.beanName, lambda, this.typeParameterizations);
|
.copyToString(new InputStreamReader(this.resource.getInputStream()));
|
||||||
|
this.factory = this.compiler.compile(this.beanName, lambda,
|
||||||
|
this.typeParameterizations);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -74,4 +79,5 @@ public class AbstractLambdaCompilingProxy<T> implements InitializingBean, BeanNa
|
|||||||
public Method getFactoryMethod() {
|
public Method getFactoryMethod() {
|
||||||
return this.factory.getFactoryMethod();
|
return this.factory.getFactoryMethod();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -22,12 +22,12 @@ import org.springframework.cloud.function.core.FunctionFactoryMetadata;
|
|||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param <T> type
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
* @author Oleg Zhurakousky
|
* @author Oleg Zhurakousky
|
||||||
*
|
|
||||||
* @param <T> type
|
|
||||||
*/
|
*/
|
||||||
public class ByteCodeLoadingConsumer<T> extends AbstractByteCodeLoadingProxy<Consumer<T>> implements FunctionFactoryMetadata<Consumer<T>>, Consumer<T> {
|
public class ByteCodeLoadingConsumer<T> extends AbstractByteCodeLoadingProxy<Consumer<T>>
|
||||||
|
implements FunctionFactoryMetadata<Consumer<T>>, Consumer<T> {
|
||||||
|
|
||||||
public ByteCodeLoadingConsumer(Resource resource) {
|
public ByteCodeLoadingConsumer(Resource resource) {
|
||||||
super(resource);
|
super(resource);
|
||||||
@@ -37,4 +37,5 @@ public class ByteCodeLoadingConsumer<T> extends AbstractByteCodeLoadingProxy<Con
|
|||||||
public void accept(T t) {
|
public void accept(T t) {
|
||||||
this.getTarget().accept(t);
|
this.getTarget().accept(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -22,13 +22,14 @@ import org.springframework.cloud.function.core.FunctionFactoryMetadata;
|
|||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Mark Fisher
|
|
||||||
* @author Oleg Zhurakousky
|
|
||||||
*
|
|
||||||
* @param <T> Function input type
|
* @param <T> Function input type
|
||||||
* @param <R> Function result 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 class ByteCodeLoadingFunction<T, R>
|
||||||
|
extends AbstractByteCodeLoadingProxy<Function<T, R>>
|
||||||
|
implements FunctionFactoryMetadata<Function<T, R>>, Function<T, R> {
|
||||||
|
|
||||||
public ByteCodeLoadingFunction(Resource resource) {
|
public ByteCodeLoadingFunction(Resource resource) {
|
||||||
super(resource);
|
super(resource);
|
||||||
@@ -38,4 +39,5 @@ public class ByteCodeLoadingFunction<T, R> extends AbstractByteCodeLoadingProxy<
|
|||||||
public R apply(T input) {
|
public R apply(T input) {
|
||||||
return this.getTarget().apply(input);
|
return this.getTarget().apply(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -22,12 +22,12 @@ import org.springframework.cloud.function.core.FunctionFactoryMetadata;
|
|||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param <T> type
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
* @author Oleg Zhurakousky
|
* @author Oleg Zhurakousky
|
||||||
*
|
|
||||||
* @param <T> type
|
|
||||||
*/
|
*/
|
||||||
public class ByteCodeLoadingSupplier<T> extends AbstractByteCodeLoadingProxy<Supplier<T>> implements FunctionFactoryMetadata<Supplier<T>>, Supplier<T> {
|
public class ByteCodeLoadingSupplier<T> extends AbstractByteCodeLoadingProxy<Supplier<T>>
|
||||||
|
implements FunctionFactoryMetadata<Supplier<T>>, Supplier<T> {
|
||||||
|
|
||||||
public ByteCodeLoadingSupplier(Resource resource) {
|
public ByteCodeLoadingSupplier(Resource resource) {
|
||||||
super(resource);
|
super(resource);
|
||||||
@@ -37,4 +37,5 @@ public class ByteCodeLoadingSupplier<T> extends AbstractByteCodeLoadingProxy<Sup
|
|||||||
public T get() {
|
public T get() {
|
||||||
return this.getTarget().get();
|
return this.getTarget().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -22,9 +22,11 @@ import org.springframework.cloud.function.compiler.ConsumerCompiler;
|
|||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param <T> input argument type
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
*/
|
*/
|
||||||
public class LambdaCompilingConsumer<T> extends AbstractLambdaCompilingProxy<Consumer<T>> implements Consumer<T> {
|
public class LambdaCompilingConsumer<T> extends AbstractLambdaCompilingProxy<Consumer<T>>
|
||||||
|
implements Consumer<T> {
|
||||||
|
|
||||||
public LambdaCompilingConsumer(Resource resource, ConsumerCompiler<T> compiler) {
|
public LambdaCompilingConsumer(Resource resource, ConsumerCompiler<T> compiler) {
|
||||||
super(resource, compiler);
|
super(resource, compiler);
|
||||||
@@ -34,4 +36,5 @@ public class LambdaCompilingConsumer<T> extends AbstractLambdaCompilingProxy<Con
|
|||||||
public void accept(T input) {
|
public void accept(T input) {
|
||||||
this.getTarget().accept(input);
|
this.getTarget().accept(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -23,9 +23,13 @@ import org.springframework.cloud.function.core.FunctionFactoryMetadata;
|
|||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param <T> input argument type
|
||||||
|
* @param <R> output argument type
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
*/
|
*/
|
||||||
public class LambdaCompilingFunction<T, R> extends AbstractLambdaCompilingProxy<Function<T, R>> implements FunctionFactoryMetadata<Function<T, R>>, Function<T, R> {
|
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) {
|
public LambdaCompilingFunction(Resource resource, FunctionCompiler<T, R> compiler) {
|
||||||
super(resource, compiler);
|
super(resource, compiler);
|
||||||
@@ -35,4 +39,5 @@ public class LambdaCompilingFunction<T, R> extends AbstractLambdaCompilingProxy<
|
|||||||
public R apply(T input) {
|
public R apply(T input) {
|
||||||
return this.getTarget().apply(input);
|
return this.getTarget().apply(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -23,9 +23,11 @@ import org.springframework.cloud.function.core.FunctionFactoryMetadata;
|
|||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param <T> target type
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
*/
|
*/
|
||||||
public class LambdaCompilingSupplier<T> extends AbstractLambdaCompilingProxy<Supplier<T>> implements FunctionFactoryMetadata<Supplier<T>>, Supplier<T> {
|
public class LambdaCompilingSupplier<T> extends AbstractLambdaCompilingProxy<Supplier<T>>
|
||||||
|
implements FunctionFactoryMetadata<Supplier<T>>, Supplier<T> {
|
||||||
|
|
||||||
public LambdaCompilingSupplier(Resource resource, SupplierCompiler<T> compiler) {
|
public LambdaCompilingSupplier(Resource resource, SupplierCompiler<T> compiler) {
|
||||||
super(resource, compiler);
|
super(resource, compiler);
|
||||||
@@ -35,4 +37,5 @@ public class LambdaCompilingSupplier<T> extends AbstractLambdaCompilingProxy<Sup
|
|||||||
public T get() {
|
public T get() {
|
||||||
return this.getTarget().get();
|
return this.getTarget().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -15,10 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.cloud.function.compiler;
|
package org.springframework.cloud.function.compiler;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
@@ -35,21 +31,25 @@ import java.util.jar.JarInputStream;
|
|||||||
import java.util.jar.JarOutputStream;
|
import java.util.jar.JarOutputStream;
|
||||||
import java.util.zip.ZipEntry;
|
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.Test;
|
import org.junit.Test;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import org.springframework.cloud.function.compiler.java.CompilationResult;
|
import org.springframework.cloud.function.compiler.java.CompilationResult;
|
||||||
import org.springframework.cloud.function.compiler.java.RuntimeJavaCompiler;
|
import org.springframework.cloud.function.compiler.java.RuntimeJavaCompiler;
|
||||||
import org.springframework.cloud.function.core.FunctionFactoryUtils;
|
import org.springframework.cloud.function.core.FunctionFactoryUtils;
|
||||||
|
|
||||||
import ch.qos.logback.classic.Level;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import ch.qos.logback.classic.Logger;
|
import static org.junit.Assert.assertEquals;
|
||||||
import ch.qos.logback.classic.LoggerContext;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that verify dependency resolution. Dependencies can be resolved against simple
|
* 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
|
* 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
|
* under BOOT-INF/lib. Finding classes in those locations enables compilation against a
|
||||||
* against a packaged boot jar.
|
* packaged boot jar.
|
||||||
*
|
*
|
||||||
* @author Andy Clement
|
* @author Andy Clement
|
||||||
*/
|
*/
|
||||||
@@ -57,9 +57,10 @@ public class CompilerDependencyResolutionTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void compilingTestClass() throws Exception {
|
public void compilingTestClass() throws Exception {
|
||||||
ClassDescriptor t1 = compile("Test1","package com.test;\npublic class Test1 { public static String doit() { return \"T1\";}}\n");
|
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);
|
String result = (String) t1.clazz.getDeclaredMethod("doit").invoke(null);
|
||||||
assertEquals("T1",result);
|
assertEquals("T1", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -71,89 +72,99 @@ public class CompilerDependencyResolutionTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Doesn't actually verify the caching helps but can be useful to run to see
|
* Doesn't actually verify the caching helps but can be useful to run to see current
|
||||||
* current numbers.
|
* numbers.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void speedtest() {
|
public void speedtest() {
|
||||||
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
|
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
|
||||||
Logger rootLogger = loggerContext.getLogger("org.springframework.cloud.function.compiler");
|
Logger rootLogger = loggerContext
|
||||||
|
.getLogger("org.springframework.cloud.function.compiler");
|
||||||
rootLogger.setLevel(Level.ERROR);
|
rootLogger.setLevel(Level.ERROR);
|
||||||
|
|
||||||
// 10 uses of a single function compiler:
|
// 10 uses of a single function compiler:
|
||||||
long stime = System.currentTimeMillis();
|
long stime = System.currentTimeMillis();
|
||||||
FunctionCompiler<String,String> fc = new FunctionCompiler<String, String>(String.class.getName());
|
FunctionCompiler<String, String> fc = new FunctionCompiler<String, String>(
|
||||||
for (int i=0;i<5;i++) {
|
String.class.getName());
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
stime = System.currentTimeMillis();
|
stime = System.currentTimeMillis();
|
||||||
CompiledFunctionFactory<Function<String, String>> result =
|
CompiledFunctionFactory<Function<String, String>> result = fc.compile("foos",
|
||||||
fc.compile("foos", "flux -> flux.map(v -> v.toUpperCase())", "Flux<String>", "Flux<String>");
|
"flux -> flux.map(v -> v.toUpperCase())", "Flux<String>",
|
||||||
assertThat(FunctionFactoryUtils.isFluxFunction(result.getFactoryMethod())).isTrue();
|
"Flux<String>");
|
||||||
System.out.println("Reusing FunctionCompiler: #"+(i+1)+" = "+(System.currentTimeMillis()-stime)+"ms");
|
assertThat(FunctionFactoryUtils.isFluxFunction(result.getFactoryMethod()))
|
||||||
|
.isTrue();
|
||||||
|
System.out.println("Reusing FunctionCompiler: #" + (i + 1) + " = "
|
||||||
|
+ (System.currentTimeMillis() - stime) + "ms");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3 separate FunctionCompilers:
|
// 3 separate FunctionCompilers:
|
||||||
stime = System.currentTimeMillis();
|
stime = System.currentTimeMillis();
|
||||||
CompiledFunctionFactory<Function<String, String>> compiled = new FunctionCompiler<String, String>(
|
CompiledFunctionFactory<Function<String, String>> compiled = new FunctionCompiler<String, String>(
|
||||||
String.class.getName()).compile("foos", "flux -> flux.map(v -> v.toUpperCase())", "Flux<String>",
|
String.class.getName()).compile("foos",
|
||||||
|
"flux -> flux.map(v -> v.toUpperCase())", "Flux<String>",
|
||||||
"Flux<String>");
|
"Flux<String>");
|
||||||
assertThat(FunctionFactoryUtils.isFluxFunction(compiled.getFactoryMethod())).isTrue();
|
assertThat(FunctionFactoryUtils.isFluxFunction(compiled.getFactoryMethod()))
|
||||||
|
.isTrue();
|
||||||
long etime = System.currentTimeMillis();
|
long etime = System.currentTimeMillis();
|
||||||
long time1 = (etime - stime);
|
long time1 = (etime - stime);
|
||||||
System.out.println("New FunctionCompiler: "+time1+"ms");
|
System.out.println("New FunctionCompiler: " + time1 + "ms");
|
||||||
|
|
||||||
stime = System.currentTimeMillis();
|
stime = System.currentTimeMillis();
|
||||||
compiled = new FunctionCompiler<String, String>(String.class.getName()).compile("foos",
|
compiled = new FunctionCompiler<String, String>(String.class.getName()).compile(
|
||||||
"flux -> flux.map(v -> v.toUpperCase())", "Flux<String>", "Flux<String>");
|
"foos", "flux -> flux.map(v -> v.toUpperCase())", "Flux<String>",
|
||||||
assertThat(FunctionFactoryUtils.isFluxFunction(compiled.getFactoryMethod())).isTrue();
|
"Flux<String>");
|
||||||
|
assertThat(FunctionFactoryUtils.isFluxFunction(compiled.getFactoryMethod()))
|
||||||
|
.isTrue();
|
||||||
etime = System.currentTimeMillis();
|
etime = System.currentTimeMillis();
|
||||||
long time2 = (etime - stime);
|
long time2 = (etime - stime);
|
||||||
System.out.println("New FunctionCompiler: "+time2+"ms");
|
System.out.println("New FunctionCompiler: " + time2 + "ms");
|
||||||
|
|
||||||
stime = System.currentTimeMillis();
|
stime = System.currentTimeMillis();
|
||||||
compiled = new FunctionCompiler<String, String>(String.class.getName()).compile("foos",
|
compiled = new FunctionCompiler<String, String>(String.class.getName()).compile(
|
||||||
"flux -> flux.map(v -> v.toUpperCase())", "Flux<String>", "Flux<String>");
|
"foos", "flux -> flux.map(v -> v.toUpperCase())", "Flux<String>",
|
||||||
assertThat(FunctionFactoryUtils.isFluxFunction(compiled.getFactoryMethod())).isTrue();
|
"Flux<String>");
|
||||||
|
assertThat(FunctionFactoryUtils.isFluxFunction(compiled.getFactoryMethod()))
|
||||||
|
.isTrue();
|
||||||
etime = System.currentTimeMillis();
|
etime = System.currentTimeMillis();
|
||||||
long time3 = (etime - stime);
|
long time3 = (etime - stime);
|
||||||
System.out.println("New FunctionCompiler: "+time3+"ms");
|
System.out.println("New FunctionCompiler: " + time3 + "ms");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void usingJarNoPackageDecl() throws Exception {
|
public void usingJarNoPackageDecl() throws Exception {
|
||||||
ClassDescriptor tx = compile("TestX","public class TestX { public static String doit() { return \"TX\";}}\n");
|
ClassDescriptor tx = compile("TestX",
|
||||||
|
"public class TestX { public static String doit() { return \"TX\";}}\n");
|
||||||
File jar = JarBuilder.create().addEntry(tx).getJar();
|
File jar = JarBuilder.create().addEntry(tx).getJar();
|
||||||
assertJarContents(jar, tx);
|
assertJarContents(jar, tx);
|
||||||
CompilationResult result = new RuntimeJavaCompiler().compile("A",
|
CompilationResult result = new RuntimeJavaCompiler().compile("A",
|
||||||
"public class A {\n" +
|
"public class A {\n" + " public static Object run() {\n"
|
||||||
" public static Object run() {\n" +
|
+ " return new TestX();\n" + " }\n" + "}",
|
||||||
" return new TestX();\n" +
|
jar.toURI().toString());
|
||||||
" }\n" +
|
assertTrue("Should be no problems: " + result.getCompilationMessages(),
|
||||||
"}",
|
result.getCompilationMessages().isEmpty());
|
||||||
jar.toURI().toString());
|
try (URLClassLoader cl = new TestClassLoader(tx, descriptorFromResult(result))) {
|
||||||
assertTrue("Should be no problems: "+result.getCompilationMessages(), result.getCompilationMessages().isEmpty());
|
|
||||||
try (URLClassLoader cl = new TestClassLoader(tx,descriptorFromResult(result))) {
|
|
||||||
Class<?> class1 = cl.loadClass("A");
|
Class<?> class1 = cl.loadClass("A");
|
||||||
Object invoke = class1.getDeclaredMethod("run").invoke(null);
|
Object invoke = class1.getDeclaredMethod("run").invoke(null);
|
||||||
assertEquals(tx.name, invoke.getClass().getName());
|
assertEquals(tx.name, invoke.getClass().getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A class with no package declaration is placed under BOOT-INF/classes/ in a jar that
|
||||||
// A class with no package declaration is placed under BOOT-INF/classes/ in a jar that is then used for resolution
|
// is then used for resolution
|
||||||
@Test
|
@Test
|
||||||
public void usingJarNoPackageDeclBootInfClasses() throws Exception {
|
public void usingJarNoPackageDeclBootInfClasses() throws Exception {
|
||||||
ClassDescriptor t1 = compile("TestX","public class TestX { public static String doit() { return \"TX\";}}\n");
|
ClassDescriptor t1 = compile("TestX",
|
||||||
File jar = JarBuilder.create().addEntryWithPrefix("BOOT-INF/classes/",t1).getJar();
|
"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);
|
assertJarContents(jar, "BOOT-INF/classes/", t1);
|
||||||
CompilationResult result = new RuntimeJavaCompiler().compile("A",
|
CompilationResult result = new RuntimeJavaCompiler().compile("A",
|
||||||
"public class A {\n" +
|
"public class A {\n" + " public static Object run() {\n"
|
||||||
" public static Object run() {\n" +
|
+ " return new TestX();\n" + " }\n" + "}",
|
||||||
" return new TestX();\n" +
|
jar.toURI().toString());
|
||||||
" }\n" +
|
assertTrue("Should be no problems: " + result.getCompilationMessages(),
|
||||||
"}",
|
result.getCompilationMessages().isEmpty());
|
||||||
jar.toURI().toString());
|
try (URLClassLoader cl = new TestClassLoader(t1, descriptorFromResult(result))) {
|
||||||
assertTrue("Should be no problems: "+result.getCompilationMessages(), result.getCompilationMessages().isEmpty());
|
|
||||||
try (URLClassLoader cl = new TestClassLoader(t1,descriptorFromResult(result))) {
|
|
||||||
Class<?> class1 = cl.loadClass("A");
|
Class<?> class1 = cl.loadClass("A");
|
||||||
Object invoke = class1.getDeclaredMethod("run").invoke(null);
|
Object invoke = class1.getDeclaredMethod("run").invoke(null);
|
||||||
assertEquals(t1.name, invoke.getClass().getName());
|
assertEquals(t1.name, invoke.getClass().getName());
|
||||||
@@ -164,27 +175,25 @@ public class CompilerDependencyResolutionTests {
|
|||||||
// under BOOT-INF/lib/ in a jar that is then used for resolution
|
// under BOOT-INF/lib/ in a jar that is then used for resolution
|
||||||
@Test
|
@Test
|
||||||
public void usingJarNoPackageDeclNestedBootInfLib() throws Exception {
|
public void usingJarNoPackageDeclNestedBootInfLib() throws Exception {
|
||||||
ClassDescriptor t1 = compile("TestX","public class TestX { public static String doit() { return \"TX\";}}\n");
|
ClassDescriptor t1 = compile("TestX",
|
||||||
|
"public class TestX { public static String doit() { return \"TX\";}}\n");
|
||||||
File jar = JarBuilder.create().addEntry(t1).getJar();
|
File jar = JarBuilder.create().addEntry(t1).getJar();
|
||||||
assertJarContents(jar, t1);
|
assertJarContents(jar, t1);
|
||||||
// Now stick that jar in another jar!
|
// Now stick that jar in another jar!
|
||||||
File jar2 = JarBuilder.create().addEntry("BOOT-INF/lib/inner.jar",jar).getJar();
|
File jar2 = JarBuilder.create().addEntry("BOOT-INF/lib/inner.jar", jar).getJar();
|
||||||
CompilationResult result = new RuntimeJavaCompiler().compile("A",
|
CompilationResult result = new RuntimeJavaCompiler().compile("A",
|
||||||
"public class A {\n" +
|
"public class A {\n" + " public static Object run() {\n"
|
||||||
" public static Object run() {\n" +
|
+ " return new TestX();\n" + " }\n" + "}",
|
||||||
" return new TestX();\n" +
|
jar2.toURI().toString());
|
||||||
" }\n" +
|
assertTrue("Should be no problems: " + result.getCompilationMessages(),
|
||||||
"}",
|
result.getCompilationMessages().isEmpty());
|
||||||
jar2.toURI().toString());
|
try (URLClassLoader cl = new TestClassLoader(t1, descriptorFromResult(result))) {
|
||||||
assertTrue("Should be no problems: "+result.getCompilationMessages(), result.getCompilationMessages().isEmpty());
|
|
||||||
try (URLClassLoader cl = new TestClassLoader(t1,descriptorFromResult(result))) {
|
|
||||||
Class<?> class1 = cl.loadClass("A");
|
Class<?> class1 = cl.loadClass("A");
|
||||||
Object invoke = class1.getDeclaredMethod("run").invoke(null);
|
Object invoke = class1.getDeclaredMethod("run").invoke(null);
|
||||||
assertEquals(t1.name, invoke.getClass().getName());
|
assertEquals(t1.name, invoke.getClass().getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Build a jar containing a type with a package declaration and building against it
|
// Build a jar containing a type with a package declaration and building against it
|
||||||
@Test
|
@Test
|
||||||
public void usingJarWithPackageDecl() throws Exception {
|
public void usingJarWithPackageDecl() throws Exception {
|
||||||
@@ -192,15 +201,13 @@ public class CompilerDependencyResolutionTests {
|
|||||||
File jar = JarBuilder.create().addEntry(t1).getJar();
|
File jar = JarBuilder.create().addEntry(t1).getJar();
|
||||||
assertJarContents(jar, t1);
|
assertJarContents(jar, t1);
|
||||||
CompilationResult result = new RuntimeJavaCompiler().compile("A",
|
CompilationResult result = new RuntimeJavaCompiler().compile("A",
|
||||||
"import " + t1.name.replace('$', '.') + ";\n" +
|
"import " + t1.name.replace('$', '.') + ";\n" + "public class A {\n"
|
||||||
"public class A {\n" +
|
+ " public static Object run() {\n" + " return new Test1();\n"
|
||||||
" public static Object run() {\n" +
|
+ " }\n" + "}",
|
||||||
" return new Test1();\n" +
|
jar.toURI().toString());
|
||||||
" }\n" +
|
assertTrue("Should be no problems: " + result.getCompilationMessages(),
|
||||||
"}",
|
result.getCompilationMessages().isEmpty());
|
||||||
jar.toURI().toString());
|
try (URLClassLoader cl = new TestClassLoader(t1, descriptorFromResult(result))) {
|
||||||
assertTrue("Should be no problems: "+result.getCompilationMessages(), result.getCompilationMessages().isEmpty());
|
|
||||||
try (URLClassLoader cl = new TestClassLoader(t1,descriptorFromResult(result))) {
|
|
||||||
Class<?> class1 = cl.loadClass("A");
|
Class<?> class1 = cl.loadClass("A");
|
||||||
Object invoke = class1.getDeclaredMethod("run").invoke(null);
|
Object invoke = class1.getDeclaredMethod("run").invoke(null);
|
||||||
assertEquals(t1.name, invoke.getClass().getName());
|
assertEquals(t1.name, invoke.getClass().getName());
|
||||||
@@ -211,25 +218,23 @@ public class CompilerDependencyResolutionTests {
|
|||||||
public void usingJarWithPackageDeclBootInfClasses() throws Exception {
|
public void usingJarWithPackageDeclBootInfClasses() throws Exception {
|
||||||
// Here the dependencies are under BOOT-INF/classes in the jar
|
// Here the dependencies are under BOOT-INF/classes in the jar
|
||||||
ClassDescriptor t1 = getTestClass("1");
|
ClassDescriptor t1 = getTestClass("1");
|
||||||
File jar = JarBuilder.create().addEntryWithPrefix("BOOT-INF/classes/",t1).getJar();
|
File jar = JarBuilder.create().addEntryWithPrefix("BOOT-INF/classes/", t1)
|
||||||
|
.getJar();
|
||||||
assertJarContents(jar, "BOOT-INF/classes/", t1);
|
assertJarContents(jar, "BOOT-INF/classes/", t1);
|
||||||
CompilationResult result = new RuntimeJavaCompiler().compile("A",
|
CompilationResult result = new RuntimeJavaCompiler().compile("A",
|
||||||
"import " + t1.name.replace('$', '.') + ";\n" +
|
"import " + t1.name.replace('$', '.') + ";\n" + "public class A {\n"
|
||||||
"public class A {\n" +
|
+ " public static Object run() {\n" + " return new Test1();\n"
|
||||||
" public static Object run() {\n" +
|
+ " }\n" + "}",
|
||||||
" return new Test1();\n" +
|
jar.toURI().toString());
|
||||||
" }\n" +
|
assertTrue("Should be no problems: " + result.getCompilationMessages(),
|
||||||
"}",
|
result.getCompilationMessages().isEmpty());
|
||||||
jar.toURI().toString());
|
try (URLClassLoader cl = new TestClassLoader(t1, descriptorFromResult(result))) {
|
||||||
assertTrue("Should be no problems: "+result.getCompilationMessages(), result.getCompilationMessages().isEmpty());
|
|
||||||
try (URLClassLoader cl = new TestClassLoader(t1,descriptorFromResult(result))) {
|
|
||||||
Class<?> class1 = cl.loadClass("A");
|
Class<?> class1 = cl.loadClass("A");
|
||||||
Object invoke = class1.getDeclaredMethod("run").invoke(null);
|
Object invoke = class1.getDeclaredMethod("run").invoke(null);
|
||||||
assertEquals(t1.name, invoke.getClass().getName());
|
assertEquals(t1.name, invoke.getClass().getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void usingJarWithPackageDeclNestedBootInfLib() throws Exception {
|
public void usingJarWithPackageDeclNestedBootInfLib() throws Exception {
|
||||||
// Here the dependencies are under BOOT-INF/lib/nested.jar
|
// Here the dependencies are under BOOT-INF/lib/nested.jar
|
||||||
@@ -237,17 +242,15 @@ public class CompilerDependencyResolutionTests {
|
|||||||
File jar = JarBuilder.create().addEntry(t1).getJar();
|
File jar = JarBuilder.create().addEntry(t1).getJar();
|
||||||
assertJarContents(jar, t1);
|
assertJarContents(jar, t1);
|
||||||
// Now stick that jar in another jar!
|
// Now stick that jar in another jar!
|
||||||
File jar2 = JarBuilder.create().addEntry("BOOT-INF/lib/inner.jar",jar).getJar();
|
File jar2 = JarBuilder.create().addEntry("BOOT-INF/lib/inner.jar", jar).getJar();
|
||||||
CompilationResult result = new RuntimeJavaCompiler().compile("A",
|
CompilationResult result = new RuntimeJavaCompiler().compile("A",
|
||||||
"import " + t1.name.replace('$', '.') + ";\n" +
|
"import " + t1.name.replace('$', '.') + ";\n" + "public class A {\n"
|
||||||
"public class A {\n" +
|
+ " public static Object run() {\n" + " return new Test1();\n"
|
||||||
" public static Object run() {\n" +
|
+ " }\n" + "}",
|
||||||
" return new Test1();\n" +
|
jar2.toURI().toString());
|
||||||
" }\n" +
|
assertTrue("Should be no problems: " + result.getCompilationMessages(),
|
||||||
"}",
|
result.getCompilationMessages().isEmpty());
|
||||||
jar2.toURI().toString());
|
try (URLClassLoader cl = new TestClassLoader(t1, descriptorFromResult(result))) {
|
||||||
assertTrue("Should be no problems: "+result.getCompilationMessages(), result.getCompilationMessages().isEmpty());
|
|
||||||
try (URLClassLoader cl = new TestClassLoader(t1,descriptorFromResult(result))) {
|
|
||||||
Class<?> class1 = cl.loadClass("A");
|
Class<?> class1 = cl.loadClass("A");
|
||||||
Object invoke = class1.getDeclaredMethod("run").invoke(null);
|
Object invoke = class1.getDeclaredMethod("run").invoke(null);
|
||||||
assertEquals(t1.name, invoke.getClass().getName());
|
assertEquals(t1.name, invoke.getClass().getName());
|
||||||
@@ -256,57 +259,31 @@ public class CompilerDependencyResolutionTests {
|
|||||||
|
|
||||||
// ---
|
// ---
|
||||||
|
|
||||||
// Simple classloader that can load from descriptors
|
|
||||||
class TestClassLoader extends URLClassLoader {
|
|
||||||
|
|
||||||
ClassDescriptor[] descriptors;
|
|
||||||
|
|
||||||
public TestClassLoader(ClassDescriptor... descriptors) {
|
|
||||||
super(new URL[0], TestClassLoader.class.getClassLoader());
|
|
||||||
this.descriptors = descriptors;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
|
||||||
for (ClassDescriptor descriptor: descriptors) {
|
|
||||||
if (descriptor.name.equals(name)) {
|
|
||||||
return defineClass(descriptor.name, descriptor.bytes, 0, descriptor.bytes.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.findClass(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple holder for the result of compilation
|
|
||||||
static class ClassDescriptor {
|
|
||||||
final String name;
|
|
||||||
final byte[] bytes;
|
|
||||||
final Class<?> clazz;
|
|
||||||
|
|
||||||
public ClassDescriptor(String name, byte[] bytes, Class<?> clazz) {
|
|
||||||
this.name = name;
|
|
||||||
this.bytes = bytes;
|
|
||||||
this.clazz = clazz;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ClassDescriptor descriptorFromResult(CompilationResult result) {
|
private ClassDescriptor descriptorFromResult(CompilationResult result) {
|
||||||
Class<?> clazz = result.getCompiledClasses().get(0);
|
Class<?> clazz = result.getCompiledClasses().get(0);
|
||||||
return new ClassDescriptor(clazz.getName(),result.getClassBytes(clazz.getName()),clazz);
|
return new ClassDescriptor(clazz.getName(), result.getClassBytes(clazz.getName()),
|
||||||
|
clazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClassDescriptor compile(String className, String classSourceCode) {
|
private ClassDescriptor compile(String className, String classSourceCode) {
|
||||||
CompilationResult compile = new RuntimeJavaCompiler().compile(className, classSourceCode);
|
CompilationResult compile = new RuntimeJavaCompiler().compile(className,
|
||||||
assertTrue("Should be empty: \n"+compile.getCompilationMessages(), compile.getCompilationMessages().isEmpty());
|
classSourceCode);
|
||||||
|
assertTrue("Should be empty: \n" + compile.getCompilationMessages(),
|
||||||
|
compile.getCompilationMessages().isEmpty());
|
||||||
Class<?> clazz = compile.getCompiledClasses().get(0);
|
Class<?> clazz = compile.getCompiledClasses().get(0);
|
||||||
return new ClassDescriptor(clazz.getName(),compile.getClassBytes(clazz.getName()), compile.getCompiledClasses().get(0));
|
return new ClassDescriptor(clazz.getName(),
|
||||||
|
compile.getClassBytes(clazz.getName()),
|
||||||
|
compile.getCompiledClasses().get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClassDescriptor getTestClass(String suffix) {
|
private ClassDescriptor getTestClass(String suffix) {
|
||||||
try {
|
try {
|
||||||
return compile("Test"+suffix,"package com.test;\npublic class Test"+suffix+" { public static String doit() { return \"T"+suffix+"\";}}\n");
|
return compile("Test" + suffix,
|
||||||
} catch (Exception e) {
|
"package com.test;\npublic class Test" + suffix
|
||||||
|
+ " { public static String doit() { return \"T" + suffix
|
||||||
|
+ "\";}}\n");
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -315,7 +292,8 @@ public class CompilerDependencyResolutionTests {
|
|||||||
assertJarContents(jar, "", classdescriptors);
|
assertJarContents(jar, "", classdescriptors);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertJarContents(File jar, String prefix, ClassDescriptor... classDescriptors) {
|
private void assertJarContents(File jar, String prefix,
|
||||||
|
ClassDescriptor... classDescriptors) {
|
||||||
List<String> clazzes = new ArrayList<>();
|
List<String> clazzes = new ArrayList<>();
|
||||||
for (ClassDescriptor classDescriptor : classDescriptors) {
|
for (ClassDescriptor classDescriptor : classDescriptors) {
|
||||||
clazzes.add(prefix + classDescriptor.name.replace('.', '/') + ".class");
|
clazzes.add(prefix + classDescriptor.name.replace('.', '/') + ".class");
|
||||||
@@ -335,7 +313,8 @@ public class CompilerDependencyResolutionTests {
|
|||||||
fn.accept(nextJarEntry);
|
fn.accept(nextJarEntry);
|
||||||
}
|
}
|
||||||
jarInputStream.close();
|
jarInputStream.close();
|
||||||
} catch (IOException ioe) {
|
}
|
||||||
|
catch (IOException ioe) {
|
||||||
ioe.printStackTrace();
|
ioe.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -348,31 +327,55 @@ public class CompilerDependencyResolutionTests {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Simple holder for the result of compilation
|
||||||
|
static class ClassDescriptor {
|
||||||
|
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
final byte[] bytes;
|
||||||
|
|
||||||
|
final Class<?> clazz;
|
||||||
|
|
||||||
|
public ClassDescriptor(String name, byte[] bytes, Class<?> clazz) {
|
||||||
|
this.name = name;
|
||||||
|
this.bytes = bytes;
|
||||||
|
this.clazz = clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
static class JarBuilder {
|
static class JarBuilder {
|
||||||
|
|
||||||
File jarFile;
|
File jarFile;
|
||||||
|
|
||||||
JarOutputStream jos;
|
JarOutputStream jos;
|
||||||
|
|
||||||
private JarBuilder() {
|
private JarBuilder() {
|
||||||
try {
|
try {
|
||||||
File newJar = File.createTempFile("test", ".jar");
|
File newJar = File.createTempFile("test", ".jar");
|
||||||
jarFile = newJar.getAbsoluteFile();
|
this.jarFile = newJar.getAbsoluteFile();
|
||||||
newJar.delete();
|
newJar.delete();
|
||||||
jos = new JarOutputStream(new FileOutputStream(jarFile));
|
this.jos = new JarOutputStream(new FileOutputStream(this.jarFile));
|
||||||
jarFile.deleteOnExit();
|
this.jarFile.deleteOnExit();
|
||||||
} catch (IOException e) {
|
}
|
||||||
|
catch (IOException e) {
|
||||||
throw new IllegalStateException("Unexpected problem creating file", e);
|
throw new IllegalStateException("Unexpected problem creating file", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static JarBuilder create() {
|
||||||
|
return new JarBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
public JarBuilder addEntry(String entryName, File entryContentFile) {
|
public JarBuilder addEntry(String entryName, File entryContentFile) {
|
||||||
try {
|
try {
|
||||||
ZipEntry ze = new ZipEntry(entryName);
|
ZipEntry ze = new ZipEntry(entryName);
|
||||||
jos.putNextEntry(ze);
|
this.jos.putNextEntry(ze);
|
||||||
jos.write(loadBytes(entryContentFile));
|
this.jos.write(loadBytes(entryContentFile));
|
||||||
jos.closeEntry();
|
this.jos.closeEntry();
|
||||||
return this;
|
return this;
|
||||||
} catch (IOException e) {
|
}
|
||||||
|
catch (IOException e) {
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -386,7 +389,8 @@ public class CompilerDependencyResolutionTests {
|
|||||||
if (bs == null) {
|
if (bs == null) {
|
||||||
bs = new byte[readCount];
|
bs = new byte[readCount];
|
||||||
System.arraycopy(buf, 0, bs, 0, readCount);
|
System.arraycopy(buf, 0, bs, 0, readCount);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
byte[] newbs = new byte[bs.length + readCount];
|
byte[] newbs = new byte[bs.length + readCount];
|
||||||
System.arraycopy(bs, 0, newbs, 0, bs.length);
|
System.arraycopy(bs, 0, newbs, 0, bs.length);
|
||||||
System.arraycopy(buf, 0, newbs, bs.length, readCount);
|
System.arraycopy(buf, 0, newbs, bs.length, readCount);
|
||||||
@@ -394,7 +398,8 @@ public class CompilerDependencyResolutionTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bs;
|
return bs;
|
||||||
} catch (IOException ioe) {
|
}
|
||||||
|
catch (IOException ioe) {
|
||||||
throw new IllegalStateException(ioe);
|
throw new IllegalStateException(ioe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -414,26 +419,47 @@ public class CompilerDependencyResolutionTests {
|
|||||||
try {
|
try {
|
||||||
String n = holder.name.replace('.', '/') + ".class";
|
String n = holder.name.replace('.', '/') + ".class";
|
||||||
ZipEntry ze = new ZipEntry(prefix + n);
|
ZipEntry ze = new ZipEntry(prefix + n);
|
||||||
jos.putNextEntry(ze);
|
this.jos.putNextEntry(ze);
|
||||||
jos.write(holder.bytes);
|
this.jos.write(holder.bytes);
|
||||||
jos.closeEntry();
|
this.jos.closeEntry();
|
||||||
return this;
|
return this;
|
||||||
} catch (IOException e) {
|
}
|
||||||
|
catch (IOException e) {
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JarBuilder create() {
|
|
||||||
return new JarBuilder();
|
|
||||||
}
|
|
||||||
|
|
||||||
private File getJar() {
|
private File getJar() {
|
||||||
try {
|
try {
|
||||||
jos.close();
|
this.jos.close();
|
||||||
} catch (IOException e) {
|
}
|
||||||
|
catch (IOException e) {
|
||||||
throw new IllegalStateException("Unable to close jar", e);
|
throw new IllegalStateException("Unable to close jar", e);
|
||||||
}
|
}
|
||||||
return jarFile;
|
return this.jarFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple classloader that can load from descriptors
|
||||||
|
class TestClassLoader extends URLClassLoader {
|
||||||
|
|
||||||
|
ClassDescriptor[] descriptors;
|
||||||
|
|
||||||
|
public 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,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -35,14 +35,16 @@ public class ConsumerCompilerTests {
|
|||||||
CompiledFunctionFactory<Consumer<String>> compiled = new ConsumerCompiler<String>(
|
CompiledFunctionFactory<Consumer<String>> compiled = new ConsumerCompiler<String>(
|
||||||
String.class.getName()).compile("foos",
|
String.class.getName()).compile("foos",
|
||||||
"flux -> flux.subscribe(System.out::println)", "Flux<String>");
|
"flux -> flux.subscribe(System.out::println)", "Flux<String>");
|
||||||
assertThat(FunctionFactoryUtils.isFluxConsumer(compiled.getFactoryMethod())).isTrue();
|
assertThat(FunctionFactoryUtils.isFluxConsumer(compiled.getFactoryMethod()))
|
||||||
|
.isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void consumesString() {
|
public void consumesString() {
|
||||||
CompiledFunctionFactory<Consumer<String>> compiled = new ConsumerCompiler<String>(
|
CompiledFunctionFactory<Consumer<String>> compiled = new ConsumerCompiler<String>(
|
||||||
String.class.getName()).compile("foos", "System.out::println", "String");
|
String.class.getName()).compile("foos", "System.out::println", "String");
|
||||||
assertThat(FunctionFactoryUtils.isFluxConsumer(compiled.getFactoryMethod())).isFalse();
|
assertThat(FunctionFactoryUtils.isFluxConsumer(compiled.getFactoryMethod()))
|
||||||
|
.isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -34,15 +34,19 @@ public class FunctionCompilerTests {
|
|||||||
public void transformsFluxString() {
|
public void transformsFluxString() {
|
||||||
CompiledFunctionFactory<Function<String, String>> compiled = new FunctionCompiler<String, String>(
|
CompiledFunctionFactory<Function<String, String>> compiled = new FunctionCompiler<String, String>(
|
||||||
String.class.getName()).compile("foos",
|
String.class.getName()).compile("foos",
|
||||||
"flux -> flux.map(v -> v.toUpperCase())", "Flux<String>", "Flux<String>");
|
"flux -> flux.map(v -> v.toUpperCase())", "Flux<String>",
|
||||||
assertThat(FunctionFactoryUtils.isFluxFunction(compiled.getFactoryMethod())).isTrue();
|
"Flux<String>");
|
||||||
|
assertThat(FunctionFactoryUtils.isFluxFunction(compiled.getFactoryMethod()))
|
||||||
|
.isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void transformsString() {
|
public void transformsString() {
|
||||||
CompiledFunctionFactory<Function<String, String>> compiled = new FunctionCompiler<String, String>(
|
CompiledFunctionFactory<Function<String, String>> compiled = new FunctionCompiler<String, String>(
|
||||||
String.class.getName()).compile("foos", "v -> v.toUpperCase()", "String", "String");
|
String.class.getName()).compile("foos", "v -> v.toUpperCase()", "String",
|
||||||
assertThat(FunctionFactoryUtils.isFluxFunction(compiled.getFactoryMethod())).isFalse();
|
"String");
|
||||||
|
assertThat(FunctionFactoryUtils.isFluxFunction(compiled.getFactoryMethod()))
|
||||||
|
.isFalse();
|
||||||
assertThat(compiled.getResult().apply("hello")).isEqualTo("HELLO");
|
assertThat(compiled.getResult().apply("hello")).isEqualTo("HELLO");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -36,15 +36,16 @@ public class SupplierCompilerTests {
|
|||||||
CompiledFunctionFactory<Supplier<String>> compiled = new SupplierCompiler<String>(
|
CompiledFunctionFactory<Supplier<String>> compiled = new SupplierCompiler<String>(
|
||||||
String.class.getName()).compile("foos",
|
String.class.getName()).compile("foos",
|
||||||
"() -> Flux.just(\"foo\", \"bar\")", "Flux<String>");
|
"() -> Flux.just(\"foo\", \"bar\")", "Flux<String>");
|
||||||
assertThat(FunctionFactoryUtils.isFluxSupplier(compiled.getFactoryMethod())).isTrue();
|
assertThat(FunctionFactoryUtils.isFluxSupplier(compiled.getFactoryMethod()))
|
||||||
|
.isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void supppliesString() {
|
public void supppliesString() {
|
||||||
CompiledFunctionFactory<Supplier<String>> compiled = new SupplierCompiler<String>(
|
CompiledFunctionFactory<Supplier<String>> compiled = new SupplierCompiler<String>(
|
||||||
String.class.getName()).compile("foos",
|
String.class.getName()).compile("foos", "() -> \"foo\"", "String");
|
||||||
"() -> \"foo\"", "String");
|
assertThat(FunctionFactoryUtils.isFluxSupplier(compiled.getFactoryMethod()))
|
||||||
assertThat(FunctionFactoryUtils.isFluxSupplier(compiled.getFactoryMethod())).isFalse();
|
.isFalse();
|
||||||
assertThat(compiled.getResult().get()).isEqualTo("foo");
|
assertThat(compiled.getResult().get()).isEqualTo("foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,9 +53,11 @@ public class SupplierCompilerTests {
|
|||||||
public void supppliesFluxStreamString() {
|
public void supppliesFluxStreamString() {
|
||||||
CompiledFunctionFactory<Supplier<Flux<String>>> compiled = new SupplierCompiler<Flux<String>>(
|
CompiledFunctionFactory<Supplier<Flux<String>>> compiled = new SupplierCompiler<Flux<String>>(
|
||||||
String.class.getName()).compile("foos",
|
String.class.getName()).compile("foos",
|
||||||
"() -> Flux.interval(Duration.ofMillis(1000)).map(Object::toString)",
|
"() -> Flux.interval(Duration.ofMillis(1000)).map(Object::toString)",
|
||||||
"Flux<String>");
|
"Flux<String>");
|
||||||
assertThat(FunctionFactoryUtils.isFluxSupplier(compiled.getFactoryMethod())).isTrue();
|
assertThat(FunctionFactoryUtils.isFluxSupplier(compiled.getFactoryMethod()))
|
||||||
|
.isTrue();
|
||||||
assertThat(compiled.getResult().get().blockFirst()).isEqualTo("0");
|
assertThat(compiled.getResult().get().blockFirst()).isEqualTo("0");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,9 +16,6 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.function.compiler.java;
|
package org.springframework.cloud.function.compiler.java;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@@ -26,6 +23,9 @@ import java.util.function.Supplier;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Andy Clement
|
* @author Andy Clement
|
||||||
*/
|
*/
|
||||||
@@ -45,21 +45,24 @@ public class RuntimeJavaCompilerTests {
|
|||||||
Locale.setDefault(Locale.ENGLISH);
|
Locale.setDefault(Locale.ENGLISH);
|
||||||
RuntimeJavaCompiler rjc = new RuntimeJavaCompiler();
|
RuntimeJavaCompiler rjc = new RuntimeJavaCompiler();
|
||||||
CompilationResult cr = rjc.compile("A",
|
CompilationResult cr = rjc.compile("A",
|
||||||
"public class A implements java.util.function.Supplier { "+
|
"public class A implements java.util.function.Supplier { "
|
||||||
" public String get() {\n"+
|
+ " public String get() {\n"
|
||||||
" ExpressionParser parser = new SpelExpressionParser();\n" +
|
+ " ExpressionParser parser = new SpelExpressionParser();\n"
|
||||||
" Expression exp = parser.parseExpression(\"'Hello World'\");\n" +
|
+ " Expression exp = parser.parseExpression(\"'Hello World'\");\n"
|
||||||
" String message = (String) exp.getValue();"+
|
+ " String message = (String) exp.getValue();"
|
||||||
" return message;\n"+
|
+ " return message;\n" + " }\n" + "}");
|
||||||
" }\n"+
|
|
||||||
"}");
|
|
||||||
List<CompilationMessage> compilationMessages = cr.getCompilationMessages();
|
List<CompilationMessage> compilationMessages = cr.getCompilationMessages();
|
||||||
assertEquals(3,compilationMessages.size());
|
assertEquals(3, compilationMessages.size());
|
||||||
assertTrue(compilationMessages.get(0).getMessage().contains("cannot find symbol"));
|
assertTrue(
|
||||||
assertTrue(compilationMessages.get(0).getMessage().contains("class ExpressionParser"));
|
compilationMessages.get(0).getMessage().contains("cannot find symbol"));
|
||||||
assertTrue(compilationMessages.get(1).getMessage().contains("cannot find symbol"));
|
assertTrue(compilationMessages.get(0).getMessage()
|
||||||
assertTrue(compilationMessages.get(1).getMessage().contains("class SpelExpressionParser"));
|
.contains("class ExpressionParser"));
|
||||||
assertTrue(compilationMessages.get(2).getMessage().contains("cannot find symbol"));
|
assertTrue(
|
||||||
|
compilationMessages.get(1).getMessage().contains("cannot find symbol"));
|
||||||
|
assertTrue(compilationMessages.get(1).getMessage()
|
||||||
|
.contains("class SpelExpressionParser"));
|
||||||
|
assertTrue(
|
||||||
|
compilationMessages.get(2).getMessage().contains("cannot find symbol"));
|
||||||
assertTrue(compilationMessages.get(2).getMessage().contains("class Expression"));
|
assertTrue(compilationMessages.get(2).getMessage().contains("class Expression"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,45 +70,44 @@ public class RuntimeJavaCompilerTests {
|
|||||||
public void okWithImportedDependencies() throws Exception {
|
public void okWithImportedDependencies() throws Exception {
|
||||||
RuntimeJavaCompiler rjc = new RuntimeJavaCompiler();
|
RuntimeJavaCompiler rjc = new RuntimeJavaCompiler();
|
||||||
CompilationResult cr = rjc.compile("A",
|
CompilationResult cr = rjc.compile("A",
|
||||||
"import org.springframework.expression.*;\n"+
|
"import org.springframework.expression.*;\n"
|
||||||
"import org.springframework.expression.spel.standard.*;\n"+
|
+ "import org.springframework.expression.spel.standard.*;\n"
|
||||||
"public class A implements java.util.function.Supplier {\n"+
|
+ "public class A implements java.util.function.Supplier {\n"
|
||||||
" public String get() {\n"+
|
+ " public String get() {\n"
|
||||||
" ExpressionParser parser = new SpelExpressionParser();\n" +
|
+ " ExpressionParser parser = new SpelExpressionParser();\n"
|
||||||
" Expression exp = parser.parseExpression(\"'Hello World'\");\n" +
|
+ " Expression exp = parser.parseExpression(\"'Hello World'\");\n"
|
||||||
" String message = (String) exp.getValue();\n"+
|
+ " String message = (String) exp.getValue();\n"
|
||||||
" return message;\n"+
|
+ " return message;\n" + " }\n" + "}",
|
||||||
" }\n"+
|
"maven://org.springframework:spring-expression:4.3.9.RELEASE");
|
||||||
"}","maven://org.springframework:spring-expression:4.3.9.RELEASE");
|
|
||||||
List<CompilationMessage> compilationMessages = cr.getCompilationMessages();
|
List<CompilationMessage> compilationMessages = cr.getCompilationMessages();
|
||||||
assertTrue(compilationMessages.isEmpty());
|
assertTrue(compilationMessages.isEmpty());
|
||||||
try (SimpleClassLoader cl = new SimpleClassLoader(this.getClass().getClassLoader())) {
|
try (SimpleClassLoader cl = new SimpleClassLoader(
|
||||||
Class<?> clazz = cl.defineClass("A",cr.getClassBytes("A"));
|
this.getClass().getClassLoader())) {
|
||||||
|
Class<?> clazz = cl.defineClass("A", cr.getClassBytes("A"));
|
||||||
Supplier<String> supplier = (Supplier<String>) clazz.newInstance();
|
Supplier<String> supplier = (Supplier<String>) clazz.newInstance();
|
||||||
assertEquals("Hello World",supplier.get());
|
assertEquals("Hello World", supplier.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void okWithImportedDependencies2() throws Exception {
|
public void okWithImportedDependencies2() throws Exception {
|
||||||
RuntimeJavaCompiler rjc = new RuntimeJavaCompiler();
|
RuntimeJavaCompiler rjc = new RuntimeJavaCompiler();
|
||||||
String source =
|
String source = "import org.joda.time.*;\n"
|
||||||
"import org.joda.time.*;\n"+
|
+ "public class A implements java.util.function.Supplier {\n"
|
||||||
"public class A implements java.util.function.Supplier {\n"+
|
+ " public String get() {\n" + " DateTime dt = new DateTime();\n"
|
||||||
" public String get() {\n"+
|
+ " int month = dt.getMonthOfYear();\n"
|
||||||
" DateTime dt = new DateTime();\n" +
|
+ " return String.valueOf(month>0);\n" + " }\n" + "}";
|
||||||
" int month = dt.getMonthOfYear();\n"+
|
CompilationResult cr = rjc.compile("A", source,
|
||||||
" return String.valueOf(month>0);\n"+
|
"maven://joda-time:joda-time:2.9.9");
|
||||||
" }\n"+
|
|
||||||
"}";
|
|
||||||
CompilationResult cr = rjc.compile("A", source, "maven://joda-time:joda-time:2.9.9");
|
|
||||||
List<CompilationMessage> compilationMessages = cr.getCompilationMessages();
|
List<CompilationMessage> compilationMessages = cr.getCompilationMessages();
|
||||||
assertTrue(compilationMessages.isEmpty());
|
assertTrue(compilationMessages.isEmpty());
|
||||||
List<File> resolvedAdditionalDependencies = cr.getResolvedAdditionalDependencies();
|
List<File> resolvedAdditionalDependencies = cr
|
||||||
try (SimpleClassLoader cl = new SimpleClassLoader(resolvedAdditionalDependencies, this.getClass().getClassLoader())) {
|
.getResolvedAdditionalDependencies();
|
||||||
Class<?> clazz = cl.defineClass("A",cr.getClassBytes("A"));
|
try (SimpleClassLoader cl = new SimpleClassLoader(resolvedAdditionalDependencies,
|
||||||
|
this.getClass().getClassLoader())) {
|
||||||
|
Class<?> clazz = cl.defineClass("A", cr.getClassBytes("A"));
|
||||||
Supplier<String> supplier = (Supplier<String>) clazz.newInstance();
|
Supplier<String> supplier = (Supplier<String>) clazz.newInstance();
|
||||||
assertEquals("true",supplier.get());
|
assertEquals("true", supplier.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
cr = rjc.compile("A", source,
|
cr = rjc.compile("A", source,
|
||||||
@@ -114,10 +116,11 @@ public class RuntimeJavaCompilerTests {
|
|||||||
compilationMessages = cr.getCompilationMessages();
|
compilationMessages = cr.getCompilationMessages();
|
||||||
assertTrue(compilationMessages.isEmpty());
|
assertTrue(compilationMessages.isEmpty());
|
||||||
resolvedAdditionalDependencies = cr.getResolvedAdditionalDependencies();
|
resolvedAdditionalDependencies = cr.getResolvedAdditionalDependencies();
|
||||||
try (SimpleClassLoader cl = new SimpleClassLoader(resolvedAdditionalDependencies, this.getClass().getClassLoader())) {
|
try (SimpleClassLoader cl = new SimpleClassLoader(resolvedAdditionalDependencies,
|
||||||
Class<?> clazz = cl.defineClass("A",cr.getClassBytes("A"));
|
this.getClass().getClassLoader())) {
|
||||||
|
Class<?> clazz = cl.defineClass("A", cr.getClassBytes("A"));
|
||||||
Supplier<String> supplier = (Supplier<String>) clazz.newInstance();
|
Supplier<String> supplier = (Supplier<String>) clazz.newInstance();
|
||||||
assertEquals("true",supplier.get());
|
assertEquals("true", supplier.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,34 +128,45 @@ public class RuntimeJavaCompilerTests {
|
|||||||
public void dependencyResolution() throws Exception {
|
public void dependencyResolution() throws Exception {
|
||||||
// Failure:
|
// Failure:
|
||||||
RuntimeJavaCompiler rjc = new RuntimeJavaCompiler();
|
RuntimeJavaCompiler rjc = new RuntimeJavaCompiler();
|
||||||
CompilationResult cr = rjc.compile("A",
|
CompilationResult cr = rjc.compile("A", "public class A {}",
|
||||||
"public class A {}",
|
"maven://org.springframework:spring-expression2:4.3.9.RELEASE"); // extra
|
||||||
"maven://org.springframework:spring-expression2:4.3.9.RELEASE"); // extra '2' in there
|
// '2'
|
||||||
|
// in
|
||||||
|
// there
|
||||||
List<CompilationMessage> compilationMessages = cr.getCompilationMessages();
|
List<CompilationMessage> compilationMessages = cr.getCompilationMessages();
|
||||||
assertEquals(1,compilationMessages.size());
|
assertEquals(1, compilationMessages.size());
|
||||||
// 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)
|
// ERROR:org.eclipse.aether.resolution.ArtifactResolutionException: Could not find
|
||||||
assertTrue(compilationMessages.get(0).getMessage().contains("Could not find artifact org.springframework:spring-expression2:jar:4.3.9.RELEASE"));
|
// artifact org.springframework:spring-expression2:jar:4.3.9.RELEASE in
|
||||||
|
// spring-snapshots (https://repo.spring.io/libs-snapshot)
|
||||||
|
assertTrue(compilationMessages.get(0).getMessage().contains(
|
||||||
|
"Could not find artifact org.springframework:spring-expression2:jar:4.3.9.RELEASE"));
|
||||||
|
|
||||||
// Failure:
|
// Failure:
|
||||||
rjc = new RuntimeJavaCompiler();
|
rjc = new RuntimeJavaCompiler();
|
||||||
cr = rjc.compile("A",
|
cr = rjc.compile("A", "public class A {}",
|
||||||
"public class A {}",
|
"trouble://org.springframework:spring-expression:4.3.9.RELEASE"); // rogue
|
||||||
"trouble://org.springframework:spring-expression:4.3.9.RELEASE"); // rogue prefix (should be "maven:")
|
// prefix
|
||||||
|
// (should
|
||||||
|
// be
|
||||||
|
// "maven:")
|
||||||
compilationMessages = cr.getCompilationMessages();
|
compilationMessages = cr.getCompilationMessages();
|
||||||
assertEquals(1,compilationMessages.size());
|
assertEquals(1, compilationMessages.size());
|
||||||
assertTrue(compilationMessages.get(0).toString(),compilationMessages.get(0).getMessage().contains("Unrecognized dependency: "));
|
assertTrue(compilationMessages.get(0).toString(), compilationMessages.get(0)
|
||||||
|
.getMessage().contains("Unrecognized dependency: "));
|
||||||
|
|
||||||
// Success
|
// Success
|
||||||
rjc = new RuntimeJavaCompiler();
|
rjc = new RuntimeJavaCompiler();
|
||||||
cr = rjc.compile("A",
|
cr = rjc.compile("A", "public class A {}", "maven://joda-time:joda-time:2.9.9");
|
||||||
"public class A {}",
|
|
||||||
"maven://joda-time:joda-time:2.9.9");
|
|
||||||
compilationMessages = cr.getCompilationMessages();
|
compilationMessages = cr.getCompilationMessages();
|
||||||
assertEquals(0,compilationMessages.size());
|
assertEquals(0, compilationMessages.size());
|
||||||
List<File> resolvedAdditionalDependencies = cr.getResolvedAdditionalDependencies();
|
List<File> resolvedAdditionalDependencies = cr
|
||||||
|
.getResolvedAdditionalDependencies();
|
||||||
assertEquals(1, resolvedAdditionalDependencies.size());
|
assertEquals(1, resolvedAdditionalDependencies.size());
|
||||||
assertTrue("Expected this to end with 'joda-time-2.9.9.jar': "+resolvedAdditionalDependencies.get(0).toString(),
|
assertTrue(
|
||||||
resolvedAdditionalDependencies.get(0).toString().endsWith("joda-time-2.9.9.jar"));
|
"Expected this to end with 'joda-time-2.9.9.jar': "
|
||||||
|
+ resolvedAdditionalDependencies.get(0).toString(),
|
||||||
|
resolvedAdditionalDependencies.get(0).toString()
|
||||||
|
.endsWith("joda-time-2.9.9.jar"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -21,6 +21,7 @@ import java.util.function.Function;
|
|||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
import org.springframework.cloud.function.compiler.CompiledFunctionFactory;
|
import org.springframework.cloud.function.compiler.CompiledFunctionFactory;
|
||||||
import org.springframework.cloud.function.compiler.ConsumerCompiler;
|
import org.springframework.cloud.function.compiler.ConsumerCompiler;
|
||||||
@@ -32,8 +33,6 @@ import org.springframework.core.io.ByteArrayResource;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
import reactor.core.publisher.Flux;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
* @author Oleg Zhurakousky
|
* @author Oleg Zhurakousky
|
||||||
@@ -44,11 +43,14 @@ public class ByteCodeLoadingFunctionTests {
|
|||||||
public void compileConsumer() throws Exception {
|
public void compileConsumer() throws Exception {
|
||||||
CompiledFunctionFactory<Consumer<String>> compiled = new ConsumerCompiler<String>(
|
CompiledFunctionFactory<Consumer<String>> compiled = new ConsumerCompiler<String>(
|
||||||
String.class.getName()).compile("foos", "System.out::println", "String");
|
String.class.getName()).compile("foos", "System.out::println", "String");
|
||||||
ByteArrayResource resource = new ByteArrayResource(compiled.getGeneratedClassBytes(), "foos");
|
ByteArrayResource resource = new ByteArrayResource(
|
||||||
ByteCodeLoadingConsumer<String> consumer = new ByteCodeLoadingConsumer<>(resource);
|
compiled.getGeneratedClassBytes(), "foos");
|
||||||
|
ByteCodeLoadingConsumer<String> consumer = new ByteCodeLoadingConsumer<>(
|
||||||
|
resource);
|
||||||
consumer.afterPropertiesSet();
|
consumer.afterPropertiesSet();
|
||||||
assertThat(consumer instanceof FunctionFactoryMetadata);
|
assertThat(consumer instanceof FunctionFactoryMetadata);
|
||||||
assertThat(FunctionFactoryUtils.isFluxConsumer(consumer.getFactoryMethod())).isFalse();
|
assertThat(FunctionFactoryUtils.isFluxConsumer(consumer.getFactoryMethod()))
|
||||||
|
.isFalse();
|
||||||
consumer.accept("foo");
|
consumer.accept("foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,35 +58,48 @@ public class ByteCodeLoadingFunctionTests {
|
|||||||
public void compileSupplier() throws Exception {
|
public void compileSupplier() throws Exception {
|
||||||
CompiledFunctionFactory<Supplier<String>> compiled = new SupplierCompiler<String>(
|
CompiledFunctionFactory<Supplier<String>> compiled = new SupplierCompiler<String>(
|
||||||
String.class.getName()).compile("foos", "() -> \"foo\"", "String");
|
String.class.getName()).compile("foos", "() -> \"foo\"", "String");
|
||||||
ByteArrayResource resource = new ByteArrayResource(compiled.getGeneratedClassBytes(), "foos");
|
ByteArrayResource resource = new ByteArrayResource(
|
||||||
ByteCodeLoadingSupplier<String> supplier = new ByteCodeLoadingSupplier<>(resource);
|
compiled.getGeneratedClassBytes(), "foos");
|
||||||
|
ByteCodeLoadingSupplier<String> supplier = new ByteCodeLoadingSupplier<>(
|
||||||
|
resource);
|
||||||
supplier.afterPropertiesSet();
|
supplier.afterPropertiesSet();
|
||||||
assertThat(supplier instanceof FunctionFactoryMetadata);
|
assertThat(supplier instanceof FunctionFactoryMetadata);
|
||||||
assertThat(FunctionFactoryUtils.isFluxSupplier(supplier.getFactoryMethod())).isFalse();
|
assertThat(FunctionFactoryUtils.isFluxSupplier(supplier.getFactoryMethod()))
|
||||||
|
.isFalse();
|
||||||
assertThat(supplier.get()).isEqualTo("foo");
|
assertThat(supplier.get()).isEqualTo("foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void compileFunction() throws Exception {
|
public void compileFunction() throws Exception {
|
||||||
CompiledFunctionFactory<Function<String, String>> compiled = new FunctionCompiler<String, String>(
|
CompiledFunctionFactory<Function<String, String>> compiled = new FunctionCompiler<String, String>(
|
||||||
String.class.getName()).compile("foos", "v -> v.toUpperCase()", "String", "String");
|
String.class.getName()).compile("foos", "v -> v.toUpperCase()", "String",
|
||||||
ByteArrayResource resource = new ByteArrayResource(compiled.getGeneratedClassBytes(), "foos");
|
"String");
|
||||||
ByteCodeLoadingFunction<String, String> function = new ByteCodeLoadingFunction<>(resource);
|
ByteArrayResource resource = new ByteArrayResource(
|
||||||
|
compiled.getGeneratedClassBytes(), "foos");
|
||||||
|
ByteCodeLoadingFunction<String, String> function = new ByteCodeLoadingFunction<>(
|
||||||
|
resource);
|
||||||
function.afterPropertiesSet();
|
function.afterPropertiesSet();
|
||||||
assertThat(function instanceof FunctionFactoryMetadata);
|
assertThat(function instanceof FunctionFactoryMetadata);
|
||||||
assertThat(FunctionFactoryUtils.isFluxFunction(function.getFactoryMethod())).isFalse();
|
assertThat(FunctionFactoryUtils.isFluxFunction(function.getFactoryMethod()))
|
||||||
|
.isFalse();
|
||||||
assertThat(function.apply("foo")).isEqualTo("FOO");
|
assertThat(function.apply("foo")).isEqualTo("FOO");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void compileFluxFunction() throws Exception {
|
public void compileFluxFunction() throws Exception {
|
||||||
CompiledFunctionFactory<Function<Flux<String>, Flux<String>>> compiled = new FunctionCompiler<Flux<String>, Flux<String>>(
|
CompiledFunctionFactory<Function<Flux<String>, Flux<String>>> compiled = new FunctionCompiler<Flux<String>, Flux<String>>(
|
||||||
String.class.getName()).compile("foos", "flux -> flux.map(v -> v.toUpperCase())", "Flux<String>", "Flux<String>");
|
String.class.getName()).compile("foos",
|
||||||
ByteArrayResource resource = new ByteArrayResource(compiled.getGeneratedClassBytes(), "foos");
|
"flux -> flux.map(v -> v.toUpperCase())", "Flux<String>",
|
||||||
ByteCodeLoadingFunction<Flux<String>, Flux<String>> function = new ByteCodeLoadingFunction<>(resource);
|
"Flux<String>");
|
||||||
|
ByteArrayResource resource = new ByteArrayResource(
|
||||||
|
compiled.getGeneratedClassBytes(), "foos");
|
||||||
|
ByteCodeLoadingFunction<Flux<String>, Flux<String>> function = new ByteCodeLoadingFunction<>(
|
||||||
|
resource);
|
||||||
function.afterPropertiesSet();
|
function.afterPropertiesSet();
|
||||||
assertThat(function instanceof FunctionFactoryMetadata);
|
assertThat(function instanceof FunctionFactoryMetadata);
|
||||||
assertThat(FunctionFactoryUtils.isFluxFunction(function.getFactoryMethod())).isTrue();
|
assertThat(FunctionFactoryUtils.isFluxFunction(function.getFactoryMethod()))
|
||||||
|
.isTrue();
|
||||||
assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO");
|
assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -28,12 +28,11 @@ import java.util.stream.Collectors;
|
|||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.reactivestreams.Publisher;
|
import org.reactivestreams.Publisher;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
import reactor.core.publisher.Flux;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* Miscellaneous utility operations to interrogate functional components (beans)
|
* Miscellaneous utility operations to interrogate functional components (beans)
|
||||||
@@ -54,6 +53,7 @@ import reactor.core.publisher.Flux;
|
|||||||
public abstract class FunctionFactoryUtils {
|
public abstract class FunctionFactoryUtils {
|
||||||
|
|
||||||
private static final String FLUX_CLASS_NAME = Flux.class.getName();
|
private static final String FLUX_CLASS_NAME = Flux.class.getName();
|
||||||
|
|
||||||
private static final String PUBLISHER_CLASS_NAME = Publisher.class.getName();
|
private static final String PUBLISHER_CLASS_NAME = Publisher.class.getName();
|
||||||
|
|
||||||
private FunctionFactoryUtils() {
|
private FunctionFactoryUtils() {
|
||||||
@@ -142,4 +142,5 @@ public abstract class FunctionFactoryUtils {
|
|||||||
&& Stream.of(types).allMatch(type -> type.startsWith(FLUX_CLASS_NAME)
|
&& Stream.of(types).allMatch(type -> type.startsWith(FLUX_CLASS_NAME)
|
||||||
|| type.startsWith(PUBLISHER_CLASS_NAME));
|
|| type.startsWith(PUBLISHER_CLASS_NAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -23,13 +23,12 @@ import java.util.function.Supplier;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.reactivestreams.Publisher;
|
import org.reactivestreams.Publisher;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
import reactor.core.publisher.Flux;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
*
|
*
|
||||||
@@ -38,7 +37,8 @@ public class FunctionFactoryUtilsTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void isFluxConsumer() {
|
public void isFluxConsumer() {
|
||||||
Method method = ReflectionUtils.findMethod(FunctionFactoryUtilsTests.class, "fluxConsumer");
|
Method method = ReflectionUtils.findMethod(FunctionFactoryUtilsTests.class,
|
||||||
|
"fluxConsumer");
|
||||||
assertThat(FunctionFactoryUtils.isFluxConsumer(method)).isTrue();
|
assertThat(FunctionFactoryUtils.isFluxConsumer(method)).isTrue();
|
||||||
assertThat(FunctionFactoryUtils.isFluxSupplier(method)).isFalse();
|
assertThat(FunctionFactoryUtils.isFluxSupplier(method)).isFalse();
|
||||||
assertThat(FunctionFactoryUtils.isFluxFunction(method)).isFalse();
|
assertThat(FunctionFactoryUtils.isFluxFunction(method)).isFalse();
|
||||||
@@ -46,7 +46,8 @@ public class FunctionFactoryUtilsTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void isFluxSupplier() {
|
public void isFluxSupplier() {
|
||||||
Method method = ReflectionUtils.findMethod(FunctionFactoryUtilsTests.class, "fluxSupplier");
|
Method method = ReflectionUtils.findMethod(FunctionFactoryUtilsTests.class,
|
||||||
|
"fluxSupplier");
|
||||||
assertThat(FunctionFactoryUtils.isFluxSupplier(method)).isTrue();
|
assertThat(FunctionFactoryUtils.isFluxSupplier(method)).isTrue();
|
||||||
assertThat(FunctionFactoryUtils.isFluxConsumer(method)).isFalse();
|
assertThat(FunctionFactoryUtils.isFluxConsumer(method)).isFalse();
|
||||||
assertThat(FunctionFactoryUtils.isFluxFunction(method)).isFalse();
|
assertThat(FunctionFactoryUtils.isFluxFunction(method)).isFalse();
|
||||||
@@ -54,7 +55,8 @@ public class FunctionFactoryUtilsTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void isFluxFunction() {
|
public void isFluxFunction() {
|
||||||
Method method = ReflectionUtils.findMethod(FunctionFactoryUtilsTests.class, "fluxFunction");
|
Method method = ReflectionUtils.findMethod(FunctionFactoryUtilsTests.class,
|
||||||
|
"fluxFunction");
|
||||||
assertThat(FunctionFactoryUtils.isFluxFunction(method)).isTrue();
|
assertThat(FunctionFactoryUtils.isFluxFunction(method)).isTrue();
|
||||||
assertThat(FunctionFactoryUtils.isFluxSupplier(method)).isFalse();
|
assertThat(FunctionFactoryUtils.isFluxSupplier(method)).isFalse();
|
||||||
assertThat(FunctionFactoryUtils.isFluxConsumer(method)).isFalse();
|
assertThat(FunctionFactoryUtils.isFluxConsumer(method)).isFalse();
|
||||||
@@ -62,7 +64,8 @@ public class FunctionFactoryUtilsTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void isReactiveFunction() {
|
public void isReactiveFunction() {
|
||||||
Method method = ReflectionUtils.findMethod(FunctionFactoryUtilsTests.class, "reactiveFunction");
|
Method method = ReflectionUtils.findMethod(FunctionFactoryUtilsTests.class,
|
||||||
|
"reactiveFunction");
|
||||||
assertThat(FunctionFactoryUtils.isFluxFunction(method)).isTrue();
|
assertThat(FunctionFactoryUtils.isFluxFunction(method)).isTrue();
|
||||||
assertThat(FunctionFactoryUtils.isFluxSupplier(method)).isFalse();
|
assertThat(FunctionFactoryUtils.isFluxSupplier(method)).isFalse();
|
||||||
assertThat(FunctionFactoryUtils.isFluxConsumer(method)).isFalse();
|
assertThat(FunctionFactoryUtils.isFluxConsumer(method)).isFalse();
|
||||||
@@ -84,6 +87,8 @@ public class FunctionFactoryUtilsTests {
|
|||||||
return flux -> flux.subscribe(System.out::println);
|
return flux -> flux.subscribe(System.out::println);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Foo {}
|
class Foo {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<artifactId>spring-cloud-function-context</artifactId>
|
<artifactId>spring-cloud-function-context</artifactId>
|
||||||
@@ -73,12 +74,16 @@
|
|||||||
<execution>
|
<execution>
|
||||||
<id>java-compile</id>
|
<id>java-compile</id>
|
||||||
<phase>compile</phase>
|
<phase>compile</phase>
|
||||||
<goals> <goal>compile</goal> </goals>
|
<goals>
|
||||||
|
<goal>compile</goal>
|
||||||
|
</goals>
|
||||||
</execution>
|
</execution>
|
||||||
<execution>
|
<execution>
|
||||||
<id>java-test-compile</id>
|
<id>java-test-compile</id>
|
||||||
<phase>test-compile</phase>
|
<phase>test-compile</phase>
|
||||||
<goals> <goal>testCompile</goal> </goals>
|
<goals>
|
||||||
|
<goal>testCompile</goal>
|
||||||
|
</goals>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2019 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -22,9 +22,7 @@ import org.springframework.core.env.StandardEnvironment;
|
|||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author Oleg Zhurakousky
|
* @author Oleg Zhurakousky
|
||||||
*
|
|
||||||
* @since 2.0.1
|
* @since 2.0.1
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -33,12 +31,14 @@ public abstract class AbstractFunctionRegistry implements FunctionRegistry {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private Environment environment = new StandardEnvironment();
|
private Environment environment = new StandardEnvironment();
|
||||||
|
|
||||||
|
|
||||||
public <T> T lookup(Class<?> type, String name) {
|
public <T> T lookup(Class<?> type, String name) {
|
||||||
String functionDefinitionName = !StringUtils.hasText(name) && environment.containsProperty("spring.cloud.function.definition")
|
String functionDefinitionName = !StringUtils.hasText(name)
|
||||||
? environment.getProperty("spring.cloud.function.definition") : name;
|
&& this.environment.containsProperty("spring.cloud.function.definition")
|
||||||
|
? this.environment.getProperty("spring.cloud.function.definition")
|
||||||
|
: name;
|
||||||
return this.doLookup(type, functionDefinitionName);
|
return this.doLookup(type, functionDefinitionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract <T> T doLookup(Class<?> type, String name);
|
protected abstract <T> T doLookup(Class<?> type, String name);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2018 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -25,20 +25,20 @@ import java.util.Set;
|
|||||||
public interface FunctionCatalog {
|
public interface FunctionCatalog {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will look up the instance of the functional interface by name only
|
* Will look up the instance of the functional interface by name only.
|
||||||
*
|
* @param <T> instance type
|
||||||
* @param name the name of the functional interface. Must not be null;
|
* @param name the name of the functional interface. Must not be null;
|
||||||
* @return instance of the functional interface registered with this catalog
|
* @return instance of the functional interface registered with this catalog
|
||||||
*/
|
*/
|
||||||
default public <T> T lookup(String name) {
|
default <T> T lookup(String name) {
|
||||||
return this.lookup(null, name);
|
return this.lookup(null, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will look up the instance of the functional interface by name and type
|
* Will look up the instance of the functional interface by name and type which can
|
||||||
* which can only be Supplier, Consumer or Function. If type is not provided, the lookup
|
* only be Supplier, Consumer or Function. If type is not provided, the lookup will be
|
||||||
* will be made based on name only
|
* made based on name only.
|
||||||
*
|
* @param <T> instance type
|
||||||
* @param type the type of functional interface. Can be null
|
* @param type the type of functional interface. Can be null
|
||||||
* @param name the name of the functional interface. Must not be null;
|
* @param name the name of the functional interface. Must not be null;
|
||||||
* @return instance of the functional interface registered with this catalog
|
* @return instance of the functional interface registered with this catalog
|
||||||
@@ -48,11 +48,12 @@ public interface FunctionCatalog {
|
|||||||
Set<String> getNames(Class<?> type);
|
Set<String> getNames(Class<?> type);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the count of functions registered in this catalog
|
* Return the count of functions registered in this catalog.
|
||||||
* @return the count of functions registered in this catalog
|
* @return the count of functions registered in this catalog
|
||||||
*/
|
*/
|
||||||
default int size() {
|
default int size() {
|
||||||
throw new UnsupportedOperationException("This instance of FunctionCatalog does not support this operation");
|
throw new UnsupportedOperationException(
|
||||||
|
"This instance of FunctionCatalog does not support this operation");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012-2018 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -27,6 +27,9 @@ import java.util.function.Consumer;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanNameAware;
|
import org.springframework.beans.factory.BeanNameAware;
|
||||||
import org.springframework.cloud.function.core.FluxConsumer;
|
import org.springframework.cloud.function.core.FluxConsumer;
|
||||||
import org.springframework.cloud.function.core.FluxFunction;
|
import org.springframework.cloud.function.core.FluxFunction;
|
||||||
@@ -34,26 +37,23 @@ import org.springframework.cloud.function.core.FluxSupplier;
|
|||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
import reactor.core.publisher.Flux;
|
|
||||||
import reactor.core.publisher.Mono;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param <T> target type
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
* @author Oleg Zhurakousky
|
* @author Oleg Zhurakousky
|
||||||
*/
|
*/
|
||||||
public class FunctionRegistration<T> implements BeanNameAware {
|
public class FunctionRegistration<T> implements BeanNameAware {
|
||||||
|
|
||||||
private T target;
|
|
||||||
|
|
||||||
private final Set<String> names = new LinkedHashSet<>();
|
private final Set<String> names = new LinkedHashSet<>();
|
||||||
|
|
||||||
private final Map<String, String> properties = new LinkedHashMap<>();
|
private final Map<String, String> properties = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
private T target;
|
||||||
|
|
||||||
private FunctionType type;
|
private FunctionType type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates instance of FunctionRegistration.
|
* Creates instance of FunctionRegistration.
|
||||||
*
|
|
||||||
* @param target instance of {@link Supplier}, {@link Function} or {@link Consumer}
|
* @param target instance of {@link Supplier}, {@link Function} or {@link Consumer}
|
||||||
* @param names additional set of names for this registration. Additional names can be
|
* @param names additional set of names for this registration. Additional names can be
|
||||||
* provided {@link #name(String)} or {@link #names(String...)} operations.
|
* provided {@link #name(String)} or {@link #names(String...)} operations.
|
||||||
@@ -65,15 +65,11 @@ public class FunctionRegistration<T> implements BeanNameAware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, String> getProperties() {
|
public Map<String, String> getProperties() {
|
||||||
return properties;
|
return this.properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getNames() {
|
public Set<String> getNames() {
|
||||||
return names;
|
return this.names;
|
||||||
}
|
|
||||||
|
|
||||||
public FunctionType getType() {
|
|
||||||
return type;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,15 +77,19 @@ public class FunctionRegistration<T> implements BeanNameAware {
|
|||||||
* want to add a name or set or names to the existing set of names use
|
* want to add a name or set or names to the existing set of names use
|
||||||
* {@link #names(Collection)} or {@link #name(String)} or {@link #names(String...)}
|
* {@link #names(Collection)} or {@link #name(String)} or {@link #names(String...)}
|
||||||
* operations.
|
* operations.
|
||||||
* @param names
|
* @param names - bean names
|
||||||
*/
|
*/
|
||||||
public void setNames(Set<String> names) {
|
public void setNames(Set<String> names) {
|
||||||
this.names.clear();
|
this.names.clear();
|
||||||
this.names.addAll(names);
|
this.names.addAll(names);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FunctionType getType() {
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
|
||||||
public T getTarget() {
|
public T getTarget() {
|
||||||
return target;
|
return this.target;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionRegistration<T> properties(Map<String, String> properties) {
|
public FunctionRegistration<T> properties(Map<String, String> properties) {
|
||||||
@@ -133,7 +133,7 @@ public class FunctionRegistration<T> implements BeanNameAware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public <S> FunctionRegistration<S> wrap() {
|
public <S> FunctionRegistration<S> wrap() {
|
||||||
if (type == null || type.isWrapper()) {
|
if (this.type == null || this.type.isWrapper()) {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
FunctionRegistration<S> value = (FunctionRegistration<S>) this;
|
FunctionRegistration<S> value = (FunctionRegistration<S>) this;
|
||||||
return value;
|
return value;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.function.context;
|
package org.springframework.cloud.function.context;
|
||||||
|
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
@@ -23,18 +24,21 @@ import java.util.function.Consumer;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||||
import org.springframework.messaging.Message;
|
import org.springframework.messaging.Message;
|
||||||
|
|
||||||
import reactor.core.publisher.Flux;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class FunctionType {
|
public class FunctionType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unclassified function types.
|
||||||
|
*/
|
||||||
public static FunctionType UNCLASSIFIED = new FunctionType(ResolvableType
|
public static FunctionType UNCLASSIFIED = new FunctionType(ResolvableType
|
||||||
.forClassWithGenerics(Function.class, Object.class, Object.class).getType());
|
.forClassWithGenerics(Function.class, Object.class, Object.class).getType());
|
||||||
|
|
||||||
@@ -61,34 +65,6 @@ public class FunctionType {
|
|||||||
this.message = messageType();
|
this.message = messageType();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Type getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Class<?> getInputWrapper() {
|
|
||||||
return inputWrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Class<?> getOutputWrapper() {
|
|
||||||
return outputWrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Class<?> getInputType() {
|
|
||||||
return inputType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Class<?> getOutputType() {
|
|
||||||
return outputType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isMessage() {
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isWrapper() {
|
|
||||||
return isWrapper(getInputWrapper()) || isWrapper(getOutputWrapper());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isWrapper(Type type) {
|
public static boolean isWrapper(Type type) {
|
||||||
if (type instanceof ParameterizedType) {
|
if (type instanceof ParameterizedType) {
|
||||||
type = ((ParameterizedType) type).getRawType();
|
type = ((ParameterizedType) type).getRawType();
|
||||||
@@ -125,6 +101,91 @@ public class FunctionType {
|
|||||||
ResolvableType.forClassWithGenerics(Consumer.class, input).getType());
|
ResolvableType.forClassWithGenerics(Consumer.class, input).getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static FunctionType compose(FunctionType input, FunctionType output) {
|
||||||
|
ResolvableType inputGeneric = input(input);
|
||||||
|
ResolvableType outputGeneric = output(output);
|
||||||
|
if (!isWrapper(outputGeneric.getType())) {
|
||||||
|
ResolvableType inputOutput = output(input);
|
||||||
|
if (isWrapper(inputOutput.getType())) {
|
||||||
|
outputGeneric = wrap(input,
|
||||||
|
extractClass(inputOutput.getType(), ParamType.OUTPUT_WRAPPER),
|
||||||
|
extractClass(outputGeneric.getType(), ParamType.OUTPUT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new FunctionType(ResolvableType
|
||||||
|
.forClassWithGenerics(Function.class, inputGeneric, outputGeneric)
|
||||||
|
.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ResolvableType wrap(FunctionType input, Class<?> wrapper,
|
||||||
|
Class<?> type) {
|
||||||
|
return input.isMessage() ? wrap(wrapper, message(type))
|
||||||
|
: ResolvableType.forClassWithGenerics(wrapper, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ResolvableType wrap(Class<?> wrapper, ResolvableType type) {
|
||||||
|
return ResolvableType.forClassWithGenerics(wrapper, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ResolvableType message(Class<?> type) {
|
||||||
|
return ResolvableType.forClassWithGenerics(Message.class, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ResolvableType input(FunctionType type) {
|
||||||
|
return type.input(type.getInputType());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ResolvableType output(FunctionType type) {
|
||||||
|
return type.output(type.getOutputType());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Class<?> extractClass(Type param, ParamType paramType) {
|
||||||
|
if (param instanceof ParameterizedType) {
|
||||||
|
ParameterizedType concrete = (ParameterizedType) param;
|
||||||
|
param = concrete.getRawType();
|
||||||
|
}
|
||||||
|
if (param == null) {
|
||||||
|
// Last ditch attempt to guess: Flux<String>
|
||||||
|
if (paramType.isWrapper()) {
|
||||||
|
param = Flux.class;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
param = String.class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Class<?> result = param instanceof Class ? (Class<?>) param : null;
|
||||||
|
// TODO: cache result
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type getType() {
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<?> getInputWrapper() {
|
||||||
|
return this.inputWrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<?> getOutputWrapper() {
|
||||||
|
return this.outputWrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<?> getInputType() {
|
||||||
|
return this.inputType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<?> getOutputType() {
|
||||||
|
return this.outputType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMessage() {
|
||||||
|
return this.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWrapper() {
|
||||||
|
return isWrapper(getInputWrapper()) || isWrapper(getOutputWrapper());
|
||||||
|
}
|
||||||
|
|
||||||
public FunctionType to(Class<?> output) {
|
public FunctionType to(Class<?> output) {
|
||||||
ResolvableType inputGeneric = input(this);
|
ResolvableType inputGeneric = input(this);
|
||||||
ResolvableType outputGeneric = output(output);
|
ResolvableType outputGeneric = output(output);
|
||||||
@@ -173,73 +234,69 @@ public class FunctionType {
|
|||||||
return wrap(wrapper, wrapper);
|
return wrap(wrapper, wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FunctionType compose(FunctionType input, FunctionType output) {
|
|
||||||
ResolvableType inputGeneric = input(input);
|
|
||||||
ResolvableType outputGeneric = output(output);
|
|
||||||
if (!isWrapper(outputGeneric.getType())) {
|
|
||||||
ResolvableType inputOutput = output(input);
|
|
||||||
if (isWrapper(inputOutput.getType())) {
|
|
||||||
outputGeneric = wrap(input,
|
|
||||||
extractClass(inputOutput.getType(), ParamType.OUTPUT_WRAPPER),
|
|
||||||
extractClass(outputGeneric.getType(), ParamType.OUTPUT));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new FunctionType(ResolvableType
|
|
||||||
.forClassWithGenerics(Function.class, inputGeneric, outputGeneric)
|
|
||||||
.getType());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
final int prime = 31;
|
final int prime = 31;
|
||||||
int result = 1;
|
int result = 1;
|
||||||
result = prime * result
|
result = prime * result
|
||||||
+ ((inputType == null) ? 0 : inputType.toString().hashCode());
|
+ ((this.inputType == null) ? 0 : this.inputType.toString().hashCode());
|
||||||
|
result = prime * result + ((this.inputWrapper == null) ? 0
|
||||||
|
: this.inputWrapper.toString().hashCode());
|
||||||
|
result = prime * result + (this.message ? 1231 : 1237);
|
||||||
result = prime * result
|
result = prime * result
|
||||||
+ ((inputWrapper == null) ? 0 : inputWrapper.toString().hashCode());
|
+ ((this.outputType == null) ? 0 : this.outputType.toString().hashCode());
|
||||||
result = prime * result + (message ? 1231 : 1237);
|
result = prime * result + ((this.outputWrapper == null) ? 0
|
||||||
result = prime * result
|
: this.outputWrapper.toString().hashCode());
|
||||||
+ ((outputType == null) ? 0 : outputType.toString().hashCode());
|
|
||||||
result = prime * result
|
|
||||||
+ ((outputWrapper == null) ? 0 : outputWrapper.toString().hashCode());
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (this == obj)
|
if (this == obj) {
|
||||||
return true;
|
return true;
|
||||||
if (obj == null)
|
}
|
||||||
|
if (obj == null) {
|
||||||
return false;
|
return false;
|
||||||
if (getClass() != obj.getClass())
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
FunctionType other = (FunctionType) obj;
|
FunctionType other = (FunctionType) obj;
|
||||||
if (inputType == null) {
|
if (this.inputType == null) {
|
||||||
if (other.inputType != null)
|
if (other.inputType != null) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (!inputType.toString().equals(other.inputType.toString()))
|
else if (!this.inputType.toString().equals(other.inputType.toString())) {
|
||||||
return false;
|
return false;
|
||||||
if (inputWrapper == null) {
|
}
|
||||||
if (other.inputWrapper != null)
|
if (this.inputWrapper == null) {
|
||||||
|
if (other.inputWrapper != null) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (!inputWrapper.toString().equals(other.inputWrapper.toString()))
|
else if (!this.inputWrapper.toString().equals(other.inputWrapper.toString())) {
|
||||||
return false;
|
return false;
|
||||||
if (message != other.message)
|
}
|
||||||
|
if (this.message != other.message) {
|
||||||
return false;
|
return false;
|
||||||
if (outputType == null) {
|
}
|
||||||
if (other.outputType != null)
|
if (this.outputType == null) {
|
||||||
|
if (other.outputType != null) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (!outputType.toString().equals(other.outputType.toString()))
|
else if (!this.outputType.toString().equals(other.outputType.toString())) {
|
||||||
return false;
|
return false;
|
||||||
if (outputWrapper == null) {
|
}
|
||||||
if (other.outputWrapper != null)
|
if (this.outputWrapper == null) {
|
||||||
|
if (other.outputWrapper != null) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (!outputWrapper.toString().equals(other.outputWrapper.toString()))
|
else if (!this.outputWrapper.toString().equals(other.outputWrapper.toString())) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,28 +304,6 @@ public class FunctionType {
|
|||||||
return wrap(this, wrapper, type);
|
return wrap(this, wrapper, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ResolvableType wrap(FunctionType input, Class<?> wrapper,
|
|
||||||
Class<?> type) {
|
|
||||||
return input.isMessage() ? wrap(wrapper, message(type))
|
|
||||||
: ResolvableType.forClassWithGenerics(wrapper, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ResolvableType wrap(Class<?> wrapper, ResolvableType type) {
|
|
||||||
return ResolvableType.forClassWithGenerics(wrapper, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ResolvableType message(Class<?> type) {
|
|
||||||
return ResolvableType.forClassWithGenerics(Message.class, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ResolvableType input(FunctionType type) {
|
|
||||||
return type.input(type.getInputType());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ResolvableType output(FunctionType type) {
|
|
||||||
return type.output(type.getOutputType());
|
|
||||||
}
|
|
||||||
|
|
||||||
private ResolvableType output(Class<?> type) {
|
private ResolvableType output(Class<?> type) {
|
||||||
ResolvableType generic;
|
ResolvableType generic;
|
||||||
ResolvableType raw = ResolvableType.forClass(type);
|
ResolvableType raw = ResolvableType.forClass(type);
|
||||||
@@ -331,25 +366,6 @@ public class FunctionType {
|
|||||||
return Object.class;
|
return Object.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Class<?> extractClass(Type param, ParamType paramType) {
|
|
||||||
if (param instanceof ParameterizedType) {
|
|
||||||
ParameterizedType concrete = (ParameterizedType) param;
|
|
||||||
param = concrete.getRawType();
|
|
||||||
}
|
|
||||||
if (param == null) {
|
|
||||||
// Last ditch attempt to guess: Flux<String>
|
|
||||||
if (paramType.isWrapper()) {
|
|
||||||
param = Flux.class;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
param = String.class;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Class<?> result = param instanceof Class ? (Class<?>) param : null;
|
|
||||||
// TODO: cache result
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Type extractType(Type type, ParamType paramType, int index) {
|
private Type extractType(Type type, ParamType paramType, int index) {
|
||||||
Type param;
|
Type param;
|
||||||
if (type instanceof ParameterizedType) {
|
if (type instanceof ParameterizedType) {
|
||||||
@@ -442,6 +458,7 @@ public class FunctionType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum ParamType {
|
enum ParamType {
|
||||||
|
|
||||||
INPUT, OUTPUT, INPUT_WRAPPER, OUTPUT_WRAPPER, INPUT_INNER_WRAPPER, OUTPUT_INNER_WRAPPER;
|
INPUT, OUTPUT, INPUT_WRAPPER, OUTPUT_WRAPPER, INPUT_INNER_WRAPPER, OUTPUT_INNER_WRAPPER;
|
||||||
|
|
||||||
public boolean isOutput() {
|
public boolean isOutput() {
|
||||||
@@ -460,6 +477,7 @@ public class FunctionType {
|
|||||||
public boolean isInnerWrapper() {
|
public boolean isInnerWrapper() {
|
||||||
return this == OUTPUT_INNER_WRAPPER || this == INPUT_INNER_WRAPPER;
|
return this == OUTPUT_INNER_WRAPPER || this == INPUT_INNER_WRAPPER;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.function.context;
|
package org.springframework.cloud.function.context;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -39,11 +40,6 @@ import org.springframework.util.ClassUtils;
|
|||||||
public class FunctionalSpringApplication
|
public class FunctionalSpringApplication
|
||||||
extends org.springframework.boot.SpringApplication {
|
extends org.springframework.boot.SpringApplication {
|
||||||
|
|
||||||
/**
|
|
||||||
* Name of default property source.
|
|
||||||
*/
|
|
||||||
private static final String DEFAULT_PROPERTIES = "defaultProperties";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag to say that context is functional beans.
|
* Flag to say that context is functional beans.
|
||||||
*/
|
*/
|
||||||
@@ -54,6 +50,23 @@ public class FunctionalSpringApplication
|
|||||||
*/
|
*/
|
||||||
public static final String SPRING_WEB_APPLICATION_TYPE = "spring.main.web-application-type";
|
public static final String SPRING_WEB_APPLICATION_TYPE = "spring.main.web-application-type";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of default property source.
|
||||||
|
*/
|
||||||
|
private static final String DEFAULT_PROPERTIES = "defaultProperties";
|
||||||
|
|
||||||
|
public FunctionalSpringApplication(Class<?>... primarySources) {
|
||||||
|
super(primarySources);
|
||||||
|
setApplicationContextClass(GenericApplicationContext.class);
|
||||||
|
if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler",
|
||||||
|
null)) {
|
||||||
|
setWebApplicationType(WebApplicationType.REACTIVE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setWebApplicationType(WebApplicationType.NONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
FunctionalSpringApplication.run(new Class<?>[0], args);
|
FunctionalSpringApplication.run(new Class<?>[0], args);
|
||||||
}
|
}
|
||||||
@@ -68,18 +81,6 @@ public class FunctionalSpringApplication
|
|||||||
return new FunctionalSpringApplication(primarySources).run(args);
|
return new FunctionalSpringApplication(primarySources).run(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionalSpringApplication(Class<?>... primarySources) {
|
|
||||||
super(primarySources);
|
|
||||||
setApplicationContextClass(GenericApplicationContext.class);
|
|
||||||
if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler",
|
|
||||||
null)) {
|
|
||||||
setWebApplicationType(WebApplicationType.REACTIVE);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setWebApplicationType(WebApplicationType.NONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
|
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
|
||||||
super.postProcessApplicationContext(context);
|
super.postProcessApplicationContext(context);
|
||||||
@@ -108,7 +109,7 @@ public class FunctionalSpringApplication
|
|||||||
handler = BeanUtils.instantiateClass(type);
|
handler = BeanUtils.instantiateClass(type);
|
||||||
}
|
}
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
ApplicationContextInitializer<GenericApplicationContext> initializer = (ApplicationContextInitializer<GenericApplicationContext>) handler;
|
ApplicationContextInitializer initializer = (ApplicationContextInitializer) handler;
|
||||||
initializer.initialize(generic);
|
initializer.initialize(generic);
|
||||||
functional = true;
|
functional = true;
|
||||||
}
|
}
|
||||||
@@ -118,8 +119,9 @@ public class FunctionalSpringApplication
|
|||||||
Class<?> functionType = type;
|
Class<?> functionType = type;
|
||||||
Object function = handler;
|
Object function = handler;
|
||||||
generic.registerBean("function", FunctionRegistration.class,
|
generic.registerBean("function", FunctionRegistration.class,
|
||||||
() -> new FunctionRegistration<>(handler(generic, function, functionType))
|
() -> new FunctionRegistration<>(
|
||||||
.type(FunctionType.of(functionType)));
|
handler(generic, function, functionType))
|
||||||
|
.type(FunctionType.of(functionType)));
|
||||||
functional = true;
|
functional = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,7 +132,8 @@ public class FunctionalSpringApplication
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object handler(GenericApplicationContext generic, Object handler, Class<?> type) {
|
private Object handler(GenericApplicationContext generic, Object handler,
|
||||||
|
Class<?> type) {
|
||||||
if (handler == null) {
|
if (handler == null) {
|
||||||
handler = generic.getAutowireCapableBeanFactory().createBean(type);
|
handler = generic.getAutowireCapableBeanFactory().createBean(type);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.function.context.catalog;
|
package org.springframework.cloud.function.context.catalog;
|
||||||
|
|
||||||
import org.springframework.context.ApplicationEvent;
|
import org.springframework.context.ApplicationEvent;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.function.context.catalog;
|
package org.springframework.cloud.function.context.catalog;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -26,6 +27,7 @@ import java.util.Set;
|
|||||||
public class FunctionRegistrationEvent extends FunctionCatalogEvent {
|
public class FunctionRegistrationEvent extends FunctionCatalogEvent {
|
||||||
|
|
||||||
private final Class<?> type;
|
private final Class<?> type;
|
||||||
|
|
||||||
private final Set<String> names;
|
private final Set<String> names;
|
||||||
|
|
||||||
public FunctionRegistrationEvent(Object source, Class<?> type, Set<String> names) {
|
public FunctionRegistrationEvent(Object source, Class<?> type, Set<String> names) {
|
||||||
@@ -35,11 +37,11 @@ public class FunctionRegistrationEvent extends FunctionCatalogEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Class<?> getType() {
|
public Class<?> getType() {
|
||||||
return type;
|
return this.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getNames() {
|
public Set<String> getNames() {
|
||||||
return names;
|
return this.names;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.function.context.catalog;
|
package org.springframework.cloud.function.context.catalog;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -26,6 +27,7 @@ import java.util.Set;
|
|||||||
public class FunctionUnregistrationEvent extends FunctionCatalogEvent {
|
public class FunctionUnregistrationEvent extends FunctionCatalogEvent {
|
||||||
|
|
||||||
private final Class<?> type;
|
private final Class<?> type;
|
||||||
|
|
||||||
private final Set<String> names;
|
private final Set<String> names;
|
||||||
|
|
||||||
public FunctionUnregistrationEvent(Object source, Class<?> type, Set<String> names) {
|
public FunctionUnregistrationEvent(Object source, Class<?> type, Set<String> names) {
|
||||||
@@ -35,11 +37,11 @@ public class FunctionUnregistrationEvent extends FunctionCatalogEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Class<?> getType() {
|
public Class<?> getType() {
|
||||||
return type;
|
return this.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getNames() {
|
public Set<String> getNames() {
|
||||||
return names;
|
return this.names;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2019 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -81,16 +81,17 @@ public class InMemoryFunctionCatalog extends AbstractFunctionRegistry
|
|||||||
FunctionRegistrationEvent event = new FunctionRegistrationEvent(this, type,
|
FunctionRegistrationEvent event = new FunctionRegistrationEvent(this, type,
|
||||||
registration.getNames());
|
registration.getNames());
|
||||||
|
|
||||||
registrations.put(registration.getTarget(), registration);
|
this.registrations.put(registration.getTarget(), registration);
|
||||||
FunctionRegistration<T> wrapped = registration.wrap();
|
FunctionRegistration<T> wrapped = registration.wrap();
|
||||||
if (wrapped != registration) {
|
if (wrapped != registration) {
|
||||||
registration = wrapped;
|
registration = wrapped;
|
||||||
registrations.put(wrapped.getTarget(), wrapped);
|
this.registrations.put(wrapped.getTarget(), wrapped);
|
||||||
if (type == Consumer.class) {
|
if (type == Consumer.class) {
|
||||||
type = Function.class;
|
type = Function.class;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Map<String, Object> map = functions.computeIfAbsent(type, key -> new HashMap<>());
|
Map<String, Object> map = this.functions.computeIfAbsent(type,
|
||||||
|
key -> new HashMap<>());
|
||||||
for (String name : registration.getNames()) {
|
for (String name : registration.getNames()) {
|
||||||
map.put(name, registration.getTarget());
|
map.put(name, registration.getTarget());
|
||||||
}
|
}
|
||||||
@@ -104,19 +105,19 @@ public class InMemoryFunctionCatalog extends AbstractFunctionRegistry
|
|||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
if (publisher != null && !functions.isEmpty()) {
|
if (this.publisher != null && !this.functions.isEmpty()) {
|
||||||
functions.keySet()
|
this.functions.keySet()
|
||||||
.forEach(type -> this.publishEvent(new FunctionRegistrationEvent(this,
|
.forEach(type -> this.publishEvent(new FunctionRegistrationEvent(this,
|
||||||
type, functions.get(type).keySet())));
|
type, this.functions.get(type).keySet())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreDestroy
|
@PreDestroy
|
||||||
public void close() {
|
public void close() {
|
||||||
if (publisher != null && !functions.isEmpty()) {
|
if (this.publisher != null && !this.functions.isEmpty()) {
|
||||||
functions.keySet().forEach(
|
this.functions.keySet().forEach(
|
||||||
type -> this.publishEvent(new FunctionUnregistrationEvent(this, type,
|
type -> this.publishEvent(new FunctionUnregistrationEvent(this, type,
|
||||||
functions.get(type).keySet())));
|
this.functions.get(type).keySet())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +126,7 @@ public class InMemoryFunctionCatalog extends AbstractFunctionRegistry
|
|||||||
public <T> T doLookup(Class<?> type, String name) {
|
public <T> T doLookup(Class<?> type, String name) {
|
||||||
T function = null;
|
T function = null;
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
function = (T) functions.values().stream()
|
function = (T) this.functions.values().stream()
|
||||||
.filter(map -> map.get(name) != null).map(map -> map.get(name))
|
.filter(map -> map.get(name) != null).map(map -> map.get(name))
|
||||||
.findFirst().orElse(null);
|
.findFirst().orElse(null);
|
||||||
}
|
}
|
||||||
@@ -138,7 +139,7 @@ public class InMemoryFunctionCatalog extends AbstractFunctionRegistry
|
|||||||
@Override
|
@Override
|
||||||
public Set<String> getNames(Class<?> type) {
|
public Set<String> getNames(Class<?> type) {
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
return functions.values().stream().flatMap(map -> map.keySet().stream())
|
return this.functions.values().stream().flatMap(map -> map.keySet().stream())
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
Map<String, Object> map = this.extractTypeMap(type);
|
Map<String, Object> map = this.extractTypeMap(type);
|
||||||
@@ -146,10 +147,10 @@ public class InMemoryFunctionCatalog extends AbstractFunctionRegistry
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> extractTypeMap(Class<?> type) {
|
private Map<String, Object> extractTypeMap(Class<?> type) {
|
||||||
return functions.keySet().stream()
|
return this.functions.keySet().stream()
|
||||||
.filter(key -> key != Object.class && key.isAssignableFrom(type))
|
.filter(key -> key != Object.class && key.isAssignableFrom(type))
|
||||||
.map(key -> functions.get(key)).findFirst()
|
.map(key -> this.functions.get(key)).findFirst()
|
||||||
.orElse(functions.get(Object.class));
|
.orElse(this.functions.get(Object.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void publishEvent(Object event) {
|
private void publishEvent(Object event) {
|
||||||
@@ -157,4 +158,5 @@ public class InMemoryFunctionCatalog extends AbstractFunctionRegistry
|
|||||||
this.publisher.publishEvent(event);
|
this.publisher.publishEvent(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2019 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -32,6 +32,11 @@ import java.util.stream.Stream;
|
|||||||
|
|
||||||
import javax.annotation.PreDestroy;
|
import javax.annotation.PreDestroy;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
@@ -62,22 +67,16 @@ import org.springframework.cloud.function.json.JacksonMapper;
|
|||||||
import org.springframework.context.ApplicationEventPublisher;
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||||
import org.springframework.context.annotation.Conditional;
|
import org.springframework.context.annotation.Conditional;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.FilterType;
|
import org.springframework.context.annotation.FilterType;
|
||||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
|
||||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||||
import org.springframework.core.type.StandardMethodMetadata;
|
import org.springframework.core.type.StandardMethodMetadata;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
|
|
||||||
import reactor.core.publisher.Flux;
|
|
||||||
import reactor.core.publisher.Mono;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
@@ -86,8 +85,10 @@ import reactor.core.publisher.Mono;
|
|||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnMissingBean(FunctionCatalog.class)
|
@ConditionalOnMissingBean(FunctionCatalog.class)
|
||||||
@ComponentScan(basePackages = "${spring.cloud.function.scan.packages:functions}",
|
// @checkstyle:off
|
||||||
includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {Supplier.class, Function.class, Consumer.class}))
|
@ComponentScan(basePackages = "${spring.cloud.function.scan.packages:functions}", includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {
|
||||||
|
Supplier.class, Function.class, Consumer.class }))
|
||||||
|
// @checkstyle:on
|
||||||
public class ContextFunctionCatalogAutoConfiguration {
|
public class ContextFunctionCatalogAutoConfiguration {
|
||||||
|
|
||||||
static final String PREFERRED_MAPPER_PROPERTY = "spring.http.converters.preferred-json-mapper";
|
static final String PREFERRED_MAPPER_PROPERTY = "spring.http.converters.preferred-json-mapper";
|
||||||
@@ -104,11 +105,10 @@ public class ContextFunctionCatalogAutoConfiguration {
|
|||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
private Map<String, FunctionRegistration<?>> registrations = Collections.emptyMap();
|
private Map<String, FunctionRegistration<?>> registrations = Collections.emptyMap();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public FunctionRegistry functionCatalog(ContextFunctionRegistry processor) {
|
public FunctionRegistry functionCatalog(ContextFunctionRegistry processor) {
|
||||||
processor.merge(registrations, consumers, suppliers, functions);
|
processor.merge(this.registrations, this.consumers, this.suppliers,
|
||||||
|
this.functions);
|
||||||
return new BeanFactoryFunctionCatalog(processor);
|
return new BeanFactoryFunctionCatalog(processor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,11 +121,15 @@ public class ContextFunctionCatalogAutoConfiguration {
|
|||||||
|
|
||||||
private final ContextFunctionRegistry processor;
|
private final ContextFunctionRegistry processor;
|
||||||
|
|
||||||
|
public BeanFactoryFunctionCatalog(ContextFunctionRegistry processor) {
|
||||||
|
this.processor = processor;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> void register(FunctionRegistration<T> registration) {
|
public <T> void register(FunctionRegistration<T> registration) {
|
||||||
Assert.notEmpty(registration.getNames(),
|
Assert.notEmpty(registration.getNames(),
|
||||||
"'registration' must contain at least one name before it is registered in catalog.");
|
"'registration' must contain at least one name before it is registered in catalog.");
|
||||||
processor.register(registration);
|
this.processor.register(registration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -133,22 +137,22 @@ public class ContextFunctionCatalogAutoConfiguration {
|
|||||||
protected <T> T doLookup(Class<?> type, String name) {
|
protected <T> T doLookup(Class<?> type, String name) {
|
||||||
T function = null;
|
T function = null;
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
function = (T) processor.lookupFunction(name);
|
function = (T) this.processor.lookupFunction(name);
|
||||||
if (function == null) {
|
if (function == null) {
|
||||||
function = (T) processor.lookupConsumer(name);
|
function = (T) this.processor.lookupConsumer(name);
|
||||||
}
|
}
|
||||||
if (function == null) {
|
if (function == null) {
|
||||||
function = (T) processor.lookupSupplier(name);
|
function = (T) this.processor.lookupSupplier(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (Supplier.class.isAssignableFrom(type)) {
|
else if (Supplier.class.isAssignableFrom(type)) {
|
||||||
function = (T) processor.lookupSupplier(name);
|
function = (T) this.processor.lookupSupplier(name);
|
||||||
}
|
}
|
||||||
else if (Consumer.class.isAssignableFrom(type)) {
|
else if (Consumer.class.isAssignableFrom(type)) {
|
||||||
function = (T) processor.lookupConsumer(name);
|
function = (T) this.processor.lookupConsumer(name);
|
||||||
}
|
}
|
||||||
else if (Function.class.isAssignableFrom(type)) {
|
else if (Function.class.isAssignableFrom(type)) {
|
||||||
function = (T) processor.lookupFunction(name);
|
function = (T) this.processor.lookupFunction(name);
|
||||||
}
|
}
|
||||||
return function;
|
return function;
|
||||||
}
|
}
|
||||||
@@ -174,26 +178,6 @@ public class ContextFunctionCatalogAutoConfiguration {
|
|||||||
+ this.processor.getConsumers().size();
|
+ this.processor.getConsumers().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public BeanFactoryFunctionCatalog(ContextFunctionRegistry processor) {
|
|
||||||
this.processor = processor;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected class BeanFactoryFunctionInspector implements FunctionInspector {
|
|
||||||
|
|
||||||
private ContextFunctionRegistry processor;
|
|
||||||
|
|
||||||
public BeanFactoryFunctionInspector(ContextFunctionRegistry processor) {
|
|
||||||
this.processor = processor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FunctionRegistration<?> getRegistration(Object function) {
|
|
||||||
FunctionRegistration<?> registration = processor.getRegistration(function);
|
|
||||||
return registration;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@@ -201,21 +185,27 @@ public class ContextFunctionCatalogAutoConfiguration {
|
|||||||
@ConditionalOnBean(Gson.class)
|
@ConditionalOnBean(Gson.class)
|
||||||
@Conditional(PreferGsonOrMissingJacksonCondition.class)
|
@Conditional(PreferGsonOrMissingJacksonCondition.class)
|
||||||
protected static class GsonConfiguration {
|
protected static class GsonConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public GsonMapper jsonMapper(Gson gson) {
|
public GsonMapper jsonMapper(Gson gson) {
|
||||||
return new GsonMapper(gson);
|
return new GsonMapper(gson);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnClass(ObjectMapper.class)
|
@ConditionalOnClass(ObjectMapper.class)
|
||||||
@ConditionalOnBean(ObjectMapper.class)
|
@ConditionalOnBean(ObjectMapper.class)
|
||||||
|
// @checkstyle:off
|
||||||
@ConditionalOnProperty(name = ContextFunctionCatalogAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "jackson", matchIfMissing = true)
|
@ConditionalOnProperty(name = ContextFunctionCatalogAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "jackson", matchIfMissing = true)
|
||||||
|
// @checkstyle:on
|
||||||
protected static class JacksonConfiguration {
|
protected static class JacksonConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public JacksonMapper jsonMapper(ObjectMapper mapper) {
|
public JacksonMapper jsonMapper(ObjectMapper mapper) {
|
||||||
return new JacksonMapper(mapper);
|
return new JacksonMapper(mapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@@ -242,10 +232,10 @@ public class ContextFunctionCatalogAutoConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public FunctionRegistration<?> getRegistration(Object function) {
|
public FunctionRegistration<?> getRegistration(Object function) {
|
||||||
if (function == null || !names.containsKey(function)) {
|
if (function == null || !this.names.containsKey(function)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new FunctionRegistration<>(function, names.get(function))
|
return new FunctionRegistration<>(function, this.names.get(function))
|
||||||
.type(findType(function).getType());
|
.type(findType(function).getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,15 +305,15 @@ public class ContextFunctionCatalogAutoConfiguration {
|
|||||||
}
|
}
|
||||||
final Object value = function;
|
final Object value = function;
|
||||||
lookup.computeIfAbsent(name, key -> value);
|
lookup.computeIfAbsent(name, key -> value);
|
||||||
if (!types.containsKey(name)) {
|
if (!this.types.containsKey(name)) {
|
||||||
if (types.containsKey(stages[0])
|
if (this.types.containsKey(stages[0])
|
||||||
&& types.containsKey(stages[stages.length - 1])) {
|
&& this.types.containsKey(stages[stages.length - 1])) {
|
||||||
FunctionType input = types.get(stages[0]);
|
FunctionType input = this.types.get(stages[0]);
|
||||||
FunctionType output = types.get(stages[stages.length - 1]);
|
FunctionType output = this.types.get(stages[stages.length - 1]);
|
||||||
types.put(name, FunctionType.compose(input, output));
|
this.types.put(name, FunctionType.compose(input, output));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
names.put(function, name);
|
this.names.put(function, name);
|
||||||
return function;
|
return function;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,12 +331,14 @@ public class ContextFunctionCatalogAutoConfiguration {
|
|||||||
Supplier<Flux<Object>> supplier = (Supplier<Flux<Object>>) a;
|
Supplier<Flux<Object>> supplier = (Supplier<Flux<Object>>) a;
|
||||||
if (b instanceof FluxConsumer) {
|
if (b instanceof FluxConsumer) {
|
||||||
if (supplier instanceof FluxSupplier) {
|
if (supplier instanceof FluxSupplier) {
|
||||||
FluxConsumer<Object> fConsumer = ((FluxConsumer<Object>)b);
|
FluxConsumer<Object> fConsumer = ((FluxConsumer<Object>) b);
|
||||||
return (Supplier<Mono<Void>>) () -> Mono.from(supplier.get().compose(v -> fConsumer.apply(supplier.get())));
|
return (Supplier<Mono<Void>>) () -> Mono.from(supplier.get()
|
||||||
|
.compose(v -> fConsumer.apply(supplier.get())));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new IllegalStateException("The provided supplier is finite (i.e., already composed with Consumer) "
|
throw new IllegalStateException(
|
||||||
+ "therefore it can not be composed with another consumer");
|
"The provided supplier is finite (i.e., already composed with Consumer) "
|
||||||
|
+ "therefore it can not be composed with another consumer");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -362,13 +354,16 @@ public class ContextFunctionCatalogAutoConfiguration {
|
|||||||
return function1.andThen(function2);
|
return function1.andThen(function2);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new IllegalStateException("The provided function is finite (i.e., returns Mono<?>) "
|
throw new IllegalStateException(
|
||||||
+ "therefore it can *only* be composed with compatible function (i.e., Function<Mono, Flux>");
|
"The provided function is finite (i.e., returns Mono<?>) "
|
||||||
|
+ "therefore it can *only* be composed with compatible function (i.e., Function<Mono, Flux>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (function2 instanceof FluxToMonoFunction) {
|
else if (function2 instanceof FluxToMonoFunction) {
|
||||||
return new FluxToMonoFunction<Object, Object>(((Function<Flux<Object>, Flux<Object>>)a)
|
return new FluxToMonoFunction<Object, Object>(
|
||||||
.andThen(((FluxToMonoFunction<Object,Object>) b).getTarget()));
|
((Function<Flux<Object>, Flux<Object>>) a)
|
||||||
|
.andThen(((FluxToMonoFunction<Object, Object>) b)
|
||||||
|
.getTarget()));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return function1.andThen(function2);
|
return function1.andThen(function2);
|
||||||
@@ -391,18 +386,18 @@ public class ContextFunctionCatalogAutoConfiguration {
|
|||||||
|
|
||||||
@PreDestroy
|
@PreDestroy
|
||||||
public void close() {
|
public void close() {
|
||||||
if (publisher != null) {
|
if (this.publisher != null) {
|
||||||
if (!functions.isEmpty()) {
|
if (!this.functions.isEmpty()) {
|
||||||
publisher.publishEvent(new FunctionUnregistrationEvent(this,
|
this.publisher.publishEvent(new FunctionUnregistrationEvent(this,
|
||||||
Function.class, functions.keySet()));
|
Function.class, this.functions.keySet()));
|
||||||
}
|
}
|
||||||
if (!consumers.isEmpty()) {
|
if (!this.consumers.isEmpty()) {
|
||||||
publisher.publishEvent(new FunctionUnregistrationEvent(this,
|
this.publisher.publishEvent(new FunctionUnregistrationEvent(this,
|
||||||
Consumer.class, consumers.keySet()));
|
Consumer.class, this.consumers.keySet()));
|
||||||
}
|
}
|
||||||
if (!suppliers.isEmpty()) {
|
if (!this.suppliers.isEmpty()) {
|
||||||
publisher.publishEvent(new FunctionUnregistrationEvent(this,
|
this.publisher.publishEvent(new FunctionUnregistrationEvent(this,
|
||||||
Supplier.class, suppliers.keySet()));
|
Supplier.class, this.suppliers.keySet()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -443,8 +438,8 @@ public class ContextFunctionCatalogAutoConfiguration {
|
|||||||
private Collection<String> getAliases(String key) {
|
private Collection<String> getAliases(String key) {
|
||||||
Collection<String> names = new LinkedHashSet<>();
|
Collection<String> names = new LinkedHashSet<>();
|
||||||
String value = getQualifier(key);
|
String value = getQualifier(key);
|
||||||
if (value.equals(key) && registry != null) {
|
if (value.equals(key) && this.registry != null) {
|
||||||
names.addAll(Arrays.asList(registry.getAliases(key)));
|
names.addAll(Arrays.asList(this.registry.getAliases(key)));
|
||||||
}
|
}
|
||||||
names.add(value);
|
names.add(value);
|
||||||
return names;
|
return names;
|
||||||
@@ -485,8 +480,8 @@ public class ContextFunctionCatalogAutoConfiguration {
|
|||||||
}
|
}
|
||||||
// this.names.remove(target);
|
// this.names.remove(target);
|
||||||
this.names.put(registration.getTarget(), key);
|
this.names.put(registration.getTarget(), key);
|
||||||
if (publisher != null) {
|
if (this.publisher != null) {
|
||||||
publisher.publishEvent(new FunctionRegistrationEvent(
|
this.publisher.publishEvent(new FunctionRegistrationEvent(
|
||||||
registration.getTarget(), type, registration.getNames()));
|
registration.getTarget(), type, registration.getNames()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -552,8 +547,8 @@ public class ContextFunctionCatalogAutoConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String getQualifier(String key) {
|
private String getQualifier(String key) {
|
||||||
if (registry != null && registry.containsBeanDefinition(key)) {
|
if (this.registry != null && this.registry.containsBeanDefinition(key)) {
|
||||||
BeanDefinition beanDefinition = registry.getBeanDefinition(key);
|
BeanDefinition beanDefinition = this.registry.getBeanDefinition(key);
|
||||||
Object source = beanDefinition.getSource();
|
Object source = beanDefinition.getSource();
|
||||||
if (source instanceof StandardMethodMetadata) {
|
if (source instanceof StandardMethodMetadata) {
|
||||||
StandardMethodMetadata metadata = (StandardMethodMetadata) source;
|
StandardMethodMetadata metadata = (StandardMethodMetadata) source;
|
||||||
@@ -568,13 +563,13 @@ public class ContextFunctionCatalogAutoConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private FunctionType findType(Object function) {
|
private FunctionType findType(Object function) {
|
||||||
String name = names.get(function);
|
String name = this.names.get(function);
|
||||||
if (types.containsKey(name)) {
|
if (this.types.containsKey(name)) {
|
||||||
return types.get(name);
|
return this.types.get(name);
|
||||||
}
|
}
|
||||||
FunctionType param;
|
FunctionType param;
|
||||||
if (name == null || registry == null
|
if (name == null || this.registry == null
|
||||||
|| !registry.containsBeanDefinition(name)) {
|
|| !this.registry.containsBeanDefinition(name)) {
|
||||||
if (function != null) {
|
if (function != null) {
|
||||||
param = new FunctionType(function.getClass());
|
param = new FunctionType(function.getClass());
|
||||||
}
|
}
|
||||||
@@ -583,9 +578,10 @@ public class ContextFunctionCatalogAutoConfiguration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
param = new FunctionType(FunctionContextUtils.findType(name, registry));
|
param = new FunctionType(
|
||||||
|
FunctionContextUtils.findType(name, this.registry));
|
||||||
}
|
}
|
||||||
types.computeIfAbsent(name, str -> param);
|
this.types.computeIfAbsent(name, str -> param);
|
||||||
return param;
|
return param;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -597,7 +593,7 @@ public class ContextFunctionCatalogAutoConfiguration {
|
|||||||
super(ConfigurationPhase.REGISTER_BEAN);
|
super(ConfigurationPhase.REGISTER_BEAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ConditionalOnProperty(name = ContextFunctionCatalogAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "gson", matchIfMissing = false)
|
@ConditionalOnProperty(name = PREFERRED_MAPPER_PROPERTY, havingValue = "gson", matchIfMissing = false)
|
||||||
static class GsonPreferred {
|
static class GsonPreferred {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -609,4 +605,21 @@ public class ContextFunctionCatalogAutoConfiguration {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected class BeanFactoryFunctionInspector implements FunctionInspector {
|
||||||
|
|
||||||
|
private ContextFunctionRegistry processor;
|
||||||
|
|
||||||
|
public BeanFactoryFunctionInspector(ContextFunctionRegistry processor) {
|
||||||
|
this.processor = processor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FunctionRegistration<?> getRegistration(Object function) {
|
||||||
|
FunctionRegistration<?> registration = this.processor
|
||||||
|
.getRegistration(function);
|
||||||
|
return registration;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -50,8 +50,14 @@ import org.springframework.util.ClassUtils;
|
|||||||
public class ContextFunctionCatalogInitializer
|
public class ContextFunctionCatalogInitializer
|
||||||
implements ApplicationContextInitializer<GenericApplicationContext> {
|
implements ApplicationContextInitializer<GenericApplicationContext> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property name for ignoring pre initilizer.
|
||||||
|
*/
|
||||||
public static final String IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME = "spring.backgroundpreinitializer.ignore";
|
public static final String IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME = "spring.backgroundpreinitializer.ignore";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag for enabling the context function catalog initializer.
|
||||||
|
*/
|
||||||
public static boolean enabled = true;
|
public static boolean enabled = true;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -69,7 +75,7 @@ public class ContextFunctionCatalogInitializer
|
|||||||
|
|
||||||
private GenericApplicationContext context;
|
private GenericApplicationContext context;
|
||||||
|
|
||||||
public ContextFunctionCatalogBeanRegistrar(
|
ContextFunctionCatalogBeanRegistrar(
|
||||||
GenericApplicationContext applicationContext) {
|
GenericApplicationContext applicationContext) {
|
||||||
this.context = applicationContext;
|
this.context = applicationContext;
|
||||||
}
|
}
|
||||||
@@ -101,65 +107,67 @@ public class ContextFunctionCatalogInitializer
|
|||||||
|
|
||||||
performPreinitialization();
|
performPreinitialization();
|
||||||
|
|
||||||
if (context.getBeanFactory().getBeanNamesForType(
|
if (this.context.getBeanFactory().getBeanNamesForType(
|
||||||
PropertySourcesPlaceholderConfigurer.class, false,
|
PropertySourcesPlaceholderConfigurer.class, false,
|
||||||
false).length == 0) {
|
false).length == 0) {
|
||||||
context.registerBean(PropertySourcesPlaceholderConfigurer.class,
|
this.context.registerBean(PropertySourcesPlaceholderConfigurer.class,
|
||||||
() -> PropertyPlaceholderAutoConfiguration
|
() -> PropertyPlaceholderAutoConfiguration
|
||||||
.propertySourcesPlaceholderConfigurer());
|
.propertySourcesPlaceholderConfigurer());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!context.getBeanFactory().containsBean(
|
if (!this.context.getBeanFactory().containsBean(
|
||||||
AnnotationConfigUtils.CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
|
AnnotationConfigUtils.CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
|
||||||
// Switch off the ConfigurationClassPostProcessor
|
// Switch off the ConfigurationClassPostProcessor
|
||||||
context.registerBean(
|
this.context.registerBean(
|
||||||
AnnotationConfigUtils.CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME,
|
AnnotationConfigUtils.CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME,
|
||||||
DummyProcessor.class, () -> new DummyProcessor());
|
DummyProcessor.class, () -> new DummyProcessor());
|
||||||
// But switch on other annotation processing
|
// But switch on other annotation processing
|
||||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.context);
|
||||||
}
|
}
|
||||||
if (!context.getBeanFactory()
|
if (!this.context.getBeanFactory()
|
||||||
.containsBean(ConfigurationBeanFactoryMetadata.BEAN_NAME)) {
|
.containsBean(ConfigurationBeanFactoryMetadata.BEAN_NAME)) {
|
||||||
context.registerBean(ConfigurationBeanFactoryMetadata.BEAN_NAME,
|
this.context.registerBean(ConfigurationBeanFactoryMetadata.BEAN_NAME,
|
||||||
ConfigurationBeanFactoryMetadata.class,
|
ConfigurationBeanFactoryMetadata.class,
|
||||||
() -> new ConfigurationBeanFactoryMetadata());
|
() -> new ConfigurationBeanFactoryMetadata());
|
||||||
context.registerBean(
|
this.context.registerBean(
|
||||||
ConfigurationPropertiesBindingPostProcessor.BEAN_NAME,
|
ConfigurationPropertiesBindingPostProcessor.BEAN_NAME,
|
||||||
ConfigurationPropertiesBindingPostProcessor.class,
|
ConfigurationPropertiesBindingPostProcessor.class,
|
||||||
() -> new ConfigurationPropertiesBindingPostProcessor());
|
() -> new ConfigurationPropertiesBindingPostProcessor());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ClassUtils.isPresent("com.google.gson.Gson", null)
|
if (ClassUtils.isPresent("com.google.gson.Gson", null)
|
||||||
&& "gson".equals(context.getEnvironment().getProperty(
|
&& "gson".equals(this.context.getEnvironment().getProperty(
|
||||||
ContextFunctionCatalogAutoConfiguration.PREFERRED_MAPPER_PROPERTY,
|
ContextFunctionCatalogAutoConfiguration.PREFERRED_MAPPER_PROPERTY,
|
||||||
"gson"))) {
|
"gson"))) {
|
||||||
if (context.getBeanFactory().getBeanNamesForType(Gson.class, false,
|
if (this.context.getBeanFactory().getBeanNamesForType(Gson.class, false,
|
||||||
false).length == 0) {
|
false).length == 0) {
|
||||||
context.registerBean(Gson.class, () -> new Gson());
|
this.context.registerBean(Gson.class, () -> new Gson());
|
||||||
}
|
}
|
||||||
context.registerBean(JsonMapper.class,
|
this.context.registerBean(JsonMapper.class,
|
||||||
() -> new ContextFunctionCatalogAutoConfiguration.GsonConfiguration()
|
() -> new ContextFunctionCatalogAutoConfiguration.GsonConfiguration()
|
||||||
.jsonMapper(context.getBean(Gson.class)));
|
.jsonMapper(this.context.getBean(Gson.class)));
|
||||||
}
|
}
|
||||||
else if (ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper",
|
else if (ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper",
|
||||||
null)) {
|
null)) {
|
||||||
if (context.getBeanFactory().getBeanNamesForType(ObjectMapper.class,
|
if (this.context.getBeanFactory().getBeanNamesForType(ObjectMapper.class,
|
||||||
false, false).length == 0) {
|
false, false).length == 0) {
|
||||||
context.registerBean(ObjectMapper.class, () -> new ObjectMapper());
|
this.context.registerBean(ObjectMapper.class,
|
||||||
|
() -> new ObjectMapper());
|
||||||
}
|
}
|
||||||
context.registerBean(JsonMapper.class,
|
this.context.registerBean(JsonMapper.class,
|
||||||
() -> new ContextFunctionCatalogAutoConfiguration.JacksonConfiguration()
|
() -> new ContextFunctionCatalogAutoConfiguration.JacksonConfiguration()
|
||||||
.jsonMapper(context.getBean(ObjectMapper.class)));
|
.jsonMapper(this.context.getBean(ObjectMapper.class)));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.getBeanFactory().getBeanNamesForType(FunctionCatalog.class, false,
|
if (this.context.getBeanFactory().getBeanNamesForType(FunctionCatalog.class,
|
||||||
false).length == 0) {
|
false, false).length == 0) {
|
||||||
context.registerBean(InMemoryFunctionCatalog.class,
|
this.context.registerBean(InMemoryFunctionCatalog.class,
|
||||||
() -> new InMemoryFunctionCatalog());
|
() -> new InMemoryFunctionCatalog());
|
||||||
context.registerBean(FunctionRegistrationPostProcessor.class,
|
this.context
|
||||||
() -> new FunctionRegistrationPostProcessor(
|
.registerBean(FunctionRegistrationPostProcessor.class,
|
||||||
context.getAutowireCapableBeanFactory()
|
() -> new FunctionRegistrationPostProcessor(this.context
|
||||||
|
.getAutowireCapableBeanFactory()
|
||||||
.getBeanProvider(FunctionRegistration.class)));
|
.getBeanProvider(FunctionRegistration.class)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -193,10 +201,11 @@ public class ContextFunctionCatalogInitializer
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class FunctionRegistrationPostProcessor implements BeanPostProcessor {
|
private class FunctionRegistrationPostProcessor implements BeanPostProcessor {
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
private final ObjectProvider<FunctionRegistration> functions;
|
private final ObjectProvider<FunctionRegistration> functions;
|
||||||
|
|
||||||
public FunctionRegistrationPostProcessor(
|
FunctionRegistrationPostProcessor(
|
||||||
@SuppressWarnings("rawtypes") ObjectProvider<FunctionRegistration> functions) {
|
@SuppressWarnings("rawtypes") ObjectProvider<FunctionRegistration> functions) {
|
||||||
this.functions = functions;
|
this.functions = functions;
|
||||||
}
|
}
|
||||||
@@ -206,7 +215,7 @@ public class ContextFunctionCatalogInitializer
|
|||||||
throws BeansException {
|
throws BeansException {
|
||||||
if (bean instanceof FunctionRegistry) {
|
if (bean instanceof FunctionRegistry) {
|
||||||
FunctionRegistry catalog = (FunctionRegistry) bean;
|
FunctionRegistry catalog = (FunctionRegistry) bean;
|
||||||
for (FunctionRegistration<?> registration : functions) {
|
for (FunctionRegistration<?> registration : this.functions) {
|
||||||
Assert.notEmpty(registration.getNames(),
|
Assert.notEmpty(registration.getNames(),
|
||||||
"FunctionRegistration must define at least one name. Was empty");
|
"FunctionRegistration must define at least one name. Was empty");
|
||||||
if (registration.getType() == null) {
|
if (registration.getType() == null) {
|
||||||
@@ -222,13 +231,19 @@ public class ContextFunctionCatalogInitializer
|
|||||||
}
|
}
|
||||||
return bean;
|
return bean;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dummy implementation of a processor.
|
||||||
|
*/
|
||||||
public static class DummyProcessor {
|
public static class DummyProcessor {
|
||||||
|
|
||||||
public void setMetadataReaderFactory(MetadataReaderFactory obj) {
|
public void setMetadataReaderFactory(MetadataReaderFactory obj) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2018 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -38,103 +38,106 @@ import org.springframework.util.ReflectionUtils;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Oleg Zhurakousky
|
* @author Oleg Zhurakousky
|
||||||
*
|
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
abstract class FunctionContextUtils {
|
abstract class FunctionContextUtils {
|
||||||
|
|
||||||
public static Type findType(String name, ConfigurableListableBeanFactory registry) {
|
public static Type findType(String name, ConfigurableListableBeanFactory registry) {
|
||||||
AbstractBeanDefinition definition = (AbstractBeanDefinition) registry.getBeanDefinition(name);
|
AbstractBeanDefinition definition = (AbstractBeanDefinition) registry
|
||||||
Object source = definition.getSource();
|
.getBeanDefinition(name);
|
||||||
Type param = null;
|
Object source = definition.getSource();
|
||||||
// Start by assuming output -> Function
|
Type param = null;
|
||||||
if (source instanceof StandardMethodMetadata) {
|
// Start by assuming output -> Function
|
||||||
// Standard @Bean metadata
|
if (source instanceof StandardMethodMetadata) {
|
||||||
param = ((StandardMethodMetadata) source).getIntrospectedMethod()
|
// Standard @Bean metadata
|
||||||
.getGenericReturnType();
|
param = ((StandardMethodMetadata) source).getIntrospectedMethod()
|
||||||
}
|
.getGenericReturnType();
|
||||||
else if (source instanceof MethodMetadataReadingVisitor) {
|
}
|
||||||
// A component scan with @Beans
|
else if (source instanceof MethodMetadataReadingVisitor) {
|
||||||
MethodMetadataReadingVisitor visitor = (MethodMetadataReadingVisitor) source;
|
// A component scan with @Beans
|
||||||
param = findBeanType(definition, visitor);
|
MethodMetadataReadingVisitor visitor = (MethodMetadataReadingVisitor) source;
|
||||||
}
|
param = findBeanType(definition, visitor);
|
||||||
else if (source instanceof Resource) {
|
}
|
||||||
param = registry.getType(name);
|
else if (source instanceof Resource) {
|
||||||
}
|
param = registry.getType(name);
|
||||||
else {
|
}
|
||||||
ResolvableType type = (ResolvableType) getField(definition, "targetType");
|
else {
|
||||||
if (type != null) {
|
ResolvableType type = (ResolvableType) getField(definition, "targetType");
|
||||||
param = type.getType();
|
if (type != null) {
|
||||||
}
|
param = type.getType();
|
||||||
else {
|
}
|
||||||
Class<?> beanClass = definition.getBeanClass();
|
else {
|
||||||
if (beanClass != null && !FunctionFactoryMetadata.class.isAssignableFrom(beanClass)) {
|
Class<?> beanClass = definition.getBeanClass();
|
||||||
param = beanClass;
|
if (beanClass != null
|
||||||
}
|
&& !FunctionFactoryMetadata.class.isAssignableFrom(beanClass)) {
|
||||||
else {
|
param = beanClass;
|
||||||
//assume FunctionFactoryMetadata
|
}
|
||||||
FunctionFactoryMetadata<?> factory = (FunctionFactoryMetadata<?>) registry.getBean(name);
|
else {
|
||||||
param = factory.getFactoryMethod().getGenericReturnType();
|
// assume FunctionFactoryMetadata
|
||||||
}
|
FunctionFactoryMetadata<?> factory = (FunctionFactoryMetadata<?>) registry
|
||||||
}
|
.getBean(name);
|
||||||
}
|
param = factory.getFactoryMethod().getGenericReturnType();
|
||||||
return param;
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
private static Type findBeanType(AbstractBeanDefinition definition,
|
private static Type findBeanType(AbstractBeanDefinition definition,
|
||||||
MethodMetadataReadingVisitor visitor) {
|
MethodMetadataReadingVisitor visitor) {
|
||||||
Class<?> factory = ClassUtils
|
Class<?> factory = ClassUtils.resolveClassName(visitor.getDeclaringClassName(),
|
||||||
.resolveClassName(visitor.getDeclaringClassName(), null);
|
null);
|
||||||
Class<?>[] params = getParamTypes(factory, definition);
|
Class<?>[] params = getParamTypes(factory, definition);
|
||||||
Method method = ReflectionUtils.findMethod(factory, visitor.getMethodName(),
|
Method method = ReflectionUtils.findMethod(factory, visitor.getMethodName(),
|
||||||
params);
|
params);
|
||||||
Type type = method.getGenericReturnType();
|
Type type = method.getGenericReturnType();
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Class<?>[] getParamTypes(Class<?> factory,
|
private static Class<?>[] getParamTypes(Class<?> factory,
|
||||||
AbstractBeanDefinition definition) {
|
AbstractBeanDefinition definition) {
|
||||||
if (definition instanceof RootBeanDefinition) {
|
if (definition instanceof RootBeanDefinition) {
|
||||||
RootBeanDefinition root = (RootBeanDefinition) definition;
|
RootBeanDefinition root = (RootBeanDefinition) definition;
|
||||||
for (Method method : getCandidateMethods(factory, root)) {
|
for (Method method : getCandidateMethods(factory, root)) {
|
||||||
if (root.isFactoryMethod(method)) {
|
if (root.isFactoryMethod(method)) {
|
||||||
return method.getParameterTypes();
|
return method.getParameterTypes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<Class<?>> params = new ArrayList<>();
|
List<Class<?>> params = new ArrayList<>();
|
||||||
for (ConstructorArgumentValues.ValueHolder holder : definition.getConstructorArgumentValues()
|
for (ConstructorArgumentValues.ValueHolder holder : definition
|
||||||
.getIndexedArgumentValues().values()) {
|
.getConstructorArgumentValues().getIndexedArgumentValues().values()) {
|
||||||
params.add(ClassUtils.resolveClassName(holder.getType(), null));
|
params.add(ClassUtils.resolveClassName(holder.getType(), null));
|
||||||
}
|
}
|
||||||
return params.toArray(new Class<?>[0]);
|
return params.toArray(new Class<?>[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Method[] getCandidateMethods(final Class<?> factoryClass,
|
private static Method[] getCandidateMethods(final Class<?> factoryClass,
|
||||||
final RootBeanDefinition mbd) {
|
final RootBeanDefinition mbd) {
|
||||||
if (System.getSecurityManager() != null) {
|
if (System.getSecurityManager() != null) {
|
||||||
return AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
|
return AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
|
||||||
@Override
|
@Override
|
||||||
public Method[] run() {
|
public Method[] run() {
|
||||||
return (mbd.isNonPublicAccessAllowed()
|
return (mbd.isNonPublicAccessAllowed()
|
||||||
? ReflectionUtils.getAllDeclaredMethods(factoryClass)
|
? ReflectionUtils.getAllDeclaredMethods(factoryClass)
|
||||||
: factoryClass.getMethods());
|
: factoryClass.getMethods());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return (mbd.isNonPublicAccessAllowed()
|
return (mbd.isNonPublicAccessAllowed()
|
||||||
? ReflectionUtils.getAllDeclaredMethods(factoryClass)
|
? ReflectionUtils.getAllDeclaredMethods(factoryClass)
|
||||||
: factoryClass.getMethods());
|
: factoryClass.getMethods());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Object getField(Object target, String name) {
|
||||||
|
Field field = ReflectionUtils.findField(target.getClass(), name);
|
||||||
|
if (field == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ReflectionUtils.makeAccessible(field);
|
||||||
|
return ReflectionUtils.getField(field, target);
|
||||||
|
}
|
||||||
|
|
||||||
private static Object getField(Object target, String name) {
|
|
||||||
Field field = ReflectionUtils.findField(target.getClass(), name);
|
|
||||||
if (field == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
ReflectionUtils.makeAccessible(field);
|
|
||||||
return ReflectionUtils.getField(field, target);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016-2017 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -39,7 +39,6 @@ public abstract class MessageUtils {
|
|||||||
* isolated class loader, then the message will be created with the target class
|
* isolated class loader, then the message will be created with the target class
|
||||||
* loader (therefore the {@link Message} class must be on the classpath of the target
|
* loader (therefore the {@link Message} class must be on the classpath of the target
|
||||||
* class loader).
|
* class loader).
|
||||||
*
|
|
||||||
* @param handler the function that will be applied to the message
|
* @param handler the function that will be applied to the message
|
||||||
* @param payload the payload of the message
|
* @param payload the payload of the message
|
||||||
* @param headers the headers for the message
|
* @param headers the headers for the message
|
||||||
@@ -74,7 +73,6 @@ public abstract class MessageUtils {
|
|||||||
* class loader. If the handler is a wrapper for a function in an isolated class
|
* class loader. If the handler is a wrapper for a function in an isolated class
|
||||||
* loader, then the message will be created with the target class loader (therefore
|
* loader, then the message will be created with the target class loader (therefore
|
||||||
* the {@link Message} class must be on the classpath of the target class loader).
|
* the {@link Message} class must be on the classpath of the target class loader).
|
||||||
*
|
|
||||||
* @param handler the function that generated the message
|
* @param handler the function that generated the message
|
||||||
* @param message the message to convert
|
* @param message the message to convert
|
||||||
* @return a message with the correct class loader
|
* @return a message with the correct class loader
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user