/* Copyright (c) 2014 Boundless and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/edl-v10.html
*
* Contributors:
* Gabriel Roldan (Boundless) - initial implementation
*/
package org.locationtech.geogig.rest.repository;
import static org.locationtech.geogig.rest.repository.RESTUtils.getGeogig;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import org.locationtech.geogig.api.GeoGIG;
import org.locationtech.geogig.api.ObjectId;
import org.locationtech.geogig.api.plumbing.CreateDeduplicator;
import org.locationtech.geogig.remote.BinaryPackedObjects;
import org.locationtech.geogig.remote.ObjectFunnel;
import org.locationtech.geogig.remote.ObjectFunnels;
import org.locationtech.geogig.repository.Repository;
import org.locationtech.geogig.storage.Deduplicator;
import org.locationtech.geogig.storage.datastream.DataStreamSerializationFactoryV1;
import org.restlet.Context;
import org.restlet.Finder;
import org.restlet.data.MediaType;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.resource.OutputRepresentation;
import org.restlet.resource.Representation;
import org.restlet.resource.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Throwables;
import com.google.common.io.CountingOutputStream;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
/**
* Takes a set of commit Ids and packs up their contents into a binary stream to send to the client.
*/
public class BatchedObjectResource extends Finder {
private static final Logger LOGGER = LoggerFactory.getLogger(BatchedObjectResource.class);
@Override
public Resource findTarget(Request request, Response response) {
return new ObjectResource(getContext(), request, response);
}
private static class ObjectResource extends Resource {
public ObjectResource(//
Context context, //
Request request, //
Response response) //
{
super(context, request, response);
}
@Override
public boolean allowPost() {
return true;
}
@Override
public void post(Representation entity) {
InputStream inStream;
try {
inStream = entity.getStream();
} catch (IOException e) {
throw Throwables.propagate(e);
}
final Reader body = new InputStreamReader(inStream);
final JsonParser parser = new JsonParser();
final JsonElement messageJson = parser.parse(body);
LOGGER.info("Serving request to send objects based on message {}", messageJson);
final List<ObjectId> want = new ArrayList<ObjectId>();
final List<ObjectId> have = new ArrayList<ObjectId>();
if (messageJson.isJsonObject()) {
final JsonObject message = messageJson.getAsJsonObject();
final JsonArray wantArray;
final JsonArray haveArray;
if (message.has("want") && message.get("want").isJsonArray()) {
wantArray = message.get("want").getAsJsonArray();
} else {
wantArray = new JsonArray();
}
if (message.has("have") && message.get("have").isJsonArray()) {
haveArray = message.get("have").getAsJsonArray();
} else {
haveArray = new JsonArray();
}
for (final JsonElement e : wantArray) {
if (e.isJsonPrimitive()) {
want.add(ObjectId.valueOf(e.getAsJsonPrimitive().getAsString()));
}
}
for (final JsonElement e : haveArray) {
if (e.isJsonPrimitive()) {
have.add(ObjectId.valueOf(e.getAsJsonPrimitive().getAsString()));
}
}
}
Request request = getRequest();
final GeoGIG ggit = getGeogig(request).get();
final Repository repository = ggit.getRepository();
final Deduplicator deduplicator = ggit.command(CreateDeduplicator.class).call();
BinaryPackedObjects packer = new BinaryPackedObjects(repository.stagingDatabase());
Representation rep = new RevObjectBinaryRepresentation(packer, want, have, deduplicator);
Response response = getResponse();
response.setEntity(rep);
}
}
private static class RevObjectBinaryRepresentation extends OutputRepresentation {
private final BinaryPackedObjects packer;
private final List<ObjectId> want;
private final List<ObjectId> have;
private Deduplicator deduplicator;
public RevObjectBinaryRepresentation( //
BinaryPackedObjects packer, //
List<ObjectId> want, //
List<ObjectId> have, //
Deduplicator deduplicator) //
{
super(MediaType.APPLICATION_OCTET_STREAM);
this.packer = packer;
this.want = want;
this.have = have;
this.deduplicator = deduplicator;
}
@Override
public void write(final OutputStream out) throws IOException {
CountingOutputStream counting = new CountingOutputStream(out);
OutputStream output = counting;
try {
ObjectFunnel funnel;
funnel = ObjectFunnels.newFunnel(output, DataStreamSerializationFactoryV1.INSTANCE);
packer.write(funnel, want, have, false, deduplicator);
counting.flush();
funnel.close();
} catch (IOException e) {
e.printStackTrace();
throw e;
} catch (RuntimeException e) {
e.printStackTrace();
throw e;
} finally {
deduplicator.release();
}
}
}
}