In this chapter we collect some coding hints and guidelines on how to properly use the APIs of Eclipse, EMF, Xtext and
other dependencies we are using, as well as our own utilities and helpers.
This chapter is only about coding; add information on things like Eclipse setup or Maven/Jenkins to one of the preceding
chapters. Similarly, this chapter is intended to provide just a quick overview, check-list and reminder; add detailed
information and diagrams to one of the succeeding chapters.
In many situations developer needs to use some kind of logging. When in need, follow these rules:
-
Use org.apache.log4j.Logger; for logging. Other logging utilities (like java built in logger) are not configured.
-
do not use System.out nor Sysetem.err for logging. It is ok to use it for debugging purposes, but those calls
should never be merged to master. (with exception of headless compiler, which uses them explicitly)
-
There is central logger configuration in org.eclipse.n4js.utils.logging (and com.enfore.n4js.utils.logging) that should
be used
-
log4j.xml used for production
-
log4j_tests.xml used when running tests
-
in Eclipse run configurations logger has to be set properly, e.g.
log4j.configuration=file:${workspace_loc:com.enfore.n4js.utils.logging/log4j_tests.xml}
-
in maven configurations logger has to be set separately, e.g.
-Dlog4j.configuration="file:${basedir}/../../plugins/com.enfore.n4js.utils.logging/log4j_tests.xml
At various occasions, Xtext provides an instance of class CancelIndicator to allow our code to handle cancellation of
long-running task.
Some things to keep in mind:
-
whenever a CancelIndicator is available any code that might not return immediately should implement proper
cancellation handling (as explained in the next items).
-
most importantly: reacting to a cancellation by returning early from a method is an anti-pattern that leads to
problems (client code might continue work on a canceled and thus invalid state); instead: throw an
OperationCanceledException!
-
don’t use CancelIndicator#isCanceled() for cancellation handling, except in certain special cases. A valid exception
case might be during logging to show a message like "operation was canceled".
-
instead, inject the Xtext service called OperationCanceledManager and invoke its method #checkCanceled(), passing-in
the cancel indicator (this method is null-safe; it will throw an OperationCanceledException in case a cancellation has
occurred). Don’t directly create and throw an OperationCanceledException yourself.
-
use the other methods provided by OperationCanceledManager when appropriate (see code of that class for details).
-
in try/catch blocks, when catching exceptions of a super type of OperationCanceledException, be sure to not suppress
cancellation exceptions. For example:
// Java code
@Inject private OperationCanceledManager operationCanceledManager;
/** Returns true on success, false otherwise. */
public boolean doSomething(CancelIndicator ci) {
try {
// do something that might be canceled
return true;
} catch(Exception e) {
operationCanceledManager.propagateIfCancelException(e); // <- IMPORTANT!
return false;
}
}
Try/finally blocks, on the other hand, do not need any special handling.
-
a cancel indicator can also be stored in the rule environment (see RuleEnvironmentExtensions#addCancelIndicator()). This
means:
-
if you create a rule environment completely from scratch and you have a cancel indicator at hand, add it to the rule
environment via RuleEnvironmentExtensions#addCancelIndicator() (not required when using RuleEnvironmentExtensions#wrap() for
deriving a rule environment from an existing one).
-
if you have a rule environment available, be sure to use its cancel indicator in long-running operations, i.e. with
code like:
// Xtend code
import static extension org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.*
class C {
@Inject private OperationCanceledManager operationCanceledManager;
def void doSomething() {
for(a : aLotOfStuff) {
operationCanceledManager.checkCanceled(G.cancelIndicator);
// main work ...
}
}
-
Resource load states: when an N4JS/N4JSD file is loaded, a certain sequence of processing is triggered (parsing,
linking, validation, etc.) and thus an N4JSResource transitions through a sequence of "load states". For details,
see N4JS Resource Load States.