/* 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.server.clientrequest;
import com.qumasoft.qvcslib.AddRevisionData;
import com.qumasoft.qvcslib.ArchiveDirManagerInterface;
import com.qumasoft.qvcslib.ArchiveDirManagerReadOnlyViewInterface;
import com.qumasoft.qvcslib.ArchiveDirManagerReadWriteViewInterface;
import com.qumasoft.qvcslib.ArchiveInfoInterface;
import com.qumasoft.qvcslib.DirectoryCoordinate;
import com.qumasoft.qvcslib.LogFileInterface;
import com.qumasoft.qvcslib.QVCSConstants;
import com.qumasoft.qvcslib.QVCSException;
import com.qumasoft.qvcslib.RevisionHeader;
import com.qumasoft.qvcslib.RevisionInformation;
import com.qumasoft.qvcslib.ServerResponseFactoryInterface;
import com.qumasoft.qvcslib.SkinnyLogfileInfo;
import com.qumasoft.qvcslib.Utility;
import com.qumasoft.qvcslib.commandargs.CheckInCommandArgs;
import com.qumasoft.qvcslib.requestdata.ClientRequestCheckInData;
import com.qumasoft.qvcslib.response.ServerResponseCheckIn;
import com.qumasoft.qvcslib.response.ServerResponseInterface;
import com.qumasoft.qvcslib.response.ServerResponseMessage;
import com.qumasoft.server.ActivityJournalManager;
import com.qumasoft.server.ArchiveDirManager;
import com.qumasoft.server.ArchiveDirManagerFactoryForServer;
import com.qumasoft.server.ArchiveInfoForTranslucentBranch;
import com.qumasoft.server.FileIDDictionary;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Client request check in.
* @author Jim Voris
*/
public class ClientRequestCheckIn implements ClientRequestInterface {
// Create our logger object
private static final Logger LOGGER = Logger.getLogger("com.qumasoft.server");
private final ClientRequestCheckInData request;
/**
* Creates a new instance of ClientRequestCheckIn.
*
* @param data the request data.
*/
public ClientRequestCheckIn(ClientRequestCheckInData data) {
request = data;
}
/**
* Check in a new revision.
*
* @param userName the user's user name.
* @param response identify the client.
* @return an object to tell the user how things went.
*/
@Override
public ServerResponseInterface execute(String userName, ServerResponseFactoryInterface response) {
ServerResponseCheckIn serverResponse;
ServerResponseInterface returnObject = null;
CheckInCommandArgs commandArgs = request.getCommandArgs();
String projectName = request.getProjectName();
String viewName = request.getViewName();
String appendedPath = request.getAppendedPath();
FileOutputStream outputStream = null;
try {
DirectoryCoordinate directoryCoordinate = new DirectoryCoordinate(projectName, viewName, appendedPath);
ArchiveDirManagerInterface archiveDirManagerInterface
= ArchiveDirManagerFactoryForServer.getInstance().getDirectoryManager(QVCSConstants.QVCS_SERVER_SERVER_NAME, directoryCoordinate,
QVCSConstants.QVCS_SERVED_PROJECT_TYPE, QVCSConstants.QVCS_SERVER_USER, response, true);
LOGGER.log(Level.FINE, "project name: " + projectName + " view name: " + viewName + " appended path: " + appendedPath);
LOGGER.log(Level.FINE, "full workfile name: " + commandArgs.getFullWorkfileName());
LOGGER.log(Level.FINE, "short workfile name: " + commandArgs.getShortWorkfileName());
LOGGER.log(Level.INFO, "User: " + userName + " checked in " + commandArgs.getShortWorkfileName() + " to view: " + viewName + ", directory: "
+ appendedPath);
ArchiveInfoInterface logfile = archiveDirManagerInterface.getArchiveInfo(commandArgs.getShortWorkfileName());
if ((logfile != null) && (archiveDirManagerInterface instanceof ArchiveDirManagerReadWriteViewInterface)) {
java.io.File tempFile = java.io.File.createTempFile("QVCS", ".tmp");
tempFile.deleteOnExit();
outputStream = new java.io.FileOutputStream(tempFile);
Utility.writeDataToStream(request.getBuffer(), outputStream);
commandArgs.setFailureReason("");
int currentRevisionCount = logfile.getRevisionCount();
if (logfile.checkInRevision(commandArgs, tempFile.getAbsolutePath(), false)) {
// Update the most recent activity date for the containing archiveDirManager.
if (archiveDirManagerInterface instanceof ArchiveDirManager) {
ArchiveDirManager archiveDirManager = (ArchiveDirManager) archiveDirManagerInterface;
archiveDirManager.updateMostRecentActivityDate(logfile.getLastCheckInDate());
}
// Things worked. Set up the response object to contain the information the client needs.
serverResponse = new ServerResponseCheckIn();
serverResponse.setShortWorkfileName(logfile.getShortWorkfileName());
serverResponse.setClientWorkfileName(commandArgs.getFullWorkfileName());
serverResponse.setProjectName(projectName);
serverResponse.setViewName(viewName);
serverResponse.setAppendedPath(appendedPath);
serverResponse.setKeepLockedFlag(commandArgs.getLockFlag());
serverResponse.setProtectWorkfileFlag(commandArgs.getProtectWorkfileFlag());
serverResponse.setNoExpandKeywordsFlag(commandArgs.getNoExpandKeywordsFlag());
serverResponse.setNewRevisionString(commandArgs.getNewRevisionString());
serverResponse.setIndex(request.getIndex());
// If there is keyword expansion and a new revision was created,
// then send back the info the client will need.
int newRevisionCount = logfile.getRevisionCount();
if (logfile.getAttributes().getIsExpandKeywords()
&& (commandArgs.getNewRevisionString() != null)
&& (currentRevisionCount != newRevisionCount)) {
serverResponse.setLogfileInfo(logfile.getLogfileInfo());
serverResponse.setAddedRevisionData(createAddedRevisionData(logfile, commandArgs));
}
byte[] digest = logfile.getDefaultRevisionDigest();
LogFileInterface logFileInterface = (LogFileInterface) logfile;
serverResponse.setSkinnyLogfileInfo(new SkinnyLogfileInfo(logFileInterface.getLogfileInfo(), File.separator, logFileInterface.getIsObsolete(), digest,
logfile.getShortWorkfileName(), logfile.getIsOverlap()));
tempFile.delete();
// If we need to create a reference copy...
if (archiveDirManagerInterface.getProjectProperties().getCreateReferenceCopyFlag()) {
archiveDirManagerInterface.createReferenceCopy(archiveDirManagerInterface.getProjectProperties(), logfile, request.getBuffer());
}
returnObject = serverResponse;
// Add an entry to the server journal file.
ActivityJournalManager.getInstance().addJournalEntry(buildJournalEntry(userName, logfile));
// If we need to capture a new fileID-to-directory association because we're creating the first revision
// on a branch...
if (commandArgs.getForceBranchFlag() && (logfile instanceof ArchiveInfoForTranslucentBranch)) {
// Add an entry into the FileIDDictionary for this branch... making sure to add it to the
// dictionary only if we're creating the branch... i.e. only if the commandArgs.getForceBranchFlag() is true.
FileIDDictionary.getInstance().saveFileIDInfo(projectName, viewName, logfile.getFileID(), appendedPath,
logfile.getShortWorkfileName(),
archiveDirManagerInterface.getDirectoryID());
}
} else {
// Return a command error.
String errorMessage = "Failed to check in " + commandArgs.getShortWorkfileName() + ". " + commandArgs.getFailureReason();
ServerResponseMessage message = new ServerResponseMessage(errorMessage, projectName, viewName, appendedPath,
ServerResponseMessage.HIGH_PRIORITY);
message.setShortWorkfileName(commandArgs.getShortWorkfileName());
returnObject = message;
}
} else {
if (logfile == null) {
// Explain the error.
ServerResponseMessage message = new ServerResponseMessage("Archive not found for " + commandArgs.getShortWorkfileName(), projectName,
viewName, appendedPath,
ServerResponseMessage.HIGH_PRIORITY);
message.setShortWorkfileName(commandArgs.getShortWorkfileName());
returnObject = message;
} else {
if (archiveDirManagerInterface instanceof ArchiveDirManagerReadOnlyViewInterface) {
// Explain the error.
ServerResponseMessage message = new ServerResponseMessage("Checkin not allowed for read-only view.", projectName, viewName,
appendedPath,
ServerResponseMessage.HIGH_PRIORITY);
message.setShortWorkfileName(commandArgs.getShortWorkfileName());
returnObject = message;
}
}
}
} catch (QVCSException e) {
ServerResponseMessage message = new ServerResponseMessage(e.getLocalizedMessage(), projectName, viewName, appendedPath,
ServerResponseMessage.HIGH_PRIORITY);
message.setShortWorkfileName(commandArgs.getShortWorkfileName());
returnObject = message;
} catch (IOException e) {
LOGGER.log(Level.WARNING, "Caught exception on check-in: " + e.getClass().toString() + ": " + e.getLocalizedMessage());
LOGGER.log(Level.WARNING, Utility.expandStackTraceToString(e));
ServerResponseMessage message = new ServerResponseMessage(e.getLocalizedMessage(), projectName, viewName, appendedPath,
ServerResponseMessage.HIGH_PRIORITY);
message.setShortWorkfileName(commandArgs.getShortWorkfileName());
returnObject = message;
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
LOGGER.log(Level.WARNING, Utility.expandStackTraceToString(e));
}
}
}
return returnObject;
}
private String buildJournalEntry(final String userName, final ArchiveInfoInterface logfile) {
CheckInCommandArgs commandArgs = request.getCommandArgs();
if (commandArgs.getApplyLabelFlag()) {
return "User: [" + userName + "] checked-in revision [" + commandArgs.getNewRevisionString() + "] of ["
+ Utility.formatFilenameForActivityJournal(request.getProjectName(), request.getViewName(), request.getAppendedPath(), logfile.getShortWorkfileName())
+ "] and applied label: [" + commandArgs.getLabel() + "]";
} else {
return "User: [" + userName + "] checked-in revision [" + commandArgs.getNewRevisionString() + "] of ["
+ Utility.formatFilenameForActivityJournal(request.getProjectName(), request.getViewName(), request.getAppendedPath(), logfile.getShortWorkfileName()) + "]";
}
}
/**
* Create data about the newly added revision.
*
* @param logfile the archive info to which the new revision was added.
* @param commandArgs the command arguments used to create the new revision which will include the new revision string.
* @return an object identifying the new revision.
*/
public static AddRevisionData createAddedRevisionData(ArchiveInfoInterface logfile, CheckInCommandArgs commandArgs) {
AddRevisionData addedRevisionData = new AddRevisionData();
RevisionInformation revisionInformation = logfile.getRevisionInformation();
String newRevisionString = commandArgs.getNewRevisionString();
String parentRevisionString = commandArgs.getParentRevisionString();
for (int i = 0; i < logfile.getRevisionCount(); i++) {
RevisionHeader revHeader = revisionInformation.getRevisionHeader(i);
if (0 == revHeader.getRevisionString().compareTo(newRevisionString)) {
addedRevisionData.setNewRevisionHeader(revHeader);
addedRevisionData.setNewRevisionIndex(i);
}
if (0 == revHeader.getRevisionString().compareTo(parentRevisionString)) {
addedRevisionData.setParentRevisionHeader(revHeader);
addedRevisionData.setParentRevisionIndex(i);
}
}
return addedRevisionData;
}
}