/*
* The MIT License
*
* Copyright 2015 Tim Boudreau.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.mastfrog.acteur.mongo.async;
import com.google.inject.Inject;
import com.mastfrog.acteur.Acteur;
import com.mastfrog.acteur.Closables;
import com.mastfrog.acteur.HttpEvent;
import com.mastfrog.acteur.errors.Err;
import com.mastfrog.acteur.preconditions.Description;
import com.mastfrog.acteur.spi.ApplicationControl;
import com.mastfrog.acteurbase.Chain;
import com.mastfrog.acteurbase.Deferral;
import com.mastfrog.acteurbase.Deferral.Resumer;
import com.mongodb.async.AsyncBatchCursor;
import com.mongodb.async.SingleResultCallback;
import com.mongodb.async.client.FindIterable;
import io.netty.handler.codec.http.HttpResponseStatus;
/**
*
* @author Tim Boudreau
*/
@Description("Write out the contents of a cursor as JSON to the response, in small batches so the "
+ "entire result never needs to be held in memory.")
public class WriteCursorContentsAsJSON extends Acteur {
@Inject
WriteCursorContentsAsJSON(Deferral def, CursorControl ctrl, Chain<Acteur> chain, Closables clos, FindIterable<?> find) {
find(find, ctrl, def, chain, clos);
}
@SuppressWarnings("unchecked")
private <T> FindIterable<T> find(FindIterable<T> result, CursorControl ctrl, Deferral def, Chain<Acteur> chain, final Closables clos) {
result = ctrl.apply(result);
final Resumer resumer = def.defer();
if (ctrl.isFindOne()) {
chain.add(SendSingleResult.class);
result.first(new SingleResultResume<T>(resumer));
} else {
chain.add(SendCursorResult.class);
result.batchCursor(new CursorResultResume<T>(clos, resumer));
}
next(result);
return result;
}
static final class SingleResultResume<T> implements SingleResultCallback<T> {
private final Resumer resumer;
public SingleResultResume(Resumer resumer) {
this.resumer = resumer;
}
@Override
public void onResult(T t, Throwable thrwbl) {
resumer.resume(new SingleResult(t, thrwbl));
}
}
static final class CursorResultResume<T> implements SingleResultCallback<AsyncBatchCursor<T>> {
private final Closables clos;
private final Resumer resumer;
public CursorResultResume(Closables clos, Resumer resumer) {
this.clos = clos;
this.resumer = resumer;
}
@Override
public void onResult(AsyncBatchCursor<T> t, Throwable thrwbl) {
clos.add(t);
resumer.resume(new CursorResult<>(t, thrwbl));
}
}
static final class SingleResult {
final Object result;
final Throwable thrown;
public SingleResult(Object result, Throwable thrown) {
this.result = result;
this.thrown = thrown;
}
}
static class SendSingleResult extends Acteur {
@Inject
SendSingleResult(SingleResult result) {
if (result.thrown != null) {
reply(Err.of(result.thrown));
} else {
ok(result.result);
}
}
}
static final class CursorResult<T> {
final AsyncBatchCursor<T> cursor;
final Throwable thrwbl;
public CursorResult(AsyncBatchCursor<T> t, Throwable thrwbl) {
this.cursor = t;
this.thrwbl = thrwbl;
}
}
static class SendCursorResult extends Acteur {
@Inject
@SuppressWarnings("unchecked")
SendCursorResult(CursorResult res, HttpEvent evt, ApplicationControl ctrl, CursorWriterFactory fact) {
if (res.thrwbl != null) {
reply(Err.of(res.thrwbl));
} else {
reply(HttpResponseStatus.OK);
setResponseBodyWriter(fact.create(res.cursor));
}
}
}
}