Resolved FELIX-1441 /Search manifest entries of bundles/
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@925123 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
index b868234..c8bb670 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
@@ -64,6 +64,8 @@
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.Version;
@@ -88,6 +90,9 @@
private static final String TITLE = "%bundles.pluginTitle";
private static final String CSS[] = { "/res/ui/bundles.css" };
+ // an LDAP filter, that is used to search manifest headers, see FELIX-1441
+ private static final String FILTER_PARAM = "filter";
+
private static final String FIELD_STARTLEVEL = "bundlestartlevel";
private static final String FIELD_START = "bundlestart";
@@ -175,7 +180,7 @@
try
{
StringWriter w = new StringWriter();
- writeJSON( w, null, null, null, true, Locale.ENGLISH );
+ writeJSON( w, null, null, null, true, Locale.ENGLISH, null );
String jsonString = w.toString();
JSONObject json = new JSONObject( jsonString );
@@ -255,7 +260,14 @@
{
final String pluginRoot = ( String ) request.getAttribute( WebConsoleConstants.ATTR_PLUGIN_ROOT );
final String servicesRoot = getServicesRoot( request );
- this.renderJSON(response, reqInfo.bundle, pluginRoot, servicesRoot, request.getLocale());
+ try
+ {
+ this.renderJSON(response, reqInfo.bundle, pluginRoot, servicesRoot, request.getLocale(), request.getParameter(FILTER_PARAM) );
+ }
+ catch (InvalidSyntaxException e)
+ {
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Invalid LDAP filter specified");
+ }
// nothing more to do
return;
@@ -371,7 +383,14 @@
}
final String pluginRoot = ( String ) req.getAttribute( WebConsoleConstants.ATTR_PLUGIN_ROOT );
final String servicesRoot = getServicesRoot( req );
- this.renderJSON( resp, null, pluginRoot, servicesRoot, req.getLocale() );
+ try
+ {
+ this.renderJSON( resp, null, pluginRoot, servicesRoot, req.getLocale(), req.getParameter(FILTER_PARAM) );
+ }
+ catch (InvalidSyntaxException e)
+ {
+ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Invalid LDAP filter specified");
+ }
}
else
{
@@ -447,7 +466,7 @@
/**
* @see org.apache.felix.webconsole.AbstractWebConsolePlugin#renderContent(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
- protected void renderContent( HttpServletRequest request, HttpServletResponse response ) throws IOException
+ protected void renderContent( HttpServletRequest request, HttpServletResponse response ) throws IOException, ServletException
{
// get request info from request attribute
final RequestInfo reqInfo = getRequestInfo(request);
@@ -463,38 +482,70 @@
final String pluginRoot = ( String ) request.getAttribute( WebConsoleConstants.ATTR_PLUGIN_ROOT );
final String servicesRoot = getServicesRoot ( request );
StringWriter w = new StringWriter();
- writeJSON(w, reqInfo.bundle, pluginRoot, servicesRoot, request.getLocale() );
+ try
+ {
+ writeJSON(w, reqInfo.bundle, pluginRoot, servicesRoot, request.getLocale(), request.getParameter(FILTER_PARAM) );
+ }
+ catch (InvalidSyntaxException e)
+ {
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Invalid LDAP filter specified");
+ return;
+ }
vars.put( "__bundles__", w.toString());
response.getWriter().print(TEMPLATE_MAIN);
}
- private void renderJSON( final HttpServletResponse response, final Bundle bundle, final String pluginRoot, final String servicesRoot, final Locale locale )
- throws IOException
+ private void renderJSON( final HttpServletResponse response, final Bundle bundle, final String pluginRoot, final String servicesRoot, final Locale locale, final String filter )
+ throws IOException, InvalidSyntaxException
{
response.setContentType( "application/json" );
response.setCharacterEncoding( "UTF-8" );
final PrintWriter pw = response.getWriter();
- writeJSON(pw, bundle, pluginRoot, servicesRoot, locale);
+ writeJSON(pw, bundle, pluginRoot, servicesRoot, locale, filter);
}
- private void writeJSON( final Writer pw, final Bundle bundle, final String pluginRoot, final String servicesRoot, final Locale locale )
- throws IOException
+ private void writeJSON( final Writer pw, final Bundle bundle, final String pluginRoot, final String servicesRoot, final Locale locale, final String filter )
+ throws IOException, InvalidSyntaxException
{
- writeJSON( pw, bundle, pluginRoot, servicesRoot, false, locale );
+ writeJSON( pw, bundle, pluginRoot, servicesRoot, false, locale, filter );
}
private void writeJSON( final Writer pw, final Bundle bundle, final String pluginRoot,
- final String servicesRoot, final boolean fullDetails, final Locale locale ) throws IOException
+ final String servicesRoot, final boolean fullDetails, final Locale locale, final String filter ) throws IOException, InvalidSyntaxException
{
final Bundle[] allBundles = this.getBundles();
final Object[] status = getStatusLine(allBundles);
final String statusLine = (String) status[5];
- final Bundle[] bundles = ( bundle != null ) ? new Bundle[]
- { bundle } : allBundles;
+ // filter bundles by headers
+ final Bundle[] bundles;
+ if (bundle != null)
+ {
+ bundles = new Bundle[] { bundle };
+ }
+ else if (filter != null)
+ {
+ Filter f = getBundleContext().createFilter(filter);
+ ArrayList list = new ArrayList(allBundles.length);
+ final String localeString = locale.toString();
+ for (int i = 0, size = allBundles.length; i < size; i++)
+ {
+ if (f.match(allBundles[i].getHeaders(localeString)))
+ {
+ list.add(allBundles[i]);
+ }
+ }
+ bundles = new Bundle[list.size()];
+ list.toArray(bundles);
+ }
+ else
+ {
+ bundles = allBundles;
+ }
+
Util.sort( bundles, locale );
final JSONWriter jw = new JSONWriter( pw );
@@ -618,6 +669,8 @@
jw.value( Util.getHeaderValue(bundle, Constants.BUNDLE_VERSION) );
jw.key( "symbolicName" );
jw.value( Util.getHeaderValue(bundle, Constants.BUNDLE_SYMBOLICNAME) );
+ jw.key( "category" );
+ jw.value( Util.getHeaderValue(bundle, Constants.BUNDLE_CATEGORY) );
if ( details )
{
diff --git a/webconsole/src/main/native2ascii/OSGI-INF/l10n/bundle_bg.properties b/webconsole/src/main/native2ascii/OSGI-INF/l10n/bundle_bg.properties
index d12234d..91adb01 100644
--- a/webconsole/src/main/native2ascii/OSGI-INF/l10n/bundle_bg.properties
+++ b/webconsole/src/main/native2ascii/OSGI-INF/l10n/bundle_bg.properties
@@ -114,6 +114,7 @@
bundles.install_update=Инсталиране/обновяване...
bundles.refreshPkg=Обновяване пакети
bundles.name=Име
+bundles.category=Категория
bundles.name.symb=Символно име
bundles.status=Статус
bundles.actions=Действия
@@ -144,6 +145,8 @@
bundles.filter.apply=Прилагане филтър
bundles.filter.clear=Изчистване филтър
bundles.filter.help=Низ или регулярен израз, който се съдържа в ID-то, името, символното име или версията на бъндъла.
+bundles.filter.ldap=Филтриране всичко
+bundles.filter.ldap.tip=Когато натиснете този бутон се прилага допълнително филтриране, спрямо *всички* хедъри на бундълите. Очаква се изразът в полето да бъде валиден OSGi LDAP филтър.
# states
bundles.state.1=Деинсталиран
bundles.state.2=Инсталиран
diff --git a/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties b/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties
index cd33fd6..8c05008 100644
--- a/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties
+++ b/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties
@@ -115,6 +115,7 @@
bundles.refreshPkg=Refresh Packages
bundles.name=Name
bundles.name.symb=Symbolic Name
+bundles.category=Category
bundles.status=Status
bundles.actions=Actions
# bundle details
@@ -143,7 +144,9 @@
# filter
bundles.filter.apply=Apply Filter
bundles.filter.clear=Clear Filter
-bundles.filter.help=A string or RegExp expression which is matched againse bundle id, name, symbolic name or version.
+bundles.filter.help=A string or RegExp expression which is matched againse bundle id, name, symbolic name or version. In case 'Filter All' is used, the value must be a valid LDAP string.
+bundles.filter.ldap=Filter All
+bundles.filter.ldap.tip=When clicked, a server-side matching is performed agains *all* bundle manifest headers. The expression is expected to be a valid OSGi Filter
# states
bundles.state.1=Uninstalled
bundles.state.2=Installed
diff --git a/webconsole/src/main/resources/res/ui/bundles.css b/webconsole/src/main/resources/res/ui/bundles.css
index 086a196..6193ed9 100644
--- a/webconsole/src/main/resources/res/ui/bundles.css
+++ b/webconsole/src/main/resources/res/ui/bundles.css
@@ -24,3 +24,7 @@
.col_Actions { width: 121px; }
.filterBox { float: left; margin-left: 1em }
#uploadDialog, div.ui-dialog-buttonpane button { font-size: 50% }
+.symName { font-style: italic }
+.symName:before { content: " (" }
+.symName:after { content: ")" }
+.filterClear { display: inline-block; vertical-align: middle }
diff --git a/webconsole/src/main/resources/res/ui/bundles.js b/webconsole/src/main/resources/res/ui/bundles.js
index b7467d3..c739abd 100644
--- a/webconsole/src/main/resources/res/ui/bundles.js
+++ b/webconsole/src/main/resources/res/ui/bundles.js
@@ -31,6 +31,7 @@
}
}
if ( drawDetails && eventData.data.length == 1 ) {
+ $('.filterBox input, .filterBox button').addClass('ui-state-disabled');
renderDetails(eventData.data[0]);
} else if ( currentBundle != null ) {
var id = currentBundle;
@@ -42,7 +43,7 @@
function entry( /* Object */ bundle, filter ) {
var matches = !(filter && typeof filter.test == 'function') ? true :
- filter.test(bundle.id) || filter.test(bundle.name) || filter.test(bundle.symbolicName) || filter.test(bundle.version);
+ filter.test(bundle.id) || filter.test(bundle.name) || filter.test(bundle.symbolicName) || filter.test(bundle.version) || filter.test(bundle.category);
if (matches) entryInternal( bundle ).appendTo(bundlesBody);
}
@@ -60,14 +61,14 @@
function entryInternal( /* Object */ bundle ) {
var tr = bundlesTemplate.clone();
var id = bundle.id;
- var name = bundle.name;
+ var name = bundle.name + '<span class="symName">' + bundle.symbolicName + '</span>';
tr.attr('id', 'entry'+id);
tr.find('td:eq(0)').text(id);
tr.find('td:eq(1) span:eq(0)').attr('id', 'img'+id).click(function() {showDetails(id)});
tr.find('td:eq(1) span:eq(1)').html( drawDetails ? name : '<a href="' + pluginRoot + '/' + id + '">' + name + '</a>' );
tr.find('td:eq(2)').text( bundle.version );
- tr.find('td:eq(3)').text( bundle.symbolicName );
+ tr.find('td:eq(3)').text( bundle.category );
tr.find('td:eq(4)').text( stateString(bundle) );
if (id == 0) { // system bundle has no actions
tr.find('td:eq(5) ul').addClass('ui-helper-hidden');
@@ -184,8 +185,8 @@
});
// filter
- $('input.filter').click(function() {$(this).val('')});
$('.filterApply').click(function() {
+ if ($(this).hasClass('ui-state-disabled')) return;
var el = $(this).parent().find('input.filter');
var filter = el.length && el.val() ? new RegExp(el.val()) : false;
renderData(lastBundleData, filter);
@@ -195,8 +196,15 @@
return false;
});
$('.filterClear').click(function() {
+ if ($(this).hasClass('ui-state-disabled')) return;
$('input.filter').val('');
- renderData(lastBundleData);
+ loadData();
+ });
+ $('.filterLDAP').click(function() {
+ if ($(this).hasClass('ui-state-disabled')) return;
+ var el = $(this).parent().find('input.filter');
+ var filter = el.val();
+ if (filter) $.get(pluginRoot + '/.json', { 'filter' : filter }, renderData, 'json');
});
// upload dialog
diff --git a/webconsole/src/main/resources/templates/bundles.html b/webconsole/src/main/resources/templates/bundles.html
index a62da40..8f956db 100644
--- a/webconsole/src/main/resources/templates/bundles.html
+++ b/webconsole/src/main/resources/templates/bundles.html
@@ -54,11 +54,12 @@
<form method="post" enctype="multipart/form-data" action="" class="filterForm">
<div class="ui-widget-header ui-corner-top buttonGroup">
<div class="filterBox">
- <input class="filter" value="^Apache" title="${bundles.filter.help}" />
- <button class="filterApply" type="button">${bundles.filter.apply}</button>
- <button class="filterClear" type="button">${bundles.filter.clear}</button>
+ <input class="filter" value="" title="${bundles.filter.help}" />
+ <span class="filterClear ui-icon ui-icon-close" title="${bundles.filter.clear}"> </span>
+ <button class="filterApply" title="${bundles.filter.help}">${bundles.filter.apply}</button>
+ <button class="filterLDAP" title="${bundles.filter.ldap.tip}">${bundles.filter.ldap}</button>
</div>
- <button class="reloadButton" type="button" style="margin-left: 60px;">${reload}</button>
+ <button class="reloadButton" type="button">${reload}</button>
<button class="installButton" type="button">${bundles.install_update}</button>
<button class="refreshPackages" type="button">${bundles.refreshPkg}</button>
</div>
@@ -70,7 +71,7 @@
<th class="col_Id">${id}</th>
<th class="col_Name">${bundles.name}</th>
<th class="col_Version">${version}</th>
- <th class="col_Symbolic_Name">${bundles.name.symb}</th>
+ <th class="col_Symbolic_Name">${bundles.category}</th>
<th class="col_Status">${bundles.status}</th>
<th class="col_Actions">${bundles.actions}</th>
</tr>
@@ -102,11 +103,12 @@
<form method="post" enctype="multipart/form-data" action="" class="filterForm">
<div class="ui-widget-header ui-corner-bottom buttonGroup">
<div class="filterBox">
- <input class="filter" value="^Apache" title="${bundles.filter.help}" />
- <button class="filterApply" type="button">${bundles.filter.apply}</button>
- <button class="filterClear" type="button">${bundles.filter.clear}</button>
+ <input class="filter" value="" title="${bundles.filter.help}" />
+ <span class="filterClear ui-icon ui-icon-close" title="${bundles.filter.clear}"> </span>
+ <button class="filterApply" title="${bundles.filter.help}">${bundles.filter.apply}</button>
+ <button class="filterLDAP" title="${bundles.filter.ldap.tip}">${bundles.filter.ldap}</button>
</div>
- <button class="reloadButton" type="button" style="margin-left: 60px;">${reload}</button>
+ <button class="reloadButton" type="button">${reload}</button>
<button class="installButton" type="button">${bundles.install_update}</button>
<button class="refreshPackages" type="button">${bundles.refreshPkg}</button>
</div>