package org.handwerkszeug.riak.mapreduce.internal; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.concurrent.TimeUnit; import org.codehaus.jackson.JsonFactory; import org.codehaus.jackson.JsonGenerationException; import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.ObjectCodec; import org.codehaus.jackson.map.ObjectMapper; import org.handwerkszeug.riak.mapreduce.MapReduceInput; import org.handwerkszeug.riak.mapreduce.MapReduceKeyFilter; import org.handwerkszeug.riak.mapreduce.grammar.Timeoutable; import org.handwerkszeug.riak.mapreduce.internal.MapReducePhase.PhaseType; import org.handwerkszeug.riak.util.Executable; import org.handwerkszeug.riak.util.JsonAppender; /** * @author taichi * @param <T> */ public abstract class MapReduceQueryContext<T> implements Executable<T>, Timeoutable<T> { protected List<JsonAppender> inputs = new ArrayList<JsonAppender>(); protected List<MapReduceKeyFilter> keyFilters = new ArrayList<MapReduceKeyFilter>(); protected List<MapReducePhase> phases = new ArrayList<MapReducePhase>(); protected Long timeout = null; protected ObjectCodec codec; public MapReduceQueryContext() { this(new ObjectMapper()); } public MapReduceQueryContext(ObjectCodec codec) { this.codec = codec; } public void add(MapReduceInput primary, MapReduceInput... inputs) { this.inputs.add(primary); for (JsonAppender input : inputs) { this.inputs.add(input); } // freeze inputs. this.inputs = Collections.unmodifiableList(this.inputs); } public void add(MapReduceKeyFilter primary, MapReduceKeyFilter... filters) { this.keyFilters.add(primary); for (MapReduceKeyFilter filter : filters) { this.keyFilters.add(filter); } } public void freezeKeyFilters() { this.keyFilters = Collections.unmodifiableList(this.keyFilters); } public void add(PhaseType type, JsonAppender phase) { this.phases.add(new MapReducePhase(type, phase)); } public void add(PhaseType type, boolean keep, JsonAppender phase) { this.phases.add(new MapReducePhase(type, keep, phase)); } @Override public Executable<T> timeout(long millis) { this.timeout = millis; return this; } @Override public Executable<T> timeout(long timeout, TimeUnit unit) { return timeout(unit.toMillis(timeout)); } protected void prepare(OutputStream stream) { try { JsonFactory factory = new JsonFactory(this.codec); JsonGenerator generator = factory.createJsonGenerator(stream); prepare(generator); } catch (IOException e) { throw new IllegalStateException(e); } } protected void prepare(JsonGenerator generator) throws IOException { generator.writeStartObject(); prepareInputs(generator); generator.writeArrayFieldStart("query"); for (Iterator<MapReducePhase> i = this.phases.iterator(); i.hasNext();) { MapReducePhase mrp = i.next(); // emulate official client mrp.mayBeKeep(i.hasNext() == false); mrp.appendTo(generator); } generator.writeEndArray(); if (this.timeout != null) { generator.writeNumberField("timeout", this.timeout); } generator.writeEndObject(); generator.flush(); } protected void prepareInputs(JsonGenerator generator) throws IOException, JsonGenerationException { generator.writeFieldName("inputs"); int size = this.inputs.size(); if (size < 1) { throw new IllegalStateException(); } else if (size == 1) { JsonAppender bucket = this.inputs.get(0); if (0 < this.keyFilters.size()) { generator.writeStartObject(); generator.writeFieldName("bucket"); bucket.appendTo(generator); generator.writeArrayFieldStart("key_filters"); for (JsonAppender f : this.keyFilters) { f.appendTo(generator); } generator.writeEndArray(); generator.writeEndObject(); } else { bucket.appendTo(generator); } } else { generator.writeStartArray(); for (JsonAppender i : this.inputs) { i.appendTo(generator); } generator.writeEndArray(); } } }