/*******************************************************************************
* Copyright 2014,
* Luis Pina <luis@luispina.me>,
* Michael Hicks <mwh@cs.umd.edu>
*
* This file is part of Rubah.
*
* Rubah is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Rubah is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Rubah. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
package rubah.tools.updater;
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import rubah.runtime.state.strategy.ArrayStrategy;
import rubah.runtime.state.strategy.ConcurrentMapStrategy;
import rubah.runtime.state.strategy.EagerLazy;
import rubah.runtime.state.strategy.ForkJoinStrategy;
import rubah.runtime.state.strategy.ForwardFieldStrategy;
import rubah.runtime.state.strategy.FullyLazyMonolithic;
import rubah.runtime.state.strategy.IdentityMapStrategy;
import rubah.runtime.state.strategy.Lazy;
import rubah.runtime.state.strategy.MappingStrategy;
import rubah.runtime.state.strategy.MigrationStrategy;
import rubah.runtime.state.strategy.SingleThreaded;
import rubah.runtime.state.strategy.ThreadPoolStrategy;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterException;
import com.beust.jcommander.converters.FileConverter;
public class ParsingArguments extends Filter {
private static abstract class CommonParameters {
@Parameter(
description="Port where client is listening",
names={"-p","--port"},
required=true)
protected int port;
public enum MigrationStrategyArgument {
SINGLE_IDENTITY,
SINGLE_CONCURRENT,
THREAD_POOL,
FORK_JOIN,
FULL_LAZY,
LAZY,
EAGER_LAZY
}
public enum MappingStrategyArgument {
IDENTITY_MAP,
CONCURRENT_MAP,
FORWARD_FIELD,
ARRAY,
}
@Parameter(
description="Strategy to traverse/migrate the program state: SINGLE_IDENTITY, SINGLE_CONCURRENT, THREAD_POOL, FORK_JOIN, LAZY, FULL_LAZY, or EAGER_LAZY",
names={"-s","--strategy"},
required=false)
protected MigrationStrategyArgument migrationStrategy = MigrationStrategyArgument.SINGLE_IDENTITY;
@Parameter(
description="Strategy to map the old objects to new ones: IDENTITY_MAP, CONCURRENT_MAP, FORWARD_FIELD, or ARRAY",
names={"-m","--map-strategy"},
variableArity=true,
required=false)
protected List<MappingStrategyArgument> mappingStrategies =
new LinkedList<MappingStrategyArgument>(Arrays.asList(new MappingStrategyArgument[]{MappingStrategyArgument.CONCURRENT_MAP}));
@Parameter(
description="Number of threads to use with applicable strategy, defaults to Runtime.availableProcessors",
names={"-t","--threads"},
required=false)
protected int nThreads = Runtime.getRuntime().availableProcessors();
@Parameter(
converter=FileConverter.class,
description="Compiled update class file",
names={"-c","--update-class-file"},
required=false)
protected File updateClass;
@Parameter(
description="Name of update class present in new version jar",
names={"-n","--update-class-name"},
required=false)
protected String updateClassName;
@Parameter(
converter=FileConverter.class,
description="Update package with the needed bytecode",
names={"-u","--update-package"},
required=false)
protected File updatePackage;
public void parseLine(String[] line) {
JCommander argParser = new JCommander(this);
argParser.parse(line);
this.validateArgs();
}
protected void validateArgs() {
// Empty by default
}
public void printUsage() throws ParameterException {
new JCommander(this).usage();
}
public final void setState(UpdateState state) {
state.setPort(this.port);
state.setUpdatePackage(this.updatePackage);
MigrationStrategy migrationStrategy;
MappingStrategy mappingStrategy = null;
Collections.reverse(this.mappingStrategies);
for (MappingStrategyArgument arg : this.mappingStrategies) {
switch (arg) {
default:
case IDENTITY_MAP:
mappingStrategy = new IdentityMapStrategy();
break;
case CONCURRENT_MAP:
mappingStrategy = new ConcurrentMapStrategy();
break;
case FORWARD_FIELD:
mappingStrategy = new ForwardFieldStrategy(mappingStrategy);
break;
case ARRAY:
mappingStrategy = new ArrayStrategy(mappingStrategy);
break;
}
}
switch (this.migrationStrategy) {
default:
case SINGLE_IDENTITY:
migrationStrategy = new SingleThreaded(mappingStrategy);
break;
case SINGLE_CONCURRENT:
migrationStrategy = new SingleThreaded(mappingStrategy);
break;
case THREAD_POOL:
migrationStrategy = new ThreadPoolStrategy(mappingStrategy, this.nThreads);
break;
case FORK_JOIN:
migrationStrategy = new ForkJoinStrategy(mappingStrategy, this.nThreads);
break;
case LAZY:
migrationStrategy = new Lazy(mappingStrategy);
state.setLazy(true);
break;
case FULL_LAZY:
// migrationStrategy = new FullyLazyWithProxies(mappingStrategy);
migrationStrategy = new FullyLazyMonolithic();
state.setFullyLazy(true);
break;
case EAGER_LAZY:
migrationStrategy = new EagerLazy(mappingStrategy, this.nThreads);
state.setFullyLazy(true);
break;
}
state.setMigrationStrategy(migrationStrategy);
if (this.updateClass != null) {
state.setUpdateClassFile(this.updateClass);
}
if (this.updateClassName != null) {
state.setUpdateClassName(this.updateClassName);
}
this.doSetState(state);
}
protected abstract void doSetState(UpdateState state);
}
private static class V0V0ModeParameters extends CommonParameters {
@Parameter(
description="Do a v0v0 update and copy all program state",
names={"-v0v0"},
required=true)
protected boolean v0v0;
@Parameter(
description="Perform stop-and-go",
names={"-stopAndGo"},
required=false)
protected boolean stopAndGo= false;
@Override
public void doSetState(UpdateState state) {
state.setV0V0(true);
state.setStopAndGo(stopAndGo);
}
}
private static class V0V1ModeParameters extends CommonParameters {
@Parameter(
description="Do a real update",
names={"-v0v1"},
required=false)
protected boolean v0v1;
@Parameter(
converter=FileConverter.class,
description="Update descriptor",
names={"-d","--descriptor"},
required=true)
protected File descriptor;
@Parameter(
converter=FileConverter.class,
description="Jar file with new version",
names={"-j1"},
required=true)
protected File jarFile1;
@Override
protected void validateArgs() {
if (!(this.updateClass == null ^ this.updateClassName == null)) {
throw new ParameterException("Please provide an update class file or an update class name (any one and not both)");
}
}
@Override
public void doSetState(UpdateState state) {
state.setV0V0(false);
state.setDescriptor(this.descriptor);
state.setNewJar(this.jarFile1);
}
}
private static CommonParameters[] getParameters() {
return new CommonParameters[]{
new V0V0ModeParameters(),
new V0V1ModeParameters()
};
}
@Override
public void execute(UpdateState state) {
CommonParameters validParameters = null;
for (CommonParameters parameters : getParameters()) {
try {
parameters.parseLine(state.getCommandLine());
validParameters = parameters;
break;
} catch (ParameterException e) {
continue;
}
}
if (validParameters == null) {
System.out.println("Error parsing arguments: possible inputs:");
for (CommonParameters parameters : getParameters()) {
try {
parameters.parseLine(state.getCommandLine());
} catch (ParameterException e) {
System.out.println(e.getMessage());
}
parameters.printUsage();
}
System.exit(1);
}
validParameters.setState(state);
}
}