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); } } }