/** * Copyright 2015-2016 Red Hat, Inc, and individual contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.wildfly.swarm.bootstrap.modules; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; import org.jboss.modules.AbstractResourceLoader; import org.jboss.modules.ResourceLoader; import org.jboss.modules.ResourceLoaders; import org.wildfly.swarm.bootstrap.performance.Performance; import org.wildfly.swarm.bootstrap.util.TempFileManager; /** * @author Bob McWhirter */ public class NestedJarResourceLoader { private static final String JAR_SUFFIX = ".jar!"; private NestedJarResourceLoader() { } public static synchronized boolean requiresExplosion(URL base) throws IOException { try (AutoCloseable locateHandle = Performance.accumulate("Is explosion needed?")) { String urlString = base.toExternalForm(); if (urlString.startsWith("jar:file:")) { int endLoc = urlString.indexOf(JAR_SUFFIX); if (endLoc > 0) { String jarPath = urlString.substring(9, endLoc + 4); File exp = exploded.get(jarPath); if (exp != null) { return true; } if (explosionNotRequired.contains(jarPath)) { return false; } try (JarFile jarFile = new JarFile(jarPath)) { Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry each = entries.nextElement(); if (!each.isDirectory()) { if (each.getName().startsWith("modules") && !each.getName().endsWith("/module.xml")) { return true; } } } } explosionNotRequired.add(jarPath); } } } catch (Exception e) { throw new RuntimeException(e); } return false; } public static synchronized Path explodedJar(URL base) throws IOException { if (!requiresExplosion(base)) { return null; } try (AutoCloseable locateHandle = Performance.accumulate("Exploded JAR locating")) { String urlString = base.toExternalForm(); if (urlString.startsWith("jar:file:")) { int endLoc = urlString.indexOf(JAR_SUFFIX); if (endLoc > 0) { String jarPath = urlString.substring(9, endLoc + 4); File exp = exploded.get(jarPath); if (exp == null) { try (AutoCloseable explodingHandle = Performance.accumulate("Exploding JAR")) { exp = TempFileManager.INSTANCE.newTempDirectory("module-jar", ".jar_d"); try (JarFile jarFile = new JarFile(jarPath)) { Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry each = entries.nextElement(); if (!each.isDirectory()) { File out = new File(exp, each.getName()); out.getParentFile().mkdirs(); InputStream in = jarFile.getInputStream(each); Files.copy(in, out.toPath(), StandardCopyOption.REPLACE_EXISTING); in.close(); } } } exploded.put(jarPath, exp); } } String remainder = urlString.substring(endLoc + JAR_SUFFIX.length()); if (remainder.startsWith("/") || remainder.startsWith("\\")) { remainder = remainder.substring(1); } return exp.toPath().resolve(remainder); } } return null; } catch (Exception e) { throw new RuntimeException(e); } } public static ResourceLoader loaderFor(URL base, String rootPath, String loaderPath, String loaderName) throws IOException { Path exp = explodedJar(base); String urlString = base.toExternalForm(); if (exp != null) { int endLoc = urlString.indexOf(JAR_SUFFIX); if (endLoc > 0) { Path resourceRoot = exp.resolve(loaderPath); if (!Files.isDirectory(resourceRoot) && (resourceRoot.getFileName().toString().endsWith(".jar") || resourceRoot.getFileName().toString().endsWith(".war"))) { JarFile jar = new JarFile(resourceRoot.toFile()); return ResourceLoaders.createJarResourceLoader(loaderName, jar); } else { return ResourceLoaders.createFileResourceLoader(loaderName, resourceRoot.toFile()); } } } else if (urlString.startsWith("file:")) { if (loaderName.endsWith(".jar") || loaderName.endsWith(".war")) { return ResourceLoaders.createJarResourceLoader( loaderName, new JarFile(new File(urlString.substring(5), loaderPath))); } return ResourceLoaders.createFileResourceLoader( loaderPath, new File(urlString.substring(5)) ); } else { return new AbstractResourceLoader() { }; } throw new IllegalArgumentException("Illegal module loader base: " + base + " // " + loaderPath + " // " + loaderName); } private static Map<String, File> exploded = new HashMap<>(); private static Set<String> explosionNotRequired = new HashSet<>(); }