| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.sling.rewriter.impl; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.ConcurrentHashMap; |
| |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.ServiceReference; |
| import org.osgi.util.tracker.ServiceTracker; |
| |
| /** |
| * This service tracker stores all services into a hash map. |
| */ |
| class HashingServiceTrackerCustomizer<T> extends ServiceTracker { |
| |
| public static final class Pair<T> { |
| |
| public final ServiceReference reference; |
| public final T service; |
| |
| public Pair(final ServiceReference r, final T s) { |
| this.reference = r; |
| this.service = s; |
| } |
| } |
| |
| public static final class Entry<T> { |
| |
| public volatile T service; |
| |
| public final List<Pair<T>> references = new ArrayList<Pair<T>>(); |
| |
| public void add(final ServiceReference ref, final T service) { |
| references.add(new Pair<T>(ref, service)); |
| Collections.sort(references, new Comparator<Pair<T>>() { |
| |
| @Override |
| public int compare(final Pair<T> o1, final Pair<T> o2) { |
| return o2.reference.compareTo(o1.reference); |
| } |
| }); |
| if ( references.get(0).reference == ref ) { |
| this.service = service; |
| } |
| } |
| |
| public void remove(final ServiceReference ref) { |
| if ( !references.isEmpty() ) { |
| boolean update = references.get(0).reference == ref; |
| final Iterator<Pair<T>> i = references.iterator(); |
| while ( i.hasNext() ) { |
| final Pair<T> pair = i.next(); |
| if ( pair.reference == ref ) { |
| i.remove(); |
| } |
| } |
| if ( update ) { |
| if ( references.isEmpty() ) { |
| this.service = null; |
| } else { |
| this.service = references.get(0).service; |
| } |
| } |
| } |
| } |
| } |
| |
| /** The services hashed by their name property. */ |
| private final Map<String, Entry<T>> services = new ConcurrentHashMap<String, Entry<T>>(); |
| |
| /** The bundle context. */ |
| protected final BundleContext context; |
| |
| public HashingServiceTrackerCustomizer(final BundleContext bc, final String serviceClassName) { |
| super(bc, serviceClassName, null); |
| this.context = bc; |
| } |
| |
| public T getFactory(final String type) { |
| final Entry<T> entry = services.get(type); |
| return entry == null ? null : entry.service; |
| } |
| |
| private String getType(final ServiceReference ref) { |
| final String type = (String) ref.getProperty(FactoryCache.PROPERTY_TYPE); |
| return type; |
| } |
| |
| /** |
| * @see org.osgi.util.tracker.ServiceTrackerCustomizer#addingService(org.osgi.framework.ServiceReference) |
| */ |
| @Override |
| public Object addingService(final ServiceReference reference) { |
| final String type = this.getType(reference); |
| @SuppressWarnings("unchecked") |
| final T factory = (type == null ? null : (T) this.context.getService(reference)); |
| if ( factory != null ) { |
| if ( FactoryCache.LOGGER.isDebugEnabled() ) { |
| FactoryCache.LOGGER.debug("Found service {}, type={}.", factory, type); |
| } |
| synchronized ( this ) { |
| Entry<T> entry = this.services.get(type); |
| if ( entry == null ) { |
| entry = new Entry<T>(); |
| this.services.put(type, entry); |
| } |
| entry.add(reference, factory); |
| } |
| } |
| return factory; |
| } |
| |
| /** |
| * @see org.osgi.util.tracker.ServiceTrackerCustomizer#removedService(org.osgi.framework.ServiceReference, java.lang.Object) |
| */ |
| @Override |
| public void removedService(final ServiceReference reference, final Object service) { |
| final String type = this.getType(reference); |
| if ( type != null ) { |
| synchronized ( this ) { |
| final Entry<T> entry = this.services.get(type); |
| if ( entry != null ) { |
| entry.remove(reference); |
| } |
| } |
| this.context.ungetService(reference); |
| } |
| } |
| } |