blob: e1fb860e79c8223450ae1ef592190e0ad9ba9026 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.datatorrent.stram.webapp;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.net.URI;
import java.util.AbstractList;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.junit.Assert;
import org.junit.Test;
import org.apache.hadoop.conf.Configuration;
import org.apache.tools.ant.DirectoryScanner;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.google.common.collect.Lists;
import com.datatorrent.api.DefaultInputPort;
import com.datatorrent.api.DefaultOutputPort;
import com.datatorrent.api.InputOperator;
import com.datatorrent.api.annotation.InputPortFieldAnnotation;
import com.datatorrent.api.annotation.OutputPortFieldAnnotation;
import com.datatorrent.common.util.BaseOperator;
import com.datatorrent.stram.plan.logical.LogicalPlan;
import com.datatorrent.stram.plan.logical.LogicalPlan.OperatorMeta;
import com.datatorrent.stram.plan.logical.LogicalPlanConfiguration;
import com.datatorrent.stram.support.StramTestSupport;
import com.datatorrent.stram.util.ObjectMapperFactory;
import com.datatorrent.stram.webapp.TypeDiscoverer.UI_TYPE;
public class OperatorDiscoveryTest
{
// private static final Logger LOG = LoggerFactory.getLogger(OperatorDiscoveryTest.class);
public static class GenericClassBase<T> extends BaseOperator
{
private int A;
private T B;
private ArrayList<?> testWildCard;
private Map<?, ? extends Number> testWildCardMapProperty;
@InputPortFieldAnnotation(optional = true)
public final transient DefaultInputPort<T> input = new DefaultInputPort<T>()
{
@Override
public void process(T tuple)
{
output.emit("abcd");
}
};
public final transient DefaultInputPort<T> input1 = new DefaultInputPort<T>()
{
@Override
public void process(T tuple)
{
// Do nothing
}
};
public final transient DefaultInputPort<Map<?, ? extends String>> input2 = new DefaultInputPort<Map<?, ? extends String>>()
{
@Override
public void process(Map<?, ? extends String> tuple)
{
// Do nothing
}
};
@OutputPortFieldAnnotation(optional = false, error = true)
public final transient DefaultOutputPort<String> output = new DefaultOutputPort<>();
public final transient DefaultOutputPort<Double> output1 = new DefaultOutputPort<>();
@Override
public String getName()
{
return "abc";
}
public int getA()
{
return A;
}
public void setA(int a)
{
A = a;
}
public T getB()
{
return B;
}
public void setB(T b)
{
B = b;
}
public ArrayList<?> getTestWildCard()
{
return testWildCard;
}
public void setTestWildCard(ArrayList<?> testWildCard)
{
this.testWildCard = testWildCard;
}
public Map<?, ? extends Number> getTestWildCardMapProperty()
{
return testWildCardMapProperty;
}
public void setTestWildCardMapProperty(Map<?, ? extends Number> testWildCardMapProperty)
{
this.testWildCardMapProperty = testWildCardMapProperty;
}
}
public static class SubClassGeneric<K extends Number> extends GenericClassBase<K>
{
}
public static class SubSubClassGeneric<T extends Long> extends SubClassGeneric<T>
{
public final transient DefaultOutputPort<T> output1 = new DefaultOutputPort<>();
}
public static class CircleType<E extends Comparator<E>>
{
private E circleType;
public E getCircleType()
{
return circleType;
}
public void setCircleType(E circleType)
{
this.circleType = circleType;
}
}
@Test
public void bruteForceTest() throws Exception
{
String[] classFilePath = getClassFileInClasspath();
OperatorDiscoverer operatorDiscoverer = new OperatorDiscoverer(classFilePath);
operatorDiscoverer.buildTypeGraph();
for (String arbitraryClass : operatorDiscoverer.getTypeGraph().getInstantiableDescendants("java.lang.Object")) {
operatorDiscoverer.describeClass(arbitraryClass);
}
}
@Test
public void testTypeGraphSerializer() throws Exception
{
String[] classFilePath = getClassFileInClasspath();
OperatorDiscoverer operatorDiscoverer = new OperatorDiscoverer(classFilePath);
operatorDiscoverer.buildTypeGraph();
// make sure (de)serialization of type graph works without problem
Kryo kryo = new Kryo();
TypeGraph.TypeGraphSerializer tgs = new TypeGraph.TypeGraphSerializer();
kryo.register(TypeGraph.class, tgs);
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024 * 1024 * 20);
Output output = new Output(baos);
kryo.writeObject(output, operatorDiscoverer.getTypeGraph());
output.close();
Input input = new Input(new ByteArrayInputStream(baos.toByteArray()));
TypeGraph tg = kryo.readObject(input, TypeGraph.class);
}
@Test
public void testOperatorDiscoverer() throws Exception
{
String[] classFilePath = getClassFileInClasspath();
OperatorDiscoverer operatorDiscoverer = new OperatorDiscoverer(classFilePath);
operatorDiscoverer.buildTypeGraph();
JSONObject oper = operatorDiscoverer.describeOperator(SubSubClassGeneric.class.getName());
String debug = "\n(ASM)type info for " + TestOperator.class + ":\n" + oper.toString(2) + "\n";
JSONArray props = oper.getJSONArray("properties");
JSONArray portTypes = oper.getJSONArray("portTypeInfo");
JSONArray inputPorts = oper.getJSONArray("inputPorts");
JSONArray outputPorts = oper.getJSONArray("outputPorts");
Assert.assertNotNull(debug + "Properties aren't null ", props);
Assert.assertEquals(debug + "Number of properties ", 4, props.length());
Assert.assertNotNull(debug + "Port types aren't null ", portTypes);
Assert.assertEquals(debug + "Number of port types ", 5, portTypes.length());
Assert.assertNotNull(debug + "inputPorts aren't null ", inputPorts);
Assert.assertEquals(debug + "Number of inputPorts ", 3, inputPorts.length());
Assert.assertNotNull(debug + "outputPorts aren't null ", outputPorts);
Assert.assertEquals(debug + "Number of outputPorts ", 2, outputPorts.length());
// Validate port types
JSONObject portType = (JSONObject)portTypes.get(0);
Assert.assertEquals(portType.get("name"), "output1");
Assert.assertEquals(portType.get("typeLiteral"), "T");
Assert.assertEquals(portType.get("type"), "long");
portType = (JSONObject)portTypes.get(3);
Assert.assertEquals(portType.get("name"), "input2");
Assert.assertEquals(portType.get("type"), "java.util.Map");
JSONArray typeArgs = portType.getJSONArray("typeArgs");
Assert.assertEquals(debug + " type " + portType,
"class " + Object.class.getName(), typeArgs.getJSONObject(0).getJSONObject("typeBounds").getJSONArray("upper").get(0));
Assert.assertEquals(debug + " type " + portType,
"class " + String.class.getName(), typeArgs.getJSONObject(1).getJSONObject("typeBounds").getJSONArray("upper").get(0));
JSONObject wildcardType = getJSONProperty(props, "testWildCardMapProperty");
Assert.assertEquals(debug + "type " + wildcardType, Map.class.getName(), wildcardType.get("type"));
Assert.assertEquals(debug + "type " + wildcardType,
"class " + Object.class.getName(), wildcardType.getJSONArray("typeArgs").getJSONObject(0).getJSONObject("typeBounds").getJSONArray("upper").get(0));
Assert.assertEquals(debug + "type " + wildcardType, "class " + Number.class.getName(),
wildcardType.getJSONArray("typeArgs").getJSONObject(1).getJSONObject("typeBounds").getJSONArray("upper").get(0));
portType = (JSONObject)portTypes.get(4);
Assert.assertEquals(portType.get("name"), "output");
Assert.assertEquals(portType.get("type"), "java.lang.String");
// Validate input port annotations
JSONObject inputPort = (JSONObject)inputPorts.get(0);
Assert.assertEquals(inputPort.get("name"), "input");
Assert.assertEquals(inputPort.get("optional"), true);
inputPort = (JSONObject)inputPorts.get(1);
Assert.assertEquals(inputPort.get("name"), "input1");
Assert.assertEquals(inputPort.get("optional"), false);
// Validate output port annotations
JSONObject outPort = (JSONObject)outputPorts.get(0);
Assert.assertEquals(outPort.get("name"), "output");
Assert.assertEquals(outPort.get("optional"), false);
Assert.assertEquals(outPort.get("error"), true);
outPort = (JSONObject)outputPorts.get(1);
Assert.assertEquals(outPort.get("name"), "output1");
Assert.assertEquals(outPort.get("optional"), true);
Assert.assertEquals(outPort.get("error"), false);
JSONObject circleType = operatorDiscoverer.describeClass(CircleType.class.getName()).getJSONArray("properties").getJSONObject(0);
Assert.assertEquals(circleType.toString(2), circleType.getString("typeLiteral"), "E");
Assert.assertEquals(circleType.toString(2), circleType.getString("type"), "java.util.Comparator");
Assert.assertEquals(circleType.toString(2), circleType.getJSONArray("typeArgs").getJSONObject(0).getString("typeLiteral"), "E");
}
@Test
public void testPropertyDiscovery() throws Exception
{
String[] classFilePath = getClassFileInClasspath();
OperatorDiscoverer od = new OperatorDiscoverer(classFilePath);
od.buildTypeGraph();
Assert.assertNotNull(od.getOperatorClass(BaseOperator.class.getName()));
JSONObject asmDesc = od.describeClassByASM(TestOperator.class.getName());
String debug = "\n(ASM)type info for " + TestOperator.class + ":\n" + asmDesc.toString(2) + "\n";
JSONArray props = asmDesc.getJSONArray("properties");
Assert.assertNotNull(debug + "Properties aren't null ", props);
Assert.assertEquals(debug + "Number of properties ", 27, props.length());
// make sure properties of excluded classes are not in the asm description of the type
for (String classN : TypeGraph.EXCLUDE_CLASSES) {
Class c = Class.forName(classN.replace('/', '.'));
BeanInfo bi = Introspector.getBeanInfo(c);
for (PropertyDescriptor pd : bi.getPropertyDescriptors()) {
Assert.assertNull(debug, getJSONProperty(props, pd.getName()));
}
}
JSONObject mapProperty = getJSONProperty(props, "map");
Assert.assertEquals(debug + "canGet " + mapProperty, true, mapProperty.get("canGet"));
Assert.assertEquals(debug + "canSet " + mapProperty, true, mapProperty.get("canSet"));
Assert.assertEquals(debug + "type " + mapProperty, java.util.Map.class.getName(), mapProperty.get("type"));
JSONArray typeArgs = mapProperty.getJSONArray("typeArgs");
Assert.assertNotNull(debug + "typeArgs of map is not null", typeArgs);
Assert.assertEquals(debug + "number of typeArgs of map ", 2, typeArgs.length());
Assert.assertEquals(debug + "The first typeArg of map", String.class.getName(), typeArgs.getJSONObject(0).get("type"));
Assert.assertEquals(debug + "The second typeArg of map", Structured.class.getName(), typeArgs.getJSONObject(1).get("type"));
JSONObject integerProperty = getJSONProperty(props, "integerProp");
Assert.assertEquals(debug + "type " + integerProperty, "int", integerProperty.get("type"));
JSONObject uriProperty = getJSONProperty(props, "uri");
Assert.assertEquals(debug + "type " + uriProperty, String.class.getName(), uriProperty.get("type"));
JSONObject structuredProperty = getJSONProperty(props, "nested");
Assert.assertEquals(debug + "type " + structuredProperty, Structured.class.getName(), structuredProperty.get("type"));
JSONObject genericArray = getJSONProperty(props, "genericArray");
Assert.assertEquals(debug + "type " + genericArray, Object[].class.getName(), genericArray.get("type"));
JSONObject propProperty = getJSONProperty(props, "props");
Assert.assertEquals(debug + "uitype " + propProperty, UI_TYPE.MAP.getName(), propProperty.get("uiType"));
JSONObject stringArrayProperty = getJSONProperty(props, "stringArray");
Assert.assertEquals(debug + "type " + stringArrayProperty, String[].class.getName(), stringArrayProperty.get("type"));
JSONObject nestedParameterizedTypeProperpty = getJSONProperty(props, "nestedParameterizedType");
Assert.assertEquals(debug + "type " + nestedParameterizedTypeProperpty, Map.class.getName(), nestedParameterizedTypeProperpty.get("type"));
Assert.assertEquals(debug + "type " + nestedParameterizedTypeProperpty, Number.class.getName(),
nestedParameterizedTypeProperpty.getJSONArray("typeArgs").getJSONObject(1).getJSONArray("typeArgs").getJSONObject(0).getJSONArray("typeArgs").getJSONObject(1).get("type"));
JSONObject wildcardType = getJSONProperty(props, "wildcardType");
Assert.assertEquals(debug + "type " + wildcardType, Map.class.getName(), wildcardType.get("type"));
Assert.assertEquals(debug + "type " + wildcardType, "class " + Object.class.getName(),
wildcardType.getJSONArray("typeArgs").getJSONObject(0).getJSONObject("typeBounds").getJSONArray("upper").get(0));
Assert.assertEquals(debug + "type " + wildcardType, "class " + Long.class.getName(),
wildcardType.getJSONArray("typeArgs").getJSONObject(1).getJSONObject("typeBounds").getJSONArray("lower").get(0));
JSONObject multiDimensionPrimitiveArray = getJSONProperty(props, "multiDimensionPrimitiveArray");
Assert.assertEquals(debug + "type " + multiDimensionPrimitiveArray, int[][].class.getName(), multiDimensionPrimitiveArray.get("type"));
JSONObject enumDesc = od.describeClass(Color.class);
debug = "\nJson for Color enum:\n" + enumDesc.toString(2) + "\n";
JSONArray enumNames = enumDesc.getJSONArray("enum");
Assert.assertNotNull(debug + "enumNames are not null", enumNames);
Assert.assertEquals(debug + "First element of color", Color.BLUE.name(), enumNames.get(0));
JSONObject desc = od.describeClass(ExtendedOperator.class);
debug = "\ntype info for " + ExtendedOperator.class + ":\n" + desc.toString(2) + "\n";
props = desc.getJSONArray("properties");
genericArray = getJSONProperty(props, "genericArray");
Assert.assertEquals(debug + "type " + genericArray, String[].class.getName(), genericArray.get("type"));
// Test complicated Type Variable override in Hierarchy
desc = od.describeClassByASM(SubSubClass.class.getName());
props = desc.getJSONArray("properties");
JSONObject aObj = getJSONProperty(props, "a");
Assert.assertEquals("type " + aObj, Number.class.getName(), aObj.get("type"));
JSONObject bObj = getJSONProperty(props, "b");
Assert.assertEquals("type " + bObj, "long", bObj.get("type"));
JSONObject cObj = getJSONProperty(props, "c");
Assert.assertEquals("type " + cObj, List.class.getName(), cObj.get("type"));
JSONObject dObj = getJSONProperty(props, "d");
Assert.assertEquals("type " + dObj, List.class.getName(), dObj.get("type"));
JSONObject eObj = getJSONProperty(props, "e");
Assert.assertEquals("type " + eObj, Runnable.class.getName(), eObj.get("type"));
// describeClassByASM now populates portTypes too, so checking only properties part
ObjectMapper om = new ObjectMapper();
desc = od.describeClass(Structured.class);
asmDesc = od.describeClassByASM(Structured.class.getName());
Assert.assertEquals("\ntype info for " + Structured.class + ":\n", om.readTree(desc.get("properties").toString()), om.readTree(asmDesc.get("properties").toString()));
desc = od.describeClass(Color.class);
asmDesc = od.describeClassByASM(Color.class.getName());
Assert.assertEquals("\ntype info for " + Color.class + ":\n", om.readTree(desc.get("properties").toString()), om.readTree(asmDesc.get("properties").toString()));
// test uitype for wrapper type
asmDesc = od.describeClassByASM(Integer.class.getName());
Assert.assertEquals("type info for" + Integer.class + ":\n" + asmDesc.toString(2), "int", asmDesc.getString("uiType"));
// test uitype for FromStringSerializer supported type
asmDesc = od.describeClassByASM(URI.class.getName());
Assert.assertEquals("type info for" + URI.class + ":\n" + asmDesc.toString(2), "java.lang.String", asmDesc.getString("uiType"));
}
public static String[] getClassFileInClasspath()
{
String classpath = System.getProperty("java.class.path");
String[] paths = classpath.split(":");
List<String> fnames = new LinkedList<>();
for (String cp : paths) {
File f = new File(cp);
if (!f.isDirectory()) {
continue;
}
DirectoryScanner ds = new DirectoryScanner();
ds.setBasedir(f);
ds.setIncludes(new String[]{"**\\*.class"});
ds.scan();
for (String name : ds.getIncludedFiles()) {
fnames.add(new File(f, name).getAbsolutePath());
}
}
return fnames.toArray(new String[]{});
}
private JSONObject getJSONProperty(JSONArray props, String name) throws JSONException
{
for (int i = 0; i < props.length(); i++) {
if (props.getJSONObject(i).get("name").equals(name)) {
return props.getJSONObject(i);
}
}
return null;
}
@Test
public void testFindDescendants() throws Exception
{
OperatorDiscoverer od = new OperatorDiscoverer();
List<String> dList = od.getTypeGraph().getInstantiableDescendants("java.util.Map");
Assert.assertTrue("The instantiable descendants list of type java.util.Map: \n" + dList, dList.contains("java.util.HashMap"));
Assert.assertTrue("The instantiable descendants list of type java.util.Map: \n" + dList, !dList.contains(AbstractMap.class.getName()));
dList = od.getTypeGraph().getInstantiableDescendants("java.util.List");
Assert.assertTrue("The instantiable descendants list of type java.util.List: \n" + dList, dList.contains("java.util.ArrayList"));
Assert.assertTrue("The instantiable descendants list of type java.util.List: \n" + dList, !dList.contains(AbstractList.class.getName()));
String[] jdkQueue = new String[] {DelayQueue.class.getName(), LinkedBlockingDeque.class.getName(),
LinkedBlockingQueue.class.getName(), PriorityBlockingQueue.class.getName(), SynchronousQueue.class.getName()};
List<String> actualQueueClass = od.getTypeGraph().getInstantiableDescendants("java.util.concurrent.BlockingQueue");
for (String expectedClass : jdkQueue) {
Assert.assertTrue("Actual queue: " + actualQueueClass.toString() + "\n Expected contained queue: " + expectedClass, actualQueueClass.contains(expectedClass));
}
List<String> expectedNumberClasses = Lists.newArrayList(Byte.class.getName(), Short.class.getName(), Long.class.getName(), Integer.class.getName(), Double.class.getName(), Float.class.getName());
List<String> actualNumberClasses = od.getTypeGraph().getInstantiableDescendants(Number.class.getName());
Assert.assertTrue("Actual Number types: " + actualNumberClasses.toString() + "\n Expected contained types: " + expectedNumberClasses, actualNumberClasses.containsAll(expectedNumberClasses));
}
@Test
public void testValueSerialization() throws Exception
{
TestOperator<String, Map<String, Number>> bean = new TestOperator<>();
bean.map.put("key1", new Structured());
bean.stringArray = new String[]{"one", "two", "three"};
bean.stringList = Lists.newArrayList("four", "five");
bean.props = new Properties();
bean.props.setProperty("key1", "value1");
bean.structuredArray = new Structured[]{new Structured()};
bean.genericArray = new String[]{"s1"};
bean.structuredArray[0].name = "s1";
bean.color = Color.BLUE;
bean.booleanProp = true;
bean.nestedList = new LinkedList<>();
Structured st = new Structured();
st.name = "nestedone";
st.size = 10;
bean.nestedList.add(st);
bean.uri = new URI("file:///tmp/file");
bean.integerProp = 44;
ObjectMapper mapper = ObjectMapperFactory.getOperatorValueSerializer();
String s = mapper.writeValueAsString(bean);
JSONObject jsonObj = new JSONObject(s);
Assert.assertTrue("Null property 'nested' should be cut off", !jsonObj.has("nested"));
TestOperator<?, ?> clone = mapper.readValue(s, TestOperator.class);
Assert.assertNotNull(clone.structuredArray);
Assert.assertEquals(Color.BLUE, clone.color);
Assert.assertEquals(bean.structuredArray.length, clone.structuredArray.length);
Assert.assertEquals(bean.integerProp, clone.integerProp);
Assert.assertEquals(bean.uri, clone.uri);
}
@Test
public void testExternalResource() throws Exception
{
StramTestSupport.createAppPackageFile();
String[] classFilePath = getClassFileInClasspath();
String[]cPaths = Lists.asList("src/test/resources/testAppPackage/mydtapp/target/mydtapp-1.0-SNAPSHOT.jar", classFilePath).toArray(new String[]{});
OperatorDiscoverer od = new OperatorDiscoverer(cPaths);
od.buildTypeGraph();
Assert.assertEquals("true", od.describeClass("com.example.mydtapp.StdoutOperator").getString("hasResource"));
StramTestSupport.removeAppPackageFile();
}
public static class Structured
{
private int size;
private String name;
private ArrayList<String> list;
public int getSize()
{
return size;
}
public void setSize(int size)
{
this.size = size;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public ArrayList<String> getList()
{
return list;
}
public void setList(ArrayList<String> list)
{
this.list = list;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((list == null) ? 0 : list.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + size;
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Structured other = (Structured)obj;
if (list == null) {
if (other.list != null) {
return false;
}
} else if (!list.equals(other.list)) {
return false;
}
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
if (size != other.size) {
return false;
}
return true;
}
}
public enum Color
{
BLUE,
RED,
WHITE
}
public static class TestOperator<T, Z extends Map<String, Number>> extends BaseOperator
{
private int intProp;
private long longProp;
private double doubleProp;
private boolean booleanProp;
private Integer integerProp;
private List<String> stringList;
private List<Structured> nestedList;
private Properties props;
private Structured nested;
private Map<String, Structured> map = new HashMap<>();
private String[] stringArray;
private Color color;
private Structured[] structuredArray;
private T[] genericArray;
private Map<String, List<Map<String, Number>>> nestedParameterizedType = new HashMap<>();
private Map<?, ? super Long> wildcardType = new HashMap<Object, Number>();
private List<int[]> listofIntArray = new LinkedList<>();
private List<T> parameterizedTypeVariable = new LinkedList<>();
private Z genericType;
private int[][] multiDimensionPrimitiveArray;
private Structured[][] multiDimensionComplexArray;
private URI uri;
private String realName = "abc";
private String getterOnly = "getterOnly";
// this property can not be deserialized by jackson but it will be ignored if it has no setter method
private Map<Class, String> mProp;
private Class aClass = Object.class;
public Class getaClass()
{
return aClass;
}
public void setaClass(Class aClass)
{
this.aClass = aClass;
}
public Map<Class, String> getmProp()
{
return mProp;
}
public String getAlias()
{
return realName;
}
public void setAlias(String alias)
{
realName = alias;
}
public String getGetterOnly()
{
return getterOnly;
}
public URI getUri()
{
return uri;
}
public void setUri(URI uri)
{
this.uri = uri;
}
public void setIntegerProp(Integer integerProp)
{
this.integerProp = integerProp;
}
public Integer getIntegerProp()
{
return integerProp;
}
public int getIntProp()
{
return intProp;
}
public void setIntProp(int intProp)
{
this.intProp = intProp;
}
public long getLongProp()
{
return longProp;
}
public void setLongProp(long longProp)
{
this.longProp = longProp;
}
public double getDoubleProp()
{
return doubleProp;
}
public void setDoubleProp(double doubleProp)
{
this.doubleProp = doubleProp;
}
public List<String> getStringList()
{
return stringList;
}
public void setStringList(List<String> stringList)
{
this.stringList = stringList;
}
public Properties getProps()
{
return props;
}
public void setProps(Properties props)
{
this.props = props;
}
public Structured getNested()
{
return nested;
}
public void setNested(Structured n)
{
this.nested = n;
}
public Map<String, Structured> getMap()
{
return map;
}
public void setMap(Map<String, Structured> m)
{
this.map = m;
}
public Color getColor()
{
return color;
}
public void setColor(Color color)
{
this.color = color;
}
public String[] getStringArray()
{
return stringArray;
}
public void setStringArray(String[] stringArray)
{
this.stringArray = stringArray;
}
public Structured[] getStructuredArray()
{
return structuredArray;
}
public void setStructuredArray(Structured[] structuredArray)
{
this.structuredArray = structuredArray;
}
public T[] getGenericArray()
{
return genericArray;
}
public void setGenericArray(T[] genericArray)
{
this.genericArray = genericArray;
}
public boolean isBooleanProp()
{
return booleanProp;
}
public void setBooleanProp(boolean booleanProp)
{
this.booleanProp = booleanProp;
}
public Map<String, List<Map<String, Number>>> getNestedParameterizedType()
{
return nestedParameterizedType;
}
public void setNestedParameterizedType(Map<String, List<Map<String, Number>>> nestedParameterizedType)
{
this.nestedParameterizedType = nestedParameterizedType;
}
public Map<? extends Object, ? super Long> getWildcardType()
{
return wildcardType;
}
public void setWildcardType(Map<? extends Object, ? super Long> wildcardType)
{
this.wildcardType = wildcardType;
}
public Z getGenericType()
{
return genericType;
}
public void setGenericType(Z genericType)
{
this.genericType = genericType;
}
public int[][] getMultiDimensionPrimitiveArray()
{
return multiDimensionPrimitiveArray;
}
public void setMultiDimensionPrimitiveArray(int[][] multiDimensionPrimitiveArray)
{
this.multiDimensionPrimitiveArray = multiDimensionPrimitiveArray;
}
public Structured[][] getMultiDimensionComplexArray()
{
return multiDimensionComplexArray;
}
public void setMultiDimensionComplexArray(Structured[][] multiDimensionComplexArray)
{
this.multiDimensionComplexArray = multiDimensionComplexArray;
}
public List<int[]> getListofIntArray()
{
return listofIntArray;
}
public void setListofIntArray(List<int[]> listofIntArray)
{
this.listofIntArray = listofIntArray;
}
public List<T> getParameterizedTypeVariable()
{
return parameterizedTypeVariable;
}
public void setParameterizedTypeVariable(List<T> parameterizedTypeVariable)
{
this.parameterizedTypeVariable = parameterizedTypeVariable;
}
public <AMAZING extends Callable<Map<String, String>>> AMAZING getAmazing()
{
return null;
}
public List<Structured> getNestedList()
{
return nestedList;
}
public void setNestedList(List<Structured> nestedList)
{
this.nestedList = nestedList;
}
}
public static class InputTestOperator<T, Z extends Map<String, Number>> extends TestOperator<T,Z> implements InputOperator
{
@Override
public void emitTuples()
{
}
}
static class ExtendedOperator extends TestOperator<String, Map<String, Number>>
{
}
public static class BaseClass<A, B, C>
{
private A a;
private B b;
private C c;
public void setA(A a)
{
this.a = a;
}
public void setB(B b)
{
this.b = b;
}
public A getA()
{
return a;
}
public B getB()
{
return b;
}
public void setC(C c)
{
this.c = c;
}
public C getC()
{
return c;
}
}
public static class SubClass<D, A extends Number> extends BaseClass<Number, A, D>
{
private D d;
public void setD(D d)
{
this.d = d;
}
public D getD()
{
return d;
}
}
public static class SubSubClass<E extends Runnable> extends SubClass<List<String>, Long>
{
private E e;
public void setE(E e)
{
this.e = e;
}
public E getE()
{
return e;
}
}
public static class ArraysHolder
{
public int[] intArray = new int[]{1, 2, 3};
public Structured[] beanArray = new Structured[]{};
public int[] getIntArray()
{
return intArray;
}
public void setIntArray(int[] intArray)
{
this.intArray = intArray;
}
public Structured[] getBeanArray()
{
return beanArray;
}
public void setBeanArray(Structured[] beanArray)
{
this.beanArray = beanArray;
}
}
@Test
public void testArraySerialization() throws Exception
{
OperatorDiscoverer od = new OperatorDiscoverer();
Assert.assertNotNull(od.getOperatorClass(BaseOperator.class.getName()));
JSONObject desc = od.describeClass(ArraysHolder.class);
String debugInfo = "\ntype info for " + ArraysHolder.class + ":\n" + desc.toString(2) + "\n";
JSONArray props = desc.getJSONArray("properties");
ArraysHolder ah = new ArraysHolder();
JSONObject beanArray = getJSONProperty(props, "beanArray");
Assert.assertEquals(debugInfo + "type " + ah.beanArray.getClass(), ah.beanArray.getClass().getName(), beanArray.get("type"));
JSONObject intArray = getJSONProperty(props, "intArray");
Assert.assertEquals(debugInfo + "type " + ah.intArray.getClass(), ah.intArray.getClass().getName(), intArray.get("type"));
ObjectMapper mapper = ObjectMapperFactory.getOperatorValueSerializer();
String s = mapper.writeValueAsString(ah);
ArraysHolder clone = mapper.readValue(s, ArraysHolder.class);
Assert.assertNotNull(clone.intArray);
Assert.assertArrayEquals(ah.intArray, clone.intArray);
}
@Test
public void testLogicalPlanConfiguration() throws Exception
{
TestOperator<String, Map<String, Number>> bean = new InputTestOperator<>();
bean.map.put("key1", new Structured());
bean.stringArray = new String[]{"one", "two", "three"};
bean.stringList = Lists.newArrayList("four", "five");
bean.props = new Properties();
bean.props.setProperty("key1", "value1");
bean.structuredArray = new Structured[]{new Structured()};
bean.genericArray = new String[]{"s1"};
bean.structuredArray[0].name = "s1";
bean.color = Color.BLUE;
bean.booleanProp = true;
bean.realName = "abc";
ObjectMapper mapper = ObjectMapperFactory.getOperatorValueSerializer();
String s = mapper.writeValueAsString(bean);
// LOG.debug(new JSONObject(s).toString(2));
//
Assert.assertTrue("Shouldn't contain field 'realName' !", !s.contains("realName"));
Assert.assertTrue("Should contain property 'alias' !", s.contains("alias"));
Assert.assertTrue("Shouldn't contain property 'getterOnly' !", !s.contains("getterOnly"));
JSONObject jsonObj = new JSONObject(s);
// create the json dag representation
JSONObject jsonPlan = new JSONObject();
jsonPlan.put("streams", new JSONArray());
JSONObject jsonOper = new JSONObject();
jsonOper.put("name", "Test Operator");
jsonOper.put("class", InputTestOperator.class.getName());
jsonOper.put("properties", jsonObj);
jsonPlan.put("operators", new JSONArray(Lists.newArrayList(jsonOper)));
Configuration conf = new Configuration(false);
LogicalPlanConfiguration lpc = new LogicalPlanConfiguration(conf);
// create logical plan from the json
LogicalPlan lp = lpc.createFromJson(jsonPlan, "jsontest");
OperatorMeta om = lp.getOperatorMeta("Test Operator");
Assert.assertTrue(om.getOperator() instanceof InputTestOperator);
@SuppressWarnings("rawtypes")
TestOperator beanBack = (InputTestOperator)om.getOperator();
// The operator deserialized back from json should be same as original operator
Assert.assertEquals(bean.map, beanBack.map);
Assert.assertArrayEquals(bean.stringArray, beanBack.stringArray);
Assert.assertEquals(bean.stringList, beanBack.stringList);
Assert.assertEquals(bean.props, beanBack.props);
Assert.assertArrayEquals(bean.structuredArray, beanBack.structuredArray);
Assert.assertArrayEquals(bean.genericArray, beanBack.genericArray);
Assert.assertEquals(bean.color, beanBack.color);
Assert.assertEquals(bean.booleanProp, beanBack.booleanProp);
Assert.assertEquals(bean.realName, beanBack.realName);
Assert.assertEquals(bean.getterOnly, beanBack.getterOnly);
}
public static class SchemaRequiredOperator extends BaseOperator implements InputOperator
{
@OutputPortFieldAnnotation(schemaRequired = true)
public final transient DefaultOutputPort<Object> output = new DefaultOutputPort<>();
@OutputPortFieldAnnotation(schemaRequired = false)
public final transient DefaultOutputPort<Object> output1 = new DefaultOutputPort<>();
public final transient DefaultOutputPort<Object> output2 = new DefaultOutputPort<>();
@Override
public void emitTuples()
{
}
}
@Test
public void testPortSchema() throws Exception
{
String[] classFilePath = getClassFileInClasspath();
OperatorDiscoverer od = new OperatorDiscoverer(classFilePath);
od.buildTypeGraph();
JSONObject operatorJson = od.describeOperator(SchemaRequiredOperator.class.getName());
JSONArray portsJson = operatorJson.getJSONArray("outputPorts");
Assert.assertEquals("no. of ports", 3, portsJson.length());
for (int i = 0; i < portsJson.length(); i++) {
JSONObject portJson = portsJson.getJSONObject(i);
String name = portJson.getString("name");
if (name.equals("output")) {
Assert.assertEquals("output schema", true, portJson.getBoolean("schemaRequired"));
} else if (name.equals("output1")) {
Assert.assertEquals("output1 schema", false, portJson.getBoolean("schemaRequired"));
} else if (name.equals("output2")) {
Assert.assertEquals("output2 schema", false, portJson.getBoolean("schemaRequired"));
}
}
}
@Test
public void testAdditionalPortInfo() throws Exception
{
String[] classFilePath = getClassFileInClasspath();
OperatorDiscoverer operatorDiscoverer = new OperatorDiscoverer(classFilePath);
operatorDiscoverer.buildTypeGraph();
JSONObject operator = operatorDiscoverer.describeOperator(SubSubClassGeneric.class.getName());
JSONObject portClassHierarchy = new JSONObject();
JSONObject portsWithSchemaClasses = new JSONObject();
operatorDiscoverer.buildAdditionalPortInfo(operator, portClassHierarchy, portsWithSchemaClasses);
JSONArray stringTypeArray = portClassHierarchy.optJSONArray("java.lang.String");
Assert.assertNotNull("string hierarchy", stringTypeArray);
Assert.assertEquals("number of immediate ancestors", 4, stringTypeArray.length());
Assert.assertEquals("number of port types with schema", 0, portsWithSchemaClasses.length());
}
@Test
public void testMethodType()
{
Assert.assertEquals("@omitFromUI", OperatorDiscoverer.MethodTagType.OMIT_FROM_UI, OperatorDiscoverer.MethodTagType.from("@omitFromUI"));
Assert.assertEquals("@useSchema", OperatorDiscoverer.MethodTagType.USE_SCHEMA, OperatorDiscoverer.MethodTagType.from("@useSchema"));
Assert.assertEquals("@description", OperatorDiscoverer.MethodTagType.DESCRIPTION, OperatorDiscoverer.MethodTagType.from("@description"));
Assert.assertEquals("@random", null, OperatorDiscoverer.MethodTagType.from("@random"));
}
}