blob: fb94b291ecfa1d32e90db9294d6fc9ab1e037f10 [file] [log] [blame]
/*
* 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.core;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Locale;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.felix.utils.json.JSONWriter;
import org.apache.felix.webconsole.DefaultVariableResolver;
import org.apache.felix.webconsole.SimpleWebConsolePlugin;
import org.apache.felix.webconsole.WebConsoleConstants;
import org.apache.felix.webconsole.WebConsoleUtil;
import org.apache.felix.webconsole.internal.OsgiManagerPlugin;
import org.apache.felix.webconsole.internal.Util;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
/**
* ServicesServlet provides a plugin for inspecting the registered services.
*/
public class ServicesServlet extends SimpleWebConsolePlugin implements OsgiManagerPlugin
{
// don't create empty reference array all the time, create it only once - it is immutable
private static final ServiceReference[] NO_REFS = new ServiceReference[0];
private final class RequestInfo
{
public final String extension;
public final ServiceReference service;
public final boolean serviceRequested;
protected RequestInfo( final HttpServletRequest request )
{
String info = request.getPathInfo();
// remove label and starting slash
info = info.substring( getLabel().length() + 1 );
// get extension
if ( info.endsWith( ".json" ) )
{
extension = "json";
info = info.substring( 0, info.length() - 5 );
}
else
{
extension = "html";
}
// we only accept direct requests to a service if they have a slash
// after the label
String serviceInfo = null;
if ( info.startsWith( "/" ) )
{
serviceInfo = info.substring( 1 );
}
if ( serviceInfo == null || serviceInfo.length() == 0 )
{
service = null;
serviceRequested = false;
}
else
{
service = getServiceById( serviceInfo );
serviceRequested = true;
}
request.setAttribute( ServicesServlet.class.getName(), this );
}
}
static RequestInfo getRequestInfo( final HttpServletRequest request )
{
return ( RequestInfo ) request.getAttribute( ServicesServlet.class.getName() );
}
/** the label for the services plugin */
public static final String LABEL = "services"; //$NON-NLS-1$
private static final String TITLE = "%services.pluginTitle"; //$NON-NLS-1$
private static final String CSS[] = null;
// an LDAP filter, that is used to search services
private static final String FILTER_PARAM = "filter";
private final String TEMPLATE;
/** Default constructor */
public ServicesServlet() {
super(LABEL, TITLE, CATEGORY_OSGI, CSS);
// load templates
TEMPLATE = readTemplateFile( "/templates/services.html" ); //$NON-NLS-1$
}
private ServiceRegistration bipReg;
public void activate(BundleContext bundleContext)
{
super.activate(bundleContext);
bipReg = new ServicesUsedInfoProvider( bundleContext.getBundle() ).register( bundleContext );
}
public void deactivate() {
if ( null != bipReg )
{
bipReg.unregister();
bipReg = null;
}
super.deactivate();
}
final ServiceReference getServiceById( String pathInfo )
{
// only use last part of the pathInfo
pathInfo = pathInfo.substring( pathInfo.lastIndexOf( '/' ) + 1 );
StringBuffer filter = new StringBuffer();
filter.append( "(" ).append( Constants.SERVICE_ID ).append( "=" );
filter.append( pathInfo ).append( ")" );
String filterStr = filter.toString();
try
{
ServiceReference[] refs = BundleContextUtil.getWorkingBundleContext(this.getBundleContext()).getAllServiceReferences( null, filterStr );
if ( refs == null || refs.length != 1 )
{
return null;
}
return refs[0];
}
catch ( InvalidSyntaxException e )
{
log( "Unable to search for services using filter " + filterStr, e );
// this shouldn't happen
return null;
}
}
private final ServiceReference[] getServices(String filter)
{
// empty filter string will return nothing, must set it to null to return all services
if (filter != null && filter.trim().length() == 0) {
filter = null;
}
try
{
final ServiceReference[] refs = BundleContextUtil.getWorkingBundleContext(this.getBundleContext()).getAllServiceReferences( null, filter );
if ( refs != null )
{
return refs;
}
}
catch ( InvalidSyntaxException e )
{
log( "Unable to access service reference list.", e );
}
// no services or invalid filter syntax (unlikely)
return NO_REFS;
}
static final String getStatusLine( final ServiceReference[] services )
{
final int count = services.length;
final StringBuffer buffer = new StringBuffer();
buffer.append( count );
buffer.append( " service" );
if ( count != 1 )
buffer.append( 's' );
buffer.append( " in total" );
return buffer.toString();
}
static final String propertyAsString( ServiceReference ref, String name )
{
final Object value = ref.getProperty( name );
return WebConsoleUtil.toString( value );
}
private void renderJSON( final HttpServletResponse response, final ServiceReference service, final Locale locale )
throws IOException
{
response.setContentType( "application/json" );
response.setCharacterEncoding( "UTF-8" );
final PrintWriter pw = response.getWriter();
writeJSON( pw, service, locale, null);
}
private void keyVal( JSONWriter jw, String key, Object val) throws IOException
{
if ( val != null )
{
jw.object();
jw.key("key").value(key);
jw.key("value").value(val);
jw.endObject();
}
}
private void serviceDetails( JSONWriter jw, ServiceReference service ) throws IOException
{
String[] keys = service.getPropertyKeys();
jw.key( "props" );
jw.array();
for ( int i = 0; i < keys.length; i++ )
{
String key = keys[i];
if ( Constants.SERVICE_PID.equals( key ) )
{
keyVal(jw, "Service PID", service.getProperty( key ));
}
else if ( Constants.SERVICE_DESCRIPTION.equals( key ) )
{
keyVal(jw, "Service Description", service.getProperty( key ));
}
else if ( Constants.SERVICE_VENDOR.equals( key ) )
{
keyVal(jw, "Service Vendor", service.getProperty( key ));
}
else if ( !Constants.OBJECTCLASS.equals( key ) && !Constants.SERVICE_ID.equals( key ) )
{
keyVal(jw, key, service.getProperty( key ));
}
}
jw.endArray();
}
private void usingBundles( JSONWriter jw, ServiceReference service, Locale locale ) throws IOException
{
jw.key( "usingBundles" );
jw.array();
Bundle[] usingBundles = service.getUsingBundles();
if ( usingBundles != null )
{
for ( int i = 0; i < usingBundles.length; i++ )
{
jw.object();
bundleInfo( jw, usingBundles[i], locale );
jw.endObject();
}
}
jw.endArray();
}
private void serviceInfo( JSONWriter jw, ServiceReference service, boolean details, final Locale locale )
throws IOException
{
jw.object();
jw.key( "id" );
jw.value( propertyAsString( service, Constants.SERVICE_ID ) );
jw.key( "types" );
jw.value( propertyAsString( service, Constants.OBJECTCLASS ) );
jw.key( "pid" );
jw.value( propertyAsString( service, Constants.SERVICE_PID ) );
jw.key( "ranking" );
final Object ranking = service.getProperty(Constants.SERVICE_RANKING);
if ( ranking != null )
{
jw.value( ranking.toString() );
}
else
{
jw.value("");
}
bundleInfo( jw, service.getBundle(), locale );
if ( details )
{
serviceDetails( jw, service );
usingBundles( jw, service, locale );
}
jw.endObject();
}
private void bundleInfo( final JSONWriter jw, final Bundle bundle, final Locale locale )
throws IOException
{
jw.key( "bundleId" );
jw.value( bundle.getBundleId() );
jw.key( "bundleName" );
jw.value( Util.getName( bundle, locale ) );
jw.key( "bundleVersion" );
jw.value( Util.getHeaderValue( bundle, Constants.BUNDLE_VERSION ) );
jw.key( "bundleSymbolicName" );
jw.value( bundle.getSymbolicName() );
}
private void writeJSON(final Writer pw, final ServiceReference service, final Locale locale, final String filter) throws IOException
{
writeJSON( pw, service, false, locale, filter );
}
private void writeJSON( final Writer pw, final ServiceReference service, final boolean fullDetails, final Locale locale, final String filter )
throws IOException
{
final ServiceReference[] allServices = this.getServices(filter);
final String statusLine = getStatusLine( allServices );
final ServiceReference[] services = ( service != null ) ? new ServiceReference[]
{ service } : allServices;
final JSONWriter jw = new JSONWriter( pw );
jw.object();
jw.key( "status" );
jw.value( statusLine );
jw.key( "serviceCount" );
jw.value( allServices.length );
jw.key( "data" );
jw.array();
for ( int i = 0; i < services.length; i++ )
{
serviceInfo( jw, services[i], fullDetails || service != null, locale );
}
jw.endArray();
jw.endObject();
}
/**
* @see org.apache.felix.webconsole.AbstractWebConsolePlugin#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
protected void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException,
IOException
{
if (request.getPathInfo().indexOf("/res/") == -1)
{ // not resource
final RequestInfo reqInfo = new RequestInfo( request );
if ( reqInfo.service == null && reqInfo.serviceRequested )
{
response.sendError( 404 );
return;
}
if ( reqInfo.extension.equals( "json" ) )
{
this.renderJSON( response, reqInfo.service, request.getLocale() );
// nothing more to do
return;
}
}
super.doGet( request, response );
}
/**
* @see org.apache.felix.webconsole.AbstractWebConsolePlugin#renderContent(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
protected void renderContent( HttpServletRequest request, HttpServletResponse response ) throws IOException
{
// get request info from request attribute
final RequestInfo reqInfo = getRequestInfo( request );
final String appRoot = ( String ) request.getAttribute( WebConsoleConstants.ATTR_APP_ROOT );
StringWriter w = new StringWriter();
final String filter = request.getParameter(FILTER_PARAM);
writeJSON(w, reqInfo.service, request.getLocale(), filter);
// prepare variables
DefaultVariableResolver vars = ( ( DefaultVariableResolver ) WebConsoleUtil.getVariableResolver( request ) );
vars.put( "bundlePath", appRoot + "/" + BundlesServlet.NAME + "/" );
vars.put( "drawDetails", String.valueOf(reqInfo.serviceRequested));
vars.put( "__data__", w.toString() );
vars.put( "filter", filter == null ? "" : WebConsoleUtil.escapeHtml(filter));
response.getWriter().print( TEMPLATE );
}
}