Workspace lock file generation

sidewalk-01
Edward M. Kagan 5 years ago
parent 5b9a60b0f1
commit 3ed3853017

@ -1,54 +1,17 @@
package link.pagan.traqtor; package link.pagan.traqtor;
import com.fasterxml.jackson.core.JsonProcessingException; import link.pagan.traqtor.deck.IoMaster;
import com.fasterxml.jackson.databind.ObjectMapper; import link.pagan.traqtor.deck.JsonMaster;
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;
/** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */ /** @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(); public static JsonMaster json () {
return JsonMaster.instance();
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 () { public static IoMaster io () {
return PROJECT_IO; return IoMaster.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;
} }
} }

@ -1,16 +1,33 @@
package link.pagan.traqtor; package link.pagan.traqtor;
import java.io.File;
import link.pagan.traqtor.project.Project; import link.pagan.traqtor.project.Project;
import link.pagan.traqtor.util.Name; import link.pagan.traqtor.util.Name;
/** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */ /** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */
class Workspace { public class Workspace {
private File root;
private Name name;
private boolean dirty;
public Workspace () {
this.root = null;
this.dirty = true;
}
public Name name () {
return this.name;
}
public Workspace name (String... parts) { public Workspace name (String... parts) {
return name(Name.of(parts)); return name(Name.of(parts));
} }
public Workspace name (Name name) { public Workspace name (Name name) {
this.name = name;
return this; return this;
} }
@ -18,4 +35,21 @@ class Workspace {
return this; return this;
} }
public File root () {
return root;
}
public Workspace root (File root) {
this.root = root;
return this;
}
public void dirty (boolean dirty) {
this.dirty = dirty;
}
public boolean dirty () {
return dirty;
}
} }

@ -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;
}
}

@ -14,6 +14,17 @@ public class Name implements Comparable<Name> {
return new Name(parts); return new Name(parts);
} }
public String asTiled () {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < this.parts.length; i++) {
sb.append(parts[i]);
if (i != this.parts.length - 1) { sb.append('-'); }
}
return sb.toString();
}
public String asDotted () { public String asDotted () {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();

@ -155,8 +155,6 @@ public class EndToEndTest {
.schemas(LITERAL) .schemas(LITERAL)
.elements(organization); .elements(organization);
System.out.println(Traqtor.io().toFileHandle(baseUniverse).content());
System.out.println(Traqtor.io().toFileHandle(dataUniverse).content());
// try { // try {
// //
// String writeValueAsString = mapper.writeValueAsString(dataUniverse); // String writeValueAsString = mapper.writeValueAsString(dataUniverse);
@ -167,12 +165,16 @@ public class EndToEndTest {
// // .name("public") // // .name("public")
// // .tables(Database.table() // // .tables(Database.table()
// // .columns())); // // .columns()));
// // new Workspace() Workspace workspace = new Workspace()
// // .name("link", "pagan") .name("link", "pagan")
// // .projects(baseUniverse, dataUniverse, mainDatabase); .projects(baseUniverse, dataUniverse);
// } catch (JsonProcessingException ex) { // } catch (JsonProcessingException ex) {
// Logger.getLogger(EndToEndTest.class.getName()).log(Level.SEVERE, null, ex); // Logger.getLogger(EndToEndTest.class.getName()).log(Level.SEVERE, null, ex);
// } // }
Traqtor.io().save(workspace);
// System.out.println(Traqtor.io().toFileHandle(baseUniverse).content());
// System.out.println(Traqtor.io().toFileHandle(dataUniverse).content());
} }
} }

@ -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…
Cancel
Save