FELIX-1655: enhance the webconsole plugin + unit tests, patch provided by David Bosschaert with many thx

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@825982 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/karaf/webconsole/admin/pom.xml b/karaf/webconsole/admin/pom.xml
index b8314dd..e2583d1 100644
--- a/karaf/webconsole/admin/pom.xml
+++ b/karaf/webconsole/admin/pom.xml
@@ -76,6 +76,14 @@
             <scope>compile</scope>
             <optional>true</optional>
         </dependency>
+        
+        <!-- Only needed while running the unit tests -->
+        <dependency>
+            <groupId>commons-fileupload</groupId>
+            <artifactId>commons-fileupload</artifactId>
+            <version>${commons.logging.version}</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
   
     <build>
diff --git a/karaf/webconsole/admin/src/main/java/org/apache/felix/karaf/webconsole/admin/AdminPlugin.java b/karaf/webconsole/admin/src/main/java/org/apache/felix/karaf/webconsole/admin/AdminPlugin.java
index cf2ddf1..33af430 100644
--- a/karaf/webconsole/admin/src/main/java/org/apache/felix/karaf/webconsole/admin/AdminPlugin.java
+++ b/karaf/webconsole/admin/src/main/java/org/apache/felix/karaf/webconsole/admin/AdminPlugin.java
@@ -20,7 +20,8 @@
 import java.io.InputStream;
 import java.io.PrintWriter;
 import java.net.URL;
-import java.util.Collections;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -112,7 +113,10 @@
         } else if ("create".equals(action)) {
             int port = parsePortNumber(req.getParameter("port"));
             String location = parseString(req.getParameter("location"));
-            success = createInstance(name, port, location);
+            List<String> featureURLs = parseStringList(req.getParameter("featureURLs"));
+            List<String> features = parseStringList(req.getParameter("features"));
+            InstanceSettings settings = new InstanceSettings(port, location, featureURLs, features); 
+            success = createInstance(name, settings);
         } else if ("destroy".equals(action)) {
             success = destroyInstance(name);
         } else if ("start".equals(action)) {
@@ -142,6 +146,20 @@
         }
         return value;
     }
+    
+    private List<String> parseStringList(String value) {
+        List<String> list = new ArrayList<String>();
+        if (value != null) {
+            for (String el : value.split(",")) {
+                String trimmed = el.trim();
+                if (trimmed.length() == 0) {
+                    continue;
+                }
+                list.add(trimmed);
+            }            
+        }
+        return list;
+    }
 
     /*
      * Parse the port number for the String given, returning 0 if the String does not represent an integer 
@@ -273,10 +291,8 @@
         return buffer.toString();
     }
 
-    private boolean createInstance(String name, int port, String location) {
+    private boolean createInstance(String name, InstanceSettings settings) {
         try {
-            InstanceSettings settings = new InstanceSettings(port, location, 
-                    Collections.<String>emptyList(), Collections.<String>emptyList());
             adminService.createInstance(name, settings);
             return true;
         } catch (Exception ex) {
diff --git a/karaf/webconsole/admin/src/main/resources/res/ui/admin.js b/karaf/webconsole/admin/src/main/resources/res/ui/admin.js
index caf62e0..cc48772 100644
--- a/karaf/webconsole/admin/src/main/resources/res/ui/admin.js
+++ b/karaf/webconsole/admin/src/main/resources/res/ui/admin.js
@@ -29,6 +29,11 @@
     "<td>Name: <input id='name' type='text' name='name' style='width:70%' colspan='2'/></td>" +
     "<td>Port: <input id='port' type='text' name='port' style='width:70%' colspan='2'/></td>" +
     "<td>Location: <input id='location' type='text' name='location' style='width:70%' colspan='2'/></td>" +
+    "<td />" +
+    "</tr><tr><td>Features: <input id='features' type='text' name='features' style='width:70%' colspan='2'" + 
+    " title='Specify initial features separated by commas.'/></td>" + 
+    "<td colspan='2'>Feature URLs: <input id='featureURLs' type='text' name='featureURLs' style='width:80%' colspan='2'" + 
+    " title='Specify additional feature URLs separate by commas.'/></td>" +
     "<td class='col_Actions'><input type='button' value='Create' onclick='createInstance()'/></td>" +
     "</tr></tbody></table></div></form><br/>";
     $("#plugin_content").append( txt );
@@ -40,11 +45,15 @@
     var name = document.getElementById( "name" ).value;
     var port = document.getElementById( "port" ).value;
     var location = document.getElementById( "location" ).value;
-    postCreateInstance( name, port, location );
+    var features = document.getElementById( "features" ).value;
+    var featureURLs = document.getElementById( "featureURLs" ).value;
+    postCreateInstance( name, port, location, features, featureURLs );
 }
 
-function postCreateInstance( /* String */ name, /* String */ port, /* String */ location ) {
-    $.post( pluginRoot, {"action": "create", "name": name, "port": port, "location": location}, function( data ) {
+function postCreateInstance( /* String */ name, /* String */ port, /* String */ location, 
+		/* String */ features, /* String */ featureURLs ) {
+    $.post( pluginRoot, {"action": "create", "name": name, "port": port, "location": location, 
+                             "features": features, "featureURLs": featureURLs }, function( data ) {
         renderData( data );
     }, "json" );
 }
diff --git a/karaf/webconsole/admin/src/test/java/org/apache/felix/karaf/webconsole/admin/AdminPluginTest.java b/karaf/webconsole/admin/src/test/java/org/apache/felix/karaf/webconsole/admin/AdminPluginTest.java
new file mode 100644
index 0000000..3690e6b
--- /dev/null
+++ b/karaf/webconsole/admin/src/test/java/org/apache/felix/karaf/webconsole/admin/AdminPluginTest.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.felix.karaf.webconsole.admin;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestCase;
+
+import org.apache.felix.karaf.shell.admin.AdminService;
+import org.apache.felix.karaf.shell.admin.Instance;
+import org.apache.felix.karaf.shell.admin.InstanceSettings;
+import org.easymock.EasyMock;
+import org.easymock.IAnswer;
+
+public class AdminPluginTest extends TestCase {
+    public void testParseStringList() throws Exception {
+        assertEquals(Arrays.asList("a", "b"), testParseStringList(" a ,b"));
+        assertEquals(Collections.emptyList(), testParseStringList(null));
+        assertEquals(Arrays.asList("hello"), testParseStringList("hello"));
+        assertEquals(Arrays.asList("b"), testParseStringList(",b,"));
+    }
+    
+    @SuppressWarnings("unchecked")
+    private List<String> testParseStringList(String s) throws Exception {
+        AdminPlugin ap = new AdminPlugin();
+        Method m = ap.getClass().getDeclaredMethod("parseStringList", new Class [] {String.class});
+        m.setAccessible(true);
+        return (List<String>) m.invoke(ap, s);
+    }
+    
+    public void testDoPostCreate() throws Exception {
+        InstanceSettings is = 
+            new InstanceSettings(1234, null, Collections.singletonList("http://someURL"), Arrays.asList("abc", "def"));
+        AdminService adminService = EasyMock.createMock(AdminService.class);
+        EasyMock.expect(adminService.createInstance("instance1", is)).andReturn(null);
+        EasyMock.expect(adminService.getInstances()).andReturn(new Instance[] {}).anyTimes();
+        EasyMock.replay(adminService);
+        
+        AdminPlugin ap = new AdminPlugin();
+        ap.setAdminService(adminService);
+
+        final Map<String, String> params = new HashMap<String, String>();
+        params.put("action", "create");
+        params.put("name", "instance1");
+        params.put("port", "1234");
+        params.put("featureURLs", "http://someURL");
+        params.put("features", "abc,def");
+        HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
+        EasyMock.expect(req.getParameter((String) EasyMock.anyObject())).andAnswer(new IAnswer<String>() {
+            public String answer() throws Throwable {
+                return params.get(EasyMock.getCurrentArguments()[0]);
+            }
+        }).anyTimes();
+        
+        HttpServletResponse res = EasyMock.createNiceMock(HttpServletResponse.class);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        PrintWriter pw = new PrintWriter(baos);
+        EasyMock.expect(res.getWriter()).andReturn(pw);
+        
+        EasyMock.replay(req);
+        EasyMock.replay(res);
+        ap.doPost(req, res);        
+        EasyMock.verify(adminService);
+        
+        // Check that the operation has succeeded. This will cause some information to be written to 
+        // the outputstream...
+        pw.flush();
+        String s = new String(baos.toByteArray());
+        assertTrue(s.contains("instance"));
+    }
+}