/* * Copyright 2006-2015 University of Dundee. All rights reserved. * Use is subject to license terms supplied in LICENSE.txt */ package integration; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertTrue; import static org.testng.AssertJUnit.fail; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Formatter; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import ome.formats.OMEROMetadataStoreClient; import ome.io.bioformats.BfPyramidPixelBuffer; import ome.io.nio.PixelsService; import omero.ApiUsageException; import omero.RLong; import omero.RString; import omero.RType; import omero.ResourceError; import omero.ServerError; import omero.api.RawFileStorePrx; import omero.api.ThumbnailStorePrx; import omero.cmd.Delete2; import omero.cmd.Delete2Response; import omero.gateway.util.Requests; import omero.grid.RawAccessRequest; import omero.grid.RepositoryMap; import omero.grid.RepositoryPrx; import omero.model.Dataset; import omero.model.DatasetI; import omero.model.FileAnnotation; import omero.model.FileAnnotationI; import omero.model.IObject; import omero.model.Image; import omero.model.ImageAnnotationLink; import omero.model.ImageAnnotationLinkI; import omero.model.OriginalFile; import omero.model.Pixels; import omero.model.Plate; import omero.model.Well; import omero.sys.EventContext; import omero.sys.Parameters; import omero.sys.ParametersI; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.io.FilenameUtils; import org.springframework.util.ResourceUtils; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import com.google.common.collect.ImmutableMap; import omero.gateway.model.FileAnnotationData; /** * Collections of tests for the <code>Delete</code> service. * * @author Jean-Marie Burel      <a * href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a> * @author Donald MacDonald      <a * href="mailto:donald@lifesci.dundee.ac.uk" * >donald@lifesci.dundee.ac.uk</a> * @author Colin Blackburn      <a * href="mailto:c.blackburn@dundee.ac.uk">c.blackburn@dundee.ac.uk</a> * @version 3.0 <small> (<b>Internal version:</b> $Revision: $Date: $) </small> * @since 3.0-Beta4 */ public class DeleteServiceFilesTest extends AbstractServerTest { /** Reference to the <code>Pixels</code> class. */ private static final String REF_PIXELS = "Pixels"; /** Reference to the <code>OriginalFile</code> class. */ private static final String REF_ORIGINAL_FILE = "OriginalFile"; /** Reference to the <code>Thumbnail</code> class. */ private static final String REF_THUMBNAIL = "Thumbnail"; /** Enum representing type of pyramid file */ enum PyramidFileType { PYRAMID, PYRAMID_LOCK, PYRAMID_TMP }; /** Reference to the standard directory. */ private String dataDir; /** * Creates an original file. * * @return See above. * @throws ServerError * Thrown if an error occurred. * @throws Exception * Thrown if an error occurred. */ private OriginalFile makeFile() throws ServerError, Exception { OriginalFile of = (OriginalFile) iUpdate.saveAndReturnObject(mmFactory .createOriginalFile()); long ofId = of.getId().getValue(); RawFileStorePrx rfPrx = factory.createRawFileStore(); try { rfPrx.setFileId(ofId); rfPrx.write(new byte[] { 1, 2, 3, 4 }, 0, 4); of = rfPrx.save(); } finally { rfPrx.close(); } return of; } /** * Makes an image with pixels files. * * @return See above. * @throws ServerError * Thrown if an error occurred. * @throws Exception * Thrown if an error occurred. * */ private Image makeImageWithPixelsFile() throws ServerError, Exception { Image img = (Image) iUpdate .saveAndReturnObject(mmFactory.createImage()); Pixels pix = img.getPrimaryPixels(); String path = getPath(REF_PIXELS, pix.getId().getValue()); RepositoryPrx legacy = getLegacyRepository(); // FIXME: legacy.create(path); return img; } /** * Makes an image with pixels files. * * @return See above. * @throws ServerError * Thrown if an error occurred. * @throws Exception * Thrown if an error occurred. * */ private Image makeImageWithPixelsFile(boolean pixels, boolean pyramid, boolean lock, boolean tmp) throws ServerError, Exception { String path; Image img = (Image) iUpdate .saveAndReturnObject(mmFactory.createImage()); Pixels pix = img.getPrimaryPixels(); RepositoryPrx legacy = getLegacyRepository(); if (pixels) { path = getPath(REF_PIXELS, pix.getId().getValue()); // FIXME: legacy.create(path); } if (pyramid) { path = getOtherPixelsPath(pix.getId().getValue(), PyramidFileType.PYRAMID); // FIXME: legacy.create(path); } if (lock) { path = getOtherPixelsPath(pix.getId().getValue(), PyramidFileType.PYRAMID_LOCK); // FIXME: legacy.create(path); } if (tmp) { path = getOtherPixelsPath(pix.getId().getValue(), PyramidFileType.PYRAMID_TMP); // FIXME: legacy.create(path); } return img; } /** * Checks if thumbnails, files and pixels have not been deleted. * * @param report * The report from the delete operation */ private void assertNoUndeletedBinaries(Delete2Response report) { assertNoUndeletedThumbnails(report); assertNoUndeletedFiles(report); assertNoUndeletedPixels(report); } /** * Checks if the thumbnails have been deleted. * * @param report * The report from the delete operation */ private void assertNoUndeletedThumbnails(Delete2Response report) { List<Long> tbIds = report.deletedObjects.get(REF_THUMBNAIL); assertTrue(CollectionUtils.isEmpty(tbIds)); } /** * Checks if the files have been deleted. * * @param report * The report from the delete operation */ private void assertNoUndeletedFiles(Delete2Response report) { List<Long> fileIds = report.deletedObjects.get(REF_ORIGINAL_FILE); assertTrue(CollectionUtils.isEmpty(fileIds)); } /** * Checks if the pixels have been deleted. * * @param report * The report from the delete operation */ private void assertNoUndeletedPixels(Delete2Response report) { List<Long> pixIds = report.deletedObjects.get(REF_PIXELS); assertTrue(CollectionUtils.isEmpty(pixIds)); } /** * Set the data directory for the tests. This is needed to find the correct * repository to test whether deletes have been successful. */ @BeforeClass public void setDataDir() throws Exception { dataDir = root.getSession().getConfigService() .getConfigValue("omero.data.dir"); } /** * Since so many tests rely on counting the number of objects present * globally, we're going to start each method with a new user in a new * group. */ @BeforeMethod public void createNewUser() throws Exception { newUserAndGroup("rw----"); } /** * Since we are creating a new client on each invocation, we should also * clean it up. Note: {@link #newUserAndGroup(String)} also closes, but not * the very last invocation. */ @AfterMethod public void close() throws Exception { if (client != null) { client.__del__(); client = null; } } /** * Basic asynchronous delete command. Used in order to reduce the number of * places that we do the same thing in case the API changes. * * @param dc * The SINGLE command to handle. * @throws ApiUsageException * @throws ServerError * @throws InterruptedException */ private Delete2Response deleteWithReport(Delete2 dc) throws ApiUsageException, ServerError, InterruptedException { return singleDeleteWithReport(client, dc); } /** * Forms a path depending on the type of file to be deleted and its id. * * @param dataDir * The path to the directory * @param klass * The type of object to handle. * @param id * The identifier of the object. */ private String getPath(String klass, Long id) throws Exception { String suffix = ""; String prefix = ""; Long remaining = id; Long dirno = 0L; if (id == null) { throw new NullPointerException("Expecting a not-null id."); } if (klass.equals(REF_ORIGINAL_FILE)) { prefix = FilenameUtils.concat(dataDir, "Files"); } else if (klass.equals(REF_PIXELS)) { prefix = FilenameUtils.concat(dataDir, "Pixels"); } else if (klass.equals(REF_THUMBNAIL)) { prefix = FilenameUtils.concat(dataDir, "Thumbnails"); } else { throw new Exception("Unknown class: " + klass); } final Formatter formatter = new Formatter(); while (remaining > 999) { remaining /= 1000; if (remaining > 0) { dirno = remaining % 1000; suffix = formatter.format("Dir-%03d", dirno).out().toString() + File.separator + suffix; } } formatter.close(); String path = FilenameUtils.concat(prefix, suffix + id); return path; } /** * Helper to resolve file names for pyramid-related files */ String getOtherPixelsPath(Long id, PyramidFileType kind) throws Exception { String path = getPath(REF_PIXELS, id); if (kind.equals(PyramidFileType.PYRAMID)) { path += PixelsService.PYRAMID_SUFFIX; } else if (kind.equals(PyramidFileType.PYRAMID_LOCK)) { File file = new File(path); File dir = file.getParentFile(); File lockFile = new File(dir, "." + id + PixelsService.PYRAMID_SUFFIX + BfPyramidPixelBuffer.PYR_LOCK_EXT); path = lockFile.getAbsolutePath(); } else if (kind.equals(PyramidFileType.PYRAMID_TMP)) { File file = new File(path); File dir = file.getParentFile(); File tmpFile = new File(dir, "." + id + PixelsService.PYRAMID_SUFFIX + "1234567890.tmp"); path = tmpFile.getAbsolutePath(); } else { throw new Exception("Unknown kind: " + kind); } return path; } /** * Gets a public repository on the OMERO data directory if one exists. * * @return See above. * @throws Exception * Thrown if an error occurred. */ RepositoryPrx getLegacyRepository() throws Exception { RepositoryPrx legacy = null; RepositoryMap rm = factory.sharedResources().repositories(); int repoCount = 0; String s = dataDir; for (OriginalFile desc : rm.descriptions) { String repoPath = desc.getPath().getValue() + desc.getName().getValue(); s += "\nFound repository:" + desc.getPath().getValue() + desc.getName().getValue(); if (FilenameUtils.equals( FilenameUtils.normalizeNoEndSeparator(dataDir), FilenameUtils.normalizeNoEndSeparator(repoPath))) { legacy = rm.proxies.get(repoCount); break; } repoCount++; } if (legacy == null) { throw new Exception("Unable to find legacy repository: " + s); } return legacy; } /** * Makes sure that the OMERO file exists of the given type and id * * @param id * The object id corresponding to the filename. * @param klass * The class (table name) of the object. * @throws Exception * Thrown if an error occurred. */ void assertFileExists(Long id, String klass) throws Exception { String path = getPath(klass, id); RepositoryPrx legacy = getLegacyRepository(); assertTrue(path + " does not exist!", legacy.fileExists(path)); } /** * Makes sure that the OMERO file exists of the given type and id * * @param id * The object id corresponding to the filename. * @param klass * The class (table name) of the object. * @throws Exception * Thrown if an error occurred. */ void assertOtherPixelsFileExists(Long id, PyramidFileType kind) throws Exception { String path = getOtherPixelsPath(id, kind); RepositoryPrx legacy = getLegacyRepository(); assertTrue(path + " does not exist!", legacy.fileExists(path)); } /** * Forcibly delete a file. * * @param id * @param klass * @throws Exception * @see ticket:3140 */ void removeFile(Long id, String klass) throws Exception { String path = getPath(klass, id); RepositoryPrx legacy = getLegacyRepository(); // For admins only. Primarily a test feature. RawAccessRequest raw = new RawAccessRequest(); raw.repoUuid = legacy.root().getHash().getValue(); raw.command = "rm"; raw.args = Arrays.asList(path); doChange(client, factory, raw, true); } /** * Makes sure that the OMERO file exists of the given type and id * * @param id * The object id corresponding to the filename. * @param klass * The class (table name) of the object. * @throws Exception * Thrown if an error occurred. */ void assertFileDoesNotExist(Long id, String klass) throws Exception { String path = getPath(klass, id); RepositoryPrx legacy = getLegacyRepository(); assertFalse(path + " exists!", legacy.fileExists(path)); } /** * Makes sure that the OMERO file exists of the given type and id * * @param id * The object id corresponding to the filename. * @param klass * The class (table name) of the object. * @throws Exception * Thrown if an error occurred. */ void assertOtherPixelsFileDoesNotExist(Long id, PyramidFileType kind) throws Exception { String path = getOtherPixelsPath(id, kind); RepositoryPrx legacy = getLegacyRepository(); assertFalse(path, legacy.fileExists(path)); } /** * Test to delete an image and make sure pixels file is deleted. * * @throws Exception * Thrown if an error occurred. */ @Test(groups = {"ticket:2880", "broken"}) public void testDeleteImageWithPixelsOnDisk() throws Exception { Image img = makeImageWithPixelsFile(); Pixels pix = img.getPrimaryPixels(); // Now check that the files have been created and then deleted. assertFileExists(pix.getId().getValue(), REF_PIXELS); Delete2 dc = Requests.delete("Image", img.getId().getValue()); Delete2Response report = deleteWithReport(dc); assertFileDoesNotExist(pix.getId().getValue(), REF_PIXELS); assertEquals(report.deletedObjects.get(REF_PIXELS).size(), 1); } /** * Test to delete an image and make sure pyramid file is deleted. * * @throws Exception * Thrown if an error occurred. */ @Test(groups = "broken") public void testDeleteImageWithPyramidOnDisk() throws Exception { Image img = makeImageWithPixelsFile(false, true, false, false); Pixels pix = img.getPrimaryPixels(); // Now check that the files have been created and then deleted. assertOtherPixelsFileExists(pix.getId().getValue(), PyramidFileType.PYRAMID); Delete2 dc = Requests.delete("Image", img.getId().getValue()); Delete2Response report = deleteWithReport(dc); assertOtherPixelsFileDoesNotExist(pix.getId().getValue(), PyramidFileType.PYRAMID); assertEquals(report.deletedObjects.get(REF_PIXELS).size(), 1); } /** * Test to delete an image and make sure pixels and pyramid files are * deleted. * * @throws Exception * Thrown if an error occurred. */ @Test(groups = "broken") public void testDeleteImageWithPixelsAndPyramidOnDisk() throws Exception { Image img = makeImageWithPixelsFile(true, true, false, false); Pixels pix = img.getPrimaryPixels(); // Now check that the files have been created and then deleted. assertFileExists(pix.getId().getValue(), REF_PIXELS); assertOtherPixelsFileExists(pix.getId().getValue(), PyramidFileType.PYRAMID); Delete2 dc = Requests.delete("Image", img.getId().getValue()); Delete2Response report = deleteWithReport(dc); assertFileDoesNotExist(pix.getId().getValue(), REF_PIXELS); assertOtherPixelsFileDoesNotExist(pix.getId().getValue(), PyramidFileType.PYRAMID); assertEquals(report.deletedObjects.get(REF_PIXELS).size(), 1); } /** * Test to delete an image and make sure pixels and pyramid files are * deleted. * * @throws Exception * Thrown if an error occurred. */ @Test(groups = "broken") public void testDeleteImageWithAllPyramidOnDisk() throws Exception { Image img = makeImageWithPixelsFile(false, true, true, true); Pixels pix = img.getPrimaryPixels(); // Now check that the files have been created and then deleted. assertOtherPixelsFileExists(pix.getId().getValue(), PyramidFileType.PYRAMID); assertOtherPixelsFileExists(pix.getId().getValue(), PyramidFileType.PYRAMID_LOCK); assertOtherPixelsFileExists(pix.getId().getValue(), PyramidFileType.PYRAMID_TMP); Delete2 dc = Requests.delete("Image", img.getId().getValue()); Delete2Response report = deleteWithReport(dc); assertOtherPixelsFileDoesNotExist(pix.getId().getValue(), PyramidFileType.PYRAMID); assertOtherPixelsFileDoesNotExist(pix.getId().getValue(), PyramidFileType.PYRAMID_LOCK); assertOtherPixelsFileDoesNotExist(pix.getId().getValue(), PyramidFileType.PYRAMID_TMP); assertEquals(report.deletedObjects.get(REF_PIXELS).size(), 1); } /** * Test to delete an image and make sure the companion file is deleted. * * @throws Exception * Thrown if an error occurred. */ @Test(groups = {"ticket:2880", "broken"}) public void testDeleteImageWithOriginalFileOnDisk() throws Exception { Image img = (Image) iUpdate .saveAndReturnObject(mmFactory.createImage()); // This creates an attached OriginalFle and a subsequent Files file. // Is there a more concise way to achieve the same thing? cgb OriginalFile of = (OriginalFile) iUpdate.saveAndReturnObject(mmFactory .createOriginalFile()); FileAnnotation fa = new FileAnnotationI(); fa.setNs(omero.rtypes.rstring(FileAnnotationData.COMPANION_FILE_NS)); fa.setFile(of); fa = (FileAnnotation) iUpdate.saveAndReturnObject(fa); ImageAnnotationLink l = new ImageAnnotationLinkI(); l.setChild(fa); l.setParent(img); iUpdate.saveAndReturnObject(l); long ofId = of.getId().getValue(); RawFileStorePrx rfPrx = factory.createRawFileStore(); try { rfPrx.setFileId(ofId); rfPrx.write(new byte[] { 1, 2, 3, 4 }, 0, 4); } finally { rfPrx.close(); } // Now check that the files have been created and then deleted. assertFileExists(ofId, REF_ORIGINAL_FILE); Delete2 dc = Requests.delete("Image", img.getId().getValue()); Delete2Response report = deleteWithReport(dc); assertFileDoesNotExist(ofId, REF_ORIGINAL_FILE); assertNoUndeletedBinaries(report); } /** * Test to delete an image with no files associated. No exceptions should * arise if the files don't exist. * * @throws Exception * Thrown if an error occurred. */ @Test(groups = "ticket:2880") public void testDeleteImageWithoutFilesOnDisk() throws Exception { Image img = (Image) iUpdate .saveAndReturnObject(mmFactory.createImage()); Pixels pixels = img.getPrimaryPixels(); long pixId = pixels.getId().getValue(); OriginalFile of = (OriginalFile) iUpdate.saveAndReturnObject(mmFactory .createOriginalFile()); FileAnnotation fa = new FileAnnotationI(); fa.setNs(omero.rtypes.rstring(FileAnnotationData.COMPANION_FILE_NS)); fa.setFile(of); fa = (FileAnnotation) iUpdate.saveAndReturnObject(fa); ImageAnnotationLink l = new ImageAnnotationLinkI(); l.setChild(fa); l.setParent(img); iUpdate.saveAndReturnObject(l); long ofId = of.getId().getValue(); // Now check that the files have NOT been created and then deleted. assertFileDoesNotExist(pixId, REF_PIXELS); assertFileDoesNotExist(ofId, REF_ORIGINAL_FILE); Delete2 dc = Requests.delete("Image", img.getId().getValue()); Delete2Response report = deleteWithReport(dc); assertNoUndeletedBinaries(report); } /** * Test to delete an image and make sure the thumbnail on disk is deleted. * * @throws Exception * Thrown if an error occurred. */ @Test(groups = {"ticket:2880", "broken"}) public void testDeleteImageWithThumbnailOnDisk() throws Exception { File f = File.createTempFile("testDeleteImageWithThumbnailOnDisk" + ModelMockFactory.FORMATS[0], "." + ModelMockFactory.FORMATS[0]); mmFactory.createImageFile(f, ModelMockFactory.FORMATS[0]); OMEROMetadataStoreClient importer = new OMEROMetadataStoreClient(); importer.initialize(factory); List<Pixels> list; try { list = importFile(importer, f, ModelMockFactory.FORMATS[0], false); } catch (Throwable e) { throw new Exception("cannot import image", e); } Pixels pixels = list.get(0); long id = pixels.getId().getValue(); List<Long> ids = new ArrayList<Long>(); ids.add(id); long imageID = pixels.getImage().getId().getValue(); ThumbnailStorePrx svc = factory.createThumbnailStore(); // make sure we have a thumbnail on disk // request a different size to make sure all thumbnails are deleted. Map<Long, byte[]> thumbnails = svc.getThumbnailSet( omero.rtypes.rint(40), omero.rtypes.rint(40), ids); byte[] values = thumbnails.get(id); assertNotNull(values); assertTrue(values.length > 0); String sql = "select i from Thumbnail i where i.pixels.id = :id"; ParametersI param = new ParametersI(); param.addId(id); List<IObject> objects = iQuery.findAllByQuery(sql, param); assertNotNull(objects); assertTrue(objects.size() > 0); List<Long> thumbIds = new ArrayList<Long>(); Iterator<IObject> i = objects.iterator(); long thumbId; while (i.hasNext()) { thumbId = i.next().getId().getValue(); assertFileExists(thumbId, REF_THUMBNAIL); } // delete the image. Delete2 dc = Requests.delete("Image", imageID); Delete2Response report = deleteWithReport(dc); assertNoUndeletedBinaries(report); assertFileDoesNotExist(id, "Pixels"); Iterator<Long> j = thumbIds.iterator(); while (j.hasNext()) { assertFileDoesNotExist(j.next(), REF_THUMBNAIL); } } /** * Test to delete an image and make sure the thumbnail on disk is deleted. * The image has been viewed another member of the group. * * @throws Exception * Thrown if an error occurred. */ @Test public void testDeleteImageViewedByOtherWithThumbnailOnDiskRWRW() throws Exception { EventContext ownerCtx = newUserAndGroup("rwrw--"); File f = File.createTempFile("testDeleteImageWithThumbnailOnDisk" + ModelMockFactory.FORMATS[0], "." + ModelMockFactory.FORMATS[0]); mmFactory.createImageFile(f, ModelMockFactory.FORMATS[0]); OMEROMetadataStoreClient importer = new OMEROMetadataStoreClient(); importer.initialize(factory); List<Pixels> list; try { list = importFile(importer, f, ModelMockFactory.FORMATS[0], false); } catch (Throwable e) { throw new Exception("cannot import image", e); } Pixels pixels = list.get(0); long id = pixels.getId().getValue(); List<Long> ids = new ArrayList<Long>(); ids.add(id); long imageID = pixels.getImage().getId().getValue(); ThumbnailStorePrx svc = factory.createThumbnailStore(); // make sure we have a thumbnail on disk // request a different size to make sure all thumbnails are deleted. int sizeX = 96; int sizeY = 96; Map<Long, byte[]> thumbnails = svc.getThumbnailSet( omero.rtypes.rint(sizeX), omero.rtypes.rint(sizeY), ids); assertNotNull(thumbnails.get(id)); String sql = "select i from Thumbnail i where i.pixels.id = :id"; ParametersI param = new ParametersI(); param.addId(id); List<IObject> objects = iQuery.findAllByQuery(sql, param); assertNotNull(objects); assertTrue(objects.size() > 0); List<Long> thumbIds = new ArrayList<Long>(); Iterator<IObject> i = objects.iterator(); while (i.hasNext()) { thumbIds.add(i.next().getId().getValue()); } newUserInGroup(ownerCtx); svc = factory.createThumbnailStore(); thumbnails = svc.getThumbnailSet(omero.rtypes.rint(sizeX), omero.rtypes.rint(sizeY), ids); assertNotNull(thumbnails.get(id)); objects = iQuery.findAllByQuery(sql, param); assertTrue(objects.size() > 0); i = objects.iterator(); long thumbId; while (i.hasNext()) { thumbId = i.next().getId().getValue(); if (!thumbIds.contains(thumbId)) thumbIds.add(thumbId); } disconnect(); loginUser(ownerCtx); // Now try to delete the image. Delete2 dc = Requests.delete("Image", imageID); Delete2Response report = deleteWithReport(dc); Iterator<Long> j = thumbIds.iterator(); while (j.hasNext()) { assertFileDoesNotExist(j.next(), REF_THUMBNAIL); } assertNoUndeletedBinaries(report); } /** * Test to delete a dataset containing and image that is also in another * dataset. The Image and its Pixels file should NOT be deleted. * * @throws Exception * Thrown if an error occurred. */ @Test(groups = {"ticket:3031", "broken"}) public void testDeletingDatasetWithPixelsFiles() throws Exception { Dataset ds1 = new DatasetI(); ds1.setName(omero.rtypes.rstring("#3031.1")); Dataset ds2 = new DatasetI(); ds2.setName(omero.rtypes.rstring("#3031.2")); Image img = makeImageWithPixelsFile(); Pixels pix = img.getPrimaryPixels(); // Now check that the files have been created and then deleted. assertFileExists(pix.getId().getValue(), REF_PIXELS); ds1.linkImage(img); ds1 = (Dataset) iUpdate.saveAndReturnObject(ds1); ds2.linkImage(img); ds2 = (Dataset) iUpdate.saveAndReturnObject(ds2); Delete2 dc = Requests.delete("Dataset", ds2.getId().getValue()); delete(client, dc); assertDoesNotExist(ds2); assertExists(ds1); assertExists(img); assertFileExists(pix.getId().getValue(), REF_PIXELS); } /** * Test to delete a dataset containing multiple images all Pixels files * should be deleted. * * @throws Exception * Thrown if an error occurred. */ @Test(groups = {"ticket:3130", "broken"}) public void testDeletingDatasetWithSeveralPixelsFiles() throws Exception { Dataset ds = new DatasetI(); ds.setName(omero.rtypes.rstring("#3130")); Image img1 = makeImageWithPixelsFile(); Pixels pix1 = img1.getPrimaryPixels(); // A second Image Image img2 = makeImageWithPixelsFile(); Pixels pix2 = img2.getPrimaryPixels(); // link to dataset ds.linkImage(img1); ds = (Dataset) iUpdate.saveAndReturnObject(ds); ds.linkImage(img2); ds = (Dataset) iUpdate.saveAndReturnObject(ds); // Now check that the files have been created and then deleted. assertFileExists(pix1.getId().getValue(), REF_PIXELS); assertFileExists(pix2.getId().getValue(), REF_PIXELS); Delete2 dc = Requests.delete("Dataset", ds.getId().getValue()); Delete2Response report = deleteWithReport(dc); assertNoUndeletedBinaries(report); assertNoneExist(ds, img1, img2, pix1, pix2); assertFileDoesNotExist(pix1.getId().getValue(), REF_PIXELS); assertFileDoesNotExist(pix2.getId().getValue(), REF_PIXELS); } /** * Test to delete a dataset containing multiple images all Pixels files * should be deleted. * * @throws Exception * Thrown if an error occurred. */ @Test(groups = {"ticket:3148", "broken"}) public void testDeletingImageWithSeveralOriginalFiles() throws Exception { Image img = (Image) iUpdate .saveAndReturnObject(mmFactory.createImage()).proxy(); // This creates an attached OriginalFle and a subsequent Files file. // Is there a more concise way to achieve the same thing? cgb OriginalFile of1 = makeFile(); FileAnnotation fa = new FileAnnotationI(); fa.setNs(omero.rtypes.rstring(FileAnnotationData.COMPANION_FILE_NS)); fa.setFile(of1); fa = (FileAnnotation) iUpdate.saveAndReturnObject(fa); ImageAnnotationLink l = new ImageAnnotationLinkI(); l.setChild(fa); l.setParent(img); iUpdate.saveAndReturnObject(l); OriginalFile of2 = makeFile(); fa = new FileAnnotationI(); fa.setNs(omero.rtypes.rstring(FileAnnotationData.COMPANION_FILE_NS)); fa.setFile(of2); fa = (FileAnnotation) iUpdate.saveAndReturnObject(fa); l = new ImageAnnotationLinkI(); l.setChild(fa); l.setParent(img); iUpdate.saveAndReturnObject(l); // Now check that the files have been created and then deleted. assertFileExists(of1.getId().getValue(), REF_ORIGINAL_FILE); assertFileExists(of2.getId().getValue(), REF_ORIGINAL_FILE); Delete2 dc = Requests.delete("Image", img.getId().getValue()); Delete2Response report = deleteWithReport(dc); assertNoneExist(img, of1, of2); assertFileDoesNotExist(of1.getId().getValue(), REF_ORIGINAL_FILE); assertFileDoesNotExist(of2.getId().getValue(), REF_ORIGINAL_FILE); assertNoUndeletedBinaries(report); } /** * Test to delete a dataset containing and image that is from a Well. The * Image and its Pixels file should NOT be deleted. * * @throws Exception * Thrown if an error occurred. */ @Test(groups = {"ticket:2946", "broken"}) public void testDeleteDatasetThatContainsImageFromAWell() throws Exception { Image img = makeImageWithPixelsFile(); Pixels pix = img.getPrimaryPixels(); Plate p = (Plate) iUpdate.saveAndReturnObject(mmFactory.createPlate(1, 1, 1, 0, false)); List<Well> wells = loadWells(p.getId().getValue(), false); wells.get(0).copyWellSamples().get(0).setImage(img); Well well = (Well) iUpdate.saveAndReturnObject(wells.get(0)); Dataset ds = new DatasetI(); ds.setName(omero.rtypes.rstring("#2946")); // link to dataset ds.linkImage(img); ds = (Dataset) iUpdate.saveAndReturnObject(ds); // Now check that the file has been created. assertFileExists(pix.getId().getValue(), REF_PIXELS); Delete2 dc = Requests.delete("Dataset", ds.getId().getValue()); Delete2Response report = deleteWithReport(dc); // The dataset should be gone but nothing else. assertNoneExist(ds); assertAllExist(p, well, img, pix); assertFileExists(pix.getId().getValue(), REF_PIXELS); assertNoUndeletedBinaries(report); } /** * Test to try to delete an image owned by another user in a collaborative * group i.e. RWR--- * * @throws Exception * Thrown if an error occurred. */ @Test(groups = {"ticket:2946", "broken"}) public void testDeleteImagePixelsFileOwnedByOtherRWR() throws Exception { // set up collaborative group and one user, "the owner" newUserAndGroup("rwr---"); Image img = makeImageWithPixelsFile(); Pixels pix = img.getPrimaryPixels(); // Now check that the file has been created. assertFileExists(pix.getId().getValue(), REF_PIXELS); // create another user and try to delete the image newUserInGroup(); Delete2 dc = Requests.delete("Image", img.getId().getValue()); Delete2Response report = deleteWithReport(dc); // check the image exists as the owner assertExists(img); // Now check that the file has not been deleted. assertFileExists(pix.getId().getValue(), REF_PIXELS); assertNoUndeletedBinaries(report); } /** * Test to remove a file and try to save it using the RawFileStore. * * @throws Exception * Thrown if an error occurred. */ @Test(groups = {"ticket:3140", "broken"}, expectedExceptions = ResourceError.class) public void testSaveThrowsResourceErrorIfDeleted() throws Exception { OriginalFile of = makeFile(); RawFileStorePrx rfs = factory.createRawFileStore(); try { rfs.setFileId(of.getId().getValue()); rfs.write(new byte[1], 0, 1); removeFile(of.getId().getValue(), "OriginalFile"); rfs.save(); fail("Should not reach here."); } finally { rfs.close(); } } /** * Test to check the <code>close</code> method of the RawFileStore after the * file has been deleted. * * @throws Exception * Thrown if an error occurred. */ @Test(groups = {"ticket:3140", "broken"}, expectedExceptions = ResourceError.class) public void testCloseThrowsResourceErrorIfDeleted() throws Exception { OriginalFile of = makeFile(); RawFileStorePrx rfs = factory.createRawFileStore(); try { rfs.setFileId(of.getId().getValue()); rfs.write(new byte[1], 0, 1); removeFile(of.getId().getValue(), "OriginalFile"); fail("Should not reach here."); } finally { rfs.close(); } } /** * Check that a set of original files may be deleted by a single {@link Delete2} request * regardless of how they are ordered with regard to containing one another in a directory hierarchy, * as enforced by {@code _fs_dir_delete trigger} and by the underlying filesystem. * @throws Throwable unexpected */ @Test public void testRecursiveDelete() throws Throwable { /* for HQL queries */ String query; Parameters params; List<List<RType>> results; /* keep count of how many files are expected to be deleted */ int fileCount = 0; /* import a small image to discover a suitable location in the repository for further testing */ final File imageFile = ResourceUtils.getFile("classpath:tinyTest.d3d.dv"); final long filesetId = importFile(imageFile, "dv").get(0).getImage().getFileset().getId().getValue(); fileCount += 2; /* for image file and import log */ /* find the managed repository directory for the imported image file */ query = "SELECT originalFile.path FROM FilesetEntry WHERE fileset.id = :id"; params = new Parameters(); params.map = ImmutableMap.<String, RType>of("id", omero.rtypes.rlong(filesetId)); results = iQuery.projection(query, params); String pathName = ((RString) results.get(0).get(0)).getValue(); /* create a deeply nested directory hierarchy with files at every level */ final int count = 12; final RepositoryPrx mrepo = getManagedRepository(); final String directoryName = "bar"; final String fileName = "baz"; final List<Long> fileIds = new ArrayList<Long>(); final byte[] toWrite = "dummy file".getBytes(); query = "SELECT id FROM OriginalFile WHERE mimetype = 'Directory' AND path = :path AND name = :name"; /* recurse into deeper directories */ for (int i = 0; i < count; i++) { /* create another directory and note its ID */ mrepo.makeDir(pathName + directoryName, false); params = new Parameters(); params.map = ImmutableMap.<String, RType>of("path", omero.rtypes.rstring(pathName), "name", omero.rtypes.rstring(directoryName)); results = iQuery.projection(query, params); fileIds.add(((RLong) results.get(0).get(0)).getValue()); fileCount++; /* create another file and note its ID */ final RawFileStorePrx rfs = mrepo.file(pathName + fileName, "rw"); rfs.write(toWrite, 0, toWrite.length); fileIds.add(rfs.save().getId().getValue()); rfs.close(); fileCount++; pathName += directoryName + '/'; } /* shuffle the file ID list randomly */ Collections.shuffle(fileIds); /* prepare to delete all the files and directories that were created */ final Delete2 request = new Delete2(); request.targetObjects = new HashMap<String, List<Long>>(); request.targetObjects.put("Fileset", Collections.singletonList(filesetId)); request.targetObjects.put("OriginalFile", fileIds); /* perform the deletion and confirm that it successfully deletes all the files */ final Delete2Response deletions = (Delete2Response) doChange(request); assertEquals(fileCount, deletions.deletedObjects.get(ome.model.core.OriginalFile.class.getName()).size()); } }