Hello everyone,

In my company, one of our Maven projects create a war that we usually
deploy in tomcat.
For debug purpose, we added a test class that instanciate an embed Tomcat
instance to run the application from an IDE (Intellij in my case).

Using tomcat-emded-core.8.5.4, at startup, it gives me a lot of warnings
like:
*Aug 29, 2016 11:22:57 AM org.apache.tomcat.util.scan.StandardJarScanner
scan*

*WARNING: Failed to scan
[file:/home/olivier/maven/repo/net/sf/jung/jung-graph-impl/2.0.1/jung-api-2.0.1.jar]
from classloader hierarchyjava.io.FileNotFoundException:
/home/olivier/maven/repo/net/sf/jung/jung-graph-impl/2.0.1/jung-api-2.0.1.jar
(No such file or directory)*

Given maven repository architecture, it is logical that the scanner can't
find the jar as we are looking for *jung-api *in* jung-graph-impl *folder
*.*
If I instead look in
*/home/olivier/maven/repo/net/sf/jung/jung-api/2.0.1/jung-api-2.0.1.jar,* I
correctly find the wanted jar.

After investigating the troubling jars and StandardJarScanner source code,
I discovered that *jung-graph-impl* includes a MANIFEST file with the
Class-Path attribute containing
*jung-api*, among others.
When this attribute is defined, the scanner reads the path of the jar
containing the MANIFEST, and uses it to get the jars mentionned in the
classpath.

*Jar file:
/home/olivier/maven/repo/net/sf/jung/jung-graph-impl/2.0.1/jung-api-2.0.1.jar*

*-> Jar path: /home/olivier/maven/repo/net/sf/jung/jung-graph-impl/2.0.1/*
*-> Looking for
/home/olivier/maven/repo/net/sf/jung/jung-graph-impl/2.0.1/jung-api-2.0.1.jar*

For Tomcat application (installed on a computer), this is fine as wars
shipped all their needed jars in WEB-INF/lib. Thus, the algo to look for
another jar is ok.
This is less ok when Tomcat is run from an IDE, where the classpath is made
from various locations.
Eg: /opt/java/jdk1.8.0_92/bin/java -Dfile.encoding=UTF-8
*-classpath
/opt/java/jdk1.8.0_92/jre/lib/charsets.jar:/opt/java/jdk1.8.0_92/jre/lib/deploy.jar:[...
many
jars]:/home/olivier/maven/repo/net/sf/jung/jung-graph-impl/2.0.1/jung-graph-impl-2.0.1.jar:/home/olivier/maven/repo/net/sf/jung/jung-api/2.0.1/jung-api-2.0.1.jar:[...
more jars]*
MyServer

Is this a bug or is there something wrong with my Tomcat config or the jars
I use ?

I attached to the ticket the main class of my application, to show how I
create my Tomcat instance.
tomcat-embed is imported in my project by requiring the following maven
dependency:
+- org.apache.tomcat.embed:tomcat-embed-websocket:jar:8.5.4:test
|  \- org.apache.tomcat.embed:tomcat-embed-core:jar:8.5.4:test

Thanks a lot for the help
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletException;

import org.apache.catalina.startup.Tomcat;

public class MyServer {

	public static final int DEFAULT_PORT = 8081;

	/**
	 * Creates a server running application.
	 * @param port port to use
	 * @param path path to static resources to serve
	 * @return a Tomcat instance (not running)
	 * @throws IOException if it fails to create a temporary directory for Tomcat
	 * @throws ServletException if it fails to create a webapp
	 */
	public static Tomcat createServer(final int port, final String path) throws IOException, ServletException {
		final Tomcat tomcat = new Tomcat();

		tomcat.setPort(port);
		final File base = createBaseDirectory();
		tomcat.setBaseDir(base.getAbsolutePath());
		tomcat.addWebapp("", getStaticResourceBasePath(path));

		System.out.printf("Creating a server on port %d with resources from %s%n", port, path);
		return tomcat;
	}

	public static void main(final String[] args) throws Exception {
		int port = DEFAULT_PORT;
		if (args != null && args.length >= 1) {
			port = Integer.parseInt(args[0]);
		}
		final String resourcePath = args != null && args.length >= 2 ? args[1] : "default/path/to/app";

		final Tomcat server = createServer(port, resourcePath);
		server.start();
		server.getServer().await();
	}

	/**
	 * Give the path to the base directory where tomcat will work.
	 * <p/>
	 * We create a temporary folder because Tomcat will create temporary files in
	 * it and we don't want to pollute current folder.
	 * @return the base directory for tomcat.
	 * @throws IOException if we could not create the temporary directory.
	 */
	protected static File createBaseDirectory() throws IOException {
		final File base = File.createTempFile("tmp-", "");

		if (!base.delete()) {
			throw new IOException("Cannot (re)create base folder: " + base.getAbsolutePath());
		}

		if (!base.mkdir()) {
			throw new IOException("Cannot create base folder: " + base.getAbsolutePath());
		}
		return base;
	}

	/**
	 * Give the path to the static resources.
	 * <p/>
	 * The resources are located under src/main/webapp/ as of Maven convention for webapps.
	 * The output of the gulp build is the subfolder build.
	 * @param resourcePath the path to web resources to load
	 * @return the static resources folder.
	 */
	protected static String getStaticResourceBasePath(final String resourcePath) {
		// Since embedded Tomcat cannot use relative paths, we cheat by finding
		// another resource and change the end of the path to get our build
		// folder
		final String hintResource = "path/to/a/known/file.txt";
		final File hintFile = new File(Thread.currentThread().getContextClassLoader().getResource(hintResource).getFile());

		// Keep only the part before target/ and fill with our known path.
		final String hintPath = Paths.get("target", "classes", hintResource).toString();
		return hintFile.getAbsolutePath().replace(hintPath, resourcePath);
	}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org

Reply via email to