blob: 32db5fffcd14de936e03ce7fb6c7a9e334d42850 [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.myfaces.resource;
import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.application.ResourceVisitOption;
import org.apache.myfaces.util.lang.ClassUtils;
/**
*
* @author lu4242
*/
public class ClassLoaderResourceLoaderIterator implements Iterator<String>
{
private Iterator<String> delegate = null;
public ClassLoaderResourceLoaderIterator(URL url, String basePath,
int maxDepth, ResourceVisitOption... options)
{
if (url == null)
{
// This library does not exists for this
// ResourceLoader
delegate = null;
}
else
{
if (url.getProtocol().equals("file"))
{
try
{
File directory = new File(url.toURI());
delegate = new FileDepthIterator(directory, basePath, maxDepth, options);
}
catch (URISyntaxException e)
{
Logger log = Logger.getLogger(ClassLoaderResourceLoader.class.getName());
if (log.isLoggable(Level.WARNING))
{
log.log(Level.WARNING, "url "+url.toString()+" cannot be translated to uri: "
+e.getMessage(), e);
}
}
}
else if (isJarResourceProtocol(url.getProtocol()))
{
url = getClassLoader().getResource(basePath);
if (url != null)
{
delegate = new JarDepthIterator(url, basePath, maxDepth, options);
}
}
}
}
@Override
public boolean hasNext()
{
if (delegate != null)
{
return delegate.hasNext();
}
return false;
}
@Override
public String next()
{
if (delegate != null)
{
return delegate.next();
}
return null;
}
@Override
public void remove()
{
//No op
}
protected ClassLoader getClassLoader()
{
return ClassUtils.getContextClassLoader();
}
private static class JarDepthIterator implements Iterator<String>
{
private URL directory;
private String basePath;
private int maxDepth;
private ResourceVisitOption[] options;
private Deque<String> stack = new LinkedList<String>();
Iterator<String> iterator = null;
public JarDepthIterator(URL directory, String basePath, int maxDepth, ResourceVisitOption... options)
{
this.directory = directory;
this.basePath = basePath;
this.maxDepth = maxDepth;
this.options = options;
if (basePath.endsWith("/"))
{
basePath = basePath.substring(0, basePath.length()-1);
}
try
{
JarURLConnection conn = (JarURLConnection)directory.openConnection();
// See DIGESTER-29 for related problem
conn.setUseCaches(false);
try
{
if (conn.getJarEntry().isDirectory())
{
// Unfortunately, we have to scan all entry files
JarFile file = conn.getJarFile();
for (Enumeration<JarEntry> en = file.entries(); en.hasMoreElements();)
{
JarEntry entry = en.nextElement();
String entryName = entry.getName();
String path;
if (entryName.startsWith(basePath + '/'))
{
if (entryName.length() == basePath.length() + 1)
{
// the same string, just skip it
continue;
}
path = entryName.substring(basePath.length(), entryName.length());
if (path.endsWith("/"))
{
// Inner Directory
continue;
}
//TODO: scan listFiles
int depth = ResourceLoaderUtils.getDepth(path);
if (depth < maxDepth)
{
stack.add(path);
}
}
}
}
}
finally
{
//See TRINIDAD-73
//just close the input stream again if
//by inspecting the entries the stream
//was let open.
try
{
conn.getInputStream().close();
}
catch (Exception exception)
{
// Ignored
}
}
}
catch (IOException e)
{
// Just return null, because library version cannot be
// resolved.
Logger log = Logger.getLogger(ClassLoaderResourceLoader.class.getName());
if (log.isLoggable(Level.WARNING))
{
log.log(Level.WARNING, "IOException when scanning for resource in jar file:", e);
}
}
iterator = stack.iterator();
}
@Override
public boolean hasNext()
{
return iterator.hasNext();
}
@Override
public String next()
{
return iterator.next();
}
@Override
public void remove()
{
//No op
}
}
private static class FileDepthIterator implements Iterator<String>
{
private File directory;
private String basePath;
private int maxDepth;
private ResourceVisitOption[] options;
private Deque<File> stack;
private String basePathName;
public FileDepthIterator(File directory, String basePath, int maxDepth, ResourceVisitOption... options)
{
this.directory = directory;
this.basePath = basePath;
this.maxDepth = maxDepth;
this.options = options;
File[] list = this.directory.listFiles();
if (list != null && list.length > 0)
{
stack = new LinkedList<>();
Collections.addAll(stack, list);
}
this.basePathName = this.directory.getPath().replace(File.separatorChar, '/');
}
@Override
public boolean hasNext()
{
if (stack == null || stack.isEmpty())
{
return false;
}
File file = stack.peek();
do
{
if (file.isDirectory())
{
file = stack.pop();
int depth = ResourceLoaderUtils.getDepth(calculatePath(file));
if (depth < maxDepth)
{
File[] list = file.listFiles();
stack.addAll(Arrays.asList(list));
}
if (!stack.isEmpty())
{
file = stack.peek();
}
else
{
file = null;
}
}
}
while (file != null && file.isDirectory() && !stack.isEmpty());
return !stack.isEmpty();
}
@Override
public String next()
{
if (stack == null || stack.isEmpty())
{
return null;
}
File file = stack.pop();
do
{
if (file.isDirectory())
{
int depth = ResourceLoaderUtils.getDepth(calculatePath(file));
if (depth < maxDepth)
{
File[] list = file.listFiles();
stack.addAll(Arrays.asList(list));
}
if (!stack.isEmpty())
{
file = stack.pop();
}
else
{
file = null;
}
}
}
while (file != null && file.isDirectory() && !stack.isEmpty());
if (file != null)
{
// Calculate name based on url, basePath.
String path = calculatePath(file);
return path;
}
return null;
}
private String calculatePath(File file)
{
return (file.getPath()).substring(this.basePathName.length()).replace(File.separatorChar, '/');
}
@Override
public void remove()
{
//No op
}
}
/**
* <p>Determines whether the given URL resource protocol refers to a JAR file. Note that
* BEA WebLogic and IBM WebSphere don't use the "jar://" protocol for some reason even
* though you can treat these resources just like normal JAR files, i.e. you can ignore
* the difference between these protocols after this method has returned.</p>
*
* @param protocol the URL resource protocol you want to check
*
* @return <code>true</code> if the given URL resource protocol refers to a JAR file,
* <code>false</code> otherwise
*/
private static boolean isJarResourceProtocol(String protocol)
{
// Websphere uses the protocol "wsjar://" and Weblogic uses the protocol "zip://".
return "jar".equals(protocol) || "wsjar".equals(protocol) || "zip".equals(protocol);
}
}