package eu.europeana.cloud.service.uis.registration; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.utils.EnsurePath; import org.apache.curator.x.discovery.ServiceDiscovery; import org.apache.curator.x.discovery.ServiceDiscoveryBuilder; import org.apache.curator.x.discovery.ServiceInstance; import org.apache.curator.x.discovery.details.InstanceSerializer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Throwables; import eu.europeana.cloud.common.service.ServiceProperties; /** * Registers services as available in Zookeeper. * * * ZooKeeper is used for service discovery: * * Services are registered on a common znode, and any client can query Zookeeper * for a list of available services. * * @author emmanouil.koufakis@theeuropeanlibrary.org */ public final class ZookeeperServiceAdvertiser { /** Logging */ private static final Logger LOGGER = LoggerFactory.getLogger(ZookeeperService.class); /** List of properties that allow a client to connect to this UIS REST Service. */ private ServiceProperties currentlyAdvertisedServiceProperties; /** Used to serialize the {@link ServiceProperties} before sending them to Zookeeper. */ private final InstanceSerializer<ServiceProperties> jsonInstanceSerializer; /** Enables the communication with Zookeeper. */ private final CuratorFramework curatorFramework; /** zNode for service registration */ private final String zNode; ZookeeperServiceAdvertiser(final CuratorFramework curatorFramework, final InstanceSerializer<ServiceProperties> instanceSerializer, final String zNode) { LOGGER.info("ZookeeperServiceAdvertiser starting..."); this.curatorFramework = curatorFramework; this.jsonInstanceSerializer = instanceSerializer; this.zNode = zNode; this.currentlyAdvertisedServiceProperties = null; // ZooKeeper paths must be explicitly created // Let's make sure the path exists try { new EnsurePath(zNode).ensure(curatorFramework.getZookeeperClient()); } catch (final Exception e) { LOGGER.error("ZooKeeper path='{}' not found... Exception='{}'", zNode, e.getMessage()); throw Throwables.propagate(e); } LOGGER.info("ZookeeperServiceAdvertiser started successfully."); } /** * Registers this service as available. * Other clients querying Zookeeper for available services will receive this service on their list. * * @param serviceProperties List of properties required to connect to this Service. */ public void startAdvertising(final ServiceProperties serviceProperties) { LOGGER.info("ZookeeperServiceAdvertiser startAdvertising..."); try { ServiceDiscovery<ServiceProperties> discovery = getDiscovery(); discovery.start(); discovery.registerService(convert(serviceProperties)); this.currentlyAdvertisedServiceProperties = serviceProperties; } catch (final Exception e) { this.currentlyAdvertisedServiceProperties = null; LOGGER.error(e.getMessage()); throw Throwables.propagate(e); } LOGGER.info("ZookeeperServiceAdvertiser has advertised the service successfully."); } public void stopAdvertising() { try { ServiceDiscovery<ServiceProperties> discovery = getDiscovery(); discovery.start(); discovery.unregisterService(convert(this.currentlyAdvertisedServiceProperties)); discovery.close(); } catch (Exception e) { LOGGER.error(e.getMessage()); throw Throwables.propagate(e); } } private static ServiceInstance<ServiceProperties> convert(final ServiceProperties p) throws Exception { return ServiceInstance.<ServiceProperties>builder() .name(p.getServiceName()) .payload(p) .address(p.getListenAddress()) .id(p.getServiceId()) .build(); } private ServiceDiscovery<ServiceProperties> getDiscovery() { return ServiceDiscoveryBuilder.builder(ServiceProperties.class) .basePath(zNode) .client(curatorFramework) .serializer(jsonInstanceSerializer) .build(); } }