| /*- |
| * Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved. |
| * |
| * This file was distributed by Oracle as part of a version of Oracle Berkeley |
| * DB Java Edition made available at: |
| * |
| * http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html |
| * |
| * Please see the LICENSE file included in the top-level directory of the |
| * appropriate version of Oracle Berkeley DB Java Edition for a copy of the |
| * license and additional information. |
| */ |
| |
| package com.sleepycat.je.rep.util; |
| |
| import static com.sleepycat.je.rep.impl.RepParams.NODE_HOST_PORT; |
| |
| import java.io.File; |
| |
| import com.sleepycat.je.Durability; |
| import com.sleepycat.je.EnvironmentConfig; |
| import com.sleepycat.je.rep.RepInternal; |
| import com.sleepycat.je.rep.ReplicatedEnvironment; |
| import com.sleepycat.je.rep.ReplicationConfig; |
| |
| /** |
| * A utility to convert an existing, non replicated JE environment for |
| * replication. This is useful when the user wants to initially prototype and |
| * develop a standalone transactional application, and then add replication as |
| * a second stage. |
| * <p> |
| * JE HA environment log files contain types of log records and metadata used |
| * only by replication. Non replicated environments are lacking that |
| * information and must undergo a one time conversion process to add that |
| * metadata and enable replication. The conversion process is one way. Once an |
| * environment directory is converted, the rules that govern {@link |
| * ReplicatedEnvironment} apply; namely, the directory cannot be opened by a |
| * read/write standalone {@link com.sleepycat.je.Environment}. Only a minimum |
| * amount of replication metadata is added, and the conversion process is not |
| * dependent on the size of the existing directory. |
| * <p> |
| * The conversion process takes these steps: |
| * <ol> |
| * <li> Use {@code DbEnableReplication} to convert an existing environment |
| * directory. {@code DbEnableReplication} can be used as a command line |
| * utility, and must be executed locally on the host which houses the |
| * environment directory. Alternatively, {@code DbEnableReplication} may be |
| * used programmatically through the provided APIs. |
| * </li> |
| * <li> Once converted, the environment directory may be treated as an existing |
| * master node, and can be opened with a {@code ReplicatedEnvironment}. No |
| * helper host configuration is needed. |
| * <li> Additional nodes may be created and can join the group as newly created |
| * replicas, as described in {@code ReplicatedEnvironment}. Since these new |
| * nodes are empty, they should be configured to use the converted master as |
| * their helper node, and will go through the <a |
| * href="{@docRoot}/../ReplicationGuide/lifecycle.html#lifecycle-nodestartup"> |
| * replication node lifecycle</a> to populate their environment |
| * directories. In this case, there will be data in the converted master that |
| * can only be transferred to the replica through a file copy executed with the |
| * help of a {@link com.sleepycat.je.rep.NetworkRestore} |
| * </li> |
| * </ol> |
| * <p> |
| * For example: |
| * <pre class="code"> |
| * // Create the first node using an existing environment |
| * DbEnableReplication converter = |
| * new DbEnableReplication(envDirMars, // env home dir |
| * "UniversalRepGroup", // group name |
| * "nodeMars", // node name |
| * "mars:5001"); // node host,port |
| * converter.convert(); |
| * |
| * ReplicatedEnvironment nodeMars = new ReplicatedEnvironment(envDirMars, ...); |
| * |
| * // Bring up additional nodes, which will be initialized from |
| * // nodeMars. |
| * ReplicationConfig repConfig = null; |
| * try { |
| * repConfig = new ReplicationConfig("UniversalRepGroup", // groupName |
| * "nodeVenus", // nodeName |
| * "venus:5008"); // nodeHostPort |
| * repConfig.setHelperHosts("mars:5001"); |
| * |
| * nodeVenus = new ReplicatedEnvironment(envDirB, repConfig, envConfig); |
| * } catch (InsufficientLogException insufficientLogEx) { |
| * |
| * // log files will be copied from another node in the group |
| * NetworkRestore restore = new NetworkRestore(); |
| * restore.execute(insufficientLogEx, new NetworkRestoreConfig()); |
| * |
| * // try opening the node now |
| * nodeVenus = new ReplicatedEnvironment(envDirVenus, |
| * repConfig, |
| * envConfig); |
| * } |
| * ... |
| * </pre> |
| */ |
| public class DbEnableReplication { |
| |
| /* |
| * The code snippet in the header comment is tested in |
| * com.sleepycat.je.rep.util.EnvConvertTest. |
| * testJavadocForDbEnableReplication(). Please update this test case |
| * when the example is changed. |
| */ |
| private File envHome; |
| private String groupName; |
| private String nodeName; |
| private String nodeHostPort; |
| |
| private static final String usageString = |
| "usage: java -cp je.jar " + |
| "com.sleepycat.je.rep.util.DbEnableReplication\n" + |
| " -h <dir> # environment home directory\n" + |
| " -groupName <group name> # replication group name\n" + |
| " -nodeName <node name> # replicated node name\n" + |
| " -nodeHostPort <host name:port number> # host name or IP address\n" + |
| " and port number to use\n" + |
| " for this node\n"; |
| |
| /** |
| * Usage: |
| * <pre> |
| * java -cp je.jar com.sleepycat.je.rep.util.DbEnableReplication |
| * -h <dir> # environment home directory |
| * -groupName <group name> # replication group name |
| * -nodeName <node name> # replicated node name |
| * -nodeHostPort <host name:port number> # host name or IP address |
| * and port number to use |
| * for this node |
| * </pre> |
| */ |
| public static void main(String[] args) { |
| DbEnableReplication converter = new DbEnableReplication(); |
| converter.parseArgs(args); |
| |
| try { |
| converter.convert(); |
| } catch (Throwable t) { |
| t.printStackTrace(); |
| } |
| } |
| |
| private void printUsage(String msg) { |
| System.err.println(msg); |
| System.err.println(usageString); |
| System.exit(-1); |
| } |
| |
| private void parseArgs(String[] args) { |
| int argc = 0; |
| int nArgs = args.length; |
| |
| while (argc < nArgs) { |
| String thisArg = args[argc++].trim(); |
| if (thisArg.equals("-h")) { |
| if (argc < nArgs) { |
| envHome = new File(args[argc++]); |
| } else { |
| printUsage("-h requires an argument"); |
| } |
| } else if (thisArg.equals("-groupName")) { |
| if (argc < nArgs) { |
| groupName = args[argc++]; |
| } else { |
| printUsage("-groupName requires an argument"); |
| } |
| } else if (thisArg.equals("-nodeName")) { |
| if (argc < nArgs) { |
| nodeName = args[argc++]; |
| } else { |
| printUsage("-nodeName requires an argument"); |
| } |
| } else if (thisArg.equals("-nodeHostPort")) { |
| if (argc < nArgs) { |
| nodeHostPort = args[argc++]; |
| try { |
| NODE_HOST_PORT.validateValue(nodeHostPort); |
| } catch (IllegalArgumentException e) { |
| e.printStackTrace(); |
| printUsage("-nodeHostPort is illegal!"); |
| } |
| } else { |
| printUsage("-nodeHostPort requires an argument"); |
| } |
| } |
| } |
| |
| if (envHome == null) { |
| printUsage("-h is a required argument."); |
| } |
| |
| if (groupName == null) { |
| printUsage("-groupName is a required argument."); |
| } |
| |
| if (nodeName == null) { |
| printUsage("-nodeName is a required argument."); |
| } |
| |
| if (nodeHostPort == null) { |
| printUsage("-nodeHostPort is a required argument."); |
| } |
| } |
| |
| private DbEnableReplication() { |
| } |
| |
| /** |
| * Create a DbEnableReplication object for this node. |
| * |
| * @param envHome The node's environment directory |
| * @param groupName The name of the new replication group |
| * @param nodeName The node's name |
| * @param nodeHostPort The host and port for this node |
| */ |
| public DbEnableReplication(File envHome, |
| String groupName, |
| String nodeName, |
| String nodeHostPort) { |
| this.envHome = envHome; |
| this.groupName = groupName; |
| this.nodeName = nodeName; |
| this.nodeHostPort = nodeHostPort; |
| } |
| |
| /** |
| * Modify the log files in the environment directory to add a modicum of |
| * replication required metadata. |
| */ |
| public void convert() { |
| |
| Durability durability = |
| new Durability(Durability.SyncPolicy.WRITE_NO_SYNC, |
| Durability.SyncPolicy.WRITE_NO_SYNC, |
| Durability.ReplicaAckPolicy.SIMPLE_MAJORITY); |
| |
| EnvironmentConfig envConfig = new EnvironmentConfig(); |
| envConfig.setAllowCreate(true); |
| envConfig.setTransactional(true); |
| envConfig.setDurability(durability); |
| |
| ReplicationConfig repConfig = |
| new ReplicationConfig(groupName, nodeName, nodeHostPort); |
| repConfig.setHelperHosts(repConfig.getNodeHostPort()); |
| RepInternal.setAllowConvert(repConfig, true); |
| |
| ReplicatedEnvironment repEnv = |
| new ReplicatedEnvironment(envHome, repConfig, envConfig); |
| |
| repEnv.close(); |
| } |
| } |