From 43f67b9b73dc46bd1c00fabe401ac739ba66b1b7 Mon Sep 17 00:00:00 2001
From: kaloyane <kaloyane>
Date: Tue, 11 Oct 2011 12:32:46 +0000
Subject: [PATCH] [LMS-2555] IGeneralInformationChangingService exposed over
 JSON

SVN: 23276
---
 .../api/gui/AddVocabularyTermDialog.java      |   2 +-
 .../api/gui/DataSetPropertiesPanel.java       |   2 +-
 .../api/gui/DataSetUploadClientModel.java     |  48 +++++---
 .../api/gui/VocabularyTermsComboBoxPanel.java |  10 +-
 .../client/api/v1/IOpenbisServiceFacade.java  |  15 +++
 .../api/v1/ISimpleOpenbisServiceFacade.java   |   6 +
 .../api/v1/impl/OpenbisServiceFacade.java     |  13 +++
 .../v1/GeneralInformationChangingService.java |  12 +-
 ...lInformationChangingServiceJsonServer.java |  49 ++++++++
 ...neralInformationChangingServiceLogger.java |  11 +-
 .../IGeneralInformationChangingService.java   |  20 ++++
 .../shared/api/v1/dto/NewVocabularyTerm.java  | 109 ++++++++++++++++++
 ...InformationChangingServiceJsonApiTest.java | 107 +++++++++++++++++
 .../GeneralInformationServiceJsonApiTest.java |  59 +++++-----
 .../api/v1/TestJsonServiceFactory.java        |  70 +++++++++++
 15 files changed, 478 insertions(+), 55 deletions(-)
 create mode 100644 openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationChangingServiceJsonServer.java
 create mode 100644 openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/NewVocabularyTerm.java
 create mode 100644 openbis/sourceTest/java/ch/systemsx/cisd/openbis/remoteapitest/api/v1/GeneralInformationChangingServiceJsonApiTest.java
 create mode 100644 openbis/sourceTest/java/ch/systemsx/cisd/openbis/remoteapitest/api/v1/TestJsonServiceFactory.java

diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/AddVocabularyTermDialog.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/AddVocabularyTermDialog.java
index 8d1c527285c..3cebcd0e563 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/AddVocabularyTermDialog.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/AddVocabularyTermDialog.java
@@ -38,8 +38,8 @@ import javax.swing.JTextArea;
 import javax.swing.JTextField;
 
 import ch.systemsx.cisd.openbis.dss.client.api.gui.VocabularyTermsComboBoxPanel.VocabularyTermAdaptor;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Vocabulary;
 import ch.systemsx.cisd.openbis.generic.shared.basic.CodeNormalizer;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Vocabulary;
 
 /**
  * @author Pawel Glyzewski
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetPropertiesPanel.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetPropertiesPanel.java
index e8dfff78cba..bef0c9c4058 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetPropertiesPanel.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetPropertiesPanel.java
@@ -51,10 +51,10 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetDTOBuilder;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetMetadataDTO;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.validation.ValidationError;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.ControlledVocabularyPropertyType;
-import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.ControlledVocabularyPropertyType.VocabularyTerm;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSetType;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.PropertyType;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.PropertyTypeGroup;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.VocabularyTerm;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataTypeCode;
 
 /**
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetUploadClientModel.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetUploadClientModel.java
index 8271f79d3d1..784fb343b55 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetUploadClientModel.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetUploadClientModel.java
@@ -18,7 +18,6 @@ package ch.systemsx.cisd.openbis.dss.client.api.gui;
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
@@ -38,15 +37,14 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetDTO;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetDTOBuilder;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetMetadataDTO;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.validation.ValidationError;
-import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.ControlledVocabularyPropertyType.VocabularyTerm;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSetType;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Experiment;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.NewVocabularyTerm;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Project;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.PropertyType;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.PropertyTypeGroup;
-import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Vocabulary;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataTypeCode;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Vocabulary;
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ProjectIdentifier;
 import ch.systemsx.cisd.openbis.generic.shared.util.SimplePropertyValidator;
 
@@ -85,7 +83,7 @@ public class DataSetUploadClientModel
 
     private final List<Observer> observers = new LinkedList<DataSetUploadClientModel.Observer>();
 
-    private HashMap<Vocabulary, List<VocabularyTerm>> vocabularyTerms;
+    private List<Vocabulary> vocabularies;
 
     private List<Project> projects;
 
@@ -96,7 +94,7 @@ public class DataSetUploadClientModel
         this.openBISService = commState.getOpenBISService();
         this.timeProvider = timeProvider;
         dataSetTypes = openBISService.listDataSetTypes();
-        vocabularyTerms = openBISService.getVocabularyTermsMap();
+        vocabularies = openBISService.listVocabularies();
         projects = openBISService.listProjects();
 
         List<String> projectIds = new ArrayList<String>();
@@ -529,12 +527,27 @@ public class DataSetUploadClientModel
     public void addUnofficialVocabularyTerm(Vocabulary vocabulary, String code, String label,
             String description, Long previousTermOrdinal)
     {
-        openBISService.addAdHocVocabularyTerm(TechId.create(vocabulary), code, label, description,
-                previousTermOrdinal);
+        NewVocabularyTerm term =
+                createNewVocabularyTerm(code, label, description, previousTermOrdinal);
+        openBISService.addAdHocVocabularyTerm(vocabulary.getId(), term);
         dataSetTypes = openBISService.listDataSetTypes();
-        vocabularyTerms = openBISService.getVocabularyTermsMap();
+        vocabularies = openBISService.listVocabularies();
 
-        notifyObservers(vocabulary, code);
+        // get the vocabulary with the new term.
+        Vocabulary updatedVocabulary = getVocabulary(vocabulary.getCode());
+        notifyObservers(updatedVocabulary, code);
+    }
+
+    private NewVocabularyTerm createNewVocabularyTerm(String code, String label,
+            String description,
+            Long previousTermOrdinal)
+    {
+        NewVocabularyTerm term = new NewVocabularyTerm();
+        term.setCode(code);
+        term.setLabel(label);
+        term.setDescription(description);
+        term.setPreviousTermOrdinal(previousTermOrdinal);
+        return term;
     }
 
     public void registerObserver(Observer observer)
@@ -550,13 +563,20 @@ public class DataSetUploadClientModel
         }
     }
 
-    public List<VocabularyTerm> getVocabularyTerms(Vocabulary vocabulary)
+    public List<Experiment> getExperiments()
     {
-        return vocabularyTerms.get(vocabulary);
+        return experiments;
     }
 
-    public List<Experiment> getExperiments()
+    public Vocabulary getVocabulary(String code)
     {
-        return experiments;
+        for (Vocabulary vocabulary : vocabularies)
+        {
+            if (vocabulary.getCode().equals(code))
+            {
+                return vocabulary;
+            }
+        }
+        return null;
     }
 }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/VocabularyTermsComboBoxPanel.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/VocabularyTermsComboBoxPanel.java
index 08c8037ecf9..064e2478e21 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/VocabularyTermsComboBoxPanel.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/VocabularyTermsComboBoxPanel.java
@@ -37,8 +37,8 @@ import javax.swing.plaf.basic.BasicComboBoxRenderer;
 
 import ch.systemsx.cisd.openbis.dss.client.api.gui.DataSetUploadClientModel.Observer;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.ControlledVocabularyPropertyType;
-import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.ControlledVocabularyPropertyType.VocabularyTerm;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Vocabulary;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Vocabulary;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.VocabularyTerm;
 
 /**
  * The class creates a ComboBox together with button which makes it possible to add new Vocabulary
@@ -131,8 +131,8 @@ public class VocabularyTermsComboBoxPanel extends JPanel implements Observer
 
         this.comboBox = new JComboBox();
         this.add(comboBox, BorderLayout.CENTER);
-        this.vocabulary = propertyType.getVocabulary();
-        fillComboBoxWithTerms(propertyType.getTerms(), null);
+        this.vocabulary = clientModel.getVocabulary(propertyType.getVocabulary().getCode());
+        fillComboBoxWithTerms(vocabulary.getTerms(), null);
         comboBox.setRenderer(new VocabularyTermsRenderer());
 
         button.addActionListener(new ActionListener()
@@ -201,6 +201,6 @@ public class VocabularyTermsComboBoxPanel extends JPanel implements Observer
         String selectedCode =
                 vocabulary.getId() != this.vocabulary.getId() ? ((VocabularyTermAdaptor) comboBox
                         .getSelectedItem()).term.getCode() : code;
-        fillComboBoxWithTerms(clientModel.getVocabularyTerms(vocabulary), selectedCode);
+        fillComboBoxWithTerms(vocabulary.getTerms(), selectedCode);
     }
 }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/IOpenbisServiceFacade.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/IOpenbisServiceFacade.java
index 75e0bbfe740..db2b9c30442 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/IOpenbisServiceFacade.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/IOpenbisServiceFacade.java
@@ -22,6 +22,7 @@ import java.util.List;
 
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.ControlledVocabularyPropertyType;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet.Connections;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.NewVocabularyTerm;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
@@ -71,12 +72,26 @@ public interface IOpenbisServiceFacade extends ISimpleOpenbisServiceFacade
      * @param description Free text describing new vocabulary term.
      * @param previousTermOrdinal new vocabulary term will be placed right after vocabulary term
      *            with given ordinal number.
+     * @deprecated Please use the {@link #addAdHocVocabularyTerm(Long, NewVocabularyTerm)} method
+     *             instead.
      */
+    @Deprecated
     public void addAdHocVocabularyTerm(TechId vocabularyId, String code, String label,
             String description, Long previousTermOrdinal);
 
+    /**
+     * Adds new ad-hoc terms to a vocabulary starting from specified ordinal + 1.
+     * 
+     * @param vocabularyId The id of vocabulary which should be extended.
+     * @param term the vocabulary term to be added.
+     */
+    public void addAdHocVocabularyTerm(Long vocabularyId, NewVocabularyTerm term);
+
     /**
      * Returns map of avaialable vocabulary terms. Available since minor version 6.
+     * 
+     * @deprecated Please use the {@link #listVocabularies()} method instead.
      */
+    @Deprecated
     public HashMap<Vocabulary, List<ControlledVocabularyPropertyType.VocabularyTerm>> getVocabularyTermsMap();
 }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/ISimpleOpenbisServiceFacade.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/ISimpleOpenbisServiceFacade.java
index 2be32866619..81e77377954 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/ISimpleOpenbisServiceFacade.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/ISimpleOpenbisServiceFacade.java
@@ -28,6 +28,7 @@ import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Experiment;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Project;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SpaceWithProjectsAndRoleAssignments;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Vocabulary;
 
 /**
  * Provides a simplified view of an openBIS backend by allowing the following operations :
@@ -114,6 +115,11 @@ public interface ISimpleOpenbisServiceFacade
      */
     List<DataSetType> listDataSetTypes();
 
+    /**
+     * Return all vocabularies available in openBIS together with the contained vocabulary terms.
+     */
+    List<Vocabulary> listVocabularies();
+
     /**
      * Upload a new data set to the DSS.
      * 
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/impl/OpenbisServiceFacade.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/impl/OpenbisServiceFacade.java
index 8c2c6e10a57..80d35a63754 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/impl/OpenbisServiceFacade.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/impl/OpenbisServiceFacade.java
@@ -42,6 +42,7 @@ import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.ControlledVocabularyPr
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet.Connections;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSetType;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Experiment;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.NewVocabularyTerm;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Project;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria;
@@ -439,6 +440,7 @@ public class OpenbisServiceFacade implements IOpenbisServiceFacade
         return convertedDataSets;
     }
 
+    @SuppressWarnings("deprecation")
     public void addAdHocVocabularyTerm(TechId vocabularyId, String code, String label,
             String description, Long previousTermOrdinal)
     {
@@ -446,9 +448,20 @@ public class OpenbisServiceFacade implements IOpenbisServiceFacade
                 description, previousTermOrdinal);
     }
 
+    public void addAdHocVocabularyTerm(Long vocabularyId, NewVocabularyTerm term)
+    {
+        changingService.addUnofficialVocabularyTerm(sessionToken, vocabularyId, term);
+    }
+
     @SuppressWarnings("deprecation")
     public HashMap<Vocabulary, List<VocabularyTerm>> getVocabularyTermsMap()
     {
         return service.getVocabularyTermsMap(sessionToken);
     }
+
+    public List<ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Vocabulary> listVocabularies()
+    {
+        return service.listVocabularies(sessionToken);
+    }
+
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationChangingService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationChangingService.java
index 6eb1099d2d9..3d760393a62 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationChangingService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationChangingService.java
@@ -29,6 +29,7 @@ import ch.systemsx.cisd.openbis.generic.server.business.IPropertiesBatchManager;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.shared.ICommonServer;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.IGeneralInformationChangingService;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.NewVocabularyTerm;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
 import ch.systemsx.cisd.openbis.generic.shared.util.EntityHelper;
@@ -77,6 +78,14 @@ public class GeneralInformationChangingService extends
                 previousTermOrdinal);
     }
 
+    public void addUnofficialVocabularyTerm(String sessionToken, Long vocabularyId,
+            NewVocabularyTerm term)
+    {
+        TechId vocabularyTechId = new TechId(vocabularyId);
+        server.addUnofficialVocabularyTerm(sessionToken, vocabularyTechId, term.getCode(),
+                term.getLabel(), term.getDescription(), term.getPreviousTermOrdinal());
+    }
+
     public int getMajorVersion()
     {
         return 1;
@@ -84,6 +93,7 @@ public class GeneralInformationChangingService extends
 
     public int getMinorVersion()
     {
-        return 0;
+        return 1;
     }
+
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationChangingServiceJsonServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationChangingServiceJsonServer.java
new file mode 100644
index 00000000000..4d6e2795175
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationChangingServiceJsonServer.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2010 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.openbis.generic.server.api.v1;
+
+import javax.annotation.Resource;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import ch.systemsx.cisd.common.api.server.AbstractApiJsonServiceExporter;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.IGeneralInformationChangingService;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.IGeneralInformationService;
+
+/**
+ * @author Kaloyan Enimanev
+ */
+@Controller
+@RequestMapping(
+    { IGeneralInformationChangingService.JSON_SERVICE_URL,
+            "/openbis" + IGeneralInformationChangingService.JSON_SERVICE_URL })
+public class GeneralInformationChangingServiceJsonServer extends AbstractApiJsonServiceExporter
+{
+    @Resource(name = ResourceNames.GENERAL_INFORMATION_CHANGING_SERVICE_SERVER)
+    private IGeneralInformationChangingService service;
+
+    @Override
+    public void afterPropertiesSet() throws Exception
+    {
+
+        establishService(IGeneralInformationChangingService.class, service,
+                IGeneralInformationService.SERVICE_NAME,
+                IGeneralInformationService.JSON_SERVICE_URL);
+        super.afterPropertiesSet();
+    }
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationChangingServiceLogger.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationChangingServiceLogger.java
index 6ca08af61d8..e540715ad60 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationChangingServiceLogger.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationChangingServiceLogger.java
@@ -22,6 +22,7 @@ import ch.systemsx.cisd.authentication.ISessionManager;
 import ch.systemsx.cisd.common.spring.IInvocationLoggerContext;
 import ch.systemsx.cisd.openbis.generic.shared.AbstractServerLogger;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.IGeneralInformationChangingService;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.NewVocabularyTerm;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
 
@@ -47,11 +48,19 @@ class GeneralInformationChangingServiceLogger extends AbstractServerLogger imple
     public void addUnofficialVocabularyTerm(String sessionToken, TechId vocabularyId, String code,
             String label, String description, Long previousTermOrdinal)
     {
-        logTracking(sessionToken, "add_unofficial_vocabulary_terms",
+        logTracking(sessionToken, "add_unofficial_vocabulary_term",
                 "ID(%s) CODE(%s), LABEL(%s), DESCRIPTION(%s), PREVIOUS_ORDINAL(%s)", vocabularyId,
                 code, label, description, Long.toString(previousTermOrdinal));
     }
 
+    public void addUnofficialVocabularyTerm(String sessionToken, Long vocabularyId,
+            NewVocabularyTerm term)
+    {
+        logTracking(sessionToken, "add-unofficial-vocabulary-term", "VOCABULARY_ID(%s) TERM(%s)",
+                vocabularyId, term);
+
+    }
+
     public int getMajorVersion()
     {
         return 0;
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/IGeneralInformationChangingService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/IGeneralInformationChangingService.java
index 7501de1e86d..28dc5b1dbb3 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/IGeneralInformationChangingService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/IGeneralInformationChangingService.java
@@ -22,6 +22,7 @@ import org.springframework.transaction.annotation.Transactional;
 
 import ch.systemsx.cisd.common.api.IRpcService;
 import ch.systemsx.cisd.openbis.generic.shared.DatabaseCreateOrDeleteModification;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.NewVocabularyTerm;
 import ch.systemsx.cisd.openbis.generic.shared.authorization.annotation.RolesAllowed;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseModificationKind.ObjectKind;
@@ -44,6 +45,11 @@ public interface IGeneralInformationChangingService extends IRpcService
      */
     public static final String SERVICE_URL = "/rmi-" + SERVICE_NAME + "-v1";
 
+    /**
+     * URL where the service is exposed via JSON interface.
+     */
+    public static final String JSON_SERVICE_URL = SERVICE_URL + ".json";
+
     @Transactional
     @RolesAllowed(RoleWithHierarchy.SPACE_USER)
     public void updateSampleProperties(String sessionToken, long sampleID,
@@ -51,10 +57,24 @@ public interface IGeneralInformationChangingService extends IRpcService
 
     /**
      * Adds new unofficial terms to a vocabulary starting from specified ordinal + 1.
+     * <p>
+     * 
+     * @deprecated Because the parameters refer to an internal openBIS class (TechID).
      */
     @Transactional
     @RolesAllowed(RoleWithHierarchy.SPACE_POWER_USER)
     @DatabaseCreateOrDeleteModification(value = ObjectKind.VOCABULARY_TERM)
+    @Deprecated
     public void addUnofficialVocabularyTerm(String sessionToken, TechId vocabularyId, String code,
             String label, String description, Long previousTermOrdinal);
+
+    /**
+     * Adds new unofficial terms to a vocabulary starting from specified ordinal + 1.
+     */
+    @Transactional
+    @RolesAllowed(RoleWithHierarchy.SPACE_POWER_USER)
+    @DatabaseCreateOrDeleteModification(value = ObjectKind.VOCABULARY_TERM)
+    public void addUnofficialVocabularyTerm(String sessionToken, Long vocabularyId,
+            NewVocabularyTerm term);
+
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/NewVocabularyTerm.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/NewVocabularyTerm.java
new file mode 100644
index 00000000000..1bd04f62bea
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/NewVocabularyTerm.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2011 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.openbis.generic.shared.api.v1.dto;
+
+import java.io.Serializable;
+
+/**
+ * A value object representing a new vocabulary term to be created by the openBIS backend.
+ * 
+ * @author Kaloyan Enimanev
+ */
+public class NewVocabularyTerm implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    private String code;
+
+    private String label;
+
+    private String description;
+
+    private Long previousTermOrdinal;
+
+    /**
+     * Return the term's code.
+     */
+    public String getCode()
+    {
+        return code;
+    }
+
+    /**
+     * Set the term's code.
+     */
+    public void setCode(String code)
+    {
+        this.code = code;
+    }
+
+    /**
+     * Return the term's label.
+     */
+    public String getLabel()
+    {
+        return label;
+    }
+
+    /**
+     * Set the term's label.
+     */
+    public void setLabel(String label)
+    {
+        this.label = label;
+    }
+
+    /**
+     * Return the term's description.
+     */
+    public String getDescription()
+    {
+        return description;
+    }
+
+    /**
+     * Set the term's description.
+     */
+    public void setDescription(String description)
+    {
+        this.description = description;
+    }
+
+    /**
+     * Return the position of predecessor term in the vocabulary.
+     */
+    public Long getPreviousTermOrdinal()
+    {
+        return previousTermOrdinal;
+    }
+
+    /**
+     * Set the position of predecessor term in the vocabulary.
+     */
+    public void setPreviousTermOrdinal(Long previousTermOrdinal)
+    {
+        this.previousTermOrdinal = previousTermOrdinal;
+    }
+
+    @Override
+    public String toString()
+    {
+        return "NewVocabularyTerm [code=" + code + ", label=" + label + ", description="
+                + description + ", previousTermOrdinal=" + previousTermOrdinal + "]";
+    }
+
+}
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/remoteapitest/api/v1/GeneralInformationChangingServiceJsonApiTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/remoteapitest/api/v1/GeneralInformationChangingServiceJsonApiTest.java
new file mode 100644
index 00000000000..0c15a988700
--- /dev/null
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/remoteapitest/api/v1/GeneralInformationChangingServiceJsonApiTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2010 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.openbis.remoteapitest.api.v1;
+
+import static org.testng.AssertJUnit.assertEquals;
+
+import java.net.MalformedURLException;
+
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.IGeneralInformationChangingService;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.IGeneralInformationService;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.NewVocabularyTerm;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Vocabulary;
+import ch.systemsx.cisd.openbis.remoteapitest.RemoteApiTestCase;
+
+/**
+ * Verifies that an instance of {@link IGeneralInformationService} is published via JSON-RPC and
+ * that it is correctly functioning with external clients.
+ * 
+ * @author Kaloyan Enimanev
+ */
+@Test(groups =
+    { "remote api" })
+public class GeneralInformationChangingServiceJsonApiTest extends RemoteApiTestCase
+{
+    protected IGeneralInformationService generalInfoService;
+
+    protected IGeneralInformationChangingService generalInfoChangingService;
+
+    protected String sessionToken;
+
+    @BeforeMethod
+    public void beforeMethod() throws MalformedURLException
+    {
+        generalInfoService = TestJsonServiceFactory.createGeneralInfoService();
+        generalInfoChangingService = TestJsonServiceFactory.createGeneralInfoChangingService();
+        sessionToken = generalInfoService.tryToAuthenticateForAllServices("test", "a");
+    }
+
+    @AfterMethod
+    public void afterMethod() throws MalformedURLException
+    {
+        generalInfoService.logout(sessionToken);
+    }
+
+    @Test
+    public void testUpdateSampleProperties()
+    {
+    }
+
+    @Test
+    public void testAddUnofficialTerm()
+    {
+        String vocabularyCode = "ORGANISM";
+        Vocabulary vocabulary = fetchVocabularyFromServer(vocabularyCode);
+
+        assertEquals(
+                "Vocabulary[ORGANISM,[VocabularyTerm[RAT,RAT], VocabularyTerm[DOG,DOG], VocabularyTerm[HUMAN,HUMAN], "
+                        + "VocabularyTerm[GORILLA,GORILLA], VocabularyTerm[FLY,FLY]]]",
+                vocabulary.toString());
+
+        NewVocabularyTerm newTerm = new NewVocabularyTerm();
+        newTerm.setCode("ALIEN");
+        newTerm.setLabel("Alien species");
+        newTerm.setDescription("Extraterrestrial form of life.");
+        newTerm.setPreviousTermOrdinal(0L);
+
+        generalInfoChangingService.addUnofficialVocabularyTerm(sessionToken, vocabulary.getId(),
+                newTerm);
+
+        Vocabulary updatedVocabulary = fetchVocabularyFromServer(vocabularyCode);
+
+        assertEquals(
+                "Vocabulary[ORGANISM,[VocabularyTerm[RAT,RAT], VocabularyTerm[DOG,DOG], VocabularyTerm[HUMAN,HUMAN], "
+                        + "VocabularyTerm[GORILLA,GORILLA], VocabularyTerm[FLY,FLY], VocabularyTerm[ALIEN,Alien species]]]",
+                updatedVocabulary.toString());
+    }
+
+    private Vocabulary fetchVocabularyFromServer(String vocabularyCode)
+    {
+        for (Vocabulary vocabulary : generalInfoService.listVocabularies(sessionToken))
+        {
+            if (vocabulary.getCode().equals(vocabularyCode))
+            {
+                return vocabulary;
+            }
+        }
+        return null;
+    }
+}
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/remoteapitest/api/v1/GeneralInformationServiceJsonApiTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/remoteapitest/api/v1/GeneralInformationServiceJsonApiTest.java
index a4034c23b8d..3fe9c53becf 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/remoteapitest/api/v1/GeneralInformationServiceJsonApiTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/remoteapitest/api/v1/GeneralInformationServiceJsonApiTest.java
@@ -17,11 +17,11 @@
 package ch.systemsx.cisd.openbis.remoteapitest.api.v1;
 
 import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
 import static org.testng.AssertJUnit.assertTrue;
 import static org.testng.AssertJUnit.fail;
 
 import java.net.MalformedURLException;
-import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -34,10 +34,6 @@ import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import com.googlecode.jsonrpc4j.JsonRpcHttpClient;
-import com.googlecode.jsonrpc4j.ProxyUtil;
-
-import ch.systemsx.cisd.common.collections.CollectionUtils;
 import ch.systemsx.cisd.common.utilities.ToStringComparator;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.IGeneralInformationService;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet;
@@ -67,25 +63,13 @@ import ch.systemsx.cisd.openbis.remoteapitest.RemoteApiTestCase;
     { "remote api" })
 public class GeneralInformationServiceJsonApiTest extends RemoteApiTestCase
 {
-    private static final String SERVICE_URL = "http://localhost:8888/openbis/"
-            + IGeneralInformationService.JSON_SERVICE_URL;
-
     protected IGeneralInformationService generalInformationService;
 
     protected String sessionToken;
 
     protected IGeneralInformationService createService()
     {
-        try
-        {
-            JsonRpcHttpClient client = new JsonRpcHttpClient(new URL(SERVICE_URL));
-            return ProxyUtil.createProxy(getClass().getClassLoader(),
-                    IGeneralInformationService.class, client);
-        } catch (MalformedURLException ex)
-        {
-            throw new RuntimeException("Failed to initialize json-rpc client: " + ex.getMessage(),
-                    ex);
-        }
+        return TestJsonServiceFactory.createGeneralInfoService();
     }
 
     @BeforeMethod
@@ -601,21 +585,32 @@ public class GeneralInformationServiceJsonApiTest extends RemoteApiTestCase
     @Test
     public void testListVocabularyTerms()
     {
-        List<Vocabulary> result = generalInformationService.listVocabularies(sessionToken);
-        List<Vocabulary> nonInternals =
-                CollectionUtils.filter(result, new CollectionUtils.ICollectionFilter<Vocabulary>()
-                    {
-                        public boolean isPresent(Vocabulary vocabulary)
-                        {
-                            return false == vocabulary.isInternalNamespace();
-                        }
-                    });
-        assertEquals(3, nonInternals.size());
+        List<Vocabulary> vocabularies = generalInformationService.listVocabularies(sessionToken);
 
+        final Vocabulary gender = findVocabulary(vocabularies, "GENDER");
+        assertEquals(
+                "Vocabulary[GENDER,[VocabularyTerm[MALE,MALE], VocabularyTerm[FEMALE,FEMALE]]]",
+                gender.toString());
+        final Vocabulary human = findVocabulary(vocabularies, "HUMAN");
         assertEquals(
-                "[Vocabulary[GENDER,[VocabularyTerm[MALE,MALE], VocabularyTerm[FEMALE,FEMALE]]],"
-                        + " Vocabulary[HUMAN,[VocabularyTerm[MAN,MAN], VocabularyTerm[WOMAN,WOMAN], VocabularyTerm[CHILD,CHILD]]],"
-                        + " Vocabulary[ORGANISM,[VocabularyTerm[RAT,RAT], VocabularyTerm[DOG,DOG], VocabularyTerm[HUMAN,HUMAN], VocabularyTerm[GORILLA,GORILLA], VocabularyTerm[FLY,FLY]]]]",
-                nonInternals.toString());
+                "Vocabulary[HUMAN,[VocabularyTerm[MAN,MAN], VocabularyTerm[WOMAN,WOMAN], VocabularyTerm[CHILD,CHILD]]]",
+                human.toString());
+
+        Vocabulary organism = findVocabulary(vocabularies, "ORGANISM");
+        assertNotNull(organism);
+        assertEquals("VocabularyTerm[RAT,RAT]", organism.getTerms().get(0).toString());
     }
+
+    private Vocabulary findVocabulary(List<Vocabulary> vocabularies, String vocabularyCode)
+    {
+        for (Vocabulary vocabulary : vocabularies)
+        {
+            if (vocabulary.getCode().equals(vocabularyCode))
+            {
+                return vocabulary;
+            }
+        }
+        return null;
+    }
+
 }
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/remoteapitest/api/v1/TestJsonServiceFactory.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/remoteapitest/api/v1/TestJsonServiceFactory.java
new file mode 100644
index 00000000000..e08e83ecfd3
--- /dev/null
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/remoteapitest/api/v1/TestJsonServiceFactory.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2011 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.openbis.remoteapitest.api.v1;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import com.googlecode.jsonrpc4j.JsonRpcHttpClient;
+import com.googlecode.jsonrpc4j.ProxyUtil;
+
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.IGeneralInformationChangingService;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.IGeneralInformationService;
+
+/**
+ * @author Kaloyan Enimanev
+ */
+public class TestJsonServiceFactory
+{
+    private static final String OPENBIS_SERVER_URL = "http://localhost:8888/openbis/";
+
+    private static final String GENERAL_INFO_SERVICE_URL = OPENBIS_SERVER_URL
+            + IGeneralInformationService.JSON_SERVICE_URL;
+
+    private static final String GENERAL_INFO_CHANGING_SERVICE_URL = OPENBIS_SERVER_URL
+            + IGeneralInformationChangingService.JSON_SERVICE_URL;
+
+    static IGeneralInformationService createGeneralInfoService()
+    {
+        try
+        {
+            JsonRpcHttpClient client = new JsonRpcHttpClient(new URL(GENERAL_INFO_SERVICE_URL));
+            return ProxyUtil.createProxy(TestJsonServiceFactory.class.getClassLoader(),
+                    IGeneralInformationService.class, client);
+        } catch (MalformedURLException ex)
+        {
+            throw new RuntimeException("Failed to initialize json-rpc client: " + ex.getMessage(),
+                    ex);
+        }
+    }
+
+    static IGeneralInformationChangingService createGeneralInfoChangingService()
+    {
+        try
+        {
+            JsonRpcHttpClient client =
+                    new JsonRpcHttpClient(new URL(GENERAL_INFO_CHANGING_SERVICE_URL));
+            return ProxyUtil.createProxy(TestJsonServiceFactory.class.getClassLoader(),
+                    IGeneralInformationChangingService.class, client);
+        } catch (MalformedURLException ex)
+        {
+            throw new RuntimeException("Failed to initialize json-rpc client: " + ex.getMessage(),
+                    ex);
+        }
+    }
+
+}
-- 
GitLab