/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Copyright (c) 2014, MPL CodeInside http://codeinside.ru
*/
package ru.codeinside.gws.p.router.registry;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.ComponentContext;
import ru.codeinside.gws.api.Declarant;
import ru.codeinside.gws.api.ProtocolFactory;
import ru.codeinside.gws.api.Server;
import ru.codeinside.gws.api.ServerProtocol;
import ru.codeinside.gws.api.ServiceDefinition;
import ru.codeinside.gws.api.ServiceDefinitionParser;
import ru.codeinside.gws.p.adapter.ProviderEntry;
import ru.codeinside.gws.p.router.web.Registry;
import ru.codeinside.gws.p.router.web.ServerLogResource;
import javax.xml.namespace.QName;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
final public class ProviderRegistry implements ru.codeinside.gws.p.registry.api.ProviderRegistry, Registry {
final Logger logger = Logger.getLogger(getClass().getName());
/**
* Все события обрабатываются в одном потоке.
*/
final ScheduledExecutorService eventDriver = Executors.newSingleThreadScheduledExecutor();
final ProviderEntry EMPTY = new ProviderEntry();
final boolean CLEAR_DEFINITION = false;
final HashMap<String, ProviderEntry> entries = new LinkedHashMap<String, ProviderEntry>();
final HashMap<String, ServiceReference> providerRefs = new LinkedHashMap<String, ServiceReference>();
final Set<String> registry = new LinkedHashSet<String>();
BundleContext bundleContext;
Declarant declarant;
ServiceDefinitionParser serviceDefinitionParser;
ProtocolFactory protocolFactory;
ServiceReference logServiceReference;
public void activate(final ComponentContext context) {
REGISTRY.set(ProviderRegistry.this);
logger.info("Активация маршрутизатора СМЭВ");
eventDriver.submit(new Runnable() {
@Override
public void run() {
bundleContext = context.getBundleContext();
}
});
}
public void deactivate(final ComponentContext context) {
REGISTRY.set(null);
eventDriver.shutdownNow();
logger.info("Выключение маршрутизатора СМЭВ");
}
public void add(final ServiceReference serviceReference) {
eventDriver.submit(new Runnable() {
@Override
public void run() {
if (bundleContext == null) {
eventDriver.schedule(this, 25, TimeUnit.MILLISECONDS);
} else {
logger.info("Регистрация поставщика: " + getInfo(serviceReference));
final String name = (String) serviceReference.getProperty("component.name");
if (name != null) {
// саморегистрация поставщика
entries.put(name, EMPTY);
addProvider(serviceReference, name);
}
}
}
});
}
public void remove(final ServiceReference serviceReference) {
execute(new Runnable() {
@Override
public void run() {
logger.info("Удаление поставщика: " + getInfo(serviceReference));
final String name = (String) serviceReference.getProperty("component.name");
if (name != null) {
removeProvider(name);
}
}
});
}
public void addLogService(final ServiceReference log) {
eventDriver.submit(new Runnable() {
@Override
public void run() {
if (bundleContext == null) {
eventDriver.schedule(this, 25, TimeUnit.MILLISECONDS);
} else {
logger.info("use log service: " + getInfo(log));
logServiceReference = log;
}
}
});
}
public void removeLogService(final ServiceReference log) {
execute(new Runnable() {
@Override
public void run() {
logServiceReference = null;
}
});
}
public void addDefinitionParser(final ServiceDefinitionParser newServiceDefinitionParser) {
eventDriver.submit(new Runnable() {
@Override
public void run() {
serviceDefinitionParser = newServiceDefinitionParser;
updateEntries();
}
});
}
public void removeDefinitionParser(final ServiceDefinitionParser oldServiceDefinitionParser) {
execute(new Runnable() {
@Override
public void run() {
serviceDefinitionParser = null;
updateEntries();
}
});
}
public void addProtocolFactory(final ProtocolFactory newProtocolFactory) {
eventDriver.submit(new Runnable() {
@Override
public void run() {
protocolFactory = newProtocolFactory;
updateEntries();
}
});
}
public void removeProtocolFactory(final ProtocolFactory oldProtocolFactory) {
execute(new Runnable() {
@Override
public void run() {
protocolFactory = null;
updateEntries();
}
});
}
public void addDeclarant(final Declarant newDeclarant) {
eventDriver.submit(new Runnable() {
@Override
public void run() {
declarant = newDeclarant;
updateEntries();
}
});
}
public void removeDeclarant(final Declarant oldDeclarant) {
execute(new Runnable() {
@Override
public void run() {
declarant = null;
updateEntries();
}
});
}
@Override
public void updateProviderNames(final Set<String> providerNames) {
final Set<String> new_ = new HashSet<String>(providerNames);
eventDriver.submit(new Runnable() {
@Override
public void run() {
logger.info("Обновление реестра поставщиков: " + new_);
registry.clear();
registry.addAll(new_);
final Set<String> old = new HashSet<String>(entries.keySet());
final Set<String> removes = new HashSet<String>(old);
removes.removeAll(new_);
for (final String name : removes) {
// удаляем лишь пустышки
final ProviderEntry entry = entries.get(name);
if (entry == EMPTY) {
removeProvider(name);
}
}
final Set<String> insertions = new HashSet<String>(new_);
insertions.removeAll(old);
for (final String name : insertions) {
entries.put(name, EMPTY);
final ServiceReference serviceReference = providerRefs.get(name);
if (serviceReference != null) {
addProvider(serviceReference, name);
}
}
}
});
}
@Override
public ProviderEntry getProviderEntry(final String name) {
final Future<ProviderEntry> f = eventDriver.submit(new Callable<ProviderEntry>() {
@Override
public ProviderEntry call() throws Exception {
return entries.get(name);
}
});
try {
return f.get(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
logger.log(Level.INFO, "Прерывание", e);
return null;
} catch (ExecutionException e) {
logger.log(Level.INFO, "Отказ", e.getCause());
return null;
} catch (TimeoutException e) {
logger.log(Level.INFO, "Таймаут ожидания поставщика " + name);
return null;
}
}
@Override
public void destroyPorts() {
final Future<?> f = eventDriver.submit(new Runnable() {
@Override
public void run() {
for (ProviderEntry entry : entries.values()) {
disposeEndpoint(entry);
}
}
});
try {
f.get();
} catch (InterruptedException e) {
logger.log(Level.INFO, "Перрывание", e);
} catch (ExecutionException e) {
logger.log(Level.INFO, "Отказ", e.getCause());
}
}
@Override
public Set<String> names() {
final Future<Set<String>> f = eventDriver.submit(new Callable<Set<String>>() {
@Override
public Set<String> call() throws Exception {
return entries.keySet();
}
});
try {
return f.get(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
logger.log(Level.INFO, "Прерывание", e);
return Collections.emptySet();
} catch (ExecutionException e) {
logger.log(Level.INFO, "Отказ", e.getCause());
return Collections.emptySet();
} catch (TimeoutException e) {
logger.log(Level.INFO, "Таймаут ожидания имён поставщиков");
return Collections.emptySet();
}
}
@Override
public ServerLogResource getServerLogResource() {
return new ServerLogResource(bundleContext, logServiceReference);
}
// --------------- internals --------------
void addProvider(final ServiceReference serviceReference, final String name) {
providerRefs.put(name, serviceReference);
final ProviderEntry entry = entries.get(name);
if (entry != null) {
disposeEndpoint(entry);
final ProviderEntry newEntry = new ProviderEntry();
newEntry.name = name;
newEntry.declarant = declarant;
try {
final Server provider = (Server) bundleContext.getService(serviceReference);
if (provider != null) {
newEntry.wsdl = provider.getWsdlUrl();
}
} catch (RuntimeException e) {
logger.log(Level.WARNING, "fail getWsdlUrl for provider " + name, e);
} finally {
bundleContext.ungetService(serviceReference);
}
if (newEntry.wsdl != null) {
updateEntry(newEntry);
entries.put(name, newEntry);
}
}
}
void removeProvider(String name) {
providerRefs.remove(name);
final ProviderEntry entry = entries.get(name);
if (entry != null) {
disposeEndpoint(entry);
if (registry.contains(name)) {
// оставляем пустышку лишь если есть в реестре
entries.put(name, EMPTY);
} else {
// поддержать саморегистрацию
entries.remove(name);
}
}
}
String getInfo(ServiceReference serviceReference) {
final StringBuilder sb = new StringBuilder();
for (String key : serviceReference.getPropertyKeys()) {
if ("objectClass".equals(key)) {
continue;
}
if (sb.length() > 0) {
sb.append(", ");
}
sb.append(key);
sb.append('=');
Object value = serviceReference.getProperty(key);
if (value instanceof String[]) {
sb.append(Arrays.asList((String[]) value));
} else {
sb.append(value);
}
}
return sb.toString();
}
/**
* Обновить все параметры всех поставщиков.
*/
void updateEntries() {
for (final ProviderEntry entry : entries.values()) {
if (entry != EMPTY) {
updateEntry(entry);
}
}
}
/**
* Обновить все параметры поставщика.
*/
void updateEntry(final ProviderEntry entry) {
disposeEndpoint(entry); // гаранировать применение новых свойств
entry.declarant = declarant;
updateWsDefinition(entry);
updateProtocol(entry);
}
void updateWsDefinition(final ProviderEntry entry) {
if (CLEAR_DEFINITION) {
entry.wsService = null;
entry.wsPort = null;
entry.wsPortDef = null;
entry.wsDef = null;
}
if (serviceDefinitionParser == null) {
return;
}
final ServiceDefinition def;
try {
def = serviceDefinitionParser.parseServiceDefinition(entry.wsdl);
} catch (RuntimeException e) {
logger.log(Level.WARNING, "fail parseServiceDefinition for provider " + entry.name, e);
return;
}
final QName serviceName;
final ServiceDefinition.Service serviceDef;
final QName portName;
final ServiceDefinition.Port port;
{
if (def.services == null || def.services.size() != 1) {
logger.log(Level.WARNING, "Invalid WSDL service definitions for " + entry.name + " : " + def.services);
return;
}
final Map.Entry<QName, ServiceDefinition.Service> e = first(def.services);
serviceDef = e.getValue();
serviceName = e.getKey();
}
{
if (serviceDef.ports == null || serviceDef.ports.size() != 1) {
logger.log(Level.WARNING, "Invalid WSDL port definitions for " + entry.name + " : " + serviceDef.ports);
return;
}
final Map.Entry<QName, ServiceDefinition.Port> e = first(serviceDef.ports);
portName = e.getKey();
port = e.getValue();
}
entry.wsDef = def;
entry.wsService = serviceName;
entry.wsPort = portName;
entry.wsPortDef = port;
}
void updateProtocol(final ProviderEntry entry) {
entry.protocol = createProtocol(entry.wsDef, entry.name);
}
ServerProtocol createProtocol(final ServiceDefinition def, String name) {
if (def == null) {
return null;
}
if (protocolFactory == null) {
return null;
}
try {
return protocolFactory.createServerProtocol(def);
} catch (RuntimeException e) {
logger.log(Level.WARNING, "fail createServerProtocol for " + name, e);
return null;
}
}
<K, V> Map.Entry<K, V> first(final Map<K, V> map) {
return map.entrySet().iterator().next();
}
void disposeEndpoint(ProviderEntry entry) {
if (entry != EMPTY) {
if (entry.servletAdapter != null) {
entry.servletAdapter.getEndpoint().dispose();
entry.servletAdapter = null;
}
}
}
void execute(final Runnable runnable) {
if (eventDriver.isShutdown()) {
synchronized (this) {
runnable.run();
}
} else {
eventDriver.submit(runnable);
}
}
}