package ibis.ipl.registry.central.client;
import ibis.ipl.Credentials;
import ibis.ipl.IbisCapabilities;
import ibis.ipl.IbisConfigurationException;
import ibis.ipl.NoSuchPropertyException;
import ibis.ipl.RegistryEventHandler;
import ibis.ipl.impl.IbisIdentifier;
import ibis.ipl.registry.central.Event;
import ibis.ipl.registry.central.Protocol;
import ibis.ipl.registry.central.RegistryProperties;
import ibis.ipl.registry.statistics.Statistics;
import ibis.ipl.support.RemoteException;
import ibis.util.TypedProperties;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Central registry.
*/
public final class Registry extends ibis.ipl.registry.Registry {
private static final Logger logger = LoggerFactory
.getLogger(Registry.class);
// A thread that forwards the events to the user event handler
private final Upcaller upcaller;
private final Statistics statistics;
// Handles incoming and outgoing communication with other registries and
// the server.
private final CommunicationHandler communicationHandler;
// client-side representation of the Pool the local Ibis is in.
private final Pool pool;
private final IbisIdentifier identifier;
private final IbisCapabilities capabilities;
// data structures that the user can poll
private final ArrayList<ibis.ipl.IbisIdentifier> joinedIbises;
private final ArrayList<ibis.ipl.IbisIdentifier> leftIbises;
private final ArrayList<ibis.ipl.IbisIdentifier> diedIbises;
private final ArrayList<String> signals;
/**
* Creates a Central Registry.
*
* @param capabilities
* Required capabilities of this registry
* @param eventHandler
* Registry handler to pass events to.
* @param userProperties
* properties of this registry.
* @param data
* Ibis implementation data to attach to the IbisIdentifier.
* @param implementationVersion
* the identification of this ibis implementation, including
* version, class and such. Must be identical for all ibises in a
* single pool.
* @param credentials
* Security credentials
* @param tag
* A tag provided by the user constructing this Ibis.
* @throws IOException
* in case of trouble.
* @throws IbisConfigurationException
* In case invalid properties/capabilities were given.
*/
public Registry(IbisCapabilities capabilities,
RegistryEventHandler eventHandler, Properties userProperties,
byte[] data, String implementationVersion, Credentials credentials,
byte[] tag) throws IbisConfigurationException, IOException {
logger.debug("creating central registry");
this.capabilities = capabilities;
TypedProperties properties = RegistryProperties
.getHardcodedProperties();
properties.addProperties(userProperties);
if (capabilities == null) {
throw new IbisConfigurationException(
"Capabilities for registry not specified");
}
if ((capabilities.hasCapability(IbisCapabilities.MEMBERSHIP_UNRELIABLE) || capabilities
.hasCapability(IbisCapabilities.MEMBERSHIP_TOTALLY_ORDERED))
&& eventHandler == null) {
joinedIbises = new ArrayList<ibis.ipl.IbisIdentifier>();
leftIbises = new ArrayList<ibis.ipl.IbisIdentifier>();
diedIbises = new ArrayList<ibis.ipl.IbisIdentifier>();
} else {
joinedIbises = null;
leftIbises = null;
diedIbises = null;
}
if (capabilities.hasCapability(IbisCapabilities.SIGNALS)
&& eventHandler == null) {
signals = new ArrayList<String>();
} else {
signals = null;
}
if (eventHandler != null) {
upcaller = new Upcaller(eventHandler);
} else {
upcaller = null;
}
if (properties.getBooleanProperty(RegistryProperties.STATISTICS)) {
statistics = new Statistics(Protocol.OPCODE_NAMES);
logger.debug("statistics: on");
} else {
statistics = null;
logger.debug("statistics: off");
}
pool = new Pool(capabilities, properties, this, statistics);
try {
communicationHandler = new CommunicationHandler(properties, pool,
statistics);
identifier = communicationHandler.join(data, implementationVersion,
credentials, tag);
communicationHandler.bootstrap();
} catch (RemoteException e) {
// error caused by server "complaining"
throw new IbisConfigurationException(e.getMessage());
}
// start writing statistics
if (statistics != null) {
statistics.setID(identifier.getID() + "@" + identifier.location(),
pool.getName());
statistics
.startWriting(properties
.getIntProperty(RegistryProperties.STATISTICS_INTERVAL) * 1000);
}
logger.debug("registry for " + identifier + " initiated");
}
@Override
public IbisIdentifier getIbisIdentifier() {
return identifier;
}
public IbisIdentifier elect(String electionName) throws IOException {
return elect(electionName, 0);
}
public String[] wonElections() {
return pool.wonElections(identifier);
}
public IbisIdentifier elect(String electionName, long timeoutMillis)
throws IOException {
if (pool.isStopped()) {
throw new IOException(
"cannot do election, registry already stopped");
}
if (!capabilities.hasCapability(IbisCapabilities.ELECTIONS_UNRELIABLE)
&& !capabilities
.hasCapability(IbisCapabilities.ELECTIONS_STRICT)) {
throw new IbisConfigurationException(
"No election support requested");
}
IbisIdentifier result = pool.getElectionResult(electionName, -1);
if (result == null) {
result = communicationHandler.elect(electionName, timeoutMillis);
}
return result;
}
public IbisIdentifier getElectionResult(String election) throws IOException {
return getElectionResult(election, 0);
}
public IbisIdentifier getElectionResult(String electionName,
long timeoutMillis) throws IOException {
if (pool.isStopped()) {
throw new IOException(
"cannot do getElectionResult, registry already stopped");
}
if (!capabilities.hasCapability(IbisCapabilities.ELECTIONS_UNRELIABLE)
&& !capabilities
.hasCapability(IbisCapabilities.ELECTIONS_STRICT)) {
throw new IbisConfigurationException(
"No election support requested");
}
logger.debug("getting election result for: \"" + electionName + "\"");
return pool.getElectionResult(electionName, timeoutMillis);
}
public void maybeDead(ibis.ipl.IbisIdentifier ibisIdentifier)
throws IOException {
if (pool.isStopped()) {
throw new IOException(
"cannot do maybeDead, registry already stopped");
}
if (pool.mustReportMaybeDead(ibisIdentifier)) {
communicationHandler.maybeDead(ibisIdentifier);
}
}
public void assumeDead(ibis.ipl.IbisIdentifier ibisIdentifier)
throws IOException {
if (pool.isStopped()) {
throw new IOException(
"cannot do assumeDead, registry already stopped");
}
communicationHandler.assumeDead(ibisIdentifier);
}
public void signal(String signal,
ibis.ipl.IbisIdentifier... ibisIdentifiers) throws IOException {
if (pool.isStopped()) {
throw new IOException(
"cannot send signals, registry already stopped");
}
if (!capabilities.hasCapability(IbisCapabilities.SIGNALS)) {
throw new IbisConfigurationException("No signal support requested");
}
logger.debug("telling " + ibisIdentifiers.length
+ " ibisses a string: " + signal);
communicationHandler.signal(signal, ibisIdentifiers);
}
public synchronized ibis.ipl.IbisIdentifier[] joinedIbises() {
if (joinedIbises == null) {
throw new IbisConfigurationException(
"Resize downcalls not configured");
}
ibis.ipl.IbisIdentifier[] retval = joinedIbises
.toArray(new ibis.ipl.IbisIdentifier[joinedIbises.size()]);
joinedIbises.clear();
return retval;
}
public synchronized ibis.ipl.IbisIdentifier[] leftIbises() {
if (leftIbises == null) {
throw new IbisConfigurationException(
"Resize downcalls not configured");
}
ibis.ipl.IbisIdentifier[] retval = leftIbises
.toArray(new ibis.ipl.IbisIdentifier[leftIbises.size()]);
leftIbises.clear();
return retval;
}
public synchronized ibis.ipl.IbisIdentifier[] diedIbises() {
if (diedIbises == null) {
throw new IbisConfigurationException(
"Resize downcalls not configured");
}
ibis.ipl.IbisIdentifier[] retval = diedIbises
.toArray(new ibis.ipl.IbisIdentifier[diedIbises.size()]);
diedIbises.clear();
return retval;
}
public synchronized String[] receivedSignals() {
if (signals == null) {
throw new IbisConfigurationException(
"Registry downcalls not configured");
}
String[] retval = signals.toArray(new String[signals.size()]);
signals.clear();
return retval;
}
public int getPoolSize() {
if (!pool.isClosedWorld()) {
throw new IbisConfigurationException(
"getPoolSize called but open world run");
}
return pool.getSize();
}
public String getPoolName() {
return identifier.poolName();
}
public boolean isClosed() {
return pool.isClosed();
}
public void waitUntilPoolClosed() {
if (!pool.isClosedWorld()) {
throw new IbisConfigurationException(
"waitForAll called but open world run");
}
pool.waitUntilPoolClosed();
}
public void enableEvents() {
if (upcaller == null) {
throw new IbisConfigurationException("Registry not configured to "
+ "produce events");
}
upcaller.enableEvents();
}
public void disableEvents() {
if (upcaller == null) {
throw new IbisConfigurationException("Registry not configured to "
+ "produce events");
}
upcaller.disableEvents();
}
@Override
public long getSequenceNumber(String name) throws IOException {
if (pool.isStopped()) {
throw new IOException(
"cannot send signals, registry already stopped");
}
return communicationHandler.getSeqno(name);
}
@Override
public void leave() throws IOException {
if (pool.isStopped()) {
throw new IOException("cannot leave, registry already stopped");
}
communicationHandler.leave();
if (statistics != null) {
statistics.write();
statistics.end();
}
}
/**
* Handles incoming user events.
*/
synchronized void handleEvent(Event event) {
logger.debug("new event passed to user: " + event);
if (event.getType() == Event.SIGNAL) {
boolean match = false;
// see if this signal is send to us.
for (IbisIdentifier ibis : event.getDestinations()) {
if (ibis.equals(identifier)) {
match = true;
}
}
if (!match) {
// do not handle this event any further
return;
}
}
// generate an upcall for this event
if (upcaller != null) {
upcaller.newEvent(event);
}
switch (event.getType()) {
case Event.JOIN:
if (joinedIbises != null) {
joinedIbises.add(event.getIbis());
}
break;
case Event.LEAVE:
if (leftIbises != null) {
leftIbises.add(event.getIbis());
}
break;
case Event.DIED:
if (leftIbises != null) {
leftIbises.add(event.getIbis());
}
break;
case Event.SIGNAL:
if (signals != null) {
signals.add(event.getDescription());
}
break;
case Event.ELECT:
case Event.UN_ELECT:
case Event.POOL_CLOSED:
case Event.POOL_TERMINATED:
// Not handled here
break;
default:
logger.error("unknown event type in registry: " + event);
}
}
public Map<String, String> managementProperties() {
return statistics.getMap();
}
public String getManagementProperty(String key)
throws NoSuchPropertyException {
String result = managementProperties().get(key);
if (result == null) {
throw new NoSuchPropertyException(key + " is not a valid property");
}
return result;
}
public void setManagementProperties(Map<String, String> properties)
throws NoSuchPropertyException {
throw new NoSuchPropertyException(
"central registry does not have any properties that can be set");
}
public void setManagementProperty(String key, String value)
throws NoSuchPropertyException {
throw new NoSuchPropertyException(
"central registry does not have any properties that can be set");
}
public void printManagementProperties(PrintStream stream) {
// NOTHING
}
public boolean hasTerminated() {
if (!capabilities.hasCapability(IbisCapabilities.TERMINATION)) {
throw new IbisConfigurationException("Registry not configured to "
+ "support termination");
}
return pool.hasTerminated();
}
public void terminate() throws IOException {
if (!capabilities.hasCapability(IbisCapabilities.TERMINATION)) {
throw new IbisConfigurationException("Registry not configured to "
+ "support termination");
}
//check if already terminated, no need to do twice.
if (!pool.hasTerminated()) {
communicationHandler.terminate();
}
}
public ibis.ipl.IbisIdentifier waitUntilTerminated() {
if (!capabilities.hasCapability(IbisCapabilities.TERMINATION)) {
throw new IbisConfigurationException("Registry not configured to "
+ "support termination");
}
return pool.waitUntilTerminated();
}
// jmx function
public synchronized boolean getTerminated() {
return hasTerminated();
}
// jmx function
public synchronized boolean getClosed() {
return isClosed();
}
public String getTime() {
return new Date() + "";
}
@Override
public IbisIdentifier getRandomPoolMember() {
return pool.getRandomMember().getIbis();
}
}