io.aeron.ChannelUri Maven / Gradle / Ivy
Show all versions of aeron-client Show documentation
/*
* Copyright 2014-2017 Real Logic Ltd.
*
* 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 io.aeron;
import java.util.*;
import static io.aeron.CommonContext.SPY_PREFIX;
/**
* Parser for Aeron channel URIs. The format is:
*
* aeron-uri = "aeron:" media [ "?" param *( "|" param ) ]
* media = *( "[^?:]" )
* param = key "=" value
* key = *( "[^=]" )
* value = *( "[^|]" )
*
*
* Multiple params with the same key are allowed, the last value specified takes precedence.
* @see ChannelUriStringBuilder
*/
public class ChannelUri
{
private enum State
{
MEDIA, PARAMS_KEY, PARAMS_VALUE
}
/**
* URI Scheme for Aeron channels.
*/
public static final String AERON_SCHEME = "aeron";
/**
* Qualifier for spy subscriptions.
*/
public static final String SPY_QUALIFIER = "aeron-spy";
private static final String AERON_PREFIX = AERON_SCHEME + ":";
private final String prefix;
private final String media;
private final Map params;
/**
* Construct with the components provided to avoid parsing.
*
* @param prefix empty if no prefix is required otherwise expected to be 'aeron-spy'
* @param media for the channel which is typically "udp" or "ipc".
* @param params for the query string as key value pairs.
*/
public ChannelUri(final String prefix, final String media, final Map params)
{
this.prefix = prefix;
this.media = media;
this.params = params;
}
/**
* Construct with the components provided to avoid parsing.
*
* @param media for the channel which is typically "udp" or "ipc".
* @param params for the query string as key value pairs.
*/
public ChannelUri(final String media, final Map params)
{
this("", media, params);
}
/**
* The prefix for the channel.
*
* @return the prefix for the channel.
*/
public String prefix()
{
return prefix;
}
/**
* The media over which the channel operates.
*
* @return the media over which the channel operates.
*/
public String media()
{
return media;
}
/**
* The scheme for the URI. Must be "aeron".
*
* @return the scheme for the URI.
*/
public String scheme()
{
return AERON_SCHEME;
}
/**
* Get a value for a given parameter key.
*
* @param key to lookup.
* @return the value if set for the key otherwise null.
*/
public String get(final String key)
{
return params.get(key);
}
/**
* Get the value for a given parameter key or the default value provided if the key does not exist.
*
* @param key to lookup.
* @param defaultValue to be returned if no key match is found.
* @return the value if set for the key otherwise the default value provided.
*/
public String get(final String key, final String defaultValue)
{
final String value = params.get(key);
if (null != value)
{
return value;
}
return defaultValue;
}
/**
* Put a key and value pair in the map of params.
*
* @param key of the param to be put.
* @param value of the param to be put.
* @return the existing value otherwise null.
*/
public String put(final String key, final String value)
{
return params.put(key, value);
}
/**
* Does the URI contain a value for the given key.
*
* @param key to be lookup.
* @return true if the key has a value otherwise false.
*/
public boolean containsKey(final String key)
{
return params.containsKey(key);
}
/**
* Generate a String representation of the URI that is valid for an Aeron channel.
*
* @return a String representation of the URI that is valid for an Aeron channel.
*/
public String toString()
{
final StringBuilder sb;
if (prefix == null || "".equals(prefix))
{
sb = new StringBuilder((params.size() * 20) + 10);
}
else
{
sb = new StringBuilder((params.size() * 20) + 20);
sb.append(SPY_PREFIX);
}
sb.append(AERON_PREFIX).append(media);
if (params.size() > 0)
{
sb.append('?');
for (final Map.Entry entry : params.entrySet())
{
sb.append(entry.getKey()).append('=').append(entry.getValue()).append('|');
}
sb.setLength(sb.length() - 1);
}
return sb.toString();
}
/**
* Parse a {@link CharSequence} which contains an Aeron URI.
*
* @param cs to be parsed.
* @return a new {@link ChannelUri} representing the URI string.
*/
public static ChannelUri parse(final CharSequence cs)
{
int position = 0;
final String prefix;
if (startsWith(cs, SPY_PREFIX))
{
prefix = SPY_QUALIFIER;
position = SPY_PREFIX.length();
}
else
{
prefix = "";
}
if (!startsWith(cs, position, AERON_PREFIX))
{
throw new IllegalArgumentException("Aeron URIs must start with 'aeron:', found: '" + cs + "'");
}
else
{
position += AERON_PREFIX.length();
}
final StringBuilder builder = new StringBuilder();
final Map params = new HashMap<>();
String media = null;
String key = null;
State state = State.MEDIA;
for (int i = position; i < cs.length(); i++)
{
final char c = cs.charAt(i);
switch (state)
{
case MEDIA:
switch (c)
{
case '?':
media = builder.toString();
builder.setLength(0);
state = State.PARAMS_KEY;
break;
case ':':
throw new IllegalArgumentException("Encountered ':' within media definition");
default:
builder.append(c);
}
break;
case PARAMS_KEY:
switch (c)
{
case '=':
key = builder.toString();
builder.setLength(0);
state = State.PARAMS_VALUE;
break;
default:
builder.append(c);
}
break;
case PARAMS_VALUE:
switch (c)
{
case '|':
params.put(key, builder.toString());
builder.setLength(0);
state = State.PARAMS_KEY;
break;
default:
builder.append(c);
}
break;
default:
throw new IllegalStateException("Que? state=" + state);
}
}
switch (state)
{
case MEDIA:
media = builder.toString();
break;
case PARAMS_VALUE:
params.put(key, builder.toString());
break;
default:
throw new IllegalArgumentException("No more input found, but was in state: " + state);
}
return new ChannelUri(prefix, media, params);
}
private static boolean startsWith(final CharSequence input, final int position, final CharSequence prefix)
{
if ((input.length() - position) < prefix.length())
{
return false;
}
for (int i = 0; i < prefix.length(); i++)
{
if (input.charAt(position + i) != prefix.charAt(i))
{
return false;
}
}
return true;
}
private static boolean startsWith(final CharSequence input, final CharSequence prefix)
{
return startsWith(input, 0, prefix);
}
}