package de.hub.srcrepo.emffrag;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;
import org.eclipse.gmt.modisco.java.emffrag.metadata.JavaPackage;
import com.google.common.base.Preconditions;
import de.hub.emffrag.EmfFragActivator;
import de.hub.emffrag.Fragmentation;
import de.hub.emffrag.FragmentationSet;
import de.hub.emffrag.datastore.DataStoreImpl;
import de.hub.emffrag.datastore.IBaseDataStore;
import de.hub.emffrag.datastore.IDataStore;
import de.hub.emffrag.mongodb.EmfFragMongoDBActivator;
import de.hub.emffrag.mongodb.MongoDBDataStore;
import de.hub.srcrepo.GitSourceControlSystem;
import de.hub.srcrepo.IRepositoryModelVisitor;
import de.hub.srcrepo.ISourceControlSystem;
import de.hub.srcrepo.ISourceControlSystem.SourceControlException;
import de.hub.srcrepo.RepositoryModelRevisionCheckoutVisitor;
import de.hub.srcrepo.RepositoryModelTraversal;
import de.hub.srcrepo.RepositoryModelUtil;
import de.hub.srcrepo.SrcRepoActivator;
import de.hub.srcrepo.repositorymodel.ImportMetaData;
import de.hub.srcrepo.repositorymodel.RepositoryMetaData;
import de.hub.srcrepo.repositorymodel.RepositoryModel;
import de.hub.srcrepo.repositorymodel.RepositoryModelDirectory;
import de.hub.srcrepo.repositorymodel.emffrag.metadata.RepositoryModelPackage;
import de.hub.srcrepo.repositorymodel.util.RepositoryModelUtils;
public class EmfFragSrcRepoDirectoryImport implements IApplication {
public interface RepositoryModelVisitorFactory {
public IRepositoryModelVisitor createRepositoryModelVisitor();
}
public static class Configuration {
private final ISourceControlSystem scs;
private String repositoryName = null;
private URI directoryModelURI = URI.createURI("mongodb://localhost/EclipseAtGitHub");
private File workingcopies = new File("workingcopies");
private File locks = new File("locks");
private int fragmentCacheSize = 100;
private boolean skipSourceCodeImport = false;
private boolean checkOutWithoutImport = false;
private boolean useCGit = false;
private RepositoryModelVisitorFactory visitorFactory = null;
public Configuration(ISourceControlSystem scs) {
super();
this.scs = scs;
}
public Configuration fragmentCacheSize(int fragmentCacheSize) {
this.fragmentCacheSize = fragmentCacheSize;
return this;
}
public Configuration skipSourceCodeImport() {
this.skipSourceCodeImport = true;
return this;
}
public Configuration checkOutWithoutImport() {
this.checkOutWithoutImport = true;
return this;
}
public Configuration useCGit() {
this.useCGit = true;
return this;
}
public Configuration repositoryName(String value) {
this.repositoryName = value;
return this;
}
public Configuration directoryModelURI(URI value) {
this.directoryModelURI = value;
return this;
}
public Configuration locks(File value) {
this.locks = value;
return this;
}
public Configuration workingcopies(File value) {
this.workingcopies = value;
return this;
}
public Configuration withRepositoryModelVisitorFactory(RepositoryModelVisitorFactory visitorFactory) {
this.visitorFactory = visitorFactory;
return this;
}
}
public static class GitConfiguration extends Configuration {
public GitConfiguration() {
super(new GitSourceControlSystem());
}
}
private Options createOptions() {
Options options = new Options();
options.addOption(Option.builder().
longOpt("workingcopies").
desc("Directory where I clone the repository to. Default is ./workingcopies").
hasArg().argName("directory").build());
options.addOption(Option.builder().
longOpt("locks").
desc("Directory where I put the lock. Default is ./locks").
hasArg().argName("directory").build());
options.addOption(Option.builder().
longOpt("directory-uri").
desc("Fragmentationi URI where the directory of repositories lies. Default is mongodb://localhost/git.eclipse.org").
hasArg().argName("uri").build());
options.addOption(Option.builder().
longOpt("repository").
desc("Imports the specifically mentioned repository.").
hasArg().argName("name").build());
options.addOption(Option.builder().
longOpt("fragments-cache").
desc("Number of cached fragments. Default is 100.").
hasArg().argName("size").build());
options.addOption(Option.builder().
longOpt("use-c-git").
desc("Use regular git shell commands and not jGit.").build());
return options;
}
private boolean checkArgs(CommandLine cl) {
return
cl.getArgList().size() == 0 &&
(!cl.hasOption("log") ||
(Integer.parseInt(cl.getOptionValue("log")) >= 0 &&
Integer.parseInt(cl.getOptionValue("log")) <=4)) &&
(!cl.hasOption("fragments-cache") || Integer.parseInt(cl.getOptionValue("fragments-cache")) > 0);
}
private void printUsage() {
new HelpFormatter().printHelp("eclipse ... [options]", createOptions());
}
public static RepositoryModelPackage createRepositoryModelPackage() {
return RepositoryModelPackage.eINSTANCE;
}
@Override
public Object start(IApplicationContext context) throws Exception {
// ensure loading of plug-ins
EmfFragActivator.class.getName();
SrcRepoActivator.class.getName();
EmfFragMongoDBActivator.class.getName();
// checking command line options
final Map<?,?> args = context.getArguments();
final String[] appArgs = (String[]) args.get("application.args");
CommandLineParser cliParser = new DefaultParser();
Options options = createOptions();
CommandLine commandLine = null;
try {
commandLine = cliParser.parse(options, appArgs);
} catch (Exception e) {
printUsage();
return IApplication.EXIT_OK;
}
if (!checkArgs(commandLine)) {
printUsage();
return IApplication.EXIT_OK;
}
Configuration config = new GitConfiguration();
if (commandLine.hasOption("directory-uri")) {
config.directoryModelURI(URI.createURI(commandLine.getOptionValue("directory-uri")));
}
if (commandLine.hasOption("locks")) {
config.locks(new File(commandLine.getOptionValue("locks")));
}
if (commandLine.hasOption("workingcopies")) {
config.workingcopies(new File(commandLine.getOptionValue("workingcopies")));
}
if (commandLine.hasOption("repository")) {
config.repositoryName(commandLine.getOptionValue("repository"));
}
if (commandLine.hasOption("fragments-cache")) {
config.fragmentCacheSize(Integer.parseInt(commandLine.getOptionValue("fragments-cache")));
}
if (commandLine.hasOption("checkout-without-import")) {
config.checkOutWithoutImport();
}
if (commandLine.hasOption("use-c-git")) {
config.useCGit();
}
importRepository(config);
return IApplication.EXIT_OK;
}
public static FragmentationSet openFragmentationSet(Configuration config) {
List<EPackage> packages = new ArrayList<EPackage>();
packages.add(JavaPackage.eINSTANCE);
packages.add(RepositoryModelPackage.eINSTANCE);
return new FragmentationSet(packages, new IDataStore.IDataStoreFactory() {
@Override
public IDataStore createDataStore(URI uri) {
IBaseDataStore baseDataStore = null;
if ("mongodb".equals(uri.scheme())) {
MongoDBDataStore mongoDbBaseDataStore = new MongoDBDataStore(uri.authority(), uri.port(), uri.path().substring(1), false);
baseDataStore = mongoDbBaseDataStore;
} else {
throw new RuntimeException("Unknown scheme " + uri.scheme());
}
IDataStore dataStore = new DataStoreImpl(baseDataStore, uri);
return dataStore;
}
}, config.fragmentCacheSize);
}
public static void importRepository(Configuration config) {
// create necessary models
JavaPackage javaModelPackage = JavaPackage.eINSTANCE;
SrcRepoActivator.INSTANCE.useCGit = config.useCGit;
RepositoryModel repositoryModel = null;
// create fragmentation
FragmentationSet fs = openFragmentationSet(config);
Fragmentation fragmentation = fs.getFragmentation(config.directoryModelURI);
Preconditions.checkArgument(fragmentation.getRoot() != null, "There is no data at " + config.directoryModelURI.toString());
File lockFile = null;
try {
// find a repository to import
RepositoryModelDirectory directory = (RepositoryModelDirectory)fragmentation.getRoot();
List<RepositoryModel> scheduledForImport = RepositoryModelUtils.scheduledForImport(directory);
Iterator<RepositoryModel> scheduledForImportIterator = scheduledForImport.iterator();
String repositoryModelNameAsFileName = null;
RepositoryModel currentRepositoryModel = null;
while (scheduledForImportIterator.hasNext()) {
// try to create a lock the next possible repositoryModel
currentRepositoryModel = scheduledForImportIterator.next();
if ((ImportMetaData)RepositoryModelUtil.getData(currentRepositoryModel, RepositoryModelPackage.eINSTANCE.getImportMetaData()) == null) {
continue; // obviously not ready to be imported
}
String currentRepositoryName = RepositoryModelUtils.qualifiedName(currentRepositoryModel);
if (config.repositoryName == null || config.repositoryName.equals(currentRepositoryName)) {
repositoryModelNameAsFileName = currentRepositoryName.replaceAll("[^\\w_\\.]+", "_");
lockFile = new File(config.locks, repositoryModelNameAsFileName);
if (!lockFile.createNewFile()) {
// could not aquire the lock
SrcRepoActivator.INSTANCE.warning("Could not aquire lock for scheduled repository " + currentRepositoryName + "!");
} else {
repositoryModel = currentRepositoryModel;
}
}
}
if (repositoryModel == null) {
SrcRepoActivator.INSTANCE.error("Could not find a scheduled repositoryModel.");
return;
}
// try to update the import status the repository model
try {
ImportMetaData importMetaData = RepositoryModelUtil.getData(repositoryModel, RepositoryModelPackage.eINSTANCE.getImportMetaData());
importMetaData.setScheduled(false);
importMetaData.setImporting(true);
fs.save();
} catch (Exception e) {
SrcRepoActivator.INSTANCE.error("Unexpected error during updating the import status on the repository.", e);
}
// creating working copy
File workingDirectory = null;
try {
RepositoryMetaData metaData = RepositoryModelUtil.getData(repositoryModel, RepositoryModelPackage.eINSTANCE.getRepositoryMetaData());
ImportMetaData importMetaData = RepositoryModelUtil.getData(repositoryModel, RepositoryModelPackage.eINSTANCE.getImportMetaData());
String repositoryCloneURL = metaData.getOrigin();
workingDirectory = new File(config.workingcopies, repositoryModelNameAsFileName);
importMetaData.setWorkingCopy(workingDirectory.getAbsolutePath());
SrcRepoActivator.INSTANCE.info("Cloning " + repositoryCloneURL + " into " + workingDirectory + ".");
config.scs.createWorkingCopy(workingDirectory, repositoryCloneURL, true);
} catch (SourceControlException e) {
SrcRepoActivator.INSTANCE.error("Could not create working copy.", e);
}
// importing rev model
SrcRepoActivator.INSTANCE.info("Importing into " + EcoreUtil.getURI(repositoryModel) + " from " + workingDirectory + ".");
try {
config.scs.importRevisions(repositoryModel);
} catch (SourceControlException e) {
SrcRepoActivator.INSTANCE.error("Could not import the revision model.", e);
return;
}
// importing source code
if (!config.skipSourceCodeImport) {
IRepositoryModelVisitor sourceImportVisitor = null;
if (config.visitorFactory != null) {
sourceImportVisitor = config.visitorFactory.createRepositoryModelVisitor();
} else {
if (config.checkOutWithoutImport) {
sourceImportVisitor = new RepositoryModelRevisionCheckoutVisitor(config.scs, repositoryModel);
} else {
sourceImportVisitor = new EmffragMoDiscoImportRepositoryModelVisitor(config.scs, repositoryModel, javaModelPackage);
}
}
RepositoryModelTraversal.traverse(repositoryModel, sourceImportVisitor);
try {
sourceImportVisitor.close(repositoryModel);
} catch (Exception e) {
SrcRepoActivator.INSTANCE.warning("Exception during cleanup and closing.", e);
}
}
// close everything
try {
SrcRepoActivator.INSTANCE.info("Import complete. Saving and closing everything.");
config.scs.close();
} catch (Exception e) {
SrcRepoActivator.INSTANCE.warning("Exception during cleanup and closing.", e);
}
// try to update the import status the repository model
try {
ImportMetaData importMetaData = RepositoryModelUtil.getData(repositoryModel, RepositoryModelPackage.eINSTANCE.getImportMetaData());
importMetaData.setScheduled(false);
importMetaData.setImporting(false);
importMetaData.setImported(true);
} catch (Exception e) {
SrcRepoActivator.INSTANCE.error("Unexpected error during updating the import status on the repository.", e);
}
} catch (Exception e) {
SrcRepoActivator.INSTANCE.error("Unexpected and unhandled exception occured.", e);
} finally {
try {
if(lockFile != null) {
if (!lockFile.delete()) {
SrcRepoActivator.INSTANCE.error("Could not delete the lock file.");
}
}
fs.close();
} catch (Exception e) {
SrcRepoActivator.INSTANCE.error("Unknown error during closing fragmentation.", e);
}
SrcRepoActivator.INSTANCE.info("Import done.");
}
return;
}
@Override
public void stop() {
}
}