Turned on checkstyle

This commit is contained in:
Marcin Grzejszczak
2019-02-01 15:48:32 +01:00
parent 94e9b8f2f8
commit e4b08a083c
268 changed files with 5114 additions and 3993 deletions

14
.editorconfig Normal file
View 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

View File

@@ -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
View File

View File

@@ -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>

View File

@@ -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
View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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);

View File

@@ -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());
} }
} }

View File

@@ -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

View File

@@ -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);
} }
} }
} }

View File

@@ -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");
}
} }

View File

@@ -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;
} }
} }
} }

View File

@@ -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);
}
}
} }

View File

@@ -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;
} }
} }
} }

View File

@@ -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;
} }
} }
} }

View File

@@ -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 {
} }
} }

View File

@@ -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>

View File

@@ -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;
} }
} }

View File

@@ -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;
} }
} }

View File

@@ -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) {

View File

@@ -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)));
} }
} }
} }

View File

@@ -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;
} }
} }

View File

@@ -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>

View File

@@ -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;
} }
} }

View File

@@ -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

View File

@@ -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);
} }
} }
} }

View File

@@ -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;
} }
} }

View File

@@ -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");
} }
} }

View File

@@ -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;
} }
} }

View File

@@ -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;
} }
} }
} }

View File

@@ -1,2 +1,2 @@
function.name: greeter function.name:greeter
function.type: function function.type:function

View File

@@ -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>

View File

@@ -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
}
} }

View File

@@ -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> {

View File

@@ -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;
} }
} }

View File

@@ -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;
} }
} }

View File

@@ -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;
} }
} }

View File

@@ -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;
} }
} }

View File

@@ -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");
} }
} }

View File

@@ -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

View File

@@ -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);
} }
} }

View File

@@ -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);
} }
} }

View File

@@ -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;

View File

@@ -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();
} }
} }

View File

@@ -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

View File

@@ -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;
} }

View File

@@ -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();
} }

View File

@@ -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

View File

@@ -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.

View File

@@ -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();
} }
} }
} }

View File

@@ -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()));
} }
} }

View File

@@ -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);
} }
} }

View File

@@ -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() {

View File

@@ -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;
}
} }
} }

View File

@@ -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;
}
}
} }

View File

@@ -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;
} }
} }

View File

@@ -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;
}
} }
} }

View File

@@ -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.

View File

@@ -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()) {

View File

@@ -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;
}
} }

View File

@@ -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()));
} }
} }

View File

@@ -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;
} }
} }

View File

@@ -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);
} }
} }

View File

@@ -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()));
} }
} }

View File

@@ -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();
} }
} }

View File

@@ -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();
} }
} }

View File

@@ -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);
} }
} }

View File

@@ -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);
} }
} }

View File

@@ -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();
} }
} }

View File

@@ -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);
} }
} }

View File

@@ -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);
} }
} }

View File

@@ -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();
} }
} }

View File

@@ -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);
} }
} }

View File

@@ -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();
} }
} }

View File

@@ -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");
} }

View File

@@ -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");
} }
} }

View File

@@ -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"));
} }
} }

View File

@@ -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");
} }
} }

View File

@@ -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));
} }
} }

View File

@@ -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 {
}
} }

View File

@@ -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>

View File

@@ -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);
} }

View File

@@ -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");
} }
} }

View File

@@ -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;

View File

@@ -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.

View File

@@ -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;
} }
} }
} }

View File

@@ -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);
} }

View File

@@ -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.

View File

@@ -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;

View File

@@ -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.

View File

@@ -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;
} }
} }

View File

@@ -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;
} }
} }

View File

@@ -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);
} }
} }
} }

View File

@@ -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;
}
}
} }

View File

@@ -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) {
} }
} }
} }

View File

@@ -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.

View File

@@ -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);
}
} }

View File

@@ -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