diff --git a/pom.xml b/pom.xml index 396aa1794..8f2d7bfa8 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,6 @@ 1.7 - UTF-8 @@ -40,11 +39,9 @@ spring-cloud-stream-tuple spring-cloud-stream-starters spring-cloud-stream-rxjava - spring-cloud-stream-module-launcher spring-cloud-stream-test-support spring-cloud-stream-test-support-internal spring-cloud-stream-integration-tests - spring-cloud-stream-configuration-metadata spring-cloud-stream-docs diff --git a/spring-cloud-stream-configuration-metadata/pom.xml b/spring-cloud-stream-configuration-metadata/pom.xml deleted file mode 100644 index 364d4e6a1..000000000 --- a/spring-cloud-stream-configuration-metadata/pom.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - 4.0.0 - - org.springframework.cloud - spring-cloud-stream-parent - 1.0.0.BUILD-SNAPSHOT - - spring-cloud-stream-configuration-metadata - - - - org.springframework.cloud - spring-cloud-stream-module-launcher - - - org.springframework.boot - spring-boot-configuration-metadata - - - diff --git a/spring-cloud-stream-configuration-metadata/src/main/java/org/springframework/cloud/stream/configuration/metadata/ModuleConfigurationMetadataResolver.java b/spring-cloud-stream-configuration-metadata/src/main/java/org/springframework/cloud/stream/configuration/metadata/ModuleConfigurationMetadataResolver.java deleted file mode 100644 index 70a0dc83b..000000000 --- a/spring-cloud-stream-configuration-metadata/src/main/java/org/springframework/cloud/stream/configuration/metadata/ModuleConfigurationMetadataResolver.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.stream.configuration.metadata; - -import java.io.Closeable; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.springframework.boot.configurationmetadata.ConfigurationMetadataGroup; -import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; -import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepositoryJsonBuilder; -import org.springframework.boot.loader.archive.Archive; -import org.springframework.boot.loader.archive.ExplodedArchive; -import org.springframework.boot.loader.archive.JarFileArchive; -import org.springframework.boot.loader.jar.JarFile; -import org.springframework.cloud.stream.module.launcher.ModuleJarLauncher; -import org.springframework.core.io.Resource; -import org.springframework.core.io.support.PathMatchingResourcePatternResolver; -import org.springframework.core.io.support.ResourcePatternResolver; - -/** - * Used to retrieve metadata about the configuration properties that can alter a module behavior. - * - * @author Eric Bottard - */ -public class ModuleConfigurationMetadataResolver { - - public ModuleConfigurationMetadataResolver() { - JarFile.registerUrlProtocolHandler(); - } - - /** - * Return metadata about configuration properties (as groups) that are documented via - * - * Spring Boot configuration metadata and visible in a module. - * - * @param module a Spring Cloud Stream module; typically a Boot uberjar, - * but directories are supported as well - */ - public List listPropertyGroups(Resource module) { - List result = new ArrayList<>(); - ClassLoader classLoader = null; - try { - File moduleFile = module.getFile(); - Archive archive = moduleFile.isDirectory() ? new ExplodedArchive(moduleFile) : new JarFileArchive(moduleFile); - classLoader = createClassLoader(archive); - ConfigurationMetadataRepositoryJsonBuilder builder = ConfigurationMetadataRepositoryJsonBuilder.create(); - ResourcePatternResolver moduleResourceLoader = new PathMatchingResourcePatternResolver(classLoader); - for (Resource r : moduleResourceLoader.getResources("classpath*:/META-INF/*spring-configuration-metadata.json")) { - builder.withJsonResource(r.getInputStream()); - } - for (ConfigurationMetadataGroup group : builder.build().getAllGroups().values()) { - result.add(group); - } - } - catch (Exception e) { - throw new RuntimeException("Exception trying to list configuration properties for module " + module, e); - } - finally { - if (classLoader instanceof Closeable) { - try { - ((Closeable) classLoader).close(); - } - catch (IOException e) { - // ignore - } - } - } - return result; - } - - /** - * Return metadata about configuration properties that are documented via - * - * Spring Boot configuration metadata and visible in a module. - * - * @param module a Spring Cloud Stream module; typically a Boot uberjar, - * but directories are supported as well - */ - public List listProperties(Resource module) { - List result = new ArrayList<>(); - ClassLoader classLoader = null; - try { - File moduleFile = module.getFile(); - Archive archive = moduleFile.isDirectory() ? new ExplodedArchive(moduleFile) : new JarFileArchive(moduleFile); - classLoader = createClassLoader(archive); - ConfigurationMetadataRepositoryJsonBuilder builder = ConfigurationMetadataRepositoryJsonBuilder.create(); - ResourcePatternResolver moduleResourceLoader = new PathMatchingResourcePatternResolver(classLoader); - for (Resource r : moduleResourceLoader.getResources("classpath*:/META-INF/*spring-configuration-metadata.json")) { - builder.withJsonResource(r.getInputStream()); - } - for (ConfigurationMetadataProperty property : builder.build().getAllProperties().values()) { - result.add(property); - } - } - catch (Exception e) { - throw new RuntimeException("Exception trying to list configuration properties for module " + module, e); - } - finally { - if (classLoader instanceof Closeable) { - try { - ((Closeable) classLoader).close(); - } - catch (IOException e) { - // ignore - } - } - } - return result; - } - - /** - * Return a {@link ClassLoader} for accessing resources in the provided - * {@link Archive}. The caller is responsible for disposing of the - * class loader. - * - * @param archive the archive for which to return a class loader - * @return class loader for the given archive - * @throws Exception if the class loader cannot be created - */ - protected ClassLoader createClassLoader(Archive archive) throws Exception { - return new ClassLoaderExposingJarLauncher(archive).createClassLoader(); - } - - - /** - * Extension of {@link ModuleJarLauncher} used for exposing a {@link ClassLoader} - * for the provided {@link Archive}. - */ - private static class ClassLoaderExposingJarLauncher extends ModuleJarLauncher { - - public ClassLoaderExposingJarLauncher(Archive archive) { - super(archive); - } - - protected ClassLoader createClassLoader() throws Exception { - List classPathArchives = getClassPathArchives(); - return createClassLoader(classPathArchives); - } - } -} diff --git a/spring-cloud-stream-configuration-metadata/src/main/java/org/springframework/cloud/stream/configuration/metadata/ModuleConfigurationMetadataResolverAutoConfiguration.java b/spring-cloud-stream-configuration-metadata/src/main/java/org/springframework/cloud/stream/configuration/metadata/ModuleConfigurationMetadataResolverAutoConfiguration.java deleted file mode 100644 index 7aa664710..000000000 --- a/spring-cloud-stream-configuration-metadata/src/main/java/org/springframework/cloud/stream/configuration/metadata/ModuleConfigurationMetadataResolverAutoConfiguration.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.stream.configuration.metadata; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Bean; - -/** - * Automatically exposes a ModuleConfigurationMetadataResolver if none is already registered. - * - * @author Eric Bottard - */ -@Configuration -public class ModuleConfigurationMetadataResolverAutoConfiguration { - - @Bean - @ConditionalOnMissingBean(ModuleConfigurationMetadataResolver.class) - public ModuleConfigurationMetadataResolver metadataResolver() { - return new ModuleConfigurationMetadataResolver(); - } -} diff --git a/spring-cloud-stream-configuration-metadata/src/main/resources/META-INF/spring.factories b/spring-cloud-stream-configuration-metadata/src/main/resources/META-INF/spring.factories deleted file mode 100644 index c53b5c782..000000000 --- a/spring-cloud-stream-configuration-metadata/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,2 +0,0 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration:\ -org.springframework.cloud.stream.configuration.metadata.ModuleConfigurationMetadataResolverAutoConfiguration diff --git a/spring-cloud-stream-dependencies/pom.xml b/spring-cloud-stream-dependencies/pom.xml index 1c5501515..afaf170da 100644 --- a/spring-cloud-stream-dependencies/pom.xml +++ b/spring-cloud-stream-dependencies/pom.xml @@ -73,11 +73,6 @@ spring-cloud-stream-binder-test ${project.version} - - org.springframework.cloud - spring-cloud-stream-module-launcher - ${project.version} - org.springframework.cloud spring-cloud-stream-tuple diff --git a/spring-cloud-stream-module-launcher/README.md b/spring-cloud-stream-module-launcher/README.md deleted file mode 100644 index 7160becdb..000000000 --- a/spring-cloud-stream-module-launcher/README.md +++ /dev/null @@ -1,88 +0,0 @@ -# Module Launcher - -The Module Launcher provides a single entry point that bootstraps module JARs located in a Maven repository. A single Docker image can then be used to launch any of those JARs based on an environment variable. When running standalone, a system property may be used instead of an environment variable, so that multiple instances of the Module Launcher may run on a single machine. The following examples demonstrate running the modules for the *ticktock* stream (`time-source | log-sink`). - -## Prerequisites - -1: clone and build the spring-cloud-stream project: - -```` -git clone https://github.com/spring-cloud/spring-cloud-stream.git -cd spring-cloud-stream -mvn package -cd .. -```` - -2: start redis locally via `redis-server` or `docker-compose` (there's a `docker-compose.yml` in `spring-cloud-stream-samples`). Optionally start `redis-cli` and use the `MONITOR` command to watch activity. - -*NOTE:* redis.conf (on OSX it is found here: /usr/local/etc/redis.conf) may need to be updated to set the binding to an address other than 127.0.0.1 else the docker instances will fail to connect. For example: bind 0.0.0.0 - -## Running Standalone - -From the `spring-cloud-stream/spring-cloud-stream-module-launcher` directory: - -```` -java -Dmodules=org.springframework.cloud.stream.module:time-source:jar:exec:1.0.0.BUILD-SNAPSHOT -Dspring.cloud.stream.bindings.output=ticktock -jar target/spring-cloud-stream-module-launcher-1.0.0.BUILD-SNAPSHOT.jar -java -Dmodules=org.springframework.cloud.stream.module:log-sink:jar:exec:1.0.0.BUILD-SNAPSHOT -Dargs.0.server.port=8081 -Dspring.cloud.stream.bindings.input=ticktock -jar target/spring-cloud-stream-module-launcher-1.0.0.BUILD-SNAPSHOT.jar -```` - -Note that `server.port` needs to be specified explicitly for the log sink module as the time source module already uses the default port `8080`. -The module launcher is able to launch several modules, hence the `args.0.` prefix. -The binding property is set to use the same name `ticktock` for both the output/input bindings of source/sink modules so that the log sink receives messages from the time source. - -The time messages will be emitted every second. The console for the log module will display each: - -```` -2015-08-26 14:21:44.546 INFO 35725 --- [hannel-adapter1] o.s.cloud.stream.module.log.LogSink : Received: 2015-08-26 14:21:44 -2015-08-26 14:21:45.548 INFO 35725 --- [hannel-adapter1] o.s.cloud.stream.module.log.LogSink : Received: 2015-08-26 14:21:45 -2015-08-26 14:21:46.550 INFO 35725 --- [hannel-adapter1] o.s.cloud.stream.module.log.LogSink : Received: 2015-08-26 14:21:46 -```` - -*NOTE:* the two modules will be launched within a single process if both are provided (comma-delimited) via `-Dmodules` - -## Running with Docker - -The easiest way to get a demo working is to use `docker-compose` (From the `spring-cloud-stream/spring-cloud-stream-module-launcher` directory): - -Make sure to set `DOCKER_HOST`. If you are running a `boot2docker` VM, $(boot2docker shellinit) would set that up. - -``` -$ mvn package docker:build -$ docker-compose up -... -logsink_1 | 2015-08-11 08:25:49.909 INFO 1 --- [hannel-adapter1] o.s.cloud.stream.module.log.LogSink : Received: 2015-08-11 08:25:49 -logsink_1 | 2015-08-11 08:25:50.909 INFO 1 --- [hannel-adapter1] o.s.cloud.stream.module.log.LogSink : Received: 2015-08-11 08:25:50 -... -``` - -You can also run each module individually as a Docker process by passing environment variables for the module name as well as the host machine's IP address for the redis connection to be established within the container: -To find out redis host IP: -``` -Get the container ID of redis: `docker ps` -Get the IP address by inspecting the container: `docker inspect ` - -``` -To run the modules individually on docker: -```` -docker run -p 8080:8080 -e MODULES=org.springframework.cloud.stream.module:time-source:jar:exec:1.0.0.BUILD-SNAPSHOT \ - -e spring.cloud.stream.bindings.output=ticktock -e SPRING_REDIS_HOST= springcloud/stream-module-launcher - -docker run -p 8081:8080 -e MODULES=org.springframework.cloud.stream.module:log-sink:jar:exec:1.0.0.BUILD-SNAPSHOT \ - -e spring.cloud.stream.bindings.input=ticktock -e SPRING_REDIS_HOST= springcloud/stream-module-launcher -```` -Note the binding name `ticktock` is specified for the source's output and sink's input. -The port mapping is done so that individual modules' http endpoints can be accessed via the docker VM port. - -To run pub/sub modules individually on docker, the binding name has to start with `topic:`. - -```` -docker run -p 8080:8080 -e MODULES=org.springframework.cloud.stream.module:time-source:jar:exec:1.0.0.BUILD-SNAPSHOT \ - -e spring.cloud.stream.bindings.output=topic:foo -e SPRING_REDIS_HOST= springcloud/stream-module-launcher - -docker run -p 8081:8080 -e MODULES=org.springframework.cloud.stream.module:log-sink:jar:exec:1.0.0.BUILD-SNAPSHOT \ - -e spring.cloud.stream.bindings.input=topic:foo -e SPRING_REDIS_HOST= springcloud/stream-module-launcher - -docker run -p 8082:8080 -e MODULES=org.springframework.cloud.stream.module:log-sink:jar:exec:1.0.0.BUILD-SNAPSHOT \ - -e spring.cloud.stream.bindings.input=topic:foo -e SPRING_REDIS_HOST= springcloud/stream-module-launcher -```` -In the above scenario, both the sink modules receive the same messages from the time source. diff --git a/spring-cloud-stream-module-launcher/docker-compose.yml b/spring-cloud-stream-module-launcher/docker-compose.yml deleted file mode 100644 index 21dd45fe0..000000000 --- a/spring-cloud-stream-module-launcher/docker-compose.yml +++ /dev/null @@ -1,24 +0,0 @@ -redis: - image: redis - -timesource: - image: springcloud/stream-module-launcher - links: - - redis - ports: - - "8080:8080" - environment: - SPRING_REDIS_HOST: redis - MODULES: org.springframework.cloud.stream.module:time-source:1.0.0.BUILD-SNAPSHOT - SPRING_CLOUD_STREAM_BINDINGS_OUTPUT: ticktock - -logsink: - image: springcloud/stream-module-launcher - links: - - redis - ports: - - "8081:8080" - environment: - SPRING_REDIS_HOST: redis - MODULES: org.springframework.cloud.stream.module:log-sink:1.0.0.BUILD-SNAPSHOT - SPRING_CLOUD_STREAM_BINDINGS_INPUT: ticktock diff --git a/spring-cloud-stream-module-launcher/pom.xml b/spring-cloud-stream-module-launcher/pom.xml deleted file mode 100644 index 3f405687e..000000000 --- a/spring-cloud-stream-module-launcher/pom.xml +++ /dev/null @@ -1,154 +0,0 @@ - - - 4.0.0 - - spring-cloud-stream-module-launcher - jar - spring-cloud-stream-module-launcher - application for launching spring-cloud-stream modules - - - org.springframework.cloud - spring-cloud-stream-parent - 1.0.0.BUILD-SNAPSHOT - - - - org.springframework.cloud.stream.module.launcher.ModuleLauncherApplication - 1.0.2.v20150114 - 3.1.0 - 1.57 - springcloud - stream-module-launcher - - - - - org.springframework.boot - spring-boot-starter-actuator - - - org.springframework.boot - spring-boot-starter-validation - - - org.springframework.boot - spring-boot-configuration-processor - true - - - org.springframework.boot - spring-boot-loader - - - org.eclipse.aether - aether-impl - ${aether.version} - - - org.eclipse.aether - aether-connector-basic - ${aether.version} - - - org.eclipse.aether - aether-transport-file - ${aether.version} - - - org.eclipse.aether - aether-transport-http - ${aether.version} - - - org.apache.maven - maven-aether-provider - ${maven.version} - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.integration - spring-integration-test - test - - - com.github.tomakehurst - wiremock - ${wiremock.version} - test - - standalone - - - org.mortbay.jetty - jetty - - - com.google.guava - guava - - - com.fasterxml.jackson.core - jackson-core - - - com.fasterxml.jackson.core - jackson-annotations - - - com.fasterxml.jackson.core - jackson-databind - - - org.apache.httpcomponents - httpclient - - - org.skyscreamer - jsonassert - - - xmlunit - xmlunit - - - com.jayway.jsonpath - json-path - - - net.sf.jopt-simple - jopt-simple - - - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - com.spotify - docker-maven-plugin - 0.2.3 - - ${docker.image.prefix}/${docker.image.name} - src/main/docker - - - / - ${project.build.directory} - ${project.build.finalName}.jar - - - - - - - diff --git a/spring-cloud-stream-module-launcher/src/main/docker/Dockerfile b/spring-cloud-stream-module-launcher/src/main/docker/Dockerfile deleted file mode 100644 index 02e2d94e6..000000000 --- a/spring-cloud-stream-module-launcher/src/main/docker/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM anapsix/alpine-java:8 -VOLUME /tmp -ADD *.jar module-launcher.jar -RUN bash -c 'touch /module-launcher.jar' -ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/module-launcher.jar"] diff --git a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ArchiveMatchingEntryFilter.java b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ArchiveMatchingEntryFilter.java deleted file mode 100644 index 419c8597a..000000000 --- a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ArchiveMatchingEntryFilter.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.stream.module.launcher; - -import org.springframework.boot.loader.archive.Archive; -import org.springframework.boot.loader.util.AsciiBytes; - -/** - * @author Marius Bogoevici - */ -class ArchiveMatchingEntryFilter implements Archive.EntryFilter { - - public static final ArchiveMatchingEntryFilter FILTER = new ArchiveMatchingEntryFilter(); - - private static final AsciiBytes LIB = new AsciiBytes("lib/"); - - @Override - public boolean matches(Archive.Entry entry) { - return isNestedArchive(entry); - } - - protected boolean isNestedArchive(Archive.Entry entry) { - return !entry.isDirectory() && entry.getName().startsWith(LIB); - } -} diff --git a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleJarLauncher.java b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleJarLauncher.java deleted file mode 100644 index e01a3fecc..000000000 --- a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleJarLauncher.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.stream.module.launcher; - - -import java.lang.reflect.Method; -import java.net.URL; - -import org.springframework.boot.loader.JarLauncher; -import org.springframework.boot.loader.archive.Archive; -import org.springframework.cloud.stream.module.utils.ClassloaderUtils; -import org.springframework.util.ClassUtils; -import org.springframework.util.ReflectionUtils; - -/** - * A {@link JarLauncher} that supports launching module archive. - * - * Also, it restricts the classloader of the launched application to the contents - * of the uber-jar and the extension classloader of the JVM. - * - * @author Mark Fisher - * @author Marius Bogoevici - * @author Ilayaperumal Gopinathan - */ -public class ModuleJarLauncher extends JarLauncher { - - public ModuleJarLauncher(Archive archive) { - super(archive); - } - - @Override - public void launch(String[] args) { - super.launch(args); - } - - @Override - protected void launch(String[] args, String mainClass, ClassLoader classLoader) - throws Exception { - if (ClassUtils.isPresent( - "org.apache.catalina.webresources.TomcatURLStreamHandlerFactory", - classLoader)) { - // Ensure the method is invoked on a class that is loaded by the provided - // class loader (not the current context class loader): - Method method = ReflectionUtils - .findMethod(classLoader.loadClass("org.apache.catalina.webresources.TomcatURLStreamHandlerFactory"), - "disable"); - ReflectionUtils.invokeMethod(method, null); - } - super.launch(args, mainClass, classLoader); - } - - @Override - protected ClassLoader createClassLoader(URL[] urls) throws Exception { - return ClassloaderUtils.createModuleClassloader(urls); - } - -} diff --git a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleLaunchRequest.java b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleLaunchRequest.java deleted file mode 100644 index 9aaa8bcf4..000000000 --- a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleLaunchRequest.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.stream.module.launcher; - -import java.util.HashMap; -import java.util.Map; - -/** - * Encapsulates a reference to a module (as maven coordinates) and a set of "arguments" that must be passed to it. - * Those arguments will be passed to the module by the launcher. - * - * @author Eric Bottard - */ -public class ModuleLaunchRequest { - - private final String module; - - private final Map arguments; - - public ModuleLaunchRequest(String module, Map arguments) { - this.module = module; - this.arguments = arguments != null ? new HashMap<>(arguments) : new HashMap(); - } - - public String getModule() { - return module; - } - - public Map getArguments() { - return arguments; - } - - public void addArgument(String name, String value) { - this.arguments.put(name, value); - } - - @Override - public String toString() { - return String.format("%s with arguments %s", module, arguments); - } -} diff --git a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleLauncher.java b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleLauncher.java deleted file mode 100644 index ec2d3f6c9..000000000 --- a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleLauncher.java +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright 2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.stream.module.launcher; - -import java.io.IOException; -import java.lang.reflect.Method; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.boot.loader.archive.Archive; -import org.springframework.boot.loader.archive.JarFileArchive; -import org.springframework.cloud.stream.module.resolver.Coordinates; -import org.springframework.cloud.stream.module.resolver.ModuleResolver; -import org.springframework.cloud.stream.module.utils.ClassloaderUtils; -import org.springframework.core.io.Resource; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringUtils; - -/** - * A component that launches one or more modules, delegating their resolution to an - * underlying {@link ModuleResolver}. - * - * @author Mark Fisher - * @author Ilayaperumal Gopinathan - * @author Marius Bogoevici - * @author Eric Bottard - */ -public class ModuleLauncher { - - public static final String AGGREGATE_APPLICATION_CLASS = "org.springframework.cloud.stream.aggregate.AggregateApplication"; - - public static final String AGGREGATE_APPLICATION_RUN_METHOD = "run"; - - public static final String MODULE_AGGREGATOR_RUNNER_THREAD_NAME = "module-aggregator-runner"; - - private static Log log = LogFactory.getLog(ModuleLauncher.class); - - private static final String DEFAULT_EXTENSION = "jar"; - - private static final String DEFAULT_CLASSIFIER = ""; - - private static final Pattern COORDINATES_PATTERN = - Pattern.compile("([^: ]+):([^: ]+)(:([^: ]*)(:([^: ]+))?)?:([^: ]+)"); - - private static final String INCLUDE_DEPENDENCIES_ARG = "includes"; - - private static final String EXCLUDE_DEPENDENCIES_ARG = "excludes"; - - private final ModuleResolver moduleResolver; - - /** - * Creates a module launcher using the provided module resolver - * @param moduleResolver the module resolver instance to use - */ - public ModuleLauncher(ModuleResolver moduleResolver) { - this.moduleResolver = moduleResolver; - } - - /** - * Launches one or more modules, with the corresponding arguments, if any. - * - * Modules must be passed in "natural" left to right order. - * - * The format of each module must conform to the Aether convention: - * <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version> - * - * @param moduleLaunchRequests a list of modules with their arguments - * @param aggregate whether the modules should be aggregated at launch - * @param aggregateArgs a list of arguments for the whole aggregate - */ - @SuppressWarnings("unchecked") - public void launch(List moduleLaunchRequests, boolean aggregate, Map aggregateArgs) { - if (moduleLaunchRequests.size() == 1 || !aggregate) { - launchIndividualModules(moduleLaunchRequests); - } - else { - launchAggregatedModules(moduleLaunchRequests, aggregateArgs != null ? aggregateArgs : Collections.EMPTY_MAP); - } - } - - public void launch(List moduleLaunchRequests, boolean aggregate) { - this.launch(moduleLaunchRequests, aggregate, Collections.emptyMap()); - } - - public void launch(List moduleLaunchRequests) { - this.launch(moduleLaunchRequests, false); - } - - /** - * Converts a set of semantic program arguments to "command line program arguments" that is, to the - * {@literal --foo=bar} form. - */ - private String[] toArgArray(Map args) { - if (args != null) { - String[] result = new String[args.size()]; - int i = 0; - for (Map.Entry kv : args.entrySet()) { - result[i++] = String.format("--%s=%s", kv.getKey(), kv.getValue()); - } - return result; - } - else { - return new String[0]; - } - } - - public void launchAggregatedModules(List moduleLaunchRequests, Map aggregateArgs) { - try { - List mainClassNames = new ArrayList<>(); - LinkedHashSet jarURLs = new LinkedHashSet<>(); - List seenArchives = new ArrayList<>(); - final List arguments = new ArrayList<>(); - final ClassLoader classLoader; - if (!(aggregateArgs.containsKey(EXCLUDE_DEPENDENCIES_ARG) || - aggregateArgs.containsKey(INCLUDE_DEPENDENCIES_ARG))) { - for (ModuleLaunchRequest moduleLaunchRequest : moduleLaunchRequests) { - Resource resource = resolveModule(moduleLaunchRequest.getModule()); - JarFileArchive jarFileArchive = new JarFileArchive(resource.getFile()); - jarURLs.add(jarFileArchive.getUrl()); - for (Archive archive : jarFileArchive.getNestedArchives(ArchiveMatchingEntryFilter.FILTER)) { - // avoid duplication based on unique JAR names - String urlAsString = archive.getUrl().toString(); - String jarNameWithExtension = urlAsString.substring(0, urlAsString.lastIndexOf("!/")); - String jarNameWithoutExtension = - jarNameWithExtension.substring(jarNameWithExtension.lastIndexOf("/") + 1); - if (!seenArchives.contains(jarNameWithoutExtension)) { - seenArchives.add(jarNameWithoutExtension); - jarURLs.add(archive.getUrl()); - } - } - mainClassNames.add(jarFileArchive.getMainClass()); - arguments.add(toArgArray(moduleLaunchRequest.getArguments())); - } - classLoader = ClassloaderUtils.createModuleClassloader(jarURLs.toArray(new URL[jarURLs.size()])); - } else { - // First, resolve modules and extract main classes - while slightly less efficient than just - // doing the same processing after resolution, this ensures that module artifacts are processed - // correctly for extracting their main class names. It is not possible in the general case to - // identify, after resolution, whether a resource represents a module artifact which was part of the - // original request or not. We will include the first module as root and the next as direct dependencies - Coordinates root = null; - ArrayList includeCoordinates = new ArrayList<>(); - for (ModuleLaunchRequest moduleLaunchRequest : moduleLaunchRequests) { - Coordinates moduleCoordinates - = toCoordinates(moduleLaunchRequest.getModule()); - if (root == null) { - root = moduleCoordinates; - } - else { - includeCoordinates.add(toCoordinates(moduleLaunchRequest.getModule())); - } - Resource moduleResource = resolveModule(moduleLaunchRequest.getModule()); - JarFileArchive moduleArchive = new JarFileArchive(moduleResource.getFile()); - mainClassNames.add(moduleArchive.getMainClass()); - arguments.add(toArgArray(moduleLaunchRequest.getArguments())); - } - for (String include : - StringUtils.commaDelimitedListToStringArray(aggregateArgs.get(INCLUDE_DEPENDENCIES_ARG))) { - includeCoordinates.add(toCoordinates(include)); - } - // Resolve all artifacts - since modules have been specified as direct dependencies, they will take - // precedence in the resolution order, ensuring that the already resolved artifacts will be returned as - // part of the response. - Resource[] libraries = moduleResolver.resolve(root, - includeCoordinates.toArray(new Coordinates[includeCoordinates.size()]), - StringUtils.commaDelimitedListToStringArray(aggregateArgs.get(EXCLUDE_DEPENDENCIES_ARG))); - for (Resource library : libraries) { - jarURLs.add(library.getURL()); - } - classLoader = new URLClassLoader(jarURLs.toArray(new URL[jarURLs.size()])); - } - - final List> mainClasses = new ArrayList<>(); - for (String mainClass : mainClassNames) { - mainClasses.add(ClassUtils.forName(mainClass, classLoader)); - } - Runnable moduleAggregatorRunner = new ModuleAggregatorRunner(classLoader, mainClasses, - toArgArray(aggregateArgs), arguments); - Thread moduleAggregatorRunnerThread = new Thread(moduleAggregatorRunner); - moduleAggregatorRunnerThread.setContextClassLoader(classLoader); - moduleAggregatorRunnerThread.setName(MODULE_AGGREGATOR_RUNNER_THREAD_NAME); - moduleAggregatorRunnerThread.start(); - } catch (Exception e) { - throw new RuntimeException("failed to start aggregated modules: " + - StringUtils.collectionToCommaDelimitedString(moduleLaunchRequests), e); - } - } - - public void launchIndividualModules(List moduleLaunchRequests) { - List reversed = new ArrayList<>(moduleLaunchRequests); - Collections.reverse(reversed); - for (ModuleLaunchRequest moduleLaunchRequest : reversed) { - String module = moduleLaunchRequest.getModule(); - Map arguments = moduleLaunchRequest.getArguments(); - if (arguments.containsKey(INCLUDE_DEPENDENCIES_ARG) || arguments.containsKey(EXCLUDE_DEPENDENCIES_ARG)) { - String includes = arguments.get(INCLUDE_DEPENDENCIES_ARG); - String excludes = arguments.get(EXCLUDE_DEPENDENCIES_ARG); - launchModuleWithDependencies(module, toArgArray(arguments), - StringUtils.commaDelimitedListToStringArray(includes), - StringUtils.commaDelimitedListToStringArray(excludes)); - } else { - launchModule(module, toArgArray(arguments)); - } - } - } - - private void launchModule(String module, String[] args) { - try { - Resource resource = resolveModule(module); - JarFileArchive jarFileArchive = new JarFileArchive(resource.getFile()); - ModuleJarLauncher jarLauncher = new ModuleJarLauncher(jarFileArchive); - jarLauncher.launch(args); - } - catch (IOException e) { - throw new RuntimeException("failed to launch module: " + module, e); - } - } - - private void launchModuleWithDependencies(String module, String[] args, String[] includes, String[] excludes) { - try { - Resource[] libraries = this.moduleResolver.resolve(toCoordinates(module), - toCoordinateArray(includes), excludes); - List archives = new ArrayList<>(); - for (Resource library : libraries) { - archives.add(new JarFileArchive(library.getFile())); - } - MultiArchiveLauncher jarLauncher = new MultiArchiveLauncher(archives); - jarLauncher.launch(args); - } - catch (IOException e) { - throw new RuntimeException("failed to launch module: " + module, e); - } - } - - private Resource resolveModule(String moduleCoordinates) { - Coordinates coordinates = toCoordinates(moduleCoordinates); - return this.moduleResolver.resolve(coordinates); - } - - private Coordinates toCoordinates(String coordinates) { - Matcher matcher = COORDINATES_PATTERN.matcher(coordinates); - Assert.isTrue(matcher.matches(), "Bad artifact coordinates " + coordinates - + ", expected format is :[:[:]]:"); - String groupId = matcher.group(1); - String artifactId = matcher.group(2); - String extension = StringUtils.hasLength(matcher.group(4)) ? matcher.group(4) : DEFAULT_EXTENSION; - String classifier = StringUtils.hasLength(matcher.group(6)) ? matcher.group(6) : DEFAULT_CLASSIFIER; - String version = matcher.group(7); - return new Coordinates(groupId, artifactId, extension, classifier, version); - } - - private Coordinates[] toCoordinateArray(String[] coordinateList) { - List result = new ArrayList<>(); - for (String coordinates : coordinateList) { - result.add(toCoordinates(coordinates)); - } - return result.toArray(new Coordinates[result.size()]); - } - - private class ModuleAggregatorRunner implements Runnable { - - private final ClassLoader classLoader; - - private final String[] parentArgs; - - private final List> mainClasses; - - private final List arguments; - - public ModuleAggregatorRunner(ClassLoader classLoader, List> mainClasses, String[] parentArgs, - List moduleArguments) { - this.classLoader = classLoader; - this.parentArgs = parentArgs; - this.mainClasses = mainClasses; - this.arguments = moduleArguments; - } - - @Override - public void run() { - try { - // we expect the class and method to be found on the module classpath - Class moduleAggregatorClass = ClassUtils.forName(AGGREGATE_APPLICATION_CLASS, classLoader); - Method aggregateMethod = ReflectionUtils.findMethod(moduleAggregatorClass, - AGGREGATE_APPLICATION_RUN_METHOD, Class[].class, String[].class, String[][].class); - aggregateMethod.invoke(null, - mainClasses.toArray(new Class[mainClasses.size()]), - parentArgs, arguments.toArray(new String[][] {})); - } catch (Exception e) { - log.error("failed to launch aggregated modules :" - + StringUtils.collectionToCommaDelimitedString(mainClasses), e); - throw new RuntimeException(e); - } - } - } -} diff --git a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleLauncherApplication.java b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleLauncherApplication.java deleted file mode 100644 index ab471a901..000000000 --- a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleLauncherApplication.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2015-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.stream.module.launcher; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** - * Bootstrap for launching one or more modules, provided via the "modules" system property - * or "MODULES" environment variable as a comma-delimited list, with the arguments - * provided at launch. - * - * @see ModuleLauncherProperties for module and argument structure and - * format - * - * @author Marius Bogoevici - * @author Ilayaperumal Gopinathan - */ -@SpringBootApplication -public class ModuleLauncherApplication { - - public static void main(String[] args) throws Exception { - List argsList = new ArrayList<>(Arrays.asList(args)); - if (!argsList.contains("spring.jmx.default-domain")) { - argsList.add("--spring.jmx.default-domain=module-launcher"); - } - SpringApplication.run(ModuleLauncherApplication.class, argsList.toArray(new String[0])); - } -} diff --git a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleLauncherConfiguration.java b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleLauncherConfiguration.java deleted file mode 100644 index 370ca3120..000000000 --- a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleLauncherConfiguration.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.stream.module.launcher; - -import org.springframework.cloud.stream.module.resolver.ModuleResolver; -import org.springframework.cloud.stream.module.resolver.ModuleResolverConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; - -/** - * Configuration class that has the beans required for module launcher. - * - * @author Marius Bogoevici - * @author Ilayaperumal Gopinathan - * @author Eric Bottard - */ -@Configuration -@Import(ModuleResolverConfiguration.class) -public class ModuleLauncherConfiguration { - - @Bean - public ModuleLauncher moduleLauncher(ModuleResolver moduleResolver) { - return new ModuleLauncher(moduleResolver); - } - -} diff --git a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleLauncherProperties.java b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleLauncherProperties.java deleted file mode 100644 index ba7a3ce2d..000000000 --- a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleLauncherProperties.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.stream.module.launcher; - -import java.util.HashMap; -import java.util.Map; - -import org.hibernate.validator.constraints.NotEmpty; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.core.env.Environment; - -/** - * Configuration properties for {@link ModuleLauncher}. - * - *

Expects the following keys (resolved from the {@link Environment}, so this could take many forms _ System - * properties, environment variables, program arguments, etc. _):

    - *
  • {@literal modules = }: an ordered list of maven coordinates of modules to launch
  • - *
  • {@literal args[][] = }: key/value pairs that will become module arguments, - * where {@literal } is the 0-based index of the module in the list above and '*' can be used for passing - * arguments to all modules
  • - *
  • {@literal aggregate = true | false}
  • : whether multiple modules launched together should be aggregated, - * case in which they will be launched as a single individual unit, and {@literal args['aggregate'][] = } - * can be used for passing arguments to the aggregate; - *
- * - * As an example, this is how one would launch the {@literal time --fixedDelay=4 | log} canonical example: - *
- *     modules = org.springframework.cloud.modules:time-source:1.0.0-SNAPSHOT,org.springframework.cloud.modules:log-sink:1.0.0-SNAPSHOT
- *     args.0.fixedDelay=4
- * 
- * - * And this is how one would launch the {@literal time --fixedDelay=4 | log} example as an aggregate: - *
- *     modules = org.springframework.cloud.modules:time-source:1.0.0-SNAPSHOT,org.springframework.cloud.modules:log-sink:1.0.0-SNAPSHOT
- *     args.0.fixedDelay=4
- *     aggregate=true
- * 
- *

- * - * @author Ilayaperumal Gopinathan - * @author Marius Bogoevici - * @author Eric Bottard - */ -@ConfigurationProperties -public class ModuleLauncherProperties { - - /** - * True if aggregating multiple modules when launched together - */ - private boolean aggregate; - - /** - * File path to a locally available maven repository, where modules will be downloaded. - */ - private String[] modules; - - /** - * Map of arguments, keyed by the 0-based index in the {@link #modules array}, allowing for an additional generic - * key for global arguments. - */ - private Map> args = new HashMap<>(); - - public boolean isAggregate() { - return aggregate; - } - - public void setAggregate(boolean aggregate) { - this.aggregate = aggregate; - } - - public void setModules(String[] modules) { - this.modules = modules; - } - - @NotEmpty(message = "A list of modules must be specified.") - public String[] getModules() { - return modules; - } - - public void setArgs(Map> args) { - this.args = args; - } - - public Map> getArgs() { - return args; - } -} diff --git a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleLauncherRunner.java b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleLauncherRunner.java deleted file mode 100644 index 7463ec1a5..000000000 --- a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleLauncherRunner.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.stream.module.launcher; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.ApplicationRunner; -import org.springframework.boot.CommandLineRunner; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.stereotype.Component; - -/** - * Spring boot {@link ApplicationRunner} that triggers {@link ModuleLauncher} to launch the modules. - * - * @author Marius Bogoevici - * @author Ilayaperumal Gopinathan - * @author Eric Bottard - */ -@Component -@EnableConfigurationProperties(ModuleLauncherProperties.class) -public class ModuleLauncherRunner implements CommandLineRunner { - - private final static Log log = LogFactory.getLog(ModuleLauncherRunner.class); - - private final static String GLOBAL_ARGS_KEY = "*"; - - private final static String AGGREGATE_ARGS_KEY = "aggregate"; - - @Autowired - private ModuleLauncherProperties moduleLauncherProperties; - - @Autowired - private ModuleLauncher moduleLauncher; - - @Override - public void run(String... args) throws Exception { - List launchRequests = generateModuleLaunchRequests(); - if (log.isInfoEnabled()) { - StringBuilder sb = new StringBuilder("Launching\n"); - for (ModuleLaunchRequest moduleLaunchRequest : launchRequests) { - sb.append('\t').append(moduleLaunchRequest).append('\n'); - } - log.info(sb.toString()); - } - this.moduleLauncher.launch(launchRequests, - moduleLauncherProperties.isAggregate(), - moduleLauncherProperties.isAggregate() ? - moduleLauncherProperties.getArgs().get(AGGREGATE_ARGS_KEY) : - Collections.emptyMap()); - } - - private List generateModuleLaunchRequests() { - List requests = new ArrayList<>(); - String[] modules = this.moduleLauncherProperties.getModules(); - Map> arguments = this.moduleLauncherProperties.getArgs(); - for (int i = 0; i < modules.length; i++) { - // merge the global arguments with the module specific arguments - Map moduleArguments = new HashMap<>(); - if (arguments != null) { - // by inserting the global args first and the module specific args next - // we ensure that the latter have precedence - Map globalArgs = arguments.get(GLOBAL_ARGS_KEY); - if (globalArgs != null) { - moduleArguments.putAll(globalArgs); - } - Map moduleSpecificArgs = arguments.get(Integer.toString(i)); - if (moduleSpecificArgs != null) { - moduleArguments.putAll(moduleSpecificArgs); - } - } - ModuleLaunchRequest moduleLaunchRequest = new ModuleLaunchRequest(modules[i], moduleArguments); - requests.add(moduleLaunchRequest); - } - return requests; - } -} diff --git a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/MultiArchiveLauncher.java b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/MultiArchiveLauncher.java deleted file mode 100644 index 0d798a7f3..000000000 --- a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/MultiArchiveLauncher.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.stream.module.launcher; - -import java.lang.reflect.Method; -import java.net.URL; -import java.util.List; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.boot.loader.Launcher; -import org.springframework.boot.loader.archive.Archive; -import org.springframework.cloud.stream.module.utils.ClassloaderUtils; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.ReflectionUtils; - -/** - * A {@link Launcher} for multiple independent JAR archives (which aren't nested in an uber jar). This class - * supports adding includes and excludes to a module's classpath. - * - * @author Marius Bogoevici - * @author Eric Bottard - */ -public class MultiArchiveLauncher extends Launcher { - - private static Log log = LogFactory.getLog(MultiArchiveLauncher.class); - - private final List archives; - - /** - * A list of archives, the first of which is expected to be a Spring boot uberJar - * - * @param archives - */ - public MultiArchiveLauncher(List archives) { - Assert.notEmpty(archives, "A list of archives must be provided"); - this.archives = archives; - } - - @Override - protected String getMainClass() throws Exception { - return archives.get(0).getManifest().getMainAttributes().getValue("Start-Class"); - } - - @Override - protected List getClassPathArchives() throws Exception { - return archives; - } - - @Override - public void launch(String[] args) { - super.launch(args); - } - - @Override - protected void launch(String[] args, String mainClass, ClassLoader classLoader) - throws Exception { - if (log.isDebugEnabled()) { - for (Archive archive : archives) { - log.debug("Launching with: " + archive.getUrl().toString()); - } - } - if (ClassUtils.isPresent( - "org.apache.catalina.webresources.TomcatURLStreamHandlerFactory", - classLoader)) { - // Ensure the method is invoked on a class that is loaded by the provided - // class loader (not the current context class loader): - Method method = ReflectionUtils - .findMethod( - classLoader - .loadClass("org.apache.catalina.webresources.TomcatURLStreamHandlerFactory"), - "disable"); - ReflectionUtils.invokeMethod(method, null); - } - super.launch(args, mainClass, classLoader); - } - - @Override - protected ClassLoader createClassLoader(URL[] urls) throws Exception { - return ClassloaderUtils.createModuleClassloader(urls); - } - -} diff --git a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/AetherModuleResolver.java b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/AetherModuleResolver.java deleted file mode 100644 index e0ea3f498..000000000 --- a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/AetherModuleResolver.java +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright 2015-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.stream.module.resolver; - -import java.io.File; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.maven.repository.internal.MavenRepositorySystemUtils; -import org.eclipse.aether.DefaultRepositorySystemSession; -import org.eclipse.aether.RepositorySystem; -import org.eclipse.aether.RepositorySystemSession; -import org.eclipse.aether.artifact.Artifact; -import org.eclipse.aether.artifact.DefaultArtifact; -import org.eclipse.aether.collection.CollectRequest; -import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory; -import org.eclipse.aether.graph.Dependency; -import org.eclipse.aether.impl.DefaultServiceLocator; -import org.eclipse.aether.repository.Authentication; -import org.eclipse.aether.repository.AuthenticationContext; -import org.eclipse.aether.repository.AuthenticationDigest; -import org.eclipse.aether.repository.LocalRepository; -import org.eclipse.aether.repository.Proxy; -import org.eclipse.aether.repository.RemoteRepository; -import org.eclipse.aether.resolution.ArtifactRequest; -import org.eclipse.aether.resolution.ArtifactResolutionException; -import org.eclipse.aether.resolution.ArtifactResult; -import org.eclipse.aether.resolution.DependencyRequest; -import org.eclipse.aether.resolution.DependencyResolutionException; -import org.eclipse.aether.resolution.DependencyResult; -import org.eclipse.aether.spi.connector.RepositoryConnectorFactory; -import org.eclipse.aether.spi.connector.transport.TransporterFactory; -import org.eclipse.aether.transport.file.FileTransporterFactory; -import org.eclipse.aether.transport.http.HttpTransporterFactory; -import org.eclipse.aether.util.artifact.JavaScopes; -import org.eclipse.aether.util.repository.DefaultProxySelector; - -import org.springframework.core.io.FileSystemResource; -import org.springframework.core.io.Resource; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import org.springframework.util.ObjectUtils; -import org.springframework.util.StringUtils; - -/** - * An implementation of ModuleResolver using remoteRepositories; - - private final RepositorySystem repositorySystem; - - private volatile boolean offline = false; - - private final AetherProxyProperties proxyProperties; - - private Authentication authentication; - - /** - * Create an instance specifying the locations of the local and remote repositories. - * @param localRepository the root path of the local maven repository - * @param remoteRepositories a Map containing pairs of (repository ID,repository URL). This - * may be null or empty if the local repository is off line. - * @param proxyProperties the proxy properties for the maven proxy settings. - */ - public AetherModuleResolver(File localRepository, Map remoteRepositories, - final AetherProxyProperties proxyProperties) { - Assert.notNull(localRepository, "Local repository path cannot be null"); - if (log.isDebugEnabled()) { - log.debug("Local repository: " + localRepository); - if (!CollectionUtils.isEmpty(remoteRepositories)) { - // just listing the values, ids are simply informative - log.debug("Remote repositories: " + StringUtils.collectionToCommaDelimitedString(remoteRepositories.values())); - } - } - this.proxyProperties = proxyProperties; - if (isProxyEnabled() && proxyHasCredentials()) { - this.authentication = new Authentication() { - @Override - public void fill(AuthenticationContext context, String key, Map data) { - context.put(context.USERNAME, proxyProperties.getAuth().getUsername()); - context.put(context.PASSWORD, proxyProperties.getAuth().getPassword()); - } - - @Override - public void digest(AuthenticationDigest digest) { - digest.update(AuthenticationContext.USERNAME, proxyProperties.getAuth().getUsername(), - AuthenticationContext.PASSWORD, proxyProperties.getAuth().getPassword()); - } - }; - } - if (!localRepository.exists()) { - Assert.isTrue(localRepository.mkdirs(), - "Unable to create directory for local repository: " + localRepository); - } - this.localRepository = localRepository; - this.remoteRepositories = new LinkedList<>(); - if (!CollectionUtils.isEmpty(remoteRepositories)) { - for (Map.Entry remoteRepo : remoteRepositories.entrySet()) { - RemoteRepository.Builder remoteRepositoryBuilder = new RemoteRepository.Builder(remoteRepo.getKey(), - DEFAULT_CONTENT_TYPE, remoteRepo.getValue()); - if (this.authentication != null) { - //todo: Set direct authentication for the remote repositories - remoteRepositoryBuilder.setProxy(new Proxy(proxyProperties.getProtocol(), proxyProperties.getHost(), - proxyProperties.getPort(), authentication)); - } - this.remoteRepositories.add(remoteRepositoryBuilder.build()); - } - } - repositorySystem = newRepositorySystem(); - } - - /** - * Check if the proxy settings are provided. - * - * @return boolean true if the proxy settings are provided. - */ - private boolean isProxyEnabled() { - return (this.proxyProperties != null && this.proxyProperties.getHost() != null && proxyProperties.getPort() > 0); - } - - /** - * Check if the proxy setting has username/password set. - * - * @return boolean true if both the username/password are set - */ - private boolean proxyHasCredentials() { - return (this.proxyProperties != null && this.proxyProperties.getAuth() != null && - this.proxyProperties.getAuth().getUsername() != null && this.proxyProperties.getAuth().getPassword() != null); - } - - public void setOffline(boolean offline) { - this.offline = offline; - } - - /** - * Resolve an artifact and return its location in the local repository. Aether performs the normal - * Maven resolution process ensuring that the latest update is cached to the local repository. - * @param coordinates the Maven coordinates of the artifact - * @return a {@link FileSystemResource} representing the resolved artifact in the local repository - * @throws RuntimeException if the artifact does not exist or the resolution fails - */ - @Override - public Resource resolve(Coordinates coordinates) { - return this.resolve(coordinates, null, null)[0]; - } - - /* - * Create a session to manage remote and local synchronization. - */ - private DefaultRepositorySystemSession newRepositorySystemSession(RepositorySystem system, String localRepoPath) { - DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession(); - LocalRepository localRepo = new LocalRepository(localRepoPath); - session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo)); - session.setOffline(this.offline); - if (isProxyEnabled()) { - DefaultProxySelector proxySelector = new DefaultProxySelector(); - Proxy proxy = new Proxy(proxyProperties.getProtocol(), proxyProperties.getHost(), proxyProperties.getPort(), - authentication); - proxySelector.add(proxy, proxyProperties.getNonProxyHosts()); - session.setProxySelector(proxySelector); - } - return session; - } - - /* - * Aether's components implement {@link org.eclipse.aether.spi.locator.Service} to ease manual wiring. - * Using the prepopulated {@link DefaultServiceLocator}, we need to register the repository connector - * and transporter factories - */ - private RepositorySystem newRepositorySystem() { - DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator(); - locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class); - locator.addService(TransporterFactory.class, FileTransporterFactory.class); - locator.addService(TransporterFactory.class, HttpTransporterFactory.class); - locator.setErrorHandler(new DefaultServiceLocator.ErrorHandler() { - @Override - public void serviceCreationFailed(Class type, Class impl, Throwable exception) { - throw new RuntimeException(exception); - } - }); - return locator.getService(RepositorySystem.class); - } - - - /** - * Resolve a set of artifacts based on their coordinates, including their dependencies, and return the locations of - * the transitive set in the local repository. Aether performs the normal Maven resolution process ensuring that the - * latest update is cached to the local repository. A number of additional includes and excludes can be specified, - * allowing to override the transitive dependencies of the original set. Includes and their transitive dependencies - * will always - * - * @param root the Maven coordinates of the artifacts - * @return a {@link FileSystemResource} representing the resolved artifact in the local repository - * @throws RuntimeException if the artifact does not exist or the resolution fails - */ - @Override - public Resource[] resolve(Coordinates root, Coordinates[] includes, String[] excludePatterns) { - Assert.notNull(root, "Root cannot be null"); - validateCoordinates(root); - if (!ObjectUtils.isEmpty(includes)) { - for (Coordinates include : includes) { - Assert.notNull(include, "Includes cannot be null"); - validateCoordinates(include); - } - } - List result = new ArrayList<>(); - Artifact rootArtifact = toArtifact(root); - RepositorySystemSession session = newRepositorySystemSession(repositorySystem, - localRepository.getAbsolutePath()); - if (ObjectUtils.isEmpty(includes) && ObjectUtils.isEmpty(excludePatterns)) { - ArtifactResult resolvedArtifact; - try { - resolvedArtifact = repositorySystem.resolveArtifact(session, - new ArtifactRequest(rootArtifact, remoteRepositories, JavaScopes.RUNTIME)); - } - catch (ArtifactResolutionException e) { - throw new RuntimeException(e); - } - result.add(toResource(resolvedArtifact)); - } - else { - try { - CollectRequest collectRequest = new CollectRequest(); - collectRequest.setRepositories(remoteRepositories); - collectRequest.setRoot(new Dependency(rootArtifact, JavaScopes.RUNTIME)); - Artifact[] includeArtifacts = new Artifact[!ObjectUtils.isEmpty(includes) ? includes.length : 0]; - int i = 0; - for (Coordinates include : includes) { - Artifact includedArtifact = toArtifact(include); - collectRequest.addDependency(new Dependency(includedArtifact, JavaScopes.RUNTIME)); - includeArtifacts[i++] = includedArtifact; - } - DependencyResult dependencyResult = - repositorySystem.resolveDependencies(session, - new DependencyRequest(collectRequest, - new ModuleDependencyFilter(includeArtifacts, excludePatterns))); - for (ArtifactResult artifactResult : dependencyResult.getArtifactResults()) { - // we are only interested in the jars - if ("jar".equalsIgnoreCase(artifactResult.getArtifact().getExtension())) { - result.add(toResource(artifactResult)); - } - } - } - catch (DependencyResolutionException e) { - throw new RuntimeException(e); - } - } - return result.toArray(new Resource[result.size()]); - } - - private void validateCoordinates(Coordinates coordinates) { - Assert.hasText(coordinates.getGroupId(), "'groupId' cannot be blank."); - Assert.hasText(coordinates.getArtifactId(), "'artifactId' cannot be blank."); - Assert.hasText(coordinates.getExtension(), "'extension' cannot be blank."); - Assert.hasText(coordinates.getVersion(), "'version' cannot be blank."); - } - - public FileSystemResource toResource(ArtifactResult resolvedArtifact) { - return new FileSystemResource(resolvedArtifact.getArtifact().getFile()); - } - - private Artifact toArtifact(Coordinates root) { - return new DefaultArtifact(root.getGroupId(), - root.getArtifactId(), - root.getClassifier() != null ? root.getClassifier() : "", - root.getExtension(), - root.getVersion()); - } -} diff --git a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/AetherProxyProperties.java b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/AetherProxyProperties.java deleted file mode 100644 index df4086d86..000000000 --- a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/AetherProxyProperties.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cloud.stream.module.resolver; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -/** - * Proxy properties for the Aether Module Resolver. - * - * @author Ilayaperumal Gopinathan - */ -@ConfigurationProperties(prefix = "aether.proxy") -public class AetherProxyProperties { - /** - * Protocol to use for proxy settings. - */ - private String protocol = "http"; - - /** - * Host for the proxy. - */ - private String host; - - /** - * Port for the proxy. - */ - private int port; - - /** - * List of non proxy hosts. - */ - private String nonProxyHosts; - - private Authentication auth; - - public String getProtocol() { - return this.protocol; - } - - public void setProtocol(String protocol) { - this.protocol = protocol; - } - - public String getHost() { - return this.host; - } - - public void setHost(String host) { - this.host = host; - } - - public int getPort() { - return this.port; - } - - public void setPort(int port) { - this.port = port; - } - - public String getNonProxyHosts() { - return this.nonProxyHosts; - } - - public void setNonProxyHosts(String nonProxyHosts) { - this.nonProxyHosts = nonProxyHosts; - } - - public Authentication getAuth() { - return this.auth; - } - - public void setAuth(Authentication auth) { - this.auth = auth; - } - - public static class Authentication { - - /** - * Username for the proxy. - */ - private String username; - - /** - * Password for the proxy. - */ - private String password; - - public String getUsername() { - return this.username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return this.password; - } - - public void setPassword(String password) { - this.password = password; - } - - } -} diff --git a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/Coordinates.java b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/Coordinates.java deleted file mode 100644 index e3fdc234f..000000000 --- a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/Coordinates.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.stream.module.resolver; - -/** - * Encapsulates Maven coordinates. - * - * @author Marius Bogoevici - * @author Eric Bottard - */ -public class Coordinates { - - private final String groupId; - - private final String artifactId; - - private final String extension; - - private String classifier; - - private final String version; - - /** - * @param groupId the groupId - * @param artifactId the artifactId - * @param extension the file extension - * @param classifier classifier - * @param version the version - */ - public Coordinates(String groupId, String artifactId, String extension, String classifier, String version) { - this.groupId = groupId; - this.artifactId = artifactId; - this.extension = extension; - this.classifier = classifier; - this.version = version; - } - - public String getGroupId() { - return groupId; - } - - public String getArtifactId() { - return artifactId; - } - - public String getExtension() { - return extension; - } - - public String getClassifier() { - return classifier; - } - - public String getVersion() { - return version; - } -} diff --git a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/ModuleDependencyFilter.java b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/ModuleDependencyFilter.java deleted file mode 100644 index 20f67e159..000000000 --- a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/ModuleDependencyFilter.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.stream.module.resolver; - -import java.util.List; - -import org.eclipse.aether.artifact.Artifact; -import org.eclipse.aether.graph.DependencyFilter; -import org.eclipse.aether.graph.DependencyNode; -import org.eclipse.aether.util.filter.PatternExclusionsDependencyFilter; - -import org.springframework.util.ObjectUtils; - -/** - * A {@link DependencyFilter} that uses a list of explicit includes and pattern-based excludes. Any items that match - * the exclusion pattern will be rejected, unless they are accepted explicitly - * - * @author Marius Bogoevici - */ -public class ModuleDependencyFilter implements DependencyFilter { - - private final PatternExclusionsDependencyFilter patternExclusionsDependencyFilter; - - private final Artifact[] includes; - - private boolean acceptOptional = false; - - public ModuleDependencyFilter(Artifact[] includes, String... excludes) { - this.patternExclusionsDependencyFilter = new PatternExclusionsDependencyFilter(excludes); - this.includes = includes != null ? includes : new Artifact[0]; - } - - public void setAcceptOptional(boolean acceptOptional) { - this.acceptOptional = acceptOptional; - } - - @Override - public boolean accept(DependencyNode node, List parents) { - // optional nodes are rejected conditionally - // nodes included explicitly are always accepted - return (acceptOptional || !node.getDependency().isOptional()) - && (isIncludedDirectly(node) || patternExclusionsDependencyFilter.accept(node, parents)); - } - - private boolean isIncludedDirectly(DependencyNode node) { - if (node.getArtifact() != null) { - for (Artifact include : includes) { - Artifact nodeArtifact = node.getArtifact(); - // we check if this was a specifically included artifact by checking its group, artifactId, extension and - // classifier. The version is left out in the case when resolution produces a different artifact version - // (there cannot be two artifacts with the same group, artifactId, extension and classifier but different - // version in the resolved group) - if (ObjectUtils.nullSafeEquals(include.getGroupId(), nodeArtifact.getGroupId()) - && ObjectUtils.nullSafeEquals(include.getArtifactId(), nodeArtifact.getArtifactId()) - && ObjectUtils.nullSafeEquals(include.getClassifier(), nodeArtifact.getClassifier()) - && ObjectUtils.nullSafeEquals(include.getExtension(), nodeArtifact.getExtension())) { - return true; - } - } - return false; - } - return true; - } -} diff --git a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/ModuleResolver.java b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/ModuleResolver.java deleted file mode 100644 index d175b2762..000000000 --- a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/ModuleResolver.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.stream.module.resolver; - -import org.springframework.core.io.Resource; - -/** - * Interface used to return a {@link Resource} that provides access to a module - * uber-jar based on its Maven coordinates. - * - * @author David Turanski - * @author Marius Bogoevici - * @author Eric Bottard - */ -public interface ModuleResolver { - - /** - * Retrieve a resource given its coordinates. - * - * @param coordinates the coordinates of a resource - * @return the resource - */ - Resource resolve(Coordinates coordinates); - - /** - * Retrieve a set of resources given their coordinates, along with additional dependencies. - * Exclusion rules patterns (conforming to {@link org.eclipse.aether.util.filter.PatternExclusionsDependencyFilter} - * can be provided as well. - * - * @param root the coordinates of the main resource - * @param includes a list of coordinates to include along the main resource - * @param excludePatterns a list of exclusion patterns - * @see org.eclipse.aether.util.filter.PatternExclusionsDependencyFilter - * @return the main resource and the additional dependencies - */ - Resource[] resolve(Coordinates root, Coordinates[] includes, String[] excludePatterns); - -} diff --git a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/ModuleResolverConfiguration.java b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/ModuleResolverConfiguration.java deleted file mode 100644 index c1a951a1d..000000000 --- a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/ModuleResolverConfiguration.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2015-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.stream.module.resolver; - -import java.util.HashMap; -import java.util.Map; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; - -/** - * Sets up the default Aether-based module resolver, unless overridden. - * - * @author Eric Bottard - * @author Ilayaperumal Gopinathan - */ -@EnableConfigurationProperties({ModuleResolverProperties.class, AetherProxyProperties.class}) -public class ModuleResolverConfiguration { - - @Autowired - private ModuleResolverProperties properties; - - @Autowired - private AetherProxyProperties proxyProperties; - - @Bean - @ConditionalOnMissingBean(ModuleResolver.class) - public ModuleResolver moduleResolver() { - int i = 1; - Map repositoriesMap = new HashMap<>(); - for (String repository : properties.getRemoteRepositories()) { - repositoriesMap.put("repository " + i++, repository); - } - AetherModuleResolver aetherModuleResolver = new AetherModuleResolver(properties.getLocalRepository(), - repositoriesMap, proxyProperties); - aetherModuleResolver.setOffline(properties.isOffline()); - return aetherModuleResolver; - } -} diff --git a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/ModuleResolverProperties.java b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/ModuleResolverProperties.java deleted file mode 100644 index 9b09b6578..000000000 --- a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/ModuleResolverProperties.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.stream.module.resolver; - -import java.io.File; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -/** - * Configuration properties for {@link ModuleResolver}. - * - * @author Ilayaperumal Gopinathan - */ -@ConfigurationProperties -public class ModuleResolverProperties { - - /** - * File path to a locally available maven repository, where modules will be downloaded. - */ - private File localRepository = new File(System.getProperty("user.home") + File.separator + ".m2" + - File.separator + "repository"); - - /** - * Location of comma separated remote maven repositories from which modules will be downloaded, if not available locally. - */ - private String[] remoteRepositories = new String[]{"https://repo.spring.io/libs-snapshot"}; - - /** - * Whether the resolver should operate in offline mode. - */ - private boolean offline = false; - - public void setRemoteRepositories(String[] remoteRepositories) { - this.remoteRepositories = remoteRepositories; - } - - public String[] getRemoteRepositories() { - return remoteRepositories; - } - - public void setLocalRepository(File localRepository) { - this.localRepository = localRepository; - } - - public File getLocalRepository() { - return localRepository; - } - - public boolean isOffline() { - return offline; - } - - public void setOffline(boolean offline) { - this.offline = offline; - } -} diff --git a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/package-info.java b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/package-info.java deleted file mode 100644 index 0a457ab6b..000000000 --- a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/package-info.java +++ /dev/null @@ -1,5 +0,0 @@ -/** - * Root package of the ModuleResolver support. - */ - -package org.springframework.cloud.stream.module.resolver; diff --git a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/utils/ClassloaderUtils.java b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/utils/ClassloaderUtils.java deleted file mode 100644 index 3c540236f..000000000 --- a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/utils/ClassloaderUtils.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.stream.module.utils; - -import java.net.URL; -import java.net.URLClassLoader; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.boot.loader.LaunchedURLClassLoader; -import org.springframework.util.StringUtils; - -/** - * @author Marius Bogoevici - */ -public class ClassloaderUtils { - - private static final Log log = LogFactory.getLog(ClassloaderUtils.class); - - /** - * Creates a ClassLoader for the launched modules by merging the URLs supplied as argument with the URLs that - * make up the additional classpath of the launched JVM (retrieved from the application classloader), and - * setting the extension classloader of the JVM as parent, if accessible. - * - * @param urls a list of library URLs - * @return the resulting classloader - */ - public static ClassLoader createModuleClassloader(URL[] urls) { - ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); - if (log.isDebugEnabled()) { - log.debug("systemClassLoader is " + systemClassLoader); - } - if (systemClassLoader instanceof URLClassLoader) { - // add the URLs of the application classloader to the created classloader - // to compensate for LaunchedURLClassLoader not delegating to parent to retrieve resources - @SuppressWarnings("resource") - URLClassLoader systemUrlClassLoader = (URLClassLoader) systemClassLoader; - URL[] mergedUrls = new URL[urls.length + systemUrlClassLoader.getURLs().length]; - if (log.isDebugEnabled()) { - log.debug("Original URLs: " + - StringUtils.arrayToCommaDelimitedString(urls)); - log.debug("Java Classpath URLs: " + - StringUtils.arrayToCommaDelimitedString(systemUrlClassLoader.getURLs())); - } - System.arraycopy(urls, 0, mergedUrls, 0, urls.length); - System.arraycopy(systemUrlClassLoader.getURLs(), 0, mergedUrls, urls.length, - systemUrlClassLoader.getURLs().length); - // add the extension classloader as parent to the created context, if accessible - if (log.isDebugEnabled()) { - log.debug("Classloader URLs: " + StringUtils.arrayToCommaDelimitedString(mergedUrls)); - } - return new LaunchedURLClassLoader(mergedUrls, systemUrlClassLoader.getParent()); - } - return new LaunchedURLClassLoader(urls, systemClassLoader); - } -} diff --git a/spring-cloud-stream-module-launcher/src/test/java/org/springframework/cloud/stream/module/resolver/AetherModuleResolverTests.java b/spring-cloud-stream-module-launcher/src/test/java/org/springframework/cloud/stream/module/resolver/AetherModuleResolverTests.java deleted file mode 100644 index 5cb755e7a..000000000 --- a/spring-cloud-stream-module-launcher/src/test/java/org/springframework/cloud/stream/module/resolver/AetherModuleResolverTests.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.stream.module.resolver; - -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static org.hamcrest.Matchers.arrayContainingInAnyOrder; -import static org.hamcrest.Matchers.arrayWithSize; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.object.HasToString.hasToString; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import org.eclipse.aether.resolution.ArtifactResolutionException; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; - -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; -import org.springframework.util.SocketUtils; - -import com.github.tomakehurst.wiremock.junit.WireMockRule; - -/** - * @author David Turanski - */ -public class AetherModuleResolverTests { - - private int port = SocketUtils.findAvailableTcpPort(); - - @Rule - public WireMockRule wireMockRule = new WireMockRule(port); - - @Test - public void testResolveLocal() throws IOException { - ClassPathResource cpr = new ClassPathResource("local-repo"); - File localRepository = cpr.getFile(); - AetherModuleResolver defaultModuleResolver = new AetherModuleResolver(localRepository, null, null); - Resource resource = defaultModuleResolver.resolve(new Coordinates("foo.bar", "foo-bar", "jar", "", "1.0.0")); - assertTrue(resource.exists()); - assertEquals(resource.getFile().getName(), "foo-bar-1.0.0.jar"); - } - - @Test - public void testResolveLocalWithIncludes() throws IOException { - ClassPathResource cpr = new ClassPathResource("local-repo"); - File localRepository = cpr.getFile(); - AetherModuleResolver defaultModuleResolver = new AetherModuleResolver(localRepository, null, null); - Resource[] resources = defaultModuleResolver.resolve( - new Coordinates("foo.bar", "foo-bar", "jar", "", "1.0.0"), - new Coordinates[]{new Coordinates("qux.bar", "qux-bar", "jar", "", "1.0.0")},new String[]{}); - assertThat(resources, arrayWithSize(2)); - assertTrue(resources[0].exists()); - assertTrue(resources[1].exists()); - // the optional dependency 'foo.baz:foo-baz:1.0.0'of 'foo.bar:foo-bar' is not included - assertThat(resources, arrayContainingInAnyOrder( - hasToString(containsString("foo-bar-1.0.0.jar")), - hasToString(containsString("qux-bar-1.0.0.jar")))); - } - - @Test(expected = RuntimeException.class) - public void testResolveDoesNotExist() throws IOException { - ClassPathResource cpr = new ClassPathResource("local-repo"); - File localRepository = cpr.getFile(); - AetherModuleResolver defaultModuleResolver = new AetherModuleResolver(localRepository, null, null); - defaultModuleResolver.resolve(new Coordinates("niente", "nada", "jar", "", "zilch")); - } - - @Test - @Ignore - public void testResolveRemote() throws IOException { - File localRepository = Files.createTempDirectory(UUID.randomUUID().toString()).toFile(); - localRepository.deleteOnExit(); - Map remoteRepos = new HashMap<>(); - remoteRepos.put("modules", "http://repo.spring.io/libs-snapshot"); - AetherModuleResolver defaultModuleResolver = new AetherModuleResolver(localRepository, remoteRepos, null); - Resource resource = defaultModuleResolver.resolve( - new Coordinates("org.springframework.cloud.stream.module", "time-source", "jar", "exec", "1.0.0.BUILD-SNAPSHOT")); - assertTrue(resource.exists()); - assertEquals(resource.getFile().getName(), "time-source-1.0.0.BUILD-SNAPSHOT-exec.jar"); - } - - @Test - public void testResolveMockRemote() throws IOException { - File localRepository = Files.createTempDirectory(UUID.randomUUID().toString()).toFile(); - localRepository.deleteOnExit(); - ClassPathResource stubJarResource = new ClassPathResource("__files/foo.jar"); - String stubFileName = stubJarResource.getFile().getName(); - Map remoteRepos = new HashMap<>(); - remoteRepos.put("repo0", "http://localhost:" + port + "/repo0"); - remoteRepos.put("repo1", "http://localhost:" + port + "/repo1"); - stubFor(get(urlEqualTo("/repo1/org/bar/foo/1.0.0/foo-1.0.0.jar")) - .willReturn(aResponse() - .withStatus(200) - .withBodyFile(stubFileName))); - AetherModuleResolver defaultModuleResolver = new AetherModuleResolver(localRepository, remoteRepos, null); - Resource resource = defaultModuleResolver.resolve(new Coordinates("org.bar", "foo", "jar", "", "1.0.0")); - assertTrue(resource.exists()); - assertEquals(resource.getFile().getName(), "foo-1.0.0.jar"); - } - - @Test - public void testResolveOfflineWithMockRemote() throws IOException { - try { - File localRepository = Files.createTempDirectory(UUID.randomUUID().toString()).toFile(); - localRepository.deleteOnExit(); - ClassPathResource stubJarResource = new ClassPathResource("__files/foo.jar"); - String stubFileName = stubJarResource.getFile().getName(); - Map remoteRepos = new HashMap<>(); - remoteRepos.put("repo0", "http://localhost:" + port + "/repo0"); - remoteRepos.put("repo1", "http://localhost:" + port + "/repo1"); - stubFor(get(urlEqualTo("/repo1/org/bar/foo/1.0.0/foo-1.0.0.jar")) - .willReturn(aResponse() - .withStatus(200) - .withBodyFile(stubFileName))); - AetherModuleResolver defaultModuleResolver = new AetherModuleResolver(localRepository, remoteRepos, null); - defaultModuleResolver.setOffline(true); - defaultModuleResolver.resolve(new Coordinates("org.bar", "foo", "jar", "", "1.0.0")); - } catch (RuntimeException e) { - // remote resolution fails because the resolver is operating offline - assertThat(e.getCause(), instanceOf(ArtifactResolutionException.class)); - } - } -} diff --git a/spring-cloud-stream-module-launcher/src/test/resources/__files/foo.jar b/spring-cloud-stream-module-launcher/src/test/resources/__files/foo.jar deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-cloud-stream-module-launcher/src/test/resources/local-repo/foo/bar/foo-bar/1.0.0/foo-bar-1.0.0.jar b/spring-cloud-stream-module-launcher/src/test/resources/local-repo/foo/bar/foo-bar/1.0.0/foo-bar-1.0.0.jar deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-cloud-stream-module-launcher/src/test/resources/local-repo/foo/bar/foo-bar/1.0.0/foo-bar-1.0.0.pom b/spring-cloud-stream-module-launcher/src/test/resources/local-repo/foo/bar/foo-bar/1.0.0/foo-bar-1.0.0.pom deleted file mode 100644 index 1210081ad..000000000 --- a/spring-cloud-stream-module-launcher/src/test/resources/local-repo/foo/bar/foo-bar/1.0.0/foo-bar-1.0.0.pom +++ /dev/null @@ -1,20 +0,0 @@ - - - 4.0.0 - - foo.bar - foo-bar - 1.0.0 - - jar - foo-bar - - - - foo.baz - foo-baz - 1.0.0 - true - - - diff --git a/spring-cloud-stream-module-launcher/src/test/resources/local-repo/foo/baz/foo-baz/1.0.0/foo-baz-1.0.0.jar b/spring-cloud-stream-module-launcher/src/test/resources/local-repo/foo/baz/foo-baz/1.0.0/foo-baz-1.0.0.jar deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-cloud-stream-module-launcher/src/test/resources/local-repo/foo/baz/foo-baz/1.0.0/foo-baz-1.0.0.pom b/spring-cloud-stream-module-launcher/src/test/resources/local-repo/foo/baz/foo-baz/1.0.0/foo-baz-1.0.0.pom deleted file mode 100644 index 29b3ddd84..000000000 --- a/spring-cloud-stream-module-launcher/src/test/resources/local-repo/foo/baz/foo-baz/1.0.0/foo-baz-1.0.0.pom +++ /dev/null @@ -1,12 +0,0 @@ - - - 4.0.0 - - foo.baz - foo-baz - 1.0.0 - - jar - foo-baz - - diff --git a/spring-cloud-stream-module-launcher/src/test/resources/local-repo/qux/bar/qux-bar/1.0.0/qux-bar-1.0.0.jar b/spring-cloud-stream-module-launcher/src/test/resources/local-repo/qux/bar/qux-bar/1.0.0/qux-bar-1.0.0.jar deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-cloud-stream-module-launcher/src/test/resources/local-repo/qux/bar/qux-bar/1.0.0/qux-bar-1.0.0.pom b/spring-cloud-stream-module-launcher/src/test/resources/local-repo/qux/bar/qux-bar/1.0.0/qux-bar-1.0.0.pom deleted file mode 100644 index 4c5205853..000000000 --- a/spring-cloud-stream-module-launcher/src/test/resources/local-repo/qux/bar/qux-bar/1.0.0/qux-bar-1.0.0.pom +++ /dev/null @@ -1,12 +0,0 @@ - - - 4.0.0 - - qux-bar - qux-bar - 1.0.0 - - jar - qux-bar - -