package com.perforce.common.process; import java.util.concurrent.Callable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.perforce.common.ConverterException; import com.perforce.common.ExitCode; import com.perforce.common.Stats; import com.perforce.common.StatsType; import com.perforce.common.asset.TypeMap; import com.perforce.common.node.PathMapTranslator; import com.perforce.config.CFG; import com.perforce.config.Config; import com.perforce.config.UserMapping; import com.perforce.config.Version; import com.perforce.svn.change.ChangeInterface; import com.perforce.svn.change.ChangeMap; public abstract class ProcessChange implements Callable<Integer> { private Logger logger = LoggerFactory.getLogger(ProcessChange.class); protected long revStart; protected long revEnd; protected boolean isLabels; protected ProcessLabel processLabel; private ChangeInterface currentChange = null; private boolean stop = false; private boolean clean = true; private ExitCode runState = ExitCode.OK; /** * This method is extended by the specific implementation of ProcessChange * * @throws Exception */ protected void processChange() throws Exception { logger.error("common.ProcessChange.processChange() should be extended"); throw new RuntimeException(); } /** * Core run method for conversions * */ @Override public Integer call() throws Exception { try { // register exception hook Runtime.getRuntime().addShutdownHook(new CleanShutdown()); // run conversion runSingle(); // log summary if (logger.isInfoEnabled()) { String summary = Stats.summary(currentChange.getChange()); logger.info(summary); } // check for warnings long warn = Stats.getLong(StatsType.warningCount); if (warn > 0) { runState = ExitCode.WARNING; } } catch (Throwable e) { // catch any remaining throws logger.error("Caught exception on exit", e); runState = ExitCode.EXCEPTION; } finally { // save state and shutdown cleanly saveState(); clean = true; } return runState.value(); } /** * Runs conversion as single threaded (blocking) Used for UI and test cases * * @throws Throwable */ public void runSingle() throws Exception { // Initialise common environment processInit(); // Call processChange implementation processChange(); } private void processInit() throws Exception { // Setup stats counters Stats.setDefault(); // Log version of jar file Version ver = new Version(); logger.info("jar build version: \t" + ver.getVersion()); // Check JRE for symlink support if (Config.isImportMode()) { String javaVer = System.getProperty("java.version"); logger.info("java.version:\t\t" + javaVer); if (!javaVer.startsWith("1.7")) { throw new RuntimeException("JRE 1.7.x required for Import mode"); } } // Set unicode handling for p4-java System.setProperty("com.perforce.p4java.defaultCharset", "UTF-8"); // Initialise changeMap tables from existing file String changeMapFile = (String) Config.get(CFG.CHANGE_MAP); ChangeMap.load(changeMapFile); // Initialise user mapping, if required String userMapFile = (String) Config.get(CFG.USER_MAP); UserMapping.load(userMapFile); // Initialise type map, if required String typeMapFile = (String) Config.get(CFG.TYPE_MAP); TypeMap.load(typeMapFile); // Initialise path map, if required String pathMapFile = (String) Config.get(CFG.PATH_MAP); boolean isPathMap = PathMapTranslator.load(pathMapFile); // else, use default path translation map if (!isPathMap) { PathMapTranslator.setDefault(); } // Report operation mode if (Config.isImportMode()) { logger.info("conversion mode: \tIMPORT (front-door)\n"); } else { logger.info("conversion mode: \tCONVERT (back-door)\n"); } } /** * Submit current pending change or delete if empty changelist * * @throws Exception */ public void submit() throws Exception { boolean skip = (Boolean) Config.get(CFG.P4_SKIP_EMPTY); // Submit or delete the current pending change. if (currentChange != null) { // update stats Stats.addUser(currentChange.getUser()); // check if current change has revisions int revCount = currentChange.getNumberOfRevisions(); long change = currentChange.getChange(); if (skip && revCount == 0) { currentChange.delete(); } else { long c = currentChange.submit(); change = (c > change) ? c : change; } // logging details Stats.inc(StatsType.currentRevision); if (logger.isInfoEnabled()) { StringBuffer log = new StringBuffer(); log.append("mapping: r" + currentChange.getSvnRevision()); log.append(" => @" + change + "\n"); logger.info(log.toString()); } // map change to subversion revision ChangeMap.add(currentChange.getSvnRevision(), change); } // Submit any labels if (isLabels && processLabel != null) { if (logger.isTraceEnabled()) { logger.trace("Submitting labels:"); logger.trace(processLabel.toString()); } processLabel.submit(); } } /** * Closes the journal and flushes to disk, typically at the end of * conversion * * @throws Exception */ public void close() throws Exception { currentChange.close(); } /** * Private method to save thread state on exit. * */ private void saveState() { try { if (logger.isDebugEnabled()) { logger.debug("Saving changeMap..."); } ChangeMap.store((String) Config.get(CFG.CHANGE_MAP)); } catch (Exception e) { logger.error("Unable to saving changeMap", e); } } /** * Private shutdown hook to exit cleanly after exception. * */ private class CleanShutdown extends Thread { public void run() { stop = true; runState = ExitCode.SHUTDOWN; if (logger.isInfoEnabled()) { logger.info("Caught EXIT shutting down ..."); } while (!clean) { try { Thread.sleep(100); } catch (InterruptedException e) { logger.error("InterruptedException", e); } } } } public boolean isStop() { return stop; } public void setClean(boolean clean) { this.clean = clean; } public ChangeInterface getCurrentChange() { return currentChange; } public void setCurrentChange(ChangeInterface currentChange) { this.currentChange = currentChange; } protected void setChangeRange(long revLast) throws Exception { // Test start and end revisions if (revStart > revLast || revEnd > revLast) { String err = "Specified revision range exceeds last revision"; logger.error(err); throw new ConverterException(err); } // Auto set end revision if (revLast > 0 && revEnd == 0) { Config.set(CFG.P4_END, revLast); revEnd = revLast; } // Test start vs end range if (revStart > revEnd) { String err = "Specified start revision exceeds end revision"; logger.error(err); throw new ConverterException(err); } // Log import range if (logger.isInfoEnabled()) { StringBuffer sb = new StringBuffer(); sb.append("importing revs: \t"); sb.append(revStart + " to " + revEnd); sb.append(" out of " + revLast); logger.info(sb.toString()); } } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 13876 | Paul Allen | Rename/move file(s) | ||
//guest/paul_allen/p4convert-maven/src/com/perforce/common/process/ProcessChange.java | |||||
#1 | 13873 | Paul Allen | Branching using p4convert-maven | ||
//guest/perforce_software/p4convert/src/com/perforce/common/process/ProcessChange.java | |||||
#6 | 12890 | Paul Allen |
Extended start/stop change limits to CVS. - Moved start and stop configuration options out of SVN name space to the generic P4 space. - Abstracted shared range logic to ProcessChange #review-12880 |
||
#5 | 11565 | Paul Allen |
SVN: Write submitted change number to Change Map. The pending change was being written to the changeMap.txt even though the submitted number was reported in the log. |
||
#4 | 11071 | Paul Allen |
Path map translator for CVS and SVN paths. (undoc) To use create a path.map file with regex and group match. Only the first matching entry is used. The regex and group match are seperated by ', ' (or in regex terms ',\s+'). Lines starting with '#' are ignored. For example, 'trunk' is renamed to 'main', but other entries are left as-is. # path.map trunk/(.*), //import/main/{1} (.*), //import/{1} Note: if no file is found the default 'depot' and 'subPath' options are used to generate the map, preserving the original behaviour. CVS paths will always stat with the 'branch' name. 'main' for 1.1 and the symbol for other branches. # path.map main/projA/(.*), //import/projA/MAIN/{1} release_(.*)/projA/(.*), //import/projA/REL{1}/{2} (.*)/projA/(.*), //import/projA/TAG-{1}/{2} (.*), //import/unexpected/{1} Node: adding a catch all is a good idea. |
||
#3 | 11040 | Paul Allen |
SVN: Static label support - Push up shared label code for CVS/SVN - Update label description base for CVS test case 030 |
||
#2 | 10983 | Paul Allen |
SVN: Updated change number mapping. Use the submitted change number not the pending number, for the changeMap. |
||
#1 | 9807 | Paul Allen | Initial import of p4-convert (from change 894340) |