/* $Id$ */
package ibis.ipl.impl;
import ibis.io.IbisIOException;
import ibis.ipl.Credentials;
import ibis.ipl.IbisCapabilities;
import ibis.ipl.IbisConfigurationException;
import ibis.ipl.IbisCreationFailedException;
import ibis.ipl.IbisFactory;
import ibis.ipl.IbisProperties;
import ibis.ipl.IbisStarter;
import ibis.ipl.MessageUpcall;
import ibis.ipl.NoSuchPropertyException;
import ibis.ipl.PortType;
import ibis.ipl.ReceivePortConnectUpcall;
import ibis.ipl.RegistryEventHandler;
import ibis.ipl.SendPortDisconnectUpcall;
import ibis.ipl.registry.Registry;
import ibis.ipl.support.management.ManagementClient;
import ibis.ipl.support.vivaldi.Coordinates;
import ibis.ipl.support.vivaldi.VivaldiClient;
import ibis.util.TypedProperties;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;
import java.util.Map.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This implementation of the {@link ibis.ipl.Ibis} interface is a base class,
* to be extended by specific Ibis implementations.
*/
public abstract class Ibis implements ibis.ipl.Ibis // , IbisMBean
{
// property to uniquely identify an Ibis locally, even when it has not
// joined the registry yet
public static final String ID_PROPERTY = "ibis.local.id";
/** Debugging output. */
private static final Logger logger = LoggerFactory
.getLogger("ibis.ipl.impl.Ibis");
/** The IbisCapabilities as specified by the user. */
private final IbisCapabilities capabilities;
/** List of port types given by the user */
private final PortType[] portTypes;
private final IbisStarter starter;
/**
* Properties, as given to
* {@link ibis.ipl.IbisFactory#createIbis(IbisCapabilities, Properties, boolean, RegistryEventHandler, PortType...)}
* .
*/
protected TypedProperties properties;
/** The Ibis registry. */
private final Registry registry;
/** Management Client */
private final ManagementClient managementClient;
/** Vivaldi Client */
private final VivaldiClient vivaldiClient;
/** Identifies this Ibis instance in the registry. */
public final IbisIdentifier ident;
/** Set when {@link #end()} is called. */
private boolean ended = false;
/** The receiveports running on this Ibis instance. */
private HashMap<String, ReceivePort> receivePorts;
/** The sendports running on this Ibis instance. */
private HashMap<String, SendPort> sendPorts;
private HashMap<ibis.ipl.IbisIdentifier, Long> sentBytesPerIbis = null;
private HashMap<ibis.ipl.IbisIdentifier, Long> receivedBytesPerIbis = null;
/** Counter for allocating names for anonymous sendports. */
private static int send_counter = 0;
/** Counter for allocating names for anonymous receiveports. */
private static int receive_counter = 0;
/** Total number of messages send by closed send ports */
private long outgoingMessageCount = 0;
/** Total number of messages received by closed receive ports */
private long incomingMessageCount = 0;
/** Total number of bytes written to messages closed send ports */
private long bytesWritten = 0;
/** Total number of bytes send by closed send ports */
private long bytesSent = 0;
/** Total number of bytes read by closed receive ports */
private long bytesReceived = 0;
/** Total number of bytes read from messages (for closed received ports) */
private long bytesRead = 0;
/**
* Version, consisting of both the generic implementation version, and the
* "actual" implementation version.
*/
private String getImplementationVersion() throws Exception {
String genericVersion = Ibis.class.getPackage()
.getImplementationVersion();
// --Roelof on android the implementation version from the manifest gets
// overwritten with a default implementation version of "0.0". This is
// not the value we're searching for.
if (genericVersion == null || genericVersion.equals("0.0")) {
// try to get version from IPL_MANIFEST properties
genericVersion = IbisFactory
.getManifestProperty("implementation.version");
}
logger.debug("Version of Generic Ibis = " + genericVersion);
if (genericVersion == null
|| starter.getImplementationVersion() == null) {
throw new Exception("cannot get version for ibis");
}
return genericVersion + starter.getImplementationVersion();
}
/**
* Constructs an <code>Ibis</code> instance with the specified parameters.
*
* @param registryHandler
* the registryHandler.
* @param capabilities
* the capabilities.
* @param applicationTag
* an application level tag for this Ibis instance
* @param portTypes
* the port types requested for this ibis implementation.
* @param userProperties
* the properties as provided by the Ibis factory.
*/
protected Ibis(RegistryEventHandler registryHandler,
IbisCapabilities capabilities, Credentials credentials,
byte[] applicationTag, PortType[] portTypes,
Properties userProperties, IbisStarter starter)
throws IbisCreationFailedException {
if (capabilities == null) {
throw new IbisConfigurationException("capabilities not specified");
}
this.capabilities = capabilities;
this.portTypes = portTypes;
this.starter = starter;
this.properties = new TypedProperties();
// bottom up add properties, starting with hard coded ones
properties.addProperties(IbisProperties.getHardcodedProperties());
properties.addProperties(userProperties);
// set unique ID for this Ibis.
properties.setProperty(ID_PROPERTY, UUID.randomUUID().toString());
if (logger.isDebugEnabled()) {
logger.debug("Ibis constructor: properties = " + properties);
}
receivePorts = new HashMap<String, ReceivePort>();
sendPorts = new HashMap<String, SendPort>();
if (registryHandler != null) {
// Only install wrapper if user actually has an event handler.
// Otherwise, registry downcalls won't work. There needs to be another
// way to let an Ibis know of died Ibises. --Ceriel
registryHandler = new RegistryEventHandlerWrapper(registryHandler, this);
}
try {
registry = Registry.createRegistry(this.capabilities,
registryHandler, properties, getData(),
getImplementationVersion(), applicationTag, credentials);
} catch (IbisConfigurationException e) {
throw e;
} catch (Throwable e) {
throw new IbisCreationFailedException("Could not create registry",
e);
}
ident = registry.getIbisIdentifier();
if (properties.getBooleanProperty("ibis.vivaldi")) {
try {
vivaldiClient = new VivaldiClient(properties, registry);
} catch (Exception e) {
throw new IbisCreationFailedException(
"Could not create vivaldi client", e);
}
} else {
vivaldiClient = null;
}
if (properties.getBooleanProperty("ibis.bytescount")) {
sentBytesPerIbis = new HashMap<ibis.ipl.IbisIdentifier, Long>();
receivedBytesPerIbis = new HashMap<ibis.ipl.IbisIdentifier, Long>();
}
if (properties.getBooleanProperty("ibis.managementclient")) {
try {
managementClient = new ManagementClient(properties, this);
} catch (Throwable e) {
throw new IbisCreationFailedException(
"Could not create management client", e);
}
} else {
managementClient = null;
}
/*
* // add bean to JMX try { MBeanServer mbs =
* ManagementFactory.getPlatformMBeanServer(); ObjectName name = new
* ObjectName("ibis.ipl.impl:type=Ibis"); mbs.registerMBean(this, name);
* } catch (Exception e) { logger.warn("cannot registry MBean", e); }
*/
}
void died(ibis.ipl.IbisIdentifier corpse) {
killConnections(corpse);
}
void left(ibis.ipl.IbisIdentifier leftIbis) {
killConnections(leftIbis);
}
protected void killConnections(ibis.ipl.IbisIdentifier corpse) {
SendPort[] sps;
ReceivePort[] rps;
synchronized(this) {
sps = sendPorts.values().toArray(new SendPort[sendPorts.size()]);
rps = receivePorts.values().toArray(new ReceivePort[receivePorts.size()]);
}
for (SendPort s : sps) {
try {
s.killConnectionsWith(corpse);
} catch (Throwable e) {
if (logger.isDebugEnabled()) {
logger.debug("Got exception from killConnectionsWith", e);
}
}
}
for (ReceivePort p : rps) {
try {
p.killConnectionsWith(corpse);
} catch (Throwable e) {
if (logger.isDebugEnabled()) {
logger.debug("Got exception from killConnectionsWith", e);
}
}
}
}
/**
* Returns the current Ibis version.
*
* @return the ibis version.
*/
public String getVersion() {
return starter.getNickName() + "-" + starter.getIplVersion();
}
public ibis.ipl.Registry registry() {
return registry;
}
public ibis.ipl.IbisIdentifier identifier() {
return ident;
}
public Properties properties() {
return new Properties(properties);
}
public void end() throws IOException {
synchronized (this) {
if (ended) {
return;
}
ended = true;
}
try {
registry.leave();
} catch (Throwable e) {
throw new IbisIOException("Registry: leave failed ", e);
}
if (managementClient != null) {
managementClient.end();
}
if (vivaldiClient != null) {
vivaldiClient.end();
}
quit();
}
public void poll() throws IOException {
// Default has empty implementation.
}
synchronized void register(ReceivePort p) throws IOException {
if (receivePorts.get(p.name) != null) {
throw new IOException("Multiple instances of receiveport named "
+ p.name);
}
receivePorts.put(p.name, p);
}
synchronized void deRegister(ReceivePort p) {
if (receivePorts.remove(p.name) != null) {
// add statistics for this receive port to "total" statistics
incomingMessageCount += p.getMessageCount();
bytesReceived += p.getBytesReceived();
bytesRead += p.getBytesRead();
}
}
synchronized void register(SendPort p) throws IOException {
if (sendPorts.get(p.name) != null) {
throw new IOException("Multiple instances of sendport named "
+ p.name);
}
sendPorts.put(p.name, p);
}
synchronized void deRegister(SendPort p) {
if (sendPorts.remove(p.name) != null) {
// add statistics for this sendport to "total" statistics
outgoingMessageCount += p.getMessageCount();
bytesSent += p.getBytesSent();
bytesWritten += p.getBytesWritten();
}
}
synchronized void addSentPerIbis(long cnt, ibis.ipl.ReceivePortIdentifier[] idents) {
if (sentBytesPerIbis == null) {
return;
}
for (ibis.ipl.ReceivePortIdentifier rp : idents) {
ibis.ipl.IbisIdentifier i = rp.ibisIdentifier();
Long oldval = sentBytesPerIbis.get(i);
if (oldval != null) {
cnt += oldval.longValue();
}
sentBytesPerIbis.put(i, new Long(cnt));
}
}
synchronized void addReceivedPerIbis(long cnt, ibis.ipl.SendPortIdentifier[] idents) {
if (receivedBytesPerIbis == null) {
return;
}
for (ibis.ipl.SendPortIdentifier sp : idents) {
ibis.ipl.IbisIdentifier i = sp.ibisIdentifier();
Long oldval = receivedBytesPerIbis.get(i);
if (oldval != null) {
cnt += oldval.longValue();
}
receivedBytesPerIbis.put(i, new Long(cnt));
}
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Public methods, may called by Ibis implementations.
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* Returns the receiveport with the specified name, or <code>null</code> if
* not present.
*
* @param name
* the name of the receiveport.
* @return the receiveport.
*/
public synchronized ReceivePort findReceivePort(String name) {
return receivePorts.get(name);
}
/**
* Returns the sendport with the specified name, or <code>null</code> if not
* present.
*
* @param name
* the name of the sendport.
* @return the sendport.
*/
public synchronized SendPort findSendPort(String name) {
return sendPorts.get(name);
}
public ReceivePortIdentifier createReceivePortIdentifier(String name,
IbisIdentifier id) {
return new ReceivePortIdentifier(name, id);
}
public SendPortIdentifier createSendPortIdentifier(String name,
IbisIdentifier id) {
return new SendPortIdentifier(name, id);
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Protected methods, to be implemented by Ibis implementations.
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* Implementation-dependent part of the {@link #end()} implementation.
*/
protected abstract void quit();
/**
* This method should provide the implementation-dependent data of the Ibis
* identifier for this Ibis instance. This method gets called from the Ibis
* constructor.
*
* @exception IOException
* may be thrown in case of trouble.
* @return the implementation-dependent data, as a byte array.
*/
protected abstract byte[] getData() throws IOException;
public ibis.ipl.SendPort createSendPort(PortType tp) throws IOException {
return createSendPort(tp, null, null, null);
}
public ibis.ipl.SendPort createSendPort(PortType tp, String name)
throws IOException {
return createSendPort(tp, name, null, null);
}
private void matchPortType(PortType tp) {
boolean matched = false;
for (PortType p : portTypes) {
if (tp.equals(p)) {
matched = true;
}
}
if (!matched) {
throw new IbisConfigurationException("PortType \"" + tp
+ "\" not specified when creating this Ibis instance");
}
}
public ibis.ipl.SendPort createSendPort(PortType tp, String name,
SendPortDisconnectUpcall cU, Properties properties)
throws IOException {
if (tp.hasCapability(PortType.CONNECTION_ULTRALIGHT)) {
if (tp.hasCapability(PortType.CONNECTION_UPCALLS)) {
throw new IbisConfigurationException(
"Ultralight connections to not support connection upcalls");
}
if (tp.hasCapability(PortType.COMMUNICATION_RELIABLE)) {
throw new IbisConfigurationException(
"Ultralight connections do not support reliability");
}
if (tp.hasCapability(PortType.COMMUNICATION_FIFO)) {
throw new IbisConfigurationException(
"Ultralight connections do not support FIFO message ordering");
}
}
if (cU != null) {
if (!tp.hasCapability(PortType.CONNECTION_UPCALLS)) {
throw new IbisConfigurationException(
"no connection upcalls requested for this port type");
}
}
if (name == null) {
synchronized (this.getClass()) {
name = "anonymous send port " + send_counter++;
}
}
matchPortType(tp);
return doCreateSendPort(tp, name, cU, properties);
}
/**
* Creates a {@link ibis.ipl.SendPort} of the specified port type.
*
* @param tp
* the port type.
* @param name
* the name of this sendport.
* @param cU
* object implementing the
* {@link SendPortDisconnectUpcall#lostConnection(ibis.ipl.SendPort, ReceivePortIdentifier, Throwable)}
* method.
* @param properties
* the port properties.
* @return the new sendport.
* @exception java.io.IOException
* is thrown when the port could not be created.
*/
protected abstract ibis.ipl.SendPort doCreateSendPort(PortType tp,
String name, SendPortDisconnectUpcall cU, Properties properties)
throws IOException;
public ibis.ipl.ReceivePort createReceivePort(PortType tp, String name)
throws IOException {
return createReceivePort(tp, name, null, null, null);
}
public ibis.ipl.ReceivePort createReceivePort(PortType tp, String name,
MessageUpcall u) throws IOException {
return createReceivePort(tp, name, u, null, null);
}
public ibis.ipl.ReceivePort createReceivePort(PortType tp, String name,
ReceivePortConnectUpcall cU) throws IOException {
return createReceivePort(tp, name, null, cU, null);
}
public ibis.ipl.ReceivePort createReceivePort(PortType tp, String name,
MessageUpcall u, ReceivePortConnectUpcall cU, Properties properties)
throws IOException {
if (tp.hasCapability(PortType.CONNECTION_ULTRALIGHT)) {
if (tp.hasCapability(PortType.CONNECTION_UPCALLS)) {
throw new IbisConfigurationException(
"Ultralight connections to not support connection upcalls");
}
if (tp.hasCapability(PortType.COMMUNICATION_RELIABLE)) {
throw new IbisConfigurationException(
"Ultralight connections do not support reliability");
}
if (tp.hasCapability(PortType.COMMUNICATION_FIFO)) {
throw new IbisConfigurationException(
"Ultralight connections do not support FIFO message ordering");
}
}
if (cU != null) {
if (!tp.hasCapability(PortType.CONNECTION_UPCALLS)) {
throw new IbisConfigurationException(
"no connection upcalls requested for this port type");
}
}
if (u != null) {
if (!tp.hasCapability(PortType.RECEIVE_AUTO_UPCALLS)
&& !tp.hasCapability(PortType.RECEIVE_POLL_UPCALLS)) {
throw new IbisConfigurationException(
"no message upcalls requested for this port type");
}
} else {
if (!tp.hasCapability(PortType.RECEIVE_EXPLICIT)) {
throw new IbisConfigurationException(
"no explicit receive requested for this port type");
}
}
if (name == null) {
synchronized (this.getClass()) {
name = "anonymous receive port " + receive_counter++;
}
}
matchPortType(tp);
return doCreateReceivePort(tp, name, u, cU, properties);
}
/**
* Creates a named {@link ibis.ipl.ReceivePort} of the specified port type,
* with upcall based communication. New connections will not be accepted
* until {@link ibis.ipl.ReceivePort#enableConnections()} is invoked. This
* is done to avoid upcalls during initialization. When a new connection
* request arrives, or when a connection is lost, a ConnectUpcall is
* performed.
*
* @param tp
* the port type.
* @param name
* the name of this receiveport.
* @param u
* the upcall handler.
* @param cU
* object implementing <code>gotConnection</code>() and
* <code>lostConnection</code>() upcalls.
* @param properties
* the port properties.
* @return the new receiveport.
* @exception java.io.IOException
* is thrown when the port could not be created.
*/
protected abstract ibis.ipl.ReceivePort doCreateReceivePort(PortType tp,
String name, MessageUpcall u, ReceivePortConnectUpcall cU,
Properties properties) throws IOException;
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Protected management methods, can be overriden/used in implementations
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
public String getManagementProperty(String key)
throws NoSuchPropertyException {
String result = managementProperties().get(key);
if (result == null) {
throw new NoSuchPropertyException("property \"" + key
+ "\" not found");
}
return result;
}
public synchronized long getOutgoingMessageCount() {
long outgoingMessageCount = this.outgoingMessageCount;
// also add numbers for current send ports
for (SendPort sendPort : sendPorts.values()) {
outgoingMessageCount += sendPort.getMessageCount();
}
return outgoingMessageCount;
}
public synchronized long getBytesSent() {
long bytesSend = this.bytesSent;
// also add numbers for current send ports
for (SendPort sendPort : sendPorts.values()) {
bytesSend += sendPort.getBytesSent();
}
return bytesSend;
}
public synchronized long getBytesWritten() {
long bytesWritten = this.bytesWritten;
// also add numbers for current send ports
for (SendPort sendPort : sendPorts.values()) {
bytesWritten += sendPort.getBytesWritten();
}
return bytesWritten;
}
public synchronized long getIncomingMessageCount() {
long incomingMessageCount = this.incomingMessageCount;
// also add numbers for current receive ports
for (ReceivePort receivePort : receivePorts.values()) {
incomingMessageCount += receivePort.getMessageCount();
}
return incomingMessageCount;
}
public synchronized long getBytesReceived() {
long bytesReceived = this.bytesReceived;
// also add numbers for current receive ports
for (ReceivePort receivePort : receivePorts.values()) {
bytesReceived += receivePort.getBytesReceived();
}
return bytesReceived;
}
public synchronized long getBytesRead() {
long bytesRead = this.bytesRead;
// also add numbers for current receive ports
for (ReceivePort receivePort : receivePorts.values()) {
bytesRead += receivePort.getBytesReceived();
}
return bytesRead;
}
/**
* @ibis.experimental
*/
public synchronized ibis.ipl.IbisIdentifier[] connectedTo() {
HashSet<ibis.ipl.IbisIdentifier> result = new HashSet<ibis.ipl.IbisIdentifier>();
Collection<SendPort> ports = sendPorts.values();
for (SendPort sendPort : ports) {
ibis.ipl.ReceivePortIdentifier[] receivePorts = sendPort
.connectedTo();
for (ibis.ipl.ReceivePortIdentifier receivePort : receivePorts) {
result.add(receivePort.ibisIdentifier());
}
}
return result.toArray(new ibis.ipl.IbisIdentifier[0]);
}
/**
* @ibis.experimental
*/
public Coordinates getVivaldiCoordinates() {
if (vivaldiClient == null) {
return null;
}
return vivaldiClient.getCoordinates();
}
/**
* @ibis.experimental
*/
public synchronized Map<ibis.ipl.IbisIdentifier, Long> getSentBytesPerIbis() {
if (sentBytesPerIbis == null) {
return null;
}
return new HashMap<ibis.ipl.IbisIdentifier, Long>(sentBytesPerIbis);
}
/**
* @ibis.experimental
*/
public synchronized Map<ibis.ipl.IbisIdentifier, Long> getReceivedBytesPerIbis() {
if (receivedBytesPerIbis == null) {
return null;
}
return new HashMap<ibis.ipl.IbisIdentifier, Long>(receivedBytesPerIbis);
}
/**
* @ibis.experimental
*/
public String[] wonElections() {
return registry.wonElections();
}
/**
* @ibis.experimental
*/
public synchronized Map<ibis.ipl.IbisIdentifier, Set<String>> getReceiverConnectionTypes() {
Map<ibis.ipl.IbisIdentifier, Set<String>> result = new HashMap<ibis.ipl.IbisIdentifier, Set<String>>();
for (ReceivePort port : receivePorts.values()) {
Map<IbisIdentifier, Set<String>> p = port.getConnectionTypes();
for (Entry<IbisIdentifier, Set<String>> entry : p.entrySet()) {
Set<String> r = result.get(entry.getKey());
if (r == null) {
r = new HashSet<String>();
}
r.addAll(entry.getValue());
result.put(entry.getKey(), r);
}
}
return result;
}
/**
* @ibis.experimental
*/
public synchronized Map<ibis.ipl.IbisIdentifier, Set<String>> getSenderConnectionTypes() {
Map<ibis.ipl.IbisIdentifier, Set<String>> result = new HashMap<ibis.ipl.IbisIdentifier, Set<String>>();
for (SendPort port : sendPorts.values()) {
Map<IbisIdentifier, Set<String>> p = port.getConnectionTypes();
for (Entry<IbisIdentifier, Set<String>> entry : p.entrySet()) {
Set<String> r = result.get(entry.getKey());
if (r == null) {
r = new HashSet<String>();
}
r.addAll(entry.getValue());
result.put(entry.getKey(), r);
}
}
return result;
}
public synchronized Map<String, String> managementProperties() {
Map<String, String> result = new HashMap<String, String>();
// put gathered statistics in the map
result.put("outgoingMessageCount", "" + getOutgoingMessageCount());
result.put("bytesWritten", "" + getBytesWritten());
result.put("bytesSent", "" + getBytesSent());
result.put("incomingMessageCount", "" + getIncomingMessageCount());
result.put("bytesReceived", "" + getBytesReceived());
result.put("bytesRead", "" + getBytesRead());
return result;
}
public void printManagementProperties(PrintStream stream) {
stream.format("Messages Sent: %d\n", getOutgoingMessageCount());
double mbWritten = getBytesWritten() / 1024.0 / 1024.0;
stream.format("Data written to messages: %.2f Mb\n", mbWritten);
double mbSent = getBytesSent() / 1024.0 / 1024.0;
stream.format("Data sent out on network: %.2f Mb\n", mbSent);
stream.format("Messages Received: %d\n", getIncomingMessageCount());
double mbReceived = getBytesReceived() / 1024.0 / 1024.0;
stream.format("Data received from network: %.2f Mb\n", mbReceived);
double mbRead = getBytesRead() / 1024.0 / 1024.0;
stream.format("Data read from messages: %.2f Mb\n", mbRead);
stream.flush();
}
public void setManagementProperties(Map<String, String> properties)
throws NoSuchPropertyException {
// override if an Ibis _can_ set properties
throw new NoSuchPropertyException("cannot set any properties");
}
public void setManagementProperty(String key, String value)
throws NoSuchPropertyException {
// override if an Ibis _can_ set properties
throw new NoSuchPropertyException("cannot set any properties");
}
// jmx function
public String getIdentifier() {
return ident.toString();
}
}