| /* |
| * 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.felix.prefs.impl; |
| |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| |
| import org.apache.felix.prefs.BackingStore; |
| import org.apache.felix.prefs.BackingStoreManager; |
| import org.apache.felix.prefs.PreferencesImpl; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleActivator; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.BundleEvent; |
| import org.osgi.framework.BundleListener; |
| import org.osgi.framework.ServiceFactory; |
| import org.osgi.framework.ServiceRegistration; |
| import org.osgi.service.prefs.BackingStoreException; |
| import org.osgi.service.prefs.PreferencesService; |
| import org.osgi.util.tracker.ServiceTracker; |
| |
| /** |
| * This activator registers itself as a service factory for the |
| * preferences service. |
| */ |
| public class PreferencesManager |
| implements BundleActivator, |
| BundleListener, |
| ServiceFactory<PreferencesService>, |
| BackingStoreManager { |
| |
| /** |
| * The map of already created services. For each client bundle |
| * a new service is created. |
| */ |
| protected final Map<Long, PreferencesServiceImpl> services = new HashMap<Long, PreferencesServiceImpl>(); |
| |
| /** The bundle context. */ |
| protected BundleContext context; |
| |
| /** The backing store service tracker. */ |
| protected ServiceTracker<BackingStore, BackingStore> storeTracker; |
| |
| /** The default store which is used if no service can be found. */ |
| protected BackingStore defaultStore; |
| |
| /** Tracking count for the store tracker to detect changes. */ |
| protected int storeTrackingCount = -1; |
| |
| /** |
| * @see org.osgi.framework.BundleListener#bundleChanged(org.osgi.framework.BundleEvent) |
| */ |
| public void bundleChanged(final BundleEvent event) { |
| if (event.getType() == BundleEvent.UNINSTALLED) { |
| final Long bundleId = new Long(event.getBundle().getBundleId()); |
| synchronized (this.services) { |
| try { |
| final BackingStore store = this.getStore(); |
| // Only in the case we're already being stopped, this situation could occur, see FELIX-3334 |
| if (store != null) { |
| store.remove(bundleId); |
| } |
| } |
| catch (final BackingStoreException ignore) { |
| // we ignore this for now |
| } |
| this.services.remove(bundleId); |
| } |
| } |
| } |
| |
| /** |
| * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) |
| */ |
| public void start(final BundleContext context) throws Exception { |
| this.context = context; |
| |
| // create the tracker for our backing store |
| this.storeTracker = new ServiceTracker<BackingStore, BackingStore>(context, BackingStore.class, null); |
| this.storeTracker.open(); |
| |
| // register this activator as a bundle lister |
| context.addBundleListener(this); |
| |
| // finally register the service factory for the preferences service |
| context.registerService(PreferencesService.class.getName(), this, null); |
| } |
| |
| /** |
| * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) |
| */ |
| public void stop(final BundleContext context) throws Exception { |
| // if we get stopped, we should save all in memory representations |
| synchronized (this.services) { |
| final Iterator<PreferencesServiceImpl> i = this.services.values().iterator(); |
| while (i.hasNext()) { |
| final PreferencesServiceImpl service = i.next(); |
| this.save(service); |
| } |
| this.services.clear(); |
| } |
| // stop tracking store service |
| if (this.storeTracker != null) { |
| this.storeTracker.close(); |
| this.storeTracker = null; |
| } |
| this.defaultStore = null; |
| |
| |
| this.context = null; |
| } |
| |
| /** |
| * @see org.osgi.framework.ServiceFactory#getService(org.osgi.framework.Bundle, org.osgi.framework.ServiceRegistration) |
| */ |
| public PreferencesService getService(final Bundle bundle, |
| final ServiceRegistration<PreferencesService> reg) { |
| final Long bundleId = new Long(bundle.getBundleId()); |
| |
| synchronized (this.services) { |
| PreferencesServiceImpl service; |
| // do we already have created a service for this bundle? |
| service = this.services.get(bundleId); |
| |
| if (service == null) { |
| // create a new service instance |
| service = new PreferencesServiceImpl(bundleId, this); |
| this.services.put(bundleId, service); |
| } |
| return service; |
| } |
| } |
| |
| /** |
| * @see org.osgi.framework.ServiceFactory#ungetService(org.osgi.framework.Bundle, org.osgi.framework.ServiceRegistration, java.lang.Object) |
| */ |
| public void ungetService(final Bundle bundle, |
| final ServiceRegistration<PreferencesService> reg, |
| final PreferencesService s) { |
| final Long bundleId = new Long(bundle.getBundleId()); |
| // we save all the preferences |
| synchronized (this.services) { |
| final PreferencesServiceImpl service = this.services.get(bundleId); |
| if (service != null) { |
| this.save(service); |
| } |
| } |
| } |
| |
| /** |
| * Save all preferences for this service. |
| * |
| * @param service |
| */ |
| protected void save(final PreferencesServiceImpl service) { |
| final Iterator<PreferencesImpl> i = service.getAllPreferences().iterator(); |
| while (i.hasNext()) { |
| final PreferencesImpl prefs = i.next(); |
| try { |
| this.getStore().store(prefs); |
| } |
| catch (final BackingStoreException ignore) { |
| // we ignore this |
| } |
| } |
| } |
| |
| /** |
| * @see org.apache.felix.prefs.BackingStoreManager#getStore() |
| */ |
| public BackingStore getStore() throws BackingStoreException { |
| BackingStore service = null; |
| ServiceTracker<BackingStore, BackingStore> storeTracker = this.storeTracker; |
| |
| // Only possible if we're not stopped already... |
| if (storeTracker != null) { |
| // has the service changed? |
| int currentCount = storeTracker.getTrackingCount(); |
| service = storeTracker.getService(); |
| if (service != null && this.storeTrackingCount < currentCount) { |
| this.storeTrackingCount = currentCount; |
| this.cleanupStore(service); |
| } |
| if (service == null) { |
| // no service available use default store |
| if (this.defaultStore == null) { |
| synchronized (this) { |
| if (this.defaultStore == null) { |
| this.defaultStore = new DataFileBackingStoreImpl(this.context); |
| this.cleanupStore(this.defaultStore); |
| } |
| } |
| } |
| service = this.defaultStore; |
| } |
| } |
| |
| if (service == null) { |
| // (still) no service available; cannot fulfill this request... |
| throw new BackingStoreException("No backing store!"); |
| } |
| |
| return service; |
| } |
| |
| /** |
| * Clean up the store and remove preferences for deleted bundles. |
| * |
| * @param store |
| */ |
| protected void cleanupStore(final BackingStore store) { |
| // check which bundles are available |
| final Long[] availableBundleIds = store.availableBundles(); |
| |
| // now check the bundles, for which we have preferences, if they are still |
| // in service and delete the preferences where the bundles are out of service. |
| // we synchronize on services in order to get not disturbed by a bundle event |
| synchronized (this.services) { |
| for (int i = 0; i < availableBundleIds.length; i++) { |
| final Long bundleId = availableBundleIds[i]; |
| final Bundle bundle = this.context.getBundle(bundleId.longValue()); |
| if (bundle == null || bundle.getState() == Bundle.UNINSTALLED) { |
| try { |
| store.remove(bundleId); |
| } |
| catch (final BackingStoreException ignore) { |
| // we ignore this for now |
| } |
| } |
| } |
| } |
| } |
| } |