Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "VIATRA/Query/UserDocumentation/API/Advanced"

(27 intermediate revisions by 4 users not shown)
Line 1: Line 1:
= Overview =
+
= Overview =
This page overviews advanced use-cases for EMF-IncQuery. The topics cover
+
* the '''Generic API''', to be used when the features supported by the generated API do not suffice (e.g. when working with dynamically defined patterns, or patterns whose handles are not known at compile time)
+
* '''Advanced change processing APIs''', and '''advanced lifecycle management techniques''' to be used for performance-critical and/or resource-constrained applications
+
* '''IncQuery Base''', the low-level query and indexer layer underneath EMF-IncQuery pattern matchers
+
  
== Running example ==
+
This page overviews advanced use-cases for EMF-IncQuery. The topics cover
All the code examples and explanations will be given in the context of the [[EMFIncQuery/UserDocumentation/HeadlessExecution|Headless]] example.
+
 
The up-to-date sample source code to this page is found in Git here: http://git.eclipse.org/c/incquery/org.eclipse.incquery.examples.git/tree/headless
+
*the '''Generic API''', to be used when the features supported by the generated API do not suffice (e.g. when working with dynamically defined patterns, or patterns whose handles are not known at compile time)
Most notably,  
+
*'''Advanced change processing APIs''', and '''advanced lifecycle management techniques''' to be used for performance-critical and/or resource-constrained applications
* the patterns are found in [http://git.eclipse.org/c/incquery/org.eclipse.incquery.examples.git/tree/headless/headlessQueries.incquery/src/headless/headlessQueries.eiq headlessQueries.eiq]
+
*'''IncQuery Base''', the low-level query and indexer layer underneath EMF-IncQuery pattern matchers
* and the API usage samples are found in [http://git.eclipse.org/c/incquery/org.eclipse.incquery.examples.git/tree/headless/org.eclipse.incquery.application/src/org/eclipse/incquery/application/common/IncQueryHeadless.java IncQueryHeadless.java] and [http://git.eclipse.org/c/incquery/org.eclipse.incquery.examples.git/tree/headless/org.eclipse.incquery.application/src/org/eclipse/incquery/application/common/IncQueryHeadlessAdvanced.java IncQueryHeadlessAdvanced.java]  
+
*'''logging in EMF-IncQuery''', which is provided by hierarchic Log4J loggers.
 +
 
 +
== Running example ==
 +
 
 +
All the code examples and explanations will be given in the context of the [[EMFIncQuery/UserDocumentation/HeadlessExecution|Headless]] example. The up-to-date sample source code to this page is found in Git here: http://git.eclipse.org/c/incquery/org.eclipse.incquery.examples.git/tree/headless Most notably,  
 +
 
 +
*the patterns are found in [http://git.eclipse.org/c/incquery/org.eclipse.incquery.examples.git/tree/headless/headlessQueries.incquery/src/headless/headlessQueries.eiq headlessQueries.eiq]  
 +
*and the API usage samples are found in [http://git.eclipse.org/c/incquery/org.eclipse.incquery.examples.git/tree/headless/org.eclipse.incquery.application/src/org/eclipse/incquery/application/common/IncQueryHeadless.java IncQueryHeadless.java] and [http://git.eclipse.org/c/incquery/org.eclipse.incquery.examples.git/tree/headless/org.eclipse.incquery.application/src/org/eclipse/incquery/application/common/IncQueryHeadlessAdvanced.java IncQueryHeadlessAdvanced.java]
  
 
== Javadoc  ==
 
== Javadoc  ==
The most up-to-date Javadocs for the EMF-IncQuery API can be found online at http://eclipse.org/incquery/javadoc/  
+
 
 +
The most up-to-date Javadocs for the EMF-IncQuery API can be found online at http://eclipse.org/incquery/javadoc/
 +
 
 
= The IncQuery Generic API  =
 
= The IncQuery Generic API  =
 +
 
The "generic" API differs from the generated one in two key aspects:  
 
The "generic" API differs from the generated one in two key aspects:  
 +
 
*it can be used to apply queries and use other IncQuery features '''without''' generating code and loading the resulting bundles into the running configuration. In other words, you just need to supply the EMF-based in-memory representation (an instance of the Pattern class)  
 
*it can be used to apply queries and use other IncQuery features '''without''' generating code and loading the resulting bundles into the running configuration. In other words, you just need to supply the EMF-based in-memory representation (an instance of the Pattern class)  
 
*the generic API is not "type safe" in the sense that the Java types of your pattern variables is not known and needs to be handled dynamically (e.g. by instanceof - typecase combos).
 
*the generic API is not "type safe" in the sense that the Java types of your pattern variables is not known and needs to be handled dynamically (e.g. by instanceof - typecase combos).
  
 
== Initializing matchers and accessing results ==
 
== Initializing matchers and accessing results ==
 
+
In EMF-IncQuery 0.8 the generic API has changed significantly. Most importantly, it has been moved into the patternlanguage.emf plug-in, and there is no UI dependency required anymore. For more details, see [[EMFIncQuery/Releases/MigrateTo0.8|the migration guide]]; for the old version of this content see [[EMFIncQuery/UserDocumentation/API/Advanced/Archive]]
=== Sample code ===
+
=== Sample code ===
 
Using the Generic API:
 
Using the Generic API:
 
<source lang="java">
 
<source lang="java">
public String executeDemo_GenericAPI(String modelPath, String patternFQN) {
+
public String executeDemo_GenericAPI_LoadFromEIQ(String modelPath, String patternFQN) {
final StringBuilder results = new StringBuilder();
+
  final StringBuilder results = new StringBuilder();
Resource resource = loadModel(modelPath);
+
  Resource resource = loadModel(modelPath);
if (resource != null) {
+
  if (resource != null) {
try {
+
    try {
// get all matches of the pattern
+
      // get all matches of the pattern
// create an *unmanaged* engine to ensure that noone else is going
+
      // create an *unmanaged* engine to ensure that noone else is going
// to use our engine
+
      // to use our engine
IncQueryEngine engine = EngineManager.getInstance().createUnmanagedIncQueryEngine(resource);
+
      AdvancedIncQueryEngine engine = AdvancedIncQueryEngine.createUnmanagedEngine(resource);
// instantiate a pattern matcher through the registry, by only knowing its FQN
+
      // instantiate a pattern matcher through the registry, by only knowing its FQN
IncQueryMatcher<? extends IPatternMatch> matcher = MatcherFactoryRegistry.getMatcherFactory(patternFQN).getMatcher(engine);
+
      // assuming that there is a pattern definition registered matching 'patternFQN'
// assuming that there is a pattern definition registered matching 'patternFQN'
+
 
if (matcher!=null) {
+
      Pattern p = null;
Collection<? extends IPatternMatch> matches = matcher.getAllMatches();
+
prettyPrintMatches(results, matches);
+
      // Initializing Xtext-based resource parser
}
+
      // Do not use if EMF-IncQuery tooling is loaded!
// wipe the engine
+
      new EMFPatternLanguageStandaloneSetup().createInjectorAndDoEMFRegistration();
engine.wipe();
+
 
// after a wipe, new patterns can be rebuilt with much less overhead than  
+
      //Loading pattern resource from file
// complete traversal (as the base indexes will be kept)
+
      ResourceSet resourceSet = new ResourceSetImpl();
// completely dispose of the engine once's it is not needed
+
      URI fileURI = URI.createPlatformPluginURI("headlessQueries.incquery/src/headless/headlessQueries.eiq", false);
engine.dispose();
+
      Resource patternResource = resourceSet.getResource(fileURI, true);
resource.unload();
+
   
} catch (IncQueryException e) {
+
      // navigate to the pattern definition that we want
e.printStackTrace();
+
      if (patternResource != null) {
results.append(e.getMessage());
+
        if (patternResource.getErrors().size() == 0 && patternResource.getContents().size() >= 1) {
}
+
          EObject topElement = patternResource.getContents().get(0);
} else {
+
          if (topElement instanceof PatternModel) {
results.append("Resource not found");
+
            for (Pattern _p  : ((PatternModel) topElement).getPatterns()) {
}
+
              if (patternFQN.equals(CorePatternLanguageHelper.getFullyQualifiedName(_p))) {
return results.toString();
+
                p = _p; break;
 +
              }
 +
            }
 +
          }
 +
        }
 +
      }
 +
      if (p == null) {
 +
        throw new RuntimeException(String.format("Pattern %s not found", patternFQN));
 +
      }
 +
      // A specification builder is used to translate patterns to query specifications
 +
      SpecificationBuilder builder = new SpecificationBuilder();
 +
   
 +
      // attempt to retrieve a registered query specification    
 +
      IncQueryMatcher<? extends IPatternMatch> matcher = engine.getMatcher(builder.getOrCreateSpecification(p));
 +
 +
      if (matcher!=null) {
 +
        Collection<? extends IPatternMatch> matches = matcher.getAllMatches();
 +
        prettyPrintMatches(results, matches);
 +
      }
 +
 +
      // wipe the engine
 +
      engine.wipe();
 +
      // after a wipe, new patterns can be rebuilt with much less overhead than  
 +
      // complete traversal (as the base indexes will be kept)
 +
 
 +
      // completely dispose of the engine once's it is not needed
 +
      engine.dispose();
 +
      resource.unload();
 +
    } catch (IncQueryException e) {
 +
      e.printStackTrace();
 +
      results.append(e.getMessage());
 +
    }
 +
  } else {
 +
    results.append("Resource not found");
 +
  }
 +
  return results.toString();
 
}
 
}
 
</source>
 
</source>
  
=== API interfaces ===
 
  
* IPatternMatch [http://eclipse.org/incquery/javadoc/milestones/m2/org/eclipse/incquery/runtime/api/IPatternMatch.html Javadoc]
 
** reflection: pattern(), patternName()
 
** getters and setters
 
** utility functions (toArray, prettyPrint)
 
* IncQueryMatcher [http://eclipse.org/incquery/javadoc/milestones/m2/org/eclipse/incquery/runtime/api/IncQueryMatcher.html Javadoc]
 
** reflection
 
** get all matches
 
** get single/arbitrary match
 
** check for a match
 
** number of matches
 
** process matches
 
** access change processing features
 
** create a new Match for input binding
 
** access projected value sets
 
  
== The Pattern Registry and Matcher Factories ==
+
=== API interfaces  ===
TODO
+
The IMatcherFactory interface [http://eclipse.org/incquery/javadoc/milestones/m2/org/eclipse/incquery/runtime/api/IMatcherFactory.html Javadoc]
+
* reflection
+
* initialize the matcher
+
  
 +
*IPatternMatch [http://eclipse.org/incquery/javadoc/milestones/m2/org/eclipse/incquery/runtime/api/IPatternMatch.html Javadoc]
 +
**reflection: pattern(), patternName()
 +
**getters and setters
 +
**utility functions (toArray, prettyPrint)
 +
*IncQueryMatcher [http://eclipse.org/incquery/javadoc/milestones/m2/org/eclipse/incquery/runtime/api/IncQueryMatcher.html Javadoc]
 +
**reflection
 +
**get all matches
 +
**get single/arbitrary match
 +
**check for a match
 +
**number of matches
 +
**process matches
 +
**access change processing features
 +
**create a new Match for input binding
 +
**access projected value sets
 +
 +
== The Pattern Registry and Matcher Factories  ==
 +
 +
TODO The IMatcherFactory interface [http://eclipse.org/incquery/javadoc/milestones/m2/org/eclipse/incquery/runtime/api/IMatcherFactory.html Javadoc]
 +
 +
*reflection
 +
*initialize the matcher
 +
 +
<br>
 +
 +
= Advanced query result set change processing  =
 +
 +
== Match update callbacks  ==
  
= Advanced query result set change processing =
 
== Match update callbacks ==
 
 
<source lang="java">
 
<source lang="java">
 
private void changeProcessing_lowlevel(final StringBuilder results, IncQueryMatcher<? extends IPatternMatch> matcher) {
 
private void changeProcessing_lowlevel(final StringBuilder results, IncQueryMatcher<? extends IPatternMatch> matcher) {
// (+) these update callbacks are called whenever there is an actual change in the
+
// (+) these update callbacks are called whenever there is an actual change in the
// result set of the pattern you are interested in. Hence, they are called fewer times
+
// result set of the pattern you are interested in. Hence, they are called fewer times
// than the "afterUpdates" option, giving better performance.
+
// than the "afterUpdates" option, giving better performance.
// (-)  the downside is that the callbacks are *not* guaranteed to be called in a consistent
+
// (-)  the downside is that the callbacks are *not* guaranteed to be called in a consistent
// state (i.e. when the update propagation is settled), hence
+
// state (i.e. when the update propagation is settled), hence
//  * you must not invoke pattern matching and model manipulation _inside_ the callback method
+
//  * you must not invoke pattern matching and model manipulation _inside_ the callback method
//  * the callbacks might encounter "hazards", i.e. when an appearance is followed immediately by a disappearance.
+
//  * the callbacks might encounter "hazards", i.e. when an appearance is followed immediately by a disappearance.
matcher.addCallbackOnMatchUpdate(new IMatchUpdateListener<IPatternMatch>() {
+
engine.addMatchUpdateListener(matcher, new IMatchUpdateListener<IPatternMatch>() {
@Override
+
  @Override
public void notifyDisappearance(IPatternMatch match) {
+
  public void notifyDisappearance(IPatternMatch match) {
// left empty
+
  // left empty
}
+
  }
@Override
+
  @Override
public void notifyAppearance(IPatternMatch match) {
+
  public void notifyAppearance(IPatternMatch match) {
results.append("\tNew match found by changeset low level callback: " + match.prettyPrint()+"\n");
+
  results.append("\tNew match found by changeset low level callback: " + match.prettyPrint()+"\n");
}
+
  }
}, false);
+
}, false);
 
}
 
}
</source>
+
</source><br>
== After update callbacks and DeltaMonitors ==
+
The '''DeltaMonitor''' can do this for you in a convenient way. It is a monitoring object that connects to the rete network as a receiver to reflect changes since an arbitrary state acknowledged by the client.&nbsp;If a new matching is found, it appears in the matchFoundEvents collection, and disappears when that particular matching cannot be found anymore. If the event of finding a match has been processed by the client, it can be removed manually. In this case, when a previously found matching is lost, the Tuple will appear in the matchLostEvents collection, and disappear upon finding the same matching again. "Matching lost" events can also be acknowledged by removing a Tuple from the collection. If the matching is found once again, it will return to matchFoundEvents.
+
<source lang="java">
+
private void changeProcessing_deltaMonitor(final StringBuilder results, IncQueryMatcher<? extends IPatternMatch> matcher) {
+
final DeltaMonitor<? extends IPatternMatch> dm = matcher.newDeltaMonitor(false);
+
// (+) these updates are guaranteed to be called in a *consistent* state,
+
// i.e. when the pattern matcher is guaranteed to be consistent with the model
+
// anything can be written into the callback method
+
// (-) the downside is that the callback is called after *every* update
+
// that propagates through the matcher, i.e. also when the updates do not actually
+
// influence the result set you are interested in. Hence, the performance is somewhat
+
// lower than for the "lowlevel" option.
+
matcher.addCallbackAfterUpdates(new Runnable() {
+
@Override
+
public void run() {
+
for (IPatternMatch newMatch : dm.matchFoundEvents) {
+
results.append("\tNew match found by changeset delta monitor: " + newMatch.prettyPrint()+"\n");
+
}
+
for (IPatternMatch lostMatch : dm.matchLostEvents) {
+
// left empty
+
}
+
}
+
});
+
}
+
</source>
+
  
= Advanced IncQuery Lifecycle management =
+
== Using the EVM  ==
  
== Managed vs. unmanaged IncQueryEngines ==
+
The [[EMFIncQuery/DeveloperDocumentation/EventDrivenVM|Event-driven VM]] can also be used for this purpose.
 +
See the section called "Efficiently reacting to pattern match set changes".
  
 +
= Advanced IncQuery Lifecycle management  =
  
== Disposing ==
+
== Managed vs. unmanaged IncQueryEngines  ==
* dispose
+
* wipe
+
  
 +
== Disposing  ==
 +
 +
If you want to remove the matchers from the engine you can call the wipe() method on it. It discards any pattern matcher caches and forgets the known patterns. The base index built directly on the underlying EMF model, however, is kept in memory to allow reuse when new pattern matchers are built. If you don’t want to use it anymore call the dispose() instead, to completely disconnect and dismantle the engine.
 +
 +
*wipe
 +
*dispose
 +
 +
= IncQuery Base  =
 +
 +
EMF-IncQuery provides a light-weight indexer library called Base that aims to provide several useful (some would even argue critical) features for querying EMF models:
 +
 +
*inverse navigation along EReferences
 +
*finding and incrementally tracking all model elements by attribute value/type (i.e. inverse navigation along EAttributes)
 +
*incrementally computing transitive reachability along given reference types (i.e. transitive closure of an EMF model)
 +
*getting and tracking all the (direct) instances of a given EClass
 +
 +
The point of IncQuery Base is to provide all of these in an incremental way, which means that once the query evaluator is attached to an EMF model, as long as it stays attached, the query results can be retrieved instantly (as the query result cache is automatically updated). IncQuery Base is a lightweight, small Java library that can be integrated easily to any EMF-based tool as it can be used in a stand-alone way, without the rest of EMF-IncQuery.
 +
 +
We are aware that some of the functionality can be found in some Ecore utility classes (for example ECrossReferenceAdapter). These standard implementations are non-incremental, and are thus do not scale well in scenarios where high query evaluation performance is necessary (such as e.g. on-the-fly well-formedness validation or live view maintenance). IncQuery Base has an additional important feature that is not present elsewhere: it contains very efficient implementations of transitive closure that can be used e.g. to maintain reachability regions incrementally, in very large EMF instance models.
 +
 +
The detailed documentation is covered in [[EMFIncQuery/UserDocumentation/API/BaseIndexer]]
 +
 +
== Extracting reachability paths from transitive closure  ==
  
= IncQuery Base =
 
TODO
 
== Extracting reachability paths from transitive closure ==
 
 
TODO
 
TODO
 +
 +
= Logging in EMF-IncQuery =
 +
 +
EMF-IncQuery logs error messages and some trace information using log4j. If you need to debug your application and would like to see these messages, you can set the log level in different hierarchy levels.
 +
Since we use standard log4j, you can configure logging both with configuration files or through API calls.
 +
 +
* All loggers are children of a top-level default logger, that can be accessed from IncQueryLoggingUtil.getDefaultLogger(), just call setLevel(Level.DEBUG) on the returned logger to see all messages (of course you can use other levels as well).
 +
* Each engine has it's own logger that is shared with the Base Index and the matchers as well. If you want to see all messages related to all engines, call IncQueryLoggingUtil.getLogger(IncQueryEngine.class) and set the level.
 +
* Some other classes also use their own loggers and the same approach is used, they get the loggers based on their class, so retrieving that logger and setting the level will work as well.
 +
 +
== Configuration problems ==
 +
 +
log4j uses a properties file as a configuration for its root logger. However, since this configuration is usually supplied by developers of applications, we do not package it in EMF-IncQuery.
 +
This means you may encounter the following on your console if no configuration was supplied:
 +
 +
log4j:WARN No appenders could be found for logger (org.eclipse.incquery.runtime.util.IncQueryLoggingUtil).
 +
log4j:WARN Please initialize the log4j system properly.
 +
 +
There are several cases where this can occur:
 +
* '''You have Xtext SDK installed''', which has a plugin fragment called org.eclipse.xtext.logging that supplies a log4j configuration. Make sure that the fragment is selected in your Runtime Configuration.
 +
* '''You are using the tooling of EMF-IncQuery without the Xtext SDK''', you will see the above warning, but since the patternlanguage.emf plugins also inject appenders to the loggers of EMF-IncQuery, log messages will be correctly displayed.
 +
* '''You are using only the runtime part of EMF-IncQuery''' that has no Xtext dependency. You have to provide your own properties file (standalone execution) or fragment (OSGi execution), see http://www.eclipsezone.com/eclipse/forums/t99588.html
 +
* Alternatively, if you just want to make sure that log messages appear in the console no matter what other configuration happens, you can call <code>IncQueryLoggingUtil.setupConsoleAppenderForDefaultLogger()</code> which will do exactly what its name says. Since appenders and log levels are separate, you will still have to set the log level on the loggers you want to see messages from.
 +
* If you wish to completely turn the logger of, call <code>IncQueryLoggingUtil.getDefaultLogger().setLevel(Level.OFF);</code>.
 +
 +
= Using Filtered Input Models During Pattern Matching =
 +
 +
In several cases it is beneficial to not include all Resources from a ResourceSet during pattern matching, but consider more than one. Such cases might include Xtext/Xbase languages or JaMoPP[http://www.jamopp.org/index.php/JaMoPP]-based instances that include resources representing the classes of the Java library.
 +
 +
EMF-IncQuery includes some options to filter out some subtrees from being indexed both by the Base Indexer and the Rete networks, by providing a filter implementation to the IncQuery Engine. These options include the IBaseIndexResourceFilter and IBaseIndexObjectFilter instances that can be used to filter out entire resources or containment subtrees, respectively.
 +
 +
Sample usage (by filtering out Java classes referred by JaMoPP):
 +
 +
<source lang="java">
 +
ResourceSet resourceSet = ...; //Use a Resource Set as the root of the engine
 +
BaseIndexOptions options = new BaseIndexOptions();
 +
options.setResourceFilterConfiguration(new IBaseIndexResourceFilter() {
 +
 +
  @Override
 +
  public boolean isResourceFiltered(Resource resource) {
 +
    // PathMap URI scheme is used to refer to JDK classes
 +
    return "pathmap".equals(resource.getURI().scheme());
 +
  }
 +
});
 +
//Initializing engine with custom options
 +
AdvancedIncQueryEngine engine = AdvancedIncQueryEngine.createUnmanagedEngine(resourceSet, options);
 +
</source>
 +
 +
'''Important!''' there are some issues to be considered while using this API:
 +
* If a Resource or containment subtree is filtered out, it is filtered out entirely. It is not possible to re-add some lower-level contents.
 +
* There is a known issue with result matches that are only partially in the filtered area. Be careful with queried edges that are connecting filtered and non-filtered elements. See {{bug|398907}} for details.

Revision as of 07:02, 26 January 2015

Overview

This page overviews advanced use-cases for EMF-IncQuery. The topics cover

  • the Generic API, to be used when the features supported by the generated API do not suffice (e.g. when working with dynamically defined patterns, or patterns whose handles are not known at compile time)
  • Advanced change processing APIs, and advanced lifecycle management techniques to be used for performance-critical and/or resource-constrained applications
  • IncQuery Base, the low-level query and indexer layer underneath EMF-IncQuery pattern matchers
  • logging in EMF-IncQuery, which is provided by hierarchic Log4J loggers.

Running example

All the code examples and explanations will be given in the context of the Headless example. The up-to-date sample source code to this page is found in Git here: http://git.eclipse.org/c/incquery/org.eclipse.incquery.examples.git/tree/headless Most notably,

Javadoc

The most up-to-date Javadocs for the EMF-IncQuery API can be found online at http://eclipse.org/incquery/javadoc/

The IncQuery Generic API

The "generic" API differs from the generated one in two key aspects:

  • it can be used to apply queries and use other IncQuery features without generating code and loading the resulting bundles into the running configuration. In other words, you just need to supply the EMF-based in-memory representation (an instance of the Pattern class)
  • the generic API is not "type safe" in the sense that the Java types of your pattern variables is not known and needs to be handled dynamically (e.g. by instanceof - typecase combos).

Initializing matchers and accessing results

In EMF-IncQuery 0.8 the generic API has changed significantly. Most importantly, it has been moved into the patternlanguage.emf plug-in, and there is no UI dependency required anymore. For more details, see the migration guide; for the old version of this content see EMFIncQuery/UserDocumentation/API/Advanced/Archive

Sample code

Using the Generic API:

public String executeDemo_GenericAPI_LoadFromEIQ(String modelPath, String patternFQN) {
  final StringBuilder results = new StringBuilder();
  Resource resource = loadModel(modelPath);
  if (resource != null) {
    try {
      // get all matches of the pattern
      // create an *unmanaged* engine to ensure that noone else is going
      // to use our engine
      AdvancedIncQueryEngine engine = AdvancedIncQueryEngine.createUnmanagedEngine(resource);
      // instantiate a pattern matcher through the registry, by only knowing its FQN
      // assuming that there is a pattern definition registered matching 'patternFQN'
 
      Pattern p = null;
 
      // Initializing Xtext-based resource parser
      // Do not use if EMF-IncQuery tooling is loaded!
      new EMFPatternLanguageStandaloneSetup().createInjectorAndDoEMFRegistration();
 
      //Loading pattern resource from file
      ResourceSet resourceSet = new ResourceSetImpl();
      URI fileURI = URI.createPlatformPluginURI("headlessQueries.incquery/src/headless/headlessQueries.eiq", false);
      Resource patternResource = resourceSet.getResource(fileURI, true);
 
      // navigate to the pattern definition that we want
      if (patternResource != null) {
        if (patternResource.getErrors().size() == 0 && patternResource.getContents().size() >= 1) {
          EObject topElement = patternResource.getContents().get(0);
          if (topElement instanceof PatternModel) {
            for (Pattern _p  : ((PatternModel) topElement).getPatterns()) {
              if (patternFQN.equals(CorePatternLanguageHelper.getFullyQualifiedName(_p))) {
                p = _p; break;
              }
            }
          }
        }
      }
      if (p == null) {
        throw new RuntimeException(String.format("Pattern %s not found", patternFQN));
      }
      // A specification builder is used to translate patterns to query specifications
      SpecificationBuilder builder = new SpecificationBuilder();
 
      // attempt to retrieve a registered query specification		    
      IncQueryMatcher<? extends IPatternMatch> matcher = engine.getMatcher(builder.getOrCreateSpecification(p));
 
      if (matcher!=null) {
        Collection<? extends IPatternMatch> matches = matcher.getAllMatches();
        prettyPrintMatches(results, matches);
      }
 
      // wipe the engine
      engine.wipe();
      // after a wipe, new patterns can be rebuilt with much less overhead than 
      // complete traversal (as the base indexes will be kept)
 
      // completely dispose of the engine once's it is not needed
      engine.dispose();
      resource.unload();
    } catch (IncQueryException e) {
      e.printStackTrace();
      results.append(e.getMessage());
    }
  } else {
    results.append("Resource not found");
  }
  return results.toString();
}


API interfaces

  • IPatternMatch Javadoc
    • reflection: pattern(), patternName()
    • getters and setters
    • utility functions (toArray, prettyPrint)
  • IncQueryMatcher Javadoc
    • reflection
    • get all matches
    • get single/arbitrary match
    • check for a match
    • number of matches
    • process matches
    • access change processing features
    • create a new Match for input binding
    • access projected value sets

The Pattern Registry and Matcher Factories

TODO The IMatcherFactory interface Javadoc

  • reflection
  • initialize the matcher


Advanced query result set change processing

Match update callbacks

private void changeProcessing_lowlevel(final StringBuilder results, IncQueryMatcher<? extends IPatternMatch> matcher) {
 // (+) these update callbacks are called whenever there is an actual change in the
 // result set of the pattern you are interested in. Hence, they are called fewer times
 // than the "afterUpdates" option, giving better performance.
 // (-)  the downside is that the callbacks are *not* guaranteed to be called in a consistent
 // state (i.e. when the update propagation is settled), hence
 //  * you must not invoke pattern matching and model manipulation _inside_ the callback method
 //  * the callbacks might encounter "hazards", i.e. when an appearance is followed immediately by a disappearance.
 engine.addMatchUpdateListener(matcher, new IMatchUpdateListener<IPatternMatch>() {
  @Override
  public void notifyDisappearance(IPatternMatch match) {
  // left empty
  }
  @Override
  public void notifyAppearance(IPatternMatch match) {
   results.append("\tNew match found by changeset low level callback: " + match.prettyPrint()+"\n");
  }
 }, false);
}

Using the EVM

The Event-driven VM can also be used for this purpose. See the section called "Efficiently reacting to pattern match set changes".

Advanced IncQuery Lifecycle management

Managed vs. unmanaged IncQueryEngines

Disposing

If you want to remove the matchers from the engine you can call the wipe() method on it. It discards any pattern matcher caches and forgets the known patterns. The base index built directly on the underlying EMF model, however, is kept in memory to allow reuse when new pattern matchers are built. If you don’t want to use it anymore call the dispose() instead, to completely disconnect and dismantle the engine.

  • wipe
  • dispose

IncQuery Base

EMF-IncQuery provides a light-weight indexer library called Base that aims to provide several useful (some would even argue critical) features for querying EMF models:

  • inverse navigation along EReferences
  • finding and incrementally tracking all model elements by attribute value/type (i.e. inverse navigation along EAttributes)
  • incrementally computing transitive reachability along given reference types (i.e. transitive closure of an EMF model)
  • getting and tracking all the (direct) instances of a given EClass

The point of IncQuery Base is to provide all of these in an incremental way, which means that once the query evaluator is attached to an EMF model, as long as it stays attached, the query results can be retrieved instantly (as the query result cache is automatically updated). IncQuery Base is a lightweight, small Java library that can be integrated easily to any EMF-based tool as it can be used in a stand-alone way, without the rest of EMF-IncQuery.

We are aware that some of the functionality can be found in some Ecore utility classes (for example ECrossReferenceAdapter). These standard implementations are non-incremental, and are thus do not scale well in scenarios where high query evaluation performance is necessary (such as e.g. on-the-fly well-formedness validation or live view maintenance). IncQuery Base has an additional important feature that is not present elsewhere: it contains very efficient implementations of transitive closure that can be used e.g. to maintain reachability regions incrementally, in very large EMF instance models.

The detailed documentation is covered in EMFIncQuery/UserDocumentation/API/BaseIndexer

Extracting reachability paths from transitive closure

TODO

Logging in EMF-IncQuery

EMF-IncQuery logs error messages and some trace information using log4j. If you need to debug your application and would like to see these messages, you can set the log level in different hierarchy levels. Since we use standard log4j, you can configure logging both with configuration files or through API calls.

  • All loggers are children of a top-level default logger, that can be accessed from IncQueryLoggingUtil.getDefaultLogger(), just call setLevel(Level.DEBUG) on the returned logger to see all messages (of course you can use other levels as well).
  • Each engine has it's own logger that is shared with the Base Index and the matchers as well. If you want to see all messages related to all engines, call IncQueryLoggingUtil.getLogger(IncQueryEngine.class) and set the level.
  • Some other classes also use their own loggers and the same approach is used, they get the loggers based on their class, so retrieving that logger and setting the level will work as well.

Configuration problems

log4j uses a properties file as a configuration for its root logger. However, since this configuration is usually supplied by developers of applications, we do not package it in EMF-IncQuery. This means you may encounter the following on your console if no configuration was supplied:

log4j:WARN No appenders could be found for logger (org.eclipse.incquery.runtime.util.IncQueryLoggingUtil).
log4j:WARN Please initialize the log4j system properly.

There are several cases where this can occur:

  • You have Xtext SDK installed, which has a plugin fragment called org.eclipse.xtext.logging that supplies a log4j configuration. Make sure that the fragment is selected in your Runtime Configuration.
  • You are using the tooling of EMF-IncQuery without the Xtext SDK, you will see the above warning, but since the patternlanguage.emf plugins also inject appenders to the loggers of EMF-IncQuery, log messages will be correctly displayed.
  • You are using only the runtime part of EMF-IncQuery that has no Xtext dependency. You have to provide your own properties file (standalone execution) or fragment (OSGi execution), see http://www.eclipsezone.com/eclipse/forums/t99588.html
  • Alternatively, if you just want to make sure that log messages appear in the console no matter what other configuration happens, you can call IncQueryLoggingUtil.setupConsoleAppenderForDefaultLogger() which will do exactly what its name says. Since appenders and log levels are separate, you will still have to set the log level on the loggers you want to see messages from.
  • If you wish to completely turn the logger of, call IncQueryLoggingUtil.getDefaultLogger().setLevel(Level.OFF);.

Using Filtered Input Models During Pattern Matching

In several cases it is beneficial to not include all Resources from a ResourceSet during pattern matching, but consider more than one. Such cases might include Xtext/Xbase languages or JaMoPP[1]-based instances that include resources representing the classes of the Java library.

EMF-IncQuery includes some options to filter out some subtrees from being indexed both by the Base Indexer and the Rete networks, by providing a filter implementation to the IncQuery Engine. These options include the IBaseIndexResourceFilter and IBaseIndexObjectFilter instances that can be used to filter out entire resources or containment subtrees, respectively.

Sample usage (by filtering out Java classes referred by JaMoPP):

ResourceSet resourceSet = ...; //Use a Resource Set as the root of the engine 
BaseIndexOptions options = new BaseIndexOptions();
options.setResourceFilterConfiguration(new IBaseIndexResourceFilter() {
 
  @Override
  public boolean isResourceFiltered(Resource resource) {
    // PathMap URI scheme is used to refer to JDK classes
    return "pathmap".equals(resource.getURI().scheme());
  }
});
//Initializing engine with custom options
AdvancedIncQueryEngine engine = AdvancedIncQueryEngine.createUnmanagedEngine(resourceSet, options);

Important! there are some issues to be considered while using this API:

  • If a Resource or containment subtree is filtered out, it is filtered out entirely. It is not possible to re-add some lower-level contents.
  • There is a known issue with result matches that are only partially in the filtered area. Be careful with queried edges that are connecting filtered and non-filtered elements. See bug 398907 for details.

Back to the top