@Test public void hiddenTargetHandlingAtRoot() throws Exception { // test subject final DefaultFSPeer subject = new DefaultFSPeer(); // repo base File repoBase = new File("target/repoId"); // the file we want to store File target = new File(repoBase, "archetype-catalog.xml"); target.getParentFile().mkdirs(); final StorageFileItem file = Mockito.mock(StorageFileItem.class); Mockito.when(file.getPath()).thenReturn("/archetype-catalog.xml"); Mockito.when(file.getParentPath()).thenReturn("/"); // getting hidden target for target File hiddenTarget = subject.getHiddenTarget(null, repoBase, target, file); assertThat(hiddenTarget, notNullValue()); assertThat(hiddenTarget, isFile()); // startsWith, as garbage is appended to it's end assertThat(hiddenTarget.getName(), startsWith("archetype-catalog.xml")); // contains, as OS path from root is prefixing this, and garbage at the end suffixing it assertThat( hiddenTarget.getPath(), containsString( "target/repoId/.nexus/tmp/archetype-catalog.xml".replace("/", File.separator))); // writing to hidden target is handled elsewhere, so we simulate content being written out final String PAYLOAD = "dummy payload"; FileUtils.write(hiddenTarget, PAYLOAD); // handle the rename subject.handleRenameOperation(hiddenTarget, target); // hidden should cease to exist assertThat(hiddenTarget, not(exists())); // target should exists assertThat(target, exists()); // name should be not garbaged anymore assertThat(target.getName(), equalTo("archetype-catalog.xml")); // path prefixed by OS from root, no garbage at tail assertThat( target.getPath(), endsWith("target/repoId/archetype-catalog.xml".replace("/", File.separator))); // content is fine too assertThat(FileUtils.readFileToString(target), equalTo(PAYLOAD)); }
@Override public void storeItem( final Repository repository, final File repositoryBaseDir, final StorageItem item, final File target, final ContentLocator cl) throws UnsupportedStorageOperationException, LocalStorageException { // create parents down to the file itself (this will make those if needed, otherwise return // silently) mkParentDirs(repository, target); if (cl != null) { // we have _content_ (content or link), hence we store a file final File hiddenTarget = getHiddenTarget(repository, repositoryBaseDir, target, item); // NEXUS-4550: Part One, saving to "hidden" (temp) file // In case of error cleaning up only what needed // No locking needed, AbstractRepository took care of that FileOutputStream os = null; InputStream is = null; try { os = new FileOutputStream(hiddenTarget); is = cl.getContent(); IOUtil.copy(is, os, getCopyStreamBufferSize()); os.flush(); } catch (EOFException e) { if (hiddenTarget != null) { hiddenTarget.delete(); } throw new LocalStorageEofException( String.format( "EOF during storing on path \"%s\" (while writing to hiddenTarget: \"%s\")", item.getRepositoryItemUid().toString(), hiddenTarget.getAbsolutePath()), e); } catch (IOException e) { if (hiddenTarget != null) { hiddenTarget.delete(); } throw new LocalStorageException( String.format( "Got exception during storing on path \"%s\" (while writing to hiddenTarget: \"%s\")", item.getRepositoryItemUid().toString(), hiddenTarget.getAbsolutePath()), e); } finally { IOUtil.close(is); IOUtil.close(os); } // NEXUS-4550: Part Two, moving the "hidden" (temp) file to final location // In case of error cleaning up both files // Locking is needed, AbstractRepository got shared lock only for destination // NEXUS-4550: FSPeer is the one that handles the rename in case of FS LS, // so we need here to claim exclusive lock on actual UID to perform the rename final RepositoryItemUidLock uidLock = item.getRepositoryItemUid().getLock(); uidLock.lock(Action.create); try { handleRenameOperation(hiddenTarget, target); target.setLastModified(item.getModified()); } catch (IOException e) { // if we ARE NOT handling attributes, do proper cleanup in case of IOEx // if we ARE handling attributes, leave backups in case of IOEx final boolean isCleanupNeeded = !item.getRepositoryItemUid() .getBooleanAttributeValue(IsItemAttributeMetacontentAttribute.class); if (target != null && (isCleanupNeeded || // NEXUS-4871 prevent zero length/corrupt files target.length() == 0)) { target.delete(); } if (hiddenTarget != null && (isCleanupNeeded || // NEXUS-4871 prevent zero length/corrupt files hiddenTarget.length() == 0)) { hiddenTarget.delete(); } if (!isCleanupNeeded) { getLogger() .warn( "No cleanup done for error that happened while trying to save attibutes of item {}, the backup is left as {}!", item.getRepositoryItemUid().toString(), hiddenTarget.getAbsolutePath()); } throw new LocalStorageException( String.format( "Got exception during storing on path \"%s\" (while moving to final destination)", item.getRepositoryItemUid().toString()), e); } finally { uidLock.unlock(); } } else { // we have no content, we talk about directory target.mkdir(); target.setLastModified(item.getModified()); } }