blob: 1a4cc431d3fe9efbd3478c47f495f54fa9c97a23 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011-2018 The University of York, Aston University.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License, v. 2.0 are satisfied: GNU General Public License, version 3.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-3.0
*
* Contributors:
* Konstantinos Barmpis - initial API and implementation
* Antonio Garcia-Dominguez - separate inserter and deletion utils creation
* for time-aware version
******************************************************************************/
package org.eclipse.hawk.graph.updater;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.hawk.core.IConsole;
import org.eclipse.hawk.core.IModelIndexer;
import org.eclipse.hawk.core.IModelUpdater;
import org.eclipse.hawk.core.IVcsManager;
import org.eclipse.hawk.core.VcsChangeType;
import org.eclipse.hawk.core.VcsCommit;
import org.eclipse.hawk.core.VcsCommitItem;
import org.eclipse.hawk.core.VcsRepositoryDelta;
import org.eclipse.hawk.core.graph.IGraphChangeListener;
import org.eclipse.hawk.core.graph.IGraphDatabase;
import org.eclipse.hawk.core.graph.IGraphIterable;
import org.eclipse.hawk.core.graph.IGraphNode;
import org.eclipse.hawk.core.graph.IGraphNodeIndex;
import org.eclipse.hawk.core.graph.IGraphTransaction;
import org.eclipse.hawk.core.model.IHawkModelResource;
public class GraphModelUpdater implements IModelUpdater {
public static final String FILEINDEX_REPO_SEPARATOR = "||||";
public static final String PROXY_REFERENCE_PREFIX = "hawkProxyRef:";
public static final boolean CARES_ABOUT_RESOURCES = true;
/**
* Used in URIs of targets when we only know the unique fragment and we
* don't know the file.
*/
public static final String PROXY_FILE_WILDCARD = "*";
protected IModelIndexer indexer;
protected IConsole console;
private boolean isActive = false;
protected Set<IGraphNode> toBeUpdated = new HashSet<>();
@Override
public void run(IConsole c, IModelIndexer hawk) throws Exception {
this.indexer = hawk;
this.console = c;
}
@Override
public boolean updateStore(VcsCommitItem f, IHawkModelResource res) {
final long start = System.currentTimeMillis();
boolean success = true;
/*
* We register this listener only for this particular updater and during
* this method call. It is used to collect information about which nodes
* should have their derived attributes updated, rather than collecting
* all changes and working from there (which can use a lot more memory).
* Ideally, if we do not have any derived attributes, this shouldn't
* require any more memory, and the CPU cost should be the same.
*/
final IGraphDatabase g = indexer.getGraph();
final DirtyDerivedFeaturesListener l = new DirtyDerivedFeaturesListener(g);
if (!indexer.getDerivedAttributes().isEmpty()) {
indexer.addGraphChangeListener(l);
}
/*
* Print more information when we have few commit items: normally this
* means we have a few big files instead of many small files.
*/
final boolean verbose = hasFewItems(f);
try {
try {
if (res == null) {
/*
* File failed to update to latest version so remove it to maintain consistency.
*/
if (!deleteAll(f)) {
console.printerrln("warning: failed to delete item: "
+ f
+ "\nafter its resource failed to be loaded");
success = false;
}
} else if (!createInserter().run(res, f, verbose)) {
console.printerrln("warning: failed to update item: " + f
+ "\nmodel resource: " + res);
success = false;
}
} catch (Exception ex) {
console.printerrln(ex);
success = false;
}
if (verbose) {
long end = System.currentTimeMillis();
console.println((end - start) / 1000 + "s" + (end - start) % 1000 + "ms [pure insertion]");
}
} finally {
final long s = System.currentTimeMillis();
if (verbose) {
console.print("marking any relevant derived attributes for update...");
indexer.getCompositeStateListener().info("Marking relevant derived attributes for update...");
}
toBeUpdated.addAll(l.getNodesToBeUpdated());
indexer.removeGraphChangeListener(l);
if (verbose) {
final long end = System.currentTimeMillis();
console.println((end - s) / 1000 + "s" + (end - s) % 1000 + "ms");
indexer.getCompositeStateListener().info("Marked relevant derived attributes for update. "
+ (end - s) / 1000 + "s" + (end - s) % 1000 + "ms");
}
}
return success;
}
private boolean hasFewItems(VcsCommitItem f){
return f.getCommit().getItems().size() < 100;
}
@Override
public void updateProxies() {
final long start = System.currentTimeMillis();
console.println("attempting to resolve any leftover cross-file references...");
try {
indexer.getCompositeStateListener().info(
"Resolving any leftover cross-file references...");
createInserter().resolveProxies(indexer.getGraph());
} catch (Exception e) {
console.printerrln("Exception in updateStore - resolving proxies, returning 0:");
console.printerrln(e);
}
console.println("attempting to resolve any uninitialized derived attributes...");
try {
indexer.getCompositeStateListener().info(
"Resolving any uninitialized derived attributes...");
createInserter().resolveDerivedAttributeProxies(
indexer.getDerivedAttributeExecutionEngine());
} catch (Exception e) {
console.printerrln("Exception in updateStore - resolving DERIVED proxies, returning 0:");
console.printerrln(e);
}
console.println("attempting to update any relevant derived attributes...");
try {
indexer.getCompositeStateListener().info(
"Updating any affected derived attributes...");
createInserter().updateDerivedAttributes(
indexer.getDerivedAttributeExecutionEngine(), toBeUpdated);
toBeUpdated = new HashSet<>();
} catch (Exception e) {
toBeUpdated = new HashSet<>();
console.printerrln("Exception in updateStore - UPDATING DERIVED attributes");
console.printerrln(e);
}
long end = System.currentTimeMillis();
console.println((end - start) / 1000 + "s" + (end - start) % 1000
+ "ms [proxy update]");
}
public boolean isActive() {
return isActive;
}
@Override
public void shutdown() {
//
}
@Override
public boolean caresAboutResources() {
return CARES_ABOUT_RESOURCES;
}
@Override
public boolean deleteAll(IVcsManager c) throws Exception {
boolean ret = false;
IGraphDatabase graph = indexer.getGraph();
try (IGraphTransaction t = graph.beginTransaction()) {
IGraphNodeIndex filedictionary = graph.getFileIndex();
IGraphIterable<? extends IGraphNode> fileNodes = filedictionary.query("id", c.getLocation() + "*");
// Construct a simulated VcsRepositoryDelta with a "-deleted" revision
final VcsCommit fakeCommit = new VcsCommit();
final VcsRepositoryDelta delta = new VcsRepositoryDelta(Collections.singleton(fakeCommit));
delta.setManager(c);
fakeCommit.setAuthor("hawk");
fakeCommit.setJavaDate(new Date());
fakeCommit.setRevision(c.getCurrentRevision() + "-deleted");
fakeCommit.setMessage("stopped indexing");
final IGraphChangeListener changeListener = indexer.getCompositeGraphChangeListener();
for (IGraphNode fileNode : fileNodes) {
VcsCommitItem item = new VcsCommitItem();
item.setChangeType(VcsChangeType.DELETED);
item.setCommit(fakeCommit);
String path = fileNode.getProperty(
IModelIndexer.IDENTIFIER_PROPERTY).toString();
item.setPath(path.startsWith("/") ? path : "/" + path);
fakeCommit.getItems().add(item);
createDeletionUtils().deleteAll(fileNode, item, changeListener);
}
t.success();
}
return ret;
}
@Override
public boolean deleteAll(VcsCommitItem c) throws Exception {
indexer.getCompositeStateListener().info("Deleting all contents of file: " + c.getPath() + "...");
boolean ret = false;
IGraphNode n = new Utils().getFileNodeFromVCSCommitItem(indexer.getGraph(), c);
if (n != null) {
try (IGraphTransaction t = indexer.getGraph().beginTransaction()) {
ret = createDeletionUtils().deleteAll(n, c, indexer.getCompositeGraphChangeListener());
t.success();
} catch (Exception e) {
console.printerrln(e);
ret = false;
}
} else {
return true;
}
indexer.getCompositeStateListener().info("Deleted all contents of file: " + c.getPath() + ".");
return ret;
}
@Override
public void updateDerivedAttribute(String metamodeluri, String typename,
String attributename, String attributetype, boolean isMany,
boolean isOrdered, boolean isUnique, String derivationlanguage,
String derivationlogic) {
createInserter()
.updateDerivedAttribute(metamodeluri,
typename, attributename, attributetype, isMany, isOrdered,
isUnique, derivationlanguage, derivationlogic);
}
@Override
public void updateIndexedAttribute(String metamodeluri, String typename,
String attributename) {
createInserter().updateIndexedAttribute(metamodeluri,
typename, attributename);
}
@Override
public String getName() {
return "Default Hawk Graph Model Updater (v1.0)";
}
@Override
public Set<VcsCommitItem> compareWithLocalFiles(
Set<VcsCommitItem> reposItems) {
if (reposItems.isEmpty()) {
return reposItems;
}
final VcsCommitItem firstItem = reposItems.iterator().next();
final String repositoryURL = firstItem.getCommit().getDelta()
.getManager().getLocation();
final Set<VcsCommitItem> changed = new HashSet<VcsCommitItem>();
changed.addAll(reposItems);
IGraphDatabase graph = indexer.getGraph();
if (graph != null) {
try (IGraphTransaction tx = graph.beginTransaction()) {
// operations on the graph
// ...
IGraphNodeIndex filedictionary = graph.getFileIndex();
if (filedictionary != null
&& filedictionary
.query("id",
repositoryURL
+ FILEINDEX_REPO_SEPARATOR
+ "*").iterator().hasNext()) {
for (VcsCommitItem r : reposItems) {
String rev = "-2";
try {
IGraphIterable<? extends IGraphNode> ret = filedictionary
.get("id",
repositoryURL
+ FILEINDEX_REPO_SEPARATOR
+ r.getPath());
if (ret.iterator().hasNext()) {
IGraphNode n = ret.getSingle();
rev = (String) n.getProperty("revision");
}
} catch (Exception e) {
console.printerrln(e);
}
if (r.getCommit().getRevision().equals(rev))
changed.remove(r);
if (IModelIndexer.VERBOSE)
console.println("comparing revisions of: "
+ r.getPath() + " | "
+ r.getCommit().getRevision() + " | " + rev);
}
}
tx.success();
} catch (Exception e) {
console.printerrln(e);
}
}
return changed;
}
public GraphModelInserter createInserter() {
return new GraphModelInserter(indexer, this::createDeletionUtils, new TypeCache(this.indexer));
}
protected DeletionUtils createDeletionUtils() {
return new DeletionUtils(indexer.getGraph());
}
}