/**
* Copyright (c) 2013-2016, The SeedStack authors <http://seedstack.org>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.seedstack.seed.web.internal.scan.tomcat;
import com.google.common.base.Joiner;
import com.google.common.collect.AbstractIterator;
import org.reflections.vfs.Vfs;
import org.seedstack.seed.SeedException;
import org.seedstack.seed.web.internal.WebErrorCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.naming.NameClassPair;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import java.net.URL;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
/**
* VFS directory implementation for JNDI directory scanning.
*/
class JndiInputDir implements Vfs.Dir {
private static final Logger LOGGER = LoggerFactory.getLogger(JndiInputDir.class);
private final URL url;
private final List<String> fullPath = new ArrayList<>();
private final Deque<DirContext> dirContextDeque = new ArrayDeque<>();
private final Deque<NamingEnumeration<NameClassPair>> enumerationDeque = new ArrayDeque<>();
JndiInputDir(URL url) {
this.url = url;
}
@Override
public Iterable<Vfs.File> getFiles() {
return () -> new AbstractIterator<Vfs.File>() {
{
try {
Object content = url.openConnection().getContent();
dirContextDeque.push((DirContext) content);
enumerationDeque.push(((DirContext) content).list("/"));
fullPath.add(null);
} catch (Exception e) {
LOGGER.warn("Unable to open JNDI directory at {}, ignoring it", url.toExternalForm(), e);
}
}
@Override
protected Vfs.File computeNext() {
try {
JndiInputFile nextFile = findNextFile();
if (nextFile == null) {
return endOfData();
} else {
return nextFile;
}
} catch (NamingException e) {
throw SeedException.wrap(e, WebErrorCode.UNABLE_TO_SCAN_TOMCAT_JNDI_DIRECTORY).put("url", url.toExternalForm());
}
}
};
}
@Override
public void close() {
for (DirContext dirContext : dirContextDeque) {
try {
dirContext.close();
} catch (NamingException e) {
LOGGER.warn("Unable to close JNDI directory at {}", url.toExternalForm(), e);
}
}
enumerationDeque.clear();
dirContextDeque.clear();
fullPath.clear();
}
@Override
public String getPath() {
return url.getPath();
}
private JndiInputFile findNextFile() throws NamingException {
while (!enumerationDeque.isEmpty() && enumerationDeque.peek().hasMore()) {
NameClassPair next = enumerationDeque.peek().next();
Object entry = dirContextDeque.peek().lookup(next.getName());
if (entry instanceof DirContext) {
dirContextDeque.push((DirContext) entry);
enumerationDeque.push(((DirContext) entry).list("/"));
fullPath.add(next.getName());
} else {
return new JndiInputFile(entry, String.format("%s/%s", Joiner.on("/").skipNulls().join(fullPath), next.getName()));
}
}
if (!enumerationDeque.isEmpty()) {
enumerationDeque.pop();
dirContextDeque.pop();
fullPath.remove(fullPath.size() - 1);
return findNextFile();
} else {
return null;
}
}
}