/** * Copyright 2013 Twitter, Inc. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.twitter.crunch.tools.jsontopology; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.util.List; import java.util.ListIterator; import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.Version; import org.codehaus.jackson.annotate.JsonIgnore; import org.codehaus.jackson.map.JsonSerializer; import org.codehaus.jackson.map.Module; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.ObjectWriter; import org.codehaus.jackson.map.SerializationConfig; import org.codehaus.jackson.map.SerializerProvider; import org.codehaus.jackson.map.annotate.JsonSerialize; import org.codehaus.jackson.map.introspect.BasicBeanDescription; import org.codehaus.jackson.map.ser.BeanPropertyWriter; import org.codehaus.jackson.map.ser.BeanSerializerModifier; import com.twitter.crunch.Node; import com.twitter.crunch.Selector; public final class JsonTopologySerializer implements TopologySerializer { public void writeTopology(Topology topology, OutputStream os) throws IOException { getWriter().writeValue(os, topology); } public void writeTopology(Topology topology, String path) throws IOException { getWriter().writeValue(new File(path), topology); } private ObjectWriter getWriter() { ObjectMapper mapper = new ObjectMapper(); // omit null fields from serialization mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL); // exclude certain fields and getter methods from node serialization via mixin mapper.getSerializationConfig().addMixInAnnotations(Node.class, MixIn.class); // register the module that suppresses the failed property if false mapper.registerModule(new IsFailedSuppressor()); return mapper.writer().withDefaultPrettyPrinter(); } private abstract class MixIn { @JsonIgnore public abstract long getId(); @JsonIgnore public abstract Node getParent(); @JsonIgnore public abstract boolean isLeaf(); @JsonIgnore public abstract Selector getSelector(); @JsonIgnore public abstract List<Node> getAllLeafNodes(); @JsonIgnore public abstract int getChildrenCount(); @JsonIgnore public abstract Node getRoot(); } private static class IsFailedSuppressor extends Module { public String getModuleName() { return "IsFailedSuppressor"; } public Version version() { return new Version(1, 0, 0, null); } public void setupModule(SetupContext context) { context.addBeanSerializerModifier(new BeanSerializerModifier() { @Override public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BasicBeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) { ListIterator<BeanPropertyWriter> it = beanProperties.listIterator(); while (it.hasNext()) { BeanPropertyWriter writer = it.next(); // replace the bean writer with my own if it is for "failed" if (writer.getName().equals("failed")) { BeanPropertyWriter newWriter = new IsFailedWriter(writer); it.set(newWriter); } } return beanProperties; } }); } } private static class IsFailedWriter extends BeanPropertyWriter { public IsFailedWriter(BeanPropertyWriter base) { super(base); } public IsFailedWriter(BeanPropertyWriter base, JsonSerializer<Object> ser) { super(base, ser); } @Override public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider prov) throws Exception { Object value = get(bean); if (value instanceof Boolean) { Boolean b = (Boolean)value; if (!b.booleanValue()) { // filter if "failed" is false return; } } super.serializeAsField(bean, jgen, prov); } @Override public BeanPropertyWriter withSerializer(JsonSerializer<Object> ser) { return new IsFailedWriter(this, ser); } } }