Merged V4.0-BUG-FIX to HEAD

33935: ALF-12854: SOLR - requires more detailed tracking information
   - Part 1: info reported for the SOLR SUMMARY action
   33936: ALF-11693: Treat logging.properties the same way as log4j.properties and excluded it from the core .jar
   33945: ALF-12867: RIP jmx-dumper.jar
   33946: Fix minor issue introduced in r33920
   33947: IT-6847 "Error displays on recent 4.0 TS upgrade while navigate to a document details page"
   - errors spotted on folder details & in document library list also resolved by this fix
   33951: ALF-12867: Fixed typo
   33955: Some unit tests and examples on how to implement file wiping or shredding
    - One API change: On EagerContentStoreCleaner, the following is now protected:
         protected boolean deleteFromStore(String contentUrl, ContentStore store)
    - Examples in unit test ContentStoreCleanerTest: testForcedImmediateShredding and testShreddingCleanup
   33962: RIP hyperic plugin
   33965: ALF-12697: Asynchronously get RSS items for dashlets on page load
   33969: Fix for ALF-12307 Solr backup fills the disk
   - number to Keep can be set by property/subsystem/Share admin
   - set default backup localtion
   33970: Fix for ALF-12854 SOLR - requires more detailed tracking information
   Part 2: basic stats added to JMX and improved statistical moment calculation
   33984: Update 4.0.1 installers to use Tomcat 6.0.35
   - Remembered to set useHttpOnly on <Context>
   - Also commented out JreMemoryLeakPreventionListener
   33988: ALF-12717	CIFS: Unfriendly message occurs when a user with consumer/contributor role tries to rename space.
   33997: ALF-12697: Remove async WebScript from dashlet family
   33999: Fix for ALF-12854 SOLR - requires more detailed tracking information
   - Final part - Expose via share admin
   34005: Fix for ALF-12708
   34007: Merged V3.4-BUG-FIX (3.4.9) to 3.4-BUG-FIX (4.0.1)
      34006: Merged V3.4 (3.4.8) to V3.4-BUG-FIX (3.4.9)
         34002: ALF-12898: MMT does not calculate the current version number correctly and does not report errors
            - ModuleManagementTool was constructing the wrong current war file version number.
              The minor and revision values were the wrong way around. 3.4.8 became 3.8.4
            - Modified unit test to use the actual version number in the test war 4.1.0 rather than 4.0.1
            - Modified the ModuleManagementTool so that problems would be reported to the user even if they did not have -verbose set
   34016: Update version.revision for 4.0.1
   34022: Merged V3.4-BUG-FIX to V4.0-BUG-FIX
      33952: ALF-5680: It is possible to cut/paste a locked file   
      34003: Merged DEV to V3.4-BUG-FIX
         34001: ALF-12709: CLONE - Run action Wizard. Can't run action.
            Fixed regression connected with separator of AVM path in .NodeRef. id that has been changed from .;. to .|.
   34023: Merged V3.4-BUG-FIX to V4.0-BUG-FIX (RECORD ONLY)
      33912: ALF-9899: Merged HEAD to V3.4-BUG-FIX (PARTIAL)
         31912: Merged DEV to HEAD
            31823: TransactionalCache provides REPEATABLE READ
                   - Values found in shared cache are placed into transactional cache
                   - Previously, values could keep changing until first write (READ COMMITTED)
                     but now the first read sets the value until it is changed by the current
                     transaction   
      33981: Updated version.revision for 3.4.9
      33985: ALF-12364: Merged V4.0-BUG-FIX to V3.4-BUG-FIX
         33984: Update 3.4.9 installers to use Tomcat 6.0.35
            - Remembered to set useHttpOnly on <Context>
            - Also commented out JreMemoryLeakPreventionListener


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@34024 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Dave Ward
2012-02-18 12:12:01 +00:00
parent 42f24b1c6a
commit 6f7095f450
17 changed files with 511 additions and 46 deletions

View File

@@ -370,21 +370,142 @@ public class ContentStoreCleanerTest extends TestCase
assertTrue("Content listener was not called", deletedUrls.contains(reader.getContentUrl()));
}
public void testProtectedRemoval() throws Exception
/**
* Test forced and immediate shredding of content
* <p/>
* There is no validation that the file is affected. It's an example of how to wire
* different listeners together to do neat things to the files before deletion.
*/
public void testForcedImmediateShredding() throws Exception
{
cleaner.setProtectDays(1);
// add some content to the store
ContentWriter writer = store.getWriter(ContentStore.NEW_CONTENT_CONTEXT);
writer.putContent("ABC");
String contentUrl = writer.getContentUrl();
// An example of an eager cleaner that will wipe files before deleting their contents
// This is very much like a listener, but listeners are only called by the standard,
// scheduled cleaner.
final Set<String> wipedUrls = new HashSet<String>(3);
final EagerContentStoreCleaner wipingEagerCleaner = new EagerContentStoreCleaner()
{
final FileWipingContentCleanerListener fileWiper = new FileWipingContentCleanerListener();
@Override
protected boolean deleteFromStore(String contentUrl, ContentStore store)
{
fileWiper.beforeDelete(store, contentUrl);
wipedUrls.add(contentUrl);
return true;
}
};
wipingEagerCleaner.setStores(Collections.singletonList(store));
/*
* Note that we don't need to wire the 'wipingEagerCleaner' into anything.
* You can if you want it to wipe for all use cases. In this case, we're just
* going to manually force it to clean.
*/
// fire the cleaner
// Create a node with content
final StoreRef storeRef = nodeService.createStore("test", getName() + "-" + GUID.generate());
RetryingTransactionCallback<NodeRef> testCallback = new RetryingTransactionCallback<NodeRef>()
{
public NodeRef 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");
// Done
return contentNodeRef;
}
};
final NodeRef contentNodeRef = transactionService.getRetryingTransactionHelper().doInTransaction(testCallback);
// Now, force the node to be deleted and make sure it gets cleaned up directly
// This can be used where some sensitive data has been identified and, before deletion,
// the URL can be marked for immediate cleanup (in the post-commit phase, of course!)
RetryingTransactionCallback<String> deleteCallback = new RetryingTransactionCallback<String>()
{
public String execute() throws Throwable
{
// Let's pretend we're in 'beforeDeleteNode'
ContentReader reader = contentService.getReader(contentNodeRef, ContentModel.PROP_CONTENT);
String contentUrl = reader.getContentUrl();
wipingEagerCleaner.registerOrphanedContentUrl(contentUrl, true);
nodeService.deleteNode(contentNodeRef);
// Done
return contentUrl;
}
};
String contentUrl = transactionService.getRetryingTransactionHelper().doInTransaction(deleteCallback);
// So, we don't fire the cleaner, but notice that the eager cleaner has 'wiped' the content
assertTrue("Expected our URL to have been wiped.", wipedUrls.contains(contentUrl));
cleaner.execute();
// the content should have disappeared as it is not in the database
assertTrue("Protected content was deleted", store.exists(contentUrl));
assertFalse("Content listener was called with deletion of protected URL", deletedUrls.contains(contentUrl));
}
/**
* Test basic wiping of file contents on normal orphan cleanup
*/
public void testShreddingCleanup() throws Exception
{
eagerCleaner.setEagerOrphanCleanup(false);
cleaner.setProtectDays(0);
// Add in a the Wiping cleaner listener
FileWipingContentCleanerListener fileWiper = new FileWipingContentCleanerListener();
List<ContentStoreCleanerListener> listeners = new ArrayList<ContentStoreCleanerListener>(1);
listeners.add(fileWiper);
eagerCleaner.setListeners(listeners);
// Create a node with content
final StoreRef storeRef = nodeService.createStore("test", getName() + "-" + GUID.generate());
RetryingTransactionCallback<NodeRef> testCallback = new RetryingTransactionCallback<NodeRef>()
{
public NodeRef 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");
// Done
return contentNodeRef;
}
};
final NodeRef contentNodeRef = transactionService.getRetryingTransactionHelper().doInTransaction(testCallback);
// Simple delete
RetryingTransactionCallback<Void> deleteCallback = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
nodeService.deleteNode(contentNodeRef);
// Done
return null;
}
};
transactionService.getRetryingTransactionHelper().doInTransaction(deleteCallback);
// It's orphaned now. Fire the cleaner.
cleaner.execute();
}
private class DummyCleanerListener implements ContentStoreCleanerListener
{
public void beforeDelete(ContentStore store, String contentUrl) throws ContentIOException

View File

@@ -143,6 +143,8 @@ public class EagerContentStoreCleaner extends TransactionListenerAdapter
* <p/>
* <b>NB: </b>Any content registered <u>will</u> be deleted if the current transaction
* commits and if 'eager' cleanup is turned on.
* <p/>
* Note that listeners are not called for this process.
*
* @return Returns <tt>true</tt> if the content was scheduled for post-transaction deletion.
* If the return value is <tt>true</tt> then the calling code <b>must</b> delete
@@ -157,7 +159,9 @@ public class EagerContentStoreCleaner extends TransactionListenerAdapter
* Queues orphaned content for post-transaction removal
* <p/>
* <b>NB: </b>Any content registered <u>will</u> be deleted if the current transaction
* commits and if 'eager' cleanup is turned on.
* commits and if 'eager' cleanup is turned on OR if 'force' is <tt>true</tt>.
* <p/>
* Note that listeners are not called for this process.
*
* @param force <tt>true</tt> for force the post-commit URL deletion
* regardless of the setting {@link #setEagerOrphanCleanup(boolean)}.
@@ -246,6 +250,8 @@ public class EagerContentStoreCleaner extends TransactionListenerAdapter
/**
* Delete the content URL from all stores
* <p/>
* Note that listeners <b>are</b> called for this process.
*
* @param contentUrl the URL to delete
* @return Returns <tt>true</tt> if all deletes were successful
@@ -298,7 +304,7 @@ public class EagerContentStoreCleaner extends TransactionListenerAdapter
/**
* Attempts to delete the URL from the store, catching and reporing errors.
*/
private boolean deleteFromStore(String contentUrl, ContentStore store)
protected boolean deleteFromStore(String contentUrl, ContentStore store)
{
try
{

View File

@@ -0,0 +1,144 @@
/*
* Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco 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 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.content.cleanup;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.alfresco.repo.content.ContentStore;
import org.alfresco.repo.content.filestore.FileContentReader;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Simple listener that overwrites files with zeros.
* <p/>
* Wire this into the {@link EagerContentStoreCleaner} as a listener and it will
* ensure that files have their contents overwritten with zeros before deletion.
* Note that this process does not affect the content lifecycyle in any way
* i.e. content will still follow the same orphan path as before.
* <p>
* Clearly wiring this up with a {@link DeletedContentBackupCleanerListener} is
* pointless as you will be making a copy of the before wiping it or end up
* copying a file full of zero depending on the order of the listeners.
*
* @author Derek Hulley
* @since 4.0.1
*/
public class FileWipingContentCleanerListener implements ContentStoreCleanerListener
{
private static Log logger = LogFactory.getLog(FileWipingContentCleanerListener.class);
public FileWipingContentCleanerListener()
{
}
public void beforeDelete(ContentStore sourceStore, String contentUrl) throws ContentIOException
{
// First check if the content is present at all
ContentReader reader = sourceStore.getReader(contentUrl);
if (reader != null && reader.exists())
{
// Call to implementation's shred
if (logger.isDebugEnabled())
{
logger.debug(
"About to shread: \n" +
" URL: " + contentUrl + "\n" +
" Source: " + sourceStore);
}
try
{
shred(reader);
}
catch (Throwable e)
{
logger.error(
"Content shredding failed: \n" +
" URL: " + contentUrl + "\n" +
" Source: " + sourceStore + "\n" +
" Reader: " + reader,
e);
}
}
else
{
logger.error(
"Content no longer exists. Unable to shred: \n" +
" URL: " + contentUrl + "\n" +
" Source: " + sourceStore);
}
}
/**
* Override to perform shredding on disparate forms of readers. This implementation will,
* by default, identify more specific readers and make calls for those.
*
* @param reader the reader to the content needing shredding
* @exception IOException any IO error
*/
protected void shred(ContentReader reader) throws IOException
{
if (reader instanceof FileContentReader)
{
FileContentReader fileReader = (FileContentReader) reader;
File file = fileReader.getFile();
shred(file);
}
}
/**
* Called by {@link #shred(ContentReader)} when the reader points to a physical file.
* The default implementation simply overwrites the content with zeros.
*
* @param file the file to shred before deletion
* @throws IOException any IO error
*/
protected void shred(File file) throws IOException
{
// Double check
if (!file.exists() || !file.canWrite())
{
throw new ContentIOException("Unable to write to file: " + file);
}
long bytes = file.length();
OutputStream os = new BufferedOutputStream(new FileOutputStream(file));
try
{
/*
* There are many more efficient ways of writing bytes into the file.
* However, it is likely that implementations will do a lot more than
* just overwrite with zeros.
*/
for (int i = 0; i < bytes; i++)
{
os.write(0);
}
}
finally
{
try {os.close(); } catch (Throwable e) {}
}
}
}