org.wildfly.discovery.ServiceURL Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
/*
* 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;
}
}