Skip to content

Commit

Permalink
New feature: thin.libs to append to classpath
Browse files Browse the repository at this point in the history
Fixes #171, #15
  • Loading branch information
dsyer committed Jan 17, 2023
1 parent 27fe148 commit 5ef85a7
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 19 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ You can set a variety of options on the command line or with system properties (
| `thin.force` | false | Force dependency resolution to happen, even if dependencies have been computed, and marked as "computed" in `thin.properties`. |
| `thin.classpath` | false | Only print the classpath. Don't run the main class. Two formats are supported: "path" and "properties". For backwards compatibility "true" or empty are equivalent to "path". |
| `thin.root` | `${user.home}/.m2` | The location of the local jar cache, laid out as a maven repository. The launcher creates a new directory here called "repository" if it doesn't exist. |
| `thin.libs` | `<empty>` | Additional classpath entries to append at runtime in the same form as you would use in `java -classpath ...`. If this property is defined then unresolved dependencies will be ignored when the classpath is computed, possibly leading to runtime class not found exceptions. |
| `thin.archive` | the same as the target archive | The archive to launch. Can be used to launch a JAR file that was build with a different version of the thin launcher, for instance, or a fat jar built by Spring Boot without the thin launcher. |
| `thin.parent` | `<empty>` | A parent archive to use for dependency management and common classpath entries. If you run two apps with the same parent, they will have a classpath that is the same, reading from left to right, until they actually differ. |
| `thin.location` | `file:.,classpath:/` | The path to directory containing thin properties files (as per `thin.name`), as a comma-separated list of resource locations (directories). These locations plus the same paths relative /META-INF will be searched. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@

import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.graph.Dependency;

import org.springframework.boot.loader.archive.Archive;
import org.springframework.boot.loader.archive.ExplodedArchive;
import org.springframework.boot.loader.archive.JarFileArchive;
Expand All @@ -53,17 +52,15 @@ public static Archive getArchive(String path) {
}
try {
return new JarFileArchive(new JarFile(file));
}
catch (IOException e) {
} catch (IOException e) {
throw new IllegalStateException("Cannot create JAR archive: " + file, e);
}
}

public static File getArchiveRoot(Archive archive) {
try {
return new File(jarFile(archive.getUrl()).toURI());
}
catch (Exception e) {
} catch (Exception e) {
throw new IllegalStateException("Cannot locate JAR archive: " + archive, e);
}
}
Expand All @@ -82,19 +79,16 @@ public static String findMainClass(Archive archive) {
return mainClass;
}
}
}
catch (Exception e) {
} catch (Exception e) {
}
try {
File root = getArchiveRoot(archive);
if (archive instanceof ExplodedArchive) {
return MainClassFinder.findSingleMainClass(root);
}
else {
} else {
return MainClassFinder.findSingleMainClass(new JarFile(root), "");
}
}
catch (Exception e) {
} catch (Exception e) {
throw new IllegalStateException("Cannot locate main class in " + archive, e);
}
}
Expand All @@ -104,8 +98,7 @@ private static URI findArchive(String path) {
if (archive != null) {
try {
return jarFile(archive.toURL()).toURI();
}
catch (Exception e) {
} catch (Exception e) {
throw new IllegalStateException("Cannot create URI for " + archive);
}
}
Expand Down Expand Up @@ -148,8 +141,7 @@ private static URL jarFile(URL url) {
}
try {
url = new URL(path);
}
catch (MalformedURLException e) {
} catch (MalformedURLException e) {
throw new IllegalStateException("Bad URL for jar file: " + path, e);
}
}
Expand All @@ -165,8 +157,7 @@ public static List<URL> nestedClasses(Archive archive, String... paths) {
extras.add(classes.getURL());
}
}
}
catch (Exception e) {
} catch (Exception e) {
throw new IllegalStateException("Cannot create urls for resources", e);
}
return extras;
Expand All @@ -189,4 +180,27 @@ private static URL[] locateFiles(URL[] urls) {
return urls;
}

public static List<Archive> getArchives(String path) {
List<Archive> list = new ArrayList<>();
for (String element : path.split(File.pathSeparator)) {
if (element.endsWith("*")) {
File dir = new File(element.substring(0, element.length() - 1));
if (dir.isDirectory()) {
for (File file : dir.listFiles()) {
if (file.getName().endsWith(".jar")) {
try {
list.add(getArchive(file.getCanonicalPath()));
} catch (IOException e) {
// ignore
}
}
}
}
} else {
list.add(getArchive(element));
}
}
return list;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,8 @@ public List<Dependency> dependencies(final Resource resource,
DependencyResolver.globals = null;
DependencyResolutionResult dependencies = result
.getDependencyResolutionResult();
if (!dependencies.getUnresolvedDependencies().isEmpty()) {
if (!dependencies.getUnresolvedDependencies().isEmpty() &&
properties.getProperty(ThinJarLauncher.THIN_LIBS, "").length()==0) {
StringBuilder builder = new StringBuilder();
for (Dependency dependency : dependencies
.getUnresolvedDependencies()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.net.URL;
import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
Expand Down Expand Up @@ -128,14 +129,22 @@ public class ThinJarLauncher extends ExecutableArchiveLauncher {

/**
* Flag to say that classloader parent should be the boot loader, not the system class
* loader. Default true;
* loader. Default true.
*/
public static final String THIN_PARENT_BOOT = "thin.parent.boot";

/**
* Additional path elements to append to classpath, with OS-specific path separator. Also
* accepts wildcards on a directory path (like java classpath). Default empty.
*/
public static final String THIN_LIBS = "thin.libs";

private StandardEnvironment environment = new StandardEnvironment();

private boolean debug;

private List<Archive> libs = new ArrayList<>();

public static void main(String[] args) throws Exception {
LogUtils.setLogLevel(Level.OFF);
new ThinJarLauncher(args).launch(args);
Expand Down Expand Up @@ -173,6 +182,7 @@ protected void launch(String[] args) throws Exception {
LogUtils.setLogLevel(Level.INFO);
}
}
this.libs.addAll(ArchiveUtils.getArchives(environment.resolvePlaceholders("${thin.libs:}")));
if (classpath) {
List<Archive> archives = getClassPathArchives();
System.out.println(classpath(archives));
Expand Down Expand Up @@ -370,8 +380,12 @@ private List<Archive> getClassPathArchives(String root) throws Exception {
profiles);
long t1 = System.currentTimeMillis();
if (log.isInfoEnabled()) {
if (!this.libs.isEmpty()) {
log.info("Adding libraries: " + this.libs);
}
log.info("Dependencies resolved in: " + (t1 - t0) + "ms");
}
archives.addAll(this.libs);
return archives;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.loader.thin;

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.File;

public class ArchiveUtilsTests {

@Test
public void resolveAll() throws Exception {
assertThat(ArchiveUtils.getArchives("src/test/resources/*").size()).isEqualTo(2);
}

@Test
public void resolveSome() throws Exception {
assertThat(ArchiveUtils.getArchives("src/test/resources/app-with-web-and-cloud-config.jar" + File.pathSeparator
+ "src/test/resources/app-with-web-in-lib-properties.jar").size()).isEqualTo(2);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,15 @@ public void authentication() throws Exception {
assertThat(dependencies.size()).isGreaterThan(16);
}

@Test
public void unresolvedButLibsProvided() throws Exception {
Resource resource = new ClassPathResource("apps/missing/pom.xml");
Properties properties = new Properties();
properties.setProperty("thin.libs", "no/such/file");
List<Dependency> dependencies = resolver.dependencies(resource, properties);
assertThat(dependencies.size()).isGreaterThan(0);
}

static Condition<Dependency> version(final String version) {
return new Condition<Dependency>("artifact matches " + version) {
@Override
Expand Down

0 comments on commit 5ef85a7

Please sign in to comment.