/* * 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.scr.impl; import java.io.PrintWriter; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import java.util.regex.Pattern; import org.apache.felix.scr.impl.config.ScrConfigurationImpl; import org.apache.felix.scr.impl.manager.ScrConfiguration; import org.apache.felix.scr.info.ScrInfo; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.ServiceRegistration; import org.osgi.framework.dto.ServiceReferenceDTO; import org.osgi.service.component.runtime.ServiceComponentRuntime; import org.osgi.service.component.runtime.dto.ComponentConfigurationDTO; import org.osgi.service.component.runtime.dto.ComponentDescriptionDTO; import org.osgi.service.component.runtime.dto.ReferenceDTO; import org.osgi.service.component.runtime.dto.SatisfiedReferenceDTO; import org.osgi.service.component.runtime.dto.UnsatisfiedReferenceDTO; /** * The <code>ScrCommand</code> class provides the implementations for the * Apache Felix Gogo and legacy Apache Felix Shell commands. The * {@link #register(BundleContext, ScrService, ScrConfiguration)} method * instantiates and registers the Gogo and Shell commands as possible. */ public class ScrCommand implements ScrInfo { private static final Comparator<ComponentDescriptionDTO> DESCRIPTION_COMP = new Comparator<ComponentDescriptionDTO>() { public int compare(final ComponentDescriptionDTO c1, final ComponentDescriptionDTO c2) { final long bundleId1 = c1.bundle.id; final long bundleId2 = c2.bundle.id; int result = Long.signum(bundleId1 - bundleId2); if ( result == 0) { // sanity check if ( c1.name == null ) { result = ( c2.name == null ? 0 : -1); } else if ( c2.name == null ) { result = 1; } else { result = c1.name.compareTo(c2.name); } } return result; } }; private static final Comparator<ComponentConfigurationDTO> CONFIGURATION_COMP = new Comparator<ComponentConfigurationDTO>() { public int compare(final ComponentConfigurationDTO c1, final ComponentConfigurationDTO c2) { return Long.signum(c1.id - c2.id); } }; private final BundleContext bundleContext; private final ServiceComponentRuntime scrService; private final ScrConfigurationImpl scrConfiguration; private ServiceRegistration<ScrInfo> reg; private ServiceRegistration<?> gogoReg; private ServiceRegistration<?> shellReg; static ScrCommand register(BundleContext bundleContext, ServiceComponentRuntime scrService, ScrConfigurationImpl scrConfiguration) { final ScrCommand cmd = new ScrCommand(bundleContext, scrService, scrConfiguration); cmd.registerCommands(bundleContext, scrService); return cmd; } //used by ComponentTestBase protected ScrCommand(BundleContext bundleContext, ServiceComponentRuntime scrService, ScrConfigurationImpl scrConfiguration) { this.bundleContext = bundleContext; this.scrService = scrService; this.scrConfiguration = scrConfiguration; } private void registerCommands(BundleContext bundleContext, ServiceComponentRuntime scrService) { /* * Register the Gogo Command as a service of its own class. */ try { final ScrGogoCommand gogoCmd = new ScrGogoCommand(this); final Hashtable<String, Object> props = new Hashtable<String, Object>(); props.put("osgi.command.scope", "scr"); props.put("osgi.command.function", new String[] { "config", "disable", "enable", "info", "list" }); props.put(Constants.SERVICE_DESCRIPTION, "SCR Gogo Shell Support"); props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation"); gogoReg = bundleContext.registerService(ScrGogoCommand.class, gogoCmd, props); } catch (Throwable t) { // Might be thrown if running in a pre-Java 5 VM } // We dynamically import the impl service API, so it // might not actually be available, so be ready to catch // the exception when we try to register the command service. try { // Register "scr" impl command service as a // wrapper for the bundle repository service. final Hashtable<String, Object> props = new Hashtable<String, Object>(); props.put(Constants.SERVICE_DESCRIPTION, "SCR Legacy Shell Support"); props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation"); shellReg = bundleContext.registerService(org.apache.felix.shell.Command.class, new ScrShellCommand(this), props); } catch (Throwable th) { // Ignore. } } void unregister() { if (gogoReg != null) { gogoReg.unregister(); gogoReg = null; } if ( shellReg != null ) { shellReg.unregister(); shellReg = null; } } // ---------- Actual implementation public void update( boolean infoAsService ) { if (infoAsService) { if ( reg == null ) { final Hashtable<String, Object> props = new Hashtable<String, Object>(); props.put(Constants.SERVICE_DESCRIPTION, "SCR Info service"); props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation"); reg = bundleContext.registerService( ScrInfo.class, this, props ); } } else { if ( reg != null ) { reg.unregister(); reg = null; } } } /* (non-Javadoc) * @see org.apache.felix.scr.impl.ScrInfo#list(java.lang.String, java.io.PrintStream, java.io.PrintStream) */ public void list(final String bundleIdentifier, final PrintWriter out) { final List<ComponentDescriptionDTO> descriptions = new ArrayList<ComponentDescriptionDTO>(); if (bundleIdentifier != null) { Bundle bundle = null; try { final long bundleId = Long.parseLong(bundleIdentifier); bundle = bundleContext.getBundle(bundleId); } catch (final NumberFormatException nfe) { // might be a bundle symbolic name final Bundle[] bundles = bundleContext.getBundles(); for (int i = 0; i < bundles.length; i++) { if (bundleIdentifier.equals(bundles[i].getSymbolicName())) { bundle = bundles[i]; break; } } } if (bundle == null) { throw new IllegalArgumentException("Missing bundle with ID " + bundleIdentifier); } if (ComponentRegistry.isBundleActive(bundle)) { descriptions.addAll(scrService.getComponentDescriptionDTOs(bundle)); if (descriptions.isEmpty()) { out.println("Bundle " + bundleIdentifier + " declares no components"); return; } } else { out.println("Bundle " + bundleIdentifier + " is not active"); return; } } else { descriptions.addAll(scrService.getComponentDescriptionDTOs()); if (descriptions.isEmpty()) { out.println("No components registered"); return; } } Collections.sort( descriptions, DESCRIPTION_COMP); out.println(" BundleId Component Name Default State"); out.println(" Component Id State PIDs (Factory PID)"); for(final ComponentDescriptionDTO desc : descriptions) { out.println( String.format( " [%1$4d] %2$s %3$s", desc.bundle.id, desc.name, desc.defaultEnabled ? "enabled" : "disabled" ) ); final List<ComponentConfigurationDTO> configs = new ArrayList<ComponentConfigurationDTO>(this.scrService.getComponentConfigurationDTOs(desc)); Collections.sort( configs, CONFIGURATION_COMP); for ( final ComponentConfigurationDTO component : configs ) { final Object servicePid = component.properties.get(Constants.SERVICE_PID); final String factoryPid = (String)component.properties.get("service.factoryPid"); final StringBuilder pid = new StringBuilder(); if ( servicePid != null ) { pid.append(servicePid); } if ( factoryPid != null ) { pid.append(" ("); pid.append(factoryPid); pid.append(" )"); } out.println( String.format( " [%1$4d] [%2$s] %3$s", component.id, toStateString( component.state ), pid.toString()) ); } } out.flush(); } /** * @see org.apache.felix.scr.impl.ScrInfo#info(java.lang.String, java.io.PrintStream, java.io.PrintStream) */ public void info(final String componentId, final PrintWriter out) { final Result result = getComponentsFromArg(componentId, false); if (result.components.isEmpty()) { return; } Collections.sort( result.components, DESCRIPTION_COMP ); long bundleId = -1; for ( ComponentDescriptionDTO component : result.components ) { if ( component.bundle.id != bundleId ) { if ( bundleId != -1 ) { out.println(); } bundleId = component.bundle.id; out.println(String.format("*** Bundle: %1$s (%2$d)", component.bundle.symbolicName, bundleId)); } out.println( "Component Description:"); out.print( " Name: " ); out.println( component.name ); out.print( " Implementation Class: " ); out.println( component.implementationClass ); out.print( " Default State: " ); out.println( component.defaultEnabled ? "enabled" : "disabled" ); out.print( " Activation: " ); out.println( component.immediate ? "immediate" : "delayed" ); // DS 1.1 new features out.print( " Configuration Policy: " ); out.println( component.configurationPolicy ); out.print( " Activate Method: " ); out.print( component.activate ); out.println(); out.print( " Deactivate Method: " ); out.print( component.deactivate ); out.println(); out.print( " Modified Method: " ); if ( component.modified != null ) { out.print( component.modified ); } else { out.print( "-" ); } out.println(); out.print( " Configuration Pid: " ); out.print( Arrays.asList(component.configurationPid) ); out.println(); if ( component.factory != null ) { out.print( " Factory: " ); out.println( component.factory ); } String[] services = component.serviceInterfaces; if ( services != null && services.length > 0 ) { out.println( " Services: " ); for ( String service: services ) { out.print( " " ); out.println( service ); } out.print( " Service Scope: " ); out.println( component.scope ); } ReferenceDTO[] refs = component.references; if ( refs != null ) { for ( ReferenceDTO ref : refs ) { out.print( " Reference: " ); out.println( ref.name ); out.print( " Interface Name: " ); out.println( ref.interfaceName ); if ( ref.target != null ) { out.print( " Target Filter: " ); out.println( ref.target ); } out.print( " Cardinality: " ); out.println( ref.cardinality ); out.print( " Policy: " ); out.println( ref.policy ); out.print( " Policy option: " ); out.println( ref.policyOption ); out.print( " Reference Scope: "); out.println( ref.scope); } } Map<String, Object> props = component.properties; propertyInfo(props, out, " ", "Component Description"); if ( result.configuration != null ) { info(result.configuration, out); } else { Collection<ComponentConfigurationDTO> componentConfigurationDTOs = scrService.getComponentConfigurationDTOs(component); if (componentConfigurationDTOs.isEmpty()) { out.println(" (No Component Configurations)"); } else { for (final ComponentConfigurationDTO cc: componentConfigurationDTOs) { info(cc, out); } } } out.println(); } out.flush(); } void propertyInfo(Map<String, Object> props, PrintWriter out, String prefix, String label) { if ( props != null ) { out.print( prefix ); out.print( label ); out.println( " Properties:" ); TreeMap<String, Object> keys = new TreeMap<String, Object>( props ); for ( Entry<String, Object> entry: keys.entrySet() ) { out.print( prefix ); out.print( " " ); out.print( entry.getKey() ); out.print( " = " ); Object prop = entry.getValue(); if ( prop.getClass().isArray() ) { out.print("["); int length = Array.getLength(prop); for (int i = 0; i< length; i++) { out.print(Array.get(prop, i)); if ( i < length - 1) { out.print(", "); } } out.println("]"); } else { out.println( prop ); } } } } private void info(ComponentConfigurationDTO cc, PrintWriter out) { out.println( " Component Configuration:"); out.print(" ComponentId: "); out.println( cc.id ); out.print(" State: "); out.println( toStateString(cc.state)); for ( SatisfiedReferenceDTO ref: cc.satisfiedReferences) { out.print( " SatisfiedReference: "); out.println( ref.name ); out.print( " Target: " ); out.println( ref.target ); ServiceReferenceDTO[] serviceRefs = ref.boundServices; if ( serviceRefs.length > 0 ) { out.print( " Bound to:" ); for ( ServiceReferenceDTO sr: serviceRefs ) { out.print( " " ); out.println( sr.id ); propertyInfo(sr.properties, out, " ", "Reference" ); } } else { out.println( " (unbound)" ); } } for ( UnsatisfiedReferenceDTO ref: cc.unsatisfiedReferences) { out.print( " UnsatisfiedReference: "); out.println( ref.name ); out.print( " Target: " ); out.println( ref.target ); ServiceReferenceDTO[] serviceRefs = ref.targetServices; if ( serviceRefs.length > 0 ) { out.print( " Target services:" ); for ( ServiceReferenceDTO sr: serviceRefs ) { out.print( " " ); out.println( sr.id ); } } else { out.println( " (no target services)" ); } } propertyInfo( cc.properties, out, " ", "Component Configuration" ); } void change(final String componentIdentifier, final PrintWriter out, final boolean enable) { final Result result = getComponentsFromArg(componentIdentifier, true); for ( final ComponentDescriptionDTO component : result.components ) { if ( enable ) { if ( !scrService.isComponentEnabled(component) ) { scrService.enableComponent(component); out.println( "Component " + component.name + " enabled" ); } else { out.println( "Component " + component.name + " already enabled" ); } } else { if ( scrService.isComponentEnabled(component) ) { scrService.disableComponent(component); out.println( "Component " + component.name + " disabled" ); } else { out.println( "Component " + component.name + " already disabled" ); } } } out.flush(); } /** * @see org.apache.felix.scr.impl.ScrInfo#config(java.io.PrintStream) */ public void config(final PrintWriter out) { out.print("Log Level: "); out.println(scrConfiguration.getLogLevel()); out.print("Obsolete Component Factory with Factory Configuration: "); out.println(scrConfiguration.isFactoryEnabled() ? "Supported" : "Unsupported"); out.print("Keep instances with no references: "); out.println(scrConfiguration.keepInstances() ? "Supported" : "Unsupported"); out.print("Lock timeount milliseconds: "); out.println(scrConfiguration.lockTimeout()); out.print("Stop timeount milliseconds: "); out.println(scrConfiguration.stopTimeout()); out.print("Global extender: "); out.println(scrConfiguration.globalExtender()); out.print("Info Service registered: "); out.println(scrConfiguration.infoAsService() ? "Supported" : "Unsupported"); out.flush(); } private String toStateString(final int state) { switch (state) { case (ComponentConfigurationDTO.UNSATISFIED_REFERENCE): return "unsatisfied reference"; case (ComponentConfigurationDTO.ACTIVE): return "active "; case (ComponentConfigurationDTO.SATISFIED): return "satisfied "; case (ComponentConfigurationDTO.UNSATISFIED_CONFIGURATION): return "unsatisfied config"; default: return "unkown: " + state; } } private static final class Result { public List<ComponentDescriptionDTO> components = new ArrayList<ComponentDescriptionDTO>(); public ComponentConfigurationDTO configuration; } private Result getComponentsFromArg(final String componentIdentifier, final boolean nameMatch) { final Pattern p = (componentIdentifier == null ? null : Pattern.compile(componentIdentifier)); final Result result = new Result(); for(final ComponentDescriptionDTO cmp : scrService.getComponentDescriptionDTOs()) { if (componentIdentifier != null) { if ( p.matcher(cmp.name).matches() ) { result.components.add(cmp); } else if ( !nameMatch ) { boolean done = false; for (final ComponentConfigurationDTO cfg: scrService.getComponentConfigurationDTOs(cmp)) { if ( p.matcher( String.valueOf( cfg.id )).matches() ) { result.components.add( cmp ); result.configuration = cfg; done = true; break; } } if ( done ) { break; } } } else { result.components.add(cmp); } } if (componentIdentifier != null && result.components.isEmpty()) { throw new IllegalArgumentException("No Component with name or configuration with ID matching " + componentIdentifier); } return result; } }