Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Javadoc2 #72

Merged
merged 6 commits into from
Sep 29, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 0 additions & 90 deletions src/main/java/org/repodriller/CommitVisitorIterator.java

This file was deleted.

64 changes: 37 additions & 27 deletions src/main/java/org/repodriller/RepoDriller.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,44 +21,54 @@

import org.apache.log4j.Logger;

/**
* This is the entry point for the RepoDriller framework.
* Users should declare a RepoDriller and a Study, and then start() the Study.
*
* @author Mauricio Aniche
*/
public class RepoDriller {

private static Logger log = Logger.getLogger(RepoDriller.class);

public static void main(String[] args) {
System.out.println("You should not run me! :/");
}

private static Logger log = Logger.getLogger(RepoDriller.class);
public RepoDriller() {
log.info("# -------------------------------------------------- #");
log.info("# RepoDriller #");
log.info("# v1.3.0 #");
log.info("# www.repodriller.org #");
log.info("# -------------------------------------------------- #");
log.info("");
}

/**
* {@link Study#execute} this study.
* Catches and logs any exceptions thrown.
* Has a stopwatch for you.
*
* @param study
*/
public void start(Study study) {
/* TODO We could just make this static, no? What's the point in having a RepoDriller instance? */
Calendar start = Calendar.getInstance();

log.info("# -------------------------------------------------- #");
log.info("Starting study at: " + new SimpleDateFormat("MM-dd-yyyy HH:mm:ss").format(start.getTime()));
try {

Calendar startDate = Calendar.getInstance();

log.info("# -------------------------------------------------- #");
log.info("# RepoDriller #");
log.info("# v1.3.0 #");
log.info("# www.repodriller.org #");
log.info("# -------------------------------------------------- #");
log.info("Starting engine: " + new SimpleDateFormat("MM-dd-yyyy HH:mm:ss").format(startDate.getTime()));

try {
study.execute();
} catch (Throwable t) {
log.error("some study error came to me", t);
}

Calendar finishDate = Calendar.getInstance();
log.info("Finished: " + new SimpleDateFormat("MM-dd-yyyy HH:mm:ss").format(finishDate.getTime()));
long seconds = (finishDate.getTimeInMillis() - startDate.getTimeInMillis())/1000;
log.info("It took " + seconds + " seconds (~" + seconds/60 + " minutes).");
log.info("Brought to you by RepoDriller (repodriller.org.br)");
log.info("# -------------------------------------------------- #");

} catch(Throwable ex) {
log.error("Some error ocurred", ex);
study.execute();
} catch (Throwable t) {
log.error("This study threw an exception:", t);
}
}
Calendar finish = Calendar.getInstance();
log.info("Finished study at: " + new SimpleDateFormat("MM-dd-yyyy HH:mm:ss").format(finish.getTime()));

long seconds = (finish.getTimeInMillis() - start.getTimeInMillis())/1000;
long minutes = seconds/60;
log.info("It took " + seconds + " seconds (~" + minutes + " minutes).");
log.info("# -------------------------------------------------- #");
}

}
9 changes: 7 additions & 2 deletions src/main/java/org/repodriller/RepoDrillerException.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package org.repodriller;

/**
* Catch-all type for internal exceptions thrown by RepoDriller.
*
* @author Mauricio Aniche
*/
public class RepoDrillerException extends RuntimeException{

private static final long serialVersionUID = 1L;

public RepoDrillerException(String msg) {
super(msg);
}

}
}
168 changes: 168 additions & 0 deletions src/main/java/org/repodriller/RepoVisitor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package org.repodriller;

import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;
import org.repodriller.domain.Commit;
import org.repodriller.persistence.PersistenceMechanism;
import org.repodriller.persistence.csv.CSVFileFormatException;
import org.repodriller.scm.CommitVisitor;
import org.repodriller.scm.SCMRepository;

/**
* A RepoVisitor offers Repo-level visiting to a group of CommitVisitors.
* The metaphor is of a "tour bus" for CommitVisitors.
* The RepoVisitor drives each CommitVisitor to the Commits for each SCMRepository.
*
* Intended use:
* - {@link RepoVisitor#addVisitor} all of the CommitVisitor/PersistenceMechanism pairs you want.
* - {@link RepoVisitor#beginRepoTour} to start a new repo
* - {@link RepoVisitor#visit} for each Commit you're visiting in the current repo
* - {#link CommitVisitorIterator#endRepoTour} after you're done with this repo
*
* @author Mauricio Aniche
*
*/
public class RepoVisitor {

private Map<CommitVisitor, PersistenceMechanism> visitors;
private static final Logger log = Logger.getLogger(RepoVisitor.class);
private SCMRepository currentRepo; // One at a time.

public RepoVisitor() {
visitors = new HashMap<CommitVisitor, PersistenceMechanism>();
currentRepo = null;
}

/***
* Methods for managing CommitVisitors.
***/

/**
* Add this <CommitVisitor, PersistenceMechanism> pair to my collection.
*
* @param visitor
* @param writer
*/
public void addVisitor(CommitVisitor visitor, PersistenceMechanism writer) {
visitors.put(visitor, writer);
}

/**
* Remove this CommitVisitors from my collection, if present.
*
* @param visitor
*/
public void removeVisitor(CommitVisitor visitor) {
visitors.remove(visitor);
}

/**
* Empty my collection of CommitVisitors.
*/
public void clearVisitors() {
visitors.clear();
}

/**
* Print a description of all of the CommitVisitors I maintain.
*/
void printVisitors() {
for(CommitVisitor visitor : visitors.keySet()) {
log.info("- " + visitor.name() + " (" + visitor.getClass().getName() + ")");
}
}

/**
* @return True if no CommitVisitors, else false.
*/
public boolean isEmpty() {
return visitors.isEmpty();
}

/***
* Methods for visiting an SCMRepository.
***/

/**
* Prepare for the next repo we visit.
* Calls {@link CommitVisitor#initialize} on each CommitVisitor in my collection.
*
* @param repo The repo we are visiting next
*/
void beginRepoVisit(SCMRepository repo) {
if (currentRepo != null)
throw new RepoDrillerException("Error, one repo at a time. We are already visiting a repo.");
currentRepo = repo;

for(Map.Entry<CommitVisitor, PersistenceMechanism> entry : visitors.entrySet()) {
CommitVisitor visitor = entry.getKey();
PersistenceMechanism writer = entry.getValue();

try {
log.info("-> Initializing visitor " + visitor.name());
visitor.initialize(currentRepo, writer);
} catch (Exception e) {
log.error("error in " + currentRepo.getPath() +
"when initializing " + visitor.name() + ", error=" + e.getMessage(), e);
}
}
}

/**
* Visit this commit in the current repo.
* Calls {@link CommitVisitor#process} on each CommitVisitor in my collection.
*
* @param commit
*/
void visitCommit(Commit commit) {
for(Map.Entry<CommitVisitor, PersistenceMechanism> entry : visitors.entrySet()) {
CommitVisitor visitor = entry.getKey();
PersistenceMechanism writer = entry.getValue();

try {
log.info("-> Processing " + commit.getHash() + " with " + visitor.name());
visitor.process(currentRepo, commit, writer);
} catch (CSVFileFormatException e) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure why this exception is here... This deserves an issue: #74

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we should define what the behavior is if a CommitVisitor throws an exception.

Options:

  1. We could remove the visitor from the RepoVisitor.
  2. We can mask the exception.
  3. We can throw.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the behavior is already correct there. If an exception happens, repodriller logs it, and moves forward. It could maybe notify the study or the visitors about the error.

But this particular CSVFileFormat, not sure what it does there. I think it halts the problem as soon as it notices that the output is not being saved. It makes sense to completely stop the execution. But we should make it more general, so that this behaviour is the same through all the persistence mechanisms! This should be in #74

log.fatal(e);
System.exit(-1);
} catch (Exception e) {
log.error("error processing #" + commit.getHash() + " in " + currentRepo.getPath() +
", processor=" + visitor.name() + ", error=" + e.getMessage(), e);
}
}
}

/**
* Conclude the visit to the current repo.
* Calls {@link CommitVisitor#finalize} on each CommitVisitor in my collection.
*/
void endRepoVisit() {
for(Map.Entry<CommitVisitor, PersistenceMechanism> entry : visitors.entrySet()) {
CommitVisitor visitor = entry.getKey();
PersistenceMechanism writer = entry.getValue();

try {
log.info("-> Finalizing visitor " + visitor.name());
visitor.finalize(currentRepo, writer);
} catch (Exception e) {
log.error("error in " + currentRepo.getPath() +
"when finalizing " + visitor.name() + ", error=" + e.getMessage(), e);
}
}

currentRepo = null;
}

/**
* Close all of the PersistenceMechanism's associated with visitors.
*/
void closeAllPersistence() {
/* TODO: This API doesn't make sense to me. Why are the persistence mechanisms closed by this class?
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where would you close them? Would you leave for the client to close them?

The problem is that, then, the user can't write a beautiful fluent instantiation of the repo:

new RepositoryMining().
...
.process(visitor, new CSVFile(...))

The user would have to store new CSVFile() in a variable, and then, close it.

In addition, I'd not expect the program to do anything else after repodriller is done. So, I guess I implemented it as a 'courtesy' to the user.

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that the inline PersistenceMechanism instantiation is lovely, and that if code is written in the functional style then RepositoryMining must close up the PersistenceMechanism's it's been given.

On the other hand, there's a mismatch between where the PM is created (client level) and where it is closed (framework level), which is surprising.

How about we overload RepositoryMining.process with a "close the PM for me" flag, and have the current 2-arg version always close the PM?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's open an issue to discuss about that (#77), so that I can move on with this PR

* I think it should be removed. */
for(PersistenceMechanism persist : visitors.values()) {
persist.close();
}
}
}
Loading