You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@taverna.apache.org by st...@apache.org on 2015/02/17 12:36:59 UTC

[39/70] [abbrv] incubator-taverna-common-activities git commit: taverna-rest-activity/

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/598ad6de/taverna-rest-activity/src/main/resources/META-INF/services/net.sf.taverna.t2.workflowmodel.health.HealthChecker
----------------------------------------------------------------------
diff --git a/taverna-rest-activity/src/main/resources/META-INF/services/net.sf.taverna.t2.workflowmodel.health.HealthChecker b/taverna-rest-activity/src/main/resources/META-INF/services/net.sf.taverna.t2.workflowmodel.health.HealthChecker
new file mode 100644
index 0000000..737de29
--- /dev/null
+++ b/taverna-rest-activity/src/main/resources/META-INF/services/net.sf.taverna.t2.workflowmodel.health.HealthChecker
@@ -0,0 +1 @@
+net.sf.taverna.t2.activities.rest.RESTActivityHealthChecker
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/598ad6de/taverna-rest-activity/src/main/resources/META-INF/spring/rest-activity-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-rest-activity/src/main/resources/META-INF/spring/rest-activity-context-osgi.xml b/taverna-rest-activity/src/main/resources/META-INF/spring/rest-activity-context-osgi.xml
new file mode 100644
index 0000000..44fa5c4
--- /dev/null
+++ b/taverna-rest-activity/src/main/resources/META-INF/spring/rest-activity-context-osgi.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:beans="http://www.springframework.org/schema/beans"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans.xsd
+                      http://www.springframework.org/schema/osgi
+                      http://www.springframework.org/schema/osgi/spring-osgi.xsd">
+
+	<service ref="RESTActivityHealthChecker" interface="net.sf.taverna.t2.workflowmodel.health.HealthChecker" />
+
+	<service ref="restActivityFactory" interface="net.sf.taverna.t2.workflowmodel.processor.activity.ActivityFactory" />
+
+	<reference id="credentialManager" interface="net.sf.taverna.t2.security.credentialmanager.CredentialManager" />
+	<reference id="edits" interface="net.sf.taverna.t2.workflowmodel.Edits" />
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/598ad6de/taverna-rest-activity/src/main/resources/META-INF/spring/rest-activity-context.xml
----------------------------------------------------------------------
diff --git a/taverna-rest-activity/src/main/resources/META-INF/spring/rest-activity-context.xml b/taverna-rest-activity/src/main/resources/META-INF/spring/rest-activity-context.xml
new file mode 100644
index 0000000..f1c900e
--- /dev/null
+++ b/taverna-rest-activity/src/main/resources/META-INF/spring/rest-activity-context.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<bean id="RESTActivityHealthChecker" class="net.sf.taverna.t2.activities.rest.RESTActivityHealthChecker" />
+
+	<bean id="restActivityFactory" class="net.sf.taverna.t2.activities.rest.RESTActivityFactory">
+		<property name="credentialManager" ref="credentialManager" />
+		<property name="edits" ref="edits" />
+	</bean>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/598ad6de/taverna-rest-activity/src/main/resources/schema.json
----------------------------------------------------------------------
diff --git a/taverna-rest-activity/src/main/resources/schema.json b/taverna-rest-activity/src/main/resources/schema.json
new file mode 100644
index 0000000..376b479
--- /dev/null
+++ b/taverna-rest-activity/src/main/resources/schema.json
@@ -0,0 +1,75 @@
+{
+    "$schema": "http://json-schema.org/draft-03/schema#",
+    "id": "http://ns.taverna.org.uk/2010/activity/rest.schema.json",
+    "title": "REST activity configuration",
+    "type": "object",
+    "properties": {
+        "@context": {
+            "description": "JSON-LD context for interpreting the configuration as RDF",
+            "required": true,
+            "enum": ["http://ns.taverna.org.uk/2010/activity/rest.context.json"]
+        },
+        "request": {
+            "type": "object",
+            "description": "The HTTP request the REST activity is to perform",
+            "required": true,
+            "properties": {
+                "httpMethod": {
+                    "description": "The HTTP method of the REST request",
+                    "default": "GET",
+                    "enum": ["GET", "POST", "PUT", "DELETE"]
+                },
+                "absoluteURITemplate": {
+                    "type": "string",
+                    "required": true,
+                    "description": "The URI template for this request.  The template is an URI with 0 or more {variables}, e.g. http://example.com/user/{username}?page={page} (RFC6570 Level 1). Matching activity input ports are created for each variable, so the variable name must be a valid Taverna input port name. "
+                },
+                "headers": {
+                    "description": "HTTP headers of the request. GET requests SHOULD include Accept. PUT and POST request SHOULD include Content-Type. The header Expect: 100-continue is recognized and supported by the REST activity for POST and PUT requests.",
+                    "type": "array",
+                    "items": {
+                        "type": "object",
+                        "properties": {
+                            "header": {
+                                "type": "string",
+                                "required": true,
+                                "description": "The HTTP header name, e.g. Accept or Content-Type"
+                            },
+                            "value": {
+                                "type": "string",
+                                "required": true,
+                                "description": "The value of the HTTP header, e.g. text/plain"
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        "outgoingDataFormat": {
+            "description": "Format of the POSTed content",
+            "default": "String",
+            "enum": ["String", "Binary"]
+        },
+        "showRedirectionOutputPort": {
+            "type": "boolean",
+            "default": false,
+            "description": "If set, the output port 'redirection' is added, showing the URL of the requested body. This is particularly useful for following redirection. "
+        },
+        "showActualURLPort": {
+            "type": "boolean",
+            "default": false,
+            "description": "If true, the output port 'actualURL' is added to show the requested URL after expanding the template."
+        },
+        "showResponseHeadersPort": {
+            "type": "boolean",
+            "default": false,
+            "description": "If true, the output port 'responseHeaders' is added to show the response header from the HTTP server."
+        },
+        "escapeParameters": {
+            "type": "boolean",
+            "default": true,
+            "description": "If true (default), input port values are URI-escaped (RFC 2396) before expanding variables in the URI template."
+        }
+        
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/598ad6de/taverna-rest-activity/src/test/java/net/sf/taverna/t2/activities/rest/ApacheHttpClientUsageTest.java.bak
----------------------------------------------------------------------
diff --git a/taverna-rest-activity/src/test/java/net/sf/taverna/t2/activities/rest/ApacheHttpClientUsageTest.java.bak b/taverna-rest-activity/src/test/java/net/sf/taverna/t2/activities/rest/ApacheHttpClientUsageTest.java.bak
new file mode 100644
index 0000000..518fbc3
--- /dev/null
+++ b/taverna-rest-activity/src/test/java/net/sf/taverna/t2/activities/rest/ApacheHttpClientUsageTest.java.bak
@@ -0,0 +1,215 @@
+package net.sf.taverna.t2.activities.rest;
+
+import java.awt.BorderLayout;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.ExecutionContext;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class ApacheHttpClientUsageTest extends JFrame implements ActionListener
+{
+  private JComboBox cbHttpMethod;
+  private JTextField tfAddressBar;
+  private JButton bGo;
+  private JTextArea taResponse;
+  private JScrollPane spResponse;
+  
+  
+  /**
+   * Constructor is solely involved in the UI initialisation.
+   */
+  public ApacheHttpClientUsageTest()
+  {
+    Container contentPane = this.getContentPane();
+    contentPane.setLayout(new BorderLayout(5,5));
+    
+    bGo = new JButton("GO!");
+    bGo.addActionListener(this);
+    bGo.setDefaultCapable(true);
+    this.getRootPane().setDefaultButton(bGo);
+    
+    tfAddressBar = new JTextField(50);
+    tfAddressBar.setPreferredSize(new Dimension(0,bGo.getPreferredSize().height));
+    tfAddressBar.setText("http://test.biocatalogue.org/");
+    
+    JPanel jpAddressBar = new JPanel();
+    jpAddressBar.add(tfAddressBar);
+    jpAddressBar.add(bGo);
+    
+    cbHttpMethod = new JComboBox(RESTActivity.HTTP_METHOD.values());
+    cbHttpMethod.setBorder(BorderFactory.createCompoundBorder(
+        BorderFactory.createEmptyBorder(5, 5, 5, 5),
+        cbHttpMethod.getBorder()));
+    
+    JPanel jpAllNavigation = new JPanel(new BorderLayout());
+    jpAllNavigation.add(jpAddressBar, BorderLayout.NORTH);
+    jpAllNavigation.add(cbHttpMethod, BorderLayout.CENTER);
+    
+    contentPane.add(jpAllNavigation, BorderLayout.NORTH);
+    
+    taResponse = new JTextArea(20, 20);
+    taResponse.setEditable(true);
+    
+    spResponse = new JScrollPane(taResponse);
+    spResponse.setBorder(BorderFactory.createCompoundBorder(
+        BorderFactory.createEmptyBorder(0, 5, 5, 5),
+        BorderFactory.createEtchedBorder()));
+    contentPane.add(spResponse, BorderLayout.CENTER);
+    
+    this.pack();
+    this.setLocationRelativeTo(null); // center on screen
+  }
+  
+  
+  /**
+   * Click handler for the only button there is - the "GO!" button 
+   */
+  public void actionPerformed(ActionEvent e)
+  {
+    if (e.getSource().equals(bGo))
+    {
+      try { 
+        switch ((RESTActivity.HTTP_METHOD)cbHttpMethod.getSelectedItem()) {
+          case GET:    doGET(); break;
+          case POST:   doPOST(); break;
+          case PUT:    doPUT(); break;
+          case DELETE: doDELETE(); break;
+        }
+      }
+      catch (Exception ex) {
+        taResponse.setText(ex + "\n\n" + ex.getStackTrace());
+      }
+    }
+  }
+  
+  
+  private void doGET() {
+    HttpGet httpGet = new HttpGet(tfAddressBar.getText());
+    httpGet.addHeader("Accept", "application/xml");
+    performHTTPRequest(httpGet);
+  }
+  
+  
+  private void doPOST() throws UnsupportedEncodingException {
+//    // POST TO MYEXPERIMENT - basic auth
+//    HttpPost httpPost = new HttpPost("http://sandbox.myexperiment.org/comment.xml");
+//    httpPost.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(("LOGIN" + ":" + "PASSWORD").getBytes())));
+//    httpPost.addHeader("Accept", "application/xml");
+//    httpPost.addHeader("Content-Type", "application/xml");
+//    httpPost.setEntity(new StringEntity("<comment><subject resource=\"http://sandbox.myexperiment.org/files/226\"/><comment>1234567</comment></comment>"));
+//    performHTTPRequest(httpPost);
+    
+    // POST TO BIOCATALOGUE - no auth
+    HttpPost httpPost = new HttpPost(tfAddressBar.getText());
+    httpPost.addHeader("Accept", "application/xml");
+    httpPost.addHeader("Content-Type", "application/xml");
+    httpPost.setEntity(new StringEntity("<searchByData><searchType>input</searchType><limit>20</limit><data>test</data></searchByData>"));
+    performHTTPRequest(httpPost);
+  }
+  
+  
+  private void doPUT() throws UnsupportedEncodingException {
+    HttpPut httpPut = new HttpPut("http://sandbox.myexperiment.org/comment.xml?id=251");
+    httpPut.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(("LOGIN" + ":" + "PASSWORD").getBytes())));
+    httpPut.addHeader("Accept", "application/xml");
+    httpPut.addHeader("Content-Type", "application/xml");
+    httpPut.setEntity(new StringEntity("<comment><subject resource=\"http://sandbox.myexperiment.org/files/226\"/><comment>12345678</comment></comment>"));
+    performHTTPRequest(httpPut);
+  }
+  
+  
+  private void doDELETE() {
+    HttpDelete httpDelete = new HttpDelete("http://sandbox.myexperiment.org/comment.xml?id=251");
+    httpDelete.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(("LOGIN" + ":" + "PASSWORD").getBytes())));
+    httpDelete.addHeader("Accept", "application/xml");
+    performHTTPRequest(httpDelete);
+  }
+  
+  
+  private void performHTTPRequest(HttpRequestBase httpRequest) {
+    try {
+      StringBuilder responseStr = new StringBuilder();
+      // ---------------------------------------------
+      
+      HttpClient httpClient = new DefaultHttpClient();
+      HttpContext localContext = new BasicHttpContext();
+      
+      HttpResponse response = httpClient.execute(httpRequest, localContext);
+      // ---
+      // TRACK WHERE THE FINAL REDIRECT ENDS UP - target host + URI
+      HttpHost target = (HttpHost) localContext.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
+      HttpUriRequest req = (HttpUriRequest) localContext.getAttribute(ExecutionContext.HTTP_REQUEST);
+
+      responseStr.append("Final request URI: " + req.getMethod() + " " + target + req.getURI() + "\n");
+//      System.out.println("Target host: " + target);
+//      System.out.println("Final request URI: " + req.getURI());
+//      System.out.println("Final request method: " + req.getMethod());
+      // ---
+      responseStr.append(response.getStatusLine() + "\n");
+      
+      HttpEntity entity = response.getEntity();
+      responseStr.append(entity.getContentType() + "\n\n");
+      
+      if (entity != null) {
+        InputStream in = entity.getContent();
+        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+        
+        String str;
+        while ((str = reader.readLine()) != null) {
+          responseStr.append(str + "\n");
+        }
+        
+        taResponse.setText(responseStr.toString());
+        taResponse.setCaretPosition(0);
+      }
+      
+      httpClient.getConnectionManager().shutdown();
+    }
+    catch (Exception ex) {
+      taResponse.setText(ex.getMessage() + "\n\n" + ex.getStackTrace());
+    }
+  }
+  
+  
+  public static void main(String[] args)
+  {
+    ApacheHttpClientUsageTest frame = new ApacheHttpClientUsageTest();
+    frame.setVisible(true);
+    frame.tfAddressBar.setCaretPosition(frame.tfAddressBar.getText().length());
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/598ad6de/taverna-rest-activity/src/test/java/net/sf/taverna/t2/activities/rest/ExampleActivityTest.java.bak
----------------------------------------------------------------------
diff --git a/taverna-rest-activity/src/test/java/net/sf/taverna/t2/activities/rest/ExampleActivityTest.java.bak b/taverna-rest-activity/src/test/java/net/sf/taverna/t2/activities/rest/ExampleActivityTest.java.bak
new file mode 100644
index 0000000..56b6904
--- /dev/null
+++ b/taverna-rest-activity/src/test/java/net/sf/taverna/t2/activities/rest/ExampleActivityTest.java.bak
@@ -0,0 +1,153 @@
+package net.sf.taverna.t2.activities.rest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import net.sf.taverna.t2.activities.testutils.ActivityInvoker;
+import net.sf.taverna.t2.reference.ErrorDocument;
+import net.sf.taverna.t2.reference.ExternalReferenceSPI;
+import net.sf.taverna.t2.workflowmodel.OutputPort;
+import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityConfigurationException;
+import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityInputPort;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class ExampleActivityTest {
+
+	private RESTActivityConfigurationBean configBean;
+
+	private RESTActivity activity = new RESTActivity();
+
+	@Before
+	public void makeConfigBean() throws Exception {
+		configBean = new RESTActivityConfigurationBean();
+		configBean.setExampleString("something");
+		configBean
+				.setExampleUri(URI.create("http://localhost:8080/myEndPoint"));
+	}
+
+	@Test(expected = ActivityConfigurationException.class)
+	public void invalidConfiguration() throws ActivityConfigurationException {
+		RESTActivityConfigurationBean invalidBean = new RESTActivityConfigurationBean();
+		invalidBean.setExampleString("invalidExample");
+		// Should throw ActivityConfigurationException
+		activity.configure(invalidBean);
+	}
+
+	@Test
+	public void executeAsynch() throws Exception {
+		activity.configure(configBean);
+
+		Map<String, Object> inputs = new HashMap<String, Object>();
+		inputs.put("firstInput", "hello");
+
+		Map<String, Class<?>> expectedOutputTypes = new HashMap<String, Class<?>>();
+		expectedOutputTypes.put("simpleOutput", String.class);
+		expectedOutputTypes.put("moreOutputs", ExternalReferenceSPI.class);
+
+		Map<String, Object> outputs = ActivityInvoker.invokeAsyncActivity(
+				activity, inputs, expectedOutputTypes);
+
+		assertEquals("Unexpected outputs", 2, outputs.size());
+		assertEquals("da39a3ee5e6b4b0d3255bfef95601890afd80709", outputs.get("simpleOutput"));
+		ErrorDocument errorDoc = (ErrorDocument) outputs.get("moreOutputs");
+		assertEquals("java.lang.Exception: There are no more values",
+		             errorDoc.getExceptionMessage());
+
+	}
+	
+	
+	
+	@Test
+  public void checksumOfExtraData() throws Exception {
+    configBean.setExampleString("specialCase");
+	  activity.configure(configBean);
+
+    Map<String, Object> inputs = new HashMap<String, Object>();
+    inputs.put("firstInput", "hello");
+    inputs.put("extraData", Arrays.asList("Test1".getBytes("utf8"),
+        "Test2".getBytes("utf8")));
+
+
+    Map<String, Class<?>> expectedOutputTypes = new HashMap<String, Class<?>>();
+    expectedOutputTypes.put("simpleOutput", String.class);
+    expectedOutputTypes.put("moreOutputs", ExternalReferenceSPI.class);
+    expectedOutputTypes.put("report", String.class);
+
+
+    Map<String, Object> outputs = ActivityInvoker.invokeAsyncActivity(
+        activity, inputs, expectedOutputTypes);
+
+    assertEquals("Unexpected outputs", 3, outputs.size());
+    assertEquals("35bceb434ff8e69fb89b829e461c921a28b423b3", outputs.get("simpleOutput"));
+    ErrorDocument errorDoc = (ErrorDocument) outputs.get("moreOutputs");
+    assertEquals("java.lang.Exception: There are no more values",
+                 errorDoc.getExceptionMessage());
+
+  }
+	
+	
+
+	@Test
+	public void reConfiguredActivity() throws Exception {
+		assertEquals("Unexpected inputs", 0, activity.getInputPorts().size());
+		assertEquals("Unexpected outputs", 0, activity.getOutputPorts().size());
+
+		activity.configure(configBean);
+		assertEquals("Unexpected inputs", 1, activity.getInputPorts().size());
+		assertEquals("Unexpected outputs", 2, activity.getOutputPorts().size());
+
+		activity.configure(configBean);
+		// Should not change on reconfigure
+		assertEquals("Unexpected inputs", 1, activity.getInputPorts().size());
+		assertEquals("Unexpected outputs", 2, activity.getOutputPorts().size());
+	}
+
+	@Test
+	public void reConfiguredSpecialPorts() throws Exception {
+		activity.configure(configBean);
+
+		RESTActivityConfigurationBean specialBean = new RESTActivityConfigurationBean();
+		specialBean.setExampleString("specialCase");
+		specialBean.setExampleUri(URI
+				.create("http://localhost:8080/myEndPoint"));
+		activity.configure(specialBean);		
+		// Should now have added the optional ports
+		assertEquals("Unexpected inputs", 2, activity.getInputPorts().size());
+		assertEquals("Unexpected outputs", 3, activity.getOutputPorts().size());
+	}
+
+	@Test
+	public void configureActivity() throws Exception {
+		Set<String> expectedInputs = new HashSet<String>();
+		expectedInputs.add("firstInput");
+
+		Set<String> expectedOutputs = new HashSet<String>();
+		expectedOutputs.add("simpleOutput");
+		expectedOutputs.add("moreOutputs");
+
+		activity.configure(configBean);
+
+		Set<ActivityInputPort> inputPorts = activity.getInputPorts();
+		assertEquals(expectedInputs.size(), inputPorts.size());
+		for (ActivityInputPort inputPort : inputPorts) {
+			assertTrue("Wrong input : " + inputPort.getName(), expectedInputs
+					.remove(inputPort.getName()));
+		}
+
+		Set<OutputPort> outputPorts = activity.getOutputPorts();
+		assertEquals(expectedOutputs.size(), outputPorts.size());
+		for (OutputPort outputPort : outputPorts) {
+			assertTrue("Wrong output : " + outputPort.getName(),
+					expectedOutputs.remove(outputPort.getName()));
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/598ad6de/taverna-rest-activity/src/test/java/net/sf/taverna/t2/activities/rest/URISignatureHandlerTest.java
----------------------------------------------------------------------
diff --git a/taverna-rest-activity/src/test/java/net/sf/taverna/t2/activities/rest/URISignatureHandlerTest.java b/taverna-rest-activity/src/test/java/net/sf/taverna/t2/activities/rest/URISignatureHandlerTest.java
new file mode 100644
index 0000000..8aad5ec
--- /dev/null
+++ b/taverna-rest-activity/src/test/java/net/sf/taverna/t2/activities/rest/URISignatureHandlerTest.java
@@ -0,0 +1,594 @@
+package net.sf.taverna.t2.activities.rest;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.*;
+
+import static org.junit.Assert.*;
+
+public class URISignatureHandlerTest {
+	// ==========================================================================
+	// TEST URIs
+	// ==========================================================================
+
+	final String validURI_NoPlaceholders = "http://sysmo-db.org/sops/";
+	final String validURI_PlaceholdersInMainPartOfURIOnly = "http://sysmo-db.org/sops/{sop_id}/experimental_conditions/{cond_id}";
+	final String validURI_PlaceholdersInQueryStringOnly = "http://sandbox.myexperiment.org/user.xml?id={user_id}&verbose=true";
+	final String validURI_3MixedPlaceholders = "http://sysmo-db.org/sops/{sop_id}/experimental_conditions/{cond_id}?condition_unit={unit}";
+
+	final String badURI_nullURI = null;
+	final String badURI_emptyURI = "";
+	final String badURI_SingleOpeningSymbolNoClosingSymbol = "http://sysmo-db.org/sops/{sop_id/experimental_conditions";
+	final String badURI_SingleClosingSymbolNoOpeningSymbol = "http://sysmo-db.org/sops/sop_id}/experimental_conditions";
+	final String badURI_DoubleOpeningSymbolsNoClosingSymbol = "http://sysmo-db.org/sops/{{sop_id/experimental_conditions";
+	final String badURI_DoubleOpeningSymbols = "http://sysmo-db.org/sops/{{sop_id}/experimental_conditions";
+	final String badURI_DoubleOpeningSymbolsSpaced = "http://sysmo-db.org/sops/{sop_{id}/experimental_conditions";
+	final String badURI_DoubleClosingSymbols = "http://sysmo-db.org/sops/{sop_id}}/experimental_conditions";
+	final String badURI_DoubleClosingSymbolsSpaced = "http://sysmo-db.org/sops/{sop}_id}/experimental_conditions";
+	final String badURI_NestedPlaceholders = "http://sandbox.myexperiment.org/user.xml?id={user_{id}}&verbose=true";
+	final String badURI_NestedPlaceholdersSpaced = "http://sandbox.myexperiment.org/user.xml?id={us{er}_id}&verbose=true";
+	final String badURI_DuplicatePlaceholders = "http://sandbox.myexperiment.org/user.xml?id={user_id}&verbose={user_id}";
+	final String badURI_DuplicatePlaceholdersWithOthers = "http://sysmo-db.org/sops/{unit}/experimental_conditions/{cond_id}?condition_unit={unit}";
+	
+     final String validURI_MultipleQueryString =
+            "http://dr-site.esrin.esa.int/{catalogue}/genesi/ASA_IMS_1P/rdf/?count={count?}&startPage={startPage?}&startIndex={startIndex?}&q={searchTerms?}"; 	
+
+	// ==========================================================================
+	// TEST URI SIGNATURE BOOLEAN VALIDATION
+	// ==========================================================================
+
+	// success cases
+
+	@Test
+	public void isValid_validURI_NoPlaceholders() {
+		assertTrue(URISignatureHandler.isValid(validURI_NoPlaceholders));
+	}
+
+	@Test
+	public void isValid_validURI_PlaceholdersInMainPartOfURIOnly() {
+		assertTrue(URISignatureHandler
+				.isValid(validURI_PlaceholdersInMainPartOfURIOnly));
+	}
+
+	@Test
+	public void isValid_validURI_PlaceholdersInQueryStringOnly() {
+		assertTrue(URISignatureHandler
+				.isValid(validURI_PlaceholdersInQueryStringOnly));
+	}
+
+	@Test
+	public void isValid_validURI_MixedPlaceholders() {
+		assertTrue(URISignatureHandler.isValid(validURI_3MixedPlaceholders));
+	}
+
+ 	@Test
+ 	public void isValid_validURI_MultipleQueryString() {
+ 		assertTrue(URISignatureHandler.isValid(validURI_MultipleQueryString));
+ 	}
+ 	
+	// failure cases
+
+	@Test
+	public void isValid_badURI_nullURI() {
+		assertFalse(URISignatureHandler.isValid(badURI_nullURI));
+	}
+
+	@Test
+	public void isValid_badURI_emptyURI() {
+		assertFalse(URISignatureHandler.isValid(badURI_emptyURI));
+	}
+
+	@Test
+	public void isValid_badURI_SingleOpeningSymbolNoClosingSymbol() {
+		assertFalse(URISignatureHandler
+				.isValid(badURI_SingleOpeningSymbolNoClosingSymbol));
+	}
+
+	@Test
+	public void isValid_badURI_SingleClosingSymbolNoOpeningSymbol() {
+		assertFalse(URISignatureHandler
+				.isValid(badURI_SingleClosingSymbolNoOpeningSymbol));
+	}
+
+	@Test
+	public void isValid_badURI_DoubleOpeningSymbolsNoClosingSymbol() {
+		assertFalse(URISignatureHandler
+				.isValid(badURI_DoubleOpeningSymbolsNoClosingSymbol));
+	}
+
+	@Test
+	public void isValid_badURI_DoubleOpeningSymbols() {
+		assertFalse(URISignatureHandler.isValid(badURI_DoubleOpeningSymbols));
+	}
+
+	@Test
+	public void isValid_badURI_DoubleOpeningSymbolsSpaced() {
+		assertFalse(URISignatureHandler
+				.isValid(badURI_DoubleOpeningSymbolsSpaced));
+	}
+
+	@Test
+	public void isValid_badURI_DoubleClosingSymbols() {
+		assertFalse(URISignatureHandler.isValid(badURI_DoubleClosingSymbols));
+	}
+
+	@Test
+	public void isValid_badURI_DoubleClosingSymbolsSpaced() {
+		assertFalse(URISignatureHandler
+				.isValid(badURI_DoubleClosingSymbolsSpaced));
+	}
+
+	@Test
+	public void isValid_badURI_NestedPlaceholders() {
+		assertFalse(URISignatureHandler.isValid(badURI_NestedPlaceholders));
+	}
+
+	@Test
+	public void isValid_badURI_NestedPlaceholdersSpaced() {
+		assertFalse(URISignatureHandler
+				.isValid(badURI_NestedPlaceholdersSpaced));
+	}
+
+	@Test
+	public void isValid_badURI_DuplicatePlaceholders() {
+		assertFalse(URISignatureHandler.isValid(badURI_DuplicatePlaceholders));
+	}
+
+	@Test
+	public void isValid_badURI_DuplicatePlaceholdersWithOthers() {
+		assertFalse(URISignatureHandler
+				.isValid(badURI_DuplicatePlaceholdersWithOthers));
+	}
+
+	// ==========================================================================
+	// TEST URI SIGNATURE DETAILED VALIDATION
+	// ==========================================================================
+
+	// success cases
+
+	@Test
+	public void validate_validURI_NoPlaceholders() {
+		// nothing should happen when this is executed if validation succeeds
+		URISignatureHandler.validate(validURI_NoPlaceholders);
+	}
+
+	@Test
+	public void validate_validURI_PlaceholdersInMainPartOfURIOnly() {
+		// nothing should happen when this is executed if validation succeeds
+		URISignatureHandler.validate(validURI_PlaceholdersInMainPartOfURIOnly);
+	}
+
+	@Test
+	public void validate_validURI_PlaceholdersInQueryStringOnly() {
+		// nothing should happen when this is executed if validation succeeds
+		URISignatureHandler.validate(validURI_PlaceholdersInQueryStringOnly);
+	}
+
+	@Test
+	public void validate_validURI_MixedPlaceholders() {
+		// nothing should happen when this is executed if validation succeeds
+		URISignatureHandler.validate(validURI_3MixedPlaceholders);
+	}
+
+ 	@Test
+ 	public void validate_validURI_validURI_Multiple() {
+ 		URISignatureHandler.validate(validURI_MultipleQueryString);
+ 	}
+	
+	// failure cases
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void validate_badURI_nullURI() {
+		URISignatureHandler.validate(badURI_nullURI);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void validate_badURI_emptyURI() {
+		URISignatureHandler.validate(badURI_emptyURI);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void validate_badURI_SingleOpeningSymbolNoClosingSymbol() {
+		URISignatureHandler.validate(badURI_SingleOpeningSymbolNoClosingSymbol);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void validate_badURI_SingleClosingSymbolNoOpeningSymbol() {
+		URISignatureHandler.validate(badURI_SingleClosingSymbolNoOpeningSymbol);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void validate_badURI_DoubleOpeningSymbolsNoClosingSymbol() {
+		URISignatureHandler
+				.validate(badURI_DoubleOpeningSymbolsNoClosingSymbol);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void validate_badURI_DoubleOpeningSymbols() {
+		URISignatureHandler.validate(badURI_DoubleOpeningSymbols);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void validate_badURI_DoubleOpeningSymbolsSpaced() {
+		URISignatureHandler.validate(badURI_DoubleOpeningSymbolsSpaced);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void validate_badURI_DoubleClosingSymbols() {
+		URISignatureHandler.validate(badURI_DoubleClosingSymbols);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void validate_badURI_DoubleClosingSymbolsSpaced() {
+		URISignatureHandler.validate(badURI_DoubleClosingSymbolsSpaced);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void validate_badURI_NestedPlaceholders() {
+		URISignatureHandler.validate(badURI_NestedPlaceholders);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void validate_badURI_NestedPlaceholdersSpaced() {
+		URISignatureHandler.validate(badURI_NestedPlaceholdersSpaced);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void validate_badURI_DuplicatePlaceholders() {
+		URISignatureHandler.validate(badURI_DuplicatePlaceholders);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void validate_badURI_DuplicatePlaceholdersWithOthers() {
+		URISignatureHandler.validate(badURI_DuplicatePlaceholdersWithOthers);
+	}
+
+	// ==========================================================================
+	// TEST PLACEHOLDER EXTRACTION FROM URI SIGNATURE
+	// ==========================================================================
+
+	// success cases
+
+	@Test
+	public void extractPlaceholders_validURI_NoPlaceholders() {
+		List<String> placeholders = URISignatureHandler
+				.extractPlaceholders(validURI_NoPlaceholders);
+		assertNotNull(placeholders);
+		assertEquals(0, placeholders.size());
+	}
+
+	@Test
+	public void extractPlaceholders_validURI_PlaceholdersInMainPartOfURIOnly() {
+		List<String> placeholders = URISignatureHandler
+				.extractPlaceholders(validURI_PlaceholdersInMainPartOfURIOnly);
+		assertNotNull(placeholders);
+		assertEquals(2, placeholders.size());
+		assertEquals("Wrong first placeholder", "sop_id", placeholders.get(0));
+		assertEquals("Wrong second placeholder", "cond_id", placeholders.get(1));
+	}
+
+	@Test
+	public void extractPlaceholders_validURI_PlaceholdersInQueryStringOnly() {
+		List<String> placeholders = URISignatureHandler
+				.extractPlaceholders(validURI_PlaceholdersInQueryStringOnly);
+		assertNotNull(placeholders);
+		assertEquals(1, placeholders.size());
+		assertEquals("Wrong first placeholder", "user_id", placeholders.get(0));
+	}
+
+	@Test
+	public void extractPlaceholders_validURI_MixedPlaceholders() {
+		List<String> placeholders = URISignatureHandler
+				.extractPlaceholders(validURI_3MixedPlaceholders);
+		assertNotNull(placeholders);
+		assertEquals("Wrong number of placeholders extracted", 3, placeholders
+				.size());
+		assertEquals("Wrong first placeholder", "sop_id", placeholders.get(0));
+		assertEquals("Wrong second placeholder", "cond_id", placeholders.get(1));
+		assertEquals("Wrong third placeholder", "unit", placeholders.get(2));
+	}
+
+	@Test
+ 	public void extractPlaceholders_validURI_MultipleQueryString() {
+ 		List<String> placeholders = URISignatureHandler
+ 				.extractPlaceholders(validURI_MultipleQueryString);
+ 		assertNotNull(placeholders);
+ 		assertEquals(5, placeholders.size());
+ 		assertEquals("Wrong first placeholder", "catalogue", placeholders.get(0));
+ 		assertEquals("Wrong second placeholder", "count?", placeholders.get(1));
+ 		assertEquals("Wrong third placeholder", "startPage?", placeholders.get(2));
+ 		assertEquals("Wrong fourth placeholder", "startIndex?", placeholders.get(3));
+ 		assertEquals("Wrong fifth placeholder", "searchTerms?", placeholders.get(4));
+ 	}
+	
+	// failure cases
+
+	/*
+	 * These tests are all meant to generate an exception - therefore, no need
+	 * to evaluate generated values, as there will be none returned.
+	 */
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void extractPlaceholders_badURI_nullURI() {
+		URISignatureHandler.extractPlaceholders(badURI_nullURI);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void extractPlaceholders_badURI_emptyURI() {
+		URISignatureHandler.extractPlaceholders(badURI_emptyURI);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void extractPlaceholders_badURI_SingleOpeningSymbolNoClosingSymbol() {
+		URISignatureHandler
+				.extractPlaceholders(badURI_SingleOpeningSymbolNoClosingSymbol);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void extractPlaceholders_badURI_SingleClosingSymbolNoOpeningSymbol() {
+		URISignatureHandler
+				.extractPlaceholders(badURI_SingleClosingSymbolNoOpeningSymbol);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void extractPlaceholders_badURI_DoubleOpeningSymbolsNoClosingSymbol() {
+		URISignatureHandler
+				.extractPlaceholders(badURI_DoubleOpeningSymbolsNoClosingSymbol);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void extractPlaceholders_badURI_DoubleOpeningSymbols() {
+		URISignatureHandler.extractPlaceholders(badURI_DoubleOpeningSymbols);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void extractPlaceholders_badURI_DoubleOpeningSymbolsSpaced() {
+		URISignatureHandler
+				.extractPlaceholders(badURI_DoubleOpeningSymbolsSpaced);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void extractPlaceholders_badURI_DoubleClosingSymbols() {
+		URISignatureHandler.extractPlaceholders(badURI_DoubleClosingSymbols);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void extractPlaceholders_badURI_DoubleClosingSymbolsSpaced() {
+		URISignatureHandler
+				.extractPlaceholders(badURI_DoubleClosingSymbolsSpaced);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void extractPlaceholders_badURI_NestedPlaceholders() {
+		URISignatureHandler.extractPlaceholders(badURI_NestedPlaceholders);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void extractPlaceholders_badURI_NestedPlaceholdersSpaced() {
+		URISignatureHandler
+				.extractPlaceholders(badURI_NestedPlaceholdersSpaced);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void extractPlaceholders_badURI_DuplicatePlaceholders() {
+		URISignatureHandler.extractPlaceholders(badURI_DuplicatePlaceholders);
+	}
+
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void extractPlaceholders_badURI_DuplicatePlaceholdersWithOthers() {
+		URISignatureHandler
+				.extractPlaceholders(badURI_DuplicatePlaceholdersWithOthers);
+	}
+
+	// ==========================================================================
+	// TEST COMPLETE URI GENERATION FROM URI SIGNATURE + PARAMETERS
+	// ==========================================================================
+
+	// success cases
+
+	@SuppressWarnings("serial")
+	@Test
+	public void generateCompleteURI_successfulURIGeneration() {
+		String uriSignature = "http://sysmo-db.org/sops/{sop_id}/experimental_conditions/{cond_id}?condition_unit={unit}";
+		Map<String, String> parameters = new HashMap<String, String>() {
+			{
+				put("sop_id", "111");
+				put("unit", "33");
+				put("cond_id", "2222");
+			}
+		};
+
+		String completeURI = URISignatureHandler.generateCompleteURI(
+				uriSignature, parameters, true);
+		assertEquals(
+				"http://sysmo-db.org/sops/111/experimental_conditions/2222?condition_unit=33",
+				completeURI);
+	}
+
+	@SuppressWarnings("serial")
+	@Test
+	public void generateCompleteURI_successfulURIGeneration_URLParameterEscaping() {
+		String uriSignature = "http://sysmo-db.org/sops/{sop_id}/experimental_conditions/{cond_id}?condition_unit={unit}";
+		Map<String, String> parameters = new HashMap<String, String>() {
+			{
+				put("sop_id", "1 11");
+				put("unit", "3;3");
+				put("cond_id", "2/2$2&2:");
+			}
+		};
+
+		String completeURI = URISignatureHandler.generateCompleteURI(
+				uriSignature, parameters, true);
+		System.err.println(completeURI);
+		assertEquals(
+				"http://sysmo-db.org/sops/1%2011/experimental_conditions/2%2F2%242%262%3A?condition_unit=3%3B3",
+				completeURI);
+	}
+
+	@SuppressWarnings("serial")
+	@Test
+	public void generateCompleteURI_successfulURIGeneration_noURLParameterEscaping() {
+		String uriSignature = "http://sysmo-db.org/sops/{sop_id}/experimental_conditions/{cond_id}?condition_unit={unit}";
+		Map<String, String> parameters = new HashMap<String, String>() {
+			{
+				put("sop_id", "1 11");
+				put("unit", "3;3");
+				put("cond_id", "2/2$2&2:");
+			}
+		};
+
+		String completeURI = URISignatureHandler.generateCompleteURI(
+				uriSignature, parameters, false);
+		assertEquals(
+				"http://sysmo-db.org/sops/1 11/experimental_conditions/2/2$2&2:?condition_unit=3;3",
+				completeURI);
+	}
+	
+ 	@SuppressWarnings("serial")
+ 	@Test
+ 	public void generateCompleteURI_successfulURIGeneration_optionalParams() {
+ 		String uriSignature = "http://dr-site.esrin.esa.int/{catalogue}/genesi/ASA_IMS_1P/rdf/?count={count?}&startPage={startPage?}&startIndex={startIndex?}&q={searchTerms?}";
+ 		Map<String, String> allParameters = new HashMap<String, String>() {
+ 			{
+ 				put("catalogue", "catalogue");
+ 				put("count?", "10");
+ 				put("startPage?", "1");
+ 				put("startIndex?", "1");
+ 				put("searchTerms?", "term1");
+ 			}
+ 		};
+ 
+ 		Map<String, String> parametersMissingOptional = new HashMap<String, String>() {
+ 			{
+ 				put("catalogue", "catalogue");
+ 				put("count?", "10");
+ 				put("searchTerms?", "term1");
+ 			}
+ 		};
+ 
+ 		Map<String, String> parametersMissingFirstOptional = new HashMap<String, String>() {
+ 			{
+ 				put("catalogue", "catalogue");
+ 				put("startPage?", "1");
+ 				put("startIndex?", "1");
+ 				put("searchTerms?", "term1");
+ 			}
+ 		};
+ 
+ 		String completeURI1 = URISignatureHandler.generateCompleteURI(
+ 				uriSignature, allParameters, false);
+ 		assertEquals(
+				"http://dr-site.esrin.esa.int/catalogue/genesi/ASA_IMS_1P/rdf/?count=10&startPage=1&startIndex=1&q=term1",
+ 				completeURI1);
+ 
+ 		String completeURI2 = URISignatureHandler.generateCompleteURI(
+ 				uriSignature, parametersMissingOptional, false);
+ 		assertEquals(
+ 				"http://dr-site.esrin.esa.int/catalogue/genesi/ASA_IMS_1P/rdf/?count=10&q=term1",
+ 				completeURI2);
+ 
+ 		String completeURI3 = URISignatureHandler.generateCompleteURI(
+ 				uriSignature, parametersMissingFirstOptional, false);
+ 		assertEquals(
+ 				"http://dr-site.esrin.esa.int/catalogue/genesi/ASA_IMS_1P/rdf/?startPage=1&startIndex=1&q=term1",
+ 				completeURI3);
+ 	}
+ 
+	@Test
+	public void generateCompleteURI_signatureWithNoPlaceholders_nullParameterMap() {
+		String completeURI = URISignatureHandler.generateCompleteURI(
+				validURI_NoPlaceholders, null, true);
+		assertEquals(validURI_NoPlaceholders, completeURI);
+	}
+
+	@Test
+	public void generateCompleteURI_signatureWithNoPlaceholders_emptyParameterMap() {
+		String completeURI = URISignatureHandler.generateCompleteURI(
+				validURI_NoPlaceholders, Collections
+						.<String, String> emptyMap(), true);
+		assertEquals(validURI_NoPlaceholders, completeURI);
+	}
+
+	// failure cases
+
+	@Test(expected = URISignatureHandler.URIGenerationFromSignatureException.class)
+	public void generateCompleteURI_signatureWithPlaceholders_nullParameterMap() {
+		URISignatureHandler.generateCompleteURI(validURI_3MixedPlaceholders,
+				null, true);
+	}
+
+	@Test(expected = URISignatureHandler.URIGenerationFromSignatureException.class)
+	public void generateCompleteURI_signatureWithPlaceholders_emptyParameterMap() {
+		URISignatureHandler.generateCompleteURI(validURI_3MixedPlaceholders,
+				Collections.<String, String> emptyMap(), true);
+	}
+
+	@SuppressWarnings("serial")
+	@Test
+	public void generateCompleteURI_signatureWithPlaceholders_missingParameterURIGeneration_FailureNotExpected() {
+		String uriSignature = "http://sysmo-db.org/sops/{sop_id}/experimental_conditions/{cond_id}?condition_unit={unit}";
+		Map<String, String> parameters = new HashMap<String, String>() {
+			{
+				put("sop_id", "111");
+				put("cond_id", "2222");
+			}
+		};
+
+		String completeURI = URISignatureHandler.generateCompleteURI(
+				uriSignature, parameters, true);
+
+		assertEquals(
+				"http://sysmo-db.org/sops/111/experimental_conditions/2222",
+				completeURI);
+	}
+
+	// the following failure case is just to confirm the validation of the URI
+	// signature by the validation mechanism, which is the same for
+	// generateCompleteURI()
+	// and extractPlaceholders()
+
+	@SuppressWarnings("serial")
+	@Test(expected = URISignatureHandler.URISignatureParsingException.class)
+	public void generateCompleteURI_duplicatePlaceholderURIGeneration_FailureExpected() {
+		String uriSignature = "http://sysmo-db.org/sops/{sop_id}/experimental_conditions/{cond_id}?condition_unit={sop_id}";
+		Map<String, String> parameters = new HashMap<String, String>() {
+			{
+				put("sop_id", "111");
+				put("unit", "33");
+				put("cond_id", "2222");
+			}
+		};
+
+		String completeURI = URISignatureHandler.generateCompleteURI(
+				uriSignature, parameters, true);
+
+		assertEquals(
+				"http://sysmo-db.org/sops/111/experimental_conditions/2222?condition_unit=33",
+				completeURI);
+	}
+
+	@SuppressWarnings("serial")
+ 	@Test(expected = URISignatureHandler.URIGenerationFromSignatureException.class)
+ 	public void generateCompleteURI_failureURIGeneration_optionalParams() {
+ 		String uriSignature = "http://dr-site.esrin.esa.int/{catalogue}/genesi/ASA_IMS_1P/rdf/?count={count?}&startPage={startPage?}&startIndex={startIndex?}&q={searchTerms?}";
+ 
+ 		Map<String, String> parametersMissingCompulsory = new HashMap<String, String>() {
+ 			{
+ 				put("count?", "10");
+ 				put("startPage?", "1");
+ 				put("startIndex?", "1");
+ 				put("searchTerms?", "term1");
+ 			}
+ 		};
+ 
+ 		String completeURI = URISignatureHandler.generateCompleteURI(
+ 				uriSignature, parametersMissingCompulsory, false);
+ 
+ 		assertEquals(
+ 				"http://dr-site.esrin.esa.int/catalogue/genesi/ASA_IMS_1P/rdf/?count={count?}&startPage={startPage?}&startIndex={startIndex?}&q={searchTerms?}",
+ 				completeURI);
+ 	}
+}