summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoffrey BION <joffrey.bion@gmail.com>2017-04-06 00:34:53 +0200
committerJoffrey BION <joffrey.bion@gmail.com>2017-04-06 00:34:53 +0200
commitcb840113e34f5dddffc66bc3d4265b9fd67c7e0a (patch)
treeac4be2380a6a34d29d20ef16f9b3253b4497c74b
parentMake GameDefinition transient in the lobby (diff)
downloadseven-wonders-cb840113e34f5dddffc66bc3d4265b9fd67c7e0a.tar.gz
seven-wonders-cb840113e34f5dddffc66bc3d4265b9fd67c7e0a.tar.bz2
seven-wonders-cb840113e34f5dddffc66bc3d4265b9fd67c7e0a.zip
Remove traversal of transient fields
This caused infinite loops in the original Jsondoc code
-rw-r--r--backend/src/main/java/org/luxons/sevenwonders/doc/JsonDocController.java2
-rw-r--r--backend/src/main/java/org/luxons/sevenwonders/doc/builders/JSONDocTemplateBuilder.java137
-rw-r--r--backend/src/main/java/org/luxons/sevenwonders/doc/scanner/JsonDocWebSocketScanner.java50
3 files changed, 187 insertions, 2 deletions
diff --git a/backend/src/main/java/org/luxons/sevenwonders/doc/JsonDocController.java b/backend/src/main/java/org/luxons/sevenwonders/doc/JsonDocController.java
index 11725150..e9edde3e 100644
--- a/backend/src/main/java/org/luxons/sevenwonders/doc/JsonDocController.java
+++ b/backend/src/main/java/org/luxons/sevenwonders/doc/JsonDocController.java
@@ -36,7 +36,7 @@ public class JsonDocController {
this.version = "1.0.0";
this.basePath = "http://localhost:8080";
this.packages = Arrays.asList("org.luxons.sevenwonders.controllers", "org.luxons.sevenwonders.doc",
- "org.luxons.sevenwonders.actions", "org.luxons.sevenwonders.game");
+ "org.luxons.sevenwonders.actions", "org.luxons.sevenwonders.game", "org.luxons.sevenwonders.lobby");
this.jsondocScanner = new JsonDocWebSocketScanner();
}
diff --git a/backend/src/main/java/org/luxons/sevenwonders/doc/builders/JSONDocTemplateBuilder.java b/backend/src/main/java/org/luxons/sevenwonders/doc/builders/JSONDocTemplateBuilder.java
new file mode 100644
index 00000000..78c86584
--- /dev/null
+++ b/backend/src/main/java/org/luxons/sevenwonders/doc/builders/JSONDocTemplateBuilder.java
@@ -0,0 +1,137 @@
+package org.luxons.sevenwonders.doc.builders;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.jsondoc.core.annotation.ApiObjectField;
+import org.jsondoc.core.pojo.JSONDocTemplate;
+import org.jsondoc.core.util.JSONDocFieldWrapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class JSONDocTemplateBuilder {
+
+ private static final Logger log = LoggerFactory.getLogger(org.jsondoc.core.util.JSONDocTemplateBuilder.class);
+ private static final Map<Class<?>, Class<?>> primitives = new HashMap<Class<?>, Class<?>>();
+
+ static {
+ primitives.put(boolean.class, Boolean.class);
+ primitives.put(byte.class, Byte.class);
+ primitives.put(char.class, String.class);
+ primitives.put(double.class, Double.class);
+ primitives.put(float.class, Float.class);
+ primitives.put(int.class, Integer.class);
+ primitives.put(long.class, Long.class);
+ primitives.put(short.class, Short.class);
+ primitives.put(void.class, Void.class);
+ }
+
+ public static JSONDocTemplate build(Class<?> clazz, Set<Class<?>> jsondocObjects) {
+ final JSONDocTemplate jsonDocTemplate = new JSONDocTemplate();
+
+ if(jsondocObjects.contains(clazz)) {
+ try {
+ Set<JSONDocFieldWrapper> fields = getAllDeclaredFields(clazz);
+
+ for (JSONDocFieldWrapper jsondocFieldWrapper : fields) {
+ Field field = jsondocFieldWrapper.getField();
+ String fieldName = field.getName();
+ ApiObjectField apiObjectField = field.getAnnotation(ApiObjectField.class);
+ if (apiObjectField != null && !apiObjectField.name().isEmpty()) {
+ fieldName = apiObjectField.name();
+ }
+
+ Object value;
+ // This condition is to avoid StackOverflow in case class "A"
+ // contains a field of type "A"
+ if (field.getType().equals(clazz) || (apiObjectField != null && !apiObjectField.processtemplate())) {
+ value = getValue(Object.class, field.getGenericType(), fieldName, jsondocObjects);
+ } else {
+ value = getValue(field.getType(), field.getGenericType(), fieldName, jsondocObjects);
+ }
+
+ jsonDocTemplate.put(fieldName, value);
+ }
+
+ } catch (Exception e) {
+ log.error("Error in JSONDocTemplate creation for class [" + clazz.getCanonicalName() + "]", e);
+ }
+ }
+
+ return jsonDocTemplate;
+ }
+
+ private static Object getValue(Class<?> fieldClass, Type fieldGenericType, String fieldName, Set<Class<?>> jsondocObjects) {
+
+ if (fieldClass.isPrimitive()) {
+ return getValue(wrap(fieldClass), null, fieldName, jsondocObjects);
+
+ } else if (Map.class.isAssignableFrom(fieldClass)) {
+ return new HashMap<Object, Object>();
+
+ } else if (Number.class.isAssignableFrom(fieldClass)) {
+ return new Integer(0);
+
+ } else if (String.class.isAssignableFrom(fieldClass) || fieldClass.isEnum()) {
+ return new String("");
+
+ } else if (Boolean.class.isAssignableFrom(fieldClass)) {
+ return new Boolean("true");
+
+ } else if (fieldClass.isArray() || Collection.class.isAssignableFrom(fieldClass)) {
+ return new ArrayList<Object>();
+
+ } else {
+ return build(fieldClass, jsondocObjects);
+ }
+
+ }
+
+ private static Set<JSONDocFieldWrapper> getAllDeclaredFields(Class<?> clazz) {
+ Set<JSONDocFieldWrapper> fields = new TreeSet<JSONDocFieldWrapper>();
+
+ List<Field> declaredFields = new ArrayList<Field>();
+ if (clazz.isEnum()) {
+ return fields;
+ } else {
+ declaredFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
+ }
+
+ for (Field field : declaredFields) {
+ if (!shouldBeSerialized(field)) {
+ continue;
+ }
+ if (field.isAnnotationPresent(ApiObjectField.class)) {
+ ApiObjectField annotation = field.getAnnotation(ApiObjectField.class);
+ fields.add(new JSONDocFieldWrapper(field, annotation.order()));
+ } else {
+ fields.add(new JSONDocFieldWrapper(field, Integer.MAX_VALUE));
+ }
+ }
+
+ if (clazz.getSuperclass() != null) {
+ fields.addAll(getAllDeclaredFields(clazz.getSuperclass()));
+ }
+
+ return fields;
+ }
+
+ private static boolean shouldBeSerialized(Field field) {
+ return !field.isSynthetic() && !Modifier.isTransient(field.getModifiers());
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T> Class<T> wrap(Class<T> clazz) {
+ return clazz.isPrimitive() ? (Class<T>) primitives.get(clazz) : clazz;
+ }
+
+}
diff --git a/backend/src/main/java/org/luxons/sevenwonders/doc/scanner/JsonDocWebSocketScanner.java b/backend/src/main/java/org/luxons/sevenwonders/doc/scanner/JsonDocWebSocketScanner.java
index f305a8ac..0f5f5557 100644
--- a/backend/src/main/java/org/luxons/sevenwonders/doc/scanner/JsonDocWebSocketScanner.java
+++ b/backend/src/main/java/org/luxons/sevenwonders/doc/scanner/JsonDocWebSocketScanner.java
@@ -4,6 +4,7 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
+import java.net.URL;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
@@ -16,6 +17,7 @@ import org.jsondoc.core.annotation.Api;
import org.jsondoc.core.annotation.ApiMethod;
import org.jsondoc.core.pojo.ApiMethodDoc;
import org.jsondoc.core.pojo.JSONDoc;
+import org.jsondoc.core.pojo.JSONDoc.MethodDisplay;
import org.jsondoc.core.pojo.JSONDocTemplate;
import org.jsondoc.core.scanner.builder.JSONDocApiMethodDocBuilder;
import org.jsondoc.core.util.JSONDocUtils;
@@ -28,8 +30,14 @@ import org.jsondoc.springmvc.scanner.builder.SpringQueryParamBuilder;
import org.jsondoc.springmvc.scanner.builder.SpringResponseBuilder;
import org.jsondoc.springmvc.scanner.builder.SpringResponseStatusBuilder;
import org.jsondoc.springmvc.scanner.builder.SpringVerbBuilder;
+import org.luxons.sevenwonders.doc.builders.JSONDocTemplateBuilder;
import org.luxons.sevenwonders.doc.builders.SpringPathBuilder;
import org.luxons.sevenwonders.doc.builders.SpringRequestBodyBuilder;
+import org.reflections.Reflections;
+import org.reflections.scanners.MethodAnnotationsScanner;
+import org.reflections.util.ClasspathHelper;
+import org.reflections.util.ConfigurationBuilder;
+import org.reflections.util.FilterBuilder;
import org.springframework.beans.BeanUtils;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.annotation.SubscribeMapping;
@@ -38,6 +46,46 @@ import org.springframework.web.bind.annotation.RequestMapping;
public class JsonDocWebSocketScanner extends Spring4JSONDocScanner {
+ /**
+ * Returns the main <code>ApiDoc</code>, containing <code>ApiMethodDoc</code> and <code>ApiObjectDoc</code> objects
+ * @return An <code>ApiDoc</code> object
+ */
+ public JSONDoc getJSONDoc(String version, String basePath, List<String> packages, boolean playgroundEnabled, MethodDisplay displayMethodAs) {
+ Set<URL> urls = new HashSet<URL>();
+ FilterBuilder filter = new FilterBuilder();
+
+ log.debug("Found " + packages.size() + " package(s) to scan...");
+ for (String pkg : packages) {
+ log.debug("Adding package to JSONDoc recursive scan: " + pkg);
+ urls.addAll(ClasspathHelper.forPackage(pkg));
+ filter.includePackage(pkg);
+ }
+
+ reflections = new Reflections(new ConfigurationBuilder().filterInputsBy(filter).setUrls(urls).addScanners(new MethodAnnotationsScanner()));
+
+ JSONDoc jsondocDoc = new JSONDoc(version, basePath);
+ jsondocDoc.setPlaygroundEnabled(playgroundEnabled);
+ jsondocDoc.setDisplayMethodAs(displayMethodAs);
+
+ jsondocControllers = jsondocControllers();
+ jsondocObjects = jsondocObjects(packages);
+ jsondocFlows = jsondocFlows();
+ jsondocGlobal = jsondocGlobal();
+ jsondocChangelogs = jsondocChangelogs();
+ jsondocMigrations = jsondocMigrations();
+
+ for (Class<?> clazz : jsondocObjects) {
+ jsondocTemplates.put(clazz, JSONDocTemplateBuilder.build(clazz, jsondocObjects));
+ }
+
+ jsondocDoc.setApis(getApiDocsMap(jsondocControllers, displayMethodAs));
+ jsondocDoc.setObjects(getApiObjectsMap(jsondocObjects));
+ jsondocDoc.setFlows(getApiFlowDocsMap(jsondocFlows, allApiMethodDocs));
+ jsondocDoc.setGlobal(getApiGlobalDoc(jsondocGlobal, jsondocChangelogs, jsondocMigrations));
+
+ return jsondocDoc;
+ }
+
@Override
public Set<Method> jsondocMethods(Class<?> controller) {
Set<Method> annotatedMethods = new LinkedHashSet<Method>();
@@ -167,7 +215,7 @@ public class JsonDocWebSocketScanner extends Spring4JSONDocScanner {
}
}
- private boolean isValidForRecursion(Field field) {
+ private static boolean isValidForRecursion(Field field) {
return !field.isSynthetic() && !field.getType().isPrimitive() && !Modifier.isTransient(field.getModifiers());
}
}
bgstack15