blob: 4d466e2f25352f146b6891a711b8178c74d2e8b8 [file] [log] [blame]
Xander Uiterlindenb64a8e22012-04-03 14:42:23 +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.dm.impl.index;
20
21import java.util.ArrayList;
22import java.util.HashMap;
23import java.util.Iterator;
24import java.util.List;
25import java.util.Map;
26import java.util.Set;
27import java.util.SortedSet;
28import java.util.TreeSet;
29
30import org.apache.felix.dm.DependencyManager;
31import org.apache.felix.dm.FilterIndex;
32import org.apache.felix.dm.ServiceUtil;
33import org.apache.felix.dm.tracker.ServiceTracker;
34import org.apache.felix.dm.tracker.ServiceTrackerCustomizer;
35import org.osgi.framework.BundleContext;
36import org.osgi.framework.Constants;
37import org.osgi.framework.InvalidSyntaxException;
38import org.osgi.framework.ServiceEvent;
39import org.osgi.framework.ServiceListener;
40import org.osgi.framework.ServiceReference;
41
42/**
43 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
44 */
45public class AdapterFilterIndex implements FilterIndex, ServiceTrackerCustomizer {
Xander Uiterlinden4f80b362012-04-06 12:07:03 +000046 // (&(objectClass=foo.Bar)(|(service.id=18233)(org.apache.felix.dependencymanager.aspect=18233)))
Xander Uiterlindenb64a8e22012-04-03 14:42:23 +000047 private static final String FILTER_START = "(&(" + Constants.OBJECTCLASS + "=";
48 private static final String FILTER_SUBSTRING_0 = ")(|(" + Constants.SERVICE_ID + "=";
49 private static final String FILTER_SUBSTRING_1 = ")(" + DependencyManager.ASPECT + "=";
50 private static final String FILTER_END = ")))";
51 private final Object m_lock = new Object();
52 private ServiceTracker m_tracker;
53 private BundleContext m_context;
54 private final Map /* <Long, SortedSet<ServiceReference>> */ m_sidToServiceReferencesMap = new HashMap();
55 private final Map /* <String, List<ServiceListener>> */ m_sidToListenersMap = new HashMap();
56 private final Map /* <ServiceListener, String> */ m_listenerToFilterMap = new HashMap();
57
58 public void open(BundleContext context) {
59 synchronized (m_lock) {
60 if (m_context != null) {
61 throw new IllegalStateException("Filter already open.");
62 }
63 try {
64 m_tracker = new ServiceTracker(context, context.createFilter("(" + Constants.OBJECTCLASS + "=*)"), this);
65 }
66 catch (InvalidSyntaxException e) {
67 throw new Error();
68 }
69 m_context = context;
70 }
71 m_tracker.open(true, true);
72 }
73
74 public void close() {
75 ServiceTracker tracker;
76 synchronized (m_lock) {
77 if (m_context == null) {
78 throw new IllegalStateException("Filter already closed.");
79 }
80 tracker = m_tracker;
81 m_tracker = null;
82 m_context = null;
83 }
84 tracker.close();
85 }
86
87 public boolean isApplicable(String clazz, String filter) {
88 return getFilterData(clazz, filter) != null;
89 }
90
91 /** Returns a value object with the relevant filter data, or <code>null</code> if this filter was not valid. */
92 private FilterData getFilterData(String clazz, String filter) {
93 // something like:
Xander Uiterlinden4f80b362012-04-06 12:07:03 +000094 // (&(objectClass=foo.Bar)(|(service.id=18233)(org.apache.felix.dependencymanager.aspect=18233)))
Xander Uiterlindenb64a8e22012-04-03 14:42:23 +000095 if ((filter != null)
96 && (filter.startsWith(FILTER_START))
97 && (filter.endsWith(FILTER_END))
98 ) {
99 // service-id =
100 int i0 = filter.indexOf(FILTER_SUBSTRING_0);
101 if (i0 == -1) {
102 return null;
103 }
104 // org.apache.felix.dependencymanager.aspect =
105 int i1 = filter.indexOf(FILTER_SUBSTRING_1);
106 if (i1 == -1 || i1 <= i0) {
107 return null;
108 }
109 long sid = Long.parseLong(filter.substring(i0 + FILTER_SUBSTRING_0.length(), i1));
110 long sid2 = Long.parseLong(filter.substring(i1 + FILTER_SUBSTRING_1.length(), filter.length() - FILTER_END.length()));
111 if (sid != sid2) {
112 return null;
113 }
114 FilterData result = new FilterData();
115 result.serviceId = sid;
116 return result;
117 }
118 return null;
119 }
120
121 public List getAllServiceReferences(String clazz, String filter) {
122 List /* <ServiceReference> */ result = new ArrayList();
123 FilterData data = getFilterData(clazz, filter);
124 if (data != null) {
125 SortedSet /* <ServiceReference> */ list = null;
126 synchronized (m_sidToServiceReferencesMap) {
127 list = (SortedSet) m_sidToServiceReferencesMap.get(Long.valueOf(data.serviceId));
Xander Uiterlinden4f80b362012-04-06 12:07:03 +0000128 if (list != null) {
129 Iterator iterator = list.iterator();
130 while (iterator.hasNext()) {
131 result.add((ServiceReference) iterator.next());
132 }
133 }
Xander Uiterlindenb64a8e22012-04-03 14:42:23 +0000134 }
Xander Uiterlindenb64a8e22012-04-03 14:42:23 +0000135 }
136 return result;
137 }
138
139 public void serviceChanged(ServiceEvent event) {
140 ServiceReference reference = event.getServiceReference();
141 Long sid = ServiceUtil.getServiceIdObject(reference);
Xander Uiterlinden4f80b362012-04-06 12:07:03 +0000142 List /* <ServiceListener> */ notificationList = new ArrayList();
Xander Uiterlindenb64a8e22012-04-03 14:42:23 +0000143 synchronized (m_sidToListenersMap) {
Xander Uiterlinden4f80b362012-04-06 12:07:03 +0000144 List /* <ServiceListener> */ list = (ArrayList) m_sidToListenersMap.get(sid);
Xander Uiterlindenb64a8e22012-04-03 14:42:23 +0000145 if (list != null) {
Xander Uiterlinden4f80b362012-04-06 12:07:03 +0000146 notificationList.addAll(list);
Xander Uiterlindenb64a8e22012-04-03 14:42:23 +0000147 }
148 }
Xander Uiterlinden4f80b362012-04-06 12:07:03 +0000149 // notify
150 Iterator iterator = notificationList.iterator();
151 while (iterator.hasNext()) {
152 ServiceListener listener = (ServiceListener) iterator.next();
153 listener.serviceChanged(event);
154 }
Xander Uiterlindenb64a8e22012-04-03 14:42:23 +0000155 }
156
157 public void addServiceListener(ServiceListener listener, String filter) {
158 FilterData data = getFilterData(null, filter);
159 if (data != null) {
160 Long sidObject = Long.valueOf(data.serviceId);
161 synchronized (m_sidToListenersMap) {
162 List /* <ServiceListener> */ listeners = (List) m_sidToListenersMap.get(sidObject);
163 if (listeners == null) {
164 listeners = new ArrayList();
165 m_sidToListenersMap.put(sidObject, listeners);
166 }
167 listeners.add(listener);
168 }
169 }
170 }
171
172 public void removeServiceListener(ServiceListener listener) {
173 synchronized (m_sidToListenersMap) {
174 String filter = (String) m_listenerToFilterMap.remove(listener);
175 FilterData data = getFilterData(null, filter);
176 if (data != null) {
177 Long sidObject = Long.valueOf(data.serviceId);
178 List /* ServiceListener */ listeners = (List) m_sidToListenersMap.get(sidObject);
179 if (listeners != null) {
180 listeners.remove(listener);
181 }
182 }
183 }
184 }
185
186 public Object addingService(ServiceReference reference) {
187 BundleContext context;
188 synchronized (m_lock) {
189 context = m_context;
190 }
191 if (context != null) {
192 return context.getService(reference);
193 }
194 else {
195 throw new IllegalStateException("No valid bundle context.");
196 }
197 }
198
199 public void addedService(ServiceReference reference, Object service) {
200 add(reference);
201 }
202
203 public void modifiedService(ServiceReference reference, Object service) {
204 modify(reference);
205 }
206
207 public void removedService(ServiceReference reference, Object service) {
208 remove(reference);
209 }
210
211 public void add(ServiceReference reference) {
212 Long sid = ServiceUtil.getServiceIdObject(reference);
213 synchronized (m_sidToServiceReferencesMap) {
214 Set list = (Set) m_sidToServiceReferencesMap.get(sid);
215 if (list == null) {
216 list = new TreeSet();
217 m_sidToServiceReferencesMap.put(sid, list);
218 }
219 list.add(reference);
220 }
221 }
222
223 public void modify(ServiceReference reference) {
224 remove(reference);
225 add(reference);
226 }
227
228 public void remove(ServiceReference reference) {
229 Long sid = ServiceUtil.getServiceIdObject(reference);
230 synchronized (m_sidToServiceReferencesMap) {
231 Set list = (Set) m_sidToServiceReferencesMap.get(sid);
232 if (list != null) {
233 list.remove(reference);
234 }
235 }
236 }
237
238 public String toString() {
239 StringBuffer sb = new StringBuffer();
240 sb.append("AdapterFilterIndex[");
241 sb.append("S2L: " + m_sidToListenersMap.size());
242 sb.append(", S2SR: " + m_sidToServiceReferencesMap.size());
243 sb.append(", L2F: " + m_listenerToFilterMap.size());
244 sb.append("]");
245 return sb.toString();
246 }
247
248 /** Structure to hold internal filter data. */
249 private static class FilterData {
250 public long serviceId;
251 }
252
253}