/**
* Get more info at : www.jrebirth.org .
* Copyright JRebirth.org © 2011-2013
* Contact : sebastien.bordes@jrebirth.org
*
* 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
*
* 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.jrebirth.af.core.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarFile;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.jrebirth.af.api.log.JRLogger;
import org.jrebirth.af.core.log.JRLoggerFactory;
import com.sun.javaws.jnl.JARDesc;
import com.sun.jnlp.JNLPClassLoaderIf;
/**
* The class <strong>ClassUtility</strong>.
*
* Some Useful class utilities to perform introspection.
*
* @author Sébastien Bordes
*/
public final class ClasspathUtility implements UtilMessages {
/** The separator used for serialization. */
public static final String SEPARATOR = "|";
/** The class logger. */
private static final JRLogger LOGGER = JRLoggerFactory.getLogger(ClasspathUtility.class);
/** The classpath of the current java executable or current folder if not defined. */
private static final String CLASSPATH = System.getProperty("java.class.path", ".");
/** String to separate all classpath entries. */
private static final String CLASSPATH_SEPARATOR = System.getProperty("path.separator");
/**
* Private Constructor.
*/
private ClasspathUtility() {
// Nothing to do
}
/**
* Retrieve all resources that match the search pattern from the java.class.path.
*
* @param searchPattern the pattern used to filter all matching files
*
* @return Sorted list of resources that match the pattern
*/
public static Collection<String> getClasspathResources(final Pattern searchPattern) {
final List<String> resources = new ArrayList<>();
final ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (hasJavaWebstartLibrary() && cl instanceof JNLPClassLoaderIf) {
LOGGER.log(USE_JNLP_CLASSLOADER);
final JNLPClassLoaderIf wsLoader = (JNLPClassLoaderIf) cl;
// System.out.println("URLs "+ wsLoader.getURLs());
for (final JARDesc jd : wsLoader.getLaunchDesc().getResources().getLocalJarDescs()) {
try {
final JarFile jarFile = wsLoader.getJarFile(jd.getLocation());
LOGGER.log(PARSE_CACHED_JAR_FILE, jarFile.getName(), jd.getLocationString());
resources.addAll(getResources(jarFile.getName(), searchPattern, true));
} catch (final IOException e) {
LOGGER.log(CANT_READ_CACHED_JAR_FILE, jd.getLocation());
}
}
} else {
LOGGER.log(USE_DEFAULT_CLASSLOADER);
final String[] classpathEntries = CLASSPATH.split(CLASSPATH_SEPARATOR);
for (final String urlEntry : classpathEntries) {
// Parse the classpath entry and apply the given pattern as filter
resources.addAll(getResources(urlEntry, searchPattern, false));
}
}
// Sort resources
Collections.sort(resources);
return resources;
}
/**
* Check that javaws.jar is accessible.
*
* @return true if javaws.jar is accessible
*/
private static boolean hasJavaWebstartLibrary() {
boolean hasWebStartLibrary = true;
try {
Class.forName("com.sun.jnlp.JNLPClassLoaderIf");
} catch (final ClassNotFoundException e) {
hasWebStartLibrary = false;
}
return hasWebStartLibrary;
}
/**
* Search all files that match the given Regex pattern.
*
* @param classpathEntryPath the root folder used for search
* @param searchPattern the regex pattern used as a filter
* @param cachedJar is a jar cached by Java Webstart without any extension
*
* @return list of resources that match the pattern
*/
private static List<String> getResources(final String classpathEntryPath, final Pattern searchPattern, final boolean cachedJar) {
final List<String> resources = new ArrayList<>();
// System.out.println("Search into "+classpathEntryPath);
final File classpathEntryFile = new File(classpathEntryPath);
// The classpath entry could be a jar or a folder
if (classpathEntryFile.isDirectory()) {
// Browse the folder content
resources.addAll(getResourcesFromDirectory(classpathEntryFile, searchPattern));
} else if (classpathEntryFile.getName().endsWith(".jar") || classpathEntryFile.getName().endsWith(".zip") || cachedJar) {
// Explode and browse jar|zip content
resources.addAll(getResourcesFromJarOrZipFile(classpathEntryFile, searchPattern));
} else {
LOGGER.log(RESOURCE_IGNORED, classpathEntryFile.getAbsolutePath());
}
return resources;
}
/**
* Browse a directory to search resources that match the pattern.
*
* @param directory the root directory to browse
* @param searchPattern the regex pattern used as a filter
*
* @return list of resources that match the pattern
*/
private static List<String> getResourcesFromDirectory(final File directory, final Pattern searchPattern) {
final List<String> resources = new ArrayList<>();
// Filter only properties files
final File[] fileList = directory.listFiles();
if (fileList != null && fileList.length > 0) {
// Iterate over each relevant file
for (final File file : fileList) {
// If the file is a directory process a recursive call to explorer the tree
if (file.isDirectory()) {
resources.addAll(getResourcesFromDirectory(file, searchPattern));
} else {
try {
checkResource(resources, searchPattern, file.getCanonicalPath());
} catch (final IOException e) {
LOGGER.log(BAD_CANONICAL_PATH, e);
}
}
}
}
return resources;
}
/**
* Browse the jar content to search resources that match the pattern.
*
* @param jarOrZipFile the jar to explore
* @param searchPattern the regex pattern used as a filter
*
* @return list of resources that match the pattern
*/
@SuppressWarnings("unchecked")
private static List<String> getResourcesFromJarOrZipFile(final File jarOrZipFile, final Pattern searchPattern) {
final List<String> resources = new ArrayList<>();
try (ZipFile zf = new ZipFile(jarOrZipFile);) {
final Enumeration<ZipEntry> e = (Enumeration<ZipEntry>) zf.entries();
while (e.hasMoreElements()) {
final ZipEntry ze = e.nextElement();
checkResource(resources, searchPattern, ze.getName());
}
} catch (final IOException e) {
LOGGER.log(FILE_UNREADABLE, e);
}
return resources;
}
/**
* Check if the resource match the regex.
*
* @param resources the list of found resources
* @param searchPattern the regex pattern
* @param resourceName the resource to check and to add
*/
private static void checkResource(final List<String> resources, final Pattern searchPattern, final String resourceName) {
if (searchPattern.matcher(resourceName).matches()) {
resources.add(resourceName);
}
}
/**
* TRy to load a custom resource file.
*
* @param custConfFileName the custom resource file to load
*
* @return the load input stream
*/
public static InputStream loadInputStream(final String custConfFileName) {
InputStream is = null;
final File resourceFile = new File(custConfFileName);
// Check if the file could be find
if (resourceFile.exists()) {
try {
is = new FileInputStream(resourceFile);
} catch (final FileNotFoundException e) {
// Nothing to do
}
} else {
// Otherwise try to load from context classloader
is = Thread.currentThread().getContextClassLoader().getResourceAsStream(custConfFileName);
}
return is;
}
}