/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 cc.kune.kunecli.cmds;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import org.naturalcli.Command;
import org.naturalcli.ExecutionException;
import org.naturalcli.ICommandExecutor;
import org.naturalcli.InvalidSyntaxException;
import org.naturalcli.ParseResult;
import org.waveprotocol.box.server.CoreSettings;
import org.waveprotocol.box.server.persistence.PersistenceModule;
import org.waveprotocol.box.server.waveserver.DeltaStore;
import org.waveprotocol.wave.util.logging.Log;
import org.waveprotocol.wave.util.settings.Setting;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.name.Names;
/**
* A cmd line utility to perform data migration from a store type to another
* one. Initially developed to replicate deltas from a file store to a mongodb
* store.
*
*
* @author pablojan@gmail.com (Pablo Ojanguren)
*
*/
public class DeltaMigrationToMongoCommand extends Command {
public static class DeltaMigrationToMongoICommand implements ICommandExecutor {
@Override
public void execute(final ParseResult result) throws ExecutionException {
final String source = result.getParameterValue(0).toString();
final String target = result.getParameterValue(1).toString();
final Module sourceSettings = bindCmdLineSettings(source);
final Injector sourceSettingsInjector = Guice.createInjector(sourceSettings);
final Module sourcePersistenceModule = sourceSettingsInjector.getInstance(PersistenceModule.class);
final Injector sourceInjector = sourceSettingsInjector.createChildInjector(
sourcePersistenceModule);
final Module targetSettings = bindCmdLineSettings(target);
final Injector targetSettingsInjector = Guice.createInjector(targetSettings);
final Module targetPersistenceModule = targetSettingsInjector.getInstance(PersistenceModule.class);
final Injector targetInjector = targetSettingsInjector.createChildInjector(
targetPersistenceModule);
runDeltasMigration(sourceInjector, targetInjector);
}
}
private static final Log LOG = Log.get(DeltaMigrationToMongoCommand.class);
private static Module bindCmdLineSettings(final String cmdLineProperties) {
// Get settings from cmd line, e.g.
// Key = delta_store_type
// Value = mongodb
final Map<String, String> propertyMap = new HashMap<String, String>();
for (final String arg : cmdLineProperties.split(",")) {
final String[] argTokens = arg.split("=");
propertyMap.put(argTokens[0], argTokens[1]);
}
// Validate settings against CoreSettings
final Map<Setting, Field> coreSettings = getCoreSettings();
// Set a suitable map to match cmd line settings
final Map<String, Setting> propertyToSettingMap = new HashMap<String, Setting>();
for (final Setting s : coreSettings.keySet()) {
propertyToSettingMap.put(s.name(), s);
}
for (final String propertyKey : propertyMap.keySet()) {
if (!propertyToSettingMap.containsKey(propertyKey)) {
usageError("Wrong setting '" + propertyKey + "'");
}
}
return new AbstractModule() {
@Override
protected void configure() {
// We must iterate the settings when binding.
// Note: do not collapse these loops as that will damage
// early error detection. The runtime is still O(n) in setting count.
for (final Map.Entry<Setting, Field> entry : coreSettings.entrySet()) {
final Setting setting = entry.getKey();
final Class<?> type = entry.getValue().getType();
final String value = propertyMap.containsKey(setting.name()) ? propertyMap.get(setting.name())
: setting.defaultValue();
if (int.class.equals(type)) {
// Integer defaultValue = null;
// if (!setting.defaultValue().isEmpty()) {
// defaultValue = Integer.parseInt(setting.defaultValue());
// }
bindConstant().annotatedWith(Names.named(setting.name())).to(Integer.parseInt(value));
} else if (boolean.class.equals(type)) {
// Boolean defaultValue = null;
// if (!setting.defaultValue().isEmpty()) {
// defaultValue = Boolean.parseBoolean(setting.defaultValue());
// }
bindConstant().annotatedWith(Names.named(setting.name())).to(Boolean.parseBoolean(value));
} else if (String.class.equals(type)) {
bindConstant().annotatedWith(Names.named(setting.name())).to(value);
} else {
/** Not supported **/
/*
* String[] value = config.getStringArray(setting.name()); if
* (value.length == 0 && !setting.defaultValue().isEmpty()) { value
* = setting.defaultValue().split(","); } bind(new
* TypeLiteral<List<String>>()
* {}).annotatedWith(Names.named(setting.name()))
* .toInstance(ImmutableList.copyOf(value));
*/
}
}
}
};
}
private static Map<Setting, Field> getCoreSettings() {
// Get all method fields
final Field[] coreSettingFields = CoreSettings.class.getDeclaredFields();
// Filter only annotated fields
final Map<Setting, Field> settings = new HashMap<Setting, Field>();
for (final Field f : coreSettingFields) {
if (f.isAnnotationPresent(Setting.class)) {
final Setting setting = f.getAnnotation(Setting.class);
settings.put(setting, f);
}
}
return settings;
}
public static void main(final String... args) {
}
private static void runDeltasMigration(final Injector sourceInjector, final Injector targetInjector) {
// We can migrate data from-to any store type,
// but it is not allowed migrate from-to the same type
final String sourceDeltaStoreType = sourceInjector.getInstance(
Key.get(String.class, Names.named(CoreSettings.DELTA_STORE_TYPE)));
final String targetDeltaStoreType = targetInjector.getInstance(
Key.get(String.class, Names.named(CoreSettings.DELTA_STORE_TYPE)));
if (sourceDeltaStoreType.equalsIgnoreCase(targetDeltaStoreType)) {
usageError("Source and Target Delta store types must be different");
}
final CustomDeltaMigrator dm = new CustomDeltaMigrator(sourceInjector.getInstance(DeltaStore.class),
targetInjector.getInstance(DeltaStore.class));
dm.run();
}
public static void usageError() {
usageError("");
}
public static void usageError(final String msg) {
LOG.severe(msg);
LOG.severe("Use: DataMigrationTool <data type> <source options> <target options>\n");
LOG.severe("supported data types : deltas");
LOG.severe("source options example : delta_store_type=file,delta_store_directory=./_deltas");
LOG.severe(
"target options example : delta_store_type=mongodb,mongodb_host=127.0.0.1,mongodb_port=27017,mongodb_database=wiab");
System.exit(1);
}
@Inject
public DeltaMigrationToMongoCommand(final DeltaMigrationToMongoICommand cmd)
throws InvalidSyntaxException {
super("deltaMigrationToMongo <source:string> <target:string>",
"migrates deltas to mongodb, source options example: delta_store_type=file,delta_store_directory=./_deltas ; "
+ "target options example: delta_store_type=mongodb,mongodb_host=127.0.0.1,mongodb_port=27017,mongodb_database=wiab ; "
+ "touch /tmp/wave-mongo-migration-stop # to stop the migration at any time",
cmd);
}
}