forked from 2pm.tech/traqtor
Workspace lock file generation
parent
5b9a60b0f1
commit
3ed3853017
@ -1,54 +1,17 @@
|
||||
package link.pagan.traqtor;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import link.pagan.traqtor.util.FileHandle;
|
||||
import link.pagan.traqtor.project.Project;
|
||||
import link.pagan.traqtor.project.universe.UniverseProject;
|
||||
import link.pagan.traqtor.project.universe.UniverseProjectSerializer;
|
||||
import link.pagan.traqtor.project.universe.element.Element;
|
||||
import link.pagan.traqtor.project.universe.element.ElementSerializer;
|
||||
import link.pagan.traqtor.project.universe.element.Particle;
|
||||
import link.pagan.traqtor.project.universe.element.PaticleInfoSerializer;
|
||||
import link.pagan.traqtor.project.universe.link.Link;
|
||||
import link.pagan.traqtor.project.universe.link.LinkSerializer;
|
||||
import link.pagan.traqtor.project.universe.schema.Constraint;
|
||||
import link.pagan.traqtor.project.universe.schema.ConstraintInfoSerializer;
|
||||
import link.pagan.traqtor.util.Name;
|
||||
import link.pagan.traqtor.util.NameSerializer;
|
||||
import link.pagan.traqtor.deck.IoMaster;
|
||||
import link.pagan.traqtor.deck.JsonMaster;
|
||||
|
||||
/** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */
|
||||
public class Traqtor {
|
||||
public abstract class Traqtor {
|
||||
|
||||
private static final Traqtor PROJECT_IO = new Traqtor();
|
||||
|
||||
private final ObjectMapper mapper;
|
||||
|
||||
private Traqtor () {
|
||||
mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
|
||||
SimpleModule module = new SimpleModule();
|
||||
module.addSerializer(Name.class, new NameSerializer());
|
||||
module.addSerializer(UniverseProject.class, new UniverseProjectSerializer());
|
||||
module.addSerializer(Element.class, new ElementSerializer());
|
||||
module.addSerializer(Particle.class, new PaticleInfoSerializer());
|
||||
module.addSerializer(Constraint.class, new ConstraintInfoSerializer());
|
||||
module.addSerializer(Link.class, new LinkSerializer());
|
||||
mapper.registerModule(module);
|
||||
}
|
||||
|
||||
public static Traqtor io () {
|
||||
return PROJECT_IO;
|
||||
public static JsonMaster json () {
|
||||
return JsonMaster.instance();
|
||||
}
|
||||
|
||||
public FileHandle toFileHandle (Project project) throws JsonProcessingException {
|
||||
|
||||
if (project.getClass().equals(UniverseProject.class)) {
|
||||
String json = mapper.writeValueAsString((UniverseProject) project);
|
||||
return new FileHandle(project.name().asDotted(), "uni", json);
|
||||
}
|
||||
return null;
|
||||
public static IoMaster io () {
|
||||
return IoMaster.instance();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
package link.pagan.traqtor;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
import java.io.IOException;
|
||||
|
||||
/** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */
|
||||
public class WorkspaceSerializer extends StdSerializer<Workspace> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public WorkspaceSerializer () {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public WorkspaceSerializer (Class<Workspace> t) {
|
||||
super(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize (Workspace value, JsonGenerator gen, SerializerProvider provider) throws IOException {
|
||||
gen.writeStartObject();
|
||||
gen.writeObjectField("name", value.name());
|
||||
gen.writeEndObject();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,112 @@
|
||||
package link.pagan.traqtor.deck;
|
||||
|
||||
import java.io.File;
|
||||
import link.pagan.traqtor.Traqtor;
|
||||
import link.pagan.traqtor.deck.op.OperationResult;
|
||||
import link.pagan.traqtor.Workspace;
|
||||
|
||||
/** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */
|
||||
public class IoMaster {
|
||||
|
||||
private static final IoMaster INSTANCE = new IoMaster();
|
||||
|
||||
private IoMaster () {}
|
||||
|
||||
public static IoMaster instance () {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public OperationResult saveAs (Workspace workspace, File parent) {
|
||||
return saveAs(workspace, parent, OperationResult.start());
|
||||
}
|
||||
|
||||
public OperationResult saveAs (Workspace workspace, File parent, OperationResult result) {
|
||||
|
||||
if (parent == null) {
|
||||
return result
|
||||
.info("Target directory must be set, are you OK?")
|
||||
.fail("Failed to save workspace, no new root directory was passed");
|
||||
}
|
||||
|
||||
if (!parent.exists()) {
|
||||
return result
|
||||
.info("Directory " + parent.getAbsolutePath() + " does not exist, deleted/moved/unmounted?")
|
||||
.fail("Failed to save workspace, due non existent parent direcory for new workspace");
|
||||
}
|
||||
|
||||
if (workspace.name() == null) {
|
||||
return result
|
||||
.info("Workspace does not contain name - how did you even achieve that?")
|
||||
.fail("Failed to save workspace, due unknown name of it, huh...");
|
||||
}
|
||||
|
||||
File root = new File(parent, workspace.name().asTiled());
|
||||
|
||||
if (!root.exists()) {
|
||||
result.info("Creating new root direcory at " + root.getAbsolutePath());
|
||||
|
||||
if (!root.mkdirs()) {
|
||||
return result
|
||||
.info("Unable to create directory " + root.getAbsolutePath() + " - permission problem?")
|
||||
.fail("Failed to save workspace, can not create new root directory");
|
||||
}
|
||||
}
|
||||
|
||||
if (root.listFiles().length > 0) {
|
||||
return result
|
||||
.info("Directory " + root.getAbsolutePath() + " is not empty, not safe to save?")
|
||||
.fail("Failed to save workspace, due to polluted new root direcory");
|
||||
}
|
||||
File oldRoot = workspace.root();
|
||||
workspace.root(root);
|
||||
result.info("Setting workspace root to " + root.getAbsolutePath());
|
||||
save(workspace, result.startSubresult());
|
||||
|
||||
if (!result.ok()) {
|
||||
result.info("Reverting workspace root to " + oldRoot.getAbsolutePath());
|
||||
workspace.root(oldRoot);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public OperationResult save (Workspace workspace) {
|
||||
return save(workspace, OperationResult.start());
|
||||
}
|
||||
|
||||
public OperationResult save (Workspace workspace, OperationResult result) {
|
||||
result.info("Saving workspace " + workspace.name().asTiled() + " ...");
|
||||
|
||||
if (workspace.root() == null) {
|
||||
return result
|
||||
.info("Workspace root is not set - is this a new workspace?")
|
||||
.fail("Failed to save workspace, due to unknown root directory");
|
||||
}
|
||||
|
||||
if (!workspace.root().exists()) {
|
||||
return result
|
||||
.info("Directory " + workspace.root().getAbsolutePath() +
|
||||
" does not exist, deleted/moved/unmounted?")
|
||||
.fail("Failed to save workspace, due non existent root direcory");
|
||||
}
|
||||
|
||||
if (!workspace.root().canRead()) {
|
||||
return result
|
||||
.info("Unable to read " + workspace.root().getAbsolutePath() + " - permission problem?")
|
||||
.fail("Failed to save workspace, due unreadable root direcory");
|
||||
}
|
||||
|
||||
if (!workspace.root().canWrite()) {
|
||||
return result
|
||||
.info("Unable to write " + workspace.root().getAbsolutePath() + " - permission problem?")
|
||||
.fail("Failed to save workspace, due write restriction on root direcory");
|
||||
|
||||
}
|
||||
|
||||
result.addSubresult(Traqtor.json().save(workspace));
|
||||
|
||||
if (result.ok()) { workspace.dirty(false); }
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
package link.pagan.traqtor.deck;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import link.pagan.traqtor.Workspace;
|
||||
import link.pagan.traqtor.WorkspaceSerializer;
|
||||
import link.pagan.traqtor.deck.op.OperationResult;
|
||||
import link.pagan.traqtor.project.universe.UniverseProject;
|
||||
import link.pagan.traqtor.project.universe.UniverseProjectSerializer;
|
||||
import link.pagan.traqtor.project.universe.element.Element;
|
||||
import link.pagan.traqtor.project.universe.element.ElementSerializer;
|
||||
import link.pagan.traqtor.project.universe.element.Particle;
|
||||
import link.pagan.traqtor.project.universe.element.PaticleInfoSerializer;
|
||||
import link.pagan.traqtor.project.universe.link.Link;
|
||||
import link.pagan.traqtor.project.universe.link.LinkSerializer;
|
||||
import link.pagan.traqtor.project.universe.schema.Constraint;
|
||||
import link.pagan.traqtor.project.universe.schema.ConstraintInfoSerializer;
|
||||
import link.pagan.traqtor.util.Name;
|
||||
import link.pagan.traqtor.util.NameSerializer;
|
||||
|
||||
/** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */
|
||||
public class JsonMaster {
|
||||
|
||||
private static final JsonMaster INSTANCE = new JsonMaster();
|
||||
|
||||
private final ObjectMapper mapper;
|
||||
|
||||
public JsonMaster () {
|
||||
this.mapper = new ObjectMapper()
|
||||
.enable(SerializationFeature.INDENT_OUTPUT)
|
||||
.registerModule(new SimpleModule()
|
||||
.addSerializer(Name.class, new NameSerializer())
|
||||
.addSerializer(UniverseProject.class, new UniverseProjectSerializer())
|
||||
.addSerializer(Element.class, new ElementSerializer())
|
||||
.addSerializer(Particle.class, new PaticleInfoSerializer())
|
||||
.addSerializer(Constraint.class, new ConstraintInfoSerializer())
|
||||
.addSerializer(Link.class, new LinkSerializer())
|
||||
.addSerializer(Workspace.class, new WorkspaceSerializer()));
|
||||
|
||||
}
|
||||
|
||||
public static JsonMaster instance () {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public OperationResult save (Workspace workspace) {
|
||||
OperationResult result = OperationResult.start();
|
||||
File workspaceDesciptorFile = new File(workspace.root(), "workspace.json");
|
||||
result.info("Writing workspace descriptor file to " + workspaceDesciptorFile.getAbsolutePath() + " ...");
|
||||
|
||||
try {
|
||||
mapper.writeValue(workspaceDesciptorFile, workspace);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(IoMaster.class.getName()).log(Level.SEVERE, null, ex);
|
||||
result.fail("Failed to write file " + workspaceDesciptorFile.getAbsolutePath() + " - IO exception");
|
||||
result.fail(ex);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package link.pagan.traqtor.deck.op;
|
||||
|
||||
/** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */
|
||||
public enum MessageType {
|
||||
INFO,
|
||||
WARN,
|
||||
FAIL
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
package link.pagan.traqtor.deck.op;
|
||||
|
||||
import java.text.Format;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */
|
||||
public class OperationMessage {
|
||||
|
||||
private static final Format TIMESTAMP_PRINTABLE_FORMAT = new SimpleDateFormat("HH:mm:ss.SSS");
|
||||
|
||||
private final String message;
|
||||
|
||||
private final MessageType type;
|
||||
|
||||
private final long timestamp;
|
||||
|
||||
private OperationMessage (String message, MessageType type) {
|
||||
this.message = message;
|
||||
this.type = type;
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
static OperationMessage warn (String message) {
|
||||
return new OperationMessage(message, MessageType.WARN);
|
||||
}
|
||||
|
||||
static OperationMessage info (String message) {
|
||||
return new OperationMessage(message, MessageType.INFO);
|
||||
}
|
||||
|
||||
static OperationMessage fail (String message) {
|
||||
return new OperationMessage(message, MessageType.FAIL);
|
||||
}
|
||||
|
||||
public String message () {
|
||||
return message;
|
||||
}
|
||||
|
||||
public MessageType type () {
|
||||
return type;
|
||||
}
|
||||
|
||||
public long timestamp () {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString () {
|
||||
return TIMESTAMP_PRINTABLE_FORMAT.format(new Date(this.timestamp)) + " [" +
|
||||
this.type.toString() +
|
||||
"] " +
|
||||
this.message;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
package link.pagan.traqtor.deck.op;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
/** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */
|
||||
public class OperationResult {
|
||||
|
||||
private final long timestamp;
|
||||
|
||||
private final LinkedList<OperationMessage> messages;
|
||||
|
||||
private final LinkedList<OperationResult> subresults;
|
||||
|
||||
private OperationResult () {
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
this.messages = new LinkedList<OperationMessage>();
|
||||
this.subresults = new LinkedList<OperationResult>();
|
||||
}
|
||||
|
||||
public OperationResult info (String message) {
|
||||
this.messages.add(OperationMessage.info(message));
|
||||
return this;
|
||||
}
|
||||
|
||||
public OperationResult warn (String message) {
|
||||
this.messages.add(OperationMessage.warn(message));
|
||||
return this;
|
||||
}
|
||||
|
||||
public OperationResult fail (Exception ex) {
|
||||
this.messages.add(OperationMessage.fail(ex.getMessage()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public OperationResult fail (String message) {
|
||||
this.messages.add(OperationMessage.fail(message));
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean ok () {
|
||||
boolean proxiedOK = proxiedOK();
|
||||
|
||||
if (!proxiedOK) { for (OperationMessage message : this.messages) { System.out.println(message.toString()); } }
|
||||
return proxiedOK;
|
||||
}
|
||||
|
||||
private boolean proxiedOK () {
|
||||
|
||||
if (this.messages.stream().anyMatch(message -> (message.type().equals(MessageType.FAIL)))) { return false; }
|
||||
|
||||
if (!this.subresults.stream().noneMatch(subresult -> (!subresult.ok()))) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
public long timestamp () {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public OperationMessage[] messages () {
|
||||
return this.messages.toArray(new OperationMessage[this.messages.size()]);
|
||||
}
|
||||
|
||||
public OperationResult[] subresults () {
|
||||
return this.subresults.toArray(new OperationResult[this.subresults.size()]);
|
||||
}
|
||||
|
||||
public static OperationResult start () {
|
||||
return new OperationResult();
|
||||
}
|
||||
|
||||
public OperationResult startSubresult () {
|
||||
OperationResult subresult = new OperationResult();
|
||||
this.subresults.add(subresult);
|
||||
return subresult;
|
||||
}
|
||||
|
||||
public OperationResult addSubresult (OperationResult subresult) {
|
||||
this.subresults.add(subresult);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package link.pagan.traqtor;
|
||||
|
||||
import java.io.IOException;
|
||||
import link.pagan.traqtor.test.TestUtils;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */
|
||||
public class IoTest {
|
||||
|
||||
@BeforeEach
|
||||
public void cleanTestDir () throws IOException {
|
||||
TestUtils.rebuildTestRoot();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void killCore () throws IOException {
|
||||
TestUtils.deleteTestRoot();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Workspace save")
|
||||
void workspaceSave () throws IOException {
|
||||
Workspace workspace = new Workspace().name("traqtor", "demo", "workspace");
|
||||
Assertions.assertTrue(!Traqtor.io().save(workspace).ok());
|
||||
TestUtils.deleteTestRoot();
|
||||
Assertions.assertTrue(!Traqtor.io().save(workspace.root(TestUtils.testRoot())).ok());
|
||||
TestUtils.rebuildTestRoot();
|
||||
Assertions.assertTrue(Traqtor.io().saveAs(workspace, TestUtils.testRoot()).ok());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package link.pagan.traqtor.test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Comparator;
|
||||
|
||||
/** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */
|
||||
public class TestUtils {
|
||||
|
||||
private static final File TEST_DIR = new File(new File("").getAbsoluteFile().getParentFile(), "traqtor-test-root");
|
||||
|
||||
public static final void rebuildTestRoot () throws IOException {
|
||||
deleteTestRoot();
|
||||
TEST_DIR.mkdirs();
|
||||
}
|
||||
|
||||
public static final void deleteTestRoot () throws IOException {
|
||||
|
||||
if (TEST_DIR.exists()) {
|
||||
Files.walk(TEST_DIR.toPath())
|
||||
.sorted(Comparator.reverseOrder())
|
||||
.map(Path::toFile)
|
||||
.forEach(File::delete);
|
||||
}
|
||||
}
|
||||
|
||||
public static File testRoot () {
|
||||
return TEST_DIR;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue