/* (c) 2016 Open Source Geospatial Foundation - all rights reserved * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.backuprestore.utils; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.logging.Level; import java.util.logging.Logger; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import org.apache.commons.vfs2.AllFileSelector; import org.apache.commons.vfs2.FileObject; import org.apache.commons.vfs2.FileSelectInfo; import org.apache.commons.vfs2.FileSelector; import org.apache.commons.vfs2.FileSystemException; import org.apache.commons.vfs2.FileSystemManager; import org.apache.commons.vfs2.FileType; import org.apache.commons.vfs2.VFS; import org.codehaus.plexus.util.FileUtils; import org.geoserver.config.GeoServerDataDirectory; import org.geoserver.platform.resource.Files; import org.geoserver.platform.resource.Paths; import org.geoserver.platform.resource.Resource; import org.geoserver.platform.resource.Resource.Type; import org.geoserver.platform.resource.Resources; import org.geoserver.util.IOUtils; import org.geotools.util.logging.Logging; /** * Utility to work with compressed files * * Based on Importer {@link VFSWorker} by Gabriel Roldan * * @author groldan * @autho Alessio Fabiani, GeoSolutions */ public class BackupUtils { private static final Logger LOGGER = Logging.getLogger(BackupUtils.class); /** * Returns a random temp folder Resource inside the System Temp Directory. * * @return * @throws IOException */ public static Resource tmpDir() throws IOException { String tempPath = null; try { tempPath = System.getProperty("java.io.tmpdir"); if (tempPath == null) { tempPath = IOUtils.createTempDirectory("backuputils").getAbsolutePath(); LOGGER.warning( "It was not possible to create a temporary folder into the System 'java.io.tmpdir'. Falling back to default TEMP [" + tempPath + "]."); } } catch (Exception e) { tempPath = null; LOGGER.log(Level.SEVERE, "It was not possible to create a temporary folder! In order to fix the problem, please check the System 'java.io.tmpdir' point to a valid folder.", e); throw new IOException( "It was not possible to create a temporary folder! In order to fix the problem, please check the System 'java.io.tmpdir' point to a valid folder.", e); } if (tempPath == null) { LOGGER.log(Level.SEVERE, "It was not possible to create or find a suitable temporary folder. 'tempPath' is NULL! In order to fix the problem, please check the System 'java.io.tmpdir' point to a valid folder."); throw new IOException( "It was not possible to create or find a suitable temporary folder. 'tempPath' is NULL! In order to fix the problem, please check the System 'java.io.tmpdir' point to a valid folder."); } return createRandomResource(tempPath); } /** * Returns a random temp folder Resource inside the GeoServer Temp Directory. * * @param geoServerDataDirectory * @return * @throws IOException */ public static Resource geoServerTmpDir(GeoServerDataDirectory geoServerDataDirectory) throws IOException { String tempPath = geoServerDataDirectory.findOrCreateDir("temp").getAbsolutePath(); return createRandomResource(tempPath); } /** * @param tempPath * @return * @throws IOException */ private static Resource createRandomResource(String tempPath) throws IOException { Resource root = Resources.fromPath(tempPath); Resource directory = Resources.createRandom("tmp", "", root); do { FileUtils.forceDelete(directory.dir()); } while (Resources.exists(directory)); FileUtils.forceMkdir(directory.dir()); return Files.asResource(directory.dir()); } /** * Extracts the archive file {@code archiveFile} to {@code targetFolder}; both shall previously exist. * * @param archiveFile * @param targetFolder * @throws IOException */ public static void extractTo(Resource archiveFile, Resource targetFolder) throws IOException { FileSystemManager manager = VFS.getManager(); String sourceURI = resolveArchiveURI(archiveFile); FileObject source = manager.resolveFile(sourceURI); if (manager.canCreateFileSystem(source)) { source = manager.createFileSystem(source); } FileObject target = manager .createVirtualFileSystem(manager.resolveFile(targetFolder.dir().getAbsolutePath())); FileSelector selector = new AllFileSelector() { @Override public boolean includeFile(FileSelectInfo fileInfo) { LOGGER.fine("Uncompressing " + fileInfo.getFile().getName().getFriendlyURI()); return true; } }; target.copyFrom(source, selector); source.close(); target.close(); manager.closeFileSystem(source.getFileSystem()); } /** * Compress {@code sourceFolder} to the archive file {@code archiveFile}; both shall previously exist. * * @param sourceFolder * @param archiveFile * @throws IOException */ public static void compressTo(Resource sourceFolder, Resource archiveFile) throws IOException { // See https://commons.apache.org/proper/commons-vfs/filesystems.html // for the supported filesystems FileSystemManager manager = VFS.getManager(); FileObject sourceDir = manager .createVirtualFileSystem(manager.resolveFile(sourceFolder.dir().getAbsolutePath())); try { if ("zip".equalsIgnoreCase(FileUtils.getExtension(archiveFile.path()))) { // apache VFS does not support ZIP as writable FileSystem OutputStream fos = archiveFile.out(); // Create access to zip. ZipOutputStream zos = new ZipOutputStream(fos); // add entry/-ies. for (FileObject sourceFile : sourceDir.getChildren()) { writeEntry(zos, sourceFile, null); } // Close streams zos.flush(); zos.close(); fos.close(); } else { // Create access to archive. FileObject zipFile = manager.resolveFile(resolveArchiveURI(archiveFile)); zipFile.createFile(); ZipOutputStream zos = new ZipOutputStream(zipFile.getContent().getOutputStream()); // add entry/-ies. for (FileObject sourceFile : sourceDir.getChildren()) { writeEntry(zos, sourceFile, null); } // Close streams zos.flush(); zos.close(); zipFile.close(); manager.closeFileSystem(zipFile.getFileSystem()); } } finally { manager.closeFileSystem(sourceDir.getFileSystem()); } } /** * @param zos * @param sourceFile * @throws FileSystemException * @throws IOException */ private static void writeEntry(ZipOutputStream zos, FileObject sourceFile, String baseDir) throws FileSystemException, IOException { if (sourceFile.getType() == FileType.FOLDER) { // add entry/-ies. for (FileObject file : sourceFile.getChildren()) { writeEntry(zos, file, Paths.path(baseDir, sourceFile.getName().getBaseName())); } } else { String fileName = (baseDir != null ? Paths.path(baseDir, sourceFile.getName().getBaseName()) : sourceFile.getName().getBaseName()); ZipEntry zipEntry = new ZipEntry(fileName); InputStream is = sourceFile.getContent().getInputStream(); // Write to zip. byte[] buf = new byte[1024]; zos.putNextEntry(zipEntry); for (int readNum; (readNum = is.read(buf)) != -1;) { zos.write(buf, 0, readNum); } zos.closeEntry(); is.close(); } } /** * * @param archiveFile * @return */ public static String resolveArchiveURI(final Resource archiveFile) { String archivePrefix = getArchiveURLProtocol(archiveFile); String absolutePath = archivePrefix + archiveFile.file().getAbsolutePath(); return absolutePath; } /** * * @param file * @return */ public static String getArchiveURLProtocol(final Resource file) { if (file.getType() == Type.DIRECTORY) { return "file://"; } String name = file.name().toLowerCase(); if (name.endsWith(".zip") || name.endsWith(".kmz")) { return "zip://"; } if (name.endsWith(".tar")) { return "tar://"; } if (name.endsWith(".tgz") || name.endsWith(".tar.gz")) { return "tgz://"; } if (name.endsWith(".tbz2") || name.endsWith(".tar.bzip2") || name.endsWith(".tar.bz2")) { return "tbz2://"; } if (name.endsWith(".gz")) { return "gz://"; } if (name.endsWith(".bz2")) { return "bz2://"; } if (name.endsWith(".jar")) { return "jar://"; } return null; } /** * * @param baseDir * @param subDir * @return */ public static Resource dir(Resource baseDir, String subDir) { final Resource targetPath = Resources.fromPath(subDir, baseDir); return Resources.fromPath( Resources.directory(targetPath, !Resources.exists(targetPath)).getAbsolutePath()); } }