com.collabnet.ccf.pi.qc.v90.QCWriter.java Source code

Java tutorial

Introduction

Here is the source code for com.collabnet.ccf.pi.qc.v90.QCWriter.java

Source

/*
 * Copyright 2009 CollabNet, Inc. ("CollabNet") 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.collabnet.ccf.pi.qc.v90;

import java.io.File;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.openadaptor.core.IDataProcessor;
import org.openadaptor.core.exception.ValidationException;

import com.collabnet.ccf.core.AbstractWriter;
import com.collabnet.ccf.core.CCFRuntimeException;
import com.collabnet.ccf.core.eis.connection.ConnectionException;
import com.collabnet.ccf.core.eis.connection.ConnectionManager;
import com.collabnet.ccf.core.eis.connection.MaxConnectionsReachedException;
import com.collabnet.ccf.core.ga.AttachmentMetaData;
import com.collabnet.ccf.core.ga.GenericArtifact;
import com.collabnet.ccf.core.ga.GenericArtifactField;
import com.collabnet.ccf.core.ga.GenericArtifactHelper;
import com.collabnet.ccf.core.ga.GenericArtifactParsingException;
import com.collabnet.ccf.core.utils.DateUtil;
import com.collabnet.ccf.core.utils.Obfuscator;
import com.collabnet.ccf.pi.qc.v90.api.DefectAlreadyLockedException;
import com.collabnet.ccf.pi.qc.v90.api.IConnection;
import com.collabnet.ccf.pi.qc.v90.api.IRecordSet;

/**
 * This class writes the incoming defect data into QC making.
 * 
 * @author venugopal
 * 
 */
public class QCWriter extends AbstractWriter<IConnection> implements IDataProcessor {

    /**
     * Calls tear-Down method of QCWriter
     * 
     * @author jnicolai
     * 
     */
    private static class CleanUpCOMHookQCWriter extends Thread {
        // private static final Log log =
        // LogFactory.getLog(CleanUpCOMHookQCReader.class);

        private QCWriter qcWriter;

        public CleanUpCOMHookQCWriter(QCWriter qcWriter) {
            this.qcWriter = qcWriter;
        }

        public void run() {
            qcWriter.tearDownCOM();
        }
    }

    /**
     * Another user name that is used to login into the CEE instance. This user
     * has to differ from the ordinary user used to log in in order to force
     * initial resyncs with the source system once a new artifact has been
     * created.
     */
    private String resyncUserName;

    /**
     * Password that belongs to the resync user. This user has to differ from
     * the ordinary user used to log in in order to force initial resyncs with
     * the source system once a new artifact has been created.
     */
    private String resyncPassword;
    private static final Log log = LogFactory.getLog(QCWriter.class);
    // private static final Log logConflictResolutor =
    // LogFactory.getLog("com.collabnet.ccf.core.conflict.resolution");
    private QCHandler artifactHandler;
    private QCAttachmentHandler attachmentHandler;
    private QCGAHelper qcGAHelper;
    // private ConnectionManager<IConnection> connectionManager = null;
    public final String ARTIFACT_TYPE_PLAINARTIFACT = "plainartifact";
    public final String ARTIFACT_TYPE_ATTACHMENT = "attachment";
    private int connectCounts = 0;

    private int countBeforeCOMReinitialization = 50000;

    private String serverUrl;

    private String userName;

    private String password;

    private boolean isConnected = false;

    private boolean comInitialized = false;

    /**
     * If this property is set to true (true by default), locked defects will be
     * quarantined and the operation will never be retried
     */
    private boolean immediatelyQuarantineLockedDefects = true;

    /**
     * If this property is enabled, QCWriter will attempt to figure out if the
     * field value has remained the same apart from formatting differences
     * incurred by the conversion to plain text. If so, the field will not be
     * updated in order to preserve the existing formatting.
     * 
     * True by default. This can be set to false for backwards compatible
     * behavior.
     */
    private boolean preserveSemanticallyUnchangedHTMLFieldValues = true;

    private boolean useAlternativeFieldName = false;

    private boolean quarantineIfQCReportsPermissionDenied = false;

    public QCWriter() {
        super();
        Runtime.getRuntime().addShutdownHook(new CleanUpCOMHookQCWriter(this));
    }

    /**
     * Checks if defect with the incoming defectId exists in the target system.
     * 
     * @param bugId
     * @param connection
     * @return boolean Returns true if the defect exists, false otherwise.
     */
    public boolean checkForBugIdInQC(int bugId, IConnection connection) {

        QCDefect thisDefect = qcGAHelper.getDefectWithId(connection, bugId);

        if (thisDefect != null)
            return true;
        else
            return false;
    }

    /**
     * In the case of memo fields like Comments, the multiple values are
     * concatinated before writing them into the target system.
     * 
     * @param genericArtifact
     * @return
     */
    public GenericArtifact concatValuesOfSameFieldNames(GenericArtifact genericArtifact) {

        List<GenericArtifactField> allFields = genericArtifact.getAllGenericArtifactFields();
        Set<String> allFieldNames = new HashSet<String>();
        for (int cnt = 0; cnt < allFields.size(); cnt++) {
            if (allFields.get(cnt).getFieldName().equals(QCConfigHelper.QC_BG_DEV_COMMENTS)
                    || allFields.get(cnt).getFieldName().equals(QCConfigHelper.QC_RQ_DEV_COMMENTS)) {
                continue;
            }
            if (!(allFieldNames.contains(allFields.get(cnt).getFieldName())) && genericArtifact
                    .getAllGenericArtifactFieldsWithSameFieldName(allFields.get(cnt).getFieldName()).size() > 1) {
                List<GenericArtifactField> allSameFields = genericArtifact
                        .getAllGenericArtifactFieldsWithSameFieldName(allFields.get(cnt).getFieldName());
                StringBuilder concatenatedString = new StringBuilder();

                for (GenericArtifactField field : allSameFields) {
                    // this code assumes that multi select fields are always of type string
                    String value = (String) field.getFieldValue();
                    if (!StringUtils.isEmpty(value)) {
                        if (concatenatedString.length() != 0) {
                            concatenatedString.append(";");
                        }
                        concatenatedString.append(value);
                    }
                }

                genericArtifact.getAllGenericArtifactFieldsWithSameFieldName(allFields.get(cnt).getFieldName())
                        .get(0).setFieldValue(concatenatedString.toString());
            }
            allFieldNames.add(allFields.get(cnt).getFieldName());
        }

        return genericArtifact;
    }

    public IConnection connect(GenericArtifact ga) {
        String targetSystemId = ga.getTargetSystemId();
        String targetSystemKind = ga.getTargetSystemKind();
        String targetRepositoryId = ga.getTargetRepositoryId();
        String targetRepositoryKind = ga.getTargetRepositoryKind();
        IConnection connection;
        try {
            if ((!ga.getArtifactAction().equals(GenericArtifact.ArtifactActionValue.CREATE))
                    || getResyncUserName() == null) {
                connection = connect(targetSystemId, targetSystemKind, targetRepositoryId, targetRepositoryKind,
                        serverUrl, getUserName() + QCConnectionFactory.PARAM_DELIMITER + getPassword(), false);
                connectCounts++;
            } else {
                connection = connect(targetSystemId, targetSystemKind, targetRepositoryId, targetRepositoryKind,
                        serverUrl, getResyncUserName() + QCConnectionFactory.PARAM_DELIMITER + getResyncPassword(),
                        true);
                connectCounts++;
            }
        } catch (MaxConnectionsReachedException e) {
            String cause = "Could not create connection to the QC system. Max connections reached for " + serverUrl;
            log.error(cause, e);
            ga.setErrorCode(GenericArtifact.ERROR_MAX_CONNECTIONS_REACHED_FOR_POOL);
            throw new CCFRuntimeException(cause, e);
        } catch (ConnectionException e) {
            String cause = "Could not create connection to the QC system " + serverUrl;
            log.error(cause, e);
            ga.setErrorCode(GenericArtifact.ERROR_EXTERNAL_SYSTEM_CONNECTION);
            throw new CCFRuntimeException(cause, e);
        }
        return connection;
    }

    /**
     * Establish a connection with QC system
     * 
     * @param systemId
     *            Id indicating a QC system.
     * @param systemKind
     *            Indicates whether it is a DEFECT or TEST and so on.
     * @param repositoryId
     *            Specifies the name of DOMAIN and PROJECT in QC system to which
     *            connection needs to established.
     * @param repositoryKind
     *            Indicates which version of QC like QC 9.0, QC9.2.
     * @param connectionInfo
     *            The server URL
     * @param credentialInfo
     *            Username and password needed for establishing a connection
     * @param forceResync
     *            create artifacts by using another account to enforce an
     *            initial resync after artifact creation
     * @return IConnection The connection object
     * 
     */
    public IConnection connect(String systemId, String systemKind, String repositoryId, String repositoryKind,
            String connectionInfo, String credentialInfo, boolean forceResync)
            throws MaxConnectionsReachedException, ConnectionException {
        IConnection connection = null;
        ConnectionManager<IConnection> connectionManager = (ConnectionManager<IConnection>) this
                .getConnectionManager();
        if (forceResync) {
            connection = connectionManager.getConnectionToCreateArtifact(systemId, systemKind, repositoryId,
                    repositoryKind, connectionInfo, credentialInfo);
        } else {
            connection = connectionManager.getConnectionToUpdateOrExtractArtifact(systemId, systemKind,
                    repositoryId, repositoryKind, connectionInfo, credentialInfo);
        }
        isConnected = true;
        return connection;
    }

    @Override
    public Document createArtifact(Document gaDocument) {
        String targetArtifactIdAfterCreation = null;
        GenericArtifact genericArtifact = getArtifactFromDocument(gaDocument);
        String targetSystemTimezone = genericArtifact.getTargetSystemTimezone();
        IConnection connection = null;
        List<GenericArtifactField> allFields = this.getAllGenericArtfactFields(genericArtifact);
        String targetRepositoryId = genericArtifact.getTargetRepositoryId();
        try {
            connection = this.connect(genericArtifact);
            if (QCConnectionFactory.isDefectRepository(targetRepositoryId)) {
                IQCDefect createdArtifact = null;
                try {
                    createdArtifact = artifactHandler.createDefect(connection, allFields, this.getUserName(),
                            targetSystemTimezone);
                    if (createdArtifact != null) {
                        targetArtifactIdAfterCreation = createdArtifact.getId();
                        log.info("Defect " + targetArtifactIdAfterCreation + " is created on "
                                + genericArtifact.getTargetRepositoryId() + " with the artifact details "
                                + genericArtifact.getSourceArtifactId() + " on "
                                + genericArtifact.getSourceRepositoryId());
                        genericArtifact.setTargetArtifactId(targetArtifactIdAfterCreation);
                        // send this artifact to RCDU (Read Connector Database Updater)
                        // indicating a success in creating the artifact

                        List<String> targetAutimeAndTxnId = getAuTimeAndTxnIdForDefect(connection,
                                targetArtifactIdAfterCreation);
                        genericArtifact.setTargetArtifactVersion(targetAutimeAndTxnId.get(0));
                        genericArtifact.setTargetArtifactLastModifiedDate(
                                DateUtil.format(DateUtil.parseQCDate(targetAutimeAndTxnId.get(1))));
                    }
                } finally {
                    if (createdArtifact != null) {
                        createdArtifact.safeRelease();
                        createdArtifact = null;
                    }
                }
            } else {
                // we create a requirement
                IQCRequirement createdArtifact = null;
                try {
                    String parentArtifactId = genericArtifact.getDepParentTargetArtifactId();
                    String informalRequirementsType = QCConnectionFactory
                            .extractInformalRequirementsType(targetRepositoryId);
                    createdArtifact = artifactHandler.createRequirement(connection, allFields, this.getUserName(),
                            targetSystemTimezone, informalRequirementsType, parentArtifactId);
                    if (createdArtifact != null) {
                        targetArtifactIdAfterCreation = createdArtifact.getId();
                        log.info("Requirement " + targetArtifactIdAfterCreation + " is created on "
                                + genericArtifact.getTargetRepositoryId() + " with the artifact details "
                                + genericArtifact.getSourceArtifactId() + " on "
                                + genericArtifact.getSourceRepositoryId());
                        genericArtifact.setTargetArtifactId(targetArtifactIdAfterCreation);

                        List<String> targetAutimeAndTxnId = getAuTimeAndTxnIdForRequirement(connection,
                                targetArtifactIdAfterCreation);
                        genericArtifact.setTargetArtifactVersion(targetAutimeAndTxnId.get(0));
                        genericArtifact.setTargetArtifactLastModifiedDate(
                                DateUtil.format(DateUtil.parseQCDate(targetAutimeAndTxnId.get(1))));
                    }
                } finally {
                    if (createdArtifact != null) {
                        createdArtifact.safeRelease();
                        createdArtifact = null;
                    }
                }
            }

        } catch (RemoteException e) {
            String cause = "Error while creating artifact in QC";
            log.error(cause, e);
            genericArtifact.setErrorCode(GenericArtifact.ERROR_EXTERNAL_SYSTEM_WRITE);
            throw new CCFRuntimeException(cause, e);
        } finally {
            disconnect(connection);
        }
        return this.returnDocument(genericArtifact);
    }

    @Override
    public Document[] createAttachment(Document gaDocument) {
        GenericArtifact genericArtifact = getArtifactFromDocument(gaDocument);
        String parentArtifactId = genericArtifact.getDepParentTargetArtifactId();
        String attachmentName = getFieldValueFromGenericArtifact(genericArtifact,
                AttachmentMetaData.getAttachmentName());
        String contentTypeValue = getFieldValueFromGenericArtifact(genericArtifact,
                AttachmentMetaData.getAttachmentType());
        String attachmentSourceUrl = getFieldValueFromGenericArtifact(genericArtifact,
                AttachmentMetaData.getAttachmentSourceUrl());
        String targetRepositoryId = genericArtifact.getTargetRepositoryId();

        String attachmentDataFileName = GenericArtifactHelper
                .getStringGAField(AttachmentMetaData.ATTACHMENT_DATA_FILE, genericArtifact);
        String contentDataType = GenericArtifactHelper.getStringGAField(AttachmentMetaData.ATTACHMENT_TYPE,
                genericArtifact);
        String attachmentDescription = GenericArtifactHelper
                .getStringGAField(AttachmentMetaData.ATTACHMENT_DESCRIPTION, genericArtifact);
        File attachmentFile = null;
        if (contentDataType.equals(AttachmentMetaData.AttachmentType.DATA.toString())) {
            if (StringUtils.isEmpty(attachmentDataFileName)) {
                byte[] attachmentData = null;
                attachmentData = genericArtifact.getRawAttachmentData();
                attachmentFile = qcGAHelper.writeDataIntoFile(attachmentData, attachmentName);
            } else {
                // we have to cope the file containing the attachment data to a file with the intended file name
                // we have to do the copy operation instead of renaming if the server throws
                // an exception and we have to repeat the procedure again
                File attachmentDataFile = new File(attachmentDataFileName);
                String tempDir = System.getProperty("java.io.tmpdir");
                attachmentFile = new File(tempDir, attachmentName);
                if (attachmentDataFile.exists()) {
                    if (attachmentFile.exists()) {
                        boolean deletingSuccess = attachmentFile.delete();
                        if (deletingSuccess) {
                            log.debug("The file " + attachmentFile.getAbsolutePath() + " existed before moving "
                                    + attachmentDataFile.getAbsolutePath() + " to that name. So deleted the file "
                                    + attachmentFile.getAbsolutePath());
                        } else {
                            log.info("The file " + attachmentFile.getAbsolutePath()
                                    + " exists. But it could not be deleted.");
                        }
                    } else {
                        log.debug("The file " + attachmentFile.getAbsolutePath() + " does not exist. So "
                                + attachmentDataFile.getAbsolutePath() + " can be moved to that name.");
                    }
                    QCGAHelper.copyFile(attachmentDataFile, attachmentFile);
                } else {
                    String message = "The attachment data file " + attachmentDataFile.getAbsolutePath()
                            + " does not exist. So the attachment " + attachmentName
                            + " can not be uploaded to the artifact " + parentArtifactId;
                    log.error(message);
                    throw new CCFRuntimeException(message);
                }
            }
            if (attachmentFile == null || (!attachmentFile.exists())) {
                String message = "The attachment data file " + attachmentFile.getAbsolutePath()
                        + " does not exist. So the attachment " + attachmentName
                        + " can not be uploaded to the artifact " + parentArtifactId;
                log.error(message);
                throw new CCFRuntimeException(message);
            } else if (attachmentFile.length() == 0) {
                log.warn("The attachment file " + attachmentFile.getAbsolutePath()
                        + " contains no data. It is uploaded to the bug " + parentArtifactId + ", though.");
            }
        }

        IConnection connection = null;
        GenericArtifact parentArtifact = null;
        List<String> targetAutimeAndTxnId = null;
        List<String> targetAutimeAndTxnIdParent = null;
        try {
            connection = this.connect(genericArtifact);
            if (QCConnectionFactory.isDefectRepository(targetRepositoryId)) {
                String attachmentId = attachmentHandler.createAttachmentForDefect(connection, parentArtifactId,
                        attachmentName, contentTypeValue, attachmentFile, attachmentSourceUrl,
                        attachmentDescription);
                genericArtifact.setTargetArtifactId(attachmentId);
                log.info("Attachment " + attachmentName + " is created with id " + attachmentId + " for defect "
                        + parentArtifactId + " on " + genericArtifact.getTargetRepositoryId());
                targetAutimeAndTxnId = getAuTimeAndTxnIdForAttachment(connection, attachmentId);
                genericArtifact.setTargetArtifactVersion(targetAutimeAndTxnId.get(0));
                genericArtifact.setTargetArtifactLastModifiedDate(
                        DateUtil.format(DateUtil.parseQCDate(targetAutimeAndTxnId.get(1))));

                targetAutimeAndTxnIdParent = getAuTimeAndTxnIdForDefect(connection, parentArtifactId);
            } else {
                // we have to attach to a requirement
                String attachmentId = attachmentHandler.createAttachmentForRequirement(connection, parentArtifactId,
                        attachmentName, contentTypeValue, attachmentFile, attachmentSourceUrl,
                        attachmentDescription);
                genericArtifact.setTargetArtifactId(attachmentId);
                log.info(
                        "Attachment " + attachmentName + " is created with id " + attachmentId + " for requirement "
                                + parentArtifactId + " on " + genericArtifact.getTargetRepositoryId());
                targetAutimeAndTxnId = getAuTimeAndTxnIdForAttachment(connection, attachmentId);
                genericArtifact.setTargetArtifactVersion(targetAutimeAndTxnId.get(0));
                genericArtifact.setTargetArtifactLastModifiedDate(
                        DateUtil.format(DateUtil.parseQCDate(targetAutimeAndTxnId.get(1))));

                targetAutimeAndTxnIdParent = getAuTimeAndTxnIdForRequirement(connection, parentArtifactId);
            }

            parentArtifact = new GenericArtifact();
            // make sure that we do not update the synchronization status record for replayed attachments
            parentArtifact.setTransactionId(genericArtifact.getTransactionId());
            parentArtifact.setArtifactType(GenericArtifact.ArtifactTypeValue.PLAINARTIFACT);
            parentArtifact.setArtifactAction(GenericArtifact.ArtifactActionValue.UPDATE);
            parentArtifact.setArtifactMode(GenericArtifact.ArtifactModeValue.CHANGEDFIELDSONLY);
            parentArtifact.setConflictResolutionPriority(genericArtifact.getConflictResolutionPriority());
            parentArtifact.setSourceArtifactId(genericArtifact.getDepParentSourceArtifactId());
            parentArtifact.setSourceArtifactLastModifiedDate(genericArtifact.getSourceArtifactLastModifiedDate());
            parentArtifact.setSourceArtifactVersion(genericArtifact.getSourceArtifactVersion());
            parentArtifact.setSourceRepositoryId(genericArtifact.getSourceRepositoryId());
            parentArtifact.setSourceSystemId(genericArtifact.getSourceSystemId());
            parentArtifact.setSourceSystemKind(genericArtifact.getSourceSystemKind());
            parentArtifact.setSourceRepositoryKind(genericArtifact.getSourceRepositoryKind());
            parentArtifact.setSourceSystemTimezone(genericArtifact.getSourceSystemTimezone());

            parentArtifact.setTargetArtifactId(parentArtifactId);
            parentArtifact.setTargetArtifactLastModifiedDate(
                    DateUtil.format(DateUtil.parseQCDate(targetAutimeAndTxnIdParent.get(1))));
            parentArtifact.setTargetArtifactVersion(targetAutimeAndTxnIdParent.get(0));
            parentArtifact.setTargetRepositoryId(genericArtifact.getTargetRepositoryId());
            parentArtifact.setTargetRepositoryKind(genericArtifact.getTargetRepositoryKind());
            parentArtifact.setTargetSystemId(genericArtifact.getTargetSystemId());
            parentArtifact.setTargetSystemKind(genericArtifact.getTargetSystemKind());
            parentArtifact.setTargetSystemTimezone(genericArtifact.getTargetSystemTimezone());

        } catch (Exception e) {
            String cause = "Error while creating attachment in QC";
            log.error(cause, e);
            genericArtifact.setErrorCode(GenericArtifact.ERROR_EXTERNAL_SYSTEM_CONNECTION);
            throw new CCFRuntimeException(cause, e);
        } finally {
            if (attachmentFile != null) {
                boolean deletingSuccess = attachmentFile.delete();
                if (!deletingSuccess) {
                    log.warn("Could not delete the attachment file " + attachmentFile.getAbsolutePath());
                }
            }
            disconnect(connection);
        }

        Document attachmentDoc = this.returnDocument(genericArtifact);
        Document parentDoc = parentArtifact == null ? null : this.returnDocument(parentArtifact);
        return new Document[] { attachmentDoc, parentDoc };
    }

    @Override
    public Document createDependency(Document gaDocument) {
        // throw new
        // CCFRuntimeException("createDependency is not implemented...!");
        log.warn("createDependency is not implemented...!");
        return null;
    }

    @Override
    public Document deleteArtifact(Document gaDocument) {
        // throw new
        // CCFRuntimeException("deleteArtifact is not implemented...!");
        log.warn("deleteArtifact is not implemented...!");
        return null;
    }

    @Override
    public Document[] deleteAttachment(Document gaDocument) {
        GenericArtifact genericArtifact = getArtifactFromDocument(gaDocument);
        String targetArtifactId = genericArtifact.getTargetArtifactId();
        String parentArtifactId = genericArtifact.getDepParentTargetArtifactId();
        String targetRepositoryId = genericArtifact.getTargetRepositoryId();
        IConnection connection = null;
        GenericArtifact parentArtifact = null;
        List<String> targetAutimeAndTxnId = null;
        List<String> targetAutimeAndTxnIdParent = null;

        try {
            connection = this.connect(genericArtifact);
            if (QCConnectionFactory.isDefectRepository(targetRepositoryId)) {
                attachmentHandler.deleteAttachmentForDefect(connection, parentArtifactId, targetArtifactId);
                getFieldValueFromGenericArtifact(genericArtifact, AttachmentMetaData.getAttachmentName());
                log.info("Attachment " + targetArtifactId + " is deleted from defect " + parentArtifactId + " on "
                        + genericArtifact.getTargetRepositoryId());
                targetAutimeAndTxnId = getAuTimeAndTxnIdForAttachment(connection, targetArtifactId);

                targetAutimeAndTxnIdParent = getAuTimeAndTxnIdForDefect(connection, parentArtifactId);
            } else {
                attachmentHandler.deleteAttachmentForRequirement(connection, parentArtifactId, targetArtifactId);
                getFieldValueFromGenericArtifact(genericArtifact, AttachmentMetaData.getAttachmentName());
                log.info("Attachment " + targetArtifactId + " is deleted from defect " + parentArtifactId + " on "
                        + genericArtifact.getTargetRepositoryId());
                targetAutimeAndTxnId = getAuTimeAndTxnIdForAttachment(connection, targetArtifactId);

                targetAutimeAndTxnIdParent = getAuTimeAndTxnIdForRequirement(connection, parentArtifactId);
            }
            genericArtifact.setTargetArtifactVersion(targetAutimeAndTxnId.get(0));
            genericArtifact.setTargetArtifactLastModifiedDate(
                    DateUtil.format(DateUtil.parseQCDate(targetAutimeAndTxnId.get(1))));

            parentArtifact = new GenericArtifact();
            // make sure that we do not update the synchronization status record for replayed attachments
            parentArtifact.setTransactionId(genericArtifact.getTransactionId());
            parentArtifact.setArtifactType(GenericArtifact.ArtifactTypeValue.PLAINARTIFACT);
            parentArtifact.setArtifactAction(GenericArtifact.ArtifactActionValue.UPDATE);
            parentArtifact.setArtifactMode(GenericArtifact.ArtifactModeValue.CHANGEDFIELDSONLY);
            parentArtifact.setConflictResolutionPriority(genericArtifact.getConflictResolutionPriority());
            parentArtifact.setSourceArtifactId(genericArtifact.getDepParentSourceArtifactId());
            parentArtifact.setSourceArtifactLastModifiedDate(genericArtifact.getSourceArtifactLastModifiedDate());
            parentArtifact.setSourceArtifactVersion(genericArtifact.getSourceArtifactVersion());
            parentArtifact.setSourceRepositoryId(genericArtifact.getSourceRepositoryId());
            parentArtifact.setSourceSystemId(genericArtifact.getSourceSystemId());
            parentArtifact.setSourceSystemKind(genericArtifact.getSourceSystemKind());
            parentArtifact.setSourceRepositoryKind(genericArtifact.getSourceRepositoryKind());
            parentArtifact.setSourceSystemTimezone(genericArtifact.getSourceSystemTimezone());

            parentArtifact.setTargetArtifactId(parentArtifactId);
            parentArtifact.setTargetArtifactLastModifiedDate(
                    DateUtil.format(DateUtil.parseQCDate(targetAutimeAndTxnIdParent.get(1))));
            parentArtifact.setTargetArtifactVersion(targetAutimeAndTxnIdParent.get(0));
            parentArtifact.setTargetRepositoryId(genericArtifact.getTargetRepositoryId());
            parentArtifact.setTargetRepositoryKind(genericArtifact.getTargetRepositoryKind());
            parentArtifact.setTargetSystemId(genericArtifact.getTargetSystemId());
            parentArtifact.setTargetSystemKind(genericArtifact.getTargetSystemKind());
            parentArtifact.setTargetSystemTimezone(genericArtifact.getTargetSystemTimezone());
        } catch (Exception e) {
            String cause = "Exception while trying to delete attachment with id " + targetArtifactId
                    + "from artifact " + parentArtifactId;
            log.error(cause, e);
            throw new CCFRuntimeException(cause, e);
        } finally {
            if (connection != null)
                this.disconnect(connection);
        }
        Document attachmentDoc = this.returnDocument(genericArtifact);
        Document parentDoc = parentArtifact == null ? null : this.returnDocument(parentArtifact);
        return new Document[] { attachmentDoc, parentDoc };
    }

    @Override
    public Document deleteDependency(Document gaDocument) {
        // throw new
        // CCFRuntimeException("deleteDependency is not implemented...!");
        log.warn("deleteDependency is not implemented...!");
        return null;
    }

    public List<String> getAuTimeAndTxnIdForAttachment(IConnection qcc, String attachmentId) {

        List<String> txnIdAndAutime = new ArrayList<String>();
        String transactionId = null;
        String auTime = null;
        String sql = null;

        sql = "select AU_TIME, AU_ACTION_ID from audit_log where au_entity_id = '" + attachmentId
                + "' and au_entity_type='CROS_REF' order by au_action_id desc";

        IRecordSet newRs = null;
        try {
            log.debug("In QCDefectHandler.getAuTimeAndTxnIdForAttachment, sql=" + sql);
            newRs = qcc.executeSQL(sql);
            int newRc = newRs.getRecordCount();
            for (int newCnt = 0; newCnt < newRc; newCnt++, newRs.next()) {
                if (newCnt == 0) {
                    transactionId = newRs.getFieldValueAsString("AU_ACTION_ID");
                    auTime = newRs.getFieldValueAsString("AU_TIME");
                    break;
                }
            }
        } finally {
            if (newRs != null) {
                newRs.safeRelease();
            }
        }
        txnIdAndAutime.add(transactionId);
        txnIdAndAutime.add(auTime);

        return txnIdAndAutime;
    }

    public List<String> getAuTimeAndTxnIdForDefect(IConnection qcc, String defectId) {

        List<String> txnIdAndAutime = new ArrayList<String>();
        String transactionId = null;
        String auTime = null;
        String sql = null;

        sql = "select AU_TIME, AU_ACTION_ID, AU_DESCRIPTION from audit_log where au_entity_id = '" + defectId
                + "' and au_entity_type='BUG' and au_father_id='-1' order by au_action_id desc";

        IRecordSet newRs = null;
        try {
            newRs = qcc.executeSQL(sql);
            log.debug("In QCWriter.getAuTimeAndTxnIdForDefect, sql=" + sql);
            int newRc = newRs.getRecordCount();
            for (int newCnt = 0; newCnt < newRc; newCnt++, newRs.next()) {
                if (newCnt == 0) {
                    transactionId = newRs.getFieldValueAsString("AU_ACTION_ID");
                    auTime = newRs.getFieldValueAsString("AU_TIME");

                    String transactionDesc = newRs.getFieldValueAsString("AU_DESCRIPTION");
                    if (transactionDesc != null) {
                        if (transactionDesc.contains("Attachment added:")) {
                            int transactionIdInt = Integer.parseInt(transactionId);
                            transactionIdInt -= 2;
                            transactionId = Integer.toString(transactionIdInt);
                        } else if (transactionDesc.contains("Attachment deleted:")) {
                            int transactionIdInt = Integer.parseInt(transactionId);
                            transactionIdInt -= 1;
                            transactionId = Integer.toString(transactionIdInt);
                        }
                    }
                    break;
                }
            }
        } finally {
            if (newRs != null) {
                newRs.safeRelease();
            }
        }
        txnIdAndAutime.add(transactionId);
        txnIdAndAutime.add(auTime);

        return txnIdAndAutime;
    }

    public List<String> getAuTimeAndTxnIdForRequirement(IConnection qcc, String requirementId) {

        List<String> txnIdAndAutime = new ArrayList<String>();
        String transactionId = null;
        String auTime = null;
        String sql = null;

        sql = "select AU_TIME, AU_ACTION_ID, AU_DESCRIPTION from audit_log where au_entity_id = '" + requirementId
                + "' and au_entity_type='REQ' and au_father_id='-1' order by au_action_id desc";

        IRecordSet newRs = null;
        try {
            log.debug("In QCWriter.getAuTimeAndTxnIdForRequirement, sql=" + sql);
            newRs = qcc.executeSQL(sql);
            int newRc = newRs.getRecordCount();
            for (int newCnt = 0; newCnt < newRc; newCnt++, newRs.next()) {
                if (newCnt == 0) {
                    transactionId = newRs.getFieldValueAsString("AU_ACTION_ID");
                    auTime = newRs.getFieldValueAsString("AU_TIME");
                    String transactionDesc = newRs.getFieldValueAsString("AU_DESCRIPTION");
                    if (transactionDesc != null) {
                        if (transactionDesc.contains("Attachment added:")) {
                            int transactionIdInt = Integer.parseInt(transactionId);
                            transactionIdInt -= 2;
                            transactionId = Integer.toString(transactionIdInt);
                        } else if (transactionDesc.contains("Attachment deleted:")) {
                            int transactionIdInt = Integer.parseInt(transactionId);
                            transactionIdInt -= 1;
                            transactionId = Integer.toString(transactionIdInt);
                        }
                    }
                    break;
                }
            }
        } finally {
            if (newRs != null) {
                newRs.safeRelease();
            }
        }
        txnIdAndAutime.add(transactionId);
        txnIdAndAutime.add(auTime);

        return txnIdAndAutime;
    }

    /**
     * @return the number of CCF operations that will be performed before CCF
     *         will destroy all COM objects This is to avoid COM memory leaks
     */
    public int getCountBeforeCOMReinitialization() {
        return countBeforeCOMReinitialization;
    }

    public QCHandler getDefectHandler() {
        return artifactHandler;
    }

    /**
     * If this property is enabled, QCWriter will attempt to figure out if the
     * field value has remained the same apart from formatting differences
     * incurred by the conversion to plain text. If so, the field will not be
     * updated in order to preserve the existing formatting.
     * 
     * @return whether QCWriter will attempt to preserve HTML formatting.
     */
    public boolean getPreserveSemanticallyUnchangedHTMLFieldValues() {
        return preserveSemanticallyUnchangedHTMLFieldValues;
    }

    public String getServerUrl() {
        return serverUrl;
    }

    /**
     * Gets the mandatory user name The user name is used to login into the HP
     * QC instance whenever an artifact should be updated or extracted. This
     * user has to differ from the resync user in order to force initial resyncs
     * with the source system once a new artifact has been created.
     * 
     * @return the userName
     */
    public String getUserName() {
        return userName;
    }

    @Override
    public boolean handleException(Throwable rootCause, ConnectionManager<IConnection> connectionManager,
            Document ga) {
        if (rootCause == null)
            return false;
        if (rootCause instanceof ConnectionException) {
            Throwable cause = rootCause.getCause();
            handleException(cause, connectionManager, ga);
            if (connectionManager.isEnableRetryAfterNetworkTimeout()) {
                return true;
            }
        } else if (rootCause instanceof com.jacob.com.ComFailException) {
            com.jacob.com.ComFailException comEx = (com.jacob.com.ComFailException) rootCause;
            String message = comEx.getMessage();
            boolean connectionErrorOccured = false;
            if (message.contains("Server is not available")) {
                connectionErrorOccured = true;
            } else if (message.contains("Your Quality Center session has been disconnected")) {
                connectionErrorOccured = true;
                this.reInitCOM();
            } else if (message.contains("The Object is locked by")) {
                if (!immediatelyQuarantineLockedDefects) {
                    connectionErrorOccured = true;
                } else {
                    // set new error code
                    ga.getRootElement().addAttribute(GenericArtifactHelper.ERROR_CODE,
                            GenericArtifact.ERROR_OBJECT_LOCKED);
                }
            } else if (message.contains("The server threw an exception.")) {
                connectionErrorOccured = true;
                this.reInitCOM();
            } else if (message.contains("Session authenticity broken")) {
                connectionErrorOccured = true;
                this.reInitCOM();
            } else if (message.contains("Server has been disconnected")) {
                connectionErrorOccured = true;
                this.reInitCOM();
            } else if (message.contains("Project is not connected")) {
                connectionErrorOccured = true;
                this.reInitCOM();
            } else if (message.contains("You do not have the required permissions to execute this action.")
                    && !isQuarantineIfQCReportsPermissionDenied()) {
                connectionErrorOccured = true;
                this.reInitCOM();
            } else if (message.contains("Failed to Run Query")) {
                connectionErrorOccured = true;
                this.reInitCOM();
            } else if (message.contains("Failed to Check Out") || message.contains("Failed to lock REQ entity")) {
                // set new error code
                ga.getRootElement().addAttribute(GenericArtifactHelper.ERROR_CODE,
                        GenericArtifact.ERROR_OBJECT_CHECKED_OUT);
            } else if (message.contains("Failed to Connect Project")) {
                log.warn("The QC Project might have been de-activated. Please activate this project");
                connectionErrorOccured = true;
                this.reInitCOM();
            }
            if (connectionManager.isEnableRetryAfterNetworkTimeout() && connectionErrorOccured) {
                return true;
            }
        } else if (rootCause instanceof DefectAlreadyLockedException) {
            if (!immediatelyQuarantineLockedDefects) {
                return true;
            } else {
                // set new error code
                ga.getRootElement().addAttribute(GenericArtifactHelper.ERROR_CODE,
                        GenericArtifact.ERROR_OBJECT_LOCKED);
            }
        } else if (rootCause instanceof CCFRuntimeException) {
            Throwable cause = rootCause.getCause();
            return handleException(cause, connectionManager, ga);
        }
        return false;
    }

    /**
     * If this property is set to true (true by default), locked defects will be
     * quarantined and the operation will never be retried
     */
    public boolean isImmediatelyQuarantineLockedDefects() {
        return immediatelyQuarantineLockedDefects;
    }

    public boolean isQuarantineIfQCReportsPermissionDenied() {
        return quarantineIfQCReportsPermissionDenied;
    }

    public boolean isUseAlternativeFieldName() {
        return useAlternativeFieldName;
    }

    public Object[] process(Object data) {
        Object[] result = null;
        if (this.connectCounts == 0) {
            initCOM();
        }
        try {
            result = super.process(data);
        } finally {
            if (this.connectCounts >= getCountBeforeCOMReinitialization()) {
                this.connectCounts = 0;
                tearDownCOM();
            }
        }
        return result;
    }

    /**
     * Setters and geters for various private variables of this class.
     * 
     * 
     */

    public void reset(Object context) {
    }

    /**
     * @param countBeforeCOMReinitialization
     *            sets the number of CCF operations that will be performed
     *            before CCF will destroy all COM objects This is to avoid COM
     *            memory leaks
     */
    public void setCountBeforeCOMReinitialization(int countBeforeCOMReinitialization) {
        this.countBeforeCOMReinitialization = countBeforeCOMReinitialization;
    }

    /**
     * If this property is set to true (true by default), locked defects will be
     * quarantined and the operation will never be retried
     */
    public void setImmediatelyQuarantineLockedDefects(boolean immediatelyQuarantineLockedDefects) {
        this.immediatelyQuarantineLockedDefects = immediatelyQuarantineLockedDefects;
    }

    /**
     * Sets the password that belongs to the username
     * 
     * @param password
     *            the password to set
     */
    public void setPassword(String password) {
        this.password = Obfuscator.deObfuscatePassword(password);
    }

    /**
     * Define whether QCWriter will attempt to figure out if the field value has
     * remained the same apart from formatting differences incurred by the
     * conversion to plain text. If so, the field will not be updated in order
     * to preserve the existing formatting. Defaults to false.
     */
    public void setPreserveSemanticallyUnchangedHTMLFieldValues(
            boolean preserveSemanticallyUnchangedHTMLFieldValues) {
        this.preserveSemanticallyUnchangedHTMLFieldValues = preserveSemanticallyUnchangedHTMLFieldValues;
    }

    public void setQuarantineIfQCReportsPermissionDenied(boolean quarantineIfQCReportsPermissionDenied) {
        this.quarantineIfQCReportsPermissionDenied = quarantineIfQCReportsPermissionDenied;
    }

    /**
     * Sets the optional resync password that belongs to the resync user
     * 
     * @param resyncPassword
     *            the resyncPassword to set
     */
    public void setResyncPassword(String resyncPassword) {
        this.resyncPassword = Obfuscator.deObfuscatePassword(resyncPassword);
    }

    /**
     * Sets the optional resync username
     * 
     * The resync user name is used to login into the HP QC instance whenever an
     * artifact should be created. This user has to differ from the ordinary
     * user used to log in in order to force initial resyncs with the source
     * system once a new artifact has been created.
     * 
     * @param resyncUserName
     *            the resyncUserName to set
     */
    public void setResyncUserName(String resyncUserName) {
        this.resyncUserName = resyncUserName;
    }

    public void setServerUrl(String serverUrl) {
        this.serverUrl = serverUrl;
    }

    public void setUseAlternativeFieldName(boolean useAlternativeFieldName) {
        this.useAlternativeFieldName = useAlternativeFieldName;
    }

    /**
     * Sets the mandatory username
     * 
     * The user name is used to login into the HP QC instance whenever an
     * artifact should be updated or extracted. This user has to differ from the
     * resync user in order to force initial resyncs with the source system once
     * a new artifact has been created.
     * 
     * @param userName
     *            the user name to set
     */
    public void setUserName(String username) {
        this.userName = username;
    }

    @Override
    public void stop() {
        log.info("Got signal to stop QC connector ...");
        while (isConnected) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                // Digest the exception
                break;
            }
        }
        tearDownCOM();
        super.stop();
    }

    public void tearDownCOM() {
        synchronized (log) {
            if (comInitialized) {
                getConnectionManager().tearDown();
                ComHandle.tearDownCOM();
                comInitialized = false;
            }
        }
    }

    @Override
    public Document updateArtifact(Document gaDocument) {
        GenericArtifact genericArtifact = getArtifactFromDocument(gaDocument);
        List<GenericArtifactField> allFields = this.getAllGenericArtfactFields(genericArtifact);
        String targetArtifactId = genericArtifact.getTargetArtifactId();
        String targetRepositoryId = genericArtifact.getTargetRepositoryId();
        IConnection connection = this.connect(genericArtifact);
        try {
            if (allFields != null) {
                if (QCConnectionFactory.isDefectRepository(targetRepositoryId)) {
                    this.updateDefect(connection, targetArtifactId, genericArtifact, allFields);
                } else {
                    this.updateRequirement(connection, targetArtifactId, genericArtifact, allFields);
                }
            } else {
                String cause = "No field for the artifact " + targetArtifactId + " is supplied";
                log.error(cause);
                throw new CCFRuntimeException(cause);
            }
        } catch (Exception e) {
            String cause = "Exception occured while updating artifact in QC:" + genericArtifact.toString();
            log.error(cause, e);
            genericArtifact.setErrorCode(GenericArtifact.ERROR_EXTERNAL_SYSTEM_WRITE);
            // sending this artifact to HOSPITAL
            throw new CCFRuntimeException(cause, e);

        } finally {
            this.disconnect(connection);
            connection = null;
        }
        return this.returnDocument(genericArtifact);
    }

    @Override
    public Document updateAttachment(Document gaDocument) {
        // throw new
        // CCFRuntimeException("updateAttachment is not implemented...!");
        log.warn("updateAttachment is not implemented...!");
        return null;
    }

    @Override
    public Document updateDependency(Document gaDocument) {
        // throw new
        // CCFRuntimeException("updateDependency is not implemented...!");
        log.warn("updateDependency is not implemented...!");
        return null;
    }

    @SuppressWarnings("unchecked")
    public void validate(List exceptions) {
        super.validate(exceptions);

        /*
         * if (getResyncUserName() == null) { log .warn(
         * "resyncUserName-property has not been set, so that initial resyncs after artifact creation are not possible."
         * ); }
         */

        if (this.getServerUrl() == null) {
            exceptions.add(new ValidationException("serverUrl property is not set for the QCWriter", this));
        }
        if (this.getUserName() == null) {
            exceptions.add(new ValidationException("userName property is not set for the QCWriter", this));
        }
        if (this.getPassword() == null) {
            exceptions.add(new ValidationException("password property is not set for the QCWriter", this));
        }

        if (exceptions.size() == 0) {
            artifactHandler = new QCHandler(isUseAlternativeFieldName());
            attachmentHandler = new QCAttachmentHandler();
            qcGAHelper = new QCGAHelper();
        }
    }

    /**
     * Disconnects from the QC using the ConnectionManager.
     * 
     * @param connection
     */
    protected void disconnect(IConnection connection) {
        ConnectionManager<IConnection> connectionManager = (ConnectionManager<IConnection>) this
                .getConnectionManager();
        if (connection != null) {
            connectionManager.releaseConnection(connection);
        }
        isConnected = false;
    }

    protected List<GenericArtifactField> getAllGenericArtfactFields(GenericArtifact genericArtifact) {
        List<GenericArtifactField> allFields = genericArtifact.getAllGenericArtifactFields();
        if (allFields != null)
            genericArtifact = concatValuesOfSameFieldNames(genericArtifact);
        allFields = genericArtifact.getAllGenericArtifactFields();
        return allFields;
    }

    protected Document returnDocument(GenericArtifact genericArtifact) {
        Document resultDoc = null;
        try {
            resultDoc = GenericArtifactHelper.createGenericArtifactXMLDocument(genericArtifact);
        } catch (GenericArtifactParsingException e) {
            String cause = "Problem occured while parsing the GenericArtifact into Document";
            log.error(cause, e);
            genericArtifact.setErrorCode(GenericArtifact.ERROR_GENERIC_ARTIFACT_PARSING);
            throw new CCFRuntimeException(cause, e);
        }
        return resultDoc;
    }

    /**
     * Update the artifact and do conflict resolution
     * 
     * @param connection
     * @param targetArtifactId
     * @param genericArtifact
     * @param allFields
     * @throws Exception
     */
    protected void updateDefect(IConnection connection, String targetArtifactId, GenericArtifact genericArtifact,
            List<GenericArtifactField> allFields) throws Exception {
        String targetSystemTimezone = genericArtifact.getTargetSystemTimezone();

        // retrieve version to update
        List<String> targetAutimeAndTxnIdBeforeUpdate = getAuTimeAndTxnIdForDefect(connection, targetArtifactId);
        String targetTransactionIdBeforeUpdate = targetAutimeAndTxnIdBeforeUpdate.get(0);
        int targetTransactionIdBeforeUpdateInt = getTargetTransactionIdBeforeUpdate(
                targetTransactionIdBeforeUpdate);
        // now do conflict resolution
        if (!AbstractWriter.handleConflicts(targetTransactionIdBeforeUpdateInt, genericArtifact)) {
            return;
        }
        String conflictResolutionPriority = genericArtifact.getConflictResolutionPriority();
        boolean ignoreLocks = conflictResolutionPriority
                .equals(GenericArtifact.VALUE_CONFLICT_RESOLUTION_PRIORITY_ALWAYS_OVERRIDE_AND_IGNORE_LOCKS);

        // IQCDefect updatedArtifact = defectHandler.updateDefect(
        // connection, targetArtifactId, allFields, this
        // .getUserName(), targetSystemTimezone);

        // FIXME This is not atomic
        artifactHandler.updateDefect(connection, targetArtifactId, allFields, this.getUserName(),
                targetSystemTimezone, getPreserveSemanticallyUnchangedHTMLFieldValues(), ignoreLocks,
                genericArtifact);
        log.info("QC Defect " + targetArtifactId + " on " + genericArtifact.getTargetRepositoryId()
                + " is updated successfully with the changes from " + genericArtifact.getSourceArtifactId() + " on "
                + genericArtifact.getSourceRepositoryId());
        genericArtifact.setTargetArtifactId(targetArtifactId);
        // FIXME This is not atomic
        List<String> targetAutimeAndTxnId = getAuTimeAndTxnIdForDefect(connection, targetArtifactId);
        genericArtifact.setTargetArtifactVersion(targetAutimeAndTxnId.get(0));
        genericArtifact.setTargetArtifactLastModifiedDate(
                DateUtil.format(DateUtil.parseQCDate(targetAutimeAndTxnId.get(1))));
    }

    /**
     * Update the requirement and do conflict resolution
     * 
     * @param connection
     * @param targetArtifactId
     * @param genericArtifact
     * @param allFields
     * @throws Exception
     */
    protected void updateRequirement(IConnection connection, String targetArtifactId,
            GenericArtifact genericArtifact, List<GenericArtifactField> allFields) throws Exception {
        String targetSystemTimezone = genericArtifact.getTargetSystemTimezone();

        // retrieve version to update
        List<String> targetAutimeAndTxnIdBeforeUpdate = getAuTimeAndTxnIdForRequirement(connection,
                targetArtifactId);
        String targetTransactionIdBeforeUpdate = targetAutimeAndTxnIdBeforeUpdate.get(0);
        int targetTransactionIdBeforeUpdateInt = getTargetTransactionIdBeforeUpdate(
                targetTransactionIdBeforeUpdate);
        // now do conflict resolution
        if (!AbstractWriter.handleConflicts(targetTransactionIdBeforeUpdateInt, genericArtifact)) {
            return;
        }
        String targetParentArtifactId = genericArtifact.getDepParentTargetArtifactId();

        String conflictResolutionPriority = genericArtifact.getConflictResolutionPriority();
        boolean ignoreLocks = conflictResolutionPriority
                .equals(GenericArtifact.VALUE_CONFLICT_RESOLUTION_PRIORITY_ALWAYS_OVERRIDE_AND_IGNORE_LOCKS);

        // IQCDefect updatedArtifact = defectHandler.updateDefect(
        // connection, targetArtifactId, allFields, this
        // .getUserName(), targetSystemTimezone);

        // FIXME This is not atomic
        if (targetArtifactId.equals("0")) {
            log.warn("It is not possible to modify the root level requirement folder, so ignoring the update ...");
        } else {
            artifactHandler.updateRequirement(connection, targetArtifactId, allFields, this.getUserName(),
                    targetSystemTimezone, targetParentArtifactId, getPreserveSemanticallyUnchangedHTMLFieldValues(),
                    ignoreLocks, genericArtifact);
            log.info("QC Requirement " + targetArtifactId + " on " + genericArtifact.getTargetRepositoryId()
                    + " is updated successfully with the changes from " + genericArtifact.getSourceArtifactId()
                    + " on " + genericArtifact.getSourceRepositoryId());
        }
        genericArtifact.setTargetArtifactId(targetArtifactId);
        // FIXME This is not atomic
        List<String> targetAutimeAndTxnId = getAuTimeAndTxnIdForRequirement(connection, targetArtifactId);
        genericArtifact.setTargetArtifactVersion(targetAutimeAndTxnId.get(0));
        genericArtifact.setTargetArtifactLastModifiedDate(
                DateUtil.format(DateUtil.parseQCDate(targetAutimeAndTxnId.get(1))));
    }

    /**
     * Gets the mandatory password that belongs to the username
     * 
     * @return the password
     */
    private String getPassword() {
        return password;
    }

    /**
     * Gets the optional resync password that belongs to the resync user
     * 
     * @return the resyncPassword
     */
    private String getResyncPassword() {
        return resyncPassword;
    }

    /**
     * Gets the optional resync username The resync user name is used to login
     * into the HP QC instance whenever an artifact should be created. This user
     * has to differ from the ordinary user used to log in in order to force
     * initial resyncs with the source system once a new artifact has been
     * created.
     * 
     * @return the resyncUserName
     */
    private String getResyncUserName() {
        return resyncUserName;
    }

    private int getTargetTransactionIdBeforeUpdate(String targetTransactionIdBeforeUpdate) {
        int transactionIdBeforeUpdate;
        try {
            transactionIdBeforeUpdate = Integer.parseInt(targetTransactionIdBeforeUpdate);
        } catch (NumberFormatException e) {
            return 0;
        }
        return transactionIdBeforeUpdate;

    }

    private void initCOM() {
        synchronized (log) {
            if (!comInitialized) {
                ComHandle.initCOM();
                comInitialized = true;
            }
        }
    }

    private void reInitCOM() {
        this.tearDownCOM();
        this.initCOM();
    }

    /**
     * Converts the genericArtifactDocument into GenericArtifact Java object
     * using the GenericArtifactHelper methods.
     * 
     * @param genericArtifactDocument
     * @return GenericArtifact The converted GenericArtifact from the dom4j
     *         Document.
     */
    public static GenericArtifact getArtifactFromDocument(Document genericArtifactDocument) {

        GenericArtifact genericArtifact = null;

        try {
            genericArtifact = GenericArtifactHelper.createGenericArtifactJavaObject(genericArtifactDocument);
        } catch (GenericArtifactParsingException e) {
            String message = "Exception occured while parsing the Document into a GenericArtifact";
            log.error(message);
            throw new CCFRuntimeException(message, e);
        }

        return genericArtifact;
    }

    /**
     * Obtains the value of the specified field from the incoming Document.
     * 
     * @param individualGenericArtifact
     * @param fieldName
     * @return String
     */
    public static String getFieldValueFromGenericArtifact(GenericArtifact individualGenericArtifact,
            String fieldName) {

        String fieldValue = null;
        if (individualGenericArtifact.getAllGenericArtifactFields() != null
                && individualGenericArtifact.getAllGenericArtifactFieldsWithSameFieldName(fieldName) != null)
            fieldValue = (String) individualGenericArtifact.getAllGenericArtifactFieldsWithSameFieldName(fieldName)
                    .get(0).getFieldValue();

        return fieldValue;
    }

}