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

com.nesscomputing.service.discovery.client.internal.AbstractDiscoveryClient Maven / Gradle / Ivy

There is a newer version: 1.6.3
Show newest version
/**
 * Copyright (C) 2012 Ness Computing, Inc.
 *
 * Licensed 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 com.nesscomputing.service.discovery.client.internal;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;


import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.inject.Singleton;
import com.nesscomputing.service.discovery.client.ReadOnlyDiscoveryClient;
import com.nesscomputing.service.discovery.client.ServiceHint;
import com.nesscomputing.service.discovery.client.ServiceInformation;
import com.nesscomputing.service.discovery.client.ServiceNotAvailableException;

/**
 * Base class for service discovery. Supplies all the meat to build a read-only
 * client, but no actual code that would insert service information into the
 * state of the world.
 */
@Singleton
public abstract class AbstractDiscoveryClient implements
		ReadOnlyDiscoveryClient {
	private final StateOfTheWorldHolder stateHolder;

	private final Random random = new Random();

	protected AbstractDiscoveryClient(final boolean enabled) {
		// Only wait for the first service update to happen if service discovery
		// is actually enabled.
		this.stateHolder = new StateOfTheWorldHolder(enabled);
	}

	@Override
	public void waitForWorldChange() throws InterruptedException {
		stateHolder.waitForWorldChange();
	}

	@Override
	public boolean waitForWorldChange(final long timeout,
			final TimeUnit timeUnit) throws InterruptedException {
		return stateHolder.waitForWorldChange(timeout, timeUnit);
	}

	protected StateOfTheWorldHolder getStateOfTheWorldHolder() {
		return stateHolder;
	}

	@Override
	public URI findServiceUri(final String serviceName,
			final String serviceType, final ServiceHint... hints)
			throws ServiceNotAvailableException {
		final ServiceInformation service = findServiceInformation(serviceName,
				serviceType, hints);

		final String locatedScheme = service
				.getProperty(ServiceInformation.PROP_SERVICE_SCHEME);
		final String locatedAddress = service
				.getProperty(ServiceInformation.PROP_SERVICE_ADDRESS);
		final String locatedPort = service
				.getProperty(ServiceInformation.PROP_SERVICE_PORT);

		if (StringUtils.isEmpty(locatedScheme)
				|| StringUtils.isEmpty(locatedAddress)
				|| StringUtils.isEmpty(locatedPort)) {
			throw new ServiceNotAvailableException(
					"Service %s/%s exists but misses address information (%s/%s)",
					serviceName, serviceType, locatedAddress, locatedPort);
		}

		try {
			return new URI(locatedScheme, null, locatedAddress,
					Integer.parseInt(locatedPort), "", null, null);
		} catch (URISyntaxException use) {
			throw new ServiceNotAvailableException(
					"Could not create URI from '%s'!", service);
		}
	}

	@Override
	public ServiceInformation findServiceInformation(final String serviceName,
			final String serviceType, final ServiceHint... hints)
			throws ServiceNotAvailableException {
		final ConsistentHashRing services = findRing(serviceName, serviceType);

		if (CollectionUtils.isEmpty(services)) {
			throw new ServiceNotAvailableException("No %s/%s service found",
					serviceName, serviceType);
		}

		final ServiceInformation service = selectHintedService(services, hints);
		if (service == null) {
			throw new ServiceNotAvailableException("No %s/%s service found",
					serviceName, serviceType);
		}
		return service;
	}

	@Override
	public List findAllServiceInformation(
			final String serviceName, final String serviceType) {
		ConsistentHashRing services = findRing(serviceName, serviceType);
		if (CollectionUtils.isEmpty(services)) {
			return Collections.emptyList();
		}

		return Lists.newArrayList(services);
	}

	private ConsistentHashRing findRing(final String serviceName,
			final String serviceType) {
		final Map current = stateHolder.getState();
		ConsistentRingGroup group = current.get(serviceName);
		if (CollectionUtils.isEmpty(group)) {
			return null;
		}
		// getRing will fall back to another type (if appropriate), if the
		// requested type can't be found
		return group.getRing(serviceType);
	}

	@Override
	public List findAllServiceInformation(
			final String serviceName) {
		List result = Lists.newArrayList();
		ConsistentRingGroup group = stateHolder.getState().get(serviceName);
		if (CollectionUtils.isEmpty(group)) {
			return Collections.emptyList();
		}
		for (ConsistentHashRing ring : group) {
			result.addAll(ring);
		}
		return result;
	}

	@Override
	public Map> findAllServiceInformation() {
		final Map current = stateHolder.getState();

		// Do a deep copy to make sure no one can hold a reference to the full
		// map or the lists.
		final Map> result = Maps.newHashMap();
		for (Map.Entry entry : current.entrySet()) {
			final List serviceList = Lists.newArrayList();
			for (ConsistentHashRing ring : entry.getValue()) {
				// The service information elements are immutable, so you can
				// hold a reference to them,
				// it does not matter. Please don't. :-)
				serviceList.addAll(ring);
			}
			result.put(entry.getKey(), serviceList);
		}
		return result;
	}

	private ServiceInformation selectHintedService(
			final ConsistentHashRing ring, final ServiceHint... hints) {
		if (CollectionUtils.isEmpty(ring)) {
			return null;
		}

		String hashKey = null;
		for (ServiceHint hint : hints) {
			if (ServiceHint.CONSISTENTHASH_HINT.equals(hint.getName())) {
				hashKey = hint.getValue();
				break;
			}
		}

		if (hashKey != null) {
			return ring.get(hashKey);
		} else {
			return ring.get(String.valueOf(random.nextInt()));
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy