/* Copyright 2004-2014 Jim Voris * * 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.qumasoft.qvcslib; import com.qumasoft.qvcslib.commandargs.GetRevisionCommandArgs; import com.qumasoft.qvcslib.commandargs.LockRevisionCommandArgs; import com.qumasoft.qvcslib.commandargs.LabelRevisionCommandArgs; import com.qumasoft.qvcslib.commandargs.CheckInCommandArgs; import com.qumasoft.qvcslib.commandargs.UnlockRevisionCommandArgs; import com.qumasoft.qvcslib.commandargs.CheckOutCommandArgs; import com.qumasoft.qvcslib.requestdata.ClientRequestListClientProjectsData; import com.qumasoft.qvcslib.requestdata.ClientRequestMoveFileData; import com.qumasoft.qvcslib.requestdata.ClientRequestRenameData; import com.qumasoft.qvcslib.requestdata.ClientRequestSetIsObsoleteData; import com.qumasoft.qvcslib.response.ServerResponseChangePassword; import com.qumasoft.qvcslib.response.ServerResponseInterface; import com.qumasoft.qvcslib.response.ServerResponseListProjects; import com.qumasoft.qvcslib.response.ServerResponseLogin; import com.qumasoft.qvcslib.response.ServerResponseMessage; import com.qumasoft.qvcslib.response.ServerResponseProjectControl; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.Logger; import javax.swing.event.ChangeListener; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; /** * QVCS ant task. A custom ant task for performing QVCS tasks from within an ant script. This custom ant task supports the following operations: <ul> * <li>get</li> * <li>label</li> * <li>checkin</li> * <li>checkout</li> * <li>lock</li> * <li>unlock</li> * <li>move</li> * <li>rename</li> * <li>delete</li> * <li>report</li> * </ul> * * @author Jim Voris */ public final class QVCSAntTask extends org.apache.tools.ant.Task implements ChangeListener, PasswordChangeListenerInterface, TransportProxyListenerInterface { // Create our logger object private static final Logger LOGGER = Logger.getLogger("com.qumasoft.qvcslib"); // The operations that we support. static final String OPERATION_GET = "get"; static final String OPERATION_LABEL = "label"; static final String OPERATION_CHECKIN = "checkin"; static final String OPERATION_CHECKOUT = "checkout"; static final String OPERATION_LOCK = "lock"; static final String OPERATION_UNLOCK = "unlock"; static final String OPERATION_MOVE = "move"; static final String OPERATION_RENAME = "rename"; static final String OPERATION_DELETE = "delete"; static final String OPERATION_REPORT = "report"; private static final String SKIPPING = "Skipping ["; private static final String BRACKET_IN = "] in ["; private static final long ONE_HUNDRED_MILLISECONDS = 100; private static final long ONE_SECOND = 1_000; private static final long TEN_SECONDS = 10_000; private String pendingPassword; private final AtomicReference<String> passwordResponse = new AtomicReference<>(QVCSConstants.QVCS_NO); private TransportProxyInterface transportProxy = null; private KeywordManagerInterface keywordManager = null; private String[] serverProjectNames = null; private Properties[] projectProperties = null; private RemoteProjectProperties remoteProjectProperties = null; private final Object classSyncObject = new Object(); private final Map<String, Object> appendedPathMap = Collections.synchronizedMap(new HashMap<String, Object>()); private final Map<String, Object> prospectiveAppendedPathCollection = Collections.synchronizedMap(new HashMap<String, Object>()); private int operationCount = 0; private boolean overWriteFlag; private boolean duplicateLabelFlag; private boolean reuseLabelFlag; private boolean floatingLabelFlag; private boolean recurseFlag = true; private String userDirectory; private String serverName; private String userName; private String password; private String projectName; private String viewName; private String appendedPath; private String workfileLocation; private String label; private String duplicateLabel; private String operation; private String fileName; private String fileExtension; private String moveToAppendedPath; private String renameToFileName; private String checkInComment; private String reportFilesWithStatus; /** * Set the overwrite flag. * @param flag the overwrite flag. */ public void setOverWriteFlag(boolean flag) { this.overWriteFlag = flag; } /** * Set the duplicate label flag. * @param flag the duplicate label flag. */ public void setDuplicateLabelFlag(boolean flag) { this.duplicateLabelFlag = flag; } /** * Set the duplicate label value. * @param arg the duplicate label value. */ public void setDuplicateLabel(String arg) { this.duplicateLabel = arg; } /** * Set the reuse label flag. * @param flag the reuse label flag. */ public void setReuseLabelFlag(boolean flag) { this.reuseLabelFlag = flag; } /** * Set the floating label flag. * @param flag the floating label flag. */ public void setFloatingLabelFlag(boolean flag) { this.floatingLabelFlag = flag; } /** * Set the recurse flag. * @param flag the recurse flag. */ public void setRecurseFlag(boolean flag) { this.recurseFlag = flag; } /** * Set the user directory. * @param directory the user directory. */ public void setUserDirectory(String directory) { this.userDirectory = directory; } /** * Set the server name. * @param server the server name. */ public void setServerName(String server) { this.serverName = server; } /** * Set the user name. * @param user the user name. */ public void setUserName(String user) { this.userName = user; } /** * Set the password. * @param arg the password. */ public void setPassword(String arg) { this.password = arg; } /** * Set the project name. * @param project the project name. */ public void setProjectName(String project) { this.projectName = project; } /** * Set the view name. * @param view the view name. */ public void setViewName(String view) { this.viewName = view; } /** * Set the appended path. * @param path the appended path. */ public void setAppendedPath(String path) { this.appendedPath = path; } /** * Set the workfile location. * @param location the workfile location. */ public void setWorkfileLocation(String location) { this.workfileLocation = location; } /** * Set the label string. * @param arg the label string. */ public void setLabel(String arg) { this.label = arg; } /** * Set the operation to perform. This must be one of the following: * <ul> * <li>get</li> * <li>label</li> * <li>checkin</li> * <li>checkout</li> * <li>lock</li> * <li>unlock</li> * <li>move</li> * <li>rename</li> * <li>delete</li> * <li>report</li> * </ul> * @param op the operation to perform. */ public void setOperation(String op) { this.operation = op; } /** * Set the filename. * @param arg the filename. */ public void setFileName(String arg) { this.fileName = arg; } /** * Set the move to appended path (for move operations). * @param path the move to appended path */ public void setMoveToAppendedPath(String path) { this.moveToAppendedPath = path; } /** * Set the rename to filename. * @param arg the rename to filename. */ public void setRenameToFileName(String arg) { this.renameToFileName = arg; } /** * Set the file extension. * @param arg the file extension. */ public void setFileExtension(String arg) { this.fileExtension = arg; } /** * Set the checkin comment. * @param comment the checkin comment. */ public void setCheckInComment(String comment) { this.checkInComment = comment; } /** * Set the status for reporting files with the given status. * @param status the status for reporting files with the given status. */ public void setReportFilesWithStatus(String status) { this.reportFilesWithStatus = status; } private boolean login() { boolean resultFlag = false; ServerProperties serverProperties = new ServerProperties(serverName); // The type of transport we'll use... TransportProxyType transportType = serverProperties.getClientTransport(); // The port we'll connect on... int port = serverProperties.getClientPort(); // Hash the password... byte[] hashedPassword = Utility.getInstance().hashPassword(password); // Initialize the workfile digest manager WorkfileDigestManager.getInstance().initialize(); // Initialize the checkout comment manager. CheckOutCommentManager.getInstance().initialize(); // Initialize the label manager String systemUserName = System.getProperty("user.name"); LabelManager.setUserName(systemUserName); LabelManager.getInstance().initialize(); // And force the login to the transport... TransportProxyFactory.getInstance().addChangeListener(this); TransportProxyFactory.getInstance().addChangedPasswordListener(this); ServerManager.getServerManager().addChangeListener(this); synchronized (passwordResponse) { try { transportProxy = TransportProxyFactory.getInstance().getTransportProxy(transportType, serverProperties, port, userName, hashedPassword, this, null); // Wait 10 seconds for a response. passwordResponse.wait(TEN_SECONDS); } catch (InterruptedException e) { log(Utility.expandStackTraceToString(e)); } } if (passwordResponse.get().equals(QVCSConstants.QVCS_YES)) { String msg = "Successful login in to server as user: '" + userName + "'"; log(msg, Project.MSG_VERBOSE); resultFlag = true; } else { log("Failed to login to server", Project.MSG_WARN); } return resultFlag; } private void initLoggingProperties() { try { String logConfigFile = userDirectory + File.separator + "antLogging.properties"; System.setProperty("java.util.logging.config.file", logConfigFile); LogManager.getLogManager().readConfiguration(); } catch (IOException | SecurityException e) { log("Caught exception: " + e.getClass().toString() + " : " + e.getLocalizedMessage()); log(Utility.expandStackTraceToString(e)); } } /** * Execute the qvcs ant task. * * @throws org.apache.tools.ant.BuildException */ @Override public void execute() { boolean loggedInFlag = false; operationCount = 0; try { System.setProperty("user.dir", userDirectory); // Initialize the logger. initLoggingProperties(); // Make sure they defined the needed properties. validateTaskProperties(); // Report what properties will be used. reportTaskProperties(); // Login to the server. loggedInFlag = login(); if (!loggedInFlag) { throw new BuildException("Failed to login to server."); } // Wait for the project list from the server. log("Getting project list from server..."); waitForProjectList(); // Get the files we may be able to work on from the server. DirectoryManagerFactory.getInstance().setServerUsername(serverName, userName); DirectoryManagerFactory.getInstance().setServerPassword(serverName, password); // Create a canonical representation for the workfile location. createCanonicalWorkfileLocation(); // We need to get a directory manager for the root of the project so we'll get the additional // segments that exist for this project. We'll then have to use those to figure out the // set of directory managers that we need to build in order to satisfy the users request. log("Getting directory list from server..."); createDirectoryManager(""); log("Getting file list from server..."); createDirectoryManagerCollection(); log("Performing requested operation..."); performRequestedOperation(); } catch (QVCSException | BuildException e) { String msg = "Caught exception: " + e.getClass().getName() + " exception: " + e.getLocalizedMessage(); log(msg, Project.MSG_WARN); log(Utility.expandStackTraceToString(e)); LOGGER.log(Level.WARNING, Utility.expandStackTraceToString(e)); } finally { if (loggedInFlag) { try { Thread.sleep(ONE_SECOND); } catch (InterruptedException e) { log(Utility.expandStackTraceToString(e)); } logout(); log("Logged off server"); // Write the stores that we may have changed. WorkfileDigestManager.getInstance().writeStore(); CheckOutCommentManager.getInstance().writeStore(); try { Thread.sleep(ONE_SECOND); } catch (InterruptedException e) { log(Utility.expandStackTraceToString(e)); } } } } private void createCanonicalWorkfileLocation() throws QVCSException { String basedir = getProject().getProperty("basedir"); log("Basedir: '" + basedir + "'"); String localWorkfileLocation = workfileLocation; // Create a canonical name for the workfile path if we're doing a get if (workfileLocation != null) { // This only needs to be done for relative pathnames. byte[] workfileLocationBytes = workfileLocation.getBytes(); if ((workfileLocationBytes[0] != File.separatorChar) && (workfileLocationBytes[1] != ':')) { localWorkfileLocation = basedir + File.separator + workfileLocation; } try { File workfile = new File(localWorkfileLocation); String canonicalFilename = workfile.getCanonicalPath(); workfileLocation = canonicalFilename; log("Workfile canonical location: '" + workfileLocation + "'"); } catch (java.io.IOException e) { throw new QVCSException(e.getLocalizedMessage()); } } } private void logout() { transportProxy.close(); } private void waitForProjectList() { ClientRequestListClientProjectsData projectsData = new ClientRequestListClientProjectsData(); projectsData.setServerName(serverName); synchronized (classSyncObject) { try { transportProxy.write(projectsData); classSyncObject.wait(); } catch (InterruptedException e) { log(Utility.expandStackTraceToString(e)); } } } private AbstractProjectProperties getProjectProperties() { if (remoteProjectProperties == null) { Properties remoteProperties = null; for (int i = 0; i < serverProjectNames.length; i++) { if (serverProjectNames[i].equals(projectName)) { remoteProperties = projectProperties[i]; String msg = "Matched project: [" + serverProjectNames[i] + "]; Index: " + i; log(msg, Project.MSG_VERBOSE); break; } } remoteProjectProperties = new RemoteProjectProperties(projectName, remoteProperties); } return remoteProjectProperties; } @Override public void stateChanged(javax.swing.event.ChangeEvent e) { try { String msg = "Change Event: [" + e.getSource().getClass().getName() + "]"; log(msg, Project.MSG_VERBOSE); Object source = e.getSource(); if (source instanceof ServerResponseListProjects) { msg = "Received list of projects for server: [" + serverName + "]"; log(msg, Project.MSG_VERBOSE); synchronized (classSyncObject) { ServerResponseListProjects projectList = (ServerResponseListProjects) source; String[] projectNames = projectList.getProjectList(); projectProperties = projectList.getPropertiesList(); for (String projectName1 : projectNames) { log(projectName1, Project.MSG_VERBOSE); } serverProjectNames = projectNames; classSyncObject.notifyAll(); } } else if (source instanceof ServerResponseProjectControl) { ServerResponseProjectControl projectControl = (ServerResponseProjectControl) source; if (projectControl.getAddFlag()) { String[] segments = projectControl.getDirectorySegments(); // Create appended path for this prospective directory manager String localAppendedPath = Utility.createAppendedPathFromSegments(segments); msg = "AppendedPath: [" + localAppendedPath + "]"; log(msg, Project.MSG_VERBOSE); // Add the appendedPath to the collection of appended paths for prospective directory // managers. We'll create a sync object that we can use to synchronize on that // directory manager, if we wind up needing to create a directory manager. // (At this point we are simply creating the entire set of appended paths // that exist for a given project... we will have to use some subset of that // collection, based on the appended path of the user's request). prospectiveAppendedPathCollection.put(localAppendedPath, new Object()); } } else if (source instanceof ArchiveDirManagerProxy) { ArchiveDirManagerProxy archiveDirManager = (ArchiveDirManagerProxy) source; String localAppendedPath = archiveDirManager.getAppendedPath(); log("Directory: " + localAppendedPath, Project.MSG_VERBOSE); Object syncObject = null; while (syncObject == null) { syncObject = appendedPathMap.get(localAppendedPath); if (syncObject == null) { // This can happen if we get the response from the server before // we've had a chance to populate the appendPathMap with an // entry for the given directory. log("Did not find synchronization object for: [" + localAppendedPath + "]. Waiting for server response..."); LOGGER.log(Level.WARNING, "Did not find synchronization object for: [" + localAppendedPath + "]. Waiting for server response..."); Thread.sleep(ONE_HUNDRED_MILLISECONDS); } } if (syncObject != null) { synchronized (syncObject) { syncObject.notifyAll(); } } } else { msg = "stateChanged received unexpected object of type " + source.getClass().getName(); log(msg, Project.MSG_WARN); LOGGER.log(Level.WARNING, msg); } } catch (InterruptedException ex) { LOGGER.log(Level.WARNING, Utility.expandStackTraceToString(ex)); } } @Override public String getPendingPassword(String name) { return pendingPassword; } /** * {@inheritDoc} */ @Override public void notifyLoginResult(ServerResponseLogin response) { synchronized (passwordResponse) { if (response.getLoginResult()) { if (response.getVersionsMatchFlag()) { passwordResponse.set(QVCSConstants.QVCS_YES); } else { log("Client jar file is out of date.", Project.MSG_WARN); passwordResponse.set(QVCSConstants.QVCS_NO); } } else { passwordResponse.set(QVCSConstants.QVCS_NO); } passwordResponse.notifyAll(); } } /** * {@inheritDoc} */ @Override public void notifyUpdateComplete() { } /** * {@inheritDoc} */ @Override public void notifyPasswordChange(ServerResponseChangePassword response) { } @Override public void savePendingPassword(String server, String arg) { pendingPassword = arg; } /** * Create the collection of directory managers that this execute request will use. The collection is the set of directories that are part of the directory tree corresponding to * the user-provided appended path. */ private void createDirectoryManagerCollection() throws QVCSException { Set appendedPathsSet = prospectiveAppendedPathCollection.keySet(); Iterator it = appendedPathsSet.iterator(); while (it.hasNext()) { String localAppendedPath = (String) it.next(); log("createDirectoryManagerCollection appended path: [" + localAppendedPath + "]", Project.MSG_VERBOSE); if (appendedPath.length() > 0) { if (localAppendedPath.startsWith(appendedPath)) { if (recurseFlag) { createDirectoryManager(localAppendedPath); } else { // We are not recursing directories, so we only need the // one directory. if (0 == localAppendedPath.compareTo(appendedPath)) { createDirectoryManager(localAppendedPath); break; } } } } else { // We already created the directory manager for the project // root directory.... don't create it again. if (localAppendedPath.length() > 0) { if (recurseFlag) { createDirectoryManager(localAppendedPath); } else { break; } } } } } private void createDirectoryManager(String path) throws QVCSException { String workfileAppendedPath; if (path.length() == 0) { workfileAppendedPath = workfileLocation; } else { String appendedPathSuffix = path.substring(path.length()); workfileAppendedPath = workfileLocation + File.separator + appendedPathSuffix; } Object syncObject = new Object(); synchronized (syncObject) { try { DirectoryManagerInterface directoryManager = DirectoryManagerFactory.getInstance().lookupDirectoryManager(serverName, projectName, viewName, path, QVCSConstants.QVCS_REMOTE_PROJECT_TYPE); if (directoryManager == null) { // Save the sync object for future use... appendedPathMap.put(path, syncObject); // Lookup or create the directory manager. DirectoryCoordinate directoryCoordinate = new DirectoryCoordinate(projectName, viewName, path); directoryManager = DirectoryManagerFactory.getInstance().getDirectoryManager(serverName, directoryCoordinate, QVCSConstants.QVCS_REMOTE_PROJECT_TYPE, getProjectProperties(), workfileAppendedPath, this, true); // Wait for the response from the server. String msg = "Waiting for server response for appended path: [" + path + "]"; log(msg, Project.MSG_VERBOSE); syncObject.wait(); msg = "Received server response for appended path: [" + directoryManager.getAppendedPath() + "]"; log(msg, Project.MSG_VERBOSE); } else { String msg = "Found existing directory manager for: [" + path + "]"; log(msg); } } catch (InterruptedException e) { LOGGER.log(Level.WARNING, Utility.expandStackTraceToString(e)); } } } /** * This is where we perform the version control operation on the list of files we got from the server. */ private void performRequestedOperation() { MyTransactionProgressListener myTransactionProgressListener = null; try { // Get rid of the project root directory manager's map entry if we can. if (operation.equals(OPERATION_MOVE)) { if (appendedPath.length() > 0 && moveToAppendedPath.length() > 0) { // We don't need the directory manager for the project root since that directory manager won't be used. appendedPathMap.remove(""); } } else { if (appendedPath.length() > 0) { // We don't need the directory manager for the project root since that directory manager won't be used. appendedPathMap.remove(""); } } // Set up a listener to listen for the end to the transaction that we wrap around all this work // so we can use that listener to notify us when the the server has completed its work. Object transactionCompleteSyncObject = new Object(); myTransactionProgressListener = new MyTransactionProgressListener(transactionCompleteSyncObject); ClientTransactionManager.getInstance().addTransactionInProgressListener(myTransactionProgressListener); int transactionId = ClientTransactionManager.getInstance().sendBeginTransaction(transportProxy); Set appendedPathsSet = appendedPathMap.keySet(); Iterator it = appendedPathsSet.iterator(); while (it.hasNext()) { String localAppendedPath = (String) it.next(); String msg = "Performing [" + operation + "] for directory: [" + localAppendedPath + "]"; log(msg, Project.MSG_VERBOSE); // Lookup up the directory manager for this directory. DirectoryManagerInterface directoryManager = DirectoryManagerFactory.getInstance().lookupDirectoryManager(serverName, projectName, viewName, localAppendedPath, QVCSConstants.QVCS_REMOTE_PROJECT_TYPE); directoryManager.mergeManagers(); log("Workfile directory for appended path of [" + localAppendedPath + "]:" + directoryManager.getWorkfileDirectoryManager().getWorkfileDirectory(), Project.MSG_VERBOSE); // Iterate over the collection of archives in this directory. Collection mergedInfoCollection = directoryManager.getMergedInfoCollection(); Iterator mergedInfoIterator = mergedInfoCollection.iterator(); while (mergedInfoIterator.hasNext()) { MergedInfoInterface mergedInfo = (MergedInfoInterface) mergedInfoIterator.next(); if (mergedInfo.getArchiveInfo() != null) { String shortWorkfileName = mergedInfo.getArchiveInfo().getShortWorkfileName(); String lowerCaseShortWorkfileName = shortWorkfileName.toLowerCase(); // Filter out extensions... if (0 != fileExtension.compareTo("*")) { if (fileExtension.length() == 0) { // File should have no extension... if (shortWorkfileName.contains(".")) { msg = SKIPPING + shortWorkfileName + "] because it has a file extension but the file extension attribute is accepting files without a file extension."; log(msg, Project.MSG_VERBOSE); continue; } } else if (!lowerCaseShortWorkfileName.endsWith(fileExtension)) { msg = SKIPPING + shortWorkfileName + "] because it doesn't match the extension: " + fileExtension; log(msg, Project.MSG_VERBOSE); continue; } } // Skip files that do not match the filename (if one is defined). if (fileName.length() > 0 && (0 != fileName.compareTo(shortWorkfileName))) { msg = SKIPPING + shortWorkfileName + "] because it doesn't match the file name: " + fileName; log(msg, Project.MSG_VERBOSE); continue; } msg = "Operating on: [" + shortWorkfileName + "]"; log(msg, Project.MSG_VERBOSE); switch (operation) { case OPERATION_GET: requestGetOperation(mergedInfo); break; case OPERATION_LABEL: requestLabelOperation(mergedInfo); break; case OPERATION_CHECKIN: requestCheckInOperation(mergedInfo); break; case OPERATION_CHECKOUT: requestCheckOutOperation(mergedInfo); break; case OPERATION_LOCK: requestLockOperation(mergedInfo); break; case OPERATION_UNLOCK: requestUnLockOperation(mergedInfo); break; case OPERATION_MOVE: requestMoveOperation(mergedInfo); break; case OPERATION_RENAME: requestRenameOperation(mergedInfo); break; case OPERATION_DELETE: requestDeleteOperation(mergedInfo); break; case OPERATION_REPORT: requestReportOperation(mergedInfo); break; default: msg = "Unsupported operation type: [" + operation + "]"; log(msg, Project.MSG_WARN); break; } } } } synchronized (transactionCompleteSyncObject) { // Send the end transaction... ClientTransactionManager.getInstance().sendEndTransaction(transportProxy, transactionId); // Wait for the end transaction to arrive. try { transactionCompleteSyncObject.wait(); } catch (InterruptedException e) { log(Utility.expandStackTraceToString(e)); } } } catch (QVCSException e) { log("Caught exception while performing operation: " + e.getClass().toString() + ": " + e.getLocalizedMessage(), Project.MSG_WARN); log(Utility.expandStackTraceToString(e)); } finally { log("Performed [" + operationCount + "] operations"); if (myTransactionProgressListener != null) { ClientTransactionManager.getInstance().removeTransactionInProgressListener(myTransactionProgressListener); } } } private boolean requestGetOperation(MergedInfoInterface mergedInfo) { // Use flag to indicate whether the operation was requested. boolean flag = false; String appendedPathSuffix = mergedInfo.getArchiveDirManager().getAppendedPath().substring(appendedPath.length()); String workfileAppendedPath = workfileLocation + File.separator + appendedPathSuffix; String fullWorkfileName = workfileAppendedPath + File.separator + mergedInfo.getShortWorkfileName(); // Figure out the command line arguments for this file. GetRevisionCommandArgs commandArgs = new GetRevisionCommandArgs(); commandArgs.setFullWorkfileName(fullWorkfileName); commandArgs.setLabel(label); commandArgs.setOutputFileName(fullWorkfileName); if (overWriteFlag) { commandArgs.setOverwriteBehavior(Utility.OverwriteBehavior.REPLACE_WRITABLE_FILE); } else { commandArgs.setOverwriteBehavior(Utility.OverwriteBehavior.DO_NOT_REPLACE_WRITABLE_FILE); } if (label.length() == 0) { commandArgs.setRevisionString(QVCSConstants.QVCS_DEFAULT_REVISION); commandArgs.setLabel(null); } else { commandArgs.setByLabelFlag(true); } commandArgs.setShortWorkfileName(mergedInfo.getShortWorkfileName()); commandArgs.setUserName(mergedInfo.getUserName()); try { if (mergedInfo.getRevision(commandArgs, fullWorkfileName)) { operationCount++; flag = true; } } catch (QVCSException e) { log(e.getLocalizedMessage()); } return flag; } private boolean requestLabelOperation(MergedInfoInterface mergedInfo) { // Use flag to indicate whether the operation was requested. boolean flag = false; // Figure out the command line arguments for this file. LabelRevisionCommandArgs commandArgs = new LabelRevisionCommandArgs(); String revisionString = mergedInfo.getDefaultRevisionString(); // If we are duplicating a label, then we cannot specify a revision string. if (duplicateLabelFlag) { revisionString = null; } commandArgs.setRevisionString(revisionString); commandArgs.setUserName(mergedInfo.getUserName()); commandArgs.setShortWorkfileName(mergedInfo.getShortWorkfileName()); commandArgs.setLabelString(label); commandArgs.setFloatingFlag(floatingLabelFlag); commandArgs.setReuseLabelFlag(reuseLabelFlag); commandArgs.setDuplicateFlag(duplicateLabelFlag); commandArgs.setDuplicateLabelString(duplicateLabel); try { if (mergedInfo.labelRevision(commandArgs)) { operationCount++; flag = true; } } catch (QVCSException e) { log(e.getLocalizedMessage()); } return flag; } private boolean requestCheckInOperation(MergedInfoInterface mergedInfo) { log("Check in operation; evaluating : [" + mergedInfo.getFullWorkfileName() + "] has status of: " + mergedInfo.getStatusString(), Project.MSG_VERBOSE); // Use flag to indicate whether the operation was requested. boolean flag = false; // If lock checking is enabled, skip any files that are not locked. if (mergedInfo.getAttributes().getIsCheckLock()) { if (mergedInfo.getLockCount() == 0) { log(SKIPPING + mergedInfo.getArchiveInfo().getShortWorkfileName() + BRACKET_IN + mergedInfo.getArchiveDirManager().getAppendedPath() + "] directory because file is not locked.", Project.MSG_VERBOSE); return false; } } // Skip any files that have an status that is unacceptable. The only statuses that // we will checkin are 'Current' (which will result in an unlocked file), and // 'Your copy changed'. Other status values will result in skipping the file. if ((mergedInfo.getStatusIndex() != MergedInfoInterface.YOUR_COPY_CHANGED_STATUS_INDEX) && (mergedInfo.getStatusIndex() != MergedInfoInterface.CURRENT_STATUS_INDEX)) { if (mergedInfo.getStatusIndex() == MergedInfoInterface.MERGE_REQUIRED_STATUS_INDEX) { log(SKIPPING + mergedInfo.getArchiveInfo().getShortWorkfileName() + BRACKET_IN + mergedInfo.getArchiveDirManager().getAppendedPath() + "] directory because a merge is required."); } else if (mergedInfo.getStatusIndex() == MergedInfoInterface.DIFFERENT_STATUS_INDEX) { log(SKIPPING + mergedInfo.getArchiveInfo().getShortWorkfileName() + BRACKET_IN + mergedInfo.getArchiveDirManager().getAppendedPath() + "] directory because status is 'Different'."); } else if (mergedInfo.getStatusIndex() == MergedInfoInterface.STALE_STATUS_INDEX) { log(SKIPPING + mergedInfo.getArchiveInfo().getShortWorkfileName() + BRACKET_IN + mergedInfo.getArchiveDirManager().getAppendedPath() + "] directory because status is 'Stale'.", Project.MSG_VERBOSE); } else if (mergedInfo.getStatusIndex() == MergedInfoInterface.MISSING_STATUS_INDEX) { log(SKIPPING + mergedInfo.getArchiveInfo().getShortWorkfileName() + BRACKET_IN + mergedInfo.getArchiveDirManager().getAppendedPath() + "] directory because status is 'Missing'.", Project.MSG_VERBOSE); } return false; } // Figure out the command line arguments for this file. CheckInCommandArgs commandArgs = new CheckInCommandArgs(); commandArgs.setUserName(mergedInfo.getUserName()); String lockedRevision; if (mergedInfo.getAttributes().getIsCheckLock()) { assert (0 == mergedInfo.getUserName().compareTo(userName)); lockedRevision = mergedInfo.getLockedRevisionString(mergedInfo.getUserName()); if (lockedRevision == null) { log(SKIPPING + mergedInfo.getArchiveInfo().getShortWorkfileName() + BRACKET_IN + mergedInfo.getArchiveDirManager().getAppendedPath() + "] directory because a file is not locked by [" + userName + "]"); return false; } else { commandArgs.setLockedRevisionString(lockedRevision); } } else { commandArgs.setLockedRevisionString(mergedInfo.getArchiveInfo().getDefaultRevisionString()); } commandArgs.setCheckInComment(checkInComment); // Create a File associated with the File we check in File checkInFile = mergedInfo.getWorkfile(); commandArgs.setInputfileTimeStamp(new Date(checkInFile.lastModified())); commandArgs.setFullWorkfileName(mergedInfo.getFullWorkfileName()); commandArgs.setShortWorkfileName(mergedInfo.getShortWorkfileName()); // Set flags; commandArgs.setLockFlag(false); commandArgs.setForceBranchFlag(false); if (label.length() > 0) { commandArgs.setApplyLabelFlag(true); } else { commandArgs.setApplyLabelFlag(false); } commandArgs.setFloatLabelFlag(floatingLabelFlag); commandArgs.setReuseLabelFlag(reuseLabelFlag); commandArgs.setCreateNewRevisionIfEqual(false); commandArgs.setNoExpandKeywordsFlag(false); commandArgs.setProtectWorkfileFlag(false); // Set some other values commandArgs.setLabel(label); commandArgs.setProjectName(mergedInfo.getProjectName()); // Contract keywords if needed. String checkInFilename = contractKeywords(checkInFile, mergedInfo, commandArgs); // The checkInFilename will be null if we are not able to read it. if (checkInFilename != null) { try { if (mergedInfo.checkInRevision(commandArgs, checkInFilename, false)) { // Remove any checkout comment. if (CheckOutCommentManager.getInstance().commentExists(mergedInfo)) { CheckOutCommentManager.getInstance().removeComment(mergedInfo); } operationCount++; } } catch (QVCSException e) { log(Utility.expandStackTraceToString(e)); flag = false; } } return flag; } private boolean requestCheckOutOperation(MergedInfoInterface mergedInfo) { String appendedPathSuffix = mergedInfo.getArchiveDirManager().getAppendedPath().substring(appendedPath.length()); String workfileAppendedPath = workfileLocation + File.separator + appendedPathSuffix; String fullWorkfileName = workfileAppendedPath + File.separator + mergedInfo.getShortWorkfileName(); log("Check out operation; evaluating : " + mergedInfo.getFullWorkfileName() + " has status of: " + mergedInfo.getStatusString(), Project.MSG_VERBOSE); // Use flag to indicate whether the operation was requested. boolean flag = false; // Skip any files that have not changed, or any files that require a merge. if ((mergedInfo.getStatusIndex() == MergedInfoInterface.DIFFERENT_STATUS_INDEX) || (mergedInfo.getStatusIndex() == MergedInfoInterface.MERGE_REQUIRED_STATUS_INDEX) || (mergedInfo.getStatusIndex() == MergedInfoInterface.NOT_CONTROLLED_STATUS_INDEX) || (mergedInfo.getStatusIndex() == MergedInfoInterface.YOUR_COPY_CHANGED_STATUS_INDEX)) { log(SKIPPING + mergedInfo.getArchiveInfo().getShortWorkfileName() + BRACKET_IN + mergedInfo.getArchiveDirManager().getAppendedPath() + "] directory because of inappropriate file status: [" + mergedInfo.getStatusString() + "]"); return false; } else { log("Proceeding to checkout [" + mergedInfo.getArchiveInfo().getShortWorkfileName() + BRACKET_IN + mergedInfo.getArchiveDirManager().getAppendedPath() + "] directory with current status of: [" + mergedInfo.getStatusString() + "]", Project.MSG_VERBOSE); } // Figure out the command line arguments for this file. CheckOutCommandArgs commandArgs = new CheckOutCommandArgs(); commandArgs.setUserName(userName); if (label.length() == 0) { commandArgs.setRevisionString(QVCSConstants.QVCS_DEFAULT_REVISION); commandArgs.setLabel(null); } else { commandArgs.setRevisionString(null); commandArgs.setLabel(label); } commandArgs.setFullWorkfileName(fullWorkfileName); commandArgs.setOutputFileName(fullWorkfileName); commandArgs.setShortWorkfileName(mergedInfo.getShortWorkfileName()); try { if (mergedInfo.checkOutRevision(commandArgs, fullWorkfileName)) { operationCount++; flag = true; } } catch (QVCSException e) { log(e.getLocalizedMessage()); } return flag; } private boolean requestLockOperation(MergedInfoInterface mergedInfo) { String appendedPathSuffix = mergedInfo.getArchiveDirManager().getAppendedPath().substring(appendedPath.length()); String workfileAppendedPath = workfileLocation + File.separator + appendedPathSuffix; String fullWorkfileName = workfileAppendedPath + File.separator + mergedInfo.getShortWorkfileName(); log("Lock operation; evaluating : [" + mergedInfo.getFullWorkfileName() + "] has status of: [" + mergedInfo.getStatusString() + "]", Project.MSG_VERBOSE); // Use flag to indicate whether the operation was requested. boolean flag = false; // Skip any files cannot be locked. if ((mergedInfo.getStatusIndex() == MergedInfoInterface.NOT_CONTROLLED_STATUS_INDEX) || (mergedInfo.getLockCount() > 0)) { if (mergedInfo.getLockCount() == 0) { log(SKIPPING + mergedInfo.getArchiveInfo().getShortWorkfileName() + BRACKET_IN + mergedInfo.getArchiveDirManager().getAppendedPath() + "] directory because of inappropriate file status: [" + mergedInfo.getStatusString() + "]"); } else { log("Skipping " + mergedInfo.getArchiveInfo().getShortWorkfileName() + " in " + mergedInfo.getArchiveDirManager().getAppendedPath() + " directory because file is already locked."); } return false; } else { log("Proceeding to lock [" + mergedInfo.getArchiveInfo().getShortWorkfileName() + BRACKET_IN + mergedInfo.getArchiveDirManager().getAppendedPath() + "] directory with current status of: [" + mergedInfo.getStatusString() + "]", Project.MSG_VERBOSE); } // Figure out the command line arguments for this file. LockRevisionCommandArgs commandArgs = new LockRevisionCommandArgs(); commandArgs.setUserName(userName); if (label.length() == 0) { commandArgs.setRevisionString(QVCSConstants.QVCS_DEFAULT_REVISION); commandArgs.setLabel(null); } else { commandArgs.setRevisionString(null); commandArgs.setLabel(label); } commandArgs.setFullWorkfileName(fullWorkfileName); commandArgs.setOutputFileName(fullWorkfileName); commandArgs.setShortWorkfileName(mergedInfo.getShortWorkfileName()); try { if (mergedInfo.lockRevision(commandArgs)) { operationCount++; flag = true; } } catch (QVCSException e) { log(e.getLocalizedMessage()); } return flag; } private boolean requestUnLockOperation(MergedInfoInterface mergedInfo) { String appendedPathSuffix = mergedInfo.getArchiveDirManager().getAppendedPath().substring(appendedPath.length()); String workfileAppendedPath = workfileLocation + File.separator + appendedPathSuffix; String fullWorkfileName = workfileAppendedPath + File.separator + mergedInfo.getShortWorkfileName(); log("Unlock operation; evaluating : [" + mergedInfo.getFullWorkfileName() + "] has status of: [" + mergedInfo.getStatusString() + "]", Project.MSG_VERBOSE); // Use flag to indicate whether the operation was requested. boolean flag = false; // Skip any files that cannot be unlocked. if ((mergedInfo.getStatusIndex() == MergedInfoInterface.NOT_CONTROLLED_STATUS_INDEX) || (mergedInfo.getLockCount() == 0)) { if (mergedInfo.getLockCount() == 0) { log(SKIPPING + mergedInfo.getArchiveInfo().getShortWorkfileName() + BRACKET_IN + mergedInfo.getArchiveDirManager().getAppendedPath() + "] directory because file is not locked."); } else { log(SKIPPING + mergedInfo.getArchiveInfo().getShortWorkfileName() + BRACKET_IN + mergedInfo.getArchiveDirManager().getAppendedPath() + "] directory because of inappropriate file status: [" + mergedInfo.getStatusString() + "]"); } return false; } else { log("Proceeding to unlock [" + mergedInfo.getArchiveInfo().getShortWorkfileName() + BRACKET_IN + mergedInfo.getArchiveDirManager().getAppendedPath() + "] directory with current status of: [" + mergedInfo.getStatusString() + "]", Project.MSG_VERBOSE); } // Figure out the command line arguments for this file. UnlockRevisionCommandArgs commandArgs = new UnlockRevisionCommandArgs(); commandArgs.setUserName(userName); commandArgs.setRevisionString(QVCSConstants.QVCS_DEFAULT_REVISION); commandArgs.setShortWorkfileName(mergedInfo.getShortWorkfileName()); commandArgs.setFullWorkfileName(fullWorkfileName); commandArgs.setOutputFileName(fullWorkfileName); commandArgs.setUndoCheckoutBehavior(Utility.UndoCheckoutBehavior.JUST_UNLOCK_ARCHIVE); try { if (mergedInfo.unlockRevision(commandArgs)) { operationCount++; flag = true; } } catch (QVCSException e) { log(e.getLocalizedMessage()); } return flag; } private boolean requestMoveOperation(MergedInfoInterface mergedInfo) { log("Move operation; evaluating : [" + mergedInfo.getFullWorkfileName() + "] has status of: [" + mergedInfo.getStatusString() + "]", Project.MSG_VERBOSE); // Use flag to indicate whether the operation was requested. boolean flag = true; // If lock checking is enabled, skip any files that are locked. if (mergedInfo.getAttributes().getIsCheckLock()) { if (mergedInfo.getLockCount() > 0) { log(SKIPPING + mergedInfo.getArchiveInfo().getShortWorkfileName() + BRACKET_IN + mergedInfo.getArchiveDirManager().getAppendedPath() + "] directory because file is locked.", Project.MSG_INFO); flag = false; } } // Skip any files that have an status that is unacceptable. The only statuses that // we will move are 'Current' or 'Missing'. Other status values will result in skipping the file. int statusIndex = mergedInfo.getStatusIndex(); if ((statusIndex != MergedInfoInterface.CURRENT_STATUS_INDEX) && (statusIndex != MergedInfoInterface.MISSING_STATUS_INDEX)) { log(SKIPPING + mergedInfo.getArchiveInfo().getShortWorkfileName() + BRACKET_IN + mergedInfo.getArchiveDirManager().getAppendedPath() + "] directory because status is not 'Current' or 'Missing'."); flag = false; } if (flag) { ClientRequestMoveFileData clientRequestMoveFileData = new ClientRequestMoveFileData(); clientRequestMoveFileData.setOriginalAppendedPath(appendedPath); clientRequestMoveFileData.setProjectName(projectName); clientRequestMoveFileData.setViewName(viewName); clientRequestMoveFileData.setShortWorkfileName(fileName); clientRequestMoveFileData.setNewAppendedPath(moveToAppendedPath); int transactionID = 0; try { transactionID = ClientTransactionManager.getInstance().sendBeginTransaction(transportProxy); transportProxy.write(clientRequestMoveFileData); } finally { ClientTransactionManager.getInstance().sendEndTransaction(transportProxy, transactionID); } } return flag; } private boolean requestRenameOperation(MergedInfoInterface mergedInfo) { log("Rename operation; evaluating : [" + mergedInfo.getFullWorkfileName() + "] has status of: [" + mergedInfo.getStatusString() + "]", Project.MSG_VERBOSE); // Use flag to indicate whether the operation was requested. boolean flag = true; // If lock checking is enabled, skip any files that are locked. if (mergedInfo.getAttributes().getIsCheckLock()) { if (mergedInfo.getLockCount() > 0) { log(SKIPPING + mergedInfo.getArchiveInfo().getShortWorkfileName() + BRACKET_IN + mergedInfo.getArchiveDirManager().getAppendedPath() + "] directory because file is locked.", Project.MSG_INFO); flag = false; } } // Skip any files that have an status that is unacceptable. The only statuses that // we will rename are 'Current' or 'Missing'. Other status values will result in skipping the file. int statusIndex = mergedInfo.getStatusIndex(); if ((statusIndex != MergedInfoInterface.CURRENT_STATUS_INDEX) && (statusIndex != MergedInfoInterface.MISSING_STATUS_INDEX)) { log(SKIPPING + mergedInfo.getArchiveInfo().getShortWorkfileName() + BRACKET_IN + mergedInfo.getArchiveDirManager().getAppendedPath() + "] directory because status is not 'Current' or 'Missing'."); flag = false; } if (flag) { ClientRequestRenameData clientRequestRenameData = new ClientRequestRenameData(); clientRequestRenameData.setAppendedPath(appendedPath); clientRequestRenameData.setProjectName(projectName); clientRequestRenameData.setViewName(viewName); clientRequestRenameData.setOriginalShortWorkfileName(fileName); clientRequestRenameData.setNewShortWorkfileName(renameToFileName); int transactionID = 0; try { transactionID = ClientTransactionManager.getInstance().sendBeginTransaction(transportProxy); transportProxy.write(clientRequestRenameData); } finally { ClientTransactionManager.getInstance().sendEndTransaction(transportProxy, transactionID); } } return flag; } private boolean requestDeleteOperation(MergedInfoInterface mergedInfo) { log("Delete operation; evaluating : [" + mergedInfo.getFullWorkfileName() + "] has status of: [" + mergedInfo.getStatusString() + "]", Project.MSG_VERBOSE); // Use flag to indicate whether the operation was requested. boolean flag = true; // If lock checking is enabled, skip any files that are locked. if (mergedInfo.getAttributes().getIsCheckLock()) { if (mergedInfo.getLockCount() > 0) { log(SKIPPING + mergedInfo.getArchiveInfo().getShortWorkfileName() + BRACKET_IN + mergedInfo.getArchiveDirManager().getAppendedPath() + "] directory because file is locked.", Project.MSG_INFO); flag = false; } } if (flag) { ClientRequestSetIsObsoleteData clientRequestSetIsObsoleteData = new ClientRequestSetIsObsoleteData(); clientRequestSetIsObsoleteData.setAppendedPath(appendedPath); clientRequestSetIsObsoleteData.setProjectName(projectName); clientRequestSetIsObsoleteData.setViewName(viewName); clientRequestSetIsObsoleteData.setShortWorkfileName(fileName); int transactionID = 0; try { transactionID = ClientTransactionManager.getInstance().sendBeginTransaction(transportProxy); transportProxy.write(clientRequestSetIsObsoleteData); } finally { ClientTransactionManager.getInstance().sendEndTransaction(transportProxy, transactionID); } } return flag; } private void requestReportOperation(MergedInfoInterface mergedInfo) { boolean reportOnFileFlag = true; // If a specific status is requested, limit the report to only those files that have that status... if (reportFilesWithStatus != null && reportFilesWithStatus.length() > 0) { if (!mergedInfo.getStatusString().equals(reportFilesWithStatus)) { reportOnFileFlag = false; } } if (reportOnFileFlag) { StringBuilder outputString = new StringBuilder(); AccessList accessList = new AccessList(mergedInfo.getLogfileInfo().getLogFileHeaderInfo().getModifierList()); RevisionHeader revHeader = mergedInfo.getRevisionInformation().getRevisionHeader(0); String revisionCreator = accessList.indexToUser(revHeader.getCreatorIndex()); if (mergedInfo.getArchiveDirManager().getAppendedPath().length() > 0) { outputString.append(mergedInfo.getArchiveDirManager().getAppendedPath()).append(QVCSConstants.QVCS_STANDARD_PATH_SEPARATOR_STRING); } outputString.append(mergedInfo.getShortWorkfileName()) .append(":") .append(" Revision: ") .append(revHeader.getRevisionString()) .append(" check in time: ") .append(revHeader.getCheckInDate().toString()) .append(" by ") .append(revisionCreator) .append(" File status: ") .append(mergedInfo.getStatusString()); System.out.println(outputString.toString()); } } private String contractKeywords(File checkInFile, MergedInfoInterface mergedInfo, CheckInCommandArgs commandArgs) { String returnFilename; try { returnFilename = checkInFile.getCanonicalPath(); ArchiveAttributes attributes = mergedInfo.getAttributes(); if (attributes.getIsExpandKeywords()) { FileInputStream inStream = null; FileOutputStream outStream = null; try { // We need to contract keywords before creating the new revision. inStream = new FileInputStream(checkInFile); File contractedOutputFile = File.createTempFile("QVCS", "tmp"); contractedOutputFile.deleteOnExit(); outStream = new FileOutputStream(contractedOutputFile); returnFilename = contractedOutputFile.getCanonicalPath(); if (attributes.getIsBinaryfile()) { KeywordExpansionContext keywordExpansionContext = new KeywordExpansionContext(outStream, contractedOutputFile, mergedInfo.getArchiveInfo().getLogfileInfo(), 0, "", "", mergedInfo.getArchiveDirManager().getProjectProperties()); keywordExpansionContext.setBinaryFileFlag(true); keywordManager.expandKeywords(inStream, keywordExpansionContext); } else { AtomicReference<String> localCheckInComment = new AtomicReference<>(); keywordManager.contractKeywords(inStream, outStream, localCheckInComment, mergedInfo.getArchiveDirManager().getProjectProperties(), false); // Snag any contractions of the Comment keyword. if (localCheckInComment.get() != null) { String newComment = commandArgs.getCheckInComment() + "; " + localCheckInComment.get(); commandArgs.setCheckInComment(newComment); } } } catch (IOException | QVCSException e) { log(Utility.expandStackTraceToString(e)); returnFilename = null; } finally { try { if (inStream != null) { inStream.close(); } if (outStream != null) { outStream.close(); } } catch (IOException e) { log(Utility.expandStackTraceToString(e)); } } } else { if (!checkInFile.canRead()) { log("Cannot read '" + returnFilename + "'. Checkin failed."); returnFilename = null; } } } catch (IOException e) { log(Utility.expandStackTraceToString(e)); returnFilename = null; } return returnFilename; } private void validateTaskProperties() { if (userDirectory == null || userDirectory.length() == 0) { log("You must define the userDirectory property"); throw new BuildException("You must define the userDirectory property"); } if (serverName == null || serverName.length() == 0) { log("You must define the serverName property"); throw new BuildException("You must define the serverName property"); } if (userName == null || userName.length() == 0) { log("You must define the userName property"); throw new BuildException("You must define the userName property"); } if (password == null || password.length() == 0) { log("You must define the password property"); throw new BuildException("You must define the password property"); } if (projectName == null || projectName.length() == 0) { log("You must define the projectName property"); throw new BuildException("You must define the projectName property"); } if (viewName == null || viewName.length() == 0) { log("ViewName not defined. Defaulting to the Trunk"); viewName = QVCSConstants.QVCS_TRUNK_VIEW; } if (appendedPath == null) { log("You must define the appendedPath property"); throw new BuildException("You must define the appendedPath property"); } if (label == null) { label = ""; } if (duplicateLabel == null) { duplicateLabel = ""; } if (fileName == null) { fileName = ""; } if (fileExtension == null) { fileExtension = "*"; } else if (0 != fileExtension.compareTo("*")) { if (fileExtension.length() > 0) { // Make sure the file extension starts with a '.' if (!fileExtension.startsWith(".")) { fileExtension = "." + fileExtension; } } fileExtension = fileExtension.toLowerCase(); } if (checkInComment == null) { checkInComment = ""; } if (operation == null) { operation = OPERATION_GET; } switch (operation) { case OPERATION_GET: if (workfileLocation == null || workfileLocation.length() == 0) { log("You must define the workfileLocation property for the get operation."); throw new BuildException("You must define the workfileLocation property for the get operation."); } break; case OPERATION_LABEL: if (workfileLocation == null || workfileLocation.length() == 0) { // Default to the default temp directory workfileLocation = System.getProperty("java.io.tmpdir"); } if (label.length() == 0) { log("Label string must be defined when performing a label operation."); throw new BuildException("Label string must be defined when performing a label operation."); } if (duplicateLabelFlag) { if (duplicateLabel.length() == 0) { log("Duplicate label string must be defined when duplicating a label."); throw new BuildException("Duplicate label string must be defined when duplicating a label."); } } break; case OPERATION_CHECKIN: if (workfileLocation == null || workfileLocation.length() == 0) { log("You must define the workfileLocation property for the checkin operation."); throw new BuildException("You must define the workfileLocation property for the checkin operation."); } if (checkInComment.length() == 0) { log("A checkin comment is required."); throw new BuildException("You must define the checkInComment property. It is required for the checkin operation."); } // Create the keyword manager just once. keywordManager = KeywordManagerFactory.getInstance().getNewKeywordManager(); break; case OPERATION_CHECKOUT: if (workfileLocation == null || workfileLocation.length() == 0) { log("You must define the workfileLocation property for the checkout operation."); throw new BuildException("You must define the workfileLocation property for the checkout operation."); } break; case OPERATION_LOCK: if (workfileLocation == null || workfileLocation.length() == 0) { // Default to the default temp directory workfileLocation = System.getProperty("java.io.tmpdir"); } break; case OPERATION_UNLOCK: if (workfileLocation == null || workfileLocation.length() == 0) { // Default to the default temp directory workfileLocation = System.getProperty("java.io.tmpdir"); } break; case OPERATION_REPORT: // If report files with status is set, the requested status must be a valid status string. if (reportFilesWithStatus != null && reportFilesWithStatus.length() > 0) { String[] validStatusStrings = MergedInfo.getStatusStrings(); boolean foundStatusString = false; for (String validStatusString : validStatusStrings) { if (reportFilesWithStatus.equals(validStatusString)) { foundStatusString = true; break; } } if (!foundStatusString) { log("If limiting your report to files with a given status, the status string must be from the following list:"); for (String validStatusString : validStatusStrings) { log("\t[" + validStatusString + "]"); } } } break; case OPERATION_MOVE: if ((fileName == null) || (fileName.length() == 0)) { throw new BuildException("Move operation must supply a fileName"); } if (moveToAppendedPath == null) { throw new BuildException("Move operation must supply a moveToAppendedPath"); } if (moveToAppendedPath.equals(appendedPath)) { throw new BuildException("Origin and destination directories must be different for a move operation."); } break; case OPERATION_RENAME: if ((fileName == null) || (fileName.length() == 0)) { throw new BuildException("Rename operation must supply fileName"); } if ((renameToFileName == null) || (renameToFileName.length() == 0)) { throw new BuildException("Rename operation must supply renameToFileName"); } if (fileName.equals(renameToFileName)) { throw new BuildException("Original fileName and renameToFileName must be different for rename operation."); } break; case OPERATION_DELETE: if ((fileName == null) || (fileName.length() == 0)) { throw new BuildException("Delete operation must supply filename"); } break; default: log("operation must be 'get', 'label', 'checkin', 'checkout', 'lock', 'unlock', 'move', rename, 'delete', or 'report'"); throw new BuildException("You must define a valid operation."); } } private void reportTaskProperties() { log("User directory: " + userDirectory); log("Server name: " + serverName); log("User name: " + userName); log("User password: " + getSpoofedPassword(password)); log("Project name: " + projectName); log("View name: " + viewName); log("Appended path: " + appendedPath); log("Workfile location: " + workfileLocation); log("Operation: " + operation); log("Label: " + label); log("Recurse flag: " + recurseFlag); if (fileName != null) { log("File name: " + fileName); } log("File extension: " + fileExtension); if (operation.equals(OPERATION_GET)) { log("OverWrite Flag: " + overWriteFlag); } if (operation.equals(OPERATION_LABEL)) { log("Duplicate Label: " + duplicateLabel); log("Floating Label Flag: " + floatingLabelFlag); log("Reuse Label Flag: " + reuseLabelFlag); } if (operation.equals(OPERATION_CHECKIN)) { log("Check in comment: " + checkInComment); log("Floating Label Flag: " + floatingLabelFlag); log("Reuse Label Flag: " + reuseLabelFlag); } if (operation.equals(OPERATION_MOVE)) { log("MoveToAppendedPath: " + moveToAppendedPath); } if (operation.equals(OPERATION_RENAME)) { log("RenameToFileName: " + renameToFileName); } if (operation.equals(OPERATION_REPORT)) { if (reportFilesWithStatus != null) { log("Report files with status: " + reportFilesWithStatus); } } } /** * Create a string of '*' that is the same length as the user's password. * * @param arg the users password. * @return a spoofed string that is the same length as the password. */ private String getSpoofedPassword(String arg) { StringBuilder buffer = new StringBuilder(); for (int i = 0; i < arg.length(); i++) { buffer.append("*"); } return buffer.toString(); } @Override public void notifyTransportProxyListener(ServerResponseInterface messageIn) { if (messageIn instanceof ServerResponseMessage) { ServerResponseMessage message = (ServerResponseMessage) messageIn; log(message.getMessage()); } } static class MyTransactionProgressListener implements TransactionInProgressListenerInterface { private final Object syncObject; MyTransactionProgressListener(final Object arg) { this.syncObject = arg; } @Override public void setTransactionInProgress(boolean flag) { synchronized (syncObject) { if (!flag) { syncObject.notifyAll(); } } } } }