FELIX-574 Start replacing Action and Render interfaces by Servlet interface:
* Add new AbstractWebConsolePlugin which implementations may extend from
* Add RenderBridge to register old-style Render as Servlet
FELIX-566 Convert Bundle handling to REST-style
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@662438 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/pom.xml b/webconsole/pom.xml
index 1dc1e1b..f20dff6 100644
--- a/webconsole/pom.xml
+++ b/webconsole/pom.xml
@@ -1,20 +1,20 @@
<!--
- 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
-
+ 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.
+
+ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
@@ -36,8 +36,12 @@
</description>
<scm>
- <connection>scm:svn:http://svn.apache.org/repos/asf/felix/trunk/webconsole</connection>
- <developerConnection>scm:svn:https://svn.apache.org/repos/asf/felix/trunk/webconsole</developerConnection>
+ <connection>
+ scm:svn:http://svn.apache.org/repos/asf/felix/trunk/webconsole
+ </connection>
+ <developerConnection>
+ scm:svn:https://svn.apache.org/repos/asf/felix/trunk/webconsole
+ </developerConnection>
<url>http://svn.apache.org/viewvc/felix/trunk/webconsole</url>
</scm>
@@ -66,12 +70,18 @@
<Bundle-SymbolicName>
${artifactId}
</Bundle-SymbolicName>
- <Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
+ <Bundle-Vendor>
+ The Apache Software Foundation
+ </Bundle-Vendor>
+ <Bundle-DocURL>
+ http://felix.apache.org
+ </Bundle-DocURL>
<Bundle-Activator>
org.apache.felix.webconsole.internal.OsgiManagerActivator
</Bundle-Activator>
<Export-Package>
org.apache.felix.webconsole,
+ org.osgi.service.obr
</Export-Package>
<Private-Package>
!org.apache.felix.webconsole,
@@ -85,18 +95,21 @@
<!-- Required by FileUpload and Util -->
org.apache.commons.io,
org.apache.commons.io.filefilter,
- org.apache.commons.io.output,
-
- <!-- Required for JSON data transfer -->
- org.json,
-
- <!-- Import/Export-Package parsing -->
- org.apache.felix.bundlerepository
+ org.apache.commons.io.output
</Private-Package>
<Import-Package>
- org.apache.felix.*;
- org.osgi.service.obr;resolution:=optional,*
+ org.apache.felix.scr;
+ org.apache.felix.shell;
+ org.osgi.service.*;resolution:=optional,*
</Import-Package>
+
+ <Embed-Dependency>
+ <!-- Import/Export-Package parsing, OBR -->
+ org.apache.felix.bundlerepository,
+
+ <!-- Required for JSON data transfer -->
+ json
+ </Embed-Dependency>
</instructions>
</configuration>
</plugin>
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java b/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java
new file mode 100644
index 0000000..972375f
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java
@@ -0,0 +1,309 @@
+/*
+ * 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;
+
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.MessageFormat;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.FileUploadException;
+import org.apache.commons.fileupload.disk.DiskFileItemFactory;
+import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.apache.commons.fileupload.servlet.ServletRequestContext;
+import org.apache.felix.webconsole.internal.servlet.OsgiManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+
+
+public abstract class AbstractWebConsolePlugin extends HttpServlet
+{
+
+ /** Pseudo class version ID to keep the IDE quite. */
+ private static final long serialVersionUID = 1L;
+
+ /** The name of the request attribute containig the map of FileItems from the POST request */
+ public static final String ATTR_FILEUPLOAD = "org.apache.felix.webconsole.fileupload";
+
+ private static final String HEADER = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">"
+ + "<html>"
+ + "<head>"
+ + "<meta http-equiv=\"Content-Type\" content=\"text/html; utf-8\">"
+ + "<link rel=\"icon\" href=\"res/imgs/favicon.ico\">"
+ + "<title>{0} - {12}</title>"
+ + "<script src=\"{15}/res/ui/admin.js\" language=\"JavaScript\"></script>"
+ + "<script language=\"JavaScript\">"
+ + "ABOUT_VERSION=''{1}'';"
+ + "ABOUT_JVERSION=''{2}'';"
+ + "ABOUT_JRT=''{3} (build {2})'';"
+ + "ABOUT_JVM=''{4} (build {5}, {6})'';"
+ + "ABOUT_MEM=\"{7} KB\";"
+ + "ABOUT_USED=\"{8} KB\";"
+ + "ABOUT_FREE=\"{9} KB\";"
+ + "</script>"
+ + "<link href=\"{15}/res/ui/admin.css\" rel=\"stylesheet\" type=\"text/css\">"
+ + "</head>"
+ + "<body>"
+ + "<div id=\"main\">"
+ + "<div id=\"lead\">"
+ + "<h1>"
+ + "{0}<br>{12}"
+ + "</h1>"
+ + "<p>"
+ + "<a target=\"_blank\" href=\"{13}\" title=\"{11}\"><img src=\"{15}/res/imgs/logo.png\" width=\"165\" height=\"63\" border=\"0\"></a>"
+ + "</p>" + "</div>";
+
+ private BundleContext bundleContext;
+
+ private String adminTitle;
+ private String adminVersion;
+ private String productName;
+ private String productWeb;
+ private String vendorName;
+ private String vendorWeb;
+
+
+ //---------- HttpServlet Overwrites ----------------------------------------
+
+ /**
+ * Returns the title for this plugin as returned by {@link #getTitle()}
+ */
+ public String getServletName()
+ {
+ return getTitle();
+ }
+
+
+ /**
+ * Renders the web console page for the request. This consist of the following
+ * four parts called in order:
+ * <ol>
+ * <li>{@link #startResponse(HttpServletRequest, HttpServletResponse)}</li>
+ * <li>{@link #renderTopNavigation(HttpServletRequest, PrintWriter)}</li>
+ * <li>{@link #renderContent(HttpServletRequest, HttpServletResponse)}</li>
+ * <li>{@link #endResponse(PrintWriter)}</li>
+ * </ol>
+ */
+ protected void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException,
+ IOException
+ {
+ PrintWriter pw = startResponse( request, response );
+ renderTopNavigation( request, pw );
+ renderContent( request, response );
+ endResponse( pw );
+ }
+
+
+ //---------- AbstractWebConsolePlugin API ----------------------------------
+
+ public void activate( BundleContext bundleContext )
+ {
+ this.bundleContext = bundleContext;
+
+ Dictionary headers = bundleContext.getBundle().getHeaders();
+
+ adminTitle = ( String ) headers.get( Constants.BUNDLE_NAME ); // "OSGi Management Console";
+ adminVersion = ( String ) headers.get( Constants.BUNDLE_NAME ); // "1.0.0-SNAPSHOT";
+ productName = "Apache Felix";
+ productWeb = ( String ) headers.get( Constants.BUNDLE_DOCURL );
+ vendorName = ( String ) headers.get( Constants.BUNDLE_VENDOR );
+ vendorWeb = "http://www.apache.org";
+ }
+
+
+ public void deactivate()
+ {
+ this.bundleContext = null;
+ }
+
+
+ public abstract String getTitle();
+
+
+ public abstract String getLabel();
+
+
+ protected abstract void renderContent( HttpServletRequest req, HttpServletResponse res ) throws ServletException,
+ IOException;
+
+
+ protected BundleContext getBundleContext()
+ {
+ return bundleContext;
+ }
+
+
+ protected PrintWriter startResponse( HttpServletRequest request, HttpServletResponse response ) throws IOException
+ {
+ response.setCharacterEncoding( "utf-8" );
+ response.setContentType( "text/html" );
+
+ PrintWriter pw = response.getWriter();
+
+ long freeMem = Runtime.getRuntime().freeMemory() / 1024;
+ long totalMem = Runtime.getRuntime().totalMemory() / 1024;
+ long usedMem = totalMem - freeMem;
+
+ String appRoot = request.getContextPath() + request.getServletPath();
+
+ String header = MessageFormat.format( HEADER, new Object[]
+ { adminTitle, adminVersion, System.getProperty( "java.runtime.version" ),
+ System.getProperty( "java.runtime.name" ), System.getProperty( "java.vm.name" ),
+ System.getProperty( "java.vm.version" ), System.getProperty( "java.vm.info" ), new Long( totalMem ),
+ new Long( usedMem ), new Long( freeMem ), vendorWeb, productName, getTitle(), productWeb, vendorName,
+ appRoot } );
+ pw.println( header );
+ return pw;
+ }
+
+
+ protected void renderTopNavigation( HttpServletRequest request, PrintWriter pw )
+ {
+ // assume pathInfo to not be null, else this would not be called
+ String current = request.getPathInfo();
+ int slash = current.indexOf( "/", 1 );
+ if ( slash > 1 )
+ {
+ current = current.substring( 1, slash );
+ }
+
+ boolean disabled = false;
+
+ Map labelMap = ( Map ) request.getAttribute( OsgiManager.ATTR_LABEL_MAP );
+ if ( labelMap != null )
+ {
+ pw.println( "<p id='technav'>" );
+
+ SortedMap map = new TreeMap();
+ for ( Iterator ri = labelMap.entrySet().iterator(); ri.hasNext(); )
+ {
+ Map.Entry labelMapEntry = ( Map.Entry ) ri.next();
+ if ( labelMapEntry.getKey() == null )
+ {
+ // ignore renders without a label
+ }
+ else if ( disabled || current.equals( labelMapEntry.getKey() ) )
+ {
+ map.put( labelMapEntry.getValue(), "<span class='technavat'>" + labelMapEntry.getValue()
+ + "</span>" );
+ }
+ else
+ {
+ map.put( labelMapEntry.getValue(), "<a href='" + labelMapEntry.getKey() + "'>"
+ + labelMapEntry.getValue() + "</a></li>" );
+ }
+ }
+
+ for ( Iterator li = map.values().iterator(); li.hasNext(); )
+ {
+ pw.println( li.next() );
+ }
+
+ pw.println( "</p>" );
+ }
+ }
+
+
+ protected void endResponse( PrintWriter pw )
+ {
+ pw.println( "</body>" );
+ pw.println( "</html>" );
+ }
+
+
+ public static String getParameter( HttpServletRequest request, String name )
+ {
+ // just get the parameter if not a multipart/form-data POST
+ if ( !ServletFileUpload.isMultipartContent( new ServletRequestContext( request ) ) )
+ {
+ return request.getParameter( name );
+ }
+
+ // check, whether we alread have the parameters
+ Map params = ( Map ) request.getAttribute( ATTR_FILEUPLOAD );
+ if ( params == null )
+ {
+ // parameters not read yet, read now
+ // Create a factory for disk-based file items
+ DiskFileItemFactory factory = new DiskFileItemFactory();
+ factory.setSizeThreshold( 256000 );
+
+ // Create a new file upload handler
+ ServletFileUpload upload = new ServletFileUpload( factory );
+ upload.setSizeMax( -1 );
+
+ // Parse the request
+ params = new HashMap();
+ try
+ {
+ List items = upload.parseRequest( request );
+ for ( Iterator fiter = items.iterator(); fiter.hasNext(); )
+ {
+ FileItem fi = ( FileItem ) fiter.next();
+ FileItem[] current = ( FileItem[] ) params.get( fi.getFieldName() );
+ if ( current == null )
+ {
+ current = new FileItem[]
+ { fi };
+ }
+ else
+ {
+ FileItem[] newCurrent = new FileItem[current.length + 1];
+ System.arraycopy( current, 0, newCurrent, 0, current.length );
+ newCurrent[current.length] = fi;
+ current = newCurrent;
+ }
+ params.put( fi.getFieldName(), current );
+ }
+ }
+ catch ( FileUploadException fue )
+ {
+ // TODO: log
+ }
+ request.setAttribute( ATTR_FILEUPLOAD, params );
+ }
+
+ FileItem[] param = ( FileItem[] ) params.get( name );
+ if ( param != null )
+ {
+ for ( int i = 0; i < param.length; i++ )
+ {
+ if ( param[i].isFormField() )
+ {
+ return param[i].getString();
+ }
+ }
+ }
+
+ // no valid string parameter, fail
+ return null;
+ }
+
+}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleConstants.java b/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleConstants.java
new file mode 100644
index 0000000..2bc1e08
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleConstants.java
@@ -0,0 +1,41 @@
+/*
+ * 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;
+
+
+public interface WebConsoleConstants
+{
+
+ /**
+ * The name of the service to register as to be used as a "plugin" for
+ * the OSGi Manager (value is "javax.servlet.Servlet").
+ */
+ public static final String SERVICE_NAME = "javax.servlet.Servlet";
+
+ /**
+ * The URI address label under which the OSGi Manager plugin is called by
+ * the OSGi Manager (value is "felix.webconsole.label").
+ * <p>
+ * Only {@link #SERVICE_NAME} services with this service registration
+ * property set to a non-empty String values are accepted by the OSGi
+ * Manager as a plugin.
+ */
+ public static final String PLUGIN_LABEL = "felix.webconsole.label";
+
+}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/BaseManagementPlugin.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/BaseManagementPlugin.java
index 83ac401..d38b03c 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/BaseManagementPlugin.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/BaseManagementPlugin.java
@@ -19,14 +19,13 @@
package org.apache.felix.webconsole.internal;
-import org.apache.felix.webconsole.internal.servlet.Logger;
import org.osgi.framework.BundleContext;
import org.osgi.service.packageadmin.PackageAdmin;
import org.osgi.service.startlevel.StartLevel;
import org.osgi.util.tracker.ServiceTracker;
-public class BaseManagementPlugin
+public class BaseManagementPlugin implements OsgiManagerPlugin
{
private BundleContext bundleContext;
@@ -42,15 +41,31 @@
}
- public void setBundleContext( BundleContext bundleContext )
+ public void activate( BundleContext bundleContext )
{
this.bundleContext = bundleContext;
+ this.log = new Logger( bundleContext );
}
- public void setLogger( Logger log )
+ public void deactivate()
{
- this.log = log;
+ if ( log != null )
+ {
+ log.dispose();
+ }
+
+ if ( startLevelService != null )
+ {
+ startLevelService.close();
+ startLevelService = null;
+ }
+
+ if ( packageAdmin != null )
+ {
+ packageAdmin.close();
+ packageAdmin = null;
+ }
}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/BaseWebConsolePlugin.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/BaseWebConsolePlugin.java
new file mode 100644
index 0000000..5490538
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/BaseWebConsolePlugin.java
@@ -0,0 +1,96 @@
+/*
+ * 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;
+
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.felix.webconsole.AbstractWebConsolePlugin;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.startlevel.StartLevel;
+import org.osgi.util.tracker.ServiceTracker;
+
+
+public abstract class BaseWebConsolePlugin extends AbstractWebConsolePlugin implements OsgiManagerPlugin
+{
+
+ private static String PACKAGE_ADMIN_NAME = PackageAdmin.class.getName();
+ private static String START_LEVEL_NAME = StartLevel.class.getName();
+
+ private Logger log;
+
+ private Map services = new HashMap();
+
+
+ public void deactivate()
+ {
+ for ( Iterator ti = services.values().iterator(); ti.hasNext(); )
+ {
+ ServiceTracker tracker = ( ServiceTracker ) ti.next();
+ tracker.close();
+ ti.remove();
+ }
+
+ if ( log != null )
+ {
+ log.dispose();
+ log = null;
+ }
+ }
+
+
+ protected Logger getLog()
+ {
+ if ( log == null )
+ {
+ log = new Logger( getBundleContext() );
+ }
+
+ return log;
+ }
+
+
+ protected StartLevel getStartLevel()
+ {
+ return ( StartLevel ) getService( START_LEVEL_NAME );
+ }
+
+
+ protected PackageAdmin getPackageAdmin()
+ {
+ return ( PackageAdmin ) getService( PACKAGE_ADMIN_NAME );
+ }
+
+
+ protected Object getService( String serviceName )
+ {
+ ServiceTracker serviceTracker = ( ServiceTracker ) services.get( serviceName );
+ if ( serviceTracker == null )
+ {
+ serviceTracker = new ServiceTracker( getBundleContext(), serviceName, null );
+ serviceTracker.open();
+
+ services.put( serviceName, serviceTracker );
+ }
+
+ return serviceTracker.getService();
+ }
+}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/Logger.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/Logger.java
similarity index 95%
rename from webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/Logger.java
rename to webconsole/src/main/java/org/apache/felix/webconsole/internal/Logger.java
index 7ff6f10..2d8e4b4 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/Logger.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/Logger.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.webconsole.internal.servlet;
+package org.apache.felix.webconsole.internal;
import org.osgi.framework.BundleContext;
@@ -30,14 +30,14 @@
private ServiceTracker logTracker;
- Logger( BundleContext bundleContext )
+ public Logger( BundleContext bundleContext )
{
logTracker = new ServiceTracker( bundleContext, LogService.class.getName(), null );
logTracker.open();
}
- void dispose()
+ public void dispose()
{
if ( logTracker != null )
{
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/OsgiManagerPlugin.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/OsgiManagerPlugin.java
new file mode 100644
index 0000000..404a9e3
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/OsgiManagerPlugin.java
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+
+import org.apache.felix.webconsole.AbstractWebConsolePlugin;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.startlevel.StartLevel;
+import org.osgi.util.tracker.ServiceTracker;
+
+
+public interface OsgiManagerPlugin
+{
+
+ void activate( BundleContext bundleContext );
+
+
+ void deactivate();
+
+}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/Util.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/Util.java
index 9d4212e..67972dc 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/Util.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/Util.java
@@ -20,16 +20,11 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
-import java.text.MessageFormat;
import java.util.Collection;
-import java.util.Iterator;
-import java.util.SortedMap;
-import java.util.TreeMap;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
-import org.apache.felix.webconsole.Render;
/**
@@ -59,104 +54,6 @@
/** Parameter value */
public static final String VALUE_SHUTDOWN = "shutdown";
- private static final String HEADER = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">"
- + "<html>"
- + "<head>"
- + "<meta http-equiv=\"Content-Type\" content=\"text/html; utf-8\">"
- + "<link rel=\"icon\" href=\"res/imgs/favicon.ico\">"
- + "<title>{0} - {12}</title>"
- + "<script src=\"res/ui/admin.js\" language=\"JavaScript\"></script>"
- + "<script language=\"JavaScript\">"
- + "ABOUT_VERSION=''{1}'';"
- + "ABOUT_JVERSION=''{2}'';"
- + "ABOUT_JRT=''{3} (build {2})'';"
- + "ABOUT_JVM=''{4} (build {5}, {6})'';"
- + "ABOUT_MEM=\"{7} KB\";"
- + "ABOUT_USED=\"{8} KB\";"
- + "ABOUT_FREE=\"{9} KB\";"
- + "</script>"
- + "<link href=\"res/ui/admin.css\" rel=\"stylesheet\" type=\"text/css\">"
- + "</head>"
- + "<body>"
- + "<div id=\"main\">"
- + "<div id=\"lead\">"
- + "<h1>"
- + "{0}<br>{12}"
- + "</h1>"
- + "<p>"
- + "<a target=\"_blank\" href=\"{13}\" title=\"{11}\"><img src=\"res/imgs/logo.png\" width=\"165\" height=\"63\" border=\"0\"></a>"
- + "</p>" + "</div>";
-
- /** The name of the request attribute containig the map of FileItems from the POST request */
- public static final String ATTR_FILEUPLOAD = "org.apache.felix.webconsole.fileupload";
-
-
- public static PrintWriter startHtml( HttpServletResponse resp, String pageTitle ) throws IOException
- {
- resp.setContentType( "text/html; utf-8" );
-
- PrintWriter pw = resp.getWriter();
-
- String adminTitle = "OSGi Management Console"; // ServletEngine.VERSION.getFullProductName();
- String productName = "Felix"; // ServletEngine.VERSION.getShortProductName();
- String productWeb = "http://felix.apache.org";
- String vendorName = "http://www.apache.org"; // ServletEngine.VERSION.getVendorWeb();
- String vendorWeb = "http://www.apache.org"; // ServletEngine.VERSION.getVendorWeb();
-
- long freeMem = Runtime.getRuntime().freeMemory() / 1024;
- long totalMem = Runtime.getRuntime().totalMemory() / 1024;
- long usedMem = totalMem - freeMem;
-
- String header = MessageFormat.format( HEADER, new Object[]
- {
- adminTitle,
- "1.0.0-SNAPSHOT", // ServletEngine.VERSION.getFullVersion(),
- System.getProperty( "java.runtime.version" ), System.getProperty( "java.runtime.name" ),
- System.getProperty( "java.vm.name" ), System.getProperty( "java.vm.version" ),
- System.getProperty( "java.vm.info" ), new Long( totalMem ), new Long( usedMem ), new Long( freeMem ),
- vendorWeb, productName, pageTitle, productWeb, vendorName } );
- pw.println( header );
- return pw;
- }
-
-
- public static void navigation( PrintWriter pw, Collection renders, String current, boolean disabled )
- {
- pw.println( "<p id='technav'>" );
-
- SortedMap map = new TreeMap();
- for ( Iterator ri = renders.iterator(); ri.hasNext(); )
- {
- Render render = ( Render ) ri.next();
- if ( render.getLabel() == null )
- {
- // ignore renders without a label
- }
- else if ( disabled || current.equals( render.getName() ) )
- {
- map.put( render.getLabel(), "<span class='technavat'>" + render.getLabel() + "</span>" );
- }
- else
- {
- map.put( render.getLabel(), "<a href='" + render.getName() + "'>" + render.getLabel() + "</a></li>" );
- }
- }
-
- for ( Iterator li = map.values().iterator(); li.hasNext(); )
- {
- pw.println( li.next() );
- }
-
- pw.println( "</p>" );
- }
-
-
- public static void endHhtml( PrintWriter pw )
- {
- pw.println( "</body>" );
- pw.println( "</html>" );
- }
-
public static void startScript( PrintWriter pw )
{
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 76a6980..76f3ef6 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
@@ -44,9 +44,9 @@
private ServiceRegistration registration;
- public void setBundleContext( BundleContext bundleContext )
+ public void activate( BundleContext bundleContext )
{
- super.setBundleContext( bundleContext );
+ super.activate( bundleContext );
registration = bundleContext.registerService( ConfigurationPrinter.SERVICE, this, null );
}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManagerBase.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManagerBase.java
index 5a5de80..225268b 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManagerBase.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManagerBase.java
@@ -41,15 +41,18 @@
*/
abstract class ConfigManagerBase extends BaseManagementPlugin
{
+ private static final String CONFIGURATION_ADMIN_NAME = ConfigurationAdmin.class.getName();
+
+ private static final String META_TYPE_NAME = MetaTypeService.class.getName();
private ServiceTracker configurationAdmin;
private ServiceTracker metaTypeService;
- public void setBundleContext( BundleContext bundleContext )
+ public void activate( BundleContext bundleContext )
{
- super.setBundleContext( bundleContext );
+ super.activate( bundleContext );
configurationAdmin = new ServiceTracker( bundleContext, ConfigurationAdmin.class.getName(), null );
configurationAdmin.open();
@@ -73,12 +76,14 @@
protected ConfigurationAdmin getConfigurationAdmin()
{
+ //TODO: getService(CONFIGURATION_ADMIN_NAME)
return ( ConfigurationAdmin ) configurationAdmin.getService();
}
protected MetaTypeService getMetaTypeService()
{
+ //TODO: getService(META_TYPE_NAME)
return ( MetaTypeService ) metaTypeService.getService();
}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/AjaxBundleDetailsAction.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/AjaxBundleDetailsAction.java
index 6fdba1a..b3a312d 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/AjaxBundleDetailsAction.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/AjaxBundleDetailsAction.java
@@ -69,9 +69,9 @@
private boolean[] bootPkgWildcards;
- public void setBundleContext( BundleContext bundleContext )
+ public void activate( BundleContext bundleContext )
{
- super.setBundleContext( bundleContext );
+ super.activate( bundleContext );
// bootdelegation property parsing from Apache Felix R4SearchPolicyCore
String bootDelegation = bundleContext.getProperty( Constants.FRAMEWORK_BOOTDELEGATION );
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundleListRender.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundleListRender.java
index 7498d7f..5418352 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundleListRender.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundleListRender.java
@@ -19,123 +19,343 @@
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.Date;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.apache.felix.webconsole.Render;
-import org.apache.felix.webconsole.internal.BaseManagementPlugin;
+import org.apache.felix.bundlerepository.R4Attribute;
+import org.apache.felix.bundlerepository.R4Export;
+import org.apache.felix.bundlerepository.R4Import;
+import org.apache.felix.bundlerepository.R4Package;
+import org.apache.felix.webconsole.internal.BaseWebConsolePlugin;
import org.apache.felix.webconsole.internal.Util;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONWriter;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
import org.osgi.framework.Version;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.component.ComponentConstants;
+import org.osgi.service.obr.Repository;
+import org.osgi.service.obr.RepositoryAdmin;
+import org.osgi.service.obr.Resource;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.startlevel.StartLevel;
import org.osgi.util.tracker.ServiceTracker;
/**
* The <code>BundleListRender</code> TODO
*/
-public class BundleListRender extends BaseManagementPlugin implements Render
+public class BundleListRender extends BaseWebConsolePlugin
{
- public static final String NAME = "list";
+ public static final String NAME = "bundles";
public static final String LABEL = "Bundles";
public static final String BUNDLE_ID = "bundleId";
- private static final String INSTALLER_SERVICE_NAME = "org.apache.sling.osgi.assembly.installer.InstallerService";
+ private static final String REPOSITORY_ADMIN_NAME = RepositoryAdmin.class.getName();
- // track the optional installer service manually
- private ServiceTracker installerService;
+ // bootdelegation property entries. wildcards are converted to package
+ // name prefixes. whether an entry is a wildcard or not is set as a flag
+ // in the bootPkgWildcards array.
+ // see #activate and #isBootDelegated
+ private String[] bootPkgs;
+
+ // a flag for each entry in bootPkgs indicating whether the respective
+ // entry was declared as a wildcard or not
+ // see #activate and #isBootDelegated
+ private boolean[] bootPkgWildcards;
- public void setBundleContext( BundleContext bundleContext )
+ public void activate( BundleContext bundleContext )
{
- super.setBundleContext( bundleContext );
+ super.activate( bundleContext );
- installerService = new ServiceTracker( bundleContext, INSTALLER_SERVICE_NAME, null );
- installerService.open();
- }
-
-
- // protected void deactivate(ComponentContext context) {
- // if (installerService != null) {
- // installerService.close();
- // installerService = null;
- // }
- // }
-
- public String getName()
- {
- return NAME;
+ // bootdelegation property parsing from Apache Felix R4SearchPolicyCore
+ String bootDelegation = bundleContext.getProperty( Constants.FRAMEWORK_BOOTDELEGATION );
+ bootDelegation = ( bootDelegation == null ) ? "java.*" : bootDelegation + ",java.*";
+ StringTokenizer st = new StringTokenizer( bootDelegation, " ," );
+ bootPkgs = new String[st.countTokens()];
+ bootPkgWildcards = new boolean[bootPkgs.length];
+ for ( int i = 0; i < bootPkgs.length; i++ )
+ {
+ bootDelegation = st.nextToken();
+ if ( bootDelegation.endsWith( "*" ) )
+ {
+ bootPkgWildcards[i] = true;
+ bootDelegation = bootDelegation.substring( 0, bootDelegation.length() - 1 );
+ }
+ bootPkgs[i] = bootDelegation;
+ }
}
public String getLabel()
{
+ return NAME;
+ }
+
+
+ public String getTitle()
+ {
return LABEL;
}
- public void render( HttpServletRequest request, HttpServletResponse response ) throws IOException
+ protected void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException,
+ IOException
+ {
+
+ String info = request.getPathInfo();
+ if ( info.endsWith( ".json" ) )
+ {
+ info = info.substring( 0, info.length() - 5 );
+ if ( getLabel().equals( info.substring( 1 ) ) )
+ {
+ // should return info on all bundles
+ }
+ else
+ {
+ Bundle bundle = getBundle( info );
+ if ( bundle != null )
+ {
+ // bundle properties
+
+ response.setContentType( "text/javascript" );
+ response.setCharacterEncoding( "UTF-8" );
+
+ PrintWriter pw = response.getWriter();
+ JSONWriter jw = new JSONWriter( pw );
+ try
+ {
+ performAction( jw, bundle );
+ }
+ catch ( JSONException je )
+ {
+ throw new IOException( je.toString() );
+ }
+ }
+ }
+
+ // nothing more to do
+ return;
+ }
+
+ super.doGet( request, response );
+ }
+
+
+ protected void doPost( HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException
+ {
+ boolean success = false;
+ Bundle bundle = getBundle( req.getPathInfo() );
+ long bundleId = bundle.getBundleId();
+ if ( bundle != null )
+ {
+ String action = req.getParameter( "action" );
+ if ( "start".equals( action ) )
+ {
+ // start bundle
+ success = true;
+ try
+ {
+ bundle.start();
+ }
+ catch ( BundleException be )
+ {
+ // log
+ }
+ }
+ else if ( "stop".equals( action ) )
+ {
+ // stop bundle
+ success = true;
+ try
+ {
+ bundle.stop();
+ }
+ catch ( BundleException be )
+ {
+ // log
+ }
+ }
+ else if ( "update".equals( action ) )
+ {
+ // update bundle
+ success = true;
+ }
+ else if ( "uninstall".equals( action ) )
+ {
+ // uninstall bundle
+ success = true;
+ try
+ {
+ bundle.uninstall();
+ bundle = null; // bundle has gone !
+ }
+ catch ( BundleException be )
+ {
+ // log
+ }
+ }
+ }
+
+ if ( success )
+ {
+ // redirect or 200
+ resp.setStatus( HttpServletResponse.SC_OK );
+ JSONWriter jw = new JSONWriter( resp.getWriter() );
+ try
+ {
+ if ( bundle != null )
+ {
+ bundleInfo( jw, bundle, true );
+ }
+ else
+ {
+ jw.object();
+ jw.key( "bundleId" );
+ jw.value( bundleId );
+ jw.endObject();
+ }
+ }
+ catch ( JSONException je )
+ {
+ throw new IOException( je.toString() );
+ }
+ }
+ else
+ {
+ super.doPost( req, resp );
+ }
+ }
+
+
+ private Bundle getBundle( String pathInfo )
+ {
+ // only use last part of the pathInfo
+ pathInfo = pathInfo.substring( pathInfo.lastIndexOf( '/' ) + 1 );
+
+ // assume bundle Id
+ long bundleId;
+ try
+ {
+ bundleId = Long.parseLong( pathInfo );
+ }
+ catch ( NumberFormatException nfe )
+ {
+ bundleId = -1;
+ }
+
+ if ( bundleId >= 0 )
+ {
+ return getBundleContext().getBundle( bundleId );
+ }
+
+ return null;
+ }
+
+
+ protected void renderContent( HttpServletRequest request, HttpServletResponse response ) throws ServletException,
+ IOException
{
PrintWriter pw = response.getWriter();
- this.header( pw );
+ String appRoot = request.getContextPath() + request.getServletPath();
+ pw.println( "<script src='" + appRoot + "/res/ui/bundles.js' language='JavaScript'></script>" );
- this.installForm( pw );
- pw.println( "<tr class='content'>" );
- pw.println( "<td colspan='7' class='content'> </th>" );
- pw.println( "</tr>" );
-
- this.tableHeader( pw );
-
- Bundle[] bundles = this.getBundles();
- if ( bundles == null || bundles.length == 0 )
+ Util.startScript( pw );
+ pw.println( "var bundleListData = " );
+ JSONWriter jw = new JSONWriter( pw );
+ try
{
- pw.println( "<tr class='content'>" );
- pw.println( "<td class='content' colspan='6'>No " + this.getLabel() + " installed currently</td>" );
- pw.println( "</tr>" );
- }
- else
- {
+ jw.object();
+ jw.key( "startlevel" );
+ jw.value( getStartLevel().getInitialBundleStartLevel() );
- sort( bundles );
+ Bundle bundle = getBundle( request.getPathInfo() );
+ Bundle[] bundles = ( bundle != null ) ? new Bundle[]
+ { bundle } : this.getBundles();
+ boolean details = ( bundle != null );
- long previousBundle = -1;
- for ( int i = 0; i < bundles.length; i++ )
+ if ( bundles != null && bundles.length > 0 )
{
+ sort( bundles );
- if ( previousBundle >= 0 )
+ jw.key( "bundles" );
+
+ jw.array();
+
+ for ( int i = 0; i < bundles.length; i++ )
{
- // prepare for injected table information row
- pw.println( "<tr id='bundle" + previousBundle + "'></tr>" );
+ bundleInfo( jw, bundles[i], details );
}
- this.bundle( pw, bundles[i] );
+ jw.endArray();
- previousBundle = bundles[i].getBundleId();
}
- if ( previousBundle >= 0 )
- {
- // prepare for injected table information row
- pw.println( "<tr id='bundle" + previousBundle + "'></tr>" );
- }
+ jw.endObject();
+
+ }
+ catch ( JSONException je )
+ {
+ throw new IOException( je.toString() );
}
- pw.println( "<tr class='content'>" );
- pw.println( "<td colspan='7' class='content'> </th>" );
- pw.println( "</tr>" );
+ pw.println( ";" );
+ pw.println( "render(bundleListData.startlevel, bundleListData.bundles);" );
+ Util.endScript( pw );
+ }
- this.installForm( pw );
- this.footer( pw );
+ private void bundleInfo( JSONWriter jw, Bundle bundle, boolean details ) throws JSONException
+ {
+ jw.object();
+ jw.key( "bundleId" );
+ jw.value( bundle.getBundleId() );
+ jw.key( "name" );
+ jw.value( 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 ) );
+
+ if ( details )
+ {
+ bundleDetails( jw, bundle );
+ }
+
+ jw.endObject();
}
@@ -145,140 +365,6 @@
}
- private void header( PrintWriter pw )
- {
- Util.startScript( pw );
- pw.println( "function showDetails(bundleId) {" );
- pw.println( " var span = document.getElementById('bundle' + bundleId);" );
- 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 + "=" + AjaxBundleDetailsAction.NAME + "&" + BUNDLE_ID
- + "=' + bundleId;" );
- pw.println( " sendRequest('GET', parm, displayBundleDetails);" );
- pw.println( "}" );
- pw.println( "function displayBundleDetails(obj) {" );
- pw.println( " var span = document.getElementById('bundle' + obj." + BUNDLE_ID + ");" );
- pw.println( " if (!span) {" );
- pw.println( " return;" );
- pw.println( " }" );
- pw
- .println( " var innerHtml = '<td class=\"content\"> </td><td class=\"content\" colspan=\"6\"><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 container' colspan='7'>Installed " +
- // getLabel() + "</th>");
- // pw.println("</tr>");
-
- 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='4'>Actions</th>" );
- pw.println( "</tr>" );
- }
-
-
- private void footer( PrintWriter pw )
- {
- pw.println( "</table>" );
- }
-
-
- private void bundle( PrintWriter pw, Bundle bundle )
- {
- String name = getName( bundle );
-
- pw.println( "<tr>" );
- pw.println( "<td class='content right'>" + bundle.getBundleId() + "</td>" );
- pw.println( "<td class='content'><a href='javascript:showDetails(" + bundle.getBundleId() + ")'>" + name
- + "</a></td>" );
- pw.println( "<td class='content center'>" + this.toStateString( bundle.getState() ) + "</td>" );
-
- // no buttons for system bundle
- if ( bundle.getBundleId() == 0 )
- {
- pw.println( "<td class='content' colspan='4'> </td>" );
- }
- else
- {
- boolean enabled = bundle.getState() == Bundle.INSTALLED || bundle.getState() == Bundle.RESOLVED;
- this.actionForm( pw, enabled, bundle.getBundleId(), StartAction.NAME, StartAction.LABEL );
-
- enabled = bundle.getState() == Bundle.ACTIVE;
- this.actionForm( pw, enabled, bundle.getBundleId(), StopAction.NAME, StopAction.LABEL );
-
- enabled = bundle.getState() != Bundle.UNINSTALLED && this.hasUpdates( bundle );
- this.actionForm( pw, enabled, bundle.getBundleId(), UpdateAction.NAME, UpdateAction.LABEL );
-
- enabled = bundle.getState() == Bundle.INSTALLED || bundle.getState() == Bundle.RESOLVED
- || bundle.getState() == Bundle.ACTIVE;
- this.actionForm( pw, enabled, bundle.getBundleId(), UninstallAction.NAME, UninstallAction.LABEL );
- }
-
- pw.println( "</tr>" );
- }
-
-
- private void actionForm( PrintWriter pw, boolean enabled, long bundleId, String action, String actionLabel )
- {
- pw.println( "<form name='form" + bundleId + "' method='post'>" );
- pw.println( "<td class='content' align='right'>" );
- pw.println( "<input type='hidden' name='" + Util.PARAM_ACTION + "' value='" + action + "' />" );
- pw.println( "<input type='hidden' name='" + BUNDLE_ID + "' value='" + bundleId + "' />" );
- pw.println( "<input class='submit' type='submit' value='" + actionLabel + "'" + ( enabled ? "" : "disabled" )
- + " />" );
- pw.println( "</td>" );
- pw.println( "</form>" );
- }
-
-
- private void installForm( PrintWriter pw )
- {
- int startLevel = getStartLevel().getInitialBundleStartLevel();
-
- pw.println( "<form method='post' enctype='multipart/form-data'>" );
- pw.println( "<tr class='content'>" );
- pw.println( "<td class='content'> </td>" );
- pw.println( "<td class='content'>" );
- pw.println( "<input type='hidden' name='" + Util.PARAM_ACTION + "' value='" + InstallAction.NAME + "' />" );
- pw.println( "<input class='input' type='file' name='" + InstallAction.FIELD_BUNDLEFILE + "'>" );
- pw.println( " - Start <input class='checkradio' type='checkbox' name='" + InstallAction.FIELD_START
- + "' value='start'>" );
- pw.println( " - Start Level <input class='input' type='input' name='" + InstallAction.FIELD_STARTLEVEL
- + "' value='" + startLevel + "' width='4'>" );
- pw.println( "</td>" );
- pw.println( "<td class='content' align='right' colspan='5' noWrap>" );
- pw.println( "<input class='submit' style='width:auto' type='submit' value='" + InstallAction.LABEL + "'>" );
- pw.println( " " );
- pw.println( "<input class='submit' style='width:auto' type='submit' value='" + RefreshPackagesAction.LABEL
- + "' onClick='this.form[\"" + Util.PARAM_ACTION + "\"].value=\"" + RefreshPackagesAction.NAME
- + "\"; return true;'>" );
- pw.println( "</td>" );
- pw.println( "</tr>" );
- pw.println( "</form>" );
- }
-
-
private String toStateString( int bundleState )
{
switch ( bundleState )
@@ -301,11 +387,24 @@
}
- private boolean hasUpdates( Bundle bundle )
+ private boolean hasStart( Bundle bundle )
{
+ return bundle.getState() == Bundle.INSTALLED || bundle.getState() == Bundle.RESOLVED;
+ }
+
+
+ private boolean hasStop( Bundle bundle )
+ {
+ return bundle.getState() == Bundle.ACTIVE;
+ }
+
+
+ private boolean hasUpdate( Bundle bundle )
+ {
+ // enabled = bundle.getState() != Bundle.UNINSTALLED && this.hasUpdates( bundle );
// no updates if there is no installer service
- Object isObject = installerService.getService();
+ Object isObject = getService( REPOSITORY_ADMIN_NAME );
if ( isObject == null )
{
return false;
@@ -316,25 +415,39 @@
{
return false;
}
- /*
- Version bundleVersion = Version.parseVersion((String) bundle.getHeaders().get(
- Constants.BUNDLE_VERSION));
- BundleRepositoryAdmin repoAdmin = ((InstallerService) isObject).getBundleRepositoryAdmin();
- for (Iterator<Resource> ri = repoAdmin.getResources(); ri.hasNext();) {
- Resource res = ri.next();
- if (bundle.getSymbolicName().equals(res.getSymbolicName())) {
- if (res.getVersion().compareTo(bundleVersion) > 0) {
- return true;
- }
+ Version bundleVersion = Version.parseVersion( ( String ) bundle.getHeaders().get( Constants.BUNDLE_VERSION ) );
+
+ RepositoryAdmin repoAdmin = ( RepositoryAdmin ) isObject;
+ Repository[] repositories = repoAdmin.listRepositories();
+ for ( int i = 0; i < repositories.length; i++ )
+ {
+ Resource[] resources = repositories[i].getResources();
+ for ( int j = 0; j < resources.length; j++ )
+ {
+ Resource res = resources[j];
+ if ( bundle.getSymbolicName().equals( res.getSymbolicName() ) )
+ {
+ if ( res.getVersion().compareTo( bundleVersion ) > 0 )
+ {
+ return true;
}
}
- */
+ }
+ }
return false;
}
+ private boolean hasUninstall( Bundle bundle )
+ {
+ return bundle.getState() == Bundle.INSTALLED || bundle.getState() == Bundle.RESOLVED
+ || bundle.getState() == Bundle.ACTIVE;
+
+ }
+
+
private void sort( Bundle[] bundles )
{
Arrays.sort( bundles, BUNDLE_NAME_COMPARATOR );
@@ -359,6 +472,476 @@
return name;
}
+
+ public void performAction( JSONWriter jw, Bundle bundle ) throws JSONException
+ {
+ jw.object();
+ jw.key( BUNDLE_ID );
+ jw.value( bundle.getBundleId() );
+
+ bundleDetails( jw, bundle );
+
+ jw.endObject();
+ }
+
+
+ public void bundleDetails( JSONWriter jw, Bundle bundle ) throws JSONException
+ {
+ Dictionary headers = bundle.getHeaders();
+
+ jw.key( "props" );
+ jw.array();
+ keyVal( jw, "Symbolic Name", bundle.getSymbolicName() );
+ keyVal( jw, "Version", headers.get( Constants.BUNDLE_VERSION ) );
+ keyVal( jw, "Location", bundle.getLocation() );
+ keyVal( jw, "Last Modification", new Date( bundle.getLastModified() ) );
+
+ keyVal( jw, "Vendor", headers.get( Constants.BUNDLE_VENDOR ) );
+ keyVal( jw, "Copyright", headers.get( Constants.BUNDLE_COPYRIGHT ) );
+ keyVal( jw, "Description", headers.get( Constants.BUNDLE_DESCRIPTION ) );
+
+ keyVal( jw, "Start Level", getStartLevel( bundle ) );
+
+ if ( bundle.getState() == Bundle.INSTALLED )
+ {
+ listImportExportsUnresolved( jw, bundle );
+ }
+ else
+ {
+ listImportExport( jw, bundle );
+ }
+
+ listServices( jw, bundle );
+
+ jw.endArray();
+ }
+
+
+ private Integer getStartLevel( Bundle bundle )
+ {
+ StartLevel sl = getStartLevel();
+ return ( sl != null ) ? new Integer( sl.getBundleStartLevel( bundle ) ) : null;
+ }
+
+
+ private void listImportExport( JSONWriter jw, Bundle bundle ) throws JSONException
+ {
+ PackageAdmin packageAdmin = getPackageAdmin();
+ if ( packageAdmin == null )
+ {
+ return;
+ }
+
+ Map usingBundles = new TreeMap();
+
+ ExportedPackage[] exports = packageAdmin.getExportedPackages( bundle );
+ if ( exports != null && exports.length > 0 )
+ {
+ // do alphabetical sort
+ Arrays.sort( exports, new Comparator()
+ {
+ public int compare( ExportedPackage p1, ExportedPackage p2 )
+ {
+ return p1.getName().compareTo( p2.getName() );
+ }
+
+
+ public int compare( Object o1, Object o2 )
+ {
+ return compare( ( ExportedPackage ) o1, ( ExportedPackage ) o2 );
+ }
+ } );
+
+ StringBuffer val = new StringBuffer();
+ for ( int j = 0; j < exports.length; j++ )
+ {
+ ExportedPackage export = exports[j];
+ printExport( val, export.getName(), export.getVersion() );
+ Bundle[] ubList = export.getImportingBundles();
+ if ( ubList != null )
+ {
+ for ( int i = 0; i < ubList.length; i++ )
+ {
+ Bundle ub = ubList[i];
+ usingBundles.put( ub.getSymbolicName(), ub );
+ }
+ }
+ }
+ keyVal( jw, "Exported Packages", val.toString() );
+ }
+ else
+ {
+ keyVal( jw, "Exported Packages", "None" );
+ }
+
+ exports = packageAdmin.getExportedPackages( ( Bundle ) null );
+ if ( exports != null && exports.length > 0 )
+ {
+ // collect import packages first
+ final List imports = new ArrayList();
+ for ( int i = 0; i < exports.length; i++ )
+ {
+ final ExportedPackage ep = exports[i];
+ final Bundle[] importers = ep.getImportingBundles();
+ for ( int j = 0; importers != null && j < importers.length; j++ )
+ {
+ if ( importers[j].getBundleId() == bundle.getBundleId() )
+ {
+ imports.add( ep );
+
+ break;
+ }
+ }
+ }
+ // now sort
+ StringBuffer val = new StringBuffer();
+ if ( imports.size() > 0 )
+ {
+ final ExportedPackage[] packages = ( ExportedPackage[] ) imports.toArray( new ExportedPackage[imports
+ .size()] );
+ Arrays.sort( packages, new Comparator()
+ {
+ public int compare( ExportedPackage p1, ExportedPackage p2 )
+ {
+ return p1.getName().compareTo( p2.getName() );
+ }
+
+
+ public int compare( Object o1, Object o2 )
+ {
+ return compare( ( ExportedPackage ) o1, ( ExportedPackage ) o2 );
+ }
+ } );
+ // and finally print out
+ for ( int i = 0; i < packages.length; i++ )
+ {
+ ExportedPackage ep = packages[i];
+ printImport( val, ep.getName(), ep.getVersion(), ep );
+ }
+ }
+ else
+ {
+ // add description if there are no imports
+ val.append( "None" );
+ }
+
+ keyVal( jw, "Imported Packages", val.toString() );
+ }
+
+ if ( !usingBundles.isEmpty() )
+ {
+ StringBuffer val = new StringBuffer();
+ for ( Iterator ui = usingBundles.values().iterator(); ui.hasNext(); )
+ {
+ Bundle usingBundle = ( Bundle ) ui.next();
+ val.append( getBundleDescriptor( usingBundle ) );
+ val.append( "<br />" );
+ }
+ keyVal( jw, "Importing Bundles", val.toString() );
+ }
+ }
+
+
+ private void listImportExportsUnresolved( JSONWriter jw, Bundle bundle ) throws JSONException
+ {
+ Dictionary dict = bundle.getHeaders();
+
+ String target = ( String ) dict.get( Constants.EXPORT_PACKAGE );
+ if ( target != null )
+ {
+ R4Package[] pkgs = R4Package.parseImportOrExportHeader( target );
+ if ( pkgs != null && pkgs.length > 0 )
+ {
+ // do alphabetical sort
+ Arrays.sort( pkgs, new Comparator()
+ {
+ public int compare( R4Package p1, R4Package p2 )
+ {
+ return p1.getName().compareTo( p2.getName() );
+ }
+
+
+ public int compare( Object o1, Object o2 )
+ {
+ return compare( ( R4Package ) o1, ( R4Package ) o2 );
+ }
+ } );
+
+ StringBuffer val = new StringBuffer();
+ for ( int i = 0; i < pkgs.length; i++ )
+ {
+ R4Export export = new R4Export( pkgs[i] );
+ printExport( val, export.getName(), export.getVersion() );
+ }
+ keyVal( jw, "Exported Packages", val.toString() );
+ }
+ else
+ {
+ keyVal( jw, "Exported Packages", "None" );
+ }
+ }
+
+ target = ( String ) dict.get( Constants.IMPORT_PACKAGE );
+ if ( target != null )
+ {
+ R4Package[] pkgs = R4Package.parseImportOrExportHeader( target );
+ if ( pkgs != null && pkgs.length > 0 )
+ {
+ Map imports = new TreeMap();
+ for ( int i = 0; i < pkgs.length; i++ )
+ {
+ R4Package pkg = pkgs[i];
+ imports.put( pkg.getName(), new R4Import( pkg ) );
+ }
+
+ // collect import packages first
+ final Map candidates = new HashMap();
+ PackageAdmin packageAdmin = getPackageAdmin();
+ if ( packageAdmin != null )
+ {
+ ExportedPackage[] exports = packageAdmin.getExportedPackages( ( Bundle ) null );
+ if ( exports != null && exports.length > 0 )
+ {
+
+ for ( int i = 0; i < exports.length; i++ )
+ {
+ final ExportedPackage ep = exports[i];
+
+ R4Import imp = ( R4Import ) imports.get( ep.getName() );
+ if ( imp != null && imp.isSatisfied( toR4Export( ep ) ) )
+ {
+ candidates.put( ep.getName(), ep );
+ }
+ }
+ }
+ }
+
+ // now sort
+ StringBuffer val = new StringBuffer();
+ if ( imports.size() > 0 )
+ {
+ for ( Iterator ii = imports.values().iterator(); ii.hasNext(); )
+ {
+ R4Import r4Import = ( R4Import ) ii.next();
+ ExportedPackage ep = ( ExportedPackage ) candidates.get( r4Import.getName() );
+
+ // if there is no matching export, check whether this
+ // bundle has the package, ignore the entry in this case
+ if ( ep == null )
+ {
+ String path = r4Import.getName().replace( '.', '/' );
+ if ( bundle.getResource( path ) != null )
+ {
+ continue;
+ }
+ }
+
+ printImport( val, r4Import.getName(), r4Import.getVersion(), ep );
+ }
+ }
+ else
+ {
+ // add description if there are no imports
+ val.append( "None" );
+ }
+
+ keyVal( jw, "Imported Packages", val.toString() );
+ }
+ }
+ }
+
+
+ private void listServices( JSONWriter jw, Bundle bundle ) throws JSONException
+ {
+ ServiceReference[] refs = bundle.getRegisteredServices();
+ if ( refs == null || refs.length == 0 )
+ {
+ return;
+ }
+
+ for ( int i = 0; i < refs.length; i++ )
+ {
+ String key = "Service ID " + refs[i].getProperty( Constants.SERVICE_ID );
+
+ StringBuffer val = new StringBuffer();
+
+ appendProperty( val, refs[i], Constants.OBJECTCLASS, "Types" );
+ appendProperty( val, refs[i], Constants.SERVICE_PID, "PID" );
+ appendProperty( val, refs[i], ConfigurationAdmin.SERVICE_FACTORYPID, "Factory PID" );
+ appendProperty( val, refs[i], ComponentConstants.COMPONENT_NAME, "Component Name" );
+ appendProperty( val, refs[i], ComponentConstants.COMPONENT_ID, "Component ID" );
+ appendProperty( val, refs[i], ComponentConstants.COMPONENT_FACTORY, "Component Factory" );
+ appendProperty( val, refs[i], Constants.SERVICE_DESCRIPTION, "Description" );
+ appendProperty( val, refs[i], Constants.SERVICE_VENDOR, "Vendor" );
+
+ keyVal( jw, key, val.toString() );
+ }
+ }
+
+
+ private void appendProperty( StringBuffer dest, ServiceReference ref, String name, String label )
+ {
+ Object value = ref.getProperty( name );
+ if ( value instanceof Object[] )
+ {
+ Object[] values = ( Object[] ) value;
+ dest.append( label ).append( ": " );
+ for ( int j = 0; j < values.length; j++ )
+ {
+ if ( j > 0 )
+ dest.append( ", " );
+ dest.append( values[j] );
+ }
+ dest.append( "<br />" ); // assume HTML use of result
+ }
+ else if ( value != null )
+ {
+ dest.append( label ).append( ": " ).append( value ).append( "<br />" );
+ }
+ }
+
+
+ private void keyVal( JSONWriter jw, String key, Object value ) throws JSONException
+ {
+ if ( key != null && value != null )
+ {
+ jw.object();
+ jw.key( "key" );
+ jw.value( key );
+ jw.key( "value" );
+ jw.value( value );
+ jw.endObject();
+ }
+ }
+
+
+ private void printExport( StringBuffer val, String name, Version version )
+ {
+ boolean bootDel = isBootDelegated( name );
+ if ( bootDel )
+ {
+ val.append( "<span style=\"color: red\">!! " );
+ }
+
+ val.append( name );
+ val.append( ",version=" );
+ val.append( version );
+
+ if ( bootDel )
+ {
+ val.append( " -- Overwritten by Boot Delegation</span>" );
+ }
+
+ val.append( "<br />" );
+ }
+
+
+ private void printImport( StringBuffer val, String name, Version version, ExportedPackage export )
+ {
+ boolean bootDel = isBootDelegated( name );
+ if ( bootDel || export == null )
+ {
+ val.append( "<span style=\"color: red\">!! " );
+ }
+
+ val.append( name );
+ val.append( ",version=" ).append( version );
+ val.append( " from " );
+
+ if ( export != null )
+ {
+ val.append( getBundleDescriptor( export.getExportingBundle() ) );
+
+ if ( bootDel )
+ {
+ val.append( " -- Overwritten by Boot Delegation</span>" );
+ }
+ }
+ else
+ {
+ val.append( " -- Cannot be resolved" );
+ if ( bootDel )
+ {
+ val.append( " and overwritten by Boot Delegation" );
+ }
+ val.append( "</span>" );
+ }
+
+ val.append( "<br />" );
+ }
+
+
+ // returns true if the package is listed in the bootdelegation property
+ private boolean isBootDelegated( String pkgName )
+ {
+
+ // bootdelegation analysis from Apache Felix R4SearchPolicyCore
+
+ // Only consider delegation if we have a package name, since
+ // we don't want to promote the default package. The spec does
+ // not take a stand on this issue.
+ if ( pkgName.length() > 0 )
+ {
+
+ // Delegate any packages listed in the boot delegation
+ // property to the parent class loader.
+ for ( int i = 0; i < bootPkgs.length; i++ )
+ {
+
+ // A wildcarded boot delegation package will be in the form of
+ // "foo.", so if the package is wildcarded do a startsWith() or
+ // a regionMatches() to ignore the trailing "." to determine if
+ // the request should be delegated to the parent class loader.
+ // If the package is not wildcarded, then simply do an equals()
+ // test to see if the request should be delegated to the parent
+ // class loader.
+ if ( ( bootPkgWildcards[i] && ( pkgName.startsWith( bootPkgs[i] ) || bootPkgs[i].regionMatches( 0,
+ pkgName, 0, pkgName.length() ) ) )
+ || ( !bootPkgWildcards[i] && bootPkgs[i].equals( pkgName ) ) )
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+
+ private R4Export toR4Export( ExportedPackage export )
+ {
+ R4Attribute version = new R4Attribute( Constants.VERSION_ATTRIBUTE, export.getVersion().toString(), false );
+ return new R4Export( export.getName(), null, new R4Attribute[]
+ { version } );
+ }
+
+
+ private String getBundleDescriptor( Bundle bundle )
+ {
+ StringBuffer val = new StringBuffer();
+ if ( bundle.getSymbolicName() != null )
+ {
+ // list the bundle name if not null
+ val.append( bundle.getSymbolicName() );
+ val.append( " (" ).append( bundle.getBundleId() );
+ val.append( ")" );
+ }
+ else if ( bundle.getLocation() != null )
+ {
+ // otherwise try the location
+ val.append( bundle.getLocation() );
+ val.append( " (" ).append( bundle.getBundleId() );
+ val.append( ")" );
+ }
+ else
+ {
+ // fallback to just the bundle id
+ // only append the bundle
+ val.append( bundle.getBundleId() );
+ }
+ return val.toString();
+ }
+
// ---------- inner classes ------------------------------------------------
private static final Comparator BUNDLE_NAME_COMPARATOR = new Comparator()
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/InstallAction.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/InstallAction.java
index b839947..6b0c658 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/InstallAction.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/InstallAction.java
@@ -29,7 +29,7 @@
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
-import org.apache.felix.webconsole.internal.Util;
+import org.apache.felix.webconsole.AbstractWebConsolePlugin;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
@@ -74,7 +74,7 @@
{
// get the uploaded data
- Map params = ( Map ) request.getAttribute( Util.ATTR_FILEUPLOAD );
+ Map params = ( Map ) request.getAttribute( AbstractWebConsolePlugin.ATTR_FILEUPLOAD );
if ( params == null )
{
return true;
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/UpdateAction.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/UpdateAction.java
index 58e1284..96ec646 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/UpdateAction.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/UpdateAction.java
@@ -42,9 +42,9 @@
private ServiceTracker installerService;
- public void setBundleContext( BundleContext bundleContext )
+ public void activate( BundleContext bundleContext )
{
- super.setBundleContext( bundleContext );
+ super.activate( bundleContext );
installerService = new ServiceTracker( bundleContext, INSTALLER_SERVICE_NAME, null );
installerService.open();
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/AbstractObrPlugin.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/AbstractObrPlugin.java
index 0252f02..450d848 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/AbstractObrPlugin.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/AbstractObrPlugin.java
@@ -20,7 +20,7 @@
import org.apache.felix.webconsole.internal.BaseManagementPlugin;
-import org.osgi.framework.BundleContext;
+import org.osgi.service.obr.RepositoryAdmin;
import org.osgi.util.tracker.ServiceTracker;
@@ -28,33 +28,27 @@
{
// track the optional installer service manually
- private ServiceTracker installerService;
+ private ServiceTracker repositoryAdmin;
- public void setBundleContext( BundleContext bundleContext )
+ protected RepositoryAdmin getRepositoryAdmin()
{
- super.setBundleContext( bundleContext );
-
- }
- /*
- protected InstallerService getInstallerService() {
- if (installerService == null) {
- try {
- installerService = new ServiceTracker(getBundleContext(),
- InstallerService.class.getName(), null);
- installerService.open();
- } catch (Throwable t) {
- // missing InstallerService class ??
- return null;
- }
-
+ if ( repositoryAdmin == null )
+ {
+ try
+ {
+ repositoryAdmin = new ServiceTracker( getBundleContext(), RepositoryAdmin.class.getName(), null );
+ repositoryAdmin.open();
+ }
+ catch ( Throwable t )
+ {
+ // missing InstallerService class ??
+ return null;
}
- return (InstallerService) installerService.getService();
}
- protected BundleRepositoryAdmin getBundleRepositoryAdmin() {
- InstallerService is = getInstallerService();
- return (is != null) ? is.getBundleRepositoryAdmin() : null;
- }*/
+ return ( RepositoryAdmin ) repositoryAdmin.getService();
+ }
+
}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/BundleRepositoryRender.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/BundleRepositoryRender.java
index cdd97ab..222c368 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/BundleRepositoryRender.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/BundleRepositoryRender.java
@@ -17,12 +17,23 @@
package org.apache.felix.webconsole.internal.obr;
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Date;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
import java.util.StringTokenizer;
+import java.util.TreeSet;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
import org.apache.felix.webconsole.Render;
import org.apache.felix.webconsole.internal.Util;
@@ -30,9 +41,12 @@
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.Version;
+import org.osgi.service.obr.Repository;
+import org.osgi.service.obr.RepositoryAdmin;
+import org.osgi.service.obr.Resource;
-public abstract class BundleRepositoryRender extends AbstractObrPlugin implements Render
+public class BundleRepositoryRender extends AbstractObrPlugin implements Render
{
public static final String NAME = "bundlerepo";
@@ -48,9 +62,9 @@
private String[] repoURLs;
- public void setBundleContext( BundleContext bundleContext )
+ public void activate( BundleContext bundleContext )
{
- super.setBundleContext( bundleContext );
+ super.activate( bundleContext );
String urlStr = bundleContext.getProperty( REPOSITORY_PROPERTY );
List urlList = new ArrayList();
@@ -80,77 +94,83 @@
}
- /*
- public void render(HttpServletRequest request, HttpServletResponse response)
- throws IOException {
+ public void render( HttpServletRequest request, HttpServletResponse response ) throws IOException
+ {
- PrintWriter pw = response.getWriter();
- this.header(pw);
+ PrintWriter pw = response.getWriter();
+ this.header( pw );
- Iterator<?> repos;
- BundleRepositoryAdmin repoAdmin = getBundleRepositoryAdmin();
- if (repoAdmin != null) {
- repos = repoAdmin.getRepositories();
- } else {
- repos = Collections.emptyList().iterator();
- }
-
- Set<String> activeURLs = new HashSet<String>();
- if (!repos.hasNext()) {
- pw.println("<tr class='content'>");
- pw.println("<td class='content' colspan='4'>No Active Repositories</td>");
- pw.println("</tr>");
- } else {
- while (repos.hasNext()) {
- Repository repo = (Repository) repos.next();
-
- activeURLs.add(repo.getURL().toString());
-
- pw.println("<tr class='content'>");
- pw.println("<td class='content'>" + repo.getName() + "</td>");
- pw.println("<td class='content'>" + repo.getURL() + "</td>");
- pw.println("<td class='content'>"
- + new Date(repo.getLastModified()) + "</td>");
- pw.println("<td class='content'>");
- pw.println("<form>");
- pw.println("<input type='hidden' name='" + Util.PARAM_ACTION
- + "' value='" + RefreshRepoAction.NAME + "'>");
- pw.println("<input type='hidden' name='"
- + RefreshRepoAction.PARAM_REPO + "' value='"
- + repo.getURL() + "'>");
- pw.println("<input class='submit' type='submit' value='Refresh'>");
- pw.println("</form>");
- pw.println("</td>");
- pw.println("</tr>");
- }
- }
-
- // list any repositories configured but not active
- for (int i = 0; i < this.repoURLs.length; i++) {
- if (!activeURLs.contains(this.repoURLs[i])) {
- pw.println("<tr class='content'>");
- pw.println("<td class='content'>-</td>");
- pw.println("<td class='content'>" + this.repoURLs[i] + "</td>");
- pw.println("<td class='content'>[inactive, click Refresh to activate]</td>");
- pw.println("<td class='content'>");
- pw.println("<form>");
- pw.println("<input type='hidden' name='" + Util.PARAM_ACTION
- + "' value='" + RefreshRepoAction.NAME + "'>");
- pw.println("<input type='hidden' name='"
- + RefreshRepoAction.PARAM_REPO + "' value='"
- + this.repoURLs[i] + "'>");
- pw.println("<input class='submit' type='submit' value='Refresh'>");
- pw.println("</form>");
- pw.println("</td>");
- pw.println("</tr>");
- }
- }
-
- this.footer(pw);
-
- this.listResources(pw);
+ RepositoryAdmin repoAdmin = getRepositoryAdmin();
+ Repository[] repos;
+ if ( repoAdmin != null )
+ {
+ repos = repoAdmin.listRepositories();
}
- */
+ else
+ {
+ repos = null;
+ }
+
+ Set activeURLs = new HashSet();
+ if ( repos == null || repos.length == 0 )
+ {
+ pw.println( "<tr class='content'>" );
+ pw.println( "<td class='content' colspan='4'>No Active Repositories</td>" );
+ pw.println( "</tr>" );
+ }
+ else
+ {
+ for ( int i = 0; i < repos.length; i++ )
+ {
+ Repository repo = repos[i];
+
+ activeURLs.add( repo.getURL().toString() );
+
+ pw.println( "<tr class='content'>" );
+ pw.println( "<td class='content'>" + repo.getName() + "</td>" );
+ pw.println( "<td class='content'>" + repo.getURL() + "</td>" );
+ pw.println( "<td class='content'>" + new Date( repo.getLastModified() ) + "</td>" );
+ pw.println( "<td class='content'>" );
+ pw.println( "<form>" );
+ pw.println( "<input type='hidden' name='" + Util.PARAM_ACTION + "' value='" + RefreshRepoAction.NAME
+ + "'>" );
+ pw.println( "<input type='hidden' name='" + RefreshRepoAction.PARAM_REPO + "' value='" + repo.getURL()
+ + "'>" );
+ pw.println( "<input class='submit' type='submit' value='Refresh'>" );
+ pw.println( "</form>" );
+ pw.println( "</td>" );
+ pw.println( "</tr>" );
+ }
+ }
+
+ // list any repositories configured but not active
+ for ( int i = 0; i < this.repoURLs.length; i++ )
+ {
+ if ( !activeURLs.contains( this.repoURLs[i] ) )
+ {
+ pw.println( "<tr class='content'>" );
+ pw.println( "<td class='content'>-</td>" );
+ pw.println( "<td class='content'>" + this.repoURLs[i] + "</td>" );
+ pw.println( "<td class='content'>[inactive, click Refresh to activate]</td>" );
+ pw.println( "<td class='content'>" );
+ pw.println( "<form>" );
+ pw.println( "<input type='hidden' name='" + Util.PARAM_ACTION + "' value='" + RefreshRepoAction.NAME
+ + "'>" );
+ pw.println( "<input type='hidden' name='" + RefreshRepoAction.PARAM_REPO + "' value='"
+ + this.repoURLs[i] + "'>" );
+ pw.println( "<input class='submit' type='submit' value='Refresh'>" );
+ pw.println( "</form>" );
+ pw.println( "</td>" );
+ pw.println( "</tr>" );
+ }
+ }
+
+ this.footer( pw );
+
+ this.listResources( pw, repos );
+ }
+
+
private void header( PrintWriter pw )
{
pw.println( "<table class='content' cellpadding='0' cellspacing='0' width='100%'>" );
@@ -194,72 +214,86 @@
}
- /*
- private void listResources(PrintWriter pw) {
- InstallerService is = getInstallerService();
- if (is == null) {
- return;
+ private void listResources( PrintWriter pw, Repository[] repos )
+ {
+
+ Map bundles = this.getBundles();
+
+ SortedSet resSet = new TreeSet( new Comparator()
+ {
+ public int compare( Object arg0, Object arg1 )
+ {
+ return compare( ( Resource ) arg0, ( Resource ) arg1 );
}
- Map<String, Version> bundles = this.getBundles();
- Iterator<?> resources = is.getBundleRepositoryAdmin().getResources();
- SortedSet<Resource> resSet = new TreeSet<Resource>(
- new Comparator<Resource>() {
- public int compare(Resource o1, Resource o2) {
- if (o1 == o2 || o1.equals(o2)) {
- return 0;
- }
+ public int compare( Resource o1, Resource o2 )
+ {
+ if ( o1 == o2 || o1.equals( o2 ) )
+ {
+ return 0;
+ }
- if (o1.getPresentationName().equals(
- o2.getPresentationName())) {
- return o1.getVersion().compareTo(o2.getVersion());
- }
+ if ( o1.getPresentationName().equals( o2.getPresentationName() ) )
+ {
+ return o1.getVersion().compareTo( o2.getVersion() );
+ }
- return o1.getPresentationName().compareTo(
- o2.getPresentationName());
- }
- });
+ return o1.getPresentationName().compareTo( o2.getPresentationName() );
+ }
+ } );
- while (resources.hasNext()) {
- Resource res = (Resource) resources.next();
- Version ver = bundles.get(res.getSymbolicName());
- if (ver == null || ver.compareTo(res.getVersion()) < 0) {
- resSet.add(res);
+ for ( int i = 0; i < repos.length; i++ )
+ {
+ Resource[] resources = repos[i].getResources();
+ for ( int j = 0; j < resources.length; j++ )
+ {
+ Resource res = resources[j];
+ Version ver = ( Version ) bundles.get( res.getSymbolicName() );
+ if ( ver == null || ver.compareTo( res.getVersion() ) < 0 )
+ {
+ resSet.add( res );
}
}
-
- this.resourcesHeader(pw, !resSet.isEmpty());
-
- for (Resource resource : resSet) {
- this.printResource(pw, resource);
- }
-
- this.resourcesFooter(pw, !resSet.isEmpty());
}
- private void printResource(PrintWriter pw, Resource res) {
- pw.println("<tr class='content'>");
- pw.println("<td class='content' valign='top' align='center'><input class='checkradio' type='checkbox' name='bundle' value='"
- + res.getSymbolicName() + "," + res.getVersion() + "'></td>");
+ this.resourcesHeader( pw, !resSet.isEmpty() );
- // check whether the resource is an assembly (category name)
- String style = "";
- String[] cat = res.getCategories();
- for (int i = 0; cat != null && i < cat.length; i++) {
- if ("assembly".equals(cat[i])) {
- style = "style='font-weight:bold'";
- }
- }
- pw.println("<td class='content' " + style + ">"
- + res.getPresentationName() + " (" + res.getSymbolicName()
- + ")</td>");
- pw.println("<td class='content' " + style + " valign='top'>"
- + res.getVersion() + "</td>");
-
- pw.println("</tr>");
+ for ( Iterator ri = resSet.iterator(); ri.hasNext(); )
+ {
+ Resource resource = ( Resource ) ri.next();
+ this.printResource( pw, resource );
}
- */
+
+ this.resourcesFooter( pw, !resSet.isEmpty() );
+ }
+
+
+ private void printResource( PrintWriter pw, Resource res )
+ {
+ pw.println( "<tr class='content'>" );
+ pw
+ .println( "<td class='content' valign='top' align='center'><input class='checkradio' type='checkbox' name='bundle' value='"
+ + res.getSymbolicName() + "," + res.getVersion() + "'></td>" );
+
+ // check whether the resource is an assembly (category name)
+ String style = "";
+ String[] cat = res.getCategories();
+ for ( int i = 0; cat != null && i < cat.length; i++ )
+ {
+ if ( "assembly".equals( cat[i] ) )
+ {
+ style = "style='font-weight:bold'";
+ }
+ }
+ pw.println( "<td class='content' " + style + ">" + res.getPresentationName() + " (" + res.getSymbolicName()
+ + ")</td>" );
+ pw.println( "<td class='content' " + style + " valign='top'>" + res.getVersion() + "</td>" );
+
+ pw.println( "</tr>" );
+ }
+
+
private void resourcesButtons( PrintWriter pw )
{
pw.println( "<tr class='content'>" );
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 f5b5c6e..4b5f4e1 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
@@ -18,47 +18,38 @@
import java.io.IOException;
-import java.io.PrintWriter;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
-import java.util.List;
import java.util.Map;
-import java.util.SortedMap;
-import java.util.TreeMap;
import javax.servlet.GenericServlet;
+import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.apache.commons.fileupload.FileItem;
-import org.apache.commons.fileupload.FileUploadException;
-import org.apache.commons.fileupload.disk.DiskFileItemFactory;
-import org.apache.commons.fileupload.servlet.ServletFileUpload;
-import org.apache.commons.fileupload.servlet.ServletRequestContext;
+import org.apache.felix.webconsole.AbstractWebConsolePlugin;
import org.apache.felix.webconsole.Action;
import org.apache.felix.webconsole.Render;
-import org.apache.felix.webconsole.internal.BaseManagementPlugin;
+import org.apache.felix.webconsole.WebConsoleConstants;
+import org.apache.felix.webconsole.internal.Logger;
+import org.apache.felix.webconsole.internal.OsgiManagerPlugin;
import org.apache.felix.webconsole.internal.Util;
import org.apache.felix.webconsole.internal.compendium.AjaxConfigManagerAction;
import org.apache.felix.webconsole.internal.compendium.ComponentConfigurationPrinter;
import org.apache.felix.webconsole.internal.compendium.ComponentRenderAction;
import org.apache.felix.webconsole.internal.compendium.ConfigManager;
-import org.apache.felix.webconsole.internal.core.AjaxBundleDetailsAction;
import org.apache.felix.webconsole.internal.core.BundleListRender;
import org.apache.felix.webconsole.internal.core.InstallAction;
import org.apache.felix.webconsole.internal.core.RefreshPackagesAction;
import org.apache.felix.webconsole.internal.core.SetStartLevelAction;
-import org.apache.felix.webconsole.internal.core.StartAction;
-import org.apache.felix.webconsole.internal.core.StopAction;
-import org.apache.felix.webconsole.internal.core.UninstallAction;
-import org.apache.felix.webconsole.internal.core.UpdateAction;
import org.apache.felix.webconsole.internal.misc.ConfigurationRender;
+import org.apache.felix.webconsole.internal.obr.BundleRepositoryRender;
import org.apache.felix.webconsole.internal.system.GCAction;
import org.apache.felix.webconsole.internal.system.ShutdownAction;
import org.apache.felix.webconsole.internal.system.ShutdownRender;
@@ -84,6 +75,8 @@
/** Pseudo class version ID to keep the IDE quite. */
private static final long serialVersionUID = 1L;
+ public static final String ATTR_LABEL_MAP = OsgiManager.class.getName() + ".labelMap";
+
/**
* The name and value of a parameter which will prevent redirection to a
* render after the action has been executed (value is "_noredir_"). This
@@ -122,12 +115,17 @@
*/
private static final String DEFAULT_MANAGER_ROOT = "/system/console";
+ // private static final Class[] PLUGIN_CLASSES =
+ // { AjaxConfigManagerAction.class, ComponentConfigurationPrinter.class, ComponentRenderAction.class,
+ // ConfigManager.class, AjaxBundleDetailsAction.class, BundleListRender.class, InstallAction.class,
+ // RefreshPackagesAction.class, SetStartLevelAction.class, StartAction.class, StopAction.class,
+ // UninstallAction.class, UpdateAction.class, ConfigurationRender.class, GCAction.class, ShutdownAction.class,
+ // ShutdownRender.class, VMStatRender.class };
private static final Class[] PLUGIN_CLASSES =
{ AjaxConfigManagerAction.class, ComponentConfigurationPrinter.class, ComponentRenderAction.class,
- ConfigManager.class, AjaxBundleDetailsAction.class, BundleListRender.class, InstallAction.class,
- RefreshPackagesAction.class, SetStartLevelAction.class, StartAction.class, StopAction.class,
- UninstallAction.class, UpdateAction.class, ConfigurationRender.class, GCAction.class, ShutdownAction.class,
- ShutdownRender.class, VMStatRender.class };
+ ConfigManager.class, BundleListRender.class, InstallAction.class, RefreshPackagesAction.class,
+ SetStartLevelAction.class, ConfigurationRender.class, GCAction.class, ShutdownAction.class,
+ ShutdownRender.class, VMStatRender.class, BundleRepositoryRender.class };
private BundleContext bundleContext;
@@ -141,13 +139,17 @@
private ServiceTracker rendersTracker;
+ private ServiceTracker pluginsTracker;
+
private ServiceRegistration configurationListener;
+ private Map plugins = new HashMap();
+
+ private Map labelMap = new HashMap();
+
private Map operations = new HashMap();
- private SortedMap renders = new TreeMap();
-
- private Render defaultRender;
+ private Servlet defaultRender;
private String defaultRenderName;
@@ -178,6 +180,8 @@
operationsTracker.open();
rendersTracker = new RenderServiceTracker( this );
rendersTracker.open();
+ pluginsTracker = new PluginServiceTracker( this );
+ pluginsTracker.open();
httpServiceTracker = new HttpServiceTracker( this );
httpServiceTracker.open();
@@ -187,18 +191,25 @@
try
{
Object plugin = pluginClass.newInstance();
- if ( plugin instanceof BaseManagementPlugin )
+ if ( plugin instanceof OsgiManagerPlugin )
{
- ( ( BaseManagementPlugin ) plugin ).setBundleContext( bundleContext );
- ( ( BaseManagementPlugin ) plugin ).setLogger( log );
+ ( ( OsgiManagerPlugin ) plugin ).activate( bundleContext );
}
- if ( plugin instanceof Action )
+ if ( plugin instanceof AbstractWebConsolePlugin )
{
- bindOperation( ( Action ) plugin );
+ AbstractWebConsolePlugin amp = ( AbstractWebConsolePlugin ) plugin;
+ bindServlet( amp.getLabel(), amp );
}
- if ( plugin instanceof Render )
+ else
{
- bindRender( ( Render ) plugin );
+ if ( plugin instanceof Action )
+ {
+ bindOperation( ( Action ) plugin );
+ }
+ if ( plugin instanceof Render )
+ {
+ bindRender( ( Render ) plugin );
+ }
}
}
catch ( Throwable t )
@@ -230,16 +241,33 @@
rendersTracker = null;
}
+ if ( pluginsTracker != null )
+ {
+ pluginsTracker.close();
+ pluginsTracker = null;
+ }
+
if ( httpServiceTracker != null )
{
httpServiceTracker.close();
httpServiceTracker = null;
}
+ // deactivate any remaining plugins
+ for ( Iterator pi = plugins.values().iterator(); pi.hasNext(); )
+ {
+ Object plugin = pi.next();
+ if ( plugin instanceof OsgiManagerPlugin )
+ {
+ ( ( OsgiManagerPlugin ) plugin ).deactivate();
+ }
+ }
+
// simply remove all operations, we should not be used anymore
this.defaultRender = null;
+ this.plugins.clear();
+ this.labelMap.clear();
this.operations.clear();
- this.renders.clear();
if ( log != null )
{
@@ -263,38 +291,45 @@
}
// check whether we are not at .../{webManagerRoot}
- if ( request.getRequestURI().endsWith( this.webManagerRoot ) )
+ if ( request.getPathInfo() == null )
{
- response.sendRedirect( request.getRequestURI() + "/" + this.defaultRender.getName() );
+ String path = request.getRequestURI();
+ if ( !path.endsWith( "/" ) )
+ {
+ path = path.concat( "/" );
+ }
+ path = path.concat( defaultRenderName );
+ response.sendRedirect( path );
return;
}
- // otherwise we render the response
- Render render = this.getRender( request );
- if ( render == null )
+ String label = request.getPathInfo();
+ int slash = label.indexOf( "/", 1 );
+ if ( slash < 2 )
+ {
+ slash = label.length();
+ }
+
+ label = label.substring( 1, slash );
+ Servlet plugin = ( Servlet ) plugins.get( label );
+ if ( plugin != null )
+ {
+ req.setAttribute( ATTR_LABEL_MAP, labelMap );
+ plugin.service( req, res );
+ }
+ else
{
response.sendError( HttpServletResponse.SC_NOT_FOUND );
return;
}
- String current = render.getName();
- boolean disabled = false; // should take action==shutdown into
- // account:
- // Boolean.valueOf(request.getParameter("disabled")).booleanValue();
-
- PrintWriter pw = Util.startHtml( response, render.getLabel() );
- Util.navigation( pw, this.renders.values(), current, disabled );
-
- render.render( request, response );
-
- Util.endHhtml( pw );
}
protected boolean handleAction( HttpServletRequest req, HttpServletResponse resp ) throws IOException
{
// check action
- String actionName = this.getParameter( req, Util.PARAM_ACTION );
+ String actionName = AbstractWebConsolePlugin.getParameter( req, Util.PARAM_ACTION );
if ( actionName != null )
{
Action action = ( Action ) this.operations.get( actionName );
@@ -315,7 +350,8 @@
}
// maybe overwrite redirect
- if ( PARAM_NO_REDIRECT_AFTER_ACTION.equals( getParameter( req, PARAM_NO_REDIRECT_AFTER_ACTION ) ) )
+ if ( PARAM_NO_REDIRECT_AFTER_ACTION.equals( AbstractWebConsolePlugin.getParameter( req,
+ PARAM_NO_REDIRECT_AFTER_ACTION ) ) )
{
resp.setStatus( HttpServletResponse.SC_OK );
resp.setContentType( "text/html" );
@@ -341,98 +377,6 @@
}
- protected Render getRender( HttpServletRequest request )
- {
-
- String page = request.getRequestURI();
-
- // remove trailing slashes
- while ( page.endsWith( "/" ) )
- {
- page = page.substring( 0, page.length() - 1 );
- }
-
- // take last part of the name
- int lastSlash = page.lastIndexOf( '/' );
- if ( lastSlash >= 0 )
- {
- page = page.substring( lastSlash + 1 );
- }
-
- Render render = ( Render ) this.renders.get( page );
- return ( render == null ) ? this.defaultRender : render;
- }
-
-
- private String getParameter( HttpServletRequest request, String name )
- {
- // just get the parameter if not a multipart/form-data POST
- if ( !ServletFileUpload.isMultipartContent( new ServletRequestContext( request ) ) )
- {
- return request.getParameter( name );
- }
-
- // check, whether we alread have the parameters
- Map params = ( Map ) request.getAttribute( Util.ATTR_FILEUPLOAD );
- if ( params == null )
- {
- // parameters not read yet, read now
- // Create a factory for disk-based file items
- DiskFileItemFactory factory = new DiskFileItemFactory();
- factory.setSizeThreshold( 256000 );
-
- // Create a new file upload handler
- ServletFileUpload upload = new ServletFileUpload( factory );
- upload.setSizeMax( -1 );
-
- // Parse the request
- params = new HashMap();
- try
- {
- List items = upload.parseRequest( request );
- for ( Iterator fiter = items.iterator(); fiter.hasNext(); )
- {
- FileItem fi = ( FileItem ) fiter.next();
- FileItem[] current = ( FileItem[] ) params.get( fi.getFieldName() );
- if ( current == null )
- {
- current = new FileItem[]
- { fi };
- }
- else
- {
- FileItem[] newCurrent = new FileItem[current.length + 1];
- System.arraycopy( current, 0, newCurrent, 0, current.length );
- newCurrent[current.length] = fi;
- current = newCurrent;
- }
- params.put( fi.getFieldName(), current );
- }
- }
- catch ( FileUploadException fue )
- {
- // TODO: log
- }
- request.setAttribute( Util.ATTR_FILEUPLOAD, params );
- }
-
- FileItem[] param = ( FileItem[] ) params.get( name );
- if ( param != null )
- {
- for ( int i = 0; i < param.length; i++ )
- {
- if ( param[i].isFormField() )
- {
- return param[i].getString();
- }
- }
- }
-
- // no valid string parameter, fail
- return null;
- }
-
-
BundleContext getBundleContext()
{
return bundleContext;
@@ -543,6 +487,50 @@
}
}
+ private static class PluginServiceTracker extends ServiceTracker
+ {
+
+ private final OsgiManager osgiManager;
+
+
+ PluginServiceTracker( OsgiManager osgiManager )
+ {
+ super( osgiManager.getBundleContext(), WebConsoleConstants.SERVICE_NAME, null );
+ this.osgiManager = osgiManager;
+ }
+
+
+ public Object addingService( ServiceReference reference )
+ {
+ Object label = reference.getProperty( WebConsoleConstants.PLUGIN_LABEL );
+ if ( label instanceof String )
+ {
+ Object operation = super.addingService( reference );
+ if ( operation instanceof Servlet )
+ {
+ // TODO: check reference properties !!
+ osgiManager.bindServlet( ( String ) label, ( Servlet ) operation );
+ }
+ return operation;
+ }
+
+ return null;
+ }
+
+
+ public void removedService( ServiceReference reference, Object service )
+ {
+ Object label = reference.getProperty( WebConsoleConstants.PLUGIN_LABEL );
+ if ( label instanceof String )
+ {
+ // TODO: check reference properties !!
+ osgiManager.unbindServlet( ( String ) label );
+ }
+
+ super.removedService( reference, service );
+ }
+ }
+
protected synchronized void bindHttpService( HttpService httpService )
{
@@ -585,48 +573,85 @@
}
+ protected void bindServlet( String label, Servlet servlet )
+ {
+ try
+ {
+ servlet.init( getServletConfig() );
+ plugins.put( label, servlet );
+
+ if ( servlet instanceof GenericServlet )
+ {
+ String title = ( ( GenericServlet ) servlet ).getServletName();
+ if ( title != null )
+ {
+ labelMap.put( label, title );
+ }
+ }
+
+ if ( this.defaultRender == null )
+ {
+ this.defaultRender = servlet;
+ }
+ else if ( label.equals( this.defaultRenderName ) )
+ {
+ this.defaultRender = servlet;
+ }
+ }
+ catch ( ServletException se )
+ {
+ // TODO: log
+ }
+ }
+
+
+ protected void unbindServlet( String label )
+ {
+ Servlet servlet = ( Servlet ) plugins.remove( label );
+ if ( servlet != null )
+ {
+ labelMap.remove( label );
+
+ if ( this.defaultRender == servlet )
+ {
+ if ( this.plugins.isEmpty() )
+ {
+ this.defaultRender = null;
+ }
+ else
+ {
+ this.defaultRender = ( Servlet ) plugins.values().iterator().next();
+ }
+ }
+
+ servlet.destroy();
+ }
+ }
+
+
protected void bindOperation( Action operation )
{
- this.operations.put( operation.getName(), operation );
+ operations.put( operation.getName(), operation );
}
protected void unbindOperation( Action operation )
{
- this.operations.remove( operation.getName() );
+ operations.remove( operation.getName() );
}
protected void bindRender( Render render )
{
- this.renders.put( render.getName(), render );
-
- if ( this.defaultRender == null )
- {
- this.defaultRender = render;
- }
- else if ( render.getName().equals( this.defaultRenderName ) )
- {
- this.defaultRender = render;
- }
+ RenderBridge bridge = new RenderBridge( render );
+ bridge.activate( getBundleContext() );
+ bindServlet( render.getName(), bridge );
}
protected void unbindRender( Render render )
{
- this.renders.remove( render.getName() );
-
- if ( this.defaultRender == render )
- {
- if ( this.renders.isEmpty() )
- {
- this.defaultRender = null;
- }
- else
- {
- this.defaultRender = ( Render ) renders.values().iterator().next();
- }
- }
+ unbindServlet( render.getName() );
}
@@ -646,9 +671,9 @@
configuration = config;
defaultRenderName = ( String ) config.get( PROP_DEFAULT_RENDER );
- if ( defaultRenderName != null && renders.get( defaultRenderName ) != null )
+ if ( defaultRenderName != null && plugins.get( defaultRenderName ) != null )
{
- defaultRender = ( Render ) renders.get( defaultRenderName );
+ defaultRender = ( Servlet ) plugins.get( defaultRenderName );
}
// get the web manager root path
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/RenderBridge.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/RenderBridge.java
new file mode 100644
index 0000000..2aae84f
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/RenderBridge.java
@@ -0,0 +1,71 @@
+/*
+ * 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.servlet;
+
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.felix.webconsole.AbstractWebConsolePlugin;
+import org.apache.felix.webconsole.Render;
+
+
+public class RenderBridge extends AbstractWebConsolePlugin
+{
+
+ /** Pseudo class version ID to keep the IDE quite. */
+ private static final long serialVersionUID = 1L;
+
+ private final Render render;
+
+
+ RenderBridge( Render render )
+ {
+ this.render = render;
+ }
+
+
+ public Render getRender()
+ {
+ return render;
+ }
+
+
+ public String getTitle()
+ {
+ return render.getLabel();
+ }
+
+
+ public String getLabel()
+ {
+ return render.getName();
+ }
+
+
+ protected void renderContent( HttpServletRequest request, HttpServletResponse response ) throws ServletException,
+ IOException
+ {
+ render.render( request, response );
+ }
+
+}
diff --git a/webconsole/src/main/resources/res/ui/admin.css b/webconsole/src/main/resources/res/ui/admin.css
index 984d9a0..fdb5084 100644
--- a/webconsole/src/main/resources/res/ui/admin.css
+++ b/webconsole/src/main/resources/res/ui/admin.css
@@ -43,7 +43,7 @@
*/
margin: 0px;
padding: 5px 0 0 8px;
- font-size: 400%;
+ font-size: 300%;
font-weight: bold;
line-height: 120%;
height: 95px;
diff --git a/webconsole/src/main/resources/res/ui/bundles.js b/webconsole/src/main/resources/res/ui/bundles.js
new file mode 100644
index 0000000..537577f
--- /dev/null
+++ b/webconsole/src/main/resources/res/ui/bundles.js
@@ -0,0 +1,230 @@
+/*
+ * 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 render(/* int */ startlevel, /* Array of Bundle Object */ bundles)
+{
+
+ header();
+
+ installForm( startlevel );
+
+ document.write( "<tr class='content'>" );
+ document.write( "<td colspan='7' class='content'> </th>" );
+ document.write( "</tr>" );
+
+ tableHeader();
+
+ if ( !bundles )
+ {
+ document.write( "<tr class='content'>" );
+ document.write( "<td class='content' colspan='6'>No Bundles installed currently</td>" );
+ document.write( "</tr>" );
+ }
+ else
+ {
+ for ( var i = 0; i < bundles.length; i++ )
+ {
+ bundle( bundles[i] );
+ }
+ }
+
+ document.write( "<tr class='content'>" );
+ document.write( "<td colspan='7' class='content'> </th>" );
+ document.write( "</tr>" );
+
+ 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 theBundle = "<td class='content right'>" + bundle.bundleId + "</td>";
+ theBundle += "<td class='content'><a href='javascript:showDetails(" + 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;
+}
+
+
+function installForm( /* int */ startLevel )
+{
+ document.write( "<form method='post' enctype='multipart/form-data'>" );
+ document.write( "<tr class='content'>" );
+ document.write( "<td class='content'> </td>" );
+ document.write( "<td class='content'>" );
+ document.write( "<input type='hidden' name='action' value='install' />" );
+ document.write( "<input class='input' type='file' name='bundlefile'>" );
+ document.write( " - Start <input class='checkradio' type='checkbox' name='bundlestart' value='start'>" );
+ document.write( " - Start Level <input class='input' type='input' name='bundlestartelevel' value='" + startLevel + "' width='4'>" );
+ document.write( "</td>" );
+ document.write( "<td class='content' align='right' colspan='5' noWrap>" );
+ document.write( "<input class='submit' style='width:auto' type='submit' value='Install or Update'>" );
+ document.write( " " );
+ document.write( "<input class='submit' style='width:auto' type='submit' value='Refresh Packages' onClick='this.form.action.value=\"refreshPackages\"; return true;'>" );
+ document.write( "</td>" );
+ 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)
+{
+ 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);
+ }
+ }
+
+ // reload --- should do better and only update the bundle
+ // document.location = document.location;
+}
+
+
+function showDetails(bundleId) {
+ var span = document.getElementById('bundle' + bundleId + '_details');
+ if (span)
+ {
+ if (span.innerHTML)
+ {
+ span.innerHTML = '';
+ }
+ else
+ {
+ sendRequest('GET', "bundles/" + bundleId + ".json", displayBundleDetails);
+ }
+ }
+}
+
+
+function displayBundleDetails(obj) {
+ var span = document.getElementById('bundle' + obj.bundleId + '_details');
+ if (span)
+ {
+ span.innerHTML = bundleDetails( obj.props );
+ }
+}
+
+
+/* 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;
+}
+