Skip to content

Commit

Permalink
Added VariantGraphSVGMessageBodyWriter.
Browse files Browse the repository at this point in the history
  • Loading branch information
rhdekker committed May 6, 2015
1 parent d5c19ee commit c824726
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,46 @@
import eu.interedition.collatex.io.IOExceptionMapper;
import eu.interedition.collatex.io.SimpleCollationJSONMessageBodyReader;
import eu.interedition.collatex.io.VariantGraphJSONMessageBodyWriter;
import eu.interedition.collatex.io.VariantGraphSVGMessageBodyWriter;

import javax.ws.rs.core.Application;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Created by ronald on 5/3/15.
*/
public class CollateApplication extends Application {
private static final Logger LOG = Logger.getLogger(CollateApplication.class.getName());

private static String detectDotPath() {
for (String detectionCommand : new String[] { "which dot", "where dot.exe" }) {
try {

final Process process = Runtime.getRuntime().exec(detectionCommand);
try (BufferedReader processReader = new BufferedReader(new InputStreamReader(process.getInputStream(), Charset.defaultCharset()))) {
final CompletableFuture<Optional<String>> path = CompletableFuture.supplyAsync(() -> processReader.lines()
.map(String::trim)
.filter(l -> l.toLowerCase().contains("dot"))
.findFirst());
process.waitFor();
final String dotPath = path.get().get();
LOG.info(() -> "Detected GraphViz' dot at '" + dotPath + "'");
return dotPath;
}
} catch (Throwable t) {
LOG.log(Level.FINE, detectionCommand, t);
}
}
return null;
}

@Override
public Set<Class<?>> getClasses() {
Expand All @@ -26,6 +57,7 @@ public Set<Class<?>> getClasses() {
public Set<Object> getSingletons() {
Set<Object> singletons = new HashSet<>();
singletons.add(new CollateResource("", 10, 0));
singletons.add(new VariantGraphSVGMessageBodyWriter(detectDotPath()));
return singletons;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package eu.interedition.collatex.io;

import eu.interedition.collatex.VariantGraph;
import eu.interedition.collatex.simple.SimpleVariantGraphSerializer;

import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.concurrent.*;

/**
* Created by ronald on 5/6/15.
* Based on code written by Gregor Middell.
* Class is intended to be used as a singleton
*/
@Provider
@Produces("image/svg+xml")
public class VariantGraphSVGMessageBodyWriter implements MessageBodyWriter<VariantGraph> {
private String dotPath;
private final ExecutorService processThreads = Executors.newCachedThreadPool();

public VariantGraphSVGMessageBodyWriter(String dotPath) {
this.dotPath = dotPath;
}

@Override
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return VariantGraph.class.isAssignableFrom(type);
}

@Override
public long getSize(VariantGraph variantGraph, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return 0;
}

@Override
public void writeTo(VariantGraph graph, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
if (dotPath == null) {
throw new WebApplicationException(Response.Status.NO_CONTENT);
} else {
final StringWriter dot = new StringWriter();
new SimpleVariantGraphSerializer(graph).toDot(dot);

final Process dotProc = new ProcessBuilder(dotPath, "-Grankdir=LR", "-Gid=VariantGraph", "-Tsvg").start();
final StringWriter errors = new StringWriter();
try {
CompletableFuture.allOf(
CompletableFuture.runAsync(() -> {
final char[] buf = new char[8192];
try (final Reader errorStream = new InputStreamReader(dotProc.getErrorStream())) {
int len;
while ((len = errorStream.read(buf)) >= 0) {
errors.write(buf, 0, len);
}
} catch (IOException e) {
throw new CompletionException(e);
}
}, processThreads),
CompletableFuture.runAsync(() -> {
try (final Writer dotProcStream = new OutputStreamWriter(dotProc.getOutputStream(), "UTF-8")) {
dotProcStream.write(dot.toString());
} catch (IOException e) {
throw new CompletionException(e);
}
}, processThreads),
CompletableFuture.runAsync(() -> {
httpHeaders.add(HttpHeaders.CONTENT_TYPE, "image/svg+xml");
final byte[] buf = new byte[8192];
try (final InputStream in = dotProc.getInputStream(); final OutputStream out = entityStream) {
int len;
while ((len = in.read(buf)) >= 0) {
out.write(buf, 0, len);
}
} catch (IOException e) {
throw new CompletionException(e);
}
}, processThreads),
CompletableFuture.runAsync(() -> {
try {
if (dotProc.waitFor() != 0) {
throw new CompletionException(new IllegalStateException(errors.toString()));
}
} catch (InterruptedException e) {
throw new CompletionException(e);
}
}, processThreads)
).exceptionally(t -> {
throw new WebApplicationException(t);
}).get();
} catch(InterruptedException|ExecutionException e) {
throw new WebApplicationException(e);
}
}
}
}

0 comments on commit c824726

Please sign in to comment.