FELIX-566 Enable RESTful URLs for component list and unify display of bundles and components
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@669450 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ComponentConfigurationPrinter.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ComponentConfigurationPrinter.java
index 76f3ef6..365c135 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ComponentConfigurationPrinter.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ComponentConfigurationPrinter.java
@@ -107,7 +107,7 @@
pw.println( " Bundle" + component.getBundle().getSymbolicName() + " (" + component.getBundle().getBundleId()
+ ")" );
- pw.println( " State=" + ComponentRenderAction.toStateString( component.getState() ) );
+ pw.println( " State=" + ComponentsServlet.toStateString( component.getState() ) );
pw.println( " DefaultState=" + ( component.isDefaultEnabled() ? "enabled" : "disabled" ) );
pw.println( " Activation=" + ( component.isImmediate() ? "immediate" : "delayed" ) );
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ComponentRenderAction.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ComponentRenderAction.java
deleted file mode 100644
index 9153ab5..0000000
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ComponentRenderAction.java
+++ /dev/null
@@ -1,484 +0,0 @@
-/*
- * 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.webconsole.internal.compendium;
-
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Dictionary;
-import java.util.Iterator;
-import java.util.TreeMap;
-import java.util.TreeSet;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.felix.scr.Component;
-import org.apache.felix.scr.Reference;
-import org.apache.felix.scr.ScrService;
-import org.apache.felix.webconsole.Action;
-import org.apache.felix.webconsole.Render;
-import org.apache.felix.webconsole.internal.Util;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.osgi.framework.Constants;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.component.ComponentConstants;
-
-
-public class ComponentRenderAction extends AbstractScrPlugin implements Render, Action
-{
-
- public static final String NAME = "components";
-
- public static final String LABEL = "Components";
-
- public static final String COMPONENT_ID = "componentId";
-
- public static final String OPERATION = "op";
-
- public static final String OPERATION_DETAILS = "details";
-
- public static final String OPERATION_ENABLE = "enable";
-
- public static final String OPERATION_DISABLE = "disable";
-
-
- public String getName()
- {
- return NAME;
- }
-
-
- public String getLabel()
- {
- return LABEL;
- }
-
-
- public boolean performAction( HttpServletRequest request, HttpServletResponse response ) throws IOException
- {
-
- ScrService scrService = getScrService();
- if ( scrService != null )
- {
-
- long componentId = getComponentId( request );
- Component component = scrService.getComponent( componentId );
-
- if ( component != null )
- {
- String op = request.getParameter( OPERATION );
- if ( OPERATION_DETAILS.equals( op ) )
- {
- return sendAjaxDetails( component, response );
- }
- else if ( OPERATION_ENABLE.equals( op ) )
- {
- component.enable();
- }
- else if ( OPERATION_DISABLE.equals( op ) )
- {
- component.disable();
- }
- }
-
- }
-
- return true;
- }
-
-
- public void render( HttpServletRequest request, HttpServletResponse response ) throws IOException
- {
-
- PrintWriter pw = response.getWriter();
-
- this.header( pw );
-
- pw.println( "<tr class='content'>" );
- pw.println( "<td colspan='5' class='content'> </th>" );
- pw.println( "</tr>" );
-
- this.tableHeader( pw );
-
- ScrService scrService = getScrService();
- if ( scrService == null )
- {
- pw.println( "<tr class='content'>" );
- pw
- .println( "<td class='content' colspan='5'>Apache Felix Declarative Service required for this function</td>" );
- pw.println( "</tr>" );
- }
- else
- {
- Component[] components = scrService.getComponents();
- if ( components == null || components.length == 0 )
- {
- pw.println( "<tr class='content'>" );
- pw.println( "<td class='content' colspan='5'>No " + this.getLabel() + " installed currently</td>" );
- pw.println( "</tr>" );
-
- }
- else
- {
-
- // order components by id
- TreeMap componentMap = new TreeMap();
- for ( int i = 0; i < components.length; i++ )
- {
- Component component = components[i];
- componentMap.put( component.getName(), component );
- }
-
- // render components
- long previousComponent = -1;
- for ( Iterator ci = componentMap.values().iterator(); ci.hasNext(); )
- {
- Component component = ( Component ) ci.next();
- if ( previousComponent >= 0 )
- {
- // prepare for injected table information row
- pw.println( "<tr id='component" + previousComponent + "'></tr>" );
- }
-
- component( pw, component );
-
- previousComponent = component.getId();
- }
-
- if ( previousComponent >= 0 )
- {
- // prepare for injected table information row
- pw.println( "<tr id='component" + previousComponent + "'></tr>" );
- }
- }
- }
-
- pw.println( "<tr class='content'>" );
- pw.println( "<td colspan='5' class='content'> </th>" );
- pw.println( "</tr>" );
-
- this.footer( pw );
- }
-
-
- private void header( PrintWriter pw )
- {
- Util.startScript( pw );
- pw.println( "function showDetails(componentId) {" );
- pw.println( " var span = document.getElementById('component' + componentId);" );
- pw.println( " if (!span) {" );
- pw.println( " return;" );
- pw.println( " }" );
- pw.println( " if (span.innerHTML) {" );
- pw.println( " span.innerHTML = '';" );
- pw.println( " return;" );
- pw.println( " }" );
- pw.println( " var parm = '?" + Util.PARAM_ACTION + "=" + NAME + "&" + OPERATION + "=" + OPERATION_DETAILS
- + "&" + COMPONENT_ID + "=' + componentId;" );
- pw.println( " sendRequest('GET', parm, displayComponentDetails);" );
- pw.println( "}" );
- pw.println( "function displayComponentDetails(obj) {" );
- pw.println( " var span = document.getElementById('component' + obj." + COMPONENT_ID + ");" );
- pw.println( " if (!span) {" );
- pw.println( " return;" );
- pw.println( " }" );
- pw
- .println( " var innerHtml = '<td class=\"content\"> </td><td class=\"content\" colspan=\"4\"><table broder=\"0\">';" );
- pw.println( " var props = obj.props;" );
- pw.println( " for (var i=0; i < props.length; i++) {" );
- pw
- .println( " innerHtml += '<tr><td valign=\"top\" noWrap>' + props[i].key + '</td><td valign=\"top\">' + props[i].value + '</td></tr>';" );
- pw.println( " }" );
- pw.println( " innerHtml += '</table></td>';" );
- pw.println( " span.innerHTML = innerHtml;" );
- pw.println( "}" );
- Util.endScript( pw );
-
- pw.println( "<table class='content' cellpadding='0' cellspacing='0' width='100%'>" );
- }
-
-
- private void tableHeader( PrintWriter pw )
- {
-
- pw.println( "<tr class='content'>" );
- pw.println( "<th class='content'>ID</th>" );
- pw.println( "<th class='content' width='100%'>Name</th>" );
- pw.println( "<th class='content'>Status</th>" );
- pw.println( "<th class='content' colspan='2'>Actions</th>" );
- pw.println( "</tr>" );
- }
-
-
- private void footer( PrintWriter pw )
- {
- pw.println( "</table>" );
- }
-
-
- private void component( PrintWriter pw, Component component )
- {
- String name = component.getName();
-
- pw.println( "<tr>" );
- pw.println( "<td class='content right'>" + component.getId() + "</td>" );
- pw.println( "<td class='content'><a href='javascript:showDetails(" + component.getId() + ")'>" + name
- + "</a></td>" );
- pw.println( "<td class='content center'>" + toStateString( component.getState() ) + "</td>" );
-
- boolean enabled = component.getState() == Component.STATE_DISABLED;
- this.actionForm( pw, enabled, component.getId(), OPERATION_ENABLE, "Enable" );
-
- enabled = component.getState() != Component.STATE_DISABLED && component.getState() != Component.STATE_DESTROYED;
- this.actionForm( pw, enabled, component.getId(), OPERATION_DISABLE, "Disable" );
-
- pw.println( "</tr>" );
- }
-
-
- private void actionForm( PrintWriter pw, boolean enabled, long componentId, String op, String opLabel )
- {
- pw.println( "<form name='form" + componentId + "' method='post'>" );
- pw.println( "<td class='content' align='right'>" );
- pw.println( "<input type='hidden' name='" + Util.PARAM_ACTION + "' value='" + NAME + "' />" );
- pw.println( "<input type='hidden' name='" + OPERATION + "' value='" + op + "' />" );
- pw.println( "<input type='hidden' name='" + COMPONENT_ID + "' value='" + componentId + "' />" );
- pw.println( "<input class='submit' type='submit' value='" + opLabel + "'" + ( enabled ? "" : "disabled" )
- + " />" );
- pw.println( "</td>" );
- pw.println( "</form>" );
- }
-
-
- private boolean sendAjaxDetails( Component component, HttpServletResponse response ) throws IOException
- {
- JSONObject result = null;
- try
- {
- if ( component != null )
- {
-
- JSONArray props = new JSONArray();
- keyVal( props, "Bundle", component.getBundle().getSymbolicName() + " ("
- + component.getBundle().getBundleId() + ")" );
- keyVal( props, "Default State", component.isDefaultEnabled() ? "enabled" : "disabled" );
- keyVal( props, "Activation", component.isImmediate() ? "immediate" : "delayed" );
-
- listServices( props, component );
- listReferences( props, component );
- listProperties( props, component );
-
- result = new JSONObject();
- result.put( ComponentRenderAction.COMPONENT_ID, component.getId() );
- result.put( "props", props );
- }
- }
- catch ( Exception exception )
- {
- // create an empty result on problems
- result = new JSONObject();
- }
-
- // send the result
- response.setContentType( "text/javascript" );
- response.getWriter().print( result.toString() );
-
- return false;
- }
-
-
- private void listServices( JSONArray props, Component component )
- {
- String[] services = component.getServices();
- if ( services == null )
- {
- return;
- }
-
- keyVal( props, "Service Type", component.isServiceFactory() ? "service factory" : "service" );
-
- StringBuffer buf = new StringBuffer();
- for ( int i = 0; i < services.length; i++ )
- {
- if ( i > 0 )
- {
- buf.append( "<br />" );
- }
- buf.append( services[i] );
- }
-
- keyVal( props, "Services", buf.toString() );
- }
-
-
- private void listReferences( JSONArray props, Component component )
- {
- Reference[] refs = component.getReferences();
- if ( refs != null )
- {
- for ( int i = 0; i < refs.length; i++ )
- {
- StringBuffer buf = new StringBuffer();
- buf.append( refs[i].isSatisfied() ? "Satisfied" : "Unsatisfied" ).append( "<br />" );
- buf.append( "Service Name: " ).append( refs[i].getServiceName() ).append( "<br />" );
- if ( refs[i].getTarget() != null )
- {
- buf.append( "Target Filter: " ).append( refs[i].getTarget() ).append( "<br />" );
- }
- buf.append( "Multiple: " ).append( refs[i].isMultiple() ? "multiple" : "single" ).append( "<br />" );
- buf.append( "Optional: " ).append( refs[i].isOptional() ? "optional" : "mandatory" ).append( "<br />" );
- buf.append( "Policy: " ).append( refs[i].isStatic() ? "static" : "dynamic" ).append( "<br />" );
-
- // list bound services
- ServiceReference[] boundRefs = refs[i].getServiceReferences();
- if ( boundRefs != null && boundRefs.length > 0 )
- {
- for ( int j = 0; j < boundRefs.length; j++ )
- {
- buf.append( "Bound Service ID " );
- buf.append( boundRefs[j].getProperty( Constants.SERVICE_ID ) );
-
- String name = ( String ) boundRefs[j].getProperty( ComponentConstants.COMPONENT_NAME );
- if ( name == null )
- {
- name = ( String ) boundRefs[j].getProperty( Constants.SERVICE_PID );
- if ( name == null )
- {
- name = ( String ) boundRefs[j].getProperty( Constants.SERVICE_DESCRIPTION );
- }
- }
- if ( name != null )
- {
- buf.append( " (" );
- buf.append( name );
- buf.append( ")" );
- }
- }
- }
- else
- {
- buf.append( "No Services bound" );
- }
- buf.append( "<br />" );
-
- keyVal( props, "Reference " + refs[i].getName(), buf.toString() );
- }
- }
- }
-
-
- private void listProperties( JSONArray jsonProps, Component component )
- {
- Dictionary props = component.getProperties();
- if ( props != null )
- {
- StringBuffer buf = new StringBuffer();
- TreeSet keys = new TreeSet( Collections.list( props.keys() ) );
- for ( Iterator ki = keys.iterator(); ki.hasNext(); )
- {
- String key = ( String ) ki.next();
- buf.append( key ).append( " = " );
-
- Object prop = props.get( key );
- if ( prop.getClass().isArray() )
- {
- prop = Arrays.asList( ( Object[] ) prop );
- }
- buf.append( prop );
- if ( ki.hasNext() )
- {
- buf.append( "<br />" );
- }
- }
- keyVal( jsonProps, "Properties", buf.toString() );
- }
-
- }
-
-
- private void keyVal( JSONArray props, String key, Object value )
- {
- if ( key != null && value != null )
- {
- try
- {
- JSONObject obj = new JSONObject();
- obj.put( "key", key );
- obj.put( "value", value );
- props.put( obj );
- }
- catch ( JSONException je )
- {
- // don't care
- }
- }
- }
-
-
- static String toStateString( int state )
- {
- switch ( state )
- {
- case Component.STATE_DISABLED:
- return "disabled";
- case Component.STATE_ENABLED:
- return "enabled";
- case Component.STATE_UNSATISFIED:
- return "unsatisifed";
- case Component.STATE_ACTIVATING:
- return "activating";
- case Component.STATE_ACTIVE:
- return "active";
- case Component.STATE_REGISTERED:
- return "registered";
- case Component.STATE_FACTORY:
- return "factory";
- case Component.STATE_DEACTIVATING:
- return "deactivating";
- case Component.STATE_DESTROYED:
- return "destroyed";
- default:
- return String.valueOf( state );
- }
- }
-
-
- protected long getComponentId( HttpServletRequest request )
- {
- String componentIdPar = request.getParameter( ComponentRenderAction.COMPONENT_ID );
- if ( componentIdPar != null )
- {
- try
- {
- return Long.parseLong( componentIdPar );
- }
- catch ( NumberFormatException nfe )
- {
- // TODO: log
- }
- }
-
- // no bundleId or wrong format
- return -1;
- }
-
-}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ComponentsServlet.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ComponentsServlet.java
new file mode 100644
index 0000000..e9ec667
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ComponentsServlet.java
@@ -0,0 +1,475 @@
+/*
+ * 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.webconsole.internal.compendium;
+
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Iterator;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.felix.scr.Component;
+import org.apache.felix.scr.Reference;
+import org.apache.felix.scr.ScrService;
+import org.apache.felix.webconsole.internal.BaseWebConsolePlugin;
+import org.apache.felix.webconsole.internal.Util;
+import org.apache.felix.webconsole.internal.servlet.OsgiManager;
+import org.json.JSONException;
+import org.json.JSONWriter;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.component.ComponentConstants;
+
+
+public class ComponentsServlet extends BaseWebConsolePlugin
+{
+
+ public static final String NAME = "components";
+
+ public static final String LABEL = "Components";
+
+ public static final String COMPONENT_ID = "componentId";
+
+ public static final String OPERATION = "action";
+
+ public static final String OPERATION_ENABLE = "enable";
+
+ public static final String OPERATION_DISABLE = "disable";
+
+ private static final String SCR_SERVICE = ScrService.class.getName();
+
+
+ public String getTitle()
+ {
+ return LABEL;
+ }
+
+
+ public String getLabel()
+ {
+ return NAME;
+ }
+
+
+ protected void doPost( HttpServletRequest request, HttpServletResponse response ) throws IOException
+ {
+ ScrService scrService = getScrService();
+ if ( scrService != null )
+ {
+
+ long componentId = getComponentId( request );
+ Component component = scrService.getComponent( componentId );
+
+ if ( component != null )
+ {
+ String op = request.getParameter( OPERATION );
+ if ( OPERATION_ENABLE.equals( op ) )
+ {
+ component.enable();
+ }
+ else if ( OPERATION_DISABLE.equals( op ) )
+ {
+ component.disable();
+ }
+
+ sendAjaxDetails( component, response );
+ }
+
+ }
+ }
+
+
+ protected void renderContent( HttpServletRequest request, HttpServletResponse response ) throws IOException
+ {
+ PrintWriter pw = response.getWriter();
+
+ String appRoot = ( String ) request.getAttribute( OsgiManager.ATTR_APP_ROOT );
+ pw.println( "<script src='" + appRoot + "/res/ui/datatable.js' language='JavaScript'></script>" );
+
+ Util.startScript( pw );
+
+ pw.print( "var components = " );
+ renderResult( request, pw );
+ pw.println( ";" );
+
+ pw.println( "renderDataTable( components );" );
+
+ Util.endScript( pw );
+ }
+
+
+ private void renderResult( HttpServletRequest request, PrintWriter pw ) throws IOException
+ {
+ JSONWriter jw = new JSONWriter( pw );
+ try
+ {
+ jw.object();
+
+ jw.key( "numActions" );
+ jw.value( 2 );
+
+ ScrService scrService = getScrService();
+ if ( scrService == null )
+ {
+ jw.key( "error" );
+ jw.value( "Apache Felix Declarative Service required for this function" );
+ }
+ else
+ {
+ Component[] components = null;
+ boolean details = false;
+
+ long componentId = getComponentId( request );
+ if ( componentId >= 0 )
+ {
+ Component component = scrService.getComponent( componentId );
+ if ( component != null )
+ {
+ components = new Component[]
+ { component };
+ details = true;
+ }
+ }
+
+ if ( components == null )
+ {
+ components = scrService.getComponents();
+ }
+
+ if ( components == null || components.length == 0 )
+ {
+ jw.key( "error" );
+ jw.value( "No components installed currently" );
+ }
+ else
+ {
+ // order components by name
+ TreeMap componentMap = new TreeMap();
+ for ( int i = 0; i < components.length; i++ )
+ {
+ Component component = components[i];
+ componentMap.put( component.getName(), component );
+ }
+
+ // render components
+ jw.key( "data" );
+ jw.array();
+ for ( Iterator ci = componentMap.values().iterator(); ci.hasNext(); )
+ {
+ component( jw, ( Component ) ci.next(), details );
+ }
+ jw.endArray();
+ }
+ }
+
+ jw.endObject();
+ }
+ catch ( JSONException je )
+ {
+ throw new IOException( je.toString() );
+ }
+ }
+
+
+ private void sendAjaxDetails( Component component, HttpServletResponse response ) throws IOException
+ {
+
+ // send the result
+ response.setContentType( "text/javascript" );
+
+ JSONWriter jw = new JSONWriter( response.getWriter() );
+ try
+ {
+ if ( component != null )
+ {
+ component( jw, component, true );
+ }
+ }
+ catch ( JSONException je )
+ {
+ throw new IOException( je.toString() );
+ }
+ }
+
+
+ private void component( JSONWriter jw, Component component, boolean details ) throws JSONException
+ {
+ String id = String.valueOf( component.getId() );
+ String name = component.getName();
+ int state = component.getState();
+
+ jw.object();
+
+ // component information
+ jw.key( "id" );
+ jw.value( id );
+ jw.key( "name" );
+ jw.value( name );
+ jw.key( "state" );
+ jw.value( toStateString( state ) );
+
+ // component actions
+ jw.key( "actions" );
+ jw.array();
+
+ jw.object();
+ jw.key( "name" );
+ jw.value( "Enable" );
+ jw.key( "link" );
+ jw.value( OPERATION_ENABLE );
+ jw.key( "enabled" );
+ jw.value( state == Component.STATE_DISABLED );
+ jw.endObject();
+
+ jw.object();
+ jw.key( "name" );
+ jw.value( "Disable" );
+ jw.key( "link" );
+ jw.value( OPERATION_DISABLE );
+ jw.key( "enabled" );
+ jw.value( state != Component.STATE_DISABLED && state != Component.STATE_DESTROYED );
+ jw.endObject();
+
+ jw.endArray();
+
+ // component details
+ if ( details )
+ {
+ gatherComponentDetails( jw, component );
+ }
+
+ jw.endObject();
+ }
+
+
+ private void gatherComponentDetails( JSONWriter jw, Component component ) throws JSONException
+ {
+ jw.key( "props" );
+ jw.array();
+
+ keyVal( jw, "Bundle", component.getBundle().getSymbolicName() + " (" + component.getBundle().getBundleId()
+ + ")" );
+ keyVal( jw, "Default State", component.isDefaultEnabled() ? "enabled" : "disabled" );
+ keyVal( jw, "Activation", component.isImmediate() ? "immediate" : "delayed" );
+
+ listServices( jw, component );
+ listReferences( jw, component );
+ listProperties( jw, component );
+
+ jw.endArray();
+ }
+
+
+ private void listServices( JSONWriter jw, Component component )
+ {
+ String[] services = component.getServices();
+ if ( services == null )
+ {
+ return;
+ }
+
+ keyVal( jw, "Service Type", component.isServiceFactory() ? "service factory" : "service" );
+
+ StringBuffer buf = new StringBuffer();
+ for ( int i = 0; i < services.length; i++ )
+ {
+ if ( i > 0 )
+ {
+ buf.append( "<br />" );
+ }
+ buf.append( services[i] );
+ }
+
+ keyVal( jw, "Services", buf.toString() );
+ }
+
+
+ private void listReferences( JSONWriter jw, Component component )
+ {
+ Reference[] refs = component.getReferences();
+ if ( refs != null )
+ {
+ for ( int i = 0; i < refs.length; i++ )
+ {
+ StringBuffer buf = new StringBuffer();
+ buf.append( refs[i].isSatisfied() ? "Satisfied" : "Unsatisfied" ).append( "<br />" );
+ buf.append( "Service Name: " ).append( refs[i].getServiceName() ).append( "<br />" );
+ if ( refs[i].getTarget() != null )
+ {
+ buf.append( "Target Filter: " ).append( refs[i].getTarget() ).append( "<br />" );
+ }
+ buf.append( "Multiple: " ).append( refs[i].isMultiple() ? "multiple" : "single" ).append( "<br />" );
+ buf.append( "Optional: " ).append( refs[i].isOptional() ? "optional" : "mandatory" ).append( "<br />" );
+ buf.append( "Policy: " ).append( refs[i].isStatic() ? "static" : "dynamic" ).append( "<br />" );
+
+ // list bound services
+ ServiceReference[] boundRefs = refs[i].getServiceReferences();
+ if ( boundRefs != null && boundRefs.length > 0 )
+ {
+ for ( int j = 0; j < boundRefs.length; j++ )
+ {
+ if ( j > 0 )
+ {
+ buf.append( "<br />" );
+ }
+
+ buf.append( "Bound Service ID " );
+ buf.append( boundRefs[j].getProperty( Constants.SERVICE_ID ) );
+
+ String name = ( String ) boundRefs[j].getProperty( ComponentConstants.COMPONENT_NAME );
+ if ( name == null )
+ {
+ name = ( String ) boundRefs[j].getProperty( Constants.SERVICE_PID );
+ if ( name == null )
+ {
+ name = ( String ) boundRefs[j].getProperty( Constants.SERVICE_DESCRIPTION );
+ }
+ }
+ if ( name != null )
+ {
+ buf.append( " (" );
+ buf.append( name );
+ buf.append( ")" );
+ }
+ }
+ }
+ else
+ {
+ buf.append( "No Services bound" );
+ }
+ buf.append( "<br />" );
+
+ keyVal( jw, "Reference " + refs[i].getName(), buf.toString() );
+ }
+ }
+ }
+
+
+ private void listProperties( JSONWriter jw, Component component )
+ {
+ Dictionary props = component.getProperties();
+ if ( props != null )
+ {
+ StringBuffer buf = new StringBuffer();
+ TreeSet keys = new TreeSet( Collections.list( props.keys() ) );
+ for ( Iterator ki = keys.iterator(); ki.hasNext(); )
+ {
+ String key = ( String ) ki.next();
+ buf.append( key ).append( " = " );
+
+ Object prop = props.get( key );
+ if ( prop.getClass().isArray() )
+ {
+ prop = Arrays.asList( ( Object[] ) prop );
+ }
+ buf.append( prop );
+ if ( ki.hasNext() )
+ {
+ buf.append( "<br />" );
+ }
+ }
+
+ keyVal( jw, "Properties", buf.toString() );
+ }
+
+ }
+
+
+ private void keyVal( JSONWriter jw, String key, Object value )
+ {
+ if ( key != null && value != null )
+ {
+ try
+ {
+ jw.object();
+ jw.key( "key" );
+ jw.value( key );
+ jw.key( "value" );
+ jw.value( value );
+ jw.endObject();
+ }
+ catch ( JSONException je )
+ {
+ // don't care
+ }
+ }
+ }
+
+
+ static String toStateString( int state )
+ {
+ switch ( state )
+ {
+ case Component.STATE_DISABLED:
+ return "disabled";
+ case Component.STATE_ENABLED:
+ return "enabled";
+ case Component.STATE_UNSATISFIED:
+ return "unsatisifed";
+ case Component.STATE_ACTIVATING:
+ return "activating";
+ case Component.STATE_ACTIVE:
+ return "active";
+ case Component.STATE_REGISTERED:
+ return "registered";
+ case Component.STATE_FACTORY:
+ return "factory";
+ case Component.STATE_DEACTIVATING:
+ return "deactivating";
+ case Component.STATE_DESTROYED:
+ return "destroyed";
+ default:
+ return String.valueOf( state );
+ }
+ }
+
+
+ protected long getComponentId( HttpServletRequest request )
+ {
+ String componentIdPar = request.getParameter( ComponentsServlet.COMPONENT_ID );
+ if ( componentIdPar == null )
+ {
+ String info = request.getPathInfo();
+ componentIdPar = info.substring( info.lastIndexOf( '/' ) + 1 );
+ }
+
+ try
+ {
+ return Long.parseLong( componentIdPar );
+ }
+ catch ( NumberFormatException nfe )
+ {
+ // TODO: log
+ }
+
+ // no bundleId or wrong format
+ return -1;
+ }
+
+
+ private ScrService getScrService()
+ {
+ return ( ScrService ) getService( SCR_SERVICE );
+ }
+}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
index 7d194ba..4f9d7bb 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
@@ -179,7 +179,11 @@
if ( bundle != null )
{
- if ( "start".equals( action ) )
+ if ( action == null )
+ {
+ success = true;
+ }
+ else if ( "start".equals( action ) )
{
// start bundle
success = true;
@@ -305,7 +309,8 @@
PrintWriter pw = response.getWriter();
- String appRoot = (String) request.getAttribute( OsgiManager.ATTR_APP_ROOT );
+ String appRoot = ( String ) request.getAttribute( OsgiManager.ATTR_APP_ROOT );
+ pw.println( "<script src='" + appRoot + "/res/ui/datatable.js' language='JavaScript'></script>" );
pw.println( "<script src='" + appRoot + "/res/ui/bundles.js' language='JavaScript'></script>" );
Util.startScript( pw );
@@ -314,9 +319,13 @@
try
{
jw.object();
- jw.key( "startlevel" );
+
+ jw.key( "startLevel" );
jw.value( getStartLevel().getInitialBundleStartLevel() );
+ jw.key( "numActions" );
+ jw.value( 4 );
+
Bundle bundle = getBundle( request.getPathInfo() );
Bundle[] bundles = ( bundle != null ) ? new Bundle[]
{ bundle } : this.getBundles();
@@ -326,7 +335,7 @@
{
Util.sort( bundles );
- jw.key( "bundles" );
+ jw.key( "data" );
jw.array();
@@ -338,6 +347,11 @@
jw.endArray();
}
+ else
+ {
+ jw.key( "error" );
+ jw.value( "No Bundles installed currently" );
+ }
jw.endObject();
@@ -348,7 +362,7 @@
}
pw.println( ";" );
- pw.println( "render(bundleListData.startlevel, bundleListData.bundles);" );
+ pw.println( "renderBundle( bundleListData );" );
Util.endScript( pw );
}
@@ -356,20 +370,31 @@
private void bundleInfo( JSONWriter jw, Bundle bundle, boolean details ) throws JSONException
{
jw.object();
- jw.key( "bundleId" );
+ jw.key( "id" );
jw.value( bundle.getBundleId() );
jw.key( "name" );
jw.value( Util.getName( bundle ) );
jw.key( "state" );
jw.value( toStateString( bundle.getState() ) );
- jw.key( "hasStart" );
- jw.value( hasStart( bundle ) );
- jw.key( "hasStop" );
- jw.value( hasStop( bundle ) );
- jw.key( "hasUpdate" );
- jw.value( hasUpdate( bundle ) );
- jw.key( "hasUninstall" );
- jw.value( hasUninstall( bundle ) );
+
+ jw.key( "actions" );
+ jw.array();
+
+ if ( bundle.getBundleId() == 0 )
+ {
+ jw.value( false );
+ jw.value( false );
+ jw.value( false );
+ jw.value( false );
+ }
+ else
+ {
+ action( jw, hasStart( bundle ), "start", "Start" );
+ action( jw, hasStop( bundle ), "stop", "Stop" );
+ action( jw, hasUpdate( bundle ), "update", "Update" );
+ action( jw, hasUninstall( bundle ), "uninstall", "Uninstall" );
+ }
+ jw.endArray();
if ( details )
{
@@ -408,6 +433,19 @@
}
+ private void action( JSONWriter jw, boolean enabled, String op, String opLabel ) throws JSONException
+ {
+ jw.object();
+ jw.key( "enabled" );
+ jw.value( enabled );
+ jw.key( "name" );
+ jw.value( opLabel );
+ jw.key( "link" );
+ jw.value( op );
+ jw.endObject();
+ }
+
+
private boolean hasStart( Bundle bundle )
{
return bundle.getState() == Bundle.INSTALLED || bundle.getState() == Bundle.RESOLVED;
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManager.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManager.java
index b0c28c4..a45fd0b 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManager.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManager.java
@@ -41,7 +41,7 @@
import org.apache.felix.webconsole.internal.OsgiManagerPlugin;
import org.apache.felix.webconsole.internal.Util;
import org.apache.felix.webconsole.internal.compendium.ComponentConfigurationPrinter;
-import org.apache.felix.webconsole.internal.compendium.ComponentRenderAction;
+import org.apache.felix.webconsole.internal.compendium.ComponentsServlet;
import org.apache.felix.webconsole.internal.compendium.ConfigManager;
import org.apache.felix.webconsole.internal.core.BundlesServlet;
import org.apache.felix.webconsole.internal.core.InstallAction;
@@ -125,7 +125,7 @@
private static final String DEFAULT_MANAGER_ROOT = "/system/console";
private static final Class[] PLUGIN_CLASSES =
- { ComponentConfigurationPrinter.class, ComponentRenderAction.class, ConfigManager.class, BundlesServlet.class,
+ { ComponentConfigurationPrinter.class, ComponentsServlet.class, ConfigManager.class, BundlesServlet.class,
InstallAction.class, SetStartLevelAction.class, ConfigurationRender.class, GCAction.class,
ShutdownAction.class, ShutdownRender.class, VMStatRender.class, BundleRepositoryRender.class,
LicenseServlet.class };
diff --git a/webconsole/src/main/resources/res/ui/bundles.js b/webconsole/src/main/resources/res/ui/bundles.js
index 35ff677..e272b51 100644
--- a/webconsole/src/main/resources/res/ui/bundles.js
+++ b/webconsole/src/main/resources/res/ui/bundles.js
@@ -15,111 +15,29 @@
* limitations under the License.
*/
-function render(/* int */ startlevel, /* Array of Bundle Object */ bundles)
+function renderBundle( /* Array of Data Objects */ bundleData )
{
- header();
-
- installForm( startlevel );
+ // number of actions plus 3 -- id, name and state
+ var columns = bundleData.numActions + 3;
+ var startLevel = bundleData.startLevel;
- document.write( "<tr class='content'>" );
- document.write( "<td colspan='7' class='content'> </th>" );
- document.write( "</tr>" );
+ header( columns );
- tableHeader();
-
- if ( !bundles )
+ installForm( startLevel );
+
+ if (bundleData.error)
{
- document.write( "<tr class='content'>" );
- document.write( "<td class='content' colspan='6'>No Bundles installed currently</td>" );
- document.write( "</tr>" );
+ error( columns, bundleData.error );
}
else
{
- for ( var i = 0; i < bundles.length; i++ )
- {
- bundle( bundles[i] );
- }
+ data ( bundleData.data );
}
- document.write( "<tr class='content'>" );
- document.write( "<td colspan='7' class='content'> </th>" );
- document.write( "</tr>" );
+ installForm( startLevel );
- installForm( startlevel );
-
- footer();
-}
-
-
-function header()
-{
- document.write( "<table class='content' cellpadding='0' cellspacing='0' width='100%'>" );
-}
-
-
-function tableHeader()
-{
- document.write( "<tr class='content'>" );
- document.write( "<th class='content'>ID</th>" );
- document.write( "<th class='content' width='100%'>Name</th>" );
- document.write( "<th class='content'>Status</th>" );
- document.write( "<th class='content' colspan='4'>Actions</th>" );
- document.write( "</tr>" );
-}
-
-
-function footer()
-{
- document.write( "</table>" );
-}
-
-
-function bundle( /* Bundle */ bundle )
-{
- document.write( "<tr id='bundle" + bundle.bundleId + "'>" );
- document.write( bundleInternal( bundle ) );
- document.write( "</tr>" );
- document.write( "<tr id='bundle" + bundle.bundleId + "_details'>" );
- if (bundle.props)
- {
- document.write( bundleDetails( bundle.props ) );
- }
- document.write( "</tr>" );
-}
-
-
-/* String */ function bundleInternal( /* Bundle */ bundle )
-{
- var icon = (bundle.props) ? "down" : "right";
- var theBundle = "<td class='content right'>" + bundle.bundleId + "</td>";
- theBundle += "<td class='content'><img src='" + appRoot + "/res/imgs/" + icon + ".gif' onClick='showDetails(" + bundle.bundleId + ")' id='bundle" + bundle.bundleId + "_inline' />";
- theBundle += " <a href='" + appRoot + "/bundles/" + bundle.bundleId + "'>" + bundle.name + "</a></td>";
- theBundle += "<td class='content center'>" + bundle.state + "</td>";
-
- // no buttons for system bundle
- if ( bundle.bundleId == 0 )
- {
- theBundle += "<td class='content' colspan='4'> </td>";
- }
- else
- {
- theBundle += actionForm( bundle.hasStart, bundle.bundleId, "start", "Start" );
- theBundle += actionForm( bundle.hasStop, bundle.bundleId, "stop", "Stop" );
- theBundle += actionForm( bundle.hasUpdate, bundle.bundleId, "update", "Update" );
- theBundle += actionForm( bundle.hasUninstall, bundle.bundleId, "uninstall", "Uninstall" );
- }
-
- return theBundle;
-}
-
-
-/* String */ function actionForm( /* boolean */ enabled, /* long */ bundleId, /* String */ action, /* String */ actionLabel )
-{
- var theButton = "<td class='content' align='right'>";
- theButton += "<input class='submit' type='button' value='" + actionLabel + "'" + ( enabled ? "" : "disabled" ) + " onClick='changeBundle(" + bundleId + ", \"" + action + "\");' />";
- theButton += "</td>";
- return theButton;
+ footer( columns );
}
@@ -142,108 +60,3 @@
document.write( "</tr>" );
document.write( "</form>" );
}
-
-
-function changeBundle(/* long */ bundleId, /* String */ action)
-{
- var parm = "bundles/" + bundleId + "?action=" + action;
- sendRequest('POST', parm, bundleChanged);
-}
-
-
-function bundleChanged(obj)
-{
- if (obj.reload)
- {
- document.location = document.location;
- }
- else
- {
- var bundleId = obj.bundleId;
- if (obj.state)
- {
- // has status, so draw the line
- var span = document.getElementById('bundle' + bundleId);
- if (span)
- {
- span.innerHTML = bundleInternal( obj );
- }
-
- if (obj.props)
- {
- var span = document.getElementById('bundle' + bundleId + '_details');
- if (span && span.innerHTML)
- {
- span.innerHTML = bundleDetails( obj.props );
- }
- }
-
- }
- else
- {
- // no status, bundle has been uninstalled
- var span = document.getElementById('bundle' + bundleId);
- if (span)
- {
- span.parentNode.removeChild(span);
- }
- var span = document.getElementById('bundle' + bundleId + '_details');
- if (span)
- {
- span.parentNode.removeChild(span);
- }
- }
- }
-}
-
-
-function showDetails(bundleId) {
- var span = document.getElementById('bundle' + bundleId + '_details');
- if (span)
- {
- if (span.innerHTML)
- {
- span.innerHTML = '';
- newLinkValue(bundleId, appRoot + "/res/imgs/right.gif");
- }
- else
- {
- sendRequest('GET', appRoot + "/bundles/" + bundleId + ".json", displayBundleDetails);
- newLinkValue(bundleId, appRoot + "/res/imgs/down.gif");
- }
- }
-}
-
-
-function displayBundleDetails(obj) {
- var span = document.getElementById('bundle' + obj.bundleId + '_details');
- if (span)
- {
- span.innerHTML = bundleDetails( obj.props );
- }
-}
-
-
-function newLinkValue(bundleId, newLinkValue)
-{
-
- var link = document.getElementById("bundle" + bundleId + "_inline");
- if (link)
- {
- link.src = newLinkValue;
- }
-}
-
-
-/* String */ function bundleDetails( props )
-{
- var innerHtml = '<td class=\"content\"> </td><td class=\"content\" colspan=\"6\"><table broder=\"0\">';
- for (var i=0; i < props.length; i++)
- {
- innerHtml += '<tr><td valign=\"top\" noWrap>' + props[i].key + '</td><td valign=\"top\">' + props[i].value + '</td></tr>';
- }
- innerHtml += '</table></td>';
-
- return innerHtml;
-}
-
diff --git a/webconsole/src/main/resources/res/ui/datatable.js b/webconsole/src/main/resources/res/ui/datatable.js
new file mode 100644
index 0000000..8ffb4d8
--- /dev/null
+++ b/webconsole/src/main/resources/res/ui/datatable.js
@@ -0,0 +1,258 @@
+/*
+ * 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.
+ */
+
+function renderDataTable( /* Array of Data Objects */ components )
+{
+ // number of actions plus 3 -- id, name and state
+ var columns = components.numActions + 3;
+
+ header( columns );
+
+ if (components.error)
+ {
+ error( columns, components.error );
+ }
+ else
+ {
+ data ( components.data );
+ }
+
+ footer( columns );
+}
+
+
+function header( /* int */ columns )
+{
+ document.write( "<table class='content' cellpadding='0' cellspacing='0' width='100%'>" );
+
+ document.write( "<tr class='content'>" );
+ document.write( "<td colspan='" + columns + "' class='content'> </th>" );
+ document.write( "</tr>" );
+
+ document.write( "<tr class='content'>" );
+ document.write( "<th class='content'>ID</th>" );
+ document.write( "<th class='content' width='100%'>Name</th>" );
+ document.write( "<th class='content'>Status</th>" );
+ document.write( "<th class='content' colspan='" + (columns - 3) + "'>Actions</th>" );
+ document.write( "</tr>" );
+
+}
+
+
+function error( /* int */ columns, /* String */ message )
+{
+ document.write( "<tr class='content'>" );
+ document.write( "<td class='content'> </td>" );
+ document.write( "<td class='content' colspan='" + (columns - 1) + "'>" + message + "</td>" );
+ document.write( "</tr>" );
+}
+
+
+function data( /* Array of Object */ dataArray )
+{
+ // render components
+ for ( var idx in dataArray )
+ {
+ entry( dataArray[idx] );
+ }
+}
+
+
+function footer( /* int */ columns )
+{
+ document.write( "<tr class='content'>" );
+ document.write( "<td colspan='" + columns + "' class='content'> </th>" );
+ document.write( "</tr>" );
+
+ document.write( "</table>" );
+}
+
+
+function entry( /* Object */ dataEntry )
+{
+ document.write( "<tr id='entry" + dataEntry.id + "'>" );
+ document.write( entryInternal( dataEntry ) );
+ document.write( "</tr>" );
+
+ // dataEntry detailed properties
+ document.write( "<tr id='entry" + dataEntry.id + "_details'>" );
+ if (dataEntry.props)
+ {
+ document.write( getDataEntryDetails( dataEntry.props ) );
+ }
+ document.write( "</tr>" );
+}
+
+
+/* String */ function entryInternal( /* Object */ dataEntry )
+{
+ var id = dataEntry.id;
+ var name = dataEntry.name;
+ var state = dataEntry.state;
+ var icon = (dataEntry.props) ? "down" : "right";
+
+ var html = "<td class='content right'>" + id + "</td>";
+ html += "<td class='content'>";
+ html += "<img src='" + appRoot + "/res/imgs/" + icon + ".gif' onClick='showDataEntryDetails(" + id + ")' id='entry" + id + "_inline' />";
+ html += "<a href='" + pluginRoot + "/" + id + "'>" + name + "</a></td>";
+
+ html += "<td class='content center'>" + state + "</td>";
+
+ for ( var aidx in dataEntry.actions )
+ {
+ var action = dataEntry.actions[aidx];
+ html += actionButton( action.enabled, id, action.link, action.name );
+ }
+
+ return html;
+}
+
+
+/* String */ function actionButton( /* boolean */ enabled, /* long */ id, /* String */ op, /* String */ opLabel )
+{
+ var theButton = "<td class='content' align='right'>";
+ if ( op )
+ {
+ theButton += "<input class='submit' type='button' value='" + opLabel + "'" + ( enabled ? "" : "disabled" ) + " onClick='changeDataEntryState(" + id + ", \"" + op + "\");' />";
+ }
+ else
+ {
+ theButton += " ";
+ }
+ theButton += "</td>";
+ return theButton;
+}
+
+
+/* String */ function getDataEntryDetails( /* Array of Object */ details )
+{
+ var innerHtml = '<td class=\"content\"> </td><td class=\"content\" colspan=\"4\"><table broder=\"0\">';
+ for (var idx in details)
+ {
+ var prop = details[idx];
+ innerHtml += '<tr><td valign=\"top\" noWrap>' + prop.key + '</td><td valign=\"top\">' + prop.value + '</td></tr>';
+ }
+ innerHtml += '</table></td>';
+ return innerHtml;
+ }
+
+
+function showDetails(bundleId) {
+ var span = document.getElementById('bundle' + bundleId + '_details');
+}
+
+
+function showDataEntryDetails( id )
+{
+ var span = document.getElementById( 'entry' + id + '_details' );
+ if (span)
+ {
+ if (span.innerHTML)
+ {
+ span.innerHTML = '';
+ newLinkValue( id, appRoot + "/res/imgs/right.gif" );
+ }
+ else
+ {
+ sendRequest( 'POST', pluginRoot + '/' + id, displayDataEntryDetails );
+ newLinkValue( id, appRoot + "/res/imgs/down.gif" );
+ }
+ }
+}
+
+
+function newLinkValue( /* long */ id, /* String */ newLinkValue )
+{
+
+ var link = document.getElementById( "entry" + id + "_inline" );
+ if (link)
+ {
+ link.src = newLinkValue;
+ }
+}
+
+
+function displayDataEntryDetails( obj )
+{
+ var span = document.getElementById('entry' + obj.id + '_details');
+ if (!span)
+ {
+ return;
+ }
+
+ span.innerHTML = getDataEntryDetails( obj.props );
+}
+
+
+function changeDataEntryState(/* long */ id, /* String */ action)
+{
+ var parm = pluginRoot + "/" + id + "?action=" + action;
+ sendRequest('POST', parm, dataEntryStateChanged);
+}
+
+
+function dataEntryStateChanged(obj)
+{
+ if (obj.reload)
+ {
+ document.location = document.location;
+ }
+ else
+ {
+ var id = obj.id;
+ if (obj.state)
+ {
+ // has status, so draw the line
+ if (obj.props)
+ {
+ var span = document.getElementById('entry' + id + '_details');
+ if (span && span.innerHTML)
+ {
+ span.innerHTML = getDataEntryDetails( obj.props );
+ }
+ else
+ {
+ obj.props = false;
+ }
+ }
+
+ var span = document.getElementById('entry' + id);
+ if (span)
+ {
+ span.innerHTML = entryInternal( obj );
+ }
+
+
+ }
+ else
+ {
+ // no status, dataEntry has been removed/uninstalled
+ var span = document.getElementById('entry' + id);
+ if (span)
+ {
+ span.parentNode.removeChild(span);
+ }
+ var span = document.getElementById('entry' + id + '_details');
+ if (span)
+ {
+ span.parentNode.removeChild(span);
+ }
+ }
+ }
+}
+
+