Package de.zib.scalaris

Source Code of de.zib.scalaris.ConnectionFactory

/**
*  Copyright 2007-2011 Zuse Institute Berlin
*
*   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 de.zib.scalaris;

import java.io.PrintStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicLong;

import com.ericsson.otp.erlang.OtpSelf;

import de.zib.tools.PropertyLoader;

/**
* Provides means to create connections to scalaris nodes.
*
* This class uses a singleton-alike pattern providing a global (static)
* instance through its {@link #getInstance()} method but also allowing for
* object construction which might be useful when using multiple threads each
* creating its own connections.
*
* The location of the default configuration file used by
* {@link #ConnectionFactory()} can be overridden by specifying the <tt>
* scalaris.java.config</tt> system property - otherwise the class tries to load
* <tt>scalaris.properties</tt>.
*
* A specific property can also be overridden specifying a (non-empty) system
* property with its name.
*
* A user-defined {@link Properties} object can also be used by creating objects
* with {@link #ConnectionFactory(Properties)} or setting the new values with
* {@link #setProperties(Properties)} but must provide the following values
* (default values as shown)
* <ul>
* <li><tt>scalaris.node = "node1@localhost"</tt></li>
* <li><tt>scalaris.cookie = "chocolate chip cookie"</tt></li>
* <li><tt>scalaris.client.name = "java_client"</tt></li>
* <li><tt>scalaris.client.appendUUID = "true"</tt></li>
* </ul>
*
* Note: {@code scalaris.node} can be a whitespace, ',' or ';' separated list of
* available nodes. See {@link DefaultConnectionPolicy} about how this list is
* used when connections are setup or when existing connections fail.
* Since Java and Erlang both need to known the same node name in order to
* connect, any "@localhost" in such a name is translated using
* {@link #fixLocalhostName(String)} to a real node name. The best method of
* changing this name is to use the name Erlang uses - it can be provided by
* setting the <tt>scalaris.erlang.nodename</tt> system property. If this is
* not set or empty, we will try to look-up the name ourselves (see
* {@link #getLocalhostName()}.
*
* The {@link #connectionPolicy} set will be passed on to created connections.
* Changing it via {@link #setConnectionPolicy(ConnectionPolicy)} will not
* change previously created connections - they keep the old policy. By
* default, {@link DefaultConnectionPolicy} is used.
*
* @author Nico Kruber, kruber@zib.de
* @version 3.10
* @since 2.0
*/
public class ConnectionFactory {
    /**
     * Default name of the configuration file.
     */
    private static final String defaultConfigFile = "scalaris.properties";

    /**
     * The name of the node to connect to.
     */
    private final List<PeerNode> nodes = Collections.synchronizedList(new ArrayList<PeerNode>());
    /**
     * The cookie name to use for connections.
     */
    private String cookie;
    /**
     * The name of the (Java) client to use when establishing a connection with
     * erlang.
     */
    private String clientName;
    /**
     * Specifies whether to append a pseudo UUID to client names or not.
     */
    private boolean clientNameAppendUUID;
    /**
     * Pseudo UUID - the number of this counter is added to client names when
     * creating a connection if clientNameAppendUUID is set.
     *
     * <p>
     * TODO: In some cases, an atomic value is not necessary, e.g. when the
     * factory is used by only one thread, then a normal long value will
     * suffice. Possible optimisation: use {@link AtomicLong} for singleton
     * instance and {@link Long} for the other instances!
     * </p>
     */
    private final AtomicLong clientNameUUID = new AtomicLong(0);

    /**
     * Stores which config file was actually read into the object's properties.
     */
    private String configFileUsed;

    /**
     * Static instance of a connection factory.
     */
    private static ConnectionFactory instance = new ConnectionFactory();

    /**
     * Contains the default node selection policy a copy of which will be set
     * for each created connection.
     *
     * @since 2.3
     */
    private ConnectionPolicy connectionPolicy = new DefaultConnectionPolicy(nodes);

    /**
     * Returns the static instance of a connection factory.
     *
     * @return a connection factory
     */
    public static ConnectionFactory getInstance() {
        return instance;
    }

    /**
     * Constructor, sets the parameters to use for connections according to
     * values given in a <tt>scalaris.properties</tt> file and falls back to
     * default values if values don't exist.
     *
     * By default the config file is assumed to be in the same directory as
     * the classes. Specify the <tt>scalaris.java.config</tt> system property
     * to set a different location.
     *
     * Default values are:
     * <ul>
     * <li><tt>scalaris.node = "node1@localhost"</tt></li>
     * <li><tt>scalaris.cookie = "chocolate chip cookie"</tt></li>
     * <li><tt>scalaris.client.name = "java_client"</tt></li>
     * <li><tt>scalaris.client.appendUUID = "true"</tt></li>
     * </ul>
     *
     * These properties can be overridden by specifying (non-empty) system
     * properties with their names.
     */
    public ConnectionFactory() {
        final Properties properties = new Properties();
        String configFile = System.getProperty("scalaris.java.config");
        if ((configFile == null) || (configFile.length() == 0)) {
            configFile = defaultConfigFile;
        }
//        System.out.println("loading config file: " + configFile);
        PropertyLoader.loadProperties(properties, configFile, true, false,
                new String[] {"scalaris.node", "scalaris.cookie", "scalaris.client.name", "scalaris.client.appendUUID"});
        setProperties(properties);
    }

    /**
     * Constructor, sets the parameters to use for connections according to
     * values given in the given {@link Properties} object and falls back to
     * default values if values don't exist.
     *
     * The {@link Properties} object should provide the following values
     * (default values as shown):
     * <ul>
     * <li><tt>scalaris.node = "node1@localhost"</tt></li>
     * <li><tt>scalaris.cookie = "chocolate chip cookie"</tt></li>
     * <li><tt>scalaris.client.name = "java_client"</tt></li>
     * <li><tt>scalaris.client.appendUUID = "true"</tt></li>
     * </ul>
     *
     * @param properties
     */
    public ConnectionFactory(final Properties properties) {
        setProperties(properties);
    }

    /**
     * Sets the object's members used for creating connections to erlang to
     * values provided by the given {@link Properties} object.
     *
     * The {@link Properties} object should provide the following values
     * (default values as shown):
     * <ul>
     * <li><tt>scalaris.node = "node1@localhost"</tt></li>
     * <li><tt>scalaris.cookie = "chocolate chip cookie"</tt></li>
     * <li><tt>scalaris.client.name = "java_client"</tt></li>
     * <li><tt>scalaris.client.appendUUID = "true"</tt></li>
     * </ul>
     *
     * NOTE: Existing connections are not changed!
     *
     * @param properties
     *            the object to get the connection parameters from
     */
    public void setProperties(final Properties properties) {
        final String[] nodesTemp = properties.getProperty("scalaris.node", "node1@localhost").split("[\\s,;]");
        nodes.clear();

        for (int i = 0; i < nodesTemp.length; ++i) {
            addNode(fixLocalhostName(nodesTemp[i]));
        }
        cookie = properties.getProperty("scalaris.cookie", "chocolate chip cookie");
        clientName = properties.getProperty("scalaris.client.name", "java_client");
        if (properties.getProperty("scalaris.client.appendUUID", "true").equals("true")) {
            clientNameAppendUUID = true;
        } else {
            clientNameAppendUUID = false;
        }
        configFileUsed = properties.getProperty("PropertyLoader.loadedfile", "");

        //System.out.println("node: " + node);
    }

    /**
     * Creates a connection to a scalaris erlang node specified by the given
     * parameters. Uses the given client name.
     *
     * If <tt>clientNameAppendUUID</tt> is specified a pseudo UUID is appended
     * to the given name. BEWARE that scalaris nodes accept only one connection
     * per client name!
     *
     * @param clientName
     *            the name that identifies the java client
     * @param clientNameAppendUUID
     *            override the object's setting for
     *            {@link #clientNameAppendUUID}
     * @param connectionPolicy
     *            override the connection policy that will be used for the new
     *            connection
     *
     * @return the created connection
     *
     * @throws ConnectionException
     *             if the connection fails
     *
     * @since 2.3
     */
    public Connection createConnection(String clientName,
            final boolean clientNameAppendUUID, final ConnectionPolicy connectionPolicy)
            throws ConnectionException {
        if (clientNameAppendUUID) {
            clientName = clientName + "_" + clientNameUUID.getAndIncrement();
        }
        try {
            final OtpSelf self = new OtpSelf(clientName, cookie);
            return new Connection(self, connectionPolicy);
        } catch (final Exception e) {
//                 e.printStackTrace();
            throw new ConnectionException(e);
        }
    }

    /**
     * Creates a connection to a scalaris erlang node specified by the given
     * parameters. Uses the given client name.
     *
     * If <tt>clientNameAppendUUID</tt> is specified a pseudo UUID is appended to
     * the given name. BEWARE that scalaris nodes accept only one connection per
     * client name!
     *
     * @param clientName
     *            the name that identifies the java client
     * @param clientNameAppendUUID
     *            override the object's setting for
     *            {@link #clientNameAppendUUID}
     *
     * @return the created connection
     *
     * @throws ConnectionException
     *             if the connection fails
     */
    public Connection createConnection(final String clientName, final boolean clientNameAppendUUID)
            throws ConnectionException {
        return createConnection(clientName, clientNameAppendUUID, connectionPolicy);
    }

    /**
     * Creates a connection to a scalaris erlang node specified by the given
     * parameters. Uses the given client name.
     *
     * If {@link #clientNameAppendUUID} has been set, a pseudo UUID is appended
     * to the given name. BEWARE that scalaris nodes accept only one connection
     * per client name!
     *
     * @param clientName
     *            the name that identifies the java client
     *
     * @return the created connection
     *
     * @throws ConnectionException
     *             if the connection fails
     */
    public Connection createConnection(final String clientName)
            throws ConnectionException {
        return createConnection(clientName, clientNameAppendUUID);
    }

    /**
     * Creates a connection to a scalaris erlang node specified by the given
     * parameters. Uses the given connection policy.
     *
     * If {@link #clientNameAppendUUID} has been set, a pseudo UUID is appended
     * to the given name. BEWARE that scalaris nodes accept only one connection
     * per client name!
     *
     * @param connectionPolicy
     *            override the connection policy that will be used for the new
     *            connection
     *
     * @return the created connection
     *
     * @throws ConnectionException
     *             if the connection fails
     *
     * @since 3.10
     */
    public Connection createConnection(final ConnectionPolicy connectionPolicy)
            throws ConnectionException {
        return createConnection(clientName, clientNameAppendUUID, connectionPolicy);
    }

    /**
     * Creates a connection to a scalaris erlang node specified by the given
     * parameters.
     *
     * @return the created connection
     *
     * @throws ConnectionException
     *             if the connection fails
     */
    public Connection createConnection() throws ConnectionException {
        return createConnection(clientName);
    }

    /**
     * Replaces <tt>localhost</tt> in the node's name to the machine's real host
     * name.
     *
     * Due to a "feature" of OtpErlang >= 1.4.1 erlang nodes are now only
     * reachable by their official host name - so <tt>...@localhost</tt> does
     * not work anymore if there is a real host name.
     *
     * @param node
     *            the name of the node to use
     *
     * @return the node's official host name as returned by
     *         {@link InetAddress#getHostName()}
     */
    public static String fixLocalhostName(final String node) {
        if (node.endsWith("@localhost")) {
            return node.replaceAll("@localhost$", "@" + getLocalhostName());
        }
        return node;
    }

    /**
     * Returns the name of the local host (or at least what Java thinks it is).
     * If the system property <tt>scalaris.erlang.nodename</tt> is set, this
     * will be returned, otherwise we look-up our hostname with
     * {@link InetAddress#getHostName()}.
     *
     * @return the local host' name or <tt>localhost</tt> if no IP address was
     *         found
     */
    public static final String getLocalhostName() {
        String hostname = "localhost";

        final String erlangNodeName = System.getProperty("scalaris.erlang.nodename");
        if ((erlangNodeName == null) || (erlangNodeName.length() == 0)) {
            try {
                // (more) reliable hostname look-up reported by
                // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4341822
                hostname = InetAddress.getLocalHost().getHostName();
                final String host = InetAddress.getByName(hostname).getHostAddress();
                hostname = InetAddress.getByName(host).getHostName();

            } catch (final UnknownHostException e) {
            }
        } else {
            hostname = erlangNodeName;
        }

        return hostname;
    }

    /**
     * Prints the object's properties to System.out.
     */
    public void printProperties() {
        printProperties(System.out);
    }

    /**
     * Prints the object's properties to the given output stream.
     *
     * @param out  the output stream to write to
     */
    public void printProperties(final PrintStream out) {
        out.println("ConnectionFactory properties:");
        out.println("  config file                = " + configFileUsed);
        out.println("  scalaris.node              = " + nodes.toString());
        out.println("  scalaris.cookie            = " + cookie);
        out.println("  scalaris.client.name       = " + clientName);
        out.println("  scalaris.client.appendUUID = " + clientNameAppendUUID);
    }

    /**
     * Gets a copy of the list of nodes available for connections.
     *
     * The {@link PeerNode} elements of the list will not be copied!
     *
     * @return a set of nodes
     *
     * @since 2.3
     */
    public List<PeerNode> getNodes() {
        return new ArrayList<PeerNode>(nodes);
    }

    /**
     * Returns the name of the first node available for connection.
     *
     * WARNING: If {@link #nodes} contains multiple nodes, only the first of
     * them will be returned. This is not necessarily the one the class will set
     * up connections with!
     *
     * @return the name of the first node or an empty string if the list is
     *         empty
     *
     * @deprecated This method returns the names of all nodes available for
     *             connection as a String (as previously when only one node was
     *             used), use the new {@link #getNodes()} method instead.
     */
    @Deprecated
    public String getNode() {
        if (nodes.size() > 0) {
            return nodes.get(0).getNode().node();
        } else {
            return "";
        }
    }

    /**
     * Sets the name of the node to connect to.
     *
     * The list of nodes available for connection will be cleared at first, the
     * node will then be added.
     *
     * @param node
     *            the node to set (will not be mangled by
     *            {@link #fixLocalhostName(String)}!)
     *
     * @see ConnectionFactory#addNode(String)
     */
    public void setNode(final String node) {
        this.nodes.clear();
        connectionPolicy.availableNodesReset();
        addNode(node);
    }

    /**
     * Adds a node to the set of nodes available for connections.
     *
     * @param node
     *            the node to add (will not be mangled by
     *            {@link #fixLocalhostName(String)}!)
     *
     * @see ConnectionFactory#setNode(String)
     *
     * @since 2.3
     */
    public void addNode(final String node) {
        final PeerNode p = new PeerNode(node);
        this.nodes.add(p);
        connectionPolicy.availableNodeAdded(p);
    }

    /**
     * Removes a node from the set of nodes available for connections.
     *
     * @param node
     *            the node to remove
     *
     * @see ConnectionFactory#getNodes()
     *
     * @since 2.3
     */
    public void removeNode(final PeerNode node) {
        this.nodes.remove(node);
        connectionPolicy.availableNodeRemoved(node);
    }

    /**
     * Returns the cookie name to use for connections.
     *
     * @return the cookie
     */
    public String getCookie() {
        return cookie;
    }

    /**
     * Sets the cookie name to use for connections.
     *
     * @param cookie
     *            the cookie to set
     */
    public void setCookie(final String cookie) {
        this.cookie = cookie;
    }

    /**
     * Returns the name of the (Java) client to use when establishing a
     * connection with erlang.
     *
     * @return the clientName
     */
    public String getClientName() {
        return clientName;
    }

    /**
     * Sets the name of the (Java) client to use when establishing a connection
     * with erlang.
     *
     * @param clientName
     *            the clientName to set
     */
    public void setClientName(final String clientName) {
        this.clientName = clientName;
    }

    /**
     * Returns whether an UUID is appended to client names or not.
     *
     * @return <tt>true</tt> if an UUID is appended, <tt>false</tt> otherwise
     */
    public boolean isClientNameAppendUUID() {
        return clientNameAppendUUID;
    }

    /**
     * Sets whether to append an UUID to client names or not.
     *
     * @param clientNameAppendUUID
     *            <tt>true</tt> if an UUID is appended, <tt>false</tt> otherwise
     */
    public void setClientNameAppendUUID(final boolean clientNameAppendUUID) {
        this.clientNameAppendUUID = clientNameAppendUUID;
    }

    /**
     * Sets the connection policy to use for new connections.
     *
     * Warning: this will effectively disconnect the previous connection policy
     * from any updates to the list of available nodes (they will still use the
     * same list but will not be able to react on changes). Prefer not to change
     * the default policy after connections have been created.
     *
     * @param connectionPolicy
     *            the connection policy to set
     *
     * @since 2.3
     */
    public void setConnectionPolicy(final ConnectionPolicy connectionPolicy) {
        this.connectionPolicy = connectionPolicy;
    }

    /**
     * Gets the current connection policy.
     *
     * @return the currently used connection policy
     *
     * @since 2.3
     */
    public ConnectionPolicy getConnectionPolicy() {
        return connectionPolicy;
    }
}
TOP

Related Classes of de.zib.scalaris.ConnectionFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.