org.eclipse.gyrex.cloud.internal.NodeInfo.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.gyrex.cloud.internal.NodeInfo.java

Source

/*******************************************************************************
 * Copyright (c) 2010, 2012 AGETO Service GmbH and others.
 * All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html.
 *
 * Contributors:
 *     Gunnar Wagenknecht - initial API and implementation
 *******************************************************************************/
package org.eclipse.gyrex.cloud.internal;

import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;

import org.eclipse.gyrex.cloud.internal.zk.ZooKeeperNodeInfo;
import org.eclipse.gyrex.server.Platform;

import org.eclipse.osgi.util.NLS;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.CharEncoding;
import org.apache.commons.lang.CharSet;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.text.StrBuilder;

/**
 * Information about this node.
 * <p>
 * In Gyrex each node is identified by a unique node id. This id is read from a
 * file in the instance location (see {@link #getNodeIdFile()}). If it doesn't
 * exist, a new node id is generated and written to that file.
 * </p>
 */
public class NodeInfo {

    /** file name of node id file */
    private static final String NODE_ID_FILENAME = "nodeId";

    /** char set of allowed chars */
    private static final CharSet ALLOWED_NODE_ID_CHARS = CharSet
            .getInstance(new String[] { "a-z", "0-9", ".", "-", "_" });

    /** verified node id */
    private static final AtomicReference<String> verifiedNodeId = new AtomicReference<String>();

    /**
     * Generates a node id based on {@link UUID#randomUUID()}.
     * 
     * @return the generated node id
     */
    private static String generateNodeId() {
        try {
            // we use the host name and hash of the instance location
            return InetAddress.getLocalHost().getHostName().toLowerCase() + "_"
                    + Integer.toHexString(Platform.getInstanceLocation().toString().hashCode());
        } catch (final Exception e) {
            // fallback to UUID id
            return UUID.randomUUID().toString();
        }
    }

    private static String getDefaultLocationInfo(final String nodeId) {
        try {
            return InetAddress.getLocalHost().getCanonicalHostName();
        } catch (final UnknownHostException e) {
            return nodeId + ": " + e.getMessage();
        }
    }

    /**
     * Returns the instance node id file.
     * 
     * @return the instance node id file
     */
    private static File getNodeIdFile() {
        return Platform.getStateLocation(CloudActivator.getInstance().getBundle()).append(NODE_ID_FILENAME)
                .toFile();
    }

    /**
     * Read node id from instance location or generate a new one (and save it
     * persistently).
     */
    private static String initializeNodeId() {
        try {
            // re-use verified id
            final String nodeId = verifiedNodeId.get();
            if (nodeId != null) {
                return nodeId;
            }

            // get file
            final File nodeIdFile = getNodeIdFile();
            if (nodeIdFile.exists() && nodeIdFile.isFile()) {
                final String rawNodeId = StringUtils
                        .trimToNull(FileUtils.readFileToString(nodeIdFile, CharEncoding.US_ASCII));

                // verify id
                if (isValidNodeId(rawNodeId)) {
                    verifiedNodeId.compareAndSet(null, rawNodeId);
                    return verifiedNodeId.get();
                }
            }

            // generate new node id
            final String newNodeId = generateNodeId();

            // write to file
            if (verifiedNodeId.compareAndSet(null, newNodeId)) {
                FileUtils.writeStringToFile(nodeIdFile, newNodeId, CharEncoding.US_ASCII);
            }

            // done
            return verifiedNodeId.get();
        } catch (final IOException e) {
            throw new IllegalStateException(NLS.bind("Unable to initialize node id. {0}", e.getMessage()), e);
        }
    }

    /**
     * Validates a node id
     * 
     * @param rawNodeId
     * @return <code>true</code> if valid, <code>false</code> otherwise
     */
    private static boolean isValidNodeId(final String rawNodeId) {
        // not null or blank
        if (StringUtils.isBlank(rawNodeId)) {
            return false;
        }

        // scan for invalid chars
        for (final char c : rawNodeId.toCharArray()) {
            if (!ALLOWED_NODE_ID_CHARS.contains(c)) {
                return false;
            }
        }

        // ok
        return true;
    }

    private final String nodeId;
    private final boolean approved;
    private final String location;
    private final String name;
    private final Set<String> tags;

    private final int version;

    /**
     * Creates a new instance.
     */
    public NodeInfo() {
        nodeId = initializeNodeId();
        location = getDefaultLocationInfo(nodeId);
        name = null;
        tags = Collections.emptySet();
        approved = false;
        version = -1;
    }

    /**
     * Creates a new instance using the specified instance data.
     * 
     * @param data
     *            the instance data
     * @param version
     *            the instance data version
     * @throws Exception
     *             if an error occurred parsing the instance data
     */
    NodeInfo(final ZooKeeperNodeInfo info) throws Exception {
        nodeId = initializeNodeId();
        if (!nodeId.equals(info.getId())) {
            throw new IllegalArgumentException("node id mismatch");
        }
        if (!info.isApproved()) {
            throw new IllegalArgumentException("node must be approved first");
        }
        approved = info.isApproved();
        version = info.getVersion();

        // name
        name = info.getName();

        // location
        location = null != info.getLocation() ? info.getLocation() : getDefaultLocationInfo(nodeId);

        // roles
        final Set<String> tags = info.getTags();
        if (tags != null) {
            this.tags = Collections.unmodifiableSet(tags);
        } else {
            this.tags = Collections.emptySet();
        }
    };

    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final NodeInfo other = (NodeInfo) obj;
        if (approved != other.approved) {
            return false;
        }
        if (location == null) {
            if (other.location != null) {
                return false;
            }
        } else if (!location.equals(other.location)) {
            return false;
        }
        if (name == null) {
            if (other.name != null) {
                return false;
            }
        } else if (!name.equals(other.name)) {
            return false;
        }
        if (nodeId == null) {
            if (other.nodeId != null) {
                return false;
            }
        } else if (!nodeId.equals(other.nodeId)) {
            return false;
        }
        if (tags == null) {
            if (other.tags != null) {
                return false;
            }
        } else if (!tags.equals(other.tags)) {
            return false;
        }
        if (version != other.version) {
            return false;
        }
        return true;
    }

    /**
     * Returns a human-reable string of the node location.
     * 
     * @return the node location
     */
    public String getLocation() {
        return location;
    }

    /**
     * Returns the name.
     * 
     * @return the name (defaults to {@link #getNodeId() node id} if the name is
     *         not set)
     */
    public String getName() {
        return name != null ? name : nodeId;
    }

    /**
     * Returns the unique identifier of this node.
     * 
     * @return the unique node identifier
     */
    public String getNodeId() {
        return nodeId;
    }

    /**
     * Returns the node tags.
     * 
     * @return an unmodifiable collection of assigned tags
     */
    public Set<String> getTags() {
        return tags;
    }

    /**
     * Returns the version.
     * 
     * @return the version
     */
    public int getVersion() {
        return version;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = (prime * result) + (approved ? 1231 : 1237);
        result = (prime * result) + ((location == null) ? 0 : location.hashCode());
        result = (prime * result) + ((name == null) ? 0 : name.hashCode());
        result = (prime * result) + ((nodeId == null) ? 0 : nodeId.hashCode());
        result = (prime * result) + ((tags == null) ? 0 : tags.hashCode());
        result = (prime * result) + version;
        return result;
    }

    /**
     * Indicates if the node is an approved member.
     * 
     * @return <code>true</code> if the node membership is approved,
     *         <code>false</code> otherwise
     */
    public boolean isApproved() {
        return approved;
    }

    @Override
    public String toString() {
        final StrBuilder info = new StrBuilder();
        info.append(getName()).append(" (");
        info.append(location);
        info.append(", ");
        info.append(approved ? "APPROVED" : "PENDING");
        info.append(", ");
        info.append(nodeId);
        info.append(" v");
        info.append(version);
        info.append(")");
        return info.toString();
    }
}