Skip to content

Change node naming #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jun 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .codestyle/pmd.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ limitations under the License.
<exclude name="LongVariable" />
<exclude name="MethodArgumentCouldBeFinal" />
<exclude name="OnlyOneReturn" />
<exclude name="PrematureDeclaration" />
<exclude name="ShortClassName" />
<exclude name="ShortMethodName" />
<exclude name="ShortVariable" />
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Rething caches in terms and modules.
- Add lazy deserialization for terms.

## [1.3.0](https://github.com/appulse-projects/encon-java/releases/tag/1.3.0) - 2018-06-28

Add correct node naming.

### Changed

- Configs from `encon-config` are now cloneable via constructor.
- `NodeDescriptor` works as in Erlang's docs said.
- Several minor fixes.

## [1.2.0](https://github.com/appulse-projects/encon-java/releases/tag/1.2.0) - 2018-06-25

Refactoring and cleaning.
Expand Down
2 changes: 1 addition & 1 deletion encon-common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ limitations under the License.
<parent>
<groupId>io.appulse.encon</groupId>
<artifactId>encon-parent</artifactId>
<version>1.2.0</version>
<version>1.3.0</version>
</parent>

<artifactId>encon-common</artifactId>
Expand Down
204 changes: 173 additions & 31 deletions encon-common/src/main/java/io/appulse/encon/common/NodeDescriptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
import java.net.UnknownHostException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
Expand All @@ -50,72 +50,214 @@
@Value
@EqualsAndHashCode(of = "fullName")
@AllArgsConstructor(access = PRIVATE)
@SuppressWarnings({
"PMD.AvoidThrowingRawExceptionTypes",
"PMD.AvoidLiteralsInIfCondition"
})
@SuppressFBWarnings("RV_RETURN_VALUE_OF_PUTIFABSENT_IGNORED")
public class NodeDescriptor implements Serializable {

private static final long serialVersionUID = 7324588959922091097L;

private static final Map<String, NodeDescriptor> NODE_DESCRIPTOR_CACHE;

private static final Function<String, NodeDescriptor> COMPUTE_NODE_DESCRIPTOR;
private static final InetAddress LOCALHOST;

private static final InetAddress LOOPBACK;

private static final String HOST_NAME;

static {
val loopback = InetAddress.getLoopbackAddress();
try {
LOCALHOST = InetAddress.getLocalHost();
} catch (UnknownHostException ex) {
throw new RuntimeException(ex);
}
LOOPBACK = InetAddress.getLoopbackAddress();
HOST_NAME = LOCALHOST.getHostName();
NODE_DESCRIPTOR_CACHE = new ConcurrentHashMap<>();
COMPUTE_NODE_DESCRIPTOR = str -> {
val tokens = str.split("@", 2);
val shortName = tokens[0];
val fullName = tokens.length == 2
? str
: shortName + '@' + loopback.getHostName();

val address = tokens.length == 2
? getByName(tokens[1])
: loopback;

return new NodeDescriptor(shortName, fullName, address);
};
}

/**
* Parses node descriptor from string.
* <p>
* A string could be short like <b>popa_node</b> and full, like <b>popa_node@192.168.0.32</b>.
* A string could be short like <b>popa_node</b> and long, like <b>popa_node@192.168.0.32</b>.
* <p>
* Node descriptors are caching.
*
* @param node node name
*
* @return parsed {@link NodeDescriptor} instance
*/
@SneakyThrows
public static NodeDescriptor from (@NonNull String node) {
val cached = NODE_DESCRIPTOR_CACHE.get(node);
if (cached != null) {
return cached;
}
return of(node)
val descriptor = getFromCacheOrCreateNew(node);
NODE_DESCRIPTOR_CACHE.putIfAbsent(node, descriptor);
NODE_DESCRIPTOR_CACHE.putIfAbsent(descriptor.getFullName(), descriptor);
NODE_DESCRIPTOR_CACHE.putIfAbsent(descriptor.getNodeName(), descriptor);
return descriptor;
}

/**
* Parses node descriptor from string.
* <p>
* A string could be short like <b>popa_node</b> and long, like <b>popa_node@192.168.0.32</b>.
* <p>
* Node descriptors are caching.
*
* @param node node name
*
* @param isShortName indicates is this node name short or long
*
* @return parsed {@link NodeDescriptor} instance
*/

public static NodeDescriptor from (@NonNull String node, boolean isShortName) {
val cached = NODE_DESCRIPTOR_CACHE.get(node);
if (cached != null) {
return cached;
}
val descriptor = getFromCacheOrCreateNew(node, isShortName);
NODE_DESCRIPTOR_CACHE.putIfAbsent(node, descriptor);
NODE_DESCRIPTOR_CACHE.putIfAbsent(descriptor.getFullName(), descriptor);
NODE_DESCRIPTOR_CACHE.putIfAbsent(descriptor.getNodeName(), descriptor);
return descriptor;
}

/**
* Checks, if this node cached or not.
*
* @param node node name
*
* @return {@code true}, if node descriptor was cached, or
* {@code false} if there was no such node
*/
public static boolean wasCached (@NonNull String node) {
val descriptor = getFromCacheOrCreateNew(node);
return wasCached(descriptor);
}

/**
* Checks, if this node cached or not.
*
* @param descriptor node descriptor
*
* @return {@code true}, if node descriptor was cached, or
* {@code false} if there was no such node
*/
public static boolean wasCached (@NonNull NodeDescriptor descriptor) {
return NODE_DESCRIPTOR_CACHE.containsKey(descriptor.getNodeName()) ||
NODE_DESCRIPTOR_CACHE.containsKey(descriptor.getFullName());
}

/**
* Removes a node from cached values.
*
* @param node node name
*
* @return {@code true}, if node descriptor was removed, or
* {@code false} if there was no such node
*/
public static boolean removeFromCache (@NonNull String node) {
val descriptor = getFromCacheOrCreateNew(node);
return removeFromCache(descriptor);
}

/**
* Removes a node from cached values.
*
* @param descriptor node descriptor
*
* @return {@code true}, if node descriptor was removed, or
* {@code false} if there was no such node
*/
public static boolean removeFromCache (@NonNull NodeDescriptor descriptor) {
val wasRemoved = NODE_DESCRIPTOR_CACHE.remove(descriptor.getNodeName()) != null;
return NODE_DESCRIPTOR_CACHE.remove(descriptor.getFullName()) != null || wasRemoved;
}

private static NodeDescriptor getFromCacheOrCreateNew (@NonNull String node) {
val atIndex = node.indexOf('@');
val isShortName = atIndex < 0 || node.indexOf('.', atIndex) < 0;
return getFromCacheOrCreateNew(node, isShortName);
}

private static NodeDescriptor getFromCacheOrCreateNew (@NonNull String node, boolean isShortName) {
String trimmedNode = of(node)
.map(String::trim)
.filter(it -> !it.isEmpty())
.map(it -> NODE_DESCRIPTOR_CACHE.computeIfAbsent(it, COMPUTE_NODE_DESCRIPTOR))
.orElseThrow(() -> new IllegalArgumentException("Invalid node descriptor string"));
.orElseThrow(() -> new IllegalArgumentException("Invalid node descriptor string '" + node + "'"));

val cached = NODE_DESCRIPTOR_CACHE.get(trimmedNode);
return cached == null
? parse(trimmedNode, isShortName)
: cached;
}

@SuppressWarnings("PMD.PreserveStackTrace")
private static InetAddress getByName (@NonNull String hostname) {
try {
return InetAddress.getByName(hostname);
} catch (UnknownHostException ex) {
try {
return InetAddress.getByName(hostname + ".local");
} catch (UnknownHostException ex1) {
throw new IllegalArgumentException("Wrapped", ex);
@SneakyThrows
private static NodeDescriptor parse (@NonNull String node, boolean isShortName) {
val tokens = node.split("@", 2);

val nodeName = tokens[0];
if (nodeName.isEmpty()) {
throw new IllegalArgumentException();
}

String hostName;
InetAddress address;
if (tokens.length == 2) {
hostName = tokens[1];
if (isShortName && hostName.contains(".")) {
val message = String.format("Using host name '%s' in short node name is illegal, " +
"because it contains dots ('.') and suits only for long name nodes",
hostName);
throw new IllegalArgumentException(message);
}
address = InetAddress.getByName(hostName);
} else if (isShortName) {
val dotIndex = HOST_NAME.indexOf('.');
hostName = dotIndex > 0
? HOST_NAME.substring(dotIndex)
: HOST_NAME;

address = LOOPBACK;
} else {
hostName = HOST_NAME;
address = LOCALHOST;
}

val fullName = nodeName + '@' + hostName;
return new NodeDescriptor(nodeName, hostName, fullName, address, isShortName);
}

String shortName;
String nodeName;

String hostName;

String fullName;

InetAddress address;

boolean shortName;

/**
* Tells is this {@link NodeDescriptor} instance for short-named node or not.
*
* @return {@code true} if node name is short, {@code false} otherwise
*/
public boolean isShortName () {
return shortName;
}

/**
* Tells is this {@link NodeDescriptor} instance for long-named node or not.
*
* @return {@code true} if node name is long, {@code false} otherwise
*/
public boolean isLongName () {
return !shortName;
}
}
Loading