/** * */ package de.zib.scalaris; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Properties; import java.util.concurrent.atomic.AtomicLong; import com.ericsson.otp.erlang.OtpConnection; import com.ericsson.otp.erlang.OtpPeer; import com.ericsson.otp.erlang.OtpSelf; import de.zib.scalaris.ConnectionException; 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 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 = "boot@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> * * @author Nico Kruber, kruber@zib.de * @version 2.1 * @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. */ public String node; /** * 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 AtomicLong clientNameUUID = new AtomicLong(0); /** * Static instance of a connection factory. */ private static ConnectionFactory instance = new ConnectionFactory(); /** * 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 = "boot@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> */ public ConnectionFactory() { Properties properties = new Properties(); String configFile = System.getProperty("scalaris.java.config"); if (configFile == null || configFile.isEmpty()) { configFile = defaultConfigFile; } // System.out.println("loading config file: " + configFile); PropertyLoader.loadProperties(properties, configFile); 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 = "boot@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(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 = "boot@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(Properties properties) { node = properties.getProperty("scalaris.node", "boot@localhost"); 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; } fixLocalhostName(); //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} * * @return the created connection * * @throws ConnectionException * if the connection fails */ public OtpConnection createConnection(String clientName, boolean clientNameAppendUUID) throws ConnectionException { try { if (clientNameAppendUUID) { clientName = clientName + "_" + clientNameUUID.getAndIncrement(); } OtpSelf self = new OtpSelf(clientName, cookie); OtpPeer other = new OtpPeer(node); return self.connect(other); } catch (Exception e) { // e.printStackTrace(); throw new ConnectionException(e.getMessage()); } } /** * Creates a connection to a scalaris erlang node specified by the given * parameters. Uses the given client name. * * If {@link #clientNameAppendUUID} 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 * * @return the created connection * * @throws ConnectionException * if the connection fails */ public OtpConnection createConnection(String clientName) throws ConnectionException { return createConnection(clientName, clientNameAppendUUID); } /** * Creates a connection to a scalaris erlang node specified by the given * parameters. * * @return the created connection * * @throws ConnectionException * if the connection fails */ public OtpConnection 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. */ private void fixLocalhostName() { if (node.endsWith("@localhost")) { String hostname = "localhost"; try { InetAddress addr = InetAddress.getLocalHost(); hostname = addr.getCanonicalHostName(); } catch (UnknownHostException e) { } node = node.replaceAll("@localhost$", "@" + hostname); } } /** * Returns the name of the node to connect to. * * @return the name of the node */ public String getNode() { return node; } /** * Sets the name of the node to connect to. * * @param node * the node to set */ public void setNode(String node) { this.node = 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(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(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(boolean clientNameAppendUUID) { this.clientNameAppendUUID = clientNameAppendUUID; } }