From 1f61ac350dd8b221f1d73c2a3937740c907054f0 Mon Sep 17 00:00:00 2001 From: Dave Cunningham Date: Thu, 7 Apr 2016 19:16:16 -0400 Subject: [PATCH] Java comparison --- doc/language/java_comparison/.gitignore | 1 + .../java_comparison/BaseTemplate.java | 92 +++++++++++++++++++ .../java_comparison/JsonnetObject.java | 22 +++++ .../java_comparison/JsonnetValue.java | 81 ++++++++++++++++ doc/language/java_comparison/README.md | 58 ++++++++++++ doc/language/java_comparison/SubTemplate.java | 31 +++++++ doc/language/java_comparison/Test.java | 21 +++++ .../java_comparison/base-template.jsonnet | 44 +++++++++ .../java_comparison/sub-template.json | 21 +++++ .../java_comparison/sub-template.jsonnet | 25 +++++ 10 files changed, 396 insertions(+) create mode 100644 doc/language/java_comparison/.gitignore create mode 100644 doc/language/java_comparison/BaseTemplate.java create mode 100644 doc/language/java_comparison/JsonnetObject.java create mode 100644 doc/language/java_comparison/JsonnetValue.java create mode 100644 doc/language/java_comparison/README.md create mode 100644 doc/language/java_comparison/SubTemplate.java create mode 100644 doc/language/java_comparison/Test.java create mode 100644 doc/language/java_comparison/base-template.jsonnet create mode 100644 doc/language/java_comparison/sub-template.json create mode 100644 doc/language/java_comparison/sub-template.jsonnet diff --git a/doc/language/java_comparison/.gitignore b/doc/language/java_comparison/.gitignore new file mode 100644 index 000000000..6b468b62a --- /dev/null +++ b/doc/language/java_comparison/.gitignore @@ -0,0 +1 @@ +*.class diff --git a/doc/language/java_comparison/BaseTemplate.java b/doc/language/java_comparison/BaseTemplate.java new file mode 100644 index 000000000..2ea5f3fa0 --- /dev/null +++ b/doc/language/java_comparison/BaseTemplate.java @@ -0,0 +1,92 @@ +/* +Copyright 2016 Google Inc. All rights reserved. + +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. +*/ + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class BaseTemplate extends JsonnetObject { + public Set nonHiddenFields() { + Set r = super.nonHiddenFields(); + r.addAll(Arrays.asList("apiVersion", "kind", "spec")); + return r; + } + + // Mandatory param + public Object accessToken() { + throw new RuntimeException("accessToken must be defined"); + } + + // Optional params + public Object image() { return "gcr.io/cooltool-1009/pipeline_image:latest"; } + public Object[] extraEnv() { return new Object[]{}; } + + public Object apiVersion() { return "v1"; } + public Object kind() { return "ReplicationController"; } + + public class BaseTemplateSpec extends JsonnetObject { + public Set nonHiddenFields() { + Set r = super.nonHiddenFields(); + r.addAll(Arrays.asList("replicas", "spec")); + return r; + } + + public Object replicas() { return 1.0; } + + public class BaseTemplateSpecSpec extends JsonnetObject { + public Set nonHiddenFields() { + Set r = super.nonHiddenFields(); + r.addAll(Arrays.asList("containers")); + return r; + } + + public class Container0 extends JsonnetObject { + public Set nonHiddenFields() { + Set r = super.nonHiddenFields(); + r.addAll(Arrays.asList("env", "image", "name")); + return r; + } + + class Env0 extends JsonnetObject { + public Set nonHiddenFields() { + Set r = super.nonHiddenFields(); + r.addAll(Arrays.asList("name", "value")); + return r; + } + public Object name() { return "ACCESSTOKEN"; } + public Object value() { return BaseTemplate.this.accessToken(); } + } + + public Object[] env() { + // Concatenating arrays (first + second) is hard in Java. + Object[] first = new Object[]{ new Env0() }; + Object[] second = BaseTemplate.this.extraEnv(); + Object[] result = Arrays.copyOf(first, first.length + second.length); + System.arraycopy(second, 0, result, first.length, second.length); + return result; + } + public Object image() { return BaseTemplate.this.image(); } + public Object name() { return "twitter-to-redis"; } + } + + public Object containers() { return new Object[] { new Container0() }; } + } + public Object spec() { return new BaseTemplateSpecSpec(); } + + } + public Object spec() { return new BaseTemplateSpec(); } +} + diff --git a/doc/language/java_comparison/JsonnetObject.java b/doc/language/java_comparison/JsonnetObject.java new file mode 100644 index 000000000..e085aae19 --- /dev/null +++ b/doc/language/java_comparison/JsonnetObject.java @@ -0,0 +1,22 @@ +/* +Copyright 2016 Google Inc. All rights reserved. + +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. +*/ + +import java.util.HashSet; +import java.util.Set; + +public class JsonnetObject { + public Set nonHiddenFields() { return new HashSet(){}; } +} diff --git a/doc/language/java_comparison/JsonnetValue.java b/doc/language/java_comparison/JsonnetValue.java new file mode 100644 index 000000000..ae09c6104 --- /dev/null +++ b/doc/language/java_comparison/JsonnetValue.java @@ -0,0 +1,81 @@ +/* +Copyright 2016 Google Inc. All rights reserved. + +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. +*/ + +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import java.util.HashSet; +import java.util.Set; + +public class JsonnetValue { + public static String manifest(Object value) { + StringBuffer buf = new StringBuffer(); + if (value == null) { + // FIXME: does not escape the string + buf.append("null"); + + } else if (value instanceof String) { + // FIXME: does not escape the string + buf.append("\"" + value + "\""); + + } else if (value instanceof Double) { + buf.append(value.toString()); + + } else if (value instanceof Boolean) { + buf.append(value.toString()); + + } else if (value instanceof Object[]) { + Object[] arr = (Object[]) value; + buf.append("["); + String prefix = " "; + for (Object element : arr) { + buf.append(prefix); + prefix = ", "; + buf.append(manifest(element)); + } + buf.append(" ]"); + + } else if (value instanceof JsonnetObject) { + JsonnetObject obj = (JsonnetObject) value; + buf.append("{"); + String prefix = " "; + for (String field : obj.nonHiddenFields()) { + buf.append(prefix); + prefix = ", "; + buf.append("\"" + field + "\": "); + try { + Method method = obj.getClass().getMethod(field); + Object fieldValue = method.invoke(obj); + buf.append(manifest(fieldValue)); + } catch (IllegalArgumentException e) { + System.out.println(e); + } catch (IllegalAccessException e) { + System.out.println(e); + } catch (InvocationTargetException e) { + System.out.println(e); + } catch (SecurityException e) { + System.out.println(e); + } catch (NoSuchMethodException e) { + System.out.println(e); + } + } + buf.append(" }"); + } else { + throw new RuntimeException("Got weird type: " + value.getClass()); + } + return buf.toString(); + } +} + diff --git a/doc/language/java_comparison/README.md b/doc/language/java_comparison/README.md new file mode 100644 index 000000000..3a7629126 --- /dev/null +++ b/doc/language/java_comparison/README.md @@ -0,0 +1,58 @@ +Jsonnet to Java +--------------- + +This little code example shows how a Jsonnet config using object-oriented features can be +transliterated (in a structure-preserving manner) to use the object-oriented features of Java. Not +all Jsonnet programs can be converted to Java because Jsonnet has mixins and virtual inner classes. +The semantics are also different, as Jsonnet is lazy and Java is eager. Likewise, Java programs +cannot all be converted to Jsonnet because they have mutable state and I/O. + +However said all this, there is a significant overlap between the two languages. This example fits +within that overlap. + + +Execute Jsonnet code +-------------------- + +``` +jsonnet sub-template.jsonnet +``` + + +Execute Java code +----------------- + +We pipe into jsonnet - to pretty-print the output JSON. + +``` +javac *.java && java Test | jsonnet - +``` + + +Structure of Java code +---------------------- + +The translation is as as follows: + +* Everything is typed as Object, as Jsonnet is dynamically typed. +* Jsonnet arrays are Object[], and primitives are Boolean / Double / String. +* Jsonnet objects because singleton instances of Java classes that extend JsonnetObject. +* Jsonnet fields become Java methods with no parameters (fields are virtual in Jsonnet). +* Jsonnet hidden field status is represented with a method nonHiddenFields which returns a set of + field names. Other fields are considered hidden. +* There are 2 liberties taken -- output strings are not escaped properly, and the JSON is printed on + a single line instead of pretty-printed with indenting. + +The Java code has a number of auxiliary framework functions to provide Jsonnet functionality that is +implicit in the Jsonnet language. + +* The Test class chooses an object and manifests it to stdout. +* The JsonnetValue class implements manifestation, as a visitor over possible JSON values that + builds JSON strings. For objects, it iterates over the non-hidden fields and manifests each value + by reflectively calling that function. + + +Reference JSON +-------------- + +For reference, the JSON that should be output is given in sub-template.json diff --git a/doc/language/java_comparison/SubTemplate.java b/doc/language/java_comparison/SubTemplate.java new file mode 100644 index 000000000..cbe99b322 --- /dev/null +++ b/doc/language/java_comparison/SubTemplate.java @@ -0,0 +1,31 @@ +/* +Copyright 2016 Google Inc. All rights reserved. + +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. +*/ + +public class SubTemplate extends BaseTemplate { + // Supply my access token and pinned version. + public String accessToken() { + return "xxxxx"; + } + public String image() { + return "gcr.io/cooltool-1009/pipeline_image@sha256:...."; + } + public class SubTemplateSpec extends BaseTemplate.BaseTemplateSpec { + public Object replicas() { return 2.0; } + } + public Object spec() { + return new SubTemplateSpec(); + } +} diff --git a/doc/language/java_comparison/Test.java b/doc/language/java_comparison/Test.java new file mode 100644 index 000000000..bed6b3cb1 --- /dev/null +++ b/doc/language/java_comparison/Test.java @@ -0,0 +1,21 @@ +/* +Copyright 2016 Google Inc. All rights reserved. + +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. +*/ + +public class Test { + public static void main(String[] args) { + System.out.println(JsonnetValue.manifest(new SubTemplate())); + } +} diff --git a/doc/language/java_comparison/base-template.jsonnet b/doc/language/java_comparison/base-template.jsonnet new file mode 100644 index 000000000..7790869a8 --- /dev/null +++ b/doc/language/java_comparison/base-template.jsonnet @@ -0,0 +1,44 @@ +/* +Copyright 2016 Google Inc. All rights reserved. + +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. +*/ + +{ + // Mandatory param + accessToken:: error "accessToken must be defined", + + // Optional params + image:: "gcr.io/cooltool-1009/pipeline_image:latest", + extraEnv:: [], + + apiVersion: "v1", + kind: "ReplicationController", + spec: { + replicas: 1, + spec: { + containers: [ + { + env: [ + { + name: "ACCESSTOKEN", + value: $.accessToken, + }, + ] + $.extraEnv, + image: $.image, + name: "twitter-to-redis", + }, + ], + }, + }, +} diff --git a/doc/language/java_comparison/sub-template.json b/doc/language/java_comparison/sub-template.json new file mode 100644 index 000000000..4ed1cba68 --- /dev/null +++ b/doc/language/java_comparison/sub-template.json @@ -0,0 +1,21 @@ +{ + "apiVersion": "v1", + "kind": "ReplicationController", + "spec": { + "replicas": 2, + "spec": { + "containers": [ + { + "env": [ + { + "name": "ACCESSTOKEN", + "value": "xxxxx" + } + ], + "image": "gcr.io/cooltool-1009/pipeline_image@sha256:....", + "name": "twitter-to-redis" + } + ] + } + } +} diff --git a/doc/language/java_comparison/sub-template.jsonnet b/doc/language/java_comparison/sub-template.jsonnet new file mode 100644 index 000000000..3d1e3e7f0 --- /dev/null +++ b/doc/language/java_comparison/sub-template.jsonnet @@ -0,0 +1,25 @@ +/* +Copyright 2016 Google Inc. All rights reserved. + +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. +*/ + +(import "base-template.jsonnet") + { + accessToken: "xxxxx", + image: "gcr.io/cooltool-1009/pipeline_image@sha256:....", + + spec+: { + replicas: 2, + } + +}