All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.paremus.dosgi.net.activator.ManagedServiceFactoryImpl Maven / Gradle / Ivy

Go to download

An implementation of OSGi Remote Service Admin, enhanced to provide support for OSGi Promises and streaming using OSGi PushStreams.

The newest version!
/*-
 * #%L
 * com.paremus.dosgi.net
 * %%
 * Copyright (C) 2016 - 2019 Paremus Ltd
 * %%
 * Licensed under the Fair Source License, Version 0.9 (the "License");
 * 
 * See the NOTICE.txt file distributed with this work for additional 
 * information regarding copyright ownership. You may not use this file 
 * except in compliance with the License. For usage restrictions see the 
 * LICENSE.txt file distributed with this work
 * #L%
 */
package com.paremus.dosgi.net.activator;

import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Stream.concat;
import static java.util.stream.Stream.of;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;

import org.osgi.framework.BundleContext;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedServiceFactory;
import org.osgi.service.remoteserviceadmin.RemoteConstants;
import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin;
import org.osgi.util.converter.Converters;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.paremus.dosgi.net.config.TransportConfig;
import com.paremus.dosgi.net.impl.RemoteServiceAdminFactoryImpl;
import com.paremus.dosgi.scoping.rsa.MultiFrameworkRemoteServiceAdmin;
import com.paremus.netty.tls.ParemusNettyTLS;

import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.EventLoopGroup;
import io.netty.util.Timer;
import io.netty.util.concurrent.EventExecutorGroup;

public class ManagedServiceFactoryImpl implements ManagedServiceFactory {

	private static final Logger logger = LoggerFactory.getLogger(ManagedServiceFactoryImpl.class);
	
	private final BundleContext context;
	
	private final Timer timer;
	
	private final EventLoopGroup serverIo;
	
	private final EventLoopGroup clientIo;
	
	private final EventExecutorGroup serverWorkers;
	
	private final EventExecutorGroup clientWorkers;
	
	private final ByteBufAllocator allocator;
	
	private final ConcurrentHashMap> trackers = new ConcurrentHashMap<>();
	
	private final ConcurrentHashMap rsas = new ConcurrentHashMap<>();
	private final ConcurrentHashMap> registrations = new ConcurrentHashMap<>();
	
	private final ConcurrentHashMap, List> usedBy = new ConcurrentHashMap<>();

	private volatile boolean open = true;
	
	public ManagedServiceFactoryImpl(BundleContext context, Timer timer, EventLoopGroup serverIo, EventLoopGroup clientIo,
			EventExecutorGroup serverWorkers, EventExecutorGroup clientWorkers, ByteBufAllocator allocator) {
		this.context = context;
		this.timer = timer;
		this.serverIo = serverIo;
		this.clientIo = clientIo;
		this.serverWorkers = serverWorkers;
		this.clientWorkers = clientWorkers;
		this.allocator = allocator;
	}

	@Override
	public String getName() {
		return "Paremus RSA additional Transports provider";
	}

	@Override
	public void updated(String pid, Dictionary properties) throws ConfigurationException {
		
		deleted(pid);
		
		if(!open) {
			return;
		}
		
		TransportConfig config;
		try {
			config = Converters.standardConverter().convert(toMap(properties)).to(TransportConfig.class);
		} catch (Exception e) {
			logger.error("Unable to process the configuration for pid {}", pid, e);
			throw new ConfigurationException(null, e.getMessage());
		}
		
		if(config.server_protocols().length == 0 && config.client_protocols().length == 0) {
			logger.info("The pid {} defines no RSA transports, so no RSA will be created", pid);
			return;
		}
		
		String filter = config.encoding_scheme_target();
		
		Predicate> selector;
		if(filter.isEmpty()) {
			selector = r -> true;
		} else {
			Filter f;
			try {
				f = context.createFilter(filter);
			} catch (InvalidSyntaxException e) {
				logger.error("Unable to process the encoding scheme target filter for pid {}", pid, e);
				deleted(pid);
				throw new ConfigurationException("encoding.scheme.target", e.getMessage());
			}
			selector = r -> f.match(r);
		}
		
		ServiceTracker tracker = new ServiceTracker(context, ParemusNettyTLS.class, null) {
				@Override
				public ParemusNettyTLS addingService(ServiceReference reference) {
					ParemusNettyTLS esf = super.addingService(reference);
					
					if(esf != null && selector.test(reference) && !rsas.containsKey(pid)) {
						setup(reference, esf, config);
					}
					
					return esf;
				}

				private boolean setup(ServiceReference reference, ParemusNettyTLS esf, TransportConfig cfg) {
					RemoteServiceAdminFactoryImpl newRSA;
					try {
						newRSA = new RemoteServiceAdminFactoryImpl(config, esf, allocator, serverIo, 
								clientIo, serverWorkers, clientWorkers, timer);
					} catch (IllegalArgumentException iae) {
						logger.error("The RSA could not be created with encoding scheme {}", reference, iae);
						return false;
					}
					
					rsas.put(pid, newRSA);
					usedBy.compute(reference, (k,v) -> v == null ? singletonList(newRSA) :
								concat(v.stream(), of(newRSA)).collect(toList()));
					
					Hashtable props = new Hashtable<>();
					
					Enumeration keys = properties.keys();
					
					while(keys.hasMoreElements()) {
						String key = keys.nextElement();
						if(!key.startsWith(".")) {
							props.put(key, properties.get(key));
						}
					}
					
					props.put(RemoteConstants.REMOTE_INTENTS_SUPPORTED, newRSA.getSupportedIntents());
			        props.put(RemoteConstants.REMOTE_CONFIGS_SUPPORTED, Collections.singletonList("com.paremus.dosgi.net"));
			        
					ServiceRegistration newRSAReg = context.registerService(new String[] {
							RemoteServiceAdmin.class.getName(), MultiFrameworkRemoteServiceAdmin.class.getName()}, 
							newRSA, props);
					
					registrations.put(newRSA, newRSAReg);
					return true;
				}
				
				@Override
				public void modifiedService(ServiceReference reference,
						ParemusNettyTLS service) {
					if(!selector.test(reference)) {
						removeAndAdd(reference);
					}
				}
				
				private void removeAndAdd(ServiceReference reference) {
					RemoteServiceAdminFactoryImpl rsa = rsas.get(pid);
					if(rsa != null) {
						if(usedBy.getOrDefault(reference, emptyList()).contains(rsa)) {
						
							usedBy.compute(reference, (k,v) -> {
									List l = v == null ? emptyList() :
										new ArrayList<>(v);
									l.remove(rsa);
									return l.isEmpty() ? null : l;
								});
							rsas.remove(pid, rsa);
							
							ServiceRegistration reg = registrations.remove(rsa);
							
							if(reg != null) {
								try {
									reg.unregister();
								} catch (IllegalStateException ise) {
									// No matter
								}
							}
							
							rsa.close();
						} else {
							return;
						}
					}
					getTracked().entrySet().stream()
						.filter(e -> setup(e.getKey(), e.getValue(), config))
						.findFirst();
				}

				@Override
				public void removedService(ServiceReference reference,
						ParemusNettyTLS service) {
					removeAndAdd(reference);
					super.removedService(reference, service);
				}
			};
		trackers.put(pid, tracker);
		
		tracker.open();
		
		if(!open) {
			deleted(pid);
		}
	}

	static Map toMap(Dictionary properties) {
		Map map = new HashMap<>();
		Enumeration keys = properties.keys();
		while(keys.hasMoreElements()) {
			String key = keys.nextElement();
			map.put(key, properties.get(key));
		}
		return map;
	}
	
	public void destroy() {
		open = false;
		trackers.keySet().stream().forEach(this::deleted);
	}

	@Override
	public void deleted(String pid) {
		ofNullable(trackers.remove(pid)).ifPresent(ServiceTracker::close);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy