com.iyonger.apm.web.repository.FileEntryRepository.java Source code

Java tutorial

Introduction

Here is the source code for com.iyonger.apm.web.repository.FileEntryRepository.java

Source

/* 
 * Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */
package com.iyonger.apm.web.repository;

import com.iyonger.apm.web.configuration.Config;
import com.iyonger.apm.web.model.FileCategory;
import com.iyonger.apm.web.model.FileEntry;
import com.iyonger.apm.web.model.FileType;
import com.iyonger.apm.web.model.Home;
import com.iyonger.apm.web.service.UserContext;
import com.iyonger.apm.web.util.EncodingUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.lang.StringUtils;
import org.ngrinder.common.exception.NGrinderRuntimeException;
import org.ngrinder.model.User;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
import org.tmatesoft.svn.core.io.ISVNEditor;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.diff.SVNDeltaGenerator;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNWCUtil;

import javax.annotation.PostConstruct;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.EmptyStackException;
import java.util.List;
import java.util.Map.Entry;

import static org.ngrinder.common.util.CollectionUtils.newArrayList;
import static org.ngrinder.common.util.ExceptionUtils.processException;
import static org.ngrinder.common.util.NoOp.noOp;
import static org.ngrinder.common.util.Preconditions.checkNotNull;

/**
 * SVN FileEntity repository.
 * <p/>
 * This class save and retrieve {@link FileEntry} from Local SVN folders.
 *
 * @author JunHo Yoon
 * @since 3.0
 */
//@Profile("production")
@Component
public class FileEntryRepository {

    private static final Logger LOG = LoggerFactory.getLogger(FileEntryRepository.class);

    @Autowired
    private Config config;

    private Home home;

    private File subversionHome;

    /**
     * Initialize the {@link FileEntryRepository}. This method should be
     * performed to set up FS Repository.
     */
    @PostConstruct
    public void init() {
        FSRepositoryFactory.setup();
        home = config.getHome();
        subversionHome = home.getSubFile("subversion");
    }

    /*@Autowired
    private UserRepository userRepository;*/

    /**
     * Get user repository.
     * <p/>
     * For unit test, This can be overridable.
     *
     * @param user the user
     * @return user repository path.
     */
    public File getUserRepoDirectory(User user) {
        return home.getUserRepoDirectory(user.getUserId());
    }

    /**
     * Return all {@link FileEntry}s under the given path.
     *
     * @param user     user
     * @param path     path under which files are searched.
     * @param revision . null if head.
     * @return found {@link FileEntry}s
     */
    public List<FileEntry> findAll(User user, final String path, Long revision) {
        return findAll(user, path, revision, false);
    }

    /**
     * Return all {@link FileEntry}s under the given path.
     *
     * @param user      user
     * @param path      path under which files are searched.
     * @param revision  null if head.
     * @param recursive true if recursive finding
     * @return found {@link FileEntry}s
     */
    public List<FileEntry> findAll(User user, final String path, Long revision, boolean recursive) {
        SVNRevision svnRevision = SVNRevision.HEAD;
        if (revision != null && -1L != revision) {
            svnRevision = SVNRevision.create(revision);
        }
        final List<FileEntry> fileEntries = newArrayList();
        SVNClientManager svnClientManager = getSVNClientManager();
        try {
            svnClientManager.getLogClient().doList(
                    SVNURL.fromFile(getUserRepoDirectory(user)).appendPath(path, true), svnRevision, svnRevision,
                    true, recursive, new ISVNDirEntryHandler() {
                        @Override
                        public void handleDirEntry(SVNDirEntry dirEntry) throws SVNException {

                            FileEntry script = new FileEntry();
                            // Exclude base path "/"
                            if (StringUtils.isBlank(dirEntry.getRelativePath())) {
                                return;
                            }
                            script.setPath(FilenameUtils.normalize(path + "/" + dirEntry.getRelativePath(), true));
                            script.setCreatedDate(dirEntry.getDate());
                            script.setLastModifiedDate(dirEntry.getDate());
                            script.setDescription(dirEntry.getCommitMessage());
                            script.setRevision(dirEntry.getRevision());
                            if (dirEntry.getKind() == SVNNodeKind.DIR) {
                                script.setFileType(FileType.DIR);
                            } else {
                                script.getFileType();
                                script.setFileSize(dirEntry.getSize());
                            }
                            fileEntries.add(script);
                        }
                    });
        } catch (Exception e) {
            LOG.debug("findAll() to the not existing folder {}", path);
        } finally {
            closeSVNClientManagerQuietly(svnClientManager);
        }
        return fileEntries;
    }

    /**
     * Return all {@link FileEntry}s which user have. It excludes
     * {@link FileType#DIR} entries.
     *
     * @param user user
     * @return found {@link FileEntry}s
     */
    public List<FileEntry> findAll(final User user) {
        final List<FileEntry> scripts = newArrayList();
        SVNClientManager svnClientManager = getSVNClientManager();
        try {
            svnClientManager.getLogClient().doList(SVNURL.fromFile(getUserRepoDirectory(user)), SVNRevision.HEAD,
                    SVNRevision.HEAD, false, true, new ISVNDirEntryHandler() {
                        @Override
                        public void handleDirEntry(SVNDirEntry dirEntry) throws SVNException {
                            FileEntry script = new FileEntry();
                            String relativePath = dirEntry.getRelativePath();
                            if (StringUtils.isBlank(relativePath)) {
                                return;
                            }
                            script.setCreatedDate(dirEntry.getDate());
                            script.setLastModifiedDate(dirEntry.getDate());
                            script.setPath(relativePath);
                            script.setDescription(dirEntry.getCommitMessage());
                            long reversion = dirEntry.getRevision();
                            script.setRevision(reversion);
                            script.setFileType(dirEntry.getKind() == SVNNodeKind.DIR ? FileType.DIR : null);
                            script.setFileSize(dirEntry.getSize());
                            scripts.add(script);
                        }
                    });
        } catch (Exception e) {
            LOG.error("Error while fetching files from SVN for {}", user.getUserId());
            LOG.debug("Error details :", e);
            throw new NGrinderRuntimeException(e);
        } finally {
            closeSVNClientManagerQuietly(svnClientManager);
        }
        return scripts;

    }

    /**
     * Return a {@link FileEntry} for the given path and revision.
     *
     * @param user     user
     * @param path     path in the svn repo
     * @param revision revision of the file
     * @return found {@link FileEntry}, null if not found
     */
    public FileEntry findOne(User user, String path, SVNRevision revision) {
        final FileEntry script = new FileEntry();
        SVNClientManager svnClientManager = null;
        ByteArrayOutputStream outputStream = null;
        try {
            svnClientManager = getSVNClientManager();

            SVNURL userRepoUrl = SVNURL.fromFile(getUserRepoDirectory(user));
            if (userRepoUrl == null) {
                return null;
            }
            SVNRepository repo = svnClientManager.createRepository(userRepoUrl, true);
            SVNNodeKind nodeKind = repo.checkPath(path, -1);
            if (nodeKind == SVNNodeKind.NONE) {
                return null;
            }
            outputStream = new ByteArrayOutputStream();
            SVNProperties fileProperty = new SVNProperties();
            // Get File.
            repo.getFile(path, revision.getNumber(), fileProperty, outputStream);
            SVNDirEntry lastRevisionedEntry = repo.info(path, -1);
            long lastRevisionNumber = (lastRevisionedEntry == null) ? -1 : lastRevisionedEntry.getRevision();
            String revisionStr = fileProperty.getStringValue(SVNProperty.REVISION);
            long revisionNumber = Long.parseLong(revisionStr);
            SVNDirEntry info = repo.info(path, revisionNumber);
            byte[] byteArray = outputStream.toByteArray();
            script.setPath(path);
            for (String name : fileProperty.nameSet()) {
                script.getProperties().put(name, fileProperty.getStringValue(name));
            }
            script.setFileType(FileType.getFileTypeByExtension(FilenameUtils.getExtension(script.getFileName())));
            if (script.getFileType().isEditable()) {
                String autoDetectedEncoding = EncodingUtils.detectEncoding(byteArray, "UTF-8");
                script.setContent(new String(byteArray, autoDetectedEncoding));
                script.setEncoding(autoDetectedEncoding);
                script.setContentBytes(byteArray);
            } else {
                script.setContentBytes(byteArray);
            }
            script.setDescription(info.getCommitMessage());
            script.setRevision(revisionNumber);
            script.setLastRevision(lastRevisionNumber);
            script.setCreatedUser(user);
        } catch (Exception e) {
            LOG.error("Error while fetching a file from SVN {}", user.getUserId() + "_" + path, e);
            return null;
        } finally {
            closeSVNClientManagerQuietly(svnClientManager);
            IOUtils.closeQuietly(outputStream);
        }
        return script;
    }

    private void addPropertyValue(ISVNEditor editor, FileEntry fileEntry) throws SVNException {
        if (fileEntry.getFileType().getFileCategory() == FileCategory.SCRIPT) {
            editor.changeFileProperty(fileEntry.getPath(), "targetHosts", SVNPropertyValue.create(""));
        }
        for (Entry<String, String> each : fileEntry.getProperties().entrySet()) {
            editor.changeFileProperty(fileEntry.getPath(), each.getKey(), SVNPropertyValue.create(each.getValue()));
        }
    }

    public void save(User user, FileEntry fileEntry, String encoding) {
        SVNClientManager svnClientManager = null;
        ISVNEditor editor = null;
        String checksum = null;
        InputStream bais = null;
        try {
            svnClientManager = getSVNClientManager();
            SVNRepository repo = svnClientManager.createRepository(SVNURL.fromFile(getUserRepoDirectory(user)),
                    true);
            SVNDirEntry dirEntry = repo.info(fileEntry.getPath(), -1);

            // Add base paths
            String fullPath = "";
            // Check.. first
            for (String each : getPathFragment(fileEntry.getPath())) {
                fullPath = fullPath + "/" + each;
                SVNDirEntry folderStepEntry = repo.info(fullPath, -1);
                if (folderStepEntry != null && folderStepEntry.getKind() == SVNNodeKind.FILE) {
                    throw processException(
                            "User " + user.getUserId() + " tried to create folder " + fullPath + ". It's file..");
                }
            }

            editor = repo.getCommitEditor(fileEntry.getDescription(), null, true, null);
            editor.openRoot(-1);
            fullPath = "";
            for (String each : getPathFragment(fileEntry.getPath())) {
                fullPath = fullPath + "/" + each;
                try {
                    editor.addDir(fullPath, null, -1);
                } catch (Exception e) {
                    // FALL THROUGH
                    noOp();
                }
            }

            if (fileEntry.getFileType() == FileType.DIR) {
                editor.addDir(fileEntry.getPath(), null, -1);
            } else {
                if (dirEntry == null) {
                    // If it's new file
                    editor.addFile(fileEntry.getPath(), null, -1);
                } else {
                    // If it's modification
                    editor.openFile(fileEntry.getPath(), -1);
                }
                editor.applyTextDelta(fileEntry.getPath(), null);

                // Calc diff
                final SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
                if (fileEntry.getContentBytes() == null && fileEntry.getFileType().isEditable()) {
                    bais = new ByteArrayInputStream(
                            checkNotNull(fileEntry.getContent()).getBytes(encoding == null ? "UTF-8" : encoding));
                } else {
                    bais = new ByteArrayInputStream(fileEntry.getContentBytes());
                }
                checksum = deltaGenerator.sendDelta(fileEntry.getPath(), bais, editor, true);
            }

            addPropertyValue(editor, fileEntry);
            editor.closeFile(fileEntry.getPath(), checksum);
        } catch (Exception e) {
            abortSVNEditorQuietly(editor);
            // If it's adding the folder which already exists... ignore..
            if (e instanceof SVNException && fileEntry.getFileType() == FileType.DIR) {
                if (SVNErrorCode.FS_ALREADY_EXISTS.equals(((SVNException) e).getErrorMessage().getErrorCode())) {
                    return;
                }
            }
            LOG.error("Error while saving file to SVN", e);
            throw processException("Error while saving file to SVN", e);
        } finally {
            closeSVNEditorQuietly(editor);
            closeSVNClientManagerQuietly(svnClientManager);
            IOUtils.closeQuietly(bais);
        }
    }

    String[] getPathFragment(String path) {
        String basePath = FilenameUtils.getPath(path);
        return StringUtils.split(FilenameUtils.separatorsToUnix(basePath), "/");

    }

    private void abortSVNEditorQuietly(ISVNEditor editor) {
        if (editor == null) {
            return;
        }
        try {
            editor.abortEdit();
        } catch (SVNException e) {
            // FALL THROUGH
            noOp();
        }
    }

    private void closeSVNEditorQuietly(ISVNEditor editor) {
        if (editor == null) {
            return;
        }
        try {
            // recursively close
            //noinspection InfiniteLoopStatement
            while (true) {
                editor.closeDir();
            }
        } catch (EmptyStackException e) {
            // FALL THROUGH
            noOp();
        } catch (SVNException e) {
            // FALL THROUGH
            noOp();
        } finally {
            try {
                editor.closeEdit();
            } catch (SVNException e) {
                // FALL THROUGH
                noOp();
            }
        }
    }

    public void delete(User user, List<String> paths) {
        SVNClientManager svnClientManager = null;
        ISVNEditor editor = null;
        try {
            svnClientManager = getSVNClientManager();
            SVNRepository repo = svnClientManager.createRepository(SVNURL.fromFile(getUserRepoDirectory(user)),
                    true);

            editor = repo.getCommitEditor("delete", null, true, null);
            editor.openRoot(-1);
            for (String each : paths) {
                editor.deleteEntry(each, -1);
            }
        } catch (Exception e) {
            abortSVNEditorQuietly(editor);
            LOG.error("Error while deleting file from SVN", e);
            throw processException("Error while deleting files from SVN", e);
        } finally {
            closeSVNEditorQuietly(editor);
            closeSVNClientManagerQuietly(svnClientManager);
        }
    }

    @Autowired
    UserContext userContext;

    public SVNClientManager getSVNClientManager() {
        DefaultSVNOptions options = SVNWCUtil.createDefaultOptions(subversionHome, true);
        ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(subversionHome,
                getCurrentUserId(), null, false);
        return SVNClientManager.newInstance(options, authManager);
    }

    protected String getCurrentUserId() {
        try {
            return userContext.getCurrentUser().getUserId();
        } catch (Exception e) {
            return "default";
        }

    }

    private void closeSVNClientManagerQuietly(SVNClientManager svnClientManager) {
        if (svnClientManager != null) {
            svnClientManager.dispose();
        }
    }

    public boolean hasOne(User user, String path) {
        SVNClientManager svnClientManager = null;
        try {
            svnClientManager = getSVNClientManager();
            SVNURL userRepoUrl = SVNURL.fromFile(getUserRepoDirectory(user));
            SVNRepository repo = svnClientManager.createRepository(userRepoUrl, true);
            SVNNodeKind nodeKind = repo.checkPath(path, -1);
            return (nodeKind != SVNNodeKind.NONE);
        } catch (Exception e) {
            LOG.error("Error while fetching files from SVN", e);
            throw processException("Error while checking file existence from SVN", e);
        } finally {
            closeSVNClientManagerQuietly(svnClientManager);
        }
    }

    public void writeContentTo(User user, String path, File toPathDir) {
        SVNClientManager svnClientManager = null;
        FileOutputStream fileOutputStream = null;
        try {
            svnClientManager = getSVNClientManager();

            SVNURL userRepoUrl = SVNURL.fromFile(getUserRepoDirectory(user));
            SVNRepository repo = svnClientManager.createRepository(userRepoUrl, true);
            SVNNodeKind nodeKind = repo.checkPath(path, -1);
            // If it's DIR, it does not work.
            if (nodeKind == SVNNodeKind.NONE || nodeKind == SVNNodeKind.DIR) {
                throw processException("It's not possible to write directory. nodeKind is " + nodeKind);
            }
            //noinspection ResultOfMethodCallIgnored
            toPathDir.mkdirs();
            File destFile = new File(toPathDir, FilenameUtils.getName(path));
            // Prepare parent folders
            fileOutputStream = new FileOutputStream(destFile);
            SVNProperties fileProperty = new SVNProperties();
            // Get file.
            repo.getFile(path, -1L, fileProperty, fileOutputStream);
        } catch (Exception e) {
            LOG.error("Error while fetching files from SVN", e);
            throw processException("Error while fetching files from SVN", e);
        } finally {
            closeSVNClientManagerQuietly(svnClientManager);
            IOUtils.closeQuietly(fileOutputStream);
        }
    }
}