/* * Copyright (C) 2016 Red Hat, inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This library 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 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA */ package org.jboss.as.repository; import static org.jboss.as.repository.ContentRepository.DELETED_CONTENT; import static org.jboss.as.repository.ContentRepository.MARKED_CONTENT; import static org.jboss.as.repository.PathUtil.copyRecursively; import static org.jboss.as.repository.PathUtil.createTempDirectory; import static org.jboss.as.repository.PathUtil.deleteRecursively; import static org.jboss.as.repository.PathUtil.deleteSilentlyRecursively; import static org.jboss.as.repository.PathUtil.getFileExtension; import static org.jboss.as.repository.PathUtil.isArchive; import static org.jboss.as.repository.PathUtil.resolveSecurely; import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.security.DigestOutputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.stream.Stream; import org.jboss.as.repository.logging.DeploymentRepositoryLogger; import org.jboss.msc.service.Service; import org.jboss.vfs.VFS; import org.jboss.vfs.VirtualFile; import static org.jboss.as.repository.PathUtil.unzip; import java.nio.file.Path; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import org.jboss.msc.service.StartContext; import org.jboss.msc.service.StartException; import org.jboss.msc.service.StopContext; import org.wildfly.common.Assert; /** * Default implementation of {@link ContentRepository}. * * @author John Bailey */ public class ContentRepositoryImpl implements ContentRepository, Service<ContentRepository> { protected static final String CONTENT = "content"; private final File repoRoot; private final File tmpRoot; protected final MessageDigest messageDigest; private final Map<String, Set<ContentReference>> contentHashReferences = new HashMap<>(); private final Map<String, ReentrantLock> lockedContents = new HashMap<>(); private final Map<String, Long> obsoleteContents = new HashMap<>(); private final long obsolescenceTimeout; private final long lockTimeout; protected ContentRepositoryImpl(final File repoRoot, final File tmpRoot, long obsolescenceTimeout, long lockTimeout) { Assert.checkNotNullParam("repoRoot", repoRoot); Assert.checkNotNullParam("tmpRoot", tmpRoot); checkDirectory(repoRoot); this.repoRoot = repoRoot; checkDirectory(tmpRoot); this.tmpRoot = tmpRoot; this.obsolescenceTimeout = obsolescenceTimeout; this.lockTimeout = lockTimeout; try { this.messageDigest = MessageDigest.getInstance("SHA-1"); } catch (NoSuchAlgorithmException e) { throw DeploymentRepositoryLogger.ROOT_LOGGER.cannotObtainSha1(e, MessageDigest.class.getSimpleName()); } } private void checkDirectory(final File directory) { if (directory.exists()) { if (!directory.isDirectory()) { throw DeploymentRepositoryLogger.ROOT_LOGGER.notADirectory(directory.getAbsolutePath()); } if (!directory.canWrite()) { throw DeploymentRepositoryLogger.ROOT_LOGGER.directoryNotWritable(directory.getAbsolutePath()); } } else if (!directory.mkdirs()) { throw DeploymentRepositoryLogger.ROOT_LOGGER.cannotCreateDirectory(null, directory.getAbsolutePath()); } } @Override public byte[] addContent(InputStream stream) throws IOException { byte[] sha1Bytes; Path tmp = File.createTempFile(CONTENT, ".tmp", repoRoot).toPath(); if (stream != null) { try (OutputStream fos = Files.newOutputStream(tmp)) { synchronized (messageDigest) { messageDigest.reset(); DigestOutputStream dos = new DigestOutputStream(fos, messageDigest); BufferedInputStream bis = new BufferedInputStream(stream); byte[] bytes = new byte[8192]; int read; while ((read = bis.read(bytes)) > -1) { dos.write(bytes, 0, read); } fos.flush(); } sha1Bytes = messageDigest.digest(); } } else {//create a directory instead Files.delete(tmp); Files.createDirectory(tmp); synchronized (messageDigest) { messageDigest.reset(); sha1Bytes = HashUtil.hashPath(messageDigest, tmp); } } final Path realFile = getDeploymentContentFile(sha1Bytes, true); if (hasContent(sha1Bytes)) { // we've already got this content try { deleteRecursively(tmp); } catch (IOException ioex) { DeploymentRepositoryLogger.ROOT_LOGGER.cannotDeleteTempFile(ioex, tmp.toAbsolutePath().toString()); tmp.toFile().deleteOnExit(); } DeploymentRepositoryLogger.ROOT_LOGGER.debugf("Content was already present in repository at location %s", realFile.toAbsolutePath().toString()); } else { moveTempToPermanent(tmp, realFile); DeploymentRepositoryLogger.ROOT_LOGGER.contentAdded(realFile.toAbsolutePath().toString()); } return sha1Bytes; } @Override public void addContentReference(ContentReference reference) { synchronized (contentHashReferences) { Set<ContentReference> references = contentHashReferences.get(reference.getHexHash()); if (references == null) { references = new HashSet<>(); contentHashReferences.put(reference.getHexHash(), references); } references.add(reference); } } @Override public VirtualFile getContent(byte[] hash) { Assert.checkNotNullParam("hash", hash); return VFS.getChild(getDeploymentContentFile(hash, true).toUri()); } @Override public boolean syncContent(ContentReference reference) { return hasContent(reference.getHash()); } @Override public boolean hasContent(byte[] hash) { return Files.exists(getDeploymentContentFile(hash)); } protected Path getRepoRoot() { return repoRoot.toPath(); } protected Path getDeploymentContentFile(byte[] deploymentHash) { return getDeploymentContentFile(deploymentHash, false); } private Path getDeploymentContentFile(byte[] deploymentHash, boolean validate) { return getDeploymentHashDir(deploymentHash, validate).resolve(CONTENT); } protected Path getDeploymentHashDir(final byte[] deploymentHash, final boolean validate) { final String sha1 = HashUtil.bytesToHexString(deploymentHash); final String partA = sha1.substring(0, 2); final String partB = sha1.substring(2); final Path base = getRepoRoot().resolve(partA); if (validate) { validateDir(base); } final Path hashDir = base.resolve(partB); if (validate && !Files.exists(hashDir)) { try { Files.createDirectories(hashDir); } catch (IOException ioex) { throw DeploymentRepositoryLogger.ROOT_LOGGER.cannotCreateDirectory(ioex, hashDir.toAbsolutePath().toString()); } } return hashDir; } protected void validateDir(Path dir) { if (!Files.exists(dir)) { try { Files.createDirectories(dir); } catch (IOException ioex) { throw DeploymentRepositoryLogger.ROOT_LOGGER.cannotCreateDirectory(ioex, dir.toAbsolutePath().toString()); } } else if (!Files.isDirectory(dir)) { throw DeploymentRepositoryLogger.ROOT_LOGGER.notADirectory(dir.toAbsolutePath().toString()); } else if (!dir.toFile().canWrite()) { //WFCORE-799 workaround for broken Files.isWritable() on Windows in JDK8 throw DeploymentRepositoryLogger.ROOT_LOGGER.directoryNotWritable(dir.toAbsolutePath().toString()); } } private void moveTempToPermanent(Path tmpFile, Path permanentFile) throws IOException { Path localTmp = permanentFile.resolveSibling("tmp"); try { Files.move(tmpFile, permanentFile); } catch (IOException ioex) { // AS7-3574. Try to avoid writing the permanent file bit by bit in we crash in the middle. // Copy tmpFile to another tmpfile in the same dir as the permanent file (and thus same filesystem) // and see then if we can rename it. copyRecursively(tmpFile, localTmp, true); try { Files.move(localTmp, permanentFile); } catch (IOException ex) { // No luck; need to copy try { copyRecursively(localTmp, permanentFile, true); } catch (IOException e) { deleteRecursively(permanentFile); throw e; } } } finally { try { deleteRecursively(tmpFile); } catch (IOException ioex) { DeploymentRepositoryLogger.ROOT_LOGGER.cannotDeleteTempFile(ioex, tmpFile.toString()); tmpFile.toFile().deleteOnExit(); } try { deleteRecursively(localTmp); } catch (IOException ioex) { DeploymentRepositoryLogger.ROOT_LOGGER.cannotDeleteTempFile(ioex, localTmp.toString()); localTmp.toFile().deleteOnExit(); } } } @Override public void removeContent(ContentReference reference) { synchronized (contentHashReferences) { final Set<ContentReference> references = contentHashReferences.get(reference.getHexHash()); if (references != null) { references.remove(reference); if (!references.isEmpty()) { return; } contentHashReferences.remove(reference.getHexHash()); } } Path contentPath; if (!HashUtil.isEachHexHashInTable(reference.getHexHash())) { contentPath = Paths.get(reference.getContentIdentifier()); } else { contentPath = getDeploymentContentFile(reference.getHash(), false); } try { if (HashUtil.isEachHexHashInTable(reference.getHexHash())) { //Otherwise this is not a deployment content if(!lock(reference.getHash())) { DeploymentRepositoryLogger.ROOT_LOGGER.contentDeletionError(DeploymentRepositoryLogger.ROOT_LOGGER.errorLockingDeployment(), contentPath.toString()); return; } } deleteRecursively(contentPath); } catch (IOException ex) { DeploymentRepositoryLogger.ROOT_LOGGER.contentDeletionError(ex, contentPath.toString()); } catch (InterruptedException ex) { Thread.interrupted(); DeploymentRepositoryLogger.ROOT_LOGGER.contentDeletionError(ex, contentPath.toString()); } finally { if (HashUtil.isEachHexHashInTable(reference.getHexHash())) { unlock(reference.getHash()); } } Path parent = contentPath.getParent(); try { Files.deleteIfExists(parent); } catch (IOException ex) { DeploymentRepositoryLogger.ROOT_LOGGER.contentDeletionError(ex, parent.toString()); } Path grandParent = parent.getParent(); if(Files.exists(grandParent)) { try (Stream<Path> files = Files.list(grandParent)) { if (!files.findAny().isPresent()) { Files.deleteIfExists(grandParent); } } catch (IOException ex) { DeploymentRepositoryLogger.ROOT_LOGGER.contentDeletionError(ex, grandParent.toString()); } } DeploymentRepositoryLogger.ROOT_LOGGER.contentRemoved(contentPath.toAbsolutePath().toString()); } /** * Clean obsolete contents from the content repository. It will first mark contents as obsolete then after some time * if these contents are still obsolete they will be removed. * * @return a map containing the list of marked contents and the list of deleted contents. */ @Override public Map<String, Set<String>> cleanObsoleteContent() { Map<String, Set<String>> cleanedContents = new HashMap<>(2); cleanedContents.put(MARKED_CONTENT, new HashSet<>()); cleanedContents.put(DELETED_CONTENT, new HashSet<>()); synchronized (contentHashReferences) { for (ContentReference fsContent : listLocalContents()) { if (!contentHashReferences.containsKey(fsContent.getHexHash())) { //We have no refrence to this content if (markAsObsolete(fsContent)) { cleanedContents.get(DELETED_CONTENT).add(fsContent.getContentIdentifier()); } else { cleanedContents.get(MARKED_CONTENT).add(fsContent.getContentIdentifier()); } } else { obsoleteContents.remove(fsContent.getHexHash()); //Remove existing references from obsoleteContents } } } return cleanedContents; } /** * Mark content as obsolete. If content was already marked for obsolescenceTimeout ms then it is removed. * * @param ref the content refrence to be marked as obsolete. * * @return true if the content refrence is removed, fale otherwise. */ private boolean markAsObsolete(ContentReference ref) { if (obsoleteContents.containsKey(ref.getHexHash())) { //This content is already marked as obsolete if (obsoleteContents.get(ref.getHexHash()) + obsolescenceTimeout < System.currentTimeMillis()) { DeploymentRepositoryLogger.ROOT_LOGGER.obsoleteContentCleaned(ref.getContentIdentifier()); removeContent(ref); return true; } } else { obsoleteContents.put(ref.getHexHash(), System.currentTimeMillis()); //Mark content as obsolete } return false; } private Set<ContentReference> listLocalContents() { Set<ContentReference> localReferences = new HashSet<>(); File[] rootHashes = repoRoot.listFiles(); if (rootHashes != null) { for (File rootHash : rootHashes) { if (rootHash.isDirectory()) { File[] complementaryHashes = rootHash.listFiles(); if (complementaryHashes == null || complementaryHashes.length == 0) { ContentReference reference = new ContentReference(rootHash.getAbsolutePath(), rootHash.getName()); localReferences.add(reference); } else { for (File complementaryHash : complementaryHashes) { String hash = rootHash.getName() + complementaryHash.getName(); ContentReference reference = new ContentReference(complementaryHash.getAbsolutePath(), hash); localReferences.add(reference); } } } } } else { DeploymentRepositoryLogger.ROOT_LOGGER.localContentListError(repoRoot.getAbsolutePath()); } return localReferences; } @Override public byte[] explodeContent(byte[] deploymentHash) throws ExplodedContentException { Path contentPath = getDeploymentContentFile(deploymentHash); if (!Files.exists(contentPath)) { throw DeploymentRepositoryLogger.ROOT_LOGGER.archiveNotFound(contentPath.toString()); } try { if (Files.isDirectory(contentPath) || !isArchive(contentPath)) { throw DeploymentRepositoryLogger.ROOT_LOGGER.notAnArchive(contentPath.toString()); } Path tmp = createTempDirectory(repoRoot.toPath(), CONTENT); Path contentDir = Files.createDirectory(tmp.resolve(CONTENT)); unzip(contentPath, contentDir); byte[] sha1Bytes = HashUtil.hashPath(messageDigest, contentDir); final Path realFile = getDeploymentContentFile(sha1Bytes, true); if (hasContent(sha1Bytes)) { // we've already got this content try { deleteRecursively(tmp); } catch (IOException ioex) { DeploymentRepositoryLogger.ROOT_LOGGER.cannotDeleteTempFile(ioex, tmp.toAbsolutePath().toString()); tmp.toFile().deleteOnExit(); } DeploymentRepositoryLogger.ROOT_LOGGER.debugf("Content was already present in repository at location %s", realFile.toAbsolutePath().toString()); } else { moveTempToPermanent(contentDir, realFile); deleteRecursively(tmp); DeploymentRepositoryLogger.ROOT_LOGGER.contentExploded(realFile.toAbsolutePath().toString()); } return sha1Bytes; } catch (IOException ioex) { DeploymentRepositoryLogger.ROOT_LOGGER.warn(ioex); throw DeploymentRepositoryLogger.ROOT_LOGGER.errorExplodingContent(ioex, contentPath.toString()); } } @Override public byte[] explodeSubContent(byte[] deploymentHash, String relativePath) throws ExplodedContentException { Path contentPath = getDeploymentContentFile(deploymentHash); Path sourcePath = resolveSecurely(contentPath, relativePath); try { if (Files.exists(contentPath) && Files.isDirectory(contentPath)) { Path tmp = createTempDirectory(repoRoot.toPath(), CONTENT); Path contentDir = tmp.resolve(CONTENT); copyRecursively(contentPath, contentDir, true); Path targetPath = resolveSecurely(contentDir, relativePath); if (!Files.exists(sourcePath)) { throw DeploymentRepositoryLogger.ROOT_LOGGER.archiveNotFound(sourcePath.toString()); } if (Files.isDirectory(sourcePath) || !isArchive(sourcePath)) { throw DeploymentRepositoryLogger.ROOT_LOGGER.notAnArchive(sourcePath.toString()); } if (Files.exists(targetPath) && !Files.isDirectory(targetPath)) { deleteRecursively(targetPath); } unzip(sourcePath, targetPath); byte[] sha1Bytes = HashUtil.hashPath(messageDigest, contentDir); final Path realFile = getDeploymentContentFile(sha1Bytes, true); if (hasContent(sha1Bytes)) { // we've already got this content try { deleteRecursively(tmp); } catch (IOException ioex) { DeploymentRepositoryLogger.ROOT_LOGGER.cannotDeleteTempFile(ioex, tmp.toAbsolutePath().toString()); tmp.toFile().deleteOnExit(); } DeploymentRepositoryLogger.ROOT_LOGGER.debugf("Content was already present in repository at location %s", realFile.toAbsolutePath().toString()); } else { moveTempToPermanent(contentDir, realFile); deleteRecursively(tmp); DeploymentRepositoryLogger.ROOT_LOGGER.contentAdded(realFile.toAbsolutePath().toString()); } return sha1Bytes; } else { throw DeploymentRepositoryLogger.ROOT_LOGGER.errorExplodingContent(null, sourcePath.toString()); } } catch (IOException ioex) { DeploymentRepositoryLogger.ROOT_LOGGER.warn(ioex); throw DeploymentRepositoryLogger.ROOT_LOGGER.errorExplodingContent(ioex, sourcePath.toString()); } } @Override public void copyExplodedContent(byte[] deploymentHash, final Path target) throws ExplodedContentException { final Path contentPath = getDeploymentContentFile(deploymentHash); try { if (Files.exists(contentPath) && Files.isDirectory(contentPath)) { copyRecursively(contentPath, target, false); } } catch (IOException ioex) { DeploymentRepositoryLogger.ROOT_LOGGER.warn(ioex); throw DeploymentRepositoryLogger.ROOT_LOGGER.errorCopyingDeployment(ioex, target.toString()); } } @Override public void copyExplodedContentFiles(byte[] deploymentHash, List<String> relativePaths, Path target) throws ExplodedContentException { final Path contentPath = getDeploymentContentFile(deploymentHash); try { if (Files.exists(contentPath) && Files.isDirectory(contentPath)) { for (String relativePath : relativePaths) { copyRecursively(resolveSecurely(contentPath, relativePath), resolveSecurely(target, relativePath), true); } } } catch (IOException ioex) { DeploymentRepositoryLogger.ROOT_LOGGER.warn(ioex); throw DeploymentRepositoryLogger.ROOT_LOGGER.errorCopyingDeployment(ioex, target.toString()); } } private boolean lock(byte[] hash) throws InterruptedException { String hashHex = HashUtil.bytesToHexString(hash); synchronized(lockedContents) { if(!lockedContents.containsKey(hashHex)) { lockedContents.put(hashHex, new ReentrantLock()); } return lockedContents.get(hashHex).tryLock(lockTimeout, TimeUnit.MILLISECONDS); } } private void unlock(byte[] hash) { String hashHex = HashUtil.bytesToHexString(hash); synchronized (lockedContents) { if (lockedContents.containsKey(hashHex)) { ReentrantLock lock = lockedContents.get(hashHex); if (lock.isHeldByCurrentThread()) { lock.unlock(); if (!Files.exists(getDeploymentContentFile(hash))) { lockedContents.remove(hashHex); } } } } } @Override public TypedInputStream readContent(byte[] deploymentHash, String path) throws ExplodedContentException { Path tmpDir = null; try { if(!lock(deploymentHash)) { throw DeploymentRepositoryLogger.ROOT_LOGGER.errorLockingDeployment(); } Path src = resolveSecurely(getDeploymentContentFile(deploymentHash), path); tmpDir = Files.createTempDirectory(tmpRoot.toPath(), HashUtil.bytesToHexString(deploymentHash)); Path file = PathUtil.readFile(src, tmpDir); Path tmp = Files.createTempFile(tmpRoot.toPath(), CONTENT, getFileExtension(src)); Files.copy(file, tmp, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING); return new TemporaryFileInputStream(tmp); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); throw new RuntimeException(ex); } catch (IOException ex) { DeploymentRepositoryLogger.ROOT_LOGGER.warn(ex); throw DeploymentRepositoryLogger.ROOT_LOGGER.errorAccessingDeployment(ex); } finally { unlock(deploymentHash); deleteSilentlyRecursively(tmpDir); } } @Override public List<ContentRepositoryElement> listContent(byte[] deploymentHash, String path, ContentFilter filter) throws ExplodedContentException { Path tmpDir = null; try { if (!lock(deploymentHash)) { throw DeploymentRepositoryLogger.ROOT_LOGGER.errorLockingDeployment(); } tmpDir = Files.createTempDirectory(tmpRoot.toPath(), HashUtil.bytesToHexString(deploymentHash)); final Path rootPath = resolveSecurely(getDeploymentContentFile(deploymentHash), path); List<ContentRepositoryElement> result = PathUtil.listFiles(rootPath, tmpDir, filter); return result; } catch (InterruptedException ex) { Thread.currentThread().interrupt(); throw new RuntimeException(ex); } catch (IOException ex) { DeploymentRepositoryLogger.ROOT_LOGGER.warn(ex); throw DeploymentRepositoryLogger.ROOT_LOGGER.errorAccessingDeployment(ex); } finally { unlock(deploymentHash); if(tmpDir != null) { deleteSilentlyRecursively(tmpDir); } } } private void deleteFileWithEmptyAncestorDirectories(Path path) throws IOException { if (!this.repoRoot.toPath().equals(path)) { Files.deleteIfExists(path); Path parent = path.getParent(); try (Stream<Path> files = Files.list(parent)) { if (Files.isDirectory(parent) && !files.findAny().isPresent()) { deleteFileWithEmptyAncestorDirectories(parent); } } } } @Override public byte[] addContentToExploded(byte[] deploymentHash, List<ExplodedContent> addFiles, boolean overwrite) throws ExplodedContentException { Path contentPath = getDeploymentContentFile(deploymentHash); try { if (Files.exists(contentPath) && Files.isDirectory(contentPath)) { Path tmp = createTempDirectory(repoRoot.toPath(), CONTENT); Path contentDir = tmp.resolve(CONTENT); copyRecursively(contentPath, contentDir, overwrite); for (ExplodedContent newContent : addFiles) { Path targetFile = resolveSecurely(contentDir, newContent.getRelativePath()); if (!Files.exists(targetFile)) { Files.createDirectories(targetFile.getParent()); } try (InputStream in = newContent.getContent()) { if(in == null) { Files.createDirectory(targetFile); } else { if(overwrite) { Files.copy(in, targetFile, StandardCopyOption.REPLACE_EXISTING); } else { Files.copy(in, targetFile); } } } } byte[] sha1Bytes = HashUtil.hashPath(messageDigest, contentDir); final Path realFile = getDeploymentContentFile(sha1Bytes, true); if (hasContent(sha1Bytes)) { // we've already got this content try { deleteRecursively(tmp); } catch (IOException ioex) { DeploymentRepositoryLogger.ROOT_LOGGER.cannotDeleteTempFile(ioex, tmp.toAbsolutePath().toString()); tmp.toFile().deleteOnExit(); } DeploymentRepositoryLogger.ROOT_LOGGER.debugf("Content was already present in repository at location %s", realFile.toAbsolutePath().toString()); } else { moveTempToPermanent(contentDir, realFile); deleteRecursively(tmp); DeploymentRepositoryLogger.ROOT_LOGGER.contentAdded(realFile.toAbsolutePath().toString()); } return sha1Bytes; } return deploymentHash; } catch (IOException ex) { DeploymentRepositoryLogger.ROOT_LOGGER.warn(ex); throw DeploymentRepositoryLogger.ROOT_LOGGER.errorUpdatingDeployment(ex); } } @Override public byte[] removeContentFromExploded(byte[] deploymentHash, List<String> paths) throws ExplodedContentException { Path contentPath = getDeploymentContentFile(deploymentHash); try { if (Files.exists(contentPath) && Files.isDirectory(contentPath)) { Path tmp = createTempDirectory(repoRoot.toPath(), CONTENT); Path contentDir = tmp.resolve(CONTENT).toAbsolutePath(); copyRecursively(contentPath, contentDir, false); for (String path : paths) { Path targetFile = resolveSecurely(contentDir, path); deleteFileWithEmptyAncestorDirectories(targetFile); } byte[] sha1Bytes = HashUtil.hashPath(messageDigest, contentDir); final Path realFile = getDeploymentContentFile(sha1Bytes, true); if (hasContent(sha1Bytes)) { // we've already got this content try { deleteRecursively(tmp); } catch (IOException ioex) { DeploymentRepositoryLogger.ROOT_LOGGER.cannotDeleteTempFile(ioex, tmp.toAbsolutePath().toString()); tmp.toFile().deleteOnExit(); } DeploymentRepositoryLogger.ROOT_LOGGER.debugf("Content was already present in repository at location %s", realFile.toAbsolutePath().toString()); } else { moveTempToPermanent(contentDir, realFile); deleteRecursively(tmp); DeploymentRepositoryLogger.ROOT_LOGGER.contentAdded(realFile.toAbsolutePath().toString()); } return sha1Bytes; } return deploymentHash; } catch (IOException ex) { DeploymentRepositoryLogger.ROOT_LOGGER.warn(ex); throw DeploymentRepositoryLogger.ROOT_LOGGER.errorUpdatingDeployment(ex); } } @Override public void start(StartContext context) throws StartException { DeploymentRepositoryLogger.ROOT_LOGGER.debugf("%s started", ContentRepository.class.getSimpleName()); } @Override public void stop(StopContext context) { DeploymentRepositoryLogger.ROOT_LOGGER.debugf("%s stopped", ContentRepository.class.getSimpleName()); } @Override public ContentRepository getValue() throws IllegalStateException, IllegalArgumentException { return this; } }