blob: 91bb74ab36f9eafb59f285513dd32d734f5478d8 [file] [log] [blame]
Marcel Offermans2f6e82b2011-04-19 07:19:58 +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 */
Marcel Offermans90ba4fa2011-04-27 07:57:44 +000019package org.apache.felix.dm.impl.index;
Marcel Offermans227dd712011-04-19 07:14:22 +000020
21import java.util.ArrayList;
22import java.util.Arrays;
23import java.util.HashMap;
24import java.util.HashSet;
25import java.util.Iterator;
26import java.util.List;
27import java.util.Map;
28import java.util.Set;
29import java.util.concurrent.CopyOnWriteArrayList;
30
Marcel Offermans837cc962011-04-27 08:00:29 +000031import org.apache.felix.dm.FilterIndex;
Marcel Offermans227dd712011-04-19 07:14:22 +000032import org.apache.felix.dm.tracker.ServiceTracker;
33import org.apache.felix.dm.tracker.ServiceTrackerCustomizer;
34import org.osgi.framework.BundleContext;
35import org.osgi.framework.Constants;
36import org.osgi.framework.InvalidSyntaxException;
37import org.osgi.framework.ServiceEvent;
38import org.osgi.framework.ServiceListener;
39import org.osgi.framework.ServiceReference;
40
Marcel Offermans5be5f142011-04-26 10:47:12 +000041/**
42 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
43 */
Marcel Offermans227dd712011-04-19 07:14:22 +000044public class MultiPropertyExactFilter implements FilterIndex, ServiceTrackerCustomizer {
45 private final Object m_lock = new Object();
46 private ServiceTracker m_tracker;
47 private BundleContext m_context;
48 private final List /* <String> */ m_propertyKeys;
49 private final Map /* <String, List<ServiceReference>> */ m_keyToServiceReferencesMap = new HashMap();
50 private final Map /* <String, List<ServiceListener>> */ m_keyToListenersMap = new HashMap();
51 private final Map /* <ServiceListener, String> */ m_listenerToFilterMap = new HashMap();
52
53 public MultiPropertyExactFilter(String[] propertyKeys) {
54 String[] keys = (String[]) Arrays.copyOf(propertyKeys, propertyKeys.length);
55 Arrays.sort(keys);
56 m_propertyKeys = Arrays.asList(keys);
57 }
58
59 public void open(BundleContext context) {
60 synchronized (m_lock) {
61 if (m_context != null) {
62 throw new IllegalStateException("Filter already open.");
63 }
64 try {
65 m_tracker = new ServiceTracker(context, context.createFilter("(" + Constants.OBJECTCLASS + "=*)"), this);
66 }
67 catch (InvalidSyntaxException e) {
68 throw new Error();
69 }
70 m_context = context;
71 }
Marcel Offermansd30ac1c2011-05-10 11:47:42 +000072 m_tracker.open(true, true);
Marcel Offermans227dd712011-04-19 07:14:22 +000073 }
74
75 public void close() {
76 ServiceTracker tracker;
77 synchronized (m_lock) {
78 if (m_context == null) {
79 throw new IllegalStateException("Filter already closed.");
80 }
81 tracker = m_tracker;
82 m_tracker = null;
83 m_context = null;
84 }
85 tracker.close();
86 }
87
Marcel Offermans5c4343a2011-04-19 09:50:24 +000088 public List /* <ServiceReference> */ getAllServiceReferences(String clazz, String filter) {
Marcel Offermans227dd712011-04-19 07:14:22 +000089 List /* <ServiceReference> */ result = new ArrayList();
90 List keys = createKeysFromFilter(clazz, filter);
91 Iterator iterator = keys.iterator();
92 while (iterator.hasNext()) {
93 String key = (String) iterator.next();
94 ServiceReference reference;
95 synchronized (m_keyToServiceReferencesMap) {
96 List references = (List) m_keyToServiceReferencesMap.get(key);
97 if (references != null) {
98 result.addAll(references);
99 }
100 }
101 }
Marcel Offermans5c4343a2011-04-19 09:50:24 +0000102 return result;
Marcel Offermans227dd712011-04-19 07:14:22 +0000103 }
104
105 public Object addingService(ServiceReference reference) {
106 BundleContext context;
107 synchronized (m_lock) {
108 context = m_context;
109 }
110 if (context != null) {
111 return context.getService(reference);
112 }
113 else {
114 throw new IllegalStateException("No valid bundle context.");
115 }
116 }
117
118 public void addedService(ServiceReference reference, Object service) {
119 if (isApplicable(reference.getPropertyKeys())) {
Marcel Offermans5c4343a2011-04-19 09:50:24 +0000120 add(reference);
Marcel Offermans227dd712011-04-19 07:14:22 +0000121 }
122 }
123
124 public void modifiedService(ServiceReference reference, Object service) {
125 if (isApplicable(reference.getPropertyKeys())) {
Marcel Offermans5c4343a2011-04-19 09:50:24 +0000126 modify(reference);
Marcel Offermans227dd712011-04-19 07:14:22 +0000127 }
128 }
129
130 public void removedService(ServiceReference reference, Object service) {
131 if (isApplicable(reference.getPropertyKeys())) {
132 remove(reference);
133 }
134 }
Marcel Offermans5c4343a2011-04-19 09:50:24 +0000135
136 public void add(ServiceReference reference) {
Marcel Offermans227dd712011-04-19 07:14:22 +0000137 List /* <String> */ keys = createKeys(reference);
138 synchronized (m_keyToServiceReferencesMap) {
Marcel Offermans5c4343a2011-04-19 09:50:24 +0000139 for (int i = 0; i < keys.size(); i++) {
140 List /* <ServiceReference> */ references = (List) m_keyToServiceReferencesMap.get(keys.get(i));
141 if (references == null) {
142 references = new ArrayList();
143 m_keyToServiceReferencesMap.put(keys.get(i), references);
144 }
145 references.add(reference);
146 }
147 }
148 }
149
150 public void modify(ServiceReference reference) {
151 List /* <String> */ keys = createKeys(reference);
152 synchronized (m_keyToServiceReferencesMap) {
153 // TODO this is a quite expensive linear scan over the existing collection
154 // because we first need to remove any existing references and they can be
155 // all over the place :)
156 Iterator iterator = m_keyToServiceReferencesMap.values().iterator();
157 while (iterator.hasNext()) {
158 List /* <ServiceReference> */ list = (List) iterator.next();
159 if (list != null) {
160 Iterator i2 = list.iterator();
161 while (i2.hasNext()) {
162 ServiceReference ref = (ServiceReference) i2.next();
163 if (ref.equals(reference)) {
164 i2.remove();
165 }
166 }
167 }
168 }
169
Marcel Offermans227dd712011-04-19 07:14:22 +0000170 for (int i = 0; i < keys.size(); i++) {
171 List /* <ServiceReference> */ references = (List) m_keyToServiceReferencesMap.get(keys.get(i));
172 if (references == null) {
173 references = new ArrayList();
174 m_keyToServiceReferencesMap.put(keys.get(i), references);
175 }
176 references.add(reference);
177 }
178 }
179 }
180
181 public void remove(ServiceReference reference) {
182 List /* <String> */ keys = createKeys(reference);
183 synchronized (m_keyToServiceReferencesMap) {
184 for (int i = 0; i < keys.size(); i++) {
185 List /* <ServiceReference> */ references = (List) m_keyToServiceReferencesMap.get(keys.get(i));
186 if (references != null) {
187 references.remove(reference);
188 }
189 }
190 }
191 }
192
193 public boolean isApplicable(String[] propertyKeys) {
194 List list = Arrays.asList(propertyKeys);
195 Iterator iterator = m_propertyKeys.iterator();
196 while (iterator.hasNext()) {
197 String item = (String) iterator.next();
198 if (!(list.contains(item))) {
199 return false;
200 }
201 }
202 return true;
203 }
204
205 public static void main(String[] args) {
206
207 System.out.println("" + (new MultiPropertyExactFilter(new String[] { "objectClass", "repository", "path", "name" })).isApplicable(null, "(objectClass=abc)"));
208 }
209
210 public boolean isApplicable(String clazz, String filter) {
211 // "(&(a=b)(c=d))"
212 // "(&(&(a=b)(c=d))(objC=aaa))"
213 // startsWith "(&" en split in "(x=y)" -> elke x bestaat in m_propertykeys
214
215 // (&(objectClass=xyz)(&(a=x)(b=y)))
216
217 Set /* <String> */found = new HashSet();
218 if (filter != null && filter.startsWith("(&(objectClass=") && filter.contains(")(&(") && filter.endsWith(")))")) {
219 int i1 = filter.indexOf(")(&(");
220 String className = filter.substring("(&(objectClass=".length(), i1);
221 if (!m_propertyKeys.contains("objectClass")) {
222 return false;
223 }
224 else {
225 found.add("objectClass");
226 }
227 String[] parts = filter.substring(i1 + ")(&(".length(), filter.length() - ")))".length()).split("\\)\\(");
228 for (int i = 0; i < parts.length; i++) {
229 String part = parts[i];
230 String[] tuple = part.split("=");
231 if (!m_propertyKeys.contains(tuple[0])) {
232 return false;
233 }
234 else {
235 found.add(tuple[0]);
236 }
237 // TODO check value tuple[1]
238 }
239 return (found.size() == m_propertyKeys.size());
240 }
241 else if (filter != null && filter.startsWith("(&(") && filter.endsWith("))")) {
242 String[] parts = filter.substring(3, filter.length() - 2).split("\\)\\(");
243 for (int i = 0; i < parts.length; i++) {
244 String part = parts[i];
245 String[] tuple = part.split("=");
246 if (!m_propertyKeys.contains(tuple[0])) {
247 return false;
248 }
249 else {
250 found.add(tuple[0]);
251 }
252 // TODO check value tuple[1]
253 }
254 return (found.size() == m_propertyKeys.size());
255 }
256 else if (filter != null && filter.startsWith("(") && filter.endsWith(")") && m_propertyKeys.size() == 1) { // TODO quick hack
257 String part = filter.substring(1, filter.length() - 1);
258 String[] tuple = part.split("=");
259 if (!m_propertyKeys.contains(tuple[0])) {
260 return false;
261 }
262 else {
263 return true;
264 }
265 }
266 else if (clazz != null && filter == null && m_propertyKeys.size() == 1 && m_propertyKeys.get(0).equals(Constants.OBJECTCLASS)) {
267 return true;
268 }
269 return false;
270 }
271
272 private List /* <String> */ createKeys(ServiceReference reference) {
273 List /* <String> */ results = new ArrayList();
274
275 results.add("");
276
277 String[] keys = reference.getPropertyKeys();
278 Arrays.sort(keys);
279 StringBuffer result = new StringBuffer();
280 for (int i = 0; i < keys.length; i++) {
281 String key = keys[i];
282 if (m_propertyKeys.contains(key)) {
283 Object value = reference.getProperty(key);
284 if (value instanceof String[]) {
285 String[] values = (String[]) value;
286 List newResults = new ArrayList();
287 for (int j = 0; j < values.length; j++) {
288 String val = values[j];
289 for (int k = 0; k < results.size(); k++) {
290 String head = (String) results.get(k);
291 if (head != null && head.length() > 0) {
292 head = head + ";";
293 }
294 newResults.add(head + key + "=" + val);
295 }
296 }
297 results = newResults;
298 }
299 else {
300 for (int k = 0; k < results.size(); k++) {
301 String head = (String) results.get(k);
302 if (head != null && head.length() > 0) {
303 head = head + ";";
304 }
305 results.set(k, head + key + "=" + value);
306 }
307 }
308 }
309 }
310 return results;
311 }
312
313 private List /* <String> */ createKeysFromFilter(String clazz, String filter) {
Marcel Offermans227dd712011-04-19 07:14:22 +0000314 List result = new ArrayList();
315 StringBuffer index = new StringBuffer();
316 Iterator iterator = m_propertyKeys.iterator();
317 while (iterator.hasNext()) {
318 String key = (String) iterator.next();
319 if (index.length() > 0) {
320 index.append(';');
321 }
322 index.append(key);
323 index.append('=');
324 String value = null;
325 if (clazz != null && Constants.OBJECTCLASS.equals(key)) {
326 value = clazz;
327 } // (&(obC=a)(&(a=b)(c=d)))
328 if (filter != null) {
329 String startString = "(" + key + "=";
330 int i1 = filter.indexOf(startString);
331 if (i1 != -1) {
332 int i2 = filter.indexOf(")(", i1);
333 if (i2 == -1) {
334 if (filter.endsWith(")))")) {
335 i2 = filter.length() - 3;
336 }
337 else if (filter.endsWith("))")) {
338 i2 = filter.length() - 2;
339 }
340 else {
341 i2 = filter.length() - 1;
342 }
343 }
344 String value2 = filter.substring(i1 + startString.length(), i2);
345 if (value != null && !value.equals(value2)) {
346 // corner case: someone specified a clazz and
347 // also a filter containing a different clazz
348 return result;
349 }
350 value = value2;
351 }
352 }
353 index.append(value);
354 }
355 result.add(index.toString());
356 return result;
357 }
358
359 public void serviceChanged(ServiceEvent event) {
360 if (isApplicable(event.getServiceReference().getPropertyKeys())) {
361 List /* <String> */ keys = createKeys(event.getServiceReference());
362 List list = new ArrayList();
363 synchronized (m_keyToListenersMap) {
364 for (int i = 0; i < keys.size(); i++) {
365 String key = (String) keys.get(i);
366 List listeners = (List) m_keyToListenersMap.get(key);
367 if (listeners != null) {
368 list.addAll(listeners);
369 }
370 }
371 }
372 if (list != null) {
373 Iterator iterator = list.iterator();
374 while (iterator.hasNext()) {
375 ServiceListener listener = (ServiceListener) iterator.next();
376 listener.serviceChanged(event);
377 }
378 }
379 }
380 }
381
382 public void addServiceListener(ServiceListener listener, String filter) {
383 List keys = createKeysFromFilter(null, filter);
384 Iterator iterator = keys.iterator();
385 while (iterator.hasNext()) {
386 String key = (String) iterator.next();
387 synchronized (m_keyToListenersMap) {
388 List /* <ServiceListener> */ listeners = (List) m_keyToListenersMap.get(key);
389 if (listeners == null) {
390 listeners = new CopyOnWriteArrayList();
391 m_keyToListenersMap.put(key, listeners);
392 }
393 listeners.add(listener);
394 m_listenerToFilterMap.put(listener, filter);
395 }
396 }
397 }
398
399 public void removeServiceListener(ServiceListener listener) {
400 synchronized (m_keyToListenersMap) {
401 String filter = (String) m_listenerToFilterMap.remove(listener);
402 List keys = createKeysFromFilter(null, filter);
403 Iterator iterator = keys.iterator();
404 while (iterator.hasNext()) {
405 String key = (String) iterator.next();
406
407 boolean result = filter != null;
408 if (result) {
409 List /* <ServiceListener> */ listeners = (List) m_keyToListenersMap.get(key);
410 if (listeners != null) {
411 listeners.remove(listener);
412 }
413 // TODO actually, if listeners == null that would be strange....
414 }
415 }
416 }
417 }
418
419 public String toString() {
420 StringBuffer sb = new StringBuffer();
421 sb.append("MultiPropertyExactFilter[");
422 sb.append("K2L: " + m_keyToListenersMap.size());
423 sb.append(", K2SR: " + m_keyToServiceReferencesMap.size());
424 sb.append(", L2F: " + m_listenerToFilterMap.size());
425 sb.append("]");
426 return sb.toString();
427 }
428}