/* * 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 ); } }