001/* 002 * (C) Copyright 2019 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Florent Guillaume 018 */ 019package org.nuxeo.ecm.core.blob; 020 021import static java.nio.file.StandardCopyOption.ATOMIC_MOVE; 022import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; 023 024import java.io.IOException; 025import java.nio.file.AtomicMoveNotSupportedException; 026import java.nio.file.Files; 027import java.nio.file.Path; 028 029import org.apache.logging.log4j.LogManager; 030import org.apache.logging.log4j.Logger; 031import org.nuxeo.ecm.core.api.NuxeoException; 032 033/** 034 * Decides at what path a given key is stored. 035 * 036 * @since 11.1 037 */ 038public abstract class PathStrategy { 039 040 private static final Logger log = LogManager.getLogger(PathStrategy.class); 041 042 protected final Path dir; 043 044 public PathStrategy(Path dir) { 045 this.dir = dir.normalize(); 046 } 047 048 /** 049 * Creates a temporary file in a location suitable for efficient move to the final path for any key. 050 * 051 * @return the temporary file 052 */ 053 public Path createTempFile() { 054 try { 055 return Files.createTempFile(dir, "bin_", ".tmp"); 056 } catch (IOException e) { 057 throw new NuxeoException(e); 058 } 059 } 060 061 /** 062 * Gets the storage path for a given key. 063 * 064 * @param key the key 065 * @return the file for this key 066 */ 067 public abstract Path getPathForKey(String key); 068 069 /** 070 * Does an atomic move from source to dest. 071 * 072 * @param source the source 073 * @param dest the destination 074 */ 075 public static void atomicMove(Path source, Path dest) throws IOException { 076 try { 077 Files.move(source, dest, ATOMIC_MOVE, REPLACE_EXISTING); 078 // move also copied the last-modified date; needed for GC 079 } catch (AtomicMoveNotSupportedException amnse) { 080 // shouldn't happen, given our usual choices of tmp and storage locations 081 // do a copy through a tmp file on the same filesystem then atomic rename 082 log.debug("Unoptimized atomic move from " + source + " to " + dest); 083 Path tmp = Files.createTempFile(dest.getParent(), "bin_", ".tmp"); 084 try { 085 Files.copy(source, tmp, REPLACE_EXISTING); 086 Files.move(tmp, dest, ATOMIC_MOVE); 087 Files.delete(source); 088 } finally { 089 try { 090 Files.deleteIfExists(tmp); 091 } catch (IOException e) { 092 log.error(e, e); 093 } 094 } 095 } 096 } 097 098}