FELIX-604 Add plugin to disply LICENSE and NOTICE files contained
in installed bundles
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@667359 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/LicenseServlet.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/LicenseServlet.java
new file mode 100644
index 0000000..4386279
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/LicenseServlet.java
@@ -0,0 +1,290 @@
+/*
+ * 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.misc;
+
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.felix.webconsole.AbstractWebConsolePlugin;
+import org.apache.felix.webconsole.internal.OsgiManagerPlugin;
+import org.apache.felix.webconsole.internal.Util;
+import org.json.JSONException;
+import org.json.JSONWriter;
+import org.osgi.framework.Bundle;
+import org.osgi.service.component.ComponentContext;
+
+
+/**
+ * The <code>LicenseServlet</code> TODO
+ */
+public class LicenseServlet extends AbstractWebConsolePlugin implements OsgiManagerPlugin
+{
+
+ public String getLabel()
+ {
+ return "licenses";
+ }
+
+
+ public String getTitle()
+ {
+ return "Licenses";
+ }
+
+
+ protected void renderContent( HttpServletRequest req, HttpServletResponse res ) throws IOException
+ {
+ PrintWriter pw = res.getWriter();
+
+ String appRoot = req.getContextPath() + req.getServletPath();
+ pw.println( "<link href='" + appRoot + "/res/ui/license.css' rel='stylesheet' type='text/css'>" );
+ pw.println( "<script src='" + appRoot + "/res/ui/license.js' language='JavaScript'></script>" );
+
+ Bundle[] bundles = getBundleContext().getBundles();
+ Util.sort( bundles );
+
+ Util.startScript( pw );
+ pw.print( "bundleData = " );
+ JSONWriter jw = new JSONWriter( pw );
+ try
+ {
+ jw.object();
+ for ( int i = 0; i < bundles.length; i++ )
+ {
+ Bundle bundle = bundles[i];
+ jw.key( String.valueOf( bundle.getBundleId() ) );
+
+ jw.object();
+
+ jw.key( "title" );
+ jw.value( Util.getName( bundle ) );
+
+ jw.key( "files" );
+ jw.object();
+ findResource( jw, bundle, new String[]
+ { "LICENSE", "NOTICE" } );
+ jw.endObject();
+
+ jw.endObject();
+ }
+ jw.endObject();
+ pw.println( ";" );
+ }
+ catch ( JSONException je )
+ {
+ throw new IOException( je.toString() );
+ }
+ Util.endScript( pw );
+
+ pw.println( "<div id='licenseContent'>" );
+
+ pw.println( "<div id='licenseLeft'>" );
+ for ( int i = 0; i < bundles.length; i++ )
+ {
+ Bundle bundle = bundles[i];
+ String link = "displayBundle( \"" + bundle.getBundleId() + "\" );";
+ pw.println( "<a href='javascript:" + link + "'>" + Util.getName( bundle ) + "</a><br />" );
+
+ }
+ pw.println( "</div>" );
+
+ pw.println( "<div id='licenseRight'>" );
+ pw.println( "<div id='licenseButtons' class='licenseButtons'> </div>" );
+ pw.println( "<br />" );
+ pw.println( "<div id='licenseDetails' class='licenseDetails'> </div>" );
+ pw.println( "</div>" );
+
+ pw.println( "<div id='licenseClear'> </div>" );
+
+ pw.println( "</div>" ); // licenseContent
+
+ Util.startScript( pw );
+ pw.println( "displayBundle( '0' );" );
+ Util.endScript( pw );
+ }
+
+
+ private String getName( String path )
+ {
+ return path.substring( path.lastIndexOf( '/' ) + 1 );
+ }
+
+
+ private void findResource( JSONWriter jw, Bundle bundle, String[] patterns ) throws IOException, JSONException
+ {
+ jw.key( "Bundle Resources" ); // aka the bundle files
+ jw.array();
+ for ( int i = 0; i < patterns.length; i++ )
+ {
+ Enumeration entries = bundle.findEntries( "/", patterns[i] + "*", true );
+ if ( entries != null )
+ {
+ while ( entries.hasMoreElements() )
+ {
+ URL url = ( URL ) entries.nextElement();
+ jw.object();
+ jw.key( "url" );
+ jw.value( getName( url.getPath() ) );
+ jw.key( "data" );
+ jw.value( readResource( url ) );
+ jw.endObject();
+ }
+ }
+ }
+ jw.endArray();
+
+ Enumeration entries = bundle.findEntries( "/", "*.jar", true );
+ if ( entries != null )
+ {
+ while ( entries.hasMoreElements() )
+ {
+ URL url = ( URL ) entries.nextElement();
+
+ jw.key( "Embedded " + getName( url.getPath() ) );
+ jw.array();
+
+ for ( int i = 0; i < patterns.length; i++ )
+ {
+ String pattern = ".*/" + patterns[i] + "[^/]*$";
+
+ InputStream ins = null;
+ try
+ {
+ ins = url.openStream();
+ ZipInputStream zin = new ZipInputStream( ins );
+ ZipEntry zentry = zin.getNextEntry();
+ while ( zentry != null )
+ {
+ String name = zentry.getName();
+ if ( !name.endsWith( "/" ) && "/".concat( name ).matches( pattern ) )
+ {
+ jw.object();
+ jw.key( "url" );
+ jw.value( getName( name ) );
+ jw.key( "data" );
+ jw.value( readResource( new FilterInputStream( zin )
+ {
+ public void close()
+ {
+ // nothing for now
+ }
+ } ) );
+ jw.endObject();
+ }
+
+ zentry = zin.getNextEntry();
+ }
+ }
+ finally
+ {
+ if ( ins != null )
+ {
+ try
+ {
+ ins.close();
+ }
+ catch ( IOException ignore )
+ {
+ }
+ }
+ }
+ }
+
+ jw.endArray();
+ }
+ }
+ }
+
+
+ private String getResource( Bundle bundle, String[] path ) throws IOException
+ {
+ for ( int i = 0; i < path.length; i++ )
+ {
+ URL resource = bundle.getResource( path[i] );
+ if ( resource != null )
+ {
+ return readResource( resource );
+ }
+ }
+
+ return null;
+ }
+
+
+ private String readResource( URL resource ) throws IOException
+ {
+ return readResource( resource.openStream() );
+ }
+
+
+ private String readResource( InputStream resource ) throws IOException
+ {
+ Reader r = null;
+ StringBuffer buffer = new StringBuffer();
+ try
+ {
+ char[] buf = new char[1024];
+ r = new InputStreamReader( resource, "ISO-8859-1" );
+ int num;
+ while ( ( num = r.read( buf ) ) >= 0 )
+ {
+ buffer.append( buf, 0, num );
+ }
+ }
+ finally
+ {
+ if ( r != null )
+ {
+ try
+ {
+ r.close();
+ }
+ catch ( IOException ignore )
+ {
+ }
+ }
+ }
+ return buffer.toString();
+ }
+
+
+ protected void activate( ComponentContext context )
+ {
+ activate( context.getBundleContext() );
+ }
+
+
+ protected void deactivate( ComponentContext context )
+ {
+ deactivate();
+ }
+
+}
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 f0b9933..170f876 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
@@ -48,6 +48,7 @@
import org.apache.felix.webconsole.internal.core.InstallAction;
import org.apache.felix.webconsole.internal.core.SetStartLevelAction;
import org.apache.felix.webconsole.internal.misc.ConfigurationRender;
+import org.apache.felix.webconsole.internal.misc.LicenseServlet;
import org.apache.felix.webconsole.internal.obr.BundleRepositoryRender;
import org.apache.felix.webconsole.internal.system.GCAction;
import org.apache.felix.webconsole.internal.system.ShutdownAction;
@@ -126,7 +127,7 @@
{ AjaxConfigManagerAction.class, ComponentConfigurationPrinter.class, ComponentRenderAction.class,
ConfigManager.class, BundlesServlet.class, InstallAction.class, SetStartLevelAction.class,
ConfigurationRender.class, GCAction.class, ShutdownAction.class, ShutdownRender.class, VMStatRender.class,
- BundleRepositoryRender.class };
+ BundleRepositoryRender.class, LicenseServlet.class };
private BundleContext bundleContext;
diff --git a/webconsole/src/main/resources/res/ui/license.css b/webconsole/src/main/resources/res/ui/license.css
new file mode 100644
index 0000000..f371c35
--- /dev/null
+++ b/webconsole/src/main/resources/res/ui/license.css
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */ /* CSS Document */
+#licenseContent {
+ position: relative;
+ margin-top: 25px; padding : 5px;
+ width: 100%;
+ padding: 5px;
+}
+
+#licenseLeft {
+ width: 300px;
+ border-right: 1px black solid;
+}
+
+#licenseRight {
+ position: absolute;
+ top: 5px;
+ left: 305px;
+ padding: 5px;
+ width: 650px;
+ border-left: 1px black solid;
+}
+
+#licenseClear {
+ clear: both;
+ height: 1px;
+}
+
+.licenseButtons {
+ padding: 5px;
+}
+
+.licenseDetails {
+ padding: 5px;
+}
\ No newline at end of file
diff --git a/webconsole/src/main/resources/res/ui/license.js b/webconsole/src/main/resources/res/ui/license.js
new file mode 100644
index 0000000..a8cbb1f
--- /dev/null
+++ b/webconsole/src/main/resources/res/ui/license.js
@@ -0,0 +1,82 @@
+/*
+ * 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 displayBundle(/* String */ bundleId)
+{
+ var theBundleData = bundleData[bundleId];
+ if (!theBundleData)
+ {
+ return;
+ }
+
+ var title = theBundleData.title;
+
+ var licenseButtons = document.getElementById('licenseButtons');
+ if (licenseButtons) {
+
+ var innerHTML = "";
+ for (var name in theBundleData.files)
+ {
+ var entry = theBundleData.files[name];
+ var buttons = "";
+ for (var idx in entry)
+ {
+ var descr = entry[idx];
+ buttons += "<a href='javascript:displayFile(\"" + bundleId + "\", \"" + name + "\", " + idx + ");'"
+ + " >" + descr.url + "</a> ";
+ }
+ if (buttons)
+ {
+ innerHTML += name + ": " + buttons + "<br />";
+ }
+ }
+
+ if (!innerHTML)
+ {
+ innerHTML = "<em>The Bundle contains neither LICENSE nor NOTICE files</em>";
+ }
+
+ licenseButtons.innerHTML = "<h1>" + title + "</h1>" + innerHTML;
+ }
+
+ var licenseDetails = document.getElementById('licenseDetails');
+ if (licenseDetails)
+ {
+ licenseDetails.innerHTML = "";
+ }
+}
+
+function displayFile ( /* String */ bundleId, /* String */ name, /* int */ idx )
+{
+ var theBundleData = bundleData[bundleId];
+ if (!theBundleData)
+ {
+ return;
+ }
+
+ var file = theBundleData.files[name][idx];
+ if (!file)
+ {
+ return;
+ }
+
+ var licenseDetails = document.getElementById('licenseDetails');
+ if (licenseDetails)
+ {
+ licenseDetails.innerHTML = "<h3>" + name + ": " + file.url + "</h3><pre>" + file.data + "</pre>";
+ }
+}