/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License, version 2 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/gpl-2.0.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program 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 General Public License for more details.
*
*
* Copyright 2006 - 2013 Pentaho Corporation. All rights reserved.
*/
package org.pentaho.platform.repository2.unified.jcr;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.platform.api.engine.IPentahoSession;
import org.pentaho.platform.engine.core.system.PentahoSessionHolder;
import org.pentaho.platform.engine.core.system.StandaloneSession;
import org.springframework.extensions.jcr.JcrCallback;
import org.springframework.extensions.jcr.JcrTemplate;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
import javax.jcr.util.TraversingItemVisitor;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* Creates an export of the JCR in various formats.
*
* <p>
* To use:
* </p>
* <ol>
* <li>Add the following to the end of {@code systemListeners.xml}:
*
* <pre>
* <bean id="dumpToFilePentahoSystemListener"
* class="org.pentaho.platform.repository2.unified.jcr.DumpToFilePentahoSystemListener" />
* </pre>
*
* </li>
* <li>Add one of the following system properties on the command line:
*
* <pre>
* -Dpentaho.repository.dumpToFile=/tmp/repodump
* </pre>
*
* or
*
* <pre>
* -Dpentaho.repository.dumpToFile.systemView=/tmp/repodump
* </pre>
*
* or
*
* <pre>
* -Dpentaho.repository.dumpToFile.documentView=/tmp/repodump
* </pre>
*
* </li>
* </ol>
*
* <p>
* Uses PentahoSystem instead of Spring injection since its collaborators are not yet instantiated when this class
* is instantiated.
* </p>
*
* @author mlowery
*/
public class JcrRepositoryDumpToFile {
// ~ Static fields/initializers
// ======================================================================================
private static final Log logger = LogFactory.getLog( JcrRepositoryDumpToFile.class );
/**
* Exports the repository using a custom TraversingItemVisitor. (It is human-readable output that is not meant to
* be parsed.)
*/
public static final String PROP_DUMP_TO_FILE = "pentaho.repository.dumpToFile"; //$NON-NLS-1$
/**
* Exports the repository using System View.
*/
public static final String PROP_DUMP_TO_FILE_SYSTEM_VIEW = "pentaho.repository.dumpToFile.systemView"; //$NON-NLS-1$
/**
* Exports the repository using Document View.
*/
public static final String PROP_DUMP_TO_FILE_DOCUMENT_VIEW = "pentaho.repository.dumpToFile.documentView"; //$NON-NLS-1$
public enum Mode {
CUSTOM, SYS, DOC
};
JcrTemplate jcrTemplate;
TransactionTemplate txnTemplate;
String repositoryAdminUsername;
String filename;
Mode mode;
// ~ Instance fields
// =================================================================================================
// ~ Constructors
// ====================================================================================================
// ~ Methods
// =========================================================================================================
public JcrRepositoryDumpToFile( final JcrTemplate jcrTemplate, TransactionTemplate txnTemplate,
String repositoryAdminUsername, String filename, Mode mode ) {
this.jcrTemplate = jcrTemplate;
this.txnTemplate = txnTemplate;
this.repositoryAdminUsername = repositoryAdminUsername;
this.filename = filename;
this.mode = mode;
}
public void execute() {
if ( filename != null ) {
final String ZIP_EXTENSION = ".zip"; //$NON-NLS-1$
// let the user know this is a zip
if ( !filename.endsWith( ZIP_EXTENSION ) ) {
filename = filename + ZIP_EXTENSION;
}
logger.debug( String.format( "dumping repository to file \"%s\"", filename ) ); //$NON-NLS-1$
ZipOutputStream tmpOut = null;
try {
tmpOut = new ZipOutputStream( new BufferedOutputStream( FileUtils.openOutputStream( new File( filename ) ) ) );
} catch ( IOException e ) {
IOUtils.closeQuietly( tmpOut );
throw new RuntimeException( e );
}
final ZipOutputStream out = tmpOut;
// stash existing session
IPentahoSession origPentahoSession = PentahoSessionHolder.getSession();
// run as repo super user
PentahoSessionHolder.setSession( createRepositoryAdminPentahoSession( repositoryAdminUsername ) );
try {
txnTemplate.execute( new TransactionCallbackWithoutResult() {
public void doInTransactionWithoutResult( final TransactionStatus status ) {
jcrTemplate.execute( new JcrCallback() {
public Object doInJcr( final Session session ) throws RepositoryException, IOException {
switch ( mode ) {
case SYS: {
final boolean SKIP_BINARY = false;
final boolean NO_RECURSE = false;
out.putNextEntry( new ZipEntry( "repository.xml" ) ); //$NON-NLS-1$
session.exportSystemView( "/", out, SKIP_BINARY, NO_RECURSE ); //$NON-NLS-1$
return null;
}
case DOC: {
final boolean SKIP_BINARY = false;
final boolean NO_RECURSE = false;
out.putNextEntry( new ZipEntry( "repository.xml" ) ); //$NON-NLS-1$
session.exportDocumentView( "/", out, SKIP_BINARY, NO_RECURSE ); //$NON-NLS-1$
return null;
}
default: {
out.putNextEntry( new ZipEntry( "repository.txt" ) ); //$NON-NLS-1$
session.getRootNode().accept( new DumpToFileTraversingItemVisitor( out ) );
return null;
}
}
}
} );
}
} );
} finally {
// restore original session
PentahoSessionHolder.setSession( origPentahoSession );
IOUtils.closeQuietly( out );
}
logger.debug( String.format( "dumped repository to file \"%s\"", filename ) ); //$NON-NLS-1$
}
}
protected IPentahoSession createRepositoryAdminPentahoSession( final String repositoryAdminUsername ) {
StandaloneSession pentahoSession = new StandaloneSession( repositoryAdminUsername );
pentahoSession.setAuthenticated( repositoryAdminUsername );
return pentahoSession;
}
public static class DumpToFileTraversingItemVisitor extends TraversingItemVisitor {
private OutputStream out;
public DumpToFileTraversingItemVisitor( final OutputStream out ) {
this.out = out;
}
private static final String INDENT = " "; //$NON-NLS-1$
private static final String ENCODING = "UTF-8"; //$NON-NLS-1$
private static final String NL = System.getProperty( "line.separator" ); //$NON-NLS-1$
@Override
protected void entering( final Property property, final int level ) throws RepositoryException {
StringBuilder buf = new StringBuilder();
for ( int i = 0; i < level; i++ ) {
buf.append( INDENT );
}
propertyToString( property, buf );
try {
IOUtils.write( buf, out, ENCODING );
} catch ( IOException e ) {
throw new RuntimeException( e );
}
}
protected void propertyToString( final Property property, final StringBuilder buf ) throws RepositoryException {
buf.append( "@" ); //$NON-NLS-1$
buf.append( property.getName() );
buf.append( "=" ); //$NON-NLS-1$
try {
Value value = property.getValue();
valueToString( value, buf );
} catch ( ValueFormatException e ) {
// multi-valued
Value[] values = property.getValues();
buf.append( "[" ); //$NON-NLS-1$
for ( int i = 0; i < values.length; i++ ) {
if ( i > 0 ) {
buf.append( "," ); //$NON-NLS-1$
}
valueToString( values[i], buf );
}
buf.append( "]" ); //$NON-NLS-1$
}
buf.append( NL );
}
protected void valueToString( final Value value, final StringBuilder buf ) throws RepositoryException {
buf.append( value.getString() );
buf.append( " (" ); //$NON-NLS-1$
buf.append( PropertyType.nameFromValue( value.getType() ) );
buf.append( ")" ); //$NON-NLS-1$
}
@Override
protected void entering( final Node node, final int level ) throws RepositoryException {
StringBuilder buf = new StringBuilder();
for ( int i = 0; i < level; i++ ) {
buf.append( INDENT );
}
nodeToString( node, buf );
try {
IOUtils.write( buf, out, ENCODING );
} catch ( IOException e ) {
throw new RuntimeException( e );
}
}
protected void nodeToString( final Node node, final StringBuilder buf ) throws RepositoryException {
buf.append( node.getName() );
buf.append( "/" ); //$NON-NLS-1$
buf.append( NL );
}
@Override
protected void leaving( final Property property, final int level ) throws RepositoryException {
}
@Override
protected void leaving( final Node node, final int level ) throws RepositoryException {
}
}
}