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