/** * This program 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 3 of the License, or * (at your option) any later version. * <p> * This program 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 General Public License for more details. * <p> * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * @author Nuno Oliveira, GeoSolutions S.A.S., Copyright 2016 */ package org.geowebcache.sqlite; import org.apache.commons.io.FileUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.geowebcache.config.BlobStoreConfig; import org.geowebcache.config.XMLConfiguration; import org.geowebcache.layer.TileLayerDispatcher; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.nio.file.Files; import java.util.UUID; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @Controller @RequestMapping("**/sqlite") public class OperationsRest { private static Log LOGGER = LogFactory.getLog(OperationsRest.class); @Autowired private TileLayerDispatcher tileLayerDispatcher; @Autowired private XMLConfiguration gwcConfiguration; @RequestMapping(value = "/replace", method = RequestMethod.POST) public @ResponseBody ResponseEntity<String> replace(@RequestParam(value = "layer") String layer, @RequestParam(value = "destination", required = false) String destination, @RequestParam(value = "source", required = false) String source, @RequestParam(value = "file", required = false) MultipartFile uploadedFile) { // we need to close this resources at the end File workingDirectory = null; File file = null; try { // create a temporary working directory (may not be used) workingDirectory = Files.createTempDirectory("replace-operation-").toFile(); // finding the blobstore associated t our layer SqliteBlobStore blobStore = getBlobStoreForLayer(layer); if (blobStore == null) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("No SQLite store could be associated with provided layer."); } // finding the file or the directory that will be used to replace if (uploadedFile != null && !uploadedFile.isEmpty()) { // it was an upload file file = handleFileUpload(uploadedFile, workingDirectory); } else if (source != null) { // the file is already present file = new File(source); } if (file == null || !file.exists()) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Provided file is NULL or doesn't exists."); } // if we have a zip file we need to unzip it file = unzipFileIfNeeded(file, workingDirectory); if (file.isDirectory()) { // we invoke the replace directory variant blobStore.replace(file); } else { if (destination == null) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Destination is required for single files."); } // we replace the single file blobStore.replace(file, destination); } } catch (Exception exception) { if (LOGGER.isErrorEnabled()) { LOGGER.error("Error executing the replace operation.", exception); } return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(exception.getMessage()); } finally { // cleaning everything FileUtils.deleteQuietly(workingDirectory); } return ResponseEntity.status(HttpStatus.OK).body(null); } private File handleFileUpload(MultipartFile uploadedFile, File workingDirectory) throws Exception { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Handling file upload."); } // getting the uploaded file content File outputFile = new File(workingDirectory, UUID.randomUUID().toString()); byte[] bytes = uploadedFile.getBytes(); try (BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(outputFile))) { stream.write(bytes); } return outputFile; } private File unzipFileIfNeeded(File file, File workingDirectory) throws Exception { if (file.isDirectory()) { // is not a zip file so nothing to do return file; } try (FileInputStream fileInput = new FileInputStream(file); ZipInputStream zipInput = new ZipInputStream(fileInput)) { ZipEntry zipEntry = zipInput.getNextEntry(); if (zipEntry == null) { // is not a zip file nothing to do return file; } // is a zip file we need to extract is content return unzip(zipInput, zipEntry, workingDirectory); } } private File unzip(ZipInputStream zipInputStream, ZipEntry zipEntry, File workingDirectory) throws Exception { // output directory for our zip file content File outputDirectory = new File(workingDirectory, UUID.randomUUID().toString()); outputDirectory.mkdir(); // unzipping all zip file entries while (zipEntry != null) { File outputFile = new File(outputDirectory, zipEntry.getName()); if (zipEntry.isDirectory()) { // this entry is a directory, so we only need to create the directory outputFile.mkdir(); } else { // is a file we need to extract is content extractFile(zipInputStream, outputFile); } zipInputStream.closeEntry(); zipEntry = zipInputStream.getNextEntry(); } return outputDirectory; } private void extractFile(ZipInputStream inputStream, File outputFile) throws Exception { // extracting zip entry file content byte[] bytes = new byte[1024]; try (FileOutputStream outputStream = new FileOutputStream(outputFile)) { int read; while ((read = inputStream.read(bytes)) != -1) { outputStream.write(bytes, 0, read); } } } private SqliteBlobStore getBlobStoreForLayer(String layerName) throws Exception { // let's find layer associated store String blobStoreId = tileLayerDispatcher.getTileLayer(layerName).getBlobStoreId(); BlobStoreConfig blobStoreConfig = null; for (BlobStoreConfig candidateBlobStoreConfig : gwcConfiguration.getBlobStores()) { if (blobStoreId == null) { // we need to find the default configuration if (candidateBlobStoreConfig.isDefault()) { // this is the default configuration, we are done blobStoreConfig = candidateBlobStoreConfig; break; } } if (candidateBlobStoreConfig.getId().equals(blobStoreId)) { // we need to find a specific store by is id blobStoreConfig = candidateBlobStoreConfig; break; } } if (blobStoreConfig == null || !(blobStoreConfig instanceof SqliteConfiguration)) { // no store found or the store is not an sqlite store return null; } // returning an instance of found store return (SqliteBlobStore) blobStoreConfig.createInstance(tileLayerDispatcher, gwcConfiguration.getLockProvider()); } }