net.aequologica.neo.serioulizer.jaxrs.service.Resource Maven / Gradle / Ivy
The newest version!
package net.aequologica.neo.serioulizer.jaxrs.service;
import static javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM_TYPE;
import static javax.ws.rs.core.MediaType.valueOf;
import static javax.ws.rs.core.Response.noContent;
import static javax.ws.rs.core.Response.ok;
import static javax.ws.rs.core.Response.status;
import static javax.ws.rs.core.Response.Status.NOT_FOUND;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.servlet.ServletContext;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
import javax.ws.rs.core.UriInfo;
import org.glassfish.jersey.media.multipart.FormDataBodyPart;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Joiner;
import net.aequologica.neo.serioulizer.Serioulizer;
@Singleton
@javax.ws.rs.Path("v1")
public class Resource {
@SuppressWarnings("unused")
private static final Logger log = LoggerFactory.getLogger(Resource.class);
@Inject
private Serioulizer serioulizer;
@Context
private ServletContext context;
@GET
@javax.ws.rs.Path("_ping_")
@Produces(MediaType.TEXT_PLAIN)
public Response ping() {
return Response.ok().entity("api/serioulizer/v1 is here").build();
}
@GET
@javax.ws.rs.Path("/list/{path : .*}")
@Produces(MediaType.APPLICATION_JSON)
public List _GET_list__path(
@DefaultValue("") @PathParam ("path") final String path,
@DefaultValue("false") @QueryParam("sort") final Boolean sort) throws IOException {
List list = paths2strings(serioulizer.list( path.length() == 0 ? Paths.get("/") : Paths.get(path)));
if (sort) {
Collections.sort(list, Comparator.naturalOrder());
}
return list;
}
/*
* read http://www.pixeldonor.com/2013/oct/12/concurrent-zip-compression-java-nio/
*/
@GET
@javax.ws.rs.Path("/zip/{path : .*}")
@Produces("application/zip")
public Response _GET_zip__path(
@DefaultValue("") @PathParam ("path") final String pathAsString,
@Context final UriInfo uriInfo) throws IOException {
final StreamingOutput streamingOutput = new StreamingOutput() {
@Override
public void write(OutputStream output) throws IOException, WebApplicationException {
try {
final List list = serioulizer.list( pathAsString.length() == 0 ? Paths.get("/") : Paths.get(pathAsString));
try (ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(output))) {
zipOutputStream.setLevel(9);
for (Path path : list) {
// @TODO Path relativized = serioulizer.relativize(path);
Path relativized = path;
zipOutputStream.putNextEntry(new ZipEntry(relativized.toString()));
Serioulizer.Stream stream = serioulizer.getStream(relativized);
if (stream != null) {
pipe(stream.getInputStream(), zipOutputStream);
}
zipOutputStream.closeEntry();
}
}
} catch (Exception e) {
throw new WebApplicationException(e);
}
}
};
String filenameWithoutExtension = Joiner.on('-')
.skipNulls()
.join( uriInfo.getBaseUri()
.getHost(),
"stnemucod",
com.google.common.base.Strings.emptyToNull( pathAsString.replace('/', ' ')
.replace('\\', ' ')
.trim()
.replace(' ', '_')));
return ok(streamingOutput)
.type("application/zip")
.header("Content-Disposition", "attachment; filename=\"" + filenameWithoutExtension + ".zip\"")
.build();
}
@GET
@javax.ws.rs.Path("/document/{path : .+}")
public Response _GET_document__path(
@DefaultValue("") @PathParam ("path") final String pathAsString) throws IOException {
Serioulizer.Stream stream = null;
try {
stream = serioulizer.getStream(Paths.get(pathAsString));
} catch (IOException ioe) {
return status(NOT_FOUND).build();
}
return ok( stream.getInputStream(),
pathAsString.endsWith("svg") ? "image/svg+xml" :
pathAsString.endsWith("css") ? "text/css" :
stream.getMimeType()
).header( "Content-Length", stream.getLength() )
.build();
}
@PUT
@javax.ws.rs.Path("/document/{path : .+}")
@Consumes(MediaType.TEXT_PLAIN)
public Response _PUT_document__path(
@DefaultValue("/") @PathParam("path") final String pathAsString,
final InputStream content) throws IOException {
serioulizer.write( Paths.get(pathAsString), Serioulizer.createStream(content, "text/plain; charset=UTF-8", -1));
return noContent().build();
}
@POST
@javax.ws.rs.Path("/upload/{path : .*}")
@Consumes(MediaType.MULTIPART_FORM_DATA) // THIS REQUIRES register(MultiPartFeature.class) !!!!!
public Response _POST_document__path(
@DefaultValue("/") @PathParam ("path") final String pathAsString,
@FormDataParam ("file") final FormDataContentDisposition fileDetail,
@FormDataParam ("file") final FormDataBodyPart body,
@FormDataParam ("file") final InputStream inputStream,
@DefaultValue("true") @QueryParam ("isUTF8") final Boolean isUTF8,
@DefaultValue("false") @QueryParam ("doUnzip") final Boolean doUnzip) throws IOException {
if (null == fileDetail) {
throw new IllegalArgumentException("@FormDataParam(\"file\") FormDataContentDisposition fileDetail is null");
}
if (null == inputStream) {
throw new IllegalArgumentException("@FormDataParam(\"file\") InputStream inputStream is null");
}
final String filename = fileDetail.getParameters().get("filename");
if (null == filename || filename.length() == 0) {
throw new IllegalArgumentException("fileDetail.getParameters().get(\"filename\") is null or empty");
}
MediaType mediaType = body.getMediaType();
if (mediaType.toString().startsWith(MediaType.APPLICATION_OCTET_STREAM.toString())) {
// APPLICATION_OCTET_STREAM is default, try to ask servlet context for something more precise
String mimeTypeFromServletContext = context.getMimeType(filename);
if (mimeTypeFromServletContext != null) {
mediaType = MediaType.valueOf(mimeTypeFromServletContext);
}
}
if (isUTF8 != null && isUTF8) {
mediaType = mediaType.withCharset(StandardCharsets.UTF_8.name());
}
final String mimeType = mediaType.toString();
if (doUnzip && (mimeType.startsWith("application/zip") ||
mimeType.startsWith("application/x-zip"))) {
try (final ZipInputStream zipInputStream = new ZipInputStream(inputStream)){
for (ZipEntry zipEntry = zipInputStream.getNextEntry(); zipEntry != null ; zipEntry = zipInputStream.getNextEntry()) {
if (!zipEntry.isDirectory()) {
serioulizer.write( Paths.get(zipEntry.getName()),
Serioulizer.createStream( zipInputStream,
guessMimeType(zipEntry.getName(), isUTF8, context),
zipEntry.getSize()));
}
zipInputStream.closeEntry();
}
}
return noContent().build();
}
serioulizer.write( Paths.get(pathAsString, filename),
Serioulizer.createStream(inputStream, mimeType, fileDetail.getSize()));
return noContent().build();
}
@DELETE
@javax.ws.rs.Path("/document/{path : .+}")
public Response _DELETE_document__path(@PathParam("path") String path) throws IOException {
serioulizer.delete(Paths.get(path));
return noContent().build();
}
static private List paths2strings(List paths) {
if (paths == null) {
return Collections.emptyList();
}
return paths.stream().map(p -> p.toString()).collect(Collectors.toList());
}
static private void pipe(final InputStream is, final OutputStream os) throws IOException {
int n;
byte[] buffer = new byte[1024];
while ((n = is.read(buffer)) > -1) {
os.write(buffer, 0, n);
}
}
private static String guessMimeType(final String filename, final Boolean isUTF8, final ServletContext context) {
MediaType mediaType = APPLICATION_OCTET_STREAM_TYPE;
String guessedMimeType = context.getMimeType(filename.toString());
if (guessedMimeType != null) {
mediaType = valueOf(guessedMimeType);
}
if (isUTF8 != null && isUTF8) {
mediaType = mediaType.withCharset(StandardCharsets.UTF_8.name());
}
return mediaType.toString();
}
}