package com.mtvi.plateng.subversion; import java.io.File; import java.io.PrintStream; import java.util.ArrayList; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.tmatesoft.svn.core.SVNCommitInfo; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNNodeKind; import org.tmatesoft.svn.core.SVNProperties; 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.io.SVNRepository; import org.tmatesoft.svn.core.io.SVNRepositoryFactory; import org.tmatesoft.svn.core.wc.SVNCommitClient; import org.tmatesoft.svn.core.wc.SVNWCUtil; /** * SVNForceImport can be used to import a maven project into an svn repository. It has the ability to import numerous * different files/folders based on matching a regular expression pattern. Each matched item can be renamed and placed * in differing folders. * * SVNForceImport can also read a projects pom file and extract Major Minor and Patch version numbers. * * @author bsmith * @version 0.1 */ public class SVNForceImport { private static final Logger LOGGER = Logger.getLogger(SVNForceImport.class.getName()); /** * Main method, used by hudson until a plugin wrapper can be written. * * @param args Arguments consist of switches and values as follows:<p> * <ul> * * <li> -r <code>repository_url</code><br> * REQUIRED: The url of the repository to be used, should include the path to the project.<br><br> * * <li> -t <code>target_path</code><br> * REQUIRED: The path to the local target directory.<br><br> * * <li> -pom <code>pom.xml_path</code><br> * REQUIRED: The path to the project's pom file.<br><br> * * <li> -i <code>file_pattern</code> <code>remote_path</code> <code>remote_name</code><br> * REQUIRED, MULTIPLE: The item(s) (file/folder) to be imported.<br> * All arguments may use _MAJOR_,_MINOR_, and _PATCH_ to replace with pom values.<br> * <ul> * <li><code>file_pattern</code>: Only items fitting this pattern will be imported.<br> * <li><code>remote_path</code>: Path from remote project to desired location, ends in "/". _ROOT_ places item in project root.<br> * <li><code>remote_name</code>: Final name for imported item, multiple items matching the same pattern are prepended with numbers.<br> * </ul><br><br> * * * <li> -u <code>svn_username</code><br> * OPTIONAL: svn username<br><br> * * <li> -p <code>svn_password</code><br> * OPTIONAL: svn password<br> * </ul> */ public static void main(String[] args) { ArrayList <ImportItem> importItems = new ArrayList <ImportItem>(); String svnURL = ""; String pomPath = ""; String target = ""; String user = ""; String password = ""; // read through given options // I thought about creating an enumeration for these, but I'd rather just write the wrapper. try{ for (int i = 0; i < args.length;){ if(args[i].equalsIgnoreCase("-r")){ // set repositoryURL svnURL = args[i+1]; i+= 2; }else if(args[i].equalsIgnoreCase("-i")){ // add item importItems.add(new ImportItem(args[i+1], args[i+2], args[i+3])); i+= 4; }else if(args[i].equalsIgnoreCase("-u")){ // set username user = args[i+1]; i+=2; }else if(args[i].equalsIgnoreCase("-p")){ // set password password = args[i+1]; i+=2; }else if(args[i].equalsIgnoreCase("-pom")){ // set pomPath pomPath = args[i+1]; i+=2; } else if(args[i].equalsIgnoreCase("-t")){ // set pomPath target = args[i+1]; i+=2; } } if (svnURL.length()==0){ System.err.println("SVNForceImport Error: Missing repository URL\n"); } } catch(Exception e){ System.err.println("SVNForceImport Error: Error while parsing options\n"); } forceImport(svnURL, user, password, target, importItems, pomPath ,null, null, null, null, null); } /** * The core SVNForceImport method, used to import files into a repository. * * @param svnURL The url of the repository including path to project root. * @param user The username to use for repository access. * @param password The password to use for repository access. * @param target The path to the local target directory, where items are found. * @param items The ImportItems to be imported. * @param pomPath The path to the project's pom.xml file. * @param majorPath The xml path to the major version in the pom file. * @param minorPath The xml path to the minor version in the pom file. * @param patchPath The xml path to the patch version in the pom file. */ public static void forceImport(String svnURL, String user, String password, String target, ArrayList<ImportItem> items, String pomPath, String majorPath, String minorPath, String patchPath, String workspace, PrintStream stream){ if (null != workspace){ workspace = workspace.substring(0,workspace.length()-1); target = target.replaceAll("_WORKSPACE_", workspace); stream.println("SVN Publisher: target: " + target); if (null != pomPath){ pomPath = pomPath.replaceAll("_WORKSPACE_", workspace); stream.println("SVN Publisher: pomPath: " + pomPath); } } // target directory is required File targetDir = new File(target); if (!targetDir.canRead()) { LOGGER.severe("SVNForceImport Error: target Directory not accessable: " + target); if (null != stream){ stream.println("SVN Publisher: Error: target Directory not accessable: " + target); } } // pom is not required, but without it MAJOR/MINOR/PATCH variables won't be available SimplePOMParser spp = new SimplePOMParser(); if (null != pomPath){ File pom = new File(pomPath); if (!pom.canRead()){ LOGGER.severe("SVNForceImport Error: pom File not accessable: " + pomPath); if (null != stream){ stream.println("SVN Publisher: Error: pom File not accessable: " + pomPath); } } spp.setMajorPath(majorPath); spp.setMinorPath(minorPath); spp.setPatchPath(patchPath); spp.parse(pom); } SVNRepository repository = null; ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(); // create the repo and authManager try { setupProtocols(); repository = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(svnURL)); if (null != user){ authManager = SVNWCUtil.createDefaultAuthenticationManager(user, password); } repository.setAuthenticationManager(authManager); // create the commit client that will do the work SVNCommitClient commitClient = new SVNCommitClient(authManager, null); // import each item String finalName; String finalPath; String finalPattern; for (ImportItem item: items){ // if the pom and major/minor/patch paths have been included attempt to do some simple replacement boolean nullName = false; finalName = ""; finalPath= ""; finalPattern = ""; if ((null == item.getName()) || (item.getName().length() < 1)){ LOGGER.info("null Name"); nullName = true; }else{ finalName = variableReplace(spp, item.getName()); } finalPattern = variableReplace(spp, item.getPattern()); finalPath = variableReplace(spp, item.getPath()); ArrayList<File> files = matchFiles(finalPattern, targetDir); String prefix = ""; for (int i = 0; i < files.size(); i++){ ensurePath(repository, commitClient, svnURL, finalPath); if (!files.get(i).canRead()) { LOGGER.severe("SVNForceImport Error: File/Directory not accessable: " + files.get(i).getAbsolutePath()); if (null != stream){ stream.println("SVN Publisher: Error: File/Directory not accessable: " + files.get(i).getAbsolutePath()); } } if (nullName){ finalName = files.get(i).getName(); } SVNNodeKind nodeKind = repository.checkPath(finalPath + prefix + finalName, -1); if (nodeKind == SVNNodeKind.NONE){ insertItem(commitClient, svnURL + "/" + finalPath, files.get(i), prefix + finalName); if (null != stream){ stream.println("SVN Publisher: Importing Item: " + prefix + finalName); } } else { deleteItem(commitClient, svnURL + "/" + finalPath + prefix + finalName); if (null != stream){ stream.println("SVN Publisher: Deleting Remote Item: " + prefix + finalName); } insertItem(commitClient, svnURL + "/" + finalPath, files.get(i), prefix + finalName); if (null != stream){ stream.println("SVN Publisher: Importing Item: " + prefix + finalName); } } prefix = Integer.toString(i + 1); } } }catch (SVNException svne) { LOGGER.severe("*SVNForceImport Error: " + svne.getMessage()); } } /** * Insert a given file/folder into the given repository location with a given name * * @param client The SVNCommitClient to be used to preform the commit action. * @param fullURL The full URL pointing to where the importItem will be placed. * @param importItem The file or folder to be imported in the repository. * @param name The file/folder name to be used in the repository. * @return The results of the commit action. * @throws SVNException */ private static SVNCommitInfo insertItem(SVNCommitClient client, String fullURL, File importItem, String name) throws SVNException { String logMessage = "SVNForceImport importing: " + importItem.getAbsolutePath(); return client.doImport( importItem, // File/Directory to be imported SVNURL.parseURIEncoded(fullURL + name), // location within svn logMessage, // svn comment new SVNProperties(), // svn properties true, // use global ignores false, // ignore unknown node types SVNDepth.INFINITY); // fully recursive } /** * Delete a given file/folder from the repository. * * @param client The SVNCommitClient to be used to preform the commit action. * @param fullURL The full URL pointing to the item to be deleted. * @return The result of the commit action. * @throws SVNException */ private static SVNCommitInfo deleteItem(SVNCommitClient client, String fullURL) throws SVNException { String logMessage = "SVNForceImport removing: " + fullURL; SVNURL[] urls = {SVNURL.parseURIEncoded(fullURL)}; return client.doDelete(urls, logMessage); } /** * Create a given directory in the repository. * * @param client The SVNCommitClient to be used to preform the mkdir action. * @param fullPath The full URL pointing to where the Directory should be created (including the directory to be created). * @return The result of the commit action. * @throws SVNException */ private static SVNCommitInfo createDir(SVNCommitClient client, String fullPath) throws SVNException { String logMessage = "SVNForceImport creating Directory : " + fullPath; SVNURL[] urls = {SVNURL.parseURIEncoded(fullPath)}; return client.doMkDir(urls, logMessage); } /** * Set up the different repository protocol factories so that http,https,svn,and file protocols can all be used. */ private static void setupProtocols() { // http and https DAVRepositoryFactory.setup(); // svn SVNRepositoryFactoryImpl.setup(); // file FSRepositoryFactory.setup(); } /** * Search through a given directory and return an ArrayList of any files/folders who's names match the given pattern. * * @param patternString The regular expression pattern to use in matching applicable file/folder names * @param parent The folder to search for matches in. * @return All files/folders matching the given pattern. */ private static ArrayList<File> matchFiles(String patternString, File parent){ Pattern pattern = Pattern.compile(patternString); Matcher matcher; ArrayList<File> files = new ArrayList <File>(); for (File file : parent.listFiles()){ matcher = pattern.matcher(file.getName()); if (matcher.matches()){ files.add(file); } } return files; } /** * Replace variable names with their values. * * @param spp The SimplePOMParser containing the required variables. * @param value The String to be filtered. * @return The new String with variable names replaced with values */ private static String variableReplace(SimplePOMParser spp, String value){ value = value.replace("_ROOT_", ""); value = value.replace("_MAJOR_", Integer.toString(spp.getMajor())); value = value.replace("_MINOR_", Integer.toString(spp.getMinor())); value = value.replace("_PATCH_", Integer.toString(spp.getPatch())); return value; } /** * Validate the the required path exists in the project on the repository. If it doesn't then create it. * * @param repository The repository to be checked. * @param commitClient The SVNCommitClient to be used to preform any commit actions. * @param svnURL The URL of the project in the repository. * @param path The path within the project to be checked/created. */ private static void ensurePath(SVNRepository repository, SVNCommitClient commitClient, String svnURL, String path){ String[] dirs = path.split("/"); String constructedPath = ""; if (dirs.length > 0){ for (int i = 0; i < dirs.length; i++){ try{ SVNNodeKind nodeKind = repository.checkPath(constructedPath + dirs[i], -1); if (nodeKind == SVNNodeKind.NONE){ createDir(commitClient, svnURL + "/" + constructedPath + dirs[i]); } constructedPath += dirs[i] + "/"; }catch (SVNException svne) { System.err.println("SVNForceImport Error: " + svne.getMessage()); } } } } }