/**
* GRANITE DATA SERVICES
* Copyright (C) 2006-2015 GRANITE DATA SERVICES S.A.S.
*
* This file is part of the Granite Data Services Platform.
*
* Granite Data Services is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* Granite Data Services is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA, or see <http://www.gnu.org/licenses/>.
*/
package org.granite.scan;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import org.granite.logging.Logger;
/**
* @author Franck WOLFF
*/
public class URLScanner implements Scanner {
private static final Logger log = Logger.getLogger(URLScanner.class);
///////////////////////////////////////////////////////////////////////////
// Fields.
private final List<ScannedItemHandler> handlers = new ArrayList<ScannedItemHandler>();
private final String marker;
private final ClassLoader loader;
///////////////////////////////////////////////////////////////////////////
// Constructors.
public URLScanner(ScannedItemHandler handler) {
this(handler, null, Thread.currentThread().getContextClassLoader());
}
public URLScanner(ScannedItemHandler handler, String marker) {
this(handler, marker, Thread.currentThread().getContextClassLoader());
}
public URLScanner(ScannedItemHandler handler, ClassLoader loader) {
this(handler, null, loader);
}
public URLScanner(ScannedItemHandler handler, String marker, ClassLoader loader) {
this.marker = marker;
this.handlers.add(handler);
this.loader = loader;
}
///////////////////////////////////////////////////////////////////////////
// Properties.
public String getMarker() {
return marker;
}
public void addHandler(ScannedItemHandler handler) {
if (!handlers.contains(handler))
handlers.add(handler);
}
public void addHandlers(Collection<ScannedItemHandler> handlers) {
for (ScannedItemHandler handler : handlers)
addHandler(handler);
}
public ClassLoader getLoader() {
return loader;
}
///////////////////////////////////////////////////////////////////////////
// Scan methods.
public void scan() throws IOException {
Set<String> paths = new HashSet<String>();
if (marker == null) {
if (!(loader instanceof URLClassLoader))
throw new RuntimeException("ClassLoader used with no marker should be a URLClassLoader: " + loader);
for (URL url : ((URLClassLoader)loader).getURLs()) {
String urlPath = url.getFile();
if (urlPath.endsWith("/"))
urlPath = urlPath.substring(0, urlPath.length() - 1);
paths.add(urlPath);
}
}
else {
for (Enumeration<URL> urlEnum = loader.getResources(marker); urlEnum.hasMoreElements(); ) {
String urlPath = URLDecoder.decode(urlEnum.nextElement().getFile(), "UTF-8");
if (urlPath.startsWith("file:"))
urlPath = urlPath.substring(5);
// Jars.
if (urlPath.indexOf('!') > 0)
urlPath = urlPath.substring(0, urlPath.indexOf('!'));
// Regular directories.
else {
File dirOrArchive = new File(urlPath);
String[] tokens = marker.split("\\Q/\\E", -1);
for (int i = 0; i < tokens.length; i++)
dirOrArchive = dirOrArchive.getParentFile();
urlPath = dirOrArchive.getPath();
}
paths.add(urlPath);
}
}
for (String urlPath : paths) {
urlPath = urlPath.replace("%20", " "); // Fix for GDS-1288
File file = new File(urlPath);
if (file.isDirectory())
handleDirectory(file, file);
else
handleArchive(file);
}
}
public void handleArchive(File file) throws ZipException, IOException {
log.debug("Scanning archive %s", file != null ? file.getName() : "");
ZipFile zip = null;
try {
zip = new ZipFile(file);
}
catch (IOException e) {
log.debug(e, "Ignored non archive or corrupt file");
return;
}
ZipScannedItem markerItem = null;
if (marker != null) {
ZipEntry markerEntry = zip.getEntry(marker);
markerItem = new ZipScannedItem(this, null, zip, markerEntry);
for (ScannedItemHandler handler : handlers) {
boolean skip = handler.handleMarkerItem(markerItem);
if (skip)
return;
}
}
for (Enumeration<? extends ZipEntry> entries = zip.entries(); entries.hasMoreElements(); ) {
ZipEntry entry = entries.nextElement();
if (!entry.isDirectory() && (markerItem == null || !markerItem.getEntry().getName().equals(entry.getName()))) {
for (ScannedItemHandler handler : handlers)
handler.handleScannedItem(new ZipScannedItem(this, markerItem, zip, entry));
}
}
}
public void handleDirectory(File root, File path) {
log.debug("Scanning directory %s, %s", root != null ? root.getName() : "", path != null ? path.getName() : "");
FileScannedItem markerItem = null;
if (marker != null) {
File markerFile = new File(root, marker);
markerItem = new FileScannedItem(this, null, root, markerFile);
for (ScannedItemHandler handler : handlers) {
boolean skip = handler.handleMarkerItem(markerItem);
if (skip)
return;
}
}
handleDirectory(markerItem, root, path);
}
public void handleDirectory(FileScannedItem markerItem, File root, File path) {
for (File child : path.listFiles()) {
if (child.isDirectory())
handleDirectory(markerItem, root, child);
else if (markerItem == null || !markerItem.getFile().equals(child)) {
for (ScannedItemHandler handler : handlers)
handler.handleScannedItem(new FileScannedItem(this, markerItem, root, child));
}
}
}
}