com.siemens.sw360.fossology.handler.FossologyFileHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.siemens.sw360.fossology.handler.FossologyFileHandler.java

Source

/*
 * Copyright Siemens AG, 2013-2015. Part of the SW360 Portal Project.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package com.siemens.sw360.fossology.handler;

import com.siemens.sw360.datahandler.common.CommonUtils;
import com.siemens.sw360.datahandler.couchdb.AttachmentConnector;
import com.siemens.sw360.datahandler.thrift.RequestStatus;
import com.siemens.sw360.datahandler.thrift.SW360Exception;
import com.siemens.sw360.datahandler.thrift.ThriftClients;
import com.siemens.sw360.datahandler.thrift.attachments.Attachment;
import com.siemens.sw360.datahandler.thrift.attachments.AttachmentContent;
import com.siemens.sw360.datahandler.thrift.attachments.FilledAttachment;
import com.siemens.sw360.datahandler.thrift.components.ClearingState;
import com.siemens.sw360.datahandler.thrift.components.ComponentService;
import com.siemens.sw360.datahandler.thrift.components.FossologyStatus;
import com.siemens.sw360.datahandler.thrift.components.Release;
import com.siemens.sw360.datahandler.thrift.users.User;
import com.siemens.sw360.fossology.ssh.FossologyUploader;
import org.apache.log4j.Logger;
import org.apache.thrift.TException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.util.*;

import static com.google.common.base.Predicates.equalTo;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.Iterables.all;
import static com.google.common.collect.Maps.newHashMap;
import static com.google.common.collect.Sets.union;
import static com.siemens.sw360.datahandler.common.CommonUtils.*;
import static com.siemens.sw360.datahandler.common.SW360Assert.assertNotNull;
import static com.siemens.sw360.datahandler.common.SW360Assert.failIf;
import static java.util.Collections.singleton;
import static org.apache.log4j.Logger.getLogger;

@Component
public class FossologyFileHandler {

    private final static Logger log = getLogger(FossologyFileHandler.class);

    private final AttachmentConnector attachmentConnector;
    private final FossologyUploader fossologyUploader;
    private final ThriftClients thriftClients;

    @Autowired
    public FossologyFileHandler(AttachmentConnector attachmentConnector, FossologyUploader fossologyUploader,
            ThriftClients thriftClients) {
        this.attachmentConnector = attachmentConnector;
        this.fossologyUploader = fossologyUploader;
        this.thriftClients = thriftClients;
    }

    public RequestStatus sendToFossology(String releaseId, User user, String clearingTeam) throws TException {
        Release release;
        final ComponentService.Iface componentClient = thriftClients.makeComponentClient();

        release = getReleaseAndUnlockIt(releaseId, user, componentClient);

        Set<Attachment> sourceAttachments = getSourceAttachment(releaseId, user, componentClient);

        if (sourceAttachments.size() != 1) {
            log.error("release " + releaseId + " does not have a single source attachment");
            return RequestStatus.FAILURE; //TODO return a summary and better fitting status
        }

        final Attachment attachment = getFirst(sourceAttachments);
        final FilledAttachment filledAttachment = fillAttachment(attachment);

        if (!release.isSetFossologyId()) {
            /* send the attachment as a new upload */
            AttachmentContent attachmentContent = filledAttachment.getAttachmentContent();
            return sendToFossologyNewUpload(releaseId, user, clearingTeam, attachmentContent, componentClient);
        } else {
            if (!checkSourceAttachment(release, filledAttachment)) {
                return RequestStatus.FAILURE; // TODO summary
            } else {
                /* duplicate the old upload making it visible for clearingTeam */
                return sendToFossologyExistingUpload(release, user, clearingTeam, componentClient);
            }
        }
    }

    protected boolean checkSourceAttachment(Release release, FilledAttachment filledAttachment) {
        boolean check = true;
        if (!Objects.equals(filledAttachment.getAttachmentContent().getId(), release.getAttachmentInFossology())) {
            log.error("the source attachment of this release is not the one in FOSSology (rel=" + release.getId()
                    + ")");
            check = false;
        }
        return check;
    }

    private RequestStatus sendToFossologyNewUpload(String releaseId, User user, String clearingTeam,
            AttachmentContent attachmentContent, ComponentService.Iface componentService) throws TException {
        InputStream stream = attachmentConnector.getAttachmentStream(attachmentContent);
        try {
            int fossologyUploadId = fossologyUploader.uploadToFossology(stream, attachmentContent, clearingTeam);
            if (fossologyUploadId > 0) {
                //to avoid race conditions, get the object again
                Release release = assertNotNull(componentService.getReleaseById(releaseId, user));
                setFossologyStatus(release, clearingTeam, FossologyStatus.SENT, Integer.toString(fossologyUploadId),
                        attachmentContent.getId());
                updateReleaseClearingState(release, FossologyStatus.SENT);
                updateRelease(release, user, componentService);
                return RequestStatus.SUCCESS;
            }
        } finally {
            closeQuietly(stream, log);
        }

        return RequestStatus.FAILURE;
    }

    private RequestStatus sendToFossologyExistingUpload(Release release, User user, String clearingTeam,
            ComponentService.Iface componentClient) throws TException {
        int fossologyUploadId;
        fossologyUploadId = toUnsignedInt(release.getFossologyId());
        failIf(fossologyUploadId <= 0, "release %s has an inconsistent FossologyId", release.getId());

        FossologyStatus currentStatus = fossologyUploader.getStatusInFossology(fossologyUploadId, clearingTeam);
        if (isVisible(currentStatus)) {
            updateFossologyStatus(release, user, clearingTeam, currentStatus, componentClient);
            return RequestStatus.SUCCESS;
        } else {
            return duplicateUploadFor(release, user, clearingTeam, fossologyUploadId, componentClient);
        }
    }

    private RequestStatus duplicateUploadFor(Release release, User user, String clearingTeam, int fossologyUploadId,
            ComponentService.Iface componentClient) throws TException {
        boolean success;
        success = fossologyUploader.duplicateInFossology(fossologyUploadId, clearingTeam);
        if (success) {
            updateFossologyStatus(release, user, clearingTeam, FossologyStatus.SENT, componentClient);
            return RequestStatus.SUCCESS;
        } else {
            return RequestStatus.FAILURE;
        }
    }

    private void updateFossologyStatus(Release release, User user, String clearingTeam,
            FossologyStatus currentStatus, ComponentService.Iface componentService) throws TException {
        setFossologyStatus(release, clearingTeam, currentStatus);
        updateReleaseClearingState(release, currentStatus);
        updateRelease(release, user, componentService);
    }

    protected static boolean isVisible(FossologyStatus statusInFossology) {
        return statusInFossology != null && FossologyStatus.INACCESSIBLE.compareTo(statusInFossology) < 0;
    }

    protected static boolean isError(FossologyStatus status) {
        return status == null || status.compareTo(FossologyStatus.ERROR) <= 0;
    }

    public Release getStatusInFossology(String releaseId, User user, String clearingTeam) throws TException {
        final Release release;
        final ComponentService.Iface componentClient;

        try {
            componentClient = thriftClients.makeComponentClient();
            release = componentClient.getReleaseById(releaseId, user);
            assertNotNull(release);
        } catch (TException e) {
            log.error("cannot get release " + releaseId, e);
            throw e;
        }

        if (release.isSetFossologyId()) {
            int fossologyId = CommonUtils.toUnsignedInt(release.getFossologyId());

            boolean updateInDb = true;
            for (String allClearingTeam : getAllClearingTeams(release, clearingTeam)) {
                final FossologyStatus status = fossologyUploader.getStatusInFossology(fossologyId, allClearingTeam);
                setFossologyStatus(release, allClearingTeam, status);

                if (isError(status)) {
                    updateInDb = false;
                }
            }
            Optional<FossologyStatus> maxStatus = nullToEmptyMap(release.getClearingTeamToFossologyStatus())
                    .values().stream().max(FossologyStatus::compareTo);
            updateReleaseClearingState(release, maxStatus);

            if (updateInDb) {
                updateRelease(release, user, componentClient);
            }
        }

        getReleaseAndUnlockIt(releaseId, user, componentClient); // just unlockit

        return release;
    }

    private void updateReleaseClearingState(Release release, FossologyStatus fossologyStatus) {
        updateReleaseClearingState(release, Optional.of(fossologyStatus));
    }

    private void updateReleaseClearingState(Release release, Optional<FossologyStatus> fossologyStatus) {
        Optional<ClearingState> newClearingState = fossologyStatus.flatMap(this::mapFossologyStatusToClearingState);
        if (newClearingState.isPresent() && newClearingState.get().compareTo(release.getClearingState()) > 0) {
            release.setClearingState(newClearingState.get());
        }
    }

    private Optional<ClearingState> mapFossologyStatusToClearingState(FossologyStatus fossologyStatus) {
        if (fossologyStatus == FossologyStatus.IN_PROGRESS) {
            return Optional.of(ClearingState.UNDER_CLEARING);
        } else if (fossologyStatus.compareTo(FossologyStatus.SENT) >= 0
                && fossologyStatus.compareTo(FossologyStatus.IN_PROGRESS) < 0) {
            return Optional.of(ClearingState.SENT_TO_FOSSOLOGY);
        }
        return Optional.empty();
    }

    protected Release getReleaseAndUnlockIt(String releaseId, User user, ComponentService.Iface componentClient)
            throws TException {
        final Release release = componentClient.getReleaseById(releaseId, user);

        assertNotNull(release, "cannot get release %s", releaseId);

        final Collection<FossologyStatus> fossologyStatuses = nullToEmptyMap(
                release.getClearingTeamToFossologyStatus()).values();

        if (!fossologyStatuses.isEmpty() && all(fossologyStatuses, equalTo(FossologyStatus.REJECTED))) {
            release.unsetAttachmentInFossology();
            release.unsetFossologyId();
            release.unsetClearingTeamToFossologyStatus();

            updateRelease(release, user, componentClient);
        }

        return release;
    }

    // not static to ease testing
    protected Iterable<? extends String> getAllClearingTeams(Release release, String clearingTeam) {
        Set<String> alreadyChecked = nullToEmptyMap(release.getClearingTeamToFossologyStatus()).keySet();
        return union(alreadyChecked, singleton(clearingTeam));
    }

    protected void setFossologyStatus(Release release, final String clearingTeam, FossologyStatus status) {
        setFossologyStatus(release, clearingTeam, status, null, null);
    }

    protected void setFossologyStatus(Release release, final String clearingTeam, FossologyStatus status,
            String fossologyUploadId, String attachmentId) {
        Map<String, FossologyStatus> clearingTeamToStatus = release.getClearingTeamToFossologyStatus();
        if (clearingTeamToStatus == null)
            clearingTeamToStatus = newHashMap();

        clearingTeamToStatus.put(clearingTeam, status);
        release.setClearingTeamToFossologyStatus(clearingTeamToStatus);

        if (!isNullOrEmpty(fossologyUploadId)) {
            release.setFossologyId(fossologyUploadId);
            release.setAttachmentInFossology(attachmentId);
        }
    }

    protected RequestStatus updateRelease(Release release, User user, ComponentService.Iface componentService)
            throws TException {
        final RequestStatus updated = componentService.updateReleaseFossology(release, user);
        if (RequestStatus.FAILURE == updated) {
            log.error("cannot update release");
        }
        return updated;
    }

    protected Set<Attachment> getSourceAttachment(String releaseId, User user,
            ComponentService.Iface componentClient) throws TException {
        try {
            return componentClient.getSourceAttachments(releaseId);
        } catch (TException e) {
            log.error("error contacting component Service", e);
            throw e;
        }
    }

    protected FilledAttachment fillAttachment(Attachment attachment) throws SW360Exception {
        String attachmentContentId = attachment.getAttachmentContentId();
        try {
            AttachmentContent attachmentContent = attachmentConnector.getAttachmentContent(attachmentContentId);
            assertNotNull(attachmentContent);
            return new FilledAttachment().setAttachment(attachment).setAttachmentContent(attachmentContent);
        } catch (SW360Exception e) {
            log.error("cannot retrieve attachment " + attachmentContentId, e);
            throw e;
        }
    }
}