-
Notifications
You must be signed in to change notification settings - Fork 723
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Revert "[BISERVER-15124] - Moving repository cleaner to scheduler plu…
…gin"
- Loading branch information
Showing
2 changed files
with
251 additions
and
0 deletions.
There are no files selected for viewing
149 changes: 149 additions & 0 deletions
149
repository/src/main/java/org/pentaho/platform/repository2/unified/jcr/RepositoryCleaner.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
/*! ****************************************************************************** | ||
* | ||
* Pentaho | ||
* | ||
* Copyright (C) 2024 by Hitachi Vantara, LLC : http://www.pentaho.com | ||
* | ||
* Use of this software is governed by the Business Source License included | ||
* in the LICENSE.TXT file. | ||
* | ||
* Change Date: 2029-07-20 | ||
******************************************************************************/ | ||
|
||
|
||
package org.pentaho.platform.repository2.unified.jcr; | ||
|
||
import org.apache.commons.logging.Log; | ||
import org.apache.commons.logging.LogFactory; | ||
import org.apache.jackrabbit.api.management.DataStoreGarbageCollector; | ||
import org.apache.jackrabbit.core.IPentahoSystemSessionFactory; | ||
import org.apache.jackrabbit.core.RepositoryImpl; | ||
import org.pentaho.platform.engine.core.system.PentahoSystem; | ||
|
||
import javax.jcr.Node; | ||
import javax.jcr.NodeIterator; | ||
import javax.jcr.Property; | ||
import javax.jcr.Repository; | ||
import javax.jcr.RepositoryException; | ||
import javax.jcr.Session; | ||
import javax.jcr.Value; | ||
import javax.jcr.version.VersionHistory; | ||
|
||
/** | ||
* This class provides a static method {@linkplain #gc()} for running JCR's GC routine. | ||
* | ||
* @author Andrey Khayrutdinov | ||
*/ | ||
public class RepositoryCleaner { | ||
|
||
private final Log logger = LogFactory.getLog( RepositoryCleaner.class ); | ||
private static final String JCR_FROZEN_NODE = "jcr:frozenNode"; | ||
private static final String JCR_FROZEN_UUID = "jcr:frozenUuid"; | ||
private static final String JCR_ROOT_VERSION = "jcr:rootVersion"; | ||
private IPentahoSystemSessionFactory systemSessionFactory = new IPentahoSystemSessionFactory.DefaultImpl(); | ||
|
||
/** | ||
* Exists primary for testing | ||
* @param systemSessionFactory | ||
*/ | ||
public void setSystemSessionFactory( IPentahoSystemSessionFactory systemSessionFactory ) { | ||
this.systemSessionFactory = systemSessionFactory; | ||
} | ||
|
||
public synchronized void gc() { | ||
Repository jcrRepository = PentahoSystem.get( Repository.class, "jcrRepository", null ); | ||
if ( jcrRepository == null ) { | ||
logger.error( "Cannot obtain JCR repository. Exiting" ); | ||
return; | ||
} | ||
|
||
if ( !( jcrRepository instanceof RepositoryImpl ) ) { | ||
logger.error( | ||
String.format( "Expected RepositoryImpl, but got: [%s]. Exiting", jcrRepository.getClass().getName() ) ); | ||
return; | ||
} | ||
|
||
final RepositoryImpl repository = (RepositoryImpl) jcrRepository; | ||
|
||
try { | ||
logger.debug( "Starting Orphaned Version Purge" ); | ||
Session systemSession = systemSessionFactory.create( repository ); | ||
Node node = systemSession.getNode( "/jcr:system/jcr:versionStorage" ); | ||
findVersionNodesAndPurge( node, systemSession ); | ||
systemSession.save(); | ||
logger.debug( "Finished Orphaned Version Purge" ); | ||
} catch ( RepositoryException e ) { | ||
logger.error( "Error running Orphaned Version purge", e ); | ||
} | ||
|
||
try { | ||
logger.info( "Creating garbage collector" ); | ||
// JCR's documentation recommends not to use RepositoryImpl.createDataStoreGarbageCollector() and | ||
// instead invoke RepositoryManager.createDataStoreGarbageCollector() | ||
// (see it here: http://wiki.apache.org/jackrabbit/DataStore#Data_Store_Garbage_Collection) | ||
|
||
// However, the example from the wiki cannot be applied directly, because | ||
// RepositoryFactoryImpl accepts only TransientRepository's instances that were created by itself; | ||
// it creates such instance in "not started" state, and when the instance tries to start, it fails, | ||
// because Pentaho's JCR repository is already running. | ||
|
||
DataStoreGarbageCollector gc = repository.createDataStoreGarbageCollector(); | ||
try { | ||
logger.debug( "Starting marking stage" ); | ||
gc.setPersistenceManagerScan( false ); | ||
gc.mark(); | ||
logger.debug( "Starting sweeping stage" ); | ||
int deleted = gc.sweep(); | ||
logger.info( String.format( "Garbage collecting completed. %d items were deleted", deleted ) ); | ||
} finally { | ||
gc.close(); | ||
} | ||
} catch ( RepositoryException e ) { | ||
logger.error( "Error during garbage collecting", e ); | ||
} | ||
|
||
} | ||
|
||
private void findVersionNodesAndPurge( Node node, Session session ) { | ||
if ( node == null || session == null ) { | ||
return; | ||
} | ||
try { | ||
if ( node.getName().equals( JCR_FROZEN_NODE ) && node.hasProperty( JCR_FROZEN_UUID ) && !node.getParent() | ||
.getName().equals( JCR_ROOT_VERSION ) ) { | ||
// Version Node | ||
Property property = node.getProperty( JCR_FROZEN_UUID ); | ||
Value uuid = property.getValue(); | ||
Node nodeByIdentifier = null; | ||
try { | ||
nodeByIdentifier = session.getNodeByIdentifier( uuid.getString() ); | ||
nodeByIdentifier = session.getNode( nodeByIdentifier.getPath() ); | ||
} catch ( RepositoryException ex ) { | ||
// ignored this means the node is gone. | ||
} | ||
if ( nodeByIdentifier == null ) { | ||
// node is gone | ||
logger.info( "Removed orphan version: " + node.getPath() ); | ||
( (VersionHistory) node.getParent().getParent() ).removeVersion( node.getParent().getName() ); | ||
} | ||
} | ||
} catch ( RepositoryException e ) { | ||
logger.error( "Error purging version nodes. Routine will continue", e ); | ||
} | ||
|
||
NodeIterator nodes = null; | ||
try { | ||
nodes = node.getNodes(); | ||
} catch ( RepositoryException e ) { | ||
logger.error( "Error purging version nodes. Routine will continue", e ); | ||
} | ||
|
||
if ( nodes == null ) { | ||
return; | ||
} | ||
|
||
while ( nodes.hasNext() ) { | ||
findVersionNodesAndPurge( nodes.nextNode(), session ); | ||
} | ||
} | ||
} |
102 changes: 102 additions & 0 deletions
102
...ory/src/test/java/org/pentaho/platform/repository2/unified/jcr/RepositoryCleanerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
/*! ****************************************************************************** | ||
* | ||
* Pentaho | ||
* | ||
* Copyright (C) 2024 by Hitachi Vantara, LLC : http://www.pentaho.com | ||
* | ||
* Use of this software is governed by the Business Source License included | ||
* in the LICENSE.TXT file. | ||
* | ||
* Change Date: 2029-07-20 | ||
******************************************************************************/ | ||
|
||
|
||
package org.pentaho.platform.repository2.unified.jcr; | ||
|
||
import org.apache.jackrabbit.core.IPentahoSystemSessionFactory; | ||
import org.apache.jackrabbit.core.RepositoryImpl; | ||
import org.apache.jackrabbit.core.gc.GarbageCollector; | ||
import org.junit.Assert; | ||
import org.junit.Test; | ||
import org.pentaho.test.platform.engine.core.MicroPlatform; | ||
|
||
import javax.jcr.Node; | ||
import javax.jcr.Repository; | ||
import javax.jcr.Session; | ||
|
||
import static org.mockito.ArgumentMatchers.any; | ||
import static org.mockito.ArgumentMatchers.anyString; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.times; | ||
import static org.mockito.Mockito.verify; | ||
import static org.mockito.Mockito.when; | ||
|
||
/** | ||
* @author Andrey Khayrutdinov | ||
*/ | ||
public class RepositoryCleanerTest { | ||
|
||
private static final String SOLUTION_PATH = "src/test/resources/solution"; | ||
|
||
@Test | ||
public void gc() throws Exception { | ||
GarbageCollector collector = mock( GarbageCollector.class ); | ||
|
||
RepositoryImpl repository = mock( RepositoryImpl.class ); | ||
when( repository.createDataStoreGarbageCollector() ).thenReturn( collector ); | ||
|
||
MicroPlatform mp = new MicroPlatform( getSolutionPath() ); | ||
mp.defineInstance( Repository.class, repository ); | ||
mp.defineInstance( "jcrRepository", repository ); | ||
mp.start(); | ||
|
||
RepositoryCleaner cleaner = new RepositoryCleaner(); | ||
Session systemSession = mock( Session.class ); | ||
IPentahoSystemSessionFactory sessionFactory = mock( IPentahoSystemSessionFactory.class ); | ||
when( sessionFactory.create( repository ) ).thenReturn( systemSession ); | ||
cleaner.setSystemSessionFactory( sessionFactory ); | ||
|
||
try { | ||
cleaner.gc(); | ||
} finally { | ||
mp.stop(); | ||
} | ||
|
||
verify( collector, times( 1 ) ).mark(); | ||
verify( collector, times( 1 ) ).sweep(); | ||
verify( collector, times( 1 ) ).close(); | ||
} | ||
|
||
protected String getSolutionPath() { | ||
return SOLUTION_PATH; | ||
} | ||
|
||
@Test | ||
public void testFindVersionNodesAndPurgeWhenNodeHasNullNodes() throws Exception { | ||
GarbageCollector collector = mock( GarbageCollector.class ); | ||
RepositoryImpl repository = mock( RepositoryImpl.class ); | ||
when( repository.createDataStoreGarbageCollector() ).thenReturn( collector ); | ||
MicroPlatform mp = new MicroPlatform( getSolutionPath() ); | ||
mp.defineInstance( Repository.class, repository ); | ||
mp.defineInstance( "jcrRepository", repository ); | ||
mp.start(); | ||
|
||
RepositoryCleaner cleaner = new RepositoryCleaner(); | ||
Session systemSession = mock( Session.class ); | ||
IPentahoSystemSessionFactory sessionFactory = mock( IPentahoSystemSessionFactory.class ); | ||
when( sessionFactory.create( any() ) ).thenReturn( systemSession ); | ||
Node parentNode = mock( Node.class ); | ||
when( systemSession.getNode( anyString() ) ).thenReturn( parentNode ); | ||
when( parentNode.getName() ).thenReturn( "" ); | ||
when( parentNode.getNodes() ).thenReturn( null ); | ||
try { | ||
cleaner.setSystemSessionFactory( sessionFactory ); | ||
cleaner.gc(); | ||
} catch ( Exception e ) { | ||
Assert.fail(); | ||
} finally { | ||
mp.stop(); | ||
} | ||
|
||
} | ||
} |