| /* |
| * 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.cm.impl.helper; |
| |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Dictionary; |
| import java.util.List; |
| |
| import org.apache.felix.cm.impl.CaseInsensitiveDictionary; |
| import org.apache.felix.cm.impl.ConfigurationManager; |
| import org.apache.felix.cm.impl.RankingComparator; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.ServiceReference; |
| import org.osgi.service.cm.ConfigurationException; |
| import org.osgi.service.cm.ManagedService; |
| import org.osgi.service.cm.ManagedServiceFactory; |
| import org.osgi.service.log.LogService; |
| import org.osgi.util.tracker.ServiceTracker; |
| |
| |
| /** |
| * The <code>BaseTracker</code> is the base class for tracking |
| * <code>ManagedService</code> and <code>ManagedServiceFactory</code> |
| * services. It maps their <code>ServiceRegistration</code> to the |
| * {@link ConfigurationMap} mapping their service PIDs to provided |
| * configuration. |
| */ |
| public abstract class BaseTracker<S> extends ServiceTracker<S, ConfigurationMap<?>> |
| { |
| protected final ConfigurationManager cm; |
| |
| private final boolean managedServiceFactory; |
| |
| |
| protected BaseTracker( final ConfigurationManager cm, final boolean managedServiceFactory ) |
| { |
| super( cm.getBundleContext(), ( managedServiceFactory ? ManagedServiceFactory.class.getName() |
| : ManagedService.class.getName() ), null ); |
| this.cm = cm; |
| this.managedServiceFactory = managedServiceFactory; |
| open(); |
| } |
| |
| |
| public ConfigurationMap<?> addingService( ServiceReference<S> reference ) |
| { |
| this.cm.log( LogService.LOG_DEBUG, "Registering service {0}", new String[] |
| { ConfigurationManager.toString( reference ) } ); |
| |
| final String[] pids = getServicePid( reference ); |
| final ConfigurationMap<?> configurations = createConfigurationMap( pids ); |
| configure( reference, pids, configurations ); |
| return configurations; |
| } |
| |
| |
| @Override |
| public void modifiedService( ServiceReference<S> reference, ConfigurationMap<?> service ) |
| { |
| this.cm.log( LogService.LOG_DEBUG, "Modified service {0}", new String[] |
| { ConfigurationManager.toString( reference ) } ); |
| |
| String[] pids = getServicePid( reference ); |
| if ( service.isDifferentPids( pids ) ) |
| { |
| service.setConfiguredPids( pids ); |
| configure( reference, pids, service ); |
| } |
| } |
| |
| |
| @Override |
| public void removedService( ServiceReference<S> reference, ConfigurationMap<?> service ) |
| { |
| // just log |
| this.cm.log( LogService.LOG_DEBUG, "Unregistering service {0}", new String[] |
| { ConfigurationManager.toString( reference ) } ); |
| } |
| |
| |
| private void configure( ServiceReference<S> reference, String[] pids, ConfigurationMap<?> configurations ) |
| { |
| if ( pids != null ) |
| { |
| this.cm.configure( pids, reference, managedServiceFactory, configurations ); |
| } |
| } |
| |
| |
| public final List<ServiceReference<S>> getServices( final TargetedPID pid ) |
| { |
| ServiceReference<S>[] refs = this.getServiceReferences(); |
| if ( refs != null ) |
| { |
| ArrayList<ServiceReference<S>> result = new ArrayList<ServiceReference<S>>( refs.length ); |
| for ( ServiceReference<S> ref : refs ) |
| { |
| ConfigurationMap map = this.getService( ref ); |
| if ( map != null |
| && ( map.accepts( pid.getRawPid() ) || ( map.accepts( pid.getServicePid() ) && pid |
| .matchesTarget( ref ) ) ) ) |
| { |
| result.add( ref ); |
| } |
| } |
| |
| if ( result.size() > 1 ) |
| { |
| Collections.sort( result, RankingComparator.SRV_RANKING ); |
| } |
| |
| return result; |
| } |
| |
| return Collections.emptyList(); |
| } |
| |
| |
| protected abstract ConfigurationMap<?> createConfigurationMap( String[] pids ); |
| |
| /** |
| * Returns the String to be used as the PID of the service PID for the |
| * {@link TargetedPID pid} retrieved from the configuration. |
| * <p> |
| * This method will return {@link TargetedPID#getServicePid()} most of |
| * the time except if the service PID used for the consumer's service |
| * registration contains one or more pipe symbols (|). In this case |
| * {@link TargetedPID#getRawPid()} might be returned. |
| * |
| * @param service The reference ot the service for which the service |
| * PID is to be returned. |
| * @param pid The {@link TargetedPID} for which to return the service |
| * PID. |
| * @return The service PID or <code>null</code> if the service does not |
| * respond to the targeted PID at all. |
| */ |
| public abstract String getServicePid( ServiceReference<S> service, TargetedPID pid ); |
| |
| |
| /** |
| * Updates the given service with the provided configuration. |
| * <p> |
| * See the implementations of this method for more information. |
| * |
| * @param service The reference to the service to update |
| * @param configPid The targeted configuration PID |
| * @param factoryPid The targeted factory PID or <code>null</code> for |
| * a non-factory configuration |
| * @param properties The configuration properties, which may be |
| * <code>null</code> if this is the provisioning call upon |
| * service registration of a ManagedService |
| * @param revision The configuration revision or -1 if there is no |
| * configuration actually to provide. |
| * @param configurationMap The PID to configuration map for PIDs |
| * used by the service to update |
| * |
| * @see ManagedServiceTracker#provideConfiguration(ServiceReference, TargetedPID, TargetedPID, Dictionary, long, ConfigurationMap) |
| * @see ManagedServiceFactoryTracker#provideConfiguration(ServiceReference, TargetedPID, TargetedPID, Dictionary, long, ConfigurationMap) |
| */ |
| public abstract void provideConfiguration( ServiceReference<S> service, TargetedPID configPid, |
| TargetedPID factoryPid, Dictionary<String, ?> properties, long revision, |
| ConfigurationMap<?> configurationMap); |
| |
| |
| /** |
| * Remove the configuration indicated by the {@code configPid} from |
| * the service. |
| * |
| * @param service The reference to the service from which the |
| * configuration is to be removed. |
| * @param configPid The {@link TargetedPID} of the configuration |
| * @param factoryPid The {@link TargetedPID factory PID} of the |
| * configuration. This may be {@code null} for a non-factory |
| * configuration. |
| */ |
| public abstract void removeConfiguration( ServiceReference<S> service, TargetedPID configPid, TargetedPID factoryPid); |
| |
| |
| protected final S getRealService( ServiceReference<S> reference ) |
| { |
| return this.context.getService( reference ); |
| } |
| |
| |
| protected final void ungetRealService( ServiceReference<S> reference ) |
| { |
| this.context.ungetService( reference ); |
| } |
| |
| |
| protected final Dictionary getProperties( Dictionary<String, ?> rawProperties, ServiceReference service, |
| String configPid, String factoryPid ) |
| { |
| Dictionary props = new CaseInsensitiveDictionary( rawProperties ); |
| this.cm.callPlugins( props, service, configPid, factoryPid ); |
| return props; |
| } |
| |
| |
| protected final void handleCallBackError( final Throwable error, final ServiceReference target, final TargetedPID pid ) |
| { |
| if ( error instanceof ConfigurationException ) |
| { |
| final ConfigurationException ce = ( ConfigurationException ) error; |
| if ( ce.getProperty() != null ) |
| { |
| this.cm.log( LogService.LOG_ERROR, |
| "{0}: Updating property {1} of configuration {2} caused a problem: {3}", new Object[] |
| { ConfigurationManager.toString( target ), ce.getProperty(), pid, ce.getReason(), ce } ); |
| } |
| else |
| { |
| this.cm.log( LogService.LOG_ERROR, "{0}: Updating configuration {1} caused a problem: {2}", |
| new Object[] |
| { ConfigurationManager.toString( target ), pid, ce.getReason(), ce } ); |
| } |
| } |
| else |
| { |
| { |
| this.cm.log( LogService.LOG_ERROR, "{0}: Unexpected problem updating configuration {1}", new Object[] |
| { ConfigurationManager.toString( target ), pid, error } ); |
| } |
| |
| } |
| } |
| |
| |
| /** |
| * Returns the <code>service.pid</code> property of the service reference as |
| * an array of strings or <code>null</code> if the service reference does |
| * not have a service PID property. |
| * <p> |
| * The service.pid property may be a single string, in which case a single |
| * element array is returned. If the property is an array of string, this |
| * array is returned. If the property is a collection it is assumed to be a |
| * collection of strings and the collection is converted to an array to be |
| * returned. Otherwise (also if the property is not set) <code>null</code> |
| * is returned. |
| * |
| * @throws NullPointerException |
| * if reference is <code>null</code> |
| * @throws ArrayStoreException |
| * if the service pid is a collection and not all elements are |
| * strings. |
| */ |
| private static String[] getServicePid( ServiceReference reference ) |
| { |
| Object pidObj = reference.getProperty( Constants.SERVICE_PID ); |
| if ( pidObj instanceof String ) |
| { |
| return new String[] |
| { ( String ) pidObj }; |
| } |
| else if ( pidObj instanceof String[] ) |
| { |
| return ( String[] ) pidObj; |
| } |
| else if ( pidObj instanceof Collection ) |
| { |
| Collection pidCollection = ( Collection ) pidObj; |
| return ( String[] ) pidCollection.toArray( new String[pidCollection.size()] ); |
| } |
| |
| return null; |
| } |
| |
| } |