/*
* Copyright © 2014-2015 Cask Data, Inc.
*
* 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 co.cask.cdap.common.queue;
import co.cask.cdap.proto.Id;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import java.net.URI;
import java.util.Iterator;
/**
* An abstraction over URI of a queue.
*/
public final class QueueName {
/**
* URI of the queue.
*/
private final URI uri;
/**
* The components of the URI.
*/
private final String[] components;
/**
* Represents the queue as byte[].
*/
private final byte[] byteName;
/**
* Represents the queue name as a string.
*/
private final String stringName;
/**
* Constructs this class from an URI.
*
* @param uri of the queue
* @return An instance of {@link QueueName}
*/
public static QueueName from(URI uri) {
Preconditions.checkNotNull(uri, "URI cannot be null.");
return new QueueName(uri);
}
/**
* Constructs this class from byte array of queue URI.
*
* @param bytes respresenting URI
* @return An instance of {@link QueueName}
*/
public static QueueName from(byte[] bytes) {
return new QueueName(URI.create(new String(bytes, Charsets.US_ASCII)));
}
public static QueueName fromFlowlet(Id.Flow flow, String flowlet, String output) {
return fromFlowlet(flow.getNamespaceId(), flow.getApplicationId(), flow.getId(), flowlet, output);
}
public static QueueName fromFlowlet(Id.Namespace namespace, String app, String flow, String flowlet, String output) {
return fromFlowlet(namespace.getId(), app, flow, flowlet, output);
}
public static QueueName fromFlowlet(String namespace, String app, String flow, String flowlet, String output) {
URI uri = URI.create(String.format("queue:///%s/%s/%s/%s/%s", namespace, app, flow, flowlet, output));
return new QueueName(uri);
}
public static String prefixForFlow(Id.Flow flowId) {
// queue:///namespace/app/flow/
// Note that the trailing / is crucial, otherwise this could match queues of flow1, flowx, etc.
return String.format("queue:///%s/%s/%s/", flowId.getNamespaceId(), flowId.getApplicationId(), flowId.getId());
}
// Note that like above the trailing '/' in the prefix for namespace is crucial,
// otherwise this could match namespaces of ns, ns1, nsx, etc.
public static String prefixForNamespacedQueue(String namespace) {
// queue:///namespace/
return String.format("queue:///%s/", namespace);
}
public static String prefixForNamedspacedStream(String namespace) {
// stream:///namespace/
return String.format("stream:///%s/", namespace);
}
/**
* Generates an QueueName for the stream.
*
* @param namespace of the stream
* @param stream name of the stream
* @return An {@link QueueName} with schema as stream
*/
public static QueueName fromStream(String namespace, String stream) {
// The old stream admin uses full URI of queue name as the name
URI uri = URI.create(String.format("stream:///%s/%s", namespace, stream));
return new QueueName(uri);
}
public static QueueName fromStream(Id.Stream streamId) {
return fromStream(streamId.getNamespaceId(), streamId.getId());
}
public Id.Stream toStreamId() {
return Id.Stream.from(getFirstComponent(), getSecondComponent());
}
/**
* Called from static method {@code QueueName#from(URI)} and {@code QueueName#from(bytes[])}.
*
* @param uri of the queue.
*/
private QueueName(URI uri) {
this.uri = uri;
this.stringName = uri.toASCIIString();
this.byteName = stringName.getBytes(Charsets.US_ASCII);
Iterable<String> comps = Splitter.on('/').omitEmptyStrings().split(uri.getPath());
components = new String[Iterables.size(comps)];
Iterator<String> iter = comps.iterator();
for (int i = 0; i < components.length; i++) {
components[i] = iter.next();
}
}
public boolean isStream() {
return "stream".equals(uri.getScheme());
}
public boolean isQueue() {
return "queue".equals(uri.getScheme());
}
private String getNthComponent(int n) {
return n < components.length ? components[n] : null;
}
/**
* @return the first component of the of the URI (the namespace)
*/
public String getFirstComponent() {
return getNthComponent(0);
}
/**
* @return the second component of the URI (the app for a queue, the stream name for a stream).
*/
public String getSecondComponent() {
return getNthComponent(1);
}
/**
* @return the third component of the URI (the flow for a queue, null for a stream).
*/
public String getThirdComponent() {
return getNthComponent(2);
}
/**
* @return the fourth component of the URI (the flowlet for a queue, null for a stream).
*/
public String getFourthComponent() {
return getNthComponent(3);
}
/**
* @return Simple name which is the last part of queue URI path and endpoint.
*/
public String getSimpleName() {
return components[components.length - 1];
}
/**
* @return the number of components in the queue name
*/
public int getNumComponents() {
return components.length;
}
/**
* Gets the bytes representation of the queue uri. Note that mutating the returned array will mutate the underlying
* byte array as well. If mutation is needed, one has to copy to a separate array.
*
* @return bytes representation of queue uri.
*/
public byte[] toBytes() {
return byteName;
}
/**
* @return A {@link URI} representation of the queue name.
*/
public URI toURI() {
return uri;
}
/**
* @return converts to ascii string.
*/
@Override
public String toString() {
return stringName;
}
/**
* @return whether the given string represents a queue.
*/
public static boolean isQueue(String name) {
return name.startsWith("queue");
}
/**
* @return whether the given string represents a queue.
*/
public static boolean isStream(String name) {
return name.startsWith("stream");
}
/**
* Compares this {@link QueueName} with another {@link QueueName}.
*
* @param o Other instance of {@link QueueName}
* @return true if equal; false otherwise.
*/
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
return uri.equals(((QueueName) o).uri);
}
/**
* @return hash code of this QueueName.
*/
@Override
public int hashCode() {
return uri.hashCode();
}
}