/* ===========================================================================
* Copyright (c) 2007 Serena Software. All rights reserved.
*
* Use of the Sample Code provided by Serena is governed by the following
* terms and conditions. By using the Sample Code, you agree to be bound by
* the terms contained herein. If you do not agree to the terms herein, do
* not install, copy, or use the Sample Code.
*
* 1. GRANT OF LICENSE. Subject to the terms and conditions herein, you
* shall have the nonexclusive, nontransferable right to use the Sample Code
* for the sole purpose of developing applications for use solely with the
* Serena software product(s) that you have licensed separately from Serena.
* Such applications shall be for your internal use only. You further agree
* that you will not: (a) sell, market, or distribute any copies of the
* Sample Code or any derivatives or components thereof; (b) use the Sample
* Code or any derivatives thereof for any commercial purpose; or (c) assign
* or transfer rights to the Sample Code or any derivatives thereof.
*
* 2. DISCLAIMER OF WARRANTIES. TO THE MAXIMUM EXTENT PERMITTED BY
* APPLICABLE LAW, SERENA PROVIDES THE SAMPLE CODE AS IS AND WITH ALL
* FAULTS, AND HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EITHER
* EXPRESSED, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY
* IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, OF FITNESS FOR A
* PARTICULAR PURPOSE, OF LACK OF VIRUSES, OF RESULTS, AND OF LACK OF
* NEGLIGENCE OR LACK OF WORKMANLIKE EFFORT, CONDITION OF TITLE, QUIET
* ENJOYMENT, OR NON-INFRINGEMENT. THE ENTIRE RISK AS TO THE QUALITY OF
* OR ARISING OUT OF USE OR PERFORMANCE OF THE SAMPLE CODE, IF ANY,
* REMAINS WITH YOU.
*
* 3. EXCLUSION OF DAMAGES. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE
* LAW, YOU AGREE THAT IN CONSIDERATION FOR RECEIVING THE SAMPLE CODE AT NO
* CHARGE TO YOU, SERENA SHALL NOT BE LIABLE FOR ANY DAMAGES WHATSOEVER,
* INCLUDING BUT NOT LIMITED TO DIRECT, SPECIAL, INCIDENTAL, INDIRECT, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, DAMAGES FOR LOSS OF
* PROFITS OR CONFIDENTIAL OR OTHER INFORMATION, FOR BUSINESS INTERRUPTION,
* FOR PERSONAL INJURY, FOR LOSS OF PRIVACY, FOR NEGLIGENCE, AND FOR ANY
* OTHER LOSS WHATSOEVER) ARISING OUT OF OR IN ANY WAY RELATED TO THE USE
* OF OR INABILITY TO USE THE SAMPLE CODE, EVEN IN THE EVENT OF THE FAULT,
* TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY, OR BREACH OF CONTRACT,
* EVEN IF SERENA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THE
* FOREGOING LIMITATIONS, EXCLUSIONS AND DISCLAIMERS SHALL APPLY TO THE
* MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW. NOTWITHSTANDING THE ABOVE,
* IN NO EVENT SHALL SERENA'S LIABILITY UNDER THIS AGREEMENT OR WITH RESPECT
* TO YOUR USE OF THE SAMPLE CODE AND DERIVATIVES THEREOF EXCEED US$10.00.
*
* 4. INDEMNIFICATION. You hereby agree to defend, indemnify and hold
* harmless Serena from and against any and all liability, loss or claim
* arising from this agreement or from (i) your license of, use of or
* reliance upon the Sample Code or any related documentation or materials,
* or (ii) your development, use or reliance upon any application or
* derivative work created from the Sample Code.
*
* 5. TERMINATION OF THE LICENSE. This agreement and the underlying
* license granted hereby shall terminate if and when your license to the
* applicable Serena software product terminates or if you breach any terms
* and conditions of this agreement.
*
* 6. CONFIDENTIALITY. The Sample Code and all information relating to the
* Sample Code (collectively "Confidential Information") are the
* confidential information of Serena. You agree to maintain the
* Confidential Information in strict confidence for Serena. You agree not
* to disclose or duplicate, nor allow to be disclosed or duplicated, any
* Confidential Information, in whole or in part, except as permitted in
* this Agreement. You shall take all reasonable steps necessary to ensure
* that the Confidential Information is not made available or disclosed by
* you or by your employees to any other person, firm, or corporation. You
* agree that all authorized persons having access to the Confidential
* Information shall observe and perform under this nondisclosure covenant.
* You agree to immediately notify Serena of any unauthorized access to or
* possession of the Confidential Information.
*
* 7. AFFILIATES. Serena as used herein shall refer to Serena Software,
* Inc. and its affiliates. An entity shall be considered to be an
* affiliate of Serena if it is an entity that controls, is controlled by,
* or is under common control with Serena.
*
* 8. GENERAL. Title and full ownership rights to the Sample Code,
* including any derivative works shall remain with Serena. If a court of
* competent jurisdiction holds any provision of this agreement illegal or
* otherwise unenforceable, that provision shall be severed and the
* remainder of the agreement shall remain in full force and effect.
* ===========================================================================
*/
/*
* This experimental plugin extends Hudson support for Dimensions SCM repositories
*
* @author Tim Payne
*
*/
// Package name
package hudson.plugins.dimensionsscm;
// Dimensions imports
import hudson.plugins.dimensionsscm.Logger;
import hudson.plugins.dimensionsscm.GenericCmdTask;
// Hudson imports
import hudson.Util;
import hudson.FilePath;
import hudson.FilePath.FileCallable;
import hudson.model.Node;
import hudson.model.Computer;
import hudson.model.Hudson.MasterComputer;
import hudson.remoting.Callable;
import hudson.remoting.DelegatingCallable;
import hudson.remoting.Channel;
import hudson.remoting.VirtualChannel;
import hudson.model.TaskListener;
// General imports
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringReader;
import java.lang.Exception;
import java.util.Calendar;
/**
* Class implementation of the checkout process.
*/
public class CheckOutCmdTask extends GenericCmdTask implements FileCallable<Boolean> {
private boolean bFreshBuild = false;
private boolean isDelete = false;
private boolean isRevert = false;
private boolean isForce = false;
private boolean isExpand = false;
private boolean isNoMetadata = false;
private String projectId = "";
private String baseline = null;
private String requests = null;
private String permissions = null;
private String[] folders;
/**
* Utility routine to create command file for dmcli
*
* @param String
* @param String
* @param File
* @return File
* @throws IOException
*/
private File createCmdFile(final String reqId, final String projDir, final File area)
throws IOException {
Calendar nowDateCal = Calendar.getInstance();
File logFile = new File("a");
FileWriter logFileWriter = null;
PrintWriter fmtWriter = null;
File tmpFile = null;
try {
tmpFile = logFile.createTempFile("dmCm"+nowDateCal.getTimeInMillis(),null,null);
logFileWriter = new FileWriter(tmpFile);
fmtWriter = new PrintWriter(logFileWriter,true);
String coCmd = "UPDATE /BRIEF ";
if (version == 10) {
coCmd = "DOWNLOAD ";
if (requests != null) {
coCmd = "FCDI ";
}
}
String cmd = coCmd;
if (reqId != null && version == 10) {
cmd += reqId;
}
if (projDir != null && !projDir.equals("\\") && !projDir.equals("/") && requests == null) {
cmd += "/DIR=\"" + projDir + "\"";
}
if (requests != null && version != 10) {
if (requests.indexOf(",")==0) {
cmd += "/CHANGE_DOC_IDS=(\"" + requests + "\") ";
} else {
cmd += "/CHANGE_DOC_IDS=("+ requests +") ";
}
cmd += "/WORKSET=\"" + projectId + "\" ";
}
else if (baseline != null) {
cmd += "/BASELINE=\"" + baseline + "\"";
} else {
cmd += "/WORKSET=\"" + projectId + "\" ";
}
cmd += "/USER_DIR=\"" + area.getAbsolutePath() + "\" ";
if (isRevert)
cmd += " /OVERWRITE";
if (isExpand)
cmd += " /EXPAND";
if (isNoMetadata)
cmd += " /NOMETADATA";
if (permissions != null && permissions.length() > 0) {
if (!permissions.equals("DEFAULT") && reqId == null) {
cmd += "/PERMS="+permissions;
}
}
fmtWriter.println(cmd);
fmtWriter.flush();
} catch (Exception e) {
throw new IOException("Unable to write command log - " + e.getMessage());
} finally {
fmtWriter.close();
}
return tmpFile;
}
/*
* Default constructor
*/
public CheckOutCmdTask(String userName, String passwd,
String database, String server,
String projectId, String baselineId,
String requestId, boolean isDelete,
boolean isRevert, boolean isForce,
boolean isExpand, boolean isNoMetadata,
boolean freshBuild, String[] folders,
int version, String permissions,
FilePath workspace,
TaskListener listener) {
this.workspace = workspace;
this.listener = listener;
// Server details
this.userName = userName;
this.passwd = passwd;
this.database = database;
this.server = server;
this.version = version;
// Config details
this.isDelete = isDelete;
this.projectId = projectId;
this.isRevert = isRevert;
this.isForce = isForce;
this.isExpand = isExpand;
this.isNoMetadata = isNoMetadata;
this.folders = folders;
this.requests = requestId;
this.baseline = baselineId;
this.permissions = permissions;
// Build details
this.bFreshBuild = freshBuild;
}
/*
* Process the checkout
*
* @param File
* @param File
* @param File
* @return boolean
* @throws IOException
*/
public Boolean execute(final File exe, final File param, final File area)
throws IOException {
FilePath wa = new FilePath(area);
boolean bRet = true;
// Emulate SVN plugin
// - if workspace exists and it is not managed by this project, blow it away
//
try {
if (bFreshBuild) {
if (listener.getLogger() != null) {
listener.getLogger().println("[DIMENSIONS] Checking out a fresh workspace because this project has not been built before...");
listener.getLogger().flush();
}
}
if (wa.exists() && (isDelete || bFreshBuild)) {
Logger.Debug("Deleting '" + wa.toURI() + "'...");
listener.getLogger().println("[DIMENSIONS] Removing '" + wa.toURI() + "'...");
listener.getLogger().flush();
wa.deleteContents();
}
if (baseline != null) {
baseline = baseline.trim();
baseline = baseline.toUpperCase();
}
if (requests != null) {
requests = requests.replaceAll(" ","");
requests = requests.toUpperCase();
}
String cmdLog = null;
if (baseline != null && baseline.length() == 0)
baseline = null;
if (requests != null && requests.length() == 0)
requests = null;
if (listener.getLogger() != null) {
if (requests != null)
listener.getLogger().println("[DIMENSIONS] Checking out request(s) \"" + requests + "\" - ignoring project folders...");
else if (baseline != null)
listener.getLogger().println("[DIMENSIONS] Checking out baseline \"" + baseline + "\"...");
else
listener.getLogger().println("[DIMENSIONS] Checking out project \"" + projectId + "\"...");
listener.getLogger().flush();
}
if (version == 10 && requests != null) {
String[] requestsProcess = requests.split(",");
if (requestsProcess.length == 0) {
requestsProcess[0] = requests;
}
listener.getLogger().println("[DIMENSIONS] Defaulting to read-only permissions as this is the only available mode...");
for (int xx=0;xx<requestsProcess.length; xx++) {
if (!bRet)
break;
String folderN = "/";
String reqId = requestsProcess[xx];
File fileName = new File(folderN);
FilePath projectDir = new FilePath(fileName);
String projDir = (projectDir!=null) ? projectDir.getRemote() : null;
File cmdFile = createCmdFile(reqId,projDir,area);
if (cmdFile == null) {
listener.getLogger().println("[DIMENSIONS] Error: Cannot create command file for Dimensions login.");
param.delete();
return false;
}
String[] cmd = new String[5];
cmd[0] = exe.getAbsolutePath();
cmd[1] = "-param";
cmd[2] = param.getAbsolutePath();
cmd[3] = "cmd";
cmd[4] = cmdFile.getAbsolutePath();
SCMLauncher proc = new SCMLauncher(cmd,listener,wa);
bRet = proc.execute();
String outputStr = proc.getResults();
cmdFile.delete();
if (bRet) {
// Check if any conflicts were identified
int confl = outputStr.indexOf("C\t");
if (confl > 0)
bRet = false;
}
if (cmdLog==null)
cmdLog = "\n";
cmdLog += outputStr;
cmdLog += "\n";
if (!bRet && isForce)
bRet = true;
}
} else {
// Iterate through the project folders and process them in Dimensions
for (int ii=0;ii<folders.length; ii++) {
if (!bRet)
break;
String folderN = folders[ii];
File fileName = new File(folderN);
FilePath projectDir = new FilePath(fileName);
String projDir = (projectDir!=null) ? projectDir.getRemote() : null;
File cmdFile = createCmdFile(null,projDir,area);
if (cmdFile == null) {
listener.getLogger().println("[DIMENSIONS] Error: Cannot create command file for Dimensions login.");
param.delete();
return false;
}
if (requests == null) {
listener.getLogger().println("[DIMENSIONS] Checking out directory '"+((projDir!=null) ? projDir : "/")+"'...");
listener.getLogger().flush();
}
String[] cmd = new String[5];
cmd[0] = exe.getAbsolutePath();
cmd[1] = "-param";
cmd[2] = param.getAbsolutePath();
cmd[3] = "cmd";
cmd[4] = cmdFile.getAbsolutePath();
SCMLauncher proc = new SCMLauncher(cmd,listener,wa);
bRet = proc.execute();
String outputStr = proc.getResults();
cmdFile.delete();
if (bRet) {
// Check if any conflicts were identified
int confl = outputStr.indexOf("C\t");
if (confl > 0)
bRet = false;
}
if (cmdLog==null)
cmdLog = "\n";
cmdLog += outputStr;
cmdLog += "\n";
if (!bRet && isForce)
bRet = true;
if (requests != null)
break;
}
}
param.delete();
if (cmdLog.length() > 0 && listener.getLogger() != null) {
listener.getLogger().println("[DIMENSIONS] (Note: Dimensions command output was - ");
cmdLog = cmdLog.replaceAll("\n\n","\n");
listener.getLogger().println(cmdLog.replaceAll("\n","\n[DIMENSIONS] ") + ")");
listener.getLogger().flush();
}
if (!bRet) {
listener.getLogger().println("[DIMENSIONS] ==========================================================");
listener.getLogger().println("[DIMENSIONS] The Dimensions checkout command returned a failure status.");
listener.getLogger().println("[DIMENSIONS] Please review the command output and correct any issues");
listener.getLogger().println("[DIMENSIONS] that may have been detected.");
listener.getLogger().println("[DIMENSIONS] ==========================================================");
listener.getLogger().flush();
}
return bRet;
} catch (Exception e) {
String errMsg = e.getMessage();
param.delete();
if (errMsg == null) {
errMsg = "An unknown error occurred. Please try the operation again.";
}
listener.fatalError("Unable to run checkout callout - " + errMsg);
// e.printStackTrace();
//throw new IOException("Unable to run checkout callout - " + e.getMessage());
return false;
}
}
}