/** * Copyright 2010-2015 Axel Fontaine * <p/> * 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 * <p/> * http://www.apache.org/licenses/LICENSE-2.0 * <p/> * 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 com.contrastsecurity.cassandra.migration.utils.scanner.classpath; import java.io.IOException; import java.net.JarURLConnection; import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; import java.util.Enumeration; import java.util.Set; import java.util.TreeSet; import java.util.jar.JarEntry; import java.util.jar.JarFile; /** * ClassPathLocationScanner for jar files. */ public class JarFileClassPathLocationScanner implements ClassPathLocationScanner { public Set<String> findResourceNames(String location, URL locationUrl) throws IOException { JarFile jarFile = getJarFromUrl(locationUrl); try { // For Tomcat and non-expanded WARs. String prefix = jarFile.getName().toLowerCase().endsWith(".war") ? "WEB-INF/classes/" : ""; return findResourceNamesFromJarFile(jarFile, prefix, location); } finally { jarFile.close(); } } /** * Retrieves the Jar file represented by this URL. * * @param locationUrl The URL of the jar. * @return The jar file. * @throws IOException when the jar could not be resolved. */ private JarFile getJarFromUrl(URL locationUrl) throws IOException { URLConnection con = locationUrl.openConnection(); if (con instanceof JarURLConnection) { // Should usually be the case for traditional JAR files. JarURLConnection jarCon = (JarURLConnection) con; jarCon.setUseCaches(false); return jarCon.getJarFile(); } // No JarURLConnection -> need to resort to URL file parsing. // We'll assume URLs of the format "jar:path!/entry", with the protocol // being arbitrary as long as following the entry format. // We'll also handle paths with and without leading "file:" prefix. String urlFile = locationUrl.getFile(); int separatorIndex = urlFile.indexOf("!/"); if (separatorIndex != -1) { String jarFileUrl = urlFile.substring(0, separatorIndex); if (jarFileUrl.startsWith("file:")) { try { return new JarFile(new URL(jarFileUrl).toURI().getSchemeSpecificPart()); } catch (URISyntaxException ex) { // Fallback for URLs that are not valid URIs (should hardly ever happen). return new JarFile(jarFileUrl.substring("file:".length())); } } return new JarFile(jarFileUrl); } return new JarFile(urlFile); } /** * Finds all the resource names contained in this directory within this jar file. * * @param jarFile The jar file. * @param prefix The prefix to ignore within the jar file. * @param location The location to look under. * @return The resource names. * @throws IOException when reading the jar file failed. */ private Set<String> findResourceNamesFromJarFile(JarFile jarFile, String prefix, String location) throws IOException { String toScan = prefix + location + (location.endsWith("/") ? "" : "/"); Set<String> resourceNames = new TreeSet<String>(); Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { String entryName = entries.nextElement().getName(); if (entryName.startsWith(toScan)) { resourceNames.add(entryName.substring(prefix.length())); } } return resourceNames; } }