SvnUtils.java

/*
 * Copyright (c) 2002-2021, City of Paris
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice
 *     and the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright notice
 *     and the following disclaimer in the documentation and/or other materials
 *     provided with the distribution.
 *
 *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * License 1.0
 */
package fr.paris.lutece.plugins.releaser.util.svn;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collections;

import org.apache.log4j.Logger;
import org.tmatesoft.svn.core.ISVNDirEntryHandler;
import org.tmatesoft.svn.core.SVNAuthenticationException;
import org.tmatesoft.svn.core.SVNCancelException;
import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNDirEntry;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNCommitPacket;
import org.tmatesoft.svn.core.wc.SVNCopyClient;
import org.tmatesoft.svn.core.wc.SVNCopySource;
import org.tmatesoft.svn.core.wc.SVNDiffClient;
import org.tmatesoft.svn.core.wc.SVNEvent;
import org.tmatesoft.svn.core.wc.SVNEventAction;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNRevisionRange;
import org.tmatesoft.svn.core.wc.SVNWCUtil;

import fr.paris.lutece.plugins.releaser.util.CommandResult;
import fr.paris.lutece.plugins.releaser.util.ConstanteUtils;
import fr.paris.lutece.plugins.releaser.util.ReleaserUtils;
import fr.paris.lutece.plugins.releaser.util.file.FileUtils;
import fr.paris.lutece.portal.service.util.AppLogService;
import fr.paris.lutece.util.ReferenceList;

// TODO: Auto-generated Javadoc
/**
 * The Class SvnUtils.
 */
public final class SvnUtils
{

    /** The Constant MESSAGE_ERROR_SVN. */
    private static final String MESSAGE_ERROR_SVN = "Impossible de se connecter au SVN. Veuillez verifier vos identifiants";

    /** The Constant CONSTANTE_SLASH. */
    private static final String CONSTANTE_SLASH = "/";

    /** The Constant logger. */
    private static final Logger logger = Logger.getLogger( SvnUtils.class );

    /**
     * Constructeur vide.
     */
    private SvnUtils( )
    {
        // nothing
    }

    /**
     * Initialise les diff�rentes factory pour le svn.
     */
    public static void init( )
    {
        /*
         * For using over http:// and https:
         */
        DAVRepositoryFactory.setup( );
        /*
         * For using over svn:// and svn+xxx:
         */
        SVNRepositoryFactoryImpl.setup( );

        /*
         * For using over file:/
         */
        FSRepositoryFactory.setup( );
    }

    /**
     * Commit.
     *
     * @param strPathFile
     *            the str path file
     * @param strCommitMessage
     *            the str commit message
     * @param commitClient
     *            the commit client
     * @throws SVNException
     *             the SVN exception
     */
    public static void doCommit( String strPathFile, String strCommitMessage, ReleaseSvnCommitClient commitClient ) throws SVNException
    {
        SVNCommitPacket commitPacket = commitClient.doCollectCommitItems( new File [ ] {
                new File( strPathFile )
        }, false, false, true );

        if ( !SVNCommitPacket.EMPTY.equals( commitPacket ) )
        {
            commitClient.doCommit( commitPacket, false, strCommitMessage );
        }
    }

    /**
     * Tag un site.
     *
     * @param strSiteName
     *            le nom du site
     * @param strTagName
     *            le nom du tag
     * @param strSrcURL
     *            the str src URL
     * @param strDstURL
     *            the str dst URL
     * @param copyClient
     *            le client svn permettant la copie
     * @return string
     * @throws SVNException
     *             the SVN exception
     */
    public static String doTagSite( String strSiteName, String strTagName, String strSrcURL, String strDstURL, SVNCopyClient copyClient ) throws SVNException
    {
        // COPY from trunk to tags/tagName
        SVNURL srcURL = SVNURL.parseURIEncoded( strSrcURL );
        SVNURL dstURL = SVNURL.parseURIEncoded( strDstURL );
        SVNCopySource svnCopySource = new SVNCopySource( SVNRevision.HEAD, SVNRevision.HEAD, srcURL );
        SVNCopySource [ ] tabSVNCopy = new SVNCopySource [ 1];
        tabSVNCopy [0] = svnCopySource;

        SVNCommitInfo info = copyClient.doCopy( tabSVNCopy, dstURL, false, false, false, "[site-release] Tag site " + strSiteName + " to " + strTagName, null );

        if ( info.getErrorMessage( ) != null )
        {

            return info.getErrorMessage( ).getMessage( );
        }

        return null;
    }

    /**
     * Do svn checkout.
     *
     * @param strUrl
     *            the str url
     * @param strCheckoutBaseSitePath
     *            the str checkout base site path
     * @param updateClient
     *            the update client
     * @param result
     *            the result
     * @return the long
     * @throws SVNException
     *             the SVN exception
     */
    public static Long doSvnCheckout( String strUrl, String strCheckoutBaseSitePath, ReleaseSvnCheckoutClient updateClient, CommandResult result )
            throws SVNException
    {
        Long nLastCommitId = null;
        SVNURL url = SVNURL.parseURIEncoded( strUrl );
        File file = new File( strCheckoutBaseSitePath );

        if ( file.exists( ) )
        {
            if ( !FileUtils.delete( file, result.getLog( ) ) )
            {
                result.setError( result.getLog( ).toString( ) );
                ReleaserUtils.addTechnicalError( result, "Fail to delete file" );

            }
        }

        SVNRepository repository = SVNRepositoryFactory.create( url, null );
        final StringBuffer logBuffer = result.getLog( );

        try
        {
            updateClient.setEventHandler( new ISVNEventHandler( )
            {
                public void checkCancelled( ) throws SVNCancelException
                {
                    // Do nothing
                }

                public void handleEvent( SVNEvent event, double progress ) throws SVNException
                {
                    logBuffer.append( ( ( event.getAction( ) == SVNEventAction.UPDATE_ADD ) ? "ADDED " : event.getAction( ) ) + " " + event.getFile( ) + "\n" );
                }
            } );

            // SVNDepth.INFINITY + dernier param�tre � FALSE pour la version 1.3.2
            nLastCommitId = updateClient.doCheckout( repository.getLocation( ), file, SVNRevision.HEAD, SVNRevision.HEAD, true );

        }
        catch( SVNAuthenticationException e )
        {
            // _result.getLog( ).append( CONSTANTE_NO_LOGIN_PASSWORD );
            // _result.setStatus( ICommandThread.STATUS_EXCEPTION );
            // _result.setRunning( false );

            ReleaserUtils.addTechnicalError( result, "Une erreur est survenue lors de la tentative d'authentification avec le svn" + e, e );

            StringWriter sw = new StringWriter( );
            PrintWriter pw = new PrintWriter( sw );
            e.printStackTrace( pw );

            String errorLog = sw.toString( );
            pw.flush( );
            pw.close( );

            try
            {
                sw.flush( );
                sw.close( );
            }
            catch( IOException e1 )
            {
                // do nothing
                // _logger.error( e1 );
            }

            // _result.setLog( _result.getLog( ).append( errorLog ) );
            // _logger.error( e );

            // _result.setIdError( ReleaseLogger.logError( _result.getLog( ).toString( ), e ) );
        }
        catch( Exception e )
        {
            // _result.setStatus( ICommandThread.STATUS_EXCEPTION );
            // _result.setRunning( false );
            StringWriter sw = new StringWriter( );
            PrintWriter pw = new PrintWriter( sw );
            e.printStackTrace( pw );

            String errorLog = sw.toString( );
            pw.flush( );
            pw.close( );

            try
            {
                sw.flush( );
                sw.close( );
            }
            catch( IOException e1 )
            {
                // do nothing
                // _logger.error( e1 );
            }

            ReleaserUtils.addTechnicalError( result, "Une erreur svn est survenue:" + e, e );

        }

        return nLastCommitId;
    }

    /**
     * Gets the svn sites.
     *
     * @param strUrlSite
     *            the str url site
     * @param clientManager
     *            the client manager
     * @return the svn sites
     * @throws SVNException
     *             the SVN exception
     */
    public static ReferenceList getSvnSites( String strUrlSite, SVNClientManager clientManager ) throws SVNException
    {
        final ReferenceList listSites = new ReferenceList( );
        final SVNURL url;

        url = SVNURL.parseURIEncoded( strUrlSite );

        SVNRepository repository = SVNRepositoryFactory.create( url, null );

        clientManager.getLogClient( ).doList( repository.getLocation( ), SVNRevision.HEAD, SVNRevision.HEAD, false, false, new ISVNDirEntryHandler( )
        {
            public void handleDirEntry( SVNDirEntry entry ) throws SVNException
            {

                if ( !url.equals( entry.getURL( ) ) )
                {
                    if ( entry.getKind( ) == SVNNodeKind.DIR )
                    {
                        listSites.addItem( entry.getName( ), entry.getName( ) );
                    }
                }
            }
        } );

        return listSites;
    }

    /**
     * Gets the last revision.
     *
     * @param strRepoPath
     *            the str repo path
     * @param strUserName
     *            the str user name
     * @param strPassword
     *            the str password
     * @return the last revision
     */
    public static Long getLastRevision( String strRepoPath, String strUserName, String strPassword )
    {
        Long lRevision = null;
        SVNClientManager clientManager = SVNClientManager.newInstance( new DefaultSVNOptions( ), strUserName, strPassword );
        SVNRevision revision;
        try
        {
            File fStrRepo = new File( strRepoPath );
            revision = clientManager.getStatusClient( ).doStatus( fStrRepo, true ).getCommittedRevision( );
            if ( revision != null )
            {
                return revision.getNumber( );
            }
        }
        catch( SVNException e )
        {
            AppLogService.error( e );
        }

        return lRevision;
    }

    /**
     * Update.
     *
     * @param strRepoPath
     *            the str repo path
     * @param strUserName
     *            the str user name
     * @param strPassword
     *            the str password
     */
    public static void update( String strRepoPath, String strUserName, String strPassword )
    {
        SVNClientManager clientManager = SVNClientManager.newInstance( new DefaultSVNOptions( ), strUserName, strPassword );

        try
        {
            File fStrRepo = new File( strRepoPath );
            clientManager.getUpdateClient( ).doUpdate( fStrRepo, SVNRevision.HEAD, true );

        }
        catch( SVNException e )
        {
            AppLogService.error( e );
        }

    }

    /**
     * Revert.
     *
     * @param strRepoPath
     *            the str repo path
     * @param strCmUrl
     *            the str cm url
     * @param strUserName
     *            the str user name
     * @param strPassword
     *            the str password
     * @param revCurrentCommit
     *            the rev current commit
     * @param lRevertCommit
     *            the l revert commit
     */
    public static void revert( String strRepoPath, String strCmUrl, String strUserName, String strPassword, Long revCurrentCommit, Long lRevertCommit )
    {

        SVNClientManager clientManager = SVNClientManager.newInstance( new DefaultSVNOptions( ), strUserName, strPassword );

        SVNDiffClient diffClient = clientManager.getDiffClient( );
        SVNRevision sRevertCommit = SVNRevision.create( lRevertCommit );
        SVNRevision sLastCommit = SVNRevision.create( revCurrentCommit );

        if ( revCurrentCommit > lRevertCommit )
        {

            SVNRevisionRange rangeToMerge = new SVNRevisionRange( sLastCommit, sRevertCommit );

            try
            {
                diffClient.doMerge( SVNURL.parseURIEncoded( strCmUrl ), sLastCommit, Collections.singleton( rangeToMerge ), new File( strRepoPath ),
                        SVNDepth.INFINITY, true, false, false, false );
            }
            catch( SVNException e )
            {
                AppLogService.error( e );
            }
        }

    }

    /**
     * Gets the svn url tag site.
     *
     * @param strScmUrl
     *            the str scm url
     * @param strTagName
     *            the str tag name
     * @return the svn url tag site
     */
    public static String getSvnUrlTagSite( String strScmUrl, String strTagName )
    {

        String strUrl = strScmUrl.contains( ConstanteUtils.CONSTANTE_TRUNK )
                ? strScmUrl.replace( ConstanteUtils.CONSTANTE_TRUNK, ConstanteUtils.CONSTANTE_TAGS )
                : strScmUrl;
        return strUrl + ConstanteUtils.CONSTANTE_SEPARATOR_SLASH + strTagName;
    }

    /**
     * Gets the repo url.
     *
     * @param strRepoUrl
     *            the str repo url
     * @return the repo url
     */
    public static String getRepoUrl( String strRepoUrl )
    {

        if ( strRepoUrl != null && strRepoUrl.startsWith( "scm:svn:" ) )
        {
            strRepoUrl = strRepoUrl.substring( 8 );

        }

        return strRepoUrl;

    }

    /**
     * Check authentication.
     *
     * @param strUrl
     *            the str url
     * @param strUserName
     *            the str user name
     * @param strPassword
     *            the str password
     * @return true, if successful
     */
    public static boolean checkAuthentication( String strUrl, String strUserName, String strPassword )
    {
        try
        {
            ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager( strUserName, strPassword );

            SVNURL url = SVNURL.parseURIEncoded( strUrl );
            SVNRepository repository = SVNRepositoryFactory.create( url, null );
            repository.setAuthenticationManager( authManager );
            repository.testConnection( );
        }
        catch( SVNException e )
        {
            return false;
        }
        return true;

    }

}