/* * The MIT License * * Copyright (c) 2010, Manufacture Française des Pneumatiques Michelin, Romain Seguy * Copyright (c) 2007-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Erik Ramfelt, * Henrik Lynggaard, Peter Liljenberg, Andrew Bayer * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.michelin.cio.hudson.plugins.clearcaseucmbaseline; import hudson.FilePath; import hudson.Util; import hudson.plugins.clearcase.ClearToolExec; import hudson.plugins.clearcase.ClearToolLauncher; import hudson.plugins.clearcase.util.PathUtil; import hudson.util.ArgumentListBuilder; import hudson.util.VariableResolver; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.util.Hashtable; import java.util.Map; import java.util.Random; import org.apache.commons.lang.StringUtils; /** * This class defines the cleartool command for use by * {@link ClearCaseUcmBaselineParameterValue#createBuildWrapper(hudson.model.AbstractBuild)}. * * <p>While this class extends {@link ClearToolExec}, most of {@link ClearToolExec}'s * methods will throw an {@link UnsupportedOperationException} since only the methods * useful for {@link ClearCaseUcmBaselineParameterValue#createBuildWrapper(hudson.model.AbstractBuild)} * have been implemented.</p> * * @author Romain Seguy (http://davadoc.deviantart.com) */ public class ClearToolUcmBaseline extends ClearToolExec { /** * Used to cache components given the baseline they belong to (the key is * a baseline selector). */ private transient Map<String, String> componentsCache = new Hashtable<String, String>(); /** * Used to cache root dirs given the component they belong to (the key is * a component selector). */ private transient Map<String, String> componentRootDirsCache = new Hashtable<String, String>(); /** * Used to cache the dependent baselines given the baseline they depend on. */ private transient Map<String, String[]> dependentBaselinesCache = new Hashtable<String, String[]>(); public ClearToolUcmBaseline(VariableResolver variableResolver, ClearToolLauncher launcher) { super(variableResolver, launcher); } /** * Returns, for a given ClearCase UCM baseline, the ClearCase UCM component * this baseline refers to. * * <p>The ClearCase UCM components are cached for each instance of the class.</p> * * @see ftp://ftp.software.ibm.com/software/rational/docs/v2002/cc/cc_ref_1.pdf (%[component]p, page 392) */ public String getComponentFromBaseline(String pvob, String baseline) throws IOException, InterruptedException { String baselineSelector = baseline + '@' + pvob; if(componentsCache.containsKey(baselineSelector)) { return componentsCache.get(baselineSelector); } // cleartool lsbl -fmt "%[component]p" <baseline>@<pvob> ArgumentListBuilder cmd = new ArgumentListBuilder(); cmd.add("lsbl"); cmd.add("-fmt"); cmd.add("%[component]p"); cmd.add(baselineSelector); ByteArrayOutputStream baos = new ByteArrayOutputStream(); launcher.run(cmd.toCommandArray(), null, baos, null); String cleartoolOutput = ClearCaseUcmBaselineUtils.processCleartoolOuput(baos); baos.close(); // ensure no error has occured if(cleartoolOutput.contains("cleartool: Error")) { launcher.getListener().error("Failed to get component from the baseline " + baselineSelector + "."); throw new IOException("Failed to get the component from the baseline " + baselineSelector + ": " + cleartoolOutput); } // no error ==> it means the cleartool ouput just contains the component // caching componentsCache.put(baselineSelector, cleartoolOutput); return cleartoolOutput; } /** * Returns, for a given ClearCase UCM component, its root dir. * * <p>Note that in case the component is rootless, an empty string is * returned (cf. HUDSON-6398).</p> * <p>The root dirs are cached for each instance of the class.</p> * * @see ftp://ftp.software.ibm.com/software/rational/docs/v2002/cc/cc_ref_1.pdf (%[root_dir]p, page 392) */ public String getComponentRootDir(String pvob, String component) throws IOException, InterruptedException { String componentSelector = component + '@' + pvob; if(componentRootDirsCache.containsKey(componentSelector)) { return componentRootDirsCache.get(componentSelector); } // cleartool lscomp -fmt "%[root_dir]p" <component>@<pvob> ArgumentListBuilder cmd = new ArgumentListBuilder(); cmd.add("lscomp"); cmd.add("-fmt"); cmd.add("%[root_dir]p"); cmd.add(componentSelector); ByteArrayOutputStream baos = new ByteArrayOutputStream(); launcher.run(cmd.toCommandArray(), null, baos, null); String cleartoolOutput = ClearCaseUcmBaselineUtils.processCleartoolOuput(baos); baos.close(); // ensure no error has occured if(cleartoolOutput != null && cleartoolOutput.contains("cleartool: Error")) { launcher.getListener().error("Failed to get root dir of the component " + componentSelector + "."); throw new IOException("Failed to get the root dir of the component " + componentSelector + ": " + cleartoolOutput); } // no error ==> it means the cleartool ouput just contains the root dir // or an empty string if the component is rootless -- cf. HUDSON-6398 // caching componentRootDirsCache.put(componentSelector, cleartoolOutput); return cleartoolOutput != null ? cleartoolOutput.trim() : StringUtils.EMPTY; } /** * Returns, for a given ClearCase UCM composite baseline, all the baselines * in the dependencies graph. * * @return An array of ClearCase UCM baseline selectors (it may be empty) * * @see ftp://ftp.software.ibm.com/software/rational/docs/v2002/cc/cc_ref_1.pdf (%[depends_on_closure]p, page 392) */ public String[] getDependentBaselines(String pvob, String baseline) throws IOException, InterruptedException { String baselineSelector = baseline + '@' + pvob; if(dependentBaselinesCache.containsKey(baselineSelector)) { return dependentBaselinesCache.get(baselineSelector); } String[] dependentBaselines = null; ArgumentListBuilder cmd = new ArgumentListBuilder(); cmd.clear(); cmd.add("lsbl"); cmd.add("-fmt"); cmd.add("%[depends_on_closure]p"); cmd.add(baselineSelector); ByteArrayOutputStream baos = new ByteArrayOutputStream(); launcher.run(cmd.toCommandArray(), null, baos, null); String cleartoolOutput = ClearCaseUcmBaselineUtils.processCleartoolOuput(baos); baos.close(); // ensure no error has occured if(cleartoolOutput.contains("cleartool: Error")) { launcher.getListener().error("Failed to get the dependent baselines from the baseline " + baselineSelector + "."); throw new IOException("Failed to get the dependent baselines from the baseline " + baselineSelector + ": " + cleartoolOutput); } if(cleartoolOutput.length() > 0) { dependentBaselines = cleartoolOutput.split(" "); } else { dependentBaselines = new String[0]; } // caching dependentBaselinesCache.put(baselineSelector, dependentBaselines); return dependentBaselines; } @Override protected FilePath getRootViewPath(ClearToolLauncher launcher) { return launcher.getWorkspace(); } public void update(String viewName, String loadRules) throws IOException, InterruptedException { unsupportedMethod(Thread.currentThread().getStackTrace()[0]); } public void rmview(String viewName) throws IOException, InterruptedException { // cleartool rmview -force <view name> ArgumentListBuilder cmd = new ArgumentListBuilder(); cmd.add("rmview"); cmd.add("-force"); cmd.add(viewName); // run the cleartool command ByteArrayOutputStream baos = new ByteArrayOutputStream(); launcher.run(cmd.toCommandArray(), null, baos, null); String cleartoolOutput = ClearCaseUcmBaselineUtils.processCleartoolOuput(baos); baos.close(); // ensure no error has occured if(cleartoolOutput.contains("cleartool: Error")) { launcher.getListener().error("Failed to remove view " + viewName + "."); throw new IOException("Failed to remove view " + viewName + ": " + cleartoolOutput); } // manually delete the view folder if cleartool rmview didn't actually remove it FilePath viewFilePath = launcher.getWorkspace().child(viewName); if(viewFilePath.exists()) { launcher.getListener().getLogger().println("View folder was not actually removed by \"cleartool rmview\"; Removing it now..."); viewFilePath.deleteRecursive(); } } public void rmviewtag(String viewName) throws IOException, InterruptedException { unsupportedMethod(Thread.currentThread().getStackTrace()[0]); } public void mkview(String viewName, String streamSelector) throws IOException, InterruptedException { mkview(viewName, null, true, null); } public void mkview(String viewName, String mkviewOptionalParam, boolean snapshotView, String streamSelector) throws IOException, InterruptedException { // cleartool mkview -tag <tag> <view path> <optional parameters> ArgumentListBuilder cmd = new ArgumentListBuilder(); cmd.add("mkview"); if(snapshotView) { cmd.add("-snapshot"); } cmd.add("-tag"); cmd.add(viewName); // let's save user config stuff: we reuse the view name as the tag name // HUDSON-6409 if(StringUtils.isNotBlank(mkviewOptionalParam)) { cmd.addTokenized(Util.replaceMacro(mkviewOptionalParam, variableResolver)); } cmd.add(viewName); // run the cleartool command ByteArrayOutputStream baos = new ByteArrayOutputStream(); launcher.run(cmd.toCommandArray(), null, baos, null); String cleartoolOutput = ClearCaseUcmBaselineUtils.processCleartoolOuput(baos); baos.close(); // ensure no error has occured if(cleartoolOutput.contains("cleartool: Error")) { launcher.getListener().error("Failed to create view " + viewName + "."); throw new IOException("Failed to create view " + viewName + ": " + cleartoolOutput); } } // ClearCase plugin 1.1 upward compatibility public void mkview(String viewName, String streamSelector, String defaultStorageDir) throws IOException, InterruptedException { unsupportedMethod(Thread.currentThread().getStackTrace()[0]); } public void setcs(String viewName, String configSpec) throws IOException, InterruptedException { FilePath workspace = launcher.getWorkspace(); FilePath configSpecFile = workspace.createTextTempFile("configspec", ".txt", configSpec); String configSpecLocation = ".." + File.separatorChar + configSpecFile.getName(); configSpecLocation = PathUtil.convertPathForOS(configSpecLocation, launcher.getLauncher()); // cleartool setcs <configspec> ArgumentListBuilder cmd = new ArgumentListBuilder(); cmd.add("setcs"); cmd.add("-tag"); cmd.add(viewName); cmd.add(configSpecLocation); // run the cleartool command ByteArrayOutputStream baos = new ByteArrayOutputStream(); launcher.run(cmd.toCommandArray(), null, baos, workspace.child(viewName)); String cleartoolOutput = ClearCaseUcmBaselineUtils.processCleartoolOuput(baos); baos.close(); configSpecFile.delete(); // ensure no error has occured if(cleartoolOutput.contains("cleartool: Error")) { launcher.getListener().error("Failed to set the config spec of the view " + viewName + "."); throw new IOException("Failed to set the config spec of the view " + viewName + ": " + cleartoolOutput); } } public void startView(String viewTags) throws IOException, InterruptedException { unsupportedMethod(Thread.currentThread().getStackTrace()[0]); } public void syncronizeViewWithStream(String viewName, String stream) throws IOException, InterruptedException { unsupportedMethod(Thread.currentThread().getStackTrace()[0]); } // ClearCase plugin 1.2 upward compatibility public void update(String viewName, String[] loadRules) throws IOException, InterruptedException { unsupportedMethod(Thread.currentThread().getStackTrace()[0]); } /** * Always throws an {@link UnsupportedOperationException}. * * @param ste a {@link StackTraceElement} from which the name of the method * calling this one will be gathered (must NOT be {@code null} */ private void unsupportedMethod(StackTraceElement ste) { throw new UnsupportedOperationException( ClearCaseUcmBaselineSCM.class.getName() + " does not support the " + ste.getMethodName() + " method."); } }