package dmg.cells.services.login;
import com.google.common.base.Joiner;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.ProtocolFamily;
import java.net.StandardProtocolFamily;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import diskCacheV111.util.FsPath;
import org.dcache.util.NetworkUtils.InetAddressScope;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.stream.Collectors.toList;
/**
* Immutable object to capture information about a door.
*
* By convention, network addresses in LoginBrokerInfo should have been
* resolved, that is, the host name should be cached within the InetAddress
* object. This should preferably be the FQDN. The exception to this
* rule is when the IP addresses doesn't have a corresponding name.
*/
public class LoginBrokerInfo implements Serializable
{
private static final long serialVersionUID = 4077557054990432737L;
public enum Capability
{
READ, WRITE
}
private final String _cellName;
private final String _domainName;
private final String _protocolFamily;
private final String _protocolVersion;
private final String _protocolEngine;
private final String _root;
private final List<InetAddress> _addresses;
@Deprecated
private final String[] _hosts; // Kept for compatibility with pcells
private final int _port;
private final double _load;
private final long _update;
private final Collection<String> _tags;
private final Collection<String> _readPaths;
private final Collection<String> _writePaths;
private transient FsPath _rootFsPath;
private transient Collection<FsPath> _readFsPaths;
private transient Collection<FsPath> _writeFsPaths;
public LoginBrokerInfo(String cellName,
String domainName,
String protocolFamily,
String protocolVersion,
String protocolEngine,
String root,
Collection<String> readPaths,
Collection<String> writePaths,
Collection<String> tags,
List<InetAddress> addresses,
int port,
double load,
long updateTime)
{
checkArgument(!addresses.isEmpty());
_cellName = checkNotNull(cellName);
_domainName = checkNotNull(domainName);
_protocolFamily = checkNotNull(protocolFamily);
_protocolVersion = checkNotNull(protocolVersion);
_protocolEngine = checkNotNull(protocolEngine);
_root = root;
_tags = checkNotNull(tags);
_readPaths = checkNotNull(readPaths);
_writePaths = checkNotNull(writePaths);
_addresses = checkNotNull(addresses);
_port = port;
_load = load;
_update = updateTime;
_hosts = new String[addresses.size()];
for (int i = 0; i < addresses.size(); i++) {
_hosts[i] = addresses.get(i).getHostAddress();
}
if (_root != null) {
_rootFsPath = FsPath.create(_root);
}
_readFsPaths = _readPaths.stream().map(FsPath::create).collect(toList());
_writeFsPaths = _writePaths.stream().map(FsPath::create).collect(toList());
}
public boolean supports(InetAddressScope scope)
{
return _addresses.stream().anyMatch(a -> InetAddressScope.of(a).ordinal() >= scope.ordinal());
}
public boolean supports(ProtocolFamily family)
{
if (family == StandardProtocolFamily.INET) {
return _addresses.stream().anyMatch(a -> a instanceof Inet4Address);
} else if (family == StandardProtocolFamily.INET6) {
return _addresses.stream().anyMatch(a -> a instanceof Inet6Address);
}
return true;
}
@Nonnull
public List<InetAddress> getAddresses()
{
return Collections.unmodifiableList(_addresses);
}
@Deprecated
public String[] getHosts()
{
return _hosts;
}
public int getPort()
{
return _port;
}
@Nonnull
public String getCellName()
{
return _cellName;
}
@Nonnull
public String getDomainName()
{
return _domainName;
}
@Nonnull
public String getProtocolFamily()
{
return _protocolFamily;
}
@Nonnull
public String getProtocolVersion()
{
return _protocolVersion;
}
@Nonnull
public String getProtocolEngine()
{
return _protocolEngine;
}
public String getRoot()
{
return _root;
}
public FsPath getRoot(FsPath userRoot)
{
return (_rootFsPath == null) ? userRoot : _rootFsPath;
}
public String relativize(FsPath userRoot, FsPath path)
{
return path.stripPrefix(getRoot(userRoot));
}
public boolean canWrite(FsPath userRoot, FsPath path)
{
return path.hasPrefix(getRoot(userRoot)) &&
_writeFsPaths.stream().anyMatch(path::hasPrefix);
}
public boolean canRead(FsPath userRoot, FsPath path)
{
return path.hasPrefix(getRoot(userRoot)) &&
_readFsPaths.stream().anyMatch(path::hasPrefix);
}
public Collection<String> getTags()
{
return Collections.unmodifiableCollection(_tags);
}
public Collection<String> getReadPaths()
{
return Collections.unmodifiableCollection(_readPaths);
}
public Collection<String> getWritePaths()
{
return Collections.unmodifiableCollection(_writePaths);
}
public void ifCapableOf(Capability capability, Consumer<LoginBrokerInfo> action)
{
Collection<String> paths;
switch (capability) {
case READ:
paths = _readPaths;
break;
case WRITE:
paths = _writePaths;
break;
default:
paths = Collections.emptyList();
break;
}
if (!paths.isEmpty()) {
action.accept(this);
}
}
public double getLoad()
{
return _load;
}
public long getUpdateTime()
{
return _update;
}
@Nonnull
public String getIdentifier()
{
return _cellName + '@' + _domainName;
}
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append(_cellName).append('@').append(_domainName).append(';');
int pos = _protocolEngine.lastIndexOf('.');
if (pos < 0 || pos == _protocolEngine.length() - 1) {
sb.append(_protocolEngine).append(';');
} else {
sb.append(_protocolEngine.substring(pos + 1)).append(';');
}
sb.append('{').append(_protocolFamily).append(',').
append(_protocolVersion).append("};");
sb.append('[');
Joiner.on(",").appendTo(sb, _addresses);
sb.append(':').append(_port).append(']').append(';');
sb.append('<');
sb.append((int) (_load * 100.)).append(',');
sb.append(_update).append(">;");
return sb.toString();
}
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException
{
stream.defaultReadObject();
if (_root != null) {
_rootFsPath = FsPath.create(_root);
}
_readFsPaths = _readPaths.stream().map(FsPath::create).collect(toList());
_writeFsPaths = _writePaths.stream().map(FsPath::create).collect(toList());
}
}