public void testImmediateRemoval() throws Exception {
    eagerCleaner.setEagerOrphanCleanup(false);

    final StoreRef storeRef = nodeService.createStore("test", getName() + "-" + GUID.generate());
    RetryingTransactionCallback<ContentData> testCallback =
        new RetryingTransactionCallback<ContentData>() {
          public ContentData execute() throws Throwable {
            // Create some content
            NodeRef rootNodeRef = nodeService.getRootNode(storeRef);
            Map<QName, Serializable> properties = new HashMap<QName, Serializable>(13);
            properties.put(ContentModel.PROP_NAME, (Serializable) "test.txt");
            NodeRef contentNodeRef =
                nodeService
                    .createNode(
                        rootNodeRef,
                        ContentModel.ASSOC_CHILDREN,
                        ContentModel.ASSOC_CHILDREN,
                        ContentModel.TYPE_CONTENT,
                        properties)
                    .getChildRef();
            ContentWriter writer =
                contentService.getWriter(contentNodeRef, ContentModel.PROP_CONTENT, true);
            writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
            writer.putContent("INITIAL CONTENT");
            ContentData contentData = writer.getContentData();

            // Delete the first node
            nodeService.deleteNode(contentNodeRef);

            // Done
            return contentData;
          }
        };
    ContentData contentData =
        transactionService.getRetryingTransactionHelper().doInTransaction(testCallback);
    // Make sure that the content URL still exists
    ContentReader reader = contentService.getRawReader(contentData.getContentUrl());
    assertNotNull(reader);
    assertTrue("Content should not have been eagerly deleted.", reader.exists());

    // fire the cleaner
    cleaner.setProtectDays(0);
    cleaner.execute();

    reader = contentService.getRawReader(contentData.getContentUrl());
    // the content should have disappeared as it is not in the database
    assertFalse("Unprotected content was not deleted", reader.exists());
    assertTrue("Content listener was not called", deletedUrls.contains(reader.getContentUrl()));
  }
  /**
   * Create ContentData set it on a Node, delete the Node, then set the ContentData on a new node
   * and check that the content is preserved during eager cleanup.
   */
  public void testEagerCleanupDereferencing() throws Exception {
    eagerCleaner.setEagerOrphanCleanup(true);

    final StoreRef storeRef = nodeService.createStore("test", getName() + "-" + GUID.generate());
    RetryingTransactionCallback<ContentData> testCallback =
        new RetryingTransactionCallback<ContentData>() {
          public ContentData execute() throws Throwable {
            // Create some content
            NodeRef rootNodeRef = nodeService.getRootNode(storeRef);
            Map<QName, Serializable> properties = new HashMap<QName, Serializable>(13);
            properties.put(ContentModel.PROP_NAME, (Serializable) "test.txt");
            NodeRef contentNodeRef =
                nodeService
                    .createNode(
                        rootNodeRef,
                        ContentModel.ASSOC_CHILDREN,
                        ContentModel.ASSOC_CHILDREN,
                        ContentModel.TYPE_CONTENT,
                        properties)
                    .getChildRef();
            ContentWriter writer =
                contentService.getWriter(contentNodeRef, ContentModel.PROP_CONTENT, true);
            writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
            writer.putContent("INITIAL CONTENT");
            ContentData contentData = writer.getContentData();

            // Delete the first node
            nodeService.deleteNode(contentNodeRef);

            ContentReader reader = contentService.getRawReader(contentData.getContentUrl());
            assertNotNull(reader);
            assertTrue("Content was cleaned before end of transaction", reader.exists());

            // Make a new copy using the same ContentData
            properties.put(ContentModel.PROP_NAME, (Serializable) "test2.txt");
            properties.put(ContentModel.PROP_CONTENT, contentData);
            contentNodeRef =
                nodeService
                    .createNode(
                        rootNodeRef,
                        ContentModel.ASSOC_CHILDREN,
                        ContentModel.ASSOC_CHILDREN,
                        ContentModel.TYPE_CONTENT,
                        properties)
                    .getChildRef();

            reader = contentService.getRawReader(contentData.getContentUrl());
            assertNotNull(reader);
            assertTrue("Content was cleaned before end of transaction", reader.exists());

            // Done
            return contentData;
          }
        };
    ContentData contentData =
        transactionService.getRetryingTransactionHelper().doInTransaction(testCallback);
    // Make sure that the content URL still exists
    ContentReader reader = contentService.getRawReader(contentData.getContentUrl());
    assertNotNull(reader);
    assertTrue(
        "Content was cleaned despite being re-referenced in the transaction", reader.exists());
  }
  public void testEagerCleanupOnCommit() throws Exception {
    eagerCleaner.setEagerOrphanCleanup(true);
    // Create a new file
    RetryingTransactionCallback<NodeRef> makeContentCallback =
        new RetryingTransactionCallback<NodeRef>() {
          public NodeRef execute() throws Throwable {
            // Create some content
            StoreRef storeRef =
                nodeService.createStore("test", "testEagerCleanupOnCommit-" + GUID.generate());
            NodeRef rootNodeRef = nodeService.getRootNode(storeRef);
            Map<QName, Serializable> properties =
                Collections.singletonMap(ContentModel.PROP_NAME, (Serializable) "test.txt");
            NodeRef contentNodeRef =
                nodeService
                    .createNode(
                        rootNodeRef,
                        ContentModel.ASSOC_CHILDREN,
                        ContentModel.ASSOC_CHILDREN,
                        ContentModel.TYPE_CONTENT,
                        properties)
                    .getChildRef();
            ContentWriter writer =
                contentService.getWriter(contentNodeRef, ContentModel.PROP_CONTENT, true);
            writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
            writer.putContent("INITIAL CONTENT");
            // Done
            return contentNodeRef;
          }
        };
    final NodeRef contentNodeRef =
        transactionService.getRetryingTransactionHelper().doInTransaction(makeContentCallback);
    ContentReader contentReader =
        contentService.getReader(contentNodeRef, ContentModel.PROP_CONTENT);
    assertTrue("Expect content to exist", contentReader.exists());

    // Now update the node, but force a failure i.e. txn rollback
    final List<String> newContentUrls = new ArrayList<String>();
    RetryingTransactionCallback<String> failUpdateCallback =
        new RetryingTransactionCallback<String>() {
          public String execute() throws Throwable {
            ContentWriter writer =
                contentService.getWriter(contentNodeRef, ContentModel.PROP_CONTENT, true);
            writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
            writer.putContent("CONTENT FOR FAIL");
            // This will have updated the metadata, so we can fail now
            newContentUrls.add(writer.getContentUrl());
            // Done
            throw new RuntimeException("FAIL");
          }
        };
    try {
      transactionService.getRetryingTransactionHelper().doInTransaction(failUpdateCallback);
      fail("Transaction was meant to fail");
    } catch (RuntimeException e) {
      if (e.getMessage().equals("FAIL")) {
        // Expected
      } else {
        // Ooops
        throw e;
      }
    }
    // Make sure that the new content is not there
    // The original content must still be there
    assertEquals("Expected one content URL to play with", 1, newContentUrls.size());
    ContentReader readerMissing = contentService.getRawReader(newContentUrls.get(0));
    assertFalse("Newly created content should have been removed.", readerMissing.exists());
    assertTrue("Original content should still be there.", contentReader.exists());

    // Now update the node successfully
    RetryingTransactionCallback<String> successUpdateCallback =
        new RetryingTransactionCallback<String>() {
          public String execute() throws Throwable {
            ContentWriter writer =
                contentService.getWriter(contentNodeRef, ContentModel.PROP_CONTENT, true);
            writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
            writer.putContent("CONTENT FOR SUCCESS");
            // Done
            return writer.getContentUrl();
          }
        };
    String newContentUrl =
        transactionService.getRetryingTransactionHelper().doInTransaction(successUpdateCallback);
    // Make sure that the new content is there
    // The original content was disposed of
    ContentReader contentReaderNew = contentService.getRawReader(newContentUrl);
    assertTrue("Newly created content should be present.", contentReaderNew.exists());
    assertFalse("Original content should have been removed.", contentReader.exists());

    // Now delete the node
    RetryingTransactionCallback<Object> deleteNodeCallback =
        new RetryingTransactionCallback<Object>() {
          public Object execute() throws Throwable {
            nodeService.deleteNode(contentNodeRef);
            // Done
            return null;
          }
        };
    transactionService.getRetryingTransactionHelper().doInTransaction(deleteNodeCallback);
    // The new content must have disappeared
    assertFalse("Newly created content should be removed.", contentReaderNew.exists());
  }