UR errors / glitches when editing
Stuff when working on the component portlet when testing https://github.com/eclipse/sw360/pull/458

1. Components:

Each time user tab out of component name field , application shows a message "To Avoid duplicate components, check there similar ones! Does yours already"

2. Releases:

If user tries to add a new release with any of the existing release name then on click of Add release , application gives an error message "Components is temporarily unavailable." and shows a blank screen.
diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentDatabaseHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentDatabaseHandler.java
index 5cb27f14..501ed123 100644
--- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentDatabaseHandler.java
+++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentDatabaseHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright Siemens AG, 2013-2018. Part of the SW360 Portal Project.
+ * Copyright Siemens AG, 2013-2019. Part of the SW360 Portal Project.
  * With modifications by Bosch Software Innovations GmbH, 2016.
  *
  * SPDX-License-Identifier: EPL-1.0
@@ -11,12 +11,8 @@
  */
 package org.eclipse.sw360.datahandler.db;
 
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.Lists;
-import org.apache.log4j.Logger;
-import org.apache.thrift.TException;
+import com.google.common.collect.*;
+
 import org.eclipse.sw360.components.summary.SummaryType;
 import org.eclipse.sw360.datahandler.common.CommonUtils;
 import org.eclipse.sw360.datahandler.common.SW360Constants;
@@ -40,6 +36,9 @@ import org.eclipse.sw360.datahandler.thrift.users.User;
 import org.eclipse.sw360.datahandler.thrift.vendors.Vendor;
 import org.eclipse.sw360.mail.MailConstants;
 import org.eclipse.sw360.mail.MailUtil;
+
+import org.apache.log4j.Logger;
+import org.apache.thrift.TException;
 import org.ektorp.DocumentOperationResult;
 import org.ektorp.http.HttpClient;
 import org.jetbrains.annotations.NotNull;
@@ -59,7 +58,9 @@ import static org.eclipse.sw360.datahandler.common.SW360Assert.assertNotNull;
 import static org.eclipse.sw360.datahandler.common.SW360Assert.fail;
 import static org.eclipse.sw360.datahandler.permissions.PermissionUtils.makePermission;
 import static org.eclipse.sw360.datahandler.thrift.ThriftUtils.copyFields;
-import static org.eclipse.sw360.datahandler.thrift.ThriftValidate.*;
+import static org.eclipse.sw360.datahandler.thrift.ThriftValidate.ensureEccInformationIsSet;
+import static org.eclipse.sw360.datahandler.thrift.ThriftValidate.prepareComponents;
+import static org.eclipse.sw360.datahandler.thrift.ThriftValidate.prepareReleases;
 
 /**
  * Class for accessing Component information from the database
@@ -400,7 +401,9 @@ public class ComponentDatabaseHandler extends AttachmentAwareDatabaseHandler {
         Component actual = componentRepository.get(component.getId());
         assertNotNull(actual, "Could not find component to update!");
 
-        if (makePermission(actual, user).isActionAllowed(RequestedAction.WRITE)) {
+        if (changeWouldResultInDuplicate(actual, component)) {
+            return RequestStatus.DUPLICATE;
+        } else if (makePermission(actual, user).isActionAllowed(RequestedAction.WRITE)) {
             // Nested releases and attachments should not be updated by this method
             if (actual.isSetReleaseIds()) {
                 component.setReleaseIds(actual.getReleaseIds());
@@ -417,6 +420,15 @@ public class ComponentDatabaseHandler extends AttachmentAwareDatabaseHandler {
 
     }
 
+    private boolean changeWouldResultInDuplicate(Component before, Component after) {
+        if (before.getName().equals(after.getName())) {
+            // sth else was changed, not one of the duplication relevant properties
+            return false;
+        }
+
+        return isDuplicate(after);
+    }
+
     private void updateComponentInternal(Component updated, Component current, User user) {
         // Update the database with the component
         componentRepository.update(updated);
@@ -609,39 +621,54 @@ public class ComponentDatabaseHandler extends AttachmentAwareDatabaseHandler {
 
         if (actual.equals(release)) {
             return RequestStatus.SUCCESS;
-        }
-        DocumentPermissions<Release> permissions = makePermission(actual, user);
-        boolean hasChangesInEccFields = hasChangesInEccFields(release, actual);
+        } else if (changeWouldResultInDuplicate(actual, release)) {
+            return RequestStatus.DUPLICATE;
+        } else {
+            DocumentPermissions<Release> permissions = makePermission(actual, user);
+            boolean hasChangesInEccFields = hasChangesInEccFields(release, actual);
 
-        if ((hasChangesInEccFields && permissions.isActionAllowed(RequestedAction.WRITE_ECC)) ||
-                (!hasChangesInEccFields && permissions.isActionAllowed(RequestedAction.WRITE))) {
+            if ((hasChangesInEccFields && permissions.isActionAllowed(RequestedAction.WRITE_ECC))
+                    || (!hasChangesInEccFields && permissions.isActionAllowed(RequestedAction.WRITE))) {
 
-            if (!hasChangesInEccFields && hasEmptyEccFields(release)) {
-                autosetEccFieldsForReleaseWithDownloadUrl(release);
-            }
+                if (!hasChangesInEccFields && hasEmptyEccFields(release)) {
+                    autosetEccFieldsForReleaseWithDownloadUrl(release);
+                }
 
-            copyFields(actual, release, immutableFields);
+                copyFields(actual, release, immutableFields);
 
-            autosetReleaseClearingState(release, actual);
-            if (hasChangesInEccFields) {
-                autosetEccUpdaterInfo(release, user);
-            }
-            release.setAttachments( getAllAttachmentsToKeep(toSource(actual), actual.getAttachments(), release.getAttachments()) );
-            deleteAttachmentUsagesOfUnlinkedReleases(release, actual);
-            releaseRepository.update(release);
-            updateReleaseDependentFieldsForComponentId(release.getComponentId());
-            //clean up attachments in database
-            attachmentConnector.deleteAttachmentDifference(nullToEmptySet(actual.getAttachments()),nullToEmptySet(release.getAttachments()));
-            sendMailNotificationsForReleaseUpdate(release, user.getEmail());
-        } else {
-            if (hasChangesInEccFields) {
-                return releaseModerator.updateReleaseEccInfo(release, user);
+                autosetReleaseClearingState(release, actual);
+                if (hasChangesInEccFields) {
+                    autosetEccUpdaterInfo(release, user);
+                }
+                release.setAttachments(
+                        getAllAttachmentsToKeep(toSource(actual), actual.getAttachments(), release.getAttachments()));
+                deleteAttachmentUsagesOfUnlinkedReleases(release, actual);
+                releaseRepository.update(release);
+                updateReleaseDependentFieldsForComponentId(release.getComponentId());
+                // clean up attachments in database
+                attachmentConnector.deleteAttachmentDifference(nullToEmptySet(actual.getAttachments()),
+                        nullToEmptySet(release.getAttachments()));
+                sendMailNotificationsForReleaseUpdate(release, user.getEmail());
             } else {
-                return releaseModerator.updateRelease(release, user);
+                if (hasChangesInEccFields) {
+                    return releaseModerator.updateReleaseEccInfo(release, user);
+                } else {
+                    return releaseModerator.updateRelease(release, user);
+                }
             }
+
+            return RequestStatus.SUCCESS;
+        }
+    }
+
+    private boolean changeWouldResultInDuplicate(Release before, Release after) {
+        if (before.getName().equals(after.getName()) && ((before.getVersion() == null && after.getVersion() == null)
+                || (before.getVersion() != null && before.getVersion().equals(after.getVersion())))) {
+            // sth else was changed, not one of the duplication relevant properties
+            return false;
         }
 
-        return RequestStatus.SUCCESS;
+        return isDuplicate(after);
     }
 
     private void deleteAttachmentUsagesOfUnlinkedReleases(Release updated, Release actual) throws SW360Exception {
diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectDatabaseHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectDatabaseHandler.java
index 9963e3ca..897aa114 100644
--- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectDatabaseHandler.java
+++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectDatabaseHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright Siemens AG, 2013-2018. Part of the SW360 Portal Project.
+ * Copyright Siemens AG, 2013-2019. Part of the SW360 Portal Project.
  * With contributions by Siemens Healthcare Diagnostics Inc, 2018.
  *
  * SPDX-License-Identifier: EPL-1.0
@@ -13,7 +13,7 @@ package org.eclipse.sw360.datahandler.db;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.*;
-import org.apache.log4j.Logger;
+
 import org.eclipse.sw360.components.summary.SummaryType;
 import org.eclipse.sw360.datahandler.businessrules.ReleaseClearingStateSummaryComputer;
 import org.eclipse.sw360.datahandler.common.*;
@@ -23,15 +23,14 @@ import org.eclipse.sw360.datahandler.entitlement.ProjectModerator;
 import org.eclipse.sw360.datahandler.thrift.*;
 import org.eclipse.sw360.datahandler.thrift.components.*;
 import org.eclipse.sw360.datahandler.thrift.moderation.ModerationRequest;
-import org.eclipse.sw360.datahandler.thrift.projects.Project;
-import org.eclipse.sw360.datahandler.thrift.projects.ProjectLink;
-import org.eclipse.sw360.datahandler.thrift.projects.ProjectRelationship;
-import org.eclipse.sw360.datahandler.thrift.projects.ProjectWithReleaseRelationTuple;
+import org.eclipse.sw360.datahandler.thrift.projects.*;
 import org.eclipse.sw360.datahandler.thrift.users.RequestedAction;
 import org.eclipse.sw360.datahandler.thrift.users.User;
 import org.eclipse.sw360.datahandler.thrift.vulnerabilities.ProjectVulnerabilityRating;
 import org.eclipse.sw360.mail.MailConstants;
 import org.eclipse.sw360.mail.MailUtil;
+
+import org.apache.log4j.Logger;
 import org.ektorp.http.HttpClient;
 
 import java.net.MalformedURLException;
@@ -45,7 +44,9 @@ import java.util.stream.Collectors;
 import static org.eclipse.sw360.datahandler.common.CommonUtils.*;
 import static org.eclipse.sw360.datahandler.common.SW360Assert.assertNotNull;
 import static org.eclipse.sw360.datahandler.common.SW360Assert.fail;
-import static org.eclipse.sw360.datahandler.common.SW360Utils.*;
+import static org.eclipse.sw360.datahandler.common.SW360Utils.getBUFromOrganisation;
+import static org.eclipse.sw360.datahandler.common.SW360Utils.getCreatedOn;
+import static org.eclipse.sw360.datahandler.common.SW360Utils.printName;
 import static org.eclipse.sw360.datahandler.permissions.PermissionUtils.makePermission;
 
 /**
@@ -191,7 +192,9 @@ public class ProjectDatabaseHandler extends AttachmentAwareDatabaseHandler {
 
         assertNotNull(project);
 
-        if (!changePassesSanityCheck(project, actual)){
+        if (changeWouldResultInDuplicate(actual, project)) {
+            return RequestStatus.DUPLICATE;
+        } else if (!changePassesSanityCheck(project, actual)) {
             return RequestStatus.FAILED_SANITY_CHECK;
         } else if (makePermission(actual, user).isActionAllowed(RequestedAction.WRITE)) {
             copyImmutableFields(project,actual);
@@ -208,6 +211,16 @@ public class ProjectDatabaseHandler extends AttachmentAwareDatabaseHandler {
         }
     }
 
+    private boolean changeWouldResultInDuplicate(Project before, Project after) {
+        if (before.getName().equals(after.getName()) && ((before.getVersion() == null && after.getVersion() == null)
+                || (before.getVersion() != null && before.getVersion().equals(after.getVersion())))) {
+            // sth else was changed, not one of the duplication relevant properties
+            return false;
+        }
+
+        return isDuplicate(after);
+    }
+
     private void deleteAttachmentUsagesOfUnlinkedReleases(Project updated, Project actual) throws SW360Exception {
         Source usedBy = Source.projectId(updated.getId());
         Set<String> updatedLinkedReleaseIds = nullToEmptyMap(updated.getReleaseIdToUsage()).keySet();
diff --git a/backend/src-common/src/test/java/org/eclipse/sw360/datahandler/db/ProjectDatabaseHandlerTest.java b/backend/src-common/src/test/java/org/eclipse/sw360/datahandler/db/ProjectDatabaseHandlerTest.java
index e6944841..467159e7 100644
--- a/backend/src-common/src/test/java/org/eclipse/sw360/datahandler/db/ProjectDatabaseHandlerTest.java
+++ b/backend/src-common/src/test/java/org/eclipse/sw360/datahandler/db/ProjectDatabaseHandlerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright Siemens AG, 2013-2017. Part of the SW360 Portal Project.
+ * Copyright Siemens AG, 2013-2017, 2019. Part of the SW360 Portal Project.
  *
  * SPDX-License-Identifier: EPL-1.0
  *
@@ -13,6 +13,7 @@ package org.eclipse.sw360.datahandler.db;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.SetMultimap;
+
 import org.eclipse.sw360.datahandler.TestUtils;
 import org.eclipse.sw360.datahandler.common.DatabaseSettings;
 import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector;
@@ -20,15 +21,10 @@ import org.eclipse.sw360.datahandler.entitlement.ProjectModerator;
 import org.eclipse.sw360.datahandler.thrift.*;
 import org.eclipse.sw360.datahandler.thrift.components.Component;
 import org.eclipse.sw360.datahandler.thrift.components.Release;
-import org.eclipse.sw360.datahandler.thrift.projects.Project;
-import org.eclipse.sw360.datahandler.thrift.projects.ProjectLink;
-import org.eclipse.sw360.datahandler.thrift.projects.ProjectRelationship;
-import org.eclipse.sw360.datahandler.thrift.projects.ProjectWithReleaseRelationTuple;
+import org.eclipse.sw360.datahandler.thrift.projects.*;
 import org.eclipse.sw360.datahandler.thrift.users.User;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
+
+import org.junit.*;
 import org.junit.runner.RunWith;
 import org.mockito.Mockito;
 import org.mockito.runners.MockitoJUnitRunner;
@@ -39,7 +35,10 @@ import static org.eclipse.sw360.datahandler.TestUtils.assertTestString;
 import static org.eclipse.sw360.datahandler.common.SW360Utils.printName;
 import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.*;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.when;
@@ -263,10 +262,8 @@ public class ProjectDatabaseHandlerTest {
         assertEquals(false, deleted);
     }
 
-
-    @Ignore("One is no longer able to create duplicate projects in the db")
-    public void testDuplicateProjectIsFound() throws Exception {
-
+    @Ignore("One is no longer able to create duplicate projects via the service, so if you want enable the test, you cannot create the duplicate project via addProject()")
+    public void testGetDuplicateProjects() throws Exception {
         String originalProjectId = "P1";
         final Project tmp = handler.getProjectById(originalProjectId, user1);
         tmp.unsetId();
@@ -279,6 +276,37 @@ public class ProjectDatabaseHandlerTest {
         assertThat(duplicateProjects.get(printName(tmp)), containsInAnyOrder(newProjectId,originalProjectId));
     }
 
+    public void testAddProjectWithDuplicateFails() throws Exception {
+        // given:
+        String originalProjectId = "P1";
+        final Project tmp = handler.getProjectById(originalProjectId, user1);
+        tmp.unsetId();
+        tmp.unsetRevision();
+
+        // when:
+        AddDocumentRequestSummary addProjectResult = handler.addProject(tmp, user1);
+
+        // then:
+        assertThat(addProjectResult.getRequestStatus(), is(RequestStatus.DUPLICATE));
+        assertThat(addProjectResult.getId(), is(nullValue()));
+    }
+
+    public void testUpdateProjectWithDuplicateFails() throws Exception {
+        // given:
+        String originalProjectId = "P1";
+        String duplicateProjectId = "P2";
+        final Project tmp = handler.getProjectById(originalProjectId, user1);
+        tmp.unsetId();
+        tmp.unsetRevision();
+        tmp.setId(duplicateProjectId);
+
+        // when:
+        RequestStatus updateProjectResult = handler.updateProject(tmp, user1);
+
+        // then:
+        assertThat(updateProjectResult, is(RequestStatus.DUPLICATE));
+    }
+
     @Test
     public void testSanityCheckFails() throws Exception {
         Project project = handler.getProjectById("P1", user1);
diff --git a/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentDatabaseHandlerTest.java b/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentDatabaseHandlerTest.java
index 28b77b28..82a94e4a 100644
--- a/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentDatabaseHandlerTest.java
+++ b/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentDatabaseHandlerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright Siemens AG, 2013-2017. Part of the SW360 Portal Project.
+ * Copyright Siemens AG, 2013-2017, 2019. Part of the SW360 Portal Project.
  *
  * SPDX-License-Identifier: EPL-1.0
  *
@@ -13,20 +13,19 @@ package org.eclipse.sw360.components.db;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+
 import org.eclipse.sw360.datahandler.TestUtils;
 import org.eclipse.sw360.datahandler.common.DatabaseSettings;
 import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector;
 import org.eclipse.sw360.datahandler.db.ComponentDatabaseHandler;
 import org.eclipse.sw360.datahandler.entitlement.ComponentModerator;
 import org.eclipse.sw360.datahandler.entitlement.ReleaseModerator;
-import org.eclipse.sw360.datahandler.thrift.ReleaseRelationship;
-import org.eclipse.sw360.datahandler.thrift.RequestStatus;
-import org.eclipse.sw360.datahandler.thrift.SW360Exception;
-import org.eclipse.sw360.datahandler.thrift.ThriftUtils;
+import org.eclipse.sw360.datahandler.thrift.*;
 import org.eclipse.sw360.datahandler.thrift.components.*;
 import org.eclipse.sw360.datahandler.thrift.users.RequestedAction;
 import org.eclipse.sw360.datahandler.thrift.users.User;
 import org.eclipse.sw360.datahandler.thrift.vendors.Vendor;
+
 import org.jetbrains.annotations.NotNull;
 import org.junit.*;
 import org.junit.rules.ExpectedException;
@@ -39,7 +38,9 @@ import java.util.concurrent.*;
 
 import static org.eclipse.sw360.datahandler.TestUtils.assertTestString;
 import static org.eclipse.sw360.datahandler.common.CommonUtils.nullToEmptyMap;
-import static org.eclipse.sw360.datahandler.common.SW360Utils.*;
+import static org.eclipse.sw360.datahandler.common.SW360Utils.getComponentIds;
+import static org.eclipse.sw360.datahandler.common.SW360Utils.getReleaseIds;
+import static org.eclipse.sw360.datahandler.common.SW360Utils.printFullname;
 import static org.eclipse.sw360.datahandler.thrift.ThriftValidate.ensureEccInformationIsSet;
 import static org.hamcrest.Matchers.*;
 import static org.hamcrest.collection.IsEmptyCollection.empty;
@@ -67,7 +68,6 @@ public class ComponentDatabaseHandlerTest {
     private List<Component> components;
     private Map<String, Component>  componentMap;
     private List<Release> releases;
-    private Map<String, Release> releaseMap;
     private Map<String, Vendor> vendors;
     private ComponentDatabaseHandler handler;
 
@@ -138,7 +138,6 @@ public class ComponentDatabaseHandlerTest {
         }
 
         componentMap= ThriftUtils.getIdMap(components);
-        releaseMap= ThriftUtils.getIdMap(releases);
 
         // Prepare the handler
         handler = new ComponentDatabaseHandler(DatabaseSettings.getConfiguredHttpClient(), dbName, attachmentsDbName, moderator, releaseModerator);
@@ -758,6 +757,18 @@ public class ComponentDatabaseHandlerTest {
         assertFalse(releasesContain(actual.getReleases(), "R2C"));
     }
 
+    @Test
+    public void testUpdateComponentDuplicate() throws Exception {
+        // given:
+        Component component = components.get(0);
+        component.setName("component2");
+
+        // when:
+        RequestStatus status = handler.updateComponent(component, user1);
+
+        // then:
+        assertThat(status, is(RequestStatus.DUPLICATE));
+    }
 
     @Test
     public void testUpdateInconsistentComponent() throws Exception {
@@ -779,7 +790,7 @@ public class ComponentDatabaseHandlerTest {
         expected.setReleases(tmpReleases);
 
         RequestStatus status = handler.updateComponent(expected, user1);
-        assertThat(RequestStatus.SUCCESS, is(status));
+        assertThat(status, is(RequestStatus.SUCCESS));
         Component actual = handler.getComponent("C1", user1);
 
         //Other asserts have been dealt with in testUpdateComponent
@@ -829,6 +840,19 @@ public class ComponentDatabaseHandlerTest {
         assertTrue(actual.getSubscribers().contains(email1));
     }
 
+    @Test
+    public void testUpdateReleaseDuplicate() throws Exception {
+        // given:
+        Release release = releases.get(0);
+        release.setVersion("releaseB");
+
+        // when:
+        RequestStatus status = handler.updateRelease(release, user2, ThriftUtils.IMMUTABLE_OF_RELEASE);
+
+        // then:
+        assertThat(status, is(RequestStatus.DUPLICATE));
+    }
+
     @Test
     public void testUpdateSentToModeration() throws Exception {
         Release release = releases.get(1);
@@ -963,7 +987,7 @@ public class ComponentDatabaseHandlerTest {
         final Component tmp = handler.getComponent(originalComponentId, user1);
         tmp.unsetId();
         tmp.unsetRevision();
-        String newComponentId = handler.addComponent(tmp, email1).getId();
+        handler.addComponent(tmp, email1).getId();
 
         final Map<String, List<String>> duplicateComponents = handler.getDuplicateComponents();
 
diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/common/ErrorMessages.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/common/ErrorMessages.java
index 587112b9..be1fde0a 100644
--- a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/common/ErrorMessages.java
+++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/common/ErrorMessages.java
@@ -18,11 +18,11 @@ import com.google.common.collect.ImmutableList;
 public class ErrorMessages {
 
     public static final String PROJECT_NOT_ADDED = "Project could not be added.";
-    public static final String PROJECT_DUPLICATE ="Project could not be added, since a project with the same name and version already exists.";
+    public static final String PROJECT_DUPLICATE = "A project with the same name and version already exists.";
     public static final String COMPONENT_NOT_ADDED = "Component could not be added.";
-    public static final String COMPONENT_DUPLICATE ="Component could not be added, since a component with the same name already exists.";
+    public static final String COMPONENT_DUPLICATE = "A component with the same name already exists.";
     public static final String RELEASE_NOT_ADDED = "Release could not be added.";
-    public static final String RELEASE_DUPLICATE ="Release could not be added, since a release with the same name and version already exists.";
+    public static final String RELEASE_DUPLICATE = "A release with the same name and version already exists.";
     public static final String ERROR_GETTING_PROJECT = "Error fetching project from backend.";
     public static final String ERROR_GETTING_COMPONENT = "Error fetching component from backend.";
     public static final String ERROR_GETTING_LICENSE = "Error fetching license from backend.";
diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/Sw360Portlet.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/Sw360Portlet.java
index 275de557..1c2b8464 100644
--- a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/Sw360Portlet.java
+++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/Sw360Portlet.java
@@ -1,5 +1,5 @@
 /*
- * Copyright Siemens AG, 2013-2017. Part of the SW360 Portal Project.
+ * Copyright Siemens AG, 2013-2017, 2019. Part of the SW360 Portal Project.
  * With contributions by Bosch Software Innovations GmbH, 2016.
  *
  * SPDX-License-Identifier: EPL-1.0
@@ -22,9 +22,7 @@ import com.liferay.portal.kernel.servlet.SessionErrors;
 import com.liferay.portal.kernel.servlet.SessionMessages;
 import com.liferay.portal.util.PortalUtil;
 import com.liferay.util.bridges.mvc.MVCPortlet;
-import org.apache.commons.lang.StringUtils;
-import org.apache.log4j.Logger;
-import org.apache.thrift.TException;
+
 import org.eclipse.sw360.datahandler.thrift.*;
 import org.eclipse.sw360.datahandler.thrift.components.ComponentService;
 import org.eclipse.sw360.datahandler.thrift.components.Release;
@@ -38,12 +36,14 @@ import org.eclipse.sw360.portal.common.ErrorMessages;
 import org.eclipse.sw360.portal.common.PortalConstants;
 import org.eclipse.sw360.portal.users.UserCacheHolder;
 
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+import org.apache.thrift.TException;
+
 import javax.portlet.*;
+
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 import static com.google.common.base.Strings.isNullOrEmpty;
 
@@ -253,29 +253,33 @@ abstract public class Sw360Portlet extends MVCPortlet {
             name = " " + name;
         }
         switch (requestStatus) {
-            case SUCCESS:
-                statusMessage = type + name + " " + verb + "d successfully!";
-                SessionMessages.add(request, "request_processed", statusMessage);
-                break;
-            case SENT_TO_MODERATOR:
-                statusMessage = "Moderation request was sent to " + verb + " the " + type + name + "!";
-                SessionMessages.add(request, "request_processed", statusMessage);
-                break;
-            case FAILURE:
-                setSW360SessionError(request, ErrorMessages.DOCUMENT_NOT_PROCESSED_SUCCESSFULLY);
-                break;
-            case IN_USE:
-                if(type.equals("License")) {
-                    setSW360SessionError(request, ErrorMessages.LICENSE_USED_BY_RELEASE);
-                } else {
-                    setSW360SessionError(request, ErrorMessages.DOCUMENT_USED_BY_PROJECT_OR_RELEASE);
-                }
-                break;
-            case FAILED_SANITY_CHECK:
-                setSW360SessionError(request, ErrorMessages.UPDATE_FAILED_SANITY_CHECK);
-                break;
-            default:
-                throw new PortletException("Unknown request status");
+        case SUCCESS:
+            statusMessage = type + name + " " + verb + "d successfully!";
+            SessionMessages.add(request, "request_processed", statusMessage);
+            break;
+        case SENT_TO_MODERATOR:
+            statusMessage = "Moderation request was sent to " + verb + " the " + type + name + "!";
+            SessionMessages.add(request, "request_processed", statusMessage);
+            break;
+        case FAILURE:
+            setSW360SessionError(request, ErrorMessages.DOCUMENT_NOT_PROCESSED_SUCCESSFULLY);
+            break;
+        case IN_USE:
+            if (type.equals("License")) {
+                setSW360SessionError(request, ErrorMessages.LICENSE_USED_BY_RELEASE);
+            } else {
+                setSW360SessionError(request, ErrorMessages.DOCUMENT_USED_BY_PROJECT_OR_RELEASE);
+            }
+            break;
+        case FAILED_SANITY_CHECK:
+            setSW360SessionError(request, ErrorMessages.UPDATE_FAILED_SANITY_CHECK);
+            break;
+        case DUPLICATE:
+            // just break to not throw an exception, error message has to be set by caller
+            // because of type specific error messages
+            break;
+        default:
+            throw new PortletException("Unknown request status");
         }
     }
 
diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/components/ComponentPortlet.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/components/ComponentPortlet.java
index 88b2c324..7ddf3b00 100644
--- a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/components/ComponentPortlet.java
+++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/components/ComponentPortlet.java
@@ -1,5 +1,5 @@
 /*
- * Copyright Siemens AG, 2013-2018. Part of the SW360 Portal Project.
+ * Copyright Siemens AG, 2013-2019. Part of the SW360 Portal Project.
  * With contributions by Bosch Software Innovations GmbH, 2016.
  *
  * SPDX-License-Identifier: EPL-1.0
@@ -27,14 +27,8 @@ import com.liferay.portal.kernel.util.WebKeys;
 import com.liferay.portal.theme.ThemeDisplay;
 import com.liferay.portal.util.PortalUtil;
 import com.liferay.portlet.PortletURLFactoryUtil;
-import org.apache.log4j.Logger;
-import org.apache.thrift.TException;
-import org.apache.thrift.TSerializer;
-import org.apache.thrift.protocol.TSimpleJSONProtocol;
-import org.eclipse.sw360.datahandler.common.CommonUtils;
-import org.eclipse.sw360.datahandler.common.SW360Constants;
-import org.eclipse.sw360.datahandler.common.SW360Utils;
-import org.eclipse.sw360.datahandler.common.ThriftEnumUtils;
+
+import org.eclipse.sw360.datahandler.common.*;
 import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector;
 import org.eclipse.sw360.datahandler.permissions.PermissionUtils;
 import org.eclipse.sw360.datahandler.thrift.*;
@@ -49,10 +43,7 @@ import org.eclipse.sw360.datahandler.thrift.users.User;
 import org.eclipse.sw360.datahandler.thrift.users.UserGroup;
 import org.eclipse.sw360.datahandler.thrift.vendors.Vendor;
 import org.eclipse.sw360.datahandler.thrift.vendors.VendorService;
-import org.eclipse.sw360.datahandler.thrift.vulnerabilities.ReleaseVulnerabilityRelation;
-import org.eclipse.sw360.datahandler.thrift.vulnerabilities.Vulnerability;
-import org.eclipse.sw360.datahandler.thrift.vulnerabilities.VulnerabilityDTO;
-import org.eclipse.sw360.datahandler.thrift.vulnerabilities.VulnerabilityService;
+import org.eclipse.sw360.datahandler.thrift.vulnerabilities.*;
 import org.eclipse.sw360.exporter.ComponentExporter;
 import org.eclipse.sw360.portal.common.*;
 import org.eclipse.sw360.portal.common.datatables.PaginationParser;
@@ -61,9 +52,15 @@ import org.eclipse.sw360.portal.portlets.FossologyAwarePortlet;
 import org.eclipse.sw360.portal.users.LifeRayUserSession;
 import org.eclipse.sw360.portal.users.UserCacheHolder;
 
+import org.apache.log4j.Logger;
+import org.apache.thrift.TException;
+import org.apache.thrift.TSerializer;
+import org.apache.thrift.protocol.TSimpleJSONProtocol;
+
 import javax.portlet.*;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.*;
@@ -1029,9 +1026,17 @@ public class ComponentPortlet extends FossologyAwarePortlet {
                 user.setCommentMadeDuringModerationRequest(ModerationRequestCommentMsg);
                 RequestStatus requestStatus = client.updateComponent(component, user);
                 setSessionMessage(request, requestStatus, "Component", "update", component.getName());
-                cleanUploadHistory(user.getEmail(),id);
-                response.setRenderParameter(PAGENAME, PAGENAME_DETAIL);
-                response.setRenderParameter(COMPONENT_ID, request.getParameter(COMPONENT_ID));
+                if (RequestStatus.DUPLICATE.equals(requestStatus)) {
+                    setSW360SessionError(request, ErrorMessages.COMPONENT_DUPLICATE);
+                    response.setRenderParameter(PAGENAME, PAGENAME_EDIT);
+                    request.setAttribute(DOCUMENT_TYPE, SW360Constants.TYPE_COMPONENT);
+                    request.setAttribute(DOCUMENT_ID, id);
+                    prepareRequestForEditAfterDuplicateError(request, component);
+                } else {
+                    cleanUploadHistory(user.getEmail(), id);
+                    response.setRenderParameter(PAGENAME, PAGENAME_DETAIL);
+                    response.setRenderParameter(COMPONENT_ID, request.getParameter(COMPONENT_ID));
+                }
             } else {
                 Component component = new Component();
                 ComponentPortletUtils.updateComponentFromRequest(request, component);
@@ -1089,11 +1094,19 @@ public class ComponentPortlet extends FossologyAwarePortlet {
 
                     RequestStatus requestStatus = client.updateRelease(release, user);
                     setSessionMessage(request, requestStatus, "Release", "update", printName(release));
-                    cleanUploadHistory(user.getEmail(),releaseId);
-
-                    response.setRenderParameter(PAGENAME, PAGENAME_RELEASE_DETAIL);
-                    response.setRenderParameter(COMPONENT_ID, request.getParameter(COMPONENT_ID));
-                    response.setRenderParameter(RELEASE_ID, request.getParameter(RELEASE_ID));
+                    if (RequestStatus.DUPLICATE.equals(requestStatus)) {
+                        setSW360SessionError(request, ErrorMessages.RELEASE_DUPLICATE);
+                        response.setRenderParameter(PAGENAME, PAGENAME_EDIT_RELEASE);
+                        request.setAttribute(DOCUMENT_TYPE, SW360Constants.TYPE_RELEASE);
+                        response.setRenderParameter(COMPONENT_ID, id);
+                        response.setRenderParameter(RELEASE_ID, releaseId);
+                        prepareRequestForReleaseEditAfterDuplicateError(request, release);
+                    } else {
+                        cleanUploadHistory(user.getEmail(), releaseId);
+                        response.setRenderParameter(PAGENAME, PAGENAME_RELEASE_DETAIL);
+                        response.setRenderParameter(COMPONENT_ID, id);
+                        response.setRenderParameter(RELEASE_ID, releaseId);
+                    }
                 } else {
                     release = new Release();
                     release.setComponentId(component.getId());
diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/projects/ProjectPortlet.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/projects/ProjectPortlet.java
index b1c31221..4a967f0c 100644
--- a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/projects/ProjectPortlet.java
+++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/projects/ProjectPortlet.java
@@ -1,5 +1,5 @@
 /*
- * Copyright Siemens AG, 2013-2018. Part of the SW360 Portal Project.
+ * Copyright Siemens AG, 2013-2019. Part of the SW360 Portal Project.
  * With contributions by Bosch Software Innovations GmbH, 2016.
  *
  * SPDX-License-Identifier: EPL-1.0
@@ -14,32 +14,19 @@ package org.eclipse.sw360.portal.portlets.projects;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.base.Strings;
 import com.google.common.collect.*;
-import com.liferay.portal.kernel.json.JSONArray;
-import com.liferay.portal.kernel.json.JSONException;
-import com.liferay.portal.kernel.json.JSONFactoryUtil;
-import com.liferay.portal.kernel.json.JSONObject;
+import com.liferay.portal.kernel.json.*;
 import com.liferay.portal.kernel.portlet.PortletResponseUtil;
 import com.liferay.portal.kernel.servlet.SessionMessages;
 import com.liferay.portal.model.Organization;
 import com.liferay.portal.util.PortalUtil;
-import org.apache.commons.lang.StringUtils;
-import org.apache.log4j.Logger;
-import org.apache.thrift.TException;
-import org.apache.thrift.TSerializer;
-import org.apache.thrift.protocol.TSimpleJSONProtocol;
-import org.eclipse.sw360.datahandler.common.CommonUtils;
-import org.eclipse.sw360.datahandler.common.SW360Constants;
-import org.eclipse.sw360.datahandler.common.SW360Utils;
-import org.eclipse.sw360.datahandler.common.ThriftEnumUtils;
+
+import org.eclipse.sw360.datahandler.common.*;
 import org.eclipse.sw360.datahandler.common.WrappedException.WrappedTException;
 import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector;
 import org.eclipse.sw360.datahandler.permissions.PermissionUtils;
 import org.eclipse.sw360.datahandler.thrift.*;
 import org.eclipse.sw360.datahandler.thrift.attachments.*;
-import org.eclipse.sw360.datahandler.thrift.components.ComponentService;
-import org.eclipse.sw360.datahandler.thrift.components.Release;
-import org.eclipse.sw360.datahandler.thrift.components.ReleaseClearingStatusData;
-import org.eclipse.sw360.datahandler.thrift.components.ReleaseLink;
+import org.eclipse.sw360.datahandler.thrift.components.*;
 import org.eclipse.sw360.datahandler.thrift.cvesearch.CveSearchService;
 import org.eclipse.sw360.datahandler.thrift.cvesearch.VulnerabilityUpdateStatus;
 import org.eclipse.sw360.datahandler.thrift.licenseinfo.*;
@@ -57,20 +44,24 @@ import org.eclipse.sw360.portal.portlets.FossologyAwarePortlet;
 import org.eclipse.sw360.portal.users.LifeRayUserSession;
 import org.eclipse.sw360.portal.users.UserCacheHolder;
 import org.eclipse.sw360.portal.users.UserUtils;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+import org.apache.thrift.TException;
+import org.apache.thrift.TSerializer;
+import org.apache.thrift.protocol.TSimpleJSONProtocol;
 import org.jetbrains.annotations.NotNull;
 
 import javax.portlet.*;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.net.URLConnection;
 import java.util.*;
 import java.util.Map.Entry;
-import java.util.function.BiConsumer;
-import java.util.function.BiFunction;
-import java.util.function.Function;
-import java.util.function.Predicate;
+import java.util.function.*;
 import java.util.stream.Collector;
 import java.util.stream.Collectors;
 
@@ -1179,9 +1170,17 @@ public class ProjectPortlet extends FossologyAwarePortlet {
                 user.setCommentMadeDuringModerationRequest(ModerationRequestCommentMsg);
                 requestStatus = client.updateProject(project, user);
                 setSessionMessage(request, requestStatus, "Project", "update", printName(project));
-                cleanUploadHistory(user.getEmail(), id);
-                response.setRenderParameter(PAGENAME, PAGENAME_DETAIL);
-                response.setRenderParameter(PROJECT_ID, request.getParameter(PROJECT_ID));
+                if (RequestStatus.DUPLICATE.equals(requestStatus)) {
+                    setSW360SessionError(request, ErrorMessages.PROJECT_DUPLICATE);
+                    response.setRenderParameter(PAGENAME, PAGENAME_EDIT);
+                    request.setAttribute(DOCUMENT_TYPE, SW360Constants.TYPE_PROJECT);
+                    request.setAttribute(DOCUMENT_ID, id);
+                    prepareRequestForEditAfterDuplicateError(request, project, user);
+                } else {
+                    cleanUploadHistory(user.getEmail(), id);
+                    response.setRenderParameter(PAGENAME, PAGENAME_DETAIL);
+                    response.setRenderParameter(PROJECT_ID, request.getParameter(PROJECT_ID));
+                }
             } else {
                 // Add project
                 Project project = new Project();
diff --git a/libraries/lib-datahandler/src/main/thrift/sw360.thrift b/libraries/lib-datahandler/src/main/thrift/sw360.thrift
index 75f3d08a..ec800247 100644
--- a/libraries/lib-datahandler/src/main/thrift/sw360.thrift
+++ b/libraries/lib-datahandler/src/main/thrift/sw360.thrift
@@ -1,5 +1,5 @@
 /*
- * Copyright Siemens AG, 2014-2017. Part of the SW360 Portal Project.
+ * Copyright Siemens AG, 2014-2017, 2019. Part of the SW360 Portal Project.
  * With contributions by Bosch Software Innovations GmbH, 2016.
  *
  * SPDX-License-Identifier: EPL-1.0
@@ -25,6 +25,7 @@ enum RequestStatus {
     FAILURE = 2,
     IN_USE=3,
     FAILED_SANITY_CHECK = 4,
+    DUPLICATE = 5,
 }
 
 enum RemoveModeratorRequestStatus {
['libraries/lib-datahandler/src/main/thrift/sw360.thrift', 'backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectDatabaseHandler.java', 'frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/common/ErrorMessages.java', 'backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentDatabaseHandler.java', 'frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/Sw360Portlet.java', 'backend/src-common/src/test/java/org/eclipse/sw360/datahandler/db/ProjectDatabaseHandlerTest.java', 'backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentDatabaseHandlerTest.java', 'frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/projects/ProjectPortlet.java', 'frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/components/ComponentPortlet.java']