Skip to content

Commit

Permalink
[WAYANG-32] Base structure for Wayang Experiments Storage functionali…
Browse files Browse the repository at this point in the history
…ties
  • Loading branch information
ro-pardo committed Aug 30, 2021
1 parent d4d9c24 commit 5344336
Show file tree
Hide file tree
Showing 17 changed files with 1,343 additions and 0 deletions.
20 changes: 20 additions & 0 deletions wayang-commons/wayang-utils/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>wayang-commons</artifactId>
<groupId>org.apache.wayang</groupId>
<version>0.6.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>wayang-utils</artifactId>
<packaging>pom</packaging>

<modules>
<module>wayang-profile-db</module>
</modules>


</project>
24 changes: 24 additions & 0 deletions wayang-commons/wayang-utils/wayang-profile-db/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>wayang-utils</artifactId>
<groupId>org.apache.wayang</groupId>
<version>0.6.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>wayang-profile-db</artifactId>

<dependencies>
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>

</dependencies>


</project>
3 changes: 3 additions & 0 deletions wayang-commons/wayang-utils/wayang-profile-db/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Base on

https://github.com/sekruse/profiledb-java.git
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package profiledb;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import profiledb.json.MeasurementDeserializer;
import profiledb.json.MeasurementSerializer;
import profiledb.model.Experiment;
import profiledb.model.Measurement;
import profiledb.storage.Storage;

import java.io.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;

/**
* This class provides facilities to save and load {@link Experiment}s.
*/
public class ProfileDB {

/**
* Maintains the full list of {@link Class}es for {@link Measurement}s. Which are required for deserialization.
*/
private List<Class<? extends Measurement>> measurementClasses = new LinkedList<>();

/**
* Controls how conducted experiments will be persisted and loaded
*/
private Storage storage;

/**
* Maintains actions to preparate {@link Gson}.
*/
private List<Consumer<GsonBuilder>> gsonPreparationSteps = new LinkedList<>();

/**
* Maintains a {@link Gson} object for efficiency. It will be dropped on changes, though.
*/
private Gson gson;

/**
* Creates a new instance.
*/
public ProfileDB(Storage storage) {

this.storage = storage;
this.storage.setContext(this);
//this.measurementClasses.add(TimeMeasurement.class);
}

/**
* To work with storage object provided to persist or load experiments
*
* @return Storage object proportioned for this instance
*/
public Storage getStorage() {
return storage;
}

/**
* Register a {@link Measurement} type. This is required before being able to load that type.
*
* @param measurementClass the {@link Measurement} {@link Class}
* @return this instance
*/
public ProfileDB registerMeasurementClass(Class<? extends Measurement> measurementClass) {
this.measurementClasses.add(measurementClass);
this.gson = null;
return this;
}

/**
* Apply any changes necessary to {@link Gson} so that it can be used for de/serialization of custom objects.
*
* @param preparation a preparatory step performed on a {@link GsonBuilder}
* @return this instance
*/
public ProfileDB withGsonPreparation(Consumer<GsonBuilder> preparation) {
this.gsonPreparationSteps.add(preparation);
this.gson = null;
return this;
}

/**
* Provide a {@link Gson} object.
*
* @return the {@link Gson} object
*/
public Gson getGson() {
if (this.gson == null) {
MeasurementSerializer measurementSerializer = new MeasurementSerializer();
MeasurementDeserializer measurementDeserializer = new MeasurementDeserializer();
this.measurementClasses.forEach(measurementDeserializer::register);
final GsonBuilder gsonBuilder = new GsonBuilder()
.registerTypeAdapter(Measurement.class, measurementDeserializer)
.registerTypeAdapter(Measurement.class, measurementSerializer);
this.gsonPreparationSteps.forEach(step -> step.accept(gsonBuilder));
this.gson = gsonBuilder.create();
}
return this.gson;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package profiledb.json;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import profiledb.model.Measurement;

import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

/**
* Custom deserializer for {@link Measurement}s
* Detects actual subclass of serialized instances and then delegates the deserialization to that subtype.
*/
public class MeasurementDeserializer implements JsonDeserializer<Measurement> {

private final Map<String, Class<? extends Measurement>> measurementTypes = new HashMap<>();

public void register(Class<? extends Measurement> measurementClass) {
String typeName = Measurement.getTypeName(measurementClass);
this.measurementTypes.put(typeName, measurementClass);
}

@Override
public Measurement deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
final JsonElement typeElement = jsonElement.getAsJsonObject().get("type");
if (typeElement == null) {
throw new IllegalArgumentException("Missing type in " + jsonElement);
}
final String typeName = typeElement.getAsString();
final Class<? extends Measurement> measurementClass = this.measurementTypes.get(typeName);
if (measurementClass == null) {
throw new JsonParseException("Unknown measurement type: " + typeName);
}
return jsonDeserializationContext.deserialize(jsonElement, measurementClass);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package profiledb.json;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import profiledb.model.Measurement;

import java.lang.reflect.Type;

/**
* Custom serializer for {@link Measurement}s
* Detects actual subclass of given instances, encodes this class membership, and then delegates serialization to that subtype.
*/
public class MeasurementSerializer implements JsonSerializer<Measurement> {

@Override
public JsonElement serialize(Measurement measurement, Type type, JsonSerializationContext jsonSerializationContext) {
final JsonObject jsonObject = (JsonObject) jsonSerializationContext.serialize(measurement);
jsonObject.addProperty("type", measurement.getType());
return jsonObject;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package profiledb.model;

import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Objects;

/**
* An experiment comprises {@link Measurement}s from one specific {@link Subject} execution.
*/
public class Experiment {

/**
* Identifier for this instance.
*/
private String id;

/**
* Description for this instance. (Optional)
*/
private String description;

/**
* When this experiment has been started.
*/
private long startTime;

/**
* Tags to group multiple Experiment instances. (Optional)
*/
private Collection<String> tags;

/**
* {@link Measurement}s captured for this instance.
*/
private Collection<Measurement> measurements;

/**
* The {@link Subject} being experimented with.
*/
private Subject subject;

/**
* For deserialization.
*/
private Experiment() {
}

/**
* Create a new instance that is starting right now.
*
* @param id Identifier for the new instance
* @param subject the {@link Subject}
* @param tags tags to group several experiments
*/
public Experiment(String id, Subject subject, String... tags) {
this(id, subject, System.currentTimeMillis(), tags);
}

/**
* Create a new instance.
*
* @param id Identifier for the new instance
* @param subject the {@link Subject} of this experiment
* @param startTime start timestamp of this experiment
* @param tags tags to group several experiments
*/
public Experiment(String id, Subject subject, long startTime, String... tags) {
this.id = id;
this.subject = subject;
this.startTime = startTime;
this.tags = Arrays.asList(tags);
this.measurements = new LinkedList<>();
}

/**
* Adds a description for this instance.
*
* @param description the description
* @return this instance
*/
public Experiment withDescription(String description) {
this.description = description;
return this;
}


public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public long getStartTime() {
return startTime;
}

public void setStartTime(long startTime) {
this.startTime = startTime;
}

public Collection<String> getTags() {
return tags;
}

public void setTags(Collection<String> tags) {
this.tags = tags;
}

public void addMeasurement(Measurement measurement) {
this.measurements.add(measurement);
}

public Collection<Measurement> getMeasurements() {
return measurements;
}

public Subject getSubject() {
return this.subject;
}

public void setSubject(Subject subject) {
this.subject = subject;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Experiment that = (Experiment) o;
return startTime == that.startTime &&
Objects.equals(id, that.id) &&
Objects.equals(description, that.description) &&
Objects.equals(tags, that.tags) &&
Objects.equals(subject, that.subject);
}

@Override
public int hashCode() {
return Objects.hash(id, startTime);
}

@Override
public String toString() {
return String.format(
"%s[%s, %d tags, %d measurements]",
this.getClass().getSimpleName(),
this.id,
this.tags.size(),
this.measurements.size()
);
}
}
Loading

0 comments on commit 5344336

Please sign in to comment.