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