
org.wildfly.discovery.ServiceURL Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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 org.wildfly.discovery;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.wildfly.common.Assert;
/**
* An RFC 2609-compliant service description URL. This implementation
* only deviates from the specification in that it does not support AppleTalk or IPX address schemes.
*
* @author David M. Lloyd
*/
public final class ServiceURL extends ServiceDesignation {
private static final long serialVersionUID = -5463002934752940723L;
private final String abstractType;
private final String abstractTypeAuthority;
private final URI uri;
private final String uriSchemeAuthority;
private final Map> attributes;
private transient Set attributeNames;
private transient int hashCode;
private transient String toString;
private transient URI toServiceURI;
ServiceURL(Builder builder, Map> attributes) {
this.abstractType = builder.abstractType;
this.abstractTypeAuthority = builder.abstractType == null ? null : builder.abstractTypeAuthority;
this.uri = Assert.checkNotNullParam("uri", builder.uri);
this.uriSchemeAuthority = builder.uriSchemeAuthority;
this.attributes = attributes;
}
/**
* A builder for service URLs.
*/
public static class Builder {
private String abstractType;
private String abstractTypeAuthority;
private URI uri;
private String uriSchemeAuthority;
private Map> attributes;
/**
* Construct a new instance.
*/
public Builder() {
attributes = new HashMap<>();
}
/**
* Construct a new instance from an original template.
*
* @param original the original service URL (must not be {@code null})
*/
public Builder(ServiceURL original) {
abstractType = original.getAbstractType();
abstractTypeAuthority = original.getAbstractTypeAuthority();
uri = original.getLocationURI();
uriSchemeAuthority = original.getUriSchemeAuthority();
final Map> attributes = original.getAttributes();
Map> map = new HashMap<>(attributes.size());
for (Map.Entry> entry : attributes.entrySet()) {
map.put(entry.getKey(), new LinkedHashSet<>(entry.getValue()));
}
this.attributes = map;
}
/**
* Get the abstract type.
*
* @return the abstract type, or {@code null} if it is not set
*/
public String getAbstractType() {
return abstractType;
}
/**
* Set the abstract type.
*
* @param abstractType the abstract type
* @return this builder
*/
public Builder setAbstractType(final String abstractType) {
this.abstractType = abstractType;
return this;
}
/**
* Get the abstract type authority.
*
* @return the abstract type authority, or {@code null} if it is not set
*/
public String getAbstractTypeAuthority() {
return abstractTypeAuthority;
}
/**
* Set the abstract authority.
*
* @param abstractTypeAuthority the abstract authority
* @return this builder
*/
public Builder setAbstractTypeAuthority(final String abstractTypeAuthority) {
this.abstractTypeAuthority = abstractTypeAuthority;
return this;
}
/**
* Get the concrete URI.
*
* @return the concrete URI
*/
public URI getUri() {
return uri;
}
/**
* Set the concrete URI.
*
* @param uri the concrete URI (must not be {@code null})
* @return this builder
*/
public Builder setUri(final URI uri) {
Assert.checkNotNullParam("uri", uri);
final String fragment = uri.getFragment();
if (fragment != null && ! fragment.isEmpty()) {
throw new IllegalArgumentException("Service URI " + uri + " may not have a fragment");
}
if (! uri.isAbsolute()) {
throw new IllegalArgumentException("Service URI " + uri + " must be absolute");
}
final String query = uri.getQuery();
// sanitized URI
try {
this.uri = uri.isOpaque() ?
new URI(uri.getScheme(), uri.getSchemeSpecificPart(), null) :
new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), uri.getPath(), query != null && query.isEmpty() ? null : query, null);
} catch (URISyntaxException e) {
// should be impossible as the original URI was valid
throw new IllegalStateException(e);
}
return this;
}
/**
* Get the URI scheme authority, if any.
*
* @return the URI scheme authority, or {@code null} if none is set
*/
public String getUriSchemeAuthority() {
return uriSchemeAuthority;
}
/**
* Set the URI scheme authority.
*
* @param uriSchemeAuthority the URI scheme authority
* @return this builder
*/
public Builder setUriSchemeAuthority(final String uriSchemeAuthority) {
this.uriSchemeAuthority = uriSchemeAuthority;
return this;
}
/**
* Add an attribute.
*
* @param name the attribute name (must not be {@code null})
* @param value the attribute value (must not be {@code null})
* @return this builder
*/
public Builder addAttribute(final String name, final AttributeValue value) {
Assert.checkNotNullParam("name", name);
Assert.checkNotNullParam("value", value);
attributes.computeIfAbsent(name, n -> new LinkedHashSet<>()).add(value);
return this;
}
/**
* Add a valueless attribute.
*
* @param name the attribute name (must not be {@code null})
* @return this builder
*/
public Builder addAttribute(final String name) {
Assert.checkNotNullParam("name", name);
attributes.computeIfAbsent(name, n -> new LinkedHashSet<>()).add(null);
return this;
}
/**
* Remove all values of the given attribute name.
*
* @param name the attribute name (must not be {@code null})
* @return the removed attribute values, or {@code null} if the attribute was not present in the builder
*/
public List removeAttribute(final String name) {
Assert.checkNotNullParam("name", name);
final LinkedHashSet removed = attributes.remove(name);
if (removed == null) {
return Collections.emptyList();
}
final Iterator iterator = removed.iterator();
if (! iterator.hasNext()) {
return Collections.emptyList();
}
final AttributeValue first = iterator.next();
if (! iterator.hasNext()) {
return Collections.singletonList(first);
}
final ArrayList list = new ArrayList<>(removed.size());
list.add(first);
do {
list.add(iterator.next());
} while (iterator.hasNext());
return list;
}
/**
* Remove the given value of the given attribute name.
*
* @param name the attribute name (must not be {@code null})
* @param value the value to remove (must not be {@code null})
* @return {@code true} if the value was present, or {@code false} otherwise
*/
public boolean removeAttributeValue(final String name, final AttributeValue value) {
Assert.checkNotNullParam("name", name);
Assert.checkNotNullParam("value", value);
final LinkedHashSet set = attributes.get(name);
if (set == null) {
return false;
}
if (set.remove(value)) {
if (set.isEmpty()) {
attributes.remove(name, set);
}
return true;
}
return false;
}
/**
* Construct the service URL.
*
* @return the service URL
* @throws IllegalArgumentException if one or more builder property values is not acceptable
*/
public ServiceURL create() {
final HashMap> map = new HashMap<>(attributes.size());
for (Map.Entry> entry : attributes.entrySet()) {
map.put(entry.getKey(), unmodList(entry.getValue()));
}
return new ServiceURL(this, map);
}
static List unmodList(Collection original) {
if (original.isEmpty()) {
return Collections.emptyList();
} else if (original.size() == 1) {
return Collections.singletonList(original.iterator().next());
} else {
return Collections.unmodifiableList(new ArrayList<>(original));
}
}
}
/**
* Determine whether this service URL satisfies the given filter specification.
*
* @param filterSpec the filter specification
* @return {@code true} if this service satisfies the filter specification, {@code false} if it does not
*/
public boolean satisfies(final FilterSpec filterSpec) {
return filterSpec == null || filterSpec.matchesMulti(attributes);
}
/**
* Determine if this service URL implies the other service URL. This is true only when the two are equal.
*
* @param other the other service URL
* @return {@code true} if they are equal, {@code false} otherwise
*/
public boolean implies(final ServiceURL other) {
return equals(other);
}
/**
* Determine if this service URL implies the other service designation. This is true only when the other designation
* is a service URL and the two are equal.
*
* @param other the other service designation
* @return {@code true} if they are equal service URLs, {@code false} otherwise
*/
public boolean implies(final ServiceDesignation other) {
return other instanceof ServiceURL && implies((ServiceURL) other);
}
/**
* Determine whether this service URL is equal to the given object. This is true when the other object is a service
* URL with the same abstract type and authority, the same concrete URI, and the same attributes.
*
* @param other the other object
* @return {@code true} if they are equal, {@code false} otherwise
*/
public boolean equals(final ServiceURL other) {
return hashCode() == other.hashCode()
&& Objects.equals(abstractType, other.abstractType)
&& Objects.equals(abstractTypeAuthority, other.abstractTypeAuthority)
&& Objects.equals(uri, other.uri)
&& Objects.equals(uriSchemeAuthority, other.uriSchemeAuthority)
&& attributes.equals(other.attributes);
}
/**
* Determine whether this service URL is equal to the given object. This is true when the other object is a service
* URL with the same abstract type and authority, the same concrete URI, and the same attributes.
*
* @param other the other object
* @return {@code true} if they are equal, {@code false} otherwise
*/
public boolean equals(final ServiceDesignation other) {
return other instanceof ServiceURL && equals((ServiceURL) other);
}
/**
* Determine whether this service URL is equal to the given object. This is true when the other object is a service
* URL with the same abstract type and authority, the same concrete URI, and the same attributes.
*
* @param other the other object
* @return {@code true} if they are equal, {@code false} otherwise
*/
public boolean equals(final Object other) {
return other instanceof ServiceURL && equals((ServiceURL) other);
}
/**
* Get the hash code of this service URL. Service URLs are suitable for use as hash table keys.
*
* @return the hash code
*/
public int hashCode() {
int hashCode = this.hashCode;
if (hashCode == 0) {
if (abstractType != null) {
hashCode = abstractType.hashCode();
}
hashCode *= 17;
if (abstractTypeAuthority != null) {
hashCode += abstractTypeAuthority.hashCode();
}
hashCode *= 17;
hashCode += uri.hashCode();
hashCode *= 17;
if (uriSchemeAuthority != null) {
hashCode += uriSchemeAuthority.hashCode();
}
hashCode *= 17;
hashCode += attributes.hashCode();
if (hashCode == 0) {
hashCode = -1;
}
this.hashCode = hashCode;
}
return hashCode;
}
/**
* Get the string representation of this service URL.
*
* @return the string representation
*/
public String toString() {
String toString = this.toString;
if (toString == null) {
StringBuilder b = new StringBuilder(40);
b.append("service:");
if (abstractType != null) {
b.append(abstractType);
if (abstractTypeAuthority != null) {
b.append('.').append(abstractTypeAuthority);
}
b.append(':');
}
b.append(uri.getScheme());
if (uriSchemeAuthority != null) {
b.append('.').append(uriSchemeAuthority);
}
b.append(':').append(uri.getRawSchemeSpecificPart());
for (Map.Entry> entry : attributes.entrySet()) {
b.append(';').append(entry.getKey());
Iterator iterator = entry.getValue().iterator();
if (iterator.hasNext()) {
b.append('=');
b.append(iterator.next());
while (iterator.hasNext()) {
b.append(',');
b.append(iterator.next());
}
}
}
this.toString = toString = b.toString();
}
return toString;
}
/**
* Convert this service URL into a URI whose contents are exactly equal to this object's.
*
* @return the URI (not {@code null})
* @throws URISyntaxException if there was some syntactical problem in the URI being constructed
*/
public URI toServiceURI() throws URISyntaxException {
URI toServiceURI = this.toServiceURI;
if (toServiceURI == null) {
toServiceURI = this.toServiceURI = new URI(toString());
}
return toServiceURI;
}
/**
* Get the concrete location URI of this service URL.
*
* @return the concrete location (not {@code null})
*/
public URI getLocationURI() {
return uri;
}
/**
* Get the service type of this URL.
*
* @return the service type (not {@code null})
*/
public ServiceType getServiceType() {
return abstractType != null ? new ServiceType(abstractType, abstractTypeAuthority, uri.getScheme(), uriSchemeAuthority) : new ServiceType(uri.getScheme(), uriSchemeAuthority, null, null);
}
/**
* Get the abstract type, if any.
*
* @return the abstract type, or {@code null} if no abstract type is set
*/
public String getAbstractType() {
return abstractType;
}
/**
* Get the abstract type authority, if any.
*
* @return the abstract type authority, or {@code null} if no abstract type authority is set
*/
public String getAbstractTypeAuthority() {
return abstractTypeAuthority;
}
/**
* Get the concrete URI scheme.
*
* @return the concrete URI scheme (not {@code null})
*/
public String getUriScheme() {
return uri.getScheme();
}
/**
* Get the concrete URI scheme authority, if any.
*
* @return the concrete URI scheme authority, or {@code null} if none is set
*/
public String getUriSchemeAuthority() {
return uriSchemeAuthority;
}
/**
* Get the user name of the concrete URI, if any.
*
* @return the user name of the concrete URI, or {@code null} if none is set
*/
public String getUserName() {
return uri.getUserInfo();
}
/**
* Get the host name of the concrete URI.
*
* @return the host name of the concrete URI (not {@code null})
*/
public String getHostName() {
return uri.getHost();
}
/**
* Get the port number of the concrete URI, if any.
*
* @return the port number of the concrete URI, or -1 if it is undefined
*/
public int getPort() {
return uri.getPort();
}
/**
* Get the path name of the concrete URI, if any.
*
* @return the path name of the concrete URI, or {@code null} if none is set
*/
public String getPath() {
return uri.getPath();
}
/**
* Get the first attribute value for the given name.
*
* @param name the attribute name (must not be {@code null})
* @return the first attribute value for the given name, or {@code null} if no such attribute exists
*/
public AttributeValue getFirstAttributeValue(String name) {
Assert.checkNotNullParam("name", name);
final List list = attributes.getOrDefault(name, Collections.emptyList());
return list.isEmpty() ? null : list.get(0);
}
/**
* Get the first attribute value for the given name.
*
* @param name the attribute name (must not be {@code null})
* @param defaultValue the value to return if no such attribute exists
* @return the first attribute value for the given name, or {@code defaultValue} if no such attribute exists
*/
public AttributeValue getFirstAttributeValue(String name, AttributeValue defaultValue) {
Assert.checkNotNullParam("name", name);
final List list = attributes.getOrDefault(name, Collections.emptyList());
return list.isEmpty() ? defaultValue : list.get(0);
}
/**
* Get the last attribute value for the given name.
*
* @param name the attribute name (must not be {@code null})
* @return the last attribute value for the given name, or {@code null} if no such attribute exists
*/
public AttributeValue getLastAttributeValue(String name) {
Assert.checkNotNullParam("name", name);
final List list = attributes.getOrDefault(name, Collections.emptyList());
return list.isEmpty() ? null : list.get(list.size() - 1);
}
/**
* Get the last attribute value for the given name.
*
* @param name the attribute name (must not be {@code null})
* @param defaultValue the value to return if no such attribute exists
* @return the last attribute value for the given name, or {@code null} if no such attribute exists
*/
public AttributeValue getLastAttributeValue(String name, AttributeValue defaultValue) {
Assert.checkNotNullParam("name", name);
final List list = attributes.getOrDefault(name, Collections.emptyList());
return list.isEmpty() ? defaultValue : list.get(list.size() - 1);
}
/**
* Get the values of the attribute with the given name. If no such attribute exists, an empty list is returned.
*
* @param name the attribute name (must not be {@code null})
* @return the values of the attribute with the given name (not {@code null})
*/
public List getAttributeValues(String name) {
Assert.checkNotNullParam("name", name);
return attributes.getOrDefault(name, Collections.emptyList());
}
/**
* Get the attribute names. If no attributes exist, an empty set is returned.
* @return an unmodifiable set of attribute names (not {@code null})
*/
public Set getAttributeNames() {
Set attributeNames = this.attributeNames;
if (attributeNames == null) {
attributeNames = this.attributeNames = Collections.unmodifiableSet(this.attributes.keySet());
}
return attributeNames;
}
Map> getAttributes() {
return attributes;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy