├── .gitignore ├── LICENSE ├── README.md ├── aether-grape ├── assembly.xml ├── pom.xml └── src │ └── main │ └── java │ ├── com │ └── github │ │ └── igorsuhorukov │ │ ├── AetherEngine.java │ │ └── DefaultAetherGrapeEngine.java │ └── org │ └── springframework │ ├── boot │ └── cli │ │ └── compiler │ │ └── maven │ │ └── MavenSettingsReader.java │ └── util │ ├── Assert.java │ ├── ObjectUtils.java │ └── StringUtils.java ├── dropship ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── github │ └── smreed │ └── dropship │ ├── ClassLoaderBuilder.java │ ├── Dropship.java │ ├── FilterOptionalRuntimeProvided.java │ ├── LoggingRepositoryListener.java │ ├── MavenClassLoader.java │ ├── MavenDependency.java │ ├── NotLogger.java │ ├── RepositoryUtils.java │ └── Settings.java ├── mvn-classloader-settings └── pom.xml ├── mvn-classloader ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── github │ │ └── igorsuhorukov │ │ ├── maven │ │ ├── DependenciesResolver.java │ │ └── MavenDependenciesResolver.java │ │ ├── service │ │ └── MavenServiceLoader.java │ │ └── url │ │ ├── UrlBootstrapLoader.java │ │ └── handler │ │ ├── ChainURLStreamHandlerFactory.java │ │ ├── UniversalURLStreamHandlerFactory.java │ │ ├── loadable │ │ ├── LoadableHandlerProvider.java │ │ └── LoadableURLStreamHandlerFactory.java │ │ └── mvn │ │ ├── Handler.java │ │ ├── MavenHandlerProvider.java │ │ └── MavenURLStreamHandlerFactory.java │ └── resources │ └── META-INF │ └── services │ ├── com.github.igorsuhorukov.springframework.boot.cli.compiler.grape.RepositorySystemSessionAutoConfiguration │ ├── groovy.grape.GrapeEngine │ ├── java.net.URLStreamHandlerFactory │ └── java.net.spi.URLStreamHandlerProvider └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .m2 2 | target 3 | *.iml 4 | .idea -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mvn-classloader 2 | Groovy Grape Aether provider, Dropship classloader, UniversalURLStreamHandlerFactory to extend java.net.URL protocols, misc. classloader tools 3 | 4 | Thanks [Steve Reed][1] for maven classloader (dropship project). 5 | [1]: https://github.com/smreed "Steve Reed" 6 | -------------------------------------------------------------------------------- /aether-grape/assembly.xml: -------------------------------------------------------------------------------- 1 | 4 | bundled 5 | 6 | jar 7 | 8 | false 9 | 10 | 11 | ${project.build.directory}/assembly 12 | / 13 | 14 | 15 | -------------------------------------------------------------------------------- /aether-grape/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | com.github.igor-suhorukov 4 | mvn-classloader-settings 5 | 1.11-SNAPSHOT 6 | ../mvn-classloader-settings 7 | 8 | 4.0.0 9 | aether-grape 10 | jar 11 | 1.11-SNAPSHOT 12 | 13 | 14 | 15 | com.github.wvengen 16 | proguard-maven-plugin 17 | 18 | 19 | package 20 | proguard 21 | 22 | 23 | 24 | false 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-cli 51 | !org/springframework/boot/cli/compiler/maven/MavenSettingsReader* 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-loader-tools 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | org.apache.maven.plugins 64 | maven-dependency-plugin 65 | 66 | 67 | unpack 68 | package 69 | 70 | unpack 71 | 72 | 73 | 74 | 75 | ${project.groupId} 76 | ${project.artifactId} 77 | ${project.version} 78 | jar 79 | ${project.build.directory}/assembly 80 | META-INF/services/* 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | maven-assembly-plugin 89 | 2.5.5 90 | 91 | 92 | assembly.xml 93 | 94 | 95 | 96 | 97 | package 98 | 99 | single 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | org.springframework.boot 109 | spring-boot-cli 110 | ${spring.boot.version} 111 | 112 | 113 | -------------------------------------------------------------------------------- /aether-grape/src/main/java/com/github/igorsuhorukov/AetherEngine.java: -------------------------------------------------------------------------------- 1 | package com.github.igorsuhorukov; 2 | 3 | import groovy.lang.GroovyClassLoader; 4 | import org.springframework.boot.cli.compiler.grape.*; 5 | 6 | import java.net.URI; 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | /** 11 | */ 12 | public class AetherEngine { 13 | 14 | public static void install(GroovyClassLoader classLoader) { 15 | install(classLoader, new DependencyResolutionContext(), Collections.singletonList( 16 | new RepositoryConfiguration("central", 17 | URI.create("https://repo.maven.apache.org/maven2/"), false))); 18 | } 19 | 20 | public static void install(GroovyClassLoader classLoader, DependencyResolutionContext resolutionContext, 21 | List repositoryConfiguration) { 22 | 23 | AetherGrapeEngine grapeEngine = AetherGrapeEngineFactory.create(classLoader, 24 | repositoryConfiguration, resolutionContext); 25 | GrapeEngineInstaller.install(grapeEngine); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /aether-grape/src/main/java/com/github/igorsuhorukov/DefaultAetherGrapeEngine.java: -------------------------------------------------------------------------------- 1 | package com.github.igorsuhorukov; 2 | 3 | import groovy.grape.GrapeEngine; 4 | import groovy.lang.GroovyClassLoader; 5 | import org.springframework.boot.cli.compiler.RepositoryConfigurationFactory; 6 | import org.springframework.boot.cli.compiler.grape.AetherGrapeEngine; 7 | import org.springframework.boot.cli.compiler.grape.AetherGrapeEngineFactory; 8 | import org.springframework.boot.cli.compiler.grape.DependencyResolutionContext; 9 | 10 | import java.net.URI; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | public class DefaultAetherGrapeEngine implements GrapeEngine { 15 | 16 | private final AetherGrapeEngine grapeEngine; 17 | private final GroovyClassLoader groovyClassLoader; 18 | 19 | public DefaultAetherGrapeEngine() { 20 | groovyClassLoader = getGroovyClassLoader(); 21 | grapeEngine = AetherGrapeEngineFactory.create(groovyClassLoader, 22 | RepositoryConfigurationFactory.createDefaultRepositoryConfiguration(), 23 | new DependencyResolutionContext()); 24 | } 25 | 26 | @Override 27 | public Object grab(String endorsedModule) { 28 | return grapeEngine.grab(endorsedModule); 29 | } 30 | 31 | @Override 32 | public Object grab(Map args) { 33 | return grapeEngine.grab(args); 34 | } 35 | 36 | @Override 37 | public Object grab(Map args, Map... dependencies) { 38 | return grapeEngine.grab(args, dependencies); 39 | } 40 | 41 | @Override 42 | public Map>> enumerateGrapes() { 43 | return grapeEngine.enumerateGrapes(); 44 | } 45 | 46 | @Override 47 | public URI[] resolve(Map args, Map... dependencies) { 48 | return grapeEngine.resolve(args, dependencies); 49 | } 50 | 51 | @Override 52 | public URI[] resolve(Map args, List depsInfo, Map... dependencies) { 53 | return grapeEngine.resolve(args, depsInfo, dependencies); 54 | } 55 | 56 | @Override 57 | public Map[] listDependencies(ClassLoader classLoader) { 58 | return grapeEngine.listDependencies(classLoader); 59 | } 60 | 61 | @Override 62 | public void addResolver(Map args) { 63 | grapeEngine.addResolver(args); 64 | } 65 | 66 | private static GroovyClassLoader getGroovyClassLoader() { 67 | ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); 68 | GroovyClassLoader classLoader; 69 | if (contextClassLoader != null && contextClassLoader instanceof GroovyClassLoader) { 70 | classLoader = (GroovyClassLoader) contextClassLoader; 71 | } else{ 72 | classLoader = new GroovyClassLoader(); 73 | } 74 | return classLoader; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /aether-grape/src/main/java/org/springframework/boot/cli/compiler/maven/MavenSettingsReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.boot.cli.compiler.maven; 18 | 19 | import java.io.File; 20 | import java.lang.reflect.Field; 21 | 22 | import org.apache.maven.settings.Settings; 23 | import org.apache.maven.settings.building.DefaultSettingsBuilderFactory; 24 | import org.apache.maven.settings.building.DefaultSettingsBuildingRequest; 25 | import org.apache.maven.settings.building.SettingsBuildingException; 26 | import org.apache.maven.settings.building.SettingsBuildingRequest; 27 | import org.apache.maven.settings.crypto.DefaultSettingsDecrypter; 28 | import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest; 29 | import org.apache.maven.settings.crypto.SettingsDecrypter; 30 | import org.apache.maven.settings.crypto.SettingsDecryptionResult; 31 | import org.sonatype.plexus.components.cipher.DefaultPlexusCipher; 32 | import org.sonatype.plexus.components.cipher.PlexusCipherException; 33 | import org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher; 34 | 35 | import org.springframework.boot.cli.util.Log; 36 | 37 | /** 38 | * {@code MavenSettingsReader} reads settings from a user's Maven settings.xml file, 39 | * decrypting them if necessary using settings-security.xml. 40 | * 41 | * @author Andy Wilkinson 42 | * @since 1.3.0 43 | */ 44 | public class MavenSettingsReader { 45 | 46 | private final String homeDir; 47 | 48 | public MavenSettingsReader() { 49 | this(System.getProperty("user.home")); 50 | } 51 | 52 | public MavenSettingsReader(String homeDir) { 53 | this.homeDir = homeDir; 54 | } 55 | 56 | public MavenSettings readSettings() { 57 | Settings settings = loadSettings(); 58 | SettingsDecryptionResult decrypted = decryptSettings(settings); 59 | if (!decrypted.getProblems().isEmpty()) { 60 | Log.error( 61 | "Maven settings decryption failed. Some Maven repositories may be inaccessible"); 62 | // Continue - the encrypted credentials may not be used 63 | } 64 | if(Boolean.getBoolean("mavenSettings.offline")) { 65 | settings.setOffline(true); 66 | } 67 | return new MavenSettings(settings, decrypted); 68 | } 69 | 70 | private Settings loadSettings() { 71 | File settingsFile = getSettingsFile(); 72 | SettingsBuildingRequest request = new DefaultSettingsBuildingRequest(); 73 | request.setUserSettingsFile(settingsFile); 74 | request.setSystemProperties(System.getProperties()); 75 | try { 76 | return new DefaultSettingsBuilderFactory().newInstance().build(request) 77 | .getEffectiveSettings(); 78 | } 79 | catch (SettingsBuildingException ex) { 80 | throw new IllegalStateException( 81 | "Failed to build settings from " + settingsFile, ex); 82 | } 83 | } 84 | 85 | private SettingsDecryptionResult decryptSettings(Settings settings) { 86 | DefaultSettingsDecryptionRequest request = new DefaultSettingsDecryptionRequest( 87 | settings); 88 | 89 | return createSettingsDecrypter().decrypt(request); 90 | } 91 | 92 | private SettingsDecrypter createSettingsDecrypter() { 93 | SettingsDecrypter settingsDecrypter = new DefaultSettingsDecrypter(); 94 | setField(DefaultSettingsDecrypter.class, "securityDispatcher", settingsDecrypter, 95 | new SpringBootSecDispatcher()); 96 | return settingsDecrypter; 97 | } 98 | 99 | private void setField(Class sourceClass, String fieldName, Object target, 100 | Object value) { 101 | try { 102 | Field field = sourceClass.getDeclaredField(fieldName); 103 | field.setAccessible(true); 104 | field.set(target, value); 105 | } 106 | catch (Exception ex) { 107 | throw new IllegalStateException( 108 | "Failed to set field '" + fieldName + "' on '" + target + "'", ex); 109 | } 110 | } 111 | 112 | private File getSettingsFile() { 113 | String settingsFile = System.getProperty("mavenSettings"); 114 | return settingsFile != null && !settingsFile.isEmpty() ? 115 | new File(settingsFile) : new File(this.homeDir, ".m2/settings.xml"); 116 | } 117 | 118 | private File getSettingsSecurityFile() { 119 | String settingsSecurityFile = System.getProperty("mavenSettingsSecurity"); 120 | final String SECURITY_XML = ".m2/settings-security.xml"; 121 | return settingsSecurityFile != null && !settingsSecurityFile.isEmpty() ? 122 | new File(settingsSecurityFile) : 123 | new File(homeDir, SECURITY_XML); 124 | } 125 | 126 | private class SpringBootSecDispatcher extends DefaultSecDispatcher { 127 | 128 | SpringBootSecDispatcher() { 129 | File file = getSettingsSecurityFile(); 130 | this._configurationFile = file.getAbsolutePath(); 131 | try { 132 | this._cipher = new DefaultPlexusCipher(); 133 | } 134 | catch (PlexusCipherException e) { 135 | throw new IllegalStateException(e); 136 | } 137 | } 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /aether-grape/src/main/java/org/springframework/util/Assert.java: -------------------------------------------------------------------------------- 1 | package org.springframework.util; 2 | 3 | /** 4 | */ 5 | public class Assert { 6 | public static void notNull(Object object, String message) { 7 | if(object == null) { 8 | throw new IllegalArgumentException(message); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /aether-grape/src/main/java/org/springframework/util/ObjectUtils.java: -------------------------------------------------------------------------------- 1 | package org.springframework.util; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * Miscellaneous object utility methods. 7 | * Mainly for internal use within the framework. 8 | * 9 | *

Thanks to Alex Ruiz for contributing several enhancements to this class! 10 | * 11 | * @author Juergen Hoeller 12 | * @author Keith Donald 13 | * @author Rod Johnson 14 | * @author Rob Harrop 15 | * @author Chris Beams 16 | * @since 19.03.2004 17 | */ 18 | public class ObjectUtils { 19 | //--------------------------------------------------------------------- 20 | // Convenience methods for content-based equality/hash-code handling 21 | //--------------------------------------------------------------------- 22 | 23 | /** 24 | * Determine if the given objects are equal, returning {@code true} 25 | * if both are {@code null} or {@code false} if only one is 26 | * {@code null}. 27 | *

Compares arrays with {@code Arrays.equals}, performing an equality 28 | * check based on the array elements rather than the array reference. 29 | * @param o1 first Object to compare 30 | * @param o2 second Object to compare 31 | * @return whether the given objects are equal 32 | * @see java.util.Arrays#equals 33 | */ 34 | public static boolean nullSafeEquals(Object o1, Object o2) { 35 | if (o1 == o2) { 36 | return true; 37 | } 38 | if (o1 == null || o2 == null) { 39 | return false; 40 | } 41 | if (o1.equals(o2)) { 42 | return true; 43 | } 44 | if (o1.getClass().isArray() && o2.getClass().isArray()) { 45 | if (o1 instanceof Object[] && o2 instanceof Object[]) { 46 | return Arrays.equals((Object[]) o1, (Object[]) o2); 47 | } 48 | if (o1 instanceof boolean[] && o2 instanceof boolean[]) { 49 | return Arrays.equals((boolean[]) o1, (boolean[]) o2); 50 | } 51 | if (o1 instanceof byte[] && o2 instanceof byte[]) { 52 | return Arrays.equals((byte[]) o1, (byte[]) o2); 53 | } 54 | if (o1 instanceof char[] && o2 instanceof char[]) { 55 | return Arrays.equals((char[]) o1, (char[]) o2); 56 | } 57 | if (o1 instanceof double[] && o2 instanceof double[]) { 58 | return Arrays.equals((double[]) o1, (double[]) o2); 59 | } 60 | if (o1 instanceof float[] && o2 instanceof float[]) { 61 | return Arrays.equals((float[]) o1, (float[]) o2); 62 | } 63 | if (o1 instanceof int[] && o2 instanceof int[]) { 64 | return Arrays.equals((int[]) o1, (int[]) o2); 65 | } 66 | if (o1 instanceof long[] && o2 instanceof long[]) { 67 | return Arrays.equals((long[]) o1, (long[]) o2); 68 | } 69 | if (o1 instanceof short[] && o2 instanceof short[]) { 70 | return Arrays.equals((short[]) o1, (short[]) o2); 71 | } 72 | } 73 | return false; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /aether-grape/src/main/java/org/springframework/util/StringUtils.java: -------------------------------------------------------------------------------- 1 | package org.springframework.util; 2 | 3 | /** 4 | * Miscellaneous {@link String} utility methods. 5 | * 6 | *

Mainly for internal use within the framework; consider 7 | * Apache's Commons Lang 8 | * for a more comprehensive suite of String utilities. 9 | * 10 | *

This class delivers some simple functionality that should really 11 | * be provided by the core Java {@code String} and {@link StringBuilder} 12 | * classes, such as the ability to {#replace} all occurrences of a given 13 | * substring in a target string. It also provides easy-to-use methods to convert 14 | * between delimited strings, such as CSV strings, and collections and arrays. 15 | * 16 | * @author Rod Johnson 17 | * @author Juergen Hoeller 18 | * @author Keith Donald 19 | * @author Rob Harrop 20 | * @author Rick Evans 21 | * @author Arjen Poutsma 22 | * @since 16 April 2001 23 | */ 24 | public class StringUtils { 25 | /** 26 | * Check that the given {@code CharSequence} is neither {@code null} nor 27 | * of length 0. 28 | *

Note: this method returns {@code true} for a {@code CharSequence} 29 | * that purely consists of whitespace. 30 | *

 31 |      * StringUtils.hasLength(null) = false
 32 |      * StringUtils.hasLength("") = false
 33 |      * StringUtils.hasLength(" ") = true
 34 |      * StringUtils.hasLength("Hello") = true
 35 |      * 
36 | * @param str the {@code CharSequence} to check (may be {@code null}) 37 | * @return {@code true} if the {@code CharSequence} is not {@code null} and has length 38 | * @see #hasText(String) 39 | */ 40 | public static boolean hasLength(CharSequence str) { 41 | return (str != null && str.length() > 0); 42 | } 43 | 44 | /** 45 | * Check that the given {@code String} is neither {@code null} nor of length 0. 46 | *

Note: this method returns {@code true} for a {@code String} that 47 | * purely consists of whitespace. 48 | * @param str the {@code String} to check (may be {@code null}) 49 | * @return {@code true} if the {@code String} is not {@code null} and has length 50 | * @see #hasLength(CharSequence) 51 | * @see #hasText(String) 52 | */ 53 | public static boolean hasLength(String str) { 54 | return hasLength((CharSequence) str); 55 | } 56 | 57 | /** 58 | * Check whether the given {@code CharSequence} contains actual text. 59 | *

More specifically, this method returns {@code true} if the 60 | * {@code CharSequence} is not {@code null}, its length is greater than 61 | * 0, and it contains at least one non-whitespace character. 62 | *

 63 |      * StringUtils.hasText(null) = false
 64 |      * StringUtils.hasText("") = false
 65 |      * StringUtils.hasText(" ") = false
 66 |      * StringUtils.hasText("12345") = true
 67 |      * StringUtils.hasText(" 12345 ") = true
 68 |      * 
69 | * @param str the {@code CharSequence} to check (may be {@code null}) 70 | * @return {@code true} if the {@code CharSequence} is not {@code null}, 71 | * its length is greater than 0, and it does not contain whitespace only 72 | * @see Character#isWhitespace 73 | */ 74 | public static boolean hasText(CharSequence str) { 75 | if (!hasLength(str)) { 76 | return false; 77 | } 78 | int strLen = str.length(); 79 | for (int i = 0; i < strLen; i++) { 80 | if (!Character.isWhitespace(str.charAt(i))) { 81 | return true; 82 | } 83 | } 84 | return false; 85 | } 86 | 87 | /** 88 | * Check whether the given {@code String} contains actual text. 89 | *

More specifically, this method returns {@code true} if the 90 | * {@code String} is not {@code null}, its length is greater than 0, 91 | * and it contains at least one non-whitespace character. 92 | * @param str the {@code String} to check (may be {@code null}) 93 | * @return {@code true} if the {@code String} is not {@code null}, its 94 | * length is greater than 0, and it does not contain whitespace only 95 | * @see #hasText(CharSequence) 96 | */ 97 | public static boolean hasText(String str) { 98 | return hasText((CharSequence) str); 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /dropship/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | com.github.igor-suhorukov 4 | mvn-classloader-settings 5 | 1.11-SNAPSHOT 6 | ../mvn-classloader-settings 7 | 8 | 4.0.0 9 | dropship 10 | jar 11 | 1.11-SNAPSHOT 12 | 13 | 14 | 15 | com.github.wvengen 16 | proguard-maven-plugin 17 | 18 | 19 | package 20 | proguard 21 | 22 | 23 | 24 | false 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | com.google.guava 51 | guava 52 | 53 | 54 | org.jodd 55 | jodd-core 56 | 57 | 58 | commons-logging 59 | commons-logging 60 | 61 | 62 | spice 63 | spice-classman 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | org.apache.maven 74 | maven-aether-provider 75 | ${mavendep.version} 76 | 77 | 78 | org.eclipse.sisu.plexus 79 | org.eclipse.sisu 80 | 81 | 82 | 83 | 84 | org.apache.maven 85 | maven-settings-builder 86 | ${mavendep.version} 87 | 88 | 89 | org.codehaus.plexus 90 | plexus-component-api 91 | ${plexus.version} 92 | 93 | 94 | * 95 | * 96 | 97 | 98 | 99 | 100 | org.eclipse.aether 101 | aether-api 102 | ${aether.version} 103 | 104 | 105 | org.eclipse.aether 106 | aether-connector-basic 107 | ${aether.version} 108 | 109 | 110 | org.eclipse.aether 111 | aether-impl 112 | ${aether.version} 113 | 114 | 115 | org.eclipse.aether 116 | aether-spi 117 | ${aether.version} 118 | 119 | 120 | org.eclipse.aether 121 | aether-transport-file 122 | ${aether.version} 123 | 124 | 125 | org.eclipse.aether 126 | aether-transport-http 127 | 128 | 129 | org.slf4j 130 | jcl-over-slf4j 131 | 132 | 133 | ${aether.version} 134 | 135 | 136 | org.eclipse.aether 137 | aether-util 138 | ${aether.version} 139 | 140 | 141 | spice 142 | spice-classman 143 | 1.1 144 | 145 | 146 | org.jodd 147 | jodd-core 148 | 3.6.6 149 | 150 | 151 | commons-logging 152 | commons-logging 153 | 1.2 154 | 155 | 156 | com.github.igor-suhorukov 157 | aether-grape 158 | ${project.version} 159 | 160 | 161 | -------------------------------------------------------------------------------- /dropship/src/main/java/com/github/smreed/dropship/ClassLoaderBuilder.java: -------------------------------------------------------------------------------- 1 | package com.github.smreed.dropship; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import com.google.common.collect.Iterables; 5 | import com.google.common.collect.Lists; 6 | import org.apache.maven.repository.internal.MavenRepositorySystemUtils; 7 | import org.codehaus.plexus.PlexusContainerException; 8 | import org.codehaus.plexus.component.repository.exception.ComponentLookupException; 9 | import org.eclipse.aether.DefaultRepositorySystemSession; 10 | import org.eclipse.aether.RepositorySystem; 11 | import org.eclipse.aether.RepositorySystemSession; 12 | import org.eclipse.aether.artifact.Artifact; 13 | import org.eclipse.aether.artifact.DefaultArtifact; 14 | import org.eclipse.aether.collection.CollectRequest; 15 | import org.eclipse.aether.collection.DependencyCollectionException; 16 | import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory; 17 | import org.eclipse.aether.graph.Dependency; 18 | import org.eclipse.aether.graph.DependencyFilter; 19 | import org.eclipse.aether.graph.DependencyNode; 20 | import org.eclipse.aether.impl.DefaultServiceLocator; 21 | import org.eclipse.aether.internal.impl.DefaultRepositorySystem; 22 | import org.eclipse.aether.repository.LocalRepository; 23 | import org.eclipse.aether.repository.LocalRepositoryManager; 24 | import org.eclipse.aether.repository.RemoteRepository; 25 | import org.eclipse.aether.resolution.*; 26 | import org.eclipse.aether.spi.connector.RepositoryConnectorFactory; 27 | import org.eclipse.aether.spi.connector.transport.TransporterFactory; 28 | import org.eclipse.aether.transport.file.FileTransporterFactory; 29 | import org.eclipse.aether.transport.http.HttpTransporterFactory; 30 | import org.eclipse.aether.util.filter.AndDependencyFilter; 31 | import org.eclipse.aether.util.filter.PatternExclusionsDependencyFilter; 32 | import org.eclipse.aether.util.filter.ScopeDependencyFilter; 33 | import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator; 34 | import org.springframework.boot.cli.compiler.grape.SettingsXmlRepositorySystemSessionAutoConfiguration; 35 | 36 | import java.io.File; 37 | import java.net.MalformedURLException; 38 | import java.net.URL; 39 | import java.net.URLClassLoader; 40 | import java.util.ArrayList; 41 | import java.util.Collection; 42 | import java.util.List; 43 | 44 | import static com.github.smreed.dropship.NotLogger.info; 45 | import static com.google.common.base.Preconditions.checkArgument; 46 | import static com.google.common.base.Preconditions.checkNotNull; 47 | import static com.google.common.base.Throwables.propagate; 48 | 49 | public class ClassLoaderBuilder { 50 | 51 | private static final String COMPILE_SCOPE = "compile"; 52 | private static final ClassLoader SHARE_NOTHING = null; 53 | 54 | private final List repositories; 55 | 56 | ClassLoaderBuilder(String repository) { 57 | this(new RemoteRepository.Builder("custom", "default", repository).build()); 58 | } 59 | 60 | ClassLoaderBuilder(RemoteRepository... repositories) { 61 | checkNotNull(repositories); 62 | checkArgument(repositories.length > 0, "Must specify at least one remote repository."); 63 | 64 | this.repositories = ImmutableList.copyOf(repositories); 65 | } 66 | 67 | public URLClassLoader forMavenCoordinates(String groupArtifactVersion) { 68 | try { 69 | return new URLClassLoader(getArtifactUrls(groupArtifactVersion), SHARE_NOTHING); 70 | } catch (Exception e) { 71 | throw propagate(e); 72 | } 73 | } 74 | 75 | public URLClassLoader forMavenCoordinates(String groupArtifactVersion, ClassLoader parent) { 76 | try { 77 | return new URLClassLoader(getArtifactUrls(groupArtifactVersion), parent); 78 | } catch (Exception e) { 79 | throw propagate(e); 80 | } 81 | } 82 | 83 | public URLClassLoader forMavenCoordinates(MavenDependency[] dependencies, ClassLoader parent) { 84 | checkNotNull(dependencies); 85 | try { 86 | List urls = getUrls(dependencies); 87 | return new URLClassLoader(Iterables.toArray(urls, URL.class), parent); 88 | } catch (Exception e) { 89 | throw propagate(e); 90 | } 91 | } 92 | 93 | public List getUrls(MavenDependency[] dependencies) throws PlexusContainerException, DependencyCollectionException, ArtifactResolutionException, MalformedURLException, ComponentLookupException, DependencyResolutionException { 94 | ArrayList urls = new ArrayList<>(); 95 | for (MavenDependency mavenDependency : dependencies) { 96 | urls.addAll(getArtifactUrlsCollection(mavenDependency.getGroupArtifactVersion(), 97 | mavenDependency.getExcludes())); 98 | } 99 | return urls; 100 | } 101 | 102 | public List getArtifactUrlsCollection(String groupArtifactVersion, Collection excludes) 103 | throws PlexusContainerException, DependencyCollectionException, ArtifactResolutionException, 104 | ComponentLookupException, DependencyResolutionException, MalformedURLException { 105 | info("Collecting maven metadata."); 106 | CollectRequest collectRequest = createCollectRequestForGAV(groupArtifactVersion); 107 | 108 | info("Resolving dependencies."); 109 | List artifacts = collectDependenciesIntoArtifacts(collectRequest, excludes); 110 | 111 | info("Building classpath for %s from %d URLs.", groupArtifactVersion, artifacts.size()); 112 | List urls = Lists.newArrayListWithExpectedSize(artifacts.size()); 113 | for (Artifact artifact : artifacts) { 114 | urls.add(artifact.getFile().toURI().toURL()); 115 | } 116 | 117 | for (String path : Settings.additionalClasspathPaths()) { 118 | info("Adding \"%s\" to classpath.", path); 119 | urls.add(new File(path).toURI().toURL()); 120 | } 121 | return urls; 122 | } 123 | 124 | private URL[] getArtifactUrls(String groupArtifactVersion) throws Exception { 125 | return Iterables.toArray(getArtifactUrlsCollection(groupArtifactVersion, null), URL.class); 126 | } 127 | 128 | protected CollectRequest createCollectRequestForGAV(String gav) { 129 | DefaultArtifact artifact = new DefaultArtifact(gav); 130 | Dependency dependency = new Dependency(artifact, getScope()); 131 | 132 | CollectRequest collectRequest = new CollectRequest(); 133 | collectRequest.setRoot(dependency); 134 | for (RemoteRepository repository : repositories) { 135 | collectRequest.addRepository(repository); 136 | } 137 | 138 | return collectRequest; 139 | } 140 | 141 | protected List collectDependenciesIntoArtifacts(CollectRequest collectRequest, Collection excludes) 142 | throws PlexusContainerException, ComponentLookupException, DependencyCollectionException, 143 | ArtifactResolutionException, DependencyResolutionException { 144 | 145 | RepositorySystem repositorySystem = newRepositorySystem(); 146 | RepositorySystemSession session = newSession(repositorySystem); 147 | RepositoryUtils.configureRepositories(collectRequest, session); 148 | 149 | DependencyNode node = repositorySystem.collectDependencies(session, collectRequest).getRoot(); 150 | 151 | DependencyFilter filter; 152 | if(Boolean.getBoolean("dropship.remove_optional_and_provided")){ 153 | if (excludes != null && !excludes.isEmpty()) { 154 | filter = new AndDependencyFilter(new FilterOptionalRuntimeProvided(), 155 | new PatternExclusionsDependencyFilter(excludes)); 156 | } else { 157 | filter = new FilterOptionalRuntimeProvided(); 158 | } 159 | } else { 160 | if (excludes != null && !excludes.isEmpty()) { 161 | filter = new PatternExclusionsDependencyFilter(excludes); 162 | } else { 163 | filter = new ScopeDependencyFilter(); 164 | } 165 | } 166 | 167 | DependencyRequest request = new DependencyRequest(node, filter); 168 | 169 | repositorySystem.resolveDependencies(session, request); 170 | 171 | PreorderNodeListGenerator nlg = new PreorderNodeListGenerator(); 172 | node.accept(nlg); 173 | 174 | return nlg.getArtifacts(false); 175 | } 176 | 177 | protected RepositorySystem newRepositorySystem() throws PlexusContainerException, ComponentLookupException { 178 | DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator(); 179 | locator.addService(RepositorySystem.class, DefaultRepositorySystem.class); 180 | locator.addService(RepositoryConnectorFactory.class, 181 | BasicRepositoryConnectorFactory.class); 182 | locator.addService(TransporterFactory.class, HttpTransporterFactory.class); 183 | locator.addService(TransporterFactory.class, FileTransporterFactory.class); 184 | return locator.getService(RepositorySystem.class); 185 | } 186 | 187 | protected RepositorySystemSession newSession(RepositorySystem system) { 188 | DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession(); 189 | applySettings(system, session); 190 | session.setRepositoryListener(new LoggingRepositoryListener()); 191 | return session; 192 | } 193 | 194 | protected void applySettings(RepositorySystem system, DefaultRepositorySystemSession session) { 195 | if(!Boolean.getBoolean("skipMavenSettings")){ 196 | SettingsXmlRepositorySystemSessionAutoConfiguration autoConfiguration = 197 | new SettingsXmlRepositorySystemSessionAutoConfiguration(); 198 | autoConfiguration.apply(session, system); 199 | } 200 | if (session.getLocalRepositoryManager() == null) { 201 | LocalRepository localRepository = new LocalRepository(Settings.localRepoPath()); 202 | LocalRepositoryManager localRepositoryManager = system.newLocalRepositoryManager(session, localRepository); 203 | session.setLocalRepositoryManager(localRepositoryManager); 204 | } 205 | } 206 | 207 | protected String getScope() { 208 | return COMPILE_SCOPE; 209 | } 210 | 211 | public URL resolveArtifact(String gav) throws PlexusContainerException, ComponentLookupException, 212 | ArtifactResolutionException, MalformedURLException { 213 | 214 | RepositorySystem repositorySystem = newRepositorySystem(); 215 | RepositorySystemSession session = newSession(repositorySystem); 216 | ArtifactRequest artifactRequest = new ArtifactRequest(); 217 | for (RemoteRepository repository : repositories) { 218 | artifactRequest.addRepository(RepositoryUtils.applySessionSettingsToRepository(session, repository)); 219 | } 220 | artifactRequest.setArtifact(new DefaultArtifact(gav)); 221 | ArtifactResult artifactResult = repositorySystem.resolveArtifact(session, artifactRequest); 222 | return artifactResult.getArtifact().getFile().toURI().toURL(); 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /dropship/src/main/java/com/github/smreed/dropship/Dropship.java: -------------------------------------------------------------------------------- 1 | package com.github.smreed.dropship; 2 | 3 | import com.google.common.base.Optional; 4 | import com.google.common.collect.ImmutableList; 5 | import com.google.common.collect.Iterables; 6 | 7 | import java.lang.reflect.Method; 8 | import java.net.URL; 9 | import java.net.URLClassLoader; 10 | import java.util.ArrayList; 11 | import java.util.Arrays; 12 | import java.util.List; 13 | import java.util.Properties; 14 | 15 | import static com.github.smreed.dropship.NotLogger.info; 16 | import static com.google.common.base.Preconditions.checkArgument; 17 | import static com.google.common.base.Preconditions.checkNotNull; 18 | 19 | /** 20 | * https://github.com/kaffamobile/dropship 21 | */ 22 | public final class Dropship { 23 | 24 | public static final String DEPENDENCY_PATTERN = "groupId:artifactId[:extension[:classifier]]:version"; 25 | 26 | private static ClassLoaderBuilder classLoaderBuilder() { 27 | Optional override = Settings.mavenRepoUrl(); 28 | if (override.isPresent()) { 29 | info("Will load artifacts from %s", override); 30 | return MavenClassLoader.using(override.get()); 31 | } else { 32 | return MavenClassLoader.usingCentralRepo(); 33 | } 34 | } 35 | 36 | private static String resolveGav(String gav) { 37 | ImmutableList tokens = ImmutableList.copyOf(Settings.GAV_SPLITTER.split(gav)); 38 | 39 | checkArgument(tokens.size() > 1, "Require " + DEPENDENCY_PATTERN); 40 | checkArgument(tokens.size() < 6, "Require " + DEPENDENCY_PATTERN); 41 | 42 | if (tokens.size() > 2) { 43 | return gav; 44 | } 45 | 46 | Properties settings = Settings.loadBootstrapPropertiesUnchecked(); 47 | 48 | if (settings.containsKey(gav)) { 49 | return Settings.GAV_JOINER.join(tokens.get(0), tokens.get(1), settings.getProperty(gav)); 50 | } else { 51 | return Settings.GAV_JOINER.join(tokens.get(0), tokens.get(1), "[0,)"); 52 | } 53 | } 54 | 55 | public static void main(String[] args) throws Exception { 56 | checkNotNull(args); 57 | checkArgument(args.length >= 2, "Must specify " + DEPENDENCY_PATTERN + " and classname psvm 'main' method!"); 58 | 59 | info("Starting Dropship v%s", Settings.dropshipVersion()); 60 | 61 | String artifactLocator = args[0]; 62 | if("justPrintDependencies".equals(args[1])){ 63 | String[] urls = resolveAndReturnFiles(artifactLocator); 64 | Arrays.asList(urls).forEach(System.out::println); 65 | return; 66 | } 67 | 68 | MavenDependency[] requestedDependencies = getMavenDependencies(artifactLocator); 69 | URLClassLoader loader = classLoaderBuilder().forMavenCoordinates(requestedDependencies, ClassLoader.getPlatformClassLoader()); 70 | 71 | Class mainClass = loader.loadClass(args[1]); 72 | 73 | Thread.currentThread().setContextClassLoader(loader); 74 | 75 | Method mainMethod = mainClass.getMethod("main", String[].class); 76 | 77 | Iterable mainArgs = Iterables.skip(ImmutableList.copyOf(args), 2); 78 | mainMethod.invoke(null, (Object) Iterables.toArray(mainArgs, String.class)); 79 | } 80 | 81 | public static MavenDependency[] getMavenDependencies(String artifactLocator) { 82 | List dependencies = new ArrayList<>(); 83 | String[] artifactGavItem = artifactLocator.split("\\|"); 84 | for(String gavString: artifactGavItem){ 85 | String gav = resolveGav(gavString); 86 | info("Requested %s, will load artifact and dependencies for %s.", gavString, gav); 87 | dependencies.add(new MavenDependency(gav)); 88 | } 89 | return dependencies.toArray(new MavenDependency[0]); 90 | } 91 | 92 | public static String[] resolveAndReturnFiles(String artifactLocator) { 93 | MavenDependency[] requestedDependencies = getMavenDependencies(artifactLocator); 94 | try { 95 | return classLoaderBuilder().getUrls(requestedDependencies).stream().map(URL::toExternalForm).toArray(String[]::new); 96 | } catch (Exception e) { 97 | throw new RuntimeException(e); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /dropship/src/main/java/com/github/smreed/dropship/FilterOptionalRuntimeProvided.java: -------------------------------------------------------------------------------- 1 | package com.github.smreed.dropship; 2 | 3 | import org.eclipse.aether.graph.Dependency; 4 | import org.eclipse.aether.graph.DependencyFilter; 5 | import org.eclipse.aether.graph.DependencyNode; 6 | 7 | import java.util.List; 8 | 9 | class FilterOptionalRuntimeProvided implements DependencyFilter { 10 | @Override 11 | public boolean accept(DependencyNode node, List parents) { 12 | Dependency dependency = node.getDependency(); 13 | if ( dependency == null ){ 14 | return true; 15 | } 16 | return !dependency.isOptional() && !"provided".equals(dependency.getScope()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /dropship/src/main/java/com/github/smreed/dropship/LoggingRepositoryListener.java: -------------------------------------------------------------------------------- 1 | package com.github.smreed.dropship; 2 | 3 | import com.google.common.collect.Maps; 4 | import org.eclipse.aether.AbstractRepositoryListener; 5 | import org.eclipse.aether.RepositoryEvent; 6 | import org.eclipse.aether.artifact.Artifact; 7 | 8 | import java.util.Map; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | import static com.github.smreed.dropship.NotLogger.debug; 12 | import static com.github.smreed.dropship.NotLogger.info; 13 | 14 | class LoggingRepositoryListener extends AbstractRepositoryListener { 15 | 16 | private Map startTimes = Maps.newHashMap(); 17 | 18 | private String artifactAsString(Artifact artifact) { 19 | return Settings.GAV_JOINER.join(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion()); 20 | } 21 | 22 | @Override 23 | public void artifactDownloading(RepositoryEvent event) { 24 | super.artifactDownloading(event); 25 | Artifact artifact = event.getArtifact(); 26 | String key = artifactAsString(artifact); 27 | startTimes.put(key, System.nanoTime()); 28 | } 29 | 30 | @Override 31 | public void artifactDownloaded(RepositoryEvent event) { 32 | super.artifactDownloaded(event); 33 | Artifact artifact = event.getArtifact(); 34 | String key = artifactAsString(artifact); 35 | long downloadTimeNanos = System.nanoTime() - startTimes.remove(key); 36 | double downloadTimeMs = TimeUnit.NANOSECONDS.toMillis(downloadTimeNanos); 37 | double downloadTimeSec = TimeUnit.NANOSECONDS.toSeconds(downloadTimeNanos); 38 | long size = artifact.getFile().length(); 39 | double sizeK = (1 / 1024D) * size; 40 | double downloadRateKBytesPerSecond = sizeK / downloadTimeSec; 41 | info("Downloaded %s (%d bytes) in %gms (%g kbytes/sec).", key, size, downloadTimeMs, downloadRateKBytesPerSecond); 42 | } 43 | 44 | @Override 45 | public void artifactResolved(RepositoryEvent event) { 46 | super.artifactResolved(event); 47 | debug("Resolved %s.", artifactAsString(event.getArtifact())); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /dropship/src/main/java/com/github/smreed/dropship/MavenClassLoader.java: -------------------------------------------------------------------------------- 1 | package com.github.smreed.dropship; 2 | 3 | import org.eclipse.aether.repository.RemoteRepository; 4 | 5 | import java.net.URLClassLoader; 6 | 7 | import static com.google.common.base.Preconditions.checkNotNull; 8 | 9 | 10 | /** 11 | * https://github.com/kaffamobile/dropship 12 | */ 13 | public final class MavenClassLoader { 14 | 15 | /** 16 | * Creates a classloader that will resolve artifacts against the default "central" repository. Throws 17 | * {@link IllegalArgumentException} if the GAV is invalid, {@link NullPointerException} if the GAV is null. 18 | * 19 | * @param gav artifact group:artifact:version, i.e. joda-time:joda-time:1.6.2 20 | * @return a classloader that can be used to load classes from the given artifact 21 | */ 22 | public static URLClassLoader forMavenCoordinates(String gav) { 23 | return usingCentralRepo().forMavenCoordinates(checkNotNull(gav)); 24 | } 25 | 26 | public static URLClassLoader forMavenCoordinates(String gav, ClassLoader parent) { 27 | return usingCentralRepo().forMavenCoordinates(checkNotNull(gav), parent); 28 | } 29 | 30 | public static URLClassLoader forMavenCoordinates(MavenDependency[] mavenDependencies, ClassLoader parent) { 31 | return usingCentralRepo().forMavenCoordinates(mavenDependencies, parent); 32 | } 33 | 34 | public static ClassLoaderBuilder using(String url) { 35 | return new ClassLoaderBuilder(url); 36 | } 37 | 38 | public static ClassLoaderBuilder usingCentralRepo() { 39 | RemoteRepository central = new RemoteRepository.Builder("central", "default", "https://repo1.maven.org/maven2/").build(); 40 | return new ClassLoaderBuilder(central); 41 | } 42 | 43 | public static ClassLoaderBuilder usingRemoteRepositories(RemoteRepository... repositories) { 44 | return new ClassLoaderBuilder(repositories); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /dropship/src/main/java/com/github/smreed/dropship/MavenDependency.java: -------------------------------------------------------------------------------- 1 | package com.github.smreed.dropship; 2 | 3 | import java.util.Collection; 4 | 5 | /** 6 | */ 7 | public class MavenDependency { 8 | private String groupArtifactVersion; 9 | private Collection excludes; 10 | 11 | public MavenDependency(String groupArtifactVersion) { 12 | if(groupArtifactVersion==null || groupArtifactVersion.isEmpty()){ 13 | throw new IllegalArgumentException("groupArtifactVersion is empty"); 14 | } 15 | this.groupArtifactVersion = groupArtifactVersion; 16 | } 17 | 18 | public MavenDependency(String groupArtifactVersion, Collection excludes) { 19 | this(groupArtifactVersion); 20 | if(excludes!=null && !excludes.isEmpty()) { 21 | this.excludes = excludes; 22 | } 23 | } 24 | 25 | public String getGroupArtifactVersion() { 26 | return groupArtifactVersion; 27 | } 28 | 29 | public Collection getExcludes() { 30 | return excludes; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /dropship/src/main/java/com/github/smreed/dropship/NotLogger.java: -------------------------------------------------------------------------------- 1 | package com.github.smreed.dropship; 2 | 3 | import static com.google.common.base.Preconditions.checkNotNull; 4 | 5 | @SuppressWarnings("UseOfSystemOutOrSystemErr") 6 | class NotLogger { 7 | 8 | static void debug(String format, Object arg, Object... otherArgs) { 9 | if (Settings.DEBUG) { 10 | debug(format(format, arg, otherArgs)); 11 | } 12 | } 13 | 14 | static void debug(String message) { 15 | if (Settings.DEBUG) { 16 | write("DEBUG", message); 17 | } 18 | } 19 | 20 | static void info(String format, Object arg, Object... otherArgs) { 21 | info(format(format, arg, otherArgs)); 22 | } 23 | 24 | static void info(String message) { 25 | write(" INFO", message); 26 | } 27 | 28 | static void warn(String format, Object arg, Object... otherArgs) { 29 | warn(format(format, arg, otherArgs)); 30 | } 31 | 32 | static void warn(String message) { 33 | write(" WARN", message); 34 | } 35 | 36 | private static void write(String level, String line) { 37 | System.out.format("[Dropship %s] %s%n", level, line); 38 | } 39 | 40 | private static String format(String format, Object arg, Object... otherArgs) { 41 | checkNotNull(format); 42 | checkNotNull(arg); 43 | checkNotNull(otherArgs); 44 | 45 | if (otherArgs.length > 0) { 46 | Object[] formatArgs = new Object[otherArgs.length + 1]; 47 | formatArgs[0] = arg; 48 | System.arraycopy(otherArgs, 0, formatArgs, 1, otherArgs.length); 49 | return String.format(format, formatArgs); 50 | } else { 51 | return String.format(format, arg); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /dropship/src/main/java/com/github/smreed/dropship/RepositoryUtils.java: -------------------------------------------------------------------------------- 1 | package com.github.smreed.dropship; 2 | 3 | import org.eclipse.aether.RepositorySystemSession; 4 | import org.eclipse.aether.collection.CollectRequest; 5 | import org.eclipse.aether.repository.RemoteRepository; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class RepositoryUtils { 11 | 12 | public static void configureRepositories(CollectRequest collectRequest, RepositorySystemSession session) { 13 | List repositories = new ArrayList(); 14 | for(RemoteRepository repository: collectRequest.getRepositories()){ 15 | repositories.add(applySessionSettingsToRepository(session, repository)); 16 | } 17 | collectRequest.setRepositories(repositories); 18 | } 19 | 20 | public static RemoteRepository applySessionSettingsToRepository(RepositorySystemSession session, RemoteRepository repository) { 21 | return applyAuthentication(applyProxy(applyMirror(repository, session), session), session); 22 | } 23 | 24 | static RemoteRepository applyMirror(RemoteRepository repository, RepositorySystemSession session) { 25 | RemoteRepository mirror = session.getMirrorSelector().getMirror(repository); 26 | return mirror != null ? mirror : repository; 27 | } 28 | 29 | static RemoteRepository applyProxy(RemoteRepository repository, RepositorySystemSession session) { 30 | if (repository.getProxy() == null) { 31 | RemoteRepository.Builder builder = new RemoteRepository.Builder(repository); 32 | builder.setProxy(session.getProxySelector().getProxy(repository)); 33 | repository = builder.build(); 34 | } 35 | return repository; 36 | } 37 | 38 | static RemoteRepository applyAuthentication(RemoteRepository repository, RepositorySystemSession session) { 39 | if (repository.getAuthentication() == null) { 40 | RemoteRepository.Builder builder = new RemoteRepository.Builder(repository); 41 | builder.setAuthentication(session.getAuthenticationSelector() 42 | .getAuthentication(repository)); 43 | repository = builder.build(); 44 | } 45 | return repository; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /dropship/src/main/java/com/github/smreed/dropship/Settings.java: -------------------------------------------------------------------------------- 1 | package com.github.smreed.dropship; 2 | 3 | import com.google.common.base.CaseFormat; 4 | import com.google.common.base.CharMatcher; 5 | import com.google.common.base.Joiner; 6 | import com.google.common.base.Optional; 7 | import com.google.common.base.Splitter; 8 | import com.google.common.collect.ImmutableList; 9 | import com.google.common.io.Resources; 10 | 11 | import java.io.IOException; 12 | import java.net.MalformedURLException; 13 | import java.net.URL; 14 | import java.util.List; 15 | import java.util.Map; 16 | import java.util.Properties; 17 | import java.util.jar.Manifest; 18 | 19 | import static com.github.smreed.dropship.NotLogger.debug; 20 | import static com.github.smreed.dropship.NotLogger.warn; 21 | import static com.google.common.base.Preconditions.checkNotNull; 22 | import static com.google.common.base.Throwables.propagate; 23 | 24 | class Settings { 25 | 26 | static final Joiner GAV_JOINER = Joiner.on(':'); 27 | static final boolean DEBUG = System.getProperty("verbose") != null; 28 | static final CharMatcher GAV_DELIMITER = CharMatcher.is(':'); 29 | static final Splitter GAV_SPLITTER = Splitter.on(GAV_DELIMITER).trimResults().omitEmptyStrings(); 30 | 31 | private static final String DEFAULT_CONFIG_FILE_NAME = "dropship.properties"; 32 | private static final Properties CACHE = new Properties(); 33 | private static final Splitter CSV = Splitter.on(',').trimResults().omitEmptyStrings(); 34 | 35 | private static volatile boolean loaded = false; 36 | 37 | static Optional mavenRepoUrl() { 38 | return loadProperty("repo.remote.url"); 39 | } 40 | 41 | static String localRepoPath() { 42 | return loadProperty("repo.local.path", System.getProperty("user.home") + "/.m2/repository"); 43 | } 44 | 45 | static String dropshipVersion() { 46 | return loadProperty("dropship.xArtifactVersion", "0.0"); 47 | } 48 | 49 | static List additionalClasspathPaths() { 50 | Optional additionalClasspathPathsString = loadProperty("dropship.additional.paths"); 51 | if (additionalClasspathPathsString.isPresent()) { 52 | return ImmutableList.copyOf(CSV.split(additionalClasspathPathsString.get())); 53 | } else { 54 | return ImmutableList.of(); 55 | } 56 | } 57 | 58 | private static String loadProperty(String name, String defaultValue) { 59 | checkNotNull(defaultValue); 60 | 61 | return loadProperty(name).or(defaultValue); 62 | } 63 | 64 | private static Optional loadProperty(String name) { 65 | return Optional.fromNullable(loadBootstrapPropertiesUnchecked().getProperty(name)); 66 | } 67 | 68 | static synchronized Properties loadBootstrapProperties() throws IOException { 69 | if (loaded) { 70 | return CACHE; 71 | } 72 | 73 | URL url = Dropship.class.getClassLoader().getResource(DEFAULT_CONFIG_FILE_NAME); 74 | if (url == null) { 75 | warn("No dropship.properties found! Using .dropship-prefixed system properties (-D)"); 76 | for(Object key: System.getProperties().keySet()) { 77 | String skey = String.valueOf(key); 78 | if(skey.startsWith("dropship.")) { 79 | String kkey = skey.substring("dropship.".length()); 80 | warn("Using " + kkey + "=" + System.getProperty(skey)); 81 | CACHE.put(kkey, System.getProperty(skey)); 82 | } 83 | } 84 | } else { 85 | debug("Loading configuration from %s.", url); 86 | CACHE.load(Dropship.class.getClassLoader().getResourceAsStream(DEFAULT_CONFIG_FILE_NAME)); 87 | for (Map.Entry entry : CACHE.entrySet()) { 88 | debug(" %s: %s = %s", DEFAULT_CONFIG_FILE_NAME, entry.getKey(), entry.getValue()); 89 | } 90 | } 91 | for (Map.Entry entry : loadPackageInformation().entrySet()) { 92 | //noinspection UseOfPropertiesAsHashtable 93 | CACHE.put(entry.getKey(), entry.getValue()); 94 | debug(" MANIFEST: %s = %s", entry.getKey(), entry.getValue()); 95 | } 96 | loaded = true; 97 | return CACHE; 98 | } 99 | 100 | static Properties loadBootstrapPropertiesUnchecked() { 101 | try { 102 | return loadBootstrapProperties(); 103 | } catch (Exception e) { 104 | throw propagate(e); 105 | } 106 | } 107 | 108 | private static Properties loadPackageInformation() { 109 | Properties versionProperties = new Properties(); 110 | Optional manifest = loadManifest(); 111 | if (manifest.isPresent()) { 112 | for (Map.Entry attributeEntry : manifest.get().getMainAttributes().entrySet()) { 113 | String manifestEntryKey = attributeEntry.getKey().toString().toLowerCase(); 114 | if (manifestEntryKey.startsWith("X-")) { 115 | manifestEntryKey = manifestEntryKey.substring(2); 116 | } 117 | String versionPropertiesKey = "dropship." + CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, manifestEntryKey); 118 | System.setProperty(versionPropertiesKey, attributeEntry.getValue().toString()); 119 | versionProperties.setProperty(versionPropertiesKey, attributeEntry.getValue().toString()); 120 | } 121 | } 122 | return versionProperties; 123 | } 124 | 125 | private static Optional loadManifest() { 126 | Optional location = Optional.fromNullable(Resources.getResource(CharMatcher.is('.').replaceFrom(Dropship.class.getName(), '/') + ".class")); 127 | if (!location.isPresent()) { 128 | return Optional.absent(); 129 | } 130 | 131 | try { 132 | String classPath = location.get().toString(); 133 | if (!classPath.startsWith("jar")) { 134 | // Class not from JAR 135 | return Optional.absent(); 136 | } 137 | String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) + "/META-INF/MANIFEST.MF"; 138 | return Optional.of(new Manifest(new URL(manifestPath).openStream())); 139 | } catch (MalformedURLException e) { 140 | return Optional.absent(); 141 | } catch (IOException e) { 142 | return Optional.absent(); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /mvn-classloader-settings/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | org.sonatype.oss 5 | oss-parent 6 | 7 7 | 8 | 4.0.0 9 | com.github.igor-suhorukov 10 | mvn-classloader-settings 11 | pom 12 | 1.11-SNAPSHOT 13 | 14 | 15 | 16 | Igor Sukhorukov 17 | http://suhorukov.blogspot.ru 18 | igor.suhorukov[skip this] at gmail.com 19 | 20 | 21 | 22 | 23 | Apache License, Version 2.0 24 | http://www.apache.org/licenses/LICENSE-2.0.txt 25 | repo 26 | 27 | 28 | 29 | https://github.com/igor-suhorukov/mvn-classloader.git 30 | scm:git:git://github.com/igor-suhorukov/mvn-classloader.git 31 | scm:git:ssh://git@github.com/igor-suhorukov/mvn-classloader.git 32 | HEAD 33 | 34 | 35 | 36 | 1.8 37 | 1.8 38 | 1.1.0 39 | 3.3.9 40 | 1.0-alpha-33 41 | 1.3.2.RELEASE 42 | 43 | 44 | -------------------------------------------------------------------------------- /mvn-classloader/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | com.github.igor-suhorukov 4 | mvn-classloader-settings 5 | 1.11-SNAPSHOT 6 | ../mvn-classloader-settings 7 | 8 | 4.0.0 9 | mvn-classloader 10 | jar 11 | 1.11-SNAPSHOT 12 | 13 | 14 | 15 | org.apache.maven.plugins 16 | maven-shade-plugin 17 | 2.4 18 | 19 | 20 | package 21 | 22 | shade 23 | 24 | 25 | 26 | 27 | 28 | 29 | com.google 30 | com.github.igorsuhorukov.google 31 | 32 | 33 | com.github.smreed 34 | com.github.igorsuhorukov.smreed 35 | 36 | 37 | org.apache 38 | com.github.igorsuhorukov.apache 39 | 40 | 41 | org.codehaus 42 | com.github.igorsuhorukov.codehaus 43 | 44 | 45 | org.springframework 46 | com.github.igorsuhorukov.springframework 47 | 48 | 49 | org.eclipse 50 | com.github.igorsuhorukov.eclipse 51 | 52 | 53 | jodd 54 | com.github.igorsuhorukov.jodd 55 | 56 | 57 | org.sonatype 58 | com.github.igorsuhorukov.sonatype 59 | 60 | 61 | 62 | 63 | *:* 64 | 65 | META-INF/plexus/** 66 | META-INF/sisu/** 67 | LICENSE 68 | licenses/** 69 | about.html 70 | 71 | 72 | 73 | 74 | 75 | 76 | com.github.igorsuhorukov.smreed.dropship.Dropship 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | com.github.igor-suhorukov 87 | aether-grape 88 | bundled 89 | jar 90 | ${project.version} 91 | 92 | 93 | org.springframework.boot 94 | spring-boot-cli 95 | 96 | 97 | 98 | 99 | com.github.igor-suhorukov 100 | dropship 101 | ${project.version} 102 | 103 | 104 | com.google.guava 105 | guava 106 | 107 | 108 | com.github.igor-suhorukov 109 | aether-grape 110 | 111 | 112 | commons-logging 113 | commons-logging 114 | 115 | 116 | spice 117 | spice-classman 118 | 119 | 120 | org.jodd 121 | jodd-core 122 | 123 | 124 | 125 | 126 | com.google.guava 127 | guava 128 | 29.0-jre 129 | provided 130 | true 131 | 132 | 133 | com.github.igor-suhorukov 134 | url-stream-handler-provider 135 | 1.2 136 | true 137 | 138 | 139 | -------------------------------------------------------------------------------- /mvn-classloader/src/main/java/com/github/igorsuhorukov/maven/DependenciesResolver.java: -------------------------------------------------------------------------------- 1 | package com.github.igorsuhorukov.maven; 2 | 3 | import java.net.URL; 4 | import java.util.List; 5 | import java.util.Set; 6 | 7 | public interface DependenciesResolver { 8 | List resolve(Set dependenciesUrl); 9 | } 10 | -------------------------------------------------------------------------------- /mvn-classloader/src/main/java/com/github/igorsuhorukov/maven/MavenDependenciesResolver.java: -------------------------------------------------------------------------------- 1 | package com.github.igorsuhorukov.maven; 2 | 3 | import com.github.smreed.dropship.ClassLoaderBuilder; 4 | import com.github.smreed.dropship.MavenClassLoader; 5 | 6 | import java.net.URL; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.Objects; 10 | import java.util.Set; 11 | 12 | public class MavenDependenciesResolver implements DependenciesResolver { 13 | 14 | @Override 15 | public List resolve(Set dependenciesUrl) { 16 | final ClassLoaderBuilder classLoaderBuilder = MavenClassLoader.usingCentralRepo(); 17 | final ArrayList classPath = new ArrayList<>(); 18 | dependenciesUrl.stream().filter(Objects::nonNull).filter(url -> url.startsWith("mvn:/")).map(dependency -> { 19 | try { 20 | String[] protocol = dependency.replace("mvn:/", "").split("\\?"); 21 | if (protocol.length == 2) { 22 | String repository = protocol[1]; 23 | String artifact = protocol[0]; 24 | return MavenClassLoader.using(repository).getArtifactUrlsCollection(artifact, null); 25 | } else { 26 | return classLoaderBuilder.getArtifactUrlsCollection(protocol[0], null); 27 | } 28 | } catch (Exception e) { 29 | throw new RuntimeException(); 30 | } 31 | }).forEach(classPath::addAll); 32 | return classPath; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /mvn-classloader/src/main/java/com/github/igorsuhorukov/service/MavenServiceLoader.java: -------------------------------------------------------------------------------- 1 | package com.github.igorsuhorukov.service; 2 | 3 | import com.github.smreed.dropship.ClassLoaderBuilder; 4 | import com.github.smreed.dropship.MavenClassLoader; 5 | 6 | import java.net.URLClassLoader; 7 | import java.util.ArrayList; 8 | import java.util.Collection; 9 | import java.util.Iterator; 10 | import java.util.ServiceLoader; 11 | 12 | public class MavenServiceLoader { 13 | 14 | public static Collection loadServices(String gav, Class serviceClass){ 15 | return loadServices(gav, null, serviceClass); 16 | } 17 | 18 | public static Collection loadServices(String gav, String repo, Class serviceClass){ 19 | URLClassLoader mavenClassLoader = getClassLoaderBuilder(repo).forMavenCoordinates(gav); 20 | return loadServices(mavenClassLoader, serviceClass); 21 | } 22 | 23 | public static Collection loadServices(ClassLoader classLoader, Class serviceClass) { 24 | Thread currentThread = Thread.currentThread(); 25 | ClassLoader prevContextClassLoader = currentThread.getContextClassLoader(); 26 | currentThread.setContextClassLoader(classLoader); 27 | try { 28 | ServiceLoader serviceLoader = ServiceLoader.load(serviceClass, classLoader); 29 | Iterator iterator = serviceLoader.iterator(); 30 | Collection services = new ArrayList(); 31 | while (iterator.hasNext()) { 32 | T service = iterator.next(); 33 | if(service!=null){ 34 | services.add(service); 35 | } 36 | } 37 | return services; 38 | } finally { 39 | currentThread.setContextClassLoader(prevContextClassLoader); 40 | } 41 | } 42 | 43 | private static ClassLoaderBuilder getClassLoaderBuilder(String repo) { 44 | ClassLoaderBuilder classLoaderBuilder; 45 | if (repo!=null && !repo.isEmpty()){ 46 | classLoaderBuilder = MavenClassLoader.using(repo); 47 | } else { 48 | classLoaderBuilder = MavenClassLoader.usingCentralRepo(); 49 | } 50 | return classLoaderBuilder; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /mvn-classloader/src/main/java/com/github/igorsuhorukov/url/UrlBootstrapLoader.java: -------------------------------------------------------------------------------- 1 | package com.github.igorsuhorukov.url; 2 | 3 | import org.codehaus.plexus.util.IOUtil; 4 | 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.lang.reflect.Method; 8 | import java.net.URL; 9 | import java.net.URLClassLoader; 10 | import java.util.jar.Attributes; 11 | import java.util.jar.JarInputStream; 12 | import java.util.jar.Manifest; 13 | 14 | public class UrlBootstrapLoader { 15 | 16 | private static final String MAIN_CLASS = "Main-Class"; 17 | private static final String MANIFEST_MF = "META-INF/MANIFEST.MF"; 18 | 19 | public static void main(String[] args) throws Exception{ 20 | String artifactUrl = System.getProperty("artifactUrl"); 21 | String artifactUrlReference = System.getProperty("artifactUrlReference"); 22 | if(artifactUrlReference!=null && !artifactUrlReference.isEmpty()){ 23 | if(artifactUrl!=null && !artifactUrl.isEmpty()){ 24 | throw new IllegalArgumentException("use either artifactUrl or artifactUrlReference system property"); 25 | } 26 | artifactUrl = getReference(artifactUrlReference); 27 | } 28 | if(artifactUrl==null || artifactUrl.isEmpty()){ 29 | throw new IllegalArgumentException("System property artifactUrl is empty"); 30 | } 31 | URL artifactJavaUrl = new URL(artifactUrl); 32 | try (URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{artifactJavaUrl})){ 33 | String mainClass = getMainClassFromManifest(artifactUrl, artifactJavaUrl); 34 | ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); 35 | Thread.currentThread().setContextClassLoader(urlClassLoader); 36 | try { 37 | invokeMainMethod(urlClassLoader, mainClass, args); 38 | } finally { 39 | Thread.currentThread().setContextClassLoader(contextClassLoader); 40 | } 41 | } 42 | } 43 | 44 | private static String getMainClassFromManifest(String artifactUrl, URL artifactJavaUrl) throws IOException { 45 | try (JarInputStream manifestStream = new JarInputStream(artifactJavaUrl.openStream())){ 46 | Manifest manifest = manifestStream.getManifest(); 47 | if(manifest==null){ 48 | throw new IllegalArgumentException(MANIFEST_MF +" not found in " + artifactUrl); 49 | } 50 | Attributes mainAttributes = manifest.getMainAttributes(); 51 | if(mainAttributes==null){ 52 | throw new IllegalArgumentException(MANIFEST_MF+" is empty"); 53 | } 54 | String mainClass = mainAttributes.getValue(MAIN_CLASS); 55 | if(mainClass==null || mainClass.isEmpty()){ 56 | throw new IllegalArgumentException(MAIN_CLASS+" attribute not found in "+MANIFEST_MF); 57 | } 58 | return mainClass; 59 | } 60 | } 61 | 62 | private static void invokeMainMethod(URLClassLoader urlClassLoader, String mainClassName, String[] args){ 63 | try { 64 | Class mainClass = urlClassLoader.loadClass(mainClassName); 65 | Method mainMethod = mainClass.getDeclaredMethod("main", new Class[]{String[].class}); 66 | mainMethod.invoke((Object)null, new Object[]{args}); 67 | } catch (Exception e) { 68 | throw new IllegalArgumentException(e); 69 | } 70 | } 71 | 72 | private static String getReference(String artifactUrlReference) throws IOException { 73 | URL referenceUrl = new URL(artifactUrlReference); 74 | InputStream referenceUrlStream = referenceUrl.openStream(); 75 | String reference = IOUtil.toString(referenceUrlStream); 76 | if(reference==null || reference.isEmpty() || reference.contains("\n")){ 77 | throw new IllegalArgumentException("artifactUrlReference resource should be non empty"); 78 | } 79 | return reference; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /mvn-classloader/src/main/java/com/github/igorsuhorukov/url/handler/ChainURLStreamHandlerFactory.java: -------------------------------------------------------------------------------- 1 | package com.github.igorsuhorukov.url.handler; 2 | 3 | import java.net.URLStreamHandler; 4 | import java.net.URLStreamHandlerFactory; 5 | import java.util.logging.Logger; 6 | 7 | /** 8 | * Class allow to make chain of URLStreamHandlerFactory invocations 9 | */ 10 | public class ChainURLStreamHandlerFactory implements URLStreamHandlerFactory{ 11 | 12 | final static private Logger logger = Logger.getLogger(ChainURLStreamHandlerFactory.class.getName()); 13 | 14 | public URLStreamHandlerFactory[] streamHandlerFactories; 15 | 16 | public ChainURLStreamHandlerFactory(URLStreamHandlerFactory[] streamHandlerFactories) { 17 | validateInputChain(streamHandlerFactories); 18 | this.streamHandlerFactories = streamHandlerFactories; 19 | } 20 | 21 | @Override 22 | public URLStreamHandler createURLStreamHandler(String protocol) { 23 | for(URLStreamHandlerFactory urlStreamHandlerFactory: streamHandlerFactories){ 24 | URLStreamHandler urlStreamHandler = null; 25 | try { 26 | urlStreamHandler = urlStreamHandlerFactory.createURLStreamHandler(protocol); 27 | } catch (Exception ignore) { 28 | logger.warning(ignore.getMessage()); 29 | } 30 | if(urlStreamHandler!=null){ 31 | return urlStreamHandler; 32 | } 33 | } 34 | return null; 35 | } 36 | 37 | private static void validateInputChain(URLStreamHandlerFactory[] streamHandlerFactories) { 38 | if(streamHandlerFactories==null || streamHandlerFactories.length==0){ 39 | throw new IllegalArgumentException("Empty URLStreamHandlerFactories array"); 40 | } 41 | for (int i = 0; i < streamHandlerFactories.length; i++) { 42 | URLStreamHandlerFactory urlStreamHandlerFactory = streamHandlerFactories[i]; 43 | if (urlStreamHandlerFactory == null) { 44 | throw new IllegalArgumentException("Null element. Index ="+i); 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /mvn-classloader/src/main/java/com/github/igorsuhorukov/url/handler/UniversalURLStreamHandlerFactory.java: -------------------------------------------------------------------------------- 1 | package com.github.igorsuhorukov.url.handler; 2 | 3 | import com.github.igorsuhorukov.url.handler.loadable.LoadableURLStreamHandlerFactory; 4 | import com.github.igorsuhorukov.url.handler.mvn.MavenURLStreamHandlerFactory; 5 | 6 | import java.net.URLStreamHandlerFactory; 7 | 8 | public class UniversalURLStreamHandlerFactory extends ChainURLStreamHandlerFactory { 9 | public UniversalURLStreamHandlerFactory() { 10 | super(new URLStreamHandlerFactory[]{new MavenURLStreamHandlerFactory(), new LoadableURLStreamHandlerFactory()}); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /mvn-classloader/src/main/java/com/github/igorsuhorukov/url/handler/loadable/LoadableHandlerProvider.java: -------------------------------------------------------------------------------- 1 | package com.github.igorsuhorukov.url.handler.loadable; 2 | 3 | import java.net.URLStreamHandler; 4 | 5 | public class LoadableHandlerProvider extends java.net.spi.URLStreamHandlerProvider{ 6 | 7 | private final static LoadableURLStreamHandlerFactory LOADABLE_URL_STREAM_HANDLER_FACTORY = new LoadableURLStreamHandlerFactory(); 8 | @Override 9 | public URLStreamHandler createURLStreamHandler(String protocol) { 10 | if(LoadableURLStreamHandlerFactory.SKIP_LIST.contains(protocol.toLowerCase())){ 11 | return null; 12 | } 13 | return LOADABLE_URL_STREAM_HANDLER_FACTORY.createURLStreamHandler(protocol); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /mvn-classloader/src/main/java/com/github/igorsuhorukov/url/handler/loadable/LoadableURLStreamHandlerFactory.java: -------------------------------------------------------------------------------- 1 | package com.github.igorsuhorukov.url.handler.loadable; 2 | 3 | import com.github.igorsuhorukov.service.MavenServiceLoader; 4 | import com.github.igorsuhorukov.url.handler.ChainURLStreamHandlerFactory; 5 | import com.google.common.cache.CacheBuilder; 6 | import com.google.common.cache.CacheLoader; 7 | import com.google.common.cache.LoadingCache; 8 | 9 | import java.net.URLStreamHandler; 10 | import java.net.URLStreamHandlerFactory; 11 | import java.util.Collection; 12 | import java.util.Set; 13 | import java.util.TreeSet; 14 | import java.util.concurrent.ExecutionException; 15 | import java.util.logging.Logger; 16 | 17 | /** 18 | * URLStreamHandlerFactory based on loadable maven modules 19 | */ 20 | public class LoadableURLStreamHandlerFactory implements java.net.URLStreamHandlerFactory{ 21 | 22 | final static private Logger logger = Logger.getLogger(LoadableURLStreamHandlerFactory.class.getName()); 23 | public static final String HANDLER_FACTORT_ARTIFACT = "com.github.igor-suhorukov:%s-url-handler:LATEST"; 24 | static final Set SKIP_LIST = new TreeSet(){{ 25 | add("mvn");add("file");add("jar");add("http");add("https");add("ftp");add("mailto");}}; 26 | 27 | private static LoadingCache urlStreamHandlerFactoryLoadingCache = CacheBuilder 28 | .newBuilder().build(new CacheLoader() { 29 | @Override 30 | public URLStreamHandlerFactory load(String protocol) throws Exception { 31 | return loadUrlStreamHandlerFactory(protocol); 32 | } 33 | }); 34 | 35 | @Override 36 | public URLStreamHandler createURLStreamHandler(String protocol) { 37 | if(protocol!=null && !SKIP_LIST.contains(protocol.toLowerCase())) { 38 | try { 39 | URLStreamHandlerFactory urlStreamHandlerFactory = urlStreamHandlerFactoryLoadingCache.get(protocol); 40 | return urlStreamHandlerFactory!=null ? urlStreamHandlerFactory.createURLStreamHandler(protocol) : null; 41 | } catch (ExecutionException e) { 42 | logger.severe(e.getMessage()); 43 | } 44 | } 45 | return null; 46 | } 47 | 48 | static URLStreamHandlerFactory loadUrlStreamHandlerFactory(String protocol) { 49 | String artifact = System.getProperty(protocol+"MvnUrlHandler",String.format(HANDLER_FACTORT_ARTIFACT,protocol)); 50 | 51 | Collection urlStreamHandlerFactories = MavenServiceLoader.loadServices( 52 | artifact, URLStreamHandlerFactory.class); 53 | 54 | if(urlStreamHandlerFactories.isEmpty()){ 55 | return null; 56 | } else if(urlStreamHandlerFactories.size() == 1){ 57 | return urlStreamHandlerFactories.iterator().next(); 58 | } else { 59 | URLStreamHandlerFactory[] streamHandlerFactories = urlStreamHandlerFactories.toArray( 60 | new URLStreamHandlerFactory[urlStreamHandlerFactories.size()]); 61 | return new ChainURLStreamHandlerFactory(streamHandlerFactories); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /mvn-classloader/src/main/java/com/github/igorsuhorukov/url/handler/mvn/Handler.java: -------------------------------------------------------------------------------- 1 | package com.github.igorsuhorukov.url.handler.mvn; 2 | 3 | import com.github.smreed.dropship.ClassLoaderBuilder; 4 | import com.github.smreed.dropship.MavenClassLoader; 5 | import org.springframework.util.StringUtils; 6 | 7 | import java.io.IOException; 8 | import java.net.URL; 9 | import java.net.URLConnection; 10 | import java.net.URLStreamHandler; 11 | 12 | public class Handler extends URLStreamHandler { 13 | 14 | @Override 15 | protected URLConnection openConnection(URL url) throws IOException { 16 | try { 17 | String repository = url.getQuery(); 18 | ClassLoaderBuilder classLoaderBuilder; 19 | if (repository == null){ 20 | classLoaderBuilder = MavenClassLoader.usingCentralRepo(); 21 | } else { 22 | classLoaderBuilder = MavenClassLoader.using(repository); 23 | } 24 | return classLoaderBuilder.resolveArtifact(getUrlPath(url)).openConnection(); 25 | } catch (Exception e) { 26 | throw new IOException(e); 27 | } 28 | } 29 | 30 | private static String getUrlPath(URL url) { 31 | if(StringUtils.hasText(url.getPath()) && url.getPath().startsWith("/")){ 32 | return url.getPath().substring(1); 33 | } else { 34 | return url.getPath(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /mvn-classloader/src/main/java/com/github/igorsuhorukov/url/handler/mvn/MavenHandlerProvider.java: -------------------------------------------------------------------------------- 1 | package com.github.igorsuhorukov.url.handler.mvn; 2 | 3 | import java.net.URLStreamHandler; 4 | 5 | public class MavenHandlerProvider extends java.net.spi.URLStreamHandlerProvider { 6 | 7 | private final static MavenURLStreamHandlerFactory MAVEN_URL_STREAM_HANDLER_FACTORY = 8 | new MavenURLStreamHandlerFactory(); 9 | 10 | @Override 11 | public URLStreamHandler createURLStreamHandler(String protocol) { 12 | return MAVEN_URL_STREAM_HANDLER_FACTORY.createURLStreamHandler(protocol); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /mvn-classloader/src/main/java/com/github/igorsuhorukov/url/handler/mvn/MavenURLStreamHandlerFactory.java: -------------------------------------------------------------------------------- 1 | package com.github.igorsuhorukov.url.handler.mvn; 2 | 3 | import java.net.URLStreamHandler; 4 | import java.util.logging.Logger; 5 | 6 | /** 7 | * Support URL syntax for maven repository artifacts. 8 | * 9 | * For example mvn:/com.github.igor-suhorukov:aspectj-scripting:pom:1.3?https://jcenter.bintray.com 10 | */ 11 | public class MavenURLStreamHandlerFactory implements java.net.URLStreamHandlerFactory{ 12 | 13 | private static final Logger logger = Logger.getLogger(MavenURLStreamHandlerFactory.class.getName()); 14 | 15 | public static final String MVN_PROTOCOL = "mvn"; 16 | 17 | protected java.net.URLStreamHandlerFactory urlStreamHandlerFactory; 18 | 19 | public MavenURLStreamHandlerFactory() { 20 | } 21 | 22 | public MavenURLStreamHandlerFactory(java.net.URLStreamHandlerFactory urlStreamHandlerFactory) { 23 | this.urlStreamHandlerFactory = urlStreamHandlerFactory; 24 | } 25 | 26 | @Override 27 | public URLStreamHandler createURLStreamHandler(String protocol) { 28 | if (MVN_PROTOCOL.equals(protocol)){ 29 | return new Handler(); 30 | } else{ 31 | if(urlStreamHandlerFactory !=null){ 32 | try { 33 | return urlStreamHandlerFactory.createURLStreamHandler(protocol); 34 | } catch (Exception ignore) { 35 | logger.warning(ignore.getMessage()); 36 | return null; 37 | } 38 | } else { 39 | return null; 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /mvn-classloader/src/main/resources/META-INF/services/com.github.igorsuhorukov.springframework.boot.cli.compiler.grape.RepositorySystemSessionAutoConfiguration: -------------------------------------------------------------------------------- 1 | com.github.igorsuhorukov.springframework.boot.cli.compiler.grape.SettingsXmlRepositorySystemSessionAutoConfiguration 2 | com.github.igorsuhorukov.springframework.boot.cli.compiler.grape.GrapeRootRepositorySystemSessionAutoConfiguration -------------------------------------------------------------------------------- /mvn-classloader/src/main/resources/META-INF/services/groovy.grape.GrapeEngine: -------------------------------------------------------------------------------- 1 | com.github.igorsuhorukov.DefaultAetherGrapeEngine -------------------------------------------------------------------------------- /mvn-classloader/src/main/resources/META-INF/services/java.net.URLStreamHandlerFactory: -------------------------------------------------------------------------------- 1 | com.github.igorsuhorukov.url.handler.mvn.MavenURLStreamHandlerFactory -------------------------------------------------------------------------------- /mvn-classloader/src/main/resources/META-INF/services/java.net.spi.URLStreamHandlerProvider: -------------------------------------------------------------------------------- 1 | com.github.igorsuhorukov.url.handler.loadable.LoadableHandlerProvider 2 | com.github.igorsuhorukov.url.handler.mvn.MavenHandlerProvider -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.sonatype.oss 4 | oss-parent 5 | 7 6 | 7 | 4.0.0 8 | com.github.igor-suhorukov 9 | mvn-classloader-aggregation 10 | pom 11 | 1.11-SNAPSHOT 12 | 13 | mvn-classloader-settings 14 | dropship 15 | aether-grape 16 | mvn-classloader 17 | 18 | 19 | https://github.com/igor-suhorukov/mvn-classloader.git 20 | scm:git:git://github.com/igor-suhorukov/mvn-classloader.git 21 | scm:git:ssh://git@github.com/igor-suhorukov/mvn-classloader.git 22 | HEAD 23 | 24 | 25 | 26 | 27 | org.apache.maven.plugins 28 | maven-deploy-plugin 29 | 2.7 30 | 31 | true 32 | 33 | 34 | 35 | 36 | 37 | --------------------------------------------------------------------------------