blob: 784ef8f8053bd975168e849012f18cd9dc5a4a4e [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
21
22import org.osgi.framework.Bundle;
23import org.osgi.framework.Constants;
24import org.osgi.framework.ServiceReference;
Felix Meschbergere9502bd2012-07-02 14:57:33 +000025import org.osgi.framework.Version;
Felix Meschbergere7f513f2012-07-02 14:00:02 +000026
27
28/**
29 * The <code>TargetedPID</code> class represents a targeted PID as read
30 * from a configuration object.
31 * <p>
32 * For a factory configuration the <code>TargetedPID</code> represents
33 * the factory PID of the configuration. Otherwise it represents the
34 * PID itself of the configuration.
35 */
36public class TargetedPID
37{
38
39 private final String rawPid;
40
41 private final String servicePid;
42
43 private final String symbolicName;
44 private final String version;
45 private final String location;
46
47
48 public TargetedPID( final String rawPid )
49 {
50 this.rawPid = rawPid;
51
52 if ( rawPid.indexOf( '|' ) < 0 )
53 {
54 this.servicePid = rawPid;
55 this.symbolicName = null;
56 this.version = null;
57 this.location = null;
58 }
59 else
60 {
61 int start = 0;
62 int end = rawPid.indexOf( '|' );
63 this.servicePid = rawPid.substring( start, end );
64
65 start = end + 1;
66 end = rawPid.indexOf( '|', start );
67 if ( end >= 0 )
68 {
69 this.symbolicName = rawPid.substring( start, end );
70 start = end + 1;
71 end = rawPid.indexOf( '|', start );
72 if ( end >= 0 )
73 {
74 this.version = rawPid.substring( start, end );
75 this.location = rawPid.substring( end + 1 );
76 }
77 else
78 {
79 this.version = rawPid.substring( start );
80 this.location = null;
81 }
82 }
83 else
84 {
85 this.symbolicName = rawPid.substring( start );
86 this.version = null;
87 this.location = null;
88 }
89 }
90 }
91
92
93 /**
94 * Returns true if the target of this PID (bundle symbolic name,
95 * version, and location) match the bundle registering the referenced
96 * service.
97 * <p>
98 * This method just checks the target not the PID value itself, so
99 * this method returning <code>true</code> does not indicate whether
100 * the service actually is registered with a service PID equal to the
101 * raw PID of this targeted PID.
102 * <p>
103 * This method also returns <code>false</code> if the service has
104 * concurrently been unregistered and the registering bundle is now
105 * <code>null</code>.
106 *
107 * @param reference <code>ServiceReference</code> to the registered
108 * service
109 * @return <code>true</code> if the referenced service matches the
110 * target of this PID.
111 */
112 public boolean matchesTarget( ServiceReference<?> reference )
113 {
Felix Meschbergere9502bd2012-07-02 14:57:33 +0000114 // already unregistered
115 final Bundle serviceBundle = reference.getBundle();
116 if ( serviceBundle == null )
117 {
118 return false;
119 }
120
Felix Meschbergere7f513f2012-07-02 14:00:02 +0000121 // This is not really targeted
122 if ( this.symbolicName == null )
123 {
124 return true;
125 }
126
Felix Meschbergere7f513f2012-07-02 14:00:02 +0000127 // bundle symbolic names don't match
128 if ( !this.symbolicName.equals( serviceBundle.getSymbolicName() ) )
129 {
130 return false;
131 }
132
Felix Meschbergere9502bd2012-07-02 14:57:33 +0000133 // no more specific target
Felix Meschbergere7f513f2012-07-02 14:00:02 +0000134 if ( this.version == null )
135 {
136 return true;
137 }
Felix Meschbergere9502bd2012-07-02 14:57:33 +0000138
139 // bundle version does not match
140 Object v = serviceBundle.getHeaders().get( Constants.BUNDLE_VERSION );
141 Version s = ( v == null ) ? Version.emptyVersion : new Version( v.toString() );
142 if ( !this.version.equals( s.toString() ) )
Felix Meschbergere7f513f2012-07-02 14:00:02 +0000143 {
144 return false;
145 }
146
147 // assert bundle location match
148 return this.location == null || this.location.equals( serviceBundle.getLocation() );
149 }
150
151
152 /**
Felix Meschberger382a19b2012-07-03 09:45:14 +0000153 * Gets the raw PID with which this instance has been created.
154 * <p>
155 * If an actual service PID contains pipe symbols that PID might be
156 * considered being targeted PID without it actually being one. This
157 * method provides access to the raw PID to allow for such services to
158 * be configured.
159 */
160 public String getRawPid()
161 {
162 return rawPid;
163 }
164
165 /**
Felix Meschbergere7f513f2012-07-02 14:00:02 +0000166 * Returns the service PID of this targeted PID which basically is
167 * the targeted PID without the targeting information.
Felix Meschbergere7f513f2012-07-02 14:00:02 +0000168 */
169 public String getServicePid()
170 {
171 return servicePid;
172 }
173
174 /**
175 * Returns how string this <code>TargetedPID</code> matches the
176 * given <code>ServiceReference</code>. The return value is one of
177 * those listed in the table:
178 *
179 * <table>
180 * <tr><th>level</th><th>Description</th></tr>
181 * <tr><td>-1</td><td>The targeted PID does not match at all. This means
182 * that either the service PID, the bundle's symbolic name, the
183 * bundle's version or bundle's location does not match the
184 * respective non-<code>null</code> parts of the targeted PID.
185 * This value is also returned if the raw PID fully matches the
186 * service PID.</td></tr>
187 * <tr><td>0</td><td>The targeted PID only has the service PID which
188 * also matches. The bundle's symbolic name, version, and
189 * location are not considered.</td></tr>
190 * <tr><td>1</td><td>The targeted PID only has the service PID and
191 * bundle symbolic name which both match. The bundle's version and
192 * location are not considered.</td></tr>
193 * <tr><td>2</td><td>The targeted PID only has the service PID, bundle
194 * symbolic name and version which all match. The bundle's
195 * location is not considered.</td></tr>
196 * <tr><td>3</td><td>The targeted PID has the service PID as well as
197 * the bundle symbolic name, version, and location which all
198 * match.</td></tr>
199 * </table>
200 *
201 * @param ref
202 * @return
203 */
204 public int matchLevel( final ServiceReference ref )
205 {
Felix Meschberger382a19b2012-07-03 09:45:14 +0000206
207 // TODO: this fails on multi-value PID properties !
Felix Meschbergere7f513f2012-07-02 14:00:02 +0000208 final Object servicePid = ref.getProperty( Constants.SERVICE_PID );
209
210 // in case the service PID contains | characters, this allows
211 // it to match the raw version of the targeted PID
212 if ( this.rawPid.equals( servicePid ) )
213 {
214 return 1;
215 }
216
217 if ( !this.servicePid.equals( servicePid ) )
218 {
219 return -1;
220 }
221
222 if ( this.symbolicName == null )
223 {
224 return 0;
225 }
226
227 final Bundle refBundle = ref.getBundle();
228 if ( !this.symbolicName.equals( refBundle.getSymbolicName() ) )
229 {
230 return -1;
231 }
232
233 if ( this.version == null )
234 {
235 return 1;
236 }
237
238 if ( !this.version.equals( refBundle.getHeaders().get( Constants.BUNDLE_VERSION ) ) )
239 {
240 return -1;
241 }
242
243 if ( this.location == null )
244 {
245 return 2;
246 }
247
248 if ( !this.location.equals( refBundle.getLocation() ) )
249 {
250 return -1;
251 }
252
253 return 3;
254 }
255
256
257 @Override
258 public int hashCode()
259 {
260 return this.rawPid.hashCode();
261 }
262
263
264 @Override
265 public boolean equals( Object obj )
266 {
267 if ( obj == null )
268 {
269 return true;
270 }
271 else if ( obj == this )
272 {
273 return true;
274 }
275
276 // assume equality if same class and raw PID equals
277 if ( this.getClass() == obj.getClass() )
278 {
279 return this.rawPid.equals( ( ( TargetedPID ) obj ).rawPid );
280 }
281
282 // not the same class or different raw PID
283 return false;
284 }
285}