The Java VTL project is an open source java implementation of the VTL 1.1 draft specification. It follows the JSR-223 Java Scripting API and exposes a simple connector interface one can implement in order to integrate with any data stores.
Visit the interactive reference manual for more information.
The project is divided in modules;
- java-vtl-parent
- java-vtl-parser, contains the lexer and parser for VTL.
- java-vtl-model, VTL data model.
- java-vtl-script, JSR-223 (ScriptEngine) implementation.
- java-vtl-connector, connector API.
- java-vtl-tools, various tools.
Add a dependency to the maven project
<dependency>
<groupId>no.ssb.vtl</groupId>
<artifactId>java-vtl-script</artifactId>
<version>[VERSION]</version>
</dependency>
ScriptEngine engine = new VTLScriptEngine(connector);
Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
engine.eval("ds1 := get(\"foo\")" +
"ds2 := get(\"bar\")" +
"ds3 := [ds1, ds2] {" +
" filter ds1.id = \"string\"," +
" total := ds1.measure + ds2.measure" +
"}");
System.out.println(bindings.get("ds3"))
VTL Java uses the no.ssb.vtl.connector.Connector
interface to access and
export data from and to external systems.
The Connector interface defines three methods:
public interface Connector {
boolean canHandle(String identifier);
Dataset getDataset(String identifier) throws ConnectorException;
Dataset putDataset(String identifier, Dataset dataset) throws ConnectorException;
}
The method canHandle(String identifier)
is used by the engine to find
which connector is able to provide a Dataset for a given identifier.
The method getDataset(String identifier)
is then called to get the dataset.
Example implementations can be found in the java-vtl-ssb-api-connector
module
but a very crude implementation could be as such:
class StaticDataset implements Dataset {
private final DataStructure structure = DataStructure.builder()
.put("id", Role.IDENTIFIER, String.class)
.put("period", Role.IDENTIFIER, Instant.class)
.put("measure", Role.MEASURE, Long.class)
.put("attribute", Role.ATTRIBUTE, String.class)
.build();
@Override
public Stream<DataPoint> getData() {
List<Map<String, Object>> data = new ArrayList<>();
HashMap<String, Object> row = new HashMap<>();
Instant period = Instant.now();
for (int i = 0; i < 100; i++) {
row.put("id", "id #" + i);
row.put("period", period);
row.put("measure", Long.valueOf(i));
row.put("attribute", "attribute #" + i);
data.add(row);
}
return data.stream().map(structure::wrap);
}
@Override
public Optional<Map<String, Integer>> getDistinctValuesCount() {
return Optional.empty();
}
@Override
public Optional<Long> getSize() {
return Optional.of(100L);
}
@Override
public DataStructure getDataStructure() {
return structure;
}
}
This is an overview of the implementation progress.