blob: 41f5287e8b200bbeed6d8225c472b7d65a784c32 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.sling.testing.teleporter.client;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/** Finds and visits resources provided by ClassLoaders based
* on their path. If a path points to a folder, its child
* resources are also visited. Currently works for the "file:"
* and "jar:" resource protocols.
*/
public class ClassResourceVisitor {
private final Class<?> clazz;
private final String path;
static interface Processor {
void process(String resourcePath, InputStream resourceStream) throws IOException;
}
/** Visit the resources provided by clazz's ClassLoader,
* based on the supplied path.
*
* @param clazz
* @param path If that points to a folder, it is visited recursively
*/
public ClassResourceVisitor(Class<?> clazz, String path) {
this.clazz = clazz;
this.path = path;
}
public void visit(Processor p) throws IOException {
final URL resourceURL = clazz.getResource(path);
if(resourceURL == null) {
return;
}
final String protocol = resourceURL.getProtocol();
if("file".equals(protocol)) {
// Get base path and remove ending slash
//String basePath = clazz.getResource("/").getPath();
//basePath = basePath.substring(0, basePath.length() - 1);
final String basePath = new File(clazz.getResource("/").getPath()).getAbsolutePath();
processFile(basePath, new File(resourceURL.getPath()), p);
} else if("jar".equals(protocol)) {
// Jar entries use relative paths
final String rPath = path.startsWith("/") ? path.substring(1) : path;
final String jarFilePath = resourceURL.getPath().split("!")[0].substring("file:".length());
final JarFile jar = new JarFile(jarFilePath);
try {
final Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
final JarEntry e = entries.nextElement();
if(!e.isDirectory() && jarEntryMatches(rPath, e.getName())) {
final InputStream is = jar.getInputStream(e);
try {
p.process("/" + e.getName(), is);
} finally {
is.close();
}
}
}
} finally {
jar.close();
}
} else {
throw new IllegalArgumentException("Unkown protocol " + protocol);
}
}
/** True if the entry part matches the given path.
* @param givenPath if it ends with / it's considered a folder name, otherwise
* we check for an exact match
* @param entryPath the path of the jar entry to check
* @return true if there's a match
*/
private boolean jarEntryMatches(String givenPath, String entryPath) {
if(givenPath.endsWith("/")) {
return entryPath.startsWith(givenPath);
} else {
return entryPath.equals(givenPath);
}
}
/* Backslashes are valid in Zip-files, BUT Java expects paths to be delimited by forward slashes.
* Windows provides file paths using backslashes, which need to be converted here.
*/
static String sanitizeResourceName(String basePath, File resource) {
return resource.getAbsolutePath().substring(basePath.length()).replace("\\", "/");
}
private void processFile(String basePath, File f, Processor p) throws IOException {
if(f.isDirectory()) {
final String [] names = f.list();
if(names != null) {
for(String name : names) {
processFile(basePath, new File(f, name), p);
}
}
} else {
final InputStream is = new BufferedInputStream(new FileInputStream(f));
try {
p.process(sanitizeResourceName(basePath, f), is);
} finally {
is.close();
}
}
}
}