blob: 5cc641e47d581521f71161d0fbfde27802634d4f [file] [log] [blame]
Felix Meschbergere7f513f2012-07-02 14:00:02 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19package org.apache.felix.cm.impl.helper;
20
Felix Meschberger382a19b2012-07-03 09:45:14 +000021
Felix Meschbergere7f513f2012-07-02 14:00:02 +000022import java.util.ArrayList;
23import java.util.Collection;
24import java.util.Collections;
25import java.util.Dictionary;
26import java.util.List;
27
28import org.apache.felix.cm.impl.CaseInsensitiveDictionary;
29import org.apache.felix.cm.impl.ConfigurationImpl;
30import org.apache.felix.cm.impl.ConfigurationManager;
31import org.apache.felix.cm.impl.RankingComparator;
32import org.osgi.framework.Constants;
33import org.osgi.framework.ServiceReference;
Felix Meschberger382a19b2012-07-03 09:45:14 +000034import org.osgi.service.cm.ConfigurationException;
35import org.osgi.service.cm.ManagedService;
36import org.osgi.service.cm.ManagedServiceFactory;
Felix Meschbergere7f513f2012-07-02 14:00:02 +000037import org.osgi.service.log.LogService;
38import org.osgi.util.tracker.ServiceTracker;
39
Felix Meschberger382a19b2012-07-03 09:45:14 +000040
Felix Meschbergere7f513f2012-07-02 14:00:02 +000041/**
42 * The <code>BaseTracker</code> is the base class for tracking
43 * <code>ManagedService</code> and <code>ManagedServiceFactory</code>
44 * services. It maps their <code>ServiceRegistration</code> to the
45 * {@link ConfigurationMap} mapping their service PIDs to provided
46 * configuration.
47 */
48public abstract class BaseTracker<S> extends ServiceTracker<S, ConfigurationMap>
49{
50 protected final ConfigurationManager cm;
51
Felix Meschberger382a19b2012-07-03 09:45:14 +000052 private final boolean managedServiceFactory;
Felix Meschbergere7f513f2012-07-02 14:00:02 +000053
Felix Meschberger382a19b2012-07-03 09:45:14 +000054
55 protected BaseTracker( final ConfigurationManager cm, final boolean managedServiceFactory )
Felix Meschbergere7f513f2012-07-02 14:00:02 +000056 {
Felix Meschberger382a19b2012-07-03 09:45:14 +000057 super( cm.getBundleContext(), ( managedServiceFactory ? ManagedServiceFactory.class.getName()
58 : ManagedService.class.getName() ), null );
Felix Meschbergere7f513f2012-07-02 14:00:02 +000059 this.cm = cm;
Felix Meschberger382a19b2012-07-03 09:45:14 +000060 this.managedServiceFactory = managedServiceFactory;
Felix Meschbergere7f513f2012-07-02 14:00:02 +000061 open();
62 }
63
64
65 public ConfigurationMap addingService( ServiceReference<S> reference )
66 {
67 final String[] pids = getServicePid( reference );
68 configure( reference, pids );
69 return new ConfigurationMap( pids );
70 }
71
72
73 @Override
74 public void modifiedService( ServiceReference<S> reference, ConfigurationMap service )
75 {
76 String[] pids = getServicePid( reference );
77 if ( service.isDifferentPids( pids ) )
78 {
79 configure( reference, pids );
80 service.setConfiguredPids( pids );
81 }
82 }
83
84
Felix Meschberger382a19b2012-07-03 09:45:14 +000085 private void configure( ServiceReference<S> reference, String[] pids )
Felix Meschbergere7f513f2012-07-02 14:00:02 +000086 {
87 if ( pids != null )
88 {
Felix Meschberger382a19b2012-07-03 09:45:14 +000089
90 /*
91 * We get the service here for performance reasons only.
92 * Assuming a service is registered as a ServiceFactory with
93 * multiple PIDs, it may be instantiated and destroyed multiple
94 * times during its inital setup phase. By getting it first
95 * and ungetting it at the end we prevent this cycling ensuring
96 * all updates go the same service instance.
97 */
98
Felix Meschbergere7f513f2012-07-02 14:00:02 +000099 S service = getRealService( reference );
100 if ( service != null )
101 {
102 try
103 {
104 if ( this.cm.isLogEnabled( LogService.LOG_DEBUG ) )
105 {
106 this.cm.log( LogService.LOG_DEBUG, "configure(ManagedService {0})", new Object[]
107 { ConfigurationManager.toString( reference ) } );
108 }
109
110 for ( int i = 0; i < pids.length; i++ )
111 {
Felix Meschberger382a19b2012-07-03 09:45:14 +0000112 this.cm.configure( pids[i], reference, managedServiceFactory );
Felix Meschbergere7f513f2012-07-02 14:00:02 +0000113 }
114 }
115 finally
116 {
117 ungetRealService( reference );
118 }
119 }
120 }
121 }
122
123
124 public final List<ServiceReference<S>> getServices( final TargetedPID pid )
125 {
126 ServiceReference<S>[] refs = this.getServiceReferences();
127 if ( refs != null )
128 {
129 ArrayList<ServiceReference<S>> result = new ArrayList<ServiceReference<S>>( refs.length );
130 for ( ServiceReference<S> ref : refs )
131 {
Felix Meschberger382a19b2012-07-03 09:45:14 +0000132 ConfigurationMap map = this.getService( ref );
133 if ( map != null
134 && ( map.accepts( pid.getRawPid() ) || ( map.accepts( pid.getServicePid() ) && pid
135 .matchesTarget( ref ) ) ) )
Felix Meschbergere7f513f2012-07-02 14:00:02 +0000136 {
Felix Meschberger382a19b2012-07-03 09:45:14 +0000137 result.add( ref );
Felix Meschbergere7f513f2012-07-02 14:00:02 +0000138 }
139 }
140
141 if ( result.size() > 1 )
142 {
143 Collections.sort( result, RankingComparator.SRV_RANKING );
144 }
145
146 return result;
147 }
148
149 return Collections.emptyList();
150 }
151
152
Felix Meschbergere7f513f2012-07-02 14:00:02 +0000153 // Updates
Felix Meschberger382a19b2012-07-03 09:45:14 +0000154 public abstract void provideConfiguration( ServiceReference<S> service, ConfigurationImpl config,
155 Dictionary<String, ?> properties );
Felix Meschbergere7f513f2012-07-02 14:00:02 +0000156
157
Felix Meschbergeraccd9012012-07-02 15:18:08 +0000158 public abstract void removeConfiguration( ServiceReference<S> service, ConfigurationImpl config );
Felix Meschbergere7f513f2012-07-02 14:00:02 +0000159
160
161 protected final S getRealService( ServiceReference<S> reference )
162 {
163 return this.context.getService( reference );
164 }
165
166
167 protected final void ungetRealService( ServiceReference<S> reference )
168 {
169 this.context.ungetService( reference );
170 }
171
Felix Meschberger382a19b2012-07-03 09:45:14 +0000172
173 protected final Dictionary getProperties( Dictionary<String, ?> rawProperties, String targetPid,
174 ServiceReference service )
Felix Meschbergere7f513f2012-07-02 14:00:02 +0000175 {
176 Dictionary props = new CaseInsensitiveDictionary( rawProperties );
Felix Meschberger382a19b2012-07-03 09:45:14 +0000177 this.cm.callPlugins( props, targetPid, service, null /* config */);
Felix Meschbergere7f513f2012-07-02 14:00:02 +0000178 return props;
179 }
180
Felix Meschberger382a19b2012-07-03 09:45:14 +0000181
182 protected final void handleCallBackError( final Throwable error, final ServiceReference target,
183 final ConfigurationImpl config )
184 {
185 if ( error instanceof ConfigurationException )
186 {
187 final ConfigurationException ce = ( ConfigurationException ) error;
188 if ( ce.getProperty() != null )
189 {
190 this.cm.log( LogService.LOG_ERROR,
191 "{0}: Updating property {1} of configuration {2} caused a problem: {3}", new Object[]
192 { ConfigurationManager.toString( target ), ce.getProperty(), config.getPid(), ce.getReason(),
193 ce } );
194 }
195 else
196 {
197 this.cm.log( LogService.LOG_ERROR, "{0}: Updating configuration {1} caused a problem: {2}",
198 new Object[]
199 { ConfigurationManager.toString( target ), config.getPid(), ce.getReason(), ce } );
200 }
201 }
202 else
203 {
204 {
205 this.cm.log( LogService.LOG_ERROR, "{0}: Unexpected problem updating configuration {1}", new Object[]
206 { ConfigurationManager.toString( target ), config, error } );
207 }
208
209 }
210 }
211
212
Felix Meschbergere7f513f2012-07-02 14:00:02 +0000213 /**
214 * Returns the <code>service.pid</code> property of the service reference as
215 * an array of strings or <code>null</code> if the service reference does
216 * not have a service PID property.
217 * <p>
218 * The service.pid property may be a single string, in which case a single
219 * element array is returned. If the property is an array of string, this
220 * array is returned. If the property is a collection it is assumed to be a
221 * collection of strings and the collection is converted to an array to be
222 * returned. Otherwise (also if the property is not set) <code>null</code>
223 * is returned.
224 *
225 * @throws NullPointerException
226 * if reference is <code>null</code>
227 * @throws ArrayStoreException
228 * if the service pid is a collection and not all elements are
229 * strings.
230 */
231 private static String[] getServicePid( ServiceReference reference )
232 {
233 Object pidObj = reference.getProperty( Constants.SERVICE_PID );
234 if ( pidObj instanceof String )
235 {
236 return new String[]
237 { ( String ) pidObj };
238 }
239 else if ( pidObj instanceof String[] )
240 {
241 return ( String[] ) pidObj;
242 }
243 else if ( pidObj instanceof Collection )
244 {
245 Collection pidCollection = ( Collection ) pidObj;
246 return ( String[] ) pidCollection.toArray( new String[pidCollection.size()] );
247 }
248
249 return null;
250 }
251
252}