/******************************************************************************* * Copyright (c) 2015 IBH SYSTEMS GmbH. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBH SYSTEMS GmbH - initial API and implementation *******************************************************************************/ package org.eclipse.packagedrone.job.apm; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import org.eclipse.packagedrone.job.JobFactory; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.osgi.util.tracker.ServiceTracker; import org.osgi.util.tracker.ServiceTrackerCustomizer; public class FactoryManager { private final BundleContext context; private final Map<String, JobFactory> services = new HashMap<> (); private final Map<ServiceReference<JobFactory>, String> refMap = new HashMap<> (); private final ReadLock readLock; private final WriteLock writeLock; private final ServiceTracker<JobFactory, JobFactory> tracker; private final ServiceTrackerCustomizer<JobFactory, JobFactory> customizer = new ServiceTrackerCustomizer<JobFactory, JobFactory> () { @Override public JobFactory addingService ( final ServiceReference<JobFactory> reference ) { return handleAdding ( reference ); } @Override public void modifiedService ( final ServiceReference<JobFactory> reference, final JobFactory service ) { handleModified ( reference, service ); } @Override public void removedService ( final ServiceReference<JobFactory> reference, final JobFactory service ) { handleRemoved ( reference, service ); } }; private static class Locked implements AutoCloseable { private final Lock lock; public Locked ( final Lock lock ) { lock.lock (); this.lock = lock; } @Override public void close () { this.lock.unlock (); } } public FactoryManager ( final BundleContext context ) { this.context = context; final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock (); this.readLock = rwLock.readLock (); this.writeLock = rwLock.writeLock (); this.tracker = new ServiceTracker<> ( context, JobFactory.class, this.customizer ); } public void start () { try ( Locked l = new Locked ( this.writeLock ) ) { this.tracker.open (); } } public void stop () { try ( Locked l = new Locked ( this.writeLock ) ) { this.tracker.close (); } } private static String getId ( final ServiceReference<JobFactory> reference ) { final Object idValue = reference.getProperty ( JobFactory.FACTORY_ID ); if ( idValue instanceof String ) { return (String)idValue; } else if ( idValue instanceof String[] ) { return ( (String[])idValue )[0]; } else { return null; } } protected JobFactory handleAdding ( final ServiceReference<JobFactory> reference ) { try ( Locked l = new Locked ( this.writeLock ) ) { final String id = getId ( reference ); if ( id == null ) { return null; } final JobFactory service = this.context.getService ( reference ); putService ( id, reference, service ); return service; } } private void putService ( final String id, final ServiceReference<JobFactory> reference, final JobFactory service ) { this.services.put ( id, service ); this.refMap.put ( reference, id ); } protected void handleModified ( final ServiceReference<JobFactory> reference, final JobFactory service ) { try ( Locked l = new Locked ( this.writeLock ) ) { removeService ( reference ); final String newId = getId ( reference ); if ( newId == null ) { return; // service is still being tracked } putService ( newId, reference, service ); } } private void removeService ( final ServiceReference<JobFactory> reference ) { final String oldId = this.refMap.remove ( reference ); if ( oldId != null ) { this.services.remove ( oldId ); } } protected synchronized void handleRemoved ( final ServiceReference<JobFactory> reference, final JobFactory service ) { try ( Locked l = new Locked ( this.writeLock ) ) { removeService ( reference ); } } public Optional<JobFactory> getFactory ( final String id ) { try ( Locked l = new Locked ( this.readLock ) ) { return Optional.ofNullable ( this.services.get ( id ) ); } } }