Xander Uiterlinden | b64a8e2 | 2012-04-03 14:42:23 +0000 | [diff] [blame^] | 1 | /* |
| 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 | */ |
| 19 | package org.apache.felix.dm.impl.index; |
| 20 | |
| 21 | import java.util.ArrayList; |
| 22 | import java.util.HashMap; |
| 23 | import java.util.Iterator; |
| 24 | import java.util.List; |
| 25 | import java.util.Map; |
| 26 | import java.util.Set; |
| 27 | import java.util.SortedSet; |
| 28 | import java.util.TreeSet; |
| 29 | |
| 30 | import org.apache.felix.dm.DependencyManager; |
| 31 | import org.apache.felix.dm.FilterIndex; |
| 32 | import org.apache.felix.dm.ServiceUtil; |
| 33 | import org.apache.felix.dm.tracker.ServiceTracker; |
| 34 | import org.apache.felix.dm.tracker.ServiceTrackerCustomizer; |
| 35 | import org.osgi.framework.BundleContext; |
| 36 | import org.osgi.framework.Constants; |
| 37 | import org.osgi.framework.InvalidSyntaxException; |
| 38 | import org.osgi.framework.ServiceEvent; |
| 39 | import org.osgi.framework.ServiceListener; |
| 40 | import org.osgi.framework.ServiceReference; |
| 41 | |
| 42 | /** |
| 43 | * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> |
| 44 | */ |
| 45 | public class AdapterFilterIndex implements FilterIndex, ServiceTrackerCustomizer { |
| 46 | // (&(objectClass=com.beinformed.product.platform.interfaces.Resource)(|(service.id=18233)(org.apache.felix.dependencymanager.aspect=18233))) |
| 47 | 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: |
| 94 | // (&(objectClass=com.beinformed.product.platform.interfaces.Resource)(|(service.id=18233)(org.apache.felix.dependencymanager.aspect=18233))) |
| 95 | 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)); |
| 128 | } |
| 129 | if (list != null) { |
| 130 | Iterator iterator = list.iterator(); |
| 131 | while (iterator.hasNext()) { |
| 132 | result.add((ServiceReference) iterator.next()); |
| 133 | } |
| 134 | } |
| 135 | } |
| 136 | return result; |
| 137 | } |
| 138 | |
| 139 | public void serviceChanged(ServiceEvent event) { |
| 140 | ServiceReference reference = event.getServiceReference(); |
| 141 | Long sid = ServiceUtil.getServiceIdObject(reference); |
| 142 | synchronized (m_sidToListenersMap) { |
| 143 | List /* <Integer, ServiceListener> */ list = (ArrayList) m_sidToListenersMap.get(sid); |
| 144 | if (list != null) { |
| 145 | Iterator iterator = list.iterator(); |
| 146 | while (iterator.hasNext()) { |
| 147 | ServiceListener listener = (ServiceListener) iterator.next(); |
| 148 | listener.serviceChanged(event); |
| 149 | } |
| 150 | } |
| 151 | } |
| 152 | } |
| 153 | |
| 154 | public void addServiceListener(ServiceListener listener, String filter) { |
| 155 | FilterData data = getFilterData(null, filter); |
| 156 | if (data != null) { |
| 157 | Long sidObject = Long.valueOf(data.serviceId); |
| 158 | synchronized (m_sidToListenersMap) { |
| 159 | List /* <ServiceListener> */ listeners = (List) m_sidToListenersMap.get(sidObject); |
| 160 | if (listeners == null) { |
| 161 | listeners = new ArrayList(); |
| 162 | m_sidToListenersMap.put(sidObject, listeners); |
| 163 | } |
| 164 | listeners.add(listener); |
| 165 | } |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | public void removeServiceListener(ServiceListener listener) { |
| 170 | synchronized (m_sidToListenersMap) { |
| 171 | String filter = (String) m_listenerToFilterMap.remove(listener); |
| 172 | FilterData data = getFilterData(null, filter); |
| 173 | if (data != null) { |
| 174 | Long sidObject = Long.valueOf(data.serviceId); |
| 175 | List /* ServiceListener */ listeners = (List) m_sidToListenersMap.get(sidObject); |
| 176 | if (listeners != null) { |
| 177 | listeners.remove(listener); |
| 178 | } |
| 179 | } |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | public Object addingService(ServiceReference reference) { |
| 184 | BundleContext context; |
| 185 | synchronized (m_lock) { |
| 186 | context = m_context; |
| 187 | } |
| 188 | if (context != null) { |
| 189 | return context.getService(reference); |
| 190 | } |
| 191 | else { |
| 192 | throw new IllegalStateException("No valid bundle context."); |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | public void addedService(ServiceReference reference, Object service) { |
| 197 | add(reference); |
| 198 | } |
| 199 | |
| 200 | public void modifiedService(ServiceReference reference, Object service) { |
| 201 | modify(reference); |
| 202 | } |
| 203 | |
| 204 | public void removedService(ServiceReference reference, Object service) { |
| 205 | remove(reference); |
| 206 | } |
| 207 | |
| 208 | public void add(ServiceReference reference) { |
| 209 | Long sid = ServiceUtil.getServiceIdObject(reference); |
| 210 | synchronized (m_sidToServiceReferencesMap) { |
| 211 | Set list = (Set) m_sidToServiceReferencesMap.get(sid); |
| 212 | if (list == null) { |
| 213 | list = new TreeSet(); |
| 214 | m_sidToServiceReferencesMap.put(sid, list); |
| 215 | } |
| 216 | list.add(reference); |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | public void modify(ServiceReference reference) { |
| 221 | remove(reference); |
| 222 | add(reference); |
| 223 | } |
| 224 | |
| 225 | public void remove(ServiceReference reference) { |
| 226 | Long sid = ServiceUtil.getServiceIdObject(reference); |
| 227 | synchronized (m_sidToServiceReferencesMap) { |
| 228 | Set list = (Set) m_sidToServiceReferencesMap.get(sid); |
| 229 | if (list != null) { |
| 230 | list.remove(reference); |
| 231 | } |
| 232 | } |
| 233 | } |
| 234 | |
| 235 | public String toString() { |
| 236 | StringBuffer sb = new StringBuffer(); |
| 237 | sb.append("AdapterFilterIndex["); |
| 238 | sb.append("S2L: " + m_sidToListenersMap.size()); |
| 239 | sb.append(", S2SR: " + m_sidToServiceReferencesMap.size()); |
| 240 | sb.append(", L2F: " + m_listenerToFilterMap.size()); |
| 241 | sb.append("]"); |
| 242 | return sb.toString(); |
| 243 | } |
| 244 | |
| 245 | /** Structure to hold internal filter data. */ |
| 246 | private static class FilterData { |
| 247 | public long serviceId; |
| 248 | } |
| 249 | |
| 250 | } |