package org.apereo.cas.util;
import com.google.common.base.Throwables;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.AbstractResource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.UrlResource;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import static org.springframework.util.ResourceUtils.CLASSPATH_URL_PREFIX;
/**
* Utility class to assist with resource operations.
*
* @author Misagh Moayyed
* @since 5.0.0
*/
public final class ResourceUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(ResourceUtils.class);
private static final String HTTP_URL_PREFIX = "http:";
private ResourceUtils() {
}
/**
* Gets resource from a String location.
*
* @param location the metadata location
* @return the resource from
* @throws IOException the exception
*/
public static AbstractResource getRawResourceFrom(final String location) throws IOException {
final AbstractResource metadataLocationResource;
if (location.toLowerCase().startsWith(HTTP_URL_PREFIX)) {
metadataLocationResource = new UrlResource(location);
} else if (location.toLowerCase().startsWith(CLASSPATH_URL_PREFIX)) {
metadataLocationResource = new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()));
} else {
metadataLocationResource = new FileSystemResource(location);
}
return metadataLocationResource;
}
/**
* Does resource exist?
*
* @param resource the resource
* @param resourceLoader the resource loader
* @return the boolean
*/
public static boolean doesResourceExist(final String resource, final ResourceLoader resourceLoader) {
try {
if (StringUtils.isNotBlank(resource)) {
final Resource res = resourceLoader.getResource(resource);
return doesResourceExist(res);
}
} catch (final Exception e) {
LOGGER.warn(e.getMessage(), e);
}
return false;
}
/**
* Does resource exist?
*
* @param res the res
* @return the boolean
*/
public static boolean doesResourceExist(final Resource res) {
if (res != null) {
try {
IOUtils.read(res.getInputStream(), new byte[1]);
return true;
} catch (final Exception e) {
LOGGER.trace(e.getMessage(), e);
return false;
}
}
return false;
}
/**
* Gets resource from a String location.
*
* @param location the metadata location
* @return the resource from
* @throws IOException the exception
*/
public static AbstractResource getResourceFrom(final String location) throws IOException {
final AbstractResource metadataLocationResource = getRawResourceFrom(location);
if (!metadataLocationResource.exists() || !metadataLocationResource.isReadable()) {
throw new FileNotFoundException("Resource " + location + " does not exist or is unreadable");
}
return metadataLocationResource;
}
/**
* Prepare classpath resource if needed file.
*
* @param resource the resource
* @return the file
*/
public static Resource prepareClasspathResourceIfNeeded(final Resource resource) {
if (resource == null) {
LOGGER.debug("No resource defined to prepare. Returning null");
return null;
}
return prepareClasspathResourceIfNeeded(resource, false, resource.getFilename());
}
/**
* If the provided resource is a classpath resource, running inside an embedded container,
* and if the container is running in a non-exploded form, classpath resources become non-accessible.
* So, this method will attempt to move resources out of classpath and onto a physical location
* outside the context, typically in the "cas" directory of the temp system folder.
*
* @param resource the resource
* @param isDirectory the if the resource is a directory, in which case entries will be copied over.
* @param containsName the resource name pattern
* @return the file
*/
public static Resource prepareClasspathResourceIfNeeded(final Resource resource,
final boolean isDirectory,
final String containsName) {
try {
if (resource == null) {
LOGGER.debug("No resource defined to prepare. Returning null");
return null;
}
if (!ClassUtils.isAssignable(resource.getClass(), ClassPathResource.class)) {
return resource;
}
if (org.springframework.util.ResourceUtils.isFileURL(resource.getURL())) {
return resource;
}
final URL url = org.springframework.util.ResourceUtils.extractArchiveURL(resource.getURL());
final File file = org.springframework.util.ResourceUtils.getFile(url);
final File casDirectory = new File(FileUtils.getTempDirectory(), "cas");
final File destination = new File(casDirectory, resource.getFilename());
if (isDirectory) {
FileUtils.forceMkdir(destination);
FileUtils.cleanDirectory(destination);
} else if (destination.exists()) {
FileUtils.forceDelete(destination);
}
try (JarFile jFile = new JarFile(file)) {
final Enumeration e = jFile.entries();
while (e.hasMoreElements()) {
final ZipEntry entry = (ZipEntry) e.nextElement();
if (entry.getName().contains(resource.getFilename()) && entry.getName().contains(containsName)) {
try (InputStream stream = jFile.getInputStream(entry)) {
File copyDestination = destination;
if (isDirectory) {
final File entryFileName = new File(entry.getName());
copyDestination = new File(destination, entryFileName.getName());
}
try (FileWriter writer = new FileWriter(copyDestination)) {
IOUtils.copy(stream, writer, StandardCharsets.UTF_8);
}
}
}
}
}
return new FileSystemResource(destination);
} catch (final IOException e) {
throw Throwables.propagate(e);
}
}
}